DoEasy 函数库中的时间序列(第五十六部分):自定义指标对象,从集合中的指标对象获取数据(基础篇)
📘

DoEasy 函数库中的时间序列(第五十六部分):自定义指标对象,从集合中的指标对象获取数据(基础篇)

第 1/3 篇

「从指标集合里把数据抠出来」

DoEasy 函数库在第五十六部分把自定义指标封装成了集合对象,重点不是画指标,而是能在 EA 或脚本里直接调用集合里某个指标实例、按缓冲区和位置取数。 原文示例发布于 2021-02-24,统计口径下该文阅读量 1229、评论 2 条,说明这类底层封装对实盘玩家属于冷门但高复用度的东西。 对交易者来说,直接价值是:不用每次在 MT5 里手写 iCustom 再 CopyBuffer,而是用库对象方法统一拿值,方便做多指标共振判定。外汇与贵金属杠杆高,任何信号都只是概率倾向,务必先在策略测试器跑历史再上模拟。

自定义指标对象的构造差异

标准指标在 MT5 终端里的集合是固定的,每个指标的输入参数数量和类型都事先明确,因此可以直接调用各自的创建方法并写死参数组合。自定义指标则不同,参数集合在编译期未知,无法用同样方式实例化。 处理自定义指标时,必须把已经填充好的“指标输入结构数组”传给创建方法,方法内部再从中提取全部必要属性和参数。代价是函数库使用者要手动维护这个结构数组,不能像标准指标那样一行调用搞定。 为了能在 EA 里随时找回已创建的指标,每个对象都会打上一个唯一 ID。已知 ID 与对象的对应关系,就能在程序任意位置按 ID 调出指标。另一种途径是按创建时指定的完整参数集反查对象,拿到引用后读取数据。 本次先落地数据接收方法:从指标缓冲区取数并复制到普通数组,后续文章再拿这些数组做统计研究。外汇与贵金属行情波动剧烈,用这类自定义指标做图形分析仍属高风险操作,结论只代表概率倾向。

◍ 给指标对象挂上可检索的 ID

在 MT5 自建指标库里,标准指标能在构造时定死分组(趋势、震荡、成交量、箭头),但自定义指标在 new 的那一刻你根本不知道它该归哪一类。办法是新增一个 ‘any’ 分组作为默认槽位,对象建完再由调用方手动指定真实分组。 为了让列表能按唯一标识捞指标,我们在 Defines.mqh 的指标整数属性枚举里加了一个 IND_ID,并把整数属性总数从 5 调到 6。构造函数里先把 ID 默认设为 -1,意味着‘还没分配’。 比较两个指标对象是否同参时,必须跳过 handle 和 ID——这两个天生唯一,不跳过的话每个实例都会被误判成‘不同指标’,导致参数比对失效。 抽象类 IndicatorDE 公开区补了 GetID / SetID,以及一组实用方法:显示 MqlParam 参数说明、按索引或时间取 CopyBuffer 数据、取柱线时间。取数失败就返回对象预设的空值,不会抛异常。 DELib 里另写了 MqlParameterDescription(),依据 MqlParam 的 type 字段拼出‘Type xxx: 值’的日志串,自定义指标四个参数说明直接能打印出来辅助排查。所有标准指标类的简称方法也顺手把 ID 塞进括号里,ID 为 -1 时只留空括号。

