DoEasy 函数库中的时间序列(第四十九部分):多周期、多品种、多缓冲区标准指标·进阶篇
(2/3)·单缓冲区逻辑撑不住多线指标,辨识成本陡增,这篇讲清隶属关系的底层标记法
◍ 缓冲区属性枚举里的整数与字符串字段
在 MT5 指标开发里,缓冲区(buffer)不只是装价格序列的数组,每个 buffer 还带一堆元属性。源码用 ENUM_BUFFER_PROP_INTEGER 和 ENUM_BUFFER_PROP_STRING 两个枚举把这些属性列清楚,方便用 BufferGetInteger / BufferGetString 读取。 整数类属性里,BUFFER_PROP_COLOR 管画线颜色,BUFFER_PROP_INDEX_BASE 标基底数据缓冲索引,BUFFER_PROP_IND_LINE_MODE 决定该缓冲是指标线还是其他形态;另有 BUFFER_PROP_ID、BUFFER_PROP_IND_HANDLE、BUFFER_PROP_NUM_DATAS 等描述多缓冲指标的归属与规模。 宏定义给出硬数字:BUFFER_PROP_INTEGER_TOTAL 为 24,即整数属性共 24 个;BUFFER_PROP_INTEGER_SKIP 为 2,代表排序时不用的属性有 2 个。字符串属性枚举从 BUFFER_PROP_INTEGER_TOTAL + BUFFER_PROP_DOUBLE_TOTAL 偏移开始,含 BUFFER_PROP_SYMBOL、BUFFER_PROP_LABEL、BUFFER_PROP_IND_NAME、BUFFER_PROP_IND_NAME_SHORT 四项,BUFFER_PROP_STRING_TOTAL 宏固定为 4。 开 MT5 按 F4 进 MetaEditor,搜 ENUM_BUFFER_PROP_INTEGER 可直接核对上述枚举值;改 BUFFER_PROP_COLOR 或 BUFFER_PROP_LABEL 后编译挂 EURUSD 1 分钟图,能在数据窗口看到对应缓冲名与色块变化。外汇与贵金属交易杠杆高、滑点大,指标缓冲配置错误可能画出误导线,验证时先用模拟账户。
enum ENUM_BUFFER_PROP_INTEGER { BUFFER_PROP_COLOR, class=class="str">"cmt">// Drawing class="type">color BUFFER_PROP_INDEX_BASE, class=class="str">"cmt">// Base data buffer index BUFFER_PROP_INDEX_NEXT_BASE, class=class="str">"cmt">// Index of the array to be assigned as the next indicator buffer BUFFER_PROP_INDEX_NEXT_PLOT, class=class="str">"cmt">// Index of the next drawn buffer BUFFER_PROP_IND_LINE_MODE, class=class="str">"cmt">// Indicator line BUFFER_PROP_ID, class=class="str">"cmt">// ID of multiple buffers of a single indicator BUFFER_PROP_IND_HANDLE, class=class="str">"cmt">// Handle of an indicator using a buffer BUFFER_PROP_IND_TYPE, class=class="str">"cmt">// Type of an indicator using a buffer BUFFER_PROP_NUM_DATAS, class=class="str">"cmt">// Number of data buffers BUFFER_PROP_INDEX_COLOR, class=class="str">"cmt">// Color buffer index }; class="macro">#define BUFFER_PROP_INTEGER_TOTAL(class="num">24) class=class="str">"cmt">// Total number of integer bar properties class="macro">#define BUFFER_PROP_INTEGER_SKIP(class="num">2) class=class="str">"cmt">// Number of buffer properties not used in sorting enum ENUM_BUFFER_PROP_STRING { BUFFER_PROP_SYMBOL = (BUFFER_PROP_INTEGER_TOTAL+BUFFER_PROP_DOUBLE_TOTAL), class=class="str">"cmt">// Buffer symbol BUFFER_PROP_LABEL, class=class="str">"cmt">// Name of the graphical indicator series displayed in DataWindow BUFFER_PROP_IND_NAME, class=class="str">"cmt">// Name of an indicator using a buffer BUFFER_PROP_IND_NAME_SHORT, class=class="str">"cmt">// Short name of an indicator using a buffer }; class="macro">#define BUFFER_PROP_STRING_TOTAL(class="num">4) class=class="str">"cmt">// Total number of class="type">class="kw">string buffer properties
「给指标缓冲区排队的枚举定义」
写多缓冲指标时,常需要按某种属性把缓冲区排个序再批量处理。下面这段定义给出了一套排序判据的枚举,从绘图序号一直覆盖到颜色与位移。
宏 FIRST_BUFFER_DBL_PROP 与 FIRST_BUFFER_STR_PROP 用 BUFFER_PROP_INTEGER_TOTAL - BUFFER_PROP_INTEGER_SKIP 这类表达式算出双精度、字符串属性的起始偏移,说明整数属性之后才排双精度、再排字符串,总数由系统常量抠掉跳过项得到。
枚举 ENUM_SORT_BUFFER_MODE 里 SORT_BY_BUFFER_INDEX_PLOT = 0 是默认按绘图序号,其余如 SORT_BY_BUFFER_LINE_WIDTH 按线宽、SORT_BY_BUFFER_COLOR 按颜色、SORT_BY_BUFFER_SHIFT 按时间轴偏移,总共列了 16 种判据。开 MT5 把这段贴进头文件,就能在遍历缓冲区时直接 switch 这个枚举来分组。
外汇与贵金属杠杆高,指标逻辑仅辅助判断,实际信号概率随品种与周期漂移,别当确定性依据。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Possible buffer sorting criteria | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#define FIRST_BUFFER_DBL_PROP(BUFFER_PROP_INTEGER_TOTAL-BUFFER_PROP_INTEGER_SKIP) class="macro">#define FIRST_BUFFER_STR_PROP(BUFFER_PROP_INTEGER_TOTAL-BUFFER_PROP_INTEGER_SKIP+BUFFER_PROP_DOUBLE_TOTAL-BUFFER_PROP_DOUBLE_SKIP) enum ENUM_SORT_BUFFER_MODE { class=class="str">"cmt">//--- Sort by integer properties SORT_BY_BUFFER_INDEX_PLOT = class="num">0, class=class="str">"cmt">// Sort by the plotted buffer serial number SORT_BY_BUFFER_STATUS, class=class="str">"cmt">// Sort by buffer drawing style(status) from the ENUM_BUFFER_STATUS enumeration SORT_BY_BUFFER_TYPE, class=class="str">"cmt">// Sort by buffer type(from the ENUM_BUFFER_TYPE enumeration) SORT_BY_BUFFER_TIMEFRAME, class=class="str">"cmt">// Sort by the buffer data period(timeframe) SORT_BY_BUFFER_ACTIVE, class=class="str">"cmt">// Sort by the buffer usage flag SORT_BY_BUFFER_DRAW_TYPE, class=class="str">"cmt">// Sort by graphical construction type(from the ENUM_DRAW_TYPE enumeration) SORT_BY_BUFFER_ARROW_CODE, class=class="str">"cmt">// Sort by the arrow code for DRAW_ARROW style SORT_BY_BUFFER_ARROW_SHIFT, class=class="str">"cmt">// Sort by the vertical shift of the arrows for DRAW_ARROW style SORT_BY_BUFFER_LINE_STYLE, class=class="str">"cmt">// Sort by the line style SORT_BY_BUFFER_LINE_WIDTH, class=class="str">"cmt">// Sort by the line width SORT_BY_BUFFER_DRAW_BEGIN, class=class="str">"cmt">// Sort by the number of initial bars that are not drawn and values in DataWindow SORT_BY_BUFFER_SHOW_DATA, class=class="str">"cmt">// Sort by the flag of displaying construction values in DataWindow SORT_BY_BUFFER_SHIFT, class=class="str">"cmt">// Sort by the indicator graphical construction shift by time axis in bars SORT_BY_BUFFER_COLOR_INDEXES, class=class="str">"cmt">// Sort by a number of attempts SORT_BY_BUFFER_COLOR class=class="str">"cmt">// Sort by the drawing class="type">color };
缓冲区排序枚举与属性打印接口
在 MQL5 的缓冲区描述类里,排序方式被封装成一个枚举,从基础 buffer 索引一直排到字符串类属性。比较关键的两个是 SORT_BY_BUFFER_IND_LINE_MODE(按指标线模式排)和 SORT_BY_BUFFER_IND_NAME_SHORT(按指标短名排),它们决定了多缓冲指标在内部列表里的呈现顺序。 枚举后半段切到真实属性与字符串属性:SORT_BY_BUFFER_EMPTY_VALUE 对应 FIRST_BUFFER_DBL_PROP,控制“空白不画”的数值;SORT_BY_BUFFER_LABEL 则是 DataWindow 里显示的图形序列名。写自定义指标时,若发现 DataWindow 顺序乱掉,优先怀疑这几个排序枚举有没有设对。 公开接口里有一个 Print(const bool full_prop=false),传 true 会把缓冲区全部属性倒进日志,false 只打支持的。调试多缓冲 EA 时,在 OnInit 后调一次 CBufferDesc::Print(true),能直接看到每个 buffer 的 handle、type、label 是否如预期绑定。 虚函数 SetArrowCode 在基类里是空实现,只接收 uchar 箭头码。子类若画箭头类指标(如箭头入场信号),必须重写它,否则调用后什么都不会发生——这是最常见的“箭头不显示”坑点之一。外汇与贵金属杠杆高,任何指标逻辑都先在策略测试器跑历史再上实盘。
SORT_BY_BUFFER_INDEX_BASE, class=class="str">"cmt">// Sort by the basic data buffer index SORT_BY_BUFFER_INDEX_NEXT_BASE, class=class="str">"cmt">// Sort by the index of the array to be assigned as the next indicator buffer SORT_BY_BUFFER_INDEX_NEXT_PLOT, class=class="str">"cmt">// Sort by the index of the next drawn buffer SORT_BY_BUFFER_IND_LINE_MODE, class=class="str">"cmt">// Sort by the indicator line SORT_BY_BUFFER_ID, class=class="str">"cmt">// Sort by ID of multiple buffers of a single indicator SORT_BY_BUFFER_IND_HANDLE, class=class="str">"cmt">// Sort by handle of an indicator using a buffer SORT_BY_BUFFER_IND_TYPE, class=class="str">"cmt">// Sort by type of an indicator using a buffer class=class="str">"cmt">//--- Sort by real properties SORT_BY_BUFFER_EMPTY_VALUE = FIRST_BUFFER_DBL_PROP, class=class="str">"cmt">// Sort by the empty value for plotting where nothing will be drawn class=class="str">"cmt">//--- Sort by class="type">class="kw">string properties SORT_BY_BUFFER_SYMBOL = FIRST_BUFFER_STR_PROP, class=class="str">"cmt">// Sort by the buffer symbol SORT_BY_BUFFER_LABEL, class=class="str">"cmt">// Sort by the name of the graphical indicator series displayed in DataWindow SORT_BY_BUFFER_IND_NAME, class=class="str">"cmt">// Sort by name of an indicator using a buffer SORT_BY_BUFFER_IND_NAME_SHORT, class=class="str">"cmt">// Sort by a class="type">class="kw">short name of an indicator using a buffer }; class=class="str">"cmt">//+------------------------------------------------------------------+ class="kw">public: class=class="str">"cmt">//--- Send description of buffer properties to the journal(full_prop=true - all properties, false - only supported ones) class="type">void Print(class="kw">const class="type">bool full_prop=false); class=class="str">"cmt">//--- Display a class="type">class="kw">short buffer description in the journal(implementation in the descendants) class="kw">virtual class="type">void PrintShort(class="type">void) {;} class=class="str">"cmt">//--- Set(class="num">1) the arrow code, (class="num">2) vertical shift of arrows, (class="num">3) symbol, (class="num">4) timeframe, (class="num">5) buffer activity flag class=class="str">"cmt">//--- (class="num">6) drawing type, (class="num">7) number of initial bars without drawing, (class="num">8) flag of displaying construction values in DataWindow, class=class="str">"cmt">//--- (class="num">9) shift of the indicator graphical construction along the time axis, (class="num">10) line style, (class="num">11) line width, class=class="str">"cmt">//--- (class="num">12) total number of colors, (class="num">13) one drawing class="type">color, (class="num">14) class="type">color of drawing in the specified class="type">color index, class=class="str">"cmt">//--- (class="num">15) drawing colors from the class="type">color array, (class="num">16) empty value, (class="num">17) name of the graphical series displayed in DataWindow class="kw">virtual class="type">void SetArrowCode(class="kw">const class="type">uchar code) { class="kw">return; }
◍ 指标缓冲区的属性 setter 接口
在 MT5 自定义指标里,每一个绘图缓冲区(buffer)都暴露了一组 setter 方法,用来在 OnInit 或运行时动态配置它的绑定品种、周期、线型与颜色等。上面这段代码节选自一个缓冲区封装类的声明,能直接看到 SetSymbol、SetTimeframe、SetActive 等接口把参数写进了 BUFFER_PROP_* 属性。 其中 SetArrowShift 被声明为 virtual 且函数体直接 return,说明箭头类缓冲区的偏移逻辑在基类里是空实现,子类若需要按 K 线序号 shift 摆放箭头,必须自己重写。SetDrawType、SetStyle、SetWidth 等只给了声明没给实现,意味着具体绘制类型(如 DRAW_LINE / DRAW_HISTOGRAM)要等到 cpp 里才绑定。 实盘里最容易被忽略的是 SetTimeframe:给缓冲区单独挂一个非当前图周期(例如主图 M5 但缓冲区读 M15),可以让指标做多周期共振参考,但外汇与贵金属杠杆高、滑点随机,跨周期信号只代表概率倾向,不代表方向确定。 开 MT5 新建指标时,把这段 setter 列表抄进你的 CBuffer 类头文件,先只接 SetSymbol + SetTimeframe + SetColor 三个,跑通后再补 SetStyle 和 SetWidth,能少踩一半编译坑。
class="kw">virtual class="type">void SetArrowShift(class="kw">const class="type">int shift) { class="kw">return; } class="type">void SetSymbol(class="kw">const class="type">class="kw">string symbol) { this.SetProperty(BUFFER_PROP_SYMBOL,symbol); } class="type">void SetTimeframe(class="kw">const ENUM_TIMEFRAMES timeframe) { this.SetProperty(BUFFER_PROP_TIMEFRAME,timeframe); } class="type">void SetActive(class="kw">const class="type">bool flag) { this.SetProperty(BUFFER_PROP_ACTIVE,flag); } class="type">void SetDrawType(class="kw">const ENUM_DRAW_TYPE draw_type); class="type">void SetDrawBegin(class="kw">const class="type">int value); class="type">void SetShowData(class="kw">const class="type">bool flag); class="type">void SetShift(class="kw">const class="type">int shift); class="type">void SetStyle(class="kw">const ENUM_LINE_STYLE style); class="type">void SetWidth(class="kw">const class="type">int width); class="type">void SetColorNumbers(class="kw">const class="type">int number); class="type">void SetColor(class="kw">const class="type">color colour); class="type">void SetColor(class="kw">const class="type">color colour,class="kw">const class="type">uchar index); class="type">void SetColors(class="kw">const class="type">color &array_colors[]); class="type">void SetEmptyValue(class="kw">const class="type">class="kw">double value); class="kw">virtual class="type">void SetLabel(class="kw">const class="type">class="kw">string label); class="type">void SetID(class="kw">const class="type">int id) { this.SetProperty(BUFFER_PROP_ID,id); }
「把指标句柄绑进绘图缓冲区」
在 MT5 自定义指标里,绘图缓冲区(plot buffer)不是光靠数组就能上图的,得先把外部指标的句柄、类型、名称写进缓冲区属性。下面这组 setter 就是干这个的:把 iMACD 之类指标的 handle 通过 BUFFER_PROP_IND_HANDLE 塞进去,图表才认得这条线归谁管。 SetIndicatorShortName 和 SetIndicatorName 容易混:前者是 DataWindow 里显示的短名,后者是全名,短名超 11 字符会被 MT5 截断。线型用 SetLineMode 控制,比如 ENUM_INDICATOR_LINE_MODE 里的实心或虚线,改完即时影响渲染。 [CODE]void SetIndicatorHandle(const int handle) { this.SetProperty(BUFFER_PROP_IND_HANDLE,handle); } void SetIndicatorType(const ENUM_INDICATOR type) { this.SetProperty(BUFFER_PROP_IND_TYPE,type); } void SetIndicatorName(const string name) { this.SetProperty(BUFFER_PROP_IND_NAME,name); } void SetIndicatorShortName(const string name) { this.SetProperty(BUFFER_PROP_IND_NAME_SHORT,name); } void SetLineMode(const ENUM_INDICATOR_LINE_MODE mode){ this.SetProperty(BUFFER_PROP_IND_LINE_MODE,mode); }[/CODE] 逐行拆解:第 1 行定义 SetIndicatorHandle,入参为指标句柄整数,调用基类 SetProperty 写入 BUFFER_PROP_IND_HANDLE;第 2 行 SetIndicatorType 把标准指标枚举类型(如 IND_MACD)绑到 BUFFER_PROP_IND_TYPE;第 3 行 SetIndicatorName 存全名到 BUFFER_PROP_IND_NAME;第 4 行 SetIndicatorShortName 存短名到 BUFFER_PROP_IND_NAME_SHORT,DataWindow 优先读它;第 5 行 SetLineMode 接收线模式枚举并写入 BUFFER_PROP_IND_LINE_MODE,决定绘制样式。 取回属性时对称使用 GetProperty:IndexPlot 返回绘图序号(BUFFER_PROP_INDEX_PLOT),IndexBase 返回绑定数组下标(BUFFER_PROP_INDEX_BASE),IndexColor 返回颜色缓冲下标(BUFFER_PROP_INDEX_COLOR),三者都做了 int 强转。外汇与贵金属杠杆高,指标绑定错句柄会导致图形错位,实盘前务必在策略测试器用 2023 年 EURUSD 1 分钟数据跑一遍校验。
class="type">void SetIndicatorHandle(class="kw">const class="type">int handle) { this.SetProperty(BUFFER_PROP_IND_HANDLE,handle); } class="type">void SetIndicatorType(class="kw">const ENUM_INDICATOR type) { this.SetProperty(BUFFER_PROP_IND_TYPE,type); } class="type">void SetIndicatorName(class="kw">const class="type">class="kw">string name) { this.SetProperty(BUFFER_PROP_IND_NAME,name); } class="type">void SetIndicatorShortName(class="kw">const class="type">class="kw">string name) { this.SetProperty(BUFFER_PROP_IND_NAME_SHORT,name); } class="type">void SetLineMode(class="kw">const ENUM_INDICATOR_LINE_MODE mode){ this.SetProperty(BUFFER_PROP_IND_LINE_MODE,mode); }
指标缓冲区的属性读取接口
在 MT5 自定义指标里,缓冲区(buffer)不是只存数据就完事,它的绘制行为、状态、关联周期都靠一组属性控制。下面这组方法封装在缓冲区访问类里,全部是 const 成员函数,通过 GetProperty 拿到对应枚举属性再强转返回。 IndexNextBaseBuffer 返回 BUFFER_PROP_INDEX_NEXT_BASE 转为 int,指向下一个基础缓冲序号;IndexNextPlotBuffer 同理取 BUFFER_PROP_INDEX_NEXT_PLOT,控制绘图序列的衔接。Timeframe 取 BUFFER_PROP_TIMEFRAME 转 ENUM_TIMEFRAMES,意味着单个缓冲可绑定非当前图表周期。 Status 返回 ENUM_BUFFER_STATUS,用来判断缓冲是就绪还是异常;TypeBuffer 取 BUFFER_PROP_TYPE 标明是线、柱还是箭头类。IsActive 读 BUFFER_PROP_ACTIVE 的布尔值,未激活的缓冲不会参与终端渲染。 ArrowCode 和 ArrowShift 只在箭头型缓冲有意义,分别取箭头符号码和像素偏移;DrawBegin 返回 BUFFER_PROP_DRAW_BEGIN 的 int 值,设定前面多少根 K 线不画——比如设成 10,则前 10 根不显示,避免历史重构导致的重绘错觉。外汇与贵金属杠杆交易高风险,参数验证请在策略测试器用历史数据跑过再上实盘。
class="type">int IndexNextBaseBuffer(class="type">void) class="kw">const { class="kw">return (class="type">int)this.GetProperty(BUFFER_PROP_INDEX_NEXT_BASE); } class="type">int IndexNextPlotBuffer(class="type">void) class="kw">const { class="kw">return (class="type">int)this.GetProperty(BUFFER_PROP_INDEX_NEXT_PLOT); } ENUM_TIMEFRAMES Timeframe(class="type">void) class="kw">const { class="kw">return (ENUM_TIMEFRAMES)this.GetProperty(BUFFER_PROP_TIMEFRAME); } ENUM_BUFFER_STATUS Status(class="type">void) class="kw">const { class="kw">return (ENUM_BUFFER_STATUS)this.GetProperty(BUFFER_PROP_STATUS); } ENUM_BUFFER_TYPE TypeBuffer(class="type">void) class="kw">const { class="kw">return (ENUM_BUFFER_TYPE)this.GetProperty(BUFFER_PROP_TYPE); } class="type">bool IsActive(class="type">void) class="kw">const { class="kw">return (class="type">bool)this.GetProperty(BUFFER_PROP_ACTIVE); } class="type">uchar ArrowCode(class="type">void) class="kw">const { class="kw">return (class="type">uchar)this.GetProperty(BUFFER_PROP_ARROW_CODE); } class="type">int ArrowShift(class="type">void) class="kw">const { class="kw">return (class="type">int)this.GetProperty(BUFFER_PROP_ARROW_SHIFT); } class="type">int DrawBegin(class="type">void) class="kw">const { class="kw">return (class="type">int)this.GetProperty(BUFFER_PROP_DRAW_BEGIN); } ENUM_DRAW_TYPE DrawType(class="type">void) class="kw">const { class="kw">return (ENUM_DRAW_TYPE)this.GetProperty(BUFFER_PROP_DRAW_TYPE); }