开发多币种 EA 交易(第 6 部分):自动选择实例组(基础篇)
多币种EA的实例组自动分配
在 MT5 里跑多币种 EA,最容易被忽略的是「实例组」归属。终端会把同一图表上加载的多个 EA 按符号与周期自动归组,若不同货币对被错分到同一组,全局变量与订单池会互相踩踏,回测和实盘表现可能偏离预期。 作者 Yuriy Bykov 在 2024-11-20 发布的系列第 6 部分指出,EA 应在初始化阶段探测自身所处实例组,并据已加载符号清单动态选择独立组名。这样多品种同图运行时,各币种统计与持仓管理互不干扰,概率上降低误平、重复开仓的风险。 外汇与贵金属杠杆高、跳空频繁,多实例并行时更需确认组隔离是否生效。开 MT5 加载两个货币对 EA,用全局变量观察器看前缀是否一致,就能当场验证分配逻辑。
「手工拼组策略很快会遇到天花板」
上一阶段我们已经能切固定仓位和可变仓位两种 sizing 模式,并依据最大回撤率把策略结果归一化、把回撤落在阈值内的归成一组。当时从单实例优化结果里手挑了几组参数做组合,三策略三组的形式表现最好。 但真要把分组数量和组合排列扩开来,手工路径立刻崩。单品种就要跑不同优化标准,跨品种还得拆时间框架分别优化;就我们这个模型策略,止损单、限价单、市价单也得分开跑优化。 参数集规模通常在 2 到 5 万组,人工要筛出 10 到 20 个不仅单跑强、组团也强的实例,逐个加耐心耗光。再往上并成更高层组做标准化,手搓顶多扛两三级,再多加一级就变成体力活。 所以这一阶段直接把 EA 开发流程自动化,才是把策略组合空间打开的唯一现实办法。外汇与贵金属市场高杠杆高风险,自动化只解决工程量,不替你背书收益。
◍ 先拿一个品种验证自动分组值不值
直接上全套自动化,多数人会被复杂度劝退。更现实的切入点,是先验证一个假设:自动分组选择可能比手动更有用。 做法很朴素——先在一个交易品种上跑一次优化,拿到结果集后人工挑出一组归一化表现好的参数,作为基线。这一步不追求优雅,只求有个可比的锚。 接着用最低成本写一段最简单的自动选组逻辑,跑完把自动组和手动基线摆在一起比。外汇与贵金属优化本身高风险,样本仅一个品种时结论只具启发意义,不能外推。 若自动组在质量不降(或略升)的前提下省了人工筛选时间,就说明值得继续做更完备的版本;若没优势,早期试错成本也极低。
把优化结果整理成可喂模型的 CSV
先在 MT5 里把 SimpleVolumesExpertSingle.mq5 的优化结果导出为 XML,再补几列不参与优化的字段:交易品种、时间框架、maxCountOfOrders,以及关键的 fittedBalance。 时间框架用代码常量 PERIOD_H1 表示,其数值为 16385,直接写进表里的对应列即可。 fittedBalance 按已知最大相对回撤 relDDpercent 推算:若初始余额 100000 美元,绝对回撤约为 100000*(relDDpercent/100),且它应是 fittedBalance 的 10%,故 fittedBalance = relDDpercent * 10000。 导出的原始表约 18000 行,其中约 1000 行利润为负,这些直接删掉,留着的才是后续构建基线版本和筛选竞争策略组的干净初始数据。 手动补列最省事,也可以用 TesterCache 库或优化时挂钩子自动存,但前者对一次性验证够用了。
「把 16 个实例压成一条基线 EA」
先给候选策略排座次:按利润、预期回报、利润因子、恢复因子、夏普比率、净值回撤百分比、交易次数这七列做最小-最大缩放,映射到 [0,1],再算加权总分 0.5*Profit_s + ExpectedPayoff_s + ProfitFactor_s + RecoveryFactor_s + SharpeRatio_s + (1-EquityDD_s) + 0.3*Trades_s,降序排。挑人时别光看参数表,要选回撤发生在不同时间的样本——若两组只在 SL 上有别但回测从未触发,开平仓时间完全重合,合并后最大回撤不会分散,这种组合没有意义。 外汇与贵金属属高风险品种,组合分散只能降低概率性回撤,不消除爆仓可能。我们选出 16 个标准化实例,固定余额设 FixedBalance=10000,单实例最大回撤 1000。实测把 16 个策略各缩仓 16 倍后,组合最大回撤仅 552 而非 1000;为维持 10% 回撤上限,套用回撤因子 Scale = 1000/552 ≈ 1.81。 把 FixedBalance=10000 与 Scale=1.81 写死成输入默认值,存为 BaselineExpert.mq5,后续自动化选组就有可比基准。下面这段代码就是基线版骨架,直接丢进 MT5 能跑出对照曲线。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Inputs | class=class="str">"cmt">//+------------------------------------------------------------------+ input group "::: Money management" input class="type">class="kw">double expectedDrawdown_ = class="num">10; class=class="str">"cmt">// - Maximum risk(%) input class="type">class="kw">double fixedBalance_ = class="num">10000; class=class="str">"cmt">// - Used deposit(class="num">0 - use all) in the account currency input class="type">class="kw">double scale_ = class="num">1.81; class=class="str">"cmt">// - Group scaling multiplier input group "::: Other parameters" input class="type">ulong magic_ = class="num">27183; class=class="str">"cmt">// - Magic CVirtualAdvisor *expert; class=class="str">"cmt">// EA object class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert initialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { class=class="str">"cmt">// Set parameters in the money management class CMoney::DepoPart(expectedDrawdown_ / class="num">10.0); CMoney::FixedBalance(fixedBalance_); class=class="str">"cmt">// Create an EA handling class="kw">virtual positions expert = new CVirtualAdvisor(magic_, "SimpleVolumes_Baseline"); class=class="str">"cmt">// Create and fill the array of all selected strategy instances CVirtualStrategy *strategies[] = { new CSimpleVolumesStrategy("EURGBP", PERIOD_H1, class="num">48, class="num">1.6, class="num">0.1, class="num">0, class="num">11200, class="num">1160, class="num">51000, class="num">3, class="num">3000), new CSimpleVolumesStrategy("EURGBP", PERIOD_H1, class="num">156, class="num">0.4, class="num">0.7, class="num">0, class="num">15800, class="num">905, class="num">18000, class="num">3, class="num">1200), new CSimpleVolumesStrategy("EURGBP", PERIOD_H1, class="num">156, class="num">1, class="num">0.8, class="num">0, class="num">19000, class="num">680, class="num">41000, class="num">3, class="num">900), new CSimpleVolumesStrategy("EURGBP", PERIOD_H1, class="num">14, class="num">0.3, class="num">0.8, class="num">0, class="num">19200, class="num">495, class="num">27000, class="num">3, class="num">1100),