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




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



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

策略逻辑



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

算法工作原理



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

  • <strong>突破检测</strong>:当价格收盘价突破唐奇安通道边界时触发入场


  • <strong>波动率确认</strong>:ATR过滤器确保市场有足够的波动率,过滤低波动环境下的假突破


  • <strong>趋势确认</strong>:可选EMA过滤器确保交易方向与更大周期趋势一致


  • <strong>风险管理</strong>:固定或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 |

    安装步骤



  • 复制代码到MT4的MetaEditor(按F4)

  • 点击编译(F7)- 确保无错误

  • 将EA附加到图表(建议EURUSD、GBPUSD、XAUUSD)

  • 在输入参数选项卡中调整参数

  • 启用自动交易(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合集。订阅后可每周获取更新和独家交易工具。