//+------------------------------------------------------------------+ //| EMA_Trend_Strategy.mq5 | //| Copyright 2025, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" #include CTrade trade; //+------------------------------------------------------------------+ //| 输入参数 | //+------------------------------------------------------------------+ input group "=====基本参数======" input int MagicNumber = 123459; input string OrderComment = "EMA_Trend_Strategy"; input group "=====交易参数=====" input double StartLots = 0.1; // 开仓手数 input bool EnableDynamicLots = true; // 是否启用动态手数(按余额比例) input double BaseBalance = 10000.0; // 基准余额(用于动态手数计算) input group "=====EMA参数=====" input int FastEMA = 12; // 快线EMA周期 input int SlowEMA = 144; // 慢线EMA周期 input group "=====交易控制=====" input bool EnableBuy = true; // 是否允许做多 input bool EnableSell = true; // 是否允许做空 //+------------------------------------------------------------------+ //| 全局变量 | //+------------------------------------------------------------------+ // EMA指标句柄 int fastEMAHandle = INVALID_HANDLE; int slowEMAHandle = INVALID_HANDLE; // 当前持仓信息 ulong currentBuyTicket = 0; ulong currentSellTicket = 0; double currentLots = 0.0; // 状态控制 bool isInitialized = false; datetime lastCrossTime = 0; int lastCrossDirection = 0; // 0=无交叉, 1=金叉(快线上穿慢线), -1=死叉(快线下穿慢线) //+------------------------------------------------------------------+ //| 计算开仓手数:可选动态手数(按余额比例)或固定手数 | //+------------------------------------------------------------------+ double GetDynamicLots() { double minLots = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN); double maxLots = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX); double stepLots = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP); if(!EnableDynamicLots) { double fixedLots = StartLots; fixedLots = MathMax(minLots, MathMin(maxLots, fixedLots)); fixedLots = NormalizeDouble(fixedLots / stepLots, 0) * stepLots; return fixedLots; } double accountBalance = AccountInfoDouble(ACCOUNT_BALANCE); double baseLots = StartLots; double dynamicLots = (accountBalance / BaseBalance) * baseLots; dynamicLots = MathMax(minLots, MathMin(maxLots, dynamicLots)); dynamicLots = NormalizeDouble(dynamicLots / stepLots, 0) * stepLots; return dynamicLots; } //+------------------------------------------------------------------+ //| 获取EMA值 | //+------------------------------------------------------------------+ double GetEMAValue(int handle, int shift) { if(handle == INVALID_HANDLE) return 0.0; double emaArray[1]; if(CopyBuffer(handle, 0, shift, 1, emaArray) <= 0) return 0.0; return emaArray[0]; } //+------------------------------------------------------------------+ //| 检测EMA交叉 | //+------------------------------------------------------------------+ int DetectEMACross() { // 获取当前K线和上一根K线的EMA值 double fastEMA_Current = GetEMAValue(fastEMAHandle, 0); double slowEMA_Current = GetEMAValue(slowEMAHandle, 0); double fastEMA_Previous = GetEMAValue(fastEMAHandle, 1); double slowEMA_Previous = GetEMAValue(slowEMAHandle, 1); // 添加调试信息 static datetime lastEMADebugTime = 0; if(TimeCurrent() - lastEMADebugTime >= 10) { // 每10秒打印一次EMA值 Print("=== EMA交叉检测调试 ==="); Print("当前K线 - EMA", FastEMA, ":", fastEMA_Current, " EMA", SlowEMA, ":", slowEMA_Current); Print("上一K线 - EMA", FastEMA, ":", fastEMA_Previous, " EMA", SlowEMA, ":", slowEMA_Previous); Print("快线变化:", fastEMA_Current - fastEMA_Previous); Print("慢线变化:", slowEMA_Current - slowEMA_Previous); lastEMADebugTime = TimeCurrent(); } if(fastEMA_Current == 0.0 || slowEMA_Current == 0.0 || fastEMA_Previous == 0.0 || slowEMA_Previous == 0.0) { Print("EMA数据不足,无法检测交叉"); return 0; // 数据不足 } // 检测金叉:快线上穿慢线 if(fastEMA_Previous <= slowEMA_Previous && fastEMA_Current > slowEMA_Current) { Print("检测到金叉信号!快线上穿慢线"); return 1; // 金叉 } // 检测死叉:快线下穿慢线 if(fastEMA_Previous >= slowEMA_Previous && fastEMA_Current < slowEMA_Current) { Print("检测到死叉信号!快线下穿慢线"); return -1; // 死叉 } return 0; // 无交叉 } //+------------------------------------------------------------------+ //| 开仓函数 | //+------------------------------------------------------------------+ bool OpenPosition(int posType, double lots, double &price, double sl, double tp, string comment) { trade.SetExpertMagicNumber(MagicNumber); bool result = false; if(posType == POSITION_TYPE_BUY) { price = SymbolInfoDouble(_Symbol, SYMBOL_ASK); result = trade.Buy(lots, _Symbol, price, sl, tp, comment); } else if(posType == POSITION_TYPE_SELL) { price = SymbolInfoDouble(_Symbol, SYMBOL_BID); result = trade.Sell(lots, _Symbol, price, sl, tp, comment); } if(result) { string direction = (posType == POSITION_TYPE_BUY) ? "多单" : "空单"; Print("开仓成功:", direction, " 价格:", price, " 手数:", lots, " 订单号:", trade.ResultOrder()); return true; } else { Print("开仓失败: 错误代码:", trade.ResultRetcode(), " 错误描述:", trade.ResultRetcodeDescription()); return false; } } //+------------------------------------------------------------------+ //| 平仓函数 | //+------------------------------------------------------------------+ bool ClosePosition(ulong ticket, string reason) { if(!PositionSelectByTicket(ticket)) return false; if(trade.PositionClose(ticket)) { Print("平仓成功:订单号:", ticket, " 原因:", reason); return true; } else { Print("平仓失败:订单号:", ticket, " 错误:", trade.ResultRetcode()); return false; } } //+------------------------------------------------------------------+ //| 计算止损止盈价格 | //+------------------------------------------------------------------+ // 移除止损计算函数,改为通过信号止损 //+------------------------------------------------------------------+ //| 处理EMA交叉信号 | //+------------------------------------------------------------------+ void ProcessEMACross(int crossDirection) { if(crossDirection == 0) return; // 避免重复处理同一交叉信号 if(crossDirection == lastCrossDirection) return; lastCrossDirection = crossDirection; lastCrossTime = TimeCurrent(); if(crossDirection == 1) { // 金叉:快线上穿慢线,开多平空 Print("=== EMA金叉信号 ==="); // 平空单 if(currentSellTicket > 0) { if(ClosePosition(currentSellTicket, "EMA金叉平空")) { currentSellTicket = 0; } } // 开多单 if(EnableBuy && currentBuyTicket == 0) { double entryPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK); Print("尝试开多单 - 手数:", currentLots, " 开仓价:", entryPrice, " 止损方式:信号止损"); string comment = OrderComment + "_EMA_GoldenCross"; if(OpenPosition(POSITION_TYPE_BUY, currentLots, entryPrice, 0, 0, comment)) { currentBuyTicket = trade.ResultOrder(); Print("EMA金叉开多成功,订单号:", currentBuyTicket); } else { Print("EMA金叉开多失败,请检查交易条件"); } } else { if(!EnableBuy) { Print("做多功能已禁用"); } else if(currentBuyTicket > 0) { Print("已有多单,订单号:", currentBuyTicket); } } } else if(crossDirection == -1) { // 死叉:快线下穿慢线,开空平多 Print("=== EMA死叉信号 ==="); // 平多单 if(currentBuyTicket > 0) { if(ClosePosition(currentBuyTicket, "EMA死叉平多")) { currentBuyTicket = 0; } } // 开空单 if(EnableSell && currentSellTicket == 0) { double entryPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID); Print("尝试开空单 - 手数:", currentLots, " 开仓价:", entryPrice, " 止损方式:信号止损"); string comment = OrderComment + "_EMA_DeathCross"; if(OpenPosition(POSITION_TYPE_SELL, currentLots, entryPrice, 0, 0, comment)) { currentSellTicket = trade.ResultOrder(); Print("EMA死叉开空成功,订单号:", currentSellTicket); } else { Print("EMA死叉开空失败,请检查交易条件"); } } else { if(!EnableSell) { Print("做空功能已禁用"); } else if(currentSellTicket > 0) { Print("已有空单,订单号:", currentSellTicket); } } } } //+------------------------------------------------------------------+ //| 检查持仓状态 | //+------------------------------------------------------------------+ void CheckPositions() { // 检查多单是否还存在 if(currentBuyTicket > 0 && !PositionSelectByTicket(currentBuyTicket)) { Print("多单已不存在,重置状态"); currentBuyTicket = 0; } // 检查空单是否还存在 if(currentSellTicket > 0 && !PositionSelectByTicket(currentSellTicket)) { Print("空单已不存在,重置状态"); currentSellTicket = 0; } } //+------------------------------------------------------------------+ //| 打印状态信息 | //+------------------------------------------------------------------+ void PrintStatus() { Print("=== EMA趋势策略状态 ==="); Print("当前多单:", currentBuyTicket, " 空单:", currentSellTicket, " 手数:", currentLots); // 获取当前EMA值 double fastEMA = GetEMAValue(fastEMAHandle, 0); double slowEMA = GetEMAValue(slowEMAHandle, 0); Print("EMA", FastEMA, ":", fastEMA, " EMA", SlowEMA, ":", slowEMA); if(fastEMA > slowEMA) { Print("当前趋势:多头趋势(快线在慢线上方)"); } else if(fastEMA < slowEMA) { Print("当前趋势:空头趋势(快线在慢线下方)"); } else { Print("当前趋势:无明确趋势(快线等于慢线)"); } if(lastCrossDirection == 1) { Print("最后信号:金叉(快线上穿慢线)"); } else if(lastCrossDirection == -1) { Print("最后信号:死叉(快线下穿慢线)"); } else { Print("最后信号:无交叉"); } } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { // 检查持仓状态 CheckPositions(); // 检测EMA交叉 int crossDirection = DetectEMACross(); // 添加调试信息 static datetime lastDebugTime = 0; if(TimeCurrent() - lastDebugTime >= 5) { // 每5秒打印一次调试信息 Print("=== 调试信息 ==="); Print("交叉检测结果:", crossDirection, " (0=无交叉, 1=金叉, -1=死叉)"); Print("最后交叉方向:", lastCrossDirection); Print("当前多单:", currentBuyTicket, " 空单:", currentSellTicket); Print("做多启用:", EnableBuy, " 做空启用:", EnableSell); Print("当前手数:", currentLots); // 获取当前EMA值 double fastEMA = GetEMAValue(fastEMAHandle, 0); double slowEMA = GetEMAValue(slowEMAHandle, 0); Print("EMA", FastEMA, ":", fastEMA, " EMA", SlowEMA, ":", slowEMA); lastDebugTime = TimeCurrent(); } // 处理交叉信号 if(crossDirection != 0) { Print("检测到交叉信号:", crossDirection, " 开始处理..."); ProcessEMACross(crossDirection); } // 每10秒打印一次状态 static datetime lastPrintTime = 0; if(TimeCurrent() - lastPrintTime >= 10) { PrintStatus(); lastPrintTime = TimeCurrent(); } } //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { Print("=== EMA趋势策略EA初始化 ==="); Print("EMA参数 - 快线:", FastEMA, " 慢线:", SlowEMA); Print("交易方向 - 多单:", (EnableBuy ? "启用" : "禁用"), " 空单:", (EnableSell ? "启用" : "禁用")); Print("止损方式:通过EMA交叉信号止损(金叉止损空单,死叉止损多单)"); Print("止盈方式:通过反向信号止盈(金叉平空,死叉平多)"); Print("手数模式:", (EnableDynamicLots ? "动态手数" : "固定手数")); if(EnableDynamicLots) { Print("基准余额:", BaseBalance, " 基准手数:", StartLots); } else { Print("固定手数:", StartLots); } Print("当前服务器时间: ", TimeToString(TimeCurrent(), TIME_DATE|TIME_MINUTES)); // 初始化EMA指标句柄 fastEMAHandle = iMA(_Symbol, _Period, FastEMA, 0, MODE_EMA, PRICE_CLOSE); slowEMAHandle = iMA(_Symbol, _Period, SlowEMA, 0, MODE_EMA, PRICE_CLOSE); if(fastEMAHandle == INVALID_HANDLE || slowEMAHandle == INVALID_HANDLE) { Print("EMA指标初始化失败"); return(INIT_FAILED); } // 计算初始手数 currentLots = GetDynamicLots(); isInitialized = true; Print("EMA趋势策略EA初始化完成"); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { // 释放指标句柄 if(fastEMAHandle != INVALID_HANDLE) IndicatorRelease(fastEMAHandle); if(slowEMAHandle != INVALID_HANDLE) IndicatorRelease(slowEMAHandle); Print("EMA趋势策略EA已停止,原因代码:", reason); Print("当前多单:", currentBuyTicket, " 空单:", currentSellTicket); }