探索标准库的交易策略类 - 自定义策略·综合运用
📘

探索标准库的交易策略类 - 自定义策略·综合运用

第 3/3 篇

◍ CCI空信号的分层判定逻辑

做空条件函数 ShortCondition 把 CCI 衍生信号的触发拆成了 0~5 共 6 档模型,靠 IS_PATTERN_USAGE(n) 开关决定当前加载哪几类。Diff(idx)<0.0 是总闸门,振荡器向下才允许后续信号成立,否则直接回 0。 模型 1 要求前一柱 Diff 为正且 CCIxx(idx+1)>100.0,即超买区上方出现向下反转;模型 4、5 则纯看零轴穿越节奏:模型 4 为 idx+1 在零轴下、idx+2 在零轴上,模型 5 再多一根 idx+3 回到零轴下,属于连续两次上下穿越。 模型 2、3 走 ExtState + CompareMaps 的扩展比对,分别抓普通背离(00000001b)与双重背离(00010001b)。注意模型 3 命中后直接 return(m_pattern_3),不再往下走模型 4、5 的零轴判断。 开仓与平仓阈值分设:Signal_ThresholdOpen=40、Signal_ThresholdClose=60,意味着信号权重过 40 才倾向开空,过 60 才倾向平。外汇与贵金属杠杆高,这类 CCI 信号只作概率参考,实盘前务必在 MT5 用历史数据跑一遍验证触发频率。

MQL5 / C++
class="type">int CSignalCCIxx::ShortCondition()
  {
   class="type">int result=class="num">0;
   class="type">int idx   =StartIndex();
class=class="str">"cmt">//---
   if(Diff(idx)<class="num">0.0)
     {
       class=class="str">"cmt">//--- the oscillator is directed downwards confirming the possibility of falling of price
       if(IS_PATTERN_USAGE(class="num">0)) result=m_pattern_0;          class=class="str">"cmt">// "confirming" signal number class="num">0
       class=class="str">"cmt">//--- if the model class="num">1 is used, search for a reverse of the oscillator downwards behind the level of overbuying
       if(IS_PATTERN_USAGE(class="num">1) && Diff(idx+class="num">1)>class="num">0.0 && CCIxx(idx+class="num">1)>class="num">100.0)
         result=m_pattern_1;          class=class="str">"cmt">// signal number class="num">1
       class=class="str">"cmt">//--- if the model class="num">2 or class="num">3 is used, perform the extended analysis of the oscillator state
       if(IS_PATTERN_USAGE(class="num">2) || IS_PATTERN_USAGE(class="num">3))
         {
          ExtState(idx);
          class=class="str">"cmt">//--- if the model class="num">2 is used, search for the "divergence" signal
          if(IS_PATTERN_USAGE(class="num">2) && CompareMaps(class="num">1,class="num">1))          class=class="str">"cmt">// 00000001b
            result=m_pattern_2;   class=class="str">"cmt">// signal number class="num">2
          class=class="str">"cmt">//--- if the model class="num">3 is used, search for the "class="type">class="kw">double divergence" signal
          if(IS_PATTERN_USAGE(class="num">3) && CompareMaps(0x11,class="num">2))   class=class="str">"cmt">// 00010001b
            class="kw">return(m_pattern_3);   class=class="str">"cmt">// signal number class="num">3
         }
       if(IS_PATTERN_USAGE(class="num">4) && CCIxx(idx+class="num">1)<class="num">0.0 && CCIxx(idx+class="num">2)>class="num">0.0)
         result=m_pattern_4;          class=class="str">"cmt">// signal number class="num">4 
       if(IS_PATTERN_USAGE(class="num">5) && CCIxx(idx+class="num">1)<class="num">0.0 && CCIxx(idx+class="num">2)>class="num">0.0 && CCIxx(idx+class="num">3)<class="num">0.0)
         result=m_pattern_5;          class=class="str">"cmt">// signal number class="num">5 
     }
class=class="str">"cmt">//--- class="kw">return the result
   class="kw">return(result);
  }

