DoEasy 函数库中的时间序列(第五十七部分):指标缓冲区数据对象·进阶篇
📘

DoEasy 函数库中的时间序列(第五十七部分):指标缓冲区数据对象·进阶篇

第 2/3 篇

指标数据属性的三数组封装

在 MT5 自定义指标的类结构里,把数据属性按类型拆成三块连续内存,是后续高效读写的底层约定。整数、双精度、字符串各占一个定长数组,下标直接对应枚举值,避免用 map 带来的额外开销。 下面这段声明把周期描述串和三类属性数组一并铺开:m_long_prop 装整数属性,长度由 IND_DATA_PROP_INTEGER_TOTAL 决定;m_double_prop 装实数属性;m_string_prop 装字符串属性。周期描述 m_period_description 单独留作可读文本。 IndexProp 是两个重载,作用只有一件——把 DOUBLE 和 STRING 枚举偏移进各自数组的正确下标。减掉前面整数和双精度数组的总长,就能从全局枚举映射到局部下标,调用方完全无感。 SetProperty / GetProperty 同样按类型重载:整数直接按下标写,双精度和字符串先过 IndexProp 再落位。这种写法在回测中访问 10 万根 bar 的属性时,比运行时查表快出一个数量级,外汇与贵金属行情高频刷新下更显必要,但杠杆交易本身高风险,参数错配可能放大亏损。 SupportProperty 默认全返回 true,派生类可只覆写自己不支持的那一项,省掉调用前的类型判断。

MQL5 / C++
  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[IND_DATA_PROP_INTEGER_TOTAL];     class=class="str">"cmt">// Integer properties
  class="type">class="kw">double  m_double_prop[IND_DATA_PROP_DOUBLE_TOTAL];    class=class="str">"cmt">// Real properties
  class="type">class="kw">string  m_string_prop[IND_DATA_PROP_STRING_TOTAL];    class=class="str">"cmt">// String properties
class=class="str">"cmt">//--- Return the index of the array the object&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_IND_DATA_PROP_DOUBLE class="kw">property)  class="kw">const { class="kw">return(class="type">int)class="kw">property-IND_DATA_PROP_INTEGER_TOTAL; }
  class="type">int    IndexProp(ENUM_IND_DATA_PROP_STRING class="kw">property)  class="kw">const { class="kw">return(class="type">int)class="kw">property-IND_DATA_PROP_INTEGER_TOTAL-IND_DATA_PROP_DOUBLE_TOTAL; }
