徵求代寫MT5 EA - MACD背離+ATR停損策略
徵求一個MT5 EA源碼主要是MACD背離,並以ATR數值設定停損價
交易邏輯如下:
1.面板可設定項目
(1)交易手數
(2)MACD快線長度(系統預設為13)
(3)MACD慢線長度(系統預設為34)
(4)ATR設置(系統預設為13)
(5)交易時區(系統預設為M15)
(6)魔術碼
2.多單進場條件
(1)MACD柱體在零線以下(柱體為負,表示空頭結構)
(2)柱體出現連續"明顯"的 2 個波峰,且高度升高(負值變小,數值往 0 靠近)
(3)對應的這 2 根柱體的 K 線,其低點呈現連續降低
(4)確認第2個波峰型態的柱體訊號穩定,對應的K棒(也就是波峰的下一根K棒,先暫稱L棒)結束後,在下一個K棒開始時進場多單
(5)止損:L棒的低點 - ATR(13)的點數,作為停損價格
(6)止盈價與止損價初始為1:1
(7)移動止盈
3.空單進場
(1)MACD 柱體在零線以上(柱體為正,表示多頭結構)
(2)柱體出現連續"明顯" 2 個波峰且高度降低(正值變小,數值往 0 靠近)
(3)對應的這 2 根柱體的 K 線,其高點連續抬高
(4)確認第2個波峰型態的柱體訊號穩定,對應的K棒(也就是波峰的下一根K棒,先暫稱H棒)結束後,在下一個K棒開始時進場空單
(5)止損:H棒的高點 + ATR(13)的點數,作為停損價格
(6)止盈價與止損價初始為1:1
(7)移動止盈
4.MACD柱體如果貫穿零線,則波峰的計算值規0,重新計算有效波峰數
5.上面敘述的內容,主要是參考這支影片,希望能將交易系統自動化
https://www.youtube.com/watch?v=Ic8V1Pue-2I&t=2s
因為這個系統的停損價,是價格直接減去ATR數值,不確定是不是能適用外匯、指數各商品,可以再討論,是否改採其他方式停損
有其他建議都歡迎討論
我可以帮你写 //+------------------------------------------------------------------+
//| MACD_Div_ATR_EA.mq5 |
//| Copyright 2025, bzcsx |
//| https://www.mql5.com/ |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, bzcsx"
#property link "https://www.mql5.com/"
#property version "2.00"
#property strict
#property description "增强版MACD背离交易系统(带ATR止损)"
#property description "做多条件: MACD柱状图低于零轴且峰值上升 + 价格低点下降"
#property description "做空条件: MACD柱状图高于零轴且峰值下降 + 价格高点上升"
#property description "包含多重过滤条件和风险管理选项"
#include <Trade\Trade.mqh>
#include <ChartObjects\ChartObjectsTxtControls.mqh>
//+------------------------------------------------------------------+
//| 输入参数 |
//+------------------------------------------------------------------+
//--- MACD核心参数
input int FastEMA = 13; // MACD快线周期
input int SlowEMA = 34; // MACD慢线周期
input int SignalPeriod = 9; // MACD信号线周期
input ENUM_TIMEFRAMES TimeFrame = PERIOD_M15; // 交易时间框架
input ulong MagicNumber = 12345; // EA魔术码
//--- 风险管理
input double RiskPercent = 2.0; // 每笔交易风险百分比
input double FixedLotSize = 0.1; // 固定交易手数(当RiskPercent=0时使用)
input double RiskRewardRatio = 1.5; // 风险回报比
input int ATRPeriod = 13; // ATR止损周期
input double ATRMultiplier = 1.5; // ATR止损倍数
input bool UseTrailingStop = true; // 启用移动止损
input int TrailingStopPoints = 50; // 移动止损点数
input int MinStopLossPoints = 30; // 最小止损点数
input int MaxStopLossPoints = 200; // 最大止损点数
//--- 交易过滤器
input bool UseTrendFilter = true; // 启用趋势MA过滤
input int TrendMAPeriod = 50; // 趋势MA周期
input ENUM_MA_METHOD TrendMAMethod = MODE_SMA; // 趋势MA计算方法
input bool UseHigherTFConfirmation = false; // 启用更高时间框架确认
input ENUM_TIMEFRAMES HigherTF = PERIOD_H1; // 更高时间框架
input bool UseTimeFilter = false; // 启用交易时间过滤
input string TradeStartTime = "09:00"; // 交易开始时间(服务器时间)
input string TradeEndTime = "17:00"; // 交易结束时间(服务器时间)
input int MinPeakCount = 2; // 背离所需最小峰值数量
input int MaxPeakCount = 3; // 考虑的最大峰值数量
//--- 出场策略
input bool UsePartialProfit = true; // 启用部分平仓获利
input double PartialProfitRatio = 0.5; // 1:1时平仓比例
input double SecondTPMultiplier = 2.0; // 剩余仓位的目标倍数
input bool CloseOnOppositeSignal = true; // 出现相反信号时平仓
//--- 资金管理
input double MaxRiskPerTrade = 0.05; // 每笔交易最大风险(0.05=5%)
input double MaxDailyLossPercent = 5.0;// 每日最大亏损百分比
input int MaxTradesPerDay = 5; // 每日最大交易次数
//+------------------------------------------------------------------+
//| 全局变量 |
//+------------------------------------------------------------------+
double macdMain[], macdSignal[], macdHistogram[], atrValues[];
datetime lastTradeTime, lastDailyCheck;
int peakCount = 0;
double lastPeakValue = 0;
double firstPeakValue = 0;
double firstPeakPrice = 0;
double secondPeakPrice = 0;
bool lookingForLong = false;
bool lookingForShort = false;
double lBarLow = 0;
double hBarHigh = 0;
double dailyProfitLoss = 0;
int dailyTradeCount = 0;
int macdHandle, atrHandle, trendMAHandle, higherTFMAHandle;
MqlRates rates[];
CTrade Trade;
bool TradingPaused = false;
// 控制按钮
CChartObjectButton BuyButton;
CChartObjectButton SellButton;
CChartObjectButton PauseButton;
CChartObjectButton CloseAllButton;
//+------------------------------------------------------------------+
//| 专家初始化函数 |
//+------------------------------------------------------------------+
int OnInit()
{
// 初始化指标句柄
macdHandle = iMACD(_Symbol, TimeFrame, FastEMA, SlowEMA, SignalPeriod, PRICE_CLOSE);
atrHandle = iATR(_Symbol, TimeFrame, ATRPeriod);
trendMAHandle = iMA(_Symbol, TimeFrame, TrendMAPeriod, 0, TrendMAMethod, PRICE_CLOSE);
if(UseHigherTFConfirmation)
higherTFMAHandle = iMA(_Symbol, HigherTF, TrendMAPeriod, 0, TrendMAMethod, PRICE_CLOSE);
// 检查参数设置是否有效
if(RiskPercent <= 0 && FixedLotSize <= 0)
{
Alert("风险管理参数配置不正确!");
return(INIT_PARAMETERS_INCORRECT);
}
// 重置每日统计
ResetDailyStats();
// 设置交易对象的魔术码
Trade.SetExpertMagicNumber(MagicNumber);
// 创建控制按钮
CreateButtons();
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| 创建控制按钮函数 |
//+------------------------------------------------------------------+
void CreateButtons()
{
// 获取图表宽度和高度
long chartWidth = (long)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS);
long chartHeight = (long)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS);
// 按钮尺寸
int buttonWidth = 80;
int buttonHeight = 30;
int buttonSpacing = 10;
// 计算按钮位置(右下角)
int xPos = (int)(chartWidth - buttonWidth - buttonSpacing);
int yPos = (int)(chartHeight - buttonHeight - buttonSpacing);
// 创建买入按钮
if(!BuyButton.Create(0, "BuyButton", 0, xPos, yPos, buttonWidth, buttonHeight))
Print("创建买入按钮失败!");
BuyButton.Description("做多");
BuyButton.Color(clrDarkGreen);
BuyButton.BackColor(clrForestGreen);
BuyButton.BorderColor(clrWhite);
BuyButton.FontSize(10);
BuyButton.Color(clrWhite);
BuyButton.State(false);
// 创建卖出按钮
yPos -= (buttonHeight + buttonSpacing);
if(!SellButton.Create(0, "SellButton", 0, xPos, yPos, buttonWidth, buttonHeight))
Print("创建卖出按钮失败!");
SellButton.Description("做空");
SellButton.Color(clrDarkRed);
SellButton.BackColor(clrFireBrick);
SellButton.BorderColor(clrWhite);
SellButton.FontSize(10);
SellButton.Color(clrWhite);
SellButton.State(false);
// 创建暂停加仓按钮
yPos -= (buttonHeight + buttonSpacing);
if(!PauseButton.Create(0, "PauseButton", 0, xPos, yPos, buttonWidth, buttonHeight))
Print("创建暂停按钮失败!");
PauseButton.Description("暂停交易");
PauseButton.Color(clrGoldenrod);
PauseButton.BackColor(clrDarkGoldenrod);
PauseButton.BorderColor(clrWhite);
PauseButton.FontSize(10);
PauseButton.Color(clrWhite);
PauseButton.State(false);
// 创建清仓按钮
yPos -= (buttonHeight + buttonSpacing);
if(!CloseAllButton.Create(0, "CloseAllButton", 0, xPos, yPos, buttonWidth, buttonHeight))
Print("创建清仓按钮失败!");
CloseAllButton.Description("全部平仓");
CloseAllButton.Color(clrDarkSlateGray);
CloseAllButton.BackColor(clrDimGray);
CloseAllButton.BorderColor(clrWhite);
CloseAllButton.FontSize(10);
CloseAllButton.Color(clrWhite);
CloseAllButton.State(false);
}
//+------------------------------------------------------------------+
//| 重置每日统计数据 |
//+------------------------------------------------------------------+
void ResetDailyStats()
{
lastDailyCheck = iTime(_Symbol, PERIOD_D1, 0);
dailyProfitLoss = 0;
dailyTradeCount = 0;
}
//+------------------------------------------------------------------+
//| 专家逆初始化函数 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
// 释放指标句柄
IndicatorRelease(macdHandle);
IndicatorRelease(atrHandle);
IndicatorRelease(trendMAHandle);
if(UseHigherTFConfirmation) IndicatorRelease(higherTFMAHandle);
// 删除按钮
BuyButton.Delete();
SellButton.Delete();
PauseButton.Delete();
CloseAllButton.Delete();
}
//+------------------------------------------------------------------+
//| 获取MACD指标值 |
//+------------------------------------------------------------------+
void GetMACDValues()
{
ArrayResize(macdMain, MaxPeakCount+2);
ArrayResize(macdSignal, MaxPeakCount+2);
ArrayResize(macdHistogram, MaxPeakCount+2);
ArrayResize(atrValues, MaxPeakCount+2);
// 获取价格数据
if(CopyRates(_Symbol, TimeFrame, 0, MaxPeakCount+2, rates) < MaxPeakCount+2)
{
Print("获取价格数据失败!");
return;
}
// 获取MACD值
if(CopyBuffer(macdHandle, MAIN_LINE, 0, MaxPeakCount+2, macdMain) < MaxPeakCount+2 ||
CopyBuffer(macdHandle, SIGNAL_LINE, 0, MaxPeakCount+2, macdSignal) < MaxPeakCount+2)
{
Print("获取MACD数据失败!");
return;
}
// 计算柱状图值
for(int i = 0; i < MaxPeakCount+2; i++)
{
macdHistogram = macdMain - macdSignal;
}
// 获取ATR值
if(CopyBuffer(atrHandle, 0, 0, MaxPeakCount+2, atrValues) < MaxPeakCount+2)
{
Print("获取ATR数据失败!");
}
}
//+------------------------------------------------------------------+
//| 根据风险计算手数 |
//+------------------------------------------------------------------+
double CalculateLotSize(double stopLossPoints)
{
if(RiskPercent <= 0) return FixedLotSize;
double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
if(tickValue == 0) return FixedLotSize;
double riskAmount = MathMin(AccountInfoDouble(ACCOUNT_BALANCE) * RiskPercent / 100,
AccountInfoDouble(ACCOUNT_BALANCE) * MaxRiskPerTrade);
double lotSize = NormalizeDouble(riskAmount / (stopLossPoints * _Point * tickValue), 2);
// 调整最小和最大手数限制
double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
return MathMax(minLot, MathMin(maxLot, lotSize));
}
//+------------------------------------------------------------------+
//| 检查当前是否在交易时间内 |
//+------------------------------------------------------------------+
bool IsTradingTime()
{
if(!UseTimeFilter) return true;
datetime start = StringToTime(TradeStartTime);
datetime end = StringToTime(TradeEndTime);
datetime now = TimeCurrent();
if(start == 0 || end == 0)
{
Print("时间格式错误! 请使用HH:MM格式");
return false;
}
// 处理隔夜交易时段
if(start < end) return (now >= start && now <= end);
else return (now >= start || now <= end);
}
//+------------------------------------------------------------------+
//| 检查更高时间框架趋势 |
//+------------------------------------------------------------------+
bool IsHigherTFTrendBullish()
{
if(!UseHigherTFConfirmation) return true;
double close, ma;
if(CopyClose(_Symbol, HigherTF, 0, 2, close) < 2 ||
CopyBuffer(higherTFMAHandle, 0, 0, 2, ma) < 2)
{
Print("获取更高时间框架数据失败!");
return true;
}
return close > ma;
}
//+------------------------------------------------------------------+
//| 检查是否超过每日限制 |
//+------------------------------------------------------------------+
bool ExceededDailyLimits()
{
datetime currentDay = iTime(_Symbol, PERIOD_D1, 0);
if(currentDay != lastDailyCheck)
{
// 新的一天,重置计数器
ResetDailyStats();
return false;
}
if(dailyProfitLoss < 0 && MathAbs(dailyProfitLoss) >= AccountInfoDouble(ACCOUNT_BALANCE) * MaxDailyLossPercent / 100)
{
Print("已达到每日亏损限额。今日停止交易。");
return true;
}
if(dailyTradeCount >= MaxTradesPerDay)
{
Print("已达到每日最大交易次数。今日停止交易。");
return true;
}
return false;
}
//+------------------------------------------------------------------+
//| 检查背离形态 |
//+------------------------------------------------------------------+
void CheckDivergence()
{
// 重置标志
lookingForLong = false;
lookingForShort = false;
// 如果交易暂停,则跳过信号检查
if(TradingPaused) return;
// 检查做多条件(MACD低于零轴)
if(macdHistogram < 0)
{
// 检查峰值形态
bool isPeak = true;
for(int i = 0; i <= MaxPeakCount; i++)
{
if(macdHistogram >= macdHistogram)
{
isPeak = false;
break;
}
}
if(isPeak && peakCount < MaxPeakCount)
{
peakCount++;
if(peakCount == 1)
{
firstPeakValue = macdHistogram;
firstPeakPrice = rates.low;
}
else if(peakCount >= MinPeakCount)
{
// 检查MACD峰值是否上升(负值减小)且价格创新低
if(macdHistogram > firstPeakValue)
{
double currentLow = rates.low;
if(currentLow < firstPeakPrice)
{
lookingForLong = true;
lBarLow = rates.low;
secondPeakPrice = currentLow;
}
}
}
}
}
// 检查做空条件(MACD高于零轴)
if(macdHistogram > 0)
{
// 检查峰值形态
bool isPeak = true;
for(int i = 0; i <= MaxPeakCount; i++)
{
if(macdHistogram <= macdHistogram)
{
isPeak = false;
break;
}
}
if(isPeak && peakCount < MaxPeakCount)
{
peakCount++;
if(peakCount == 1)
{
firstPeakValue = macdHistogram;
firstPeakPrice = rates.high;
}
else if(peakCount >= MinPeakCount)
{
// 检查MACD峰值是否下降(正值减小)且价格创新高
if(macdHistogram < firstPeakValue)
{
double currentHigh = rates.high;
if(currentHigh > firstPeakPrice)
{
lookingForShort = true;
hBarHigh = rates.high;
secondPeakPrice = currentHigh;
}
}
}
}
}
// 如果MACD穿越零线则重置峰值计数
if((macdHistogram <= 0 && macdHistogram > 0) ||
(macdHistogram >= 0 && macdHistogram < 0))
{
peakCount = 0;
}
}
//+------------------------------------------------------------------+
//| 下多单 |
//+------------------------------------------------------------------+
void PlaceLongOrder()
{
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double atrValue = atrValues * ATRMultiplier;
double stopLoss = NormalizeDouble(lBarLow - atrValue, _Digits);
double stopLossPoints = (ask - stopLoss) / _Point;
// 验证止损
if(stopLossPoints < MinStopLossPoints) stopLoss = NormalizeDouble(ask - MinStopLossPoints * _Point, _Digits);
if(stopLossPoints > MaxStopLossPoints) stopLoss = NormalizeDouble(ask - MaxStopLossPoints * _Point, _Digits);
double takeProfit = NormalizeDouble(ask + (ask - stopLoss) * RiskRewardRatio, _Digits);
double lotSize = CalculateLotSize((ask - stopLoss) / _Point);
if(UsePartialProfit)
{
// 分成两个不同止盈的订单
double firstLot = NormalizeDouble(lotSize * PartialProfitRatio, 2);
double secondLot = NormalizeDouble(lotSize - firstLot, 2);
if(firstLot > 0)
{
double firstTP = NormalizeDouble(ask + (ask - stopLoss), _Digits);
if(Trade.Buy(firstLot, _Symbol, ask, stopLoss, firstTP, "MACD背离做多1"))
{
lastTradeTime = iTime(_Symbol, TimeFrame, 0);
dailyTradeCount++;
}
else
{
Print("买入订单1失败: ", Trade.ResultRetcodeDescription());
}
}
if(secondLot > 0)
{
double secondTP = NormalizeDouble(ask + (ask - stopLoss) * SecondTPMultiplier, _Digits);
if(Trade.Buy(secondLot, _Symbol, ask, stopLoss, secondTP, "MACD背离做多2"))
{
lastTradeTime = iTime(_Symbol, TimeFrame, 0);
dailyTradeCount++;
}
else
{
Print("买入订单2失败: ", Trade.ResultRetcodeDescription());
}
}
}
else
{
if(Trade.Buy(lotSize, _Symbol, ask, stopLoss, takeProfit, "MACD背离做多"))
{
lastTradeTime = iTime(_Symbol, TimeFrame, 0);
dailyTradeCount++;
}
else
{
Print("买入订单失败: ", Trade.ResultRetcodeDescription());
}
}
lookingForLong = false;
peakCount = 0;
}
//+------------------------------------------------------------------+
//| 下空单 |
//+------------------------------------------------------------------+
void PlaceShortOrder()
{
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double atrValue = atrValues * ATRMultiplier;
double stopLoss = NormalizeDouble(hBarHigh + atrValue, _Digits);
double stopLossPoints = (stopLoss - bid) / _Point;
// 验证止损
if(stopLossPoints < MinStopLossPoints) stopLoss = NormalizeDouble(bid + MinStopLossPoints * _Point, _Digits);
if(stopLossPoints > MaxStopLossPoints) stopLoss = NormalizeDouble(bid + MaxStopLossPoints * _Point, _Digits);
double takeProfit = NormalizeDouble(bid - (stopLoss - bid) * RiskRewardRatio, _Digits);
double lotSize = CalculateLotSize((stopLoss - bid) / _Point);
if(UsePartialProfit)
{
// 分成两个不同止盈的订单
double firstLot = NormalizeDouble(lotSize * PartialProfitRatio, 2);
double secondLot = NormalizeDouble(lotSize - firstLot, 2);
if(firstLot > 0)
{
double firstTP = NormalizeDouble(bid - (stopLoss - bid), _Digits);
if(Trade.Sell(firstLot, _Symbol, bid, stopLoss, firstTP, "MACD背离做空1"))
{
lastTradeTime = iTime(_Symbol, TimeFrame, 0);
dailyTradeCount++;
}
else
{
Print("卖出订单1失败: ", Trade.ResultRetcodeDescription());
}
}
if(secondLot > 0)
{
double secondTP = NormalizeDouble(bid - (stopLoss - bid) * SecondTPMultiplier, _Digits);
if(Trade.Sell(secondLot, _Symbol, bid, stopLoss, secondTP, "MACD背离做空2"))
{
lastTradeTime = iTime(_Symbol, TimeFrame, 0);
dailyTradeCount++;
}
else
{
Print("卖出订单2失败: ", Trade.ResultRetcodeDescription());
}
}
}
else
{
if(Trade.Sell(lotSize, _Symbol, bid, stopLoss, takeProfit, "MACD背离做空"))
{
lastTradeTime = iTime(_Symbol, TimeFrame, 0);
dailyTradeCount++;
}
else
{
Print("卖出订单失败: ", Trade.ResultRetcodeDescription());
}
}
lookingForShort = false;
peakCount = 0;
}
//+------------------------------------------------------------------+
//| 管理移动止损 |
//+------------------------------------------------------------------+
void ManageTrailingStop()
{
for(int i = PositionsTotal()-1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(PositionSelectByTicket(ticket))
{
if(PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetString(POSITION_SYMBOL) == _Symbol)
{
double currentStop = PositionGetDouble(POSITION_SL);
double newStop = 0;
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
{
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
newStop = NormalizeDouble(bid - TrailingStopPoints * _Point, _Digits);
if((newStop > currentStop) || (currentStop == 0))
{
if(!Trade.PositionModify(ticket, newStop, PositionGetDouble(POSITION_TP)))
{
Print("移动止损修改失败: ", Trade.ResultRetcodeDescription());
}
}
}
else if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
{
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
newStop = NormalizeDouble(ask + TrailingStopPoints * _Point, _Digits);
if((newStop < currentStop) || (currentStop == 0))
{
if(!Trade.PositionModify(ticket, newStop, PositionGetDouble(POSITION_TP)))
{
Print("移动止损修改失败: ", Trade.ResultRetcodeDescription());
}
}
}
}
}
}
}
//+------------------------------------------------------------------+
//| 检查部分获利平仓 |
//+------------------------------------------------------------------+
void CheckPartialProfit()
{
for(int i = PositionsTotal()-1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(PositionSelectByTicket(ticket))
{
if(PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetString(POSITION_SYMBOL) == _Symbol)
{
double profitInPips = 0;
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
profitInPips = (SymbolInfoDouble(_Symbol, SYMBOL_BID) - PositionGetDouble(POSITION_PRICE_OPEN)) / _Point;
else if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
profitInPips = (PositionGetDouble(POSITION_PRICE_OPEN) - SymbolInfoDouble(_Symbol, SYMBOL_ASK)) / _Point;
double stopLossPips = MathAbs(PositionGetDouble(POSITION_PRICE_OPEN) - PositionGetDouble(POSITION_SL)) / _Point;
// 检查是否达到第一个目标(1:1)
if(profitInPips >= stopLossPips && StringFind(PositionGetString(POSITION_COMMENT), " 1") < 0)
{
double volumeToClose = NormalizeDouble(PositionGetDouble(POSITION_VOLUME) * PartialProfitRatio, 2);
if(volumeToClose > 0)
{
if(!Trade.PositionClosePartial(ticket, volumeToClose))
{
Print("部分平仓失败: ", Trade.ResultRetcodeDescription());
}
}
}
}
}
}
}
//+------------------------------------------------------------------+
//| 检查相反信号并平仓 |
//+------------------------------------------------------------------+
void CheckOppositeSignal()
{
for(int i = PositionsTotal()-1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(PositionSelectByTicket(ticket))
{
if(PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetString(POSITION_SYMBOL) == _Symbol)
{
bool shouldClose = false;
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY && lookingForShort)
shouldClose = true;
else if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL && lookingForLong)
shouldClose = true;
if(shouldClose)
{
if(!Trade.PositionClose(ticket))
{
Print("平仓失败: ", Trade.ResultRetcodeDescription());
}
}
}
}
}
}
//+------------------------------------------------------------------+
//| 关闭所有仓位 |
//+------------------------------------------------------------------+
void CloseAllPositions()
{
for(int i = PositionsTotal()-1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(PositionSelectByTicket(ticket))
{
if(PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetString(POSITION_SYMBOL) == _Symbol)
{
if(!Trade.PositionClose(ticket))
{
Print("平仓失败: ", Trade.ResultRetcodeDescription());
}
}
}
}
}
//+------------------------------------------------------------------+
//| 更新每日盈亏统计 |
//+------------------------------------------------------------------+
void UpdateDailyProfit()
{
datetime currentDay = iTime(_Symbol, PERIOD_D1, 0);
if(currentDay != lastDailyCheck)
{
// 新的一天,重置计数器
ResetDailyStats();
}
// 计算当日盈亏
dailyProfitLoss = 0;
for(int i = PositionsTotal()-1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(PositionSelectByTicket(ticket))
{
if(PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetString(POSITION_SYMBOL) == _Symbol)
{
MqlDateTime posTime, currTime;
TimeToStruct((datetime)PositionGetInteger(POSITION_TIME), posTime);
TimeToStruct(currentDay, currTime);
if(posTime.day == currTime.day && posTime.mon == currTime.mon && posTime.year == currTime.year)
{
dailyProfitLoss += PositionGetDouble(POSITION_PROFIT);
}
}
}
}
// 添加当日已平仓的盈亏
HistorySelect(lastDailyCheck, TimeCurrent());
for(int i = HistoryDealsTotal()-1; i >= 0; i--)
{
ulong dealTicket = HistoryDealGetTicket(i);
if(HistoryDealGetInteger(dealTicket, DEAL_MAGIC) == MagicNumber &&
HistoryDealGetString(dealTicket, DEAL_SYMBOL) == _Symbol)
{
MqlDateTime dealTime, currTime;
TimeToStruct((datetime)HistoryDealGetInteger(dealTicket, DEAL_TIME), dealTime);
TimeToStruct(currentDay, currTime);
if(dealTime.day == currTime.day && dealTime.mon == currTime.mon && dealTime.year == currTime.year)
{
dailyProfitLoss += HistoryDealGetDouble(dealTicket, DEAL_PROFIT);
}
}
}
}
//+------------------------------------------------------------------+
//| 专家报价处理函数 |
//+------------------------------------------------------------------+
void OnTick()
{
// 检查新K线
static datetime lastBarTime;
datetime currentBarTime = iTime(_Symbol, TimeFrame, 0);
if(currentBarTime == lastBarTime) return;
lastBarTime = currentBarTime;
// 检查每日限制
if(ExceededDailyLimits()) return;
// 检查交易时间
if(!IsTradingTime()) return;
// 获取指标值
GetMACDValues();
// 检查背离形态
CheckDivergence();
// 检查入场条件
if((lookingForLong || lookingForShort) && currentBarTime != lastTradeTime)
{
// 附加过滤器
if(UseTrendFilter)
{
double maValue;
if(CopyBuffer(trendMAHandle, 0, 0, 2, maValue) < 2)
{
Print("获取趋势MA数据失败!");
return;
}
if(lookingForLong && rates.close < maValue) lookingForLong = false;
if(lookingForShort && rates.close > maValue) lookingForShort = false;
}
if(UseHigherTFConfirmation)
{
if(lookingForLong && !IsHigherTFTrendBullish()) lookingForLong = false;
if(lookingForShort && IsHigherTFTrendBullish()) lookingForShort = false;
}
// 如果通过过滤器则下单
if(lookingForLong) PlaceLongOrder();
if(lookingForShort) PlaceShortOrder();
}
// 移动止损管理
if(UseTrailingStop) ManageTrailingStop();
// 部分获利平仓
if(UsePartialProfit) CheckPartialProfit();
// 出现相反信号时平仓
if(CloseOnOppositeSignal) CheckOppositeSignal();
}
//+------------------------------------------------------------------+
//| 专家订单处理函数 |
//+------------------------------------------------------------------+
void OnTrade()
{
UpdateDailyProfit();
}
//+------------------------------------------------------------------+
//| 图表事件处理函数 |
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
// 处理按钮点击事件
if(id == CHARTEVENT_OBJECT_CLICK)
{
if(sparam == "BuyButton")
{
// 手动买入
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double atrValue = atrValues * ATRMultiplier;
double stopLoss = NormalizeDouble(ask - atrValue, _Digits);
double takeProfit = NormalizeDouble(ask + (ask - stopLoss) * RiskRewardRatio, _Digits);
double lotSize = CalculateLotSize((ask - stopLoss) / _Point);
if(Trade.Buy(lotSize, _Symbol, ask, stopLoss, takeProfit, "手动做多"))
{
lastTradeTime = iTime(_Symbol, TimeFrame, 0);
dailyTradeCount++;
}
}
else if(sparam == "SellButton")
{
// 手动卖出
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double atrValue = atrValues * ATRMultiplier;
double stopLoss = NormalizeDouble(bid + atrValue, _Digits);
double takeProfit = NormalizeDouble(bid - (stopLoss - bid) * RiskRewardRatio, _Digits);
double lotSize = CalculateLotSize((stopLoss - bid) / _Point);
if(Trade.Sell(lotSize, _Symbol, bid, stopLoss, takeProfit, "手动做空"))
{
lastTradeTime = iTime(_Symbol, TimeFrame, 0);
dailyTradeCount++;
}
}
else if(sparam == "PauseButton")
{
// 暂停/恢复交易
TradingPaused = !TradingPaused;
if(TradingPaused)
{
PauseButton.Description("恢复交易");
PauseButton.BackColor(clrDarkOrange);
Print("EA交易已暂停");
}
else
{
PauseButton.Description("暂停交易");
PauseButton.BackColor(clrDarkGoldenrod);
Print("EA交易已恢复");
}
}
else if(sparam == "CloseAllButton")
{
// 清仓
CloseAllPositions();
Print("已平仓所有头寸");
}
}
// 处理图表大小变化事件,调整按钮位置
if(id == CHARTEVENT_CHART_CHANGE)
{
long chartWidth = (long)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS);
long chartHeight = (long)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS);
int buttonWidth = 80;
int buttonHeight = 30;
int buttonSpacing = 10;
int xPos = (int)(chartWidth - buttonWidth - buttonSpacing);
int yPos = (int)(chartHeight - buttonHeight - buttonSpacing);
BuyButton.X_Distance(xPos);
BuyButton.Y_Distance(yPos);
yPos -= (buttonHeight + buttonSpacing);
SellButton.X_Distance(xPos);
SellButton.Y_Distance(yPos);
yPos -= (buttonHeight + buttonSpacing);
PauseButton.X_Distance(xPos);
PauseButton.Y_Distance(yPos);
yPos -= (buttonHeight + buttonSpacing);
CloseAllButton.X_Distance(xPos);
CloseAllButton.Y_Distance(yPos);
}
}
//+------------------------------------------------------------------+ //+------------------------------------------------------------------+
//| Enhanced_MACD_Divergence_EA |
//| Copyright 2023, MetaQuotes Software Corp. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Software Corp."
#property link "https://www.mql5.com"
#property version "3.10"
#property strict
#property description "增强版MACD背离交易系统(带ATR止损)"
#property description "做多条件: MACD柱状图低于零轴且峰值上升 + 价格低点下降"
#property description "做空条件: MACD柱状图高于零轴且峰值下降 + 价格高点上升"
#property description "包含多重过滤条件和风险管理选项"
#include <Trade\Trade.mqh>
#include <ChartObjects\ChartObjectsTxtControls.mqh>
//+------------------------------------------------------------------+
//| 输入参数 |
//+------------------------------------------------------------------+
//--- MACD核心参数
input int FastEMA = 12; // MACD快线周期
input int SlowEMA = 26; // MACD慢线周期
input int SignalPeriod = 9; // MACD信号线周期
input ENUM_TIMEFRAMES TimeFrame = PERIOD_H1; // 交易时间框架
input ulong MagicNumber = 12345; // EA魔术码
//--- 风险管理
input double RiskPercent = 1.0; // 每笔交易风险百分比
input double FixedLotSize = 0.1; // 固定交易手数(当RiskPercent=0时使用)
input double RiskRewardRatio = 2.0; // 风险回报比
input int ATRPeriod = 14; // ATR止损周期
input double ATRMultiplier = 1.8; // ATR止损倍数
input bool UseTrailingStop = true; // 启用移动止损
input int TrailingStopPoints = 100; // 移动止损点数
input int MinStopLossPoints = 50; // 最小止损点数
input int MaxStopLossPoints = 300; // 最大止损点数
//--- 交易过滤器
input bool UseTrendFilter = true; // 启用趋势MA过滤
input int TrendMAPeriod = 200; // 趋势MA周期
input ENUM_MA_METHOD TrendMAMethod = MODE_EMA; // 趋势MA计算方法
input bool UseHigherTFConfirmation = true; // 启用更高时间框架确认
input ENUM_TIMEFRAMES HigherTF = PERIOD_D1; // 更高时间框架
input bool UseTimeFilter = false; // 启用交易时间过滤
input string TradeStartTime = "00:00"; // 交易开始时间(服务器时间)
input string TradeEndTime = "23:59"; // 交易结束时间(服务器时间)
input int MinPeakCount = 2; // 背离所需最小峰值数量
input int MaxPeakCount = 3; // 考虑的最大峰值数量
//--- 出场策略
input bool UsePartialProfit = true; // 启用部分平仓获利
input double PartialProfitRatio = 0.5; // 1:1时平仓比例
input double SecondTPMultiplier = 3.0; // 剩余仓位的目标倍数
input bool CloseOnOppositeSignal = true; // 出现相反信号时平仓
//--- 资金管理
input double MaxRiskPerTrade = 0.05; // 每笔交易最大风险(0.05=5%)
input double MaxDailyLossPercent = 2.0;// 每日最大亏损百分比
input int MaxTradesPerDay = 3; // 每日最大交易次数
//--- 调试选项
input bool EnableDebug = true; // 启用调试输出
//+------------------------------------------------------------------+
//| 全局变量 |
//+------------------------------------------------------------------+
double macdMain[], macdSignal[], macdHistogram[], atrValues[];
datetime lastTradeTime, lastDailyCheck;
int peakCount = 0;
double lastPeakValue = 0;
double firstPeakValue = 0;
double firstPeakPrice = 0;
double secondPeakValue = 0;
double secondPeakPrice = 0;
bool lookingForLong = false;
bool lookingForShort = false;
double lBarLow = 0;
double hBarHigh = 0;
double dailyProfitLoss = 0;
int dailyTradeCount = 0;
int macdHandle, atrHandle, trendMAHandle, higherTFMAHandle;
MqlRates rates[];
CTrade Trade;
bool TradingPaused = false;
// 控制按钮
CChartObjectButton BuyButton;
CChartObjectButton SellButton;
CChartObjectButton PauseButton;
CChartObjectButton CloseAllButton;
//+------------------------------------------------------------------+
//| 专家初始化函数 |
//+------------------------------------------------------------------+
int OnInit()
{
// 初始化指标句柄
macdHandle = iMACD(_Symbol, TimeFrame, FastEMA, SlowEMA, SignalPeriod, PRICE_CLOSE);
atrHandle = iATR(_Symbol, TimeFrame, ATRPeriod);
trendMAHandle = iMA(_Symbol, TimeFrame, TrendMAPeriod, 0, TrendMAMethod, PRICE_CLOSE);
if(UseHigherTFConfirmation)
higherTFMAHandle = iMA(_Symbol, HigherTF, TrendMAPeriod, 0, TrendMAMethod, PRICE_CLOSE);
// 检查参数设置是否有效
if(RiskPercent <= 0 && FixedLotSize <= 0)
{
Alert("风险管理参数配置不正确!");
return(INIT_PARAMETERS_INCORRECT);
}
// 重置每日统计
ResetDailyStats();
// 设置交易对象的魔术码
Trade.SetExpertMagicNumber(MagicNumber);
// 创建控制按钮
CreateButtons();
if(EnableDebug) Print("EA初始化完成,开始运行...");
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| 创建控制按钮函数 |
//+------------------------------------------------------------------+
void CreateButtons()
{
// 获取图表宽度和高度
long chartWidth = (long)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS);
long chartHeight = (long)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS);
// 按钮尺寸
int buttonWidth = 80;
int buttonHeight = 30;
int buttonSpacing = 10;
// 计算按钮位置(右下角)
int xPos = (int)(chartWidth - buttonWidth - buttonSpacing);
int yPos = (int)(chartHeight - buttonHeight - buttonSpacing);
// 创建买入按钮
if(!BuyButton.Create(0, "BuyButton", 0, xPos, yPos, buttonWidth, buttonHeight))
Print("创建买入按钮失败!");
BuyButton.Description("买入");
BuyButton.Color(clrWhite);
BuyButton.BackColor(clrDarkGreen);
BuyButton.BorderColor(clrWhite);
BuyButton.FontSize(10);
BuyButton.State(false);
// 创建卖出按钮
yPos -= (buttonHeight + buttonSpacing);
if(!SellButton.Create(0, "SellButton", 0, xPos, yPos, buttonWidth, buttonHeight))
Print("创建卖出按钮失败!");
SellButton.Description("卖出");
SellButton.Color(clrWhite);
SellButton.BackColor(clrDarkRed);
SellButton.BorderColor(clrWhite);
SellButton.FontSize(10);
SellButton.State(false);
// 创建暂停加仓按钮
yPos -= (buttonHeight + buttonSpacing);
if(!PauseButton.Create(0, "PauseButton", 0, xPos, yPos, buttonWidth, buttonHeight))
Print("创建暂停按钮失败!");
PauseButton.Description("暂停加仓");
PauseButton.Color(clrWhite);
PauseButton.BackColor(clrDarkOrange);
PauseButton.BorderColor(clrWhite);
PauseButton.FontSize(10);
PauseButton.State(false);
// 创建清仓按钮
yPos -= (buttonHeight + buttonSpacing);
if(!CloseAllButton.Create(0, "CloseAllButton", 0, xPos, yPos, buttonWidth, buttonHeight))
Print("创建清仓按钮失败!");
CloseAllButton.Description("清仓");
CloseAllButton.Color(clrWhite);
CloseAllButton.BackColor(clrDarkSlateGray);
CloseAllButton.BorderColor(clrWhite);
CloseAllButton.FontSize(10);
CloseAllButton.State(false);
ChartRedraw();
}
//+------------------------------------------------------------------+
//| 重置每日统计数据 |
//+------------------------------------------------------------------+
void ResetDailyStats()
{
lastDailyCheck = iTime(_Symbol, PERIOD_D1, 0);
dailyProfitLoss = 0;
dailyTradeCount = 0;
if(EnableDebug) Print("每日统计数据已重置");
}
//+------------------------------------------------------------------+
//| 专家逆初始化函数 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
// 释放指标句柄
IndicatorRelease(macdHandle);
IndicatorRelease(atrHandle);
IndicatorRelease(trendMAHandle);
if(UseHigherTFConfirmation) IndicatorRelease(higherTFMAHandle);
// 删除按钮
BuyButton.Delete();
SellButton.Delete();
PauseButton.Delete();
CloseAllButton.Delete();
if(EnableDebug) Print("EA已卸载");
}
//+------------------------------------------------------------------+
//| 获取MACD指标值 |
//+------------------------------------------------------------------+
bool GetMACDValues()
{
int barsNeeded = MaxPeakCount + 5;
ArrayResize(macdMain, barsNeeded);
ArrayResize(macdSignal, barsNeeded);
ArrayResize(macdHistogram, barsNeeded);
ArrayResize(atrValues, barsNeeded);
ArrayResize(rates, barsNeeded);
// 获取价格数据
if(CopyRates(_Symbol, TimeFrame, 0, barsNeeded, rates) < barsNeeded)
{
if(EnableDebug) Print("获取价格数据失败! 需要: ", barsNeeded, " 实际: ", ArraySize(rates));
return false;
}
// 获取MACD值
if(CopyBuffer(macdHandle, 0, 0, barsNeeded, macdMain) < barsNeeded ||
CopyBuffer(macdHandle, 1, 0, barsNeeded, macdSignal) < barsNeeded)
{
if(EnableDebug) Print("获取MACD数据失败!");
return false;
}
// 计算柱状图值
for(int i = 0; i < barsNeeded; i++)
{
macdHistogram = macdMain - macdSignal;
}
// 获取ATR值
if(CopyBuffer(atrHandle, 0, 0, barsNeeded, atrValues) < barsNeeded)
{
if(EnableDebug) Print("获取ATR数据失败!");
}
return true;
}
//+------------------------------------------------------------------+
//| 根据风险计算手数 |
//+------------------------------------------------------------------+
double CalculateLotSize(double stopLossPoints)
{
if(RiskPercent <= 0) return FixedLotSize;
double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
if(tickValue == 0 || tickSize == 0) return FixedLotSize;
double riskAmount = MathMin(AccountInfoDouble(ACCOUNT_EQUITY) * RiskPercent / 100,
AccountInfoDouble(ACCOUNT_EQUITY) * MaxRiskPerTrade);
double pointValue = tickValue / (SymbolInfoDouble(_Symbol, SYMBOL_POINT) / tickSize);
double lotSize = riskAmount / (stopLossPoints * pointValue);
// 调整最小和最大手数限制
double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
double step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
lotSize = MathMax(minLot, MathMin(maxLot, lotSize));
lotSize = NormalizeDouble(lotSize / step, 0) * step;
return lotSize;
}
//+------------------------------------------------------------------+
//| 检查当前是否在交易时间内 |
//+------------------------------------------------------------------+
bool IsTradingTime()
{
if(!UseTimeFilter) return true;
datetime start = StringToTime(TradeStartTime);
datetime end = StringToTime(TradeEndTime);
datetime now = TimeCurrent();
if(start == 0 || end == 0)
{
if(EnableDebug) Print("时间格式错误! 请使用HH:MM格式");
return false;
}
// 处理隔夜交易时段
if(start < end) return (now >= start && now <= end);
else return (now >= start || now <= end);
}
//+------------------------------------------------------------------+
//| 检查更高时间框架趋势 |
//+------------------------------------------------------------------+
bool IsHigherTFTrendBullish()
{
if(!UseHigherTFConfirmation) return true;
double ma;
if(CopyBuffer(higherTFMAHandle, 0, 0, 1, ma) < 1)
{
if(EnableDebug) Print("获取更高时间框架数据失败!");
return true;
}
double close = iClose(_Symbol, HigherTF, 0);
return close > ma;
}
//+------------------------------------------------------------------+
//| 检查是否超过每日限制 |
//+------------------------------------------------------------------+
bool ExceededDailyLimits()
{
datetime currentDay = iTime(_Symbol, PERIOD_D1, 0);
if(currentDay != lastDailyCheck)
{
// 新的一天,重置计数器
ResetDailyStats();
return false;
}
if(dailyProfitLoss < 0 && MathAbs(dailyProfitLoss) >= AccountInfoDouble(ACCOUNT_EQUITY) * MaxDailyLossPercent / 100)
{
if(EnableDebug) Print("已达到每日亏损限额。今日停止交易。");
return true;
}
if(dailyTradeCount >= MaxTradesPerDay)
{
if(EnableDebug) Print("已达到每日最大交易次数。今日停止交易。");
return true;
}
return false;
}
//+------------------------------------------------------------------+
//| 检查背离形态 |
//+------------------------------------------------------------------+
void CheckDivergence()
{
// 重置标志
lookingForLong = false;
lookingForShort = false;
// 如果交易暂停,则跳过信号检查
if(TradingPaused)
{
if(EnableDebug) Print("交易已暂停,跳过信号检查");
return;
}
// 检查做多条件(MACD低于零轴)
if(macdHistogram < 0)
{
// 寻找最近的2-3个峰值
int valleysFound = 0;
double valleyValues = {0};
double valleyPrices = {0};
int valleyIndexes = {0};
// 从最近的bar开始向前搜索
for(int i = 3; i < ArraySize(macdHistogram) - 1; i++)
{
// 检查是否为谷底 (MACD柱状图低点)
if(macdHistogram < macdHistogram && macdHistogram < macdHistogram)
{
valleyValues = macdHistogram;
valleyPrices = rates.low;
valleyIndexes = i;
valleysFound++;
if(valleysFound >= 3) break;
}
}
// 检查是否有足够的谷底
if(valleysFound >= 2)
{
// 检查价格是否创新低而MACD谷底升高
if(valleyPrices < valleyPrices && valleyValues > valleyValues)
{
lookingForLong = true;
lBarLow = rates.low;
firstPeakValue = valleyValues;
firstPeakPrice = valleyPrices;
secondPeakValue = valleyValues;
secondPeakPrice = valleyPrices;
if(EnableDebug)
{
Print("检测到看涨背离:");
Print("价格1: ", valleyPrices, " MACD1: ", valleyValues);
Print("价格2: ", valleyPrices, " MACD2: ", valleyValues);
}
}
}
}
// 检查做空条件(MACD高于零轴)
if(macdHistogram > 0)
{
// 寻找最近的2-3个峰值
int peaksFound = 0;
double peakValues = {0};
double peakPrices = {0};
int peakIndexes = {0};
// 从最近的bar开始向前搜索
for(int i = 3; i < ArraySize(macdHistogram) - 1; i++)
{
// 检查是否为峰值 (MACD柱状图高点)
if(macdHistogram > macdHistogram && macdHistogram > macdHistogram)
{
peakValues = macdHistogram;
peakPrices = rates.high;
peakIndexes = i;
peaksFound++;
if(peaksFound >= 3) break;
}
}
// 检查是否有足够的峰值
if(peaksFound >= 2)
{
// 检查价格是否创新高而MACD峰值降低
if(peakPrices > peakPrices && peakValues < peakValues)
{
lookingForShort = true;
hBarHigh = rates.high;
firstPeakValue = peakValues;
firstPeakPrice = peakPrices;
secondPeakValue = peakValues;
secondPeakPrice = peakPrices;
if(EnableDebug)
{
Print("检测到看跌背离:");
Print("价格1: ", peakPrices, " MACD1: ", peakValues);
Print("价格2: ", peakPrices, " MACD2: ", peakValues);
}
}
}
}
}
//+------------------------------------------------------------------+
//| 专家订单处理函数 |
//+------------------------------------------------------------------+
void OnTrade()
{
UpdateDailyProfit();
}
//+------------------------------------------------------------------+
//| 专家报价处理函数 |
//+------------------------------------------------------------------+
void OnTick()
{
static datetime lastBarTime = 0;
datetime currentBarTime = iTime(_Symbol, TimeFrame, 0);
// 只在新的K线开始时处理
if(currentBarTime == lastBarTime)
return;
lastBarTime = currentBarTime;
if(EnableDebug) Print("新K线开始: ", TimeToString(currentBarTime));
// 检查每日限制
if(ExceededDailyLimits())
{
if(EnableDebug) Print("超过每日限制,跳过交易");
return;
}
// 检查交易时间
if(!IsTradingTime())
{
if(EnableDebug) Print("不在交易时间内,跳过交易");
return;
}
// 获取指标值
if(!GetMACDValues())
{
if(EnableDebug) Print("获取指标数据失败,跳过处理");
return;
}
// 检查背离形态
CheckDivergence();
// 检查入场条件
if((lookingForLong || lookingForShort) && currentBarTime != lastTradeTime)
{
if(EnableDebug) Print("检测到交易信号 - 长: ", lookingForLong, " 短: ", lookingForShort);
// 附加过滤器
if(UseTrendFilter)
{
double maValue;
if(CopyBuffer(trendMAHandle, 0, 0, 1, maValue) < 1)
{
if(EnableDebug) Print("获取趋势MA数据失败!");
return;
}
double currentClose = rates.close;
if(lookingForLong && currentClose < maValue)
{
if(EnableDebug) Print("长信号被趋势过滤器拒绝");
lookingForLong = false;
}
if(lookingForShort && currentClose > maValue)
{
if(EnableDebug) Print("短信号被趋势过滤器拒绝");
lookingForShort = false;
}
}
if(UseHigherTFConfirmation)
{
if(lookingForLong && !IsHigherTFTrendBullish())
{
if(EnableDebug) Print("长信号被更高时间框架过滤器拒绝");
lookingForLong = false;
}
if(lookingForShort && IsHigherTFTrendBullish())
{
if(EnableDebug) Print("短信号被更高时间框架过滤器拒绝");
lookingForShort = false;
}
}
// 如果通过过滤器则下单
if(lookingForLong)
{
PlaceLongOrder();
if(EnableDebug) Print("执行长订单");
}
if(lookingForShort)
{
PlaceShortOrder();
if(EnableDebug) Print("执行短订单");
}
}
// 移动止损管理
if(UseTrailingStop) ManageTrailingStop();
// 部分获利平仓
if(UsePartialProfit) CheckPartialProfit();
// 出现相反信号时平仓
if(CloseOnOppositeSignal) CheckOppositeSignal();
}
//+------------------------------------------------------------------+
//| 下多单 |
//+------------------------------------------------------------------+
void PlaceLongOrder()
{
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double atrValue = atrValues * ATRMultiplier;
double stopLoss = NormalizeDouble(lBarLow - atrValue, _Digits);
double stopLossPoints = (ask - stopLoss) / _Point;
// 验证止损
if(stopLossPoints < MinStopLossPoints)
{
stopLoss = NormalizeDouble(ask - MinStopLossPoints * _Point, _Digits);
stopLossPoints = MinStopLossPoints;
}
if(stopLossPoints > MaxStopLossPoints)
{
stopLoss = NormalizeDouble(ask - MaxStopLossPoints * _Point, _Digits);
stopLossPoints = MaxStopLossPoints;
}
double takeProfit = NormalizeDouble(ask + (ask - stopLoss) * RiskRewardRatio, _Digits);
double lotSize = CalculateLotSize(stopLossPoints);
if(EnableDebug)
{
Print("尝试买入: ", lotSize, " 手");
Print("进场价: ", ask, " 止损: ", stopLoss, " 止盈: ", takeProfit);
}
if(UsePartialProfit)
{
// 分成两个不同止盈的订单
double firstLot = NormalizeDouble(lotSize * PartialProfitRatio, 2);
double secondLot = NormalizeDouble(lotSize - firstLot, 2);
if(firstLot > 0)
{
double firstTP = NormalizeDouble(ask + (ask - stopLoss), _Digits);
if(Trade.Buy(firstLot, _Symbol, ask, stopLoss, firstTP, "MACD背离做多1"))
{
lastTradeTime = iTime(_Symbol, TimeFrame, 0);
dailyTradeCount++;
if(EnableDebug) Print("买入订单1成功,手数: ", firstLot);
}
else
{
Print("买入订单1失败: ", Trade.ResultRetcodeDescription());
}
}
if(secondLot > 0)
{
double secondTP = NormalizeDouble(ask + (ask - stopLoss) * SecondTPMultiplier, _Digits);
if(Trade.Buy(secondLot, _Symbol, ask, stopLoss, secondTP, "MACD背离做多2"))
{
lastTradeTime = iTime(_Symbol, TimeFrame, 0);
dailyTradeCount++;
if(EnableDebug) Print("买入订单2成功,手数: ", secondLot);
}
else
{
Print("买入订单2失败: ", Trade.ResultRetcodeDescription());
}
}
}
else
{
if(Trade.Buy(lotSize, _Symbol, ask, stopLoss, takeProfit, "MACD背离做多"))
{
lastTradeTime = iTime(_Symbol, TimeFrame, 0);
dailyTradeCount++;
if(EnableDebug) Print("买入订单成功,手数: ", lotSize);
}
else
{
Print("买入订单失败: ", Trade.ResultRetcodeDescription());
}
}
lookingForLong = false;
}
//+------------------------------------------------------------------+
//| 下空单 |
//+------------------------------------------------------------------+
void PlaceShortOrder()
{
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double atrValue = atrValues * ATRMultiplier;
double stopLoss = NormalizeDouble(hBarHigh + atrValue, _Digits);
double stopLossPoints = (stopLoss - bid) / _Point;
// 验证止损
if(stopLossPoints < MinStopLossPoints)
{
stopLoss = NormalizeDouble(bid + MinStopLossPoints * _Point, _Digits);
stopLossPoints = MinStopLossPoints;
}
if(stopLossPoints > MaxStopLossPoints)
{
stopLoss = NormalizeDouble(bid + MaxStopLossPoints * _Point, _Digits);
stopLossPoints = MaxStopLossPoints;
}
double takeProfit = NormalizeDouble(bid - (stopLoss - bid) * RiskRewardRatio, _Digits);
double lotSize = CalculateLotSize(stopLossPoints);
if(EnableDebug)
{
Print("尝试卖出: ", lotSize, " 手");
Print("进场价: ", bid, " 止损: ", stopLoss, " 止盈: ", takeProfit);
}
if(UsePartialProfit)
{
// 分成两个不同止盈的订单
double firstLot = NormalizeDouble(lotSize * PartialProfitRatio, 2);
double secondLot = NormalizeDouble(lotSize - firstLot, 2);
if(firstLot > 0)
{
double firstTP = NormalizeDouble(bid - (stopLoss - bid), _Digits);
if(Trade.Sell(firstLot, _Symbol, bid, stopLoss, firstTP, "MACD背离做空1"))
{
lastTradeTime = iTime(_Symbol, TimeFrame, 0);
dailyTradeCount++;
if(EnableDebug) Print("卖出订单1成功,手数: ", firstLot);
}
else
{
Print("卖出订单1失败: ", Trade.ResultRetcodeDescription());
}
}
if(secondLot > 0)
{
double secondTP = NormalizeDouble(bid - (stopLoss - bid) * SecondTPMultiplier, _Digits);
if(Trade.Sell(secondLot, _Symbol, bid, stopLoss, secondTP, "MACD背离做空2"))
{
lastTradeTime = iTime(_Symbol, TimeFrame, 0);
dailyTradeCount++;
if(EnableDebug) Print("卖出订单2成功,手数: ", secondLot);
}
else
{
Print("卖出订单2失败: ", Trade.ResultRetcodeDescription());
}
}
}
else
{
if(Trade.Sell(lotSize, _Symbol, bid, stopLoss, takeProfit, "MACD背离做空"))
{
lastTradeTime = iTime(_Symbol, TimeFrame, 0);
dailyTradeCount++;
if(EnableDebug) Print("卖出订单成功,手数: ", lotSize);
}
else
{
Print("卖出订单失败: ", Trade.ResultRetcodeDescription());
}
}
lookingForShort = false;
}
//+------------------------------------------------------------------+
//| 管理移动止损 |
//+------------------------------------------------------------------+
void ManageTrailingStop()
{
for(int i = PositionsTotal()-1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(PositionSelectByTicket(ticket))
{
if(PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetString(POSITION_SYMBOL) == _Symbol)
{
double currentStop = PositionGetDouble(POSITION_SL);
double currentProfit = PositionGetDouble(POSITION_PROFIT);
double newStop = 0;
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
{
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
newStop = NormalizeDouble(bid - TrailingStopPoints * _Point, _Digits);
// 确保新的止损高于当前止损且不会造成亏损
if((newStop > currentStop) && (newStop > PositionGetDouble(POSITION_PRICE_OPEN)))
{
if(!Trade.PositionModify(ticket, newStop, PositionGetDouble(POSITION_TP)))
{
if(EnableDebug) Print("移动止损修改失败: ", Trade.ResultRetcodeDescription());
}
else if(EnableDebug) Print("多头止损更新为: ", newStop);
}
}
else if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
{
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
newStop = NormalizeDouble(ask + TrailingStopPoints * _Point, _Digits);
// 确保新的止损低于当前止损且不会造成亏损
if((newStop < currentStop) && (newStop < PositionGetDouble(POSITION_PRICE_OPEN)))
{
if(!Trade.PositionModify(ticket, newStop, PositionGetDouble(POSITION_TP)))
{
if(EnableDebug) Print("移动止损修改失败: ", Trade.ResultRetcodeDescription());
}
else if(EnableDebug) Print("空头止损更新为: ", newStop);
}
}
}
}
}
}
//+------------------------------------------------------------------+
//| 检查部分获利平仓 |
//+------------------------------------------------------------------+
void CheckPartialProfit()
{
for(int i = PositionsTotal()-1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(PositionSelectByTicket(ticket))
{
if(PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetString(POSITION_SYMBOL) == _Symbol)
{
double profitInPips = 0;
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
double stopLoss = PositionGetDouble(POSITION_SL);
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
{
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
profitInPips = (bid - openPrice) / _Point;
}
else if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
{
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
profitInPips = (openPrice - ask) / _Point;
}
double stopLossPips = MathAbs(openPrice - stopLoss) / _Point;
// 检查是否达到第一个目标(1:1)
if(profitInPips >= stopLossPips && StringFind(PositionGetString(POSITION_COMMENT), " 1") < 0)
{
double volume = PositionGetDouble(POSITION_VOLUME);
double volumeToClose = NormalizeDouble(volume * PartialProfitRatio, 2);
if(volumeToClose > 0)
{
if(!Trade.PositionClosePartial(ticket, volumeToClose))
{
if(EnableDebug) Print("部分平仓失败: ", Trade.ResultRetcodeDescription());
}
else
{
if(EnableDebug) Print("部分平仓成功: ", volumeToClose, " 手");
}
}
}
}
}
}
}
//+------------------------------------------------------------------+
//| 检查相反信号并平仓 |
//+------------------------------------------------------------------+
void CheckOppositeSignal()
{
for(int i = PositionsTotal()-1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(PositionSelectByTicket(ticket))
{
if(PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetString(POSITION_SYMBOL) == _Symbol)
{
bool shouldClose = false;
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY && lookingForShort)
shouldClose = true;
else if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL && lookingForLong)
shouldClose = true;
if(shouldClose)
{
if(!Trade.PositionClose(ticket))
{
if(EnableDebug) Print("平仓失败: ", Trade.ResultRetcodeDescription());
}
else
{
if(EnableDebug) Print("因相反信号平仓: ", ticket);
}
}
}
}
}
}
//+------------------------------------------------------------------+
//| 关闭所有仓位 |
//+------------------------------------------------------------------+
void CloseAllPositions()
{
int count = 0;
for(int i = PositionsTotal()-1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(PositionSelectByTicket(ticket))
{
if(PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetString(POSITION_SYMBOL) == _Symbol)
{
if(Trade.PositionClose(ticket))
{
count++;
if(EnableDebug) Print("平仓成功: ", ticket);
}
else
{
if(EnableDebug) Print("平仓失败: ", Trade.ResultRetcodeDescription());
}
}
}
}
if(EnableDebug) Print("已平仓所有头寸,数量: ", count);
}
//+------------------------------------------------------------------+
//| 更新每日盈亏统计 |
//+------------------------------------------------------------------+
void UpdateDailyProfit()
{
datetime currentDay = iTime(_Symbol, PERIOD_D1, 0);
if(currentDay != lastDailyCheck)
{
// 新的一天,重置计数器
ResetDailyStats();
}
// 计算当日盈亏
dailyProfitLoss = 0;
for(int i = PositionsTotal()-1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(PositionSelectByTicket(ticket))
{
if(PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetString(POSITION_SYMBOL) == _Symbol)
{
MqlDateTime posTime, currTime;
TimeToStruct((datetime)PositionGetInteger(POSITION_TIME), posTime);
TimeToStruct(currentDay, currTime);
if(posTime.day == currTime.day && posTime.mon == currTime.mon && posTime.year == currTime.year)
{
dailyProfitLoss += PositionGetDouble(POSITION_PROFIT);
}
}
}
}
// 添加当日已平仓的盈亏
HistorySelect(lastDailyCheck, TimeCurrent());
for(int i = HistoryDealsTotal()-1; i >= 0; i--)
{
ulong dealTicket = HistoryDealGetTicket(i);
if(HistoryDealGetInteger(dealTicket, DEAL_MAGIC) == MagicNumber &&
HistoryDealGetString(dealTicket, DEAL_SYMBOL) == _Symbol)
{
MqlDateTime dealTime, currTime;
TimeToStruct((datetime)HistoryDealGetInteger(dealTicket, DEAL_TIME), dealTime);
TimeToStruct(currentDay, currTime);
if(dealTime.day == currTime.day && dealTime.mon == currTime.mon && dealTime.year == currTime.year)
{
dailyProfitLoss += HistoryDealGetDouble(dealTicket, DEAL_PROFIT);
}
}
}
}
//+------------------------------------------------------------------+
//| 图表事件处理函数 |
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
// 处理按钮点击事件
if(id == CHARTEVENT_OBJECT_CLICK)
{
if(sparam == "BuyButton")
{
// 手动买入
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double atrValue = atrValues * ATRMultiplier;
double stopLoss = NormalizeDouble(ask - atrValue, _Digits);
double takeProfit = NormalizeDouble(ask + (ask - stopLoss) * RiskRewardRatio, _Digits);
double lotSize = CalculateLotSize((ask - stopLoss) / _Point);
if(Trade.Buy(lotSize, _Symbol, ask, stopLoss, takeProfit, "手动买入"))
{
lastTradeTime = iTime(_Symbol, TimeFrame, 0);
dailyTradeCount++;
if(EnableDebug) Print("手动买入成功: ", lotSize, " 手");
}
}
else if(sparam == "SellButton")
{
// 手动卖出
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double atrValue = atrValues * ATRMultiplier;
double stopLoss = NormalizeDouble(bid + atrValue, _Digits);
double takeProfit = NormalizeDouble(bid - (stopLoss - bid) * RiskRewardRatio, _Digits);
double lotSize = CalculateLotSize((stopLoss - bid) / _Point);
if(Trade.Sell(lotSize, _Symbol, bid, stopLoss, takeProfit, "手动卖出"))
{
lastTradeTime = iTime(_Symbol, TimeFrame, 0);
dailyTradeCount++;
if(EnableDebug) Print("手动卖出成功: ", lotSize, " 手");
}
}
else if(sparam == "PauseButton")
{
// 暂停/恢复加仓
TradingPaused = !TradingPaused;
if(TradingPaused)
{
PauseButton.Description("恢复加仓");
PauseButton.BackColor(clrDarkSlateBlue);
if(EnableDebug) Print("EA已暂停自动加仓");
}
else
{
PauseButton.Description("暂停加仓");
PauseButton.BackColor(clrDarkOrange);
if(EnableDebug) Print("EA已恢复自动加仓");
}
ChartRedraw();
}
else if(sparam == "CloseAllButton")
{
// 清仓
CloseAllPositions();
if(EnableDebug) Print("已平仓所有头寸");
}
}
// 处理图表大小变化事件,调整按钮位置
if(id == CHARTEVENT_CHART_CHANGE)
{
long chartWidth = (long)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS);
long chartHeight = (long)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS);
int buttonWidth = 80;
int buttonHeight = 30;
int buttonSpacing = 10;
int xPos = (int)(chartWidth - buttonWidth - buttonSpacing);
int yPos = (int)(chartHeight - buttonHeight - buttonSpacing);
BuyButton.X_Distance(xPos);
BuyButton.Y_Distance(yPos);
yPos -= (buttonHeight + buttonSpacing);
SellButton.X_Distance(xPos);
SellButton.Y_Distance(yPos);
yPos -= (buttonHeight + buttonSpacing);
PauseButton.X_Distance(xPos);
PauseButton.Y_Distance(yPos);
yPos -= (buttonHeight + buttonSpacing);
CloseAllButton.X_Distance(xPos);
CloseAllButton.Y_Distance(yPos);
ChartRedraw();
}
} //+------------------------------------------------------------------+
//| Enhanced_MACD_Divergence_EA|
//| Copyright 2023, MetaQuotes Software Corp. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Software Corp."
#property link "https://www.mql5.com"
#property version "3.23"
#property strict
#property description "增强版MACD背离交易系统(带ATR止损)"
#property description "做多条件: MACD柱状图低于零轴且峰值上升 + 价格低点下降"
#property description "做空条件: MACD柱状图高于零轴且峰值下降 + 价格高点上升"
#property description "包含多重过滤条件和风险管理选项"
#include <Trade\Trade.mqh>
#include <ChartObjects\ChartObjectsTxtControls.mqh>
//+------------------------------------------------------------------+
//| 输入参数 |
//+------------------------------------------------------------------+
//--- MACD核心参数
input int FastEMA = 12; // MACD快线周期
input int SlowEMA = 26; // MACD慢线周期
input int SignalPeriod = 9; // MACD信号线周期
input ENUM_TIMEFRAMES TimeFrame = PERIOD_H1; // 交易时间框架
input ulong MagicNumber = 12345; // EA魔术码
//--- 风险管理
input double RiskPercent = 1.0; // 每笔交易风险百分比
input double FixedLotSize = 0.1; // 固定交易手数(当RiskPercent=0时使用)
input double RiskRewardRatio = 2.0; // 风险回报比
input int ATRPeriod = 14; // ATR止损周期
input double ATRMultiplier = 1.5; // ATR止损倍数(从1.8降低到1.5)
input bool UseTrailingStop = true; // 启用移动止损
input int TrailingStopPoints = 100; // 移动止损点数
input int MinStopLossPoints = 30; // 最小止损点数(从50降低到30)
input int MaxStopLossPoints = 300; // 最大止损点数
//--- 交易过滤器
input bool UseTrendFilter = false; // 启用趋势MA过滤(默认禁用)
input int TrendMAPeriod = 200; // 趋势MA周期
input ENUM_MA_METHOD TrendMAMethod = MODE_EMA; // 趋势MA计算方法
input bool UseHigherTFConfirmation = false; // 启用更高时间框架确认(默认禁用)
input ENUM_TIMEFRAMES HigherTF = PERIOD_D1; // 更高时间框架
input bool UseTimeFilter = false; // 启用交易时间过滤
input string TradeStartTime = "00:00"; // 交易开始时间(服务器时间)
input string TradeEndTime = "23:59"; // 交易结束时间(服务器时间)
input int MinPeakCount = 2; // 背离所需最小峰值数量
input int MaxPeakCount = 3; // 考虑的最大峰值数量
//--- 出场策略
input bool UsePartialProfit = true; // 启用部分平仓获利
input double PartialProfitRatio = 0.5; // 1:1时平仓比例
input double SecondTPMultiplier = 3.0; // 剩余仓位的目标倍数
input bool CloseOnOppositeSignal = true; // 出现相反信号时平仓
//--- 资金管理
input double MaxRiskPerTrade = 0.05; // 每笔交易最大风险(0.05=5%)
input double MaxDailyLossPercent = 5.0;// 每日最大亏损百分比(从2%提高到5%)
input int MaxTradesPerDay = 5; // 每日最大交易次数(从3提高到5)
//--- 调试选项
input bool EnableDebug = true; // 启用调试输出
input bool VisualMode = true; // 在图表上显示标记
//+------------------------------------------------------------------+
//| 全局变量 |
//+------------------------------------------------------------------+
double macdMain[], macdSignal[], macdHistogram[], atrValues[];
double trendMAValues[], higherTFMAValues[];
MqlRates rates[];
datetime lastTradeTime, lastDailyCheck;
int peakCount = 0;
double lastPeakValue = 0;
double firstPeakValue = 0;
double firstPeakPrice = 0;
double secondPeakValue = 0;
double secondPeakPrice = 0;
bool lookingForLong = false;
bool lookingForShort = false;
double lBarLow = 0;
double hBarHigh = 0;
double dailyProfitLoss = 0;
int dailyTradeCount = 0;
int macdHandle = INVALID_HANDLE;
int atrHandle = INVALID_HANDLE;
int trendMAHandle = INVALID_HANDLE;
int higherTFMAHandle = INVALID_HANDLE;
CTrade Trade;
bool TradingPaused = false;
bool SignalAlerted = false;
// 控制按钮
CChartObjectButton BuyButton;
CChartObjectButton SellButton;
CChartObjectButton PauseButton;
CChartObjectButton CloseAllButton;
//+------------------------------------------------------------------+
//| 创建控制按钮函数 |
//+------------------------------------------------------------------+
void CreateButtons()
{
// 获取图表宽度和高度
long chartWidth = (long)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS);
long chartHeight = (long)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS);
// 按钮尺寸
int buttonWidth = 80;
int buttonHeight = 30;
int buttonSpacing = 10;
// 计算按钮位置(右下角)
int xPos = (int)(chartWidth - buttonWidth - buttonSpacing);
int yPos = (int)(chartHeight - buttonHeight - buttonSpacing);
// 创建买入按钮
if(!BuyButton.Create(0, "BuyButton", 0, xPos, yPos, buttonWidth, buttonHeight))
Print("创建买入按钮失败!");
BuyButton.Description("买入");
BuyButton.Color(clrWhite);
BuyButton.BackColor(clrDarkGreen);
BuyButton.BorderColor(clrWhite);
BuyButton.FontSize(10);
BuyButton.State(false);
// 创建卖出按钮
yPos -= (buttonHeight + buttonSpacing);
if(!SellButton.Create(0, "SellButton", 0, xPos, yPos, buttonWidth, buttonHeight))
Print("创建卖出按钮失败!");
SellButton.Description("卖出");
SellButton.Color(clrWhite);
SellButton.BackColor(clrDarkRed);
SellButton.BorderColor(clrWhite);
SellButton.FontSize(10);
SellButton.State(false);
// 创建暂停加仓按钮
yPos -= (buttonHeight + buttonSpacing);
if(!PauseButton.Create(0, "PauseButton", 0, xPos, yPos, buttonWidth, buttonHeight))
Print("创建暂停按钮失败!");
PauseButton.Description("暂停加仓");
PauseButton.Color(clrWhite);
PauseButton.BackColor(clrDarkOrange);
PauseButton.BorderColor(clrWhite);
PauseButton.FontSize(10);
PauseButton.State(false);
// 创建清仓按钮
yPos -= (buttonHeight + buttonSpacing);
if(!CloseAllButton.Create(0, "CloseAllButton", 0, xPos, yPos, buttonWidth, buttonHeight))
Print("创建清仓按钮失败!");
CloseAllButton.Description("清仓");
CloseAllButton.Color(clrWhite);
CloseAllButton.BackColor(clrDarkSlateGray);
CloseAllButton.BorderColor(clrWhite);
CloseAllButton.FontSize(10);
CloseAllButton.State(false);
ChartRedraw();
}
//+------------------------------------------------------------------+
//| 重置每日统计数据 |
//+------------------------------------------------------------------+
void ResetDailyStats()
{
MqlDateTime today;
TimeCurrent(today);
today.hour = 0;
today.min = 0;
today.sec = 0;
lastDailyCheck = StructToTime(today);
dailyProfitLoss = 0;
dailyTradeCount = 0;
if(EnableDebug) Print("每日统计数据已重置");
}
//+------------------------------------------------------------------+
//| 专家初始化函数 |
//+------------------------------------------------------------------+
int OnInit()
{
// 初始化指标句柄
macdHandle = iMACD(_Symbol, TimeFrame, FastEMA, SlowEMA, SignalPeriod, PRICE_CLOSE);
atrHandle = iATR(_Symbol, TimeFrame, ATRPeriod);
if(UseTrendFilter)
trendMAHandle = iMA(_Symbol, TimeFrame, TrendMAPeriod, 0, TrendMAMethod, PRICE_CLOSE);
if(UseHigherTFConfirmation)
higherTFMAHandle = iMA(_Symbol, HigherTF, TrendMAPeriod, 0, TrendMAMethod, PRICE_CLOSE);
// 检查参数设置是否有效
if(RiskPercent <= 0 && FixedLotSize <= 0)
{
Alert("风险管理参数配置不正确!");
return(INIT_PARAMETERS_INCORRECT);
}
// 重置每日统计
ResetDailyStats();
// 设置交易对象的魔术码
Trade.SetExpertMagicNumber(MagicNumber);
// 创建控制按钮
CreateButtons();
if(EnableDebug) Print("EA初始化完成,开始运行...");
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| 专家逆初始化函数 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
// 释放指标句柄
if(macdHandle != INVALID_HANDLE) IndicatorRelease(macdHandle);
if(atrHandle != INVALID_HANDLE) IndicatorRelease(atrHandle);
if(trendMAHandle != INVALID_HANDLE) IndicatorRelease(trendMAHandle);
if(UseHigherTFConfirmation && higherTFMAHandle != INVALID_HANDLE) IndicatorRelease(higherTFMAHandle);
// 删除按钮
BuyButton.Delete();
SellButton.Delete();
PauseButton.Delete();
CloseAllButton.Delete();
// 删除所有图形对象
ObjectsDeleteAll(0, "DIVERGENCE_");
if(EnableDebug) Print("EA已卸载");
}
//+------------------------------------------------------------------+
//| 获取指标数据 |
//+------------------------------------------------------------------+
bool GetIndicatorValues()
{
int barsNeeded = 100; // 确保足够的历史数据
// 获取价格数据
if(CopyRates(_Symbol, TimeFrame, 0, barsNeeded, rates) < barsNeeded)
{
if(EnableDebug) Print("获取价格数据失败! 需要: ", barsNeeded, " 实际: ", ArraySize(rates));
return false;
}
// 获取MACD值
ArrayResize(macdMain, barsNeeded);
ArrayResize(macdSignal, barsNeeded);
ArrayResize(macdHistogram, barsNeeded);
if(CopyBuffer(macdHandle, 0, 0, barsNeeded, macdMain) < barsNeeded)
{
if(EnableDebug) Print("获取MACD主数据失败!");
return false;
}
if(CopyBuffer(macdHandle, 1, 0, barsNeeded, macdSignal) < barsNeeded)
{
if(EnableDebug) Print("获取MACD信号数据失败!");
return false;
}
// 计算柱状图值
for(int i = 0; i < barsNeeded; i++)
{
macdHistogram = macdMain - macdSignal;
}
// 获取ATR值
ArrayResize(atrValues, barsNeeded);
if(CopyBuffer(atrHandle, 0, 0, barsNeeded, atrValues) < barsNeeded)
{
if(EnableDebug) Print("获取ATR数据失败!");
return false;
}
// 获取趋势MA值
if(UseTrendFilter)
{
ArrayResize(trendMAValues, barsNeeded);
if(CopyBuffer(trendMAHandle, 0, 0, barsNeeded, trendMAValues) < barsNeeded)
{
if(EnableDebug) Print("获取趋势MA数据失败!");
return false;
}
}
// 获取更高时间框架MA值
if(UseHigherTFConfirmation)
{
ArrayResize(higherTFMAValues, barsNeeded);
if(CopyBuffer(higherTFMAHandle, 0, 0, barsNeeded, higherTFMAValues) < barsNeeded)
{
if(EnableDebug) Print("获取更高时间框架MA数据失败!");
return false;
}
}
return true;
}
//+------------------------------------------------------------------+
//| 根据风险计算手数 |
//+------------------------------------------------------------------+
double CalculateLotSize(double stopLossPoints)
{
if(RiskPercent <= 0) return FixedLotSize;
double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
double pointValue = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
if(tickValue <= 0 || tickSize <= 0 || pointValue <= 0)
{
if(EnableDebug) Print("无法获取点值信息,使用固定手数");
return FixedLotSize;
}
double riskAmount = MathMin(AccountInfoDouble(ACCOUNT_EQUITY) * RiskPercent / 100,
AccountInfoDouble(ACCOUNT_EQUITY) * MaxRiskPerTrade);
double lotSize = riskAmount / (stopLossPoints * tickValue * pointValue);
// 调整最小和最大手数限制
double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
double step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
lotSize = MathMax(minLot, MathMin(maxLot, lotSize));
lotSize = NormalizeDouble(lotSize, 2);
if(EnableDebug) Print("计算手数: ", lotSize, " SL点数: ", stopLossPoints, " 风险金额: ", riskAmount);
return lotSize;
}
//+------------------------------------------------------------------+
//| 检查当前是否在交易时间内 |
//+------------------------------------------------------------------+
bool IsTradingTime()
{
if(!UseTimeFilter) return true;
datetime start = StringToTime(TradeStartTime);
datetime end = StringToTime(TradeEndTime);
datetime now = TimeCurrent();
if(start == 0 || end == 0)
{
if(EnableDebug) Print("时间格式错误! 请使用HH:MM格式");
return false;
}
// 处理隔夜交易时段
if(start < end) return (now >= start && now <= end);
else return (now >= start || now <= end);
}
//+------------------------------------------------------------------+
//| 检查更高时间框架趋势 |
//+------------------------------------------------------------------+
bool IsHigherTFTrendBullish()
{
if(!UseHigherTFConfirmation) return true;
double close = iClose(_Symbol, HigherTF, 0);
double ma;
if(CopyBuffer(higherTFMAHandle, 0, 0, 1, ma) < 1)
{
if(EnableDebug) Print("获取更高时间框架MA数据失败!");
return true;
}
return close > ma;
}
//+------------------------------------------------------------------+
//| 检查是否超过每日限制 |
//+------------------------------------------------------------------+
bool ExceededDailyLimits()
{
MqlDateTime today, last;
TimeCurrent(today);
TimeToStruct(lastDailyCheck, last);
if(today.day != last.day || today.mon != last.mon || today.year != last.year)
{
// 新的一天,重置计数器
ResetDailyStats();
return false;
}
if(dailyProfitLoss < 0 && MathAbs(dailyProfitLoss) >= AccountInfoDouble(ACCOUNT_EQUITY) * MaxDailyLossPercent / 100)
{
if(EnableDebug) Print("已达到每日亏损限额。今日停止交易。");
return true;
}
if(dailyTradeCount >= MaxTradesPerDay)
{
if(EnableDebug) Print("已达到每日最大交易次数。今日停止交易。");
return true;
}
return false;
}
//+------------------------------------------------------------------+
//| 检查背离形态 |
//+------------------------------------------------------------------+
void CheckDivergence()
{
// 重置标志
lookingForLong = false;
lookingForShort = false;
SignalAlerted = false;
// 如果交易暂停,则跳过信号检查
if(TradingPaused)
{
if(EnableDebug) Print("交易已暂停,跳过信号检查");
return;
}
// 检查做多条件(MACD低于零轴)
if(macdHistogram < 0)
{
// 寻找最近的2-3个谷底
int valleysFound = 0;
double valleyValues = {0};
double valleyPrices = {0};
int valleyIndexes = {0};
// 从最近的bar开始向前搜索(索引0是最新)
for(int i = 1; i < MathMin(50, ArraySize(macdHistogram)-1); i++)
{
// 检查是否为谷底 (MACD柱状图低点)
if(macdHistogram < macdHistogram &&
macdHistogram < macdHistogram)
{
valleyValues = macdHistogram;
valleyPrices = rates.low;
valleyIndexes = i;
valleysFound++;
if(valleysFound >= MaxPeakCount) break;
}
}
// 检查是否有足够的谷底
if(valleysFound >= MinPeakCount)
{
// 检查价格是否创新低而MACD谷底升高
if(valleyPrices < valleyPrices && valleyValues > valleyValues)
{
lookingForLong = true;
lBarLow = valleyPrices; // 使用最新谷底价格
firstPeakValue = valleyValues;
firstPeakPrice = valleyPrices;
secondPeakValue = valleyValues;
secondPeakPrice = valleyPrices;
if(EnableDebug)
{
Print("检测到看涨背离:");
Print("价格1: ", valleyPrices, " MACD1: ", valleyValues, " 索引: ", valleyIndexes);
Print("价格2: ", valleyPrices, " MACD2: ", valleyValues, " 索引: ", valleyIndexes);
}
if(VisualMode)
{
// 在图表上标记背离
string objName = "DIVERGENCE_LONG_" + TimeToString(rates].time);
ObjectCreate(0, objName, OBJ_ARROW_BUY, 0, rates].time, valleyPrices);
ObjectSetInteger(0, objName, OBJPROP_COLOR, clrGreen);
ObjectSetInteger(0, objName, OBJPROP_WIDTH, 2);
objName = "DIVERGENCE_LONG_" + TimeToString(rates].time);
ObjectCreate(0, objName, OBJ_ARROW_BUY, 0, rates].time, valleyPrices);
ObjectSetInteger(0, objName, OBJPROP_COLOR, clrGreen);
ObjectSetInteger(0, objName, OBJPROP_WIDTH, 2);
}
}
}
}
// 检查做空条件(MACD高于零轴)
if(macdHistogram > 0)
{
// 寻找最近的2-3个峰值
int peaksFound = 0;
double peakValues = {0};
double peakPrices = {0};
int peakIndexes = {0};
// 从最近的bar开始向前搜索
for(int i = 1; i < MathMin(50, ArraySize(macdHistogram)-1); i++)
{
// 检查是否为峰值 (MACD柱状图高点)
if(macdHistogram > macdHistogram &&
macdHistogram > macdHistogram)
{
peakValues = macdHistogram;
peakPrices = rates.high;
peakIndexes = i;
peaksFound++;
if(peaksFound >= MaxPeakCount) break;
}
}
// 检查是否有足够的峰值
if(peaksFound >= MinPeakCount)
{
// 检查价格是否创新高而MACD峰值降低
if(peakPrices > peakPrices && peakValues < peakValues)
{
lookingForShort = true;
hBarHigh = peakPrices; // 使用最新峰值价格
firstPeakValue = peakValues;
firstPeakPrice = peakPrices;
secondPeakValue = peakValues;
secondPeakPrice = peakPrices;
if(EnableDebug)
{
Print("检测到看跌背离:");
Print("价格1: ", peakPrices, " MACD1: ", peakValues, " 索引: ", peakIndexes);
Print("价格2: ", peakPrices, " MACD2: ", peakValues, " 索引: ", peakIndexes);
}
if(VisualMode)
{
// 在图表上标记背离
string objName = "DIVERGENCE_SHORT_" + TimeToString(rates].time);
ObjectCreate(0, objName, OBJ_ARROW_SELL, 0, rates].time, peakPrices);
ObjectSetInteger(0, objName, OBJPROP_COLOR, clrRed);
ObjectSetInteger(0, objName, OBJPROP_WIDTH, 2);
objName = "DIVERGENCE_SHORT_" + TimeToString(rates].time);
ObjectCreate(0, objName, OBJ_ARROW_SELL, 0, rates].time, peakPrices);
ObjectSetInteger(0, objName, OBJPROP_COLOR, clrRed);
ObjectSetInteger(0, objName, OBJPROP_WIDTH, 2);
}
}
}
}
}
//+------------------------------------------------------------------+
//| 专家订单处理函数 |
//+------------------------------------------------------------------+
void OnTrade()
{
UpdateDailyProfit();
}
//+------------------------------------------------------------------+
//| 专家报价处理函数 |
//+------------------------------------------------------------------+
void OnTick()
{
static datetime lastBarTime = 0;
datetime currentBarTime = iTime(_Symbol, TimeFrame, 0);
// 只在新的K线开始时处理
if(currentBarTime == lastBarTime)
{
// 即使不是新K线,也检查是否有手动交易信号
if((lookingForLong || lookingForShort) && !SignalAlerted)
{
string signal = lookingForLong ? "买入信号" : "卖出信号";
Print("检测到交易信号 - ", signal, " 等待新K线确认");
SignalAlerted = true;
}
return;
}
lastBarTime = currentBarTime;
if(EnableDebug) Print("新K线开始: ", TimeToString(currentBarTime));
// 检查每日限制
if(ExceededDailyLimits())
{
if(EnableDebug) Print("超过每日限制,跳过交易");
return;
}
// 检查交易时间
if(!IsTradingTime())
{
if(EnableDebug) Print("不在交易时间内,跳过交易");
return;
}
// 获取指标值
if(!GetIndicatorValues())
{
if(EnableDebug) Print("获取指标数据失败,跳过处理");
return;
}
// 检查背离形态
CheckDivergence();
// 检查入场条件
if((lookingForLong || lookingForShort) && currentBarTime != lastTradeTime)
{
string signalType = lookingForLong ? "长" : "短";
if(EnableDebug) Print("检测到交易信号 - 类型: ", signalType);
// 附加过滤器
bool trendFilterPassed = true;
if(UseTrendFilter)
{
double currentClose = rates.close;
double ma;
if(CopyBuffer(trendMAHandle, 0, 0, 1, ma) < 1)
{
if(EnableDebug) Print("获取趋势MA数据失败!");
}
else
{
if(lookingForLong && currentClose < ma)
{
if(EnableDebug) Print("长信号被趋势过滤器拒绝");
trendFilterPassed = false;
}
if(lookingForShort && currentClose > ma)
{
if(EnableDebug) Print("短信号被趋势过滤器拒绝");
trendFilterPassed = false;
}
}
}
bool higherTFFilterPassed = true;
if(UseHigherTFConfirmation)
{
if(lookingForLong && !IsHigherTFTrendBullish())
{
if(EnableDebug) Print("长信号被更高时间框架过滤器拒绝");
higherTFFilterPassed = false;
}
if(lookingForShort && IsHigherTFTrendBullish())
{
if(EnableDebug) Print("短信号被更高时间框架过滤器拒绝");
higherTFFilterPassed = false;
}
}
// 如果通过过滤器则下单
if(lookingForLong && trendFilterPassed && higherTFFilterPassed)
{
PlaceLongOrder();
if(EnableDebug) Print("执行长订单");
}
else if(lookingForShort && trendFilterPassed && higherTFFilterPassed)
{
PlaceShortOrder();
if(EnableDebug) Print("执行短订单");
}
else
{
if(EnableDebug) Print("信号被过滤器阻止");
}
}
else
{
if(EnableDebug) Print("无有效交易信号或已在当前K线交易");
}
// 移动止损管理
if(UseTrailingStop) ManageTrailingStop();
// 部分获利平仓
if(UsePartialProfit) CheckPartialProfit();
// 出现相反信号时平仓
if(CloseOnOppositeSignal) CheckOppositeSignal();
}
//+------------------------------------------------------------------+
//| 下多单 |
//+------------------------------------------------------------------+
void PlaceLongOrder()
{
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double atrValue = atrValues * ATRMultiplier; // 使用最新ATR值
double stopLoss = NormalizeDouble(lBarLow - atrValue, _Digits);
double stopLossPoints = (ask - stopLoss) / _Point;
// 验证止损
if(stopLossPoints < MinStopLossPoints)
{
stopLoss = NormalizeDouble(ask - MinStopLossPoints * _Point, _Digits);
stopLossPoints = MinStopLossPoints;
}
if(stopLossPoints > MaxStopLossPoints)
{
stopLoss = NormalizeDouble(ask - MaxStopLossPoints * _Point, _Digits);
stopLossPoints = MaxStopLossPoints;
}
double takeProfit = NormalizeDouble(ask + (ask - stopLoss) * RiskRewardRatio, _Digits);
double lotSize = CalculateLotSize(stopLossPoints);
if(EnableDebug)
{
Print("尝试买入: ", lotSize, " 手");
Print("进场价: ", ask, " 止损: ", stopLoss, " 止盈: ", takeProfit);
}
if(UsePartialProfit)
{
// 分成两个不同止盈的订单
double firstLot = NormalizeDouble(lotSize * PartialProfitRatio, 2);
double secondLot = NormalizeDouble(lotSize - firstLot, 2);
if(firstLot > 0)
{
double firstTP = NormalizeDouble(ask + (ask - stopLoss), _Digits);
if(Trade.Buy(firstLot, _Symbol, ask, stopLoss, firstTP, "MACD背离做多1"))
{
lastTradeTime = iTime(_Symbol, TimeFrame, 0);
dailyTradeCount++;
if(EnableDebug) Print("买入订单1成功,手数: ", firstLot);
}
else
{
Print("买入订单1失败: ", Trade.ResultRetcodeDescription());
}
}
if(secondLot > 0)
{
double secondTP = NormalizeDouble(ask + (ask - stopLoss) * SecondTPMultiplier, _Digits);
if(Trade.Buy(secondLot, _Symbol, ask, stopLoss, secondTP, "MACD背离做多2"))
{
lastTradeTime = iTime(_Symbol, TimeFrame, 0);
dailyTradeCount++;
if(EnableDebug) Print("买入订单2成功,手数: ", secondLot);
}
else
{
Print("买入订单2失败: ", Trade.ResultRetcodeDescription());
}
}
}
else
{
if(Trade.Buy(lotSize, _Symbol, ask, stopLoss, takeProfit, "MACD背离做多"))
{
lastTradeTime = iTime(_Symbol, TimeFrame, 0);
dailyTradeCount++;
if(EnableDebug) Print("买入订单成功,手数: ", lotSize);
}
else
{
Print("买入订单失败: ", Trade.ResultRetcodeDescription());
}
}
lookingForLong = false;
SignalAlerted = false;
}
//+------------------------------------------------------------------+
//| 下空单 |
//+------------------------------------------------------------------+
void PlaceShortOrder()
{
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double atrValue = atrValues * ATRMultiplier; // 使用最新ATR值
double stopLoss = NormalizeDouble(hBarHigh + atrValue, _Digits);
double stopLossPoints = (stopLoss - bid) / _Point;
// 验证止损
if(stopLossPoints < MinStopLossPoints)
{
stopLoss = NormalizeDouble(bid + MinStopLossPoints * _Point, _Digits);
stopLossPoints = MinStopLossPoints;
}
if(stopLossPoints > MaxStopLossPoints)
{
stopLoss = NormalizeDouble(bid + MaxStopLossPoints * _Point, _Digits);
stopLossPoints = MaxStopLossPoints;
}
double takeProfit = NormalizeDouble(bid - (stopLoss - bid) * RiskRewardRatio, _Digits);
double lotSize = CalculateLotSize(stopLossPoints);
if(EnableDebug)
{
Print("尝试卖出: ", lotSize, " 手");
Print("进场价: ", bid, " 止损: ", stopLoss, " 止盈: ", takeProfit);
}
if(UsePartialProfit)
{
// 分成两个不同止盈的订单
double firstLot = NormalizeDouble(lotSize * PartialProfitRatio, 2);
double secondLot = NormalizeDouble(lotSize - firstLot, 2);
if(firstLot > 0)
{
double firstTP = NormalizeDouble(bid - (stopLoss - bid), _Digits);
if(Trade.Sell(firstLot, _Symbol, bid, stopLoss, firstTP, "MACD背离做空1"))
{
lastTradeTime = iTime(_Symbol, TimeFrame, 0);
dailyTradeCount++;
if(EnableDebug) Print("卖出订单1成功,手数: ", firstLot);
}
else
{
Print("卖出订单1失败: ", Trade.ResultRetcodeDescription());
}
}
if(secondLot > 0)
{
double secondTP = NormalizeDouble(bid - (stopLoss - bid) * SecondTPMultiplier, _Digits);
if(Trade.Sell(secondLot, _Symbol, bid, stopLoss, secondTP, "MACD背离做空2"))
{
lastTradeTime = iTime(_Symbol, TimeFrame, 0);
dailyTradeCount++;
if(EnableDebug) Print("卖出订单2成功,手数: ", secondLot);
}
else
{
Print("卖出订单2失败: ", Trade.ResultRetcodeDescription());
}
}
}
else
{
if(Trade.Sell(lotSize, _Symbol, bid, stopLoss, takeProfit, "MACD背离做空"))
{
lastTradeTime = iTime(_Symbol, TimeFrame, 0);
dailyTradeCount++;
if(EnableDebug) Print("卖出订单成功,手数: ", lotSize);
}
else
{
Print("卖出订单失败: ", Trade.ResultRetcodeDescription());
}
}
lookingForShort = false;
SignalAlerted = false;
}
//+------------------------------------------------------------------+
//| 管理移动止损 |
//+------------------------------------------------------------------+
void ManageTrailingStop()
{
for(int i = PositionsTotal()-1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(PositionSelectByTicket(ticket))
{
if(PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetString(POSITION_SYMBOL) == _Symbol)
{
double currentStop = PositionGetDouble(POSITION_SL);
double newStop = 0;
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
{
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
newStop = NormalizeDouble(bid - TrailingStopPoints * _Point, _Digits);
// 确保新的止损高于当前止损且不会造成亏损
if((newStop > currentStop || currentStop == 0) && (newStop > PositionGetDouble(POSITION_PRICE_OPEN)))
{
if(!Trade.PositionModify(ticket, newStop, PositionGetDouble(POSITION_TP)))
{
if(EnableDebug) Print("移动止损修改失败: ", Trade.ResultRetcodeDescription());
}
else if(EnableDebug) Print("多头止损更新为: ", newStop);
}
}
else if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
{
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
newStop = NormalizeDouble(ask + TrailingStopPoints * _Point, _Digits);
// 确保新的止损低于当前止损且不会造成亏损
if((newStop < currentStop || currentStop == 0) && (newStop < PositionGetDouble(POSITION_PRICE_OPEN)))
{
if(!Trade.PositionModify(ticket, newStop, PositionGetDouble(POSITION_TP)))
{
if(EnableDebug) Print("移动止损修改失败: ", Trade.ResultRetcodeDescription());
}
else if(EnableDebug) Print("空头止损更新为: ", newStop);
}
}
}
}
}
}
//+------------------------------------------------------------------+
//| 检查部分获利平仓 |
//+------------------------------------------------------------------+
void CheckPartialProfit()
{
for(int i = PositionsTotal()-1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(PositionSelectByTicket(ticket))
{
if(PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetString(POSITION_SYMBOL) == _Symbol)
{
double profitInPips = 0;
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
double stopLoss = PositionGetDouble(POSITION_SL);
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
{
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
profitInPips = (bid - openPrice) / _Point;
}
else if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
{
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
profitInPips = (openPrice - ask) / _Point;
}
double stopLossPips = MathAbs(openPrice - stopLoss) / _Point;
// 检查是否达到第一个目标(1:1)
if(profitInPips >= stopLossPips && StringFind(PositionGetString(POSITION_COMMENT), " 1") < 0)
{
double volume = PositionGetDouble(POSITION_VOLUME);
double volumeToClose = NormalizeDouble(volume * PartialProfitRatio, 2);
if(volumeToClose > 0)
{
if(!Trade.PositionClosePartial(ticket, volumeToClose))
{
if(EnableDebug) Print("部分平仓失败: ", Trade.ResultRetcodeDescription());
}
else
{
if(EnableDebug) Print("部分平仓成功: ", volumeToClose, " 手");
}
}
}
}
}
}
}
//+------------------------------------------------------------------+
//| 检查相反信号并平仓 |
//+------------------------------------------------------------------+
void CheckOppositeSignal()
{
for(int i = PositionsTotal()-1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(PositionSelectByTicket(ticket))
{
if(PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetString(POSITION_SYMBOL) == _Symbol)
{
bool shouldClose = false;
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY && lookingForShort)
shouldClose = true;
else if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL && lookingForLong)
shouldClose = true;
if(shouldClose)
{
if(!Trade.PositionClose(ticket))
{
if(EnableDebug) Print("平仓失败: ", Trade.ResultRetcodeDescription());
}
else
{
if(EnableDebug) Print("因相反信号平仓: ", ticket);
}
}
}
}
}
}
//+------------------------------------------------------------------+
//| 关闭所有仓位 |
//+------------------------------------------------------------------+
void CloseAllPositions()
{
int count = 0;
for(int i = PositionsTotal()-1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(PositionSelectByTicket(ticket))
{
if(PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetString(POSITION_SYMBOL) == _Symbol)
{
if(Trade.PositionClose(ticket))
{
count++;
if(EnableDebug) Print("平仓成功: ", ticket);
}
else
{
if(EnableDebug) Print("平仓失败: ", Trade.ResultRetcodeDescription());
}
}
}
}
if(EnableDebug) Print("已平仓所有头寸,数量: ", count);
}
//+------------------------------------------------------------------+
//| 更新每日盈亏统计 |
//+------------------------------------------------------------------+
void UpdateDailyProfit()
{
MqlDateTime today, last;
TimeCurrent(today);
TimeToStruct(lastDailyCheck, last);
if(today.day != last.day || today.mon != last.mon || today.year != last.year)
{
// 新的一天,重置计数器
ResetDailyStats();
return;
}
// 计算当日盈亏
double totalProfit = 0;
// 获取当日所有持仓的盈亏
for(int i = PositionsTotal()-1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(PositionSelectByTicket(ticket))
{
if(PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetString(POSITION_SYMBOL) == _Symbol)
{
datetime positionTime = (datetime)PositionGetInteger(POSITION_TIME);
MqlDateTime positionTimeStruct;
TimeToStruct(positionTime, positionTimeStruct);
if(positionTimeStruct.day == today.day &&
positionTimeStruct.mon == today.mon &&
positionTimeStruct.year == today.year)
{
totalProfit += PositionGetDouble(POSITION_PROFIT);
}
}
}
}
// 获取当日已平仓交易的盈亏
HistorySelect(lastDailyCheck, TimeCurrent());
int dealsTotal = HistoryDealsTotal();
for(int i = 0; i < dealsTotal; i++)
{
ulong dealTicket = HistoryDealGetTicket(i);
if(HistoryDealGetInteger(dealTicket, DEAL_MAGIC) == MagicNumber &&
HistoryDealGetString(dealTicket, DEAL_SYMBOL) == _Symbol)
{
datetime dealTime = (datetime)HistoryDealGetInteger(dealTicket, DEAL_TIME);
MqlDateTime dealTimeStruct;
TimeToStruct(dealTime, dealTimeStruct);
if(dealTimeStruct.day == today.day &&
dealTimeStruct.mon == today.mon &&
dealTimeStruct.year == today.year)
{
totalProfit += HistoryDealGetDouble(dealTicket, DEAL_PROFIT);
}
}
}
dailyProfitLoss = totalProfit;
if(EnableDebug) Print("每日盈亏更新: ", dailyProfitLoss);
}
//+------------------------------------------------------------------+
//| 图表事件处理函数 |
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
// 处理按钮点击事件
if(id == CHARTEVENT_OBJECT_CLICK)
{
if(sparam == "BuyButton")
{
// 手动买入
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double atrArray[];
if(CopyBuffer(atrHandle, 0, 0, 1, atrArray) < 1)
{
Print("获取ATR值失败");
return;
}
double atrValue = atrArray * ATRMultiplier;
double stopLoss = NormalizeDouble(ask - atrValue, _Digits);
double takeProfit = NormalizeDouble(ask + (ask - stopLoss) * RiskRewardRatio, _Digits);
double stopLossPoints = (ask - stopLoss) / _Point;
double lotSize = CalculateLotSize(stopLossPoints);
if(Trade.Buy(lotSize, _Symbol, ask, stopLoss, takeProfit, "手动买入"))
{
lastTradeTime = iTime(_Symbol, TimeFrame, 0);
dailyTradeCount++;
if(EnableDebug) Print("手动买入成功: ", lotSize, " 手");
}
}
else if(sparam == "SellButton")
{
// 手动卖出
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double atrArray[];
if(CopyBuffer(atrHandle, 0, 0, 1, atrArray) < 1)
{
Print("获取ATR值失败");
return;
}
double atrValue = atrArray * ATRMultiplier;
double stopLoss = NormalizeDouble(bid + atrValue, _Digits);
double takeProfit = NormalizeDouble(bid - (stopLoss - bid) * RiskRewardRatio, _Digits);
double stopLossPoints = (stopLoss - bid) / _Point;
double lotSize = CalculateLotSize(stopLossPoints);
if(Trade.Sell(lotSize, _Symbol, bid, stopLoss, takeProfit, "手动卖出"))
{
lastTradeTime = iTime(_Symbol, TimeFrame, 0);
dailyTradeCount++;
if(EnableDebug) Print("手动卖出成功: ", lotSize, " 手");
}
}
else if(sparam == "PauseButton")
{
// 暂停/恢复加仓
TradingPaused = !TradingPaused;
if(TradingPaused)
{
PauseButton.Description("恢复加仓");
PauseButton.BackColor(clrDarkSlateBlue);
if(EnableDebug) Print("EA已暂停自动加仓");
}
else
{
PauseButton.Description("暂停加仓");
PauseButton.BackColor(clrDarkOrange);
if(EnableDebug) Print("EA已恢复自动加仓");
}
ChartRedraw();
}
else if(sparam == "CloseAllButton")
{
// 清仓
CloseAllPositions();
if(EnableDebug) Print("已平仓所有头寸");
}
}
// 处理图表大小变化事件,调整按钮位置
if(id == CHARTEVENT_CHART_CHANGE)
{
long chartWidth = (long)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS);
long chartHeight = (long)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS);
int buttonWidth = 80;
int buttonHeight = 30;
int buttonSpacing = 10;
int xPos = (int)(chartWidth - buttonWidth - buttonSpacing);
int yPos = (int)(chartHeight - buttonHeight - buttonSpacing);
BuyButton.X_Distance(xPos);
BuyButton.Y_Distance(yPos);
yPos -= (buttonHeight + buttonSpacing);
SellButton.X_Distance(xPos);
SellButton.Y_Distance(yPos);
yPos -= (buttonHeight + buttonSpacing);
PauseButton.X_Distance(xPos);
PauseButton.Y_Distance(yPos);
yPos -= (buttonHeight + buttonSpacing);
CloseAllButton.X_Distance(xPos);
CloseAllButton.Y_Distance(yPos);
ChartRedraw();
}
}
是因为下单的条件太严格,所以下单量很少,下单是会下的,如果想用的话,参数可以继续修改一下
//+------------------------------------------------------------------+
//| Enhanced_MACD_Divergence_EA|
//| Copyright 2023, MetaQuotes Software Corp. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Software Corp."
#property link "https://www.mql5.com"
#property version "3.23"
#property strict
#property description "增强版MACD背离交易系统(带ATR止损)"
#property description "做多条件: MACD柱状图低于零轴且峰值上升 + 价格低点下降"
#property description "做空条件: MACD柱状图高于零轴且峰值下降 + 价格高点上升"
#property description "包含多重过滤条件和风险管理选项"
#include <Trade\Trade.mqh>
#include <ChartObjects\ChartObjectsTxtControls.mqh>
//+------------------------------------------------------------------+
//| 输入参数 |
//+------------------------------------------------------------------+
//--- MACD核心参数
input int FastEMA = 12; // MACD快线周期
input int SlowEMA = 26; // MACD慢线周期
input int SignalPeriod = 9; // MACD信号线周期
input ENUM_TIMEFRAMES TimeFrame = PERIOD_H1; // 交易时间框架
input ulong MagicNumber = 12345; // EA魔术码
//--- 风险管理
input double RiskPercent = 1.0; // 每笔交易风险百分比
input double FixedLotSize = 0.1; // 固定交易手数(当RiskPercent=0时使用)
input double RiskRewardRatio = 2.0; // 风险回报比
input int ATRPeriod = 14; // ATR止损周期
input double ATRMultiplier = 1.5; // ATR止损倍数(从1.8降低到1.5)
input bool UseTrailingStop = true; // 启用移动止损
input int TrailingStopPoints = 100; // 移动止损点数
input int MinStopLossPoints = 30; // 最小止损点数(从50降低到30)
input int MaxStopLossPoints = 300; // 最大止损点数
//--- 交易过滤器
input bool UseTrendFilter = false; // 启用趋势MA过滤(默认禁用)
input int TrendMAPeriod = 200; // 趋势MA周期
input ENUM_MA_METHOD TrendMAMethod = MODE_EMA; // 趋势MA计算方法
input bool UseHigherTFConfirmation = false; // 启用更高时间框架确认(默认禁用)
input ENUM_TIMEFRAMES HigherTF = PERIOD_D1; // 更高时间框架
input bool UseTimeFilter = false; // 启用交易时间过滤
input string TradeStartTime = "00:00"; // 交易开始时间(服务器时间)
input string TradeEndTime = "23:59"; // 交易结束时间(服务器时间)
input int MinPeakCount = 2; // 背离所需最小峰值数量
input int MaxPeakCount = 3; // 考虑的最大峰值数量
//--- 出场策略
input bool UsePartialProfit = true; // 启用部分平仓获利
input double PartialProfitRatio = 0.5; // 1:1时平仓比例
input double SecondTPMultiplier = 3.0; // 剩余仓位的目标倍数
input bool CloseOnOppositeSignal = true; // 出现相反信号时平仓
//--- 资金管理
input double MaxRiskPerTrade = 0.05; // 每笔交易最大风险(0.05=5%)
input double MaxDailyLossPercent = 5.0;// 每日最大亏损百分比(从2%提高到5%)
input int MaxTradesPerDay = 5; // 每日最大交易次数(从3提高到5)
//--- 调试选项
input bool EnableDebug = true; // 启用调试输出
input bool VisualMode = true; // 在图表上显示标记
//+------------------------------------------------------------------+
//| 全局变量 |
//+------------------------------------------------------------------+
double macdMain[], macdSignal[], macdHistogram[], atrValues[];
double trendMAValues[], higherTFMAValues[];
MqlRates rates[];
datetime lastTradeTime, lastDailyCheck;
int peakCount = 0;
double lastPeakValue = 0;
double firstPeakValue = 0;
double firstPeakPrice = 0;
double secondPeakValue = 0;
double secondPeakPrice = 0;
bool lookingForLong = false;
bool lookingForShort = false;
double lBarLow = 0;
double hBarHigh = 0;
double dailyProfitLoss = 0;
int dailyTradeCount = 0;
int macdHandle = INVALID_HANDLE;
int atrHandle = INVALID_HANDLE;
int trendMAHandle = INVALID_HANDLE;
int higherTFMAHandle = INVALID_HANDLE;
CTrade Trade;
bool TradingPaused = false;
bool SignalAlerted = false;
// 控制按钮
CChartObjectButton BuyButton;
CChartObjectButton SellButton;
CChartObjectButton PauseButton;
CChartObjectButton CloseAllButton;
//+------------------------------------------------------------------+
//| 创建控制按钮函数 |
//+------------------------------------------------------------------+
void CreateButtons()
{
// 获取图表宽度和高度
long chartWidth = (long)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS);
long chartHeight = (long)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS);
// 按钮尺寸
int buttonWidth = 80;
int buttonHeight = 30;
int buttonSpacing = 10;
// 计算按钮位置(右下角)
int xPos = (int)(chartWidth - buttonWidth - buttonSpacing);
int yPos = (int)(chartHeight - buttonHeight - buttonSpacing);
// 创建买入按钮
if(!BuyButton.Create(0, "BuyButton", 0, xPos, yPos, buttonWidth, buttonHeight))
Print("创建买入按钮失败!");
BuyButton.Description("买入");
BuyButton.Color(clrWhite);
BuyButton.BackColor(clrDarkGreen);
BuyButton.BorderColor(clrWhite);
BuyButton.FontSize(10);
BuyButton.State(false);
// 创建卖出按钮
yPos -= (buttonHeight + buttonSpacing);
if(!SellButton.Create(0, "SellButton", 0, xPos, yPos, buttonWidth, buttonHeight))
Print("创建卖出按钮失败!");
SellButton.Description("卖出");
SellButton.Color(clrWhite);
SellButton.BackColor(clrDarkRed);
SellButton.BorderColor(clrWhite);
SellButton.FontSize(10);
SellButton.State(false);
// 创建暂停加仓按钮
yPos -= (buttonHeight + buttonSpacing);
if(!PauseButton.Create(0, "PauseButton", 0, xPos, yPos, buttonWidth, buttonHeight))
Print("创建暂停按钮失败!");
PauseButton.Description("暂停加仓");
PauseButton.Color(clrWhite);
PauseButton.BackColor(clrDarkOrange);
PauseButton.BorderColor(clrWhite);
PauseButton.FontSize(10);
PauseButton.State(false);
// 创建清仓按钮
yPos -= (buttonHeight + buttonSpacing);
if(!CloseAllButton.Create(0, "CloseAllButton", 0, xPos, yPos, buttonWidth, buttonHeight))
Print("创建清仓按钮失败!");
CloseAllButton.Description("清仓");
CloseAllButton.Color(clrWhite);
CloseAllButton.BackColor(clrDarkSlateGray);
CloseAllButton.BorderColor(clrWhite);
CloseAllButton.FontSize(10);
CloseAllButton.State(false);
ChartRedraw();
}
//+------------------------------------------------------------------+
//| 重置每日统计数据 |
//+------------------------------------------------------------------+
void ResetDailyStats()
{
MqlDateTime today;
TimeCurrent(today);
today.hour = 0;
today.min = 0;
today.sec = 0;
lastDailyCheck = StructToTime(today);
dailyProfitLoss = 0;
dailyTradeCount = 0;
if(EnableDebug) Print("每日统计数据已重置");
}
//+------------------------------------------------------------------+
//| 专家初始化函数 |
//+------------------------------------------------------------------+
int OnInit()
{
// 初始化指标句柄
macdHandle = iMACD(_Symbol, TimeFrame, FastEMA, SlowEMA, SignalPeriod, PRICE_CLOSE);
atrHandle = iATR(_Symbol, TimeFrame, ATRPeriod);
if(UseTrendFilter)
trendMAHandle = iMA(_Symbol, TimeFrame, TrendMAPeriod, 0, TrendMAMethod, PRICE_CLOSE);
if(UseHigherTFConfirmation)
higherTFMAHandle = iMA(_Symbol, HigherTF, TrendMAPeriod, 0, TrendMAMethod, PRICE_CLOSE);
// 检查参数设置是否有效
if(RiskPercent <= 0 && FixedLotSize <= 0)
{
Alert("风险管理参数配置不正确!");
return(INIT_PARAMETERS_INCORRECT);
}
// 重置每日统计
ResetDailyStats();
// 设置交易对象的魔术码
Trade.SetExpertMagicNumber(MagicNumber);
// 创建控制按钮
CreateButtons();
if(EnableDebug) Print("EA初始化完成,开始运行...");
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| 专家逆初始化函数 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
// 释放指标句柄
if(macdHandle != INVALID_HANDLE) IndicatorRelease(macdHandle);
if(atrHandle != INVALID_HANDLE) IndicatorRelease(atrHandle);
if(trendMAHandle != INVALID_HANDLE) IndicatorRelease(trendMAHandle);
if(UseHigherTFConfirmation && higherTFMAHandle != INVALID_HANDLE) IndicatorRelease(higherTFMAHandle);
// 删除按钮
BuyButton.Delete();
SellButton.Delete();
PauseButton.Delete();
CloseAllButton.Delete();
// 删除所有图形对象
ObjectsDeleteAll(0, "DIVERGENCE_");
if(EnableDebug) Print("EA已卸载");
}
//+------------------------------------------------------------------+
//| 获取指标数据 |
//+------------------------------------------------------------------+
bool GetIndicatorValues()
{
int barsNeeded = 100; // 确保足够的历史数据
// 获取价格数据
if(CopyRates(_Symbol, TimeFrame, 0, barsNeeded, rates) < barsNeeded)
{
if(EnableDebug) Print("获取价格数据失败! 需要: ", barsNeeded, " 实际: ", ArraySize(rates));
return false;
}
// 获取MACD值
ArrayResize(macdMain, barsNeeded);
ArrayResize(macdSignal, barsNeeded);
ArrayResize(macdHistogram, barsNeeded);
if(CopyBuffer(macdHandle, 0, 0, barsNeeded, macdMain) < barsNeeded)
{
if(EnableDebug) Print("获取MACD主数据失败!");
return false;
}
if(CopyBuffer(macdHandle, 1, 0, barsNeeded, macdSignal) < barsNeeded)
{
if(EnableDebug) Print("获取MACD信号数据失败!");
return false;
}
// 计算柱状图值
for(int i = 0; i < barsNeeded; i++)
{
macdHistogram = macdMain - macdSignal;
}
// 获取ATR值
ArrayResize(atrValues, barsNeeded);
if(CopyBuffer(atrHandle, 0, 0, barsNeeded, atrValues) < barsNeeded)
{
if(EnableDebug) Print("获取ATR数据失败!");
return false;
}
// 获取趋势MA值
if(UseTrendFilter)
{
ArrayResize(trendMAValues, barsNeeded);
if(CopyBuffer(trendMAHandle, 0, 0, barsNeeded, trendMAValues) < barsNeeded)
{
if(EnableDebug) Print("获取趋势MA数据失败!");
return false;
}
}
// 获取更高时间框架MA值
if(UseHigherTFConfirmation)
{
ArrayResize(higherTFMAValues, barsNeeded);
if(CopyBuffer(higherTFMAHandle, 0, 0, barsNeeded, higherTFMAValues) < barsNeeded)
{
if(EnableDebug) Print("获取更高时间框架MA数据失败!");
return false;
}
}
return true;
}
//+------------------------------------------------------------------+
//| 根据风险计算手数 |
//+------------------------------------------------------------------+
double CalculateLotSize(double stopLossPoints)
{
if(RiskPercent <= 0) return FixedLotSize;
double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
double pointValue = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
if(tickValue <= 0 || tickSize <= 0 || pointValue <= 0)
{
if(EnableDebug) Print("无法获取点值信息,使用固定手数");
return FixedLotSize;
}
double riskAmount = MathMin(AccountInfoDouble(ACCOUNT_EQUITY) * RiskPercent / 100,
AccountInfoDouble(ACCOUNT_EQUITY) * MaxRiskPerTrade);
double lotSize = riskAmount / (stopLossPoints * tickValue * pointValue);
// 调整最小和最大手数限制
double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
double step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
lotSize = MathMax(minLot, MathMin(maxLot, lotSize));
lotSize = NormalizeDouble(lotSize, 2);
if(EnableDebug) Print("计算手数: ", lotSize, " SL点数: ", stopLossPoints, " 风险金额: ", riskAmount);
return lotSize;
}
//+------------------------------------------------------------------+
//| 检查当前是否在交易时间内 |
//+------------------------------------------------------------------+
bool IsTradingTime()
{
if(!UseTimeFilter) return true;
datetime start = StringToTime(TradeStartTime);
datetime end = StringToTime(TradeEndTime);
datetime now = TimeCurrent();
if(start == 0 || end == 0)
{
if(EnableDebug) Print("时间格式错误! 请使用HH:MM格式");
return false;
}
// 处理隔夜交易时段
if(start < end) return (now >= start && now <= end);
else return (now >= start || now <= end);
}
//+------------------------------------------------------------------+
//| 检查更高时间框架趋势 |
//+------------------------------------------------------------------+
bool IsHigherTFTrendBullish()
{
if(!UseHigherTFConfirmation) return true;
double close = iClose(_Symbol, HigherTF, 0);
double ma;
if(CopyBuffer(higherTFMAHandle, 0, 0, 1, ma) < 1)
{
if(EnableDebug) Print("获取更高时间框架MA数据失败!");
return true;
}
return close > ma;
}
//+------------------------------------------------------------------+
//| 检查是否超过每日限制 |
//+------------------------------------------------------------------+
bool ExceededDailyLimits()
{
MqlDateTime today, last;
TimeCurrent(today);
TimeToStruct(lastDailyCheck, last);
if(today.day != last.day || today.mon != last.mon || today.year != last.year)
{
// 新的一天,重置计数器
ResetDailyStats();
return false;
}
if(dailyProfitLoss < 0 && MathAbs(dailyProfitLoss) >= AccountInfoDouble(ACCOUNT_EQUITY) * MaxDailyLossPercent / 100)
{
if(EnableDebug) Print("已达到每日亏损限额。今日停止交易。");
return true;
}
if(dailyTradeCount >= MaxTradesPerDay)
{
if(EnableDebug) Print("已达到每日最大交易次数。今日停止交易。");
return true;
}
return false;
}
//+------------------------------------------------------------------+
//| 检查背离形态 |
//+------------------------------------------------------------------+
void CheckDivergence()
{
// 重置标志
lookingForLong = false;
lookingForShort = false;
SignalAlerted = false;
// 如果交易暂停,则跳过信号检查
if(TradingPaused)
{
if(EnableDebug) Print("交易已暂停,跳过信号检查");
return;
}
// 检查做多条件(MACD低于零轴)
if(macdHistogram < 0)
{
// 寻找最近的2-3个谷底
int valleysFound = 0;
double valleyValues = {0};
double valleyPrices = {0};
int valleyIndexes = {0};
// 从最近的bar开始向前搜索(索引0是最新)
for(int i = 1; i < MathMin(50, ArraySize(macdHistogram)-1); i++)
{
// 检查是否为谷底 (MACD柱状图低点)
if(macdHistogram < macdHistogram &&
macdHistogram < macdHistogram)
{
valleyValues = macdHistogram;
valleyPrices = rates.low;
valleyIndexes = i;
valleysFound++;
if(valleysFound >= MaxPeakCount) break;
}
}
// 检查是否有足够的谷底
if(valleysFound >= MinPeakCount)
{
// 检查价格是否创新低而MACD谷底升高
if(valleyPrices < valleyPrices && valleyValues > valleyValues)
{
lookingForLong = true;
lBarLow = valleyPrices; // 使用最新谷底价格
firstPeakValue = valleyValues;
firstPeakPrice = valleyPrices;
secondPeakValue = valleyValues;
secondPeakPrice = valleyPrices;
if(EnableDebug)
{
Print("检测到看涨背离:");
Print("价格1: ", valleyPrices, " MACD1: ", valleyValues, " 索引: ", valleyIndexes);
Print("价格2: ", valleyPrices, " MACD2: ", valleyValues, " 索引: ", valleyIndexes);
}
if(VisualMode)
{
// 在图表上标记背离
string objName = "DIVERGENCE_LONG_" + TimeToString(rates].time);
ObjectCreate(0, objName, OBJ_ARROW_BUY, 0, rates].time, valleyPrices);
ObjectSetInteger(0, objName, OBJPROP_COLOR, clrGreen);
ObjectSetInteger(0, objName, OBJPROP_WIDTH, 2);
objName = "DIVERGENCE_LONG_" + TimeToString(rates].time);
ObjectCreate(0, objName, OBJ_ARROW_BUY, 0, rates].time, valleyPrices);
ObjectSetInteger(0, objName, OBJPROP_COLOR, clrGreen);
ObjectSetInteger(0, objName, OBJPROP_WIDTH, 2);
}
}
}
}
// 检查做空条件(MACD高于零轴)
if(macdHistogram > 0)
{
// 寻找最近的2-3个峰值
int peaksFound = 0;
double peakValues = {0};
double peakPrices = {0};
int peakIndexes = {0};
// 从最近的bar开始向前搜索
for(int i = 1; i < MathMin(50, ArraySize(macdHistogram)-1); i++)
{
// 检查是否为峰值 (MACD柱状图高点)
if(macdHistogram > macdHistogram &&
macdHistogram > macdHistogram)
{
peakValues = macdHistogram;
peakPrices = rates.high;
peakIndexes = i;
peaksFound++;
if(peaksFound >= MaxPeakCount) break;
}
}
// 检查是否有足够的峰值
if(peaksFound >= MinPeakCount)
{
// 检查价格是否创新高而MACD峰值降低
if(peakPrices > peakPrices && peakValues < peakValues)
{
lookingForShort = true;
hBarHigh = peakPrices; // 使用最新峰值价格
firstPeakValue = peakValues;
firstPeakPrice = peakPrices;
secondPeakValue = peakValues;
secondPeakPrice = peakPrices;
if(EnableDebug)
{
Print("检测到看跌背离:");
Print("价格1: ", peakPrices, " MACD1: ", peakValues, " 索引: ", peakIndexes);
Print("价格2: ", peakPrices, " MACD2: ", peakValues, " 索引: ", peakIndexes);
}
if(VisualMode)
{
// 在图表上标记背离
string objName = "DIVERGENCE_SHORT_" + TimeToString(rates].time);
ObjectCreate(0, objName, OBJ_ARROW_SELL, 0, rates].time, peakPrices);
ObjectSetInteger(0, objName, OBJPROP_COLOR, clrRed);
ObjectSetInteger(0, objName, OBJPROP_WIDTH, 2);
objName = "DIVERGENCE_SHORT_" + TimeToString(rates].time);
ObjectCreate(0, objName, OBJ_ARROW_SELL, 0, rates].time, peakPrices);
ObjectSetInteger(0, objName, OBJPROP_COLOR, clrRed);
ObjectSetInteger(0, objName, OBJPROP_WIDTH, 2);
}
}
}
}
}
//+------------------------------------------------------------------+
//| 专家订单处理函数 |
//+------------------------------------------------------------------+
void OnTrade()
{
UpdateDailyProfit();
}
//+------------------------------------------------------------------+
//| 专家报价处理函数 |
//+------------------------------------------------------------------+
void OnTick()
{
static datetime lastBarTime = 0;
datetime currentBarTime = iTime(_Symbol, TimeFrame, 0);
// 只在新的K线开始时处理
if(currentBarTime == lastBarTime)
{
// 即使不是新K线,也检查是否有手动交易信号
if((lookingForLong || lookingForShort) && !SignalAlerted)
{
string signal = lookingForLong ? "买入信号" : "卖出信号";
Print("检测到交易信号 - ", signal, " 等待新K线确认");
SignalAlerted = true;
}
return;
}
lastBarTime = currentBarTime;
if(EnableDebug) Print("新K线开始: ", TimeToString(currentBarTime));
// 检查每日限制
if(ExceededDailyLimits())
{
if(EnableDebug) Print("超过每日限制,跳过交易");
return;
}
// 检查交易时间
if(!IsTradingTime())
{
if(EnableDebug) Print("不在交易时间内,跳过交易");
return;
}
// 获取指标值
if(!GetIndicatorValues())
{
if(EnableDebug) Print("获取指标数据失败,跳过处理");
return;
}
// 检查背离形态
CheckDivergence();
// 检查入场条件
if((lookingForLong || lookingForShort) && currentBarTime != lastTradeTime)
{
string signalType = lookingForLong ? "长" : "短";
if(EnableDebug) Print("检测到交易信号 - 类型: ", signalType);
// 附加过滤器
bool trendFilterPassed = true;
if(UseTrendFilter)
{
double currentClose = rates.close;
double ma;
if(CopyBuffer(trendMAHandle, 0, 0, 1, ma) < 1)
{
if(EnableDebug) Print("获取趋势MA数据失败!");
}
else
{
if(lookingForLong && currentClose < ma)
{
if(EnableDebug) Print("长信号被趋势过滤器拒绝");
trendFilterPassed = false;
}
if(lookingForShort && currentClose > ma)
{
if(EnableDebug) Print("短信号被趋势过滤器拒绝");
trendFilterPassed = false;
}
}
}
bool higherTFFilterPassed = true;
if(UseHigherTFConfirmation)
{
if(lookingForLong && !IsHigherTFTrendBullish())
{
if(EnableDebug) Print("长信号被更高时间框架过滤器拒绝");
higherTFFilterPassed = false;
}
if(lookingForShort && IsHigherTFTrendBullish())
{
if(EnableDebug) Print("短信号被更高时间框架过滤器拒绝");
higherTFFilterPassed = false;
}
}
// 如果通过过滤器则下单
if(lookingForLong && trendFilterPassed && higherTFFilterPassed)
{
PlaceLongOrder();
if(EnableDebug) Print("执行长订单");
}
else if(lookingForShort && trendFilterPassed && higherTFFilterPassed)
{
PlaceShortOrder();
if(EnableDebug) Print("执行短订单");
}
else
{
if(EnableDebug) Print("信号被过滤器阻止");
}
}
else
{
if(EnableDebug) Print("无有效交易信号或已在当前K线交易");
}
// 移动止损管理
if(UseTrailingStop) ManageTrailingStop();
// 部分获利平仓
if(UsePartialProfit) CheckPartialProfit();
// 出现相反信号时平仓
if(CloseOnOppositeSignal) CheckOppositeSignal();
}
//+------------------------------------------------------------------+
//| 下多单 |
//+------------------------------------------------------------------+
void PlaceLongOrder()
{
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double atrValue = atrValues * ATRMultiplier; // 使用最新ATR值
double stopLoss = NormalizeDouble(lBarLow - atrValue, _Digits);
double stopLossPoints = (ask - stopLoss) / _Point;
// 验证止损
if(stopLossPoints < MinStopLossPoints)
{
stopLoss = NormalizeDouble(ask - MinStopLossPoints * _Point, _Digits);
stopLossPoints = MinStopLossPoints;
}
if(stopLossPoints > MaxStopLossPoints)
{
stopLoss = NormalizeDouble(ask - MaxStopLossPoints * _Point, _Digits);
stopLossPoints = MaxStopLossPoints;
}
double takeProfit = NormalizeDouble(ask + (ask - stopLoss) * RiskRewardRatio, _Digits);
double lotSize = CalculateLotSize(stopLossPoints);
if(EnableDebug)
{
Print("尝试买入: ", lotSize, " 手");
Print("进场价: ", ask, " 止损: ", stopLoss, " 止盈: ", takeProfit);
}
if(UsePartialProfit)
{
// 分成两个不同止盈的订单
double firstLot = NormalizeDouble(lotSize * PartialProfitRatio, 2);
double secondLot = NormalizeDouble(lotSize - firstLot, 2);
if(firstLot > 0)
{
double firstTP = NormalizeDouble(ask + (ask - stopLoss), _Digits);
if(Trade.Buy(firstLot, _Symbol, ask, stopLoss, firstTP, "MACD背离做多1"))
{
lastTradeTime = iTime(_Symbol, TimeFrame, 0);
dailyTradeCount++;
if(EnableDebug) Print("买入订单1成功,手数: ", firstLot);
}
else
{
Print("买入订单1失败: ", Trade.ResultRetcodeDescription());
}
}
if(secondLot > 0)
{
double secondTP = NormalizeDouble(ask + (ask - stopLoss) * SecondTPMultiplier, _Digits);
if(Trade.Buy(secondLot, _Symbol, ask, stopLoss, secondTP, "MACD背离做多2"))
{
lastTradeTime = iTime(_Symbol, TimeFrame, 0);
dailyTradeCount++;
if(EnableDebug) Print("买入订单2成功,手数: ", secondLot);
}
else
{
Print("买入订单2失败: ", Trade.ResultRetcodeDescription());
}
}
}
else
{
if(Trade.Buy(lotSize, _Symbol, ask, stopLoss, takeProfit, "MACD背离做多"))
{
lastTradeTime = iTime(_Symbol, TimeFrame, 0);
dailyTradeCount++;
if(EnableDebug) Print("买入订单成功,手数: ", lotSize);
}
else
{
Print("买入订单失败: ", Trade.ResultRetcodeDescription());
}
}
lookingForLong = false;
SignalAlerted = false;
}
//+------------------------------------------------------------------+
//| 下空单 |
//+------------------------------------------------------------------+
void PlaceShortOrder()
{
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double atrValue = atrValues * ATRMultiplier; // 使用最新ATR值
double stopLoss = NormalizeDouble(hBarHigh + atrValue, _Digits);
double stopLossPoints = (stopLoss - bid) / _Point;
// 验证止损
if(stopLossPoints < MinStopLossPoints)
{
stopLoss = NormalizeDouble(bid + MinStopLossPoints * _Point, _Digits);
stopLossPoints = MinStopLossPoints;
}
if(stopLossPoints > MaxStopLossPoints)
{
stopLoss = NormalizeDouble(bid + MaxStopLossPoints * _Point, _Digits);
stopLossPoints = MaxStopLossPoints;
}
double takeProfit = NormalizeDouble(bid - (stopLoss - bid) * RiskRewardRatio, _Digits);
double lotSize = CalculateLotSize(stopLossPoints);
if(EnableDebug)
{
Print("尝试卖出: ", lotSize, " 手");
Print("进场价: ", bid, " 止损: ", stopLoss, " 止盈: ", takeProfit);
}
if(UsePartialProfit)
{
// 分成两个不同止盈的订单
double firstLot = NormalizeDouble(lotSize * PartialProfitRatio, 2);
double secondLot = NormalizeDouble(lotSize - firstLot, 2);
if(firstLot > 0)
{
double firstTP = NormalizeDouble(bid - (stopLoss - bid), _Digits);
if(Trade.Sell(firstLot, _Symbol, bid, stopLoss, firstTP, "MACD背离做空1"))
{
lastTradeTime = iTime(_Symbol, TimeFrame, 0);
dailyTradeCount++;
if(EnableDebug) Print("卖出订单1成功,手数: ", firstLot);
}
else
{
Print("卖出订单1失败: ", Trade.ResultRetcodeDescription());
}
}
if(secondLot > 0)
{
double secondTP = NormalizeDouble(bid - (stopLoss - bid) * SecondTPMultiplier, _Digits);
if(Trade.Sell(secondLot, _Symbol, bid, stopLoss, secondTP, "MACD背离做空2"))
{
lastTradeTime = iTime(_Symbol, TimeFrame, 0);
dailyTradeCount++;
if(EnableDebug) Print("卖出订单2成功,手数: ", secondLot);
}
else
{
Print("卖出订单2失败: ", Trade.ResultRetcodeDescription());
}
}
}
else
{
if(Trade.Sell(lotSize, _Symbol, bid, stopLoss, takeProfit, "MACD背离做空"))
{
lastTradeTime = iTime(_Symbol, TimeFrame, 0);
dailyTradeCount++;
if(EnableDebug) Print("卖出订单成功,手数: ", lotSize);
}
else
{
Print("卖出订单失败: ", Trade.ResultRetcodeDescription());
}
}
lookingForShort = false;
SignalAlerted = false;
}
//+------------------------------------------------------------------+
//| 管理移动止损 |
//+------------------------------------------------------------------+
void ManageTrailingStop()
{
for(int i = PositionsTotal()-1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(PositionSelectByTicket(ticket))
{
if(PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetString(POSITION_SYMBOL) == _Symbol)
{
double currentStop = PositionGetDouble(POSITION_SL);
double newStop = 0;
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
{
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
newStop = NormalizeDouble(bid - TrailingStopPoints * _Point, _Digits);
// 确保新的止损高于当前止损且不会造成亏损
if((newStop > currentStop || currentStop == 0) && (newStop > PositionGetDouble(POSITION_PRICE_OPEN)))
{
if(!Trade.PositionModify(ticket, newStop, PositionGetDouble(POSITION_TP)))
{
if(EnableDebug) Print("移动止损修改失败: ", Trade.ResultRetcodeDescription());
}
else if(EnableDebug) Print("多头止损更新为: ", newStop);
}
}
else if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
{
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
newStop = NormalizeDouble(ask + TrailingStopPoints * _Point, _Digits);
// 确保新的止损低于当前止损且不会造成亏损
if((newStop < currentStop || currentStop == 0) && (newStop < PositionGetDouble(POSITION_PRICE_OPEN)))
{
if(!Trade.PositionModify(ticket, newStop, PositionGetDouble(POSITION_TP)))
{
if(EnableDebug) Print("移动止损修改失败: ", Trade.ResultRetcodeDescription());
}
else if(EnableDebug) Print("空头止损更新为: ", newStop);
}
}
}
}
}
}
//+------------------------------------------------------------------+
//| 检查部分获利平仓 |
//+------------------------------------------------------------------+
void CheckPartialProfit()
{
for(int i = PositionsTotal()-1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(PositionSelectByTicket(ticket))
{
if(PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetString(POSITION_SYMBOL) == _Symbol)
{
double profitInPips = 0;
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
double stopLoss = PositionGetDouble(POSITION_SL);
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
{
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
profitInPips = (bid - openPrice) / _Point;
}
else if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
{
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
profitInPips = (openPrice - ask) / _Point;
}
double stopLossPips = MathAbs(openPrice - stopLoss) / _Point;
// 检查是否达到第一个目标(1:1)
if(profitInPips >= stopLossPips && StringFind(PositionGetString(POSITION_COMMENT), " 1") < 0)
{
double volume = PositionGetDouble(POSITION_VOLUME);
double volumeToClose = NormalizeDouble(volume * PartialProfitRatio, 2);
if(volumeToClose > 0)
{
if(!Trade.PositionClosePartial(ticket, volumeToClose))
{
if(EnableDebug) Print("部分平仓失败: ", Trade.ResultRetcodeDescription());
}
else
{
if(EnableDebug) Print("部分平仓成功: ", volumeToClose, " 手");
}
}
}
}
}
}
}
//+------------------------------------------------------------------+
//| 检查相反信号并平仓 |
//+------------------------------------------------------------------+
void CheckOppositeSignal()
{
for(int i = PositionsTotal()-1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(PositionSelectByTicket(ticket))
{
if(PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetString(POSITION_SYMBOL) == _Symbol)
{
bool shouldClose = false;
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY && lookingForShort)
shouldClose = true;
else if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL && lookingForLong)
shouldClose = true;
if(shouldClose)
{
if(!Trade.PositionClose(ticket))
{
if(EnableDebug) Print("平仓失败: ", Trade.ResultRetcodeDescription());
}
else
{
if(EnableDebug) Print("因相反信号平仓: ", ticket);
}
}
}
}
}
}
//+------------------------------------------------------------------+
//| 关闭所有仓位 |
//+------------------------------------------------------------------+
void CloseAllPositions()
{
int count = 0;
for(int i = PositionsTotal()-1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(PositionSelectByTicket(ticket))
{
if(PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetString(POSITION_SYMBOL) == _Symbol)
{
if(Trade.PositionClose(ticket))
{
count++;
if(EnableDebug) Print("平仓成功: ", ticket);
}
else
{
if(EnableDebug) Print("平仓失败: ", Trade.ResultRetcodeDescription());
}
}
}
}
if(EnableDebug) Print("已平仓所有头寸,数量: ", count);
}
//+------------------------------------------------------------------+
//| 更新每日盈亏统计 |
//+------------------------------------------------------------------+
void UpdateDailyProfit()
{
MqlDateTime today, last;
TimeCurrent(today);
TimeToStruct(lastDailyCheck, last);
if(today.day != last.day || today.mon != last.mon || today.year != last.year)
{
// 新的一天,重置计数器
ResetDailyStats();
return;
}
// 计算当日盈亏
double totalProfit = 0;
// 获取当日所有持仓的盈亏
for(int i = PositionsTotal()-1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(PositionSelectByTicket(ticket))
{
if(PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetString(POSITION_SYMBOL) == _Symbol)
{
datetime positionTime = (datetime)PositionGetInteger(POSITION_TIME);
MqlDateTime positionTimeStruct;
TimeToStruct(positionTime, positionTimeStruct);
if(positionTimeStruct.day == today.day &&
positionTimeStruct.mon == today.mon &&
positionTimeStruct.year == today.year)
{
totalProfit += PositionGetDouble(POSITION_PROFIT);
}
}
}
}
// 获取当日已平仓交易的盈亏
HistorySelect(lastDailyCheck, TimeCurrent());
int dealsTotal = HistoryDealsTotal();
for(int i = 0; i < dealsTotal; i++)
{
ulong dealTicket = HistoryDealGetTicket(i);
if(HistoryDealGetInteger(dealTicket, DEAL_MAGIC) == MagicNumber &&
HistoryDealGetString(dealTicket, DEAL_SYMBOL) == _Symbol)
{
datetime dealTime = (datetime)HistoryDealGetInteger(dealTicket, DEAL_TIME);
MqlDateTime dealTimeStruct;
TimeToStruct(dealTime, dealTimeStruct);
if(dealTimeStruct.day == today.day &&
dealTimeStruct.mon == today.mon &&
dealTimeStruct.year == today.year)
{
totalProfit += HistoryDealGetDouble(dealTicket, DEAL_PROFIT);
}
}
}
dailyProfitLoss = totalProfit;
if(EnableDebug) Print("每日盈亏更新: ", dailyProfitLoss);
}
//+------------------------------------------------------------------+
//| 图表事件处理函数 |
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
// 处理按钮点击事件
if(id == CHARTEVENT_OBJECT_CLICK)
{
if(sparam == "BuyButton")
{
// 手动买入
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double atrArray[];
if(CopyBuffer(atrHandle, 0, 0, 1, atrArray) < 1)
{
Print("获取ATR值失败");
return;
}
double atrValue = atrArray * ATRMultiplier;
double stopLoss = NormalizeDouble(ask - atrValue, _Digits);
double takeProfit = NormalizeDouble(ask + (ask - stopLoss) * RiskRewardRatio, _Digits);
double stopLossPoints = (ask - stopLoss) / _Point;
double lotSize = CalculateLotSize(stopLossPoints);
if(Trade.Buy(lotSize, _Symbol, ask, stopLoss, takeProfit, "手动买入"))
{
lastTradeTime = iTime(_Symbol, TimeFrame, 0);
dailyTradeCount++;
if(EnableDebug) Print("手动买入成功: ", lotSize, " 手");
}
}
else if(sparam == "SellButton")
{
// 手动卖出
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double atrArray[];
if(CopyBuffer(atrHandle, 0, 0, 1, atrArray) < 1)
{
Print("获取ATR值失败");
return;
}
double atrValue = atrArray * ATRMultiplier;
double stopLoss = NormalizeDouble(bid + atrValue, _Digits);
double takeProfit = NormalizeDouble(bid - (stopLoss - bid) * RiskRewardRatio, _Digits);
double stopLossPoints = (stopLoss - bid) / _Point;
double lotSize = CalculateLotSize(stopLossPoints);
if(Trade.Sell(lotSize, _Symbol, bid, stopLoss, takeProfit, "手动卖出"))
{
lastTradeTime = iTime(_Symbol, TimeFrame, 0);
dailyTradeCount++;
if(EnableDebug) Print("手动卖出成功: ", lotSize, " 手");
}
}
else if(sparam == "PauseButton")
{
// 暂停/恢复加仓
TradingPaused = !TradingPaused;
if(TradingPaused)
{
PauseButton.Description("恢复加仓");
PauseButton.BackColor(clrDarkSlateBlue);
if(EnableDebug) Print("EA已暂停自动加仓");
}
else
{
PauseButton.Description("暂停加仓");
PauseButton.BackColor(clrDarkOrange);
if(EnableDebug) Print("EA已恢复自动加仓");
}
ChartRedraw();
}
else if(sparam == "CloseAllButton")
{
// 清仓
CloseAllPositions();
if(EnableDebug) Print("已平仓所有头寸");
}
}
// 处理图表大小变化事件,调整按钮位置
if(id == CHARTEVENT_CHART_CHANGE)
{
long chartWidth = (long)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS);
long chartHeight = (long)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS);
int buttonWidth = 80;
int buttonHeight = 30;
int buttonSpacing = 10;
int xPos = (int)(chartWidth - buttonWidth - buttonSpacing);
int yPos = (int)(chartHeight - buttonHeight - buttonSpacing);
BuyButton.X_Distance(xPos);
BuyButton.Y_Distance(yPos);
yPos -= (buttonHeight + buttonSpacing);
SellButton.X_Distance(xPos);
SellButton.Y_Distance(yPos);
yPos -= (buttonHeight + buttonSpacing);
PauseButton.X_Distance(xPos);
PauseButton.Y_Distance(yPos);
yPos -= (buttonHeight + buttonSpacing);
CloseAllButton.X_Distance(xPos);
CloseAllButton.Y_Distance(yPos);
ChartRedraw();
}
}
这个改好的EA,周五一天只下了四个单子,下单量很少
页:
[1]