多币种多系统EA架构实战
🤖

多币种多系统EA架构实战

用for循环整合多策略跨品种交易与风控

MQL5 编程 难度 · 进阶 2026-03-18 5 分钟阅读
#MQL5#多币种EA#多策略#OnTimer#风控

为什么需要多币种多系统EA

很多交易者不会只做一个品种或一种策略。靠资金管理和策略分散,能提高潜在利润并降低大笔亏损风险。但把全部策略塞进一个EA时,第一步优化完参数后,仍有一个核心问题:所有策略在同一账户同时跑,测试结果和分别测试再相加一样吗?某些品种或策略的亏损可能同时发生,造成整体回撤甚至预付款追保。多币种多系统EA的概念,就是在一个程序里统一调度,真实评估组合效益。

MetaTrader 5 中每个交易品种只能有一个净仓位(代表多次买卖总和),因此多策略作用于同一品种的结果,不等于分别测试之和。用组合EA能直接看到联动效果,也适合那些经纪商只允许挂一个EA的账户环境。

✦ 适用场景
当你需要在同一账户运行AUDUSD突破、EURUSD布林、GBPUSD反转等互不干扰又共享资金的策略时,组合EA比多EA更利于统一风控与观察。

EA整体结构:以for循环为骨架

多币种多系统EA的基础是一个for循环,每次迭代负责一个交易品种的一项策略。你可以在循环里挂任意多策略,只要电脑资源够用。文中示例采用策略A(布林带通道)与策略B(上一根K线突破),各交易两个符号:EURUSD、GBPUSD与AUDUSD、EURJPY。

策略A规则:买价为根据最低价算出的布林下带时买入,卖价达根据最高价算出的布林下带时平仓;卖价达上带时卖出,买价达下带时平空。每根柱限一笔。策略B:上根熊势且买价破前高则买,上根牛势且卖价破前低则卖,用止损获利平,每根柱限一笔。

为脱离具体图表品种的价格变动,建议用OnTimer()做多币种调度。初始化时EventSetTimer()设频率,去初始化EventKillTimer()停事件。不要用过度频繁的毫秒定时器滥用资源。

//--- Initialization of the Expert Advisor
int OnInit()
  {
   EventSetTimer(1); // 1 second
   return(0);
  }
void OnTimer()
  {
   // ...
  }
void OnDeinit(const int reason)
  {
   EventKillTimer();
  }

关键类与全局定义

访问账户、仓位、品种与交易函数,分别用CAccountInfo、CPositionInfo、CSymbolInfo、CTrade类。需在EA顶部include标准库。外部参数用#define常量确定数组大小,例如策略A、B各2符号。

#include <Trade\AccountInfo.mqh>
#include <Trade\PositionInfo.mqh>
#include <Trade\SymbolInfo.mqh>
#include <Trade\Trade.mqh>

#define Strategy_A 2
#define Strategy_B 2

外部参数示例:每个符号有Symbol、IsTrade开关、周期、布林周期(uint防负)、偏移、偏差等。全局数组存这些参数,以及MinLot、Point、Handle等。Magic与Slippage也按策略设。

input string Symbol_A0 = "EURUSD";
input bool IsTrade_A0 = true;
input uint BBPeriod_A0 = 20;
string Symbol_A[Strategy_A];
bool IsTrade_A[Strategy_A];
int BBPeriod_A[Strategy_A];
CTrade Trade_A;

初始化流程与参数校验

OnInit中先取杠杆等账户级数据(无需数组)。再把外部变量复制到数组,注意uint转int避免iBands警告。然后检查市场报价是否存在该符号、同策略是否重复用同一符号。

Leverage=AccountInfo.Leverage();
Symbol_A[0]=Symbol_A0;
BBPeriod_A[0]=(int)BBPeriod_A0;
for(int i=0;i<Strategy_A;i++)
{
  if(IsTrade_A[i]==false) continue;
  if(IsSymbolInMarketWatch(Symbol_A[i])==false)
  { Print(Symbol_A[i]," not found!"); ExpertRemove(); }
}

接着在for里建指标句柄、算Lot所需数据(点值、合约大小),并对CTrade设魔术码、滑点、填充模式、异步。最后跨策略检查同符号冲突,防止净仓位逻辑混乱。

BB_handle_high_A[i]=iBands(Symbol_A[i],Period_A[i],BBPeriod_A[i],BBShift_A[i],BBDeviation_A[i],PRICE_HIGH);
Trade_A.SetExpertMagicNumber(MagicNumber_A);
Trade_A.SetAsyncMode(true);
⚠ 常见坑
若某符号在市价表不存在或同策略重复,必须ExpertRemove终止,否则后续CopyBuffer会报错;跨策略共用符号会令仓位互斥,初始化就要拦截。

OnTimer交易循环实操

OnTimer里先判终端连服,再写各策略的for循环。单品种EA用return停全部,这里用continue跳本次迭代。若新策略内部需中断全部,可用标志位+break再continue。

void OnTimer()
{
  if(TerminalInfoInteger(TERMINAL_CONNECTED)==false) return;
  for(int A=0;A<Strategy_A;A++)
  {
    if(IsTrade_A[A]==false) continue;
  }
}

把原单一EA代码粘进循环,将_Symbol换Symbol_A[i]、_Point换Point_A[i]等。取指标缓冲前CopyBuffer失败就continue。平仓前PositionSelect,用Bid/Ask比带值。

if(CopyBuffer(BB_handle_high_A[A],LOWER_BAND,BBShift_A[A],1,BB_lower_band_high)<=0) continue;
SymbolInfo.Name(Symbol_A[A]); SymbolInfo.RefreshRates();
double Ask_price=SymbolInfo.Ask();
✦ 性能提醒
评论区有人问:若OnTimer计算超1秒是否改EventSetTimer(5)。答案是应匹配耗时,避免重叠调用导致资源挤占与逻辑乱序。

回测对比与组合效益

分别测策略A(EURUSD、GBPUSD)、策略B(AUDUSD、EURJPY)后,再开全部品种同跑。图2-7显示分开与组合的权益差异。组合EA能暴露相关性亏损,这是分别测试加总看不到的。

实践上,若账户只允许一个EA,此类结构直接满足;且便于统一魔法码分段、总权益风控。文末提供2multi_en.mq5源码供学。

与其他方案对比及总结

对比多图表多EA:组合EA省终端实例、易统管;对比信号跟单:自主逻辑、零延迟。缺点为代码复杂度高、需自管资源。总体结构简便,可放几乎所有策略,是进阶MQL5必会模式。

常见问题

OnTick只响应当前图表品种跳动,多币种需脱离图表用EventSetTimer在OnTimer里主动轮询各Symbol,避免依赖单一品种行情触发。
必须检查Symbol是否在MarketWatch、同策略/跨策略是否重复符号,否则用ExpertRemove终止;并正确复制外部参数数组、建指标句柄。
MT5每品种仅一个净仓位,多策略平仓开仓会互斥,需在初始化拦截同符号跨策略,并在循环里用PositionSelect判断现仓。
要。若单次执行耗2-3秒,应将EventSetTimer设为更高值如5秒,防止重叠调用造成资源挤占与逻辑错误。
用CPositionInfo类PositionSelect后读Profit、Volume;或账户级用AccountInfoDouble(ACCOUNT_EQUITY)看总权益,不依赖单一仓。