class="kw">public:
class=class="str">"cmt">//--- Set(class="num">1) integer, (class="num">2) real and(class="num">3) class="type">class="kw">string properties of indicator data
  class="type">void    SetProperty(ENUM_IND_DATA_PROP_INTEGER class="kw">property,class="type">long value) { this.m_long_prop[class="kw">property]=value; }
  class="type">void    SetProperty(ENUM_IND_DATA_PROP_DOUBLE class="kw">property,class="type">class="kw">double value){ this.m_double_prop[this.IndexProp(class="kw">property)]=value; }
  class="type">void    SetProperty(ENUM_IND_DATA_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 class="kw">property of indicator data from the properties array
  class="type">long    GetProperty(ENUM_IND_DATA_PROP_INTEGER class="kw">property)  class="kw">const { class="kw">return this.m_long_prop[class="kw">property]; }
  class="type">class="kw">double  GetProperty(ENUM_IND_DATA_PROP_DOUBLE class="kw">property)    class="kw">const { class="kw">return this.m_double_prop[this.IndexProp(class="kw">property)]; }
  class="type">class="kw">string  GetProperty(ENUM_IND_DATA_PROP_STRING class="kw">property)    class="kw">const { class="kw">return this.m_string_prop[this.IndexProp(class="kw">property)]; }
class=class="str">"cmt">//--- Return the flag of the object supporting this class="kw">property
  class="kw">virtual class="type">bool   SupportProperty(ENUM_IND_DATA_PROP_INTEGER class="kw">property)    { class="kw">return true; }
  class="kw">virtual class="type">bool   SupportProperty(ENUM_IND_DATA_PROP_DOUBLE class="kw">property)     { class="kw">return true; }

◍ 指标数据对象的内部接口与构造

在 MT5 自定义指标框架里,CDataInd 这类数据容器负责把「某品种、某周期、某根 K 线」上的指标状态打包成可排序、可比对的对象。它暴露的一组 Set 方法,本质是把散落的指标属性写进统一的属性槽位,方便后续在列表里按字段检索。 看下面这段接口声明,SupportProperty 直接返回 true,意味着字符串类指标属性在该对象上全部被放行;GetObject 则返回自身指针,让外部调用者能原地修改而不必拷贝。 [CODE] virtual bool SupportProperty(ENUM_IND_DATA_PROP_STRING property) { return true; } //--- Return itself CDataInd *GetObject(void) { return &this; } //--- Set (1) symbol, timeframe and time for the object, (2) indicator type, (3) number of buffers, (4) number of data buffer, //--- (5) ID, (6) data value, (7) name, (8) indicator short name void SetSymbolPeriod(const string symbol,const ENUM_TIMEFRAMES timeframe,const datetime time); void SetIndicatorType(const ENUM_INDICATOR type) { this.SetProperty(IND_DATA_PROP_INDICATOR_TYPE,type); } void SetBufferNum(const int num) { this.SetProperty(IND_DATA_PROP_IND_BUFFER_NUM,num); } void SetIndicatorID(const int id) { this.SetProperty(IND_DATA_PROP_IND_ID,id); } void SetBufferValue(const double value) { this.SetProperty(IND_DATA_PROP_BUFFER_VALUE,value); } void SetIndicatorName(const string name) { this.SetProperty(IND_DATA_PROP_IND_NAME,name); } void SetIndicatorShortname(const string shortname) { this.SetProperty(IND_DATA_PROP_IND_SHORTNAME,shortname); } //--- Compare CDataInd objects with each other by all possible properties (for sorting the lists by a specified object property) virtual int Compare(const CObject *node,const int mode=0) const; //--- Compare CDataInd objects with each other by all properties (to search equal objects) bool IsEqual(CDataInd* compared_data) const; //--- Constructors CDataInd(){;} CDataInd(const ENUM_INDICATOR ind_type, [/CODE] 逐行拆一下关键行:SupportProperty 返回 true 表示接受所有字符串型指标属性;GetObject 回传 this 指针,外部拿到后能直接调 Set 系列改属性。SetIndicatorType 到 SetIndicatorShortname 这七个 inline 方法,分别把指标类型、缓冲区数、ID、缓冲值、名称、短名写进对应的枚举属性槽,比如 IND_DATA_PROP_BUFFER_VALUE 存的就是那根 K 线上的指标数值(double 型)。 Compare 带 mode 参数,是按属性排序用的虚函数,默认 mode=0 一般对应基础比较逻辑;IsEqual 则拿另一个 CDataInd 指针做全属性比对,用来在列表里查重。两个构造函数中,无参版留空,带 ENUM_INDICATOR 参数的重载为后续按类型初始化留了入口。 开 MT5 在 MetaEditor 里搜 CDataInd 能看到完整声明,把上面代码贴进自定义类的公有段,就能在 EA 里用 SetBufferValue 把实时指标值灌进对象、再用 IsEqual 做跨品种信号去重。外汇与贵金属杠杆高,这类对象级比对只解决代码结构问题,不预示任何方向。

MQL5 / C++
class="kw">virtual class="type">bool SupportProperty(ENUM_IND_DATA_PROP_STRING class="kw">property) { class="kw">return true; }
class=class="str">"cmt">//--- Return itself
CDataInd *GetObject(class="type">void) { class="kw">return &this; }
class=class="str">"cmt">//--- Set(class="num">1) symbol, timeframe and time for the object, (class="num">2) indicator type, (class="num">3) number of buffers, (class="num">4) number of data buffer,
class=class="str">"cmt">//--- (class="num">5) ID, (class="num">6) data value, (class="num">7) name, (class="num">8) indicator class="type">class="kw">short name
class="type">void SetSymbolPeriod(class="kw">const class="type">class="kw">string symbol,class="kw">const ENUM_TIMEFRAMES timeframe,class="kw">const class="type">class="kw">datetime time);
class="type">void SetIndicatorType(class="kw">const ENUM_INDICATOR type) { this.SetProperty(IND_DATA_PROP_INDICATOR_TYPE,type); }
class="type">void SetBufferNum(class="kw">const class="type">int num) { this.SetProperty(IND_DATA_PROP_IND_BUFFER_NUM,num); }
class="type">void SetIndicatorID(class="kw">const class="type">int id) { this.SetProperty(IND_DATA_PROP_IND_ID,id); }
class="type">void SetBufferValue(class="kw">const class="type">class="kw">double value) { this.SetProperty(IND_DATA_PROP_BUFFER_VALUE,value); }
class="type">void SetIndicatorName(class="kw">const class="type">class="kw">string name) { this.SetProperty(IND_DATA_PROP_IND_NAME,name); }
class="type">void SetIndicatorShortname(class="kw">const class="type">class="kw">string shortname) { this.SetProperty(IND_DATA_PROP_IND_SHORTNAME,shortname); }
class=class="str">"cmt">//--- Compare CDataInd objects with each other by all possible properties(for sorting the lists by a specified object class="kw">property)
class="kw">virtual class="type">int Compare(class="kw">const CObject *node,class="kw">const class="type">int mode=class="num">0) class="kw">const;
class=class="str">"cmt">//--- Compare CDataInd objects with each other by all properties(to search equal objects)
class="type">bool IsEqual(CDataInd* compared_data) class="kw">const;
class=class="str">"cmt">//--- Constructors
CDataInd(){;}
CDataInd(class="kw">const ENUM_INDICATOR ind_type,

「从指标事件里直接抠出周期和数值」

在 MT5 的指标事件回调里,系统传进来的不是一个裸数值,而是一组描述「这条数据从哪来」的上下文。上面这段接口声明定义了事件触发时你能拿到的核心参数:指标 ID、缓冲区编号、品种名、时间周期和具体时间。 真正方便的是封装好的访问方法。Time() 直接返回该 bar 的起始时间,Timeframe() 给出 ENUM_TIMEFRAMES 周期枚举,IndicatorType() 告诉你触发的是哪类指标,BufferNum() 和 IndicatorID() 则定位到具体缓冲区和实例。PriceValue() 才是你写策略最常用的一行——它返回该缓冲区在当前事件下的数值。 写 EA 时不用自己解析事件结构体,直接 double v = data.PriceValue(); 就能拿到值,再配合 data.Timeframe() 判断是不是在 H1 上触发。外汇与贵金属杠杆高、滑点随机,这类取值只解决「拿到数」,不代表信号一定有效,回测前先在策略测试器跑一遍不同点差环境。

MQL5 / C++
class="kw">const class="type">int ind_id,
class="kw">const class="type">int buffer_num,
class="kw">const class="type">class="kw">string symbol,
class="kw">const ENUM_TIMEFRAMES timeframe,
class="kw">const class="type">class="kw">datetime time);

class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Methods of simplified access to object properties                |
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//--- Return(class="num">1) bar period start time, (class="num">2) timeframe, (class="num">3) indicator type, (class="num">4) number of buffers, (class="num">5) buffer number, (class="num">6) indicator ID
  class="type">class="kw">datetime           Time(class="type">void)                                                    class="kw">const { class="kw">return (class="type">class="kw">datetime)this.GetProperty(IND_DATA_PROP_TIME);                    }
  ENUM_TIMEFRAMES    Timeframe(class="type">void)                                                class="kw">const { class="kw">return (ENUM_TIMEFRAMES)this.GetProperty(IND_DATA_PROP_PERIOD);           }
  ENUM_INDICATOR     IndicatorType(class="type">void)                                            class="kw">const { class="kw">return (ENUM_INDICATOR)this.GetProperty(IND_DATA_PROP_INDICATOR_TYPE);    }
  class="type">int                BufferNum(class="type">void)                                                class="kw">const { class="kw">return (ENUM_INDICATOR)this.GetProperty(IND_DATA_PROP_IND_BUFFER_NUM);    }
  class="type">int                IndicatorID(class="type">void)                                              class="kw">const { class="kw">return (ENUM_INDICATOR)this.GetProperty(IND_DATA_PROP_IND_ID);            }
class=class="str">"cmt">//--- Return the price of indicator buffer data
  class="type">class="kw">double             PriceValue(class="type">void)                                               class="kw">const { class="kw">return this.GetProperty(IND_DATA_PROP_BUFFER_VALUE);                      }

class=class="str">"cmt">//--- Return(class="num">1) data symbol, (class="num">2) name, (class="num">3) indicator class="type">class="kw">short name

指标数据对象的属性读取与跨周期定位

在 MT5 自定义指标类里,CIndicatorData 对象封装了当前图表的品种、指标名、短名与小数位等基础属性。直接调用 Symbol()、IndicatorName()、IndicatorShortName() 和 Digits() 即可拿到对应字符串或整数,无需再走全局函数二次查询。 跨周期对齐是实盘常用的动作。Index() 方法内部调用 iBarShift,把对象所属 K 线时间映射到指定周期(PERIOD_CURRENT 会回落到当前图表 Period())的 bar 索引;若时间不在该周期历史内,返回 -1。 属性自省方面,GetPropertyDescription 提供了整数、双精度、字符串三类枚举的描述文本,IndicatorTypeDescription() 则转调全局函数给出指标类型说明。Print(false) 只打印对象支持的属性,Print(true) 输出全部属性,方便在专家日志里排查配置漂移。 外汇与贵金属波动剧烈、杠杆风险高,跨周期索引错位可能导致信号延迟,建议先在策略测试器用历史数据验证 Index() 的返回值边界。

MQL5 / C++
class="type">class="kw">string Symbol(class="type">void) class="kw">const { class="kw">return this.GetProperty(IND_DATA_PROP_SYMBOL); }
class="type">class="kw">string IndicatorName(class="type">void) class="kw">const { class="kw">return this.GetProperty(IND_DATA_PROP_IND_NAME); }
class="type">class="kw">string IndicatorShortName(class="type">void) class="kw">const { class="kw">return this.GetProperty(IND_DATA_PROP_IND_SHORTNAME); }
class=class="str">"cmt">//--- Return bar index on the specified timeframe the object bar time falls into
class="type">int Index(class="kw">const ENUM_TIMEFRAMES timeframe) class="kw">const
   { class="kw">return ::iBarShift(this.Symbol(),(timeframe==PERIOD_CURRENT ? ::Period() : timeframe),this.Time()); }
class=class="str">"cmt">//--- Return Digits set for the object
class="type">int Digits(class="type">void) class="kw">const { class="kw">return this.m_digits; }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Description of properties of the object - indicator data          |
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//--- Return description of object&class="macro">#x27;s(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_IND_DATA_PROP_INTEGER class="kw">property);
class="type">class="kw">string GetPropertyDescription(ENUM_IND_DATA_PROP_DOUBLE class="kw">property);
class="type">class="kw">string GetPropertyDescription(ENUM_IND_DATA_PROP_STRING class="kw">property);
class=class="str">"cmt">//--- Return indicator type description
class="type">class="kw">string IndicatorTypeDescription(class="type">void) class="kw">const { class="kw">return ::IndicatorTypeDescription(this.IndicatorType()); }
class=class="str">"cmt">//--- Display the description of object properties in the journal(full_prop=true - all properties, class="kw">false - supported ones only)
class="type">void Print(class="kw">const class="type">bool full_prop=class="kw">false);

◍ 指标数据对象的比较与判等逻辑

在自建指标数据容器时,CDataInd 类用一套统一接口处理对象间的排序与判等,避免每次都比对所有字段写一堆 if。Compare() 按 mode 参数分流:小于 IND_DATA_PROP_INTEGER_TOTAL 走整型比较,中间段走双精度比较,再往后走字符串比较,返回 1 / -1 / 0 三态,直接兼容 CObject 派生类的集合排序。 IsEqual() 则是穷举式判等:先扫整型属性 0 到 BAR_PROP_INTEGER_TOTAL,再偏移双精度与字符串区段,任意一项不等立即 return false,全过才 return true。注意它起手用 beg=0, end=BAR_PROP_INTEGER_TOTAL,若你自定义整型属性总数与此宏不一致,拷贝这段代码会漏比或越界。 外汇与贵金属行情下这类对象常用于多周期指标快照比对,杠杆与滑点放大了微小数值差异,双精度比较建议自行加容差,不要直接依赖原版等于逻辑。

MQL5 / C++
class=class="str">"cmt">//--- Display a class="type">class="kw">short description of the object in the journal
  class="kw">virtual class="type">void      PrintShort(class="type">void);
class=class="str">"cmt">//---
  };
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+-------------------------------------------------------------------+
class=class="str">"cmt">//| Compare CDataInd objects with each other by the specified class="kw">property|
class=class="str">"cmt">//+-------------------------------------------------------------------+
class="type">int CDataInd::Compare(class="kw">const CObject *node,class="kw">const class="type">int mode=class="num">0) class="kw">const
  {
   class="kw">const CDataInd *obj_compared=node;
class=class="str">"cmt">//--- compare integer properties of two objects
   if(mode<IND_DATA_PROP_INTEGER_TOTAL)
     {
      class="type">long value_compared=obj_compared.GetProperty((ENUM_IND_DATA_PROP_INTEGER)mode);
      class="type">long value_current=this.GetProperty((ENUM_IND_DATA_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 objects
   else if(mode<IND_DATA_PROP_DOUBLE_TOTAL+IND_DATA_PROP_INTEGER_TOTAL)
     {
      class="type">class="kw">double value_compared=obj_compared.GetProperty((ENUM_IND_DATA_PROP_DOUBLE)mode);
      class="type">class="kw">double value_current=this.GetProperty((ENUM_IND_DATA_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 objects
   else if(mode<IND_DATA_PROP_DOUBLE_TOTAL+IND_DATA_PROP_INTEGER_TOTAL+IND_DATA_PROP_STRING_TOTAL)
     {
      class="type">class="kw">string value_compared=obj_compared.GetProperty((ENUM_IND_DATA_PROP_STRING)mode);
      class="type">class="kw">string value_current=this.GetProperty((ENUM_IND_DATA_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">//+------------------------------------------------------------------+
class=class="str">"cmt">//|Compare CDataInd objects with each other by all properties          |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CDataInd::IsEqual(CDataInd *compared_obj) class="kw">const
  {
   class="type">int beg=class="num">0, end=BAR_PROP_INTEGER_TOTAL;
   for(class="type">int i=beg; i<end; i++)
     {
      ENUM_IND_DATA_PROP_INTEGER prop=(ENUM_IND_DATA_PROP_INTEGER)i;
      if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) class="kw">return class="kw">false;
     }
   beg=end; end+=IND_DATA_PROP_DOUBLE_TOTAL;
   for(class="type">int i=beg; i<end; i++)
     {
      ENUM_IND_DATA_PROP_DOUBLE prop=(ENUM_IND_DATA_PROP_DOUBLE)i;
      if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) class="kw">return class="kw">false;
     }
   beg=end; end+=IND_DATA_PROP_STRING_TOTAL;
   for(class="type">int i=beg; i<end; i++)
     {
      ENUM_IND_DATA_PROP_STRING prop=(ENUM_IND_DATA_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">//+------------------------------------------------------------------+

「给指标对象绑定品种周期并打印自检」

把数据指标挂到具体品种和周期上,靠的是 SetSymbolPeriod 这个方法。它一次性写入三个属性:基准时间、交易品种字符串、以及 ENUM_TIMEFRAMES 周期枚举,后续该对象取价就锁定在这组上下文里。 Print 方法负责把对象属性铺到 MT5 Experts 日志里。整数、双精度、字符串三类属性分三段循环打印,full_prop 为 false 时只输出对象实际支持的属性,避免刷屏;设为 true 才会把全部枚举项都列出来。 PrintShort 则只吐一行极简信息:指标短名加缓冲区编号和索引。实测日志里能看到类似「AMA(EURUSD,H1) [Buffer 0, Index 0]」和「Examples\Custom Moving Average.ex5(EURUSD,H1) [Buffer 0, Index 1]」这样的输出,说明同一品种周期下不同加载实例的索引是递增分配的。 开 MT5 自己写一个 CDataInd 派生类,挂 EURUSD 的 H1 后调一次 PrintShort,就能确认你的实例索引有没有和已有指标撞车。外汇与贵金属杠杆高,这类底层绑定错误可能在回测和实盘切换时引发取价错位,需自行验证。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Set symbol, timeframe and object bar start time                  |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CDataInd::SetSymbolPeriod(class="kw">const class="type">class="kw">string symbol,class="kw">const ENUM_TIMEFRAMES timeframe,class="kw">const class="type">class="kw">datetime time)
  {
   this.SetProperty(IND_DATA_PROP_TIME,time);
   this.SetProperty(IND_DATA_PROP_SYMBOL,symbol);
   this.SetProperty(IND_DATA_PROP_PERIOD,timeframe);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Display object properties in the journal                         |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CDataInd::Print(class="kw">const class="type">bool full_prop=class="kw">false)
  {
   ::Print("============= ",CMessage::Text(MSG_LIB_PARAMS_LIST_BEG)," (",this.IndicatorShortName(),") =============");
   class="type">int beg=class="num">0, end=IND_DATA_PROP_INTEGER_TOTAL;
   for(class="type">int i=beg; i<end; i++)
     {
      ENUM_IND_DATA_PROP_INTEGER prop=(ENUM_IND_DATA_PROP_INTEGER)i;
      if(!full_prop && !this.SupportProperty(prop)) class="kw">continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("------");
   beg=end; end+=IND_DATA_PROP_DOUBLE_TOTAL;
   for(class="type">int i=beg; i<end; i++)
     {
      ENUM_IND_DATA_PROP_DOUBLE prop=(ENUM_IND_DATA_PROP_DOUBLE)i;
      if(!full_prop && !this.SupportProperty(prop)) class="kw">continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("------");
   beg=end; end+=IND_DATA_PROP_STRING_TOTAL;
   for(class="type">int i=beg; i<end; i++)
     {
      ENUM_IND_DATA_PROP_STRING prop=(ENUM_IND_DATA_PROP_STRING)i;
      if(!full_prop && !this.SupportProperty(prop)) class="kw">continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("============= ",CMessage::Text(MSG_LIB_PARAMS_LIST_END)," (",this.IndicatorShortName(),") =============\n");
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Display a class="type">class="kw">short description of the object in the journal          |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CDataInd::PrintShort(class="type">void)
  {
   ::Print(
      this.IndicatorShortName(),
      " [",CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_BUFFER)," ",this.BufferNum(),
      ", ",CMessage::Text(MSG_SYM_STATUS_INDEX)," ",this.Index(this.Timeframe()),"]"
     );
  }
class=class="str">"cmt">//+------------------------------------------------------------------+

常见问题

用三数组封装把时间、值和缓冲索引分开存,读取时按索引对应取,避免混写导致错位。
可以,在事件回调里直接读事件带出的周期与数值字段,省一次计算,但要注意事件源品种是否匹配。
小布可读取你绑定的品种周期配置并做自检提示,打开对应品种页就能看到错位或空值警告。
用比较与判等逻辑比品种、周期、缓冲号和柱时间,四者一致才视为相等,别只比数值。
在构造后调打印接口输出品种、周期与缓冲状态,若显示空周期就是没绑成功,重设即可。