市场模型权重的多套预设与零值屏蔽

这段初始化逻辑里,m_pattern_x 并不是单一写死的值,而是按不同分析维度反复赋值,相当于给同一组模型挂了好几套权重预设。第一组针对价格与指标的位置关系:模型0(价格在指标所需一侧)给80,模型1(价格反方向穿越指标)给10,模型2(同方向穿越)给60,模型3(刺透)给60。 第二组切到振荡器视角:模型0(振荡器方向符合要求)70,模型1(超买超卖区反转)直接拉满100,模型2(失败摆动)90,模型3(背离)80,模型4(双背离)100,模型5(头肩)只给20。这里能看出作者对「超买超卖反转」和「双背离」的置信度最高。 第三组又换了权重分配:模型2和模型4(自研新形态)被提到100,模型0降到90,模型1掉到60,模型3(双背离)反而只有50。说明同一套模型在不同策略分支里,权重可以完全颠倒,别拿一组数当万能参数。 代码里还留了模型4的触发判定:当启用模型4且 CCIxx(idx+1)>0.0 同时 CCIxx(idx+2)<0.0,也就是CCI在相邻两根bar从零轴下方穿到上方,result 赋为 m_pattern_4。开MT5把这段塞进EA初始化,改 CCIxx 周期就能验证信号频率变化。 最后两组把所有 m_pattern_0~5 全部置0,这是典型的「关闭该组模型参与」写法。若你实盘不想用头肩或刺透,直接整组清零比注释掉判断分支更干净,也不会漏改条件。

MQL5 / C++
  m_pattern_0 =class="num">80;                 class=class="str">"cmt">// model class="num">0 "price is on the necessary side from the indicator"
  m_pattern_1 =class="num">10;                 class=class="str">"cmt">// model class="num">1 "price crossed the indicator with opposite direction"
  m_pattern_2 =class="num">60;                 class=class="str">"cmt">// model class="num">2 "price crossed the indicator with the same direction"
  m_pattern_3 =class="num">60;                 class=class="str">"cmt">// model class="num">3 "piercing"
class=class="str">"cmt">//--- setting class="kw">default "weights" of the market models
  m_pattern_0  =class="num">70;      class=class="str">"cmt">// model class="num">0 "the oscillator has required direction"
  m_pattern_1  =class="num">100;     class=class="str">"cmt">// model class="num">1 "reverse behind the level of overbuying/overselling"
  m_pattern_2  =class="num">90;      class=class="str">"cmt">// model class="num">2 "failed swing"
  m_pattern_3  =class="num">80;      class=class="str">"cmt">// model class="num">3 "divergence of the oscillator and price"
  m_pattern_4  =class="num">100;     class=class="str">"cmt">// model class="num">4 "class="type">class="kw">double divergence of the oscillator and price"
  m_pattern_5  =class="num">20;      class=class="str">"cmt">// model class="num">5 "head/shoulders"
class=class="str">"cmt">//--- setting class="kw">default "weights" of the market models
  m_pattern_0  =class="num">90;      class=class="str">"cmt">// model class="num">0 "the oscillator has required direction"
  m_pattern_1  =class="num">60;      class=class="str">"cmt">// model class="num">1 "reverse behind the level of overbuying/overselling"
  m_pattern_2  =class="num">100;     class=class="str">"cmt">// model class="num">3 "divergence of the oscillator and price"
  m_pattern_3  =class="num">50;      class=class="str">"cmt">// model class="num">4 "class="type">class="kw">double divergence of the oscillator and price"
  m_pattern_4  =class="num">80;      class=class="str">"cmt">// model class="num">4 "our own first new pattern: "
  m_pattern_5  =class="num">90;      class=class="str">"cmt">// model class="num">5 "our own second new pattern: "
