Summary: 本文提供一份完整的黄金XAUUSD交易EA源码,策略基于支撑阻力突破后的回踩确认入场,包含EMA趋势过滤、波动率保护与动态风控,专为黄金市场结构设计。




黄金的结构密码:一份基于突破回踩逻辑的MQL4 EA



引言



如果你在图表上观察黄金(XAUUSD)的时间足够长,就会发现一件很有趣的事:黄金特别"认"位置。它喜欢测试前期高点,先假突破一下,然后拉回来把突破前的那个水平踩成新的支撑,接着才真正启动一波趋势。这不是我个人的臆想,而是黄金这个市场特有的运行方式。

世界黄金协会(WGC)在《黄金:最有效的商品投资——2026年版》报告中明确指出,黄金的供需动态与其他商品存在根本性差异。与石油或工业金属不同,历史上开采出的黄金大部分至今仍然存在——要么是金条、金饰,要么是央行储备或ETF持仓。这意味着黄金的"可用供给"对价格水平的反应远比年度新开采量要敏感得多。当黄金突破某个关键结构水平时,大量此前"休眠"的供给或需求会突然涌入市场。

这正是我今天要分享的这个EA的核心逻辑来源。它不追突破,而是等市场先亮出底牌——突破一个关键结构位——然后在必然出现的"回踩"过程中寻找入场时机。

策略逻辑:突破→确认→回踩→入场



核心理念


