DoEasy 函数库中的时间序列(第三十五部分):柱线对象和品种时间序列列表(基础篇)
📘

DoEasy 函数库中的时间序列(第三十五部分):柱线对象和品种时间序列列表(基础篇)

第 1/3 篇

「在 DoEasy 里把柱线对象和时间序列列表跑起来」

DoEasy 函数库把 MT5 的时序数据封装成对象,第三十五部分重点落在柱线对象与多品种时间序列列表的管理上。对做跨品种价格行为比对的交易者来说,直接拿到标准化对象比反复调 CopyRates 更省事。 在 MT5 里加载该函数库后,可针对任意品种建立时间序列列表,列表内每个元素即一根柱线对象,包含开高低收与成交量等字段。贵金属与外汇品种共用同一套接口,但跨周期拉取时需注意点差与跳空带来的偏差,这类品种波动大、杠杆高,实盘验证前先用策略测试器跑一遍。 下面的代码演示了从函数库获取当前品种柱线对象并读取收盘价的逻辑,复制到 MT5 的 EA 或脚本里即可编译验证。

MQL5 / C++
class=class="str">"cmt">//class="macro">#include <DoEasy.h>
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Script program start function                                    |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnStart()
  {
   class=class="str">"cmt">// 获取当前图表品种的时间序列对象
   CTimeSeries *ts = new CTimeSeries(Symbol(), PERIOD_CURRENT);
   class=class="str">"cmt">// 取最新一根柱线对象
   CBar *bar = ts.GetBar(class="num">0);
   class=class="str">"cmt">// 输出最新收盘价到日志
   Print("Last close: ", bar.GetClose());
   class="kw">delete ts;
  }

函数库对象概念与两个系列分工

这套函数库把 MT5/MT4 的程序开发拆成了清晰的对象层。第一个系列共 34 篇,专门讲对象及其互连概念,落地目标是操控账户当前状态和历史记录。 已实现的账户侧能力包括:搜索、排序、比较任意订单或仓位数据;快捷访问全部订单和仓位属性;跟踪账户事件;获取并比较账户与品种数据;当集合里对象的属性变化时将事件通知给程序。还可以指定函数只响应哪些事件,只推送受监视的通知。 前一个系列还做了延后交易请求——一种新型交易请求,让你按各种交易条件写行为逻辑,并具备创建新型挂单的完整能力。 第二个系列转向价格数据、品种图表、市场深度、指标等。核心是把任何价格数据数组的快速访问、搜索、比较、排序,以及它们的存储对象和来源都封装好。外汇和贵金属波动剧烈、杠杆高风险极大,这类封装只解决工程效率,不预示任何方向。

◍ 把图表柱线拆成可排序的对象

任何同类数据组都能抽象成对象列表:列表里每个对象属性结构相同,但对应数值各异。给列表按整数、实数或字符串属性排序后,就能在自定义程序里快速定位某根对象、取数、改写。 函数库对象大多能自我跟踪属性——你给某个对象设一个阈值,属性被改到那个数,对象就向主控程序发消息说“碰线了”。底层依托平台标准库的 CObject 及其派生类,对象集合本质是指向这些实例的动态指针数组。 本文要做的 Bar 对象,装下单一品种单周期一根柱线的全部固有数据。MqlRates 里本来就带时间、开高低收、Tick 量、点差、成交量为基础项;创建时还能补年份、月份、周几、年内序号、时分、序列索引、实体与影线尺寸等扩展项。 这些属性叠加后,就能在柱线列表里筛任意组合。列表默认存 1000 根柱线;若实际历史不足或手动改了范围,就存全部可用历史。外汇和贵金属行情跳空频繁,这类对象化历史在回测与实盘间可能有偏差,验证时先开 MT5 用默认品种拉一遍千根柱确认。

「给柱线对象建一套属性骨架」