class=class="str">"cmt">// if the model class="num">4 is used, look for crossing of the zero line
        if(IS_PATTERN_USAGE(class="num">4) && CCIxx(idx+class="num">1)>class="num">0.0 && CCIxx(idx+class="num">2)<class="num">0.0)
            result=m_pattern_4;       class=class="str">"cmt">// signal number class="num">4 
class=class="str">"cmt">//--- setting class="kw">default "weights" of the market models
  m_pattern_0 =class="num">0;                 class=class="str">"cmt">// model class="num">0 "price is on the necessary side from the indicator"
  m_pattern_1 =class="num">0;                 class=class="str">"cmt">// model class="num">1 "price crossed the indicator with opposite direction"
  m_pattern_2 =class="num">0;                 class=class="str">"cmt">// model class="num">2 "price crossed the indicator with the same direction"
  m_pattern_3 =class="num">0;                 class=class="str">"cmt">// model class="num">3 "piercing"
class=class="str">"cmt">//--- setting class="kw">default "weights" of the market models
  m_pattern_0  =class="num">0;      class=class="str">"cmt">// model class="num">0 "the oscillator has required direction"
  m_pattern_1  =class="num">0;      class=class="str">"cmt">// model class="num">1 "reverse behind the level of overbuying/overselling"
  m_pattern_2  =class="num">0;      class=class="str">"cmt">// model class="num">2 "failed swing"
  m_pattern_3  =class="num">0;      class=class="str">"cmt">// model class="num">3 "divergence of the oscillator and price"
  m_pattern_4  =class="num">0;      class=class="str">"cmt">// model class="num">4 "class="type">class="kw">double divergence of the oscillator and price"
  m_pattern_5  =class="num">0;      class=class="str">"cmt">// model class="num">5 "head/shoulders"

「权重投票里的多空信号强度」

在 MT5 的模型初始化代码里,市场形态默认权重大多置 0,唯独 m_pattern_4 被写死成 80,这是作者自研的第一个新模式,相当于在投票机制里直接给了它压倒性话语权。 配合 CCI(8, PRICE_CLOSE) 的 Signal_CCIxx_Weight = 0.8,单看 m_pattern_4 的多头信号强度就是 0.8 × 80 = 64;若权重调到 0.4,则降为 32。负值 -80 对应空头,0.8 × (-80) = -64,负数即被系统判为做空票数。 把 m_pattern_4 和 m_pattern_2(权重 0.7、满权 100)两两组合,取平均后四种情形分别是:双多 67、多空 -3、空多 3、双空 -67。可见一多一空会几乎抵消,只有同向叠加才给出明确偏向。 外汇与贵金属杠杆高、滑点突兀,这类投票数值只是概率倾向,开 MT5 把 m_pattern_4 改回 0 或换权重,能直接看到信号面板如何塌缩。

MQL5 / C++
class=class="str">"cmt">//--- setting class="kw">default "weights" of the market models
  m_pattern_0  =class="num">0;      class=class="str">"cmt">// model class="num">0 "the oscillator has required direction"
  m_pattern_1  =class="num">0;      class=class="str">"cmt">// model class="num">1 "reverse behind the level of overbuying/overselling"
  m_pattern_2  =class="num">0;      class=class="str">"cmt">// model class="num">3 "divergence of the oscillator and price"
  m_pattern_3  =class="num">0;      class=class="str">"cmt">// model class="num">4 "class="type">class="kw">double divergence of the oscillator and price"
  m_pattern_4  =class="num">80;     class=class="str">"cmt">// model class="num">4 "our own first new pattern: "
  m_pattern_5  =class="num">0;      class=class="str">"cmt">// model class="num">5 "our own second new pattern: "
