# MACD金叉死叉EA带EMA趋势过滤 - 完整MQL4源码
本文提供一个基于MACD金叉死叉策略的完整自动交易EA,并加入趋势过滤增强功能。该策略因某热门交易视频声称回测胜率高达87%而在交易者中广泛传播。EA采用纪律化交易方式:只顺应大周期趋势方向开仓,通过MACD金叉死叉确认入场信号。
策略逻辑
EA使用200周期指数移动平均线(EMA)作为主趋势过滤器。当价格在EMA上方时,只考虑做多信号;当价格在EMA下方时,只考虑做空信号。MACD(12,26,9)通过快线与慢线的交叉产生入场信号,在零轴下方发生的金叉信号强度更高。EA还包含三级加仓机制,允许在每个方向上最多加仓三次,以改善平均入场价格。
完整MQL4代码
```mql4
//+------------------------------------------------------------------+
//| MACD_Trend_Filter_EA.mq4 |
//| 自主编译 |
//+------------------------------------------------------------------+
#property copyright "AI助手"
#property link ""
#property version "1.00"
#property strict
//--- 输入参数
input double LotSize = 0.1; // 固定手数
input int SignalValidity = 7; // 信号有效K线数
input int SlowEMAPeriod = 200; // 趋势过滤EMA周期
input int FastMACD = 12; // MACD快线周期
input int SlowMACD = 26; // MACD慢线周期
input int SignalMACD = 9; // MACD信号线周期
input int StopLoss = 80; // 止损点数
input double RiskRewardRatio = 1.5; // 盈亏比(止盈/止损)
input int TrailingStop = 40; // 移动止损点数(0=关闭)
input int MaxSpread = 35; // 最大允许点差
input int MagicNumber = 202501; // EA魔术号
input bool UseThreeTier = true; // 启用三级加仓
input double SecondEntryDistance = 30; // 第二级加仓触发点数
input double ThirdEntryDistance = 60; // 第三级加仓触发点数
//--- 全局变量
double emaTrend = 0;
double macdMain = 0, macdSignal = 0;
double macdMainPrev = 0, macdSignalPrev = 0;
datetime lastSignalTime = 0;
int pointMultiplier = 10;
bool signalValid = false;
int signalDirection = 0; // 1=多, -1=空
datetime signalCandleTime = 0;
int entryCount = 0;
//+------------------------------------------------------------------+
//| EA初始化函数 |
//+------------------------------------------------------------------+
int OnInit()
{
if(Digits == 3 || Digits == 5)
pointMultiplier = 10;
else if(Digits == 2 || Digits == 4)
pointMultiplier = 1;
if(SlowEMAPeriod < 10)
{
Print("错误: EMA周期至少为10");
return(INIT_PARAMETERS_INCORRECT);
}
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| EA反初始化函数 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
}
//+------------------------------------------------------------------+
//| EA报价处理函数 |
//+------------------------------------------------------------------+
void OnTick()
{
// 点差检查
if(!IsSpreadOK())
return;
// 计算指标值
CalculateIndicators();
// 移动止损管理
if(TrailingStop > 0)
ManageTrailingStop();
// 更新信号有效性
UpdateSignalValidity();
// 检测新的MACD信号
CheckForNewSignal();
// 根据信号执行交易
if(signalValid)
{
if(signalDirection == 1)
ExecuteBuyStrategy();
else if(signalDirection == -1)
ExecuteSellStrategy();
}
// 三级加仓管理
if(UseThreeTier)
ManageThreeTierEntries();
}
//+------------------------------------------------------------------+
//| 计算所有指标值 |
//+------------------------------------------------------------------+
void CalculateIndicators()
{
emaTrend = iMA(Symbol(), 0, SlowEMAPeriod, 0, MODE_EMA, PRICE_CLOSE, 0);
macdMain = iMACD(Symbol(), 0, FastMACD, SlowMACD, SignalMACD, PRICE_CLOSE, MODE_MAIN, 0);
macdSignal = iMACD(Symbol(), 0, FastMACD, SlowMACD, SignalMACD, PRICE_CLOSE, MODE_SIGNAL, 0);
macdMainPrev = iMACD(Symbol(), 0, FastMACD, SlowMACD, SignalMACD, PRICE_CLOSE, MODE_MAIN, 1);
macdSignalPrev = iMACD(Symbol(), 0, FastMACD, SlowMACD, SignalMACD, PRICE_CLOSE, MODE_SIGNAL, 1);
}
//+------------------------------------------------------------------+
//| 检测新的MACD交叉信号 |
//+------------------------------------------------------------------+
void CheckForNewSignal()
{
bool buyCondition = false;
bool sellCondition = false;
// 做多条件:MACD金叉,价格在EMA上方
if(Close[0] > emaTrend && macdMainPrev <= macdSignalPrev && macdMain > macdSignal)
{
buyCondition = true;
}
// 做空条件:MACD死叉,价格在EMA下方
if(Close[0] < emaTrend && macdMainPrev >= macdSignalPrev && macdMain < macdSignal)
{
sellCondition = true;
}
if(buyCondition || sellCondition)
{
datetime currentCandle = iTime(Symbol(), 0, 0);
if(currentCandle != signalCandleTime)
{
signalCandleTime = currentCandle;
signalValid = true;
signalDirection = buyCondition ? 1 : -1;
lastSignalTime = TimeCurrent();
entryCount = 0;
Print("检测到新信号: ", signalDirection == 1 ? "做多" : "做空");
}
}
}
//+------------------------------------------------------------------+
//| 基于K线数量更新信号有效性 |
//+------------------------------------------------------------------+
void UpdateSignalValidity()
{
if(!signalValid) return;
datetime currentCandle = iTime(Symbol(), 0, 0);
int candlesSinceSignal = 0;
if(signalCandleTime > 0)
{
candlesSinceSignal = (int)((currentCandle - signalCandleTime) / PeriodSeconds());
}
if(candlesSinceSignal >= SignalValidity)
{
signalValid = false;
signalDirection = 0;
Print("信号已失效,经过了", SignalValidity, "根K线");
}
}
//+------------------------------------------------------------------+
//| 执行做多策略 |
//+------------------------------------------------------------------+
void ExecuteBuyStrategy()
{
if(CountBuyPositions() == 0 && entryCount == 0)
{
double sl = Bid - StopLoss * Point * pointMultiplier;
double tp = Bid + (StopLoss * RiskRewardRatio) * Point * pointMultiplier;
OpenOrder(OP_BUY, sl, tp);
entryCount = 1;
}
}
//+------------------------------------------------------------------+
//| 执行做空策略 |
//+------------------------------------------------------------------+
void ExecuteSellStrategy()
{
if(CountSellPositions() == 0 && entryCount == 0)
{
double sl = Ask + StopLoss * Point * pointMultiplier;
double tp = Ask - (StopLoss * RiskRewardRatio) * Point * pointMultiplier;
OpenOrder(OP_SELL, sl, tp);
entryCount = 1;
}
}
//+------------------------------------------------------------------+
//| 管理三级加仓 |
//+------------------------------------------------------------------+
void ManageThreeTierEntries()
{
if(!signalValid) return;
if(signalDirection == 1)
{
if(entryCount == 1 && CountBuyPositions() > 0)
{
double currentProfit = (Bid - GetAverageBuyPrice()) / Point / pointMultiplier;
if(currentProfit >= SecondEntryDistance && CountBuyPositions() < 2)
{
double sl = Bid - StopLoss * Point * pointMultiplier;
double tp = Bid + (StopLoss * RiskRewardRatio) * Point * pointMultiplier;
OpenOrder(OP_BUY, sl, tp);
entryCount = 2;
Print("第二级做多加仓触发");
}
else if(currentProfit >= ThirdEntryDistance && CountBuyPositions() < 3)
{
double sl = Bid - StopLoss * Point * pointMultiplier;
double tp = Bid + (StopLoss * RiskRewardRatio) * Point * pointMultiplier;
OpenOrder(OP_BUY, sl, tp);
entryCount = 3;
Print("第三级做多加仓触发");
}
}
}
else if(signalDirection == -1)
{
if(entryCount == 1 && CountSellPositions() > 0)
{
double currentProfit = (GetAverageSellPrice() - Ask) / Point / pointMultiplier;
if(currentProfit >= SecondEntryDistance && CountSellPositions() < 2)
{
double sl = Ask + StopLoss * Point * pointMultiplier;
double tp = Ask - (StopLoss * RiskRewardRatio) * Point * pointMultiplier;
OpenOrder(OP_SELL, sl, tp);
entryCount = 2;
Print("第二级做空加仓触发");
}
else if(currentProfit >= ThirdEntryDistance && CountSellPositions() < 3)
{
double sl = Ask + StopLoss * Point * pointMultiplier;
double tp = Ask - (StopLoss * RiskRewardRatio) * Point * pointMultiplier;
OpenOrder(OP_SELL, sl, tp);
entryCount = 3;
Print("第三级做空加仓触发");
}
}
}
}
//+------------------------------------------------------------------+
//| 开仓函数(带自定义止损止盈) |
//+------------------------------------------------------------------+
void OpenOrder(int cmd, double sl, double tp)
{
double price = (cmd == OP_BUY) ? Ask : Bid;
double lot = CalculateAdjustedLotSize();
int ticket = OrderSend(Symbol(), cmd, lot, price, 3, sl, tp, "MACD Trend EA", MagicNumber, 0, clrNONE);
if(ticket < 0)
Print("开仓失败. 错误码: ", GetLastError());
else
Print("开仓成功. 订单号: ", ticket, " 手数: ", lot, " 方向: ", cmd == OP_BUY ? "做多" : "做空");
}
//+------------------------------------------------------------------+
//| 计算手数(基于风险百分比) |
//+------------------------------------------------------------------+
double CalculateAdjustedLotSize()
{
double riskPercent = 1.0; // 每笔风险1%
double stopLossPoints = StopLoss * pointMultiplier;
double riskAmount = AccountBalance() * riskPercent / 100.0;
double tickValue = MarketInfo(Symbol(), MODE_TICKVALUE);
if(tickValue == 0) return LotSize;
double calculatedLot = riskAmount / (stopLossPoints * tickValue);
calculatedLot = NormalizeDouble(calculatedLot, 2);
double minLot = MarketInfo(Symbol(), MODE_MINLOT);
double maxLot = MarketInfo(Symbol(), MODE_MAXLOT);
if(calculatedLot < minLot) calculatedLot = minLot;
if(calculatedLot > maxLot) calculatedLot = maxLot;
return calculatedLot;
}
//+------------------------------------------------------------------+
//| 移动止损管理 |
//+------------------------------------------------------------------+
void ManageTrailingStop()
{
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
double trailPoints = TrailingStop * Point * pointMultiplier;
if(OrderType() == OP_BUY)
{
double newSL = Bid - trailPoints;
if(newSL > OrderStopLoss() && OrderStopLoss() != 0)
OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0, clrNONE);
}
else if(OrderType() == OP_SELL)
{
double newSL = Ask + trailPoints;
if((newSL < OrderStopLoss() || OrderStopLoss() == 0) && newSL > 0)
OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0, clrNONE);
}
}
}
}
}
//+------------------------------------------------------------------+
//| 检查点差是否在限制范围内 |
//+------------------------------------------------------------------+
bool IsSpreadOK()
{
if(MaxSpread <= 0) return true;
int currentSpread = (int)((Ask - Bid) / Point / pointMultiplier);
return (currentSpread <= MaxSpread);
}
//+------------------------------------------------------------------+
//| 统计买单数量 |
//+------------------------------------------------------------------+
int CountBuyPositions()
{
int count = 0;
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber && OrderType() == OP_BUY)
count++;
}
}
return count;
}
//+------------------------------------------------------------------+
//| 统计卖单数量 |
//+------------------------------------------------------------------+
int CountSellPositions()
{
int count = 0;
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber && OrderType() == OP_SELL)
count++;
}
}
return count;
}
//+------------------------------------------------------------------+
//| 获取平均做多价格 |
//+------------------------------------------------------------------+
double GetAverageBuyPrice()
{
double totalPrice = 0;
double totalLots = 0;
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber && OrderType() == OP_BUY)
{
totalPrice += OrderOpenPrice() * OrderLots();
totalLots += OrderLots();
}
}
}
if(totalLots > 0)
return totalPrice / totalLots;
return 0;
}
//+------------------------------------------------------------------+
//| 获取平均做空价格 |
//+------------------------------------------------------------------+
double GetAverageSellPrice()
{
double totalPrice = 0;
double totalLots = 0;
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber && OrderType() == OP_SELL)
{
totalPrice += OrderOpenPrice() * OrderLots();
totalLots += OrderLots();
}
}
}
if(totalLots > 0)
return totalPrice / totalLots;
return 0;
}
//+------------------------------------------------------------------+
```
参数详解
| 参数 | 说明 | 推荐值 |
|------|------|--------|
| 固定手数 | 固定交易手数 | 0.01-0.1 |
| 信号有效K线数 | 信号保持有效的K线数量 | 5-10 |
| 趋势过滤EMA周期 | EMA周期用于趋势判断 | 200 |
| MACD快线周期 | MACD快线EMA周期 | 12 |
| MACD慢线周期 | MACD慢线EMA周期 | 26 |
| MACD信号线周期 | MACD信号线周期 | 9 |
| 止损点数 | 止损距离 | 60-100 |
| 盈亏比 | 止盈相对于止损的倍数 | 1.5-2.0 |
| 移动止损点数 | 追踪止损距离(0=关闭) | 30-50 |
| 最大点差 | 允许的最大点差 | 30-40 |
| 魔术号 | EA唯一标识 | 任意 |
| 启用三级加仓 | 是否启用三级加仓机制 | true/false |
| 第二级加仓点数 | 触发第二级加仓的盈利点数 | 25-40 |
| 第三级加仓点数 | 触发第三级加仓的盈利点数 | 50-70 |
安装步骤
1. 复制代码到MT4的MetaEditor(按F4)
2. 点击编译(F7)- 确保无错误
3. 将EA附加到图表(建议EURUSD,M15时间框架)
4. 在输入参数选项卡中调整参数
5. 启用自动交易(Alt+T)
编译与修改技巧
主要修改方向:
最佳市场环境:
本策略在具有明确方向性的趋势市场中表现最佳。应避免在高冲击新闻事件期间或极低波动率环境下使用。
参考来源
本文EA源码为自主编译。策略概念基于MACD金叉死叉配合EMA趋势过滤方法。三级加仓和移动止损等增强功能为自主添加。
*如需更专业的优化版EA策略(含多周期分析、完整回测报告、高级资金管理和专业技术支持),请查看我们的付费EA合集。订阅后可每周获取更新和独家交易工具。*