在 MT5 里做价格行为分析,第一步是把每根 bar 变成可操作的类对象。DoEasy 函数库的做法是在 \MQL5\Include\DoEasy\Defines.mqh 里先补一组枚举:柱线类型分看涨、看跌、零,以及单独的「零实体蜡烛」——后者四个 OHLC 同价但可能带上下影,不能和空白 bar 混为一谈。 实体类型单列出来,是因为蜡烛形态判定要靠实体尺寸和影线位置,零类蜡烛的灯芯尺寸必须参与形状计算。光把类型枚举完还不够,整数、实数、字符串三类属性数组要分别建好,私密段里放计算真实索引和派生属性的方法,公开段暴露读写接口,Bar 类默认支持全部属性返回 true。 构造函数给了三条路:无参声明后补设;传品种+时间帧+索引,内部直接调 CopyRates() 第一种形式拉单根数据;或传现成的 MqlRates 结构数组在循环里批量造对象。Compare() 重写自 CObject,相等返 0、高低返 ±1,配合标准库 Search() 做排序。 时间帧名称不用手敲,从 PERIOD_H1 这类枚举文本里截下标 7 到末尾拿到 “H1” 即可;返回月份名则按编号映射描述。日志输出分简版和全属性版,不支持的属性在 full_prop=false 时自动隐去。 只造 Bar 类还不够——CopyRates() 单次调用没带来额外优势。要管多个品种多周期,得把柱线对象塞进列表,并写个「新柱线」类来侦测任意图表出新 K 线时抛通知,这块留到下一节落地。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Info for working with serial data                                |
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Bar type(candle body)                                           |
class=class="str">"cmt">//+------------------------------------------------------------------+
enum ENUM_BAR_BODY_TYPE
  {
   BAR_BODY_TYPE_BULLISH,                                           class=class="str">"cmt">// Bullish bar
   BAR_BODY_TYPE_BEARISH,                                           class=class="str">"cmt">// Bearish bar
   BAR_BODY_TYPE_NULL,                                              class=class="str">"cmt">// Zero bar
   BAR_BODY_TYPE_CANDLE_ZERO_BODY,                                  class=class="str">"cmt">// Candle with a zero body
   };
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Bar integer properties                                           |
class=class="str">"cmt">//+------------------------------------------------------------------+
enum ENUM_BAR_PROP_INTEGER
  {
   BAR_PROP_INDEX = class="num">0,                                              class=class="str">"cmt">// Bar index in timeseries

K线属性枚举的整型与双精度切分

在 MT5 自定义排序或特征工程里,K线(Bar)的元数据被拆成两套枚举:整型属性 ENUM_BAR_PROP_INTEGER 与双精度属性 ENUM_BAR_PROP_DOUBLE。前者覆盖类型、周期、点差、成交量、时间戳等 14 项,由宏 BAR_PROP_INTEGER_TOTAL 显式定义为 14,跳过排序的项 BAR_PROP_INTEGER_SKIP 为 0。 双精度枚举从 BAR_PROP_INTEGER_TOTAL 的偏移量起步,例如 BAR_PROP_OPEN = BAR_PROP_INTEGER_TOTAL 实际等于 14,后续 BAR_PROP_HIGH 自动顺延为 15。这样设计让整型与浮点属性共用同一索引空间,调用 CopyBuffer 或自定义结构体时不会撞号。 外汇与贵金属属高杠杆品种,直接拿 BAR_PROP_SPREAD 做过滤要小心盘后空白点差;回测里 BAR_PROP_VOLUME_REAL 在多数零售经纪商可能恒为 0,真要用量能就得改用 BAR_PROP_VOLUME_TICK。开 MT5 按 F4 把下面片段塞进头文件,编译能过就说明你的偏移计算没错位。

MQL5 / C++
  BAR_PROP_TYPE,                                                                     class=class="str">"cmt">// Bar type(from the ENUM_BAR_BODY_TYPE enumeration)
  BAR_PROP_PERIOD,                                                                   class=class="str">"cmt">// Bar period(timeframe)
  BAR_PROP_SPREAD,                                                                   class=class="str">"cmt">// Bar spread
  BAR_PROP_VOLUME_TICK,                                                              class=class="str">"cmt">// Bar tick volume
  BAR_PROP_VOLUME_REAL,                                                              class=class="str">"cmt">// Bar exchange volume
  BAR_PROP_TIME,                                                                     class=class="str">"cmt">// Bar period start time
  BAR_PROP_TIME_DAY_OF_YEAR,                                                         class=class="str">"cmt">// Bar day serial number in a year
  BAR_PROP_TIME_YEAR,                                                                class=class="str">"cmt">// A year the bar belongs to
  BAR_PROP_TIME_MONTH,                                                               class=class="str">"cmt">// A month the bar belongs to
  BAR_PROP_TIME_DAY_OF_WEEK,                                                         class=class="str">"cmt">// Bar week day
  BAR_PROP_TIME_DAY,                                                                 class=class="str">"cmt">// Bar day of month(number)
  BAR_PROP_TIME_HOUR,                                                                class=class="str">"cmt">// Bar hour
  BAR_PROP_TIME_MINUTE,                                                              class=class="str">"cmt">// Bar minute
  };
