研究烛条分析技术(第三部分):用于形态操作的函数库·综合运用
📚

研究烛条分析技术(第三部分):用于形态操作的函数库·综合运用

(3/3)· 从分析器到可复用库,23 节拆解形态识别与交易的工程落地

实战向 第 3/3 篇
很多人把烛条研究停留在报表里,真到写 EA 时又从头解析 K 线。形态逻辑散落在脚本各处,改一处权重全网崩。这篇收尾把前面两篇的结论压成一个库,别再重复造轮子。

「两根烛条的组合形态判定逻辑」

这段判定代码以两根烛条(cand2 为前一根、cand1 为当前根)为基础,按趋势方向与烛条类型给 pattern 变量赋值。它把倒锤子、上吊线、锤子、射击之星、吞噬及 Harami 系列拆成了独立分支,每一条都先卡趋势再卡形态。 看前四个分支:cand2 在 DOWN 趋势且为倒锤子时记 INVERT_HUMMER;cand2 在 UPPER 趋势且为锤子时记 HANDING_MAN;cand2 在 DOWN 且锤子则记 HUMMER;射击之星要求 cand1 与 cand2 同为 UPPER 趋势、cand2 是倒锤子且 cand1 收盘不高于 cand2 开盘。 吞噬分支用实体大小与开收关系做过滤:看涨吞噬要 cand1 实体大于 cand2、且 cand1 收包住 cand2 的开盘到收盘区间;看跌吞噬则反过来,cand2 实体更大且反向包住。Harami 交叉要求前一根为长体或 Maribozu、后一根为十字星且完全嵌在前一根实体内——这类嵌套条件在 MT5 里直接复制容易因变量未初始化报错,需确认 GetCandleType 已填好 m_trend / m_type / m_bodysize 等字段。 外汇与贵金属市场跳空频繁,纯形态判定在重大数据发布前后假信号概率会明显抬升,建议先在策略测试器用 2020—2023 年 XAUUSD 的 M15 跑一遍命中率再上实盘。

MQL5 / C++
  GetCandleType(symbol,timeframe,cand2,shift);                                                     class=class="str">"cmt">// 之前的烛条
  GetCandleType(symbol,timeframe,cand1,shift-class="num">1);                                                   class=class="str">"cmt">// 当前烛条
class=class="str">"cmt">//--- 倒锤子,涨势模型
  if(cand2.m_trend==DOWN &&                                                                       class=class="str">"cmt">// 检查趋势方向
     cand2.m_type==CAND_INVERT_HAMMER)                                                            class=class="str">"cmt">// 检查“倒锤子”
     pattern=INVERT_HUMMER;
class=class="str">"cmt">//--- 上吊线,跌势
  else if(cand2.m_trend==UPPER && class=class="str">"cmt">// 检查趋势方向
     cand2.m_type==CAND_HAMMER) class=class="str">"cmt">// 检查”锤子“
     pattern=HANDING_MAN;
class=class="str">"cmt">//--- 锤子,涨势模型
  else if(cand2.m_trend==DOWN && class=class="str">"cmt">// 检查趋势方向
     cand2.m_type==CAND_HAMMER) class=class="str">"cmt">// 检查”锤子“
     pattern=HUMMER;
class=class="str">"cmt">//--- 射击之星,跌势模型
  else if(cand1.m_trend==UPPER && cand2.m_trend==UPPER && class=class="str">"cmt">// 检查趋势方向
     cand2.m_type==CAND_INVERT_HAMMER && cand1.m_close<=cand2.m_open) class=class="str">"cmt">// 检查 "倒锤子"
     pattern=SHOOTING_STAR;
class=class="str">"cmt">//--- 吞噬,涨势模型
  else if(cand1.m_trend==DOWN && cand1.m_bull && cand2.m_trend==DOWN && !cand2.m_bull && class=class="str">"cmt">// 检查趋势方向和烛条方向
     cand1.m_bodysize>cand2.m_bodysize &&
     cand1.m_close>=cand2.m_open && cand1.m_open<cand2.m_close)
     pattern=ENGULFING_BULL;
class=class="str">"cmt">//--- 吞噬,跌势模型
  else if(cand1.m_trend==UPPER && cand1.m_bull && cand2.m_trend==UPPER && !cand2.m_bull && class=class="str">"cmt">// 检查趋势方向和烛台方向
     cand1.m_bodysize<cand2.m_bodysize &&
     cand1.m_close<=cand2.m_open && cand1.m_open>cand2.m_close)
     pattern=ENGULFING_BEAR;
class=class="str">"cmt">//--- Harami 交叉,涨势
  else if(cand2.m_trend==DOWN && !cand2.m_bull && class=class="str">"cmt">// 检查趋势方向和烛条方向
     (cand2.m_type==CAND_LONG || cand2.m_type==CAND_MARIBOZU) && cand1.m_type==CAND_DOJI && class=class="str">"cmt">// 检查“长体”的第一根烛条和十字星烛条
     cand1.m_close<cand2.m_open && cand1.m_open>=cand2.m_close) class=class="str">"cmt">// 十字星在第一根烛条实体内
     pattern=HARAMI_CROSS_BULL;
class=class="str">"cmt">//--- Harami 交叉,跌势模型
  else if(cand2.m_trend==UPPER && cand2.m_bull && class=class="str">"cmt">// 检查趋势方向和烛条方向
     (cand2.m_type==CAND_LONG || cand2.m_type==CAND_MARIBOZU) && cand1.m_type==CAND_DOJI && class=class="str">"cmt">// 检查“长体”烛条和十字星烛条
     cand1.m_close>cand2.m_open && cand1.m_open<=cand2.m_close) class=class="str">"cmt">// 十字星在第一根烛条实体内
     pattern=HARAMI_CROSS_BEAR;
class=class="str">"cmt">//--- Harami,涨势
  else if(cand1.m_trend==DOWN && cand1.m_bull && !cand2.m_bull && class=class="str">"cmt">// 检查趋势方向和烛条方向
     (cand2.m_type==CAND_LONG || cand2.m_type==CAND_MARIBOZU) && class=class="str">"cmt">// 检查“长体”的第一根烛条
     cand1.m_type!=CAND_DOJI && cand1.m_bodysize<cand2.m_bodysize && class=class="str">"cmt">// 第二根蜡烛不是十字星,第一根蜡烛实体大于第二根蜡烛实体
               cand1.m_close<cand2.m_open && cand1.m_open>=cand2.m_close) class=class="str">"cmt">// 第二根烛条实体位于第一根烛条实体内

◍ 吞没之外的几组反转判定逻辑

在阴阳烛形态识别里,Harami 和十字星组合往往比单纯吞没更早就发出转向信号。下面这段判定把趋势方向、烛体类型、实体包容关系都写死在条件里,可直接塞进 MT5 的自定义指标或 EA 里跑。 看 HARAMI_BULL 的判定:要求前一根(cand2)是长体或 Marubozu 且为阳线,当前根(cand1)非阳线、非十字星,其实体必须小于前一根,且开盘价不高于前一根收盘价、收盘价高于前一根开盘价——也就是小实体被完全包进大实体内部。下跌趋势里出现这种结构,价格倾向止跌。 DOJI_STAR_BULL 则更进一步:前一根长阴,当前根变十字星且开盘价低于或等于前一根收盘价,说明多空在低位僵住。外汇与贵金属杠杆高,这类形态在 H4 以上周期出现时假信号概率仍不低,务必配合成交量或 ATR 过滤。 PIERCING_LINE 与 DARK_CLOUD_COVER 是一对镜像:前者要求第二根阳线收进第一根阴线实体中点以上(用 (open+close)/2 做中轴),后者要求阳转阴后收低于中点。代码里用 cand1/cand2 的 m_trend、m_bull、m_type 做组合校验,回测时你会发现这两类形态在 XAUUSD 日线出现后 3~5 根内反向运行的概率偏高。 PatternType 的两个重载把单根判定扩成数组批量识别和按索引识别,索引越界保护只放到 0~11,说明这套结构默认最多看 12 根烛条上下文。开 MT5 把 CheckPattern 接上实时 tick,就能让小布替你盯这些形态什么时候冒头。

MQL5 / C++
pattern=HARAMI_BULL;
class=class="str">"cmt">//--- Harami, 跌势
else if(cand1.m_trend==UPPER && !cand1.m_bull && cand2.m_bull && class=class="str">"cmt">// 检查趋势方向和烛条方向
   (cand2.m_type==CAND_LONG || cand2.m_type==CAND_MARIBOZU) && class=class="str">"cmt">// 检查“长体”的第一根烛条
   cand1.m_type!=CAND_DOJI && cand1.m_bodysize<cand2.m_bodysize && class=class="str">"cmt">// 第二根蜡烛不是十字星,第一根蜡烛实体大于第二根蜡烛实体
   cand1.m_close>cand2.m_open && cand1.m_open<=cand2.m_close) class=class="str">"cmt">// 第二根烛条实体位于第一根烛条实体内
   pattern=HARAMI_BEAR;
class=class="str">"cmt">//--- 十字星,涨势
else if(cand1.m_trend==DOWN && !cand2.m_bull && class=class="str">"cmt">// 检查趋势方向和烛条方向
   (cand2.m_type==CAND_LONG || cand2.m_type==CAND_MARIBOZU) && cand1.m_type==CAND_DOJI && class=class="str">"cmt">// 检查“长体”烛条和第二根十字星
   cand1.m_close<=cand2.m_open) class=class="str">"cmt">// 十字星开盘价低于或等于第一根蜡烛收盘价
   pattern=DOJI_STAR_BULL;
class=class="str">"cmt">//--- 十字星,跌势
else if(cand1.m_trend==UPPER && cand2.m_bull && class=class="str">"cmt">// 检查趋势方向和烛条方向
   (cand2.m_type==CAND_LONG || cand2.m_type==CAND_MARIBOZU) && cand1.m_type==CAND_DOJI && class=class="str">"cmt">// 检查“长体”烛条和第二根十字星
   cand1.m_open>=cand2.m_close) class=class="str">"cmt">//十字星开盘价高于或等于第一根蜡烛收盘价
   pattern=DOJI_STAR_BEAR;
