Summary: 专业的ATR追踪止损突破MT4 EA,结合200周期EMA趋势确认。基于ATR波动率的动态仓位计算、保本止损管理、点差控制和警报系统。完整源码可直接编译。




# ATR追踪止损突破EA带EMA趋势过滤 - 完整MQL4源码

本文提供一个基于ATR追踪止损突破策略并配合EMA趋势过滤的完整自动交易EA。这种方法与专业EA合集(如“2018ATR-Trailer趋势突破EA”)中的概念类似,专注于捕捉强势方向性运动,同时使用波动率调整的止损来保护利润。

策略逻辑



该策略结合了三个核心组件:

1. ATR追踪止损机制:EA计算基于ATR的追踪止损,以动态距离跟随价格。当价格在趋势方向上收盘突破追踪止损线时,产生突破信号。

2. EMA趋势过滤(200周期):为避免在盘整市场中产生假突破信号,EA只接受与长期趋势方向一致的信号。价格在EMA200上方时只允许做多信号;价格在EMA200下方时只允许做空信号。

3. 波动率自适应仓位管理:基于当前ATR波动率计算仓位大小,确保在不同市场条件下风险暴露一致。

该策略在黄金(XAUUSD)等趋势性强的品种以及高波动时段的主要货币对上效果尤为显著。

完整MQL4代码