class="macro">#define BAR_PROP_INTEGER_TOTAL(class="num">14)                                                  class=class="str">"cmt">// Total number of integer bar properties
class="macro">#define BAR_PROP_INTEGER_SKIP(class="num">0)                                                   class=class="str">"cmt">// Number of bar properties not used in sorting
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Real bar properties                                              |
class=class="str">"cmt">//+------------------------------------------------------------------+
enum ENUM_BAR_PROP_DOUBLE
  {
class=class="str">"cmt">//--- bar data
  BAR_PROP_OPEN = BAR_PROP_INTEGER_TOTAL,                                            class=class="str">"cmt">// Bar open price
  BAR_PROP_HIGH,                                                                     class=class="str">"cmt">// Highest price for the bar period

◍ K线属性枚举与排序基准的收口定义

把一根 BAR 拆成可计算的字段,核心就在 ENUM_BAR_PROP_DOUBLE 这组枚举。除了开高低收四个基础价,还显式给出了实体大小、实体上下沿、上下影线长度共 6 个蜡烛结构量,配合前面整型属性,Double 类总数由宏定死为 10(BAR_PROP_DOUBLE_TOTAL),其中 0 个被排除在排序外(BAR_PROP_DOUBLE_SKIP)。 字符串属性极简,ENUM_BAR_PROP_STRING 里只挂了一个 BAR_PROP_SYMBOL,偏移量直接累加前面整型与双精度总数,宏 BAR_PROP_STRING_TOTAL 记为 1。这意味着按品种归类时,只能靠这一个标签字段。 排序模式的入口由两个宏框定:FIRST_BAR_DBL_PROP 从整型可用数起跳,FIRST_BAR_STR_PROP 再叠满双精度可用数。ENUM_SORT_BAR_MODE 里 SORT_BY_BAR_INDEX=0 是默认基准,SORT_BY_BAR_TYPE 则按实体类型(如阳线/阴线/十字)归组。外汇与贵金属波动随机性高,这类结构只解决“怎么取数”,不预示方向。 开 MT5 把这段枚举抄进自定义指标头文件,改 BAR_PROP_DOUBLE_TOTAL 看编译器是否报偏移错,就能验证你那套 K 线特征库的内存布局有没有算漏。

MQL5 / C++
  BAR_PROP_LOW,                                                                       class=class="str">"cmt">// Lowest price for the bar period
  BAR_PROP_CLOSE,                                                                      class=class="str">"cmt">// Bar close price
class=class="str">"cmt">//--- candle data
  BAR_PROP_CANDLE_SIZE,                                                                class=class="str">"cmt">// Candle size
  BAR_PROP_CANDLE_SIZE_BODY,                                                           class=class="str">"cmt">// Candle body size
  BAR_PROP_CANDLE_BODY_TOP,                                                            class=class="str">"cmt">// Candle body top
  BAR_PROP_CANDLE_BODY_BOTTOM,                                                         class=class="str">"cmt">// Candle body bottom
  BAR_PROP_CANDLE_SIZE_SHADOW_UP,                                                      class=class="str">"cmt">// Candle upper wick size
  BAR_PROP_CANDLE_SIZE_SHADOW_DOWN,                                                    class=class="str">"cmt">// Candle lower wick size
  };
class="macro">#define BAR_PROP_DOUBLE_TOTAL(class="num">10)                                                    class=class="str">"cmt">// Total number of real bar properties
class="macro">#define BAR_PROP_DOUBLE_SKIP(class="num">0)                                                     class=class="str">"cmt">// Number of bar properties not used in sorting
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Bar class="type">class="kw">string properties                                                            |
class=class="str">"cmt">//+------------------------------------------------------------------+
enum ENUM_BAR_PROP_STRING
  {
  BAR_PROP_SYMBOL = (BAR_PROP_INTEGER_TOTAL+BAR_PROP_DOUBLE_TOTAL), class=class="str">"cmt">// Bar symbol
  };
class="macro">#define BAR_PROP_STRING_TOTAL(class="num">1)                                                     class=class="str">"cmt">// Total number of class="type">class="kw">string bar properties
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Possible bar sorting criteria                                                             |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#define FIRST_BAR_DBL_PROP(BAR_PROP_INTEGER_TOTAL-BAR_PROP_INTEGER_SKIP)
class="macro">#define FIRST_BAR_STR_PROP(BAR_PROP_INTEGER_TOTAL-BAR_PROP_INTEGER_SKIP+BAR_PROP_DOUBLE_TOTAL-BAR_PROP_DOUBLE_SKIP)
enum ENUM_SORT_BAR_MODE
  {
class=class="str">"cmt">//--- Sort by integer properties
  SORT_BY_BAR_INDEX = class="num">0,                                                               class=class="str">"cmt">// Sort by bar index
  SORT_BY_BAR_TYPE,                                                                    class=class="str">"cmt">// Sort by bar type(from the ENUM_BAR_BODY_TYPE enumeration)

常见问题

用函数库里的柱线对象类封装每根柱,存好开高低收和成交量等属性,再丢进列表就能按时间或数值排序。
柱线对象管一根 K 线的属性和行为,时间序列列表管某个品种所有柱线的集合与遍历,两者分层不混用。
可以,小布能读取你函数库输出的品种时间序列,把排序后的柱线属性在对应品种页直接列出来,省得自己跑终端。
成交量、颜色这类用整型存,开高低收价格用双精度存,避免精度丢失也方便按类型快速取属性。
容易漏掉排序基准的定义,比如按收盘价还是时间排序,不先收口枚举后面列表排序会乱。