探索标准库的交易策略类 - 自定义策略·综合运用
◍ 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 用历史数据跑一遍验证触发频率。
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,这是典型的「关闭该组模型参与」写法。若你实盘不想用头肩或刺透,直接整组清零比注释掉判断分支更干净,也不会漏改条件。
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 或换权重,能直接看到信号面板如何塌缩。
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 数据确认频次。
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 内没成交就自动作废,避免僵尸单堆在盘面上。外汇与贵金属波动剧烈,这类挂单过期机制能降低误成交风险,但具体数值要按品种波幅自己调。
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 策略测试器跑历史,再上模拟盘验逻辑,别把回测顺滑当实盘保票。
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);">></span></b> Signal_ThresholdOpen Open_short<span style="class="type">class="kw">color:rgb(class="num">44, class="num">114, class="num">199);"><b>></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><</b></span> Signal_ThresholdOpen