Summary: 一份可直接编译的MQL4黄金EA源码。针对XAUUSD的高波动与点差敏感特性,融入ATR波动率过滤与亚洲时段交易逻辑,并引用了BIS关于黄金市场波动与CME关于亚洲时段流动性的研究。




XAUUSD黄金EA:基于波动率过滤与亚洲时段特性的MQL4源码



开发一款真正适合黄金(XAUUSD)的EA,不能简单套用直盘或交叉盘货币对的策略。黄金市场有着自己独特的“脾气”:它对宏观事件高度敏感,对美元走势反应剧烈,并且其24小时交易在不同时段呈现截然不同的流动性与波动特征。以下这份MQL4源码,是我基于对黄金市场微观结构的理解编写而成的,力求在捕捉趋势与控制风险之间找到一个务实的平衡点。

EA策略说明:为什么这样设计?



这套策略的核心思路并不复杂:主要采用双均线交叉捕捉趋势,但用ATR(平均真实波幅)作为核心过滤器。它不是一个试图抓住所有行情的“全能型”机器人,而是希望在有流动性、有方向的市场中稳健运行。

加载周期推荐:M15(15分钟图)

理由很简单:M15的时间框架既能过滤掉M1和M5上由于报价质量或流动性不足产生的“噪音”,又比H1等更大级别更能捕捉到日内的交易机会。许多开发者在测试黄金剥头皮EA时,都发现M1级别在回测中因报价质量问题导致结果失真,而M15的信号相对更可靠。

独家视角:基于BIS研究的“波动率陷阱”过滤



我在这款EA中加入了波动率过高则暂停开仓的过滤条件,这并非一个常见的风控手段,而是我基于国际清算银行(BIS) 最新研究得出的独立判断。

BIS的《季度评论》明确指出,当前黄金市场的波动已不仅仅由基本面驱动,散户通过杠杆ETF的参与以及随之而来的保证金追缴,正在成为价格剧烈波动的核心放大器。报告中提到,杠杆ETF的每日再平衡机制会形成“强化既有趋势,并可能扭曲价格表现”的反馈回路。

我的理解是:当ATR(平均真实波幅)急剧飙升时,很大概率并非趋势的确认,而是这种杠杆化资金流冲击造成的“噪音”。此时盲目追随均线交叉信号入场,极易被随后的大幅震荡止损出局。因此,我设计的EA会计算当前的ATR,如果它相对于前一周期的ATR过高(超出设定的倍数),则拒绝开仓。这个逻辑并非传统的“回避风险”,而是“回避由市场结构缺陷带来的无效波动”。

亚洲时段的“战略聚焦”



另一个重要设计是“亚洲时段交易开关”。根据芝商所(CME Group)发布的数据,黄金期货在亚洲交易时段(约北京时间6:00-18:00)的流动性占比已从历史平均的25%提升至三分之一以上,在微型黄金期货中甚至占到了42%。这说明亚洲时段不再是“交易荒漠”,而是有着充沛流动性的重要窗口。该时段内,点差通常更低,价格走势也更倾向于反映该地区的实际供需,这对EA的运行是有利的。

MQL4源码



``cpp
//+------------------------------------------------------------------+
//| Gold_EA_XAUUSD |
//| Version 1.0 (Compile-ready MQL4) |
//| Based on BIS & World Gold Council Data |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, FXEAR.com"
#property link "https://www.fxear.com"
#property version "1.00"
#property strict

//+------------------------------------------------------------------+
//| 外部输入参数(带详细注释) |
//+------------------------------------------------------------------+

// --- 风险与资金管理 ---
input double RiskPercent = 1.5; // 每笔交易风险(账户余额百分比)
// 取值范围:0.1 - 5.0,默认值:1.5
// 根据止损距离自动计算手数。

input double MaxLotSize = 1.0; // 最大允许开仓手数
// 取值范围:0.01 - 10.0,默认值:1.0
// 防止单笔交易仓位过重。

input int MagicNumber = 20260626; // EA的魔术号码
// 取值范围:任意整数,默认值:20260626
// 用于识别本EA开立的订单。

input int Slippage = 30; // 允许的最大滑点(点数)
// 取值范围:10 - 100,默认值:30
// 防止在快速行情中因滑点导致成交价偏离。

// --- 交易逻辑过滤器 ---
input int FastMAPeriod = 8; // 快线移动平均线周期
// 取值范围:3 - 20,默认值:8
// 均线交叉策略中的快线参数。

input int SlowMAPeriod = 21; // 慢线移动平均线周期
// 取值范围:13 - 50,默认值:21
// 均线交叉策略中的慢线参数。

input int ATRPeriod = 14; // ATR指标周期
// 取值范围:7 - 21,默认值:14
// 用于评估市场波动率。

input double ATRMultiplierSL = 1.5; // 止损的ATR倍数
// 取值范围:1.0 - 3.0,默认值:1.5
// 根据当前ATR设定止损距离。

input double ATRMultiplierTP = 2.0; // 止盈的ATR倍数
// 取值范围:1.5 - 4.0,默认值:2.0
// 设定一个约1:1.3的风险回报比。

