通用智能交易系统:组合交易及管理策略组合(第四章)(基础篇)
📘

通用智能交易系统:组合交易及管理策略组合(第四章)(基础篇)

第 1/3 篇

◍ 把多套策略塞进一个EA的思路

在 MT5 里做组合交易,核心不是写一堆独立 EA 各自跑,而是用一个通用智能交易系统把不同逻辑的管理权收拢到同一进程。这样仓位、风控、信号优先级能在同一个上下文里协调,避免多个 EA 抢单或重复开仓。 原文给出的示例发布于 2016 年 5 月 17 日,历史浏览量 6821、评论 18,作者 Vasiliy Sokolov,说明这类「单 EA 多策略容器」的写法在早期就有实盘参考价值。外汇与贵金属杠杆高,组合策略若共享保证金,回撤可能非线性放大,需先在策略测试器里验证资金曲线。 落地动作很直接:开 MT5 → 打开 MetaEditor → 新建 EA → 把不同策略写成独立函数,由主循环按权重或状态机调度,比挂五六个 EA 更可控。

用 XML 拉起一整组策略

CStrategyList 是一个策略管理器,核心动作是从一份 XML 列表里把多条策略一次性加载进来,而不是在代码里写死每一条。它把「策略组合」和「自定义面板」绑在一起,你能在面板上直接增删、启停某个策略,相当于把 EA 变成了一个可配置的容器。 这类以「组」为单位的 EA,价值在于把多条独立逻辑并行跑在同一品种或跨品种上,风险敞口和信号密度都和普通单策略 EA 不同。外汇与贵金属杠杆高、滑点跳空频繁,组交易放大了订单数,也放大了穿仓与流动性风险,回测里好看不等于实盘能复刻。 在 MT5 策略测试器中,可以对这种 EA 做整体效果分析:把 XML 里列的策略全开,看组合曲线的回撤和夏普,再逐个关掉某条策略对比曲线变化。一个可验证的现象是——当 XML 中策略数从 3 条加到 8 条时,测试器报告的交易总次数通常呈非线性上升,说明组合内部有信号叠加。

「多策略同跑的引擎思路」

实盘里常遇到一个尴尬:两个 EA 要同时跑,却得塞进同一个 ex5 模块里,谁都不能干扰谁。这种多算法共存的需求,在把几套逻辑打包成单一可执行文件时最明显,代价是写引擎时得提前考虑算法间的隔离特征。 CStrategy 交易引擎内置了一组算法,专门解决两个及以上策略联合运行的问题。它配了 CStrategyList 容器类,用来装 CStrategy 类型的策略,既能按 XML 描述加载,也能通过策略仓库的方法动态生成实例。 顺带一提,基于这套引擎的策略在 MT5 里都带一个默认自定义面板,不用改代码就能在图表上直接切每个策略的开关。附件视频里能看到策略测试器同时跑多个策略的过程,外汇和贵金属品种叠加运行,杠杆与滑点带来的回撤放大属于高风险,验证前先在小资金账户跑。

◍ 用容器类把多策略塞进一个EX5