```mql4
//+------------------------------------------------------------------+
//| ATR_Trailing_Breakout_EA.mq4 |
//| 自主编译 |
//| Based on ATR Trailer |
//+------------------------------------------------------------------+
#property copyright "AI助手"
#property link ""
#property version "1.00"
#property strict

//--- 输入参数
input double RiskPercent = 1.5; // 每笔风险百分比(0=使用固定手数)
input double FixedLotSize = 0.1; // 固定手数(RiskPercent=0时使用)
input int ATRPeriod = 14; // ATR周期(追踪止损)
input double ATRMultiplier = 3.0; // ATR倍数(止损距离)
input int TrendEMAPeriod = 200; // EMA趋势过滤周期
input int BreakEvenPips = 40; // 保本触发点数(0=关闭)
input double RiskRewardRatio = 2.0; // 盈亏比(止盈/止损)
input int MaxSpread = 35; // 最大允许点差
input int Slippage = 10; // 最大滑点
input int MagicNumber = 202409; // EA魔术号
input bool UseAlert = true; // 启用弹窗提醒
input string AlertMessage = "ATR Breakout Signal"; // 提醒文本

//--- 全局变量
int pointMultiplier = 10;
double atrValue = 0;
double emaTrend = 0;
bool isNewBar = false;
datetime lastBarTime = 0;
double lastSignalPrice = 0;
datetime lastSignalTime = 0;

//+------------------------------------------------------------------+
//| EA初始化函数 |
//+------------------------------------------------------------------+
int OnInit()
{
// 检测平台报价格式
if(Digits == 3 || Digits == 5)
pointMultiplier = 10;
else if(Digits == 2 || Digits == 4)
pointMultiplier = 1;

// 参数验证
if(ATRPeriod < 2)
{
Print("错误: ATR周期至少为2");
return(INIT_PARAMETERS_INCORRECT);
}

if(ATRMultiplier <= 0)
{
Print("错误: ATR倍数必须大于0");
return(INIT_PARAMETERS_INCORRECT);
}

Print("ATR追踪突破EA初始化成功");
Print("平台位数: ", Digits, " | 点数乘数: ", pointMultiplier);
Print("ATR周期: ", ATRPeriod, " | 倍数: ", ATRMultiplier);

return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| EA反初始化函数 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
Print("ATR追踪突破EA已移除. 原因: ", reason);
}

//+------------------------------------------------------------------+
//| 检测新K线 |
//+------------------------------------------------------------------+
bool CheckNewBar()
{
datetime currentBarTime = iTime(Symbol(), 0, 0);
if(currentBarTime != lastBarTime)
{
lastBarTime = currentBarTime;
return true;
}
return false;
}

//+------------------------------------------------------------------+
//| 计算ATR(Wilder平滑) |
//+------------------------------------------------------------------+
double CalculateATR(int period, int shift)
{
if(period < 2 || shift + period >= Bars)
return 0;

double sumTR = 0;
for(int i = shift; i < shift + period; i++)
{
double high = iHigh(Symbol(), 0, i);
double low = iLow(Symbol(), 0, i);
double prevClose = iClose(Symbol(), 0, i + 1);
double tr = MathMax(high, prevClose) - MathMin(low, prevClose);
sumTR += tr;
}

return sumTR / period;
}

//+------------------------------------------------------------------+
//| 使用ATR计算追踪止损水平 |
//+------------------------------------------------------------------+
double CalculateTrailingStop()
{
atrValue = CalculateATR(ATRPeriod, 1);
if(atrValue <= 0)
return 0;

double stopDistance = atrValue * ATRMultiplier;
double currentClose = Close[1]; // 使用已收盘的K线

// 基础追踪止损计算
double trailingStop = currentClose - stopDistance;

return trailingStop;
}

//+------------------------------------------------------------------+
//| 从EMA过滤器获取趋势方向 |
//+------------------------------------------------------------------+
int GetTrendDirection()
{
emaTrend = iMA(Symbol(), 0, TrendEMAPeriod, 0, MODE_EMA, PRICE_CLOSE, 0);
double prevEMA = iMA(Symbol(), 0, TrendEMAPeriod, 0, MODE_EMA, PRICE_CLOSE, 1);

if(Close[0] > emaTrend && Close[1] > prevEMA)
return 1; // 上升趋势
else if(Close[0] < emaTrend && Close[1] < prevEMA)
return -1; // 下降趋势

return 0; // 中性/盘整
}

//+------------------------------------------------------------------+
//| 检查买入突破信号 |
//+------------------------------------------------------------------+
bool IsBuySignal()
{
int trend = GetTrendDirection();
if(trend != 1)
return false;

double trailingStop = CalculateTrailingStop();
if(trailingStop <= 0)
return false;

// 买入信号:上升趋势中价格向上突破追踪止损
double prevClose = Close[1];

bool signal = (prevClose > trailingStop && Low[0] <= trailingStop);

// 防止同一K线重复信号
if(signal && Time[0] == lastSignalTime)
return false;

if(signal)
{
lastSignalTime = Time[0];
lastSignalPrice = Close[0];
Print("检测到买入信号于 K线 ", Time[0]);
}

return signal;
}

//+------------------------------------------------------------------+
//| 检查卖出突破信号 |
//+------------------------------------------------------------------+
bool IsSellSignal()
{
int trend = GetTrendDirection();
if(trend != -1)
return false;

double trailingStop = CalculateTrailingStop();
if(trailingStop <= 0)
return false;

// 卖出信号:下降趋势中价格向下突破追踪止损
double prevClose = Close[1];

bool signal = (prevClose < trailingStop && High[0] >= trailingStop);

// 防止重复信号
if(signal && Time[0] == lastSignalTime)
return false;

if(signal)
{
lastSignalTime = Time[0];
lastSignalPrice = Close[0];
Print("检测到卖出信号于 K线 ", Time[0]);
}

return signal;
}

//+------------------------------------------------------------------+
//| 基于ATR波动率计算动态手数 |
//+------------------------------------------------------------------+
double CalculateLotSize()
{
if(RiskPercent <= 0)
return FixedLotSize;

double accountBalance = AccountBalance();
double riskAmount = accountBalance * RiskPercent / 100.0;

// 使用ATR作为动态止损距离
atrValue = CalculateATR(ATRPeriod, 0);
double stopDistance = atrValue / Point / pointMultiplier;
if(stopDistance < 20)
stopDistance = 20; // 最小止损点数

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);
}

//+------------------------------------------------------------------+
//| 基于盈亏比计算止盈 |
//+------------------------------------------------------------------+
double CalculateTakeProfit(int cmd, double entryPrice, double stopLoss)
{
if(RiskRewardRatio <= 0)
return 0;

double stopDistance = MathAbs(entryPrice - stopLoss);
double tpDistance = stopDistance * RiskRewardRatio;

if(cmd == OP_BUY)
return entryPrice + tpDistance;
else
return entryPrice - tpDistance;
}

//+------------------------------------------------------------------+
//| 开仓函数 |
//+------------------------------------------------------------------+
void OpenOrder(int cmd)
{
double price = (cmd == OP_BUY) ? Ask : Bid;
double lot = CalculateLotSize();

// 使用ATR追踪止损计算止损
double stopPrice = 0;
double atrStop = CalculateTrailingStop();

if(atrStop > 0)
{
if(cmd == OP_BUY)
stopPrice = atrStop;
else
stopPrice = atrStop;
}
else
{
// 如果ATR计算失败,使用固定止损
double fixedStop = 50 * Point * pointMultiplier;
if(cmd == OP_BUY)
stopPrice = price - fixedStop;
else
stopPrice = price + fixedStop;
}

double takeProfit = CalculateTakeProfit(cmd, price, stopPrice);

int ticket = OrderSend(Symbol(), cmd, lot, price, Slippage, stopPrice, takeProfit, "ATR Breakout EA", MagicNumber, 0, clrNONE);

if(ticket < 0)
{
Print("开仓失败. 错误码: ", GetLastError());
}
else
{
Print("开仓成功. 订单号: ", ticket);
Print(" 方向: ", cmd == OP_BUY ? "做多" : "做空");
Print(" 手数: ", lot);
Print(" 开仓价: ", price);
Print(" 止损: ", stopPrice);
Print(" 止盈: ", takeProfit);

if(UseAlert)
{
Alert(AlertMessage, ": ", cmd == OP_BUY ? "做多" : "做空", " 在 ", Symbol());
SendNotification("ATR Breakout EA: " + (cmd == OP_BUY ? "做多" : "做空") + "信号在 " + Symbol());
}
}
}

//+------------------------------------------------------------------+
//| 管理现有持仓的保本止损 |
//+------------------------------------------------------------------+
void ManageBreakeven()
{
if(BreakEvenPips <= 0)
return;

for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
double breakevenPoints = BreakEvenPips * Point * pointMultiplier;

if(OrderType() == OP_BUY)
{
double profitPips = (Bid - OrderOpenPrice()) / Point / pointMultiplier;
if(profitPips >= BreakEvenPips && OrderStopLoss() < OrderOpenPrice())
{
bool modified = OrderModify(OrderTicket(), OrderOpenPrice(), OrderOpenPrice(), OrderTakeProfit(), 0, clrNONE);
if(modified)
Print("保本止损已触发 买单 #", OrderTicket());
}
}
else if(OrderType() == OP_SELL)
{
double profitPips = (OrderOpenPrice() - Ask) / Point / pointMultiplier;
if(profitPips >= BreakEvenPips && (OrderStopLoss() > OrderOpenPrice() || OrderStopLoss() == 0))
{
bool modified = OrderModify(OrderTicket(), OrderOpenPrice(), OrderOpenPrice(), OrderTakeProfit(), 0, clrNONE);
if(modified)
Print("保本止损已触发 卖单 #", OrderTicket());
}
}
}
}
}
}

//+------------------------------------------------------------------+
//| 更新现有持仓的追踪止损(基于ATR) |
//+------------------------------------------------------------------+
void UpdateTrailingStop()
{
double currentTrailingStop = CalculateTrailingStop();
if(currentTrailingStop <= 0)
return;

for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
double newSL = 0;

if(OrderType() == OP_BUY)
{
newSL = currentTrailingStop;
if(newSL > OrderStopLoss() && newSL < Bid)
{
OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0, clrNONE);
Print("ATR追踪止损已更新 买单 #", OrderTicket(), " 至 ", newSL);
}
}
else if(OrderType() == OP_SELL)
{
newSL = currentTrailingStop;
if((newSL < OrderStopLoss() || OrderStopLoss() == 0) && newSL > Ask)
{
OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0, clrNONE);
Print("ATR追踪止损已更新 卖单 #", OrderTicket(), " 至 ", newSL);
}
}
}
}
}
}

//+------------------------------------------------------------------+
//| 平仓所有持仓 |
//+------------------------------------------------------------------+
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);
}
}
}
}

//+------------------------------------------------------------------+
//| 统计持仓数量 |
//+------------------------------------------------------------------+
int CountPositions(int type = -1)
{
int count = 0;
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
if(type == -1 || OrderType() == type)
count++;
}
}
}
return count;
}

//+------------------------------------------------------------------+
//| 检查点差是否在限制范围内 |
//+------------------------------------------------------------------+
bool IsSpreadOK()
{
if(MaxSpread <= 0)
return true;

int currentSpread = (int)((Ask - Bid) / Point / pointMultiplier);
return (currentSpread <= MaxSpread);
}

//+------------------------------------------------------------------+
//| EA报价处理函数 |
//+------------------------------------------------------------------+
void OnTick()
{
// 点差检查
if(!IsSpreadOK())
return;

// 更新现有持仓的追踪止损
UpdateTrailingStop();

// 保本管理
ManageBreakeven();

// 仅在新K线时检测信号
if(!CheckNewBar())
return;

// 检查买入信号
if(IsBuySignal())
{
if(CountPositions(OP_SELL) > 0)
CloseAllPositions();

if(CountPositions(OP_BUY) == 0)
OpenOrder(OP_BUY);
}
// 检查卖出信号
else if(IsSellSignal())
{
if(CountPositions(OP_BUY) > 0)
CloseAllPositions();

if(CountPositions(OP_SELL) == 0)
OpenOrder(OP_SELL);
}
}
//+------------------------------------------------------------------+
```