class="kw">input class="type">int                Signal_CCIxx_PeriodCCI =class="num">8;            class=class="str">"cmt">// Commodity Channel Index(class="num">8,...) Period of calculation
class="kw">input ENUM_APPLIED_PRICE Signal_CCIxx_Applied   =PRICE_CLOSE;  class=class="str">"cmt">// Commodity Channel Index(class="num">8,...) Prices series
class="kw">input class="type">class="kw">double             Signal_CCIxx_Weight    =class="num">0.8;          class=class="str">"cmt">// Commodity Channel Index(class="num">8,...) Weight [class="num">0...class="num">1.0]

◍ 阈值交叉如何决定持仓反转还是离场

EA 的建仓与平仓不是简单金叉死叉,而是围绕 Signal_ThresholdOpen 与 Signal_ThresholdClose 两个阈值做布尔判断。当 Close 阈值小于等于 Open 阈值时,反向信号出现会让原持仓直接反转;当 Close 阈值大于等于 Open 阈值时,由于『>=』已经覆盖 Open 值本身,平仓后必然被反向条件接管,仓位无从保留只能翻向。 以买入持仓为例:收到卖出信号后,若 Signal_ThresholdClose >= Signal_ThresholdOpen,持仓先平再反手做空,这个 OR 逻辑不是冗余,而是把『阈值跨越』和『阈值包含』并成一条路径,避免漏单。卖出持仓对称处理,信号反向时同理翻多。 下面这段是向导生成的原始条件串,注意 AND 与 OR 的混用决定了『临界区』内的动作: Open_long >= Signal_ThresholdOpen Open_short > Signal_ThresholdClose AND Open_short > Signal_ThresholdOpen Open_short > Signal_ThresholdClose AND Open_short < Signal_ThresholdOpen Open_short > Signal_ThresholdClose OR Open_short > Signal_ThresholdOpen Open_short > Signal_ThresholdClose OR Open_short < Signal_ThresholdOpen Open_short >= Signal_ThresholdOpen. Open_long > Signal_ThresholdClose AND Open_long > Signal_ThresholdOpen Open_long > Signal_ThresholdClose AND Open_long < Signal_ThresholdOpen Open_long > Signal_ThresholdClose OR Open_long > Signal_ThresholdOpen Open_long > Signal_ThresholdClose OR Open_long < Signal_ThresholdOpen 在 MT5 里把这两个阈值拉开 0.1~0.3 的间距,能明显减少毛刺行情中的连续反转;外汇与贵金属杠杆高,阈值过窄会放大搓交易成本,实盘前请用策略测试器跑至少 3 个月 Tick 数据确认频次。

MQL5 / C++
Open_long >= Signal_ThresholdOpen
Open_short > Signal_ThresholdClose AND Open_short > Signal_ThresholdOpen
Open_short > Signal_ThresholdClose AND Open_short < Signal_ThresholdOpen
Open_short > Signal_ThresholdClose OR Open_short > Signal_ThresholdOpen
Open_short > Signal_ThresholdClose OR Open_short < Signal_ThresholdOpen
Open_short >= Signal_ThresholdOpen.
Open_long > Signal_ThresholdClose AND Open_long > Signal_ThresholdOpen
Open_long > Signal_ThresholdClose AND Open_long < Signal_ThresholdOpen
Open_long > Signal_ThresholdClose OR Open_long > Signal_ThresholdOpen
Open_long > Signal_ThresholdClose OR Open_long < Signal_ThresholdOpen

止损还是限价:Signal_PriceLevel 的符号说了算

用 MT5 向导生成的 EA 里,Signal_PriceLevel 这个输入量直接决定持仓信号落地成哪种挂单。它的正负号是硬规则:负值一律对应止损单(Buy Stop / Sell Stop),正值一律对应限价单(Buy Limit / Sell Limit),跟你是做多还是做空信号无关,只跟符号走。 拿 EURUSD 举例更直观。假设触发信号时现价 1.2500:买入信号配 -70,EA 下 Buy Stop 于 1.2570(比现价高 70 点,牛市视角属于追劣价);配 +60,则下 Buy Limit 于 1.2440(低 60 点,拿更优价)。卖出信号反过来,-70 下 Sell Stop 于 1.2430,+60 下 Sell Limit 于 1.2560。 另一个容易忽略的量是 Signal_Expiration,它管挂单能活几根 K 线。代码里默认设成 4,意味着 pending order 若 4 根 bar 内没成交就自动作废,避免僵尸单堆在盘面上。外汇与贵金属波动剧烈,这类挂单过期机制能降低误成交风险,但具体数值要按品种波幅自己调。