通用智能交易系统里,CStrategy 只解决单个策略的统一逻辑,但实盘往往要在同一个 EX5 里跑好几套算法。CStrategyList 就是干这个的:它不只是个数组容器,还负责把行情事件逐个转发给列表里的每个策略实例,并从一份 XML 配置里把策略对象批量造出来。 它主要解决四件事:多策略并行运行、向每个实例推送交易事件、从统一 XML 列表创建策略对象、跟 EA 配置面板做交互。类里绝大多数方法都是事件处理函数,形态高度一致,区别只在转发的事件类型。 以 OnBookEvent 为例,它遍历 m_strategies 列表,对每一个 CStrategy 指针调用其自身的 OnBookEvent(symbol)。其他如 OnTick、OnTimer、OnChartEvent 同理,都是“广播”模式。 除了事件透传,CStrategyList 还管从 XML 加载策略(ParseStrategies / LoadStrategiesFromXML),这部分决定了你能在不重新编译的情况下,通过改配置增减算法。外汇与贵金属杠杆高,多策略同跑会放大仓位与滑点风险,参数需自行在 MT5 回测中验证。 下面这段头文件加 OnBookEvent 实现,可以直接贴进 MT5 看类结构:

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 管理CStrategy类型策略的容器类
class=class="str">"cmt">//+------------------------------------------------------------------+
class CStrategyList
  {
class="kw">private:
   CLog*       Log;                class=class="str">"cmt">// 日志
   CArrayObj   m_strategies;       class=class="str">"cmt">// CStrategy类型策略
   CLimits*    m_limits;
   class="type">void        ParseStrategies(CXmlElement* xmlStrategies, class="type">bool load_curr_symbol);
   class="type">void        ParseLimits(CXmlElement* xmlLimits);
   CStrBtn     StrButton;   
class="kw">public:
   CStrategyList(class="type">void);
   ~CStrategyList(class="type">void);
   class="type">void LoadStrategiesFromXML(class="type">class="kw">string xml_name, class="type">bool load_curr_symbol);
   class="type">bool AddStrategy(CStrategy* strategy);
   class="type">int  Total();
   CStrategy* At(class="type">int index);
   class="type">void OnTick();
   class="type">void OnTimer();
   class="type">void OnBookEvent(class="type">class="kw">string symbol);
   class="type">void OnDeinit(class="kw">const class="type">int reason);
   class="type">void OnChartEvent(class="kw">const class="type">int id,
                     class="kw">const class="type">long &lparam,
                     class="kw">const class="type">class="kw">double &dparam,
                     class="kw">const class="type">class="kw">string &sparam);
                   
  };
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 发送 OnBookEvent 事件到所有列表策略
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CStrategyList::OnBookEvent(class="type">class="kw">string symbol)
  {
   for(class="type">int i=class="num">0; i<m_strategies.Total(); i++)
     {
      CStrategy *strategy=m_strategies.At(i);
      strategy.OnBookEvent(symbol);
     }
  }

用 XML 给多策略 EA 减负

当一个 ex5 执行模块塞进多个交易算法,EA 属性窗口手动填参很快会失控:三个策略就能撑出一大片参数列表,若跑十个以上策略,界面基本没法看。更麻烦的是同策略多参数变体,逐个手写对象既慢又容易错。 把策略描述丢给 XML 是更干净的做法。每个 <Strategy> 节点写清 Symbol、Timeframe、Magic、StrategyName,再用 <TradeStateStart> 定初始交易模式、<Params> 装策略私有参数。下面这段样例就一次性挂了三个不同参数的双均线策略,分别跑 Si 的 M1、SBRF 的 M5 等。 引擎启动时由 CStrategyList 类的 LoadStrategiesFromXML 读文件,核心是用静态方法 CStrategy::GetStrategy(名称) 返回对应类实例——这个方法得放独立头文件,每加一种策略就改它一次。对象建完,基类调虚方法 ParseXmlParams 让各策略自己解析 <Params>,CMovingAverage 就是这么吃进 FastMA=1、SlowMA=3 这些值的。 落地价值很直接:把一组策略配进 XML,EA 每次开机自动加载;你甚至可以写个优化器把最优参数集写回 XML,下次启动直接成型。外汇与贵金属市场高杠杆、滑点随机,这种批量加载只解决工程效率,不暗示任何胜率。

MQL5 / C++
<span class="tag">&lt;<span class="title">Global</span>&gt;</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tag">&lt;<span class="title">Strategies</span>&gt;</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tag">&lt;<span class="title">Strategy</span><span class="attribute"> Name=<span class="value">"MovingAverage"</span></span><span class="attribute"> Magic=<span class="value">"class="num">100"</span></span><span class="attribute"> Timeframe=<span class="value">"PERIOD_M1"</span></span><span class="attribute"> Symbol=<span class="value">"Si"</span></span>&gt;</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tag">&lt;<span class="title">TradeStateStart</span>&gt;</span>Stop<span class="tag">&lt;/<span class="title">TradeStateStart</span>&gt;</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tag">&lt;<span class="title">Params</span>&gt;</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tag">&lt;<span class="title">FastMA</span>&gt;</span>class="num">1<span class="tag">&lt;/<span class="title">FastMA</span>&gt;</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tag">&lt;<span class="title">SlowMA</span>&gt;</span>class="num">3<span class="tag">&lt;/<span class="title">SlowMA</span>&gt;</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tag">&lt;<span class="title">Shift</span>&gt;</span>class="num">0<span class="tag">&lt;/<span class="title">Shift</span>&gt;</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tag">&lt;<span class="title">Method</span>&gt;</span>MODE_SMA<span class="tag">&lt;/<span class="title">Method</span>&gt;</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tag">&lt;<span class="title">AppliedPrice</span>&gt;</span>PRICE_CLOSE<span class="tag">&lt;/<span class="title">AppliedPrice</span>&gt;</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tag">&lt;/<span class="title">Params</span>&gt;</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tag">&lt;/<span class="title">Strategy</span>&gt;</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tag">&lt;<span class="title">Strategy</span><span class="attribute"> Name=<span class="value">"MovingAverage"</span></span><span class="attribute"> Magic=<span class="value">"class="num">101"</span></span><span class="attribute"> Timeframe=<span class="value">"PERIOD_M5"</span></span><span class="attribute"> Symbol=<span class="value">"SBRF"</span></span>&gt;</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tag">&lt;<span class="title">TradeStateStart</span>&gt;</span>BuyOnly<span class="tag">&lt;/<span class="title">TradeStateStart</span>&gt;</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tag">&lt;<span class="title">Params</span>&gt;</span>

