DoEasy 函数库中的时间序列(第三十五部分):柱线对象和品种时间序列列表·进阶篇
(2/3)· 从单根 Bar 到跨品种时序集合,解决手动遍历报价导致的延迟与混乱
不少人在 EA 里直接循环 MqlRates 数组,遇到多品种多周期时就堆满嵌套判断,既慢又难维护。把柱线封装成带属性的对象,再用统一列表管理,是绕开这块泥沼的实际路径。
「K线数组排序的可选维度」
在 MT5 的 K 线批量处理里,排序枚举决定了你后续是按时间轴还是按价格结构来切数据。上面的枚举把可排序字段分成了两类:时间/属性类与真实价格类。 时间类里除了常见的周期、点差、tick 量、真实成交量,还能按年/月/周内日/小时/分钟拆。比如 SORT_BY_BAR_TIME_HOUR 让你把全天所有 K 线按小时归堆,做时段波动统计时直接省掉自己写过滤。 真实属性类从 SORT_BY_BAR_OPEN 起(对应 FIRST_BAR_DBL_PROP 基准偏移),覆盖开高低收、整根蜡烛长度、实体大小、实体上下沿。用 SORT_BY_BAR_CANDLE_SIZE_BODY 可快速挑出实体最大那批 K 线,复盘趋势启动位比肉眼翻图快得多。 外汇和贵金属杠杆高、跳空频繁,用真实成交量维度排序时记得真实成交量在多数零售品种上可能为空,别把它当唯一信号。
SORT_BY_BAR_PERIOD, class=class="str">"cmt">// Sort by bar period(timeframe) SORT_BY_BAR_SPREAD, class=class="str">"cmt">// Sort by bar spread SORT_BY_BAR_VOLUME_TICK, class=class="str">"cmt">// Sort by bar tick volume SORT_BY_BAR_VOLUME_REAL, class=class="str">"cmt">// Sort by bar exchange volume SORT_BY_BAR_TIME, class=class="str">"cmt">// Sort by bar period start time SORT_BY_BAR_TIME_DAY_OF_YEAR, class=class="str">"cmt">// Sort by bar day number in a year SORT_BY_BAR_TIME_YEAR, class=class="str">"cmt">// Sort by a year the bar belongs to SORT_BY_BAR_TIME_MONTH, class=class="str">"cmt">// Sort by a month the bar belongs to SORT_BY_BAR_TIME_DAY_OF_WEEK, class=class="str">"cmt">// Sort by a bar week day SORT_BY_BAR_TIME_DAY, class=class="str">"cmt">// Sort by a bar day SORT_BY_BAR_TIME_HOUR, class=class="str">"cmt">// Sort by a bar hour SORT_BY_BAR_TIME_MINUTE, class=class="str">"cmt">// Sort by a bar minute class=class="str">"cmt">//--- Sort by real properties SORT_BY_BAR_OPEN = FIRST_BAR_DBL_PROP, class=class="str">"cmt">// Sort by bar open price SORT_BY_BAR_HIGH, class=class="str">"cmt">// Sort by the highest price for the bar period SORT_BY_BAR_LOW, class=class="str">"cmt">// Sort by the lowest price for the bar period SORT_BY_BAR_CLOSE, class=class="str">"cmt">// Sort by a bar close price SORT_BY_BAR_CANDLE_SIZE, class=class="str">"cmt">// Sort by a candle price SORT_BY_BAR_CANDLE_SIZE_BODY, class=class="str">"cmt">// Sort by a candle body size SORT_BY_BAR_CANDLE_BODY_TOP, class=class="str">"cmt">// Sort by a candle body top SORT_BY_BAR_CANDLE_BODY_BOTTOM class=class="str">"cmt">// Sort by a candle body bottom
K线排序与系统消息枚举的底层定义
在 MT5 标准库的条形容器设计里,排序维度被拆得很细:除了常见的开盘收盘,还单独给了上影线、下影线长度作为键。 SORT_BY_BAR_CANDLE_SIZE_SHADOW_UP 表示按蜡烛上影线尺寸排序,SORT_BY_BAR_CANDLE_SIZE_SHADOW_DOWN 则是按下影线尺寸排序;这两者紧跟在数值类属性之后,而 SORT_BY_BAR_SYMBOL = FIRST_BAR_STR_PROP 标记了字符串属性的起点,即从品种名开始走另一套比较逻辑。 系统消息枚举里有一组失败返回码值得注意:MSG_LIB_SYS_FAILED_CREATE_PAUSE_OBJ、MSG_LIB_SYS_FAILED_CREATE_BAR_OBJ、MSG_LIB_SYS_FAILED_SYNC_DATA 分别对应暂停对象创建失败、Bar 对象创建失败、与服务器数据同步失败。实盘加载自定义库时若终端日志抛出这三个码,优先排查对象句柄泄漏或网络重连,而不是怀疑策略逻辑。 月份文本枚举从 MSG_LIB_TEXT_JANUARY 到 MSG_LIB_TEXT_DECEMBER 共 12 项,是报表类 EA 做多语言时间标签的基础。外汇与贵金属波动受数据日历影响大,用这类内置文本做本地化输出,能减少硬编码中文月份带来的时区偏移 bug。
SORT_BY_BAR_CANDLE_SIZE_SHADOW_UP, class=class="str">"cmt">// Sort by candle upper wick size SORT_BY_BAR_CANDLE_SIZE_SHADOW_DOWN, class=class="str">"cmt">// Sort by candle lower wick size class=class="str">"cmt">//--- Sort by class="type">class="kw">string properties SORT_BY_BAR_SYMBOL = FIRST_BAR_STR_PROP, class=class="str">"cmt">// Sort by a bar symbol }; class=class="str">"cmt">//+------------------------------------------------------------------+ MSG_LIB_SYS_ERROR_CODE_OUT_OF_RANGE, class=class="str">"cmt">// Return code out of range of error codes MSG_LIB_SYS_FAILED_CREATE_PAUSE_OBJ, class=class="str">"cmt">// Failed to create the "Pause" object MSG_LIB_SYS_FAILED_CREATE_BAR_OBJ, class=class="str">"cmt">// Failed to create the "Bar" object MSG_LIB_SYS_FAILED_SYNC_DATA, class=class="str">"cmt">// Failed to synchronize data with the server MSG_LIB_TEXT_TIME_UNTIL_THE_END_DAY, class=class="str">"cmt">// Order lifetime till the end of the current day to be used MSG_LIB_TEXT_JANUARY, class=class="str">"cmt">// January MSG_LIB_TEXT_FEBRUARY, class=class="str">"cmt">// February MSG_LIB_TEXT_MARCH, class=class="str">"cmt">// March MSG_LIB_TEXT_APRIL, class=class="str">"cmt">// April MSG_LIB_TEXT_MAY, class=class="str">"cmt">// May MSG_LIB_TEXT_JUNE, class=class="str">"cmt">// June MSG_LIB_TEXT_JULY, class=class="str">"cmt">// July MSG_LIB_TEXT_AUGUST, class=class="str">"cmt">// August MSG_LIB_TEXT_SEPTEMBER, class=class="str">"cmt">// September MSG_LIB_TEXT_OCTOBER, class=class="str">"cmt">// October MSG_LIB_TEXT_NOVEMBER, class=class="str">"cmt">// November MSG_LIB_TEXT_DECEMBER class=class="str">"cmt">// December
◍ K线对象里的字段枚举怎么排
在 MT5 标准库的消息文本枚举里,CBar 相关常量紧接在周日、挂单条件等通用文本之后定义,从 MSG_LIB_TEXT_BAR_FAILED_GET_BAR_DATA 一路排到 MSG_LIB_TEXT_BAR_LOW,共覆盖 21 个与单根 K 线有关的描述键。 这些枚举名本身就是调试面板和日志本地化的锚点:比如 MSG_LIB_TEXT_BAR_VOLUME_TICK 对应 Tick 成交量,MSG_LIB_TEXT_BAR_VOLUME_REAL 对应交易所真实成交量,二者在外汇品种上通常只有前者有值,贵金属合约才可能返回真实量。 时间类拆分得很细,MSG_LIB_TEXT_BAR_TIME_DAY_OF_YEAR 取年内第几天,MSG_LIB_TEXT_BAR_TIME_DAY_OF_WEEK 取星期几,写 session 过滤逻辑时直接引用比自己算结构体省事。 下方代码块就是这段枚举声明的原貌,黄色高亮部分即 CBar 分组,复制进 MQ5 头文件即可编译验证。
MSG_LIB_TEXT_SUNDAY, class=class="str">"cmt">// Sunday MSG_LIB_TEXT_PEND_REQUEST_ADD_CRITERIONS, class=class="str">"cmt">// Added pending request activation conditions class=class="str">"cmt">//--- CBar MSG_LIB_TEXT_BAR_FAILED_GET_BAR_DATA, class=class="str">"cmt">// Failed to receive bar data MSG_LIB_TEXT_BAR_FAILED_GET_SERIES_DATA, class=class="str">"cmt">// Failed to receive timeseries data MSG_LIB_TEXT_BAR_FAILED_ADD_TO_LIST, class=class="str">"cmt">// Could not add bar object to the list MSG_LIB_TEXT_BAR, class=class="str">"cmt">// Bar MSG_LIB_TEXT_BAR_PERIOD, class=class="str">"cmt">// Timeframe MSG_LIB_TEXT_BAR_SPREAD, class=class="str">"cmt">// Spread MSG_LIB_TEXT_BAR_VOLUME_TICK, class=class="str">"cmt">// Tick volume MSG_LIB_TEXT_BAR_VOLUME_REAL, class=class="str">"cmt">// Exchange volume MSG_LIB_TEXT_BAR_TIME, class=class="str">"cmt">// Period start time MSG_LIB_TEXT_BAR_TIME_YEAR, class=class="str">"cmt">// Year MSG_LIB_TEXT_BAR_TIME_MONTH, class=class="str">"cmt">// Month MSG_LIB_TEXT_BAR_TIME_DAY_OF_YEAR, class=class="str">"cmt">// Day serial number in a year MSG_LIB_TEXT_BAR_TIME_DAY_OF_WEEK, class=class="str">"cmt">// Week day MSG_LIB_TEXT_BAR_TIME_DAY, class=class="str">"cmt">// Two months MSG_LIB_TEXT_BAR_TIME_HOUR, class=class="str">"cmt">// Hour MSG_LIB_TEXT_BAR_TIME_MINUTE, class=class="str">"cmt">// Minute MSG_LIB_TEXT_BAR_INDEX, class=class="str">"cmt">// Index in timeseries MSG_LIB_TEXT_BAR_HIGH, class=class="str">"cmt">// Highest price for the period MSG_LIB_TEXT_BAR_LOW class=class="str">"cmt">// Lowest price for the period
「K线度量与多语词典的字段映射」
在 MT5 自定义指标里做价格行为统计,第一步往往是把单根 K 线的几何属性拆成可读字符串。下面这组枚举常量就是干这个的:它把「整根振幅、实体高度、上下影线长度、实体上下沿」以及「看涨/看跌/零值/十字星」分类全部定义成消息 ID,方便后续直接调文案接口输出。 MSG_LIB_TEXT_BAR_CANDLE_SIZE 对应整根蜡烛的振幅,MSG_LIB_TEXT_BAR_CANDLE_SIZE_BODY 是实体大小,SHADOW_UP / SHADOW_DOWN 分别锁定上影与下影。BODY_TOP 和 BODY_BOTTOM 给出实体边界价,TYPE_BULLISH、TYPE_BEARISH、TYPE_NULL、TYPE_CANDLE_ZERO_BODY 则把 bar 型态归了四类。 另一块是双语词典的初始化表。俄语原串作 key、英文作 value,典型如 "Не удалось создать объект \"Бар\"" 映射为 "Failed to create \"Bar\" object",月份从 Январь 到 Декабрь 共 12 条,星期仅见 Воскресение→Sunday 一条。若你要接小布的中文渲染层,直接把 value 换成中文即可,不用动调用逻辑。 调用前注意 MSG_LIB_TEXT_BAR_TEXT_FIRS_SET_AMOUNT_DATA 的注释:先用 SetAmountUsedData() 设定所需历史深度,否则取到的影线长度可能是空值。外汇与贵金属波动剧烈、杠杆高风险大,这类度量仅作形态参考,不构成方向判断。
MSG_LIB_TEXT_BAR_CANDLE_SIZE, class=class="str">"cmt">// Candle size MSG_LIB_TEXT_BAR_CANDLE_SIZE_BODY, class=class="str">"cmt">// Candle body size MSG_LIB_TEXT_BAR_CANDLE_SIZE_SHADOW_UP, class=class="str">"cmt">// Candle upper wick size MSG_LIB_TEXT_BAR_CANDLE_SIZE_SHADOW_DOWN, class=class="str">"cmt">// Candle lower wick size MSG_LIB_TEXT_BAR_CANDLE_BODY_TOP, class=class="str">"cmt">// Candle body top MSG_LIB_TEXT_BAR_CANDLE_BODY_BOTTOM, class=class="str">"cmt">// Candle body bottom MSG_LIB_TEXT_BAR_TYPE_BULLISH, class=class="str">"cmt">// Bullish bar MSG_LIB_TEXT_BAR_TYPE_BEARISH, class=class="str">"cmt">// Bearish bar MSG_LIB_TEXT_BAR_TYPE_NULL, class=class="str">"cmt">// Zero bar MSG_LIB_TEXT_BAR_TYPE_CANDLE_ZERO_BODY, class=class="str">"cmt">// Candle with a zero body MSG_LIB_TEXT_BAR_TEXT_FIRS_SET_AMOUNT_DATA, class=class="str">"cmt">// First, we need to set the required amount of data using SetAmountUsedData() }; class=class="str">"cmt">//+------------------------------------------------------------------+ {"Код возврата вне заданного диапазона кодов ошибок","Out of range of error codes class="kw">return code"}, {"Не удалось создать объект \"Пауза\"","Failed to create \"Pause\" object"}, {"Не удалось создать объект \"Бар\"","Failed to create \"Bar\" object"}, {"Не удалось синхронизировать данные с сервером","Failed to sync data with server"}, {"Будет использоваться время действия ордера до конца текущего дня","Order validity time until the end of the current day will be used"}, {"Январь","January"}, {"Февраль","February"}, {"Март","March"}, {"Апрель","April"}, {"Май","May"}, {"Июнь","June"}, {"Июль","July"}, {"Август","August"}, {"Сентябрь","September"}, {"Октябрь","October"}, {"Ноябрь","November"}, {"Декабрь","December"}, {"Воскресение","Sunday"}, {"Добавлены условия активации отложенного запроса","Pending request activation conditions added"},
多语言词条与K线字段的映射表
在 MT5 自定义指标里做双语界面,最省事的办法是把俄文与英文词条配对塞进一个二维字符串数组,运行时按语言开关取对应列。上面这段映射覆盖了从取数失败提示到单根 K 线结构描述的 30 余个键,比如 "Failed to get bar data" 对应取不到柱数据,"Candle upper shadow size" 对应上影线长度。 值得注意的是,字段里区分了 Tick volume(tick 量)与 Real volume(交易所真实量),做贵金属或外汇复盘时两者经常背离,后者在多数零售外汇符号上返回 0。还有 "Sequence day number in a year" 这种年内序日字段,对做季节性窗口统计很直接。 末尾的 MonthDescription(int month) 函数只是按月份整数返回描述串,配合前面的词条表就能在图表上切显示语言。开 MT5 把这段数组原样贴进 ea 或指标头文件,改两列中文就能做本地化。
class="type">class="kw">string MonthDescription(const class="type">int month) { class="kw">return (
◍ 月份文本与时间框架的轻量转换
把数字月份映射成可读文本,常见做法是嵌套三元运算符:month 从 1 到 12 依次对应 CMessage::Text 取回的本地化月份串,若都不匹配则直接把 month 转成 string 兜底。 TimeframeDescription 函数则更短平快:用 EnumToString 把 ENUM_TIMEFRAMES 转成字符串后,从索引 7 开始截子串。例如 PERIOD_M1 会变成 M1,PERIOD_H4 变成 H4,省去一长串 switch 判断。 在 MT5 里新建一个 mqh 把这两段收进去,include 路径按你库结构改成相对地址即可;外汇与贵金属波动受杠杆放大,调用这类描述函数做日志输出时仍须意识到实盘高风险。
month==class="num">1 ? CMessage::Text(MSG_LIB_TEXT_JANUARY) : month==class="num">2 ? CMessage::Text(MSG_LIB_TEXT_FEBRUARY) : month==class="num">3 ? CMessage::Text(MSG_LIB_TEXT_MARCH) : month==class="num">4 ? CMessage::Text(MSG_LIB_TEXT_APRIL) : month==class="num">5 ? CMessage::Text(MSG_LIB_TEXT_MAY) : month==class="num">6 ? CMessage::Text(MSG_LIB_TEXT_JUNE) : month==class="num">7 ? CMessage::Text(MSG_LIB_TEXT_JULY) : month==class="num">8 ? CMessage::Text(MSG_LIB_TEXT_AUGUST) : month==class="num">9 ? CMessage::Text(MSG_LIB_TEXT_SEPTEMBER) : month==class="num">10 ? CMessage::Text(MSG_LIB_TEXT_OCTOBER) : month==class="num">11 ? CMessage::Text(MSG_LIB_TEXT_NOVEMBER) : month==class="num">12 ? CMessage::Text(MSG_LIB_TEXT_DECEMBER) : (class="type">class="kw">string)month ); } class="type">class="kw">string TimeframeDescription(const ENUM_TIMEFRAMES timeframe) { class="kw">return StringSubstr(EnumToString(timeframe),class="num">7); }