# 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的动态仓位计算确保在不同市场波动率条件下风险一致。
编译与修改技巧
主要修改方向:
最佳市场环境
本策略在具有持续方向性走势且波动率适中的市场中表现尤为出色。适用于:
应避免在以下环境中使用:
参考来源
本文EA源码为自主编译,基于类似“2018ATR-Trailer趋势突破EA”和专业EA合集中的ATR追踪止损概念。该策略结合了基于ATR的追踪止损与EMA趋势过滤,以增强信号质量。
*如需更专业的优化版EA策略(含多周期分析、AI市场状态检测、10年以上完整回测报告和专业技术支持),请查看我们的付费EA合集。订阅后可每周获取更新和独家交易工具。*