参数详解



| 参数 | 说明 | 推荐值 |
|------|------|--------|
| 每笔风险百分比 | 每笔交易风险占账户余额的百分比 | 1.0-2.5 |
| 固定手数 | 固定交易手数(RiskPercent=0时使用) | 0.01-0.1 |
| ATR周期 | ATR计算周期 | 14(标准) |
| ATR倍数 | 止损距离的ATR倍数 | 黄金:2.5-3.5,外汇:2.0-3.0 |
| 趋势EMA周期 | 趋势过滤EMA周期 | 200 |
| 保本点数 | 移动止损到开仓价的触发点数 | 30-50 |
| 盈亏比 | 止盈相对于止损的倍数 | 1.5-3.0 |
| 最大点差 | 允许的最大点差 | 30-40 |
| 魔术号 | EA唯一标识 | 任意不重复数字 |

各品种推荐设置



| 品种 | ATR倍数 | 盈亏比 | ATR周期 | 时间周期 |
|------|---------|--------|---------|----------|
| XAUUSD(黄金) | 3.0-3.5 | 2.0-2.5 | 14 | H1 |
| EURUSD | 2.0-2.5 | 2.0 | 14 | H1 |
| GBPUSD | 2.5-3.0 | 2.0 | 14 | H1 |
| BTCUSD | 3.0-4.0 | 1.5-2.0 | 14 | H4 |