input double MaxATRFilter = 3.0; // 最大波动率过滤(ATR倍数)
// 取值范围:1.0 - 5.0,默认值:3.0
// 当ATR超过此阈值时,不开新仓。
// 这是我基于BIS报告设计的关键过滤器。

// --- 点差与时段控制 ---
input int MaxSpreadPoints = 50; // 允许的最大点差(点数)
// 取值范围:10 - 100,默认值:50
// 避免在点差扩大时入场。

input bool UseAsianSession = true; // 是否限制在亚洲时段交易?
// 取值范围:True/False,默认值:True
// 在流动性更好的窗口交易。

input int SessionStartHour = 6; // 亚洲时段开始(经纪商时间)
// 取值范围:0 - 23,默认值:6(对应新加坡时间8点)

input int SessionEndHour = 17; // 亚洲时段结束(经纪商时间)
// 取值范围:0 - 23,默认值:17(对应新加坡时间19点)

// --- 移动止损 ---
input bool UseTrailing = true; // 是否启用移动止损
// 取值范围:True/False,默认值:True
// 在行情有利时锁定利润。

input double TrailStart = 0.6; // 移动止损启动阈值(ATR倍数)
// 取值范围:0.3 - 1.0,默认值:0.6
// 盈利达到该倍数ATR时启动移动止损。

input double TrailDistance = 0.3; // 移动止损距离(ATR倍数)
// 取值范围:0.2 - 0.8,默认值:0.3

//+------------------------------------------------------------------+
//| 全局变量 |
//+------------------------------------------------------------------+
double currentATR;
int tradeTicket;
bool isTradingAllowed;

//+------------------------------------------------------------------+
//| EA初始化函数 |
//+------------------------------------------------------------------+
int OnInit()
{
// 验证ATR指标是否可用
if(iATR(_Symbol, PERIOD_CURRENT, ATRPeriod, 0) == 0)
{
Print("ATR初始化失败,请检查品种和周期。");
return(INIT_FAILED);
}

Print("XAUUSD黄金EA初始化成功。");
Print("策略设计参考了BIS及芝商所关于黄金市场结构的研究。");
return(INIT_SUCCEEDED);
}

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

//+------------------------------------------------------------------+
//| EA核心Tick函数 |
//+------------------------------------------------------------------+
void OnTick()
{
// 刷新报价数据
RefreshRates();

// --- 跳过交易的核心条件 ---
if(!IsNewBar()) return; // 等待新K线,避免重复入场。
if(!IsTradingTimeAllowed()) return; // 检查是否在允许的交易时段。
if(IsSpreadTooHigh()) return; // 检查点差是否过高。
if(IsVolatilityTooHigh()) return; // 波动率过高则不开仓(独家过滤逻辑)。

// 检查是否已有持仓,避免重复开单
if(CountOpenPositions() > 0)
{
ManageTrailingStop(); // 对已有仓位管理移动止损。
return; // 已有仓位则退出,等待平仓信号。
}

// --- 交易信号逻辑 ---
double fastMA = iMA(_Symbol, PERIOD_CURRENT, FastMAPeriod, 0, MODE_EMA, PRICE_CLOSE, 1);
double slowMA = iMA(_Symbol, PERIOD_CURRENT, SlowMAPeriod, 0, MODE_EMA, PRICE_CLOSE, 1);
double prevFast = iMA(_Symbol, PERIOD_CURRENT, FastMAPeriod, 0, MODE_EMA, PRICE_CLOSE, 2);
double prevSlow = iMA(_Symbol, PERIOD_CURRENT, SlowMAPeriod, 0, MODE_EMA, PRICE_CLOSE, 2);

if(fastMA == 0 || slowMA == 0) return; // 数据安全校验

// --- 开多条件:金叉(快线上穿慢线) ---
if(prevFast <= prevSlow && fastMA > slowMA)
{
OpenBuyOrder();
}
// --- 开空条件:死叉(快线下穿慢线) ---
else if(prevFast >= prevSlow && fastMA < slowMA)
{
OpenSellOrder();
}
}

//+------------------------------------------------------------------+
//| 信号与过滤器函数 |
//+------------------------------------------------------------------+

// 独家见解:波动率过滤器
// 基于BIS对散户杠杆资金放大黄金波动的研究,我的判断是:
// 当ATR异常飙升时,往往是杠杆资金冲击造成的“噪音”而非有效趋势。
// 此过滤器旨在避开这种由市场结构问题导致的无效波动。
bool IsVolatilityTooHigh()
{
// 计算当前周期的ATR值
currentATR = iATR(_Symbol, PERIOD_CURRENT, ATRPeriod, 0);

// 如果当前ATR大于上一周期的MaxATRFilter倍,则暂停交易。
// 例如:若ATR=500,MaxATRFilter=3.0,则价格波动超过1500点时不开仓。
if(currentATR > (MaxATRFilter iATR(_Symbol, PERIOD_CURRENT, ATRPeriod, 1)))
return true;

return false;
}

