DoEasy 函数库中的时间序列(第五十二部分):多周期、多品种单缓冲区标准指标的跨平台性质·进阶篇
枚举结构里的交易属性分类
这段源码骨架把 MT5 里和订单、持仓、风控相关的属性拆成一串枚举分组,虽然具体实现被省略,但标题列出来的分类本身就是接口设计的地图。 从 StopLoss/TakeProfit 层级设定,到期权类型、行权权利、保证金计算方式,再到隔夜 swap 收取逻辑,覆盖了建仓前后两条线。外汇与贵金属杠杆高,swap 与保证金算法不同券商差异大,实盘前应在 MT5 的规格窗口逐类核对。 后面连续列出交易操作类型、订单填充策略、订单存活时长,以及持仓的整数/实数/字符串三类属性,最后收在技术指标类型上。打开 MetaEditor 搜这些注释对应的 ENUM_ 宏,能直接看到每个分组的取值清单,比看文档快。
◍ 指标与绘图枚举的底层常量映射
在 MT5 自定义指标开发里,系统用一组连续整数枚举来指代内置指标类型。从 AC 的 5 到 CUSTOM 的 43,覆盖了 39 个内置指标加自定义槽位,写 EA 或指标时直接传这些常量比硬写字符串稳。 绘图样式另有一套枚举,MQL5 与 MQL4 的起始值并不一致。比如 DRAW_COLOR_LINE 在 MQL5 底层等于 1,而 MQL4 里是 0;DRAW_COLOR_HISTOGRAM 两边都是 2。跨版本移植代码时,若直接抄枚举数值容易错,应走宏名。 下面这段枚举定义可直接贴进 MQ5 文件编译,用来统一管指标 ID 与画线方式,省去每次查文档。外汇与贵金属波动剧烈、杠杆高风险大,任何指标映射错误都可能让信号逻辑整体偏移,上真实账户前务必在策略测试器跑一遍。
enum ENUM_INDICATOR { IND_AC = class="num">5, IND_AD = class="num">6, IND_ALLIGATOR = class="num">7, IND_ADX = class="num">8, IND_ADXW = class="num">9, IND_ATR = class="num">10, IND_AO = class="num">11, IND_BEARS = class="num">12, IND_BANDS = class="num">13, IND_BULLS = class="num">14, IND_CCI = class="num">15, IND_DEMARKER = class="num">16, IND_ENVELOPES = class="num">17, IND_FORCE = class="num">18, IND_FRACTALS = class="num">19, IND_GATOR = class="num">20, IND_ICHIMOKU = class="num">21, IND_BWMFI = class="num">22, IND_MACD = class="num">23, IND_MOMENTUM = class="num">24, IND_MFI = class="num">25, IND_MA = class="num">26, IND_OSMA = class="num">27, IND_OBV = class="num">28, IND_SAR = class="num">29, IND_RSI = class="num">30, IND_RVI = class="num">31, IND_STDDEV = class="num">32, IND_STOCHASTIC = class="num">33, IND_VOLUMES = class="num">34, IND_WPR = class="num">35, IND_DEMA = class="num">36, IND_TEMA = class="num">37, IND_TRIX = class="num">38, IND_FRAMA = class="num">39, IND_AMA = class="num">40, IND_CHAIKIN = class="num">41, IND_VIDYA = class="num">42, IND_CUSTOM = class="num">43, }; enum ENUM_DRAW_TYPE { DRAW_COLOR_LINE = DRAW_LINE, class=class="str">"cmt">// MQL5 = class="num">1, MQL4 = class="num">0 DRAW_COLOR_HISTOGRAM = DRAW_HISTOGRAM, class=class="str">"cmt">// MQL5 = class="num">2, MQL4 = class="num">2
「从 MQL4 到 MQL5 的绘图枚举映射细节」
把老 MQL4 指标往 MQL5 搬时,绘图类型常量不是一一对应的。比如 DRAW_COLOR_HISTOGRAM2、DRAW_COLOR_BARS、DRAW_COLOR_CANDLES 在 MQL5 里统一落到 DRAW_NONE(数值 0),而在 MQL4 中它们对应的内部编号是 12;DRAW_COLOR_SECTION 在 MQL5 是 4、MQL4 是 1。直接照抄旧常量可能在 MT5 上完全不画东西。 成交量枚举只保留了两个选项:VOLUME_TICK 与 VOLUME_REAL,比早期客户端精简很多。写自定义指标时若依赖真实成交手数,必须显式选 VOLUME_REAL,否则默认 tick 量会扭曲贵金属品种的量价结构。 指标缓冲区对象提供了 IndicatorBarsCalculated() 这种内联方法,底层调的是 BarsCalculated 并传入隐藏的 indicator handle。开 MT5 随便挂一个系统指标,用类似封装读一下返回值,就能确认当前品种计算了多少根有效柱——外汇和贵金属杠杆高、跳空多,这个数和图表可见柱数可能不一致。
DRAW_COLOR_ARROW = DRAW_ARROW, class=class="str">"cmt">// MQL5 = class="num">3, MQL4 = class="num">3 DRAW_COLOR_SECTION = DRAW_SECTION, class=class="str">"cmt">// MQL5 = class="num">4, MQL4 = class="num">1 DRAW_COLOR_HISTOGRAM2 = DRAW_NONE, class=class="str">"cmt">// MQL5 = class="num">0, MQL4 = class="num">12 DRAW_COLOR_ZIGZAG = DRAW_ZIGZAG, class=class="str">"cmt">// MQL5 = class="num">6, MQL4 = class="num">4 DRAW_COLOR_BARS = DRAW_NONE, class=class="str">"cmt">// MQL5 = class="num">0, MQL4 = class="num">12 DRAW_COLOR_CANDLES = DRAW_NONE, class=class="str">"cmt">// MQL5 = class="num">0, MQL4 = class="num">12 class=class="str">"cmt">// DRAW_FILLING MQL4 = class="num">5 DRAW_COLOR_NONE = DRAW_NONE, class=class="str">"cmt">// MQL5 = class="num">0, MQL4 = class="num">12 }; enum ENUM_APPLIED_VOLUME { VOLUME_TICK, VOLUME_REAL }; class="type">int IndicatorBarsCalculated(class="type">void) class="kw">const { class="kw">return ::BarsCalculated((class="type">int)this.GetProperty(BUFFER_PROP_IND_HANDLE)); }
从缓冲区对象抠出指标身份与计算进度
在 MT5 自建指标或 EA 里接管缓冲区时,CBuffer 类提供了一组只读方法,用来反查当前缓冲区的归属指标与绘制属性。下面这段声明直接暴露了它能拿到的字段:线型模式、指标类型、全称、短名、附加线编号,全部走 GetProperty 读内置属性,不碰外部变量。 IndicatorLineMode 返回 BUFFER_PROP_IND_LINE_MODE 转成的 int,指示该缓冲画的是线、柱还是点;IndicatorType 拿 BUFFER_PROP_IND_TYPE 映射成 ENUM_INDICATOR,能区分是 MACD 还是自定义指标;IndicatorName 与 IndicatorShortName 分别取全名和短名,SubWindow 里排重或日志追踪时很实用。 真正影响计算节奏的是 IndicatorBarsCalculated。MQL5 分支调 BarsCalculated 并传入指标句柄,返回该指标已算出的 bar 数;MQL4 分支退化成当前品种周期的总 Bars 数。若返回 0 或 -1,说明句柄无效或数据未就绪,后续拷贝缓冲区可能读到空值。 构造函数 CBuffer(...) 用闭合参数表锁定缓冲区状态、类型、plot 索引、基础数组索引、数据量和总数组数,意味着一个 buffer 对象在诞生时就绑定了存储结构,运行期改不了这些底层设定。外汇与贵金属波动剧烈,这类底层对象若句柄失效,EA 可能误判行情,实盘前务必在策略测试器里跑一遍句柄生命周期。
class="type">int IndicatorLineMode(class="type">void) class="kw">const { class="kw">return (class="type">int)this.GetProperty(BUFFER_PROP_IND_LINE_MODE); } ENUM_INDICATOR IndicatorType(class="type">void) class="kw">const { class="kw">return (ENUM_INDICATOR)this.GetProperty(BUFFER_PROP_IND_TYPE); } class="type">class="kw">string IndicatorName(class="type">void) class="kw">const { class="kw">return this.GetProperty(BUFFER_PROP_IND_NAME); } class="type">class="kw">string IndicatorShortName(class="type">void) class="kw">const { class="kw">return this.GetProperty(BUFFER_PROP_IND_NAME_SHORT); } class="type">int IndicatorLineAdditionalNumber(class="type">void) class="kw">const { class="kw">return (class="type">int)this.GetProperty(BUFFER_PROP_IND_LINE_ADDITIONAL_NUM); } class="type">int IndicatorLineMode(class="type">void) class="kw">const { class="kw">return (class="type">int)this.GetProperty(BUFFER_PROP_IND_LINE_MODE); } class="type">int IndicatorBarsCalculated(class="type">void); class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Return the number of standard indicator calculated bars | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int CBuffer::IndicatorBarsCalculated(class="type">void) { class="kw">return(class="macro">#ifdef __MQL5__ ::BarsCalculated((class="type">int)this.GetProperty(BUFFER_PROP_IND_HANDLE)) class="macro">#else ::Bars(this.Symbol(),this.Timeframe()) class="macro">#endif); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Closed parametric constructor | class=class="str">"cmt">//+------------------------------------------------------------------+ CBuffer::CBuffer(ENUM_BUFFER_STATUS buffer_status, ENUM_BUFFER_TYPE buffer_type, class="kw">const class="type">uint index_plot, class="kw">const class="type">uint index_base_array, class="kw">const class="type">int num_datas, class="kw">const class="type">uchar total_arrays,
◍ 指标缓冲区的构造与绘制类型推导
在 MT5 自定义指标里,缓冲区对象的构造函数负责把一组整数属性一次性落位。上面这段逻辑出现在某缓冲区类的初始化中,先通过 __MQL5__ 宏隔离 MQL5 专属分支,把集合类型标记为 COLLECTION_BUFFERS_ID,并打开激活触发开关 m_act_state_trigger。 随后用 m_long_prop 数组保存状态、类型、ID 等基础字段:BUFFER_PROP_ID 先填 WRONG_VALUE,主线模式默认 INDICATOR_LINE_MODE_MAIN,句柄留 INVALID_HANDLE。这些占位值会在后续绑定真实指标时改写,开 MT5 调试时若看到缓冲区不画线,优先查这几个字段是否仍为负值。 绘制类型 type 的推导值得细看:若类型或状态为空则 DRAW_NONE;状态是 BUFFER_STATUS_FILLING 时走 DRAW_FILLING(带状填充);否则把状态值加 8 强转成 ENUM_DRAW_TYPE。也就是说,状态枚举和绘图枚举在数值上偏移了 8,调参时别直接把状态当绘图类型用。 线宽由入参 width 决定,箭头代码默认 0x9F(即 Wingdings 里的向下箭头字形),偏移 0、起始绘制位 0。SHOW_DATA 仅在 buffer_type 大于 BUFFER_TYPE_CALCULATE 时才为真,COLOR_INDEXES 则按状态分 0/1/2 三档——填充态占两色,普通线占一色,无状态不占色。外汇与贵金属行情跳空频繁,这类缓冲区若用于实时 EA,需自测重绘开销,高风险环境别盲目堆缓冲层数。
class="kw">const class="type">int width, class="kw">const class="type">class="kw">string label) { class="macro">#ifdef __MQL5__ this.m_type=COLLECTION_BUFFERS_ID; this.m_act_state_trigger=true; this.m_total_arrays=total_arrays; class=class="str">"cmt">//--- Save integer properties this.m_long_prop[BUFFER_PROP_STATUS] = buffer_status; this.m_long_prop[BUFFER_PROP_TYPE] = buffer_type; this.m_long_prop[BUFFER_PROP_ID] = WRONG_VALUE; this.m_long_prop[BUFFER_PROP_IND_LINE_MODE] = INDICATOR_LINE_MODE_MAIN; this.m_long_prop[BUFFER_PROP_IND_HANDLE] = INVALID_HANDLE; this.m_long_prop[BUFFER_PROP_IND_TYPE] = WRONG_VALUE; this.m_long_prop[BUFFER_PROP_IND_LINE_ADDITIONAL_NUM] = WRONG_VALUE; ENUM_DRAW_TYPE type= ( !this.TypeBuffer() || !this.Status() ? DRAW_NONE : this.Status()==BUFFER_STATUS_FILLING ? DRAW_FILLING : ENUM_DRAW_TYPE(this.Status()+class="num">8) ); this.m_long_prop[BUFFER_PROP_DRAW_TYPE] = type; this.m_long_prop[BUFFER_PROP_TIMEFRAME] = PERIOD_CURRENT; this.m_long_prop[BUFFER_PROP_ACTIVE] = true; this.m_long_prop[BUFFER_PROP_ARROW_CODE] = 0x9F; this.m_long_prop[BUFFER_PROP_ARROW_SHIFT] = class="num">0; this.m_long_prop[BUFFER_PROP_DRAW_BEGIN] = class="num">0; this.m_long_prop[BUFFER_PROP_SHOW_DATA] = (buffer_type>BUFFER_TYPE_CALCULATE ? true : false); this.m_long_prop[BUFFER_PROP_SHIFT] = class="num">0; this.m_long_prop[BUFFER_PROP_LINE_STYLE] = STYLE_SOLID; this.m_long_prop[BUFFER_PROP_LINE_WIDTH] = width; this.m_long_prop[BUFFER_PROP_COLOR_INDEXES] = (this.Status()>BUFFER_STATUS_NONE ? (this.Status()!=BUFFER_STATUS_FILLING ? class="num">1 : class="num">2) : class="num">0);
「正文」
<span class="keyword">this</span>.m_long_prop[BUFFER_PROP_COLOR] = <span class="macro">clrRed</span>; <span class="keyword">this</span>.m_long_prop[BUFFER_PROP_NUM_DATAS] = num_datas; <span class="keyword">this</span>.m_long_prop[BUFFER_PROP_INDEX_PLOT] = index_plot; <span class="keyword">this</span>.m_long_prop[BUFFER_PROP_INDEX_BASE] = index_base_array; <span class="keyword">this</span>.m_long_prop[BUFFER_PROP_INDEX_COLOR] = <span class="keyword">this</span>.GetProperty(BUFFER_PROP_INDEX_BASE)+