MQL5 / C++
  MSG_LIB_TEXT_IND_TEXT_GROUP,                    class=class="str">"cmt">// Indicator group
  MSG_LIB_TEXT_IND_TEXT_GROUP_TREND,                class=class="str">"cmt">// Trend indicator
  MSG_LIB_TEXT_IND_TEXT_GROUP_OSCILLATOR,           class=class="str">"cmt">// Oscillator
  MSG_LIB_TEXT_IND_TEXT_GROUP_VOLUMES,              class=class="str">"cmt">// Volumes
  MSG_LIB_TEXT_IND_TEXT_GROUP_ARROWS,               class=class="str">"cmt">// Arrow indicator
  MSG_LIB_TEXT_IND_TEXT_ID,                         class=class="str">"cmt">// Indicator ID

  MSG_LIB_TEXT_IND_TEXT_EMPTY_VALUE,                class=class="str">"cmt">// Empty value for plotting where nothing will be drawn
  MSG_LIB_TEXT_IND_TEXT_SYMBOL,                     class=class="str">"cmt">// Indicator symbol
  MSG_LIB_TEXT_IND_TEXT_NAME,                       class=class="str">"cmt">// Indicator name
  MSG_LIB_TEXT_IND_TEXT_SHORTNAME,                  class=class="str">"cmt">// Indicator class="type">class="kw">short name

  MSG_LIB_TEXT_IND_TEXT_IND_PARAMETERS,             class=class="str">"cmt">// Indicator parameters
  MSG_LIB_TEXT_IND_TEXT_APPLIED_VOLUME,             class=class="str">"cmt">// Volume type for calculation
  MSG_LIB_TEXT_IND_TEXT_PERIOD,                     class=class="str">"cmt">// Averaging period
  MSG_LIB_TEXT_IND_TEXT_FAST_PERIOD,                class=class="str">"cmt">// Fast MA period
  MSG_LIB_TEXT_IND_TEXT_SLOW_PERIOD,                class=class="str">"cmt">// Slow MA period
  MSG_LIB_TEXT_IND_TEXT_SIGNAL,                     class=class="str">"cmt">// Difference averaging period
  MSG_LIB_TEXT_IND_TEXT_TENKAN_PERIOD,              class=class="str">"cmt">// Tenkan-sen period
  MSG_LIB_TEXT_IND_TEXT_KIJUN_PERIOD,               class=class="str">"cmt">// Kijun-sen period
  MSG_LIB_TEXT_IND_TEXT_SPANB_PERIOD,               class=class="str">"cmt">// Senkou Span B period

「指标参数与容器报错的中文映射」

在 MT5 自编指标库里,参数说明文本通常用枚举常量统一管理。下面这段枚举覆盖了从鳄鱼线(Jaw/Teeth/Lips)周期与位移,到布林类通道的标准差、偏离,再到随机指标 K/D/Slowing 与 CMO 周期等常见输入项,每个常量后跟的注释就是界面上实际显示的中文释义。 MSG_LIB_TEXT_IND_TEXT_JAW_PERIOD 对应颌线计算周期,MSG_LIB_TEXT_IND_TEXT_TEETH_PERIOD 对应齿线周期,MSG_LIB_TEXT_IND_TEXT_LIPS_PERIOD 对应唇线周期;三组水平位移(SHIFT)分别控制各线在图表上的滞后偏移量。 MSG_LIB_TEXT_IND_TEXT_STD_DEVIATION 是通道标准差倍数,MSG_LIB_TEXT_IND_TEXT_DEVIATION 是通道边界相对中线的偏离,这两个值直接决定通道宽度。MSG_LIB_TEXT_IND_TEXT_STEP 与 MAXIMUM 是加速因子步长与上限,常用于抛物线类指标。 高亮的三行属于自定义与容器层:MSG_LIB_TEXT_IND_TEXT_CUSTOM_PARAM 是通用输入参数占位;CIndicatorsCollection 相关的 MSG_LIB_SYS_INVALID_IND_POINTER(指针无效)与 MSG_LIB_SYS_IND_ID_EXIST(ID 重复)是往指标列表塞对象时最常见的两条运行时报错。 另有一段结构化文本常量,把 Arrow indicator、Indicator ID、Empty value for plotting 等字段名直接写成字符串数组,供 UI 或日志调用。开 MT5 在 MetaEditor 搜这些 MSG_LIB_TEXT 前缀,就能看到你当前标准库里的实际措辞。