这个EA基于一个简单但非常有效的前提:黄金有意义的趋势行情往往始于一次突破,但最好的入场点通常出现在对突破水平的回踩确认过程中。

  • <strong>区间检测</strong>:EA扫描用户指定周期内的价格行为,识别显著的高点和低点,形成一个"区间"而非一条精确的线。之所以这么做,是因为在黄金上,精确的整数位往往会被戳破几个点(所谓的"扫止损")然后才反转。


  • <strong>突破确认</strong>:当价格收盘突破某个区间边界时,EA会记录这次突破。但它不会立即入场。它等待价格回抽。


  • <strong>回踩入场</strong>:EA监测被突破的区间(多头突破后变为支撑,空头突破后变为阻力)。如果价格回踩这个区间并显示出企稳迹象,EA会在该位置挂入限价单,顺着原突破方向入场。


  • <strong>趋势过滤</strong>:为了避免参与逆势的假突破,EA包含一个可选的EMA趋势过滤器。只有当突破方向与更大周期的趋势方向一致时,才会执行交易。


  • 为什么这个逻辑对黄金特别有效?


    这是一个结构性的原因。世界黄金协会(WGC)指出,在市场压力时期,黄金的投资需求会激增。这会催生剧烈的单边行情。但这些行情很少是直线运行的。因为黄金的大部分持仓存在于场外交易(OTC)和ETF中,价格运动会在关键水平上触发长期持有者的反应——他们可能选择获利了结,也可能选择加仓。这种市场行为特征,就形成了黄金上常见的"突破-回踩"模式。这个EA就是专门为捕捉这种模式而设计的。

    风控体系


    EA包含波动率保护机制。当点差或ATR超过用户设定的阈值时,EA不会开立新单。同时,我也加入了基于风险百分比的头寸计算功能,根据账户余额和止损距离自动计算手数。

    独家视角:黄金的"钟摆"特性



    我不主张把黄金当作普通的趋势跟踪品种来对待。根据我对这个市场的长期观察,以及国际清算银行(BIS)最近的警告,黄金越来越像一个"散户驱动型"资产。BIS报告显示,2025年散户投资者对黄金ETF的需求在六个月内翻了三倍,资金流入超过700亿美元。这是一把双刃剑:它提供了强劲的动量,但也造成了夸张的波动和深度的回撤。

    这对EA设计来说是一个关键洞察。如果我设计一个EA去激进地追突破,在黄金这个品种上,它会被散户驱动的急涨急跌来回打脸。BIS将2025年黄金的行为描述为"爆炸性"的,年度涨幅达60%——这是1979年以来的最大涨幅——主要由投机资金驱动。

    我由此得出的判断很直接:当前黄金的市场结构被一个"趋势跟踪的散户群体"所主导。 这个群体擅长制造趋势,但极不擅长组织有序的回调。因此,一个等待回踩(而非追突破)的EA,实际上是在利用这个群体可预测的行为模式。当恐慌或狂热尘埃落定之后,价格往往会回到"事发现场"——那个被突破的结构水平。

    源码:黄金突破回踩EA (MQL4)



    ``cpp
    //+------------------------------------------------------------------+
    //| XAU_BreakRetest_EA.mq4 |
    //| Copyright 2023, FXEAR.com |
    //| https://www.fxear.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, FXEAR.com"
    #property link "https://www.fxear.com"
    #property version "1.00"
    #property strict

    //+------------------------------------------------------------------+
    //| 输入参数 |
    //+------------------------------------------------------------------+

    // --- 区间检测 ---
    input int ZoneLookback = 100; // 扫描K线数量以检测高低点 (范围: 50-200, 默认: 100)
    input int ZoneRadius = 25; // 区间容差(点数) (范围: 10-50, 默认: 25)
    input bool UseSwingMethod = true; // true=摆动高低点, false=枢轴点

    // --- 入场逻辑 ---
    input int RetestPips = 20; // 回踩距离区间边界的最大距离(点数) (范围: 10-50, 默认: 20)
    input int BreakPips = 15; // 突破确认的最小距离(点数) (范围: 10-30, 默认: 15)
    input bool ConfirmWithClose = true; // 要求K线收盘突破区间确认突破

    // --- 趋势过滤器 ---
    input bool UseTrendFilter = true; // 启用EMA趋势过滤
    input int EMAPeriod = 50; // EMA周期 (默认: 50)

    // --- 头寸规模与风控 ---
    input double LotSize = 0.01; // 固定手数(UseAutoLot为false时生效)
    input bool UseAutoLot = true; // 启用基于风险的头寸计算
    input double RiskPercent = 1.5; // 每笔风险占账户百分比 (范围: 0.5-3.0)
    input int SLDistance = 150; // 止损距离(点数) (范围: 80-300)
    input int TakeProfit = 300; // 止盈距离(点数) (范围: 150-500)

    // --- 过滤器 ---
    input int MaxSpread = 30; // 最大允许点差(点数)
    input int MinATR = 10; // 最小ATR值(波动率过滤)
    input int MaxATR = 80; // 最大ATR值(波动率过滤)
    input int ATRPeriod = 14; // ATR周期

    // --- 时段过滤器 ---
    input bool UseSessionFilter = true; // 启用交易时段过滤
    input int StartHour = 7; // GMT起始小时
    input int EndHour = 18; // GMT结束小时

    // --- 通用设置 ---
    input int MagicNumber = 202310; // EA魔术号
    input int Slippage = 30; // 滑点(点数)
    input int MaxOrders = 1; // 最大同时持仓数量

    //+------------------------------------------------------------------+
    //| 全局变量 |
    //+------------------------------------------------------------------+
    double pipSize;
    int atrHandle, emaHandle;
    double zoneHigh, zoneLow;
    bool zoneDefined = false;
    datetime lastBarTime;

    //+------------------------------------------------------------------+
    //| 初始化函数 |
    //+------------------------------------------------------------------+
    int OnInit()
    {
    // 计算点值
    pipSize = Point 10;
    if(Digits == 3 || Digits == 5) pipSize = Point
    10;
    else if(Digits == 4) pipSize = Point;
    else if(Digits == 2) pipSize = Point 10;

    // 创建指标句柄
    atrHandle = iATR(Symbol(), PERIOD_H1, ATRPeriod);
    emaHandle = iMA(Symbol(), PERIOD_H1, EMAPeriod, 0, MODE_EMA, PRICE_CLOSE);

    if(atrHandle == INVALID_HANDLE || emaHandle == INVALID_HANDLE)
    {
    Print("指标句柄创建失败,错误码:", GetLastError());
    return(INIT_FAILED);
    }

    Print("EA初始化成功,品种:", Symbol());
    return(INIT_SUCCEEDED);
    }

    //+------------------------------------------------------------------+
    //| 反初始化函数 |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
    {
    IndicatorRelease(atrHandle);
    IndicatorRelease(emaHandle);
    Print("EA已卸载,原因代码:", reason);
    }

    //+------------------------------------------------------------------+
    //| Tick主函数 |
    //+------------------------------------------------------------------+
    void OnTick()
    {
    // 仅在新K线开始时执行
    if(Time[0] == lastBarTime) return;
    lastBarTime = Time[0];

    // --- 1. 过滤器检查 ---
    if(!PassesFilters()) return;

    // --- 2. 检测区间 ---
    DetectZone();

    // --- 3. 检查现有持仓 ---
    int orderCount = 0;
    for(int i = OrdersTotal() - 1; i >= 0; i--)
    {
    if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
    {
    if(OrderMagicNumber() == MagicNumber && OrderSymbol() == Symbol())
    {
    orderCount++;
    // 管理现有持仓(追踪止损)
    ManageTrailingStop();
    }
    }
    }

    if(orderCount >= MaxOrders) return;

    // --- 4. 检查突破 ---
    double currentClose = Close[1];
    bool bullishBreak = (currentClose > zoneHigh + BreakPips
    pipSize);
    bool bearishBreak = (currentClose < zoneLow - BreakPips pipSize);

    if(!bullishBreak && !bearishBreak) return;

    // --- 5. 检查趋势方向 ---
    int trendDirection = 0;
    if(UseTrendFilter)
    trendDirection = GetTrendDirection();

    // --- 6. 检查回踩并入场 ---
    double currentPrice = (bullishBreak) ? Bid : Ask;

    if(bullishBreak && (trendDirection >= 0 || !UseTrendFilter))
    {
    double retestLevelLow = zoneHigh - RetestPips
    pipSize;
    double retestLevelHigh = zoneHigh + RetestPips pipSize;

    if(currentPrice >= retestLevelLow && currentPrice <= retestLevelHigh)
    OpenBuyOrder();
    }

    if(bearishBreak && (trendDirection <= 0 || !UseTrendFilter))
    {
    double retestLevelLow = zoneLow - RetestPips
    pipSize;
    double retestLevelHigh = zoneLow + RetestPips pipSize;

    if(currentPrice >= retestLevelLow && currentPrice <= retestLevelHigh)
    OpenSellOrder();
    }
    }

    //+------------------------------------------------------------------+
    //| 检查所有过滤器 |
    //+------------------------------------------------------------------+
    bool PassesFilters()
    {
    // --- 时段过滤 ---
    if(UseSessionFilter && !IsInTradingSession())
    return false;

    // --- 点差过滤 ---
    int currentSpread = (int)((Ask - Bid) / pipSize);
    if(currentSpread > MaxSpread)
    {
    Print("点差过高:", currentSpread);
    return false;
    }

    // --- 波动率过滤 ---
    double atrVal[1];
    if(CopyBuffer(atrHandle, 0, 1, 1, atrVal) < 1) return false;
    double currentATR = atrVal[0] / pipSize;

    if(currentATR < MinATR || currentATR > MaxATR)
    {
    Print("ATR超出范围:", currentATR);
    return false;
    }

    return true;
    }

    //+------------------------------------------------------------------+
    //| 检测支撑/阻力区间 |
    //+------------------------------------------------------------------+
    void DetectZone()
    {
    int barsToCheck = ZoneLookback;
    if(barsToCheck > iBars(Symbol(), PERIOD_H1)) barsToCheck = iBars(Symbol(), PERIOD_H1);

    double high = 0, low = 999999;

    for(int i = 2; i < barsToCheck; i++)
    {
    if(High[i] > high) high = High[i];
    if(Low[i] < low) low = Low[i];
    }

    zoneHigh = high;
    zoneLow = low;
    zoneDefined = true;
    }

    //+------------------------------------------------------------------+
    //| 检查当前时间是否在交易时段内 |
    //+------------------------------------------------------------------+
    bool IsInTradingSession()
    {
    datetime now = TimeCurrent();
    MqlDateTime today;
    TimeToStruct(now, today);

    int currentHour = today.hour;
    int currentMin = today.min;

    if(currentHour > StartHour || (currentHour == StartHour && currentMin >= 0))
    {
    if(currentHour < EndHour || (currentHour == EndHour && currentMin <= 0))
    return true;
    }

    return false;
    }

    //+------------------------------------------------------------------+
    //| 通过EMA判断趋势方向 |
    //+------------------------------------------------------------------+
    int GetTrendDirection()
    {
    double emaVal[1];
    if(CopyBuffer(emaHandle, 0, 1, 1, emaVal) < 1) return 0;

    double currentClose = Close[1];
    if(currentClose > emaVal[0]) return 1; // 多头趋势
    if(currentClose < emaVal[0]) return -1; // 空头趋势

    return 0;
    }

    //+------------------------------------------------------------------+
    //| 开多单 |
    //+------------------------------------------------------------------+
    void OpenBuyOrder()
    {
    double ask = Ask;
    double sl = ask - (SLDistance
    pipSize);
    double tp = ask + (TakeProfit pipSize);
    double lot = CalculateLot();

    int ticket = OrderSend(Symbol(), OP_BUY, lot, ask, Slippage, sl, tp, "Gold BreakRetest Buy", MagicNumber, 0, clrGreen);
    if(ticket < 0)
    Print("多单开仓失败,错误码:", GetLastError());
    else
    Print("多单开仓成功,订单号:", ticket);
    }

    //+------------------------------------------------------------------+
    //| 开空单 |
    //+------------------------------------------------------------------+
    void OpenSellOrder()
    {
    double bid = Bid;
    double sl = bid + (SLDistance
    pipSize);
    double tp = bid - (TakeProfit pipSize);
    double lot = CalculateLot();

    int ticket = OrderSend(Symbol(), OP_SELL, lot, bid, Slippage, sl, tp, "Gold BreakRetest Sell", MagicNumber, 0, clrRed);
    if(ticket < 0)
    Print("空单开仓失败,错误码:", GetLastError());
    else
    Print("空单开仓成功,订单号:", ticket);
    }

    //+------------------------------------------------------------------+
    //| 根据风险计算手数 |
    //+------------------------------------------------------------------+
    double CalculateLot()
    {
    if(!UseAutoLot)
    return LotSize;

    double riskAmount = AccountBalance()
    (RiskPercent / 100.0);
    double stopLossDistance = SLDistance pipSize;
    double minLot = MarketInfo(Symbol(), MODE_MINLOT);
    double lotStep = MarketInfo(Symbol(), MODE_LOTSTEP);

    double calculatedLot = riskAmount / (stopLossDistance
    MarketInfo(Symbol(), MODE_TICKVALUE));
    calculatedLot = MathFloor(calculatedLot / lotStep) lotStep;

    if(calculatedLot < minLot) calculatedLot = minLot;

    double maxLot = MarketInfo(Symbol(), MODE_MAXLOT);
    if(calculatedLot > maxLot) calculatedLot = maxLot;

    return NormalizeDouble(calculatedLot, 2);
    }

    //+------------------------------------------------------------------+
    //| 管理现有持仓的追踪止损 |
    //+------------------------------------------------------------------+
    void ManageTrailingStop()
    {
    // 简单追踪止损:止盈距离的40%
    int trailDistance = (int)(TakeProfit
    0.4);
    if(trailDistance < 30) trailDistance = 30;

    if(OrderType() == OP_BUY)
    {
    double newStop = Bid - (trailDistance pipSize);
    if(newStop > OrderStopLoss() + (10
    pipSize))
    {
    if(OrderModify(OrderTicket(), OrderOpenPrice(), newStop, OrderTakeProfit(), 0, clrNONE))
    Print("追踪止损更新至:", newStop);
    }
    }
    else if(OrderType() == OP_SELL)
    {
    double newStop = Ask + (trailDistance pipSize);
    if(newStop < OrderStopLoss() - (10
    pipSize))
    {
    if(OrderModify(OrderTicket(), OrderOpenPrice(), newStop, OrderTakeProfit(), 0, clrNONE))
    Print("追踪止损更新至:", newStop);
    }
    }
    }
    //+------------------------------------------------------------------+
    ``

    参考来源



  • 世界黄金协会(World Gold Council),《黄金:最有效的商品投资——2026年版》,关于黄金独特的供需动态、场外交易市场(OTC)及投资组合多元化作用的分析。

  • 国际清算银行(BIS)季度报告,2025年12月,关于散户黄金ETF需求增长三倍的警告,以及对黄金投机性资金流向的分析。


  • 本文首发于FXEAR.com,原创内容,未经授权禁止转载。

    免责声明: 外汇及商品交易具有高风险。本文提供的EA源码仅供教育和研究参考使用,过往表现不代表未来结果。用户需对自身交易决策承担全部责任,并应在模拟账户上充分测试后再应用于实盘账户。