Summary: 专业的唐奇安通道突破策略MT4 EA,采用双重确认系统(ATR过滤+EMA趋势)。包含多种出场策略、动态仓位管理和完整的风险控制,可直接编译使用。




# 唐奇安通道突破EA with ATR波动率过滤 - 完整MQL4源码

本文提供一个基于唐奇安通道(Donchian Channel)突破策略的完整自动交易EA。该策略是传奇海龟交易系统(Turtle Trading System)的核心组件,由理查德·丹尼斯和威廉·埃克哈特于1980年代开发,在五年内创造了超过1亿美元的利润,证明了交易可以被系统化教授。

策略逻辑



唐奇安通道是一种基于波动率的技术指标,跟踪指定周期内的最高价和最低价。当价格突破通道上轨时产生做多信号,当价格跌破通道下轨时产生做空信号。

算法工作原理



本EA采用双重确认的入场机制:

1. 突破检测:当价格收盘价突破唐奇安通道边界时触发入场

2. 波动率确认:ATR过滤器确保市场有足够的波动率,过滤低波动环境下的假突破

3. 趋势确认:可选EMA过滤器确保交易方向与更大周期趋势一致

4. 风险管理:固定或ATR止损,配合移动止损和保本止损多种出场策略

完整MQL4代码



```mql4
//+------------------------------------------------------------------+
//| Donchian_Breakout_EA.mq4 |
//| 自主编译 |
//| Based on Turtle Trading System |
//+------------------------------------------------------------------+
#property copyright "AI助手"
#property link ""
#property version "1.00"
#property strict

//--- 输入参数
input double LotSize = 0.1; // 固定手数
input double RiskPercent = 1.5; // 风险百分比(0=使用固定手数)
input int DonchianPeriod = 20; // 唐奇安通道周期
input int ATRPeriod = 14; // ATR周期(波动率过滤)
input double MinATR = 0; // 最小ATR值(点数,0=禁用)
input bool UseTrendFilter = true; // 启用EMA趋势过滤
input int TrendEMAPeriod = 200; // 趋势过滤EMA周期
input int StopLossMethod = 1; // 止损方式: 1=ATR倍数, 2=固定点数
input double ATRMultiplier = 2.0; // ATR倍数(止损距离)
input int FixedStopLoss = 80; // 固定止损点数
input int TakeProfit = 150; // 止盈点数(0=禁用)
input int TrailingStop = 40; // 移动止损点数(0=关闭)
input int BreakEvenTrigger = 30; // 保本触发点数(0=关闭)
input int MaxSpread = 35; // 最大允许点差
input int MaxDailyTrades = 3; // 每日最大交易次数
input int Slippage = 10; // 最大滑点
input int MagicNumber = 202610; // EA魔术号
input bool CloseOpposite = true; // 反向信号时平仓反向单
input bool UseHeikinAshi = false; // 使用Heikin Ashi收盘价入场

//--- 全局变量
double donchianHigh = 0, donchianLow = 0;
double atrValue = 0;
int pointMultiplier = 10;
datetime lastTradeDate = 0;
int tradesToday = 0;
datetime lastSignalTime = 0;

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

// 参数验证
if(DonchianPeriod < 5)
{
Print("错误: 唐奇安周期至少为5");
return(INIT_PARAMETERS_INCORRECT);
}

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

Print("唐奇安突破EA初始化成功");
Print("唐奇安周期: ", DonchianPeriod, " | ATR周期: ", ATRPeriod);

return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| EA反初始化函数 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
Print("唐奇安突破EA已移除");
}

//+------------------------------------------------------------------+
//| 更新每日交易计数 |
//+------------------------------------------------------------------+
void UpdateDailyTradeCount()
{
datetime currentTime = TimeCurrent();
MqlDateTime dt;
TimeToStruct(currentTime, dt);
currentTime = StringToTime(TimeToString(currentTime));

if(lastTradeDate == 0)
{
lastTradeDate = currentTime;
tradesToday = 0;
}
else if(currentTime > lastTradeDate + 86400)
{
lastTradeDate = currentTime;
tradesToday = 0;
}
}

//+------------------------------------------------------------------+
//| 获取收盘价(标准或Heikin Ashi) |
//+------------------------------------------------------------------+
double GetClosePrice(int shift)
{
if(UseHeikinAshi && shift >= 0 && shift < Bars)
{
double haClose = (Open[shift] + High[shift] + Low[shift] + Close[shift]) / 4.0;
return haClose;
}
return Close[shift];
}

//+------------------------------------------------------------------+
//| 计算唐奇安通道上轨 |
//+------------------------------------------------------------------+
double GetDonchianHigh(int period, int shift)
{
if(shift + period >= Bars) return 0;

double highest = -1;
for(int i = shift; i < shift + period; i++)
{
double h = High[i];
if(h > highest) highest = h;
}
return highest;
}

//+------------------------------------------------------------------+
//| 计算唐奇安通道下轨 |
//+------------------------------------------------------------------+
double GetDonchianLow(int period, int shift)
{
if(shift + period >= Bars) return 0;

double lowest = DBL_MAX;
for(int i = shift; i < shift + period; i++)
{
double l = Low[i];
if(l < lowest) lowest = l;
}
return lowest;
}

//+------------------------------------------------------------------+
//| 计算ATR |
//+------------------------------------------------------------------+
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 = High[i];
double low = Low[i];
double prevClose = (i + 1 < Bars) ? Close[i + 1] : Close[i];

double tr = MathMax(high, prevClose) - MathMin(low, prevClose);
sumTR += tr;
}

return sumTR / period;
}

//+------------------------------------------------------------------+
//| 计算基于ATR的止损价格 |
//+------------------------------------------------------------------+
double GetATRStopLoss(int direction, double entryPrice)
{
double atrPoints = atrValue / Point / pointMultiplier;
double stopDistance = atrPoints * ATRMultiplier;

if(direction == OP_BUY)
return entryPrice - stopDistance * Point * pointMultiplier;
else
return entryPrice + stopDistance * Point * pointMultiplier;
}

//+------------------------------------------------------------------+
//| 检查波动率是否足够 |
//+------------------------------------------------------------------+
bool IsVolatilityOK()
{
if(MinATR <= 0) return true;

double atrPoints = atrValue / Point / pointMultiplier;
return (atrPoints >= MinATR);
}

//+------------------------------------------------------------------+
//| 获取趋势方向(EMA过滤器) |
//+------------------------------------------------------------------+
int GetTrendDirection()
{
if(!UseTrendFilter) return 0;

double emaValue = iMA(Symbol(), 0, TrendEMAPeriod, 0, MODE_EMA, PRICE_CLOSE, 0);
double currentClose = GetClosePrice(0);

if(currentClose > emaValue)
return 1; // 看涨
else if(currentClose < emaValue)
return -1; // 看跌

return 0;
}

//+------------------------------------------------------------------+
//| 检查买入突破信号 |
//+------------------------------------------------------------------+
bool IsBuySignal()
{
// 计算指标值
donchianHigh = GetDonchianHigh(DonchianPeriod, 1);
double currentClose = GetClosePrice(0);
double prevClose = GetClosePrice(1);

if(donchianHigh <= 0) return false;

// 突破条件:收盘价突破唐奇安上轨
bool breakout = (prevClose <= donchianHigh && currentClose > donchianHigh);

if(!breakout) return false;

// ATR过滤器
if(!IsVolatilityOK()) return false;

// 趋势过滤器 - 只在上升趋势中做多
if(UseTrendFilter && GetTrendDirection() != 1) return false;

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

lastSignalTime = Time[0];
return true;
}

//+------------------------------------------------------------------+
//| 检查卖出突破信号 |
//+------------------------------------------------------------------+
bool IsSellSignal()
{
// 计算指标值
donchianLow = GetDonchianLow(DonchianPeriod, 1);
double currentClose = GetClosePrice(0);
double prevClose = GetClosePrice(1);

if(donchianLow <= 0) return false;

// 突破条件:收盘价跌破唐奇安下轨
bool breakout = (prevClose >= donchianLow && currentClose < donchianLow);

if(!breakout) return false;

// ATR过滤器
if(!IsVolatilityOK()) return false;

// 趋势过滤器 - 只在下降趋势中做空
if(UseTrendFilter && GetTrendDirection() != -1) return false;

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

lastSignalTime = Time[0];
return true;
}

//+------------------------------------------------------------------+
//| 基于风险百分比计算手数 |
//+------------------------------------------------------------------+
double CalculateLotSize(int direction, double entryPrice)
{
if(RiskPercent <= 0)
return LotSize;

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

// 确定止损距离
double stopDistance = 0;
if(StopLossMethod == 1)
{
double atrPoints = atrValue / Point / pointMultiplier;
stopDistance = atrPoints * ATRMultiplier;
}
else
{
stopDistance = FixedStopLoss;
}

if(stopDistance <= 0) return LotSize;

double tickValue = MarketInfo(Symbol(), MODE_TICKVALUE);
double lotStep = MarketInfo(Symbol(), MODE_LOTSTEP);

if(tickValue <= 0 || lotStep <= 0) return LotSize;

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 OpenOrder(int cmd)
{
double price = (cmd == OP_BUY) ? Ask : Bid;
double sl = 0, tp = 0;

// 计算止损
if(StopLossMethod == 1)
{
sl = GetATRStopLoss(cmd, price);
}
else if(FixedStopLoss > 0)
{
if(cmd == OP_BUY)
sl = price - FixedStopLoss * Point * pointMultiplier;
else
sl = price + FixedStopLoss * Point * pointMultiplier;
}

// 计算止盈
if(TakeProfit > 0)
{
if(cmd == OP_BUY)
tp = price + TakeProfit * Point * pointMultiplier;
else
tp = price - TakeProfit * Point * pointMultiplier;
}

double lot = CalculateLotSize(cmd, price);

int ticket = OrderSend(Symbol(), cmd, lot, price, Slippage, sl, tp, "Donchian EA", MagicNumber, 0, clrNONE);

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

//+------------------------------------------------------------------+
//| 平仓所有持仓 |
//+------------------------------------------------------------------+
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(int newDirection)
{
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
bool isOpposite = (newDirection == OP_BUY && OrderType() == OP_SELL) ||
(newDirection == OP_SELL && OrderType() == OP_BUY);

if(isOpposite)
{
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;
}

//+------------------------------------------------------------------+
//| 管理移动止损 |
//+------------------------------------------------------------------+
void ManageTrailingStop()
{
if(TrailingStop <= 0) return;

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

//+------------------------------------------------------------------+
//| 管理保本止损 |
//+------------------------------------------------------------------+
void ManageBreakeven()
{
if(BreakEvenTrigger <= 0) return;

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

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

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

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

//+------------------------------------------------------------------+
//| 检查每日交易限制 |
//+------------------------------------------------------------------+
bool IsDailyLimitOK()
{
return (tradesToday < MaxDailyTrades);
}

//+------------------------------------------------------------------+
//| 更新买单移动止损 |
//+------------------------------------------------------------------+
void UpdateBuyTrailingStop()
{
if(TrailingStop <= 0) return;

for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber && OrderType() == OP_BUY)
{
double trailPoints = TrailingStop * Point * pointMultiplier;
double newSL = Bid - trailPoints;

if(newSL > OrderStopLoss() && newSL > OrderOpenPrice())
{
OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0, clrNONE);
}
}
}
}
}

//+------------------------------------------------------------------+
//| 更新卖单移动止损 |
//+------------------------------------------------------------------+
void UpdateSellTrailingStop()
{
if(TrailingStop <= 0) return;

for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber && OrderType() == OP_SELL)
{
double trailPoints = TrailingStop * Point * pointMultiplier;
double newSL = Ask + trailPoints;

if((newSL < OrderStopLoss() || OrderStopLoss() == 0) && newSL < OrderOpenPrice())
{
OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0, clrNONE);
}
}
}
}
}

//+------------------------------------------------------------------+
//| EA报价处理函数 |
//+------------------------------------------------------------------+
void OnTick()
{
// 更新每日交易计数
UpdateDailyTradeCount();

// 点差检查
if(!IsSpreadOK())
return;

// 更新指标
atrValue = CalculateATR(ATRPeriod, 0);

// 管理现有持仓
if(TrailingStop > 0)
{
UpdateBuyTrailingStop();
UpdateSellTrailingStop();
}
ManageBreakeven();

// 仅在新K线时检测信号
static datetime lastBarTime = 0;
datetime currentBarTime = Time[0];

if(currentBarTime == lastBarTime)
return;
lastBarTime = currentBarTime;

// 检查每日限制
if(!IsDailyLimitOK())
return;

// 检查买入信号
if(IsBuySignal())
{
if(CloseOpposite)
CloseOppositePositions(OP_BUY);

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

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

参数详解



| 参数 | 说明 | 推荐值 |
|------|------|--------|
| 唐奇安周期 | 通道最高最低价回看周期 | 20(经典),55(长线) |
| ATR周期 | 波动率计算周期 | 14 |
| 最小ATR | 最小ATR阈值(点数,0=禁用) | 10-20 |
| 使用趋势过滤 | 启用EMA200趋势过滤 | true |
| 止损方式 | 1=ATR倍数,2=固定点数 | 1 |
| ATR倍数 | ATR倍数(止损距离) | 2.0 |
| 固定止损点数 | 固定止损点数(方式2) | 60-100 |
| 止盈点数 | 固定止盈点数(0=禁用) | 0或150-200 |
| 移动止损点数 | 追踪止损距离 | 30-50 |
| 保本触发点数 | 移动止损到开仓价的触发点数 | 20-40 |
| 每日最大交易次数 | 每天最多开仓次数 | 1-3 |
| 最大点差 | 允许的最大点差 | 30-40 |
| 反向平仓 | 反向信号时平掉反向持仓 | true |
| 使用Heikin Ashi | 平滑入场确认 | false |

安装步骤



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

各时间周期推荐设置



| 时间周期 | 唐奇安周期 | ATR倍数 | 移动止损 |
|----------|------------|---------|----------|
| M15(超短) | 10-20 | 1.5 | 15-25 |
| H1(日内) | 20 | 2.0 | 30-40 |
| H4(波段) | 20-55 | 2.5 | 50-70 |
| D1(长线) | 55 | 2.5-3.0 | 70-100 |

编译与修改技巧



主要修改方向:

  • 根据时间周期调整DonchianPeriod:日内交易使用20,波段交易使用55

  • 在波动剧烈的市场中增加ATRMultiplier至2.5以获得更大喘息空间

  • 设置UseTrendFilter为true可只在EMA200方向交易

  • 启用UseHeikinAshi可获得更平滑的价格走势,减少假突破


  • 最佳市场环境:

    本突破策略在具有持续方向性走势的趋势市场中表现最佳。在低波动率盘整阶段或重大新闻事件期间应避免使用。

    参考来源



    本文EA源码为自主编译,基于市场文献中记载的海龟交易系统原理。原始唐奇安通道突破方法由理查德·丹尼斯和威廉·埃克哈特在1980年代推广,MT4实现遵循标准MQL4编程规范。

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