Summary: 完整的传奇海龟交易系统MT4 EA实现,包含20/55日唐奇安通道突破、ATR动态头寸计算、金字塔加仓和反马丁格尔资金管理。完整源码带参数详解。




海龟交易系统EA - 完整MQL4源码(含ATR仓位管理)



本文提供一个完整的传奇海龟交易系统EA源码。该策略由Richard Dennis和William Eckhardt于1980年代开创,原始海龟交易员在培训期间创造了超过1亿美元的利润,至今仍是趋势跟踪策略的黄金标准。

策略概述



海龟交易系统是纯粹的趋势跟踪策略,包含两个子系统:系统1(20日突破)和系统2(55日突破)。EA使用唐奇安通道作为入场信号,ATR(平均真实波幅)计算仓位大小,并融入反马丁格尔原则——仅在盈利交易后增加仓位规模。

核心组件



  • <strong>唐奇安通道突破</strong>:价格突破N周期最高点时入场

  • <strong>ATR仓位计算</strong>:风险 = 账户1% / (ATR × 每点价值)

  • <strong>金字塔加仓</strong>:每方向最多4次递增入场

  • <strong>反马丁格尔</strong>:基于盈利的仓位缩放

  • <strong>波动率止损</strong>:初始止损2倍ATR,通道跟踪止损


  • 完整MQL4代码



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

    //--- 输入参数 - 系统选择
    input int SystemChoice = 2; // 1=系统1(20日), 2=系统2(55日)
    input int CustomBreakout = 0; // 自定义突破周期(0=使用系统默认)
    input int CustomExit = 0; // 自定义离场周期(0=使用系统默认)

    //--- 仓位管理
    input double RiskPercent = 1.0; // 每笔风险占账户百分比
    input double ATRMultiplier = 2.0; // 初始止损ATR倍数
    input double MaxRiskPerUnit = 0.02; // 每单位最大风险(2%)
    input double MaxDrawdownPercent = 30.0; // 最大回撤限制(紧急停止)

    //--- 金字塔加仓设置
    input int MaxAdds = 3; // 最大金字塔加仓次数(1-4)
    input double AddDistance = 0.5; // 金字塔加仓间隔(ATR倍数)

    //--- 反马丁格尔
    input bool UseAntiMartingale = true; // 盈利后增加仓位
    input int WinStreakRequired = 2; // 需要连续盈利次数
    input double ScaleUpFactor = 0.25; // 仓位增加系数(25%)

    //--- 追踪止损参数
    input bool UseChandelierExit = true; // 使用通道离场(10日低点/高点)
    input int ChandelierPeriod = 10; // 离场通道周期

    //--- 风控保护
    input int MaxSpread = 35; // 最大允许点差
    input int Slippage = 10; // 最大滑点
    input int MagicNumber = 198312; // EA魔术号(海龟年份)
    input string TradeComment = "Turtle v2";

    //--- 全局变量
    int breakoutPeriod = 20;
    int exitPeriod = 10;
    double unitSize = 0;
    double currentATR = 0;
    int consecutiveWins = 0;
    int entriesThisSignal = 0;
    datetime lastSignalTime = 0;
    double lastTradeProfit = 0;
    int pointMultiplier = 10;

    //--- 交易记录结构体
    struct TradeRecord {
    int ticket;
    datetime openTime;
    double openPrice;
    double unitNumber;
    double stopLoss;
    };
    TradeRecord activeTrades[];

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

    // 根据选择设置系统参数
    if(CustomBreakout > 0) {
    breakoutPeriod = CustomBreakout;
    } else if(SystemChoice == 1) {
    breakoutPeriod = 20; // 系统1: 20日突破
    exitPeriod = 10; // 系统1: 10日离场
    } else {
    breakoutPeriod = 55; // 系统2: 55日突破
    exitPeriod = 20; // 系统2: 20日离场
    }

    if(CustomExit > 0) exitPeriod = CustomExit;

    // 参数验证
    if(breakoutPeriod < 10) {
    Print("错误: 突破周期至少为10");
    return(INIT_PARAMETERS_INCORRECT);
    }

    Print("海龟系统EA初始化成功");
    Print("突破周期: ", breakoutPeriod, " | 离场周期: ", exitPeriod);
    Print("每笔风险: ", RiskPercent, "% | 最大加仓: ", MaxAdds);

    ArrayResize(activeTrades, 0);

    return(INIT_SUCCEEDED);
    }

    //+------------------------------------------------------------------+
    //| EA反初始化函数 |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
    {
    Print("海龟系统EA已移除");
    }

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

    //+------------------------------------------------------------------+
    //| 计算唐奇安通道高点 |
    //+------------------------------------------------------------------+
    double GetDonchianHigh(int period, int shift)
    {
    double highest = 0;
    for(int i = shift; i < shift + period; i++) {
    double high = iHigh(Symbol(), 0, i);
    if(high > highest || highest == 0)
    highest = high;
    }
    return highest;
    }

    //+------------------------------------------------------------------+
    //| 计算唐奇安通道低点 |
    //+------------------------------------------------------------------+
    double GetDonchianLow(int period, int shift)
    {
    double lowest = DBL_MAX;
    for(int i = shift; i < shift + period; i++) {
    double low = iLow(Symbol(), 0, i);
    if(low < lowest)
    lowest = low;
    }
    return lowest;
    }

    //+------------------------------------------------------------------+
    //| 计算ATR(标准算法) |
    //+------------------------------------------------------------------+
    double CalculateATR(int period, int shift)
    {
    if(period < 1 || 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;
    }

    //+------------------------------------------------------------------+
    //| 使用海龟公式计算头寸单位 |
    //| 单位 = 账户1% / (ATR × 每点美元价值) |
    //+------------------------------------------------------------------+
    double CalculateUnitSize()
    {
    currentATR = CalculateATR(14, 0);
    if(currentATR <= 0) return 0.01;

    double accountEquity = AccountBalance();
    double riskAmount = accountEquity RiskPercent / 100.0;
    double tickValue = MarketInfo(Symbol(), MODE_TICKVALUE);
    double tickSize = MarketInfo(Symbol(), MODE_TICKSIZE);

    if(tickValue <= 0 || tickSize <= 0) return 0.01;

    // 将ATR转换为货币价值
    double atrPoints = currentATR / Point / pointMultiplier;
    double dollarRiskPerUnit = atrPoints
    tickValue;

    if(dollarRiskPerUnit <= 0) return 0.01;

    double calculatedUnit = riskAmount / dollarRiskPerUnit;

    // 按步长取整
    double lotStep = MarketInfo(Symbol(), MODE_LOTSTEP);
    if(lotStep > 0) {
    calculatedUnit = MathFloor(calculatedUnit / lotStep) lotStep;
    }

    // 应用反马丁格尔缩放
    if(UseAntiMartingale && consecutiveWins >= WinStreakRequired) {
    double scaleBonus = 1.0 + (consecutiveWins - WinStreakRequired + 1)
    ScaleUpFactor;
    calculatedUnit = calculatedUnit MathMin(scaleBonus, 2.0); // 上限2倍
    }

    // 最小/最大手数验证
    double minLot = MarketInfo(Symbol(), MODE_MINLOT);
    double maxLot = MarketInfo(Symbol(), MODE_MAXLOT);
    if(calculatedUnit < minLot) calculatedUnit = minLot;
    if(calculatedUnit > maxLot) calculatedUnit = maxLot;

    return NormalizeDouble(calculatedUnit, 2);
    }

    //+------------------------------------------------------------------+
    //| 检查突破买入信号 |
    //+------------------------------------------------------------------+
    bool IsBreakoutBuy()
    {
    double currentHigh = iHigh(Symbol(), 0, 1); // 前一根K线高点
    double breakoutLevel = GetDonchianHigh(breakoutPeriod, 1); // N周期高点(不含当前)

    if(breakoutLevel <= 0) return false;

    bool breakout = (currentHigh > breakoutLevel);

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

    return breakout;
    }

    //+------------------------------------------------------------------+
    //| 检查突破卖出信号 |
    //+------------------------------------------------------------------+
    bool IsBreakoutSell()
    {
    double currentLow = iLow(Symbol(), 0, 1); // 前一根K线低点
    double breakoutLevel = GetDonchianLow(breakoutPeriod, 1); // N周期低点(不含当前)

    if(breakoutLevel >= DBL_MAX - 1) return false;

    bool breakout = (currentLow < breakoutLevel);

    if(breakout && Time[0] == lastSignalTime) return false;
    if(breakout) lastSignalTime = Time[0];

    return breakout;
    }

    //+------------------------------------------------------------------+
    //| 检查离场信号 - 突破N日低点/高点 |
    //+------------------------------------------------------------------+
    bool ShouldExitLong()
    {
    if(!UseChandelierExit) return false;

    double currentLow = iLow(Symbol(), 0, 1);
    double exitLevel = GetDonchianLow(exitPeriod, 1);

    return (currentLow < exitLevel);
    }

    bool ShouldExitShort()
    {
    if(!UseChandelierExit) return false;

    double currentHigh = iHigh(Symbol(), 0, 1);
    double exitLevel = GetDonchianHigh(exitPeriod, 1);

    return (currentHigh > exitLevel);
    }

    //+------------------------------------------------------------------+
    //| 计算金字塔加仓价格 |
    //| 每盈利0.5倍ATR加仓一次 |
    //+------------------------------------------------------------------+
    double GetPyramidBuyPrice(double entryPrice)
    {
    double addDistancePoints = AddDistance
    currentATR / Point / pointMultiplier;
    double nextLevel = entryPrice + addDistancePoints Point pointMultiplier;
    return nextLevel;
    }

    double GetPyramidSellPrice(double entryPrice)
    {
    double addDistancePoints = AddDistance currentATR / Point / pointMultiplier;
    double nextLevel = entryPrice - addDistancePoints
    Point pointMultiplier;
    return nextLevel;
    }

    //+------------------------------------------------------------------+
    //| 计算初始止损(2倍ATR) |
    //+------------------------------------------------------------------+
    double GetInitialStopLoss(int cmd, double entryPrice)
    {
    double stopDistance = ATRMultiplier
    currentATR;
    if(cmd == OP_BUY)
    return entryPrice - stopDistance;
    else
    return entryPrice + stopDistance;
    }

    //+------------------------------------------------------------------+
    //| 开仓函数 |
    //+------------------------------------------------------------------+
    int OpenOrder(int cmd, double sl = 0, double tp = 0)
    {
    double price = (cmd == OP_BUY) ? Ask : Bid;
    double lot = CalculateUnitSize();

    if(lot <= 0) return -1;

    // 自动计算止损
    if(sl == 0 && currentATR > 0) {
    sl = GetInitialStopLoss(cmd, price);
    }

    // 价格标准化
    sl = NormalizeDouble(sl, Digits);
    if(tp > 0) tp = NormalizeDouble(tp, Digits);
    price = NormalizeDouble(price, Digits);

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

    if(ticket > 0) {
    Print("开仓成功: 订单号=", ticket, " | 方向=", (cmd==OP_BUY?"买入":"卖出"), " | 手数=", lot);

    // 记录交易用于金字塔追踪
    int newSize = ArraySize(activeTrades);
    ArrayResize(activeTrades, newSize + 1);
    activeTrades[newSize].ticket = ticket;
    activeTrades[newSize].openTime = TimeCurrent();
    activeTrades[newSize].openPrice = price;
    activeTrades[newSize].unitNumber = entriesThisSignal + 1;
    activeTrades[newSize].stopLoss = sl;

    entriesThisSignal++;
    } else {
    Print("开仓失败. 错误码: ", GetLastError());
    }

    return ticket;
    }

    //+------------------------------------------------------------------+
    //| 平仓指定订单 |
    //+------------------------------------------------------------------+
    bool CloseOrder(int ticket)
    {
    if(OrderSelect(ticket, SELECT_BY_TICKET)) {
    double closePrice = (OrderType() == OP_BUY) ? Bid : Ask;
    bool closed = OrderClose(ticket, OrderLots(), closePrice, Slippage, clrNONE);
    if(closed) {
    // 追踪盈利用于反马丁格尔
    double profit = (closePrice - OrderOpenPrice()) OrderLots() MarketInfo(Symbol(), MODE_TICKVALUE);
    if(profit > 0) consecutiveWins++;
    else consecutiveWins = 0;

    Print("平仓成功: 订单号=", ticket, " | 盈亏=", profit);
    }
    return closed;
    }
    return false;
    }

    //+------------------------------------------------------------------+
    //| 平仓所有持仓 |
    //+------------------------------------------------------------------+
    void CloseAllPositions()
    {
    for(int i = OrdersTotal() - 1; i >= 0; i--) {
    if(OrderSelect(i, SELECT_BY_POS)) {
    if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber) {
    CloseOrder(OrderTicket());
    }
    }
    }
    entriesThisSignal = 0;
    ArrayResize(activeTrades, 0);
    }

    //+------------------------------------------------------------------+
    //| 平仓指定方向的所有持仓 |
    //+------------------------------------------------------------------+
    void ClosePositionsByType(int cmd)
    {
    for(int i = OrdersTotal() - 1; i >= 0; i--) {
    if(OrderSelect(i, SELECT_BY_POS)) {
    if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber && OrderType() == cmd) {
    CloseOrder(OrderTicket());
    }
    }
    }
    if((cmd == OP_BUY && CountPositions(OP_BUY) == 0) ||
    (cmd == OP_SELL && CountPositions(OP_SELL) == 0)) {
    entriesThisSignal = 0;
    ArrayResize(activeTrades, 0);
    }
    }

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

    //+------------------------------------------------------------------+
    //| 基于唐奇安通道更新追踪止损 |
    //+------------------------------------------------------------------+
    void UpdateTrailingStops()
    {
    if(!UseChandelierExit) return;

    double exitLevelLong = GetDonchianLow(exitPeriod, 1);
    double exitLevelShort = GetDonchianHigh(exitPeriod, 1);

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

    if(OrderType() == OP_BUY) {
    if(exitLevelLong > OrderStopLoss() && exitLevelLong < Bid) {
    newSL = exitLevelLong;
    }
    }
    else if(OrderType() == OP_SELL) {
    if(exitLevelShort < OrderStopLoss() && exitLevelShort > Ask) {
    newSL = exitLevelShort;
    }
    }

    if(newSL != 0 && newSL != OrderStopLoss()) {
    OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0, clrNONE);
    Print("追踪止损已更新: 订单号=", OrderTicket(), " | 新止损=", newSL);
    }
    }
    }
    }
    }

    //+------------------------------------------------------------------+
    //| 检查金字塔加仓机会 |
    //+------------------------------------------------------------------+
    void CheckPyramidEntries()
    {
    if(entriesThisSignal >= MaxAdds) return;
    if(currentATR <= 0) return;

    int currentDirection = 0;
    double avgPrice = 0;
    int positionCount = 0;

    // 确定当前持仓方向和均价
    for(int i = 0; i < OrdersTotal(); i++) {
    if(OrderSelect(i, SELECT_BY_POS)) {
    if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber) {
    if(OrderType() == OP_BUY) {
    currentDirection = 1;
    avgPrice += OrderOpenPrice() OrderLots();
    positionCount += OrderLots();
    }
    else if(OrderType() == OP_SELL) {
    currentDirection = -1;
    avgPrice += OrderOpenPrice()
    OrderLots();
    positionCount += OrderLots();
    }
    }
    }
    }

    if(positionCount > 0) avgPrice = avgPrice / positionCount;

    // 检查加仓条件
    if(currentDirection == 1) {
    double targetPrice = GetPyramidBuyPrice(avgPrice);
    if(Ask >= targetPrice && CountPositions(OP_BUY) < MaxAdds) {
    Print("金字塔买入加仓触发: 等级 ", entriesThisSignal + 1);
    OpenOrder(OP_BUY);
    }
    }
    else if(currentDirection == -1) {
    double targetPrice = GetPyramidSellPrice(avgPrice);
    if(Bid <= targetPrice && CountPositions(OP_SELL) < MaxAdds) {
    Print("金字塔卖出加仓触发: 等级 ", entriesThisSignal + 1);
    OpenOrder(OP_SELL);
    }
    }
    }

    //+------------------------------------------------------------------+
    //| 检查最大回撤保护 |
    //+------------------------------------------------------------------+
    bool IsDrawdownExceeded()
    {
    double equity = AccountEquity();
    double balance = AccountBalance();
    if(balance <= 0) return false;

    double drawdownPercent = (balance - equity) / balance * 100;
    return (drawdownPercent >= MaxDrawdownPercent);
    }

    //+------------------------------------------------------------------+
    //| 检查点差是否可接受 |
    //+------------------------------------------------------------------+
    bool IsSpreadOK()
    {
    if(MaxSpread <= 0) return true;
    int currentSpread = (int)((Ask - Bid) / Point / pointMultiplier);
    return (currentSpread <= MaxSpread);
    }

    //+------------------------------------------------------------------+
    //| 更新ATR值 |
    //+------------------------------------------------------------------+
    void UpdateATR()
    {
    currentATR = CalculateATR(14, 0);
    }

    //+------------------------------------------------------------------+
    //| 交易前检查 |
    //+------------------------------------------------------------------+
    bool CanTrade()
    {
    if(!IsSpreadOK()) return false;
    if(IsDrawdownExceeded()) {
    Print("最大回撤超限,交易暂停");
    return false;
    }
    return true;
    }

    //+------------------------------------------------------------------+
    //| EA报价处理函数 |
    //+------------------------------------------------------------------+
    void OnTick()
    {
    // 更新技术指标
    UpdateATR();

    // 安全检查
    if(!CanTrade()) return;

    // 更新现有持仓的追踪止损
    if(UseChandelierExit) {
    UpdateTrailingStops();
    }

    // 仅在新K线时检测新信号
    if(!IsNewBar()) {
    // 每tick检查金字塔加仓
    if(MaxAdds > 0 && entriesThisSignal > 0 && entriesThisSignal < MaxAdds) {
    CheckPyramidEntries();
    }
    return;
    }

    // 先检查离场信号
    if(ShouldExitLong() && CountPositions(OP_BUY) > 0) {
    Print("离场信号: 平仓所有多头");
    ClosePositionsByType(OP_BUY);
    entriesThisSignal = 0;
    }

    if(ShouldExitShort() && CountPositions(OP_SELL) > 0) {
    Print("离场信号: 平仓所有空头");
    ClosePositionsByType(OP_SELL);
    entriesThisSignal = 0;
    }

    // 检查突破入场信号
    if(IsBreakoutBuy() && CountPositions(OP_SELL) == 0) {
    Print("检测到突破买入信号");
    ClosePositionsByType(OP_SELL);
    OpenOrder(OP_BUY);
    }
    else if(IsBreakoutSell() && CountPositions(OP_BUY) == 0) {
    Print("检测到突破卖出信号");
    ClosePositionsByType(OP_BUY);
    OpenOrder(OP_SELL);
    }
    }
    //+------------------------------------------------------------------+
    ``

    参数详解



    | 参数 | 说明 | 海龟原始值 |
    |------|------|-----------|
    | 系统选择 | 1=系统1(20日), 2=系统2(55日) | 1或2 |
    | 自定义突破周期 | 覆盖突破周期(0=使用默认) | 0 |
    | 风险百分比 | 每笔风险占账户百分比 | 1.0-2.0% |
    | ATR倍数 | 初始止损的ATR倍数 | 2.0 |
    | 最大加仓 | 每信号最多金字塔加仓次数 | 4 |
    | 加仓间隔 | 金字塔加仓的ATR倍数间隔 | 0.5 |
    | 使用反马丁格尔 | 盈利后增加仓位规模 | true |
    | 连盈要求 | 增加仓位前需要的连续盈利次数 | 2 |
    | 仓位增加系数 | 仓位规模增加比例 | 0.25 |
    | 使用通道离场 | 通道突破时离场 | true |
    | 离场通道周期 | 离场通道周期(系统1=10,系统2=20) | 10/20 |
    | 最大回撤限制 | 紧急停止的最大回撤 | 30 |

    安装步骤



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

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

  • 将EA附加到图表(建议H1或H4周期)

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

  • 启用自动交易(Alt+T)


  • 各品种推荐设置



    | 品种 | 系统 | 风险% | 时间周期 | 说明 |
    |------|------|-------|----------|------|
    | EURUSD, GBPUSD | 系统2(55日) | 1.0 | H1/H4 | 低波动,趋势稳定 |
    | 黄金(XAUUSD) | 系统1(20日) | 1.5 | H1 | 高波动,需要更快入场 |
    | GBPJPY, EURJPY | 系统1(20日) | 1.0 | H1 | 强趋势,适合加仓 |
    | USDJPY | 系统2(55日) | 1.0 | H4 | 慢趋势,假突破少 |

    编译与修改技巧



    理解海龟规则:
  • 原始海龟使用"每笔风险1%"——手数 = 账户1% / (2 ATR × 每点美元价值)

  • 金字塔加仓在盈利0.5倍ATR时添加仓位(最多4个)

  • 离场信号:价格突破10日低点(系统1)或20日低点(系统2)


  • 主要修改方向:
  • 根据账户大小调整RiskPercent(趋势跟踪建议1-2%)

  • 将MaxAdds增加到4可获得完整的海龟金字塔效果

  • 设置UseChandelierExit = false可使用固定ATR止损代替

  • 降低AddDistance到0.25倍ATR可获得更激进的加仓


  • 最佳市场环境:
    海龟系统专为具有持续方向性走势的趋势市场设计。在盘整/震荡市场中表现较差。建议在较高时间周期(H1-H4)上使用以获得最佳效果。

    参考来源



    本文EA源码为自主编译,基于Richard Dennis和William Eckhardt记载的原始海龟交易系统规则。参考了MQL5及其他交易平台开源仓库中的相关实现。

    如需更专业的优化版EA策略(含机器学习过滤、20+品种完整回测组合、高级风控和专业支持),请查看我们的付费EA合集。订阅后可每周获取更新和独家交易工具。