MQL5 / C++
  MSG_LIB_TEXT_IND_TEXT_JAW_PERIOD,                    class=class="str">"cmt">// Period for jaw line calculation
  MSG_LIB_TEXT_IND_TEXT_TEETH_PERIOD,                   class=class="str">"cmt">// Period for teeth line calculation
  MSG_LIB_TEXT_IND_TEXT_LIPS_PERIOD,                   class=class="str">"cmt">// Period for lips line calculation
  MSG_LIB_TEXT_IND_TEXT_JAW_SHIFT,                     class=class="str">"cmt">// Horizontal shift of jaws line
  MSG_LIB_TEXT_IND_TEXT_TEETH_SHIFT,                   class=class="str">"cmt">// Horizontal shift of teeth line
  MSG_LIB_TEXT_IND_TEXT_LIPS_SHIFT,                    class=class="str">"cmt">// Horizontal shift of lips line
  MSG_LIB_TEXT_IND_TEXT_SHIFT,                         class=class="str">"cmt">// Horizontal shift of the indicator
  MSG_LIB_TEXT_IND_TEXT_MA_METHOD,                     class=class="str">"cmt">// Smoothing type
  MSG_LIB_TEXT_IND_TEXT_APPLIED_PRICE,                 class=class="str">"cmt">// Price type or handle
  MSG_LIB_TEXT_IND_TEXT_STD_DEVIATION,                 class=class="str">"cmt">// Number of standard deviations
  MSG_LIB_TEXT_IND_TEXT_DEVIATION,                     class=class="str">"cmt">// Deviation of channel borders from the central line
  MSG_LIB_TEXT_IND_TEXT_STEP,                          class=class="str">"cmt">// Price change step — acceleration factor
  MSG_LIB_TEXT_IND_TEXT_MAXIMUM,                       class=class="str">"cmt">// Maximum step
  MSG_LIB_TEXT_IND_TEXT_KPERIOD,                       class=class="str">"cmt">// K-period(number of bars for calculation)
  MSG_LIB_TEXT_IND_TEXT_DPERIOD,                       class=class="str">"cmt">// D-period(primary smoothing period)
  MSG_LIB_TEXT_IND_TEXT_SLOWING,                       class=class="str">"cmt">// Final smoothing
  MSG_LIB_TEXT_IND_TEXT_PRICE_FIELD,                   class=class="str">"cmt">// Stochastic calculation method
  MSG_LIB_TEXT_IND_TEXT_CMO_PERIOD,                    class=class="str">"cmt">// Chande Momentum period
  MSG_LIB_TEXT_IND_TEXT_SMOOTHING_PERIOD,              class=class="str">"cmt">// Smoothing factor period
  MSG_LIB_TEXT_IND_TEXT_CUSTOM_PARAM,                  class=class="str">"cmt">// Input parameter

class=class="str">"cmt">//--- CIndicatorsCollection
  MSG_LIB_SYS_FAILED_ADD_IND_TO_LIST,                  class=class="str">"cmt">// Error. Failed to add indicator object to the list
  MSG_LIB_SYS_INVALID_IND_POINTER,                     class=class="str">"cmt">// Error. Invalid pointer to indicator object is passed
  MSG_LIB_SYS_IND_ID_EXIST,                            class=class="str">"cmt">// Error. Indicator object with ID already exists

  };
class=class="str">"cmt">//+------------------------------------------------------------------+
  {"Arrow indicator"},
  {"Indicator ID"},
  {"Empty value for plotting, for which there is no drawing"},
  {"Indicator symbol"},
  {"Indicator name"},
  {"Indicator shortname"},

聚合指标的参数与分组枚举

在把多个指标塞进同一个管理容器时,先用一组字符串常量描述每个输入项。从成交量类型、均线快/慢周期,到 Ichimoku 的 Tenkan-sen(转换线)、Kijun-sen(基准线)、Senkou Span B 周期,再到鳄鱼线的颚/齿/唇三段周期与水平位移,覆盖了 28 个以上的配置字段。 这些常量后面紧跟三条错误描述串:添加指标对象到列表失败、传入的指标对象指针无效、以及相同 ID 的指标对象已存在。你在 MT5 里跑多指标调度框架时,若日志出现这三类中文报错,基本可锁定是容器注册环节出了岔子。 指标归类用 ENUM_INDICATOR_GROUP 枚举完成,包含趋势、震荡、成交量、箭头四类,外加一个 INDICATOR_GROUP_ANY 兜底项。外汇与贵金属波动剧烈、杠杆风险高,回测多指标组合时建议先用 ANY 组做宽口过滤,再按具体类别细分。

MQL5 / C++
enum ENUM_INDICATOR_GROUP
  {
  INDICATOR_GROUP_TREND,                                           class=class="str">"cmt">// Trend indicator
  INDICATOR_GROUP_OSCILLATOR,                                      class=class="str">"cmt">// Oscillator
  INDICATOR_GROUP_VOLUMES,                                         class=class="str">"cmt">// Volumes
  INDICATOR_GROUP_ARROWS,                                          class=class="str">"cmt">// Arrow indicator
  INDICATOR_GROUP_ANY,                                             class=class="str">"cmt">// Any indicator
  };

◍ 指标整数属性的枚举与排序起点