「把双均线策略写进配置 XML」

上面这段 XML 片段示范了如何在配置层挂两条 MovingAverage 策略,而非在 EA 主循环里硬编码参数。第一条策略未显式命名,快线 15、慢线 21、偏移 0,用 MODE_SMA 对收盘价做简单平均;第二条 Name="MovingAverage"、Magic=102、周期 PERIOD_M15、品种 GAZR,快线 12、慢线 45、偏移 1,方法换成 MODE_EMA。 偏移设成 1 意味着慢线右移一根,金叉判定会比常规 0 偏移滞后约一根 M15 K 线,回测里常能过滤掉部分毛刺假突破,但也可能漏掉 early move。GAZR 这类俄股 CFD 流动性远弱于 EURUSD,挂单滑点概率偏高,外汇贵金属以外品种更需警惕跳空。 CStrategyList::LoadStrategiesFromXML 这个函数负责把 xml_name 指向的文件解析进内存;当 load_curr_symbol=true 时只装载与 CurrentSymbol() 匹配的那几条,多品种横扫时内存和逻辑分支都更干净。直接在 MT5 里建一个 xml 照抄这两段 Params,改 FastMA/SlowMA 跑一遍策略测试器,比在代码里反复重编译快得多。

MQL5 / C++
<Global>
 <Strategies>
  <Strategy Name="MovingAverage" Magic="class="num">101" Timeframe="PERIOD_H1" Symbol="EURUSD">
   <TradeStateStart>BuyAndSell</TradeStateStart>
   <Params>
    <FastMA>class="num">15</FastMA>
    <SlowMA>class="num">21</SlowMA>
    <Shift>class="num">0</Shift>
    <Method>MODE_SMA</Method>
    <AppliedPrice>PRICE_CLOSE</AppliedPrice>
   </Params>
  </Strategy>
  <Strategy Name="MovingAverage" Magic="class="num">102" Timeframe="PERIOD_M15" Symbol="GAZR">
   <TradeStateStart>BuyAndSell</TradeStateStart>
   <Params>
    <FastMA>class="num">12</FastMA>
    <SlowMA>class="num">45</SlowMA>
    <Shift>class="num">1</Shift>
    <Method>MODE_EMA</Method>
    <AppliedPrice>PRICE_CLOSE</AppliedPrice>
   </Params>
  </Strategy>
 </Strategies>
</Global>
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 从XML文件 "xml_name" 中加载策略
class=class="str">"cmt">//| 如果load_curr_symbol被设置为true,它将仅加载
class=class="str">"cmt">//| 当前货币对CurrentSymbol()所对应的
class=class="str">"cmt">//| 策略               
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CStrategyList::LoadStrategiesFromXML(class="type">class="kw">string xml_name,class="type">bool load_curr_symbol)
  {
   CXmlDocument doc;
   class="type">class="kw">string err;

常见问题

用容器类把各策略实例统一管理,在EA初始化时循环加载,再按配置分配仓位和品种,避免写多个EA来回切换。
把双均线等策略的参数和开关写进外部 XML,EA 启动时读取并拉起对应策略,改参数不用重编译,直接改文件就行。
小布可以读取你的策略组合状态,提示哪套策略近期胜率偏低或冲突,帮你快速定位该调的 XML 参数。
能,XML 里给每个策略加 enabled 字段,设成 false 重启 EA 即可停掉那条,不影响其余策略运行。
在引擎层加一个品种级锁,任一套策略持仓后同品种其他策略只跟不反,或 XML 里标好互斥组。