# 三级MACD趋势跟踪EA - 完整MQL4源码(基于Trading Rush策略)
本文提供一个基于Trading Rush视频中广为流传的"87%胜率策略"的完整自动交易EA。该策略在2025年初引发广泛关注,其核心是纪律化的"三试"方法,每个交易信号最多允许三次分批入场,以改善平均入场价格并提高整体胜率。
策略逻辑
该EA将MACD金叉死叉信号与长期EMA趋势过滤相结合。当有效信号出现时,EA可以在预设的价格水平最多入场三次,随着价格有利方向移动而分批加仓。这种"金字塔式"或"分批"入场方法旨在改善平均入场价格,同时保持有利的风险回报比。
核心组件
1. 信号生成:MACD(12,26,9)金叉死叉,配合价格相对于200周期EMA的位置确认
2. 三级加仓系统:每个信号最多三次入场,间隔为用户定义的距离
3. 虚拟止损/止盈:内部追踪精确的出场水平,避免经纪商止损猎杀
4. 两步下单法:避免常见的MT4 OrderSend错误130
完整MQL4代码
```mql4
//+------------------------------------------------------------------+
//| ThreeTier_MACD_Trend_EA.mq4 |
//| 自主编译 |
//| Based on Trading Rush Strategy |
//+------------------------------------------------------------------+
#property copyright "AI助手"
#property link ""
#property version "1.00"
#property strict
//--- 输入参数 - 信号设置
input int FastMACD = 12; // MACD快线周期
input int SlowMACD = 26; // MACD慢线周期
input int SignalMACD = 9; // MACD信号线周期
input int TrendEMAPeriod = 200; // 趋势过滤EMA周期
input int SignalValidityBars = 5; // 信号有效K线数
//--- 输入参数 - 三级加仓
input bool UseThreeTier = true; // 启用三级加仓
input double Tier1Distance = 0; // 第一级距离点数(0=市价)
input double Tier2Distance = 25; // 第二级距离点数
input double Tier3Distance = 50; // 第三级距离点数
//--- 输入参数 - 风险管理
input double RiskPercent = 1.0; // 每完整系列风险%(0=使用固定手数)
input double FixedLotSize = 0.1; // 固定手数(RiskPercent=0时)
input double RiskRewardRatio = 1.5; // 盈亏比(止盈/止损)
input int BaseStopLoss = 60; // 基础止损点数
input int MaxSpread = 35; // 最大允许点差
input int Slippage = 10; // 最大滑点
//--- 输入参数 - 虚拟止损/止盈
input bool UseVirtualSLTP = true; // 启用虚拟止损止盈管理
input int VirtualBuffer = 15; // 经纪商止损止盈缓冲点数
//--- 输入参数 - 交易管理
input int MagicNumber = 202608; // EA魔术号
input bool CloseOnOpposite = true; // 反向信号时平仓反向单
input bool UseTimeFilter = false; // 启用时间过滤
input int StartHour = 8; // 开始交易小时
input int EndHour = 20; // 结束交易小时
//--- 全局变量
double emaTrend = 0;
double macdMain = 0, macdSignal = 0;
double macdMainPrev = 0, macdSignalPrev = 0;
datetime signalCandleTime = 0;
int signalDirection = 0; // 1=做多, -1=做空
int currentTier = 0; // 0=无持仓, 1=第一级, 2=第二级, 3=第三级
int pointMultiplier = 10;
double virtualStopLoss = 0;
double virtualTakeProfit = 0;
double weightedAvgPrice = 0;
double totalLots = 0;
datetime lastBarTime = 0;
//+------------------------------------------------------------------+
//| EA初始化函数 |
//+------------------------------------------------------------------+
int OnInit()
{
// 检测平台报价格式
if(Digits == 3 || Digits == 5)
pointMultiplier = 10;
else if(Digits == 2 || Digits == 4)
pointMultiplier = 1;
if(FastMACD >= SlowMACD)
{
Print("错误: MACD快线周期必须小于慢线周期");
return(INIT_PARAMETERS_INCORRECT);
}
Print("三级MACD EA初始化成功");
Print("每系列风险: ", RiskPercent, "% | 盈亏比: ", RiskRewardRatio);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| EA反初始化函数 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
Print("EA已移除. 原因: ", reason);
}
//+------------------------------------------------------------------+
//| 检测新K线 |
//+------------------------------------------------------------------+
bool IsNewBar()
{
datetime currentBarTime = iTime(Symbol(), 0, 0);
if(currentBarTime != lastBarTime)
{
lastBarTime = currentBarTime;
return true;
}
return false;
}
//+------------------------------------------------------------------+
//| 检查点差是否在限制范围内 |
//+------------------------------------------------------------------+
bool IsSpreadOK()
{
if(MaxSpread <= 0) return true;
int currentSpread = (int)((Ask - Bid) / Point / pointMultiplier);
return (currentSpread <= MaxSpread);
}
//+------------------------------------------------------------------+
//| 检查交易时间 |
//+------------------------------------------------------------------+
bool IsTradingTime()
{
if(!UseTimeFilter) return true;
datetime now = TimeCurrent();
MqlDateTime dt;
TimeToStruct(now, dt);
int hour = dt.hour;
return (hour >= StartHour && hour < EndHour);
}
//+------------------------------------------------------------------+
//| 计算指标值 |
//+------------------------------------------------------------------+
void CalculateIndicators()
{
emaTrend = iMA(Symbol(), 0, TrendEMAPeriod, 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 buySignal = false;
bool sellSignal = false;
// 做多信号:MACD金叉 AND 价格在EMA200上方
if(Close[0] > emaTrend && macdMainPrev <= macdSignalPrev && macdMain > macdSignal)
{
buySignal = true;
}
// 做空信号:MACD死叉 AND 价格在EMA200下方
if(Close[0] < emaTrend && macdMainPrev >= macdSignalPrev && macdMain < macdSignal)
{
sellSignal = true;
}
if(buySignal || sellSignal)
{
datetime currentCandle = iTime(Symbol(), 0, 0);
if(currentCandle != signalCandleTime)
{
signalCandleTime = currentCandle;
signalDirection = buySignal ? 1 : -1;
Print("检测到新信号: ", signalDirection == 1 ? "做多" : "做空");
}
}
}
//+------------------------------------------------------------------+
//| 基于风险百分比计算手数 |
//+------------------------------------------------------------------+
double CalculatePositionSize()
{
if(RiskPercent <= 0)
return FixedLotSize;
double accountBalance = AccountBalance();
double riskAmount = accountBalance * RiskPercent / 100.0;
double stopDistance = BaseStopLoss * pointMultiplier;
double tickValue = MarketInfo(Symbol(), MODE_TICKVALUE);
double lotStep = MarketInfo(Symbol(), MODE_LOTSTEP);
if(tickValue <= 0 || lotStep <= 0)
return FixedLotSize;
double calculatedLot = riskAmount / (stopDistance * tickValue);
calculatedLot = MathFloor(calculatedLot / lotStep) * lotStep;
double minLot = MarketInfo(Symbol(), MODE_MINLOT);
double maxLot = MarketInfo(Symbol(), MODE_MAXLOT);
if(calculatedLot < minLot) calculatedLot = minLot;
if(calculatedLot > maxLot) calculatedLot = maxLot;
return NormalizeDouble(calculatedLot, 2);
}
//+------------------------------------------------------------------+
//| 计算虚拟止损止盈 |
//+------------------------------------------------------------------+
void CalculateVirtualLevels(int direction, double entryPrice)
{
double stopPips = BaseStopLoss * Point * pointMultiplier;
double tpPips = stopPips * RiskRewardRatio;
if(direction == 1) // 做多
{
virtualStopLoss = entryPrice - stopPips;
virtualTakeProfit = entryPrice + tpPips;
}
else // 做空
{
virtualStopLoss = entryPrice + stopPips;
virtualTakeProfit = entryPrice - tpPips;
}
}
//+------------------------------------------------------------------+
//| 两步法开仓(避免错误130) |
//+------------------------------------------------------------------+
bool OpenOrderTwoStep(int cmd, double lot, double price)
{
// 第一步:无止损止盈开仓
int ticket = OrderSend(Symbol(), cmd, lot, price, Slippage, 0, 0, "3Tier MACD", MagicNumber, 0, clrNONE);
if(ticket < 0)
{
Print("第一步开仓失败. 错误码: ", GetLastError());
return false;
}
// 第二步:修改订单添加止损止盈
if(OrderSelect(ticket, SELECT_BY_TICKET))
{
double sl = 0, tp = 0;
double buffer = VirtualBuffer * Point * pointMultiplier;
if(cmd == OP_BUY)
{
sl = virtualStopLoss - buffer;
tp = virtualTakeProfit + buffer;
}
else
{
sl = virtualStopLoss + buffer;
tp = virtualTakeProfit - buffer;
}
if(!OrderModify(ticket, OrderOpenPrice(), sl, tp, 0, clrNONE))
{
Print("修改订单失败 订单号", ticket, ". 错误码: ", GetLastError());
}
}
Print("开仓成功. 订单号: ", ticket, " | 手数: ", lot, " | 方向: ", cmd == OP_BUY ? "做多" : "做空");
return true;
}
//+------------------------------------------------------------------+
//| 开仓指定级别的入场 |
//+------------------------------------------------------------------+
void OpenTierEntry(int tier)
{
if(signalDirection == 0) return;
double lotSize = CalculatePositionSize();
double entryPrice = 0;
if(tier == 1)
{
entryPrice = (signalDirection == 1) ? Ask : Bid;
}
else if(tier == 2)
{
double distance = Tier2Distance * Point * pointMultiplier;
entryPrice = (signalDirection == 1) ? weightedAvgPrice + distance : weightedAvgPrice - distance;
}
else if(tier == 3)
{
double distance = Tier3Distance * Point * pointMultiplier;
entryPrice = (signalDirection == 1) ? weightedAvgPrice + distance : weightedAvgPrice - distance;
}
// 第二、三级使用半仓
if(tier > 1 && UseThreeTier)
{
lotSize = lotSize / 2;
}
if(lotSize <= 0) return;
bool success = OpenOrderTwoStep(signalDirection == 1 ? OP_BUY : OP_SELL, lotSize, entryPrice);
if(success)
{
currentTier = tier;
UpdateWeightedAverage();
}
}
//+------------------------------------------------------------------+
//| 更新加权平均价格和总手数 |
//+------------------------------------------------------------------+
void UpdateWeightedAverage()
{
double totalPrice = 0;
totalLots = 0;
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
totalPrice += OrderOpenPrice() * OrderLots();
totalLots += OrderLots();
}
}
}
if(totalLots > 0)
weightedAvgPrice = totalPrice / totalLots;
// 基于加权平均价格更新虚拟水平
if(weightedAvgPrice > 0)
CalculateVirtualLevels(signalDirection, weightedAvgPrice);
}
//+------------------------------------------------------------------+
//| 检查虚拟止损止盈 |
//+------------------------------------------------------------------+
void CheckVirtualLevels()
{
if(!UseVirtualSLTP) return;
if(virtualStopLoss == 0 || virtualTakeProfit == 0) return;
double currentPrice = (signalDirection == 1) ? Bid : Ask;
bool shouldClose = false;
string reason = "";
if(signalDirection == 1) // 做多持仓
{
if(currentPrice <= virtualStopLoss)
{
shouldClose = true;
reason = "虚拟止损";
}
else if(currentPrice >= virtualTakeProfit)
{
shouldClose = true;
reason = "虚拟止盈";
}
}
else // 做空持仓
{
if(currentPrice >= virtualStopLoss)
{
shouldClose = true;
reason = "虚拟止损";
}
else if(currentPrice <= virtualTakeProfit)
{
shouldClose = true;
reason = "虚拟止盈";
}
}
if(shouldClose)
{
CloseAllPositions();
Print("仓位已由", reason, "平仓,价格: ", currentPrice);
ResetSignalState();
}
}
//+------------------------------------------------------------------+
//| 检查加仓条件 |
//+------------------------------------------------------------------+
void CheckTierEntries()
{
if(!UseThreeTier) return;
if(signalDirection == 0) return;
if(currentTier >= 3) return;
double currentPrice = (signalDirection == 1) ? Bid : Ask;
double priceDiff = MathAbs(currentPrice - weightedAvgPrice) / Point / pointMultiplier;
if(currentTier == 1 && priceDiff >= Tier2Distance)
{
OpenTierEntry(2);
}
else if(currentTier == 2 && priceDiff >= Tier3Distance)
{
OpenTierEntry(3);
}
}
//+------------------------------------------------------------------+
//| 平仓所有持仓 |
//+------------------------------------------------------------------+
void CloseAllPositions()
{
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
double closePrice = (OrderType() == OP_BUY) ? Bid : Ask;
OrderClose(OrderTicket(), OrderLots(), closePrice, Slippage, clrNONE);
}
}
}
}
//+------------------------------------------------------------------+
//| 平仓反向持仓 |
//+------------------------------------------------------------------+
void CloseOppositePositions()
{
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
bool isOpposite = (signalDirection == 1 && OrderType() == OP_SELL) ||
(signalDirection == -1 && OrderType() == OP_BUY);
if(isOpposite)
{
double closePrice = (OrderType() == OP_BUY) ? Bid : Ask;
OrderClose(OrderTicket(), OrderLots(), closePrice, Slippage, clrNONE);
}
}
}
}
}
//+------------------------------------------------------------------+
//| 统计持仓数量 |
//+------------------------------------------------------------------+
int CountPositions()
{
int count = 0;
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
count++;
}
}
return count;
}
//+------------------------------------------------------------------+
//| 重置信号状态 |
//+------------------------------------------------------------------+
void ResetSignalState()
{
signalDirection = 0;
currentTier = 0;
virtualStopLoss = 0;
virtualTakeProfit = 0;
weightedAvgPrice = 0;
totalLots = 0;
}
//+------------------------------------------------------------------+
//| 检查信号是否仍然有效 |
//+------------------------------------------------------------------+
bool IsSignalValid()
{
if(signalCandleTime == 0) return false;
datetime currentCandle = iTime(Symbol(), 0, 0);
int candlesPassed = (int)((currentCandle - signalCandleTime) / PeriodSeconds());
return (candlesPassed <= SignalValidityBars);
}
//+------------------------------------------------------------------+
//| EA报价处理函数 |
//+------------------------------------------------------------------+
void OnTick()
{
// 基础检查
if(!IsSpreadOK()) return;
if(!IsTradingTime()) return;
// 每个tick计算指标
CalculateIndicators();
// 检查现有持仓的虚拟水平
if(CountPositions() > 0)
{
CheckVirtualLevels();
if(UseThreeTier && signalDirection != 0)
CheckTierEntries();
return;
}
// 仅在新K线时检测新信号
if(!IsNewBar()) return;
// 检测新信号
CheckForNewSignal();
// 信号有效时执行初始入场
if(signalDirection != 0 && IsSignalValid())
{
if(CloseOnOpposite)
CloseOppositePositions();
if(CountPositions() == 0)
{
double entryPrice = (signalDirection == 1) ? Ask : Bid;
CalculateVirtualLevels(signalDirection, entryPrice);
OpenTierEntry(1);
}
}
else if(!IsSignalValid() && signalDirection != 0)
{
Print("信号已失效,经过了", SignalValidityBars, "根K线");
ResetSignalState();
}
}
//+------------------------------------------------------------------+
```
参数详解
| 参数 | 说明 | 推荐值 |
|------|------|--------|
| MACD快线周期 | MACD快线EMA周期 | 12 |
| MACD慢线周期 | MACD慢线EMA周期 | 26 |
| MACD信号线周期 | MACD信号线周期 | 9 |
| 趋势EMA周期 | 趋势过滤EMA周期 | 200 |
| 信号有效K线数 | 信号保持有效的K线数量 | 5-10 |
| 启用三级加仓 | 启用/禁用三级分批加仓 | true |
| 第二级距离 | 从平均价到第二级入场的点数 | 20-30 |
| 第三级距离 | 从平均价到第三级入场的点数 | 40-60 |
| 风险百分比 | 每完整系列风险(账户余额%) | 1.0-2.0 |
| 固定手数 | 固定交易手数(RiskPercent=0时) | 0.01-0.1 |
| 盈亏比 | 目标盈利相对于风险的比例 | 1.5-2.0 |
| 基础止损 | 基础止损点数 | 50-80 |
| 最大点差 | 允许的最大点差 | 30-40 |
| 虚拟止损止盈 | 启用虚拟止损止盈管理 | true |
| 虚拟缓冲 | 经纪商止损止盈缓冲点数 | 10-20 |
| 反向平仓 | 新信号时平仓反向持仓 | true |
安装步骤
1. 复制代码到MT4的MetaEditor(按F4)
2. 点击编译(F7)- 确保无错误
3. 将EA附加到图表(推荐EURUSD,M15时间周期)
4. 在输入参数选项卡中调整参数
5. 启用自动交易(Alt+T)
三级加仓系统工作原理
"三试"策略旨在改善平均入场价格:
1. 第一级入场:MACD交叉信号出现时立即开仓
2. 第二级入场:价格从平均入场价有利移动Tier2Distance点数时开仓
3. 第三级入场:价格继续有利移动Tier3Distance点数时开仓
这种金字塔式加仓方法允许EA在盈利持仓上加仓,同时保持基于成交量加权平均入场价的单一止损水平。
虚拟止损止盈系统
本EA的关键特性是两步下单法结合虚拟止损止盈管理:
1. 订单首先以"裸单"形式开仓(无止损止盈),避免OrderSend错误130
2. 然后修改订单添加带缓冲的经纪商级止损作为安全网
3. EA内部追踪精确的虚拟止损止盈水平,触发时主动平仓
这种方法提供更精确的出场控制,并有助于避免经纪商止损猎杀。
编译与修改技巧
主要修改方向:
最佳时间周期和品种:
参考来源
本文EA源码为自主编译,基于Forex Factory论坛2025年5月帖子"EA based on Trading Rush Video"中记载的策略。三级加仓方法和虚拟止损止盈系统改编自该社区资源中讨论的原则。
*如需更专业的优化版EA策略(含多周期分析、AI市场状态检测、完整回测报告和专业技术支持),请查看我们的付费EA合集。订阅后可每周获取更新和独家交易工具。*