DoEasy 函数库中的价格(第六十二部分):实时更新即时报价序列,为操控市场深度做准备·进阶篇
📡

DoEasy 函数库中的价格(第六十二部分):实时更新即时报价序列,为操控市场深度做准备·进阶篇

(2/3)· 默认 20 万条报价上限如何自动滚动淘汰旧数据,避免多品种内存爆仓

实战向进阶 第 2/3 篇
很多人以为 OnTick 能拿到所有品种的即时报价,结果只更新了当前图表品种,其余品种序列悄悄失真。多品种统计研究若依赖这种残破数据,回测和实时判断都会偏航。本篇解决的就是这个隐蔽漏洞。

「交易品种整数属性的边界与跳过项」

在 MT5 的 ENUM_SYMBOL_INFO_INTEGER 体系里,止损挂单最小距离、冻结层级、成交模式、掉期计算模型等都被定义为整数属性。其中 SYMBOL_PROP_BACKGROUND_COLOR 之前的 SYMBOL_PROP_TRADE_STOPS_LEVELSYMBOL_PROP_OPTION_RIGHT 覆盖了订单执行与期权类型的核心约束。 代码里用宏明确标出:整数属性总数为 37,排序时跳过 1 个(即背景色这类非交易属性),实际可用于排序的整数属性为 36。 如果你在写自定义品种扫描器,直接引用 SYMBOL_PROP_INTEGER_TOTALSYMBOL_PROP_INTEGER_SKIP 两个宏,能避免硬编数字导致版本升级后越界。外汇与贵金属品种受经纪商参数限制,止损距离和冻结层级可能随时调整,属高风险变量,开 MT5 用 SymbolInfoInteger() 实时读取比缓存更稳妥。

