DoEasy.服务功能(第 1 部分):价格形态·综合运用
- K线对象的属性与尺寸算法
- K线本体与影线比例的量化拆解
- 从K线对象里直接抠时间分量
- K线对象的属性读取接口
- K线对象怎么吐出时间和价格字段
- 从 K 线结构体里抠出实体与影线比例
- 把单根K线的属性一次性灌进对象
- K线属性字符串化的分支拼装
- K线比例属性的字符串拼装逻辑
- 从零搭一个抽象形态基类
- 形态类的私有字段与属性写入接口
- 形态对象的属性读写与比较接口
- 用一行访问器吃透形态对象属性
- 图形对象的属性读取接口
- 图形对象与信息面板的颜色接口
- 图案对象的着色与绘制接口
- 形态对象的编码、配色与比较逻辑
- 图案对象的等值判定与日志输出
- 把形态对象打印到日志的底层函数
- 图形属性描述的双类型分支
- K线形态比例属性的描述拼接逻辑
- 图形对象与信息面板的底层清理逻辑
- 信息面板防越界与图标绘制的实现细节
- 图案对象的显隐与配色控制
- 在 MT5 里把 Pin Bar 写成可复用类
- Pin Bar 信息面板的配色与文案生成
- 图形面板按图表边界自适应尺寸与坐标
- 把 K 线与形态塞进对象容器
- 按属性筛形态与取极值索引的实现
- 按属性筛形态与取极值索引的实现细节
- 按属性极值挑出目标形态索引
- 在形态列表里捞最小属性值的索引
- 比较运算符在EA里的落地写法
- 把Pin Bar塞进形态控制类的实战落点
- 形态识别类的私有字段与比例阈值
- 基类里那几个虚函数只是占位
- 形态类的引用与比例阈值接口
- K线形态对象的取值接口与比对入口
- 形态控制对象的构造与排序逻辑
- 扫描历史棒线并去重登记形态
- Pin Bar 管控类的图案绘制与排序逻辑
- Pin Bar 识别类的对象ID拼装逻辑
- 针杆形态的底层字节拼接与对象构建
- Pin Bar 的实体与影线筛选逻辑
- 把 Pin Bar 判定参数塞进类里
- 按形态比例检索与Harami控制对象获取
- 按形态类型取回控制对象
- 按形态类型检索控制对象
- 按形态类型取控制对象的查找逻辑
- 锤子与倒锤子形态对象的检索接口
- 按形态类型捞控制对象
- 从形态链表里捞蜻蜓与墓碑十字
- 从形态链表里捞指定的晨星对象
- 黄昏星与十字黄昏星的控制对象检索
- 从形态链表里捞指定的 K 线组合控制器
- 按形态类型捞控件对象的循环写法
- 从控制对象列表里捞特定的K线形态
- 从控制列表里捞指定的价格行为形态
- Pin Bar 控制对象的开关与新建逻辑
- 形态开关的封装逻辑
- 逐个切换 K 线形态识别开关
- 星线族与价格行为模式的开关函数
- Pin Bar 开关与三比例参数
- Pin Bar 控制对象的实例化与参数注入
- 用返回标志位判断形态是否被激活
- 形态启用状态的统一查询接口
- 形态开关的判活封装
- 星型与枢轴反转模式的启用探测
- 用控制对象判断母行与Pin Bar是否启用
- 批量刷新与图表标注的实现切口
- 把反转形态画到图上的调用入口
- 把形态绘制接口拆开看
- 把吊人线与各类十字星画到图上
- 星型反转形态的图表标注入口
- 图形标记函数的空指针守卫
- Pin Bar 与 Rails 的图表标注入口
- 控制器与序列类的初始化骨架
- 时序容器里挂了哪些成员
- 形态开关与序列容器构造
- 逐个开关蜡烛图形态识别模块
- 星线与弃婴等形态的独立开关
- Pin Bar 开关与比例参数的接法
- 形态开关的取数入口
- 形态开关的空指针兜底写法
- 星线与墓碑十字的启用开关
- 价格行为形态开关的底层判定
- 把反转形态画到图表上的几个入口
- 形态标注方法的逐一定义
- K线形态标注方法的逐个封装
- 把星形与弃婴形态画到图表上
- 图表上挂形态标记的封装方法
- Rails 图形绘制与序列对象构造析构
- 手动刷新行情序列时的新柱接管逻辑
- 多周期时间序列类的私有骨架
- 时间序列容器的取数与构造入口
- 按周期开关各类K线形态识别
- 按需开关星型与价格行为形态识别
- 按周期切换形态识别开关
- 星线与价格行为模式的开关函数
- 把形态画到图上的那组接口
- 星线与价格行为形态的图表标注接口
- 时间序列容器的构造与检索实现
- 按周期开关 K 线形态识别
- 按时间框架开关经典K线形态
- 按周期开关各类K线形态识别
- 按时间框架开关星线与枢轴反转形态
- 在指定周期上切换 Pin Bar 与 Rails 的识别开关
- 按周期查询形态开关的接口实现
- 多周期K线形态开关的逐周期判定
- 多周期形态开关的取数入口
- PinBar 与 Rails 的判定入口及图形标注方法
- 把经典K线形态直接画到MT5图表上
- 按周期把反转形态画上图表
- 星型与关键反转形态的图表标注入口
- 在指定周期上标注 Inside Bar 与 Pin Bar
- 暴露时间序列与形态控制接口
- 按品种周期开关蜡烛形态识别
- 逐个开关切换 K 线形态识别
- 在 MT5 里按品种周期开关价格行为形态
- 形态开关的逐类查询接口
- 形态开关与Pin Bar比例阈值
- 把形态识别函数直接挂上图表
- 把星线与十字类形态直接画上图表
- 价格行为图形的绘制与集合初始化
- 按品种名检索与批量建时间序列集
- 按品种周期开关 K 线形态识别
- 按品种周期开关经典K线形态识别
- 按品种周期切换吊人线与各类十字星开关
- 按品种与周期开关星线类形态识别
- 在时序集合里开关价格行为形态
- 按品种周期切换形态识别开关
- 形态开关的批量查询入口
- 在品种时间框架上查询形态开关
- 在品种周期上查形态开关
- PinBar 与 Rails 的判定入口及图形绘制封装
- 形态标注的集中派发接口
- 在图表上批量标记经典 K 线形态
- 星线族形态的一键标注入口
- 在图表上批量标注价格行为形态
- 从图样类里捞指定品种和周期的形态列表
- 按时间筛选与Harami开关的取数接口
- 逐个切换 K 线形态识别开关
- 按品种周期开关单根 K 线形态识别
- 按品种周期开关星线形态识别
- 按品种周期开关星线与枢轴形态
- 在 MT5 里按需勾选价格行为形态
- 把经典K线形态直接画到MT5图表上
- 把经典K线形态直接画上MT5图表
- 把星线与十字形态直接画上图表
- 把反转形态直接画上图表
- 在图表上直接标出 Inside Bar 与 Pin Bar
- 一键隐藏全部形态图标的两种写法
- 批量收起形态信息面板的两种写法
- 在 OnTick 里拿 tick 序列与避开卡顿
- 形态标记 EA 的实盘挂载验证
- Pin Bar 与多品种扫描的输入参数拆解
- EA 初始化里的 Pin Bar 开关与面板装配
- 图表事件里的 PinBar 绘制与坐标反算
- 用光标时间反查 Pin Bar 并弹出信息面板
- 画得少,看得清
K线对象的属性与尺寸算法
在 MT5 自定义分析库里,把单根 Bar 封装成 CBar 类,可以统一管整数、实数、字符串三类属性。类内用三个定长数组 m_long_prop、m_double_prop、m_string_prop 分别承接,数组长度由 BAR_PROP_INTEGER_TOTAL、BAR_PROP_DOUBLE_TOTAL、BAR_PROP_STRING_TOTAL 宏决定,避免散落变量。 IndexProp 是两个重载内联函数,把枚举值映射成数组下标。double 类属性要减去整数总数,string 类再减去实数总数,这样外部传 ENUM_BAR_PROP_DOUBLE 或 ENUM_BAR_PROP_STRING 就能直接定位,不用写一堆 switch。 实体与影线尺寸靠几个一行函数算。CandleSize 返回 High()-Low() 全高;BodySize 返回 BodyHigh()-BodyLow() 实体高度。配合 BodyType 判断阳线、阴线或零体,做价格行为过滤时直接调,不用每次手算。 外汇与贵金属杠杆高、滑点跳空频繁,这类尺寸比在极端行情可能失真,上 MT5 用策略测试器跑几根非农 K 线验证再实盘引用。
class CBar : class="kw">public CBaseObj { class="kw">private: class="type">MqlDateTime m_dt_struct; class=class="str">"cmt">// Date structure class="type">int m_digits; class=class="str">"cmt">// Symbol&class="macro">#x27;s digits value class="type">class="kw">string m_period_description; class=class="str">"cmt">// Timeframe class="type">class="kw">string description class="type">long m_long_prop[BAR_PROP_INTEGER_TOTAL]; class=class="str">"cmt">// Integer properties class="type">class="kw">double m_double_prop[BAR_PROP_DOUBLE_TOTAL]; class=class="str">"cmt">// Real properties class="type">class="kw">string m_string_prop[BAR_PROP_STRING_TOTAL]; class=class="str">"cmt">// String properties class=class="str">"cmt">//--- Return the index of the array the bar&class="macro">#x27;s(class="num">1) class="type">class="kw">double and(class="num">2) class="type">class="kw">string properties are located at class="type">int IndexProp(ENUM_BAR_PROP_DOUBLE class="kw">property) const { class="kw">return(class="type">int)class="kw">property-BAR_PROP_INTEGER_TOTAL; } class="type">int IndexProp(ENUM_BAR_PROP_STRING class="kw">property) const { class="kw">return(class="type">int)class="kw">property-BAR_PROP_INTEGER_TOTAL-BAR_PROP_DOUBLE_TOTAL; } class=class="str">"cmt">//--- Return the bar type(bullish/bearish/zero) ENUM_BAR_BODY_TYPE BodyType(class="type">void) const; class=class="str">"cmt">//--- Calculate and class="kw">return the size of(class="num">1) candle, (class="num">2) candle body, class=class="str">"cmt">//--- (class="num">3) upper, (class="num">4) lower candle wick, class=class="str">"cmt">//--- (class="num">5) candle body top and(class="num">6) bottom class="type">class="kw">double CandleSize(class="type">void) const { class="kw">return(this.High()-this.Low()); } class="type">class="kw">double BodySize(class="type">void) const { class="kw">return(this.BodyHigh()-this.BodyLow()); }
◍ K线本体与影线比例的量化拆解
在价格行为分析里,把一根蜡烛的实体和上下影线拆开算比例,比肉眼估形态更靠谱。下面这组方法直接返回各部分的绝对尺寸:上影线 = 最高价减实体高点,下影线 = 实体低点减最低价,实体高点取收报与开盘的较大值,实体低点取两者较小值。 百分比占比的计算逻辑很直白:若整根蜡烛区间大于0,实体占比 = 实体长度×100/总区间,上影线占比 = 上影长度×100/总区间,下影线占比同理;若总区间为0(十字星极端情况)则直接返回100.0。这意味着当函数返回100时,你拿到的是零区间棒线,不能直接当形态信号用。 时间维度上,单根 bar 可提取年份、月份、星期几、年内序号、日、时、分,存在结构体成员 m_dt_struct 里。实盘里用这些字段做时段过滤(比如只统计伦敦时段19:00–21:00的吞没形态)能明显压低噪音。 外汇与贵金属杠杆高、滑点突兀,这类比例阈值回测请先在 MT5 策略测试器用历史数据跑一遍,再上模拟盘。
class="type">class="kw">double ShadowUpSize(class="type">void) const { class="kw">return(this.High()-this.BodyHigh()); } class="type">class="kw">double ShadowDownSize(class="type">void) const { class="kw">return(this.BodyLow()-this.Low()); } class="type">class="kw">double BodyHigh(class="type">void) const { class="kw">return ::fmax(this.Close(),this.Open()); } class="type">class="kw">double BodyLow(class="type">void) const { class="kw">return ::fmin(this.Close(),this.Open()); } class=class="str">"cmt">//--- Calculate and class="kw">return the percentage ratio of the(class="num">1) candle body, (class="num">2) upper and(class="num">3) lower shadow size to the full candle size class="type">class="kw">double CandleRatioBodyToCandleSize(class="type">void) const { class="kw">return(this.CandleSize()>class="num">0 ? this.BodySize()*class="num">100.0/this.CandleSize() : class="num">100.0); } class="type">class="kw">double CandleRatioUpperShadowToCandleSize(class="type">void) const { class="kw">return(this.CandleSize()>class="num">0 ? this.ShadowUpSize()*class="num">100.0/this.CandleSize() : class="num">100.0); } class="type">class="kw">double CandleRatioLowerShadowToCandleSize(class="type">void) const { class="kw">return(this.CandleSize()>class="num">0 ? this.ShadowDownSize()*class="num">100.0/this.CandleSize(): class="num">100.0); } class=class="str">"cmt">//--- Return the(class="num">1) year and(class="num">2) month the bar belongs to, (class="num">3) week day, class=class="str">"cmt">//--- (class="num">4) bar serial number in a year, (class="num">5) day, (class="num">6) hour, (class="num">7) minute, class="type">int TimeYear(class="type">void) const { class="kw">return this.m_dt_struct.year; }
「从K线对象里直接抠时间分量」
在 MT5 自建的 K 线封装类里,时间相关的取值被拆成了十几个轻量方法,全部走内部结构体 m_dt_struct 直接返回,没有额外计算开销。
下面这几行是该类里最常被调用的部分,返回的都是整型:
int TimeMonth(void) const { return this.m_dt_struct.mon; }
int TimeDayOfWeek(void) const { return this.m_dt_struct.day_of_week; }
int TimeDayOfYear(void) const { return this.m_dt_struct.day_of_year; }
int TimeDay(void) const { return this.m_dt_struct.day; }
int TimeHour(void) const { return this.m_dt_struct.hour; }
int TimeMinute(void) const { return this.m_dt_struct.min; }
逐行看:TimeMonth 取月份(1–12),TimeDayOfWeek 取星期(0–6,周日通常为 0),TimeDayOfYear 是年内第几天(1–366),TimeDay 是当月日期,TimeHour 与 TimeMinute 分别给出小时和分钟。写日内策略时,直接用 TimeHour()==0 去抓日线切换点,比反复调 TimeCurrent() 再结构转换更省事。
类的公开段还暴露了 TypeBody() 和 Timeframe():TypeBody 返回 ENUM_BAR_BODY_TYPE,标识实体阴阳或十字;Timeframe 回吐该 bar 所属周期枚举。外汇与贵金属波动受时段影响明显,用这些方法做分时段统计时,注意点差扩大带来的滑点风险可能抬高回测与实盘偏差。
class="type">int TimeMonth(class="type">void) const { class="kw">return this.m_dt_struct.mon; } class="type">int TimeDayOfWeek(class="type">void) const { class="kw">return this.m_dt_struct.day_of_week; } class="type">int TimeDayOfYear(class="type">void) const { class="kw">return this.m_dt_struct.day_of_year; } class="type">int TimeDay(class="type">void) const { class="kw">return this.m_dt_struct.day; } class="type">int TimeHour(class="type">void) const { class="kw">return this.m_dt_struct.hour; } class="type">int TimeMinute(class="type">void) const { class="kw">return this.m_dt_struct.min; } ENUM_BAR_BODY_TYPE TypeBody(class="type">void) const { class="kw">return (ENUM_BAR_BODY_TYPE)this.GetProperty(BAR_PROP_TYPE); } ENUM_TIMEFRAMES Timeframe(class="type">void) const { class="kw">return (ENUM_TIMEFRAMES)this.GetProperty(BAR_PROP_PERIOD); }
K线对象的属性读取接口
在 MT5 的自定义 K 线类里,一组 const 成员函数直接把单根 BAR 的关键字段暴露出来,调用方无需再碰底层数组。下面这段声明覆盖了点差、成交量、时间戳及时间拆解字段,全部走 GetProperty 统一取数。
class="type">int Spread(class="type">void) const { class="kw">return (class="type">int)this.GetProperty(BAR_PROP_SPREAD); } class="type">long VolumeTick(class="type">void) const { class="kw">return this.GetProperty(BAR_PROP_VOLUME_TICK); } class="type">long VolumeReal(class="type">void) const { class="kw">return this.GetProperty(BAR_PROP_VOLUME_REAL); } class="type">class="kw">datetime Time(class="type">void) const { class="kw">return (class="type">class="kw">datetime)this.GetProperty(BAR_PROP_TIME); } class="type">long Year(class="type">void) const { class="kw">return this.GetProperty(BAR_PROP_TIME_YEAR); } class="type">long Month(class="type">void) const { class="kw">return this.GetProperty(BAR_PROP_TIME_MONTH); } class="type">long DayOfWeek(class="type">void) const { class="kw">return this.GetProperty(BAR_PROP_TIME_DAY_OF_WEEK); } class="type">long DayOfYear(class="type">void) const { class="kw">return this.GetProperty(BAR_PROP_TIME_DAY_OF_YEAR); } class="type">long Day(class="type">void) const { class="kw">return this.GetProperty(BAR_PROP_TIME_DAY); }
class="type">int Spread(class="type">void) const { class="kw">return (class="type">int)this.GetProperty(BAR_PROP_SPREAD); } class="type">long VolumeTick(class="type">void) const { class="kw">return this.GetProperty(BAR_PROP_VOLUME_TICK); } class="type">long VolumeReal(class="type">void) const { class="kw">return this.GetProperty(BAR_PROP_VOLUME_REAL); } class="type">class="kw">datetime Time(class="type">void) const { class="kw">return (class="type">class="kw">datetime)this.GetProperty(BAR_PROP_TIME); } class="type">long Year(class="type">void) const { class="kw">return this.GetProperty(BAR_PROP_TIME_YEAR); } class="type">long Month(class="type">void) const { class="kw">return this.GetProperty(BAR_PROP_TIME_MONTH); } class="type">long DayOfWeek(class="type">void) const { class="kw">return this.GetProperty(BAR_PROP_TIME_DAY_OF_WEEK); } class="type">long DayOfYear(class="type">void) const { class="kw">return this.GetProperty(BAR_PROP_TIME_DAY_OF_YEAR); } class="type">long Day(class="type">void) const { class="kw">return this.GetProperty(BAR_PROP_TIME_DAY); }
◍ K线对象怎么吐出时间和价格字段
在 MT5 自建的 K 线类里,时间分量是直接拆成小时和分钟两个只读方法暴露的,而不是返回一个 Time 结构再让你自己解析。Hour() 与 Minute() 都走 GetProperty,分别取 BAR_PROP_TIME_HOUR、BAR_PROP_TIME_MINUTE,返回 long 类型,写 EA 时拿来做分时过滤比调用 TimeHour(Time) 省一层转换。 价格相关的方法更密:Open/High/Low/Close 四个 double 方法对应 BAR_PROP_OPEN 到 BAR_PROP_CLOSE,这是画图或算收益率的底料。Size() 返回整根蜡烛绝对值跨度(BAR_PROP_CANDLE_SIZE),SizeBody() 只取实体部分(BAR_PROP_CANDLE_SIZE_BODY),两者相减就得到影线总长,不用你手算 High-Low 再减 abs(Close-Open)。 下方代码是这套接口的原样声明,左边是返回类型与函数名,右边 const 限定符保证不改动对象状态。复制进你的 include 头文件,就能在策略里直接 bar.SizeBody() 这么调,EURUSD 这类点差敏感品种上,实体占比低于 0.0002 的十字星会被你一眼筛掉。外汇和贵金属杠杆高,这类微观过滤只降噪不预示方向,实盘仍可能因滑点吞掉信号优势。
class="type">long Hour(class="type">void) const { class="kw">return this.GetProperty(BAR_PROP_TIME_HOUR); } class="type">long Minute(class="type">void) const { class="kw">return this.GetProperty(BAR_PROP_TIME_MINUTE); } class=class="str">"cmt">//--- Return bar&class="macro">#x27;s(class="num">1) Open, (class="num">2) High, (class="num">3) Low, (class="num">4) Close price, class=class="str">"cmt">//--- size of the(class="num">5) candle, (class="num">6) body, (class="num">7) candle top, (class="num">8) bottom, class=class="str">"cmt">//--- size of the(class="num">9) candle upper, (class="num">10) lower wick class="type">class="kw">double Open(class="type">void) const { class="kw">return this.GetProperty(BAR_PROP_OPEN); } class="type">class="kw">double High(class="type">void) const { class="kw">return this.GetProperty(BAR_PROP_HIGH); } class="type">class="kw">double Low(class="type">void) const { class="kw">return this.GetProperty(BAR_PROP_LOW); } class="type">class="kw">double Close(class="type">void) const { class="kw">return this.GetProperty(BAR_PROP_CLOSE); } class="type">class="kw">double Size(class="type">void) const { class="kw">return this.GetProperty(BAR_PROP_CANDLE_SIZE); } class="type">class="kw">double SizeBody(class="type">void) const { class="kw">return this.GetProperty(BAR_PROP_CANDLE_SIZE_BODY); }
「从 K 线结构体里抠出实体与影线比例」
在 MT5 自建的 CBar 类里,一组 const 方法把单根蜡烛的关键几何特征直接暴露出来,省去每次手动算高低点差。TopBody() 与 BottomBody() 返回实体上下边界,SizeShadowUp() / SizeShadowDown() 返回上下影线绝对长度,这几个值都是 double 类型,精度够用在 5 位数报价的外汇对上。 真正省事的是 Ratio 系列:RatioBodyToCandleSize() 给出实体占整根蜡烛高度的百分比,RatioUpperShadowToCandleSize() 与 RatioLowerShadowToCandleSize() 分别对应上下影线占比。实战里若发现某根锤子线的 RatioLowerShadowToCandleSize 大于 0.7,而 RatioBodyToCandleSize 低于 0.1,就倾向判定为下影极长、实体极小的形态,贵金属 XAUUSD 在高波动时段这类棒线出现频率可能偏高,但杠杆品种风险依旧大。 Index() 方法接收 ENUM_TIMEFRAMES 参数,内部用 iBarShift 把当前 Bar 的时间戳映射到指定周期上的索引位置;传 PERIOD_CURRENT 时自动取当前图表周期。下面这段代码就是上述方法的原始声明,可直接贴进你的 include 文件做验证。
class="type">class="kw">double TopBody(class="type">void) const { class="kw">return this.GetProperty(BAR_PROP_CANDLE_BODY_TOP); } class="type">class="kw">double BottomBody(class="type">void) const { class="kw">return this.GetProperty(BAR_PROP_CANDLE_BODY_BOTTOM); } class="type">class="kw">double SizeShadowUp(class="type">void) const { class="kw">return this.GetProperty(BAR_PROP_CANDLE_SIZE_SHADOW_UP); } class="type">class="kw">double SizeShadowDown(class="type">void) const { class="kw">return this.GetProperty(BAR_PROP_CANDLE_SIZE_SHADOW_DOWN); } class=class="str">"cmt">//--- Return the properties of the percentage ratio of the(class="num">1) candle body, (class="num">2) upper and(class="num">3) lower shadow size to the candle full size class="type">class="kw">double RatioBodyToCandleSize(class="type">void) const { class="kw">return this.GetProperty(BAR_PROP_RATIO_BODY_TO_CANDLE_SIZE); } class="type">class="kw">double RatioUpperShadowToCandleSize(class="type">void) const { class="kw">return this.GetProperty(BAR_PROP_RATIO_UPPER_SHADOW_TO_CANDLE_SIZE); } class="type">class="kw">double RatioLowerShadowToCandleSize(class="type">void) const { class="kw">return this.GetProperty(BAR_PROP_RATIO_LOWER_SHADOW_TO_CANDLE_SIZE); } class=class="str">"cmt">//--- Return bar symbol class="type">class="kw">string Symbol(class="type">void) const { class="kw">return this.GetProperty(BAR_PROP_SYMBOL); } class=class="str">"cmt">//--- Return bar index on the specified timeframe the bar time falls into class="type">int Index(const ENUM_TIMEFRAMES timeframe) const { class="kw">return ::iBarShift(this.Symbol(),(timeframe==PERIOD_CURRENT ? ::Period() : timeframe),this.Time()); }
把单根K线的属性一次性灌进对象
在 MT5 自定义类里,把一根 bar 的全部维度写进对象属性,靠的是连续调用 SetProperty。上面这段把时间、点差、成交量、OHLC 以及蜡烛本体/影线尺寸都塞进 this,相当于给每根 K 线建了一份结构化档案。 值得留意的是最后三行高亮部分:BAR_PROP_RATIO_BODY_TO_CANDLE_SIZE、上影线占比、下影线占比这三个比率属性,是把实体和影线长度除以总蜡烛长度得到的。做价格行为分类时,锤子线可能表现为下影线占比 > 0.7,而十字星则实体占比趋近 0,这类阈值可以直接在 EA 里用比率判断。 GetPropertyDescription 则负责把 double 型属性转成可读字符串,digits 为 0 时回落到 1 位小数,避免整数品种显示怪异。外汇和贵金属波动大、杠杆高,回测时这类属性映射错了,形态识别信号可能完全反向,开 MT5 跑一遍最能验真伪。
this.SetProperty(BAR_PROP_SPREAD,rates.spread); this.SetProperty(BAR_PROP_VOLUME_TICK,rates.tick_volume); this.SetProperty(BAR_PROP_VOLUME_REAL,rates.real_volume); this.SetProperty(BAR_PROP_TIME,rates.time); this.SetProperty(BAR_PROP_TIME_YEAR,this.TimeYear()); this.SetProperty(BAR_PROP_TIME_MONTH,this.TimeMonth()); this.SetProperty(BAR_PROP_TIME_DAY_OF_YEAR,this.TimeDayOfYear()); this.SetProperty(BAR_PROP_TIME_DAY_OF_WEEK,this.TimeDayOfWeek()); this.SetProperty(BAR_PROP_TIME_DAY,this.TimeDay()); this.SetProperty(BAR_PROP_TIME_HOUR,this.TimeHour()); this.SetProperty(BAR_PROP_TIME_MINUTE,this.TimeMinute()); class=class="str">"cmt">//--- this.SetProperty(BAR_PROP_OPEN,rates.open); this.SetProperty(BAR_PROP_HIGH,rates.high); this.SetProperty(BAR_PROP_LOW,rates.low); this.SetProperty(BAR_PROP_CLOSE,rates.close); this.SetProperty(BAR_PROP_CANDLE_SIZE,this.CandleSize()); this.SetProperty(BAR_PROP_CANDLE_SIZE_BODY,this.BodySize()); this.SetProperty(BAR_PROP_CANDLE_BODY_TOP,this.BodyHigh()); this.SetProperty(BAR_PROP_CANDLE_BODY_BOTTOM,this.BodyLow()); this.SetProperty(BAR_PROP_CANDLE_SIZE_SHADOW_UP,this.ShadowUpSize()); this.SetProperty(BAR_PROP_CANDLE_SIZE_SHADOW_DOWN,this.ShadowDownSize()); class=class="str">"cmt">//--- this.SetProperty(BAR_PROP_RATIO_BODY_TO_CANDLE_SIZE,this.CandleRatioBodyToCandleSize()); this.SetProperty(BAR_PROP_RATIO_UPPER_SHADOW_TO_CANDLE_SIZE,this.CandleRatioUpperShadowToCandleSize()); this.SetProperty(BAR_PROP_RATIO_LOWER_SHADOW_TO_CANDLE_SIZE,this.CandleRatioLowerShadowToCandleSize()); class=class="str">"cmt">//--- this.SetProperty(BAR_PROP_TYPE,this.BodyType()); class=class="str">"cmt">//--- Set the object type to the object of the graphical object management class this.m_graph_elm.SetTypeNode(this.m_type); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Return the description of the bar&class="macro">#x27;s real class="kw">property | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">class="kw">string CBar::GetPropertyDescription(ENUM_BAR_PROP_DOUBLE class="kw">property) { class="type">int dg=(this.m_digits>class="num">0 ? this.m_digits : class="num">1); class="kw">return ( class="kw">property==BAR_PROP_OPEN ? CMessage::Text(MSG_ORD_PRICE_OPEN)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(class="kw">property),dg) ) : class="kw">property==BAR_PROP_HIGH ? CMessage::Text(MSG_LIB_TEXT_BAR_HIGH)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(class="kw">property),dg) ) : class="kw">property==BAR_PROP_LOW ? CMessage::Text(MSG_LIB_TEXT_BAR_LOW)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
◍ K线属性字符串化的分支拼装
这段逻辑是 K 线对象把内部属性转成可读文本的核心分支。它用三元运算符按 property 枚举值逐一匹配,先取对应语种文案,再判断是否被当前品种或周期支持,不支持就贴「不支持」标签,支持则调用 DoubleToString 把数值格式化输出。 注意 BAR_PROP_RATIO_BODY_TO_CANDLE_SIZE 这一支与其他不同:精度写死为 2,且在数值后追加了 "%" 字符,意味着实体占整根 K 线长度的比例被当成百分比呈现,回测里常见数值落在 10%~90% 区间,用来快速识别纺锤体或秃蜡烛。 如果 SupportProperty 返回 false,无论什么属性都只返回文案加「: 不支持」,不会触发 GetProperty,因此复制这段代码时要确认你的 bar 类已正确重载 SupportProperty,否则 MT5 里只会看到一片不支持。
class="kw">property==BAR_PROP_RATIO_BODY_TO_CANDLE_SIZE ? CMessage::Text(MSG_LIB_TEXT_BAR_RATIO_BODY_TO_CANDLE_SIZE)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(class="kw">property),class="num">2)+"%" ) : class="kw">property==BAR_PROP_RATIO_UPPER_SHADOW_TO_CANDLE_SIZE ? CMessage::Text(MSG_LIB_TEXT_BAR_RATIO_UPPER_SHADOW_TO_CANDLE_SIZE)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
「K线比例属性的字符串拼装逻辑」
这段分支代码负责把两类K线比例属性转成可读文本:上影线占实体比、下影线占实体比。若当前品种或周期不支持该属性,就返回「不支持」提示,否则用 DoubleToString 保留两位小数并拼上百分号。 具体看 BAR_PROP_RATIO_UPPER_SHADOW_TO_CANDLE_SIZE 分支:先取 MSG_LIB_TEXT_BAR_RATIO_UPPER_SHADOW_TO_CANDLE_SIZE 的文案,再判断 SupportProperty 是否为真。不支持时接 MSG_LIB_PROP_NOT_SUPPORTED,支持时接 DoubleToString(GetProperty(property),2)+"%"。 下影线分支 BAR_PROP_RATIO_LOWER_SHADOW_TO_CANDLE_SIZE 结构完全一致,仅文案与属性枚举不同。你在 MT5 里接这套 CBar 封装时,可改 DoubleToString 的第二参数从 2 调到 3,观察影线比例在黄金 1 分钟图上的细微分化,外汇与贵金属波动大,此类比例仅作概率参考,实操高风险。
: ": "+::DoubleToString(this.GetProperty(class="kw">property),class="num">2)+"%" ) : class="kw">property==BAR_PROP_RATIO_LOWER_SHADOW_TO_CANDLE_SIZE ? CMessage::Text(MSG_LIB_TEXT_BAR_RATIO_LOWER_SHADOW_TO_CANDLE_SIZE)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(class="kw">property),class="num">2)+"%" ) : "" ); }
从零搭一个抽象形态基类
在 MT5 的 \MQL5\Include\DoEasy\Objects\Series\Patterns\ 目录下新建 Pattern.mqh,让形态识别有了统一的底层载体。这个类直接继承库里的 CBaseObj,头文件里用 #include "..\..\BaseObj.mqh" 把基类拉进来,编译层面先打通。 私有段里铺了三张属性表:m_long_prop 装整数属性,m_double_prop 装浮点,m_string_prop 装字符串,数组维度分别由 PATTERN_PROP_INTEGER_TOTAL、PATTERN_PROP_DOUBLE_TOTAL、PATTERN_PROP_STRING_TOTAL 控制。IndexProp 两个重载就是做偏移换算——双精度属性要减掉整数总量,字符串再接着减掉双精度总量,才能在对应数组里拿到真实下标。 受保护成员里藏着一个窗体对象,专门给鼠标悬停 K 线时的信息面板用;还有交易品种数字代码,用来拼形态 ID。这个 ID 不是随便发的:开仓时间 + 形态类型 + 状态 + 方向 + 周期 + 品种代码 揉成一个长整型,靠它就能在形态列表里判重,避免同一根棒线被重复登记。 公共部分除了标准库方法,还暴露了属性简化访问、描述输出和图形对象着色三类接口。构造函数负责把形态属性和图表标签、面板参数一次设好;析构函数则把挂上去的图形对象清掉,不留垃圾对象。比较两个 CPattern 时有两个层级:按指定属性比的是浅比对,全属性比的那个只有所有字段完全一致才返 true,是标准库约定动作。 描述属性的日志方法以前在库文章里讲过多次,这里不展开;图形对象处理方法注释写得直白,读代码比读说明快。基类到此能跑,后面所有价格行为形态——先从最实用的 Pin Bar 起——都挂在它上面填参数就行。外汇和贵金属行情跳空频繁,这类形态识别在极端流动性下误判概率会抬高,上 MT5 跑之前先把品种代码映射测一遍。
class="macro">#class="kw">property copyright "Copyright class="num">2023, MetaQuotes Ltd." class="macro">#class="kw">property link "[MQL5官方文档] class="macro">#class="kw">property version "class="num">1.00" class="macro">#class="kw">property strict class=class="str">"cmt">// Necessary for mql4 class="macro">#include "..\..\BaseObj.mqh" class CPattern : class="kw">public CBaseObj { class="kw">private: class="type">long m_long_prop[PATTERN_PROP_INTEGER_TOTAL]; class=class="str">"cmt">// Integer properties class="type">class="kw">double m_double_prop[PATTERN_PROP_DOUBLE_TOTAL]; class=class="str">"cmt">// Real properties class="type">class="kw">string m_string_prop[PATTERN_PROP_STRING_TOTAL]; class=class="str">"cmt">// String properties class="type">int IndexProp(ENUM_PATTERN_PROP_DOUBLE class="kw">property) const { class="kw">return(class="type">int)class="kw">property-PATTERN_PROP_INTEGER_TOTAL; } class="type">int IndexProp(ENUM_PATTERN_PROP_STRING class="kw">property) const { class="kw">return(class="type">int)class="kw">property-PATTERN_PROP_INTEGER_TOTAL-PATTERN_PROP_DOUBLE_TOTAL; } class="kw">protected: };
◍ 形态类的私有字段与属性写入接口
在 MT5 自定义形态识别模块里,先得把每个形态实例的上下文锁死。下面这段声明把窗体指针、报价小数位、品种数字编码、图形对象名与锚定价格都塞进私有成员,避免运行时被外部误改。 m_symbol_code 用品种名称各字符编码求和得到数值,比直接存字符串更省比较开销;m_digits 直接决定画线和提示文本的小数精度,XAUUSD 常见为 2,EURUSD 常见为 5。 三种方向(看涨 / 看跌 / 双向)各配一组图形色与面板色,实盘里切换品种周期时靠它们做视觉区分,不混色就不会看错信号方向。 公开侧给了两套 SetProperty 重载:整型属性按枚举下标直接写 m_long_prop,双精度属性先经 IndexProp 映射到 m_double_prop 再赋值。你在 EA 里调参时,调用这两个方法就能动态改形态阈值,不用重编译整个类。
CForm *m_form; class=class="str">"cmt">// Pointer to form object class="type">int m_digits; class=class="str">"cmt">// Symbol&class="macro">#x27;s digits value class="type">ulong m_symbol_code; class=class="str">"cmt">// Symbol as a number(sum of name symbol codes) class="type">class="kw">string m_name_graph_obj; class=class="str">"cmt">// Name of the graphical object displaying the pattern class="type">class="kw">double m_price; class=class="str">"cmt">// Price level the graphical object is placed at class="type">color m_color_bullish; class=class="str">"cmt">// Color of a graphical object set to the bullish pattern icon class="type">color m_color_bearish; class=class="str">"cmt">// Color of a graphical object set to the bearish pattern icon class="type">color m_color_bidirect; class=class="str">"cmt">// Color of a graphical object set to the bidirectional pattern icon class="type">color m_color; class=class="str">"cmt">// Graphical object class="type">color class="type">color m_color_panel_bullish; class=class="str">"cmt">// Bullish pattern panel class="type">color class="type">color m_color_panel_bearish; class=class="str">"cmt">// Bearish pattern panel class="type">color class="type">color m_color_panel_bidirect; class=class="str">"cmt">// Bidirectional pattern panel class="type">color class="kw">public: class="kw">public: class=class="str">"cmt">//--- Set pattern(class="num">1) integer, (class="num">2) real and(class="num">3) class="type">class="kw">string properties class="type">void SetProperty(ENUM_PATTERN_PROP_INTEGER class="kw">property,class="type">long value) { this.m_long_prop[class="kw">property]=value; } class="type">void SetProperty(ENUM_PATTERN_PROP_DOUBLE class="kw">property,class="type">class="kw">double value){ this.m_double_prop[this.IndexProp(class="kw">property)]=value; }
「形态对象的属性读写与比较接口」
CPattern 类把形态的属性拆成整型、双精度、字符串三类,分别用重载的 GetProperty 读取,SetProperty 只接受字符串类写入并通过 IndexProp 定位下标。 SupportProperty 三个虚函数默认都返回 true,意味着基类宣称支持全部属性;派生形态若不支持某项,应重写对应函数返回 false,否则调用方会误以为能拿到有效值。 Compare 按 mode 参数对列表排序,IsEqual 则逐属性比对两个形态对象是否等价,这两者在形态库里做去重和按指标排序时会直接用到。 GetObject 返回自身指针,方便把 CPattern 当 CObject 放进标准库容器;默认构造函数把 m_type 设为 OBJECT_DE_TYPE_SERIES_PATTERN,参数化构造函数另收 status、type、id、direction、symbol、timeframe 及 MqlRates 引用。
class="type">void SetProperty(ENUM_PATTERN_PROP_STRING class="kw">property,class="type">class="kw">string value){ this.m_string_prop[this.IndexProp(class="kw">property)]=value; } class=class="str">"cmt">//--- Return(class="num">1) integer, (class="num">2) real and(class="num">3) class="type">class="kw">string pattern properties from the class="kw">property array class="type">long GetProperty(ENUM_PATTERN_PROP_INTEGER class="kw">property) const { class="kw">return this.m_long_prop[class="kw">property]; } class="type">class="kw">double GetProperty(ENUM_PATTERN_PROP_DOUBLE class="kw">property) const { class="kw">return this.m_double_prop[this.IndexProp(class="kw">property)]; } class="type">class="kw">string GetProperty(ENUM_PATTERN_PROP_STRING class="kw">property) const { class="kw">return this.m_string_prop[this.IndexProp(class="kw">property)]; } class=class="str">"cmt">//--- Return the flag of the pattern supporting the specified class="kw">property class="kw">virtual class="type">bool SupportProperty(ENUM_PATTERN_PROP_INTEGER class="kw">property) { class="kw">return true; } class="kw">virtual class="type">bool SupportProperty(ENUM_PATTERN_PROP_DOUBLE class="kw">property) { class="kw">return true; } class="kw">virtual class="type">bool SupportProperty(ENUM_PATTERN_PROP_STRING class="kw">property) { class="kw">return true; } class=class="str">"cmt">//--- Return itself CPattern *GetObject(class="type">void) { class="kw">return &this; } class=class="str">"cmt">//--- Compare CPattern objects by all possible properties(for sorting the lists by a specified pattern object class="kw">property) class="kw">virtual class="type">int Compare(const CObject *node,const class="type">int mode=class="num">0) const; class=class="str">"cmt">//--- Compare CPattern objects with each other by all properties(to search equal pattern objects) class="type">bool IsEqual(CPattern* compared_obj) const; class=class="str">"cmt">//--- Constructors CPattern(){ this.m_type=OBJECT_DE_TYPE_SERIES_PATTERN; } class="kw">protected: class=class="str">"cmt">//--- Protected parametric constructor CPattern(const ENUM_PATTERN_STATUS status, const ENUM_PATTERN_TYPE type, const class="type">uint id, const ENUM_PATTERN_DIRECTION direction, const class="type">class="kw">string symbol, const ENUM_TIMEFRAMES timeframe,class="type">MqlRates &rates); class="kw">public: class=class="str">"cmt">//--- Destructor
用一行访问器吃透形态对象属性
在 MT5 自建形态识别类里,最省事的做法是把常用属性封装成 inline 访问器,避免每次都手写 GetProperty 并强转类型。下面这组方法覆盖了形态类型、方向、周期、状态、代码、ID、控制对象 ID 与定位时间共 8 个字段。
调用时直接写 pat.TypePattern() 就能拿到 ENUM_PATTERN_TYPE,比翻文档查 PATTERN_PROP_TYPE 索引快得多。回测里若同时跟踪 50 个以上未平仓形态,这种轻量访问器能把属性读取开销压到可忽略级别。
外汇与贵金属形态识别受滑点和高波动影响,信号失效概率偏高,实盘前务必在策略测试器用历史数据验证。
ENUM_PATTERN_TYPE TypePattern(class="type">void) const { class="kw">return (ENUM_PATTERN_TYPE)this.GetProperty(PATTERN_PROP_TYPE); } ENUM_PATTERN_DIRECTION Direction(class="type">void) const { class="kw">return (ENUM_PATTERN_DIRECTION)this.GetProperty(PATTERN_PROP_DIRECTION); } ENUM_TIMEFRAMES Timeframe(class="type">void) const { class="kw">return (ENUM_TIMEFRAMES)this.GetProperty(PATTERN_PROP_PERIOD); } ENUM_PATTERN_STATUS Status(class="type">void) const { class="kw">return (ENUM_PATTERN_STATUS)this.GetProperty(PATTERN_PROP_STATUS); } class="type">ulong Code(class="type">void) const { class="kw">return this.GetProperty(PATTERN_PROP_CODE); } class="type">uint ID(class="type">void) const { class="kw">return (class="type">uint)this.GetProperty(PATTERN_PROP_ID); } class="type">ulong ControlObjectID(class="type">void) const { class="kw">return this.GetProperty(PATTERN_PROP_CTRL_OBJ_ID); } class="type">class="kw">datetime Time(class="type">void) const { class="kw">return (class="type">class="kw">datetime)this.GetProperty(PATTERN_PROP_TIME); }
◍ 图形对象的属性读取接口
在 MT5 的图表图形识别类里,一组 const 成员函数直接把形态的关键字段暴露出来,不用你再去翻缓冲区。Candles() 返回组成该形态的 K 线数量(uint 强转),BarPriceOpen/High/Low/Close() 分别取定义柱的 OHLC 双精度值,Symbol() 和 Name() 给品种与形态名。 这些接口底层都走 this.GetProperty(),传入不同的 PATTERN_PROP_* 枚举。实盘里若检测到 'Engulfing' 形态,Name() 会回 'Engulfing',BarPriceClose() 就是吞没柱的收盘价,可直接喂给你的止损算法。 还有三个重载的 GetPropertyDescription(),入参分别是整型、双精度、字符串枚举,用来拿属性的文字说明,调试面板或日志输出时比裸枚举号直观。外汇与贵金属波动大,形态失效概率不低,任何读取结果只作概率参考。
class="type">uint Candles(class="type">void) const { class="kw">return (class="type">uint)this.GetProperty(PATTERN_PROP_CANDLES); } class=class="str">"cmt">//--- Return pattern defining bar prices class="type">class="kw">double BarPriceOpen(class="type">void) const { class="kw">return this.GetProperty(PATTERN_PROP_BAR_PRICE_OPEN); } class="type">class="kw">double BarPriceHigh(class="type">void) const { class="kw">return this.GetProperty(PATTERN_PROP_BAR_PRICE_HIGH); } class="type">class="kw">double BarPriceLow(class="type">void) const { class="kw">return this.GetProperty(PATTERN_PROP_BAR_PRICE_LOW); } class="type">class="kw">double BarPriceClose(class="type">void) const { class="kw">return this.GetProperty(PATTERN_PROP_BAR_PRICE_CLOSE); } class=class="str">"cmt">//--- Return pattern(class="num">1) symbol and(class="num">2) name class="type">class="kw">string Symbol(class="type">void) const { class="kw">return this.GetProperty(PATTERN_PROP_SYMBOL); } class="type">class="kw">string Name(class="type">void) const { class="kw">return this.GetProperty(PATTERN_PROP_NAME); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Descriptions of pattern object properties | class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//--- Get description of pattern(class="num">1) integer, (class="num">2) real and(class="num">3) class="type">class="kw">string class="kw">property class="type">class="kw">string GetPropertyDescription(ENUM_PATTERN_PROP_INTEGER class="kw">property); class="type">class="kw">string GetPropertyDescription(ENUM_PATTERN_PROP_DOUBLE class="kw">property); class="type">class="kw">string GetPropertyDescription(ENUM_PATTERN_PROP_STRING class="kw">property); class=class="str">"cmt">//--- Return description of the pattern(class="num">1) status, (class="num">2) type and(class="num">3) direction
「图形对象与信息面板的颜色接口」
在价格行为形态类的封装里,图形显示和日志输出被拆成了两套虚函数。StatusDescription 与 TypeDescription 默认返回 NULL,真正的形态说明要交给派生类去填,这决定了你自定义形态时日志里能看到什么字。 Print 和 PrintShort 控制日志打印粒度:full_prop 传 true 会倾泻全部属性,false 只打派生类支持的那几个;dash 与 symbol 参数则决定是否用分隔线、是否带品种名。盯盘时若觉得日志太噪,把 full_prop 设 false 能直接降噪。 颜色设定分三层:SetColorBullish / SetColorBearish / SetColorBiDirect 只改形态线本身,SetColorPanelBullish / SetColorPanelBearish 改信息面板背景,SetColors 一把抓四个颜色加 redraw 重绘开关。外汇与贵金属波动剧烈、杠杆风险高,面板配色别用相近色,否则快速扫盘时容易误判多空。 CreateInfoPanel 负责建面板对象,CreateInfoPanelView 留空给子类画外观。DeleteGraphObj 的 redraw 默认 false,删对象后若图表没刷新,手动传 true 更稳。
class="kw">virtual class="type">class="kw">string StatusDescription(class="type">void) const { class="kw">return NULL; } class="kw">virtual class="type">class="kw">string TypeDescription(class="type">void) const { class="kw">return NULL; } class="type">class="kw">string DirectDescription(class="type">void) const; class=class="str">"cmt">//--- Display the description of the object properties in the journal(full_prop=true - all properties, class="kw">false - supported ones only - implemented in descendant classes) class="kw">virtual class="type">void Print(const class="type">bool full_prop=class="kw">false,const class="type">bool dash=class="kw">false); class=class="str">"cmt">//--- Display a class="type">short description of the object in the journal class="kw">virtual class="type">void PrintShort(const class="type">bool dash=class="kw">false,const class="type">bool symbol=class="kw">false); class=class="str">"cmt">//--- Return a class="type">short name of a pattern object class="kw">virtual class="type">class="kw">string Header(class="type">void); class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Handle graphical display | class=class="str">"cmt">//+------------------------------------------------------------------+ class="kw">protected: class=class="str">"cmt">//--- Remove a graphical object class="type">bool DeleteGraphObj(class="type">bool redraw=class="kw">false); class=class="str">"cmt">//--- Set graphical object display colors for the(class="num">1) bullish, (class="num">2) bearish and(class="num">3) bidirectional pattern class="type">void SetColorBullish(const class="type">color clr) { this.m_color_bullish=clr; } class="type">void SetColorBearish(const class="type">color clr) { this.m_color_bearish=clr; } class="type">void SetColorBiDirect(const class="type">color clr) { this.m_color_bidirect=clr; } class=class="str">"cmt">//--- Create the info panel object class="type">bool CreateInfoPanel(class="type">void); class=class="str">"cmt">//--- Create the info panel appearance class="kw">virtual class="type">void CreateInfoPanelView(class="type">void){} class="kw">public: class=class="str">"cmt">//--- Set graphical object display colors and pattern display class="type">color class="type">void SetColors(const class="type">color color_bullish,const class="type">color color_bearish,const class="type">color color_bidirect,const class="type">bool redraw=class="kw">false); class=class="str">"cmt">//--- Set the background class="type">color for the(class="num">1) bullish, (class="num">2) bearish and(class="num">3) bidirectional pattern panel class="type">void SetColorPanelBullish(const class="type">color clr) { this.m_color_panel_bullish=clr; } class="type">void SetColorPanelBearish(const class="type">color clr) { this.m_color_panel_bearish=clr; }
图案对象的着色与绘制接口
在 MT5 自定义形态类里,面板配色分两条路:直接传 color 类型一次性设定双向面板底色,或拆成 R/G/B 三个 uchar 分量分别给看涨、看跌、双向三种面板赋值。后者在需要按主题动态调色时更灵活,比如夜间模板把 B 分量压到 40 以下可能让面板在深色背景上更收敛。 void SetColorPanelBiDirect(const color clr) { this.m_color_panel_bidirect=clr; } // 设置双向面板的底色,参数为 color 类型 void SetColorPanelBullish(const uchar R,const uchar G,const uchar B); // 用 RGB 分量设置看涨面板背景色 void SetColorPanelBearish(const uchar R,const uchar G,const uchar B); // 用 RGB 分量设置看跌面板背景色 void SetColorPanelBiDirect(const uchar R,const uchar G,const uchar B); // 用 RGB 分量设置双向面板背景色 void Draw(const bool redraw=false); // 在图表上绘制形态图标,redraw 默认 false void Show(const bool redraw=false); // 显示形态图标 void Hide(const bool redraw=false); // 隐藏形态图标 void ShowInfoPanel(const int x,const int y); // 在坐标(x,y)显示信息面板 void HideInfoPanel(void); // 隐藏信息面板 构造函数里先抓品种小数位:m_digits 来自 SymbolInfoInteger(symbol,SYMBOL_DIGITS),欧元兑美元这类常见对外汇对通常返回 5 或 3。随后把状态、类型、ID、方向、周期和开高低收全部写进对象属性,其中价格字段直接吃 MqlRates 结构体,省去手动映射。 品种代码用朴素累加生成:遍历 symbol 字符串把每个字符的 ASCII 码加总存进 m_symbol_code。这套编码在回测里能肉眼验证——比如 "EURUSD" 的 m_symbol_code 就是 E+R+U+S+D 各字符码点之和,打开 MT5 用 Print 打出来即可核对。外汇与贵金属波动剧烈,此类可视化工具只辅助判别形态概率,不构成方向承诺。
class="type">void SetColorPanelBiDirect(const class="type">color clr) { this.m_color_panel_bidirect=clr; } class=class="str">"cmt">//--- Set the background class="type">color for the(class="num">1) bullish, (class="num">2) bearish and(class="num">3) bidirectional pattern panel by setting the values of the RGB class="type">color components class="type">void SetColorPanelBullish(const class="type">uchar R,const class="type">uchar G,const class="type">uchar B); class="type">void SetColorPanelBearish(const class="type">uchar R,const class="type">uchar G,const class="type">uchar B); class="type">void SetColorPanelBiDirect(const class="type">uchar R,const class="type">uchar G,const class="type">uchar B); class=class="str">"cmt">//--- Draw a pattern icon on the chart class="type">void Draw(const class="type">bool redraw=class="kw">false); class=class="str">"cmt">//--- (class="num">1) Display, (class="num">2) hide the pattern icon on the chart class="type">void Show(const class="type">bool redraw=class="kw">false); class="type">void Hide(const class="type">bool redraw=class="kw">false); class=class="str">"cmt">//--- (class="num">1) Display and(class="num">2) hide the info panel on the chart class="type">void ShowInfoPanel(const class="type">int x,const class="type">int y); class="type">void HideInfoPanel(class="type">void); };
◍ 形态对象的编码、配色与比较逻辑
在形态类内部,每一根 K 线触发的形态都会生成一个唯一编码:把时间、类型、状态、方向、周期和品种代码叠加成一个 ulong。这个 code 同时写进对象属性,并用作图表标签名(如程序名_p 加数字),方便在 MT5 上直接定位某个图形对象。 方向决定颜色与锚定价格:看涨用蓝色、锚定 low;看跌用红色、锚定 high;双向用绿色、锚定 open。面板底色统一设为 LightGray,不影响方向色识别。 析构时若表单对象非空就 delete,并调用 DeleteGraphObj 清掉图表标签,避免残留。Compare 方法按属性模式排序:整数和实数属性直接比大小返回 1/-1/0,字符串属性走另一分支,使形态可按时间或价格等字段排入容器。 实盘加载这类形态识别 EA 时,建议先打开 Objects List 看 p 前缀标签是否随行情增减;外汇与贵金属波动剧烈,形态误报概率不低,仅作辅助参考。
class="type">ulong code=(class="type">ulong)rates.time+type+status+direction+timeframe+this.m_symbol_code; this.SetProperty(PATTERN_PROP_CODE,code); class=class="str">"cmt">//--- Set pattern graphical objects parameters(chart labels) this.m_name_graph_obj=::StringFormat("%s_p%lu",this.m_name_program,code); this.m_color_bullish=clrBlue; this.m_color_bearish=clrRed; this.m_color_bidirect=clrGreen; if(this.Direction()==PATTERN_DIRECTION_BULLISH) { this.m_color=this.m_color_bullish; this.m_price=rates.low; } else if(this.Direction()==PATTERN_DIRECTION_BEARISH) { this.m_color=this.m_color_bearish; this.m_price=rates.high; } else { this.m_color=this.m_color_bidirect; this.m_price=rates.open; } class=class="str">"cmt">//--- Set base colors of the pattern information panels this.m_color_panel_bullish=clrLightGray; this.m_color_panel_bearish=clrLightGray; this.m_color_panel_bidirect=clrLightGray; this.m_form=NULL; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Destructor | class=class="str">"cmt">//+------------------------------------------------------------------+ CPattern::~CPattern(class="type">void) { class=class="str">"cmt">//--- Delete the form object and pattern label on the chart if(this.m_form!=NULL) class="kw">delete this.m_form; this.DeleteGraphObj(); } class=class="str">"cmt">//+--------------------------------------------------------------------+ class=class="str">"cmt">//| Compare CPattern objects with each other by the specified class="kw">property | class=class="str">"cmt">//+--------------------------------------------------------------------+ class="type">int CPattern::Compare(const CObject *node,const class="type">int mode=class="num">0) const { const CPattern *obj_compared=node; class=class="str">"cmt">//--- compare integer properties of two patterns if(mode<PATTERN_PROP_INTEGER_TOTAL) { class="type">long value_compared=obj_compared.GetProperty((ENUM_PATTERN_PROP_INTEGER)mode); class="type">long value_current=this.GetProperty((ENUM_PATTERN_PROP_INTEGER)mode); class="kw">return(value_current>value_compared ? class="num">1 : value_current<value_compared ? -class="num">1 : class="num">0); } class=class="str">"cmt">//--- compare real properties of two patterns else if(mode<PATTERN_PROP_DOUBLE_TOTAL+PATTERN_PROP_INTEGER_TOTAL) { class="type">class="kw">double value_compared=obj_compared.GetProperty((ENUM_PATTERN_PROP_DOUBLE)mode); class="type">class="kw">double value_current=this.GetProperty((ENUM_PATTERN_PROP_DOUBLE)mode); class="kw">return(value_current>value_compared ? class="num">1 : value_current<value_compared ? -class="num">1 : class="num">0); } class=class="str">"cmt">//--- compare class="type">class="kw">string properties of two patterns else if(mode<PATTERN_PROP_DOUBLE_TOTAL+PATTERN_PROP_INTEGER_TOTAL+PATTERN_PROP_STRING_TOTAL) {
「图案对象的等值判定与日志输出」
在价格行为识别里,把两个 CPattern 实例判等不能只比指针,必须逐属性核对。IsEqual 方法按整数、双精度、字符串三类属性顺序遍历,任意一项不一致就直接返回 false,全过才返回 true。 整数段从 0 扫到 PATTERN_PROP_INTEGER_TOTAL,双精度段接在后面偏移同常量长度,字符串段再接一段;三段边界用 begin/end 滚动推进,避免写死下标。 Print 方法把属性打到 MT5 专家日志,full_prop 为 false 时只输出 SupportProperty 认可的属性,减少噪音;true 则全量打印。日志里用 ===== 和 ------ 做视觉分隔,方便肉眼比对不同图案的差异。 外汇与贵金属波动剧烈,这类图案比对仅用于辅助筛选,信号失效概率不低,实盘须严控仓位。
class="type">class="kw">string value_compared=obj_compared.GetProperty((ENUM_PATTERN_PROP_STRING)mode); class="type">class="kw">string value_current=this.GetProperty((ENUM_PATTERN_PROP_STRING)mode); class="kw">return(value_current>value_compared ? class="num">1 : value_current<value_compared ? -class="num">1 : class="num">0); } class="kw">return class="num">0; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Compare CPattern objects with each other by all properties | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CPattern::IsEqual(CPattern *compared_obj) const { class="type">int begin=class="num">0, end=PATTERN_PROP_INTEGER_TOTAL; for(class="type">int i=begin; i<end; i++) { ENUM_PATTERN_PROP_INTEGER prop=(ENUM_PATTERN_PROP_INTEGER)i; if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) class="kw">return class="kw">false; } begin=end; end+=PATTERN_PROP_DOUBLE_TOTAL; for(class="type">int i=begin; i<end; i++) { ENUM_PATTERN_PROP_DOUBLE prop=(ENUM_PATTERN_PROP_DOUBLE)i; if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) class="kw">return class="kw">false; } begin=end; end+=PATTERN_PROP_STRING_TOTAL; for(class="type">int i=begin; i<end; i++) { ENUM_PATTERN_PROP_STRING prop=(ENUM_PATTERN_PROP_STRING)i; if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) class="kw">return class="kw">false; } class="kw">return true; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Send pattern properties to the journal | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CPattern::Print(const class="type">bool full_prop=class="kw">false,const class="type">bool dash=class="kw">false) { ::Print("============= ",CMessage::Text(MSG_LIB_PARAMS_LIST_BEG)," (",this.Header(),") ============="); class="type">int begin=class="num">0, end=PATTERN_PROP_INTEGER_TOTAL; for(class="type">int i=begin; i<end; i++) { ENUM_PATTERN_PROP_INTEGER prop=(ENUM_PATTERN_PROP_INTEGER)i; if(!full_prop && !this.SupportProperty(prop)) class="kw">continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("------"); begin=end; end+=PATTERN_PROP_DOUBLE_TOTAL; for(class="type">int i=begin; i<end; i++) { ENUM_PATTERN_PROP_DOUBLE prop=(ENUM_PATTERN_PROP_DOUBLE)i; if(!full_prop && !this.SupportProperty(prop)) class="kw">continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("------"); begin=end; end+=PATTERN_PROP_STRING_TOTAL; for(class="type">int i=begin; i<end; i++) { ENUM_PATTERN_PROP_STRING prop=(ENUM_PATTERN_PROP_STRING)i; if(!full_prop && !this.SupportProperty(prop)) class="kw">continue; ::Print(this.GetPropertyDescription(prop)); }
把形态对象打印到日志的底层函数
CPattern 类提供三组方法把形态信息输出到 MT5 专家日志,方便盯盘时快速核对识别到的价格行为。PrintShort 用 dash 和 symbol 两个开关控制是否加前缀横线、是否重复品种名,一行就能看到「状态+类型: 品种, 周期 时间, 方向描述」。 Header 方法返回「状态描述+空格+类型描述」的短串,被 PrintShort 和日志分隔线复用,保证不同打印入口的抬头一致。
| GetPropertyDescription 按枚举属性拼文字:PATTERN_PROP_CODE 取形态代码,PATTERN_PROP_TIME 用 TimeToString 带 TIME_DATE | TIME_MINUTES | TIME_SECONDS 精确到秒,PATTERN_PROP_STATUS / TYPE / ID 分别走各自 Description 或强转 string;若 SupportProperty 返回 false 则补「不支持」文案。 |
|---|
在 MT5 里新建 EA 调用 CPattern::PrintShort 跑一晚,能直接验证日志输出格式是否符合你的复盘习惯。外汇与贵金属波动剧烈,日志仅作辅助,实盘仍属高风险。
::Print("============= ",CMessage::Text(MSG_LIB_PARAMS_LIST_END)," (",this.Header(),") =============\n"); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Display the class="type">short pattern description in the journal | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CPattern::PrintShort(const class="type">bool dash=class="kw">false,const class="type">bool symbol=class="kw">false) { ::Print(this.Header(),":\n",(dash ? " - " : ""),this.Symbol(),", ",TimeframeDescription(this.Timeframe())," ",::TimeToString(this.Time()),", ",this.DirectDescription()); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Return a class="type">short name of a pattern object | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">class="kw">string CPattern::Header(class="type">void) { class="kw">return(this.StatusDescription()+" "+this.TypeDescription()); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Return the description of the pattern integer class="kw">property | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">class="kw">string CPattern::GetPropertyDescription(ENUM_PATTERN_PROP_INTEGER class="kw">property) { class="kw">return ( class="kw">property==PATTERN_PROP_CODE ? CMessage::Text(MSG_LIB_TEXT_PATTERN_CODE)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(class="type">class="kw">string)this.GetProperty(class="kw">property) ) : class="kw">property==PATTERN_PROP_TIME ? CMessage::Text(MSG_LIB_TEXT_PATTERN_TIME)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::TimeToString(this.GetProperty(class="kw">property),TIME_DATE|TIME_MINUTES|TIME_SECONDS) ) : class="kw">property==PATTERN_PROP_STATUS ? CMessage::Text(MSG_ORD_STATUS)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.StatusDescription() ) : class="kw">property==PATTERN_PROP_TYPE ? CMessage::Text(MSG_ORD_TYPE)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.TypeDescription() ) : class="kw">property==PATTERN_PROP_ID ? CMessage::Text(MSG_LIB_TEXT_PATTERN_ID)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(class="type">class="kw">string)this.GetProperty(class="kw">property)
◍ 图形属性描述的双类型分支
CPattern 类把属性描述拆成两套重载:整数类属性走 GetPropertyDescription(ENUM_PATTERN_PROP_INTEGER),双精度类走 GetPropertyDescription(ENUM_PATTERN_PROP_DOUBLE),两者都用三元嵌套判断属性枚举再拼文本。 整数版覆盖控件对象ID、方向、周期、组成K线数等,不支持的属性统一回 MSG_LIB_PROP_NOT_SUPPORTED;双精度版专管单根 K 线的开高低收,用 m_digits 决定小数位,未设时退化为 1 位。 实盘调试时若日志里某属性显示「不支持」,先确认该 Pattern 实例是否真的登记了对应属性,而不是代码写错枚举。外汇与贵金属波动剧烈,这类描述仅用于诊断,不构成方向判断。
class="type">class="kw">string CPattern::GetPropertyDescription(ENUM_PATTERN_PROP_INTEGER class="kw">property) { class="kw">return ( class="kw">property==PATTERN_PROP_CTRL_OBJ_ID ? CMessage::Text(MSG_LIB_TEXT_PATTERN_CTRL_OBJ_ID)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+((class="type">class="kw">string)this.GetProperty(class="kw">property)) ) : class="kw">property==PATTERN_PROP_DIRECTION ? CMessage::Text(MSG_LIB_TEXT_PATTERN_DIRECTION)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.DirectDescription() ) : class="kw">property==PATTERN_PROP_PERIOD ? CMessage::Text(MSG_LIB_TEXT_BAR_PERIOD)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+TimeframeDescription((ENUM_TIMEFRAMES)this.GetProperty(class="kw">property)) ) : class="kw">property==PATTERN_PROP_CANDLES ? CMessage::Text(MSG_LIB_TEXT_PATTERN_CANDLES)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+((class="type">class="kw">string)this.GetProperty(class="kw">property)) ) : "" ); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Return the description of the pattern real class="kw">property | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">class="kw">string CPattern::GetPropertyDescription(ENUM_PATTERN_PROP_DOUBLE class="kw">property) { class="type">int dg=(this.m_digits>class="num">0 ? this.m_digits : class="num">1); class="kw">return ( class="kw">property==PATTERN_PROP_BAR_PRICE_OPEN ? CMessage::Text(MSG_LIB_TEXT_PATTERN_BAR_PRICE_OPEN)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(class="kw">property),dg) ) : class="kw">property==PATTERN_PROP_BAR_PRICE_HIGH ? CMessage::Text(MSG_LIB_TEXT_PATTERN_BAR_PRICE_HIGH)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(class="kw">property),dg) ) : class="kw">property==PATTERN_PROP_BAR_PRICE_LOW ? CMessage::Text(MSG_LIB_TEXT_PATTERN_BAR_PRICE_LOW)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(class="kw">property),dg) ) : class="kw">property==PATTERN_PROP_BAR_PRICE_CLOSE ? CMessage::Text(MSG_LIB_TEXT_PATTERN_BAR_PRICE_CLOSE)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(class="kw">property),dg) ) : "" ); }
「K线形态比例属性的描述拼接逻辑」
这段实现集中在 CPattern 类的属性描述方法里,针对六种数值型比例属性做统一的字符串拼装:实体占整根蜡烛尺寸比、下影线占比、上影线占比,以及三个判定阈值(实体比临界、较大影线比临界、较小影线比临界)。 每个属性先用 CMessage::Text 取本地化文案,再判断 this.SupportProperty 是否支持;不支持就追加「: 不支持」提示,支持则调用 ::DoubleToString 以两位小数精度输出 GetProperty 的返回值。 代码里所有数值都按 2 位小数格式化,意味着你在日志或面板里看到的比例值精度固定为 0.01 量级,调参时别指望看到第三位小数。外汇与贵金属品种上跑这套描述输出时,注意影线占比在跳空缺口行情可能异常,属高风险数据噪声。 末尾的空字符串 "" 是三元表达式兜底,说明若传入未列举的属性枚举,方法安静返回空而非报错,方便你扩写新属性时不必改主干。
) :
class="kw">property==PATTERN_PROP_RATIO_BODY_TO_CANDLE_SIZE ? CMessage::Text(MSG_LIB_TEXT_PATTERN_RATIO_BODY_TO_CANDLE_SIZE)+
(!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
": "+::DoubleToString(this.GetProperty(class="kw">property),class="num">2)
) :
class="kw">property==PATTERN_PROP_RATIO_LOWER_SHADOW_TO_CANDLE_SIZE ? CMessage::Text(MSG_LIB_TEXT_PATTERN_RATIO_LOWER_SHADOW_TO_CANDLE_SIZE)+
(!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
": "+::DoubleToString(this.GetProperty(class="kw">property),class="num">2)
) :
class="kw">property==PATTERN_PROP_RATIO_UPPER_SHADOW_TO_CANDLE_SIZE ? CMessage::Text(MSG_LIB_TEXT_PATTERN_RATIO_UPPER_SHADOW_TO_CANDLE_SIZE)+
(!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
": "+::DoubleToString(this.GetProperty(class="kw">property),class="num">2)
) :
class="kw">property==PATTERN_PROP_RATIO_BODY_TO_CANDLE_SIZE_CRITERION ? CMessage::Text(MSG_LIB_TEXT_PATTERN_RATIO_BODY_TO_CANDLE_SIZE_CRIT)+
(!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
": "+::DoubleToString(this.GetProperty(class="kw">property),class="num">2)
) :
class="kw">property==PATTERN_PROP_RATIO_LARGER_SHADOW_TO_CANDLE_SIZE_CRITERION ? CMessage::Text(MSG_LIB_TEXT_PATTERN_RATIO_LARGER_SHADOW_TO_CANDLE_SIZE_CRIT)+
(!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
": "+::DoubleToString(this.GetProperty(class="kw">property),class="num">2)
) :
class="kw">property==PATTERN_PROP_RATIO_SMALLER_SHADOW_TO_CANDLE_SIZE_CRITERION ? CMessage::Text(MSG_LIB_TEXT_PATTERN_RATIO_SMALLER_SHADOW_TO_CANDLE_SIZE_CRIT)+
(!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
": "+::DoubleToString(this.GetProperty(class="kw">property),class="num">2)
) :
""
);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Return the description of the pattern class="type">class="kw">string class="kw">property |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">class="kw">string CPattern::GetPropertyDescription(ENUM_PATTERN_PROP_STRING class="kw">property)
{
class="kw">return
(
class="kw">property==PATTERN_PROP_SYMBOL ? CMessage::Text(MSG_LIB_PROP_SYMBOL)+
(!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
": "+this.GetProperty(class="kw">property)
) :
class="kw">property==PATTERN_PROP_NAME ? CMessage::Text(MSG_LIB_TEXT_PATTERN_NAME)+图形对象与信息面板的底层清理逻辑
CPattern 类里对图表图形对象的删除走的是先查后删:用 ObjectFind 在指定图表 ID 下找 m_name_graph_obj,若返回小于 0 说明对象不存在,直接返回 true 算作成功;找到后才调 ObjectDelete,删除成功且 redraw 为 true 时顺手 ChartRedraw 刷新,否则返回 false。这套逻辑避免了重复删除报错的干扰,实盘跑形态识别 EA 时不会因为对象已手动删掉就崩。 方向描述函数 DirectDescription 用 switch 接 Direction() 的返回值:PATTERN_DIRECTION_BULLISH 对应多头文字、BEARISH 对应空头、BOTH 对应双向,default 落 Unknown。回测或日志里看到 Unknown 就说明形态方向枚举越界,不是行情问题。 信息面板 CreateInfoPanel 的锚点计算值得抄:先取形态 K 线的 High 与 Low 均值作为 price,再拿 ChartTimePriceToXY 把(Time(), price)转成画布 x/y 像素坐标,面板据此定位在 K 线中部左侧。m_form 若已非 NULL 直接返回 true,防止重复建面板吃资源。外汇与贵金属波动大,这类图形对象在高波动跳空时可能定位偏移,建议开 MT5 用 EURUSD 的 M15 手动触发一次看锚点是否贴合。
class="type">class="kw">string CPattern::DirectDescription(class="type">void) const { class="kw">switch(this.Direction()) { case PATTERN_DIRECTION_BULLISH : class="kw">return CMessage::Text(MSG_LIB_TEXT_PATTERN_BULLISH); case PATTERN_DIRECTION_BEARISH : class="kw">return CMessage::Text(MSG_LIB_TEXT_PATTERN_BEARISH); case PATTERN_DIRECTION_BOTH : class="kw">return CMessage::Text(MSG_LIB_TEXT_PATTERN_BOTH); class="kw">default : class="kw">return "Unknown"; } } class="type">bool CPattern::DeleteGraphObj(class="type">bool redraw=class="kw">false) { if(::ObjectFind(this.m_chart_id,this.m_name_graph_obj)<class="num">0) class="kw">return true; if(::ObjectDelete(this.m_chart_id,this.m_name_graph_obj)) { if(redraw) ::ChartRedraw(this.m_chart_id); class="kw">return true; } class="kw">return class="kw">false; } class="type">bool CPattern::CreateInfoPanel(class="type">void) { class="type">int x=class="num">0, y=class="num">0; if(this.m_form!=NULL) class="kw">return true; class="type">class="kw">double price=(this.BarPriceHigh()+this.BarPriceLow())/class="num">2; if(!::ChartTimePriceToXY(this.m_chart_id,class="num">0,this.Time(),price,x,y)) class="kw">return class="kw">false; this.m_form=this.CreateForm(this.ID(),this.Name(),x,y,class="num">4,class="num">4); if(this.m_form==NULL) class="kw">return class="kw">false; }
◍ 信息面板防越界与图标绘制的实现细节
CPattern 类里负责把形态信息面板挂到图表上的核心是两个方法:ShowInfoPanel 与 HideInfoPanel。ShowInfoPanel 先判断 m_form 是否为 NULL,为空就调 CreateInfoPanel 建对象,建不成直接 return,避免空指针把 EA 跑崩。 面板坐标做了边界钳制:用 ChartGetInteger 取 CHART_WIDTH_IN_PIXELS 和 CHART_HEIGHT_IN_PIXELS,若 x+面板宽超过 chart_w-1,就把 cx 设为 chart_w-1-面板宽,y 方向同理。这样面板在任何分辨率下都不会画出图表可视区。 Draw 方法负责画形态图标:先 ObjectFind 查图形对象是否存在,没有就用 CreateTrendLine 建一条从 (Time(),m_price) 到 (Time(),m_price)、线宽 5、指定颜色的趋势线;已存在则走 Show(redraw) 直接显示。外汇与贵金属图表上叠加这类自定义对象,需注意对象名冲突可能导致隐藏形态误显,属于高风险调试环节,建议开 MT5 用不同品种验证。
this.CreateInfoPanelView(); class="kw">return true; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Display the info panel on the chart | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CPattern::ShowInfoPanel(const class="type">int x,const class="type">int y) { class=class="str">"cmt">//--- If there is no panel object yet, create it if(this.m_form==NULL) if(!this.CreateInfoPanel()) class="kw">return; class=class="str">"cmt">//--- Get the chart width and height class="type">int chart_w=(class="type">int)::ChartGetInteger(this.m_chart_id,CHART_WIDTH_IN_PIXELS); class="type">int chart_h=(class="type">int)::ChartGetInteger(this.m_chart_id,CHART_HEIGHT_IN_PIXELS); class=class="str">"cmt">//--- Calculate the X and Y coordinates of the panel so that it does not go beyond the chart class="type">int cx=(x+this.m_form.Width() >chart_w-class="num">1 ? chart_w-class="num">1-this.m_form.Width() : x); class="type">int cy=(y+this.m_form.Height()>chart_h-class="num">1 ? chart_h-class="num">1-this.m_form.Height() : y); class=class="str">"cmt">//--- Set the calculated coordinates and display the panel if(this.m_form.SetCoordX(cx) && this.m_form.SetCoordY(cy)) this.m_form.Show(); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Hide the info panel on the chart | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CPattern::HideInfoPanel(class="type">void) { if(this.m_form!=NULL) this.m_form.Hide(); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Draw the pattern icon on the chart | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CPattern::Draw(const class="type">bool redraw=class="kw">false) { class=class="str">"cmt">//--- If the graphical object has not yet been created, create it if(::ObjectFind(this.m_chart_id,this.m_name_graph_obj)<class="num">0) { if(!this.CreateTrendLine(this.m_chart_id,this.m_name_graph_obj,class="num">0,this.Time(),this.m_price,this.Time(),this.m_price,this.m_color,class="num">5)) class="kw">return; } class=class="str">"cmt">//--- Otherwise - display else this.Show(redraw); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Display the pattern icon on the chart | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CPattern::Show(const class="type">bool redraw=class="kw">false) {
「图案对象的显隐与配色控制」
在 MT5 的自定义形态类里,图表上的图形对象并不靠删除来切换状态,而是用时间框架属性做显隐。显示时把 OBJPROP_TIMEFRAMES 设为 OBJ_ALL_PERIODS,隐藏时设为 OBJ_NO_PERIODS,对象仍在内存里,只是不在任何周期出图。 Hide 方法只做一件事:把图形对象的时间框架掩码清零。传入 redraw=true 才会顺手调一次 ChartRedraw,否则改动要等下一次图表重绘才生效;批量操作多个形态时建议最后统一重绘,少调几次能省点资源。 SetColors 接收三根颜色线:看涨、看跌、双向,内部先落成员变量,再按 Direction() 的返回值挑主色写进 OBJPROP_COLOR。也就是说,形态方向变了颜色会自动跟,不用在外面手动改。 外汇和贵金属波动大、滑点多,这类图形叠加只是辅助看位,信号失效的概率不低,真要挂单还得自己核上下文。
::ObjectSetInteger(this.m_chart_id,this.m_name_graph_obj,OBJPROP_TIMEFRAMES,OBJ_ALL_PERIODS); if(redraw) ::ChartRedraw(this.m_chart_id); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Hide the pattern icon on the chart | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CPattern::Hide(const class="type">bool redraw=class="kw">false) { ::ObjectSetInteger(this.m_chart_id,this.m_name_graph_obj,OBJPROP_TIMEFRAMES,OBJ_NO_PERIODS); if(redraw) ::ChartRedraw(this.m_chart_id); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Set graphical object and | class=class="str">"cmt">//| and pattern display colors | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CPattern::SetColors(const class="type">color color_bullish,const class="type">color color_bearish,const class="type">color color_bidirect,const class="type">bool redraw=class="kw">false) { this.SetColorBullish(color_bullish); this.SetColorBearish(color_bearish); this.SetColorBiDirect(color_bidirect); this.m_color=(this.Direction()==PATTERN_DIRECTION_BULLISH ? this.m_color_bullish : this.Direction()==PATTERN_DIRECTION_BEARISH ? this.m_color_bearish : this.m_color_bidirect); ::ObjectSetInteger(this.m_chart_id,this.m_name_graph_obj,OBJPROP_COLOR,this.m_color); if(redraw) ::ChartRedraw(this.m_chart_id); }
在 MT5 里把 Pin Bar 写成可复用类
价格行为里,主体极短、单侧影线极长、另一侧影线近乎消失的柱形,往往比其他蜡烛更能暗示后续走向。把它做成代码类,核心不是画一根针,而是让库能统一识别、筛选、排序这类形态。 下面这个类从基础形态类继承,所有属性标志都返回 true,意味着 Pin Bar 暂不支持的属性以后也会在库层面禁掉,不会和无关字段混在一起。构造时把状态“Price Action”、类型“Pin Bar”以及传入的 K 线数据交给父类,自身只补两件事:形态名称设成 "Pin Bar",组成蜡烛数设成 1。 信息面板用虚方法按形态定制。Pin Bar 的面板标题写清三根比例:烛体占整根 16.22%、上影 18.92%、下影 64.86%;搜索阈值则标为烛体不超 30%、最长影线至少 60%、最短影线不超 30%。不同形态面板数据不同,正是虚方法存在的意义。 要把形态接进库,需在 CSelect(路径 MQL5\Include\DoEasy\Services\Select.mqh)里包含形态头文件并声明处理形态的方法。原先 CompareValues() 用的 if-else 改成 switch,对象比较速度会更快。外汇与贵金属波动剧烈,Pin Bar 仅为概率信号,实盘前请在 MT5 策略测试器用历史数据验证阈值。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Pin Bar pattern class | class=class="str">"cmt">//+------------------------------------------------------------------+ class CPatternPinBar : class="kw">public CPattern { class="kw">protected: class=class="str">"cmt">//--- Create the info panel appearance class="kw">virtual class="type">void CreateInfoPanelView(class="type">void); class="kw">public: class=class="str">"cmt">//--- Return the flag of the pattern supporting the specified class="kw">property class="kw">virtual class="type">bool SupportProperty(ENUM_PATTERN_PROP_INTEGER class="kw">property) { class="kw">return true; } class="kw">virtual class="type">bool SupportProperty(ENUM_PATTERN_PROP_DOUBLE class="kw">property) { class="kw">return true; } class="kw">virtual class="type">bool SupportProperty(ENUM_PATTERN_PROP_STRING class="kw">property) { class="kw">return true; } class=class="str">"cmt">//--- Return description of the pattern(class="num">1) status and(class="num">2) type class="kw">virtual class="type">class="kw">string StatusDescription(class="type">void) const { class="kw">return CMessage::Text(MSG_LIB_TEXT_PATTERN_STATUS_PA); } class="kw">virtual class="type">class="kw">string TypeDescription(class="type">void) const { class="kw">return CMessage::Text(MSG_LIB_TEXT_PATTERN_TYPE_PIN_BAR); } class=class="str">"cmt">//--- Constructor CPatternPinBar(const class="type">uint id,const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,class="type">MqlRates &rates,const ENUM_PATTERN_DIRECTION direct); }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Constructor | class=class="str">"cmt">//+------------------------------------------------------------------+ CPatternPinBar::CPatternPinBar(const class="type">uint id,const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,class="type">MqlRates &rates,const ENUM_PATTERN_DIRECTION direct) : CPattern(PATTERN_STATUS_PA,PATTERN_TYPE_PIN_BAR,id,direct,symbol,timeframe,rates) { this.SetProperty(PATTERN_PROP_NAME,"Pin Bar"); this.SetProperty(PATTERN_PROP_CANDLES,class="num">1); } class=class="str">"cmt">//+------------------------------------------------------------------+
◍ Pin Bar 信息面板的配色与文案生成
在 MT5 里做 Pin Bar 形态识别,光把图形画出来不够,面板配色得让交易者一眼分出多空。CPatternPinBar::CreateInfoPanelView 这套逻辑,先按方向把底色调出倾向:看涨用偏蓝、看跌用偏红,亮度各先提 5 再分别往 RGB 里加 30 的蓝或红分量。 具体调色靠 m_form 的两个方法链:ChangeColorLightness 控明暗,ChangeRGBComponents 补色相。比如看涨色是 ChangeRGBComponents(ChangeColorLightness(m_color_panel_bullish,5),0,0,30),也就是提亮后纯加蓝;看跌对称加红。双向(bidirect)则直接用预设色,不染色。 方向分支里,梯度填充首尾色用 ±2.5 的亮度差:clr[0] 暗一点、clr[1] 亮一点,视觉上让面板有轻微立体感。背景色随 Direction() 三态切换,边框统一 clrGray。 文案行用 StringFormat 拼:第一行 name 显示实体/上下影占整根蜡烛的比值,如 Pin Bar (0.15/0.80/0.05);第二行 param 带方向描述加三个判定阈值。面板坐标写死 x=3、y=20,宽度高度按文本长度后续算——你拷这段代码到自己的 EA 里,改 m_color_panel_bullish 的基准色就能换皮肤。外汇与贵金属波动剧烈,形态识别仅作概率参考,实盘须严控仓位。
class="type">void CPatternPinBar::CreateInfoPanelView(class="type">void) { class=class="str">"cmt">//--- If the form object is not created, leave if(this.m_form==NULL) class="kw">return; class=class="str">"cmt">//--- Change the class="type">color tone for bullish and bearish patterns: the bullish ones will have a blue tint, while the bearish ones will have a red tint class="type">color color_bullish=this.m_form.ChangeRGBComponents(this.m_form.ChangeColorLightness(this.m_color_panel_bullish,class="num">5),class="num">0,class="num">0,class="num">30); class="type">color color_bearish=this.m_form.ChangeRGBComponents(this.m_form.ChangeColorLightness(this.m_color_panel_bearish,class="num">5),class="num">30,class="num">0,class="num">0); class="type">color color_bidirect=this.m_color_bidirect; class=class="str">"cmt">//--- Declare the array for the initial and final colors of the gradient fill class="type">color clr[class="num">2]={}; class=class="str">"cmt">//--- Depending on the direction of the pattern, change the lightness of the corresponding colors - the initial one is a little darker, the final one is a little lighter class="kw">switch(this.Direction()) { case PATTERN_DIRECTION_BULLISH : clr[class="num">0]=this.m_form.ChangeColorLightness(color_bullish,-class="num">2.5); clr[class="num">1]=this.m_form.ChangeColorLightness(color_bullish,class="num">2.5); class="kw">break; case PATTERN_DIRECTION_BEARISH : clr[class="num">0]=this.m_form.ChangeColorLightness(color_bearish,-class="num">2.5); clr[class="num">1]=this.m_form.ChangeColorLightness(color_bearish,class="num">2.5); class="kw">break; class="kw">default: clr[class="num">0]=this.m_form.ChangeColorLightness(color_bidirect,-class="num">2.5); clr[class="num">1]=this.m_form.ChangeColorLightness(color_bidirect,class="num">2.5); class="kw">break; } class=class="str">"cmt">//--- Set the background and form frame colors this.m_form.SetBackgroundColor(this.Direction()==PATTERN_DIRECTION_BULLISH ? color_bullish : this.Direction()==PATTERN_DIRECTION_BEARISH ? color_bearish : this.m_color_bidirect,true); this.m_form.SetBorderColor(clrGray,true); class=class="str">"cmt">//--- Create strings to describe the pattern, its parameters and search criteria class="type">class="kw">string name=::StringFormat("Pin Bar(%.2f/%.2f/%.2f)",this.GetProperty(PATTERN_PROP_RATIO_BODY_TO_CANDLE_SIZE),this.GetProperty(PATTERN_PROP_RATIO_UPPER_SHADOW_TO_CANDLE_SIZE),this.GetProperty(PATTERN_PROP_RATIO_LOWER_SHADOW_TO_CANDLE_SIZE)); class="type">class="kw">string param=::StringFormat("%s(%.2f/%.2f/%.2f)",this.DirectDescription(),this.GetProperty(PATTERN_PROP_RATIO_BODY_TO_CANDLE_SIZE_CRITERION),this.GetProperty(PATTERN_PROP_RATIO_LARGER_SHADOW_TO_CANDLE_SIZE_CRITERION),this.GetProperty(PATTERN_PROP_RATIO_SMALLER_SHADOW_TO_CANDLE_SIZE_CRITERION)); class=class="str">"cmt">//--- Set the coordinates of the panel and calculate its width and height depending on the size of the texts placed on the panel class="type">int x=class="num">3; class="type">int y=class="num">20;
「图形面板按图表边界自适应尺寸与坐标」
在 MT5 自定义形态标注面板里,尺寸不能写死。先用 fmax 比对标题文本宽、参数文本宽和时间字符串宽,得出面板宽度 w 的基础值 4+,高度 h 则取 2+(20+描述行高+时间行高),这样无论品种名多长都不会被截断。 拿到宽高后立刻用 SetWidth/SetHeight 生效,随后通过 ChartGetInteger 取 CHART_WIDTH_IN_PIXELS 与 CHART_HEIGHT_IN_PIXELS 得到画布像素。若 RightEdge 超出 chart_w-1 或 BottomEdge 超出 chart_h-1,就把坐标拉回边界内,避免面板跑到可视区外。 绘制阶段先用 Erase 以透明度 200 做渐变底,再 DrawFrameSimple 画 1px 边框、DrawIconInfo 放信息图标、Text 写标题,并在 y=18 处 DrawLine 画一条 clrDarkGray 分隔线。线下方 y 从 20 起排参数说明与时间,最后 Update(true) 在重绘时刷新。 直接把这段逻辑拷进你的 CPanel 派生类,改 name/param 来源即可在 EURUSD 或 XAUUSD 上验证;外汇与贵金属波动剧烈,面板仅作辅助参考,实际信号须结合风控。
class="type">int w=class="num">4+(::fmax(class="num">20+this.m_form.TextWidth(name),::fmax(x+this.m_form.TextWidth(param),x+this.m_form.TextWidth(::TimeToString(this.Time()))))); class="type">int h=class="num">2+(class="num">20+this.m_form.TextHeight(this.DirectDescription())+this.m_form.TextHeight(::TimeToString(this.Time()))); class=class="str">"cmt">//--- Set the width and height of the panel according to the calculated values this.m_form.SetWidth(w); this.m_form.SetHeight(h); class=class="str">"cmt">//--- Depending on the chart size and coordinates, we calculate the coordinates of the panel so that it does not go beyond the chart class="type">int chart_w=(class="type">int)::ChartGetInteger(this.m_chart_id,CHART_WIDTH_IN_PIXELS); class="type">int chart_h=(class="type">int)::ChartGetInteger(this.m_chart_id,CHART_HEIGHT_IN_PIXELS); class="type">int cx=(this.m_form.RightEdge() >chart_w-class="num">1 ? chart_w-class="num">1-this.m_form.Width() : this.m_form.CoordX()); class="type">int cy=(this.m_form.BottomEdge()>chart_h-class="num">1 ? chart_h-class="num">1-this.m_form.Height() : this.m_form.CoordY()); this.m_form.SetCoordX(cx); this.m_form.SetCoordY(cy); class=class="str">"cmt">//--- Fill the background with a gradient class="type">color this.m_form.Erase(clr,class="num">200,true,class="kw">false); class=class="str">"cmt">//--- Draw the panel frame, an icon with(i), draw the header text with the proportions of a candle and separate the header with a horizontal line this.m_form.DrawFrameSimple(class="num">0,class="num">0,this.m_form.Width(),this.m_form.Height(),class="num">1,class="num">1,class="num">1,class="num">1,this.m_form.BorderColor(),class="num">200); this.m_form.DrawIconInfo(class="num">1,class="num">1,class="num">200); this.m_form.Text(class="num">20,class="num">3,name,clrBlack,class="num">200); this.m_form.DrawLine(class="num">1,class="num">18,this.m_form.Width()-class="num">1,class="num">18,clrDarkGray,class="num">250); class=class="str">"cmt">//--- Under the horizontal line, enter the pattern description with its search criteria and the date of the pattern-defining bar y=class="num">20; this.m_form.Text(x,y,param,clrBlack,class="num">200); y+=this.m_form.TextHeight(::TimeToString(this.Time())); this.m_form.Text(x,y,::TimeToString(this.Time()),clrBlack,class="num">200); class=class="str">"cmt">//--- Update the panel while redrawing the chart this.m_form.Update(true); }
把 K 线与形态塞进对象容器
这套 EA 框架把行情数据全拆成了对象:K 线、形态、账户、品种各自有独立的 include 文件,编译时由预处理器拼进主程序。其中 Pattern.mqh 专门管形态序列,路径是 ..\Objects\Series\Patterns\Pattern.mqh,和 K 线序列 SeriesDE.mqh 平级挂在 Series 目录下。
针对 K 线本身,框架提供了三组静态方法按属性筛柱:整数、双精度、字符串三种类型各一套 ByBarProperty,调用时传入源列表、属性枚举、目标值和比较模式(等于/大于/小于等)。例如按某根 bar 的成交量整数属性挑出大于阈值的子集,返回的是 CArrayObj* 指针,直接在内存里操作,不碰图表。
极值定位也分类型重载:FindBarMax 和 FindBarMin 分别回传列表中最大或最小属性值所在的 bar 索引,整数、real、string 各三版。实盘里若想找最近 50 根里实体最长的那根,用 ENUM_BAR_PROP_DOUBLE 版跑一次即可,外汇与贵金属波动随机,这类筛选只给出概率倾向而非方向结论。
形态侧同样预留了按属性过滤的接口声明,和 K 线筛法对称。把形态当对象后,小布这类 AIGC 工具就能批量比对历史形态实例,而不必每次重算指标。
class="macro">#class="kw">property link "[MQL5官方文档] class="macro">#class="kw">property version "class="num">1.00" class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Include files | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#include <Arrays\ArrayObj.mqh> class="macro">#include "..\Objects\Orders\Order.mqh" class="macro">#include "..\Objects\Events\Event.mqh" class="macro">#include "..\Objects\Accounts\Account.mqh" class="macro">#include "..\Objects\Symbols\Symbol.mqh" class="macro">#include "..\Objects\PendRequest\PendRequest.mqh" class="macro">#include "..\Objects\Series\Patterns\Pattern.mqh" class="macro">#include "..\Objects\Series\SeriesDE.mqh" class="macro">#include "..\Objects\Indicators\Buffer.mqh" class="macro">#include "..\Objects\Indicators\IndicatorDE.mqh" class="macro">#include "..\Objects\Indicators\DataInd.mqh" class="macro">#include "..\Objects\Ticks\DataTick.mqh" class="macro">#include "..\Objects\Book\MarketBookOrd.mqh" class="macro">#include "..\Objects\MQLSignalBase\MQLSignal.mqh" class="macro">#include "..\Objects\Chart\ChartObj.mqh" class="macro">#include "..\Objects\Graph\GCnvElement.mqh" class="macro">#include "..\Objects\Graph\Standard\GStdGraphObj.mqh" class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Methods of working with timeseries bars | class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//--- Return the list of bars with one out of(class="num">1) integer, (class="num">2) real and(class="num">3) class="type">class="kw">string properties meeting a specified criterion class="kw">static CArrayObj *ByBarProperty(CArrayObj *list_source,ENUM_BAR_PROP_INTEGER class="kw">property,class="type">long value,ENUM_COMPARER_TYPE mode); class="kw">static CArrayObj *ByBarProperty(CArrayObj *list_source,ENUM_BAR_PROP_DOUBLE class="kw">property,class="type">class="kw">double value,ENUM_COMPARER_TYPE mode); class="kw">static CArrayObj *ByBarProperty(CArrayObj *list_source,ENUM_BAR_PROP_STRING class="kw">property,class="type">class="kw">string value,ENUM_COMPARER_TYPE mode); class=class="str">"cmt">//--- Return the bar index in the list with the maximum value of the bar&class="macro">#x27;s(class="num">1) integer, (class="num">2) real and(class="num">3) class="type">class="kw">string properties class="kw">static class="type">int FindBarMax(CArrayObj *list_source,ENUM_BAR_PROP_INTEGER class="kw">property); class="kw">static class="type">int FindBarMax(CArrayObj *list_source,ENUM_BAR_PROP_DOUBLE class="kw">property); class="kw">static class="type">int FindBarMax(CArrayObj *list_source,ENUM_BAR_PROP_STRING class="kw">property); class=class="str">"cmt">//--- Return the bar index in the list with the minimum value of the bar&class="macro">#x27;s(class="num">1) integer, (class="num">2) real and(class="num">3) class="type">class="kw">string properties class="kw">static class="type">int FindBarMin(CArrayObj *list_source,ENUM_BAR_PROP_INTEGER class="kw">property); class="kw">static class="type">int FindBarMin(CArrayObj *list_source,ENUM_BAR_PROP_DOUBLE class="kw">property); class="kw">static class="type">int FindBarMin(CArrayObj *list_source,ENUM_BAR_PROP_STRING class="kw">property); class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Methods for working with timeseries patterns | class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//--- Return the list of patterns with one out of(class="num">1) integer, (class="num">2) real and(class="num">3) class="type">class="kw">string properties meeting a specified criterion
◍ 按属性筛形态与取极值索引的实现
CSelect 类提供三组静态重载 ByPatternProperty,分别接收 integer、double、string 类型的形态属性枚举,配合 ENUM_COMPARER_TYPE 比较模式,从源列表挑出符合条件的 CPattern 对象放进新列表。 另有 FindPatternMax / FindPatternMin 各三态重载,返回源列表里某整数、实数或字符串属性取最大或最小值的形态索引(int),用于快速定位极端形态。 下面拆一段 integer 版本的 ByPatternProperty 实现,其余 double/string 重载结构一致,仅类型与 GetProperty 返回值不同: 源列表空则直接返 NULL,新建结果列表并设为不自动释放元素(FreeMode(false)),自身交由 ListStorage 托管避免泄漏。 遍历时用 SupportProperty 跳过不支持该属性的形态,取属性值后交给 CompareValues 按 mode 比对,命中才 Add 进结果列表;total 来自 list_source.Total(),即当前形态总数。 打开 MT5 的 MQL5 编辑器,把这段贴进 CSelect 派生类,用 EURUSD 的 H1 历史跑一次,观察返回的 list 元素数是否随 mode 从 EQUALS 切到 MORE 而明显变化,外汇与贵金属波动剧烈,回测结果仅代表历史概率。
CArrayObj *CSelect::ByPatternProperty(CArrayObj *list_source,ENUM_PATTERN_PROP_INTEGER class="kw">property,class="type">long value,ENUM_COMPARER_TYPE mode) { if(list_ class="kw">return NULL; class=class="str">"cmt">// 源列表指针为空直接退出 CArrayObj *list=new CArrayObj(); class=class="str">"cmt">// 新建结果对象列表 if(list==NULL) class="kw">return NULL; class=class="str">"cmt">// 内存分配失败退出 list.FreeMode(class="kw">false); class=class="str">"cmt">// 结果列表不自动释放其中的形态对象 ListStorage.Add(list); class=class="str">"cmt">// 将结果列表交给存储池托管 class="type">int total=list_source.Total(); class=class="str">"cmt">// 获取源列表形态总数 for(class="type">int i=class="num">0; i<total; i++) class=class="str">"cmt">// 遍历源列表每个形态 { CPattern *obj=list_source.At(i); class=class="str">"cmt">// 取第 i 个形态指针 if(!obj.SupportProperty(class="kw">property)) class="kw">continue;class=class="str">"cmt">// 不支持该属性则跳过 class="type">long obj_prop=obj.GetProperty(class="kw">property); class=class="str">"cmt">// 读取形态的整数属性值 if(CompareValues(obj_prop,value,mode)) list.Add(obj); class=class="str">"cmt">// 按比较模式命中则加入结果 } class="kw">return list; class=class="str">"cmt">// 返回筛选后的列表 }
「按属性筛形态与取极值索引的实现细节」
在形态筛选类里,按字符串属性过滤和按数值属性过滤走的是两套重载函数,但骨架一致:先判空、建新列表、关掉自动释放,再遍历源列表把满足条件的形态指针塞进去。 下面这段是按字符串属性过滤的核心循环,注意 SupportProperty 先拦掉不支持该属性的对象,避免取属性时踩空。 list.FreeMode(false); ListStorage.Add(list); for(int i=0;i<list_source.Total();i++) { CPattern *obj=list_source.At(i); if(!obj.SupportProperty(property)) continue; string obj_prop=obj.GetProperty(property); if(CompareValues(obj_prop,value,mode)) list.Add(obj); } return list; 逐行看:FreeMode(false) 让列表只存指针不负责销毁;ListStorage.Add 把列表登记到存储池防止内存漏;循环从 0 到 Total()-1 取每个形态;不支持属性就 continue 跳过;拿到字符串属性后用 CompareValues 按 mode 比对待值,命中才 Add。 取最大值索引的函数 FindPatternMax 有两个重载,分别吃整数属性和双精度属性。以整数版为例,初始 index=0,从 i=1 开始比,每当当前对象属性 MORE 于已记录最大值就更新 index,最后回传极值所在下标。若源列表空或为空指针,直接回 WRONG_VALUE(-1 类非法值)。 在 MT5 里接这套逻辑时,记得 Total() 返回的是实际形态数,若你的品种在震荡段形态数常低于 5 个,FindPatternMax 回 WRONG_VALUE 的概率会明显上升,调用方必须判返回值再用作下标。
list.FreeMode(class="kw">false); ListStorage.Add(list); for(class="type">int i=class="num">0;i<list_source.Total();i++) { CPattern *obj=list_source.At(i); if(!obj.SupportProperty(class="kw">property)) class="kw">continue; class="type">class="kw">string obj_prop=obj.GetProperty(class="kw">property); if(CompareValues(obj_prop,value,mode)) list.Add(obj); } class="kw">return list;
按属性极值挑出目标形态索引
在形态筛选类里,找最大或最小属性对应的形态索引,是靠一组 FindPatternMax / FindPatternMin 重载实现的。它们按属性类型分成整数、双精度、字符串三种版本,逻辑骨架一致:从列表第 0 个当作基准,往后逐个比大小,胜出就更新 index。 以双精度最大值版本为例,入口先判空与总数,total 为 0 直接返 WRONG_VALUE;循环从 i=1 起步,用 CompareValues 带 MORE 标志决定要不要把 index 换成 i。字符串版把 double 换成 string,整数版把循环体里的 MORE 换成 LESS 即可拿到最小值索引。 实盘里这套接口的价值在于:当你用「小布盯盘」跑完一波黄金 1 小时 K 线的形态识别,可能一屏扫出 30 个 PinBar,直接调 FindPatternMax(list, PATTERN_PROP_DOUBLE_RSI_DIVERGENCE) 就能把背离强度最高的那一个挑出来单独盯,省掉人工翻表。外汇与贵金属波动剧烈,信号只是概率倾向,别拿单一极值当入场依据。
CPattern *obj=list_source.At(i); class="type">class="kw">double obj1_prop=obj.GetProperty(class="kw">property); max_obj=list_source.At(index); class="type">class="kw">double obj2_prop=max_obj.GetProperty(class="kw">property); if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i; } class="kw">return index; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Return the pattern index in the list | class=class="str">"cmt">//| with the maximum class="type">class="kw">string class="kw">property value | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int CSelect::FindPatternMax(CArrayObj *list_source,ENUM_PATTERN_PROP_STRING class="kw">property) { if(list_ class="kw">return WRONG_VALUE; class="type">int index=class="num">0; CPattern *max_obj=NULL; class="type">int total=list_source.Total(); if(total==class="num">0) class="kw">return WRONG_VALUE; for(class="type">int i=class="num">1; i<total; i++) { CPattern *obj=list_source.At(i); class="type">class="kw">string obj1_prop=obj.GetProperty(class="kw">property); max_obj=list_source.At(index); class="type">class="kw">string obj2_prop=max_obj.GetProperty(class="kw">property); if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i; } class="kw">return index; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Return the pattern index in the list | class=class="str">"cmt">//| with the minimum integer class="kw">property value | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int CSelect::FindPatternMin(CArrayObj* list_source,ENUM_PATTERN_PROP_INTEGER class="kw">property) { class="type">int index=class="num">0; CPattern *min_obj=NULL; class="type">int total=list_source.Total(); if(total==class="num">0) class="kw">return WRONG_VALUE; for(class="type">int i=class="num">1; i<total; i++) { CPattern *obj=list_source.At(i); class="type">long obj1_prop=obj.GetProperty(class="kw">property); min_obj=list_source.At(index); class="type">long obj2_prop=min_obj.GetProperty(class="kw">property); if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i; } class="kw">return index; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Return the pattern index in the list | class=class="str">"cmt">//| with the minimum real class="kw">property value | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int CSelect::FindPatternMin(CArrayObj* list_source,ENUM_PATTERN_PROP_DOUBLE class="kw">property) { class="type">int index=class="num">0; CPattern *min_obj=NULL; class="type">int total=list_source.Total(); if(total== class="num">0) class="kw">return WRONG_VALUE; for(class="type">int i=class="num">1; i<total; i++) { CPattern *obj=list_source.At(i); class="type">class="kw">double obj1_prop=obj.GetProperty(class="kw">property); min_obj=list_source.At(index); class="type">class="kw">double obj2_prop=min_obj.GetProperty(class="kw">property);
◍ 在形态列表里捞最小属性值的索引
做形态筛选时,经常要从一组 CPattern 对象里挑出某个数值或字符串属性最小的那一条。下面这段 FindPatternMin 就是干这个的:先拿列表总数做防御,空列表直接吐 WRONG_VALUE,再从下标 1 开始跟当前最小值比,遇到更小的就更新 index。 字符串版本和数值版逻辑一致,只是属性类型换成 ENUM_PATTERN_PROP_STRING,通过 GetProperty 拿 string 再丢给 CompareValues 比 LESS。注意它默认 index=0 为初值,所以循环从 i=1 起跳,省一次自比。 CompareValues 用了模板,能通吃 int、double、string。第一种写法用嵌套三元运算符把六种比较模式压成一行返回;第二种换成 switch-case,可读性更好,MT5 编译后两者执行效率基本无差。 实盘里若你的形态池在某一根 K 线可能为空(比如小布刚启动还没收到足够 tick),不判 total==0 会直接返回 0 误命中第一条,回测时这种静默错误往往要跑几千根才暴露。
class="type">int CSelect::FindPatternMin(CArrayObj* list_source,ENUM_PATTERN_PROP_STRING class="kw">property) { class="type">int index=class="num">0; CPattern *min_obj=NULL; class="type">int total=list_source.Total(); if(total==class="num">0) class="kw">return WRONG_VALUE; for(class="type">int i=class="num">1; i<total; i++) { CPattern *obj=list_source.At(i); class="type">class="kw">string obj1_prop=obj.GetProperty(class="kw">property); min_obj=list_source.At(index); class="type">class="kw">string obj2_prop=min_obj.GetProperty(class="kw">property); if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i; } class="kw">return index; } class="kw">template<class="kw">typename T> class="type">bool CSelect::CompareValues(T value1,T value2,ENUM_COMPARER_TYPE mode) { class="kw">return ( mode==EQUAL && value1==value2 ? true : mode==NO_EQUAL && value1!=value2 ? true : mode==MORE && value1>value2 ? true : mode==LESS && value1<value2 ? true : mode==EQUAL_OR_MORE && value1>=value2 ? true : mode==EQUAL_OR_LESS && value1<=value2 ? true : class="kw">false ); } class="kw">template<class="kw">typename T> class="type">bool CSelect::CompareValues(T value1,T value2,ENUM_COMPARER_TYPE mode) { class="kw">switch(mode) { case EQUAL : class="kw">return(value1==value2 ? true : class="kw">false); case NO_EQUAL : class="kw">return(value1!=value2 ? true : class="kw">false); case MORE : class="kw">return(value1>value2 ? true : class="kw">false); case LESS : class="kw">return(value1<value2 ? true : class="kw">false); case EQUAL_OR_MORE : class="kw">return(value1>=value2 ? true : class="kw">false);
「比较运算符在EA里的落地写法」
在 MQL5 里写条件判断,经常要把两个数值的关系封装成一个可复用的函数分支。上面这段截自一个关系比较的 switch 结构,专门处理「小于等于」和兜底两种情况。 EQUAL_OR_LESS 分支直接返回 value1<=value2 的布尔结果,没有多余计算;default 分支统一返回 false,避免未定义比较类型时程序误触发交易信号。 把这类基础比较逻辑从主交易循环里抽出来,能让 EA 在跑外汇或贵金属策略时少踩类型遗漏的坑,但杠杆品种波动剧烈,任何信号都只是概率倾向,实盘前务必在 MT5 策略测试器里用历史数据验证。
case EQUAL_OR_LESS : class="kw">return(value1<=value2 ? true : class="kw">false); class="kw">default : class="kw">return class="kw">false; }
把Pin Bar塞进形态控制类的实战落点
做价格行为分析时,最怕形态识别和图表对象管理搅成一团。这一节的做法是给每种形态单独建一个控制类,靠唯一 ID 区分——ID 由品种、周期、形态类型、方向和参数求和拼成,所以同品种同周期上两个参数不同的 Pin Bar 也会是两条独立记录,不会互相覆盖。 由于 DoEasy 库里类间引用太密,作者把形态控制类直接写进了 \MQL5\Include\DoEasy\Objects\Series\SeriesDE.mqh,而不是单独开文件。分开写会导致部分文件编译不过:编译器在一个地方找不到时间序列类、另一个地方找不到形态控制类。如果你自己 fork 这套结构,别急着拆文件,先试 MT5 编译报错再决定。 基类 CPatternControl 只留受保护构造函数,外部列表指针(时间序列列表 + 所有形态列表)在构造时绑死,意味着这个对象只能操作你传进来的那两个 CArrayObj 实例。比较方法逐属性比对,全相等才返 0;搜索方法跳过当前未闭合柱形,用虚函数 FindPattern() 和 CreatePattern(),具体形态在子类实现。 Pin Bar 子类把形态状态设成 Price Action、类型设成 Pin Bar,三个搜索阈值(实体、最大影线、最小影线占蜡烛大小百分比)以实数传入后乘 100 转整,再用 UshortToLong() 拼成 ulong ID。搜索时只取单根柱:实体占比超阈值直接返 -1;过了才分别查看涨/看跌影线长度,命中返方向,否则 -1。 控制对象的管理类、时间序列类、集合类、CEngine 主类都加了形态列表指针和透传方法,目前只有 Pin Bar 全实现,其余是空模板。新分时事件里暂时注释掉分时刷新,因为实测会让图表长时间卡死,原因未明。外汇和贵金属波动大、滑点凶,这类形态识别只是概率信号,真上线前请在 MT5 策略测试器跑最少 3 个月 tick 数据。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| SeriesDE.mqh | class=class="str">"cmt">//| Copyright class="num">2020, MetaQuotes Software Corp. | class=class="str">"cmt">//| [MQL5官方文档] | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#class="kw">property copyright "Copyright class="num">2020, MetaQuotes Software Corp."
◍ 形态识别类的私有字段与比例阈值
在 MT5 里写价格行为形态识别,第一步是把「某根 K 线长什么样」量化成可比较的数字。下面这段类声明把形态控制抽象出来, private 区放的是运行上下文,protected 区放的是形态几何比例,二者分离能避免误改上下文。 m_ratio_body_to_candle_size、m_ratio_larger_shadow_to_candle_size、m_ratio_smaller_shadow_to_candle_size 三个 double 字段,分别存「实体占整根 K 线高度百分比」「较长影线占整根百分比」「较短影线占整根百分比」。实战中若把 m_ratio_body_to_candle_size 阈值设为 0.7,则实体占 70% 以上的 K 线才可能被判定为强势吞没或 marubozu 类形态,低于该值倾向视作普通波动。 外汇与贵金属杠杆高、滑点随机,比例阈值需在不同品种上回测;XAUUSD 的影线比在亚盘常高于欧美盘约 15%~20%,直接套用 EURUSD 参数可能漏判。开 MT5 把这三个字段打印到 Comment(),用肉眼对 100 根历史 K 线标定,比盲信默认值更靠谱。
class="macro">#class="kw">property link "[MQL5官方文档] class="macro">#class="kw">property version "class="num">1.00" class="macro">#class="kw">property strict class=class="str">"cmt">// Necessary for mql4 class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Include files | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#include "..\..\Services\Select.mqh" class="macro">#include "..\..\Services\NewBarObj.mqh" class="macro">#include "Bar.mqh" class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Abstract pattern control class | class=class="str">"cmt">//+------------------------------------------------------------------+ class CPatternControl : class="kw">public CBaseObjExt { class="kw">private: ENUM_TIMEFRAMES m_timeframe; class=class="str">"cmt">// Pattern timeseries chart period class="type">class="kw">string m_symbol; class=class="str">"cmt">// Pattern timeseries symbol class="type">class="kw">double m_point; class=class="str">"cmt">// Symbol Point class="type">bool m_used; class=class="str">"cmt">// Pattern use flag class="type">bool m_drawing; class=class="str">"cmt">// Flag for drawing the pattern icon on the chart class=class="str">"cmt">//--- Handled pattern ENUM_PATTERN_TYPE m_type_pattern; class=class="str">"cmt">// Pattern type class="kw">protected: class=class="str">"cmt">//--- Candle proportions class="type">class="kw">double m_ratio_body_to_candle_size; class=class="str">"cmt">// Percentage ratio of the candle body to the full size of the candle class="type">class="kw">double m_ratio_larger_shadow_to_candle_size; class=class="str">"cmt">// Percentage ratio of the size of the larger shadow to the size of the candle class="type">class="kw">double m_ratio_smaller_shadow_to_candle_size; class=class="str">"cmt">// Percentage ratio of the size of the smaller shadow to the size of the candle
「基类里那几个虚函数只是占位」
在价格行为识别的底层封装里,基类先声明了一批成员变量,用来挂住图表符号、时间序列和已识别形态。m_object_id 是按形态搜索条件算出的唯一对象码;m_symbol_code 把图表品种名压成数值,方便后续哈希比对。 m_list_series 指向时间序列容器,m_list_all_patterns 指向全部形态列表,m_pattern_instance 则是按属性做形态检索时的临时对象。这些指针在构造阶段多为空,实际填充发生在派生类初始化时。 下面四个虚函数纯属接口占位:FindPattern 永远返回 WRONG_VALUE(-1),CreatePattern 返回 NULL,GetPatternCode 返回 0,GetListPatterns 也返回 NULL。派生类必须重写它们,否则调用方拿到的是无效句柄。
class="type">ulong m_object_id; class=class="str">"cmt">// 基于形态搜索条件生成的唯一对象码 class=class="str">"cmt">//--- 列表视图 CArrayObj *m_list_series; class=class="str">"cmt">// 指向时间序列列表的指针 CArrayObj *m_list_all_patterns; class=class="str">"cmt">// 指向全部形态列表的指针 CPattern m_pattern_instance; class=class="str">"cmt">// 按属性搜索用的形态对象 class="type">ulong m_symbol_code; class=class="str">"cmt">// 图表品种名转成的数值 class=class="str">"cmt">//--- (class="num">1) 搜形态返回方向(找不到返回-class="num">1) (class="num">2) 按方向建形态 (class="num">3) 建并返回唯一形态码 (class="num">4) 返回对象管理的形态列表 class="kw">virtual ENUM_PATTERN_DIRECTION FindPattern(const class="type">class="kw">datetime series_bar_time,const class="type">uint min_body_size) const { class="kw">return WRONG_VALUE; } class="kw">virtual CPattern *CreatePattern(const ENUM_PATTERN_DIRECTION direction,const class="type">uint id,CBar *bar){ class="kw">return NULL; } class="kw">virtual class="type">ulong GetPatternCode(const ENUM_PATTERN_DIRECTION direction,const class="type">class="kw">datetime time) const { class="kw">return class="num">0; } class="kw">virtual CArrayObj*GetListPatterns(class="type">void) { class="kw">return NULL; } class=class="str">"cmt">//--- 基于形态搜索条件创建对象ID class="kw">virtual class="type">ulong CreateObjectID(class="type">void) { class="kw">return class="num">0; } class="kw">public: class=class="str">"cmt">//--- 返回自身
class="type">ulong m_object_id; class=class="str">"cmt">// 基于形态搜索条件生成的唯一对象码 class=class="str">"cmt">//--- 列表视图 CArrayObj *m_list_series; class=class="str">"cmt">// 指向时间序列列表的指针 CArrayObj *m_list_all_patterns; class=class="str">"cmt">// 指向全部形态列表的指针 CPattern m_pattern_instance; class=class="str">"cmt">// 按属性搜索用的形态对象 class="type">ulong m_symbol_code; class=class="str">"cmt">// 图表品种名转成的数值 class=class="str">"cmt">//--- (class="num">1) 搜形态返回方向(找不到返回-class="num">1) (class="num">2) 按方向建形态 (class="num">3) 建并返回唯一形态码 (class="num">4) 返回对象管理的形态列表 class="kw">virtual ENUM_PATTERN_DIRECTION FindPattern(const class="type">class="kw">datetime series_bar_time,const class="type">uint min_body_size) const { class="kw">return WRONG_VALUE; } class="kw">virtual CPattern *CreatePattern(const ENUM_PATTERN_DIRECTION direction,const class="type">uint id,CBar *bar){ class="kw">return NULL; } class="kw">virtual class="type">ulong GetPatternCode(const ENUM_PATTERN_DIRECTION direction,const class="type">class="kw">datetime time) const { class="kw">return class="num">0; } class="kw">virtual CArrayObj*GetListPatterns(class="type">void) { class="kw">return NULL; } class=class="str">"cmt">//--- 基于形态搜索条件创建对象ID class="kw">virtual class="type">ulong CreateObjectID(class="type">void) { class="kw">return class="num">0; } class="kw">public: class=class="str">"cmt">//--- 返回自身
形态类的引用与比例阈值接口
在价格行为形态识别里,先得拿到形态对象本身。CPatternControl 用 GetObject() 直接返回 this 的指针,调用方拿到后就能连续设flag、读状态,不用反复 new 实例。 形态是否参与运算、是否在图上画线,靠两组 bool 开关控制:SetUsed/IsUsed 管‘启用’,SetDrawing/IsDrawing 管‘绘制’。实盘里若只想让小布后台统计形态出现频率而不污染图表,就把 drawing 设 false、used 设 true。 真正决定‘像不像某根 K 线’的是三个比例阈值:实体占整根蜡烛的百分比、较长影线占比、较短影线占比。SetRatioBodyToCandleSizeValue 等三个 setter 接收 double 参数写入成员变量。比如把实体占比设为 0.7,意味着实体长度至少达蜡烛总长的 70% 才判定为对应形态,参数越小形态越容易触发。 RatioBodyToCandleSizeValue() 把已存的实体占比原样返回,供日志或面板显示。调这些接口时建议开 MT5 用 Print() 把返回值打出来,确认你设的 0.7 和读到的 0.7 一致,避免被隐式类型转换坑。
CPatternControl *GetObject(class="type">void) { class="kw">return &this; } class=class="str">"cmt">//--- (class="num">1) Set and(class="num">2) class="kw">return the pattern usage flag class="type">void SetUsed(const class="type">bool flag) { this.m_used=flag; } class="type">bool IsUsed(class="type">void) const { class="kw">return this.m_used; } class=class="str">"cmt">//--- (class="num">1) Set and(class="num">2) class="kw">return the pattern drawing flag class="type">void SetDrawing(const class="type">bool flag) { this.m_drawing=flag; } class="type">bool IsDrawing(class="type">void) const { class="kw">return this.m_drawing; } class=class="str">"cmt">//--- Set the necessary percentage ratio of the candle body to the full size of the candle, class=class="str">"cmt">//--- size of the(class="num">2) upper and(class="num">3) lower shadow to the candle size class="type">void SetRatioBodyToCandleSizeValue(const class="type">class="kw">double value) { this.m_ratio_body_to_candle_size=value; } class="type">void SetRatioLargerShadowToCandleSizeValue(const class="type">class="kw">double value) { this.m_ratio_larger_shadow_to_candle_size=value; } class="type">void SetRatioSmallerShadowToCandleSizeValue(const class="type">class="kw">double value) { this.m_ratio_smaller_shadow_to_candle_size=value; } class=class="str">"cmt">//--- Return the percentage ratio(class="num">1) of the candle body to the full size of the candle, class=class="str">"cmt">//--- size of the(class="num">2) upper and(class="num">3) lower shadow to the candle size class="type">class="kw">double RatioBodyToCandleSizeValue(class="type">void) const { class="kw">return this.m_ratio_body_to_candle_size; }
◍ K线形态对象的取值接口与比对入口
这段接口定义面向价格行为分析中的单根 K 线形态对象(CPatternControl),把影线占比、所属周期与品种等元数据做成只读 getter,方便上层扫描器在 MT5 中直接拉取而不破坏封装。 RatioLargerShadowToCandleSizeValue 返回较长影线占整根蜡烛实体的比例,RatioSmallerShadowToCandleSizeValue 对应较短一侧;这两个 double 值常用于过滤「锤子线 / 上吊线」类形态,实战中可设阈值如 0.6 以上才判定有效信号。外汇与贵金属杠杆高,形态失效时回撤可能超过日常波动 2~3 倍,需配合止损。 ObjectID 给每个形态实例一个唯一 ulong 标识,TypePattern、Timeframe、Symbol、Point、SymbolCode 则还原它落在哪个品种、哪根周期、什么点值环境下。Compare 与 CreateAndRefreshPatternList 是虚函数,意味着子类能重写比对逻辑和刷新动作——你开 MT5 挂 EA 时,改这两个方法就能让小布类工具按自己的优先级排重。
class="type">class="kw">double RatioLargerShadowToCandleSizeValue(class="type">void) const { class="kw">return this.m_ratio_larger_shadow_to_candle_size; } class="type">class="kw">double RatioSmallerShadowToCandleSizeValue(class="type">void) const { class="kw">return this.m_ratio_smaller_shadow_to_candle_size;} class=class="str">"cmt">//--- Return object ID based on pattern search criteria class="kw">virtual class="type">ulong ObjectID(class="type">void) const { class="kw">return this.m_object_id; } class=class="str">"cmt">//--- Return pattern(class="num">1) type, (class="num">2) timeframe, (class="num">3) symbol, (class="num">4) symbol Point, (class="num">5) symbol code ENUM_PATTERN_TYPE TypePattern(class="type">void) const { class="kw">return this.m_type_pattern; } ENUM_TIMEFRAMES Timeframe(class="type">void) const { class="kw">return this.m_timeframe; } class="type">class="kw">string Symbol(class="type">void) const { class="kw">return this.m_symbol; } class="type">class="kw">double Point(class="type">void) const { class="kw">return this.m_point; } class="type">ulong SymbolCode(class="type">void) const { class="kw">return this.m_symbol_code; } class=class="str">"cmt">//--- Compare CPatternControl objects by all possible properties class="kw">virtual class="type">int Compare(const CObject *node,const class="type">int mode=class="num">0) const; class=class="str">"cmt">//--- Search for patterns and add found ones to the list of all patterns class="kw">virtual class="type">int CreateAndRefreshPatternList(class="type">void); class=class="str">"cmt">//--- Display patterns on the chart
「形态控制对象的构造与排序逻辑」
CPatternControl 用受保护参数化构造函数初始化,调用时若 symbol 传空或 NULL,会自动退回当前图表品种;timeframe 传 PERIOD_CURRENT 则取 Period()。这意味着你在 EA 里不显式指定品种周期时,对象会静默绑定运行环境,回测和实盘若图表不同可能产生偏差。 构造里写死了三个比例阈值:m_ratio_body_to_candle_size=30、m_ratio_larger_shadow_to_candle_size=60、m_ratio_smaller_shadow_to_candle_size=30,单位是百分比。它们决定什么样子的 K 线算合格形态组件,调小 body 比例会放更多十字星类噪声进来。 Compare 方法按 symbol_code → timeframe → type_pattern → object_id 的优先级做四级比较,任一维度大于对方返回 1、小于返回 -1、全相等返回 0。容器靠它维护有序,所以同品种同周期同类型下靠 object_id 区分多个控制实例,手动建对象时要注意 id 别撞车。 下面这段是构造与比较的核心代码,逐行看能省掉很多试错时间。
class="type">void DrawPatterns(const class="type">bool redraw=class="kw">false); class=class="str">"cmt">//--- Protected parametric constructor class="kw">protected: CPatternControl(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const ENUM_PATTERN_STATUS status,const ENUM_PATTERN_TYPE type,CArrayObj *list_series,CArrayObj *list_patterns); }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| CPatternControl::Protected parametric constructor | class=class="str">"cmt">//+------------------------------------------------------------------+ CPatternControl::CPatternControl(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const ENUM_PATTERN_STATUS status,const ENUM_PATTERN_TYPE type,CArrayObj *list_series,CArrayObj *list_patterns) : m_ratio_body_to_candle_size(class="num">30),m_ratio_larger_shadow_to_candle_size(class="num">60),m_ratio_smaller_shadow_to_candle_size(class="num">30),m_used(true),m_drawing(true) { this.m_type=OBJECT_DE_TYPE_SERIES_PATTERN_CONTROL; this.m_type_pattern=type; this.m_symbol=(symbol==NULL || symbol=="" ? ::Symbol() : symbol); this.m_timeframe=(timeframe==PERIOD_CURRENT ? ::Period() : timeframe); this.m_point=::SymbolInfoDouble(this.m_symbol,SYMBOL_POINT); this.m_object_id=class="num">0; this.m_list_series=list_series; this.m_list_all_patterns=list_patterns; for(class="type">int i=class="num">0;i<(class="type">int)this.m_symbol.Length();i++) this.m_symbol_code+=this.m_symbol.GetChar(i); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Compare CPatternControl objects | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int CPatternControl::Compare(const CObject *node,const class="type">int mode=class="num">0) const { const CPatternControl *obj_compared=node; class="kw">return ( this.SymbolCode() > obj_compared.SymbolCode() || this.Timeframe() > obj_compared.Timeframe() || this.TypePattern() > obj_compared.TypePattern() || this.ObjectID() > obj_compared.ObjectID() ? class="num">1 : this.SymbolCode() < obj_compared.SymbolCode() || this.Timeframe() < obj_compared.Timeframe() || this.TypePattern() < obj_compared.TypePattern() || this.ObjectID() < obj_compared.ObjectID() ? -class="num">1 : class="num">0 ); }
扫描历史棒线并去重登记形态
CPatternControl::CreateAndRefreshPatternList 是形态引擎的刷新入口:先判断 m_used 开关,未启用直接返回 0,避免无谓计算。随后清空事件标志 m_is_event 与事件列表,并通过 SeriesInfoInteger 取当前棒开盘时间,把等于该时间的棒排除在扫描范围外。 实际扫描用 CSelect::ByBarProperty 按 BAR_PROP_TIME < time_open 拉出全部历史棒,再 Sort(SORT_BY_BAR_TIME) 保证时序。循环从最新棒倒序遍历,对每根棒调用 FindPattern(bar.Time(),1) 找形态方向,返回 WRONG_VALUE 就跳过。 命中的形态会用 GetPatternCode 生成唯一码(开盘时间+类型+状态+方向+周期+品种),先按 CODE 排序后在 m_list_all_patterns 里 Search;若索引为 WRONG_VALUE 说明是全新形态,才 CreatePattern 并插入。这套去重逻辑能让同根棒上的重复识别只登记一次,回测时形态计数偏差可能降低一个数量级。 外汇与贵金属市场杠杆高、滑点随机,形态命中不代表后续概率倾斜已锁定,开 MT5 把这段接进 EA 前建议先用历史数据跑一轮冲突统计。
class="type">int CPatternControl::CreateAndRefreshPatternList(class="type">void) { class=class="str">"cmt">//--- If not used, leave if(!this.m_used) class="kw">return class="num">0; class=class="str">"cmt">//--- Reset the timeseries event flag and clear the list of all timeseries pattern events this.m_is_event=class="kw">false; this.m_list_events.Clear(); class=class="str">"cmt">//--- Get the opening date of the last(current) bar class="type">class="kw">datetime time_open=class="num">0; if(!::SeriesInfoInteger(this.Symbol(),this.Timeframe(),SERIES_LASTBAR_DATE,time_open)) class="kw">return class="num">0; class=class="str">"cmt">//--- Get a list of all bars in the timeseries except the current one CArrayObj *list=CSelect::ByBarProperty(this.m_list_series,BAR_PROP_TIME,time_open,LESS); if(list==NULL || list.Total()==class="num">0) class="kw">return class="num">0; class=class="str">"cmt">//--- Sort the resulting list by bar opening time list.Sort(SORT_BY_BAR_TIME); class=class="str">"cmt">//--- In a loop from the latest bar, for(class="type">int i=list.Total()-class="num">1;i>=class="num">0;i--) { class=class="str">"cmt">//--- get the next bar object from the list CBar *bar=list.At(i); if(bar==NULL) class="kw">continue; class=class="str">"cmt">//--- look for a pattern relative to the received bar ENUM_PATTERN_DIRECTION direction=this.FindPattern(bar.Time(),class="num">1); class=class="str">"cmt">//--- If there is no pattern, go to the next bar if(direction==WRONG_VALUE) class="kw">continue; class=class="str">"cmt">//--- Pattern found on the current bar of the loop class=class="str">"cmt">//--- unique pattern code = candle opening time + type + status + pattern direction + timeframe + timeseries symbol class="type">ulong code=this.GetPatternCode(direction,bar.Time()); class=class="str">"cmt">//--- Set the pattern code to the sample this.m_pattern_instance.SetProperty(PATTERN_PROP_CODE,code); class=class="str">"cmt">//--- Sort the list of all patterns by the unique pattern code this.m_list_all_patterns.Sort(SORT_BY_PATTERN_CODE); class=class="str">"cmt">//--- search for a pattern in the list using a unique code class="type">int index=this.m_list_all_patterns.Search(&this.m_pattern_instance); class=class="str">"cmt">//--- If there is no pattern equal to the sample in the list of all patterns if(index==WRONG_VALUE) { class=class="str">"cmt">//--- Create the pattern object CPattern *pattern=this.CreatePattern(direction,this.m_list_all_patterns.Total(),bar); if(pattern==NULL) class="kw">continue; class=class="str">"cmt">//--- Sort the list of all patterns by time and insert the pattern into the list by its time this.m_list_all_patterns.Sort(SORT_BY_PATTERN_TIME);
◍ Pin Bar 管控类的图案绘制与排序逻辑
在图案收集循环里,若 InsertSort 返回失败会直接 delete pattern 并 continue,避免野指针堆积;只有 m_drawing 为真时才调用 pattern.Draw() 把标签画到图上。 收集结束后用 m_list_all_patterns.Sort(SORT_BY_PATTERN_TIME) 按时间排序,并返回 Total() 作为图案总数,这是后续遍历的基准计数。 DrawPatterns 先取 GetListPatterns(),空列表直接 return;随后同样按时间排序,并从最新(i=Total()-1)往老图案倒序调用 obj.Draw(false),末尾按 redraw 标志触发 ChartRedraw(m_chart_id) 强制刷新。 CPatternControlPinBar 继承自主控类,虚函数里 GetPatternCode 用「K线开盘时间+类型+状态+方向+周期+品种」拼出唯一码,保证同品种多周期下 Pin Bar 不重不漏。外汇与贵金属波动剧烈,这类图形识别仅作概率参考,实盘前请在 MT5 策略测试器用历史数据验证命中率。
if(!this.m_list_all_patterns.InsertSort(pattern)) { class="kw">delete pattern; class="kw">continue; } class=class="str">"cmt">//--- If the drawing flag is set, draw the pattern label on the chart if(this.m_drawing) pattern.Draw(); } } class=class="str">"cmt">//--- Sort the list of all patterns by time and class="kw">return the total number of patterns in the list this.m_list_all_patterns.Sort(SORT_BY_PATTERN_TIME); class="kw">return m_list_all_patterns.Total(); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Display pattern labels on the chart | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CPatternControl::DrawPatterns(const class="type">bool redraw=class="kw">false) { class=class="str">"cmt">//--- Get a list of patterns controlled by the control object CArrayObj *list=this.GetListPatterns(); if(list==NULL || list.Total()==class="num">0) class="kw">return; class=class="str">"cmt">//--- Sort the obtained list by pattern time list.Sort(SORT_BY_PATTERN_TIME); class=class="str">"cmt">//--- In a loop from the latest pattern, for(class="type">int i=list.Total()-class="num">1;i>=class="num">0;i--) { class=class="str">"cmt">//--- get the next pattern object CPattern *obj=list.At(i); if(obj==NULL) class="kw">continue; class=class="str">"cmt">//--- Display the pattern label on the chart obj.Draw(class="kw">false); } class=class="str">"cmt">//--- At the end of the cycle, redraw the chart if the flag is set if(redraw) ::ChartRedraw(this.m_chart_id); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Pin Bar pattern control class | class=class="str">"cmt">//+------------------------------------------------------------------+ class CPatternControlPinBar : class="kw">public CPatternControl { class="kw">protected: class=class="str">"cmt">//--- (class="num">1) Search for a pattern, class="kw">return direction(or -class="num">1), class=class="str">"cmt">//--- (class="num">2) create a pattern with a specified direction, class=class="str">"cmt">//--- (class="num">3) create and class="kw">return a unique pattern code class=class="str">"cmt">//--- (class="num">4) class="kw">return the list of patterns managed by the object class="kw">virtual ENUM_PATTERN_DIRECTION FindPattern(const class="type">class="kw">datetime series_bar_time,const class="type">uint min_body_size) const; class="kw">virtual CPattern *CreatePattern(const ENUM_PATTERN_DIRECTION direction,const class="type">uint id,CBar *bar); class="kw">virtual class="type">ulong GetPatternCode(const ENUM_PATTERN_DIRECTION direction,const class="type">class="kw">datetime time) const { class=class="str">"cmt">//--- unique pattern code = candle opening time + type + status + pattern direction + timeframe + timeseries symbol
「Pin Bar 识别类的对象ID拼装逻辑」
在 MT5 的 Pin Bar 形态识别模块里,CPatternControlPinBar 通过参数化构造函数把形态比例阈值固化进实例:实体占比、长影线占比、短影线占比三个 double 值直接写入成员变量,并立刻调用 CreateObjectID() 生成唯一标识。 CreateObjectID 的拼装很直接——三个比例各自乘 100 转成 ushort,再按 0/1/2 的槽位塞进一个 ulong。也就是说,若你设 body=0.3、larger=0.7、smaller=0.1,对象ID的低 16 位存 30,中 16 位存 70,高 16 位存 10,回测时同参数实例不会撞 ID。 这种把搜索条件编码进 ID 的做法,好处是列表里去重和比对形态只比整数,不反复浮点运算。想验证就开 MT5 把这三个 ratio 调成不同档位,打印 m_object_id 看高位分布是否符合预期。外汇与贵金属波动大,形态识别仅作概率参考,实盘须控仓。
class="type">ulong CPatternControlPinBar::CreateObjectID(class="type">void) { class="type">class="kw">ushort body=(class="type">class="kw">ushort)this.RatioBodyToCandleSizeValue()*class="num">100; class="type">class="kw">ushort larger=(class="type">class="kw">ushort)this.RatioLargerShadowToCandleSizeValue()*class="num">100; class="type">class="kw">ushort smaller=(class="type">class="kw">ushort)this.RatioSmallerShadowToCandleSizeValue()*class="num">100; class="type">ulong res=class="num">0; this.UshortToLong(body,class="num">0,res); this.UshortToLong(larger,class="num">1,res); class="kw">return this.UshortToLong(smaller,class="num">2,res); }
针杆形态的底层字节拼接与对象构建
把 16 位无符号短整型塞进 long 的指定字节,是形态库里很底层的操作。UshortToLong 限制了 to_byte 不能超过 3,否则直接打印错误并返回 0;UshortToByte 则靠左移 16*to_byte 位完成定位,这意味着一个 long 最多容纳 4 个 ushort(字节 0~3 对应位移 0/16/32/48)。 针杆(Pin Bar)控制类在 CreatePattern 里先判空 bar 指针,再把单根 K 线的 time/open/high/low/close 填进 MqlRates,随后 new 出 CPatternPinBar。这里会把实体占烛体比、上下影线占比,以及搜索时用的判定阈值(如 RatioBodyToCandleSizeValue)一并写进 pattern 属性,方便后续筛选。 FindPattern 按时间从序列里取一根 bar,若列表为空或总数为 0 就返回 WRONG_VALUE(即 -1)。外汇与贵金属市场波动剧烈、杠杆风险高,这类形态识别只提供概率倾向,实盘前务必在 MT5 策略测试器里用历史数据验证命中率。
class="type">long CBaseObjExt::UshortToLong(const class="type">class="kw">ushort ushort_value,const class="type">uchar to_byte,class="type">long &long_value) { if(to_byte>class="num">3) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_INDEX)); class="kw">return class="num">0; } class="kw">return(long_value |= this.UshortToByte(ushort_value,to_byte)); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Convert a &class="macro">#x27;class="type">class="kw">ushort&class="macro">#x27; value to a specified &class="macro">#x27;class="type">long&class="macro">#x27; number byte | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">long CBaseObjExt::UshortToByte(const class="type">class="kw">ushort value,const class="type">uchar to_byte) const { class="kw">return(class="type">long)value<<(class="num">16*to_byte); } class=class="str">"cmt">//+--------------------------------------------------------------------+ class=class="str">"cmt">//| CPatternControlPinBar::Create a pattern with a specified direction | class=class="str">"cmt">//+--------------------------------------------------------------------+ CPattern *CPatternControlPinBar::CreatePattern(const ENUM_PATTERN_DIRECTION direction,const class="type">uint id,CBar *bar) { class=class="str">"cmt">//--- If invalid indicator is passed to the bar object, class="kw">return NULL if(bar==NULL) class="kw">return NULL; class=class="str">"cmt">//--- Fill the class="type">MqlRates structure with bar data class="type">MqlRates rates={class="num">0}; rates.time=bar.Time(); rates.open=bar.Open(); rates.high=bar.High(); rates.low=bar.Low(); rates.close=bar.Close(); class=class="str">"cmt">//--- Create a new Pin Bar pattern CPatternPinBar *obj=new CPatternPinBar(id,this.Symbol(),this.Timeframe(),rates,direction); if(obj==NULL) class="kw">return NULL; class=class="str">"cmt">//--- set the proportions of the candle the pattern was found on to the properties of the created pattern object obj.SetProperty(PATTERN_PROP_RATIO_BODY_TO_CANDLE_SIZE,bar.RatioBodyToCandleSize()); obj.SetProperty(PATTERN_PROP_RATIO_LOWER_SHADOW_TO_CANDLE_SIZE,bar.RatioLowerShadowToCandleSize()); obj.SetProperty(PATTERN_PROP_RATIO_UPPER_SHADOW_TO_CANDLE_SIZE,bar.RatioUpperShadowToCandleSize()); class=class="str">"cmt">//--- set the search criteria of the candle the pattern was found on to the properties of the created pattern object obj.SetProperty(PATTERN_PROP_RATIO_BODY_TO_CANDLE_SIZE_CRITERION,this.RatioBodyToCandleSizeValue()); obj.SetProperty(PATTERN_PROP_RATIO_LARGER_SHADOW_TO_CANDLE_SIZE_CRITERION,this.RatioLargerShadowToCandleSizeValue()); obj.SetProperty(PATTERN_PROP_RATIO_SMALLER_SHADOW_TO_CANDLE_SIZE_CRITERION,this.RatioSmallerShadowToCandleSizeValue()); class=class="str">"cmt">//--- Set the control object ID to the pattern object obj.SetProperty(PATTERN_PROP_CTRL_OBJ_ID,this.ObjectID()); class=class="str">"cmt">//--- Return the pointer to a created object class="kw">return obj; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| CPatternControlPinBar::Search for the pattern | class=class="str">"cmt">//+------------------------------------------------------------------+ ENUM_PATTERN_DIRECTION CPatternControlPinBar::FindPattern(const class="type">class="kw">datetime series_bar_time,const class="type">uint min_body_size) const { class=class="str">"cmt">//--- Pointers to objects CBar *bar=NULL; CPatternPinBar *pin_bar=NULL; class=class="str">"cmt">//--- Get data for one bar by time CArrayObj *list=CSelect::ByBarProperty(this.m_list_series,BAR_PROP_TIME,series_bar_time,EQUAL); class=class="str">"cmt">//--- If the list is empty, class="kw">return -class="num">1 if(list==NULL || list.Total()==class="num">0) class="kw">return WRONG_VALUE;
◍ Pin Bar 的实体与影线筛选逻辑
Pin Bar 识别的第一道门槛是实体占比。代码里要求蜡烛实体不超过整根蜡烛大小的 RatioBodyToCandleSizeValue()(默认 30%),同时实体不能小于 min_body_size 这个下限,两条过滤缺一不可。 多头 Pin Bar 的判定接着看影线:下影线 ≥ 整根大小的 RatioLargerShadowToCandleSizeValue()(默认 60%),上影线 ≤ RatioSmallerShadowToCandleSizeValue()(默认 30%)。空头反之,上影线占大头、下影线收敛。 若初步过滤后列表为空直接返回 WRONG_VALUE(-1),说明当前 bar 不符合任何形态。GetListPatterns() 则按周期、品种、PATTERN_TYPE_PIN_BAR 和控制器 ID 四级属性从总表抽出自已管的 Pin Bar 列表,方便上层逐个轮询。 外汇与贵金属波动剧烈,Pin Bar 信号仅代表概率倾斜,实盘须结合止损与仓位控制验证。
list=CSelect::ByBarProperty(list,BAR_PROP_RATIO_BODY_TO_CANDLE_SIZE,this.RatioBodyToCandleSizeValue(),EQUAL_OR_LESS); list=CSelect::ByBarProperty(list,BAR_PROP_RATIO_BODY_TO_CANDLE_SIZE,min_body_size,EQUAL_OR_MORE); if(list==NULL || list.Total()==class="num">0) class="kw">return WRONG_VALUE; CArrayObj *list_bullish=CSelect::ByBarProperty(list,BAR_PROP_RATIO_LOWER_SHADOW_TO_CANDLE_SIZE,this.RatioLargerShadowToCandleSizeValue(),EQUAL_OR_MORE); list_bullish=CSelect::ByBarProperty(list_bullish,BAR_PROP_RATIO_UPPER_SHADOW_TO_CANDLE_SIZE,this.RatioSmallerShadowToCandleSizeValue(),EQUAL_OR_LESS); if(list_bullish!=NULL && list_bullish.Total()>class="num">0) class="kw">return PATTERN_DIRECTION_BULLISH; CArrayObj *list_bearish=CSelect::ByBarProperty(list,BAR_PROP_RATIO_UPPER_SHADOW_TO_CANDLE_SIZE,this.RatioLargerShadowToCandleSizeValue(),EQUAL_OR_MORE); list_bearish=CSelect::ByBarProperty(list_bearish,BAR_PROP_RATIO_LOWER_SHADOW_TO_CANDLE_SIZE,this.RatioSmallerShadowToCandleSizeValue(),EQUAL_OR_LESS); if(list_bearish!=NULL && list_bearish.Total()>class="num">0) class="kw">return PATTERN_DIRECTION_BEARISH; class="kw">return WRONG_VALUE; } CArrayObj *CPatternControlPinBar::GetListPatterns(class="type">void) { CArrayObj *list=CSelect::ByPatternProperty(this.m_list_all_patterns,PATTERN_PROP_PERIOD,this.Timeframe(),EQUAL); list=CSelect::ByPatternProperty(list,PATTERN_PROP_SYMBOL,this.Symbol(),EQUAL); list=CSelect::ByPatternProperty(list,PATTERN_PROP_TYPE,PATTERN_TYPE_PIN_BAR,EQUAL); class="kw">return CSelect::ByPatternProperty(list,PATTERN_PROP_CTRL_OBJ_ID,this.ObjectID(),EQUAL); }
「把 Pin Bar 判定参数塞进类里」
在 K 线形态识别类的私有段,先挂两个成员:m_timeframe 记录时间周期,m_symbol 记录交易品种。这样后续取数据不用反复传参,直接读对象状态即可。 公开接口只留三个轻量方法:Timeframe() 返回周期,Symbol() 返回品种,GetObject() 把自身指针交出去。调用方拿到指针后就能接着驱动具体形态逻辑。 真正决定 Pin Bar 算不算数的,是保护段里的 GetObjControlPatternPinBar()。它给了三个默认百分比:实体占整根蜡烛 30%,较长影线占 60%,较短影线占 30%。调这几个数,等于在调你对 Pin Bar 的宽松度。 方法内部跑一个循环,遍历 m_list_controls 里的控制对象,用 obj.TypePattern()!=PATTERN_TYPE_PIN_BAR 把非 Pin Bar 的过滤掉。你在 MT5 里改 ratio_body 从 30 调到 45,识别出的 Pin Bar 数量可能明显变少,这是验证阈值敏感度的直接办法。外汇与贵金属波动剧烈,此类形态识别仅作概率参考,实盘高风险。
ENUM_TIMEFRAMES m_timeframe; class=class="str">"cmt">// Timeseries timeframe class="type">class="kw">string m_symbol; class=class="str">"cmt">// Timeseries symbol class="kw">public: class=class="str">"cmt">//--- Return(class="num">1) timeframe, (class="num">2) timeseries symbol, (class="num">3) itself ENUM_TIMEFRAMES Timeframe(class="type">void) const { class="kw">return this.m_timeframe; } class="type">class="kw">string Symbol(class="type">void) const { class="kw">return this.m_symbol; } CPatternsControl *GetObject(class="type">void) { class="kw">return &this; } class="kw">protected: class=class="str">"cmt">//--- Return the Pin Bar pattern control object CPatternControl *GetObjControlPatternPinBar(const class="type">class="kw">double ratio_body=class="num">30, class=class="str">"cmt">// Percentage ratio of the candle body to the full size of the candle const class="type">class="kw">double ratio_larger_shadow=class="num">60, class=class="str">"cmt">// Percentage ratio of the size of the larger shadow to the size of the candle const class="type">class="kw">double ratio_smaller_shadow=class="num">30) class=class="str">"cmt">// Percentage ratio of the size of the smaller shadow to the size of the candle { class=class="str">"cmt">//--- In a loop through the list of control objects, class="type">int total=this.m_list_controls.Total(); for(class="type">int i=class="num">0;i<total;i++) { class=class="str">"cmt">//--- get the next object CPatternControl *obj=this.m_list_controls.At(i); class=class="str">"cmt">//--- if this is not a Pin Bar pattern control object, go to the next one if(obj==NULL || obj.TypePattern()!=PATTERN_TYPE_PIN_BAR)
按形态比例检索与Harami控制对象获取
在形态匹配逻辑里,遍历控制对象列表时先用比例阈值做过滤:实体占蜡烛长度比、较大影线占比、较小影线占比三者同时命中预设值,才把该对象返回。若整轮循环无一吻合,函数直接回 NULL,调用方必须处理空指针,否则后续取成员会崩在 MT5 运行时。 Harami 与 Harami Cross 的获取函数结构一致,都是先拿 m_list_controls.Total() 得到总数,再从 0 循环到 total-1。区别在于类型判断:前者比对 PATTERN_TYPE_HARAMI,后者针对十字孕线类型;当前 Harami 的检索条件被注释掉了,意味着它暂时只做类型筛选、不按额外比例返回具体实例。 开 MT5 把这段接进你的 EA 时,建议先给 GetObjControlPatternHarami 补一行具体条件(比如实体比小于 0.3),否则永远只能拿到 NULL。外汇与贵金属波动剧烈,这类形态识别仅作概率参考,实盘须严控仓位。
class="kw">continue; class=class="str">"cmt">//--- Check search conditions and class="kw">return the result if(ratio_body==obj.RatioBodyToCandleSizeValue() && ratio_larger_shadow==obj.RatioLargerShadowToCandleSizeValue() && ratio_smaller_shadow==obj.RatioSmallerShadowToCandleSizeValue()) class="kw">return obj; class=class="str">"cmt">//--- Not found - class="kw">return NULL class="kw">return NULL; } class="kw">protected: class=class="str">"cmt">//--- Return the Harami pattern control object CPatternControl *GetObjControlPatternHarami(class="type">void) { class="type">int total=this.m_list_controls.Total(); for(class="type">int i=class="num">0;i<total;i++) { CPatternControl *obj=this.m_list_controls.At(i); if(obj==NULL || obj.TypePattern()!=PATTERN_TYPE_HARAMI) class="kw">continue; class=class="str">"cmt">//--- Check search conditions and class="kw">return the result class=class="str">"cmt">//if(condition) class=class="str">"cmt">// class="kw">return obj; } class="kw">return NULL; } class=class="str">"cmt">//--- Return the Harami Cross pattern control object CPatternControl *GetObjControlPatternHaramiCross(class="type">void) { class="type">int total=this.m_list_controls.Total(); for(class="type">int i=class="num">0;i<total;i++) { CPatternControl *obj=this.m_list_controls.At(i);
◍ 按形态类型取回控制对象
在形态识别模块里,每类蜡烛组合都对应一个 CPatternControl 实例,统一挂在 m_list_controls 链表下。要单独调某类形态(比如十字孕线、镊子线、刺透线),就得遍历链表并按 TypePattern() 做匹配过滤。 下面这段截取自实际工程:先拿 total=this.m_list_controls.Total() 得到链表长度,再用 for 从 0 跑到 total-1,逐节点取 CPatternControl*。若节点为空或类型不等于目标枚举(如 PATTERN_TYPE_HARAMI_CROSS),直接 continue 跳过;全表扫完没命中就 return NULL。
if(obj==NULL || obj.TypePattern()!=PATTERN_TYPE_HARAMI_CROSS) class="kw">continue; class=class="str">"cmt">//--- Check search conditions and class="kw">return the result class=class="str">"cmt">//if(condition) class=class="str">"cmt">// class="kw">return obj; } class="kw">return NULL; } class=class="str">"cmt">//--- Return the Tweezer pattern control object CPatternControl *GetObjControlPatternTweezer(class="type">void) { class="type">int total=this.m_list_controls.Total(); for(class="type">int i=class="num">0;i<total;i++) { CPatternControl *obj=this.m_list_controls.At(i); if(obj==NULL || obj.TypePattern()!=PATTERN_TYPE_TWEEZER) class="kw">continue; class=class="str">"cmt">//--- Check search conditions and class="kw">return the result class=class="str">"cmt">//if(condition) class=class="str">"cmt">// class="kw">return obj; } class="kw">return NULL; } class=class="str">"cmt">//--- Return the Piercing Line pattern control object CPatternControl *GetObjControlPatternPiercingLine(class="type">void) { class="type">int total=this.m_list_controls.Total(); for(class="type">int i=class="num">0;i<total;i++) { CPatternControl *obj=this.m_list_controls.At(i); if(obj==NULL || obj.TypePattern()!=PATTERN_TYPE_PIERCING_LINE)
if(obj==NULL || obj.TypePattern()!=PATTERN_TYPE_HARAMI_CROSS) class="kw">continue; class=class="str">"cmt">//--- Check search conditions and class="kw">return the result class=class="str">"cmt">//if(condition) class=class="str">"cmt">// class="kw">return obj; } class="kw">return NULL; } class=class="str">"cmt">//--- Return the Tweezer pattern control object CPatternControl *GetObjControlPatternTweezer(class="type">void) { class="type">int total=this.m_list_controls.Total(); for(class="type">int i=class="num">0;i<total;i++) { CPatternControl *obj=this.m_list_controls.At(i); if(obj==NULL || obj.TypePattern()!=PATTERN_TYPE_TWEEZER) class="kw">continue; class=class="str">"cmt">//--- Check search conditions and class="kw">return the result class=class="str">"cmt">//if(condition) class=class="str">"cmt">// class="kw">return obj; } class="kw">return NULL; } class=class="str">"cmt">//--- Return the Piercing Line pattern control object CPatternControl *GetObjControlPatternPiercingLine(class="type">void) { class="type">int total=this.m_list_controls.Total(); for(class="type">int i=class="num">0;i<total;i++) { CPatternControl *obj=this.m_list_controls.At(i); if(obj==NULL || obj.TypePattern()!=PATTERN_TYPE_PIERCING_LINE)
「按形态类型检索控制对象」
在形态管理类里,获取指定蜡烛图模式的控制对象靠的是遍历链表加类型过滤。下面三段函数分别指向乌云盖顶、三只白兵等典型价格行为形态,逻辑骨架完全一致:先拿链表总数,再从 0 循环到 total-1,逐个取出 CPatternControl 指针。
若指针为空或类型枚举不匹配(例如不是 PATTERN_TYPE_DARK_CLOUD_COVER),直接 continue 跳过;匹配时本应补条件并返回 obj,原文把返回分支注释掉了,所以当前永远落到的终点是 return NULL。
这种写法在 MT5 自建指标里很实用——你只要在注释处补 if(condition) return obj; 就能按附加约束(如成交量阈值)精确抓取形态实例。外汇与贵金属行情跳空频繁,此类形态识别在高波动时段可能出现假信号,实盘验证时须留意风险。
class="kw">continue; class=class="str">"cmt">//--- Check search conditions and class="kw">return the result class=class="str">"cmt">//if(condition) class=class="str">"cmt">// class="kw">return obj; } class="kw">return NULL; } class=class="str">"cmt">//--- Return the Cloud Cover pattern control object CPatternControl *GetObjControlPatternDarkCloudCover(class="type">void) { class="type">int total=this.m_list_controls.Total(); for(class="type">int i=class="num">0;i<total;i++) { CPatternControl *obj=this.m_list_controls.At(i); if(obj==NULL || obj.TypePattern()!=PATTERN_TYPE_DARK_CLOUD_COVER) class="kw">continue; class=class="str">"cmt">//--- Check search conditions and class="kw">return the result class=class="str">"cmt">//if(condition) class=class="str">"cmt">// class="kw">return obj; } class="kw">return NULL; } class=class="str">"cmt">//--- Return the Three White Soldiers pattern control object CPatternControl *GetObjControlPatternThreeWhiteSoldiers(class="type">void) { class="type">int total=this.m_list_controls.Total(); for(class="type">int i=class="num">0;i<total;i++) { CPatternControl *obj=this.m_list_controls.At(i); if(obj==NULL || obj.TypePattern()!=PATTERN_TYPE_THREE_WHITE_SOLDIERS) class="kw">continue;
按形态类型取控制对象的查找逻辑
在形态识别模块里,每一种蜡烛图形态都对应一个 CPatternControl 实例,统一挂在 m_list_controls 链表上。要单独调出某类形态(比如三只黑乌鸦或射击之星),做法是先拿链表总数 total,再从索引 0 跑循环到 total-1。 循环体内用 m_list_controls.At(i) 取出指针,立刻判空并比对 TypePattern() 返回值。类型不对就 continue 跳过,避免误用别的形态控制块。 代码里搜索条件段被注释掉了://if(condition) return obj; 说明实际返回前还能叠加额外过滤(如出现位置、成交量阈值),但现在只按类型粗筛,筛不到就 return NULL。开 MT5 把这段粘进 EA,把注释条件补上,就能只抓取符合你自己定义的形态实例。外汇与贵金属波动剧烈,这类形态识别仅作概率参考,实盘须控仓。
CPatternControl *GetObjControlPatternThreeBlackCrows(class="type">void) { class="type">int total=this.m_list_controls.Total(); for(class="type">int i=class="num">0;i<total;i++) { CPatternControl *obj=this.m_list_controls.At(i); if(obj==NULL || obj.TypePattern()!=PATTERN_TYPE_THREE_BLACK_CROWS) class="kw">continue; class=class="str">"cmt">//--- Check search conditions and class="kw">return the result class=class="str">"cmt">//if(condition) class=class="str">"cmt">// class="kw">return obj; } class="kw">return NULL; } CPatternControl *GetObjControlPatternShootingStar(class="type">void) { class="type">int total=this.m_list_controls.Total(); for(class="type">int i=class="num">0;i<total;i++) { CPatternControl *obj=this.m_list_controls.At(i); if(obj==NULL || obj.TypePattern()!=PATTERN_TYPE_SHOOTING_STAR) class="kw">continue; class=class="str">"cmt">//--- Check search conditions and class="kw">return the result class=class="str">"cmt">//if(condition)
◍ 锤子与倒锤子形态对象的检索接口
在 MT5 自定义形态识别类里,获取特定 K 线形态的控制对象通常靠遍历链表实现。下面三个函数分别针对 Hammer、Inverted Hammer 做类型过滤,遍历 this.m_list_controls 里的全部元素。 以 GetObjControlPatternHammer 为例:先取 total=this.m_list_controls.Total() 拿到链表长度,再用 for 从 i=0 跑到 i<total。每次 At(i) 取出指针,若为空或类型不等于 PATTERN_TYPE_HAMMER 就 continue 跳过。 注意原代码里 //if(condition) 和 //return obj 被注释掉了,意味着实际返回特定实例的筛选条件留了空。你在回测 EURUSD 15 分钟图时,若链表存了 200 个形态对象,该循环最多跑 200 次才返回 NULL,属于 O(n) 线性查找。 倒锤子函数逻辑完全一致,仅把类型常量换成 PATTERN_TYPE_INVERTED_HAMMER。外汇与贵金属波动剧烈,这类形态识别仅作概率参考,实盘须自担高风险。
class=class="str">"cmt">//--- Return the Hammer pattern control object CPatternControl *GetObjControlPatternHammer(class="type">void) { class="type">int total=this.m_list_controls.Total(); for(class="type">int i=class="num">0;i<total;i++) { CPatternControl *obj=this.m_list_controls.At(i); if(obj==NULL || obj.TypePattern()!=PATTERN_TYPE_HAMMER) class="kw">continue; class=class="str">"cmt">//--- Check search conditions and class="kw">return the result class=class="str">"cmt">//if(condition) class=class="str">"cmt">// class="kw">return obj; } class="kw">return NULL; } class=class="str">"cmt">//--- Return the Inverted Hammer pattern control object CPatternControl *GetObjControlPatternInvertedHammer(class="type">void) { class="type">int total=this.m_list_controls.Total(); for(class="type">int i=class="num">0;i<total;i++) { CPatternControl *obj=this.m_list_controls.At(i); if(obj==NULL || obj.TypePattern()!=PATTERN_TYPE_INVERTED_HAMMER) class="kw">continue; class=class="str">"cmt">//--- Check search conditions and class="kw">return the result class=class="str">"cmt">//if(condition)
「按形态类型捞控制对象」
在 MT5 的自定义形态识别类里,获取某个特定 K 线形态的控制对象,靠的是遍历内部链表 m_list_controls。上面这段分别给出了 Hanging Man(上吊线)和 Doji(十字星)的取对象函数,结构完全一致,只是比对枚举不同。 以 GetObjControlPatternHangingMan 为例:先取链表总数 total,从 i=0 循环到 total-1,每次用 At(i) 拿到 CPatternControl 指针。若指针为空或 TypePattern() 不等于 PATTERN_TYPE_HANGING_MAN 就 continue 跳过,否则命中后本应返回 obj,目前注释掉了条件判断,所以函数尾统一 return NULL。 Doji 的版本仅把判断换成 PATTERN_TYPE_DOJI,其余逻辑一行不差。你在自己 EA 里接这段时,把 //if(condition) 那两行补上实际过滤条件(比如只取最近 50 根内的形态),才能拿到非空指针。外汇与贵金属波动剧烈,形态识别仅作概率参考,实盘须控仓。
CPatternControl *GetObjControlPatternHangingMan(class="type">void) { class="type">int total=this.m_list_controls.Total(); for(class="type">int i=class="num">0;i<total;i++) { CPatternControl *obj=this.m_list_controls.At(i); if(obj==NULL || obj.TypePattern()!=PATTERN_TYPE_HANGING_MAN) class="kw">continue; class=class="str">"cmt">//--- Check search conditions and class="kw">return the result class=class="str">"cmt">//if(condition) class=class="str">"cmt">// class="kw">return obj; } class="kw">return NULL; } CPatternControl *GetObjControlPatternDoji(class="type">void) { class="type">int total=this.m_list_controls.Total(); for(class="type">int i=class="num">0;i<total;i++) { CPatternControl *obj=this.m_list_controls.At(i); if(obj==NULL || obj.TypePattern()!=PATTERN_TYPE_DOJI) class="kw">continue; class=class="str">"cmt">//--- Check search conditions and class="kw">return the result class=class="str">"cmt">//if(condition) class=class="str">"cmt">// class="kw">return obj; } class="kw">return NULL; }
从形态链表里捞蜻蜓与墓碑十字
在 K 线形态管理类里,蜻蜓十字(Dragonfly Doji)和墓碑十字(Gravestone Doji)各自有独立的取值函数,逻辑都是遍历 m_list_controls 这个形态控制对象链表。 先取链表总长度 total = this.m_list_controls.Total(),再用 for 从 i=0 跑到 i<total,逐节点用 At(i) 拿出 CPatternControl 指针。 若 obj 为空,或 obj.TypePattern() 不等于对应的 PATTERN_TYPE_DRAGONFLY_DOJI / PATTERN_TYPE_GRAVESTONE_DOJI,就 continue 跳过;匹配到的对象本来应在注释处的条件判断后 return,但当前代码把返回逻辑注释掉了,函数末尾统一 return NULL。 在 MT5 里跑这段,若没补上条件分支,两个 Getter 永远返回空指针,下游想画形态标记或发信号都会失效。外汇与贵金属波动大、假信号多,这种取不到对象的坑可能让策略漏掉关键反转形态。
CPatternControl *GetObjControlPatternDragonflyDoji(class="type">void) { class="type">int total=this.m_list_controls.Total(); for(class="type">int i=class="num">0;i<total;i++) { CPatternControl *obj=this.m_list_controls.At(i); if(obj==NULL || obj.TypePattern()!=PATTERN_TYPE_DRAGONFLY_DOJI) class="kw">continue; class=class="str">"cmt">//--- Check search conditions and class="kw">return the result class=class="str">"cmt">//if(condition) class=class="str">"cmt">// class="kw">return obj; } class="kw">return NULL; } CPatternControl *GetObjControlPatternGravestoneDoji(class="type">void) { class="type">int total=this.m_list_controls.Total(); for(class="type">int i=class="num">0;i<total;i++) { CPatternControl *obj=this.m_list_controls.At(i); if(obj==NULL || obj.TypePattern()!=PATTERN_TYPE_GRAVESTONE_DOJI) class="kw">continue; class=class="str">"cmt">//--- Check search conditions and class="kw">return the result class=class="str">"cmt">//if(condition) class=class="str">"cmt">// class="kw">return obj; } class="kw">return NULL; }
◍ 从形态链表里捞指定的晨星对象
在 MT5 的自定义形态识别框架里,所有 K 线形态的控制对象都挂在一个链表 m_list_controls 上。想单独调取某类形态(比如晨星或晨十字星)的控制器,就得遍历这个链表并按类型过滤。 下面两个函数就是干这活的:GetObjControlPatternMorningStar 只认 PATTERN_TYPE_MORNING_STAR,GetObjControlPatternMorningDojiStar 只认 PATTERN_TYPE_MORNING_DOJI_STAR。遍历用 for 从 i=0 跑到 total-1,total 来自 m_list_controls.Total(),也就是链表当前节点数。 注意原代码里检索到匹配对象后,并没有直接 return obj,而是留了 //if(condition) 的注释位——意思是你可以加自己的附加条件(比如只取最近 N 根 Bar 内的形态)再返回,否则循环跑完没命中就 return NULL。外汇与贵金属市场波动剧烈、杠杆风险高,这类形态识别仅作概率参考,实盘前务必在策略测试器里跑一遍验证命中率。 把这段直接贴进你的 EA 头文件,编译后调用 GetObjControlPatternMorningStar() 就能拿到晨星控制器指针,返回 NULL 则说明当前链表里没有该形态实例。
CPatternControl *GetObjControlPatternMorningStar(class="type">void) { class="type">int total=this.m_list_controls.Total(); for(class="type">int i=class="num">0;i<total;i++) { CPatternControl *obj=this.m_list_controls.At(i); if(obj==NULL || obj.TypePattern()!=PATTERN_TYPE_MORNING_STAR) class="kw">continue; class=class="str">"cmt">//--- Check search conditions and class="kw">return the result class=class="str">"cmt">//if(condition) class=class="str">"cmt">// class="kw">return obj; } class="kw">return NULL; } CPatternControl *GetObjControlPatternMorningDojiStar(class="type">void) { class="type">int total=this.m_list_controls.Total(); for(class="type">int i=class="num">0;i<total;i++) { CPatternControl *obj=this.m_list_controls.At(i); if(obj==NULL || obj.TypePattern()!=PATTERN_TYPE_MORNING_DOJI_STAR) class="kw">continue; class=class="str">"cmt">//--- Check search conditions and class="kw">return the result class=class="str">"cmt">//if(condition) class=class="str">"cmt">// class="kw">return obj; } class="kw">return NULL; }
「黄昏星与十字黄昏星的控制对象检索」
在形态管理类里,取某类蜡烛图形态的控制对象靠的是遍历链表再比类型。下面两个方法分别锁定黄昏星(Evening Star)和十字黄昏星(Evening Doji Star),逻辑完全一致,只是比对枚举不同。 以 GetObjControlPatternEveningStar 为例:先拿 m_list_controls.Total() 得到链表总长度,再从 0 跑到 total-1,每次用 At(i) 取指针。若指针为空或 TypePattern() 不等于 PATTERN_TYPE_EVENING_STAR 就 continue 跳过。 注释里留了 //if(condition) return obj; 的空位,说明原设计允许按额外条件(比如出现位置、成交量)过滤,但当前版本没接任何条件,循环结束直接 return NULL。这意味着现在调这个方法,只要链里没挂对应类型控制块,就一定拿到空指针。 外汇与贵金属行情里黄昏星偏反转提示,但形态命中不代表概率倾斜,实盘前请在 MT5 用这几行代码验证链表填充时机,避免空指针误用。
CPatternControl *GetObjControlPatternEveningStar(class="type">void) { class="type">int total=this.m_list_controls.Total(); for(class="type">int i=class="num">0;i<total;i++) { CPatternControl *obj=this.m_list_controls.At(i); if(obj==NULL || obj.TypePattern()!=PATTERN_TYPE_EVENING_STAR) class="kw">continue; class=class="str">"cmt">//--- Check search conditions and class="kw">return the result class=class="str">"cmt">//if(condition) class=class="str">"cmt">// class="kw">return obj; } class="kw">return NULL; } CPatternControl *GetObjControlPatternEveningDojiStar(class="type">void) { class="type">int total=this.m_list_controls.Total(); for(class="type">int i=class="num">0;i<total;i++) { CPatternControl *obj=this.m_list_controls.At(i); if(obj==NULL || obj.TypePattern()!=PATTERN_TYPE_EVENING_DOJI_STAR) class="kw">continue; class=class="str">"cmt">//--- Check search conditions and class="kw">return the result class=class="str">"cmt">//if(condition) class=class="str">"cmt">// class="kw">return obj; } class="kw">return NULL; }
从形态链表里捞指定的 K 线组合控制器
在 MT5 的 Price Action 分析框架里,所有识别到的 K 线形态都挂在同一个 m_list_controls 链表上。想单独处理某一种形态,就得写个 getter 去遍历它,而不是重新算一遍。
下面这段就是「三颗星」和「弃婴」两个形态的取对象函数。逻辑完全一致:先拿链表总长度 total,从 0 跑到 total-1,每次用 At(i) 取出一个 CPatternControl*,判空且比对 TypePattern() 枚举,不对就 continue 跳过。
注意被注释掉的 //if(condition) return obj; —— 原作者留了钩子,你可以按自己条件(比如只取最近 N 根内的形态)把对象提前返回;现在函数永远走到末尾 return NULL,也就是暂未实现筛选。
CPatternControl *GetObjControlPatternThreeStars(class="type">void) { class="type">int total=this.m_list_controls.Total(); for(class="type">int i=class="num">0;i<total;i++) { CPatternControl *obj=this.m_list_controls.At(i); if(obj==NULL || obj.TypePattern()!=PATTERN_TYPE_THREE_STARS) class="kw">continue; class=class="str">"cmt">//--- Check search conditions and class="kw">return the result class=class="str">"cmt">//if(condition) class=class="str">"cmt">// class="kw">return obj; } class="kw">return NULL; } class=class="str">"cmt">//--- Return the Abandoned Body pattern control object CPatternControl *GetObjControlPatternAbandonedBaby(class="type">void) { class="type">int total=this.m_list_controls.Total(); for(class="type">int i=class="num">0;i<total;i++) { CPatternControl *obj=this.m_list_controls.At(i); if(obj==NULL || obj.TypePattern()!=PATTERN_TYPE_ABANDONED_BABY) class="kw">continue; class=class="str">"cmt">//--- Check search conditions and class="kw">return the result class=class="str">"cmt">//if(condition) class=class="str">"cmt">// class="kw">return obj; } class="kw">return NULL; } class=class="str">"cmt">//--- Price Action class=class="str">"cmt">//--- Return the Pivot Point Reversal pattern control object CPatternControl *GetObjControlPatternPivotPointReversal(class="type">void)
total 取链表元素数;第 4–5 行 for 循环索引;第 7 行 At(i) 取第 i 个对象;第 8 行过滤空指针且类型不符者;第 9 行跳过;注释行是预留返回点;循环结束第 14 行返回空。弃婴函数结构同构,仅枚举换成 PATTERN_TYPE_ABANDONED_BABY。
开 MT5 把这段塞进你的形态管理类,先 Print(GetObjControlPatternThreeStars()!=NULL) 验证链表里有没有三颗星实例;外汇和贵金属波动大,形态失效概率不低,实盘前务必用历史数据回测。
CPatternControl *GetObjControlPatternThreeStars(class="type">void) { class="type">int total=this.m_list_controls.Total(); for(class="type">int i=class="num">0;i<total;i++) { CPatternControl *obj=this.m_list_controls.At(i); if(obj==NULL || obj.TypePattern()!=PATTERN_TYPE_THREE_STARS) class="kw">continue; class=class="str">"cmt">//--- Check search conditions and class="kw">return the result class=class="str">"cmt">//if(condition) class=class="str">"cmt">// class="kw">return obj; } class="kw">return NULL; } class=class="str">"cmt">//--- Return the Abandoned Body pattern control object CPatternControl *GetObjControlPatternAbandonedBaby(class="type">void) { class="type">int total=this.m_list_controls.Total(); for(class="type">int i=class="num">0;i<total;i++) { CPatternControl *obj=this.m_list_controls.At(i); if(obj==NULL || obj.TypePattern()!=PATTERN_TYPE_ABANDONED_BABY) class="kw">continue; class=class="str">"cmt">//--- Check search conditions and class="kw">return the result class=class="str">"cmt">//if(condition) class=class="str">"cmt">// class="kw">return obj; } class="kw">return NULL; } class=class="str">"cmt">//--- Price Action class=class="str">"cmt">//--- Return the Pivot Point Reversal pattern control object CPatternControl *GetObjControlPatternPivotPointReversal(class="type">void)
◍ 按形态类型捞控件对象的循环写法
在 MT5 自定义形态识别库里,经常需要从一个统一的控制对象链表 m_list_controls 里,按形态类别把对应的 CPatternControl 指针捞出来。下面这段就是典型的遍历过滤骨架,分别针对枢轴反转、Outside Bar、Inside Bar 三种类型写了独立的获取函数。 以 GetObjControlPatternPivotPointReversal 为例,先取链表总数 total,再用 for 从 0 跑到 total-1;每次 At(i) 拿指针,若为空或 TypePattern() 不等于目标枚举就 continue 跳过。注释里留了 //if(condition) return obj; 的坑位,实际接搜索条件时把判断填进去即可,找不到统一 return NULL。 Outside Bar 与 Inside Bar 的两个函数结构完全一致,仅把枚举换成 PATTERN_TYPE_OUTSIDE_BAR 和 PATTERN_TYPE_INSIDE_BAR。这种写法在 160 个形态类里会反复出现,链表长度若到几十量级,线性扫描的耗时在 Tick 级可接受,但别指望它替代哈希索引。 开 MT5 自己建个 CList 塞几个假控件,把这三段抄进 Expert Advisor 测一下返回指针,比光看省事。
{
class="type">int total=this.m_list_controls.Total();
for(class="type">int i=class="num">0;i<total;i++)
{
CPatternControl *obj=this.m_list_controls.At(i);
if(obj==NULL || obj.TypePattern()!=PATTERN_TYPE_PIVOT_POINT_REVERSAL)
class="kw">continue;
class=class="str">"cmt">//--- Check search conditions and class="kw">return the result
class=class="str">"cmt">//if(condition)
class=class="str">"cmt">// class="kw">return obj;
}
class="kw">return NULL;
}
class=class="str">"cmt">//--- Return the Outside Bar pattern control object
CPatternControl *GetObjControlPatternOutsideBar(class="type">void)
{
class="type">int total=this.m_list_controls.Total();
for(class="type">int i=class="num">0;i<total;i++)
{
CPatternControl *obj=this.m_list_controls.At(i);
if(obj==NULL || obj.TypePattern()!=PATTERN_TYPE_OUTSIDE_BAR)
class="kw">continue;
class=class="str">"cmt">//--- Check search conditions and class="kw">return the result
class=class="str">"cmt">//if(condition)
class=class="str">"cmt">// class="kw">return obj;
}
class="kw">return NULL;
}
class=class="str">"cmt">//--- Return the Inside Bar pattern control object
CPatternControl *GetObjControlPatternInsideBar(class="type">void)
{「从控制对象列表里捞特定的K线形态」
在形态识别类 EA 里,通常会把各种蜡烛形态的控制对象塞进一个链表(如 m_list_controls),需要时再遍历提取。下面这段逻辑就是先取链表总数,然后逐个判断类型,不是目标形态就直接 continue 跳过。 遍历内部 Bar 形态时,代码只认 PATTERN_TYPE_INSIDE_BAR:遇到空指针或类型不符立即跳过,保留的注释行显示了原本可加搜索条件后 return 具体对象的位置,目前函数尾部统一返回 NULL。 Pin Bar 的提取函数给了三个默认阈值参数:实体占蜡烛全长比 ratio_body=30,较长影线占比 ratio_larger_shadow=60,较短影线占比 ratio_smaller_shadow=30。在 MT5 里把这三个数改成 20/70/20,往往能滤掉更多弱势 Pin Bar,但外汇与贵金属波动剧烈,参数调整只是改变识别概率,不预示方向。 让小布替你跑这套 把上面 Pin Bar 函数直接贴进你的形态类,用 Commentary 打印 Total() 返回值,能立刻看到当前图表加载了多少个控制对象,验证链表是否真的接进了指标。
class="type">int total=this.m_list_controls.Total(); for(class="type">int i=class="num">0;i<total;i++) { CPatternControl *obj=this.m_list_controls.At(i); if(obj==NULL || obj.TypePattern()!=PATTERN_TYPE_INSIDE_BAR) class="kw">continue; class=class="str">"cmt">//--- Check search conditions and class="kw">return the result class=class="str">"cmt">//if(condition) class=class="str">"cmt">// class="kw">return obj; } class="kw">return NULL; class=class="str">"cmt">//--- Return the Pin Bar pattern control object CPatternControl *GetObjControlPatternPinBar(const class="type">class="kw">double ratio_body=class="num">30, class=class="str">"cmt">// Percentage ratio of the candle body to the full size of the candle const class="type">class="kw">double ratio_larger_shadow=class="num">60, class=class="str">"cmt">// Percentage ratio of the size of the larger shadow to the size of the candle const class="type">class="kw">double ratio_smaller_shadow=class="num">30) class=class="str">"cmt">// Percentage ratio of the size of the smaller shadow to the size of the candle { class=class="str">"cmt">//--- In a loop through the list of control objects, class="type">int total=this.m_list_controls.Total(); for(class="type">int i=class="num">0;i<total;i++) { class=class="str">"cmt">//--- get the next object CPatternControl *obj=this.m_list_controls.At(i); class=class="str">"cmt">//--- if this is not a Pin Bar pattern control object, go to the next one
从控制列表里捞指定的价格行为形态
在形态管理类里,取 Pin Bar 控制对象靠的是遍历内部链表 m_list_controls,并按形态类型与三项比例参数做匹配。代码先拿到链表总数 total,从 0 到 total-1 逐个取出 CPatternControl 指针。 若指针为空或类型不是 PATTERN_TYPE_PIN_BAR 就 continue 跳过;剩下来的用 RatioBodyToCandleSizeValue()、RatioLargerShadowToCandleSizeValue()、RatioSmallerShadowToCandleSizeValue() 三个比值和传入的 ratio_body、ratio_larger_shadow、ratio_smaller_shadow 比对,全相等才 return 该对象,否则循环结束返 NULL。 Rails 形态的取用函数结构完全一致,只是判断类型换成 PATTERN_TYPE_RAILS,原文里检索条件被注释掉了,所以现在永远返 NULL,要落地得自己补 if(condition) 逻辑。 对外接口 SetUsedPatternPinBar 接收 flag 与 ratio_body 默认 30(即实体占整根蜡烛尺寸 30%),用来开关 Pin Bar 并懒创建控制对象;外汇与贵金属波动剧烈,这类比例阈值仅作形态筛选参考,实际信号概率受周期与流动性影响。
if(obj==NULL || obj.TypePattern()!=PATTERN_TYPE_PIN_BAR) class="kw">continue; class=class="str">"cmt">//--- Check search conditions and class="kw">return the result if(ratio_body==obj.RatioBodyToCandleSizeValue() && ratio_larger_shadow==obj.RatioLargerShadowToCandleSizeValue() && ratio_smaller_shadow==obj.RatioSmallerShadowToCandleSizeValue()) class="kw">return obj; class=class="str">"cmt">//--- Not found - class="kw">return NULL class="kw">return NULL; class=class="str">"cmt">//--- Return the Rails pattern control object CPatternControl *GetObjControlPatternRails(class="type">void) { class="type">int total=this.m_list_controls.Total(); for(class="type">int i=class="num">0;i<total;i++) { CPatternControl *obj=this.m_list_controls.At(i); if(obj==NULL || obj.TypePattern()!=PATTERN_TYPE_RAILS) class="kw">continue; class=class="str">"cmt">//--- Check search conditions and class="kw">return the result class=class="str">"cmt">//if(condition) class=class="str">"cmt">// class="kw">return obj; } class="kw">return NULL; } class="kw">public: class=class="str">"cmt">//--- Set the flag for using the Pin Bar pattern and create a control object if it does not already exist class="type">void SetUsedPatternPinBar(const class="type">bool flag, class=class="str">"cmt">// Price Action Pin Bar usage flag const class="type">class="kw">double ratio_body=class="num">30, class=class="str">"cmt">// Percentage ratio of the candle body to the full size of the candle
◍ Pin Bar 控制对象的开关与新建逻辑
在 MT5 的 Pin Bar 识别模块里,影线占比是硬阈值:较大影线默认占实体蜡烛尺寸的 60%,较小影线占 30%,这两个常量直接决定形态是否被判定为 Pin Bar。外汇与贵金属波动剧烈,这类比例阈值在不同品种上可能需回测后微调,否则容易漏掉有效反转信号或吞掉噪音。 下面这段控制函数负责按给定比例参数拿到(或新建)Pin Bar 控制对象,并统一设置使用标志。若对象已存在就直接改 flag;若对象不存在且 flag 为 true,才走新建分支并把指针挂进管理链表。 [CODE] 逐行拆解: const double ratio_larger_shadow=60, // 较大影线占蜡烛尺寸的百分比阈值 const double ratio_smaller_shadow=30) // 较小影线占蜡烛尺寸的百分比阈值 { // 函数体开始 //--- 获取指定参数下的 Pin Bar 控制对象指针 CPatternControlPinBar *obj=this.GetObjControlPatternPinBar(ratio_body,ratio_larger_shadow,ratio_smaller_shadow); //--- 若指针有效(对象已存在),设置使用标志 if(obj!=NULL) obj.SetUsed(flag); //--- 若对象不存在且 flag 传入 true else if(flag) { //--- 用指定参数新建 Pin Bar 控制对象 obj=new CPatternControlPinBar(this.Symbol(),this.Timeframe(),this.m_list_series,this.m_list_all_patterns,ratio_body,ratio_larger_shadow,ratio_smaller_shadow); if(obj==NULL) // 内存分配失败直接返回 return; //--- 将新对象指针加入链表 if(!this.m_list_controls.Add(obj)) { delete obj; // 加入失败则释放避免泄漏 return; } //--- 设置使用标志与实体占比参数 obj.SetUsed(flag); obj.SetRatioBodyToCandleSizeValue(ratio_body); 开 MT5 把 ratio_larger_shadow 从 60 调到 50 试一轮,EURUSD 的 H1 上 Pin Bar 触发频率可能明显上升,但假信号概率也会随之抬高。
const class="type">class="kw">double ratio_larger_shadow=class="num">60, class=class="str">"cmt">// Percentage ratio of the size of the larger shadow to the size of the candle const class="type">class="kw">double ratio_smaller_shadow=class="num">30) class=class="str">"cmt">// Percentage ratio of the size of the smaller shadow to the size of the candle { class=class="str">"cmt">//--- Get the pointer to the Pin Bar pattern control object with the specified parameters CPatternControlPinBar *obj=this.GetObjControlPatternPinBar(ratio_body,ratio_larger_shadow,ratio_smaller_shadow); class=class="str">"cmt">//--- If the pointer is received(the object exists), set the use flag if(obj!=NULL) obj.SetUsed(flag); class=class="str">"cmt">//--- f there is no object and the flag is passed as &class="macro">#x27;true&class="macro">#x27; else if(flag) { class=class="str">"cmt">//--- Create a new Pin Bar pattern control object with the specified parameters obj=new CPatternControlPinBar(this.Symbol(),this.Timeframe(),this.m_list_series,this.m_list_all_patterns,ratio_body,ratio_larger_shadow,ratio_smaller_shadow); if(obj==NULL) class="kw">return; class=class="str">"cmt">//--- Add pointer to the created object to the list if(!this.m_list_controls.Add(obj)) { class="kw">delete obj; class="kw">return; } class=class="str">"cmt">//--- Set the usage flag and pattern parameters to the control object obj.SetUsed(flag); obj.SetRatioBodyToCandleSizeValue(ratio_body);
「形态开关的封装逻辑」
这段接口把 K 线形态识别做成可插拔开关:每个 SetUsedPatternXxx(bool flag) 只负责置位对应形态的使用标志,并在控制对象不存在时顺手建一个。Harami、Harami Cross、Tweezer、Piercing Line、Dark Cloud Cover、Three White Soldiers 都走同一套 public 方法模板,调用方不用关心底层对象生命周期。 前面那段私有代码已经把影线比例写死了:obj.SetRatioLargerShadowToCandleSizeValue(ratio_larger_shadow) 和 obj.SetRatioSmallerShadowToCandleSizeValue(ratio_smaller_shadow) 之后立刻 CreateAndRefreshPatternList(),意味着改完阈值会重算形态列表。你在 MT5 里接这套类时,注意先调比例设置再开形态开关,否则可能刷出旧参数下的识别结果。 外汇与贵金属波动大、滑点随机,形态命中只是概率倾向,实盘前务必用历史数据跑一遍命中率。
obj.SetRatioLargerShadowToCandleSizeValue(ratio_larger_shadow); obj.SetRatioSmallerShadowToCandleSizeValue(ratio_smaller_shadow); obj.CreateAndRefreshPatternList(); } } class="kw">public: class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Methods for setting the pattern use flag | class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//--- Set the flag for using the Harami pattern and create a control object if it does not already exist class="type">void SetUsedPatternHarami(const class="type">bool flag) { } class=class="str">"cmt">//--- Set the flag for using the Harami Cross pattern and create a control object if it does not already exist class="type">void SetUsedPatternHaramiCross(const class="type">bool flag) { } class=class="str">"cmt">//--- Set the flag for using the Tweezer pattern and create a control object if it does not already exist class="type">void SetUsedPatternTweezer(const class="type">bool flag) { } class=class="str">"cmt">//--- Set the flag for using the Piercing Line pattern and create a control object if it does not already exist class="type">void SetUsedPatternPiercingLine(const class="type">bool flag) { } class=class="str">"cmt">//--- Set the flag for using the Cloud Cover pattern and create a control object if it does not already exist class="type">void SetUsedPatternDarkCloudCover(const class="type">bool flag) { } class=class="str">"cmt">//--- Set the flag for using the Three White Soldiers pattern and create a control object if it does not already exist class="type">void SetUsedPatternThreeWhiteSoldiers(const class="type">bool flag) { }
逐个切换 K 线形态识别开关
在 MT5 的自定义形态识别模块里,每一类蜡烛图模式都对应一个独立的启用函数,而不是用一张总表去勾选。上面这组接口覆盖了外汇与贵金属盘面里最常被盯防的 8 种结构:三黑鸦、射击之星、锤子线、倒锤子、吊人线、十字星、蜻蜓十字、墓碑十字。 调用时只需传入 bool 值,true 即把该形态纳入实时扫描,false 则跳过。比如只做黄金反转,可以只开 SetUsedPatternHammer 与 SetUsedPatternShootingStar,其余置 false,EA 的运算负载会明显下降。 外汇与贵金属杠杆高、滑点突发行情多,形态触发只是概率信号,真要在实盘跑前务必在策略测试器里用 2020—2024 年 tick 数据回测一遍开关组合。
class=class="str">"cmt">//--- Set the flag for using the Three Black Crows pattern and create a control object if it does not already exist class="type">void SetUsedPatternThreeBlackCrows(const class="type">bool flag) { } class=class="str">"cmt">//--- Set the flag for using the Shooting Star pattern and create a control object if it does not already exist class="type">void SetUsedPatternShootingStar(const class="type">bool flag) { } class=class="str">"cmt">//--- Set the flag for using the Hammer pattern and create a control object if it does not already exist class="type">void SetUsedPatternHammer(const class="type">bool flag) { } class=class="str">"cmt">//--- Set the flag for using the Inverted Hammer pattern and create a control object if it does not already exist class="type">void SetUsedPatternInvertedHammer(const class="type">bool flag) { } class=class="str">"cmt">//--- Set the flag for using the Hanging Man pattern and create a control object if it does not already exist class="type">void SetUsedPatternHangingMan(const class="type">bool flag) { } class=class="str">"cmt">//--- Set the flag for using the Doji pattern and create a control object if it does not already exist class="type">void SetUsedPatternDoji(const class="type">bool flag) { } class=class="str">"cmt">//--- Set the flag for using the Dragonfly Doji pattern and create a control object if it does not already exist class="type">void SetUsedPatternDragonflyDoji(const class="type">bool flag) { } class=class="str">"cmt">//--- Set the flag for using the Doji Gravestone pattern and create a control object if it does not already exist class="type">void SetUsedPatternGravestoneDoji(const class="type">bool flag) {
◍ 星线族与价格行为模式的开关函数
这段接口集中暴露了蜡烛图识别模块里一组同名风格的 setter:每个函数负责打开或关闭某一种形态的检测,并在对应控制器对象尚未创建时顺手补建。 从 Morning Star、Morning Doji Star 到 Evening Star、Evening Doji Star,再到 Three Stars、Abandoned Baby,都属于星线族变体;后面紧跟的 Pivot Point Reversal 与 Outside 则归到 Price Action 类。 函数签名高度一致:传入 const bool flag,由调用方决定启用还是停用。实参为 true 时倾向开启该形态扫描,false 则关闭,具体对象生命周期由底层类自行维护。 在 MT5 里接这套逻辑时,建议先只开 Evening Star + Outside 两个开关做回测,观察其在 XAUUSD 的 M15 上误报率,再逐步叠加其余形态——外汇与贵金属杠杆高,信号冗余会放大交易成本。
class="type">void SetUsedPatternMorningStar(const class="type">bool flag) { } class="type">void SetUsedPatternMorningDojiStar(const class="type">bool flag) { } class="type">void SetUsedPatternEveningStar(const class="type">bool flag) { } class="type">void SetUsedPatternEveningDojiStar(const class="type">bool flag) { } class="type">void SetUsedPatternThreeStars(const class="type">bool flag) { } class="type">void SetUsedPatternAbandonedBaby(const class="type">bool flag) { } class="type">void SetUsedPatternPivotPointReversal(const class="type">bool flag) { } class=class="str">"cmt">//--- Set the flag for using the Pattern Outside and create a control object if it does not already exist
「Pin Bar 开关与三比例参数」
在价格行为识别模块里,Outside Bar 与 Inside Bar 只靠一个 bool 开关控制启停,函数体留空表示仅设置内部标记位,真正的判定逻辑在别处对象中。 Pin Bar 的接口则复杂得多,除了 flag 还带三个默认比例参数:实体占蜡烛全长 30%、较长影线占 60%、较短影线占 30%,这些百分比直接决定了一根 K 线是否被判定为 Pin Bar。 调用 SetUsedPatternPinBar 时,若对应比例的控制对象已存在就直接改 used 标记;若不存在且 flag 为 true,则按当前比例新建控制对象。想收紧或放宽信号,调那三个 double 参数即可,例如把 ratio_larger_shadow 从 60 降到 50 会让 Pin Bar 触发概率倾向提高。 外汇与贵金属波动剧烈,这类形态识别仅作辅助参考,实盘前务必在 MT5 策略测试器用历史数据验证参数敏感性。
class="type">void SetUsedPatternOutsideBar(const class="type">bool flag) { } class=class="str">"cmt">//--- Set the flag for using the Pattern Inside and create a control object if it does not already exist class="type">void SetUsedPatternInsideBar(const class="type">bool flag) { } class=class="str">"cmt">//--- Set the flag for using the Pin Bar pattern and create a control object if it does not already exist class="type">void SetUsedPatternPinBar(const class="type">bool flag, class=class="str">"cmt">// Price Action Pin Bar usage flag const class="type">class="kw">double ratio_body=class="num">30, class=class="str">"cmt">// Percentage ratio of the candle body to the full size of the candle const class="type">class="kw">double ratio_larger_shadow=class="num">60, class=class="str">"cmt">// Percentage ratio of the size of the larger shadow to the size of the candle const class="type">class="kw">double ratio_smaller_shadow=class="num">30)class=class="str">"cmt">// Percentage ratio of the size of the smaller shadow to the size of the candle { class=class="str">"cmt">//--- Get the pointer to the Pin Bar pattern control object with the specified parameters CPatternControlPinBar *obj=this.GetObjControlPatternPinBar(ratio_body,ratio_larger_shadow,ratio_smaller_shadow); class=class="str">"cmt">//--- If the pointer is received(the object exists), set the use flag if(obj!=NULL) obj.SetUsed(flag); class=class="str">"cmt">//--- f there is no object and the flag is passed as &class="macro">#x27;true&class="macro">#x27; else if(flag) { class=class="str">"cmt">//--- Create a new Pin Bar pattern control object with the specified parameters
Pin Bar 控制对象的实例化与参数注入
在 MT5 的 K 线形态识别模块里,Pin Bar 不是一个静态判断,而是被封装成 CPatternControlPinBar 类的运行实例。上面这段逻辑干的事很直接:用当前图表品种、周期和两根序列列表去 new 一个控制对象,把实体占比、长影线占比、短影线占比三个阈值一并传进去。 若 obj 返回 NULL,说明内存或上下文创建失败,函数直接 return,不会往下走;若 m_list_controls.Add(obj) 添加失败,则立刻 delete obj 并退出,避免野指针。这个防御写法在跑多品种扫描时很关键,否则一个品种报错可能拖挂整个 EA。 创建成功后,依次调用 SetUsed、SetRatioBodyToCandleSizeValue、SetRatioLargerShadowToCandleSizeValue、SetRatioSmallerShadowToCandleSizeValue 把开关和比例写进对象,最后 CreateAndRefreshPatternList 刷新形态列表。注意 IsUsedPatternPinBar 的默认参数是 ratio_body=30、ratio_larger_shadow=60,即实体不超 30%、长影线至少 60% 才倾向被认定为 Pin Bar,外汇与贵金属波动大,实盘前建议在 MT5 里改这几个数回测验证。 Rails 模式的 SetUsedPatternRails 函数体为空,说明原文此处尚未实现,别照抄就当它能用;真要接 Rails 得自己补控制对象生成逻辑。
obj=new CPatternControlPinBar(this.Symbol(),this.Timeframe(),this.m_list_series,this.m_list_all_patterns,ratio_body,ratio_larger_shadow,ratio_smaller_shadow); if(obj==NULL) class="kw">return; class=class="str">"cmt">//--- Add pointer to the created object to the list if(!this.m_list_controls.Add(obj)) { class="kw">delete obj; class="kw">return; } class=class="str">"cmt">//--- Set the usage flag and pattern parameters to the control object obj.SetUsed(flag); obj.SetRatioBodyToCandleSizeValue(ratio_body); obj.SetRatioLargerShadowToCandleSizeValue(ratio_larger_shadow); obj.SetRatioSmallerShadowToCandleSizeValue(ratio_smaller_shadow); obj.CreateAndRefreshPatternList(); } } class=class="str">"cmt">//--- Set the flag for using the Rails pattern and create a control object if it does not already exist class="type">void SetUsedPatternRails(const class="type">bool flag) { } class=class="str">"cmt">//--- Return the flag of using the Pin Bar pattern class="type">bool IsUsedPatternPinBar(const class="type">class="kw">double ratio_body=class="num">30, class=class="str">"cmt">// Percentage ratio of the candle body to the full size of the candle const class="type">class="kw">double ratio_larger_shadow=class="num">60, class=class="str">"cmt">// Percentage ratio of the size of the larger shadow to the size of the candle
◍ 用返回标志位判断形态是否被激活
在 MT5 的自定义形态识别类里,一组 IsUsedPatternXxx() 方法负责向外暴露某 K 线形态当前是否参与计算。它们统一通过获取对应的 CPatternControl 对象指针,再调用 IsUsed() 来拿状态;若指针为空则直接回 false,避免空引用崩脚本。 以 PinBar 为例,调用时还能传入三组比例阈值:实体占蜡烛大小的比例、较长影线占比、较短影线占比,其中较短影线默认上限被设为 30(百分比)。这意味着若某根蜡烛的小影线超过实体 30% 就倾向不认定为标准 PinBar。 Harami、Harami Cross、Tweezer、Piercing Line 等形态则不需要额外参数,直接取各自的控制对象即可。你在 EA 里想动态关闭某种形态识别,只需盯这些 bool 返回值,不必改底层绘制逻辑。 外汇与贵金属波动大、滑点频繁,形态激活仅代表算法判定条件满足,实际触发信号后仍需结合风控,回测胜率可能随品种和周期漂移。
const class="type">class="kw">double ratio_smaller_shadow=class="num">30) class=class="str">"cmt">// Percentage ratio of the size of the smaller shadow to the size of the candle { class=class="str">"cmt">//--- Get the pattern control object based on its parameters CPatternControl *obj=this.GetObjControlPatternPinBar(ratio_body,ratio_larger_shadow,ratio_smaller_shadow); class=class="str">"cmt">//--- Return the pattern use flag, or &class="macro">#x27;class="kw">false&class="macro">#x27; if the object is not found class="kw">return(obj!=NULL ? obj.IsUsed() : class="kw">false); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Methods for returning the pattern usage flag | class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//--- Candle formations class=class="str">"cmt">//--- Return the flag of using the Harami pattern class="type">bool IsUsedPatternHarami(class="type">void) { CPatternControl *obj=GetObjControlPatternHarami(); class="kw">return(obj!=NULL ? obj.IsUsed() : class="kw">false); } class=class="str">"cmt">//--- Return the flag of using the Harami Cross pattern class="type">bool IsUsedPatternHaramiCross(class="type">void) { CPatternControl *obj=GetObjControlPatternHaramiCross(); class="kw">return(obj!=NULL ? obj.IsUsed() : class="kw">false); } class=class="str">"cmt">//--- Return the flag of using the Tweezer pattern class="type">bool IsUsedPatternTweezer(class="type">void) { CPatternControl *obj=GetObjControlPatternTweezer(); class="kw">return(obj!=NULL ? obj.IsUsed() : class="kw">false); } class=class="str">"cmt">//--- Return the flag of using the Piercing Line pattern class="type">bool IsUsedPatternPiercingLine(class="type">void) { CPatternControl *obj=GetObjControlPatternPiercingLine();
「形态启用状态的统一查询接口」
在 MT5 的 K 线形态识别模块里,每一个蜡烛图形态都配了一个 IsUsed 查询函数,用来确认该形态在当前图表周期是否被激活参与信号计算。 上面这批函数覆盖了吞没、乌云盖顶、三白兵、三乌鸦、流星、锤子、倒锤子等常见价格行为形态,结构完全一致:先通过 GetObjControl 拿到形态控制器指针,再返回 IsUsed 的结果。 若指针为空(说明对象未创建)则直接返回 false,避免空指针报错。实盘里你可以循环调用这些接口,在 EA 日志里打印哪些形态处于启用态,从而快速排查「为什么某形态没触发」类问题。外汇与贵金属波动剧烈,这类开关误配可能导致信号缺失,需结合回测验证。
class="type">bool IsUsedPatternDarkCloudCover(class="type">void) { CPatternControl *obj=GetObjControlPatternDarkCloudCover(); class="kw">return(obj!=NULL ? obj.IsUsed() : class="kw">false); } class="type">bool IsUsedPatternThreeWhiteSoldiers(class="type">void) { CPatternControl *obj=GetObjControlPatternThreeWhiteSoldiers(); class="kw">return(obj!=NULL ? obj.IsUsed() : class="kw">false); } class="type">bool IsUsedPatternThreeBlackCrows(class="type">void) { CPatternControl *obj=GetObjControlPatternThreeBlackCrows(); class="kw">return(obj!=NULL ? obj.IsUsed() : class="kw">false); } class="type">bool IsUsedPatternShootingStar(class="type">void) { CPatternControl *obj=GetObjControlPatternShootingStar(); class="kw">return(obj!=NULL ? obj.IsUsed() : class="kw">false); } class="type">bool IsUsedPatternHammer(class="type">void) { CPatternControl *obj=GetObjControlPatternHammer(); class="kw">return(obj!=NULL ? obj.IsUsed() : class="kw">false); } class="type">bool IsUsedPatternInvertedHammer(class="type">void) { CPatternControl *obj=GetObjControlPatternInvertedHammer();
形态开关的判活封装
在 MT5 的 K 线形态识别模块里,每一个蜡烛形态都配了一个独立的控制对象(CPatternControl),但外部调用方往往只关心“这个形态当前有没有被启用”。上面这组函数就是典型的判活封装:先通过 GetObjControlXxx() 拿到对应形态的控制器指针,再问它 IsUsed() 返回真还是假。 如果指针是 NULL(比如该形态在编译期就被裁掉或初始化失败),函数直接返回 false,避免空指针崩 EA。这种写法在回测里很实用——你可以在策略里只开启 Morning Star 和 Doji,其余七个形态函数恒返回 false,MT5 的优化器就能少算一大截。 实际验证时,把下面任意一段贴进你的形态管理类,用 Print(IsUsedPatternDoji()) 在 EURUSD 的 M15 上跑,就能看到当前图表配置下 Doji 是否被加载。外汇与贵金属杠杆高,形态开关只是过滤噪音的手段,信号触发后仍可能反向,务必轻仓验证。
CPatternControl *obj=GetObjControlPatternInvertedHammer(); class="kw">return(obj!=NULL ? obj.IsUsed() : class="kw">false); class=class="str">"cmt">//--- Return the flag of using the Hanging Man pattern class="type">bool IsUsedPatternHangingMan(class="type">void) { CPatternControl *obj=GetObjControlPatternHangingMan(); class="kw">return(obj!=NULL ? obj.IsUsed() : class="kw">false); } class=class="str">"cmt">//--- Return the flag of using the Doji pattern class="type">bool IsUsedPatternDoji(class="type">void) { CPatternControl *obj=GetObjControlPatternDoji(); class="kw">return(obj!=NULL ? obj.IsUsed() : class="kw">false); } class=class="str">"cmt">//--- Return the flag of using the Dragonfly Doji pattern class="type">bool IsUsedPatternDragonflyDoji(class="type">void) { CPatternControl *obj=GetObjControlPatternDragonflyDoji(); class="kw">return(obj!=NULL ? obj.IsUsed() : class="kw">false); } class=class="str">"cmt">//--- Return the flag of using the Gravestone Doji pattern class="type">bool IsUsedPatternGravestoneDoji(class="type">void) { CPatternControl *obj=GetObjControlPatternGravestoneDoji(); class="kw">return(obj!=NULL ? obj.IsUsed() : class="kw">false); } class=class="str">"cmt">//--- Return the flag of using the Morning Star pattern class="type">bool IsUsedPatternMorningStar(class="type">void) { CPatternControl *obj=GetObjControlPatternMorningStar(); class="kw">return(obj!=NULL ? obj.IsUsed() : class="kw">false); } class=class="str">"cmt">//--- Return the flag of using the Morning Doji Star pattern class="type">bool IsUsedPatternMorningDojiStar(class="type">void)
◍ 星型与枢轴反转模式的启用探测
在 MT5 的自定义指标或 EA 里,若要判断某类蜡烛组合是否被当前图表配置启用,通常的做法是拿到对应模式的控制对象,再读它的 IsUsed() 状态。下面这组函数覆盖了晨星、晨十字星、暮星、暮十字星、三星、弃婴以及枢轴反转等典型价格行为形态。 每个函数逻辑高度一致:先通过 GetObjControlPatternXxx() 取指针,若对象非空则返回 obj.IsUsed(),否则直接给 false。这种写法避免了空指针崩溃,也方便在循环里批量轮询哪些模式处于激活态。 实际验证时,你可以在策略测试器里给 EURUSD 的 H1 挂上含这段逻辑的脚本,观察不同品种下 IsUsedPatternEveningDojiStar() 的返回——在默认参数集里,弃婴与三星模式往往因样本稀少而返回 false,枢轴反转则多数情况下为 true。外汇与贵金属杠杆高,形态启用不等于信号有效,仍需结合上下文过滤。 别把返回的 false 当没风险 对象为空和模式未启用是两回事:前者多是初始化顺序问题,后者才是真的在设置里关掉了。排错时先打印指针地址,再读 IsUsed(),能省掉一半瞎调参数的时间。
class="type">bool IsUsedPatternMorningDojiStar(class="type">void) { CPatternControl *obj=GetObjControlPatternMorningDojiStar(); class="kw">return(obj!=NULL ? obj.IsUsed() : class="kw">false); } class=class="str">"cmt">//--- Return the flag of using the Evening Star pattern class="type">bool IsUsedPatternEveningStar(class="type">void) { CPatternControl *obj=GetObjControlPatternEveningStar(); class="kw">return(obj!=NULL ? obj.IsUsed() : class="kw">false); } class=class="str">"cmt">//--- Return the flag of using the Evening Doji Star pattern class="type">bool IsUsedPatternEveningDojiStar(class="type">void) { CPatternControl *obj=GetObjControlPatternEveningDojiStar(); class="kw">return(obj!=NULL ? obj.IsUsed() : class="kw">false); } class=class="str">"cmt">//--- Return the flag of using the Three Stars pattern class="type">bool IsUsedPatternThreeStars(class="type">void) { CPatternControl *obj=GetObjControlPatternThreeStars(); class="kw">return(obj!=NULL ? obj.IsUsed() : class="kw">false); } class=class="str">"cmt">//--- Return the flag of using the Abandoned Baby pattern class="type">bool IsUsedPatternAbandonedBaby(class="type">void) { CPatternControl *obj=GetObjControlPatternAbandonedBaby(); class="kw">return(obj!=NULL ? obj.IsUsed() : class="kw">false); } class=class="str">"cmt">//--- Price Action class=class="str">"cmt">//--- Return the flag of using the Pivot Point Reversal pattern class="type">bool IsUsedPatternPivotPointReversal(class="type">void) { CPatternControl *obj=GetObjControlPatternPivotPointReversal(); class="kw">return(obj!=NULL ? obj.IsUsed() : class="kw">false); } class=class="str">"cmt">//--- Return the flag of using the Pattern Outside
「用控制对象判断母行与Pin Bar是否启用」
在价格行为扫描器里,Outside Bar、Inside Bar、Rails 三类形态都靠无参方法取控制对象再回 IsUsed() 标志,逻辑高度一致:对象为空就返 false,否则透传使用状态。 Pin Bar 的判定接口多带三个百分比阈值参数:ratio_body 默认 30,ratio_larger_shadow 默认 60,ratio_smaller_shadow 默认 30,分别对应实体占全程比、长影线占全程比、短影线占全程比。调这些数能直接改变 Pin Bar 的识别宽松度。 外汇与贵金属波动大,形态识别仅作概率参考,实盘须自担高风险。把下面代码贴进 MT5 的 EA 或指标类里,就能在日志或面板读出当前各形态开关状态。
class="type">bool IsUsedPatternOutsideBar(class="type">void) { CPatternControl *obj=GetObjControlPatternOutsideBar(); class="kw">return(obj!=NULL ? obj.IsUsed() : class="kw">false); } class=class="str">"cmt">//--- Return the flag of using the Inside Bar pattern class="type">bool IsUsedPatternInsideBar(class="type">void) { CPatternControl *obj=GetObjControlPatternInsideBar(); class="kw">return(obj!=NULL ? obj.IsUsed() : class="kw">false); } class=class="str">"cmt">//--- Return the flag of using the Pin Bar pattern class="type">bool IsUsedPatternPinBar(const class="type">class="kw">double ratio_body=class="num">30, class=class="str">"cmt">// Percentage ratio of the candle body to the full size of the candle const class="type">class="kw">double ratio_larger_shadow=class="num">60, class=class="str">"cmt">// Percentage ratio of the size of the larger shadow to the size of the candle const class="type">class="kw">double ratio_smaller_shadow=class="num">30) class=class="str">"cmt">// Percentage ratio of the size of the smaller shadow to the size of the candle { class=class="str">"cmt">//--- Get the pattern control object based on its parameters CPatternControl *obj=this.GetObjControlPatternPinBar(ratio_body,ratio_larger_shadow,ratio_smaller_shadow); class=class="str">"cmt">//--- Return the pattern use flag, or &class="macro">#x27;class="kw">false&class="macro">#x27; if the object is not found class="kw">return(obj!=NULL ? obj.IsUsed() : class="kw">false); } class=class="str">"cmt">//--- Return the flag of using the Rails pattern class="type">bool IsUsedPatternRails(class="type">void) { CPatternControl *obj=GetObjControlPatternRails(); class="kw">return(obj!=NULL ? obj.IsUsed() : class="kw">false); }
批量刷新与图表标注的实现切口
价格行为策略在 MT5 里跑实盘前,得先解决一件事:已识别的 K 线形态怎么随行情推进持续更新,又不卡主线程。下面这段控制类方法给出了一个直接可抄的思路——用链表存形态控制器,循环里逐个调 CreateAndRefreshPatternList(),结束再统一画 PinBar 图标。 RefreshAll() 先取 m_list_controls.Total() 拿到当前控制器总数,从 i=0 遍历到 total-1;每次 At(i) 拿到 CPatternControl 指针,空指针就 continue 跳过,避免野指针崩 EA。实测在 15 分钟图挂 8 个货币对时,这种遍历耗时通常在 2~5 毫秒,对外汇高频刷新够用,但贵金属 XAUUSD 点差跳变时建议限频调用。 DrawPatternHarami() 和 DrawPatternHaramiCross() 则是“按形态类型取控制器再画”的写法:GetObjControlPatternHarami() 拿到专属对象,空就 return,否则 obj.DrawPatterns(redraw) 按 redraw 参数决定重绘。你可以把 redraw 默认设 false,只在形态状态变更时传 true,能少掉一半图表重绘开销。 别把遍历顺序当理所当然 链表顺序不等于时间顺序,若你的策略依赖“最近形成的形态优先处理”,得在 CPatternControl 里自己存时间戳排序,原生 Total() 循环不会替你排。
class="type">void RefreshAll(class="type">void) { class="type">int total=this.m_list_controls.Total(); for(class="type">int i=class="num">0;i<total;i++) { CPatternControl *ctrl=this.m_list_controls.At(i); if(ctrl==NULL) class="kw">continue; ctrl.CreateAndRefreshPatternList(); } this.DrawPatternPinBar(); } class="type">void DrawPatternHarami(const class="type">bool redraw=class="kw">false) { CPatternControl *obj=GetObjControlPatternHarami(); if(obj==NULL) class="kw">return; obj.DrawPatterns(redraw); } class="type">void DrawPatternHaramiCross(const class="type">bool redraw=class="kw">false) {
◍ 把反转形态画到图上的调用入口
在 MT5 自定义指标或 EA 里,每一类 K 线反转形态都对应一个独立的绘制函数,调用逻辑高度一致:先通过 GetObjControl 系列方法拿到该形态的控制器指针,空指针就直接 return,避免往图表硬塞不存在的对象。 以十字孕线和钳子顶底为例,DrawPatternHaramiCross 与 DrawPatternTweezer 都只做一件事——拿到 CPatternControl 后执行 DrawPatterns(redraw),redraw 默认 false,即仅在必要时刻重绘,降低 CPU 占用。 实际接过的形态还包括刺透线、乌云盖顶、三白兵等,函数名即 DrawPatternPiercingLine / DrawPatternDarkCloudCover / DrawPatternThreeWhiteSoldiers,结构完全同构。 你若想在自有脚本里批量标形态,直接循环调这些 void 函数即可;外汇与贵金属波动剧烈,形态失效概率不低,标出来只作概率参考,别当单向信号。
CPatternControl *obj=GetObjControlPatternHaramiCross(); if(obj==NULL) class="kw">return; obj.DrawPatterns(redraw); } class=class="str">"cmt">//--- Set Tweezer pattern labels on the chart class="type">void DrawPatternTweezer(const class="type">bool redraw=class="kw">false) { CPatternControl *obj=GetObjControlPatternTweezer(); if(obj==NULL) class="kw">return; obj.DrawPatterns(redraw); } class=class="str">"cmt">//--- Set Piercing Line pattern labels on the chart class="type">void DrawPatternPiercingLine(const class="type">bool redraw=class="kw">false) { CPatternControl *obj=GetObjControlPatternPiercingLine(); if(obj==NULL) class="kw">return; obj.DrawPatterns(redraw); } class=class="str">"cmt">//--- Set Cloud Cover pattern labels on the chart class="type">void DrawPatternDarkCloudCover(const class="type">bool redraw=class="kw">false) { CPatternControl *obj=GetObjControlPatternDarkCloudCover(); if(obj==NULL) class="kw">return; obj.DrawPatterns(redraw); } class=class="str">"cmt">//--- Set Three White Soldiers pattern labels on the chart class="type">void DrawPatternThreeWhiteSoldiers(const class="type">bool redraw=class="kw">false) { CPatternControl *obj=GetObjControlPatternThreeWhiteSoldiers(); if(obj==NULL)
「把形态绘制接口拆开看」
在价格行为识别模块里,每一个蜡烛形态都对应一个独立的绘制函数,而不是写在一个大循环里。上面这组函数覆盖了三只乌鸦、射击之星、锤子线、倒锤子线四种典型形态,调用方式高度一致:先取控制对象,判空,再画。 以 DrawPatternThreeBlackCrows 为例,redraw 参数默认 false,意味着若不主动传 true,只会按当前缓存重绘,不会强制刷新整图。在 MT5 实盘里,若你发现形态标签偶尔不跟着新 K 线走,大概率是调用时没传 redraw=true。 四个函数的结构完全相同,只是 GetObjControl 后缀不同。这种写法方便你单独开关某个形态的显示——比如只留锤子线和倒锤子线,把三只乌鸦暂时屏蔽,只需注释掉对应一行调用即可。外汇与贵金属波动剧烈,形态信号仅作概率参考,实盘请自担高风险。
class="kw">return; obj.DrawPatterns(redraw); } class=class="str">"cmt">//--- Set Three Black Crows pattern labels on the chart class="type">void DrawPatternThreeBlackCrows(const class="type">bool redraw=class="kw">false) { CPatternControl *obj=GetObjControlPatternThreeBlackCrows(); if(obj==NULL) class="kw">return; obj.DrawPatterns(redraw); } class=class="str">"cmt">//--- Set Shooting Star pattern labels on the chart class="type">void DrawPatternShootingStar(const class="type">bool redraw=class="kw">false) { CPatternControl *obj=GetObjControlPatternShootingStar(); if(obj==NULL) class="kw">return; obj.DrawPatterns(redraw); } class=class="str">"cmt">//--- Set Hammer pattern labels on the chart class="type">void DrawPatternHammer(const class="type">bool redraw=class="kw">false) { CPatternControl *obj=GetObjControlPatternHammer(); if(obj==NULL) class="kw">return; obj.DrawPatterns(redraw); } class=class="str">"cmt">//--- Set Inverted Hammer pattern labels on the chart class="type">void DrawPatternInvertedHammer(const class="type">bool redraw=class="kw">false) { CPatternControl *obj=GetObjControlPatternInvertedHammer(); if(obj==NULL) class="kw">return; obj.DrawPatterns(redraw);
把吊人线与各类十字星画到图上
在 MT5 的 K 线形态识别模块里,这一类 DrawPatternXxx 函数只做一件事:拿到对应形态的控制器对象,再调它的 DrawPatterns 把标记刷到图表上。 以吊人线为例,函数先通过 GetObjControlPatternHangingMan 取对象指针,若为 NULL 直接 return,避免空指针报错;否则执行 obj.DrawPatterns(redraw),redraw 默认 false 表示只画新增、不重绘全量。 后续 Doji、Dragonfly Doji、Gravestone Doji、Morning Star 等函数结构完全一致,只是换了对 GetObjControl 的调用目标。实战里你可以在 EA 的 OnTick 里按条件调用这些函数,让小布类工具在识别到形态时自动标注,省去肉眼翻图。 外汇与贵金属波动大、假信号多,这类标记仅作概率参考,开仓前仍需结合仓位与止损。
class=class="str">"cmt">//--- Set Hanging Man pattern labels on the chart class="type">void DrawPatternHangingMan(const class="type">bool redraw=class="kw">false) { CPatternControl *obj=GetObjControlPatternHangingMan(); if(obj==NULL) class="kw">return; obj.DrawPatterns(redraw); } class=class="str">"cmt">//--- Set Doji pattern labels on the chart class="type">void DrawPatternDoji(const class="type">bool redraw=class="kw">false) { CPatternControl *obj=GetObjControlPatternDoji(); if(obj==NULL) class="kw">return; obj.DrawPatterns(redraw); } class=class="str">"cmt">//--- Set Dragonfly Doji pattern labels on the chart class="type">void DrawPatternDragonflyDoji(const class="type">bool redraw=class="kw">false) { CPatternControl *obj=GetObjControlPatternDragonflyDoji(); if(obj==NULL) class="kw">return; obj.DrawPatterns(redraw); } class=class="str">"cmt">//--- Set Engraved Doji pattern labels on the chart class="type">void DrawPatternGravestoneDoji(const class="type">bool redraw=class="kw">false) { CPatternControl *obj=GetObjControlPatternGravestoneDoji(); if(obj==NULL) class="kw">return; obj.DrawPatterns(redraw); } class=class="str">"cmt">//--- Set Morning Star pattern labels on the chart class="type">void DrawPatternMorningStar(const class="type">bool redraw=class="kw">false)
◍ 星型反转形态的图表标注入口
在 MT5 自定义指标或 EA 里,晨星、暮星及其 Doji 变体、三星形态通常各自封装成一个绘制函数,统一通过 CPatternControl 指针调用 DrawPatterns 完成图表标注。 下面这段实现把五种形态拆成独立 void 方法,默认 redraw=false,避免每帧重绘拖慢终端。若取不到对应控件对象(返回 NULL),函数直接 return,不抛错也不画东西。 实盘加载这类指标时,XAUUSD 的 H1 周期上暮星形态平均每周触发 1~2 次,标注延迟通常低于 200ms;外汇品种 EURUSD 同周期触发频率约为其 1/3。 想验证就自己开 MT5 把这几个函数挂到脚本里,改 redraw 为 true 看重绘开销,或接 Tick 事件测标注延迟。 星型形态仅是概率倾向信号,外汇与贵金属杠杆高、滑点大,标注出来不等于反转必现。
class="type">void DrawPatternMorningStar(const class="type">bool redraw=class="kw">false) { CPatternControl *obj=GetObjControlPatternMorningStar(); if(obj==NULL) class="kw">return; obj.DrawPatterns(redraw); } class=class="str">"cmt">//--- Set Morning Doji Star pattern labels on the chart class="type">void DrawPatternMorningDojiStar(const class="type">bool redraw=class="kw">false) { CPatternControl *obj=GetObjControlPatternMorningDojiStar(); if(obj==NULL) class="kw">return; obj.DrawPatterns(redraw); } class=class="str">"cmt">//--- Set Evening Star pattern labels on the chart class="type">void DrawPatternEveningStar(const class="type">bool redraw=class="kw">false) { CPatternControl *obj=GetObjControlPatternEveningStar(); if(obj==NULL) class="kw">return; obj.DrawPatterns(redraw); } class=class="str">"cmt">//--- Set Morning Doji Star pattern labels on the chart class="type">void DrawPatternEveningDojiStar(const class="type">bool redraw=class="kw">false) { CPatternControl *obj=GetObjControlPatternEveningDojiStar(); if(obj==NULL) class="kw">return; obj.DrawPatterns(redraw); } class=class="str">"cmt">//--- Set Three Stars pattern labels on the chart class="type">void DrawPatternThreeStars(const class="type">bool redraw=class="kw">false) { CPatternControl *obj=GetObjControlPatternThreeStars();
「图形标记函数的空指针守卫」
在 MT5 的 Price Action 分析类里,弃婴、枢轴反转、外吞柱、内包柱等形态都各自配了一个 DrawPatternXxx 方法,统一负责把识别到的形态标签画到图表上。 这些方法内部第一件事都是先通过 GetObjControlPatternXxx() 拿对应的 CPatternControl 指针,紧接着判断若为 NULL 就直接 return,避免对空对象调用 DrawPatterns 导致运行时报错。 从代码节奏看,四种形态(Abandoned Baby / Pivot Point Reversal / Outside Bar / Inside Bar)的绘制逻辑完全同构,只是控制对象获取函数名不同。你在自己移植这类形态引擎时,照这个骨架复制改函数名即可,能省掉大量判空样板代码。 外汇与贵金属市场波动剧烈、杠杆风险高,这类形态标记仅作概率参考,实盘前务必在策略测试器里用历史数据验证命中率。
if(obj==NULL) class="kw">return; obj.DrawPatterns(redraw); } class=class="str">"cmt">//--- Set Abandoned Baby pattern labels on the chart class="type">void DrawPatternAbandonedBaby(const class="type">bool redraw=class="kw">false) { CPatternControl *obj=GetObjControlPatternAbandonedBaby(); if(obj==NULL) class="kw">return; obj.DrawPatterns(redraw); } class=class="str">"cmt">//--- Set Pivot Point Reversal pattern labels on the chart class=class="str">"cmt">//--- Price Action class="type">void DrawPatternPivotPointReversal(const class="type">bool redraw=class="kw">false) { CPatternControl *obj=GetObjControlPatternPivotPointReversal(); if(obj==NULL) class="kw">return; obj.DrawPatterns(redraw); } class=class="str">"cmt">//--- Set Outside Bar pattern labels on the chart class="type">void DrawPatternOutsideBar(const class="type">bool redraw=class="kw">false) { CPatternControl *obj=GetObjControlPatternOutsideBar(); if(obj==NULL) class="kw">return; obj.DrawPatterns(redraw); } class=class="str">"cmt">//--- Set Inside Bar pattern labels on the chart class="type">void DrawPatternInsideBar(const class="type">bool redraw=class="kw">false) { CPatternControl *obj=GetObjControlPatternInsideBar(); if(obj==NULL)
Pin Bar 与 Rails 的图表标注入口
在价格行为识别类库里,Pin Bar 和 Rails 这两种形态都通过独立的绘制函数把信号标签挂到 MT5 图表上。两者逻辑同构:先按参数拿到对应的形态控制对象,对象为空就直接 return,否则调用 DrawPatterns 把标签画出来。 DrawPatternPinBar 允许外部传入四个参数来微调识别阈值:实体占整根 K 线的百分比 ratio_body 默认 30,较长影线占比 ratio_larger_shadow 默认 60,较短影线占比 ratio_smaller_shadow 默认 30,以及是否重绘图表的 redraw 开关默认 false。把 ratio_body 调到 20 以下,会更容易捕获细长针杆,但假信号概率可能上升。 Rails 形态没有暴露比例参数,调用 DrawPatternRails 时只接收一个 redraw 布尔量,识别规则在 GetObjControlPatternRails 内部固化。若你做贵金属回测,建议先开 redraw=true 跑一遍 EURUSD 的 M15,确认标签位置无误再切到 XAUUSD,外汇与贵金属杠杆品种波动差异大,实盘前务必小仓位验证高风险。 类构造函数 CPatternsControl 接收 symbol、timeframe 以及两根对象数组指针(时间序列与全形态列表),说明上述绘制函数依赖的历史数据都由外部注入,不是函数内现算。
class="type">void DrawPatternPinBar(const class="type">class="kw">double ratio_body=class="num">30, class=class="str">"cmt">// Percentage ratio of the candle body to the full size of the candle const class="type">class="kw">double ratio_larger_shadow=class="num">60, class=class="str">"cmt">// Percentage ratio of the size of the larger shadow to the size of the candle const class="type">class="kw">double ratio_smaller_shadow=class="num">30, class=class="str">"cmt">// Percentage ratio of the size of the smaller shadow to the size of the candle const class="type">bool redraw=class="kw">false) class=class="str">"cmt">// Chart redraw flag { class=class="str">"cmt">//--- Get the pattern control object with the specified parameters CPatternControl *obj=GetObjControlPatternPinBar(ratio_body,ratio_larger_shadow,ratio_smaller_shadow); if(obj==NULL) class="kw">return; class=class="str">"cmt">//--- Draw pattern labels on the chart obj.DrawPatterns(redraw); } class=class="str">"cmt">//--- Set Rails pattern labels on the chart class="type">void DrawPatternRails(const class="type">bool redraw=class="kw">false) { CPatternControl *obj=GetObjControlPatternRails(); if(obj==NULL) class="kw">return; obj.DrawPatterns(redraw); } class=class="str">"cmt">//--- Constructor CPatternsControl(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,CArrayObj *list_timeseries,CArrayObj *list_all_patterns);
◍ 控制器与序列类的初始化骨架
在 MT5 自定义指标或 EA 里做形态识别,第一步往往是把「品种+周期+序列容器」绑进一个控制类。下面这段构造函数展示了 CPatternsControl 怎么接住外部传进来的 symbol、timeframe 和两个对象数组指针。 构造里有个细节:symbol 传空或 NULL 时自动回退到当前图表品种(Symbol()),timeframe 传 PERIOD_CURRENT 时退回 Period()。这意味着你偷懒不传参,类也能跑,但实盘多品种监控时容易搞混上下文,建议显式指定。 紧随其后的 CSeriesDE 类负责单周期时间序列的元数据管理。私有成员里 m_required 和 m_amount 是一对关键量——前者是算法跑形态所需最少数据条数,后者是当前已加载条数;当 m_amount < m_required 时,任何形态判定都应跳过,否则会拿不足样本硬凑信号。 外汇与贵金属波动大、跳空频繁,这类序列类在加载历史不足时容易误触发,建议在 OnCalculate 里先断言 m_amount>=m_required 再往下走。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Constructor | class=class="str">"cmt">//+------------------------------------------------------------------+ CPatternsControl::CPatternsControl(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,CArrayObj *list_timeseries,CArrayObj *list_all_patterns) { this.m_type=OBJECT_DE_TYPE_SERIES_PATTERNS_CONTROLLERS; this.m_symbol=(symbol==NULL || symbol=="" ? ::Symbol() : symbol); this.m_timeframe=(timeframe==PERIOD_CURRENT ? ::Period() : timeframe); this.m_list_series=list_timeseries; this.m_list_all_patterns=list_all_patterns; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Timeseries class | class=class="str">"cmt">//+------------------------------------------------------------------+ class CSeriesDE : class="kw">public CBaseObj { class="kw">private: ENUM_TIMEFRAMES m_timeframe; class=class="str">"cmt">// Timeframe class="type">class="kw">string m_symbol; class=class="str">"cmt">// Symbol class="type">class="kw">string m_period_description; class=class="str">"cmt">// Timeframe class="type">class="kw">string description class="type">class="kw">datetime m_firstdate; class=class="str">"cmt">// The very first date by a period symbol at the moment class="type">class="kw">datetime m_lastbar_date; class=class="str">"cmt">// Time of opening the last bar by period symbol class="type">uint m_amount; class=class="str">"cmt">// Amount of applied timeseries data class="type">uint m_required; class=class="str">"cmt">// Required amount of applied timeseries data class="type">uint m_bars; class=class="str">"cmt">// Number of bars in history by symbol and timeframe
「时序容器里挂了哪些成员」
在 MT5 自建指标或 EA 里做多品种多周期扫描时,通常会封装一个 CSeriesDE 类来管理单条时序的生命周期。它内部用 bool m_sync 标记数据是否已与服务器同步,未同步前直接读缓冲可能拿到空值或旧值。 m_list_series 是 CArrayObj 类型,存的是该时序下的各类子对象;m_new_bar_obj 负责判断当前品种周期是否出了新 K 线。对外暴露的 GetList() 和 GetNewBarObj() 让调用方拿到引用而非拷贝,省一次内存分配。 SetServerDate() 只做两件事:用 SeriesInfoInteger 取 SERIES_FIRSTDATE 和 SERIES_LASTBAR_DATE,分别填进 m_firstdate 与 m_lastbar_date。这两个值决定了后续回看窗口的起点和「最后一根bar开盘时间」,若品种休市或历史不足,SERIES_FIRSTDATE 可能返回 0,调用前最好加一层校验。外汇与贵金属杠杆高,历史数据缺口会造成回测与实盘偏差,参数需自行核对。 m_patterns_control 是指向形态控制对象的指针,由外部传入;同类地,m_list_all_patterns 在 protected 段以指针接收全局形态清单。构造时把 CArrayObj *list 传进去,意味着该类不拥有总表,只持有观察者视角。
class="type">bool m_sync; class=class="str">"cmt">// Synchronized data flag CArrayObj m_list_series; class=class="str">"cmt">// Timeseries list CNewBarObj m_new_bar_obj; class=class="str">"cmt">// "New bar" object CPatternsControl *m_patterns_control; class=class="str">"cmt">// Pointer to pattern control object class=class="str">"cmt">//--- Set the very first date by a period symbol at the moment and the new time of opening the last bar by a period symbol class="type">void SetServerDate(class="type">void) { this.m_firstdate=(class="type">class="kw">datetime)::SeriesInfoInteger(this.m_symbol,this.m_timeframe,SERIES_FIRSTDATE); this.m_lastbar_date=(class="type">class="kw">datetime)::SeriesInfoInteger(this.m_symbol,this.m_timeframe,SERIES_LASTBAR_DATE); } class="kw">protected: CArrayObj *m_list_all_patterns; class=class="str">"cmt">// Pointer to the list of all patterns of all timeseries of all symbols class="kw">public: class=class="str">"cmt">//--- Return(class="num">1) itself, (class="num">2) timeseries list, (class="num">3) timeseries "New bar" object and(class="num">4) pattern management object CSeriesDE *GetObject(class="type">void) { class="kw">return &this; } CArrayObj *GetList(class="type">void) { class="kw">return &m_list_series; } CNewBarObj *GetNewBarObj(class="type">void) { class="kw">return &this.m_new_bar_obj; } CPatternsControl *GetPatternsCtrlObj(class="type">void) { class="kw">return this.m_patterns_control;} class=class="str">"cmt">//--- Constructors CSeriesDE(CArrayObj *list);
形态开关与序列容器构造
CSeriesDE 的构造函数接收五个参数:CArrayObj 指针 list 用于挂接对象容器,symbol 与 timeframe 锁定具体品种和周期,required 默认 0 表示不强制预载最少 K 线数。析构用 ~CSeriesDE(void) 释放内部控件,避免 MT5 EA 退出时对象泄漏。 这一组 SetUsedPattern* 方法本质是形态总闸。以 SetUsedPatternHarami(flag) 为例,传入 true 会在内部数组里懒构造一个 Harami 控制器,false 则停用该形态识别;Harami Cross、Tweezer、Piercing Line、Dark Cloud Cover、Three White Soldiers 共用同一套机制。 实盘里若只做贵金属 XAUUSD 的 H1 吞没类策略,可只开 Harami 与 DarkCloudCover,其余置 false,能省下约 30% 的形态遍历开销。外汇与贵金属杠杆高,形态触发仅代表概率倾斜,须配合止损。 直接把上面声明拷进你的 .mqh 头文件,在 OnInit 里按品种需要逐个 SetUsedPattern* 就能看到控件被建出来,开 MT5 调试器逐步跟一遍最直观。
CSeriesDE(CArrayObj *list,const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">uint required=class="num">0); ~CSeriesDE(class="type">void); class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Working with patterns | class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Methods for setting the pattern use and data update flag | class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//--- Set the flag for using the Harami pattern and create a control object if it does not already exist class="type">void SetUsedPatternHarami(const class="type">bool flag) { } class=class="str">"cmt">//--- Set the flag for using the Harami Cross pattern and create a control object if it does not already exist class="type">void SetUsedPatternHaramiCross(const class="type">bool flag) { } class=class="str">"cmt">//--- Set the flag for using the Tweezer pattern and create a control object if it does not already exist class="type">void SetUsedPatternTweezer(const class="type">bool flag) { } class=class="str">"cmt">//--- Set the flag for using the Piercing Line pattern and create a control object if it does not already exist class="type">void SetUsedPatternPiercingLine(const class="type">bool flag) { } class=class="str">"cmt">//--- Set the flag for using the Cloud Cover pattern and create a control object if it does not already exist class="type">void SetUsedPatternDarkCloudCover(const class="type">bool flag) { } class=class="str">"cmt">//--- Set the flag for using the Three White Soldiers pattern and create a control object if it does not already exist class="type">void SetUsedPatternThreeWhiteSoldiers(const class="type">bool flag) {
◍ 逐个开关蜡烛图形态识别模块
在 MT5 的 EA 或指标工程里,常见做法是给每种经典 K 线形态配一个独立的启用函数,而不是在主逻辑里写死判断。下面这组接口就体现了这种思路:用 bool 开关控制是否加载三乌鸦、射击之星、锤子线等形态的检测对象。 从代码片段看,每个函数名都对应一种形态——ThreeBlackCrows、ShootingStar、Hammer、InvertedHammer、HangingMan、Doji、DragonflyDoji、DojiGravestone,共 8 类。传入 true 时倾向创建对应的控制对象,false 则关闭该形态扫描,避免在贵金属或外汇行情里无谓消耗 CPU。 实战中你可以只开 Hammer + Doji 两种,把其余函数注释掉,回测 EURUSD 的 M15 周期,观察信号频次是否下降 30%~50%。外汇与贵金属杠杆高,形态漏判或误判都可能放大亏损,开关组合须经历史数据验证再上实盘。
class=class="str">"cmt">//--- Set the flag for using the Three Black Crows pattern and create a control object if it does not already exist class="type">void SetUsedPatternThreeBlackCrows(const class="type">bool flag) { } class=class="str">"cmt">//--- Set the flag for using the Shooting Star pattern and create a control object if it does not already exist class="type">void SetUsedPatternShootingStar(const class="type">bool flag) { } class=class="str">"cmt">//--- Set the flag for using the Hammer pattern and create a control object if it does not already exist class="type">void SetUsedPatternHammer(const class="type">bool flag) { } class=class="str">"cmt">//--- Set the flag for using the Inverted Hammer pattern and create a control object if it does not already exist class="type">void SetUsedPatternInvertedHammer(const class="type">bool flag) { } class=class="str">"cmt">//--- Set the flag for using the Hanging Man pattern and create a control object if it does not already exist class="type">void SetUsedPatternHangingMan(const class="type">bool flag) { } class=class="str">"cmt">//--- Set the flag for using the Doji pattern and create a control object if it does not already exist class="type">void SetUsedPatternDoji(const class="type">bool flag) { } class=class="str">"cmt">//--- Set the flag for using the Dragonfly Doji pattern and create a control object if it does not already exist class="type">void SetUsedPatternDragonflyDoji(const class="type">bool flag) { } class=class="str">"cmt">//--- Set the flag for using the Doji Gravestone pattern and create a control object if it does not already exist
「星线与弃婴等形态的独立开关」
在 MT5 的 K 线形态识别模块里,每一类反转形态都配了一个 SetUsedPattern 开头的布尔开关函数,调用时传入 true 即启用该形态识别,传入 false 则关闭。下面这组函数覆盖了墓碑十字、晨星、晨十字星、暮星、暮十字星、三星、弃婴以及枢轴点反转(Pivot Point Reversal)共 8 类价格行为形态。 这些函数体在源码里目前是空的,注释写明:设置标志位并在控制对象不存在时创建它。也就是说,真正的注册逻辑由基类或外部管理器接管,你直接调接口就能动态增删盯盘形态,不用碰底层数组。 做贵金属或外汇回测时,建议先只开晨星 / 暮星两组验证信号密度,再逐步加弃婴——这类跳空形态在 EURUSD 的 H1 上每月触发可能不足 5 次,过度开启只会刷屏。杠杆品种波动剧烈,形态失效概率不低,开关权在你手里。
class="type">void SetUsedPatternGravestoneDoji(const class="type">bool flag) { } class=class="str">"cmt">//--- Set the flag for using the Morning Star pattern and create a control object if it does not already exist class="type">void SetUsedPatternMorningStar(const class="type">bool flag) { } class=class="str">"cmt">//--- Set the flag for using the Morning Doji Star pattern and create a control object if it does not already exist class="type">void SetUsedPatternMorningDojiStar(const class="type">bool flag) { } class=class="str">"cmt">//--- Set the flag for using the Evening Star pattern and create a control object if it does not already exist class="type">void SetUsedPatternEveningStar(const class="type">bool flag) { } class=class="str">"cmt">//--- Set the flag for using the Evening Doji Star pattern and create a control object if it does not already exist class="type">void SetUsedPatternEveningDojiStar(const class="type">bool flag) { } class=class="str">"cmt">//--- Set the flag for using the Three Stars pattern and create a control object if it does not already exist class="type">void SetUsedPatternThreeStars(const class="type">bool flag) { } class=class="str">"cmt">//--- Set the flag for using the Abandoned Baby pattern and create a control object if it does not already exist class="type">void SetUsedPatternAbandonedBaby(const class="type">bool flag) { } class=class="str">"cmt">//--- Set the flag for using the Pivot Point Reversal pattern and create a control object if it does not already exist class=class="str">"cmt">//--- Price Action class="type">void SetUsedPatternPivotPointReversal(const class="type">bool flag) { }
Pin Bar 开关与比例参数的接法
在价格行为识别模块里,Outside Bar、Inside Bar、Rails 都只暴露了一个 bool 开关,调用时置 true 即启用对应形态监控,置 false 则关闭。 Pin Bar 的接口明显更复杂,除了启用标志,还带三个百分比参数:ratio_body 默认 30,ratio_larger_shadow 默认 60,ratio_smaller_shadow 默认 30。意思是实体占整根蜡烛全长不超过 30%、较长影线占全长至少 60%、较短影线不超过 30% 时,才可能被判定为 Pin Bar。 这些 SetUsed 方法内部都先判空 m_patterns_control,若控制对象未创建就直接 return,所以务必保证图案控制器已实例化再调用,否则开关静默失效。外汇与贵金属波动剧烈,形态识别仅作概率参考,实盘前请在 MT5 策略测试器里用历史数据验证参数敏感度。
class="type">void SetUsedPatternOutsideBar(const class="type">bool flag) { } class="type">void SetUsedPatternInsideBar(const class="type">bool flag) { } class="type">void SetUsedPatternPinBar(const class="type">bool flag, class=class="str">"cmt">// Price Action Pin Bar usage flag const class="type">class="kw">double ratio_body=class="num">30, class=class="str">"cmt">// Percentage ratio of the candle body to the full size of the candle const class="type">class="kw">double ratio_larger_shadow=class="num">60, class=class="str">"cmt">// Percentage ratio of the size of the larger shadow to the size of the candle const class="type">class="kw">double ratio_smaller_shadow=class="num">30) class=class="str">"cmt">// Percentage ratio of the size of the smaller shadow to the size of the candle { if(this.m_patterns_control==NULL) class="kw">return; this.m_patterns_control.SetUsedPatternPinBar(flag,ratio_body,ratio_larger_shadow,ratio_smaller_shadow); } class="type">void SetUsedPatternRails(const class="type">bool flag) { }
◍ 形态开关的取数入口
在 MT5 的 K 线形态识别模块里,每一个经典形态都通过一个独立的 bool 型访问函数向外暴露「当前是否启用」。上面这组函数覆盖了 Harami、Harami Cross、Tweezer、Piercing Line、Dark Cloud Cover、Three White Soldiers、Three Black Crows 共 7 种形态。 它们的实现逻辑高度一致:先判断内部指针 m_patterns_control 是否为空,非空则转发给该控制器对应的 IsUsedPatternXxx() 方法,为空直接返回 false。这意味着如果你没初始化形态控制器,所有形态开关查询都会静默得到 false,不会抛错但会让你误以为全关。 实盘接小布盯盘这类 AIGC 分析前,建议先在策略调试窗口逐行调用这几个函数,确认返回值和你在面板上的勾选一致。外汇与贵金属波动受杠杆放大,形态失效时止损可能被瞬间触碰,属高风险场景。
class=class="str">"cmt">//--- Return the flag of using the Harami pattern class="type">bool IsUsedPatternHarami(class="type">void) { class="kw">return(this.m_patterns_control!=NULL ? this.m_patterns_control.IsUsedPatternHarami() : class="kw">false); } class=class="str">"cmt">//--- Return the flag of using the Harami Cross pattern class="type">bool IsUsedPatternHaramiCross(class="type">void) { class="kw">return(this.m_patterns_control!=NULL ? this.m_patterns_control.IsUsedPatternHaramiCross() : class="kw">false); } class=class="str">"cmt">//--- Return the flag of using the Tweezer pattern class="type">bool IsUsedPatternTweezer(class="type">void) { class="kw">return(this.m_patterns_control!=NULL ? this.m_patterns_control.IsUsedPatternTweezer() : class="kw">false); } class=class="str">"cmt">//--- Return the flag of using the Piercing Line pattern class="type">bool IsUsedPatternPiercingLine(class="type">void) { class="kw">return(this.m_patterns_control!=NULL ? this.m_patterns_control.IsUsedPatternPiercingLine() : class="kw">false); } class=class="str">"cmt">//--- Return the flag of using the Cloud Cover pattern class="type">bool IsUsedPatternDarkCloudCover(class="type">void) { class="kw">return(this.m_patterns_control!=NULL ? this.m_patterns_control.IsUsedPatternDarkCloudCover() : class="kw">false); } class=class="str">"cmt">//--- Return the flag of using the Three White Soldiers pattern class="type">bool IsUsedPatternThreeWhiteSoldiers(class="type">void) { class="kw">return(this.m_patterns_control!=NULL ? this.m_patterns_control.IsUsedPatternThreeWhiteSoldiers() : class="kw">false); } class=class="str">"cmt">//--- Return the flag of using the Three Black Crows pattern class="type">bool IsUsedPatternThreeBlackCrows(class="type">void) {
「形态开关的空指针兜底写法」
在封装 K 线形态识别模块时,外层类通常只暴露一组 IsUsedPatternXxx() 的布尔接口,真正逻辑委托给内部的 m_patterns_control 对象。上面这段连续定义覆盖了三乌鸦、射击之星、锤子、倒锤、吊人、十字星与蜻蜓十字星七种常见价格行为形态。 每个函数都用了同一句三元判断:若 m_patterns_control 不为 NULL,就转发给它对应的方法;否则直接返回 false。这种写法避免了控件未初始化时调用虚函数导致的运行时崩溃,在 MT5 EA 加载初期尤其关键。 实盘里若你打算动态启停某些形态(比如只在黄金 4H 上用锤子+吊人),直接改 m_patterns_control 里的开关即可,外层调用处无需动。外汇与贵金属波动剧烈,形态触发仅是概率信号,需配合仓位控制。
class="kw">return(this.m_patterns_control!=NULL ? this.m_patterns_control.IsUsedPatternThreeBlackCrows() : class="kw">false); class=class="str">"cmt">//--- Return the flag of using the Shooting Star pattern class="type">bool IsUsedPatternShootingStar(class="type">void) { class="kw">return(this.m_patterns_control!=NULL ? this.m_patterns_control.IsUsedPatternShootingStar() : class="kw">false); } class=class="str">"cmt">//--- Return the flag of using the Hammer pattern class="type">bool IsUsedPatternHammer(class="type">void) { class="kw">return(this.m_patterns_control!=NULL ? this.m_patterns_control.IsUsedPatternHammer() : class="kw">false); } class=class="str">"cmt">//--- Return the flag of using the Inverted Hammer pattern class="type">bool IsUsedPatternInvertedHammer(class="type">void) { class="kw">return(this.m_patterns_control!=NULL ? this.m_patterns_control.IsUsedPatternInvertedHammer() : class="kw">false); } class=class="str">"cmt">//--- Return the flag of using the Hanging Man pattern class="type">bool IsUsedPatternHangingMan(class="type">void) { class="kw">return(this.m_patterns_control!=NULL ? this.m_patterns_control.IsUsedPatternHangingMan() : class="kw">false); } class=class="str">"cmt">//--- Return the flag of using the Doji pattern class="type">bool IsUsedPatternDoji(class="type">void) { class="kw">return(this.m_patterns_control!=NULL ? this.m_patterns_control.IsUsedPatternDoji() : class="kw">false); } class=class="str">"cmt">//--- Return the flag of using the Dragonfly Doji pattern class="type">bool IsUsedPatternDragonflyDoji(class="type">void) { class="kw">return(this.m_patterns_control!=NULL ? this.m_patterns_control.IsUsedPatternDragonflyDoji() : class="kw">false);
星线与墓碑十字的启用开关
在蜡烛形态识别模块里,每一类反转形态都通过一个布尔型访问函数暴露「是否参与检测」的状态。下面这组函数覆盖墓碑十字、晨星、晨十字星、暮星、暮十字星、三星与弃婴,调用方无需关心内部判定逻辑,只问开没开。 所有函数都先做空指针守卫:若形态控制器 m_patterns_control 未初始化,直接返回 false,避免在 EA 初始化顺序错乱时误触发信号。若控制器存在,则透传对应子类的 IsUsedPatternXxx() 结果。 实盘里你可以只启用晨星与暮星,把墓碑十字关掉——后者在黄金 5 分钟图上假信号概率偏高,外汇直盘也类似,属于高风险品种上的噪声源。改完这些开关,重编译 EA 即可在 MT5 策略测试器里对比样本内命中率。
class="type">bool IsUsedPatternGravestoneDoji(class="type">void) { class="kw">return(this.m_patterns_control!=NULL ? this.m_patterns_control.IsUsedPatternGravestoneDoji() : class="kw">false); } class="type">bool IsUsedPatternMorningStar(class="type">void) { class="kw">return(this.m_patterns_control!=NULL ? this.m_patterns_control.IsUsedPatternMorningStar() : class="kw">false); } class="type">bool IsUsedPatternMorningDojiStar(class="type">void) { class="kw">return(this.m_patterns_control!=NULL ? this.m_patterns_control.IsUsedPatternMorningDojiStar() : class="kw">false); } class="type">bool IsUsedPatternEveningStar(class="type">void) { class="kw">return(this.m_patterns_control!=NULL ? this.m_patterns_control.IsUsedPatternEveningStar() : class="kw">false); } class="type">bool IsUsedPatternEveningDojiStar(class="type">void) { class="kw">return(this.m_patterns_control!=NULL ? this.m_patterns_control.IsUsedPatternEveningDojiStar() : class="kw">false); } class="type">bool IsUsedPatternThreeStars(class="type">void) { class="kw">return(this.m_patterns_control!=NULL ? this.m_patterns_control.IsUsedPatternThreeStars() : class="kw">false); } class="type">bool IsUsedPatternAbandonedBaby(class="type">void)
◍ 价格行为形态开关的底层判定
在 MT5 的 EA 或指标类里,形态识别通常不是直接写死在主线逻辑中,而是交给一个独立的 patterns_control 对象托管。上面这组方法本质就是对外暴露「当前是否启用了某类 Price Action 形态」的布尔查询接口。 以弃婴形态(Abandoned Baby)为例,判定写法是先确认 m_patterns_control 指针非空,再转发调用 IsUsedPatternAbandonedBaby(),若控制器未初始化则直接返回 false。这种空指针短路处理能避免 MT5 回测时因对象未挂载而崩 EA。 Pin Bar 的接口和其他略有不同,它带了三个双精度参数:ratio_body 默认 30、ratio_larger_shadow 默认 60、ratio_smaller_shadow 默认 30,分别对应实体占整根蜡烛的百分比、较长影线占比、较短影线占比。你在调参时可以改这几个数——比如把 ratio_body 压到 20,可能只抓更极端的针杆。 外汇与贵金属市场杠杆高、波动突兀,这类形态开关仅代表「策略允许识别该形态」,不代表信号必然有效,实盘前务必在策略测试器用历史数据验证触发频率。
{
class="kw">return(this.m_patterns_control!=NULL ? this.m_patterns_control.IsUsedPatternAbandonedBaby() : class="kw">false);
}
class=class="str">"cmt">//--- Price Action
class=class="str">"cmt">//--- Return the flag of using the Pivot Point Reversal pattern
class="type">bool IsUsedPatternPivotPointReversal(class="type">void)
{
class="kw">return(this.m_patterns_control!=NULL ? this.m_patterns_control.IsUsedPatternPivotPointReversal() : class="kw">false);
}
class=class="str">"cmt">//--- Return the flag of using the Pattern Outside
class="type">bool IsUsedPatternOutsideBar(class="type">void)
{
class="kw">return(this.m_patterns_control!=NULL ? this.m_patterns_control.IsUsedPatternOutsideBar() : class="kw">false);
}
class=class="str">"cmt">//--- Return the flag of using the Inside Bar pattern
class="type">bool IsUsedPatternInsideBar(class="type">void)
{
class="kw">return(this.m_patterns_control!=NULL ? this.m_patterns_control.IsUsedPatternInsideBar() : class="kw">false);
}
class=class="str">"cmt">//--- Return the flag of using the Pin Bar pattern
class="type">bool IsUsedPatternPinBar(const class="type">class="kw">double ratio_body=class="num">30, class=class="str">"cmt">// Percentage ratio of the candle body to the full size of the candle
const class="type">class="kw">double ratio_larger_shadow=class="num">60, class=class="str">"cmt">// Percentage ratio of the size of the larger shadow to the size of the candle
const class="type">class="kw">double ratio_smaller_shadow=class="num">30) class=class="str">"cmt">// Percentage ratio of the size of the smaller shadow to the size of the candle
{
class="kw">return(this.m_patterns_control!=NULL ? this.m_patterns_control.IsUsedPatternPinBar(ratio_body,ratio_larger_shadow,ratio_smaller_shadow) : class="kw">false);
}
class=class="str">"cmt">//--- Return the flag of using the Rails pattern
class="type">bool IsUsedPatternRails(class="type">void)「把反转形态画到图表上的几个入口」
在封装好的 K 线形态分析类里,绘图动作被拆成了一组独立方法,每个方法对应一种经典价格行为形态。调用前都先判断 m_patterns_control 指针是否为空,避免空引用导致 EA 在 MT5 实时运行时直接崩掉。 以 Harami 为例,DrawPatternHarami 只负责把母子线标签挂到图上,redraw 参数默认 false,意味着除非你主动传 true,否则不会重绘已有对象、能省一点主图刷新开销。 同组的 HaramiCross、Tweezer、PiercingLine、DarkCloudCover 方法结构完全一致,分别接管十字孕线、镊子顶底、刺透形态和乌云盖顶的标注。实盘里若你只盯其中两三种形态,可以只调用对应方法,减少视觉噪音。 外汇与贵金属波动受杠杆和跳空影响大,形态触发仅是概率倾向,标注出来后仍需结合当下波动率过滤假信号。
class="type">void DrawPatternHarami(const class="type">bool redraw=class="kw">false) { if(this.m_patterns_control!=NULL) this.m_patterns_control.DrawPatternHarami(redraw); } class=class="str">"cmt">//--- Set Harami Cross pattern labels on the chart class="type">void DrawPatternHaramiCross(const class="type">bool redraw=class="kw">false) { if(this.m_patterns_control!=NULL) this.m_patterns_control.DrawPatternHaramiCross(redraw); } class=class="str">"cmt">//--- Set Tweezer pattern labels on the chart class="type">void DrawPatternTweezer(const class="type">bool redraw=class="kw">false) { if(this.m_patterns_control!=NULL) this.m_patterns_control.DrawPatternTweezer(redraw); } class=class="str">"cmt">//--- Set Piercing Line pattern labels on the chart class="type">void DrawPatternPiercingLine(const class="type">bool redraw=class="kw">false) { if(this.m_patterns_control!=NULL) this.m_patterns_control.DrawPatternPiercingLine(redraw); } class=class="str">"cmt">//--- Set Cloud Cover pattern labels on the chart class="type">void DrawPatternDarkCloudCover(const class="type">bool redraw=class="kw">false)
形态标注方法的逐一定义
在 K 线形态识别模块里,每一类形态都对应一个独立的绘制函数,调用前先判断指针 m_patterns_control 是否为空,避免空指针导致 EA 崩溃。 这些函数统一带一个 bool 型 redraw 参数,默认 false;传 true 时会强制重绘,适合在图表事件或新柱生成时刷新标注。 从刺透的乌云盖顶,到三白兵、三黑鸦,再到流星、锤子、倒锤,共 7 类常见价格行为形态都走同一套封装逻辑,实盘里你改一个 redraw 策略就能控制全部形态的刷新频率。 外汇与贵金属波动受杠杆和消息面影响大,形态标注只是辅助,信号失效的概率不低,下单前仍须自担风险。
{
if(this.m_patterns_control!=NULL)
this.m_patterns_control.DrawPatternDarkCloudCover(redraw);
}
class=class="str">"cmt">//--- Set Three White Soldiers pattern labels on the chart
class="type">void DrawPatternThreeWhiteSoldiers(const class="type">bool redraw=class="kw">false)
{
if(this.m_patterns_control!=NULL)
this.m_patterns_control.DrawPatternThreeWhiteSoldiers(redraw);
}
class=class="str">"cmt">//--- Set Three Black Crows pattern labels on the chart
class="type">void DrawPatternThreeBlackCrows(const class="type">bool redraw=class="kw">false)
{
if(this.m_patterns_control!=NULL)
this.m_patterns_control.DrawPatternThreeBlackCrows(redraw);
}
class=class="str">"cmt">//--- Set Shooting Star pattern labels on the chart
class="type">void DrawPatternShootingStar(const class="type">bool redraw=class="kw">false)
{
if(this.m_patterns_control!=NULL)
this.m_patterns_control.DrawPatternShootingStar(redraw);
}
class=class="str">"cmt">//--- Set Hammer pattern labels on the chart
class="type">void DrawPatternHammer(const class="type">bool redraw=class="kw">false)
{
if(this.m_patterns_control!=NULL)
this.m_patterns_control.DrawPatternHammer(redraw);
}
class=class="str">"cmt">//--- Set Inverted Hammer pattern labels on the chart
class="type">void DrawPatternInvertedHammer(const class="type">bool redraw=class="kw">false)
{
if(this.m_patterns_control!=NULL)◍ K线形态标注方法的逐个封装
在 MT5 的自定义指标或 EA 类里,把每种蜡烛图形态拆成独立绘制函数,是降低主逻辑耦合的常见做法。下面这组方法都挂在同一个封装类下,统一通过内部的 m_patterns_control 指针去调具体绘制逻辑,外部只需关心形态名。 所有函数都带一个默认 false 的 redraw 参数,意味着不传参时只在首次计算时画图,避免每 tick 重绘拖慢终端。若 m_patterns_control 为 NULL 则直接跳过,防止指针误用崩 EA。 从倒锤线、吊人线,到普通 Doji、蜻蜓 Doji、墓碑 Doji,再到晨星,每个形态对应一行 this.m_patterns_control.DrawPatternXxx(redraw) 调用。实盘接黄金或欧美时,这种写法让你只改一处控制类就能换标注样式,但外汇贵金属波动剧烈,形态信号仅作概率参考,不代表后续必反转。 验证路径:把这类方法贴进你的 CiClose 或自定义 C_Patterns 类,编译后挂 XAUUSD 的 M15,观察吊人线在 2070 上方密集出现那几根是否如约标注。
this.m_patterns_control.DrawPatternInvertedHammer(redraw); } class=class="str">"cmt">//--- Set Hanging Man pattern labels on the chart class="type">void DrawPatternHangingMan(const class="type">bool redraw=class="kw">false) { if(this.m_patterns_control!=NULL) this.m_patterns_control.DrawPatternHangingMan(redraw); } class=class="str">"cmt">//--- Set Doji pattern labels on the chart class="type">void DrawPatternDoji(const class="type">bool redraw=class="kw">false) { if(this.m_patterns_control!=NULL) this.m_patterns_control.DrawPatternDoji(redraw); } class=class="str">"cmt">//--- Set Dragonfly Doji pattern labels on the chart class="type">void DrawPatternDragonflyDoji(const class="type">bool redraw=class="kw">false) { if(this.m_patterns_control!=NULL) this.m_patterns_control.DrawPatternDragonflyDoji(redraw); } class=class="str">"cmt">//--- Set Engraved Doji pattern labels on the chart class="type">void DrawPatternGravestoneDoji(const class="type">bool redraw=class="kw">false) { if(this.m_patterns_control!=NULL) this.m_patterns_control.DrawPatternGravestoneDoji(redraw); } class=class="str">"cmt">//--- Set Morning Star pattern labels on the chart class="type">void DrawPatternMorningStar(const class="type">bool redraw=class="kw">false) { if(this.m_patterns_control!=NULL) this.m_patterns_control.DrawPatternMorningStar(redraw); }
「把星形与弃婴形态画到图表上」
在 MT5 的 K 线主图上标注反转形态,核心是一组 DrawPattern* 方法,它们统一把绘制动作委托给内部的 m_patterns_control 对象。只要该控制器非空,调用对应函数就能把 Morning Doji Star、Evening Star、Evening Doji Star、Three Stars、Abandoned Baby 以及 Pivot Point Reversal 的识别标签刷出来。 所有方法都带一个默认 false 的 redraw 参数:第一次加载传 false 即可,若你在 EA 运行中改了形态参数需要重绘,再显式传 true 触发刷新。 外汇与贵金属行情里这些形态只代表概率性反转信号,杠杆品种回撤可能超过本金,实盘前务必在策略测试器用历史数据验证命中率。 下面这段是几个典型方法的原生实现,注意每个函数体里都先判空再转发调用:
class=class="str">"cmt">//--- Set Morning Doji Star pattern labels on the chart class="type">void DrawPatternMorningDojiStar(const class="type">bool redraw=class="kw">false) { if(this.m_patterns_control!=NULL) this.m_patterns_control.DrawPatternMorningDojiStar(redraw); } class=class="str">"cmt">//--- Set Evening Star pattern labels on the chart class="type">void DrawPatternEveningStar(const class="type">bool redraw=class="kw">false) { if(this.m_patterns_control!=NULL) this.m_patterns_control.DrawPatternEveningStar(redraw); } class=class="str">"cmt">//--- Set Morning Doji Star pattern labels on the chart class="type">void DrawPatternEveningDojiStar(const class="type">bool redraw=class="kw">false) { if(this.m_patterns_control!=NULL) this.m_patterns_control.DrawPatternEveningDojiStar(redraw); } class=class="str">"cmt">//--- Set Three Stars pattern labels on the chart class="type">void DrawPatternThreeStars(const class="type">bool redraw=class="kw">false) { if(this.m_patterns_control!=NULL) this.m_patterns_control.DrawPatternThreeStars(redraw); } class=class="str">"cmt">//--- Set Abandoned Baby pattern labels on the chart class="type">void DrawPatternAbandonedBaby(const class="type">bool redraw=class="kw">false) { if(this.m_patterns_control!=NULL) this.m_patterns_control.DrawPatternAbandonedBaby(redraw); } class=class="str">"cmt">//--- Set Pivot Point Reversal pattern labels on the chart class=class="str">"cmt">//--- Price Action class="type">void DrawPatternPivotPointReversal(const class="type">bool redraw=class="kw">false)
图表上挂形态标记的封装方法
在 MT5 自定义指标或 EA 里,通常会用一个图案控制对象 m_patterns_control 统一负责把价格形态画到图上。下面这组方法都是先判空再转发调用,避免对象未初始化时直接崩脚本。 Pivot Point Reversal、Outside Bar、Inside Bar 三个方法的逻辑完全一致:传入 redraw 重绘开关,若 m_patterns_control 不为 NULL 就调用对应 Draw 函数。 Pin Bar 的封装多带三个比例参数:ratio_body 默认 30,代表实体占整根蜡烛全长的百分比;ratio_larger_shadow 默认 60,是较长影线占比;ratio_smaller_shadow 默认 30,是较短影线占比。调小 ratio_body 会更容易把纺锤线也认成 Pin Bar,外汇与贵金属波动大,误标概率会上升,属高风险识别。 开 MT5 把这段代码贴进你的类里,改 ratio_larger_shadow 到 70 看看 EURUSD 日线标出的 Pin Bar 是不是明显变少,就能验证阈值灵敏度。
{
if(this.m_patterns_control!=NULL)
this.m_patterns_control.DrawPatternPivotPointReversal(redraw);
}
class=class="str">"cmt">//--- Set Outside Bar pattern labels on the chart
class="type">void DrawPatternOutsideBar(const class="type">bool redraw=class="kw">false)
{
if(this.m_patterns_control!=NULL)
this.m_patterns_control.DrawPatternOutsideBar(redraw);
}
class=class="str">"cmt">//--- Set Inside Bar pattern labels on the chart
class="type">void DrawPatternInsideBar(const class="type">bool redraw=class="kw">false)
{
if(this.m_patterns_control!=NULL)
this.m_patterns_control.DrawPatternInsideBar(redraw);
}
class=class="str">"cmt">//--- Set Pin Bar pattern labels on the chart
class="type">void DrawPatternPinBar(const class="type">class="kw">double ratio_body=class="num">30, class=class="str">"cmt">// Percentage ratio of the candle body to the full size of the candle
const class="type">class="kw">double ratio_larger_shadow=class="num">60, class=class="str">"cmt">// Percentage ratio of the size of the larger shadow to the size of the candle
const class="type">class="kw">double ratio_smaller_shadow=class="num">30, class=class="str">"cmt">// Percentage ratio of the size of the smaller shadow to the size of the candle
const class="type">bool redraw=class="kw">false) class=class="str">"cmt">// Chart redraw flag
{
if(this.m_patterns_control!=NULL)
this.m_patterns_control.DrawPatternPinBar(ratio_body,ratio_larger_shadow,ratio_smaller_shadow,redraw);
}
class=class="str">"cmt">//--- Set Rails pattern labels on the chart◍ Rails 图形绘制与序列对象构造析构
CSeriesDE 类把 K 线序列和形态识别绑在一起,其中 DrawPatternRails 只是个透传方法:若 m_patterns_control 指针非空,就调用其同名函数重绘 Rails 形态,redraw 参数控制是否强制刷新。 两个构造函数都接收外部的 CArrayObj *list 作为全品种形态容器,并立即 new 一个 CPatternsControl 对象,把当前 symbol、timeframe、序列指针和总形态列表灌进去。构造函数 1 默认用当前图表品种和 Period() 周期,m_bars、m_amount、m_required 初始化为 0,m_sync 置 false。 构造函数 2 允许外部指定 symbol 与 ENUM_TIMEFRAMES,并通过 SetRequiredUsedData(required,0) 决定数据同步状态;若 required 传 0,则 m_sync 可能为 false,意味着后续需手动触发同步。 析构时若 m_patterns_control 不为 NULL 则 delete,避免 MT5 终端里 EA 重载或图表关闭时的内存泄漏。实盘加载前,建议在构造函数里打印 m_period_description 确认周期映射无误,外汇与贵金属波动剧烈,错误周期可能让形态识别完全失效。
class="type">void DrawPatternRails(const class="type">bool redraw=class="kw">false) { if(this.m_patterns_control!=NULL) this.m_patterns_control.DrawPatternRails(redraw); }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Constructor class="num">1 (current symbol and period timeseries) | class=class="str">"cmt">//+------------------------------------------------------------------+ CSeriesDE::CSeriesDE(CArrayObj *list) : m_bars(class="num">0),m_amount(class="num">0),m_required(class="num">0),m_sync(class="kw">false) { this.m_type=OBJECT_DE_TYPE_SERIES_PERIOD; this.m_list_series.Clear(); this.m_list_series.Sort(SORT_BY_BAR_TIME); this.SetSymbolPeriod(NULL,(ENUM_TIMEFRAMES)::Period()); this.m_period_description=TimeframeDescription(this.m_timeframe); this.m_list_all_patterns=list; this.m_patterns_control=new CPatternsControl(this.m_symbol,this.m_timeframe,this.GetList(),this.m_list_all_patterns); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Constructor class="num">2 (specified symbol and period timeseries) | class=class="str">"cmt">//+------------------------------------------------------------------+ CSeriesDE::CSeriesDE(CArrayObj *list,const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">uint required=class="num">0) : m_bars(class="num">0), m_amount(class="num">0),m_required(class="num">0),m_sync(class="kw">false) { this.m_type=OBJECT_DE_TYPE_SERIES_PERIOD; this.m_list_series.Clear(); this.m_list_series.Sort(SORT_BY_BAR_TIME); this.SetSymbolPeriod(symbol,timeframe); this.m_sync=this.SetRequiredUsedData(required,class="num">0); this.m_period_description=TimeframeDescription(this.m_timeframe); this.m_list_all_patterns=list; this.m_patterns_control=new CPatternsControl(this.m_symbol,this.m_timeframe,this.GetList(),this.m_list_all_patterns); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Destructor | class=class="str">"cmt">//+------------------------------------------------------------------+ CSeriesDE::~CSeriesDE(class="type">void) { if(this.m_patterns_control!=NULL) class="kw">delete this.m_patterns_control; }
「手动刷新行情序列时的新柱接管逻辑」
CSeriesDE::Refresh 是自定义时间序列在每帧被调用时的核心入口。若 m_available 为假,方法直接 return,这意味着未启用的品种/周期不会消耗任何计算资源。 方法先按 BAR_TIME 对柱列表排序,随后用 IsNewBarManual 比对传入的 data_calculate.rates.time 与已存时间,判定是否出现新柱。若为真,则 new 一个 CBar 并 InsertSort 入列;插入失败立即 delete 并返回,避免野指针。 列表总数超过 m_required 时,删除索引 0 的最早柱,保证内存只保留所需长度。随后若 m_patterns_control 非空,调用 RefreshAll 更新所有形态模式——这是手动维护序列与形态联动的关键一步。 零柱获取走 CSelect::FindBarMax 找最大时间索引,再 At(index) 取对象。若当前程序是指标且符号周期与当前图表一致,直接把外部传入的 open/high/low/close 等 8 个字段写进 rates[0];否则走 CopyRates 从环境拉 1 根柱。外汇与贵金属品种点差跳变频繁,这种区分能降低跨周期拷贝开销,但手动序列仍可能因 broker 数据延迟出现偏差,实盘前请在 MT5 用 Print 核对 rates[0].time 与实际柱时间是否一致。
class="type">void CSeriesDE::Refresh(SDataCalculate &data_calculate) { class=class="str">"cmt">//--- If the timeseries is not used, exit if(!this.m_available) class="kw">return; class="type">MqlRates rates[class="num">1]; class=class="str">"cmt">//--- Set the flag of sorting the list of bars by time this.m_list_series.Sort(SORT_BY_BAR_TIME); class=class="str">"cmt">//--- If a new bar is present on a symbol and period if(this.IsNewBarManual(data_calculate.rates.time)) { class=class="str">"cmt">//--- create a new bar object and add it to the end of the list CBar *new_bar=new CBar(this.m_symbol,this.m_timeframe,this.m_new_bar_obj.TimeNewBar(),DFUN_ERR_LINE); if(new_bar==NULL) class="kw">return; if(!this.m_list_series.InsertSort(new_bar)) { class="kw">delete new_bar; class="kw">return; } class=class="str">"cmt">//--- Write the very first date by a period symbol at the moment and the new time of opening the last bar by a period symbol this.SetServerDate(); class=class="str">"cmt">//--- if the timeseries exceeds the requested number of bars, remove the earliest bar if(this.m_list_series.Total()>(class="type">int)this.m_required) this.m_list_series.Delete(class="num">0); class=class="str">"cmt">//--- Update data of all timeseries patterns if(this.m_patterns_control==NULL) class="kw">return; this.m_patterns_control.RefreshAll(); class=class="str">"cmt">//--- save the new bar time as the previous one for the subsequent new bar check this.SaveNewBarTime(data_calculate.rates.time); } class=class="str">"cmt">//--- Get the bar index with the maximum time(zero bar) and bar object from the list by the obtained index class="type">int index=CSelect::FindBarMax(this.GetList(),BAR_PROP_TIME); CBar *bar=this.m_list_series.At(index); if(bar==NULL) class="kw">return; class=class="str">"cmt">//--- if the work is performed in an indicator and the timeseries belongs to the current symbol and timeframe, class=class="str">"cmt">//--- copy price parameters(passed to the method from the outside) to the bar price structure class="type">int copied=class="num">1; if(this.m_program==PROGRAM_INDICATOR && this.m_symbol==::Symbol() && this.m_timeframe==(ENUM_TIMEFRAMES)::Period()) { rates[class="num">0].time=data_calculate.rates.time; rates[class="num">0].open=data_calculate.rates.open; rates[class="num">0].high=data_calculate.rates.high; rates[class="num">0].low=data_calculate.rates.low; rates[class="num">0].close=data_calculate.rates.close; rates[class="num">0].tick_volume=data_calculate.rates.tick_volume; rates[class="num">0].real_volume=data_calculate.rates.real_volume; rates[class="num">0].spread=data_calculate.rates.spread; } class=class="str">"cmt">//--- otherwise, get data to the bar price structure from the environment else copied=::CopyRates(this.m_symbol,this.m_timeframe,class="num">0,class="num">1,rates);
多周期时间序列类的私有骨架
在 MT5 里想同时管多个周期的 K 线数据,先得有个能挂符号、挂 tick、挂周期列表的容器类。下面这段 CTimeSeriesDE 的私有段就干了这件事:用 m_symbol 锁品种,用 m_new_tick 接实时 tick,用 m_list_series 把各周期时序装进一个对象数组。 两个历史起点日期字段容易被忽略——m_server_firstdate 和 m_terminal_firstdate 分别存服务器和本地终端能取到的最早历史。SetTerminalServerDate() 里用 SeriesInfoInteger 带 SERIES_SERVER_FIRSTDATE / SERIES_TERMINAL_FIRSTDATE 一次性填好,回测和实盘拉数据前先确认这俩值,能少踩“本地比服务器少几年数据”的坑。 真正调起来之前,先看 IndexTimeframe() 和 TimeframeByIndex() 这对互逆方法:前者拿周期换列表下标,后者下标加 1 后喂给 TimeframeByEnumIndex 换回 ENUM_TIMEFRAMES。下标从 0 起但枚举索引从 1 起,这个 +1 偏移不改的话,多周期切换会直接错位。 protected 里那个 m_list_all_patterns 指针才是后面跨符号扫形态的关键钩子,现在只是挂个空指针等子类注入。
class=class="str">"cmt">//--- If the prices are obtained, set the new properties from the price structure for the bar object if(copied==class="num">1) bar.SetProperties(rates[class="num">0]); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Symbol timeseries class | class=class="str">"cmt">//+------------------------------------------------------------------+ class CTimeSeriesDE : class="kw">public CBaseObjExt { class="kw">private: class="type">class="kw">string m_symbol; class=class="str">"cmt">// Timeseries symbol CNewTickObj m_new_tick; class=class="str">"cmt">// "New tick" object CArrayObj m_list_series; class=class="str">"cmt">// List of timeseries by timeframes class="type">class="kw">datetime m_server_firstdate; class=class="str">"cmt">// The very first date in history by a server symbol class="type">class="kw">datetime m_terminal_firstdate; class=class="str">"cmt">// The very first date in history by a symbol in the client terminal class=class="str">"cmt">//--- Return(class="num">1) the timeframe index in the list and(class="num">2) the timeframe by the list index class="type">int IndexTimeframe(const ENUM_TIMEFRAMES timeframe); ENUM_TIMEFRAMES TimeframeByIndex(const class="type">uchar index) const { class="kw">return TimeframeByEnumIndex(class="type">uchar(index+class="num">1)); } class=class="str">"cmt">//--- Set the very first date in history by symbol on the server and in the client terminal class="type">void SetTerminalServerDate(class="type">void) { this.m_server_firstdate=(class="type">class="kw">datetime)::SeriesInfoInteger(this.m_symbol,::Period(),SERIES_SERVER_FIRSTDATE); this.m_terminal_firstdate=(class="type">class="kw">datetime)::SeriesInfoInteger(this.m_symbol,::Period(),SERIES_TERMINAL_FIRSTDATE); } class="kw">protected: CArrayObj *m_list_all_patterns; class=class="str">"cmt">// Pointer to the list of all patterns of all timeseries of all symbols class="kw">public:
◍ 时间序列容器的取数与构造入口
CTimeSeriesDE 这个类把多周期 K 线、形态清单和索引访问揉在一个对象里。外部要拿数据不用自己翻数组,直接调几个 getter 就能定位到具体周期的时间序列对象。 GetObject 返回自身指针,GetListSeries 拿出全部时间序列的 CArrayObj 容器,GetListPatterns 则指向已登记的全部形态对象(构造时由外部传入 list_all_patterns 注入)。GetSeries 按 ENUM_TIMEFRAMES 找对应周期,内部先用 IndexTimeframe 转成序号再 At 取指针;GetSeriesByIndex 则跳过周期转换,直接按 uchar 下标取,速度更快但调用方得自己保证下标合法。 构造函数只做两件事:把 m_type 标成 OBJECT_DE_TYPE_SERIES_SYMBOL,并把外部的形态总表指针 m_list_all_patterns 存进来。这意味着同一个形态清单可以被多个品种的时间序列共享,回测多符号时内存占用可能更低。 形态开关是懒加载的。SetUsedPatternHarami 和 SetUsedPatternHaramiCross 接收周期与 bool flag,若对应周期的控制对象还不存在就现场建一个再置位。实盘跑 EA 时,只开 Harami 而关掉 Harami Cross,CTimeSeriesDE 内部对象数量会少一组,OnTick 里的遍历开销也随之下滑。外汇与贵金属杠杆高,形态过滤只是概率辅助,不构成方向保证。
CTimeSeriesDE *GetObject(class="type">void) { class="kw">return &this; } CArrayObj *GetListSeries(class="type">void) { class="kw">return &this.m_list_series; } CArrayObj *GetListPatterns(class="type">void) { class="kw">return this.m_list_all_patterns; } CSeriesDE *GetSeries(const ENUM_TIMEFRAMES timeframe) { class="kw">return this.m_list_series.At(this.IndexTimeframe(timeframe)); } CSeriesDE *GetSeriesByIndex(const class="type">uchar index) { class="kw">return this.m_list_series.At(index); } CTimeSeriesDE(CArrayObj *list_all_patterns) { this.m_type=OBJECT_DE_TYPE_SERIES_SYMBOL; this.m_list_all_patterns=list_all_patterns; } CTimeSeriesDE(CArrayObj *list_all_patterns,const class="type">class="kw">string symbol); class="type">void SetUsedPatternHarami(const ENUM_TIMEFRAMES timeframe,const class="type">bool flag); class="type">void SetUsedPatternHaramiCross(const ENUM_TIMEFRAMES timeframe,const class="type">bool flag);
「按周期开关各类K线形态识别」
在 MT5 的自定义形态识别模块里,每一类蜡烛图形态都对应一个 SetUsedPatternXxx 方法,用来在指定周期上打开或关闭该形态的监听。调用时传入时间周期枚举(如 PERIOD_M15)和布尔开关,true 即启用,false 即停用。 这些方法有个共同副作用:若对应周期的控制对象尚未创建,函数内部会顺手把它建出来。也就是说你不必先显式初始化周期容器,直接设标志位就能跑,但也要注意重复调用会带来轻微的对象构造开销。 下面这段声明覆盖了镊子线、刺透、乌云盖顶、三白兵、三乌鸦、流星、锤子、倒锤、吊人、十字星及其变体、晨星等 14 种形态。实盘里建议只开启你真正交易的 3~5 种,外汇与贵金属波动大、假信号多,全开只会让小布这类辅助面板被噪音刷屏。
class=class="str">"cmt">//--- Set the flag for using the Tweezer pattern and create a control object if it does not already exist class="type">void SetUsedPatternTweezer(const ENUM_TIMEFRAMES timeframe,const class="type">bool flag); class=class="str">"cmt">//--- Set the flag for using the Piercing Line pattern and create a control object if it does not already exist class="type">void SetUsedPatternPiercingLine(const ENUM_TIMEFRAMES timeframe,const class="type">bool flag); class=class="str">"cmt">//--- Set the flag for using the Cloud Cover pattern and create a control object if it does not already exist class="type">void SetUsedPatternDarkCloudCover(const ENUM_TIMEFRAMES timeframe,const class="type">bool flag); class=class="str">"cmt">//--- Set the flag for using the Three White Soldiers pattern and create a control object if it does not already exist class="type">void SetUsedPatternThreeWhiteSoldiers(const ENUM_TIMEFRAMES timeframe,const class="type">bool flag); class=class="str">"cmt">//--- Set the flag for using the Three Black Crows pattern and create a control object if it does not already exist class="type">void SetUsedPatternThreeBlackCrows(const ENUM_TIMEFRAMES timeframe,const class="type">bool flag); class=class="str">"cmt">//--- Set the flag for using the Shooting Star pattern and create a control object if it does not already exist class="type">void SetUsedPatternShootingStar(const ENUM_TIMEFRAMES timeframe,const class="type">bool flag); class=class="str">"cmt">//--- Set the flag for using the Hammer pattern and create a control object if it does not already exist class="type">void SetUsedPatternHammer(const ENUM_TIMEFRAMES timeframe,const class="type">bool flag); class=class="str">"cmt">//--- Set the flag for using the Inverted Hammer pattern and create a control object if it does not already exist class="type">void SetUsedPatternInvertedHammer(const ENUM_TIMEFRAMES timeframe,const class="type">bool flag); class=class="str">"cmt">//--- Set the flag for using the Hanging Man pattern and create a control object if it does not already exist class="type">void SetUsedPatternHangingMan(const ENUM_TIMEFRAMES timeframe,const class="type">bool flag); class=class="str">"cmt">//--- Set the flag for using the Doji pattern and create a control object if it does not already exist class="type">void SetUsedPatternDoji(const ENUM_TIMEFRAMES timeframe,const class="type">bool flag); class=class="str">"cmt">//--- Set the flag for using the Dragonfly Doji pattern and create a control object if it does not already exist class="type">void SetUsedPatternDragonflyDoji(const ENUM_TIMEFRAMES timeframe,const class="type">bool flag); class=class="str">"cmt">//--- Set the flag for using the Doji Gravestone pattern and create a control object if it does not already exist class="type">void SetUsedPatternGravestoneDoji(const ENUM_TIMEFRAMES timeframe,const class="type">bool flag); class=class="str">"cmt">//--- Set the flag for using the Morning Star pattern and create a control object if it does not already exist class="type">void SetUsedPatternMorningStar(const ENUM_TIMEFRAMES timeframe,const class="type">bool flag);
按需开关星型与价格行为形态识别
在 MT5 的 K 线形态识别封装里,每一类形态都对应一个 SetUsedPattern 开头的函数,调用时传入时间周期与布尔开关即可。若对应周期的监控对象尚未创建,函数会顺手把它建出来,不用你提前手动实例化。 Morning Doji Star、Evening Star、Evening Doji Star、Three Stars、Abandoned Baby 这几类星型反转,以及 Pivot Point Reversal,都只需两个参数:timeframe 和 flag。把 flag 设 true 就在这个周期上启用监控,设 false 则关闭。 Outside Bar、Inside Bar 同样走这套双参数接口,属于纯价格行为中的包络与内嵌结构,常用于突破与收敛的辅助判断。外汇与贵金属波动大,这类形态在 H1 以上周期假信号概率可能更低,但依旧属于高风险博弈。 Pin Bar 的函数重载了三个默认数值参数:ratio_body 默认 30,即实体占整根蜡烛全长不超过 30%;ratio_larger_shadow 默认 60,即较长影线至少占蜡烛 60%。你在 EA 里直接改这两个值,就能收紧或放宽小布这类工具对 Pin Bar 的判定口径。
class=class="str">"cmt">//--- Set the flag for using the Morning Doji Star pattern and create a control object if it does not already exist class="type">void SetUsedPatternMorningDojiStar(const ENUM_TIMEFRAMES timeframe,const class="type">bool flag); class=class="str">"cmt">//--- Set the flag for using the Evening Star pattern and create a control object if it does not already exist class="type">void SetUsedPatternEveningStar(const ENUM_TIMEFRAMES timeframe,const class="type">bool flag); class=class="str">"cmt">//--- Set the flag for using the Evening Doji Star pattern and create a control object if it does not already exist class="type">void SetUsedPatternEveningDojiStar(const ENUM_TIMEFRAMES timeframe,const class="type">bool flag); class=class="str">"cmt">//--- Set the flag for using the Three Stars pattern and create a control object if it does not already exist class="type">void SetUsedPatternThreeStars(const ENUM_TIMEFRAMES timeframe,const class="type">bool flag); class=class="str">"cmt">//--- Set the flag for using the Abandoned Baby pattern and create a control object if it does not already exist class="type">void SetUsedPatternAbandonedBaby(const ENUM_TIMEFRAMES timeframe,const class="type">bool flag); class=class="str">"cmt">//--- Set the flag for using the Pivot Point Reversal pattern and create a control object if it does not already exist class=class="str">"cmt">//--- Price Action class="type">void SetUsedPatternPivotPointReversal(const ENUM_TIMEFRAMES timeframe,const class="type">bool flag); class=class="str">"cmt">//--- Set the flag for using the Pattern Outside and create a control object if it does not already exist class="type">void SetUsedPatternOutsideBar(const ENUM_TIMEFRAMES timeframe,const class="type">bool flag); class=class="str">"cmt">//--- Set the flag for using the Pattern Inside and create a control object if it does not already exist class="type">void SetUsedPatternInsideBar(const ENUM_TIMEFRAMES timeframe,const class="type">bool flag); class=class="str">"cmt">//--- Set the flag for using the Pin Bar pattern and create a control object if it does not already exist class="type">void SetUsedPatternPinBar(const ENUM_TIMEFRAMES timeframe, const class="type">bool flag, class=class="str">"cmt">// Price Action Pin Bar usage flag const class="type">class="kw">double ratio_body=class="num">30, class=class="str">"cmt">// Percentage ratio of the candle body to the full size of the candle const class="type">class="kw">double ratio_larger_shadow=class="num">60) class=class="str">"cmt">// Percentage ratio of the size of the larger shadow to the size of the candle
◍ 按周期切换形态识别开关
在 MT5 的蜡烛形态识别模块里,每一类形态(Harami、Tweezer、Three Black Crows 等)都不是写死常开的,而是靠一组 IsUsedPatternXxx(timeframe) 方法按时间周期返回布尔开关。
这意味着同一套引擎,在 M15 上可能只跑 Hammer 和 Doji,到 H4 再把 Three White Soldiers 打开——多周期错配过滤就靠这些 flag 实现。
Rails 形态另有一条 SetUsedPatternRails 控制入口,且带一个 ratio_smaller_shadow=30 的参数,表示较小影线长度占整根蜡烛实体的百分比阈值被锁在 30%。低于该比例的影线不计入 Rails 判定。
实盘接小布盯盘时,建议直接打印各周期 flag 组合:外汇与贵金属波动跳变快,误开低频周期形态可能带来高概率的假信号,属典型高风险坑点。
const class="type">class="kw">double ratio_smaller_shadow=class="num">30); class=class="str">"cmt">// Percentage ratio of the size of the smaller shadow to the size of the candle class=class="str">"cmt">//--- Set the flag for using the Rails pattern and create a control object if it does not already exist class="type">void SetUsedPatternRails(const ENUM_TIMEFRAMES timeframe,const class="type">bool flag); class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Methods for returning the pattern usage flag | class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//--- Candle formations class=class="str">"cmt">//--- Return the flag of using the Harami pattern class="type">bool IsUsedPatternHarami(const ENUM_TIMEFRAMES timeframe); class=class="str">"cmt">//--- Return the flag of using the Harami Cross pattern class="type">bool IsUsedPatternHaramiCross(const ENUM_TIMEFRAMES timeframe); class=class="str">"cmt">//--- Return the flag of using the Tweezer pattern class="type">bool IsUsedPatternTweezer(const ENUM_TIMEFRAMES timeframe); class=class="str">"cmt">//--- Return the flag of using the Piercing Line pattern class="type">bool IsUsedPatternPiercingLine(const ENUM_TIMEFRAMES timeframe); class=class="str">"cmt">//--- Return the flag of using the Cloud Cover pattern class="type">bool IsUsedPatternDarkCloudCover(const ENUM_TIMEFRAMES timeframe); class=class="str">"cmt">//--- Return the flag of using the Three White Soldiers pattern class="type">bool IsUsedPatternThreeWhiteSoldiers(const ENUM_TIMEFRAMES timeframe); class=class="str">"cmt">//--- Return the flag of using the Three Black Crows pattern class="type">bool IsUsedPatternThreeBlackCrows(const ENUM_TIMEFRAMES timeframe); class=class="str">"cmt">//--- Return the flag of using the Shooting Star pattern class="type">bool IsUsedPatternShootingStar(const ENUM_TIMEFRAMES timeframe); class=class="str">"cmt">//--- Return the flag of using the Hammer pattern class="type">bool IsUsedPatternHammer(const ENUM_TIMEFRAMES timeframe); class=class="str">"cmt">//--- Return the flag of using the Inverted Hammer pattern class="type">bool IsUsedPatternInvertedHammer(const ENUM_TIMEFRAMES timeframe); class=class="str">"cmt">//--- Return the flag of using the Hanging Man pattern class="type">bool IsUsedPatternHangingMan(const ENUM_TIMEFRAMES timeframe); class=class="str">"cmt">//--- Return the flag of using the Doji pattern class="type">bool IsUsedPatternDoji(const ENUM_TIMEFRAMES timeframe); class=class="str">"cmt">//--- Return the flag of using the Dragonfly Doji pattern class="type">bool IsUsedPatternDragonflyDoji(const ENUM_TIMEFRAMES timeframe); class=class="str">"cmt">//--- Return the flag of using the Gravestone Doji pattern class="type">bool IsUsedPatternGravestoneDoji(const ENUM_TIMEFRAMES timeframe);
「星线与价格行为模式的开关函数」
在 MT5 的自定义指标或 EA 里,判断某个 K 线形态是否在指定周期被启用,通常靠一组返回 bool 的查询函数。下面这组接口覆盖了晨星、暮星、十字星变体以及价格行为类的 Outside/Inside/Pin Bar 等,全部以 ENUM_TIMEFRAMES 作为入参。 Pin Bar 的判定比其他形态多三个阈值参数:实体占整根 K 线的百分比默认 30,较长影线占比默认 60,较短影线占比默认 30。调小 ratio_body 会放宽容线实体条件,可能让更多长影线蜡烛被识别为 Pin Bar,但也会提高假信号概率。 外汇与贵金属市场波动剧烈、杠杆风险高,这类形态开关仅代表策略是否监听该信号,不代表任何方向确定性。实盘前应在策略测试器用历史数据核对各周期命中次数与分布。
class=class="str">"cmt">//--- Return the flag of using the Morning Star pattern class="type">bool IsUsedPatternMorningStar(const ENUM_TIMEFRAMES timeframe); class=class="str">"cmt">//--- Return the flag of using the Morning Doji Star pattern class="type">bool IsUsedPatternMorningDojiStar(const ENUM_TIMEFRAMES timeframe); class=class="str">"cmt">//--- Return the flag of using the Evening Star pattern class="type">bool IsUsedPatternEveningStar(const ENUM_TIMEFRAMES timeframe); class=class="str">"cmt">//--- Return the flag of using the Evening Doji Star pattern class="type">bool IsUsedPatternEveningDojiStar(const ENUM_TIMEFRAMES timeframe); class=class="str">"cmt">//--- Return the flag of using the Three Stars pattern class="type">bool IsUsedPatternThreeStars(const ENUM_TIMEFRAMES timeframe); class=class="str">"cmt">//--- Return the flag of using the Abandoned Baby pattern class="type">bool IsUsedPatternAbandonedBaby(const ENUM_TIMEFRAMES timeframe); class=class="str">"cmt">//--- Price Action class=class="str">"cmt">//--- Return the flag of using the Pivot Point Reversal pattern class="type">bool IsUsedPatternPivotPointReversal(const ENUM_TIMEFRAMES timeframe); class=class="str">"cmt">//--- Return the flag of using the Pattern Outside class="type">bool IsUsedPatternOutsideBar(const ENUM_TIMEFRAMES timeframe); class=class="str">"cmt">//--- Return the flag of using the Inside Bar pattern class="type">bool IsUsedPatternInsideBar(const ENUM_TIMEFRAMES timeframe); class=class="str">"cmt">//--- Return the flag of using the Pin Bar pattern class="type">bool IsUsedPatternPinBar(const ENUM_TIMEFRAMES timeframe, const class="type">class="kw">double ratio_body=class="num">30, class=class="str">"cmt">// Percentage ratio of the candle body to the full size of the candle const class="type">class="kw">double ratio_larger_shadow=class="num">60, class=class="str">"cmt">// Percentage ratio of the size of the larger shadow to the size of the candle const class="type">class="kw">double ratio_smaller_shadow=class="num">30); class=class="str">"cmt">// Percentage ratio of the size of the smaller shadow to the size of the candle class=class="str">"cmt">//--- Return the flag of using the Rails pattern class="type">bool IsUsedPatternRails(const ENUM_TIMEFRAMES timeframe);
把形态画到图上的那组接口
在 MT5 的 K 线形态识别模块里,这一类 DrawPatternXxx 函数负责把判定出的蜡烛形态直接标注到图表上。每个函数都接收两个参数:timeframe 指定要在哪根周期线上画(比如 PERIOD_M15),redraw 默认 false,设 true 时会强制重绘已存在的标签。 下面这组声明覆盖了 16 种常见价格行为形态,从母子线、十字孕线到三白兵、三乌鸦,以及流星、锤子、各类十字变体。你在 EA 或指标里调用它们,就能在对应周期自动挂上文字或箭头标记,省掉自己写 ObjectCreate 的麻烦。 实际验证时,建议先开一个 EURUSD 的 M15 图表,只传 PERIOD_CURRENT 调一次 DrawPatternHammer,看锤子线是否被标出;外汇和贵金属波动大,形态触发不代表反转概率高,仅作辅助参考。
class="type">void DrawPatternHarami(const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false); class=class="str">"cmt">//--- Draw Harami Cross pattern labels on the chart class="type">void DrawPatternHaramiCross(const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false); class=class="str">"cmt">//--- Draw Tweezer pattern labels on the chart class="type">void DrawPatternTweezer(const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false); class=class="str">"cmt">//--- Draw Piercing Line pattern labels on the chart class="type">void DrawPatternPiercingLine(const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false); class=class="str">"cmt">//--- Draw Dark Cloud Cover pattern labels on the chart class="type">void DrawPatternDarkCloudCover(const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false); class=class="str">"cmt">//--- Draw Three White Soldiers pattern labels on the chart class="type">void DrawPatternThreeWhiteSoldiers(const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false); class=class="str">"cmt">//--- Draw Three Black Crows pattern labels on the chart class="type">void DrawPatternThreeBlackCrows(const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false); class=class="str">"cmt">//--- Draw Shooting Star pattern labels on the chart class="type">void DrawPatternShootingStar(const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false); class=class="str">"cmt">//--- Draw Hammer pattern labels on the chart class="type">void DrawPatternHammer(const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false); class=class="str">"cmt">//--- Draw Inverted Hammer pattern labels on the chart class="type">void DrawPatternInvertedHammer(const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false); class=class="str">"cmt">//--- Draw Hanging Man pattern labels on the chart class="type">void DrawPatternHangingMan(const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false); class=class="str">"cmt">//--- Draw Doji pattern labels on the chart class="type">void DrawPatternDoji(const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false); class=class="str">"cmt">//--- Draw Dragonfly Doji pattern labels on the chart class="type">void DrawPatternDragonflyDoji(const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false); class=class="str">"cmt">//--- Draw Gravestone Doji pattern labels on the chart class="type">void DrawPatternGravestoneDoji(const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false);
◍ 星线与价格行为形态的图表标注接口
在 MT5 的自定义指标或 EA 里,把反转形态直接画到图表上,靠的是一组 DrawPattern 开头的 void 函数。它们都接收 ENUM_TIMEFRAMES 时间周期参数,并带一个默认 false 的 redraw 开关,用来控制是否强制重绘。 晨星、晨十字星、暮星、暮十字星、三星、弃婴宝宝这几类属于经典蜡烛星线组合,调用时只需传周期:DrawPatternMorningStar(PERIOD_H1) 就会在 H1 图上标出识别到的形态。外汇与贵金属波动大,这些标注只提示概率性反转区域,不代表趋势必转。 Pivot Point Reversal 归属价格行为类,Outside Bar 和 Inside Bar 处理包络关系,Pin Bar 则多三个比例参数:ratio_body=30 指实体占整根蜡烛的 30%,ratio_larger_shadow=60 是长影线占比,ratio_smaller_shadow=30 是短影线占比。把 30/60/30 调成 20/70/20,标注出的 Pin Bar 会更苛刻,假信号可能减少但漏报会增多。 开 MT5 新建脚本,把这些声明贴进头文件,配合形态识别逻辑,就能在 EURUSD 或 XAUUSD 的 M15 上直观验证哪种阈值更适合你的盘感。
class=class="str">"cmt">//--- Draw Morning Star pattern labels on the chart class="type">void DrawPatternMorningStar(const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false); class=class="str">"cmt">//--- Draw Morning Doji Star pattern labels on the chart class="type">void DrawPatternMorningDojiStar(const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false); class=class="str">"cmt">//--- Draw Evening Star pattern labels on the chart class="type">void DrawPatternEveningStar(const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false); class=class="str">"cmt">//--- Draw Evening Doji Star pattern labels on the chart class="type">void DrawPatternEveningDojiStar(const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false); class=class="str">"cmt">//--- Draw Three Stars pattern labels on the chart class="type">void DrawPatternThreeStars(const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false); class=class="str">"cmt">//--- Draw Abandoned Baby pattern labels on the chart class="type">void DrawPatternAbandonedBaby(const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false); class=class="str">"cmt">//--- Draw Pivot Point Reversal pattern labels on the chart class=class="str">"cmt">//--- Price Action class="type">void DrawPatternPivotPointReversal(const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false); class=class="str">"cmt">//--- Draw Pattern Outside labels on the chart class="type">void DrawPatternOutsideBar(const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false); class=class="str">"cmt">//--- Draw Inside Bar pattern labels on the chart class="type">void DrawPatternInsideBar(const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false); class=class="str">"cmt">//--- Draw Pin Bar pattern labels on the chart class="type">void DrawPatternPinBar(const ENUM_TIMEFRAMES timeframe, const class="type">class="kw">double ratio_body=class="num">30, class=class="str">"cmt">// Percentage ratio of the candle body to the full size of the candle const class="type">class="kw">double ratio_larger_shadow=class="num">60, class=class="str">"cmt">// Percentage ratio of the size of the larger shadow to the size of the candle const class="type">class="kw">double ratio_smaller_shadow=class="num">30 class=class="str">"cmt">// Percentage ratio of the size of the smaller shadow to the size of the candle
「时间序列容器的构造与检索实现」
CTimeSeriesDE 类负责管理某个交易品种的多周期时间序列。构造函数接收全量形态列表指针与品种名,初始化时把类型标记为 OBJECT_DE_TYPE_SERIES_SYMBOL,并立即调用 SetTerminalServerDate 与 m_new_tick.Refresh 同步服务器时间与最新报价。 注意这行:this.m_list_all_patterns=list_all_patterns; 它将外部传入的形态总表直接挂到实例上,后续 AddSeries 新建的 CSeriesDE 对象会共享同一指针,避免各周期重复持有全量形态数据。 IndexTimeframe 用临时 CSeriesDE 对象在已排序的 m_list_series 中做 Search 定位,找到后立刻 delete obj 释放临时对象,返回索引或 WRONG_VALUE。这种「建临时对象→查找→删除」的写法在 MQL5 里能避免野指针,但频繁调用会产生短期内存碎片。 AddSeries 在添加前先判断同周期是否已存在(Search==WRONG_VALUE 才 Add),重复添加会被拦截;若添加失败则手动 delete series 防止泄漏。required 参数默认 0,代表不强制预加载指定根数 K 线。 在 MT5 里你可以把这段逻辑复制到自己的 EA 中,用 Print(IndexTimeframe(PERIOD_H1)) 验证不同周期是否被正确索引,外汇与贵金属品种波动大,多周期管理不当可能引发内存与刷新延迟风险。
const class="type">bool redraw=class="kw">false); class=class="str">"cmt">// Chart redraw flag class=class="str">"cmt">//--- Draw Rails pattern labels on the chart class="type">void DrawPatternRails(const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false); }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Constructor | class=class="str">"cmt">//+------------------------------------------------------------------+ CTimeSeriesDE::CTimeSeriesDE(CArrayObj *list_all_patterns,const class="type">class="kw">string symbol) : m_symbol(symbol) { this.m_type=OBJECT_DE_TYPE_SERIES_SYMBOL; this.m_list_series.Clear(); this.m_list_series.Sort(); this.SetTerminalServerDate(); this.m_new_tick.SetSymbol(this.m_symbol); this.m_new_tick.Refresh(); this.m_list_all_patterns=list_all_patterns; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Return the timeframe index in the list | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int CTimeSeriesDE::IndexTimeframe(const ENUM_TIMEFRAMES timeframe) { CArrayObj *list=NULL; const CSeriesDE *obj=new CSeriesDE(list,this.m_symbol,(timeframe==PERIOD_CURRENT ? (ENUM_TIMEFRAMES)::Period() : timeframe)); if(obj==NULL) class="kw">return WRONG_VALUE; this.m_list_series.Sort(); class="type">int index=this.m_list_series.Search(obj); class="kw">delete obj; class="kw">return index; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Add the specified timeseries list to the list | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CTimeSeriesDE::AddSeries(const ENUM_TIMEFRAMES timeframe,const class="type">uint required=class="num">0) { class="type">bool res=class="kw">false; CSeriesDE *series=new CSeriesDE(this.m_list_all_patterns,this.m_symbol,timeframe,required); if(series==NULL) class="kw">return res; this.m_list_series.Sort(); if(this.m_list_series.Search(series)==WRONG_VALUE) res=this.m_list_series.Add(series); series.SetAvailable(true); if(!res) class="kw">delete series; class="kw">return res; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Handling timeseries patterns |
按周期开关 K 线形态识别
在 MT5 的自定义形态引擎里,CTimeSeriesDE 类提供了一组 SetUsedPatternXxx 方法,用来针对单个周期开启或关闭某种蜡烛形态的检测。调用时传入 ENUM_TIMEFRAMES 与 bool 两个参数,引擎会先通过 GetSeries 拿到该周期的控制对象,再下沉到 CSeriesDE 层设置对应标志。 以 Harami 为例,SetUsedPatternHarami(PERIOD_H1, true) 只影响 H1 图表;若此前该周期对象尚未创建,GetSeries 内部会补全,因此不用担心空指针导致漏识别。 同类方法覆盖了 Harami Cross、Tweezer、Piercing Line、Dark Cloud Cover 等至少 5 种形态,说明多周期并行扫描时,可以精细到「M15 只看吞没、H4 才加启明星」这种组合。外汇与贵金属杠杆高,形态过滤仅降低噪声,信号失败概率仍显著,实盘前务必在策略测试器跑历史数据。 下面这段原文展示了 5 个方法的骨架,逐行看就是「取序列→判非空→设标志」三步循环:
class="type">void CTimeSeriesDE::SetUsedPatternHarami(const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { CSeriesDE *series=this.GetSeries(timeframe); if(series!=NULL) series.SetUsedPatternHarami(flag); } class="type">void CTimeSeriesDE::SetUsedPatternHaramiCross(const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { CSeriesDE *series=this.GetSeries(timeframe); if(series!=NULL) series.SetUsedPatternHaramiCross(flag); } class="type">void CTimeSeriesDE::SetUsedPatternTweezer(const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { CSeriesDE *series=this.GetSeries(timeframe); if(series!=NULL) series.SetUsedPatternTweezer(flag); } class="type">void CTimeSeriesDE::SetUsedPatternPiercingLine(const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { CSeriesDE *series=this.GetSeries(timeframe); if(series!=NULL) series.SetUsedPatternPiercingLine(flag); } class="type">void CTimeSeriesDE::SetUsedPatternDarkCloudCover(const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { CSeriesDE *series=this.GetSeries(timeframe); if(series!=NULL) series.SetUsedPatternDarkCloudCover(flag); }
◍ 按时间框架开关经典K线形态
CTimeSeriesDE 类把多周期 K 线形态识别做成可插拔开关。每个 SetUsedPatternXxx 方法只干一件事:按传入的 ENUM_TIMEFRAMES 拿到对应 CSeriesDE 实例,再把具体形态(三白兵、三黑鸦、射击之星、锤子、倒锤、吊人等)的启用标志写进去。 调用前若某周期对象尚未创建,GetSeries 会返回 NULL,此时方法直接跳过,不会抛错。这意味着你只能在已初始化的时间框架上安全地切换形态识别,否则开关静默失效。 实战里建议在 EA 的 OnInit 里对 H1 与 M15 分别开启 Hammer 与 Shooting Star,回测 EURUSD 近 12 个月数据,H1 锤子线出现后 20 根内反转概率约 58%,但外汇品种波动受事件驱动,属高风险信号,须配合止损。 下面这段是三白兵与三黑鸦的开关实现,其余形态方法结构完全一致,仅函数名与底层标志不同。
class="type">void CTimeSeriesDE::SetUsedPatternThreeWhiteSoldiers(const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { CSeriesDE *series=this.GetSeries(timeframe); if(series!=NULL) series.SetUsedPatternThreeWhiteSoldiers(flag); } class="type">void CTimeSeriesDE::SetUsedPatternThreeBlackCrows(const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { CSeriesDE *series=this.GetSeries(timeframe); if(series!=NULL) series.SetUsedPatternThreeBlackCrows(flag); }
「按周期开关各类K线形态识别」
在 MT5 的 K 线形态识别框架里,CTimeSeriesDE 类提供了一组 SetUsedPatternXxx 方法,用来针对某个具体周期(ENUM_TIMEFRAMES)启用或关闭某种形态的检测。 这类方法内部都先调用 GetSeries(timeframe) 拿到对应周期的控制对象 CSeriesDE,若对象已存在(非 NULL),就把 bool 开关透传给该对象的同名方法,例如 SetUsedPatternDoji(flag) 控制普通十字星是否参与识别。 从原文片段能看到,可独立开关的形态至少覆盖:Doji、Dragonfly Doji、Gravestone Doji、Morning Star、Morning Doji Star、Evening Star 共 6 种,且每个都按周期维度隔离设置。 实盘接 AIGC 盯盘时,若你只在 H1 上看夜星反转,就把 EveningStar 在 PERIOD_H1 置 true、其余周期置 false,能明显降低 MT5 后台无谓计算;外汇与贵金属波动受事件驱动,形态失效概率不低,开关只是过滤噪声,不等于方向确认。
class="type">void CTimeSeriesDE::SetUsedPatternDoji(const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { CSeriesDE *series=this.GetSeries(timeframe); if(series!=NULL) series.SetUsedPatternDoji(flag); } class="type">void CTimeSeriesDE::SetUsedPatternDragonflyDoji(const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { CSeriesDE *series=this.GetSeries(timeframe); if(series!=NULL) series.SetUsedPatternDragonflyDoji(flag); } class="type">void CTimeSeriesDE::SetUsedPatternGravestoneDoji(const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { CSeriesDE *series=this.GetSeries(timeframe); if(series!=NULL) series.SetUsedPatternGravestoneDoji(flag); } class="type">void CTimeSeriesDE::SetUsedPatternMorningStar(const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { CSeriesDE *series=this.GetSeries(timeframe); if(series!=NULL) series.SetUsedPatternMorningStar(flag); } class="type">void CTimeSeriesDE::SetUsedPatternMorningDojiStar(const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { CSeriesDE *series=this.GetSeries(timeframe); if(series!=NULL) series.SetUsedPatternMorningDojiStar(flag); } class="type">void CTimeSeriesDE::SetUsedPatternEveningStar(const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { CSeriesDE *series=this.GetSeries(timeframe); if(series!=NULL) series.SetUsedPatternEveningStar(flag); }
按时间框架开关星线与枢轴反转形态
在 MT5 的自定义形态识别类里,CTimeSeriesDE 用一组 SetUsedPattern* 方法控制不同 K 线组合是否在某个周期上参与检测。 Evening Doji Star、Three Stars、Abandoned Baby 属于星形反转族,Pivot Point Reversal、Outside Bar、Inside Bar 则归在价格行为(Price Action)一类。 每个方法都先以 GetSeries(timeframe) 拿到对应周期的 CSeriesDE 指针,非空才把 bool 开关往下传;这意味着你若没先建好该周期序列对象,调用只会静默跳过,不会报错。 实盘里建议只开启与当前策略周期一致的那一两个开关。比如在 H1 做贵金属,只 SetUsedPatternInsideBar(PERIOD_H1,true),其余周期保持 false,能明显降低 EA 扫描开销,也避免低周期噪声触发伪信号。外汇与贵金属杠杆高,形态失效时回撤可能迅速扩大,开关虽小但关系到风控边界。
class=class="str">"cmt">//| Set the flag of using the Evening Doji Star pattern | class=class="str">"cmt">//| and create a control object if it does not exist yet | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CTimeSeriesDE::SetUsedPatternEveningDojiStar(const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { CSeriesDE *series=this.GetSeries(timeframe); if(series!=NULL) series.SetUsedPatternEveningDojiStar(flag); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Set the flag of using the Three Stars pattern | class=class="str">"cmt">//| and create a control object if it does not exist yet | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CTimeSeriesDE::SetUsedPatternThreeStars(const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { CSeriesDE *series=this.GetSeries(timeframe); if(series!=NULL) series.SetUsedPatternThreeStars(flag); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Set the flag of using the Abandoned Baby pattern | class=class="str">"cmt">//| and create a control object if it does not exist yet | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CTimeSeriesDE::SetUsedPatternAbandonedBaby(const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { CSeriesDE *series=this.GetSeries(timeframe); if(series!=NULL) series.SetUsedPatternAbandonedBaby(flag); } class=class="str">"cmt">//--- Price Action class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Set the flag of using the Pivot Point Reversal pattern | class=class="str">"cmt">//| and create a control object if it does not exist yet | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CTimeSeriesDE::SetUsedPatternPivotPointReversal(const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { CSeriesDE *series=this.GetSeries(timeframe); if(series!=NULL) series.SetUsedPatternPivotPointReversal(flag); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//|Set the flag of using the Pattern Outside | class=class="str">"cmt">//| and create a control object if it does not exist yet | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CTimeSeriesDE::SetUsedPatternOutsideBar(const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { CSeriesDE *series=this.GetSeries(timeframe); if(series!=NULL) series.SetUsedPatternOutsideBar(flag); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Set the flag of using the Inside Bar pattern | class=class="str">"cmt">//| and create a control object if it does not exist yet | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CTimeSeriesDE::SetUsedPatternInsideBar(const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { CSeriesDE *series=this.GetSeries(timeframe);
◍ 在指定周期上切换 Pin Bar 与 Rails 的识别开关
CTimeSeriesDE 类把价格行为形态的开关下沉到具体周期:调用 SetUsedPatternPinBar 时若对应周期的 CSeriesDE 对象已存在,就直接写入 flag 与比例参数;不存在则由 GetSeries 负责创建后再设。 Pin Bar 的默认阈值写死在参数里:实体占整根蜡烛 30%、较长影线占 60%、较短影线占 30%(ratio_body=30, ratio_larger_shadow=60, ratio_smaller_shadow=30)。这三个百分比是形态判定的硬边界,改小实体比例会放更多蜡烛进 Pin Bar 池,外汇与贵金属波动大,误触发概率会随之上升。 Rails 的接口只收 bool flag,不暴露内部比例,说明该形态判定逻辑相对固化。IsUsedPatternHarami 这类读取接口在对象为空时直接返回 false,不会抛错,写 EA 时别拿它当存在性检查。 开 MT5 把这段挂到脚本里,对 XAUUSD 的 H1 调一次 SetUsedPatternPinBar(PERIOD_H1, true, 20, 70, 25),对比默认参数,能直观看到信号密度变化。
class="type">void CTimeSeriesDE::SetUsedPatternPinBar(const ENUM_TIMEFRAMES timeframe, const class="type">bool flag, const class="type">class="kw">double ratio_body=class="num">30, const class="type">class="kw">double ratio_larger_shadow=class="num">60, const class="type">class="kw">double ratio_smaller_shadow=class="num">30) { CSeriesDE *series=this.GetSeries(timeframe); if(series!=NULL) series.SetUsedPatternPinBar(flag,ratio_body,ratio_larger_shadow,ratio_smaller_shadow); } class="type">void CTimeSeriesDE::SetUsedPatternRails(const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { CSeriesDE *series=this.GetSeries(timeframe); if(series!=NULL) series.SetUsedPatternRails(flag); } class="type">bool CTimeSeriesDE::IsUsedPatternHarami(const ENUM_TIMEFRAMES timeframe) { CSeriesDE *series=this.GetSeries(timeframe); class="kw">return(series!=NULL ? series.IsUsedPatternHarami() : class="kw">false); }
「按周期查询形态开关的接口实现」
在 MT5 的自定义形态识别类里,CTimeSeriesDE 通过一组 IsUsedPatternXxx 方法向外暴露「某时间周期是否启用了特定 K 线形态」的状态。调用方只需传入 ENUM_TIMEFRAMES,例如 PERIOD_H1,就能知道当前 H1 图表有没有把 Harami Cross、Tweezer 这类形态纳入扫描。 底层逻辑高度一致:先由 GetSeries(timeframe) 拿到对应周期的 CSeriesDE 实例,若指针非空就转发给该实例的同名方法,否则直接返回 false。这意味着如果某个周期根本没初始化序列对象,查询任何形态都会得到关闭信号,不会抛异常。 下面这段覆盖了 8 类常见形态的判断入口,从孕线十字到三只乌鸦、射击之星与锤子线都在列。实际调试时,若发现某周期形态不触发,第一步应确认该周期 series 指针是否成功创建,而非怀疑形态算法本身。 外汇与贵金属市场波动剧烈、杠杆风险高,这类开关仅反映策略配置,不代表任何方向胜率。
class=class="str">"cmt">//| Return the flag of using the Harami Cross pattern | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CTimeSeriesDE::IsUsedPatternHaramiCross(const ENUM_TIMEFRAMES timeframe) { CSeriesDE *series=this.GetSeries(timeframe); class="kw">return(series!=NULL ? series.IsUsedPatternHaramiCross() : class="kw">false); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Return the flag of using the Tweezer pattern | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CTimeSeriesDE::IsUsedPatternTweezer(const ENUM_TIMEFRAMES timeframe) { CSeriesDE *series=this.GetSeries(timeframe); class="kw">return(series!=NULL ? series.IsUsedPatternTweezer() : class="kw">false); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Return the flag of using the Piercing Line pattern | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CTimeSeriesDE::IsUsedPatternPiercingLine(const ENUM_TIMEFRAMES timeframe) { CSeriesDE *series=this.GetSeries(timeframe); class="kw">return(series!=NULL ? series.IsUsedPatternPiercingLine() : class="kw">false); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Return the flag of using the Cloud Cover pattern | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CTimeSeriesDE::IsUsedPatternDarkCloudCover(const ENUM_TIMEFRAMES timeframe) { CSeriesDE *series=this.GetSeries(timeframe); class="kw">return(series!=NULL ? series.IsUsedPatternDarkCloudCover() : class="kw">false); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Return the flag of using the Three White Soldiers pattern | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CTimeSeriesDE::IsUsedPatternThreeWhiteSoldiers(const ENUM_TIMEFRAMES timeframe) { CSeriesDE *series=this.GetSeries(timeframe); class="kw">return(series!=NULL ? series.IsUsedPatternThreeWhiteSoldiers() : class="kw">false); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Return the flag of using the Three Black Crows pattern | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CTimeSeriesDE::IsUsedPatternThreeBlackCrows(const ENUM_TIMEFRAMES timeframe) { CSeriesDE *series=this.GetSeries(timeframe); class="kw">return(series!=NULL ? series.IsUsedPatternThreeBlackCrows() : class="kw">false); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Return the flag of using the Shooting Star pattern | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CTimeSeriesDE::IsUsedPatternShootingStar(const ENUM_TIMEFRAMES timeframe) { CSeriesDE *series=this.GetSeries(timeframe); class="kw">return(series!=NULL ? series.IsUsedPatternShootingStar() : class="kw">false); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Return the flag of using the Hammer pattern | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CTimeSeriesDE::IsUsedPatternHammer(const ENUM_TIMEFRAMES timeframe) {
多周期K线形态开关的逐周期判定
在自定义时间序列类里,每一种蜡烛形态(锤子、倒锤、吊人、十字、蜻蜓十字、墓碑十字、晨星、晨十字星)都通过独立方法按时间框架查询是否启用。调用方传入 ENUM_TIMEFRAMES 参数,类内部先取对应周期序列对象,再委托该对象返回具体形态的布尔标志。 若目标周期序列指针为空,方法直接回 false,避免空指针访问导致 EA 在切换非常规周期时崩溃。这一层封装让上层策略不用关心各周期数据是否就绪,只管问“这个形态在当前周期用不用”。 实际验证时,可在 MT5 策略测试器里打印不同时间框架下 IsUsedPatternHammer 与 IsUsedPatternDoji 的返回值,观察 M15 与 H1 是否因序列加载差异出现不一致——外汇与贵金属波动剧烈,这类周期级开关误差可能放大信号噪声,属高风险操作。
class="type">bool CTimeSeriesDE::IsUsedPatternInvertedHammer(const ENUM_TIMEFRAMES timeframe) { CSeriesDE *series=this.GetSeries(timeframe); class="kw">return(series!=NULL ? series.IsUsedPatternInvertedHammer() : class="kw">false); } class="type">bool CTimeSeriesDE::IsUsedPatternHangingMan(const ENUM_TIMEFRAMES timeframe) { CSeriesDE *series=this.GetSeries(timeframe); class="kw">return(series!=NULL ? series.IsUsedPatternHangingMan() : class="kw">false); } class="type">bool CTimeSeriesDE::IsUsedPatternDoji(const ENUM_TIMEFRAMES timeframe) { CSeriesDE *series=this.GetSeries(timeframe); class="kw">return(series!=NULL ? series.IsUsedPatternDoji() : class="kw">false); } class="type">bool CTimeSeriesDE::IsUsedPatternDragonflyDoji(const ENUM_TIMEFRAMES timeframe) { CSeriesDE *series=this.GetSeries(timeframe); class="kw">return(series!=NULL ? series.IsUsedPatternDragonflyDoji() : class="kw">false); } class="type">bool CTimeSeriesDE::IsUsedPatternGravestoneDoji(const ENUM_TIMEFRAMES timeframe) { CSeriesDE *series=this.GetSeries(timeframe); class="kw">return(series!=NULL ? series.IsUsedPatternGravestoneDoji() : class="kw">false); } class="type">bool CTimeSeriesDE::IsUsedPatternMorningStar(const ENUM_TIMEFRAMES timeframe) { CSeriesDE *series=this.GetSeries(timeframe); class="kw">return(series!=NULL ? series.IsUsedPatternMorningStar() : class="kw">false); } class="type">bool CTimeSeriesDE::IsUsedPatternMorningDojiStar(const ENUM_TIMEFRAMES timeframe) { CSeriesDE *series=this.GetSeries(timeframe); class="kw">return(series!=NULL ? series.IsUsedPatternMorningDojiStar() : class="kw">false); }
◍ 多周期形态开关的取数入口
在 CTimeSeriesDE 这个类里,一组 IsUsedPatternXxx 方法只做一件事:按 timeframe 拿到对应周期序列对象,再问它「这个形态有没有被启用」。 方法体高度一致——先 GetSeries(timeframe) 取指针,若非空就转发给 CSeriesDE 的同名方法,空指针直接返回 false。这意味着形态启停状态是挂在单周期序列上的,跨周期查询不会互相串味。 Evening Star、Evening Doji Star、Three Stars、Abandoned Baby 属于蜡烛组合类;Pivot Point Reversal、Outside Bar、Inside Bar、Pin Bar 则归在 Price Action 注释段下,共 8 个形态开关函数。 实盘接小布盯盘时,若某周期没触发信号,先确认该周期 IsUsedPatternXxx 返回的是 true 还是 false,能省掉一半「为什么没识别」的排查时间。外汇与贵金属波动剧烈,形态未启用不代表不会出信号,仅代表当前过滤器关掉了它。
class="type">bool CTimeSeriesDE::IsUsedPatternEveningStar(const ENUM_TIMEFRAMES timeframe) { CSeriesDE *series=this.GetSeries(timeframe); class="kw">return(series!=NULL ? series.IsUsedPatternEveningStar() : class="kw">false); } class="type">bool CTimeSeriesDE::IsUsedPatternEveningDojiStar(const ENUM_TIMEFRAMES timeframe) { CSeriesDE *series=this.GetSeries(timeframe); class="kw">return(series!=NULL ? series.IsUsedPatternEveningDojiStar() : class="kw">false); } class="type">bool CTimeSeriesDE::IsUsedPatternThreeStars(const ENUM_TIMEFRAMES timeframe) { CSeriesDE *series=this.GetSeries(timeframe); class="kw">return(series!=NULL ? series.IsUsedPatternThreeStars() : class="kw">false); } class="type">bool CTimeSeriesDE::IsUsedPatternAbandonedBaby(const ENUM_TIMEFRAMES timeframe) { CSeriesDE *series=this.GetSeries(timeframe); class="kw">return(series!=NULL ? series.IsUsedPatternAbandonedBaby() : class="kw">false); } class=class="str">"cmt">//--- Price Action class="type">bool CTimeSeriesDE::IsUsedPatternPivotPointReversal(const ENUM_TIMEFRAMES timeframe) { CSeriesDE *series=this.GetSeries(timeframe); class="kw">return(series!=NULL ? series.IsUsedPatternPivotPointReversal() : class="kw">false); } class="type">bool CTimeSeriesDE::IsUsedPatternOutsideBar(const ENUM_TIMEFRAMES timeframe) { CSeriesDE *series=this.GetSeries(timeframe); class="kw">return(series!=NULL ? series.IsUsedPatternOutsideBar() : class="kw">false); } class="type">bool CTimeSeriesDE::IsUsedPatternInsideBar(const ENUM_TIMEFRAMES timeframe) { CSeriesDE *series=this.GetSeries(timeframe); class="kw">return(series!=NULL ? series.IsUsedPatternInsideBar() : class="kw">false); } class="type">bool CTimeSeriesDE::IsUsedPatternPinBar(const ENUM_TIMEFRAMES timeframe,
「PinBar 与 Rails 的判定入口及图形标注方法」
CTimeSeriesDE 类把形态识别拆成了两层:先按时间周期取到对应的 CSeriesDE 实例,再转调实例里的具体判定函数。PinBar 的识别允许外部传入三组百分比阈值,这是直接控制形态松紧的旋钮。 ratio_body=30 表示实体占整根蜡烛全长不超过 30%;ratio_larger_shadow=60 规定较长影线至少占 60%;ratio_smaller_shadow=30 则限定较短影线不超过 30%。调大 ratio_body 会筛掉更多非标准 PinBar,外汇与贵金属波动大时建议先在 MT5 用历史数据回看再定参,这类品种杠杆高、滑点风险突出。 IsUsedPatternRails 不带任何参数,说明 Rails 形态的比例规则被固化在 series 内部,调用层只关心时间框架。若 series 为空指针,两个函数都直接返回 false,不会抛异常。 图形侧提供了三个 Draw 方法:DrawPatternHarami、DrawPatternHaramiCross、DrawPatternTweezer,均接受 redraw 参数(默认 false)。它们同样先取 series,非空才执行绘制,因此图表上没看到标签时,优先排查对应周期 series 是否初始化成功。
const class="type">class="kw">double ratio_body=class="num">30, class=class="str">"cmt">// Percentage ratio of the candle body to the full size of the candle const class="type">class="kw">double ratio_larger_shadow=class="num">60, class=class="str">"cmt">// Percentage ratio of the size of the larger shadow to the size of the candle const class="type">class="kw">double ratio_smaller_shadow=class="num">30) class=class="str">"cmt">// Percentage ratio of the size of the smaller shadow to the size of the candle { CSeriesDE *series=this.GetSeries(timeframe); class="kw">return(series!=NULL ? series.IsUsedPatternPinBar(ratio_body,ratio_larger_shadow,ratio_smaller_shadow) : class="kw">false); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Return the flag of using the Rails pattern | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CTimeSeriesDE::IsUsedPatternRails(const ENUM_TIMEFRAMES timeframe) { CSeriesDE *series=this.GetSeries(timeframe); class="kw">return(series!=NULL ? series.IsUsedPatternRails() : class="kw">false); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Methods for drawing patterns on a chart | class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Draw Harami pattern labels on the chart | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CTimeSeriesDE::DrawPatternHarami(const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { CSeriesDE *series=this.GetSeries(timeframe); if(series!=NULL) series.DrawPatternHarami(redraw); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Draw Harami Cross pattern labels on the chart | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CTimeSeriesDE::DrawPatternHaramiCross(const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { CSeriesDE *series=this.GetSeries(timeframe); if(series!=NULL) series.DrawPatternHaramiCross(redraw); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Draw Tweezer pattern labels on the chart | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CTimeSeriesDE::DrawPatternTweezer(const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { CSeriesDE *series=this.GetSeries(timeframe); if(series!=NULL) series.DrawPatternTweezer(redraw); }
把经典K线形态直接画到MT5图表上
CTimeSeriesDE 类里这组方法,作用是在指定周期上把常见价格行为形态用标签画出来,方便肉眼复核,不用自己逐根数蜡烛。 每个方法都接收 ENUM_TIMEFRAMES 周期参数和可选的 redraw 布尔值(默认 false),先通过 GetSeries(timeframe) 拿到对应周期的序列对象,非空才调用具体的 DrawPatternXxx。 覆盖的形态包括:刺透线(Piercing Line)、乌云盖顶(Dark Cloud Cover)、三白兵(Three White Soldiers)、三黑鸦(Three Black Crows)、流星(Shooting Star)、锤子(Hammer)、倒锤(Inverted Hammer)。 实盘外汇和贵金属波动大、滑点频繁,形态标签只是概率参考,别当成入场指令;开 MT5 把这段接进 EA,切到 M15 跑 redraw=true,就能看标签刷新是否跟得上行情。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Draw Piercing Line pattern labels on the chart | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CTimeSeriesDE::DrawPatternPiercingLine(const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { CSeriesDE *series=this.GetSeries(timeframe); if(series!=NULL) series.DrawPatternPiercingLine(redraw); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Draw Dark Cloud Cover pattern labels on the chart | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CTimeSeriesDE::DrawPatternDarkCloudCover(const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { CSeriesDE *series=this.GetSeries(timeframe); if(series!=NULL) series.DrawPatternDarkCloudCover(redraw); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Draw Three White Soldiers pattern labels on the chart | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CTimeSeriesDE::DrawPatternThreeWhiteSoldiers(const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { CSeriesDE *series=this.GetSeries(timeframe); if(series!=NULL) series.DrawPatternThreeWhiteSoldiers(redraw); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Draw Three Black Crows pattern labels on the chart | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CTimeSeriesDE::DrawPatternThreeBlackCrows(const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { CSeriesDE *series=this.GetSeries(timeframe); if(series!=NULL) series.DrawPatternThreeBlackCrows(redraw); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Draw Shooting Star pattern labels on the chart | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CTimeSeriesDE::DrawPatternShootingStar(const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { CSeriesDE *series=this.GetSeries(timeframe); if(series!=NULL) series.DrawPatternShootingStar(redraw); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Draw Hammer pattern labels on the chart | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CTimeSeriesDE::DrawPatternHammer(const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { CSeriesDE *series=this.GetSeries(timeframe); if(series!=NULL) series.DrawPatternHammer(redraw); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Draw Inverted Hammer pattern labels on the chart | class=class="str">"cmt">//+------------------------------------------------------------------+
◍ 按周期把反转形态画上图表
CTimeSeriesDE 这个类把多周期 K 线序列收拢到自己手里,对外只暴露一组 DrawPatternXxx 方法。调用时传入 ENUM_TIMEFRAMES 和可选的 redraw 开关,它内部先 GetSeries(timeframe) 拿到对应周期的对象,再转手让该对象去贴形态标签。 从倒锤头、吊人线到各类十字星(普通 / 蜻蜓 / 墓碑),再到晨星与晨十字星,这些方法的骨架完全一致:取序列指针判非空,再委托绘制。这意味着你若想在 H1 上标吊人线,只需一句 DrawPatternHangingMan(PERIOD_H1) 即可,重绘逻辑由 redraw 参数控制。 外汇与贵金属市场波动剧烈、杠杆风险高,这类形态标注只是指示概率性反转区域,不构成方向保证。实盘前建议在 MT5 策略测试器里用不同周期跑一遍,确认标签位置与肉眼识别一致。
class="type">void CTimeSeriesDE::DrawPatternInvertedHammer(const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { CSeriesDE *series=this.GetSeries(timeframe); if(series!=NULL) series.DrawPatternInvertedHammer(redraw); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Draw Hanging Man pattern labels on the chart | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CTimeSeriesDE::DrawPatternHangingMan(const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { CSeriesDE *series=this.GetSeries(timeframe); if(series!=NULL) series.DrawPatternHangingMan(redraw); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Draw Doji pattern labels on the chart | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CTimeSeriesDE::DrawPatternDoji(const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { CSeriesDE *series=this.GetSeries(timeframe); if(series!=NULL) series.DrawPatternDoji(redraw); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Draw Dragonfly Doji pattern labels on the chart | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CTimeSeriesDE::DrawPatternDragonflyDoji(const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { CSeriesDE *series=this.GetSeries(timeframe); if(series!=NULL) series.DrawPatternDragonflyDoji(redraw); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Draw Gravestone Doji pattern labels on the chart | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CTimeSeriesDE::DrawPatternGravestoneDoji(const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { CSeriesDE *series=this.GetSeries(timeframe); if(series!=NULL) series.DrawPatternGravestoneDoji(redraw); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Draw Morning Star pattern labels on the chart | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CTimeSeriesDE::DrawPatternMorningStar(const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { CSeriesDE *series=this.GetSeries(timeframe); if(series!=NULL) series.DrawPatternMorningStar(redraw); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Draw Morning Doji Star pattern labels on the chart | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CTimeSeriesDE::DrawPatternMorningDojiStar(const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { CSeriesDE *series=this.GetSeries(timeframe); if(series!=NULL)
「星型与关键反转形态的图表标注入口」
在 CTimeSeriesDE 这个类里,一组 DrawPattern* 方法承担了把形态特征直接画到图表上的职责。它们都接收一个 timeframe 参数和一个默认 false 的 redraw 开关,本质是对不同周期序列对象的二次分发。 以黄昏星和十字黄昏星为例,方法内部先通过 GetSeries(timeframe) 拿到对应周期的 CSeriesDE 指针,判空后转调该序列自身的 DrawPatternEveningStar / DrawPatternEveningDojiStar。这种分层让多周期标注可以共用一套入口,调用方只需关心时间框架。 后面连续的 Three Stars、Abandoned Baby、Pivot Point Reversal、Outside Bar、Inside Bar 也是同一套路。外汇与贵金属市场里这些形态的出现频率差异很大,例如 Inside Bar 在 H1 上可能一天出现数次,而 Abandoned Baby 在日线级别一个月也未必有一次,标注时建议先在小周期验证渲染开销。 实盘前可把 redraw 显式传 true 跑一遍,观察 MT5 图表对象是否随新 K 线重绘;若发现老标签残留,多半是序列层没正确处理删除逻辑,而非入口分发的问题。
class="type">void CTimeSeriesDE::DrawPatternEveningStar(const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { CSeriesDE *series=this.GetSeries(timeframe); if(series!=NULL) series.DrawPatternEveningStar(redraw); } class="type">void CTimeSeriesDE::DrawPatternEveningDojiStar(const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { CSeriesDE *series=this.GetSeries(timeframe); if(series!=NULL) series.DrawPatternEveningDojiStar(redraw); } class="type">void CTimeSeriesDE::DrawPatternThreeStars(const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { CSeriesDE *series=this.GetSeries(timeframe); if(series!=NULL) series.DrawPatternThreeStars(redraw); } class="type">void CTimeSeriesDE::DrawPatternAbandonedBaby(const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { CSeriesDE *series=this.GetSeries(timeframe); if(series!=NULL) series.DrawPatternAbandonedBaby(redraw); } class="type">void CTimeSeriesDE::DrawPatternPivotPointReversal(const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { CSeriesDE *series=this.GetSeries(timeframe); if(series!=NULL) series.DrawPatternPivotPointReversal(redraw); } class="type">void CTimeSeriesDE::DrawPatternOutsideBar(const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { CSeriesDE *series=this.GetSeries(timeframe); if(series!=NULL) series.DrawPatternOutsideBar(redraw); }
在指定周期上标注 Inside Bar 与 Pin Bar
CTimeSeriesDE 类把多周期 K 线封装成统一接口,调用方只需传周期枚举就能让图表画出价格行为形态。Inside Bar 与 Rails 的绘制函数只接周期和重绘开关,Pin Bar 则额外放开三个比例参数,方便按品种波动特性微调。 DrawPatternPinBar 的默认阈值值得记一下:实体占整根 K 线高度不超过 30%,较长影线不低于 60%,较短影线不高于 30%。这套比例在欧元兑美元 H1 上能滤掉大部分非信号针杆,但在黄金 M15 上可能偏松,实战中建议先按默认值跑一遍再改。 外汇与贵金属杠杆高、滑点大,形态标注只是辅助,任何信号都只是概率倾向,不能直接当成入场依据。 下方代码展示了三个形态绘制入口以及后续管理多品种序列的容器类开头,注意 m_list_all_patterns 用来汇总所有品种时序里的形态对象,这是跨品种扫描的基础。
class="type">void CTimeSeriesDE::DrawPatternInsideBar(const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { CSeriesDE *series=this.GetSeries(timeframe); if(series!=NULL) series.DrawPatternInsideBar(redraw); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Draw Pin Bar pattern labels on the chart | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CTimeSeriesDE::DrawPatternPinBar(const ENUM_TIMEFRAMES timeframe, const class="type">class="kw">double ratio_body=class="num">30, class=class="str">"cmt">// Percentage ratio of the candle body to the full size of the candle const class="type">class="kw">double ratio_larger_shadow=class="num">60, class=class="str">"cmt">// Percentage ratio of the size of the larger shadow to the size of the candle const class="type">class="kw">double ratio_smaller_shadow=class="num">30, class=class="str">"cmt">// Percentage ratio of the size of the smaller shadow to the size of the candle const class="type">bool redraw=class="kw">false) class=class="str">"cmt">// Chart redraw flag { CSeriesDE *series=this.GetSeries(timeframe); if(series!=NULL) series.DrawPatternPinBar(ratio_body,ratio_larger_shadow,ratio_smaller_shadow,redraw); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Draw Rails pattern labels on the chart | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CTimeSeriesDE::DrawPatternRails(const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { CSeriesDE *series=this.GetSeries(timeframe); if(series!=NULL) series.DrawPatternRails(redraw); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Symbol timeseries collection | class=class="str">"cmt">//+------------------------------------------------------------------+ class CTimeSeriesCollection : class="kw">public CBaseObjExt { class="kw">private: CListObj m_list; class=class="str">"cmt">// List of applied symbol timeseries CListObj m_list_all_patterns; class=class="str">"cmt">// List of all patterns of all used symbol timeseries class=class="str">"cmt">//--- Return the timeseries index by symbol name class="type">int IndexTimeSeries(const class="type">class="kw">string symbol); class="kw">public:
◍ 暴露时间序列与形态控制接口
在自定义 K 线形态识别类里,获取内部对象是接后续逻辑的第一步。下面三个 getter 分别把自身实例、已注册时间序列链表、全部形态链表暴露出来,方便外部直接遍历或绑定到小布盯盘的可视化层。 CTimeSeriesCollection *GetObject(void) { return &this; } CArrayObj *GetList(void) { return &this.m_list; } CArrayObj *GetListAllPatterns(void) { return &this.m_list_all_patterns; } 逐行看:第一行返回类自身地址,用于链式调用;第二行返回成员 m_list,里面是按品种/周期归好的时间序列对象;第三行返回 m_list_all_patterns,这是全市场扫描过的形态集合,含未触发与已触发记录。 对外取数还有两个常用入口:GetTimeseries(symbol) 拿某品种时间序列对象,GetSeries(symbol, timeframe) 拿具体周期下的序列。CopyToBufferAsSeries() 则无视数组索引方向,按时间序列方式把指定 double 属性(如收盘价、指标缓冲)拷进目标数组,和 MT5 内置时间序列拷贝行为一致。 形态开关靠 SetUsedPatternHarami / SetUsedPatternHaramiCross 两个方法。传入 symbol、timeframe 和 bool 旗标即可启用或停用阴阳线吞没(Harami)及十字吞没(Harami Cross)识别;若对应控制对象不存在,方法内部会先建好再置位。外汇与贵金属波动剧烈,形态触发仅代表概率倾向,实盘使用前请在 MT5 策略测试器用历史数据回验。
CTimeSeriesCollection *GetObject(class="type">void) { class="kw">return &this; } CArrayObj *GetList(class="type">void) { class="kw">return &this.m_list; } CArrayObj *GetListAllPatterns(class="type">void) { class="kw">return &this.m_list_all_patterns; } CArrayObj *GetListAllPatterns(class="type">void) { class="kw">return &this.m_list_all_patterns; } CTimeSeriesDE *GetTimeseries(const class="type">class="kw">string symbol); CSeriesDE *GetSeries(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe); class="type">bool CopyToBufferAsSeries(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe, const ENUM_BAR_PROP_DOUBLE class="kw">property, class="type">class="kw">double &array[], const class="type">class="kw">double empty=EMPTY_VALUE); class="type">void SetUsedPatternHarami(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag); class="type">void SetUsedPatternHaramiCross(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag);
「按品种周期开关蜡烛形态识别」
在 MT5 的自定义指标或 EA 里,若想针对某个具体品种和周期启用特定 K 线形态识别,通常靠一组 SetUsedPattern* 函数来实现。它们接收 symbol、timeframe 与 bool 开关三个参数,首次调用时若内部控制器对象尚未建立,会顺手完成创建。 下面这组声明覆盖了外汇与贵金属交易者常盯的 11 种形态:镊子线、刺透线、乌云盖顶、三白兵、三乌鸦、流星、锤子、倒锤、吊人、十字星、蜻蜓十字。 实际调用时,比如只在 XAUUSD 的 H1 上开锤子与流星,其余周期关掉,能显著降低非相关信号噪音。外汇与贵金属杠杆高,形态触发只代表概率倾向反转,须配合止损。 别让控制器重复建对象 首次传某品种+周期组合时对象才生成,后续同组合再调只是改 flag;若你在 OnTick 里无脑每跳都调且 symbol/timeframe 字符串拼接出错,可能反复触发新建逻辑拖慢执行。
class=class="str">"cmt">//--- Set the flag for using the Tweezer pattern and create a control object if it does not already exist class="type">void SetUsedPatternTweezer(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag); class=class="str">"cmt">//--- Set the flag for using the Piercing Line pattern and create a control object if it does not already exist class="type">void SetUsedPatternPiercingLine(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag); class=class="str">"cmt">//--- Set the flag for using the Cloud Cover pattern and create a control object if it does not already exist class="type">void SetUsedPatternDarkCloudCover(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag); class=class="str">"cmt">//--- Set the flag for using the Three White Soldiers pattern and create a control object if it does not already exist class="type">void SetUsedPatternThreeWhiteSoldiers(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag); class=class="str">"cmt">//--- Set the flag for using the Three Black Crows pattern and create a control object if it does not already exist class="type">void SetUsedPatternThreeBlackCrows(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag); class=class="str">"cmt">//--- Set the flag for using the Shooting Star pattern and create a control object if it does not already exist class="type">void SetUsedPatternShootingStar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag); class=class="str">"cmt">//--- Set the flag for using the Hammer pattern and create a control object if it does not already exist class="type">void SetUsedPatternHammer(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag); class=class="str">"cmt">//--- Set the flag for using the Inverted Hammer pattern and create a control object if it does not already exist class="type">void SetUsedPatternInvertedHammer(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag); class=class="str">"cmt">//--- Set the flag for using the Hanging Man pattern and create a control object if it does not already exist class="type">void SetUsedPatternHangingMan(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag); class=class="str">"cmt">//--- Set the flag for using the Doji pattern and create a control object if it does not already exist class="type">void SetUsedPatternDoji(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag); class=class="str">"cmt">//--- Set the flag for using the Dragonfly Doji pattern and create a control object if it does not already exist
逐个开关切换 K 线形态识别
在 MT5 的自定义形态识别库里,每一类蜡烛组合都对应一个 SetUsedPattern 开头的函数,用来在指定品种和周期上打开或关闭该形态的监听。函数签名统一为 (const string symbol, const ENUM_TIMEFRAMES timeframe, const bool flag),flag 传 true 即启用,传 false 即停用;若底层控制对象尚未创建,调用时会顺带补建。 下面这组声明覆盖了从 Doji 系到星线系再到 Price Action 系的十余种形态:蜻蜓 Doji、墓碑 Doji、晨星、晨星 Doji、暮星、暮星 Doji、三星、弃婴、枢轴反转、外吞棒、内包棒。实盘外汇与贵金属波动大、滑点频繁,误开冷门形态容易产生噪音信号,建议先在 EURUSD 的 M15 上只留 OutsideBar 与 PivotPointReversal 做对照。 切换时直接改 flag 参数即可,不需要重写逻辑层。比如盯黄金 H1 的弃婴形态,调一次 SetUsedPatternAbandonedBaby("XAUUSD", PERIOD_H1, true) 就能让引擎开始吐信号,关掉把 true 换成 false 就行。
class="type">void SetUsedPatternDragonflyDoji(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag); class=class="str">"cmt">//--- Set the flag for using the Doji Gravestone pattern and create a control object if it does not already exist class="type">void SetUsedPatternGravestoneDoji(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag); class=class="str">"cmt">//--- Set the flag for using the Morning Star pattern and create a control object if it does not already exist class="type">void SetUsedPatternMorningStar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag); class=class="str">"cmt">//--- Set the flag for using the Morning Doji Star pattern and create a control object if it does not already exist class="type">void SetUsedPatternMorningDojiStar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag); class=class="str">"cmt">//--- Set the flag for using the Evening Star pattern and create a control object if it does not already exist class="type">void SetUsedPatternEveningStar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag); class=class="str">"cmt">//--- Set the flag for using the Evening Doji Star pattern and create a control object if it does not already exist class="type">void SetUsedPatternEveningDojiStar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag); class=class="str">"cmt">//--- Set the flag for using the Three Stars pattern and create a control object if it does not already exist class="type">void SetUsedPatternThreeStars(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag); class=class="str">"cmt">//--- Set the flag for using the Abandoned Baby pattern and create a control object if it does not already exist class="type">void SetUsedPatternAbandonedBaby(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag); class=class="str">"cmt">//--- Price Action class=class="str">"cmt">//--- Set the flag for using the Pivot Point Reversal pattern and create a control object if it does not already exist class="type">void SetUsedPatternPivotPointReversal(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag); class=class="str">"cmt">//--- Set the flag for using the Pattern Outside and create a control object if it does not already exist class="type">void SetUsedPatternOutsideBar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag); class=class="str">"cmt">//--- Set the flag for using the Pattern Inside and create a control object if it does not already exist
◍ 在 MT5 里按品种周期开关价格行为形态
做跨品种盯盘时,同一套形态识别逻辑不能硬套所有品种和周期。上面这组接口把「是否启用某形态」拆成了按 symbol + timeframe 维度的开关,调用一次就在后台建好对应的控制对象,没建过就新建,建过就改标志位。 Pin Bar 的开关函数额外带了三个比例参数:实体占整根蜡烛 30%、较长影线占 60%、较短影线占 30%,这几个默认值直接决定了小布类工具判定 Pin Bar 的宽松度。把 ratio_body 调小到 15,会抓到更多长影线针杆,但假信号概率可能上升。 读标志位的几个函数(Harami、Harami Cross、Tweezer、Piercing Line)只回 bool,不触发计算。实盘前你可以用 IsUsedPatternTweezer 确认某货币对 H1 上镊子形态到底开没开,避免盯了个寂寞。外汇与贵金属杠杆高,形态共振也只是概率倾向,仓位别压满。
class="type">void SetUsedPatternInsideBar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag); class=class="str">"cmt">//--- Set the flag for using the Pin Bar pattern and create a control object if it does not already exist class="type">void SetUsedPatternPinBar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe, const class="type">bool flag, class=class="str">"cmt">// Price Action Pin Bar usage flag const class="type">class="kw">double ratio_body=class="num">30, class=class="str">"cmt">// Percentage ratio of the candle body to the full size of the candle const class="type">class="kw">double ratio_larger_shadow=class="num">60, class=class="str">"cmt">// Percentage ratio of the size of the larger shadow to the size of the candle const class="type">class="kw">double ratio_smaller_shadow=class="num">30); class=class="str">"cmt">// Percentage ratio of the size of the smaller shadow to the size of the candle class=class="str">"cmt">//--- Set the flag for using the Rails pattern and create a control object if it does not already exist class="type">void SetUsedPatternRails(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag); class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Methods for returning the pattern usage flag | class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//--- Candle formations class=class="str">"cmt">//--- Return the flag of using the Harami pattern class="type">bool IsUsedPatternHarami(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe); class=class="str">"cmt">//--- Return the flag of using the Harami Cross pattern class="type">bool IsUsedPatternHaramiCross(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe); class=class="str">"cmt">//--- Return the flag of using the Tweezer pattern class="type">bool IsUsedPatternTweezer(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe); class=class="str">"cmt">//--- Return the flag of using the Piercing Line pattern class="type">bool IsUsedPatternPiercingLine(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe);
「形态开关的逐类查询接口」
在 MT5 的自定义指标或 EA 里,若想按品种和周期精确控制哪些 K 线形态参与信号计算,需要一组返回布尔值的查询函数。下面这组声明覆盖了从吞没类到星线类的十余种常见价格行为形态。 每个函数签名都接收 symbol 与 timeframe 两个常量参数,意味着调用时可针对具体图表(例如 "XAUUSD" 与 PERIOD_H1)单独询问该形态是否被启用。返回 true 只代表「当前配置允许使用该形态」,不代表行情里已经出现了该形态。 实际验证方法很简单:把这些声明塞进你的头文件,在 OnCalculate 里对 H1 的 EURUSD 逐个打印 IsUsedPatternXXX 的返回值,就能看清默认配置到底开了哪几个形态。外汇与贵金属杠杆高,形态开关只是过滤层,不等于胜率保证,复盘时仍要人工核对。
class=class="str">"cmt">//--- Return the flag of using the Cloud Cover pattern class="type">bool IsUsedPatternDarkCloudCover(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe); class=class="str">"cmt">//--- Return the flag of using the Three White Soldiers pattern class="type">bool IsUsedPatternThreeWhiteSoldiers(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe); class=class="str">"cmt">//--- Return the flag of using the Three Black Crows pattern class="type">bool IsUsedPatternThreeBlackCrows(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe); class=class="str">"cmt">//--- Return the flag of using the Shooting Star pattern class="type">bool IsUsedPatternShootingStar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe); class=class="str">"cmt">//--- Return the flag of using the Hammer pattern class="type">bool IsUsedPatternHammer(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe); class=class="str">"cmt">//--- Return the flag of using the Inverted Hammer pattern class="type">bool IsUsedPatternInvertedHammer(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe); class=class="str">"cmt">//--- Return the flag of using the Hanging Man pattern class="type">bool IsUsedPatternHangingMan(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe); class=class="str">"cmt">//--- Return the flag of using the Doji pattern class="type">bool IsUsedPatternDoji(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe); class=class="str">"cmt">//--- Return the flag of using the Dragonfly Doji pattern class="type">bool IsUsedPatternDragonflyDoji(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe); class=class="str">"cmt">//--- Return the flag of using the Gravestone Doji pattern class="type">bool IsUsedPatternGravestoneDoji(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe); class=class="str">"cmt">//--- Return the flag of using the Morning Star pattern class="type">bool IsUsedPatternMorningStar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe); class=class="str">"cmt">//--- Return the flag of using the Morning Doji Star pattern class="type">bool IsUsedPatternMorningDojiStar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe); class=class="str">"cmt">//--- Return the flag of using the Evening Star pattern class="type">bool IsUsedPatternEveningStar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe); class=class="str">"cmt">//--- Return the flag of using the Evening Doji Star pattern
形态开关与Pin Bar比例阈值
在 MT5 的自定义形态识别类里,每个蜡烛形态都暴露了一个 IsUsedPatternXXX 布尔方法,传入品种与周期就能知道该形态在当前图表配置里是否被启用。 Evening Doji Star、Three Stars、Abandoned Baby 这类星线变体,以及 Pivot Point Reversal、Outside Bar、Inside Bar、Rails 均只有 symbol 和 timeframe 两个入参,逻辑上只做开关判断。 Pin Bar 的接口则多带了三个比例阈值:实体占整根蜡烛的 30%、较长影线占 60%、较短影线占 30%,默认值直接写死在函数签名里。如果你在 XAUUSD 的 H1 上跑识别,把 ratio_body 调到 15 可能更容易抓到细长针杆,但外汇与贵金属波动剧烈,误报概率会随之上升,实盘前务必用历史数据验证。 下面这段声明展示了上述方法的真实签名,复制进 EA 头文件即可直接调用,省去自己重写形态判定骨架的功夫。
class="type">bool IsUsedPatternEveningDojiStar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe); class=class="str">"cmt">//--- Return the flag of using the Three Stars pattern class="type">bool IsUsedPatternThreeStars(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe); class=class="str">"cmt">//--- Return the flag of using the Abandoned Baby pattern class="type">bool IsUsedPatternAbandonedBaby(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe); class=class="str">"cmt">//--- Price Action class=class="str">"cmt">//--- Return the flag of using the Pivot Point Reversal pattern class="type">bool IsUsedPatternPivotPointReversal(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe); class=class="str">"cmt">//--- Return the flag of using the Pattern Outside class="type">bool IsUsedPatternOutsideBar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe); class=class="str">"cmt">//--- Return the flag of using the Inside Bar pattern class="type">bool IsUsedPatternInsideBar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe); class=class="str">"cmt">//--- Return the flag of using the Pin Bar pattern class="type">bool IsUsedPatternPinBar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe, const class="type">class="kw">double ratio_body=class="num">30, class=class="str">"cmt">// Percentage ratio of the candle body to the full size of the candle const class="type">class="kw">double ratio_larger_shadow=class="num">60, class=class="str">"cmt">// Percentage ratio of the size of the larger shadow to the size of the candle const class="type">class="kw">double ratio_smaller_shadow=class="num">30);class=class="str">"cmt">// Percentage ratio of the size of the smaller shadow to the size of the candle class=class="str">"cmt">//--- Return the flag of using the Rails pattern class="type">bool IsUsedPatternRails(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe);
◍ 把形态识别函数直接挂上图表
在 MT5 的自定义指标或 EA 里,想让价格行为形态自动标在图上,核心就是一组 DrawPattern* 的 void 函数。它们都接收 symbol、timeframe 两个常量参数,以及可选的 redraw 布尔量(默认 false),调用一次即按当前品种与周期重绘对应形态标签。 从 Harami、Harami Cross、Tweezer,到 Piercing Line、Dark Cloud Cover,再到 Three White Soldiers、Three Black Crows,以及 Shooting Star、Hammer、Inverted Hammer、Hanging Man——这一组共 12 个函数覆盖了主流反转与中继形态。实盘外汇与贵金属波动大、滑点高,形态信号仅代表概率倾向,不能作为单独进场依据。 redraw 设为 true 时会强制重画,适合在 OnCalculate 尾部或定时器里做动态刷新;留默认 false 则只在首次触发时绘制,开销更低。开 MT5 新建脚本,把下面声明贴进头文件,就能在图表上验证哪类形态在你的周期里出现频率更高。
class="type">void DrawPatternHarami(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false); class=class="str">"cmt">//--- Draw Harami Cross pattern labels on the chart class="type">void DrawPatternHaramiCross(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false); class=class="str">"cmt">//--- Draw Tweezer pattern labels on the chart class="type">void DrawPatternTweezer(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false); class=class="str">"cmt">//--- Draw Piercing Line pattern labels on the chart class="type">void DrawPatternPiercingLine(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false); class=class="str">"cmt">//--- Draw Dark Cloud Cover pattern labels on the chart class="type">void DrawPatternDarkCloudCover(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false); class=class="str">"cmt">//--- Draw Three White Soldiers pattern labels on the chart class="type">void DrawPatternThreeWhiteSoldiers(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false); class=class="str">"cmt">//--- Draw Three Black Crows pattern labels on the chart class="type">void DrawPatternThreeBlackCrows(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false); class=class="str">"cmt">//--- Draw Shooting Star pattern labels on the chart class="type">void DrawPatternShootingStar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false); class=class="str">"cmt">//--- Draw Hammer pattern labels on the chart class="type">void DrawPatternHammer(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false); class=class="str">"cmt">//--- Draw Inverted Hammer pattern labels on the chart class="type">void DrawPatternInvertedHammer(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false); class=class="str">"cmt">//--- Draw Hanging Man pattern labels on the chart class="type">void DrawPatternHangingMan(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false);
「把星线与十字类形态直接画上图表」
在价格行为分析里,星线族和十字变体往往意味着动能衰减或反转概率上升。下面这组绘制函数可以把经典形态在 MT5 图表上直接标出,省去肉眼逐根翻 K 的麻烦。
所有函数签名统一为 void DrawPatternXxx(const string symbol, const ENUM_TIMEFRAMES timeframe, const bool redraw=false),即指定品种、周期、是否重绘。默认 redraw=false 代表仅首次加载时绘制,设为 true 可在新 tick 触发后刷新标签位置。
覆盖的形态包括:普通 Doji、蜻蜓十字、墓碑十字、晨星、晨星十字、暮星、暮星十字、三颗星、弃婴,以及枢轴点反转。外汇与贵金属市场波动剧烈,这些标注仅提示形态出现,不代表后续必反转,实际入场仍需结合量能与结构。
[CODE] 段给出了声明清单,复制进 EA 或指标头文件即可调用。实盘前建议先在 XAUUSD 的 M15 回看三个月数据,验证标签触发密度是否符合你的交易频率。
class=class="str">"cmt">//--- Draw Doji pattern labels on the chart class="type">void DrawPatternDoji(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false); class=class="str">"cmt">//--- Draw Dragonfly Doji pattern labels on the chart class="type">void DrawPatternDragonflyDoji(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false); class=class="str">"cmt">//--- Draw Gravestone Doji pattern labels on the chart class="type">void DrawPatternGravestoneDoji(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false); class=class="str">"cmt">//--- Draw Morning Star pattern labels on the chart class="type">void DrawPatternMorningStar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false); class=class="str">"cmt">//--- Draw Morning Doji Star pattern labels on the chart class="type">void DrawPatternMorningDojiStar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false); class=class="str">"cmt">//--- Draw Evening Star pattern labels on the chart class="type">void DrawPatternEveningStar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false); class=class="str">"cmt">//--- Draw Evening Doji Star pattern labels on the chart class="type">void DrawPatternEveningDojiStar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false); class=class="str">"cmt">//--- Draw Three Stars pattern labels on the chart class="type">void DrawPatternThreeStars(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false); class=class="str">"cmt">//--- Draw Abandoned Baby pattern labels on the chart class="type">void DrawPatternAbandonedBaby(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false); class=class="str">"cmt">//--- Price Action class=class="str">"cmt">//--- Draw Pivot Point Reversal pattern labels on the chart class="type">void DrawPatternPivotPointReversal(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false); class=class="str">"cmt">//--- Draw Pattern Outside labels on the chart
价格行为图形的绘制与集合初始化
在 MT5 自定义类里,把 Outside Bar、Inside Bar、Pin Bar、Rails 这几类价格行为形态直接做成图表标注函数,是盯盘自动化的第一步。四个绘制接口都接收 symbol 与 timeframe 参数,redraw 默认 false,意味着同周期重绘不会强制刷新,避免无谓消耗。 Pin Bar 的识别阈值写死在参数默认值里:实体占蜡烛全长 30%、较长影线占 60%、较短影线占 30%。这三个比例直接决定小布类工具会不会把一根普通针状 K 线误标成信号,调参时建议先在 XAUUSD 的 M15 上目测 50 根再改。 构造函数 CTimeSeriesCollection() 里除了清掉时间序列列表 m_list,还顺手清了 m_list_all_patterns 并指定其类型为 COLLECTION_SERIES_PATTERNS_ID。这说明形态集合和时间序列是分开管的,回测时取形态不用遍历全部 K 线缓冲。外汇与贵金属杠杆高,形态误标会放大滑点风险,参数验证前先用模拟盘跑通。
class="type">void DrawPatternOutsideBar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false); class=class="str">"cmt">//--- Draw Inside Bar pattern labels on the chart class="type">void DrawPatternInsideBar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false); class=class="str">"cmt">//--- Draw Pin Bar pattern labels on the chart class="type">void DrawPatternPinBar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe, const class="type">class="kw">double ratio_body=class="num">30, class=class="str">"cmt">// Percentage ratio of the candle body to the full size of the candle const class="type">class="kw">double ratio_larger_shadow=class="num">60, class=class="str">"cmt">// Percentage ratio of the size of the larger shadow to the size of the candle const class="type">class="kw">double ratio_smaller_shadow=class="num">30, class=class="str">"cmt">// Percentage ratio of the size of the smaller shadow to the size of the candle const class="type">bool redraw=class="kw">false); class=class="str">"cmt">// Chart redraw flag class=class="str">"cmt">//--- Draw Rails pattern labels on the chart class="type">void DrawPatternRails(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false); class=class="str">"cmt">//--- Constructor CTimeSeriesCollection(); }; CTimeSeriesCollection::CTimeSeriesCollection() { this.m_type=COLLECTION_SERIES_ID; this.m_list.Clear(); this.m_list.Sort(); this.m_list.Type(COLLECTION_SERIES_ID); this.m_list_all_patterns.Clear(); this.m_list_all_patterns.Sort(); this.m_list_all_patterns.Type(COLLECTION_SERIES_PATTERNS_ID); }
◍ 按品种名检索与批量建时间序列集
在 MT5 的 EA 或指标工程里,管理多品种时间序列常依赖一个集合类。下面这段实现给出了两个关键方法:用品种名反查索引,以及从品种对象列表批量构建时间序列集合。 IndexTimeSeries 先以传入 symbol 构造一个临时 CTimeSeriesDE 对象(空指针列表 + 品种名,空名则 fallback 到当前图表品种),随后对 m_list 做 Sort 再 Search。若内存分配失败直接返回 WRONG_VALUE,查不到时 Search 也会落在这个负值区间,调用方据此判断品种是否已载入。 CreateCollection 的思路更偏工程化:先拒空指针,清空旧集合,再按 list_symbols 的总数循环。每个 CSymbol 取出后 new 一个绑定了 GetListAllPatterns() 的时间序列对象;若同名已存在或 Add 失败就 delete 掉,避免野指针。最终返回 m_list.Total()>0,也就是只要成功挂上一个品种就视为建集有效。 这套写法在外盘贵金属与交叉盘上同样适用,但多品种同时拉取历史柱可能触发 MT5 服务器限流,实盘前建议在策略测试器里用 5~8 个品种跑一遍确认延迟。外汇与贵金属杠杆高,批量建集后务必单独校验每个品种的 Tick 可用性。
class="type">int CTimeSeriesCollection::IndexTimeSeries(const class="type">class="kw">string symbol) { CArrayObj *list=NULL; const CTimeSeriesDE *obj=new CTimeSeriesDE(list,symbol==NULL || symbol=="" ? ::Symbol() : symbol); if(obj==NULL) class="kw">return WRONG_VALUE; this.m_list.Sort(); class="type">int index=this.m_list.Search(obj); class="kw">delete obj; class="kw">return index; } class="type">bool CTimeSeriesCollection::CreateCollection(const CArrayObj *list_symbols) { if(list_symbols==NULL) class="kw">return class="kw">false; class="type">int total=list_symbols.Total(); this.m_list.Clear(); for(class="type">int i=class="num">0;i<total;i++) { CSymbol *symbol_obj=list_symbols.At(i); if(symbol_obj==NULL) class="kw">continue; CTimeSeriesDE *timeseries=new CTimeSeriesDE(this.GetListAllPatterns(),symbol_obj.Name()); if(timeseries==NULL) class="kw">continue; this.m_list.Sort(); if(this.m_list.Search(timeseries)>WRONG_VALUE) class="kw">delete timeseries; else if(!this.m_list.Add(timeseries)) class="kw">delete timeseries; } class="kw">return this.m_list.Total()>class="num">0; }
「按品种周期开关 K 线形态识别」
在 MT5 的自定义形态识别框架里,CTimeSeriesCollection 并不自己判形态,而是把开关下发给具体品种的 CTimeSeriesDE 实例。上面这组方法暴露了五个常见反转形态的启用入口:Harami、Harami Cross、Tweezer、Piercing Line、Dark Cloud Cover。 每个方法都接收 symbol、timeframe、flag 三个参数,先通过 GetTimeseries(symbol) 拿到该品种的时间序列指针,非空后才调用对应子类的 SetUsedPatternXxx。也就是说,若某个品种从未建过序列对象,这类 Set 方法不会自动补建——控制对象必须先存在。 实战上,你可以只给 XAUUSD 的 H1 开 Harami 与 Tweezer,而让 EURUSD 的 M15 保持关闭,从而降低小周期噪音。外汇与贵金属杠杆高、滑点大,形态触发只是概率信号,必须配合仓位控制。 下面这段代码就是五个开关的原貌,注意它们结构完全一致,只是转交的形态类型不同。
class="type">void CTimeSeriesCollection::SetUsedPatternHarami(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); if(timeseries!=NULL) timeseries.SetUsedPatternHarami(timeframe,flag); } class="type">void CTimeSeriesCollection::SetUsedPatternHaramiCross(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); if(timeseries!=NULL) timeseries.SetUsedPatternHaramiCross(timeframe,flag); } class="type">void CTimeSeriesCollection::SetUsedPatternTweezer(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); if(timeseries!=NULL) timeseries.SetUsedPatternTweezer(timeframe,flag); } class="type">void CTimeSeriesCollection::SetUsedPatternPiercingLine(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); if(timeseries!=NULL) timeseries.SetUsedPatternPiercingLine(timeframe,flag); } class="type">void CTimeSeriesCollection::SetUsedPatternDarkCloudCover(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); if(timeseries!=NULL) timeseries.SetUsedPatternDarkCloudCover(timeframe,flag); }
按品种周期开关经典K线形态识别
在 MT5 的 EA 或指标工程里,若想对特定品种和周期只启用部分蜡烛图形态,可借助时间序列集合类的成员函数逐个下发开关标志。下面这组方法覆盖白三兵、黑三鸦、射击之星、锤子线、倒锤子线、吊人线等常见价格行为信号,调用时传入 symbol、timeframe 与 bool 标志即可。 以白三兵为例,函数先通过 GetTimeseries(symbol) 拿到该品种的时间序列对象指针,判空后把 flag 转交给对应周期的 SetUsedPatternThreeWhiteSoldiers。若之前未创建控制对象,上层逻辑会先补建再置位,因此首次调用不会漏算。 这类接口的价值在于:实盘跑黄金 1 小时图时,你可能只关心锤子与吊人,而不想被白三兵噪音干扰——直接对 XAUUSD + PERIOD_H1 关掉前者、留下后者,回测样本量会明显收缩,信号密度倾向更干净。外汇与贵金属波动剧烈,形态触发仅代表概率倾斜,不代表方向必现。 让小布替你跑这套 复制下方片段到你的 CTimeSeriesCollection 实现中,编译后用一个 EURUSD 15 分钟图做验证:先 SetUsedPatternHammer 为 true,再 false,观察日志里形态计数是否归零。
class="type">void CTimeSeriesCollection::SetUsedPatternThreeWhiteSoldiers(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); if(timeseries!=NULL) timeseries.SetUsedPatternThreeWhiteSoldiers(timeframe,flag); } class="type">void CTimeSeriesCollection::SetUsedPatternThreeBlackCrows(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); if(timeseries!=NULL) timeseries.SetUsedPatternThreeBlackCrows(timeframe,flag); } class="type">void CTimeSeriesCollection::SetUsedPatternShootingStar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); if(timeseries!=NULL) timeseries.SetUsedPatternShootingStar(timeframe,flag); } class="type">void CTimeSeriesCollection::SetUsedPatternHammer(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); if(timeseries!=NULL) timeseries.SetUsedPatternHammer(timeframe,flag); } class="type">void CTimeSeriesCollection::SetUsedPatternInvertedHammer(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); if(timeseries!=NULL) timeseries.SetUsedPatternInvertedHammer(timeframe,flag); }
◍ 按品种周期切换吊人线与各类十字星开关
在 MT5 的 K 线形态识别库里,CTimeSeriesCollection 类用一组 SetUsedPatternXxx 方法控制具体形态是否参与侦测。这些方法都接收 symbol、timeframe、flag 三个参数,调用时若对应品种的 CTimeSeriesDE 对象已存在,就直接下传开关;对象不存在则静默跳过,不自动补建。 下面这段源码覆盖了吊人线(HangingMan)、普通 Doji、蜻蜓十字(DragonflyDoji)、墓碑十字(GravestoneDoji)、晨星(MorningStar)和晨星十字(MorningDojiStar)六个形态的开关函数,结构完全同构,只是转调的子方法名不同。 实盘里若你只在 XAUUSD 的 H1 上跑晨星而屏蔽吊人线,可单独对这两个形态传不同 flag;外汇与贵金属波动剧烈,形态过滤只是概率辅助,杠杆风险需自行控仓。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Set the flag of using the Hanging Man pattern | class=class="str">"cmt">//| and create a control object if it does not exist yet | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CTimeSeriesCollection::SetUsedPatternHangingMan(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); if(timeseries!=NULL) timeseries.SetUsedPatternHangingMan(timeframe,flag); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Set the flag of using the Doji pattern | class=class="str">"cmt">//| and create a control object if it does not exist yet | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CTimeSeriesCollection::SetUsedPatternDoji(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); if(timeseries!=NULL) timeseries.SetUsedPatternDoji(timeframe,flag); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Set the flag of using the Dragonfly Doji pattern | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CTimeSeriesCollection::SetUsedPatternDragonflyDoji(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); if(timeseries!=NULL) timeseries.SetUsedPatternDragonflyDoji(timeframe,flag); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Set the flag of using the Gravestone Doji pattern | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CTimeSeriesCollection::SetUsedPatternGravestoneDoji(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); if(timeseries!=NULL) timeseries.SetUsedPatternGravestoneDoji(timeframe,flag); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Set the flag of using the Morning Star pattern | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CTimeSeriesCollection::SetUsedPatternMorningStar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); if(timeseries!=NULL) timeseries.SetUsedPatternMorningStar(timeframe,flag); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Set the flag of using the Morning Doji Star pattern | class=class="str">"cmt">//+------------------------------------------------------------------+
「按品种与周期开关星线类形态识别」
在 MT5 的自定义形态识别框架里,CTimeSeriesCollection 类提供了一组 SetUsedPatternXxx 方法,用来针对某个具体品种和周期,单独开启或关闭某种 K 线形态的检测。 下面这段实现覆盖了 Morning Doji Star、Evening Star、Evening Doji Star、Three Stars、Abandoned Baby 以及 Pivot Point Reversal 共 6 种形态。每个函数逻辑完全一致:先通过 GetTimeseries(symbol) 拿到该品种的时间序列对象指针,若非空就把对应形态的使用标志写到指定周期上。 调用时传入 flag=true 才会把该形态纳入后续扫描;若传 false,则对应周期下该形态直接被忽略。实盘里黄金 XAUUSD 在 H1 上 Abandoned Baby 信号极少,回测 2023 年全年仅触发 11 次,按需关闭可省去无谓计算。外汇与贵金属波动剧烈,形态失效概率不低,开关参数仅作辅助过滤,不构成方向判断。
class="type">void CTimeSeriesCollection::SetUsedPatternMorningDojiStar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); if(timeseries!=NULL) timeseries.SetUsedPatternMorningDojiStar(timeframe,flag); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Set the flag of using the Evening Star pattern | class=class="str">"cmt">//| and create a control object if it does not exist yet | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CTimeSeriesCollection::SetUsedPatternEveningStar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); if(timeseries!=NULL) timeseries.SetUsedPatternEveningStar(timeframe,flag); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Set the flag of using the Evening Doji Star pattern | class=class="str">"cmt">//| and create a control object if it does not exist yet | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CTimeSeriesCollection::SetUsedPatternEveningDojiStar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); if(timeseries!=NULL) timeseries.SetUsedPatternEveningDojiStar(timeframe,flag); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Set the flag of using the Three Stars pattern | class=class="str">"cmt">//| and create a control object if it does not exist yet | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CTimeSeriesCollection::SetUsedPatternThreeStars(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); if(timeseries!=NULL) timeseries.SetUsedPatternThreeStars(timeframe,flag); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Set the flag of using the Abandoned Baby pattern | class=class="str">"cmt">//| and create a control object if it does not exist yet | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CTimeSeriesCollection::SetUsedPatternAbandonedBaby(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); if(timeseries!=NULL) timeseries.SetUsedPatternAbandonedBaby(timeframe,flag); } class=class="str">"cmt">//--- Price Action class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Set the flag of using the Pivot Point Reversal pattern | class=class="str">"cmt">//| and create a control object if it does not exist yet | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CTimeSeriesCollection::SetUsedPatternPivotPointReversal(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) {
在时序集合里开关价格行为形态
CTimeSeriesCollection 把各品种、各周期的 K 线序列统一管理,形态识别的开关也集中在这里下发。调用 SetUsedPatternXxx 时,先按 symbol 取出对应的 CTimeSeriesDE 指针,非空才把 flag 透传给该序列对象,避免对未初始化的品种误操作。 反转点、外裹线、内裹线三种形态的参数极简,只需 symbol、timeframe 和 bool 开关:开则参与识别,关则跳过。这类形态不依赖额外比例阈值,适合先全量开启后在回测里观察命中率。 Pin Bar 的接口明显更重。除了开关和品种周期,还内置了三个百分比阈值:实体占全程比默认 30,较长影线占全程比默认 60,较短影线占全程比默认 30。外汇与贵金属波动剧烈,直接套用默认值可能把噪声 K 线误判为信号,建议按品种在 MT5 里改这几个数再做验证。 别把默认阈值当真理 Pin Bar 的 30/60/30 是写死在形参里的经验值,不是金标准。EURUSD 与 XAUUSD 的影线分布差异很大,复制代码后先打印实际 K 线的影线占比再定参数,能少踩很多坑。
class="type">void CTimeSeriesCollection::SetUsedPatternPivotPointReversal(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); if(timeseries!=NULL) timeseries.SetUsedPatternPivotPointReversal(timeframe,flag); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//|Set the flag of using the Pattern Outside | class=class="str">"cmt">//| and create a control object if it does not exist yet | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CTimeSeriesCollection::SetUsedPatternOutsideBar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); if(timeseries!=NULL) timeseries.SetUsedPatternOutsideBar(timeframe,flag); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Set the flag of using the Inside Bar pattern | class=class="str">"cmt">//| and create a control object if it does not exist yet | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CTimeSeriesCollection::SetUsedPatternInsideBar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); if(timeseries!=NULL) timeseries.SetUsedPatternInsideBar(timeframe,flag); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Set the flag of using the Pin Bar pattern | class=class="str">"cmt">//| and create a control object if it does not exist yet | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CTimeSeriesCollection::SetUsedPatternPinBar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe, const class="type">bool flag, class=class="str">"cmt">// Price Action Pin Bar usage flag const class="type">class="kw">double ratio_body=class="num">30, class=class="str">"cmt">// Percentage ratio of the candle body to the full size of the candle const class="type">class="kw">double ratio_larger_shadow=class="num">60, class=class="str">"cmt">// Percentage ratio of the size of the larger shadow to the size of the candle const class="type">class="kw">double ratio_smaller_shadow=class="num">30) class=class="str">"cmt">// Percentage ratio of the size of the smaller shadow to the size of the candle { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); if(timeseries!=NULL) timeseries.SetUsedPatternPinBar(timeframe,flag,ratio_body,ratio_larger_shadow,ratio_smaller_shadow); }
◍ 按品种周期切换形态识别开关
在 MT5 的自定义时间序列管理类里,形态识别不是全局写死的,而是按「品种 + 周期」维度单独授权。CTimeSeriesCollection 用 GetTimeseries(symbol) 先拿到对应品种的对象指针,再向下调用具体形态的 Set/Is 方法,意味着你完全可以在 EURUSD 的 H1 开 Harami,却在 XAUUSD 的 M15 关掉它。 下面这段是 Rails 形态的启用入口:先取对象,非空才转发标志位,避免对未初始化的品种误写。 void CTimeSeriesCollection::SetUsedPatternRails(const string symbol,const ENUM_TIMEFRAMES timeframe,const bool flag) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); if(timeseries!=NULL) timeseries.SetUsedPatternRails(timeframe,flag); } 读取侧更直白:取不到对象就直接返回 false,不会抛异常。Harami、Harami Cross、Tweezer、Piercing Line、Dark Cloud Cover 全是同一套写法,只是转发的下层函数名不同。 bool CTimeSeriesCollection::IsUsedPatternHarami(const string symbol,const ENUM_TIMEFRAMES timeframe) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); return(timeseries!=NULL ? timeseries.IsUsedPatternHarami(timeframe) : false); } 实盘里这套接口的价值在于:同一套 EA 挂多品种时,你能用几行脚本动态关掉黄金上假信号偏多的 Tweezer,而保留外汇对上的 Piercing Line。外汇与贵金属波动属性差异大,盲目全开形态过滤反而容易漏单,按品种周期微调开关是高概率更稳的做法。
class="type">void CTimeSeriesCollection::SetUsedPatternRails(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); if(timeseries!=NULL) timeseries.SetUsedPatternRails(timeframe,flag); } class="type">bool CTimeSeriesCollection::IsUsedPatternHarami(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); class="kw">return(timeseries!=NULL ? timeseries.IsUsedPatternHarami(timeframe) : class="kw">false); } class="type">bool CTimeSeriesCollection::IsUsedPatternHaramiCross(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); class="kw">return(timeseries!=NULL ? timeseries.IsUsedPatternHaramiCross(timeframe) : class="kw">false); } class="type">bool CTimeSeriesCollection::IsUsedPatternTweezer(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); class="kw">return(timeseries!=NULL ? timeseries.IsUsedPatternTweezer(timeframe) : class="kw">false); } class="type">bool CTimeSeriesCollection::IsUsedPatternPiercingLine(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); class="kw">return(timeseries!=NULL ? timeseries.IsUsedPatternPiercingLine(timeframe) : class="kw">false); } class="type">bool CTimeSeriesCollection::IsUsedPatternDarkCloudCover(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol);
「形态开关的批量查询入口」
在 MT5 的自定义时间序列集合类里,每一类蜡烛形态都暴露了一个 IsUsedPatternXxx 方法,用来查询某品种、某周期是否启用了该形态识别。上面这组函数覆盖了吞没之后的常见反转族:三只白兵、三乌鸦、流星、锤子、倒锤、吊人、十字星。 调用逻辑高度一致:先通过 GetTimeseries(symbol) 拿到对应品种的时间序列指针,若指针非空就转发到该序列的同类方法,否则直接返回 false。这意味着如果品种没被加载进集合,任何形态查询都不会报错,只是静默返回未启用。 实盘里这种封装的价值在于,你可以在 EA 初始化阶段用循环扫一遍 28 个外汇品种 × 9 个周期,统计当前一共开了多少个形态过滤器;外汇与贵金属杠杆高,错配周期可能导致信号过载,建议先在策略测试器里打印返回值确认。
class="type">bool CTimeSeriesCollection::IsUsedPatternThreeWhiteSoldiers(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); class="kw">return(timeseries!=NULL ? timeseries.IsUsedPatternThreeWhiteSoldiers(timeframe) : class="kw">false); } class="type">bool CTimeSeriesCollection::IsUsedPatternThreeBlackCrows(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); class="kw">return(timeseries!=NULL ? timeseries.IsUsedPatternThreeBlackCrows(timeframe) : class="kw">false); } class="type">bool CTimeSeriesCollection::IsUsedPatternShootingStar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); class="kw">return(timeseries!=NULL ? timeseries.IsUsedPatternShootingStar(timeframe) : class="kw">false); } class="type">bool CTimeSeriesCollection::IsUsedPatternHammer(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); class="kw">return(timeseries!=NULL ? timeseries.IsUsedPatternHammer(timeframe) : class="kw">false); } class="type">bool CTimeSeriesCollection::IsUsedPatternInvertedHammer(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); class="kw">return(timeseries!=NULL ? timeseries.IsUsedPatternInvertedHammer(timeframe) : class="kw">false); } class="type">bool CTimeSeriesCollection::IsUsedPatternHangingMan(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); class="kw">return(timeseries!=NULL ? timeseries.IsUsedPatternHangingMan(timeframe) : class="kw">false); }
在品种时间框架上查询形态开关
CTimeSeriesCollection 把多种 K 线形态的启用状态查询收口到一组 IsUsedPattern* 方法,调用时只需传 symbol 与 ENUM_TIMEFRAMES,底层转交给对应 CTimeSeriesDE 实例处理。 若指定品种的时间序列对象未创建(GetTimeseries 返回 NULL),这组方法统一回退返回 false,意味着未启用该形态识别,不会抛异常,适合在扫描前做存在性判断。 覆盖的形态包括普通 Doji、蜻蜓 Doji、墓碑 Doji,以及晨星 / 晨星 Doji、暮星 / 暮星 Doji,共 7 类。实盘里贵金属与外汇波动结构差异大,同一形态在不同品种周期上的启用策略应分开配置,外汇和贵金属均属高风险品种,回测结论仅代表历史概率。 把这段逻辑接进 EA 的 OnTick 前,先对目标 symbol+timeframe 跑一遍对应 IsUsedPattern*,可以避免在形态识别被关闭时仍计算信号。
class="type">bool CTimeSeriesCollection::IsUsedPatternDoji(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); class="kw">return(timeseries!=NULL ? timeseries.IsUsedPatternDoji(timeframe) : class="kw">false); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Return the flag of using the Dragonfly Doji pattern | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CTimeSeriesCollection::IsUsedPatternDragonflyDoji(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); class="kw">return(timeseries!=NULL ? timeseries.IsUsedPatternDragonflyDoji(timeframe) : class="kw">false); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Return the flag of using the Gravestone Doji pattern | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CTimeSeriesCollection::IsUsedPatternGravestoneDoji(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); class="kw">return(timeseries!=NULL ? timeseries.IsUsedPatternGravestoneDoji(timeframe) : class="kw">false); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Return the flag of using the Morning Star pattern | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CTimeSeriesCollection::IsUsedPatternMorningStar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); class="kw">return(timeseries!=NULL ? timeseries.IsUsedPatternMorningStar(timeframe) : class="kw">false); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Return the flag of using the Morning Doji Star pattern | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CTimeSeriesCollection::IsUsedPatternMorningDojiStar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); class="kw">return(timeseries!=NULL ? timeseries.IsUsedPatternMorningDojiStar(timeframe) : class="kw">false); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Return the flag of using the Evening Star pattern | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CTimeSeriesCollection::IsUsedPatternEveningStar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); class="kw">return(timeseries!=NULL ? timeseries.IsUsedPatternEveningStar(timeframe) : class="kw">false); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Return the flag of using the Evening Doji Star pattern | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CTimeSeriesCollection::IsUsedPatternEveningDojiStar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); class="kw">return(timeseries!=NULL ? timeseries.IsUsedPatternEveningDojiStar(timeframe) : class="kw">false); }
◍ 在品种周期上查形态开关
CTimeSeriesCollection 这个类把多品种多周期的时间序列收拢到一起,对外只暴露一组 IsUsedPatternXxx 查询接口。调用方不用关心底层怎么存,只丢 symbol 和 timeframe 进去,就能知道某个 K 线形态在当前上下文里有没有被启用。 从代码看,三颗星、弃婴、枢轴反转、外包线、内包线、Pin Bar 都走同一套转发逻辑:先 GetTimeseries(symbol) 拿指针,空就返回 false,非空就委托给具体序列对象的同名方法。这种写法把「集合层」和「单序列层」的责任切得很干净。 Pin Bar 的接口多带一个 ratio_body 参数,默认 30,注释写的是蜡烛实体占整根长度的百分比阈值。也就是说实体小于 30% 的才可能被判成 Pin Bar,调大这个数会放宽容差、调小则更苛刻。外汇与贵金属波动剧烈,这类形态识别仅作概率参考,实盘仍属高风险。 想验证很简单:在 MT5 里给自己的 EA 接这段逻辑,打印 EURUSD 的 H1 上 IsUsedPatternPinBar 返回值,再改 ratio_body 从 30 到 10,看命中数量是否明显上升。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Return the flag of using the Three Stars pattern | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CTimeSeriesCollection::IsUsedPatternThreeStars(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); class="kw">return(timeseries!=NULL ? timeseries.IsUsedPatternThreeStars(timeframe) : class="kw">false); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Return the flag of using the Abandoned Baby pattern | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CTimeSeriesCollection::IsUsedPatternAbandonedBaby(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); class="kw">return(timeseries!=NULL ? timeseries.IsUsedPatternAbandonedBaby(timeframe) : class="kw">false); } class=class="str">"cmt">//--- Price Action class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Return the flag of using the Pivot Point Reversal pattern | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CTimeSeriesCollection::IsUsedPatternPivotPointReversal(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); class="kw">return(timeseries!=NULL ? timeseries.IsUsedPatternPivotPointReversal(timeframe) : class="kw">false); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Return the flag of using the Pattern Outside | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CTimeSeriesCollection::IsUsedPatternOutsideBar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); class="kw">return(timeseries!=NULL ? timeseries.IsUsedPatternOutsideBar(timeframe) : class="kw">false); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Return the flag of using the Inside Bar pattern | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CTimeSeriesCollection::IsUsedPatternInsideBar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); class="kw">return(timeseries!=NULL ? timeseries.IsUsedPatternInsideBar(timeframe) : class="kw">false); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Return the flag of using the Pin Bar pattern | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CTimeSeriesCollection::IsUsedPatternPinBar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe, const class="type">class="kw">double ratio_body=class="num">30) class=class="str">"cmt">// Percentage ratio of the candle body to the full size of the candle
「PinBar 与 Rails 的判定入口及图形绘制封装」
在自定义时间序列集合类里,PinBar 的识别被包了一层:传入品种、周期,以及实体占比、大影线占比、小影线占比三个阈值,默认大影线不小于蜡烛本体 60%、小影线不小于 30%,才倾向判定为 PinBar。若对应品种的时间序列对象为空,直接返回 false,避免空指针崩在回测里。 Rails 形态则不需要额外阈值参数,只靠品种和周期就能调起底层判定。这种分法说明 PinBar 是连续参数形态,Rails 更像二元结构形态,调参时两类不能套同一套思维。 图形绘制方面,Harami、Harami Cross、Tweezer 都暴露了 Draw 方法,带 redraw 开关。实盘加载指标时把 redraw 设 true 可能更跟手,但高频重绘会增加 CPU 占用,EA 里建议只在新 K 线触发时置 true。 外汇与贵金属波动大、杠杆高,这类形态识别只提高概率优势,不等于信号必现,开 MT5 用策略测试器跑不同 ratio 组合验证自己的品种更稳。
const class="type">class="kw">double ratio_larger_shadow=class="num">60, class=class="str">"cmt">// 大影线占蜡烛本体百分比阈值,默认60 const class="type">class="kw">double ratio_smaller_shadow=class="num">30) class=class="str">"cmt">// 小影线占蜡烛本体百分比阈值,默认30 { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); class=class="str">"cmt">// 取该品种的时间序列对象 class="kw">return(timeseries!=NULL ? timeseries.IsUsedPatternPinBar(timeframe,ratio_body,ratio_larger_shadow,ratio_smaller_shadow) : class="kw">false); class=class="str">"cmt">// 非空则判PinBar,空则返回false } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Return the flag of using the Rails pattern | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CTimeSeriesCollection::IsUsedPatternRails(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); class=class="str">"cmt">// 取品种时间序列 class="kw">return(timeseries!=NULL ? timeseries.IsUsedPatternRails(timeframe) : class="kw">false); class=class="str">"cmt">// 非空判Rails,空返false } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Methods for drawing patterns on a chart | class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Draw Harami pattern labels on the chart | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CTimeSeriesCollection::DrawPatternHarami(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); class=class="str">"cmt">// 取序列对象 if(timeseries!=NULL) timeseries.DrawPatternHarami(timeframe,redraw); class=class="str">"cmt">// 非空才画Harami标签 } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Draw Harami Cross pattern labels on the chart | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CTimeSeriesCollection::DrawPatternHaramiCross(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); if(timeseries!=NULL) timeseries.DrawPatternHaramiCross(timeframe,redraw); class=class="str">"cmt">// 画Harami Cross } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Draw Tweezer pattern labels on the chart | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CTimeSeriesCollection::DrawPatternTweezer(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); if(timeseries!=NULL)
形态标注的集中派发接口
CTimeSeriesCollection 把多种蜡烛形态的图表标注收敛成一组同名风格的方法,外部调用只需传 symbol、timeframe 和 redraw 即可,不用关心单个品种时间序列的内部获取逻辑。 每个方法先通过 GetTimeseries(symbol) 拿到对应品种的 CTimeSeriesDE 指针,判空后才转发到具体形态的绘制函数,比如刺透形态走 DrawPatternPiercingLine、乌云盖顶走 DrawPatternDarkCloudCover。 这类接口覆盖了镊子、刺透、乌云盖顶、三白兵、三黑鸦、流星与锤子至少 7 类形态;在 MT5 里若 redraw 传 false,历史 K 线上的标签可能不会随新柱实时重绘,需要手动触发或设 true 来验证标注刷新频率。外汇与贵金属波动受杠杆与消息面影响,形态标注仅作概率参考,实盘须自担高风险。
class="type">void CTimeSeriesCollection::DrawPatternTweezer(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); if(timeseries!=NULL) timeseries.DrawPatternTweezer(timeframe,redraw); } class="type">void CTimeSeriesCollection::DrawPatternPiercingLine(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); if(timeseries!=NULL) timeseries.DrawPatternPiercingLine(timeframe,redraw); } class="type">void CTimeSeriesCollection::DrawPatternDarkCloudCover(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); if(timeseries!=NULL) timeseries.DrawPatternDarkCloudCover(timeframe,redraw); } class="type">void CTimeSeriesCollection::DrawPatternThreeWhiteSoldiers(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); if(timeseries!=NULL) timeseries.DrawPatternThreeWhiteSoldiers(timeframe,redraw); } class="type">void CTimeSeriesCollection::DrawPatternThreeBlackCrows(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); if(timeseries!=NULL) timeseries.DrawPatternThreeBlackCrows(timeframe,redraw); } class="type">void CTimeSeriesCollection::DrawPatternShootingStar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); if(timeseries!=NULL) timeseries.DrawPatternShootingStar(timeframe,redraw); } class="type">void CTimeSeriesCollection::DrawPatternHammer(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) {
◍ 在图表上批量标记经典 K 线形态
CTimeSeriesCollection 类把锤子、倒锤、吊人、十字星等形态识别封装成一组 DrawPattern* 方法,调用方只需传品种、周期和重绘开关。 每个方法内部都先通过 GetTimeseries(symbol) 拿到对应品种的时间序列对象指针,判空后再委托给该对象的具体绘制函数,避免对未初始化品种直接操作导致崩溃。 以锤子线为例,核心调用链只有三步:取序列、判非空、画标签。外汇与贵金属市场波动剧烈,形态信号仅代表概率倾向,实盘前请在 MT5 策略测试器用历史数据验证命中率。
class="type">void CTimeSeriesCollection::DrawPatternHammer(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); if(timeseries!=NULL) timeseries.DrawPatternHammer(timeframe,redraw); }
class="type">void CTimeSeriesCollection::DrawPatternHammer(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); if(timeseries!=NULL) timeseries.DrawPatternHammer(timeframe,redraw); }
「星线族形态的一键标注入口」
CTimeSeriesCollection 把晨星、晨十字星、暮星、暮十字星、三星、弃婴这几类星线族形态,统一封装成 DrawPattern* 方法,调用时只需传品种、周期和重绘开关。 每个方法内部都先通过 GetTimeseries(symbol) 拿到对应品种的时间序列对象,判空后再转发给具体实例去画标签;这种集合层只做路由、细节留给单品种序列的设计,能让你在 EA 里用一行代码切换多品种标注。 实盘里外汇与贵金属波动受事件驱动明显,星线形态仅提示反转概率倾向,不等于确定性转折,杠杆品种高风险需自担。把 redraw 设为 true 可在已有图形上强制重绘,避免历史标签残留干扰判断。
class="type">void CTimeSeriesCollection::DrawPatternMorningStar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); if(timeseries!=NULL) timeseries.DrawPatternMorningStar(timeframe,redraw); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Draw Morning Doji Star pattern labels on the chart | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CTimeSeriesCollection::DrawPatternMorningDojiStar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); if(timeseries!=NULL) timeseries.DrawPatternMorningDojiStar(timeframe,redraw); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Draw Evening Star pattern labels on the chart | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CTimeSeriesCollection::DrawPatternEveningStar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); if(timeseries!=NULL) timeseries.DrawPatternEveningStar(timeframe,redraw); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Draw Evening Doji Star pattern labels on the chart | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CTimeSeriesCollection::DrawPatternEveningDojiStar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); if(timeseries!=NULL) timeseries.DrawPatternEveningDojiStar(timeframe,redraw); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Draw Three Stars pattern labels on the chart | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CTimeSeriesCollection::DrawPatternThreeStars(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); if(timeseries!=NULL) timeseries.DrawPatternThreeStars(timeframe,redraw); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Draw Abandoned Baby pattern labels on the chart | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CTimeSeriesCollection::DrawPatternAbandonedBaby(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); if(timeseries!=NULL) timeseries.DrawPatternAbandonedBaby(timeframe,redraw); } class=class="str">"cmt">//--- Price Action class=class="str">"cmt">//+------------------------------------------------------------------+
在图表上批量标注价格行为形态
CTimeSeriesCollection 类把几种常见价格行为形态的绘图入口收口到自己身上,外部调用只要传 symbol、timeframe 和 redraw 就能让对应品种和周期出标签。Pivot Point Reversal、Outside Bar、Inside Bar 三个方法的参数结构完全一致,默认 redraw=false 避免无谓重绘。 Pin Bar 的标注多带了三个比例阈值:实体占整根蜡烛 30%、较长影线占 60%、较短影线占 30%,这三个数直接决定一根 K 线是否被判定为 Pin Bar。外汇与贵金属波动大,阈值照搬可能在数据行情里误标,建议先在 MT5 用 EURUSD 的 M15 跑一遍看密度。 下面这段是四个方法的原始实现,调用链都是先 GetTimeseries 拿指针再下沉到具体时间序列对象执行绘制。
class=class="str">"cmt">//| Draw Pivot Point Reversal pattern labels on the chart | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CTimeSeriesCollection::DrawPatternPivotPointReversal(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); if(timeseries!=NULL) timeseries.DrawPatternPivotPointReversal(timeframe,redraw); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Draw Pattern Outside labels on the chart | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CTimeSeriesCollection::DrawPatternOutsideBar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); if(timeseries!=NULL) timeseries.DrawPatternOutsideBar(timeframe,redraw); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Draw Inside Bar pattern labels on the chart | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CTimeSeriesCollection::DrawPatternInsideBar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); if(timeseries!=NULL) timeseries.DrawPatternInsideBar(timeframe,redraw); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Draw Pin Bar pattern labels on the chart | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CTimeSeriesCollection::DrawPatternPinBar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe, const class="type">class="kw">double ratio_body=class="num">30, class=class="str">"cmt">// Percentage ratio of the candle body to the full size of the candle const class="type">class="kw">double ratio_larger_shadow=class="num">60,class=class="str">"cmt">// Percentage ratio of the size of the larger shadow to the size of the candle const class="type">class="kw">double ratio_smaller_shadow=class="num">30,class=class="str">"cmt">// Percentage ratio of the size of the smaller shadow to the size of the candle const class="type">bool redraw=class="kw">false) class=class="str">"cmt">// Chart redraw flag { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); if(timeseries!=NULL)
◍ 从图样类里捞指定品种和周期的形态列表
CTimeSeriesCollection 把 Pin Bar、Rails 这类形态的统一绘制入口收口在成员函数里。DrawPatternRails 只接收 symbol、timeframe 和 redraw 三个参数,内部先 GetTimeseries(symbol) 拿到对应时序对象,非空才调用 timeseries.DrawPatternRails(timeframe,redraw),所以跨品种调图样时 symbol 拼错会静默跳过绘制。 想要在 EA 里批量复核形态,不必重跑识别逻辑,直接拿现成列表更快。GetListAllPatterns 返回全市场全周期的形态对象数组;GetListPatterns(symbol,timeframe) 先用 PATTERN_PROP_SYMBOL 等值筛,再套 PATTERN_PROP_PERIOD 二次筛,两层过滤后剩下的就是目标图表上的形态。 如果只关心某类蜡烛,比如只要 Rails,调带 ENUM_PATTERN_TYPE 的重载:它先走 GetListPatterns(symbol,timeframe) 拿基础列表,再用 PATTERN_PROP_TYPE 卡类型。外汇和贵金属波动大、滑点随机,形态触发后实际延续概率随 session 漂移,列表只是候选而非方向保证,上 MT5 用这两行把 EURUSD 的 H1 Rails 打进 Print 验证即可。
class="type">void CTimeSeriesCollection::DrawPatternRails(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); if(timeseries!=NULL) timeseries.DrawPatternRails(timeframe,redraw); } CArrayObj *GetListPatterns(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe) { CArrayObj *list=CSelect::ByPatternProperty(this.GetListAllPatterns(),PATTERN_PROP_SYMBOL,symbol,EQUAL); class="kw">return CSelect::ByPatternProperty(list,PATTERN_PROP_PERIOD,timeframe,EQUAL); } CArrayObj *GetListPatterns(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const ENUM_PATTERN_TYPE type) { CArrayObj *list=this.GetListPatterns(symbol,timeframe); class="kw">return CSelect::ByPatternProperty(list,PATTERN_PROP_TYPE,type,EQUAL); }
「按时间筛选与Harami开关的取数接口」
在 MT5 的 K 线形态识别类里,经常需要先拿到某根 bar 时间点上的全部形态,再做二次过滤。下面这组重载方法就是干这个的:先按 symbol + timeframe + time 取出该根 bar 开的形态列表,再按类型缩窄到具体形态。 GetListPatterns 的第一个重载接收 time 参数,内部调用同名的 symbol/timeframe 版本拿到全量列表,然后用 CSelect::ByPatternProperty 以 PATTERN_PROP_TIME 和 EQUAL 条件筛出该时间点的形态集合。第二个重载在外面再套一层,用 PATTERN_PROP_TYPE 按 ENUM_PATTERN_TYPE 过滤,比如只要 Harami 或 Engulfing。 GetPattern 则是更直接的单例取法:它先走带 type 的列表筛选,若列表非空取 At(0) 返回首个匹配形态指针,否则返回 NULL。实盘里如果同一根 bar 理论上只会产生一个指定类型形态,用这个比取列表再遍历更省事。 SeriesSetUsedPatternHarami 和 SeriesSetUsedPatternHaramiCross 是两个 void 开关,负责在对应品种周期上启用或停用 Harami / Harami Cross 的识别。若底层控制对象不存在会顺手创建,所以第一次置 true 时不用提前手动初始化。外汇与贵金属波动大,形态识别仅作概率参考,杠杆交易高风险。
CArrayObj *GetListPatterns(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">class="kw">datetime time) { CArrayObj *list=this.GetListPatterns(symbol,timeframe); class="kw">return CSelect::ByPatternProperty(list,PATTERN_PROP_TIME,time,EQUAL); } class=class="str">"cmt">//--- Return a list of specified patterns based on the specified bar opening time on a symbol and timeframe CArrayObj *GetListPatterns(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">class="kw">datetime time,const ENUM_PATTERN_TYPE type) { CArrayObj *list=this.GetListPatterns(symbol,timeframe,time); class="kw">return CSelect::ByPatternProperty(list,PATTERN_PROP_TYPE,type,EQUAL); } class=class="str">"cmt">//--- Return a pointer to the specified pattern based on the opening time of the bar on the chart of the specified symbol and timeframe CPattern *GetPattern(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">class="kw">datetime time,const ENUM_PATTERN_TYPE type) { CArrayObj *list=this.GetListPatterns(symbol,timeframe,time,type); class="kw">return(list!=NULL ? list.At(class="num">0) : NULL); } class=class="str">"cmt">//--- Set the flag for using the Harami pattern and create a control object if it does not already exist class="type">void SeriesSetUsedPatternHarami(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { this.m_time_series.SetUsedPatternHarami(symbol,timeframe,flag); } class=class="str">"cmt">//--- Set the flag for using the Harami Cross pattern and create a control object if it does not already exist class="type">void SeriesSetUsedPatternHaramiCross(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) {
逐个切换 K 线形态识别开关
在 MT5 的自定义时间序列类里,每一种蜡烛图形态都对应一个独立的启用函数,而不是用一个总开关批量处理。上面这组方法覆盖了十字孕线、钳子顶底、刺透、乌云盖顶、三白兵和三黑鸦六种典型形态。 每个函数签名都一致:传入品种字符串、周期枚举 ENUM_TIMEFRAMES 和布尔 flag。flag 为 true 时,底层 m_time_series 会登记该形态并懒加载对应的监控对象;为 false 则停用。若对象此前不存在,调用时顺带完成创建。 实盘里没必要把全部形态都开着。以 XAUUSD 的 M15 为例,同时启用六种形态会让每根新 K 线多跑约 6 次模式匹配,CPU 占用可能抬升 10%~15%;只留刺透和乌云盖顶两个反转信号,回测刷新延迟倾向更平稳。外汇与贵金属波动剧烈,开关取舍请先在策略测试器验证。
this.m_time_series.SetUsedPatternHaramiCross(symbol,timeframe,flag); } class=class="str">"cmt">//--- Set the flag for using the Tweezer pattern and create a control object if it does not already exist class="type">void SeriesSetUsedPatternTweezer(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { this.m_time_series.SetUsedPatternTweezer(symbol,timeframe,flag); } class=class="str">"cmt">//--- Set the flag for using the Piercing Line pattern and create a control object if it does not already exist class="type">void SeriesSetUsedPatternPiercingLine(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { this.m_time_series.SetUsedPatternPiercingLine(symbol,timeframe,flag); } class=class="str">"cmt">//--- Set the flag for using the Cloud Cover pattern and create a control object if it does not already exist class="type">void SeriesSetUsedPatternDarkCloudCover(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { this.m_time_series.SetUsedPatternDarkCloudCover(symbol,timeframe,flag); } class=class="str">"cmt">//--- Set the flag for using the Three White Soldiers pattern and create a control object if it does not already exist class="type">void SeriesSetUsedPatternThreeWhiteSoldiers(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { this.m_time_series.SetUsedPatternThreeWhiteSoldiers(symbol,timeframe,flag); } class=class="str">"cmt">//--- Set the flag for using the Three Black Crows pattern and create a control object if it does not already exist class="type">void SeriesSetUsedPatternThreeBlackCrows(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { this.m_time_series.SetUsedPatternThreeBlackCrows(symbol,timeframe,flag);
◍ 按品种周期开关单根 K 线形态识别
在 MT5 的 K 线形态识别框架里,每一类单根形态都对应一个独立的开关函数,而不是在参数面板里打勾了事。下面这组方法允许你针对某个 symbol + timeframe 组合,单独启用或停用某种形态的检测,底层如果还没建控制对象就顺手建出来。 以流星线(Shooting Star)为例,调用 SeriesSetUsedPatternShootingStar("XAUUSD",PERIOD_H1,true) 后,只在黄金 1 小时图上激活该类形态扫描;同一品种其他周期不受影响。这种粒度对多周期共振策略很实用——你可能只在 H4 看锤子线,却在 M15 屏蔽十字星噪音。 函数签名高度一致:void SeriesSetUsedPatternXxx(const string symbol, const ENUM_TIMEFRAMES timeframe, const bool flag),Xxx 替换成 Hammer、InvertedHammer、HangingMan、Doji、DragonflyDoji 等。flag 为 false 即关闭该品种周期下的对应形态,不会删除历史已识别标记。 外汇与贵金属杠杆高、滑点跳空频繁,形态开关只是过滤信号的第一步,真要下单仍须结合风控与实时流动性判断,信号触发不代表概率占优。
class=class="str">"cmt">//--- Set the flag for using the Shooting Star pattern and create a control object if it does not already exist class="type">void SeriesSetUsedPatternShootingStar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { this.m_time_series.SetUsedPatternShootingStar(symbol,timeframe,flag); } class=class="str">"cmt">//--- Set the flag for using the Hammer pattern and create a control object if it does not already exist class="type">void SeriesSetUsedPatternHammer(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { this.m_time_series.SetUsedPatternHammer(symbol,timeframe,flag); } class=class="str">"cmt">//--- Set the flag for using the Inverted Hammer pattern and create a control object if it does not already exist class="type">void SeriesSetUsedPatternInvertedHammer(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { this.m_time_series.SetUsedPatternInvertedHammer(symbol,timeframe,flag); } class=class="str">"cmt">//--- Set the flag for using the Hanging Man pattern and create a control object if it does not already exist class="type">void SeriesSetUsedPatternHangingMan(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { this.m_time_series.SetUsedPatternHangingMan(symbol,timeframe,flag); } class=class="str">"cmt">//--- Set the flag for using the Doji pattern and create a control object if it does not already exist class="type">void SeriesSetUsedPatternDoji(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { this.m_time_series.SetUsedPatternDoji(symbol,timeframe,flag); } class=class="str">"cmt">//--- Set the flag for using the Dragonfly Doji pattern and create a control object if it does not already exist
「按品种周期开关星线形态识别」
在 MT5 的 K 线形态识别封装里,蜻蜓十字、墓碑十字、晨星、晨十字星、暮星、暮十字星这六类形态都走同一套开关接口:给品种、周期和布尔值,底层时间序列对象就去登记或撤销该形态的使用标记。 调用时如果对应周期的控制对象还不存在,接口会顺手创建它,所以你不必预先手动初始化时间序列容器,直接传 EURUSD、PERIOD_H1 和 true 就能让晨星形态进入监听。 外汇与贵金属杠杆高、滑点随机,形态触发只代表概率倾斜,不代表后续必定反转;实盘前建议在策略测试器里用 2020—2023 年 tick 数据回测,确认某品种在 M15 上墓碑十字的假信号率是否超过 40%。 下面这段是六类形态的开关函数原型,逐行看:void 开头定义返回空的函数;SeriesSetUsedPatternDragonflyDoji 接收 symbol、timeframe、flag 三个只读参数;函数体里 this.m_time_series 调同名方法把标记下发给时间序列;其余晨星、暮星等只是函数名和底层方法名替换,结构完全一致。
class="type">void SeriesSetUsedPatternDragonflyDoji(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { this.m_time_series.SetUsedPatternDragonflyDoji(symbol,timeframe,flag); } class=class="str">"cmt">//--- Set the flag for using the Doji Gravestone pattern and create a control object if it does not already exist class="type">void SeriesSetUsedPatternGravestoneDoji(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { this.m_time_series.SetUsedPatternGravestoneDoji(symbol,timeframe,flag); } class=class="str">"cmt">//--- Set the flag for using the Morning Star pattern and create a control object if it does not already exist class="type">void SeriesSetUsedPatternMorningStar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { this.m_time_series.SetUsedPatternMorningStar(symbol,timeframe,flag); } class=class="str">"cmt">//--- Set the flag for using the Morning Doji Star pattern and create a control object if it does not already exist class="type">void SeriesSetUsedPatternMorningDojiStar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { this.m_time_series.SetUsedPatternMorningDojiStar(symbol,timeframe,flag); } class=class="str">"cmt">//--- Set the flag for using the Evening Star pattern and create a control object if it does not already exist class="type">void SeriesSetUsedPatternEveningStar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { this.m_time_series.SetUsedPatternEveningStar(symbol,timeframe,flag); } class=class="str">"cmt">//--- Set the flag for using the Evening Doji Star pattern and create a control object if it does not already exist
按品种周期开关星线与枢轴形态
在 MT5 的自定义形态识别类里,这组 SeriesSetUsedPattern* 方法负责把某个 K 线形态在指定品种和周期上「激活」或「停用」。它们都接收 symbol、timeframe、flag 三个参数,flag 为 true 时才会让底层 m_time_series 建立并维护对应的监控对象。 EveningDojiStar(黄昏十字星)、ThreeStars(三星)、AbandonedBaby(弃婴)属于蜡烛反转类,PivotPointReversal(枢轴反转)、OutsideBar(外包线)、InsideBar(内包线)偏价格行为结构。若你只在 EURUSD 的 H1 上验证弃婴形态,传 SeriesSetUsedPatternAbandonedBaby("EURUSD",PERIOD_H1,true) 即可,其余周期默认不占用计算资源。 外汇与贵金属杠杆高,形态触发只是概率信号,实盘前务必在策略测试器用 2020—2023 年 tick 数据回测,观察不同品种下 flag 开启后的误报率差异。
class="type">void SeriesSetUsedPatternEveningDojiStar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { this.m_time_series.SetUsedPatternEveningDojiStar(symbol,timeframe,flag); } class=class="str">"cmt">//--- Set the flag for using the Three Stars pattern and create a control object if it does not already exist class="type">void SeriesSetUsedPatternThreeStars(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { this.m_time_series.SetUsedPatternThreeStars(symbol,timeframe,flag); } class=class="str">"cmt">//--- Set the flag for using the Abandoned Baby pattern and create a control object if it does not already exist class="type">void SeriesSetUsedPatternAbandonedBaby(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { this.m_time_series.SetUsedPatternAbandonedBaby(symbol,timeframe,flag); } class=class="str">"cmt">//--- Price Action class=class="str">"cmt">//--- Set the flag for using the Pivot Point Reversal pattern and create a control object if it does not already exist class="type">void SeriesSetUsedPatternPivotPointReversal(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { this.m_time_series.SetUsedPatternPivotPointReversal(symbol,timeframe,flag); } class=class="str">"cmt">//--- Set the flag for using the Pattern Outside and create a control object if it does not already exist class="type">void SeriesSetUsedPatternOutsideBar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { this.m_time_series.SetUsedPatternOutsideBar(symbol,timeframe,flag); } class=class="str">"cmt">//--- Set the flag for using the Pattern Inside and create a control object if it does not already exist
◍ 在 MT5 里按需勾选价格行为形态
想让盯盘引擎只认你关心的形态,得靠 SeriesSetUsedPattern 这组方法逐个开关。Inside Bar、Rails 只需 symbol、timeframe 和 flag 三个参数就能挂上或摘下对应控制对象,不用的形态直接 false 关掉,省掉无谓计算。 Pin Bar 的接口明显更重:除了基础三参,还带了 ratio_body=30、ratio_larger_shadow=60、ratio_smaller_shadow=30 三个百分比阈值。意思很直白——实体占整根蜡烛 30% 以内、长影线占 60% 以上、短影线占 30% 以内才被算作 Pin Bar,你可以照着改这些数字来卡形态松紧。 Harami 则走 SeriesDrawPatternHarami,多一个 redraw 参数默认 false,控制是否在已有标签上重绘。外汇和贵金属波动大、假信号多,这类形态识别只能作为概率参考,实盘前务必在 MT5 策略测试器里用历史数据验证一遍。
class="type">void SeriesSetUsedPatternInsideBar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { this.m_time_series.SetUsedPatternInsideBar(symbol,timeframe,flag); } class=class="str">"cmt">//--- Set the flag for using the Pin Bar pattern and create a control object if it does not already exist class="type">void SeriesSetUsedPatternPinBar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe, const class="type">bool flag, class=class="str">"cmt">// Price Action Pin Bar usage flag const class="type">class="kw">double ratio_body=class="num">30, class=class="str">"cmt">// Percentage ratio of the candle body to the full size of the candle const class="type">class="kw">double ratio_larger_shadow=class="num">60, class=class="str">"cmt">// Percentage ratio of the size of the larger shadow to the size of the candle const class="type">class="kw">double ratio_smaller_shadow=class="num">30) class=class="str">"cmt">// Percentage ratio of the size of the smaller shadow to the size of the candle { this.m_time_series.SetUsedPatternPinBar(symbol,timeframe,flag,ratio_body,ratio_larger_shadow,ratio_smaller_shadow); } class=class="str">"cmt">//--- Set the flag for using the Rails pattern and create a control object if it does not already exist class="type">void SeriesSetUsedPatternRails(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool flag) { this.m_time_series.SetUsedPatternRails(symbol,timeframe,flag); } class=class="str">"cmt">//--- Draw Harami pattern labels on the chart class="type">void SeriesDrawPatternHarami(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false)
「把经典K线形态直接画到MT5图表上」
在自研指标或EA里,如果想把锤子线之外的其他日本蜡烛图形态也标出来,可以复用同一套时间序列封装方法。下面这组成员函数都挂在某个时间序列对象上,入参统一为交易品种、周期和重绘开关,默认不重绘。 调用时只需传 symbol 和 timeframe,例如 SeriesDrawPatternHarami("XAUUSD",PERIOD_H1),就会在黄金1小时图上尝试标注孕线形态。外汇与贵金属杠杆高、滑点大,形态触发后价格反向的概率并不低,信号仅作参考。 从代码看,Harami Cross(十字孕线)、Tweezer(镊子顶底)、Piercing Line(刺透线)、Dark Cloud Cover(乌云盖顶)、Three White Soldiers(红三兵)各有独立绘制入口,底层都委托给 m_time_series 的同名方法。想验证覆盖度,开MT5挂上对应品种多周期,逐个函数试一遍便知哪类形态在你的经纪商数据上漏标多。
this.m_time_series.DrawPatternHarami(symbol,timeframe,redraw); class=class="str">"cmt">//--- Draw Harami Cross pattern labels on the chart class="type">void SeriesDrawPatternHaramiCross(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { this.m_time_series.DrawPatternHaramiCross(symbol,timeframe,redraw); } class=class="str">"cmt">//--- Draw Tweezer pattern labels on the chart class="type">void SeriesDrawPatternTweezer(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { this.m_time_series.DrawPatternTweezer(symbol,timeframe,redraw); } class=class="str">"cmt">//--- Draw Piercing Line pattern labels on the chart class="type">void SeriesDrawPatternPiercingLine(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { this.m_time_series.DrawPatternPiercingLine(symbol,timeframe,redraw); } class=class="str">"cmt">//--- Draw Dark Cloud Cover pattern labels on the chart class="type">void SeriesDrawPatternDarkCloudCover(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { this.m_time_series.DrawPatternDarkCloudCover(symbol,timeframe,redraw); } class=class="str">"cmt">//--- Draw Three White Soldiers pattern labels on the chart class="type">void SeriesDrawPatternThreeWhiteSoldiers(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { this.m_time_series.DrawPatternThreeWhiteSoldiers(symbol,timeframe,redraw);
把经典K线形态直接画上MT5图表
在自定义时间序列类里,可以封装一组只负责「贴标签」的方法,把形态识别结果可视化到指定品种和周期。下面这组函数不计算形态,只调用底层 m_time_series 的绘制接口,入参统一是 symbol、timeframe 和可选的 redraw。 redraw 默认 false,意味着初次绘制后不会每帧重画;若你在 EA 里动态切换品种或周期,记得显式传 true 强制刷新,否则图表上的形态标记可能停留在旧数据上。 覆盖的形态包括三黑鸦、射击之星、锤子线、倒锤子、吊人线和十字星,基本把日线级别反转常用的裸K信号都包进来了。外汇与贵金属杠杆高,形态失效时回撤可能超过日常波动 2~3 倍,标记出来也只是概率参考。 真要验证,把这段代码丢进你的指标工程编译,挂 EURUSD 的 H1,肉眼比对标记位置和裸K,比看文字描述来得直接。
class=class="str">"cmt">//--- Draw Three Black Crows pattern labels on the chart class="type">void SeriesDrawPatternThreeBlackCrows(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { this.m_time_series.DrawPatternThreeBlackCrows(symbol,timeframe,redraw); } class=class="str">"cmt">//--- Draw Shooting Star pattern labels on the chart class="type">void SeriesDrawPatternShootingStar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { this.m_time_series.DrawPatternShootingStar(symbol,timeframe,redraw); } class=class="str">"cmt">//--- Draw Hammer pattern labels on the chart class="type">void SeriesDrawPatternHammer(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { this.m_time_series.DrawPatternHammer(symbol,timeframe,redraw); } class=class="str">"cmt">//--- Draw Inverted Hammer pattern labels on the chart class="type">void SeriesDrawPatternInvertedHammer(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { this.m_time_series.DrawPatternInvertedHammer(symbol,timeframe,redraw); } class=class="str">"cmt">//--- Draw Hanging Man pattern labels on the chart class="type">void SeriesDrawPatternHangingMan(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { this.m_time_series.DrawPatternHangingMan(symbol,timeframe,redraw); } class=class="str">"cmt">//--- Draw Doji pattern labels on the chart
◍ 把星线与十字形态直接画上图表
在 MT5 的自定义指标或 EA 里,如果想把经典价格行为形态(Doji、蜻蜓十字、墓碑十字、晨星、晨十字星、暮星等)直接标注在 K 线上,可以调用 SeriesDrawPattern 系列方法。这些方法都挂在时间序列对象 m_time_series 之下,传入品种、周期和重绘开关即可。 下面这段实现展示了六个常见形态的绘制入口,参数结构完全一致:symbol 指定交易品种(如 "XAUUSD"),timeframe 用 ENUM_TIMEFRAMES 枚举(如 PERIOD_H1),redraw 默认 false 表示不强制重画、避免每 tick 重绘造成的闪烁。 实盘里贵金属与外汇波动大,这类标注只辅助识别概率倾向,不构成方向承诺;XAUUSD 在 H1 上墓碑十字出现后反转概率可能略高,但仍需结合量价验证。 让小布替你跑这套 把 redraw 设为 true 后挂到 EURUSD 的 M15 上,观察晨星标注是否比肉眼数 K 线更快——若延迟明显,说明你的时间序列缓存该优化了。
class="type">void SeriesDrawPatternDoji(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { this.m_time_series.DrawPatternDoji(symbol,timeframe,redraw); } class=class="str">"cmt">//--- Draw Dragonfly Doji pattern labels on the chart class="type">void SeriesDrawPatternDragonflyDoji(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { this.m_time_series.DrawPatternDragonflyDoji(symbol,timeframe,redraw); } class=class="str">"cmt">//--- Draw Gravestone Doji pattern labels on the chart class="type">void SeriesDrawPatternGravestoneDoji(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { this.m_time_series.DrawPatternGravestoneDoji(symbol,timeframe,redraw); } class=class="str">"cmt">//--- Draw Morning Star pattern labels on the chart class="type">void SeriesDrawPatternMorningStar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { this.m_time_series.DrawPatternMorningStar(symbol,timeframe,redraw); } class=class="str">"cmt">//--- Draw Morning Doji Star pattern labels on the chart class="type">void SeriesDrawPatternMorningDojiStar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { this.m_time_series.DrawPatternMorningDojiStar(symbol,timeframe,redraw); } class=class="str">"cmt">//--- Draw Evening Star pattern labels on the chart class="type">void SeriesDrawPatternEveningStar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false)
「把反转形态直接画上图表」
在 MT5 的自定义指标或 EA 里,若已持有封装好的时间序列对象 m_time_series,可以通过一组 SeriesDrawPattern* 方法把经典 K 线反转形态直接标注在图上。 这类方法统一接收三个参数:symbol(交易品种)、timeframe(周期枚举)、redraw(是否重绘,默认 false)。调用时只需写一行 this.m_time_series.DrawPatternXxx(symbol,timeframe,redraw); 形态标签就会落在对应品种与周期的蜡烛上。 下面这段代码覆盖了 Evening Star、Evening Doji Star、Three Stars、Abandoned Baby 四类顶部反转,以及 Pivot Point Reversal、Outside Bar 两种价格行为信号。实盘外汇与贵金属波动剧烈,形态触发只代表概率倾向反转,不代表必然转向,高杠杆下仍需自担风险。 想验证效果,可在 EURUSD 的 H1 上把 redraw 设为 true 跑一遍,观察 Outside Bar 在趋势末端的标记密度是否明显高于中段。
class="type">void SeriesDrawPatternEveningStar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { this.m_time_series.DrawPatternEveningStar(symbol,timeframe,redraw); } class=class="str">"cmt">//--- Draw Evening Doji Star pattern labels on the chart class="type">void SeriesDrawPatternEveningDojiStar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { this.m_time_series.DrawPatternEveningDojiStar(symbol,timeframe,redraw); } class=class="str">"cmt">//--- Draw Three Stars pattern labels on the chart class="type">void SeriesDrawPatternThreeStars(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { this.m_time_series.DrawPatternThreeStars(symbol,timeframe,redraw); } class=class="str">"cmt">//--- Draw Abandoned Baby pattern labels on the chart class="type">void SeriesDrawPatternAbandonedBaby(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { this.m_time_series.DrawPatternAbandonedBaby(symbol,timeframe,redraw); } class=class="str">"cmt">//--- Price Action class=class="str">"cmt">//--- Draw Pivot Point Reversal pattern labels on the chart class="type">void SeriesDrawPatternPivotPointReversal(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { this.m_time_series.DrawPatternPivotPointReversal(symbol,timeframe,redraw); } class=class="str">"cmt">//--- Draw Pattern Outside labels on the chart class="type">void SeriesDrawPatternOutsideBar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { this.m_time_series.DrawPatternOutsideBar(symbol,timeframe,redraw);
在图表上直接标出 Inside Bar 与 Pin Bar
想把价格行为形态从「肉眼翻」变成「代码替你圈」,核心就是这组 SeriesDrawPattern* 方法。它们挂在时间序列对象上,调用一次就把对应形态的标签画到指定品种的图表上。 Inside Bar 的绘制最简单,只要给品种和时间周期:SeriesDrawPatternInsideBar(symbol, timeframe, redraw)。redraw 默认 false,不强制重绘。 Pin Bar 多三个阈值参数,默认值分别是实体占比 30%、较长影线占比 60%、较短影线占比 30%(均为占整根蜡烛全长的百分比)。调小 ratio_body 会逮到更多「针」形态,但假信号可能随之变多。 Rails 的接口和 Inside Bar 同构,仅传 symbol、timeframe、redraw。三者都走 this.m_time_series 底层,Pin Bar 在 redraw=true 时会额外触发 ::ChartRedraw() 强制刷新。 外汇与贵金属波动剧烈,形态标注只是辅助,不等于方向确认,实盘仍属高风险。
class=class="str">"cmt">//--- Draw Inside Bar pattern labels on the chart class="type">void SeriesDrawPatternInsideBar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { this.m_time_series.DrawPatternInsideBar(symbol,timeframe,redraw); } class=class="str">"cmt">//--- Draw Pin Bar pattern labels on the chart class="type">void SeriesDrawPatternPinBar(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe, const class="type">class="kw">double ratio_body=class="num">30, class=class="str">"cmt">// Percentage ratio of the candle body to the full size of the candle const class="type">class="kw">double ratio_larger_shadow=class="num">60, class=class="str">"cmt">// Percentage ratio of the size of the larger shadow to the size of the candle const class="type">class="kw">double ratio_smaller_shadow=class="num">30,class=class="str">"cmt">// Percentage ratio of the size of the smaller shadow to the size of the candle const class="type">bool redraw=class="kw">false) class=class="str">"cmt">// Chart redraw flag { this.m_time_series.DrawPatternPinBar(symbol,timeframe,ratio_body,ratio_larger_shadow,ratio_smaller_shadow,redraw); if(redraw) ::ChartRedraw(); } class=class="str">"cmt">//--- Draw Rails pattern labels on the chart class="type">void SeriesDrawPatternRails(const class="type">class="kw">string symbol,const ENUM_TIMEFRAMES timeframe,const class="type">bool redraw=class="kw">false) { this.m_time_series.DrawPatternRails(symbol,timeframe,redraw); }
◍ 一键隐藏全部形态图标的两种写法
在 MT5 自定义形态分析类里,批量隐藏图表上的形态标记是常见的界面清理动作。下面两段方法都从总形态列表取对象,区别只在于是否保留某一个特定形态。 第一段 SeriesPatternHideAll 遍历 GetListAllPatterns() 拿到的全部对象,从尾到头逐个调用 Hide()。传入 redraw=true 时末尾强制 ChartRedraw(),否则只改属性不重绘,适合连续批量操作省开销。 第二段 SeriesPatternHideAllExceptOne 用 CSelect::ByPatternProperty 按 PATTERN_PROP_CODE 做不等于筛选,只返回非目标编码的形态。这样能在隐藏其余所有图标时,单独留下你正在盯的那一个形态不被清掉。 实盘接小布盯盘时,若同时加载了 30+ 形态而只想看头肩顶,传对应 pattern_code 调第二个方法,图表干扰可能立刻降一大半。外汇与贵金属波动剧烈,隐藏不代表风险消失,参数错了也可能漏看反转信号。
class="type">void SeriesPatternHideAll(const class="type">bool redraw=class="kw">false) { CArrayObj *list=this.GetListAllPatterns(); for(class="type">int i=list.Total()-class="num">1;i>=class="num">0;i--) { CPattern *obj=list.At(i); if(obj!=NULL) obj.Hide(); } if(redraw) ::ChartRedraw(); } class="type">void SeriesPatternHideAllExceptOne(const class="type">ulong pattern_code,const class="type">bool redraw=class="kw">false) { CArrayObj *list=CSelect::ByPatternProperty(this.GetListAllPatterns(),PATTERN_PROP_CODE,pattern_code,NO_EQUAL); if(list==NULL) class="kw">return; for(class="type">int i=list.Total()-class="num">1;i>=class="num">0;i--) { CPattern *obj=list.At(i); if(obj!=NULL) obj.Hide(); } if(redraw) ::ChartRedraw(); }
「批量收起形态信息面板的两种写法」
在 MT5 自定义形态识别类里,经常需要一次性把图表上所有形态的信息面板隐藏,避免多个标签叠在一起干扰读图。下面两个方法分别处理「全隐藏」和「保留某一个」两种场景,参数 redraw 控制是否立即重绘图表。 第一个方法 SeriesPatternHideAllInfoPanels 从 this.GetListAllPatterns() 拿到全部形态对象指针数组,倒序遍历到 0,对每个非空 CPattern 调用 HideInfoPanel()。若传入 redraw=true,最后执行 ::ChartRedraw() 强制刷新,否则面板隐藏动作会等到下一次图表事件才生效。 第二个方法 SeriesPatternHideAllInfoPanelsExceptOne 多一个 ulong pattern_code 入参,用 CSelect::ByPatternProperty 按 PATTERN_PROP_CODE 做「不等于该 code」的筛选,只返回其余形态列表;若筛选结果为 NULL 直接 return。后续隐藏逻辑与全隐藏一致。实盘外汇与贵金属波动快、跳空频繁,这类批量隐藏建议在形态刷新回调里调用,否则可能短暂露出过期面板,带来误判风险。
class="type">void SeriesPatternHideAllInfoPanels(const class="type">bool redraw=class="kw">false) { CArrayObj *list=this.GetListAllPatterns(); for(class="type">int i=list.Total()-class="num">1;i>=class="num">0;i--) { CPattern *obj=list.At(i); if(obj!=NULL) obj.HideInfoPanel(); } if(redraw) ::ChartRedraw(); } class=class="str">"cmt">//--- Hides info panels of all patterns except the specified one class="type">void SeriesPatternHideAllInfoPanelsExceptOne(const class="type">ulong pattern_code,const class="type">bool redraw=class="kw">false) { CArrayObj *list=CSelect::ByPatternProperty(this.GetListAllPatterns(),PATTERN_PROP_CODE,pattern_code,NO_EQUAL); if(list==NULL) class="kw">return; for(class="type">int i=list.Total()-class="num">1;i>=class="num">0;i--) { CPattern *obj=list.At(i); if(obj!=NULL) obj.HideInfoPanel(); } if(redraw) ::ChartRedraw(); }
在 OnTick 里拿 tick 序列与避开卡顿
CEngine 暴露了两个取数口:GetTickSeriesCollection 直接返回内部 m_tick_series 的指针,GetListTickSeries 则走 m_tick_series.GetList() 拿到对象数组。想在外层遍历逐笔 tick,这两个方法够用,不用再去翻底层容器。 OnTick 的事件处理里先卡了一道:非 EA 程序直接 return,说明这套引擎逻辑只打算在 Expert Advisor 上下文跑。随后 SeriesSync 重建空白序列并同步当前品种,SeriesRefresh 刷新数据,这两步是每跳必做的。 有一行被注释掉了:this.TickSeriesRefresh(NULL)。原作者标注它会引发冻结,暂未排查。你在 MT5 里若想启用 tick 级刷新,先把这行放开跑一遍,观察是否出现主线程卡死;外汇与贵金属波动大,tick 刷新频率高,这类阻塞会直接拖慢下单响应,属高风险操作。
CTickSeriesCollection *GetTickSeriesCollection(class="type">void) { class="kw">return &this.m_tick_series; } CArrayObj *GetListTickSeries(class="type">void) { class="kw">return this.m_tick_series.GetList(); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| NewTick event handler | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CEngine::OnTick(SDataCalculate &data_calculate,const class="type">uint required=class="num">0) { class=class="str">"cmt">//--- If this is not a EA, exit if(this.m_program_type!=PROGRAM_EXPERT) class="kw">return; class=class="str">"cmt">//--- Re-create empty timeseries and update the current symbol timeseries this.SeriesSync(data_calculate,required); this.SeriesRefresh(NULL,data_calculate); class=class="str">"cmt">//--- Commented out because TickSeriesRefresh causes freezes - I will look into it later class=class="str">"cmt">//this.TickSeriesRefresh(NULL); class=class="str">"cmt">//--- end }
◍ 形态标记 EA 的实盘挂载验证
把前期文章里那个跟踪图表对象参数的 EA 拷进 \MQL5\Experts\TestDoEasy\Part134,存成 TestDoEasyPart134.mq5,就能直接拿来跑形态识别测试。输入参数里要补上蜡烛比例搜索阈值,勾选仅当前时间框架工作,同时关掉市场深度、信号和图表事件跟踪,避免面板被无关事件刷掉。 OnInit() 里先备好要识别的形态清单,OnChartEvent() 则加了两块逻辑:图表一变动就隐藏当前活动面板;光标停到带标签的烛形上(高低点区间以内)才弹出该形态的描述与参数面板。目前这是最省事的验证方案,后续可能改成停留超时自动隐藏。 编译挂上图表后,看涨和看跌形态用了不同颜色标签。实测里光标移开柱形,面板不会立刻消失,直到你移动图表或悬停到另一个形态上才切换——这点对对照多形态很有用,但也容易让屏幕堆满旧面板。外汇和贵金属波动剧烈,这类标记仅辅助判读,不预示方向。 下面这段是 EA 输入参数原样,StopLoss 与 TakeProfit 都设了 150 点、滑点 5 点,挂单距离 50 点,可直接照抄到 MT5 里改阈值做对照。
class=class="str">"cmt">//--- input variables input class="type">class="kw">ushort InpMagic = class="num">123; class=class="str">"cmt">// Magic number input class="type">class="kw">double InpLots = class="num">0.1; class=class="str">"cmt">// Lots input class="type">uint InpStopLoss = class="num">150; class=class="str">"cmt">// StopLoss in points input class="type">uint InpTakeProfit = class="num">150; class=class="str">"cmt">// TakeProfit in points input class="type">uint InpDistance = class="num">50; class=class="str">"cmt">// Pending orders distance(points) input class="type">uint InpDistanceSL = class="num">50; class=class="str">"cmt">// StopLimit orders distance(points) input class="type">uint InpDistancePReq = class="num">50; class=class="str">"cmt">// Distance for Pending Request&class="macro">#x27;s activate(points) input class="type">uint InpBarsDelayPReq = class="num">5; class=class="str">"cmt">// Bars delay for Pending Request&class="macro">#x27;s activate(current timeframe) input class="type">uint InpSlippage = class="num">5; class=class="str">"cmt">// Slippage in points input class="type">uint InpSpreadMultiplier = class="num">1; class=class="str">"cmt">// Spread multiplier for adjusting stop-orders by StopLevel input class="type">uchar InpTotalAttempts = class="num">5; class=class="str">"cmt">// Number of trading attempts sinput class="type">class="kw">double InpWithdrawal = class="num">10; class=class="str">"cmt">// Withdrawal funds(in tester) sinput class="type">uint InpButtShiftX = class="num">0; class=class="str">"cmt">// Buttons X shift sinput class="type">uint InpButtShiftY = class="num">10; class=class="str">"cmt">// Buttons Y shift input class="type">uint InpTrailingStop = class="num">50; class=class="str">"cmt">// Trailing Stop(points)
「Pin Bar 与多品种扫描的输入参数拆解」
这段输入区定义了价格行为扫描器的可调边界,直接决定 EA 在 MT5 里会盯哪些品种、哪些周期、以及什么样的 K 线算 Pin Bar。外汇与贵金属波动随机性高,参数只是过滤噪音的筛子,不代表信号必然有效。
input class="type">uint InpTrailingStep = class="num">20; class=class="str">"cmt">// Trailing Step(points) input class="type">uint InpTrailingStart = class="num">0; class=class="str">"cmt">// Trailing Start(points) input class="type">uint InpStopLossModify = class="num">20; class=class="str">"cmt">// StopLoss for modification(points) input class="type">uint InpTakeProfitModify = class="num">60; class=class="str">"cmt">// TakeProfit for modification(points) sinput ENUM_SYMBOLS_MODE InpModeUsedSymbols = SYMBOLS_MODE_CURRENT; class=class="str">"cmt">// Mode of used symbols list sinput class="type">class="kw">string InpUsedSymbols = "EURUSD,AUDUSD,EURAUD,EURCAD,EURGBP,EURJPY,EURUSD,GBPUSD,NZDUSD,USDCAD,USDJPY"; class=class="str">"cmt">// List of used symbols(comma - separator) sinput ENUM_TIMEFRAMES_MODE InpModeUsedTFs = TIMEFRAMES_MODE_CURRENT; class=class="str">"cmt">// Mode of used timeframes list sinput class="type">class="kw">string InpUsedTFs = "M1,M5,M15,M30,H1,H4,D1,W1,MN1"; class=class="str">"cmt">// List of used timeframes(comma - separator) sinput class="type">class="kw">double InpPinBarRatioBody = class="num">30.0; class=class="str">"cmt">// Pin Bar Ratio Body to Candle size sinput class="type">class="kw">double InpPinBarRatioLarger = class="num">60.0; class=class="str">"cmt">// Pin Bar Ratio Larger shadow to Candle size sinput class="type">class="kw">double InpPinBarRatioSmaller = class="num">30.0; class=class="str">"cmt">// Pin Bar Ratio Smaller shadow to Candle size sinput ENUM_INPUT_YES_NO InpUseBook = INPUT_NO; class=class="str">"cmt">// Use Depth of Market sinput ENUM_INPUT_YES_NO InpUseMqlSignals = INPUT_NO; class=class="str">"cmt">// Use signal service sinput ENUM_INPUT_YES_NO InpUseCharts = INPUT_NO; class=class="str">"cmt">// Use Charts control sinput ENUM_INPUT_YES_NO InpUseSounds = INPUT_YES; class=class="str">"cmt">// Use sounds
input class="type">uint InpTrailingStep = class="num">20; class=class="str">"cmt">// Trailing Step(points) input class="type">uint InpTrailingStart = class="num">0; class=class="str">"cmt">// Trailing Start(points) input class="type">uint InpStopLossModify = class="num">20; class=class="str">"cmt">// StopLoss for modification(points) input class="type">uint InpTakeProfitModify = class="num">60; class=class="str">"cmt">// TakeProfit for modification(points) sinput ENUM_SYMBOLS_MODE InpModeUsedSymbols = SYMBOLS_MODE_CURRENT; class=class="str">"cmt">// Mode of used symbols list sinput class="type">class="kw">string InpUsedSymbols = "EURUSD,AUDUSD,EURAUD,EURCAD,EURGBP,EURJPY,EURUSD,GBPUSD,NZDUSD,USDCAD,USDJPY"; class=class="str">"cmt">// List of used symbols(comma - separator) sinput ENUM_TIMEFRAMES_MODE InpModeUsedTFs = TIMEFRAMES_MODE_CURRENT; class=class="str">"cmt">// Mode of used timeframes list sinput class="type">class="kw">string InpUsedTFs = "M1,M5,M15,M30,H1,H4,D1,W1,MN1"; class=class="str">"cmt">// List of used timeframes(comma - separator) sinput class="type">class="kw">double InpPinBarRatioBody = class="num">30.0; class=class="str">"cmt">// Pin Bar Ratio Body to Candle size sinput class="type">class="kw">double InpPinBarRatioLarger = class="num">60.0; class=class="str">"cmt">// Pin Bar Ratio Larger shadow to Candle size sinput class="type">class="kw">double InpPinBarRatioSmaller = class="num">30.0; class=class="str">"cmt">// Pin Bar Ratio Smaller shadow to Candle size sinput ENUM_INPUT_YES_NO InpUseBook = INPUT_NO; class=class="str">"cmt">// Use Depth of Market sinput ENUM_INPUT_YES_NO InpUseMqlSignals = INPUT_NO; class=class="str">"cmt">// Use signal service sinput ENUM_INPUT_YES_NO InpUseCharts = INPUT_NO; class=class="str">"cmt">// Use Charts control sinput ENUM_INPUT_YES_NO InpUseSounds = INPUT_YES; class=class="str">"cmt">// Use sounds
EA 初始化里的 Pin Bar 开关与面板装配
这段 OnInit 逻辑把 EA 运行前的全局变量、按钮面板和价格行为识别做了一次性装配。外汇与贵金属品种点值差异大,lot 用 NormalizeLot 夹住下限:取输入手数和「最小手数×2.0」的较大值再规范化,避免微点差品种下单量越界。 trailing_stop 和 trailing_step 直接乘 Point() 把「点数」转成绝对价格距,例如 InpTrailingStop 设 30 点、EURUSD 的 Point 为 0.00001,实际跟踪距就是 0.00030。distance_pending_request 和 bars_delay_pending_request 都有硬下限保护:前者小于 5 强制取 5,后者小于 1 强制取 1,防止挂单请求在极端行情里被秒拒。 被高亮的那几行才是价格行为学的落点:engine.GetListAllPatterns().Clear() 先清空旧形态缓存,再用 SeriesSetUsedPatternPinBar 按 InpPinBarRatioBody 等参数激活 Pin Bar 识别。最后 SeriesPatternHideAll 与 HideAllInfoPanels 把图标和面板先藏起来,等主图事件触发再显示——你改完比率参数,重加载 EA 听那声 600 毫秒后的「掉币」音效,就能确认初始化跑通了。
class="type">int OnInit() { class=class="str">"cmt">//--- Calling the function displays the list of enumeration constants in the journal class=class="str">"cmt">//--- (the list is set in the strings class="num">22 and class="num">25 of the DELib.mqh file) for checking the constants validity class=class="str">"cmt">//EnumNumbersTest(); class=class="str">"cmt">//--- Set EA global variables prefix=MQLInfoString(MQL_PROGRAM_NAME)+"_"; testing=engine.IsTester(); for(class="type">int i=class="num">0;i<TOTAL_BUTT;i++) { butt_data[i].name=prefix+EnumToString((ENUM_BUTTONS)i); butt_data[i].text=EnumToButtText((ENUM_BUTTONS)i); } lot=NormalizeLot(Symbol(),fmax(InpLots,MinimumLots(Symbol())*class="num">2.0)); magic_number=InpMagic; stoploss=InpStopLoss; takeprofit=InpTakeProfit; distance_pending=InpDistance; distance_stoplimit=InpDistanceSL; slippage=InpSlippage; trailing_stop=InpTrailingStop*Point(); trailing_step=InpTrailingStep*Point(); trailing_start=InpTrailingStart; stoploss_to_modify=InpStopLossModify; takeprofit_to_modify=InpTakeProfitModify; distance_pending_request=(InpDistancePReq<class="num">5 ? class="num">5 : InpDistancePReq); bars_delay_pending_request=(InpBarsDelayPReq<class="num">1 ? class="num">1 : InpBarsDelayPReq); g_point=SymbolInfoDouble(NULL,SYMBOL_POINT); g_digits=(class="type">int)SymbolInfoInteger(NULL,SYMBOL_DIGITS); class=class="str">"cmt">//--- Initialize random group numbers group1=class="num">0; group2=class="num">0; srand(GetTickCount()); class=class="str">"cmt">//--- Initialize DoEasy library OnInitDoEasy(); class=class="str">"cmt">//--- Check and remove remaining EA graphical objects if(IsPresentObectByPrefix(prefix)) ObjectsDeleteAll(class="num">0,prefix); class=class="str">"cmt">//--- Create the button panel if(!CreateButtons(InpButtShiftX,InpButtShiftY)) class="kw">return INIT_FAILED; class=class="str">"cmt">//--- Set trailing activation button status ButtonState(butt_data[TOTAL_BUTT-class="num">1].name,trailing_on); class=class="str">"cmt">//--- Reset states of the buttons for working using pending requests for(class="type">int i=class="num">0;i<class="num">14;i++) { ButtonState(butt_data[i].name+"_PRICE",class="kw">false); ButtonState(butt_data[i].name+"_TIME",class="kw">false); } class=class="str">"cmt">//--- Check playing a standard sound by macro substitution and a custom sound by description engine.PlaySoundByDescription(SND_OK); class=class="str">"cmt">//--- Wait for class="num">600 milliseconds engine.Pause(class="num">600); engine.PlaySoundByDescription(TextByLanguage("Звук упавшей монетки class="num">2","Falling coin class="num">2")); class=class="str">"cmt">//--- Check the calculation of the cursor coordinates in the chart windows. class=class="str">"cmt">//--- Allow the current chart to track mouse movement events if(engine.ChartGetMainChart()!=NULL) engine.ChartGetMainChart().SetEventMouseMoveON(); class=class="str">"cmt">//--- Clear the list of all patterns engine.GetListAllPatterns().Clear(); class=class="str">"cmt">//--- Set the flag of using the Pin Bar pattern with the parameters specified in the settings engine.SeriesSetUsedPatternPinBar(NULL,PERIOD_CURRENT,true,InpPinBarRatioBody,InpPinBarRatioLarger,InpPinBarRatioSmaller); class=class="str">"cmt">//--- Hide all pattern icons, if any engine.SeriesPatternHideAll(); class=class="str">"cmt">//--- Hide all pattern info panels, if any engine.SeriesPatternHideAllInfoPanels(); class=class="str">"cmt">//--- Display the pattern icons on the chart with the parameters specified in the settings
◍ 图表事件里的 PinBar 绘制与坐标反算
在初始化尾部直接调用 engine.SeriesDrawPatternPinBar(NULL,PERIOD_CURRENT,InpPinBarRatioBody,InpPinBarRatioLarger,InpPinBarRatioSmaller,true); 即可在当前图表与周期上按三个比例参数画出 PinBar 形态,随后 return(INIT_SUCCEEDED) 结束指标加载。 OnChartEvent 里先判 MQLInfoInteger(MQL_TESTER),在策略测试器中直接 return,避免回测时响应鼠标与图表事件浪费资源。 鼠标点击若命中 sparam 含 "BUTT_" 的面板按钮,转交 PressButtonEvents 处理;id 大于 CHARTEVENT_CUSTOM-1 的自定义事件交给 OnDoEasyEvent。图表变动 CHARTEVENT_CHART_CHANGE 触发时调 engine.SeriesPatternHideAllInfoPanels() 清空所有信息面板再 return。 CHARTEVENT_MOUSE_MOVE 分支用 chart.XYToTimePrice(lparam,dparam) 拿到光标所在子窗口号,再用 TimeFromXY/PriceFromXY 取时间价格,GetWindowByNum 取窗口对象后调 TimePriceToXY 做反向换算,可验证坐标互转是否一致。外汇与贵金属波动剧烈,这类图形识别仅作概率参考,实盘须控仓。
engine.SeriesDrawPatternPinBar(NULL,PERIOD_CURRENT,InpPinBarRatioBody,InpPinBarRatioLarger,InpPinBarRatioSmaller,true); class=class="str">"cmt">//--- class="kw">return(INIT_SUCCEEDED); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| ChartEvent function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnChartEvent(const class="type">int id, const class="type">long &lparam, const class="type">class="kw">double &dparam, const class="type">class="kw">string &sparam) { class=class="str">"cmt">//--- If working in the tester, exit if(MQLInfoInteger(MQL_TESTER)) class="kw">return; class=class="str">"cmt">//--- Handling mouse events if(id==CHARTEVENT_OBJECT_CLICK) { class=class="str">"cmt">//--- Handle pressing the buttons in the panel if(StringFind(sparam,"BUTT_")>class="num">0) PressButtonEvents(sparam); } class=class="str">"cmt">//--- Handling DoEasy library events if(id>CHARTEVENT_CUSTOM-class="num">1) { OnDoEasyEvent(id,lparam,dparam,sparam); } class=class="str">"cmt">//--- Chart change if(id==CHARTEVENT_CHART_CHANGE) { class=class="str">"cmt">//--- Whenever the chart changes, hide all information panels engine.SeriesPatternHideAllInfoPanels(); class="kw">return; } class=class="str">"cmt">//--- Check ChartXYToTimePrice() if(id==CHARTEVENT_MOUSE_MOVE) { class=class="str">"cmt">//--- Get the chart object of the current(main) program chart CChartObj *chart=engine.ChartGetMainChart(); if(chart==NULL) class="kw">return; class=class="str">"cmt">//--- Get the index of a subwindow the cursor is located at class="type">int wnd_num=chart.XYToTimePrice(lparam,dparam); if(wnd_num==WRONG_VALUE) class="kw">return; class=class="str">"cmt">//--- Get the calculated cursor location time and price class="type">class="kw">datetime time=chart.TimeFromXY(); class="type">class="kw">double price=chart.PriceFromXY(); class=class="str">"cmt">//--- Get the window object of the chart the cursor is located in by the subwindow index CChartWnd *wnd=chart.GetWindowByNum(wnd_num); if(wnd==NULL) class="kw">return; class=class="str">"cmt">//--- If X and Y coordinates are calculated by time and price(make a reverse conversion), if(wnd.TimePriceToXY(time,price)) { class=class="str">"cmt">//--- in the comment, show the time, price and index of the window that are calculated by X and Y cursor coordinates, class=class="str">"cmt">//--- as well as the cursor X and Y coordinates converted back from the time and price class=class="str">"cmt">//Comment class=class="str">"cmt">// (
「用光标时间反查 Pin Bar 并弹出信息面板」
这段逻辑解决一个很实际的问题:交易者在 MT5 图表上移动鼠标时,怎么让程序自动识别光标所在 K 线是不是 Pin Bar,并把形态信息画出来。 核心是先拿光标时间反推当前周期 K 线的开盘时间,再用这个开盘时间去形态引擎里取对应的 Pin Bar 对象。若取到的对象不为空,且光标价格落在该 K 线的高低点区间之内,才认为用户确实悬停在了这根 Pin Bar 上。 拿到形态后调用 PrintShort(true) 往日志打简报,再用 ChartTimePriceToXY 把时间和价格换算成图坐标 x、y。换算成功才继续:先隐藏其他形态的信息面板,只留当前这根,再在光标附近 ShowInfoPanel 显示详情。 外汇与贵金属波动剧烈,Pin Bar 仅为价格行为的概率信号,悬停识别不等于入场确认,实盘须自担高风险。 下面把关键代码逐行拆一下: // 用光标时间快速取当前周期 K 线开盘时间 datetime bar_time=GetStartTimeOfBarFast(PERIOD_CURRENT,time); // 按品种、周期、开盘时间去引擎取 Pin Bar 形态指针 CPattern *pinbar=engine.GetPattern(Symbol(),Period(),bar_time,PATTERN_TYPE_PIN_BAR); // 若形态存在 if(pinbar!=NULL) // 且光标价格在该 K 线高低点之间 if(price>=pinbar.BarPriceLow() && price<=pinbar.BarPriceHigh()) // 日志打印形态简报 pinbar.PrintShort(true); int x=0; int y=0; // 时间价格转图坐标,成功才继续 if(ChartTimePriceToXY(pinbar.GetChartID(),0,bar_time,price,x,y)) // 隐藏其他面板,只留当前 engine.SeriesPatternHideAllInfoPanelsExceptOne(pinbar.Code()); // 在坐标处显示信息面板 pinbar.ShowInfoPanel(x,y);
class=class="str">"cmt">//--- Get the bar open time on the chart using the cursor time class="type">class="kw">datetime bar_time=GetStartTimeOfBarFast(PERIOD_CURRENT,time); class=class="str">"cmt">//--- Get a pattern from the chart bar the open time was found for CPattern *pinbar=engine.GetPattern(Symbol(),Period(),bar_time,PATTERN_TYPE_PIN_BAR); class=class="str">"cmt">//--- If there is a Pin Bar pattern on the bar if(pinbar!=NULL) { class=class="str">"cmt">//--- If the cursor is within the candle size if(price>=pinbar.BarPriceLow() && price<=pinbar.BarPriceHigh()) { class=class="str">"cmt">//--- Print a class="type">short description of the pattern in the journal pinbar.PrintShort(true); class=class="str">"cmt">//--- Get the chart coordinates where you want to display the information panel class="type">int x=class="num">0; class="type">int y=class="num">0; if(ChartTimePriceToXY(pinbar.GetChartID(),class="num">0,bar_time,price,x,y)) { class=class="str">"cmt">//--- Hide all panels except those belonging to the current pattern under the cursor engine.SeriesPatternHideAllInfoPanelsExceptOne(pinbar.Code()); class=class="str">"cmt">//--- Display the information panel on the chart pinbar.ShowInfoPanel(x,y); } } }
画得少,看得清
这套 MVC 表格库把价格行为形态拆成了视图、控制器与模型三层,你下载的 ZIP 里已含 DoEasy 框架,解压到终端 MQL5 目录即可在 Include 下看到。有读者在 MT5 5506 上编译报出 TabControl.mqh 366 行「9 个参数传入但需 10 个」这类错误,说明旧版头文件没跟上编译器的方法隐藏新规。 真要验证,别急着加新形态,先把针形柱、十字星归到价格行为类,烛台类单放,用库里的 CGCnvElement 画一次标准图表,编译能过、图形不叠层,才算跑通。外汇与贵金属波动剧烈,任何形态识别都只是概率倾向,实盘前务必在策略测试器用历史数据过一遍。 留个余地:形态分类本来就可按自己逻辑改,库只是容器,别被浩如烟海的代码绑死——少画几类、看清结构,比堆功能更有用。