安装步骤



1. 复制代码到MT4的MetaEditor(按F4)
2. 点击编译(F7)- 确保无错误
3. 将EA附加到图表(推荐XAUUSD或EURUSD,H1时间周期)
4. 在输入参数选项卡中调整参数
5. 启用自动交易(Alt+T)

策略优势



与传统的基于指标的系统相比,本EA具有以下几个独特优势:

1. ATR自适应:追踪止损在高波动率时自动放宽,在低波动率时自动收紧,适应市场条件。

2. 趋势确认:EMA200过滤器防止逆势入场,显著减少回撤。

3. 风险一致性:基于ATR的动态仓位计算确保在不同市场波动率条件下风险一致。

编译与修改技巧



主要修改方向:

  • 将`ATRMultiplier`调整为2.0可获得更紧的止损(更多信号),或调整为4.0获得更宽的止损(更少但质量更高的信号)

  • 修改`TrendEMAPeriod`为50可获得更快的趋势响应,或100获得中周期趋势

  • 设置`BreakEvenPips`为0可禁用保本功能

  • 将`RiskRewardRatio`改为1.5可获得更频繁的止盈


  • 最佳市场环境



    本策略在具有持续方向性走势且波动率适中的市场中表现尤为出色。适用于:

  • 具有明确方向的趋势市场

  • 高波动时段(伦敦/纽约重叠时间)

  • 黄金和主要货币对等趋势特征强的品种


  • 应避免在以下环境中使用:
  • 低波动率的盘整市场

  • 重大新闻发布期间(初始波动阶段)

  • 流动性薄弱的节假日时段


  • 参考来源



    本文EA源码为自主编译,基于类似“2018ATR-Trailer趋势突破EA”和专业EA合集中的ATR追踪止损概念。该策略结合了基于ATR的追踪止损与EMA趋势过滤,以增强信号质量。

    *如需更专业的优化版EA策略(含多周期分析、AI市场状态检测、10年以上完整回测报告和专业技术支持),请查看我们的付费EA合集。订阅后可每周获取更新和独家交易工具。*