MQL5 / C++
  SYMBOL_PROP_TRADE_STOPS_LEVEL,                                      class=class="str">"cmt">// Minimum distance in points from the current close price for setting Stop orders
  SYMBOL_PROP_TRADE_FREEZE_LEVEL,                                       class=class="str">"cmt">// Freeze distance for trading operations(in points)
  SYMBOL_PROP_TRADE_EXEMODE,                                            class=class="str">"cmt">// Deal execution mode(from the ENUM_SYMBOL_TRADE_EXECUTION enumeration)
  SYMBOL_PROP_SWAP_MODE,                                                class=class="str">"cmt">// Swap calculation model(from the ENUM_SYMBOL_SWAP_MODE enumeration)
  SYMBOL_PROP_SWAP_ROLLOVER3DAYS,                                       class=class="str">"cmt">// Triple-day swap(from the ENUM_DAY_OF_WEEK enumeration)
  SYMBOL_PROP_MARGIN_HEDGED_USE_LEG,                                    class=class="str">"cmt">// Calculating hedging margin using the larger leg(Buy or Sell)
  SYMBOL_PROP_EXPIRATION_MODE,                                          class=class="str">"cmt">// Flags of allowed order expiration modes
  SYMBOL_PROP_FILLING_MODE,                                             class=class="str">"cmt">// Flags of allowed order filling modes
  SYMBOL_PROP_ORDER_MODE,                                               class=class="str">"cmt">// Flags of allowed order types
  SYMBOL_PROP_ORDER_GTC_MODE,                                           class=class="str">"cmt">// Expiration of Stop Loss and Take Profit orders if SYMBOL_EXPIRATION_MODE=SYMBOL_EXPIRATION_GTC(from the ENUM_SYMBOL_ORDER_GTC_MODE enumeration)
  SYMBOL_PROP_OPTION_MODE,                                              class=class="str">"cmt">// Option type(from the ENUM_SYMBOL_OPTION_MODE enumeration)
  SYMBOL_PROP_OPTION_RIGHT,                                             class=class="str">"cmt">// Option right(Call/Put) (from the ENUM_SYMBOL_OPTION_RIGHT enumeration)
  class=class="str">"cmt">//--- skip the class="kw">property
  SYMBOL_PROP_BACKGROUND_COLOR                                          class=class="str">"cmt">// The class="type">color of the background used for the symbol in Market Watch
};
class="macro">#define SYMBOL_PROP_INTEGER_TOTAL(class="num">37)                                class=class="str">"cmt">// Total number of integer properties
class="macro">#define SYMBOL_PROP_INTEGER_SKIP(class="num">1)                                 class=class="str">"cmt">// Number of symbol integer properties not used in sorting
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Possible symbol sorting criteria                                 |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#define FIRST_SYM_DBL_PROP(SYMBOL_PROP_INTEGER_TOTAL-SYMBOL_PROP_INTEGER_SKIP)
class="macro">#define FIRST_SYM_STR_PROP(SYMBOL_PROP_INTEGER_TOTAL-SYMBOL_PROP_INTEGER_SKIP+SYMBOL_PROP_DOUBLE_TOTAL-SYMBOL_PROP_DOUBLE_SKIP)
enum ENUM_SORT_SYMBOLS_MODE
  {
  class=class="str">"cmt">//--- Sort by integer properties

◍ 枚举排序字段的实战含义

MT5 的 ENUM_SYMBOL_SORT_MODE 里,SORT_BY_SYMBOL_STATUS = 0 是排序基准的起点,后续每个枚举值对应一种市场观察窗口(Market Watch)内可排序的维度。 从 SORT_BY_SYMBOL_INDEX_MW 到 SORT_BY_SYMBOL_TICKS_BOOKDEPTH,一共罗列了 22 个排序键。它们覆盖的状态包括:自定义属性、图表价源(Bid 或 Last)、品种是否存在、是否被选中、是否可见,以及当前会话的成交与多空挂单数量。 成交量相关有 SORT_BY_SYMBOL_VOLUME(最近一笔成交量)、VOLUMEHIGH(当日最大)、VOLUMELOW(当日最小);时间维度用 SORT_BY_SYMBOL_TIME 取最后报价时间;点值精度则由 DIGITS 与 DIGITS_LOT 分别控制报价小数位和手数小数位。 点差层面提供 SPREAD(以点计的固定/当前点差)与 SPREAD_FLOAT(浮动点差标志),而 TICKS_BOOKDEPTH 决定市场深度窗口能刷新的最大挂单层数。 开 MT5 按 F4 进 MetaEditor,搜 ENUM_SYMBOL_SORT_MODE 可直接核对这串枚举;写自定义观察面板时,挑 SORT_BY_SYMBOL_SPREAD 或 SESSION_SELL_ORDERS 做排序键,能更快筛出流动性异常的外汇或贵金属品种——这类品种跳空与滑点风险偏高,参数验证前先开模拟盘跑一遍。

MQL5 / C++
SORT_BY_SYMBOL_STATUS = class="num">0,                                                                       class=class="str">"cmt">// Sort by symbol status
SORT_BY_SYMBOL_INDEX_MW,                                                                             class=class="str">"cmt">// Sort by index in the Market Watch window
SORT_BY_SYMBOL_CUSTOM,                                                                               class=class="str">"cmt">// Sort by custom symbol class="kw">property
SORT_BY_SYMBOL_CHART_MODE,                                                                           class=class="str">"cmt">// Sort by price type for constructing bars – Bid or Last(from the ENUM_SYMBOL_CHART_MODE enumeration)
SORT_BY_SYMBOL_EXIST,                                                                                class=class="str">"cmt">// Sort by the flag that a symbol with such a name exists
SORT_BY_SYMBOL_SELECT,                                                                               class=class="str">"cmt">// Sort by the flag indicating that a symbol is selected in Market Watch
SORT_BY_SYMBOL_VISIBLE,                                                                              class=class="str">"cmt">// Sort by the flag indicating that a selected symbol is displayed in Market Watch
SORT_BY_SYMBOL_SESSION_DEALS,                                                                        class=class="str">"cmt">// Sort by the number of deals in the current session 
SORT_BY_SYMBOL_SESSION_BUY_ORDERS,                                                                   class=class="str">"cmt">// Sort by the total number of current buy orders
SORT_BY_SYMBOL_SESSION_SELL_ORDERS,                                                                  class=class="str">"cmt">// Sort by the total number of current sell orders
SORT_BY_SYMBOL_VOLUME,                                                                               class=class="str">"cmt">// Sort by last deal volume
SORT_BY_SYMBOL_VOLUMEHIGH,                                                                           class=class="str">"cmt">// Sort by maximum volume for a day
SORT_BY_SYMBOL_VOLUMELOW,                                                                            class=class="str">"cmt">// Sort by minimum volume for a day
SORT_BY_SYMBOL_TIME,                                                                                 class=class="str">"cmt">// Sort by the last quote time
SORT_BY_SYMBOL_DIGITS,                                                                               class=class="str">"cmt">// Sort by a number of decimal places
SORT_BY_SYMBOL_DIGITS_LOT,                                                                           class=class="str">"cmt">// Sort by a number of decimal places in a lot
SORT_BY_SYMBOL_SPREAD,                                                                               class=class="str">"cmt">// Sort by spread in points
SORT_BY_SYMBOL_SPREAD_FLOAT,                                                                         class=class="str">"cmt">// Sort by floating spread
SORT_BY_SYMBOL_TICKS_BOOKDEPTH,                                                                      class=class="str">"cmt">// Sort by a maximum number of requests displayed in the market depth

按交易属性给品种列表排序的枚举项

在 MT5 的 ENUM_SYMBOLS_SORT_MODE 体系里,除了基础名称、点值等字段,还有一批围绕交易规则本身的排序键。它们直接对应品种配置里的执行与风控参数,写脚本批量扫描品种时很好用。 例如 SORT_BY_SYMBOL_TRADE_STOPS_LEVEL 按当前收盘价到止损单的最小距离(点数)排序,SORT_BY_SYMBOL_TRADE_FREEZE_LEVEL 按交易冻结距离(点数)排序,这两个值能快速筛出受限较严的品种。 SORT_BY_SYMBOL_FILLING_MODESORT_BY_SYMBOL_ORDER_MODE 分别按允许的成交填充模式、允许的下单类型标志位排序;做跨品种套利或自动化下单前,用它们归类品种比人工翻规格书快得多。外汇与贵金属杠杆高、规则差异大,实盘使用前建议在策略测试器或模拟环境先验证排序结果。

MQL5 / C++
  SORT_BY_SYMBOL_BOOKDEPTH_STATE,                                                                     class=class="str">"cmt">// Sort by the DOM subscription flag
  SORT_BY_SYMBOL_TRADE_CALC_MODE,                                                                       class=class="str">"cmt">// Sort by contract price calculation method(from the ENUM_SYMBOL_CALC_MODE enumeration)
  SORT_BY_SYMBOL_TRADE_MODE,                                                                            class=class="str">"cmt">// Sort by order execution type(from the ENUM_SYMBOL_TRADE_MODE enumeration)
  SORT_BY_SYMBOL_START_TIME,                                                                            class=class="str">"cmt">// Sort by an instrument trading start date(usually used for futures)
  SORT_BY_SYMBOL_EXPIRATION_TIME,                                                                       class=class="str">"cmt">// Sort by an instrument trading end date(usually used for futures)
  SORT_BY_SYMBOL_TRADE_STOPS_LEVEL,                                                                     class=class="str">"cmt">// Sort by the minimum indent from the current close price(in points) for setting Stop orders
  SORT_BY_SYMBOL_TRADE_FREEZE_LEVEL,                                                                    class=class="str">"cmt">// Sort by trade operation freeze distance(in points)
  SORT_BY_SYMBOL_TRADE_EXEMODE,                                                                         class=class="str">"cmt">// Sort by trade execution mode(from the ENUM_SYMBOL_TRADE_EXECUTION enumeration)
  SORT_BY_SYMBOL_SWAP_MODE,                                                                             class=class="str">"cmt">// Sort by swap calculation model(from the ENUM_SYMBOL_SWAP_MODE enumeration)
  SORT_BY_SYMBOL_SWAP_ROLLOVER3DAYS,                                                                    class=class="str">"cmt">// Sort by week day for accruing a triple swap(from the ENUM_DAY_OF_WEEK enumeration)
  SORT_BY_SYMBOL_MARGIN_HEDGED_USE_LEG,                                                                 class=class="str">"cmt">// Sort by the calculation mode of a hedged margin using the larger leg(Buy or Sell)
  SORT_BY_SYMBOL_EXPIRATION_MODE,                                                                       class=class="str">"cmt">// Sort by flags of allowed order expiration modes
  SORT_BY_SYMBOL_FILLING_MODE,                                                                          class=class="str">"cmt">// Sort by flags of allowed order filling modes
  SORT_BY_SYMBOL_ORDER_MODE,                                                                            class=class="str">"cmt">// Sort by flags of allowed order types
  SORT_BY_SYMBOL_ORDER_GTC_MODE,                                                                        class=class="str">"cmt">// Sort by StopLoss and TakeProfit orders lifetime
  SORT_BY_SYMBOL_OPTION_MODE,                                                                           class=class="str">"cmt">// Sort by option type(from the ENUM_SYMBOL_OPTION_MODE enumeration)
  SORT_BY_SYMBOL_OPTION_RIGHT,                                                                          class=class="str">"cmt">// Sort by option right(Call/Put) (from the ENUM_SYMBOL_OPTION_RIGHT enumeration)
class=class="str">"cmt">//--- Sort by real properties

「用毫秒偏移避免重复灌入即时报价」

即时报价在 MT5 里经常以一束(batch)形式并发到达,若逐个往序列里塞,会漏掉同批内的其他报价。可行做法是:记录上一次 OnTick() 收到的最后一个即时报价的毫秒时间,从该时间 +1 毫秒起调用 CopyTicksRange() 向历史末尾复制,从而不重复复制已处理的那根。 类私密成员里用 ulong m_last_time 存最后报价毫秒时间;CreateNewTickObj(const MqlTick &tick) 负责新建数据对象并加入列表,成功返指针、失败返 NULL。构造函数里先指定品种再一次性刷新“New tick”对象,把 m_last_time 初始化好。 更新逻辑很直接:用 m_last_time+1 作为复制起点,复制完若列表总数超过预设上限,就从最旧一端剔除。原文调试段只针对 AUDUSD 打印复制数量与时间窗,验证完下一版会摘掉。 集合类里另写了一个跳过当前图表品种、只刷新其余品种序列的方法,挂在库计时器上跑;当前品种则在 OnTick() 里调更新。这样非本源品种的报价变动也能在后台持续补全,外汇与贵金属报价受流动性影响跳变频繁,用这套机制时务必留意点差放大带来的复制空洞风险。

MQL5 / C++
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="comment">class=class="str">"cmt">//| "Tick data series" class  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   |</span>
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="keyword">class</span> CTickSeries : <span class="keyword">class="kw">public</span> CBaseObj
&nbsp;&nbsp;{
<span class="keyword">class="kw">private</span>:
&nbsp;&nbsp; <span class="keyword">class="type">class="kw">string</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_symbol;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">// Symbol</span>
&nbsp;&nbsp; <span style="background-class="type">color:rgb(class="num">255, class="num">242, class="num">153);"><span class="keyword">class="type">ulong</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_last_time;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="comment">class=class="str">"cmt">// Last tick time</span></span>
&nbsp;&nbsp; <span class="keyword">class="type">uint</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_amount;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">// Amount of applied tick series data</span>
&nbsp;&nbsp; <span class="keyword">class="type">uint</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_required;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">// Required number of days for tick series data</span>
&nbsp;&nbsp; CArrayObj&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_list_ticks;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">// List of tick data</span>
&nbsp;&nbsp; CNewTickObj&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_new_tick_obj;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">// "New tick" object</span>
<span class="comment">class=class="str">"cmt">//--- Create a new tick data object</span>
&nbsp;&nbsp; <span style="background-class="type">color:rgb(class="num">216, class="num">232, class="num">194);">CDataTick&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*CreateNewTickObj(<span class="keyword">const</span> <span class="predefines">class="type">MqlTick</span> &amp;tick);</span>
<span class="keyword">class="kw">public</span>:
<span class="comment">class=class="str">"cmt">//--- Return the object of tick data by(class="num">1) index in the list, (class="num">2) time,</span>
<span class="comment">class=class="str">"cmt">//--- (class="num">3) time in milliseconds, (class="num">4) the last one in the list and(class="num">5) the list size</span>

◍ 逐笔tick序列的取数与构造细节

CTickSeries 对外暴露了多组取数接口:按列表下标取 CDataTick* 用 GetTickByListIndex(uint index),按秒级时间 datetime 或毫秒时间 ulong time_msc 检索用两个重载的 GetTick,实时最新一笔直接调 GetLastTick(void)。当前已缓存的 tick 总数由 DataTotal() 返回,内部就是 m_list_ticks.Total(),调用零开销。 构造器 CTickSeries(string symbol, uint required=0) 在初始化时清空链表、按 SORT_BY_TICK_TIME_MSC 排序,并通过 SetRequiredUsedDays(required) 设定回看天数;随后把 m_new_tick_obj 绑定到对应 symbol 并立刻 Refresh(),意味着对象一建好就带有一份新鲜行情快照。 往序列里插新 tick 走 CreateNewTickObj(MqlTick &tick):先 new CDataTick 包装传入结构,失败则打印错误并返回 NULL;成功后再做 InsertSort 插入有序链表,若插入失败会 delete 掉刚建的对象并回 NULL,避免野指针堆积。 这套机制在 MT5 里可直接验证:挂一个 EA 在 EURUSD 上 new CTickSeries("EURUSD", 1),循环里打印 GetLastTick().time_msc,应能观察到毫秒级 tick 流。外汇与贵金属 tick 流速受流动性影响波动极大,高频序列处理不当可能拖慢回测,实盘前务必在策略测试器跑一遍。

MQL5 / C++
CDataTick      *GetTickByListIndex(const class="type">uint index);
CDataTick      *GetTick(const class="type">class="kw">datetime time);
CDataTick      *GetTick(const class="type">ulong time_msc);
CDataTick      *GetLastTick(class="type">void);
class="type">int             DataTotal(class="type">void) const { class="kw">return this.m_list_ticks.Total(); }
class=class="str">"cmt">//--- The comparison method for searching identical tick series objects by a symbol
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Parametric constructor                                            |
class=class="str">"cmt">//+------------------------------------------------------------------+
CTickSeries::CTickSeries(const class="type">class="kw">string symbol,const class="type">uint required=class="num">0) : m_symbol(symbol),m_last_time(class="num">0)
  {
   this.m_list_ticks.Clear();
   this.m_list_ticks.Sort(SORT_BY_TICK_TIME_MSC);
   this.SetRequiredUsedDays(required);
   this.m_new_tick_obj.SetSymbol(this.m_symbol);
   this.m_new_tick_obj.Refresh();
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Create a new tick data object                                     |
class=class="str">"cmt">//+------------------------------------------------------------------+
CDataTick *CTickSeries::CreateNewTickObj(const class="type">MqlTick &tick)
  {
   class=class="str">"cmt">//--- create a new object of tick data out of the class="type">MqlTick structure passed to the method
   class="type">int err=ERR_SUCCESS;
   ::ResetLastError();
   class=class="str">"cmt">//--- If failed to create an object, inform of that and class="kw">return NULL
   CDataTick* tick_obj=new CDataTick(this.m_symbol,tick);
   if(tick_obj==NULL)
     {
      ::Print(
         DFUN,CMessage::Text(MSG_TICKSERIES_FAILED_CREATE_TICK_DATA_OBJ)," ",this.Header()," ",::TimeMSCtoString(tick.time_msc),". ",
         CMessage::Text(MSG_LIB_SYS_ERROR),": ",CMessage::Text(::GetLastError())
        );
      class="kw">return NULL;
     }
   class=class="str">"cmt">//--- If failed to add a new tick data object to the list,
   class=class="str">"cmt">//--- display the appropriate message with the error description in the journal,
   class=class="str">"cmt">//--- remove the newly created object and class="kw">return NULL
   this.m_list_ticks.Sort();
   if(!this.m_list_ticks.InsertSort(tick_obj))
     {
      err=::GetLastError();
      ::Print(DFUN,CMessage::Text(MSG_TICKSERIES_FAILED_ADD_TO_LIST)," ",tick_obj.Header()," ",
                   CMessage::Text(MSG_LIB_SYS_ERROR),": ",CMessage::Text(err),CMessage::Retcode(err));
      class="kw">delete tick_obj;
      class="kw">return NULL;
     }

用 CopyTicksRange 把历史 tick 灌进自定义序列

CTickSeries::Create() 负责把指定天数起的历史 tick 拉进内存链表。先判 m_available,未启用就 Print 提示并返 false,避免空跑浪费资源。 起点时间由 iTime(m_symbol, PERIOD_D1, m_required) 取日线开盘,再把时分许秒清零乘 1000 转成毫秒;若算出来小于 1 则强制置 1,防止 CopyTicksRange 传参异常。 核心只有一行:m_amount = CopyTicksRange(m_symbol, ticks_array, COPY_TICKS_ALL, date_from)。返回小于 1 说明取数失败,用 GetLastError() 打码后直接 return 0,这时链表是空的,后续更新逻辑必须判空。 循环里每根 tick 调 CreateNewTickObj() 入链,并持续用 m_last_time 记录最大 TimeMSC。这个毫秒时间戳就是下次增量更新的游标——开 MT5 把 m_required 改成 0 能拉当天全量 tick,改大则回溯更多历史日,验证链表长度随参数线性增长即可。外汇与贵金属 tick 数据受流动性影响,断点缺失概率不低,实盘使用前建议先打印 m_amount 确认覆盖度。

MQL5 / C++
class="type">int CTickSeries::Create(const class="type">uint required=class="num">0)
  {
  if(!this.m_available)
    {
    ::Print(DFUN,this.m_symbol,": ",CMessage::Text(MSG_TICKSERIES_TEXT_IS_NOT_USE));
    class="kw">return false;
    }
  class="type">MqlTick ticks_array[];
  this.m_list_ticks.Clear();
  this.m_list_ticks.Sort(SORT_BY_TICK_TIME_MSC);
  this.m_last_time=class="num">0;
  ::ResetLastError();
  class="type">int err=ERR_SUCCESS;
  class="type">MqlDateTime date_str={class="num">0};
  class="type">class="kw">datetime date=::iTime(m_symbol,PERIOD_D1,this.m_required);
  ::TimeToStruct(date,date_str);
  date_str.hour=date_str.min=date_str.sec=class="num">0;
  date=::StructToTime(date_str);
  class="type">long date_from=(class="type">long)date*class="num">1000;
  if(date_from<class="num">1) date_from=class="num">1;
  this.m_amount=::CopyTicksRange(m_symbol,ticks_array,COPY_TICKS_ALL,date_from);
  if(this.m_amount<class="num">1)
    {
    err=::GetLastError();
    ::Print(DFUN,CMessage::Text(MSG_TICKSERIES_ERR_GET_TICK_DATA),": ",CMessage::Text(err),CMessage::Retcode(err));
    class="kw">return class="num">0;
    }
  for(class="type">int i=class="num">0; i<(class="type">int)this.m_amount; i++)
    {
    CDataTick *tick_obj=this.CreateNewTickObj(ticks_array[i]);
    if(tick_obj==NULL)
      class="kw">continue;
    if(this.m_last_time<(class="type">ulong)tick_obj.TimeMSC())
      this.m_last_time=tick_obj.TimeMSC();
    }
  class="kw">return this.m_list_ticks.Total();
  }
把多品种报价巡检交给小布
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到实时报价序列的滚动状态与内存占用,你只需关注策略信号本身。

常见问题

OnTick 仅在当前品种新报价到达时触发,其他品种的价格变动不会激活它,必须靠库内独立定时器轮询才能补齐。
默认上限 20 万条,按常规 tick 频率大约覆盖两天,可在 Defines 里改常数调整。
集合超量时自动按序剔除最旧对象,只保最近 N 条,倾向不影响短期统计,但超长周期研究需手动调大上限。
可以,小布盯盘对应品种页已集成这类 AIGC 诊断,打开即可看滚动量与占用,不必自己打日志。
需新增整数属性标记 DOM 广播订阅状态,并在类里暴露订阅接口,下一篇才真正操控 DOM。