bool IsSpreadTooHigh()
{
int spread = int((Ask - Bid) / Point);
if(spread >= MaxSpreadPoints) return true;
return false;
}

bool IsTradingTimeAllowed()
{
if(!UseAsianSession) return true;
datetime currentTime = TimeCurrent();
int currentHour = TimeHour(currentTime);

// 聚焦于流动性较好的亚洲交易窗口
if(currentHour >= SessionStartHour && currentHour < SessionEndHour)
return true;

return false;
}

bool IsNewBar()
{
static datetime lastBarTime = 0;
datetime currentBarTime = iTime(_Symbol, PERIOD_CURRENT, 0);
if(currentBarTime != lastBarTime)
{
lastBarTime = currentBarTime;
return true;
}
return false;
}

//+------------------------------------------------------------------+
//| 交易执行函数 |
//+------------------------------------------------------------------+

void OpenBuyOrder()
{
double slDistance = currentATR
ATRMultiplierSL;
double tpDistance = currentATR ATRMultiplierTP;
double slPrice = NormalizeDouble(Ask - slDistance, _Digits);
double tpPrice = NormalizeDouble(Ask + tpDistance, _Digits);

double lotSize = CalculateLotSize(slDistance);

// 检查手数是否有效且在限制范围内
if(lotSize <= 0 || lotSize > MaxLotSize) return;

tradeTicket = OrderSend(Symbol(), OP_BUY, lotSize, Ask, Slippage, slPrice, tpPrice, "Gold Buy", MagicNumber, 0, Green);
if(tradeTicket > 0)
Print("BUY订单开仓成功。订单号:", tradeTicket);
else
Print("BUY订单开仓失败。错误代码:", GetLastError());
}

void OpenSellOrder()
{
double slDistance = currentATR
ATRMultiplierSL;
double tpDistance = currentATR ATRMultiplierTP;
double slPrice = NormalizeDouble(Bid + slDistance, _Digits);
double tpPrice = NormalizeDouble(Bid - tpDistance, _Digits);

double lotSize = CalculateLotSize(slDistance);

if(lotSize <= 0 || lotSize > MaxLotSize) return;

tradeTicket = OrderSend(Symbol(), OP_SELL, lotSize, Bid, Slippage, slPrice, tpPrice, "Gold Sell", MagicNumber, 0, Red);
if(tradeTicket > 0)
Print("SELL订单开仓成功。订单号:", tradeTicket);
else
Print("SELL订单开仓失败。错误代码:", GetLastError());
}

//+------------------------------------------------------------------+
//| 资金管理与仓位计数函数 |
//+------------------------------------------------------------------+

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

// 根据风险和止损距离计算手数
double lotSize = riskAmount / (stopLossDistanceInPoints (tickValue / tickSize));
lotSize = NormalizeDouble(lotSize, 2); // 标准化到小数点后2位

// 检查最小/最大手数限制
double minLot = MarketInfo(Symbol(), MODE_MINLOT);
double maxLot = MarketInfo(Symbol(), MODE_MAXLOT);

if(lotSize < minLot) lotSize = minLot;
if(lotSize > maxLot) lotSize = maxLot;

return lotSize;
}

int CountOpenPositions()
{
int count = 0;
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
count++;
}
}
return count;
}

//+------------------------------------------------------------------+
//| 移动止损管理 |
//+------------------------------------------------------------------+

void ManageTrailingStop()
{
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) continue;
if(OrderMagicNumber() != MagicNumber || OrderSymbol() != _Symbol) continue;

double atrValue = iATR(_Symbol, PERIOD_CURRENT, ATRPeriod, 0);
if(atrValue <= 0) return;

// 计算当前浮盈点数
int profitPoints;
if(OrderType() == OP_BUY)
{
profitPoints = int((Bid - OrderOpenPrice()) / Point);
if(profitPoints > 0)
{
// 检查浮盈是否达到启动移动止损的条件
if(profitPoints > TrailStart
atrValue / Point)
{
double newSL = NormalizeDouble(Bid - TrailDistance atrValue, _Digits);
if(newSL > OrderStopLoss() && newSL > OrderOpenPrice())
{
if(OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0, Blue))
Print("买单移动止损更新至:", newSL);
}
}
}
}
else if(OrderType() == OP_SELL)
{
profitPoints = int((OrderOpenPrice() - Ask) / Point);
if(profitPoints > 0)
{
if(profitPoints > TrailStart
atrValue / Point)
{
double newSL = NormalizeDouble(Ask + TrailDistance * atrValue, _Digits);
if(newSL < OrderStopLoss() || OrderStopLoss() == 0)
{
if(OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0, Red))
Print("卖单移动止损更新至:", newSL);
}
}
}
}
break; // 每次只管理一个持仓
}
}
//+------------------------------------------------------------------+
``

参考来源:
  • 国际清算银行(BIS)。"BIS警告:散户黄金购买激增三倍,华尔街加速抛售,市场波动风险上升",2026年。

  • 芝商所(CME Group)。"亚洲时段黄金期货流动性占比超过全球总量三分之一",2025年。


  • *本文首发于[FXEAR