MQL5 / C++
class="kw">input class="type">class="kw">double        Signal_PriceLevel    =class="num">0.0;      class=class="str">"cmt">// Price level to execute a deal
class="kw">input class="type">int          Signal_Expiration    =class="num">4;          class=class="str">"cmt">// Expiration of pending orders(in bars)

「把 CCI 信号编译进 EA 跑一轮回测」

编译 SignalCCIxx.mqh 后若无报错,说明两类市场交易决策模式已挂进信号模块:每类都带独立的买/卖触发与建仓、平仓条件。接着编译 MyExpert.mq5,零警告零错误才能进策略测试器。 我在策略测试程序里用 EUR/USD 跑了组参数,周期参照 2011 年自动交易锦标赛的设定。固定手数下,不到 3 个月初始资金翻了两倍多,但这个成绩只在回测里成立。 外汇和贵金属实盘是高风险的,这套 EA 我不建议直接上实盘。更合理的做法是你往里加自己的模式,反复调参直到贴合你的风险偏好。 MQL5 向导加本文这种改库思路,本质就是让人能低成本试错。你也能把新模型丢到社区共享,验证门槛很低。

◍ 别急着下结论

把 CCI 的两种过滤模式接进向导只是起点,同样套路能套到任意标准指标上,拼出你自己的信号群。真正拉开差距的,是模式权重和阈值有没有经过结构化推敲,而不是堆了多少指标。 MQL5 向导把下单、仓位翻转这些脏活全包了,你只盯策略核心就行。下面这段开空逻辑就是现成例子,注意 OR 两侧阈值不等时的行为差异: Open_short > Signal_ThresholdClose OR Open_short > Signal_ThresholdOpen Open_short > Signal_ThresholdClose OR Open_short < Signal_ThresholdOpen 前者两个大于号,后者一大于一小于,实盘里可能给出完全不同的反手信号。 外汇和贵金属波动剧烈、杠杆高风险大,任何自定义 EA 都先在 MT5 策略测试器跑历史,再上模拟盘验逻辑,别把回测顺滑当实盘保票。

MQL5 / C++
Open_short>b> Signal_ThresholdClose<b><span style="class="type">class="kw">color:rgb(class="num">177, class="num">79, class="num">154);">OR</span></b> Open_short<b><span style="class="type">class="kw">color:rgb(class="num">44, class="num">114, class="num">199);">&gt;</span></b> Signal_ThresholdOpen
Open_short<span style="class="type">class="kw">color:rgb(class="num">44, class="num">114, class="num">199);"><b>&gt;</b></span> Signal_ThresholdClose<span style="class="type">class="kw">color:rgb(class="num">177, class="num">79, class="num">154);"><b>OR</b></span> Open_short<span style="class="type">class="kw">color:rgb(class="num">232, class="num">87, class="num">82);"><b>&lt;</b></span> Signal_ThresholdOpen

常见问题

用分层判定:先把CCI绝对值小于阈值的部分判为空白信号,只在突破分层边界后才视作有效方向,避免零轴摩擦单。
可以,将该模型权重设为0即触发零值屏蔽,它不再计入多空投票,不影响其他模型加权结果。
可以,小布能按你设定的分层阈值和权重预设自动标注空白信号与多空强度,盘中直接推送异常。
符号决定:正值为限价单参考价,负值为止损单参考价,编译进EA时按符号切换下单类型即可。
看阈值交叉类型:穿透离场阈值只平仓,穿透反转阈值才反手,两者阈值要分开设避免频繁来回。