MT5 自定义指标在内部以整数属性描述自身状态,核心枚举 ENUM_INDICATOR_PROP_INTEGER 列出了 6 个字段:状态、类型、周期、句柄、分组与 ID。其中 INDICATOR_PROP_ID 常被忽略,但它是指标在终端内部的唯一身份标记,调试多指标协同时有用。 宏 INDICATOR_PROP_INTEGER_TOTAL 被定为 6,SKIP 为 0,说明这 6 项全部参与排序逻辑;FIRST_INDICATOR_DBL_PROP 与 FIRST_INDICATOR_STR_PROP 则按 (总数-跳过数) 递推,给双精度与字符串属性预留偏移位。 排序枚举 ENUM_SORT_INDICATOR_MODE 从 SORT_BY_INDICATOR_INDEX_STATUS 起,到 SORT_BY_INDICATOR_ID 止,均映射上述整数属性。若你在 EA 里批量管理十几个指标,按 ID 或句柄排序比按名称稳,能规避重名覆盖。 开 MT5 新建脚本,把下面枚举贴进去编译,改 INDICATOR_PROP_INTEGER_TOTAL 的值观察 FIRST_INDICATOR_DBL_PROP 宏展开变化,就能确认偏移计算是否符合你的面板设计。

MQL5 / C++
enum ENUM_INDICATOR_PROP_INTEGER
  {
   INDICATOR_PROP_STATUS = class="num">0,                 class=class="str">"cmt">// Indicator status(from enumeration ENUM_INDICATOR_STATUS)
   INDICATOR_PROP_TYPE,                       class=class="str">"cmt">// Indicator type(from enumeration ENUM_INDICATOR)
   INDICATOR_PROP_TIMEFRAME,                  class=class="str">"cmt">// Indicator timeframe
   INDICATOR_PROP_HANDLE,                     class=class="str">"cmt">// Indicator handle
   INDICATOR_PROP_GROUP,                      class=class="str">"cmt">// Indicator group
   INDICATOR_PROP_ID,                         class=class="str">"cmt">// Indicator ID
   };
class="macro">#define INDICATOR_PROP_INTEGER_TOTAL(class="num">6)      class=class="str">"cmt">// Total number of indicator integer properties
class="macro">#define INDICATOR_PROP_INTEGER_SKIP(class="num">0)      class=class="str">"cmt">// Number of indicator properties not used in sorting
class="macro">#define FIRST_INDICATOR_DBL_PROP(INDICATOR_PROP_INTEGER_TOTAL-INDICATOR_PROP_INTEGER_SKIP)
class="macro">#define FIRST_INDICATOR_STR_PROP(INDICATOR_PROP_INTEGER_TOTAL-INDICATOR_PROP_INTEGER_SKIP+INDICATOR_PROP_DOUBLE_TOTAL-INDICATOR_PROP_DOUBLE_SKIP)
enum ENUM_SORT_INDICATOR_MODE
  {
class=class="str">"cmt">//--- Sort by integer properties
   SORT_BY_INDICATOR_INDEX_STATUS = class="num">0,        class=class="str">"cmt">// Sort by indicator status
   SORT_BY_INDICATOR_TYPE,                    class=class="str">"cmt">// Sort by indicator type
   SORT_BY_INDICATOR_TIMEFRAME,               class=class="str">"cmt">// Sort by indicator timeframe
   SORT_BY_INDICATOR_HANDLE,                  class=class="str">"cmt">// Sort by indicator handle
   SORT_BY_INDICATOR_GROUP,                   class=class="str">"cmt">// Sort by indicator group
   SORT_BY_INDICATOR_ID,                      class=class="str">"cmt">// Sort by indicator ID
class=class="str">"cmt">//--- Sort by real properties

常见问题

给每个指标对象在构造时挂上唯一 ID,再用集合的按 ID 检索方法拿到对象,调用其数据接口读取对应缓冲区的最新柱值即可。
差异主要在参数传入方式和缓冲区绑定逻辑,自定义指标需显式声明参数数组与数据类型,平台内置指标已封装好这些细节。
可以,小布能按你设定的品种和指标 ID 自动扫描容器状态,把参数错误和空数据预警直接推到看盘界面,省去手动排查。
通常是聚合指标的分组枚举值没按库定义的顺序填,或者把不同组的参数混在了一个数组里,对照分组枚举表拆开传参就能解。
固定起点是为了避免和平台原有属性 ID 冲突,自定义枚举从约定偏移量往后排,能保证集合内排序和检索不串号。