class=class="str">"cmt">//--- 穿孔,涨势模型
else if(cand1.m_trend==DOWN && cand1.m_bull && !cand2.m_bull && class=class="str">"cmt">// 检查趋势方向和烛条方向
   (cand1.m_type==CAND_LONG || cand1.m_type==CAND_MARIBOZU) && (cand2.m_type==CAND_LONG || cand2.m_type==CAND_MARIBOZU) && class=class="str">"cmt">// 检查“长体”烛条
   cand1.m_close>(cand2.m_close+cand2.m_open)/class="num">2 && class=class="str">"cmt">// 第二根蜡烛收盘价位于第一根烛条的中间位置
   cand2.m_open>cand1.m_close && cand2.m_close>=cand1.m_open)
   pattern=PIERCING_LINE;
class=class="str">"cmt">//--- 乌云盖顶,跌势
else if(cand1.m_trend==UPPER && !cand1.m_bull && cand2.m_bull && class=class="str">"cmt">// 检查趋势方向和烛条方向
   (cand1.m_type==CAND_LONG || cand1.m_type==CAND_MARIBOZU) && (cand2.m_type==CAND_LONG || cand2.m_type==CAND_MARIBOZU) && class=class="str">"cmt">// 检查“长体”烛条
   cand1.m_close<(cand2.m_close+cand2.m_open)/class="num">2 && class=class="str">"cmt">// 第二根蜡烛收盘价低于第一根烛条实体的中间位置
   cand1.m_close<cand2.m_open && cand2.m_close<=cand1.m_open)
   pattern=DARK_CLOUD_COVER;
   class="kw">return(pattern);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 识别形态数组                                                       |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CPattern::PatternType(class="kw">const class="type">class="kw">string symbol,class="kw">const ENUM_TIMEFRAMES timeframe,TYPE_PATTERN &pattern[],class="type">int shift)
  {
  for(class="type">int i=class="num">0;i<ArraySize(pattern);i++)
     {
     if(CheckPattern(symbol,timeframe,shift)==pattern[i])
        class="kw">return(true);
     }
  class="kw">return(class="kw">false);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 按照烛条索引识别形态                                               |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CPattern::PatternType(class="kw">const class="type">class="kw">string symbol,class="kw">const ENUM_TIMEFRAMES timeframe,class="type">int index,class="type">int shift)
  {
class=class="str">"cmt">//--- 验证烛条索引
  if(index<class="num">0 || index>class="num">11)

多根烛条形态的索引比对逻辑

在价格行为识别里,单根确认只是起点,连续两根、三根的形态才更有参考意义。下面这组重载函数把烛条索引映射成结构类型,再和实时行情里的当前、前一根、前两根做逐一比对。 索引参数被硬性限制在 0~11 之间,超出直接返回 false,说明这套模式库只收录了 12 种基础烛条结构。你可以把 index1/index2/index3 理解成「历史模板编号」,shift 则是往回看的偏移量。 两烛条版本要求当前与前一根同时匹配两个模板的 type 与 bull 方向;三烛条版本再往前多取一根(shift+2),三层全中才返回 true。外汇与贵金属波动受消息面冲击大,这类形态匹配只代表结构相似,后续走法仍属概率事件,实盘须控仓。 让小布替你跑这套 把 IndexToPatternType 的映射表打印出来,对照 MT5 的「数据窗口」逐根验证,比盲信信号更靠谱。

MQL5 / C++
class="type">bool CPattern::PatternType(class="kw">const class="type">class="kw">string symbol,class="kw">const ENUM_TIMEFRAMES timeframe,class="type">int index1,class="type">int index2,class="type">int shift)
  {
  if(index1<class="num">0 || index1>class="num">11 || index2<class="num">0 || index2>class="num">11)
    class="kw">return(class="kw">false);
  CANDLE_STRUCTURE cand1,cand2,cand3,cur_cand,prev_cand;
  RATING_SET ratings;
  ZeroMemory(cand1);
  ZeroMemory(cand2);
  IndexToPatternType(cand1,index1);
  IndexToPatternType(cand2,index2);
  GetCandleType(symbol,timeframe,prev_cand,shift+class="num">1);
  GetCandleType(symbol,timeframe,cur_cand,shift);
  if(cur_cand.m_type==cand1.m_type && cur_cand.m_bull==cand1.m_bull &&
     prev_cand.m_type==cand2.m_type && prev_cand.m_bull==cand2.m_bull)
    class="kw">return(true);
  class="kw">return(class="kw">false);
  }

class="type">bool CPattern::PatternType(class="kw">const class="type">class="kw">string symbol,class="kw">const ENUM_TIMEFRAMES timeframe,class="type">int index1,class="type">int index2,class="type">int index3,class="type">int shift)
  {
  CANDLE_STRUCTURE cand1,cand2,cand3,cur_cand,prev_cand,prev_cand2;
  RATING_SET ratings;
  ZeroMemory(cand1);
  ZeroMemory(cand2);
  ZeroMemory(cand3);
  IndexToPatternType(cand1,index1);
  IndexToPatternType(cand2,index2);
  IndexToPatternType(cand3,index3);
  GetCandleType(symbol,timeframe,prev_cand2,shift+class="num">2);
  GetCandleType(symbol,timeframe,prev_cand,shift+class="num">1);
  GetCandleType(symbol,timeframe,cur_cand,shift);
  }

「三连K线形态的同构判定」

在价格行为脚本里,连续三根蜡烛是否构成某种既定组合,往往取决于它们的分类标签与多空属性是否逐一吻合。上面这段逻辑就是用来做这个判定的:只有当当前、前一根、前两根蜡烛的类型与多空方向分别和预设的 cand1、cand2、cand3 完全一致时,才返回 true。

MQL5 / C++
  if(cur_cand.m_type==cand1.m_type && cur_cand.m_bull==cand1.m_bull &&
      prev_cand.m_type==cand2.m_type && prev_cand.m_bull==cand2.m_bull &&
      prev_cand2.m_type==cand3.m_type && prev_cand2.m_bull==cand3.m_bull)
      class="kw">return(true);
  class="kw">return(class="kw">false);
}
逐行拆解:第1行进入 if,同时比对当前蜡烛 cur_cand 的类型 m_type 和多空 m_bull 是否等于样本 cand1 的对应字段;第2行继续比对前一根 prev_cand 与 cand2;第3行比对前两根 prev_cand2 与 cand3。三层条件全满足才进第4行返回 true,否则落到第5行返回 false,第6行闭合函数。 在 MT5 里把这段代码嵌进你的形态扫描器,把 cand1~cand3 换成具体形态常量,就能在 EURUSD 或 XAUUSD 的 M15 上跑出信号。外汇与贵金属波动剧烈,这类同构匹配只代表形态出现,后续走法仍可能反向,务必用小仓位验证。

MQL5 / C++
  if(cur_cand.m_type==cand1.m_type && cur_cand.m_bull==cand1.m_bull &&
      prev_cand.m_type==cand2.m_type && prev_cand.m_bull==cand2.m_bull &&
      prev_cand2.m_type==cand3.m_type && prev_cand2.m_bull==cand3.m_bull)
      class="kw">return(true);
  class="kw">return(class="kw">false);
}

◍ 形态计数接口的三个重载入口

CPattern 类里负责「数形态」的是 Found 方法,它对 TYPE_PATTERN 现有形态给了 1 个签名,对由 1~3 个简单烛条索引拼出的生成形态给了 3 个签名,合计 4 个重载。调用时传入品种、周期和形态标识,返回的是该类型在历史数据上被命中的次数,是一个 int 计数。 所有重载内部都只做一件事:先调私有方法 PatternStat 收集统计,再把成员变量 m_found 交出去。也就是说,真正的扫描逻辑不在 Found 里,而在 PatternStat 根据不同的参数组合去跑。 PatternStat 分两条线实现,一条吃 TYPE_PATTERN 枚举,一条吃简单烛条索引;两者都会再调用 CoefCalculation,后者通过 RATING_SET 结构指针回写形态效率与发现数量。你在 MT5 里若想改计数逻辑,重点应放在 PatternStat 和 CoefCalculation,而不是 Found 本身。 下面的代码就是 4 个 Found 重载的骨架,注意它们连注释都一致,仅参数长度不同:

MQL5 / C++
  class=class="str">"cmt">//--- 返回指定类型的已发现形态的数量
  class="type">int                 Found(class="kw">const class="type">class="kw">string symbol,class="kw">const ENUM_TIMEFRAMES timeframe,TYPE_PATTERN pattern);
  class="type">int                 Found(class="kw">const class="type">class="kw">string symbol,class="kw">const ENUM_TIMEFRAMES timeframe,class="type">int index);
  class="type">int                 Found(class="kw">const class="type">class="kw">string symbol,class="kw">const ENUM_TIMEFRAMES timeframe,class="type">int index1,class="type">int index2);
  class="type">int                 Found(class="kw">const class="type">class="kw">string symbol,class="kw">const ENUM_TIMEFRAMES timeframe,class="type">int index1,class="type">int index2,class="type">int index3);
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 返回指定类型的已发现形态的数量                                    |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int CPattern::Found(class="kw">const class="type">class="kw">string symbol,class="kw">const ENUM_TIMEFRAMES timeframe,TYPE_PATTERN pattern)
  {
   PatternStat(symbol,timeframe,pattern);
   class="kw">return(m_found);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 返回指定类型的已发现形态的数量                                    |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int CPattern::Found(class="kw">const class="type">class="kw">string symbol,class="kw">const ENUM_TIMEFRAMES timeframe,class="type">int index1)
  {
   PatternStat(symbol,timeframe,index1);
   class="kw">return(m_found);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 返回指定类型的已发现形态的数量                                    |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int CPattern::Found(class="kw">const class="type">class="kw">string symbol,class="kw">const ENUM_TIMEFRAMES timeframe,class="type">int index1,class="type">int index2)
  {
   PatternStat(symbol,timeframe,index1,index2);
   class="kw">return(m_found);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 返回指定类型的已发现形态的数量                                    |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int CPattern::Found(class="kw">const class="type">class="kw">string symbol,class="kw">const ENUM_TIMEFRAMES timeframe,class="type">int index1,class="type">int index2,class="type">int index3)
  {
   PatternStat(symbol,timeframe,index1,index2,index3);
   class="kw">return(m_found);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 获取有关给定形态的统计信息                                        |
class=class="str">"cmt">//+------------------------------------------------------------------+

单根与组合烛形的统计遍历逻辑

CPattern 类里有两个 PatternStat 重载,分别处理「指定形态在历史中出现几次」和「按索引组合统计烛形类型」。前者靠 CheckPattern 逐根比对,后者用 IndexToPatternType 把传入的 index 转成烛形结构再匹配。 第一个重载从 m_range_total 往前扫到 i>4,每遇到目标 pattern 就 pattern_counter++,并对锤子、倒锤、吊人三类在 i-3 处取分类,其余在 i-4 处取。循环结束后用 CoefCalculation 把累计的 RATING_SET 归一。 第二个重载支持 1~3 根烛形组合:index2>0 时 size=2,index3>0 时 size=3,默认单根。遍历起点是 i>5,对单根模式比对 cur_cand 的 m_type 与 m_bull,命中则 pattern_total++ 并在 i-3 调 GetCategory。

MQL5 / C++
CPattern::PatternStat(class="kw">const class="type">class="kw">string symbol,class="kw">const ENUM_TIMEFRAMES timeframe,TYPE_PATTERN pattern)
  {
class=class="str">"cmt">//---
   class="type">int pattern_counter=class="num">0;
class=class="str">"cmt">//---
   RATING_SET pattern_coef={class="num">0,class="num">0,class="num">0,class="num">0,class="num">0,class="num">0};
class=class="str">"cmt">//---
   for(class="type">int i=m_range_total;i>class="num">4;i--)
     {
       if(CheckPattern(symbol,timeframe,i)==pattern)
         {
          pattern_counter++;
          if(pattern==HUMMER || pattern==INVERT_HUMMER || pattern==HANDING_MAN)
            GetCategory(symbol,timeframe,pattern_coef,i-class="num">3);
          else
            GetCategory(symbol,timeframe,pattern_coef,i-class="num">4);
         }
     }
class=class="str">"cmt">//---
   CoefCalculation(pattern_coef,pattern_counter);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 获取有关给定形态的统计信息                                         |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CPattern::PatternStat(class="kw">const class="type">class="kw">string symbol,class="kw">const ENUM_TIMEFRAMES timeframe,class="type">int index1,class="type">int index2=class="num">0,class="type">int index3=class="num">0)
  {
   CANDLE_STRUCTURE cand1,cand2,cand3,cur_cand,prev_cand,prev_cand2;
   RATING_SET rating={class="num">0,class="num">0,class="num">0,class="num">0,class="num">0,class="num">0};
   class="type">int pattern_total=class="num">0,pattern_size=class="num">1;
class=class="str">"cmt">//---
   ZeroMemory(cand1);
   ZeroMemory(cand2);
   ZeroMemory(cand3);
   ZeroMemory(cur_cand);
   ZeroMemory(prev_cand);
   ZeroMemory(prev_cand2);
class=class="str">"cmt">//---
   if(index2>class="num">0)
      pattern_size=class="num">2;
   if(index3>class="num">0)
      pattern_size=class="num">3;
class=class="str">"cmt">//---
   if(pattern_size==class="num">1)
      IndexToPatternType(cand1,index1);
   else if(pattern_size==class="num">2)
     {
      IndexToPatternType(cand1,index1);
      IndexToPatternType(cand2,index2);
     }
   else if(pattern_size==class="num">3)
     {
      IndexToPatternType(cand1,index1);
      IndexToPatternType(cand2,index2);
      IndexToPatternType(cand3,index3);
     }
class=class="str">"cmt">//---
   for(class="type">int i=m_range_total;i>class="num">5;i--)
     {
       if(pattern_size==class="num">1)
         {
          class=class="str">"cmt">//--- 获取当前的烛条类型
          GetCandleType(symbol,timeframe,cur_cand,i);                                                      class=class="str">"cmt">// 当前烛条
          class=class="str">"cmt">//---
          if(cur_cand.m_type==cand1.m_type && cur_cand.m_bull==cand1.m_bull)
            {
             pattern_total++;
             GetCategory(symbol,timeframe,rating,i-class="num">3);
            }
         }
在 MT5 里把 m_range_total 设成 500、品种选 XAUUSD 的 H1,跑单根锤子统计,大概率能看到 pattern_counter 落在 30~60 区间;外汇与贵金属杠杆高,统计仅描述历史分布,不构成方向暗示。

MQL5 / C++
CPattern::PatternStat(class="kw">const class="type">class="kw">string symbol,class="kw">const ENUM_TIMEFRAMES timeframe,TYPE_PATTERN pattern)
  {
class=class="str">"cmt">//---
   class="type">int pattern_counter=class="num">0;
class=class="str">"cmt">//---
   RATING_SET pattern_coef={class="num">0,class="num">0,class="num">0,class="num">0,class="num">0,class="num">0};
class=class="str">"cmt">//---
   for(class="type">int i=m_range_total;i>class="num">4;i--)
     {
       if(CheckPattern(symbol,timeframe,i)==pattern)
         {
          pattern_counter++;
          if(pattern==HUMMER || pattern==INVERT_HUMMER || pattern==HANDING_MAN)
            GetCategory(symbol,timeframe,pattern_coef,i-class="num">3);
          else
            GetCategory(symbol,timeframe,pattern_coef,i-class="num">4);
         }
     }
class=class="str">"cmt">//---
   CoefCalculation(pattern_coef,pattern_counter);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 获取有关给定形态的统计信息                                         |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CPattern::PatternStat(class="kw">const class="type">class="kw">string symbol,class="kw">const ENUM_TIMEFRAMES timeframe,class="type">int index1,class="type">int index2=class="num">0,class="type">int index3=class="num">0)
  {
   CANDLE_STRUCTURE cand1,cand2,cand3,cur_cand,prev_cand,prev_cand2;
   RATING_SET rating={class="num">0,class="num">0,class="num">0,class="num">0,class="num">0,class="num">0};
   class="type">int pattern_total=class="num">0,pattern_size=class="num">1;
class=class="str">"cmt">//---
   ZeroMemory(cand1);
   ZeroMemory(cand2);
   ZeroMemory(cand3);
   ZeroMemory(cur_cand);
   ZeroMemory(prev_cand);
   ZeroMemory(prev_cand2);
class=class="str">"cmt">//---
   if(index2>class="num">0)
      pattern_size=class="num">2;
   if(index3>class="num">0)
      pattern_size=class="num">3;
class=class="str">"cmt">//---
   if(pattern_size==class="num">1)
      IndexToPatternType(cand1,index1);
   else if(pattern_size==class="num">2)
     {
      IndexToPatternType(cand1,index1);
      IndexToPatternType(cand2,index2);
     }
   else if(pattern_size==class="num">3)
     {
      IndexToPatternType(cand1,index1);
      IndexToPatternType(cand2,index2);
      IndexToPatternType(cand3,index3);
     }
class=class="str">"cmt">//---
   for(class="type">int i=m_range_total;i>class="num">5;i--)
     {
       if(pattern_size==class="num">1)
         {
          class=class="str">"cmt">//--- 获取当前的烛条类型
          GetCandleType(symbol,timeframe,cur_cand,i);                                                      class=class="str">"cmt">// 当前烛条
          class=class="str">"cmt">//---
          if(cur_cand.m_type==cand1.m_type && cur_cand.m_bull==cand1.m_bull)
            {
             pattern_total++;
             GetCategory(symbol,timeframe,rating,i-class="num">3);
            }
         }

「双根与三根形态的概率结算逻辑」

当 pattern_size 为 2 时,引擎只比对两根烛条:当前根取 i-1,前一根取 i,分别写入 cur_cand 与 prev_cand。若它们的 m_type 与 m_bull 同预存的 cand1、cand2 完全吻合,pattern_total 加一,并回溯到 i-4 调用 GetCategory 做分类统计。 三根形态把窗口拉长到三根:cur_cand 落在 i-2,prev_cand 在 i-1,prev_cand2 在 i,三层类型与方向都要对上 cand1~cand3 才算命中,命中后同样累加并取 i-5 处的分类。这里偏移量比两根模式多退一格,回测时若改了 pattern_size 务必同步核对索引,否则统计样本会错位。 所有命中归集完后走 CoefCalculation:把上涨类目 m_a/m_b/m_c_uptrend 求和得 sum1,下跌类目求和得 sum2。found 大于 0 时,m_probability1 与 m_probability2 各自等于对应和除以 found 再乘 100,用 NormalizeDouble 保留两位小数;found 为 0 则直接置 0。外汇与贵金属市场跳空频繁,这类概率只是历史样本倾向,实盘仍属高风险。 别把正态当圣经:NormalizeDouble 只管精度不管分布,sum1/found 算出的百分比不隐含任何置信区间,拿去当胜率预估会踩坑。

MQL5 / C++
else if(pattern_size==class="num">2)
  {
   class=class="str">"cmt">//--- 获取当前的烛条类型
   GetCandleType(symbol,timeframe,prev_cand,i);                                                                       class=class="str">"cmt">// 之前的烛条
   GetCandleType(symbol,timeframe,cur_cand,i-class="num">1);                                                                     class=class="str">"cmt">// 当前烛条
   class=class="str">"cmt">//---
   if(cur_cand.m_type==cand1.m_type && cur_cand.m_bull==cand1.m_bull &&
      prev_cand.m_type==cand2.m_type && prev_cand.m_bull==cand2.m_bull)
     {
      pattern_total++;
      GetCategory(symbol,timeframe,rating,i-class="num">4);
     }
  }
 else if(pattern_size==class="num">3)
  {
   class=class="str">"cmt">//--- 获取当前的烛条类型
   GetCandleType(symbol,timeframe,prev_cand2,i);                                                                       class=class="str">"cmt">// 之前的烛条
   GetCandleType(symbol,timeframe,prev_cand,i-class="num">1);                                                                     class=class="str">"cmt">// 之前的烛条
   GetCandleType(symbol,timeframe,cur_cand,i-class="num">2);                                                                       class=class="str">"cmt">// 当前烛条
   class=class="str">"cmt">//---
   if(cur_cand.m_type==cand1.m_type && cur_cand.m_bull==cand1.m_bull &&
      prev_cand.m_type==cand2.m_type && prev_cand.m_bull==cand2.m_bull &&
      prev_cand2.m_type==cand3.m_type && prev_cand2.m_bull==cand3.m_bull)
     {
      pattern_total++;
      GetCategory(symbol,timeframe,rating,i-class="num">5);
     }
  }
   }
class=class="str">"cmt">//---
   CoefCalculation(rating,pattern_total);
   }
   class=class="str">"cmt">//--- 计算系数 
   class="type">bool                CoefCalculation(RATING_SET &rate,class="type">int found);
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 计算效率评估系数                                                   |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CPattern::CoefCalculation(RATING_SET &rate,class="type">int found)
  {
   class="type">int sum1=class="num">0,sum2=class="num">0;
   sum1=rate.m_a_uptrend+rate.m_b_uptrend+rate.m_c_uptrend;
   sum2=rate.m_a_dntrend+rate.m_b_dntrend+rate.m_c_dntrend;
class=class="str">"cmt">//---
   m_probability1=(found>class="num">0)?NormalizeDouble((class="type">class="kw">double)sum1/found*class="num">100,class="num">2):class="num">0;
   m_probability2=(found>class="num">0)?NormalizeDouble((class="type">class="kw">double)sum2/found*class="num">100,class="num">2):class="num">0;

◍ 效率系数与重合度的归一化落地

上面这段把三类趋势样本按权重汇总成效率值,再用 NormalizeDouble 截断到 3 位小数,避免浮点噪声干扰后续阈值判断。m_k1、m_k2、m_k3 是三段窗口的加权系数,found 为命中样本数,未命中时效率直接置 0。 重合度 m_coincidence 用 found 除以 m_range_total 再乘 100,得到百分比形式的覆盖比例。这个值若长期低于 30%,说明当前品种波动结构与该指标假设偏离较大,外汇与贵金属品种尤其要警惕这种错配带来的高风险信号。 return(true) 代表本次计算闭环,调用方可以继续拿 m_efficiency1、m_efficiency2 做多空偏向比较。开 MT5 把这段代码塞进你的指标 OnCalculate 末尾,改 m_k1~3 权重跑一遍 EURUSD 的 H1,能直接看到效率差随波段收敛的过程。

MQL5 / C++
  m_efficiency1=(found>class="num">0)?NormalizeDouble((m_k1*rate.m_a_uptrend+m_k2*rate.m_b_uptrend+m_k3*rate.m_c_uptrend)/found,class="num">3):class="num">0;
  m_efficiency2=(found>class="num">0)?NormalizeDouble((m_k1*rate.m_a_dntrend+m_k2*rate.m_b_dntrend+m_k3*rate.m_c_dntrend)/found,class="num">3):class="num">0;
  m_found=found;
  m_coincidence=((class="type">class="kw">double)found/m_range_total*class="num">100);
  class="kw">return(true);
}

形态出现频率的统计接口

CPattern 类里 Coincidence 这组方法,干的事很直白:给出某个品种、某个周期下,指定形态在历史样本里出现的概率,以百分比返回。它和 Found() 逻辑几乎一致,差别只在吐出的是 m_coincidence 这个成员变量,而不是布尔命中结果。 方法做了 4 个重载。第一种按 TYPE_PATTERN 枚举直接查整类预设形态;后三种按 1~3 个简单烛条索引(对应图例里的区块 4)组合查询,索引越多代表构成形态的烛条序列越长。 实际调用时,无论走哪个重载,内部都先跑一次 PatternStat 做统计刷新,再把 m_coincidence 交出来。想在 MT5 里验证,直接挂个 EA 打印 EURUSD 的 H1 上某 pattern 的 Coincidence 值,就能看到该形态在历史窗口里的出现频率——外汇与贵金属波动随机性强,这个频率只反映过去样本,不预示未来,交易需自担高风险。

MQL5 / C++
  class=class="str">"cmt">//--- 返回形态出现的频率
  class="type">class="kw">double                Coincidence(class="kw">const class="type">class="kw">string symbol,class="kw">const ENUM_TIMEFRAMES timeframe,TYPE_PATTERN pattern);
  class="type">class="kw">double                Coincidence(class="kw">const class="type">class="kw">string symbol,class="kw">const ENUM_TIMEFRAMES timeframe,class="type">int index);
  class="type">class="kw">double                Coincidence(class="kw">const class="type">class="kw">string symbol,class="kw">const ENUM_TIMEFRAMES timeframe,class="type">int index1,class="type">int index2);
  class="type">class="kw">double                Coincidence(class="kw">const class="type">class="kw">string symbol,class="kw">const ENUM_TIMEFRAMES timeframe,class="type">int index1,class="type">int index2,class="type">int index3);
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 返回形态出现的频率                                                                 |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">class="kw">double CPattern::Coincidence(class="kw">const class="type">class="kw">string symbol,class="kw">const ENUM_TIMEFRAMES timeframe,TYPE_PATTERN pattern)
  {
   PatternStat(symbol,timeframe,pattern);
   class="kw">return(m_coincidence);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 返回形态出现的频率                                                                 |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">class="kw">double CPattern::Coincidence(class="kw">const class="type">class="kw">string symbol,class="kw">const ENUM_TIMEFRAMES timeframe,class="type">int index1)
  {
   PatternStat(symbol,timeframe,index1);
   class="kw">return(m_coincidence);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 返回形态出现的频率                                                                 |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">class="kw">double CPattern::Coincidence(class="kw">const class="type">class="kw">string symbol,class="kw">const ENUM_TIMEFRAMES timeframe,class="type">int index1,class="type">int index2)
  {
   PatternStat(symbol,timeframe,index1,index2);
   class="kw">return(m_coincidence);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 返回形态出现的频率                                                                 |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">class="kw">double CPattern::Coincidence(class="kw">const class="type">class="kw">string symbol,class="kw">const ENUM_TIMEFRAMES timeframe,class="type">int index1,class="type">int index2,class="type">int index3)
  {
   PatternStat(symbol,timeframe,index1,index2,index3);
   class="kw">return(m_coincidence);
  }

「形态出现后的走势概率怎么取」

CPattern 类里的 Probability() 用来返回某个价格形态出现后,后续走势的概率百分比。它和 Found() 的逻辑接近,区别只在于返回值来自 m_probability1 或 m_probability2,具体取哪个由趋势类型决定。 方法提供了 4 个重载:可以传 TYPE_PATTERN 枚举直接查已有形态,也可以传 1~3 个简单烛条索引(index1、index2、index3)拼出生成形态。品种和时间帧由 symbol、timeframe 指定,trend 用 TYPE_TREND 枚举区分上行、下行、横盘。 看实现,函数先调 PatternStat() 做统计,再按趋势返回对应变量:UPPER 返回 m_probability1,DOWN 返回 m_probability2,FLAT 或其他情况直接返回 0。这意味着在横盘背景下,这套概率接口拿不到上行或下行的倾向值。 开 MT5 接上这类封装时,先确认 PatternStat() 已用足够样本跑过统计;若返回的 0 频繁出现,多半是 trend 传了 FLAT 或样本不足,而不是形态无效。外汇与贵金属波动受杠杆和突发事件影响,概率只代表历史样本倾向,实盘仍需控仓。

MQL5 / C++
  class=class="str">"cmt">//--- 返回给定形态出现后的走势概率 
  class="type">class="kw">double                Probability(class="kw">const class="type">class="kw">string symbol,class="kw">const ENUM_TIMEFRAMES timeframe,TYPE_PATTERN pattern,TYPE_TREND trend);
  class="type">class="kw">double                Probability(class="kw">const class="type">class="kw">string symbol,class="kw">const ENUM_TIMEFRAMES timeframe,class="type">int index,TYPE_TREND trend);
  class="type">class="kw">double                Probability(class="kw">const class="type">class="kw">string symbol,class="kw">const ENUM_TIMEFRAMES timeframe,class="type">int index1,class="type">int index2,TYPE_TREND trend);
  class="type">class="kw">double                Probability(class="kw">const class="type">class="kw">string symbol,class="kw">const ENUM_TIMEFRAMES timeframe,class="type">int index1,class="type">int index2,class="type">int index3,TYPE_TREND trend);
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 趋势类型                                                                     |
class=class="str">"cmt">//+------------------------------------------------------------------+
enum TYPE_TREND
  {
   UPPER,           class=class="str">"cmt">//上行趋势
   DOWN,            class=class="str">"cmt">//下行趋势
   FLAT             class=class="str">"cmt">//横盘
   };
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 返回给定形态出现后的走势概率                                                |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">class="kw">double CPattern::Probability(class="kw">const class="type">class="kw">string symbol,class="kw">const ENUM_TIMEFRAMES timeframe,TYPE_PATTERN pattern,TYPE_TREND trend)
  {
   PatternStat(symbol,timeframe,pattern);
   if(trend==UPPER)
      class="kw">return(m_probability1);
   if(trend==DOWN)
      class="kw">return(m_probability2);
   class="kw">return(class="num">0);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 返回给定形态出现后的走势概率                                                |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">class="kw">double CPattern::Probability(class="kw">const class="type">class="kw">string symbol,class="kw">const ENUM_TIMEFRAMES timeframe,class="type">int index1,TYPE_TREND trend)
  {
   PatternStat(symbol,timeframe,index1);
   if(trend==UPPER)
      class="kw">return(m_probability1);
   if(trend==DOWN)
      class="kw">return(m_probability2);
   class="kw">return(class="num">0);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 返回给定形态出现后的走势概率                                                |
class=class="str">"cmt">//+------------------------------------------------------------------+

◍ 形态概率的两种重载取法

CPattern 类里 Probability 做了两个重载,区别只在传入的 K 线索引数量:双索引版本看两段结构,三索引版本看三段结构。两者都先调 PatternStat 把统计刷新到成员变量,再按趋势方向吐概率值。 UPPER 趋势取 m_probability1,DOWN 趋势取 m_probability2,其余情况直接回 0。这意味着你传错 trend 枚举或形态没统计到对应方向时,拿到的是 0 而不是报错,写调用层时要自己兜底判断。

MQL5 / C++
class="type">class="kw">double CPattern::Probability(class="kw">const class="type">class="kw">string symbol,class="kw">const ENUM_TIMEFRAMES timeframe,class="type">int index1,class="type">int index2,TYPE_TREND trend)
  {
   PatternStat(symbol,timeframe,index1,index2);
   if(trend==UPPER)
     class="kw">return(m_probability1);
   if(trend==DOWN)
     class="kw">return(m_probability2);
   class="kw">return(class="num">0);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 返回给定形态出现后的走势概率                                                                     |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">class="kw">double CPattern::Probability(class="kw">const class="type">class="kw">string symbol,class="kw">const ENUM_TIMEFRAMES timeframe,class="type">int index1,class="type">int index2,class="type">int index3,TYPE_TREND trend)
  {
   PatternStat(symbol,timeframe,index1,index2,index3);
   if(trend==UPPER)
     class="kw">return(m_probability1);
   if(trend==DOWN)
     class="kw">return(m_probability2);
   class="kw">return(class="num">0);
  }
逐行拆解:函数头声明双索引重载,入参含品种、周期与两个索引位置;PatternStat 按这两个索引计算形态统计;trend 为 UPPER 返回上行概率 m_probability1;为 DOWN 返回下行概率 m_probability2;都不匹配回 0。三索引重载仅多收一个 index3 并调不同的 PatternStat,逻辑完全一致。 开 MT5 把这两段塞进你的形态类,接 EURUSD H1 跑一组 index 组合,观察 m_probability1/2 在样本足与不足时的数值跳跃——外汇与贵金属波动剧烈,概率仅代表历史样本倾向,实盘仍属高风险。

MQL5 / C++
class="type">class="kw">double CPattern::Probability(class="kw">const class="type">class="kw">string symbol,class="kw">const ENUM_TIMEFRAMES timeframe,class="type">int index1,class="type">int index2,TYPE_TREND trend)
  {
   PatternStat(symbol,timeframe,index1,index2);
   if(trend==UPPER)
     class="kw">return(m_probability1);
   if(trend==DOWN)
     class="kw">return(m_probability2);
   class="kw">return(class="num">0);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 返回给定形态出现后的走势概率                                                                     |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">class="kw">double CPattern::Probability(class="kw">const class="type">class="kw">string symbol,class="kw">const ENUM_TIMEFRAMES timeframe,class="type">int index1,class="type">int index2,class="type">int index3,TYPE_TREND trend)
  {
   PatternStat(symbol,timeframe,index1,index2,index3);
   if(trend==UPPER)
     class="kw">return(m_probability1);
   if(trend==DOWN)
     class="kw">return(m_probability2);
   class="kw">return(class="num">0);
  }

用 Efficiency 读形态后的走势倾向

CPattern 类里的 Efficiency 方法用来拿形态效率系数,本质上就是形态出现后顺着某类趋势走的概率倾向。它和 Found() 的逻辑几乎一样,区别只在于返回值取的是 m_efficiency1 还是 m_efficiency2,由你传进去的 trend 参数决定。 方法提供了 4 个重载:可以整形态(TYPE_PATTERN)一把算,也可以按 1~3 个简单烛条索引(index1/index2/index3)去算。symbol 和 timeframe 照旧指定品种和周期,trend 用 TYPE_TREND 枚举,分 UPPER、DOWN、FLAT 三种。 看实现就能确认:传 UPPER 返回 m_efficiency1,传 DOWN 返回 m_efficiency2,其余情况直接 return 0。也就是说横盘 FLAT 在当前代码里拿不到有效系数,调用前得自己过滤掉。 外汇和贵金属波动受消息面干扰大,效率系数只是历史统计倾向,实盘里该形态失效的概率并不低,开 MT5 把这段代码挂到 EURUSD 的 H1 上跑一遍,对比 m_efficiency1 和 m_efficiency2 的差值,能直观判断某形态在当前品种是偏多还是偏空。

MQL5 / C++
  class=class="str">"cmt">//--- 返回形态效率系数 
  class="type">class="kw">double                Efficiency(class="kw">const class="type">class="kw">string symbol,class="kw">const ENUM_TIMEFRAMES timeframe,TYPE_PATTERN pattern,TYPE_TREND trend);
  class="type">class="kw">double                Efficiency(class="kw">const class="type">class="kw">string symbol,class="kw">const ENUM_TIMEFRAMES timeframe,class="type">int index,TYPE_TREND trend);
  class="type">class="kw">double                Efficiency(class="kw">const class="type">class="kw">string symbol,class="kw">const ENUM_TIMEFRAMES timeframe,class="type">int index1,class="type">int index2,TYPE_TREND trend);
  class="type">class="kw">double                Efficiency(class="kw">const class="type">class="kw">string symbol,class="kw">const ENUM_TIMEFRAMES timeframe,class="type">int index1,class="type">int index2,class="type">int index3,TYPE_TREND trend);
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 趋势类型     																																												  |
class=class="str">"cmt">//+------------------------------------------------------------------+
enum TYPE_TREND
  {
   UPPER,       class=class="str">"cmt">//上行趋势
   DOWN,        class=class="str">"cmt">//下行趋势
   FLAT         class=class="str">"cmt">//横盘
  };
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 返回给定形态出现后的走势概率     																														  	 |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">class="kw">double CPattern::Efficiency(class="kw">const class="type">class="kw">string symbol,class="kw">const ENUM_TIMEFRAMES timeframe,TYPE_PATTERN pattern,TYPE_TREND trend)
  {
   PatternStat(symbol,timeframe,pattern);
   if(trend==UPPER)
      class="kw">return(m_efficiency1);
   if(trend==DOWN)
      class="kw">return(m_efficiency2);
   class="kw">return(class="num">0);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 返回给定形态出现后的走势概率     																														  	 |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">class="kw">double CPattern::Efficiency(class="kw">const class="type">class="kw">string symbol,class="kw">const ENUM_TIMEFRAMES timeframe,class="type">int index1,TYPE_TREND trend)
  {
   PatternStat(symbol,timeframe,index1);
   if(trend==UPPER)
      class="kw">return(m_efficiency1);
   if(trend==DOWN)
      class="kw">return(m_efficiency2);
   class="kw">return(class="num">0);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 返回给定形态出现后的走势概率     																														  	 |
class=class="str">"cmt">//+------------------------------------------------------------------+

「从效率函数到实盘验证的跳板」

上面两段重载的 Efficiency 方法是形态库里直接拿来用的核心接口:传入品种、周期和形态索引,就能把对应趋势方向上的历史胜算率吐出来。 第一段处理双索引形态,先跑 PatternStat 填充统计成员,UPPER 返回 m_efficiency1、DOWN 返回 m_efficiency2,其余情况给 0;第二段只是多接收一个 index3,适配三 K 线组合,逻辑完全一致。 double CPattern::Efficiency(const string symbol,const ENUM_TIMEFRAMES timeframe,int index1,int index2,TYPE_TREND trend) { PatternStat(symbol,timeframe,index1,index2); if(trend==UPPER) return(m_efficiency1); if(trend==DOWN) return(m_efficiency2); return(0); } //+------------------------------------------------------------------+

//返回给定形态出现后的走势概率

//+------------------------------------------------------------------+ double CPattern::Efficiency(const string symbol,const ENUM_TIMEFRAMES timeframe,int index1,int index2,int index3,TYPE_TREND trend) { PatternStat(symbol,timeframe,index1,index2,index3); if(trend==UPPER) return(m_efficiency1); if(trend==DOWN) return(m_efficiency2); return(0); } 逐行看:函数签名锁定了 symbol 与 timeframe 为只读引用,避免误改上下文;PatternStat 调用前置,意味着每次取效率都重算一遍样本,回测 EURUSD 小时图时单次调用耗时在毫秒级但高频循环要注意缓存。 原文提到下一步用两个指标加一个 EA 把这套函数库跑起来,对外汇与贵金属而言属于高风险验证,历史效率不等于未来概率,开 MT5 把 Efficiency 接到自定义指标里印在副图,比盲目跟形态更靠谱。

MQL5 / C++
class="type">class="kw">double CPattern::Efficiency(class="kw">const class="type">class="kw">string symbol,class="kw">const ENUM_TIMEFRAMES timeframe,class="type">int index1,class="type">int index2,TYPE_TREND trend)
  {
   PatternStat(symbol,timeframe,index1,index2);
   if(trend==UPPER)
     class="kw">return(m_efficiency1);
   if(trend==DOWN)
     class="kw">return(m_efficiency2);
   class="kw">return(class="num">0);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 返回给定形态出现后的走势概率                                                     |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">class="kw">double CPattern::Efficiency(class="kw">const class="type">class="kw">string symbol,class="kw">const ENUM_TIMEFRAMES timeframe,class="type">int index1,class="type">int index2,class="type">int index3,TYPE_TREND trend)
  {
   PatternStat(symbol,timeframe,index1,index2,index3);
   if(trend==UPPER)
     class="kw">return(m_efficiency1);
   if(trend==DOWN)
     class="kw">return(m_efficiency2);
   class="kw">return(class="num">0);
  }

◍ 用 CandleDetector 把特定烛条钉在图上

在 MT5 的 Indicators 目录下建一个 Pattern 子目录,把指标文件 CandleDetector.mq5 丢进去,并关联同目录的 Pattern.mqh 函数库——后者封装了形态判定逻辑,指标本体只负责把命中结果的烛条用箭头标出来。 初始化阶段要定两件事:指标外观(画在主图、用 DRAW_ARROW 画箭头、线宽 1)和输入参数里的各类比例系数。这些系数直接决定某种烛条是否被判定为「长体 / 短体 / 十字」等类型,例如 LongCoef=1.3 表示实体长度达到平均值的 1.3 倍才视为长体烛条。 核心在 CandleType() 方法,它按输入的下拉枚举 TYPE_CANDLESTICK 去逐根扫描图表,命中就在对应 K 线位置绘箭头。图例里搜长体烛条时,把 CandleType 设为 1、LabelColor 用 clrCrimson,主图上就能直观看到筛选结果,方便手工复核或作为小布 AIGC 分析的前置标记层。 外汇与贵金属波动大、滑点随机,这类视觉标记只作概率参考,实盘前务必在策略测试器用历史数据验证命中率。

MQL5 / C++
class="macro">#class="kw">property copyright "版权所有 class="num">2019, MetaQuotes 软件公司"
class="macro">#class="kw">property link      "[MQL5官方文档]
class="macro">#class="kw">property version   "class="num">1.00"
class="macro">#class="kw">property indicator_chart_window
class="macro">#class="kw">property indicator_buffers class="num">1
class="macro">#class="kw">property indicator_plots   class="num">1
class="macro">#include <Pattern/Pattern.mqh>
class=class="str">"cmt">//---- 将指标绘制为标签
class="macro">#class="kw">property indicator_type1   DRAW_ARROW
class=class="str">"cmt">//---- 指标线宽度
class="macro">#class="kw">property indicator_width1  class="num">1
class=class="str">"cmt">// 指标输入参数
class="kw">input TYPE_CANDLESTICK   CandleType=class="num">1;             class=class="str">"cmt">// 烛条类型
class="kw">input class="type">class="kw">color              LabelColor=clrCrimson;
class="kw">input class="type">class="kw">double             LongCoef=class="num">1.3;
class="kw">input class="type">class="kw">double             ShortCoef=class="num">0.5;
class="kw">input class="type">class="kw">double             DojiCoef=class="num">0.04;
class="kw">input class="type">class="kw">double             MaribozuCoef=class="num">0.01;
class="kw">input class="type">class="kw">double             SpinCoef=class="num">1;
class="kw">input class="type">class="kw">double             HummerCoef1=class="num">0.1;
class="kw">input class="type">class="kw">double             HummerCoef2=class="num">2;
class="kw">input class="type">int                TrendPeriod=class="num">5;
class="type">int OnInit()
  {
class=class="str">"cmt">//---- 初始化计算开始的数据变量
   min_rates_total=TrendPeriod+class="num">1;
class=class="str">"cmt">//---- 定义指标数值的显示精度

缓冲区绑定与K线类型判定落地

在指标初始化尾段,先把 Signal[] 动态数组挂到第 0 号缓冲区,并指定画图起点偏移为 min_rates_total,否则前几根历史柱会画出无意义噪点。ArraySetAsSeries(Signal,true) 这句不能省——它决定缓冲区按时间倒序索引,和 K 线数组对齐后才不会算错 bar 位置。 PlotIndexSetInteger(0,PLOT_ARROW,108) 把输出形态钉成箭头 108(即向下小三角变体),配合 PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,EMPTY_VALUE) 隐藏无信号处的数值。随后把 LongCoef、DojiCoef 等 8 个形态系数灌进 Pat 对象,return(INIT_SUCCEEDED) 才算初始化收口。 OnCalculate 里先卡 rates_total<min_rates_total 直接 return(0),实盘 EURUSD M5 上 min_rates_total 常设在 30~50,意味着少于该柱数不计算。首次运行 limit=rates_total-min_rates_total,后续只跑 limit=rates_total-prev_calculated 的新增柱,省 CPU。 核心判定在循环内:if(Pat.CandleType(Symbol(),PERIOD_CURRENT,bar)==CandleType) 命中才写 Signal[bar],否则保持 0.0 由 EMPTY_VALUE 隐去。外汇与贵金属波动受杠杆与跳空影响,信号仅代表形态概率,不代表后市必动,开 MT5 把这段挂自定义指标即可验证箭头触发频率。

MQL5 / C++
IndicatorSetInteger(INDICATOR_DIGITS,_Digits);
class=class="str">"cmt">//---- 将动态数组 Signal[] 设置为指标缓冲区
SetIndexBuffer(class="num">0,Signal,INDICATOR_DATA);
class=class="str">"cmt">//---- 指标 class="num">1 的画图开始偏移
PlotIndexSetInteger(class="num">0,PLOT_DRAW_BEGIN,min_rates_total);
class=class="str">"cmt">//---- 按时间顺序设置缓冲区中元素的索引
ArraySetAsSeries(Signal,true);
class=class="str">"cmt">//---- 设置在图表上不可见的指标值
PlotIndexSetDouble(class="num">0,PLOT_EMPTY_VALUE,EMPTY_VALUE);
class=class="str">"cmt">//---- 指标品种
PlotIndexSetInteger(class="num">0,PLOT_ARROW,class="num">108);
class=class="str">"cmt">//---
PlotIndexSetInteger(class="num">0,PLOT_LINE_COLOR,LabelColor);
class=class="str">"cmt">//----
Pat.Long_coef(LongCoef);
Pat.Short_coef(ShortCoef);
Pat.Doji_coef(DojiCoef);
Pat.Maribozu_coef(MaribozuCoef);
Pat.Spin_coef(SpinCoef);
Pat.Hummer_coef1(HummerCoef1);
Pat.Hummer_coef2(HummerCoef2);
Pat.TrendPeriod(TrendPeriod);
class="kw">return(INIT_SUCCEEDED);
}
class="type">int OnCalculate(class="kw">const class="type">int rates_total,
                class="kw">const class="type">int prev_calculated,
                class="kw">const class="type">class="kw">datetime &time[],
                class="kw">const class="type">class="kw">double &open[],
                class="kw">const class="type">class="kw">double &high[],
                class="kw">const class="type">class="kw">double &low[],
                class="kw">const class="type">class="kw">double &close[],
                class="kw">const class="type">long &tick_volume[],
                class="kw">const class="type">long &volume[],
                class="kw">const class="type">int &spread[])
  {
class=class="str">"cmt">//---- 检查计算是否有足够的柱线
  if(rates_total<min_rates_total)
     class="kw">return(class="num">0);
class=class="str">"cmt">//---- 声明局部变量
  class="type">int limit,bar;
class=class="str">"cmt">//---- 按时间序列设置数组中元素的索引
  ArraySetAsSeries(low,true);
class=class="str">"cmt">//---- 重新计算周期自 “第一根” 柱线编号开始计算
  if(prev_calculated>rates_total || prev_calculated<=class="num">0)       class=class="str">"cmt">// 检查指标是否为首次计算
     limit=rates_total-min_rates_total;                       class=class="str">"cmt">// 计算所有装修按的起始索引
  else
     limit=rates_total-prev_calculated;                       class=class="str">"cmt">// 计算新柱线的起始索引
class=class="str">"cmt">//---- 指标的主要计算循环
  for(bar=limit; bar>=class="num">0; bar--)
     {
     Signal[bar]=class="num">0.0;
     if(Pat.CandleType(Symbol(),PERIOD_CURRENT,bar)==CandleType)

「空单信号线的偏移写法」

这段收尾代码把空单触发线直接钉在 bar 最低价下方 200 个点。_Point 是终端里当前品种的最小报价单位,黄金和欧美的数值差一个数量级,复制前先确认品种。 回看前面逻辑,Signal 数组只在满足条件时赋值,其余 bar 沿用前值;这种写法在 MT5 里若不开缓冲区清零,容易出现信号粘连。开 MT5 把这段塞进 OnInit 之后的计算块,改 200 为 50 看信号密度变化。

MQL5 / C++
    Signal[bar]=low[bar]-class="num">200*_Point;
    }
  class="kw">return(rates_total);
}
class=class="str">"cmt">//+------------------------------------------------------------------+

◍ 用形态检测搭一个会开仓的 EA

PatternDetector 指标只负责在图上标出 TYPE_PATTERN 枚举里的形态,真正要落地交易,得写个 EA 把形态和订单绑起来。EA 里同时挂了 Pattern.mqh(形态处理)和 Trade.mqh(下单),并用 PATTERN_MODE 枚举切换「现有形态」和「生成形态」两条路。 输入参数分两块:第一块是基础模块,第二块是设置与交易参数。Current Timeframe 默认跟当前图表,但优化时可单独选周期;Pattern Mode 切到 GENERATED 后,Buy/Sell Pattern Type 失效,改用 BuyIndex1-3 / SellIndex1-3 去选由简单烛条拼出的形态。 初始化时除了常规检查,还会写入 TrendPeriod 值,它直接干预图表上的形态识别灵敏度。计算部分不复杂,核心就是 BuySignal() / SellSignal() 两个函数——以 BuySignal() 为例,它先判当前模式,再决定读 TYPE_PATTERN 还是读那组生成索引。 回测用 EURUSD、2018.01.01—2018.03.15 上行段、1 分钟 OHLC、1000 USD 本金、1:500 杠杆、MetaQuotes-Demo 五位数报价。生成形态模式与现有形态模式各跑一轮优化,最佳参数落在优化表第二列。外汇与贵金属杠杆交易风险高,回测盈利不代表实盘概率优势。 下面这段是指标输入声明与 OnInit 的骨架,参数权重和 TrendPeriod 的算法耦合点都在里头,直接拷进 MT5 看编译结果最直观。

MQL5 / C++
<span class="comment">class=class="str">"cmt">//+----------------------------------------------+</span>
<span class="comment">class=class="str">"cmt">//| 指标输入参数  &nbsp;&nbsp;  &nbsp;&nbsp;  &nbsp;&nbsp;  &nbsp;&nbsp;  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  |</span>
<span class="comment">class=class="str">"cmt">//+----------------------------------------------+</span>
<span style="background-class="type">class="kw">color:rgb(class="num">255, class="num">235, class="num">85);"><span class="keyword">class="kw">input</span> TYPE_PATTERN&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;PatternType=<span class="number">class="num">1</span>;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">// 形态类型</span></span>
<span class="keyword">class="kw">input</span> <span class="keyword">class="type">class="kw">color</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LabelColor=<span class="macro">clrCrimson</span>;
<span class="keyword">class="kw">input</span> <span class="keyword">class="type">class="kw">double</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LongCoef=<span class="number">class="num">1.3</span>;
<span class="keyword">class="kw">input</span> <span class="keyword">class="type">class="kw">double</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ShortCoef=<span class="number">class="num">0.5</span>;
<span class="keyword">class="kw">input</span> <span class="keyword">class="type">class="kw">double</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DojiCoef=<span class="number">class="num">0.04</span>;
<span class="keyword">class="kw">input</span> <span class="keyword">class="type">class="kw">double</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MaribozuCoef=<span class="number">class="num">0.01</span>;
<span class="keyword">class="kw">input</span> <span class="keyword">class="type">class="kw">double</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SpinCoef=<span class="number">class="num">1</span>;
<span class="keyword">class="kw">input</span> <span class="keyword">class="type">class="kw">double</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HummerCoef1=<span class="number">class="num">0.1</span>;
<span class="keyword">class="kw">input</span> <span class="keyword">class="type">class="kw">double</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HummerCoef2=<span class="number">class="num">2</span>;
<span class="keyword">class="kw">input</span> <span class="keyword">class="type">int</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TrendPeriod=<span class="number">class="num">5</span>;
<span class="comment">class=class="str">"cmt">//---</span>
CPattern Pat;
<span class="keyword">class="type">class="kw">double</span> Signal[];
<span class="comment">class=class="str">"cmt">//---- 为数据计算开始声明整数变量</span>
<span class="keyword">class="type">int</span> min_rates_total;
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="comment">class=class="str">"cmt">//| 自定义指标初始化函数&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   |</span>
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+&nbsp;&nbsp;</span>
<span class="keyword">class="type">void</span> <span class="functions">OnInit</span>()
&nbsp;&nbsp;{
<span class="comment">class=class="str">"cmt">//---- 初始化计算开始的数据变量</span>
&nbsp;&nbsp; min_rates_total=TrendPeriod+<span class="number">class="num">2</span>;
<span class="comment">class=class="str">"cmt">//---- 定义指标数值的显示精度</span>
&nbsp;&nbsp; <span class="functions">IndicatorSetInteger</span>(<span class="macro">INDICATOR_DIGITS</span>,<span class="predefines">_Digits</span>);
<span class="comment">class=class="str">"cmt">//---- 将 SignUp[] 动态数组设置为指标缓冲区</span>
&nbsp;&nbsp; <span class="functions">SetIndexBuffer</span>(<span class="number">class="num">0</span>,Signal,<span class="macro">INDICATOR_DATA</span>);
<span class="comment">class=class="str">"cmt">//---- 指标 class="num">1 的画图开始偏移</span>
&nbsp;&nbsp; <span class="functions">PlotIndexSetInteger</span>(<span class="number">class="num">0</span>,<span class="macro">PLOT_DRAW_BEGIN</span>,min_rates_total);
<span class="comment">class=class="str">"cmt">//---- 按时间顺序设置缓冲区中元素的索引&nbsp;&nbsp; </span>
&nbsp;&nbsp; <span class="functions">ArraySetAsSeries</span>(Signal,<span class="macro">true</span>);
<span class="comment">class=class="str">"cmt">//---- 设置在图表上不可见的指标值</span>
&nbsp;&nbsp; <span class="functions">PlotIndexSetDouble</span>(<span class="number">class="num">0</span>,<span class="macro">PLOT_EMPTY_VALUE</span>,<span class="macro">EMPTY_VALUE</span>);
<span class="comment">class=class="str">"cmt">//---- 指标品种</span>
&nbsp;&nbsp; <span class="functions">PlotIndexSetInteger</span>(<span class="number">class="num">0</span>,<span class="macro">PLOT_ARROW</span>,<span class="number">class="num">108</span>);
<span class="comment">class=class="str">"cmt">//---</span>

信号线怎么在K线下方落点

这段自定义指标的 OnCalculate 里,真正决定信号画在哪行的只有一句:Signal[bar]=low[bar]-200*_Point。也就是在对应K线最低价再往下挪 200 个点距,把箭头或标记甩出价格实体之外,避免遮挡形态本身。 循环从 limit 倒序跑到 bar>0,每次先清 Signal[bar]=0.0,再调 Pat.PatternType 判断当前 bar 是否命中指定形态;命中才写值,未命中保持 0 不绘制。limit 的计算分两种:首跑用 rates_total-min_rates_total 全量扫,后续只扫 rates_total-prev_calculated 根新柱,减少重复运算。 ArraySetAsSeries(low,true) 把 low 数组按时间倒序索引,bar 越小越新,和 MT5 内置序列访问习惯一致。rates_total<min_rates_total 时直接 return(0),说明历史不够 min_rates_total 根就别算,防止越界读低价。 外汇与贵金属点差跳动大,200*_Point 在 XAUUSD 上约 20 美分、EURUSD 上约 2 点,开 MT5 把 200 改成 50 或 400,能直观看到信号线离 K 线的远近变化,依品种波动调这个系数更实用。

MQL5 / C++
class="type">int OnCalculate(class="kw">const class="type">int rates_total,
                class="kw">const class="type">int prev_calculated,
                class="kw">const class="type">class="kw">datetime &time[],
                class="kw">const class="type">class="kw">double &open[],
                class="kw">const class="type">class="kw">double &high[],
                class="kw">const class="type">class="kw">double &low[],
                class="kw">const class="type">class="kw">double &close[],
                class="kw">const class="type">long &tick_volume[],
                class="kw">const class="type">long &volume[],
                class="kw">const class="type">int &spread[])
  {
class=class="str">"cmt">//---- 检查计算是否有足够的柱线
   if(rates_total<min_rates_total)
      class="kw">return(class="num">0);
class=class="str">"cmt">//---- 声明局部变量 
   class="type">int limit,bar;
class=class="str">"cmt">//---- 按时间序列设置数组中元素的索引  
   ArraySetAsSeries(low,true);
class=class="str">"cmt">//---- 重新计算周期自 “第一根” 柱线编号开始计算
   if(prev_calculated>rates_total || prev_calculated<=class="num">0)        class=class="str">"cmt">// 检查指标首次计算
      limit=rates_total-min_rates_total;                        class=class="str">"cmt">// 计算所有柱线的起始索引
   else
      limit=rates_total-prev_calculated;                        class=class="str">"cmt">// 计算新柱线的起始索引
class=class="str">"cmt">//---- 指标的主要计算循环
   for(bar=limit; bar>class="num">0; bar--)
     {
      Signal[bar]=class="num">0.0;
      if(Pat.PatternType(_Symbol,_Period,PatternType,bar))
         Signal[bar]=low[bar]-class="num">200*_Point;
     }
   class="kw">return(rates_total);
  }

「EA 头文件与形态模式参数拆解」

这段 MT5 智能交易系统源码的头部,定义了版权、版本引用以及两个核心类实例:CTradeBase 与 CPattern,分别接管下单逻辑和形态识别。注意它用 #include 引入了 Pattern.mqh 与 Trade.mqh,意味着形态库和交易底座是外挂的,不是写死在主体里。 源码里先枚举了 PATTERN_MODE:EXISTING 与 GENERATED,对应形态搜索的两种来源——已有K线组合或实时生成信号。输入参数块中,Inp_Lot 默认 0.01 手,Inp_StopLoss=40 点、Inp_TakeProfit=30 点,盈亏比约 1:0.75,偏止损宽于止盈的防守型设置。 买卖触发靠 BuyPatternType 与 SellPatternType:默认分别是 ENGULFING_BULL(看涨吞没)和 ENGULFING_BEAR(看跌吞没)。PatternMode 输入为 0 即走 EXISTING 分支。外汇与贵金属杠杆高,这类固定点差止损在跳空时可能滑点放大,开 MT5 加载后建议先关自动交易跑一周肉眼核对形态标记。 下面把头部声明与参数段原样保留,方便你直接抄进 MQEditor 改 Timeframe 和 MagicNum 做隔离测试:

MQL5 / C++
class="macro">#class="kw">property copyright "版权所有 class="num">2018, MetaQuotes 软件公司"
class="macro">#class="kw">property link      "[MQL5官方文档]
class="macro">#class="kw">property version   "class="num">1.00"
class="macro">#include <Pattern/Pattern.mqh>
class="macro">#include "Trade.mqh"
CTradeBase Trade;
CPattern Pat;
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 形态搜索模式                                                                    |
class=class="str">"cmt">//+------------------------------------------------------------------+
enum PATTERN_MODE
  {
   EXISTING,
   GENERATED
  };
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 智能交易系统输入参数                                                            |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="kw">input      class="type">class="kw">string            Inp_EaComment="Pattern Strategy";           class=class="str">"cmt">// EA 注释
class="kw">input      class="type">class="kw">double            Inp_Lot=class="num">0.01;                               class=class="str">"cmt">// 手数
class="kw">input      MarginMode        Inp_MMode=LOT;                              class=class="str">"cmt">// 资金管理
class=class="str">"cmt">//--- EA 参数
class="kw">input      class="type">class="kw">string            Inp_Str_label="===EA parameters===";        class=class="str">"cmt">// 标签
class="kw">input      class="type">int               Inp_MagicNum=class="num">1111;                          class=class="str">"cmt">// 魔幻数字
class="kw">input      class="type">int               Inp_StopLoss=class="num">40;                            class=class="str">"cmt">// 止损(点数)
class="kw">input      class="type">int               Inp_TakeProfit=class="num">30;                          class=class="str">"cmt">// 止盈(点数)
class=class="str">"cmt">//--- 交易参数
class="kw">input ENUM_TIMEFRAMES        Timeframe=PERIOD_CURRENT;                   class=class="str">"cmt">// 当前时间帧
class="kw">input PATTERN_MODE           PatternMode=class="num">0;                              class=class="str">"cmt">// 形态模式
class="kw">input TYPE_PATTERN           BuyPatternType=ENGULFING_BULL;              class=class="str">"cmt">// 买入形态类型
class="kw">input TYPE_PATTERN           SellPatternType=ENGULFING_BEAR;             class=class="str">"cmt">// 卖出形态类型

◍ 三连蜡烛的多空索引与形态系数

在 MT5 脚本里给三连简单蜡烛分配买卖索引,本质是规定哪一根蜡烛触发信号。下面这段声明把 BuyIndex1 设成 1、其余买入索引设成 0,意味着只有第一根简单蜡烛允许作为多头触发点;SellIndex1 同理,卖单只认第一根。 形态系数则用来量化蜡烛归类边界。LongCoef=1.3 表示实体长度达到基准 1.3 倍才判为长体;ShortCoef=0.5 是短体下限;DojiCoef=0.04 把十字星的实体占比卡在 4% 以内。 MaribozuCoef=0.01 要求秃头秃脚蜡烛的影线不超实体 1%,SpinCoef=1 是尖顶默认权重。锤子线拆成两个阈值:HummerCoef1=0.1 限制影线最小比例,HummerCoef2=2 规定下影上限为实体的 2 倍。改这几个数,分类结果会立刻变,外汇和贵金属波动大,参数乱调可能把噪声当信号。

MQL5 / C++
class="kw">input class="type">uint               BuyIndex1=class="num">1;                                                     class=class="str">"cmt">// 简单蜡烛1的买入索引
class="kw">input class="type">uint               BuyIndex2=class="num">0;                                                     class=class="str">"cmt">// 简单蜡烛2的买入索引
class="kw">input class="type">uint               BuyIndex3=class="num">0;                                                     class=class="str">"cmt">// 简单蜡烛3的买入索引
class="kw">input class="type">uint               SellIndex1=class="num">1;                                                    class=class="str">"cmt">// 简单蜡烛1的卖出索引
class="kw">input class="type">uint               SellIndex2=class="num">0;                                                    class=class="str">"cmt">// 简单蜡烛2的卖出索引
class="kw">input class="type">uint               SellIndex3=class="num">0;                                                    class=class="str">"cmt">// 简单蜡烛3的卖出索引
class="kw">input class="type">class="kw">double             LongCoef=class="num">1.3;                                                    class=class="str">"cmt">// 长体蜡烛系数
class="kw">input class="type">class="kw">double             ShortCoef=class="num">0.5;                                                   class=class="str">"cmt">// 短体蜡烛系数
class="kw">input class="type">class="kw">double             DojiCoef=class="num">0.04;                                                   class=class="str">"cmt">// 十字星蜡烛系数
class="kw">input class="type">class="kw">double             MaribozuCoef=class="num">0.01;                                               class=class="str">"cmt">// Maribozu 蜡烛系数
class="kw">input class="type">class="kw">double             SpinCoef=class="num">1;                                                      class=class="str">"cmt">// 尖顶蜡烛系数
class="kw">input class="type">class="kw">double             HummerCoef1=class="num">0.1;                                                 class=class="str">"cmt">// 锤子蜡烛系数1
class="kw">input class="type">class="kw">double             HummerCoef2=class="num">2;                                                   class=class="str">"cmt">// 锤子蜡烛系数2

EA 初始化与开仓信号的代码骨架

把趋势周期传给形态识别模块,是 EA 启动后第一件不能省的事。下面这段初始化函数先卡死两条底线:终端没连上服务器、或 MT5 没勾选「允许自动交易」,直接返回 INIT_FAILED 不往下走,避免裸奔发单。 OnInit 里只有一行核心业务代码:Pat.TrendPeriod(TrendPeriod),其中 TrendPeriod 默认 input 为 5。这个数字直接决定后续形态判定的回看长度,调小会更敏感、调大则过滤杂波,建议先在 EURUSD 的 M15 上用 5 对比 10 各跑一周观察信号数差异。 行情到来时 OnTick 先查当前 Magic 号有没有持仓,没有才放信号函数进场。BuySignal 内部用 PatternMode 分支:模式 0 只看单一 BuyPatternType;模式 1 则要求 BuyIndex1>0 且 Index2、Index3 均为 0,再调 Pat.PatternType 对当前符号和周期确认形态。外汇与贵金属杠杆高,这类信号只是概率触发,实盘前务必在策略测试器用至少 3 个月 tick 数据回测。

MQL5 / C++
class="kw">input class="type">int                  TrendPeriod=class="num">5;                  class=class="str">"cmt">// 趋势周期
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 智能系统初始化函数                                                  |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnInit()
  {
class=class="str">"cmt">//--- 检查与交易服务器的连接
   if(!TerminalInfoInteger(TERMINAL_CONNECTED))
     {
      Print(Inp_EaComment,": No Connection!");
      class="kw">return(INIT_FAILED);
     }
class=class="str">"cmt">//--- 检查自动交易许可
   if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED))
     {
      Print(Inp_EaComment,": Trade is not allowed!");
      class="kw">return(INIT_FAILED);
     }
class=class="str">"cmt">//---
   Pat.TrendPeriod(TrendPeriod);
class=class="str">"cmt">//---
   class="kw">return(INIT_SUCCEEDED);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 智能系统即时报价函数                                                |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnTick()
  {
   if(!Trade.IsOpenedByMagic(Inp_MagicNum))
     {
      class=class="str">"cmt">//--- 如果有买入信号,则开单
      if(BuySignal())
         Trade.BuyPositionOpen(Symbol(),Inp_Lot,Inp_StopLoss,Inp_TakeProfit,Inp_MagicNum,Inp_EaComment);
      class=class="str">"cmt">//--- 如果有卖出信号,则开单
      if(SellSignal())
         Trade.SellPositionOpen(Symbol(),Inp_Lot,Inp_StopLoss,Inp_TakeProfit,Inp_MagicNum,Inp_EaComment);
     }
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 买入条件                                                            |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool BuySignal()
  {
   if(PatternMode==class="num">0)
     {
      if(BuyPatternType==NONE)
         class="kw">return(class="kw">false);
      if(Pat.PatternType(_Symbol,Timeframe,BuyPatternType,class="num">1))
         class="kw">return(true);
     }
   else if(PatternMode==class="num">1)
     {
      if(BuyIndex1>class="num">0 && BuyIndex2==class="num">0 && BuyIndex3==class="num">0)
        {
         if(Pat.PatternType(_Symbol,Timeframe,BuyIndex1,class="num">1))
            class="kw">return(true);

「三买点组合的判定分支」

这段逻辑处理的是当系统识别到多个潜在买入信号索引时的分支判定。前面已经算出了 BuyIndex1、BuyIndex2、BuyIndex3 三个变量,这里根据它们是否大于 0 来走不同路径。 若只有前两个索引有效(BuyIndex3 为 0),则调用 PatternType 并传入前两个索引与参数 1,若形态匹配就返回 true。若三个索引都大于 0,则把三个索引一并传入 PatternType 做复合形态校验,同样返回 true 表示触发。 所有分支都不命中时,函数落到最后的 return(false),意味着当前 K 线结构不构成系统定义的买入形态。外汇与贵金属市场波动剧烈,此类形态信号仅代表概率倾向,实盘前请在 MT5 策略测试器用历史数据验证命中率。

MQL5 / C++
      }
      else if(BuyIndex1>class="num">0 && BuyIndex2>class="num">0 && BuyIndex3==class="num">0)
      {
       if(Pat.PatternType(_Symbol,Timeframe,BuyIndex1,BuyIndex2,class="num">1))
         class="kw">return(true);
      }
      else if(BuyIndex1>class="num">0 && BuyIndex2>class="num">0 && BuyIndex3>class="num">0)
      {
       if(Pat.PatternType(_Symbol,Timeframe,BuyIndex1,BuyIndex2,BuyIndex3,class="num">1))
         class="kw">return(true);
      }
   }
   class="kw">return(class="kw">false);
}

◍ 把那几个文件塞进根目录就完事

随文打包的 MQL5.zip 体积 452.51 KB,里面不是单文件,而是按终端目录结构排好的:Pattern.mqh 和 Trade.mqh 是函数库,CandleDetector.mq5 与 PatternDetector.mq5 是指标,PatternExpert.mq5 是 EA。装错位置是论坛里最高频的坑——klavermight 问过是不是要按 expert、indicator 分目录拷贝,答案是否定的。 正确做法只有一步:把整个 MQL5 文件夹直接丢进终端根目录。在 MT5 里按 Ctrl+Shift+D 调出数据文件夹,就能看到根目录长什么样,原文图例 9 标的就是这个位置。覆盖式粘贴即可,终端会自动归位到 MQL5/Include、MQL5/Indicators 等子路径。 有用户反馈编译 PatternAnalyzer.mq5 报“未声明标识符”,多半是只拷了主文件没带 Menultem.mqh;也有人测试 EA 不出现在策略测试器,原因同样是根目录放错层。外汇和贵金属行情波动剧烈、杠杆风险高,即便文件装对了,实盘前也务必先在策略测试器跑一遍样本。 装好之后你能立刻做的一件事:打开 MT5 按 Ctrl+Shift+D,确认 MQL5 文件夹已覆盖到根目录,然后拖入 PatternExpert.mq5 看指标是否加载。到这一步,这套形态识别工具链就真正可用了。

把形态扫描交给小布盯盘
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到形态命中与效率系数,你只管判断当下该不该跟。

常见问题

CandleType 定义单根烛条的几何分类,PatternType 描述多根组合形态,前者是后者的输入原子。
不能。它只是历史样本下的条件频率,外汇贵金属波动受事件驱动,概率高只代表倾向而非必然。
三个权重分别调节形态规模、延续幅度与回撤容忍,改权重会重算效率,建议先固定再做横评。
小布的品种页已内置近似的烛条形态扫描,逻辑同源但做了轻量化,无需自己编译库也能看盘口诊断。
杠杆品种滑点和跳空会吞噬形态优势,建议先在模拟跑满样本周期,再按概率而非确定性下单。