轻松快捷开发 MetaTrader 程序的函数库(第十八部分):帐户与任意其他函数库对象之间的交互·进阶篇
🧩

轻松快捷开发 MetaTrader 程序的函数库(第十八部分):帐户与任意其他函数库对象之间的交互·进阶篇

(2/3)· 基准与品种对象方法重叠、事件标志误写,正悄悄拖慢你的 EA 诊断效率

案例拆解 第 2/3 篇
把 CheckEvents 同时塞进基类和衍生类,看似省事,实则让同逻辑散落多处,改一处漏一处。用 LONG_MAX 初始化属性还会掩盖小幅变化,等你想抓大波动时事件早已失准。账户对象没挂到基准事件体系前,跨对象联动基本靠手搓。

「持仓属性越界事件的取值接口」

在 MT5 的持仓事件封装类里,整数型与双精度型属性各自维护了一张事件标记二维数组。整数属性走 m_long_prop_event[property][索引],实数属性因偏移了 m_long_prop_total 个整数槽位,需以 property - m_long_prop_total 定位行号。 下面这组 getter 只做一件事:把数组第 5~9 列的控制标记吐出来。第 5 列是增量超阈值(INC),第 6 列是减量超阈值(DEC),第 7 列是增量冲破控制线(MORE),第 8 列是减量跌破控制线(LESS),第 9 列是恰好等于控制线(EQUAL)。 实战中你可以用 GetPropLongFlagMORE(POSITION_VOLUME) 判断当前持仓手数是否突破了你设的风控上限,返回非 0 即代表触发。外汇与贵金属杠杆高,这类标记仅作程序内风控哨兵,不代表行情会按预期走。

MQL5 / C++
class="type">long GetPropLongFlagINC(class="kw">const class="type">int class="kw">property) class="kw">const { class="kw">return this.m_long_prop_event[class="kw">property][class="num">5]; }
class="type">class="kw">double GetPropDoubleFlagINC(class="kw">const class="type">int class="kw">property) class="kw">const { class="kw">return this.m_double_prop_event[class="kw">property-this.m_long_prop_total][class="num">5]; }
class=class="str">"cmt">//--- Return the flag of an(class="num">1) integer and(class="num">2) real class="kw">property value change exceeding the decrease value
class="type">long GetPropLongFlagDEC(class="kw">const class="type">int class="kw">property) class="kw">const { class="kw">return this.m_long_prop_event[class="kw">property][class="num">6]; }
class="type">class="kw">double GetPropDoubleFlagDEC(class="kw">const class="type">int class="kw">property) class="kw">const { class="kw">return this.m_double_prop_event[class="kw">property-this.m_long_prop_total][class="num">6]; }
class=class="str">"cmt">//--- Return the flag of an(class="num">1) integer and(class="num">2) real class="kw">property value increase exceeding the control level
class="type">long GetPropLongFlagMORE(class="kw">const class="type">int class="kw">property) class="kw">const { class="kw">return this.m_long_prop_event[class="kw">property][class="num">7]; }
class="type">class="kw">double GetPropDoubleFlagMORE(class="kw">const class="type">int class="kw">property) class="kw">const { class="kw">return this.m_double_prop_event[class="kw">property-this.m_long_prop_total][class="num">7]; }
class=class="str">"cmt">//--- Return the flag of an(class="num">1) integer and(class="num">2) real class="kw">property value decrease being less than the control level
class="type">long GetPropLongFlagLESS(class="kw">const class="type">int class="kw">property) class="kw">const { class="kw">return this.m_long_prop_event[class="kw">property][class="num">8]; }
class="type">class="kw">double GetPropDoubleFlagLESS(class="kw">const class="type">int class="kw">property) class="kw">const { class="kw">return this.m_double_prop_event[class="kw">property-this.m_long_prop_total][class="num">8]; }
class=class="str">"cmt">//--- Return the flag of an(class="num">1) integer and(class="num">2) real class="kw">property being equal to the control level
class="type">long GetPropLongFlagEQUAL(class="kw">const class="type">int class="kw">property) class="kw">const { class="kw">return this.m_long_prop_event[class="kw">property][class="num">9]; }

对象属性事件标记的底层数组布局

在 MT5 自定义控件库里,CBaseObj 用一张二维数组统一管理对象属性的监控状态。数组第二维长度由 CONTROLS_TOTAL 决定,从下标 0 到 9 分别承载增量阈值、减量阈值、控制水位、当前值、变动值以及 5 个布尔事件旗标,其中 [9] 专门记录属性值是否等于控制水位。 GetPropDoubleFlagEQUAL 这个函数只服务于 double 类属性:它用 property 减去 m_long_prop_total 来平移索引,直接掏出 m_double_prop_event 里第 9 列的值。你在 EA 里想判断某 double 属性是否刚好触线,调这一行比自己写判断省事。 FillPropertySettings 是模板函数,long 和 double 共用一套逻辑,靠 typename(T)=="double" 来偏移 event_id。每次进来先 for(j=5;j<CONTROLS_TOTAL;j++) 把五个事件旗标清零,再算 value=array[index][3]-array_prev[index][3],这一步是后续所有阈值比较的基准。外汇与贵金属行情跳动快,这种清零+重算机制能避免上一根 K 的旗标污染当前 tick 的判断,但高频下仍可能因滑点漏触,属正常概率风险。

MQL5 / C++
class="type">class="kw">double GetPropDoubleFlagEQUAL(class="kw">const class="type">int class="kw">property) class="kw">const { class="kw">return this.m_double_prop_event[class="kw">property-this.m_long_prop_total][class="num">9]; }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Fill in the object class="kw">property array                               |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="kw">template<class="kw">typename T> class="type">bool CBaseObj::FillPropertySettings(class="kw">const class="type">int index,T &array[][CONTROLS_TOTAL],T &array_prev[][CONTROLS_TOTAL],class="type">int &event_id)
  {
   class=class="str">"cmt">//--- Data in the array cells
   class=class="str">"cmt">//--- [Property index][class="num">0] Controlled class="kw">property increase value
   class=class="str">"cmt">//--- [Property index][class="num">1] Controlled class="kw">property decrease value
   class=class="str">"cmt">//--- [Property index][class="num">2] Controlled class="kw">property value level
   class=class="str">"cmt">//--- [Property index][class="num">3] Property value
   class=class="str">"cmt">//--- [Property index][class="num">4] Property value change
   class=class="str">"cmt">//--- [Property index][class="num">5] Flag of a class="kw">property change exceeding the increase value
   class=class="str">"cmt">//--- [Property index][class="num">6] Flag of a class="kw">property change exceeding the decrease value
   class=class="str">"cmt">//--- [Property index][class="num">7] Flag of a class="kw">property increase exceeding the control level
   class=class="str">"cmt">//--- [Property index][class="num">8] Flag of a class="kw">property decrease being less than the control level
   class=class="str">"cmt">//--- [Property index][class="num">9] Flag of a class="kw">property value being equal to the control level

   class=class="str">"cmt">//--- Set the shift of the &class="macro">#x27;class="type">class="kw">double&class="macro">#x27; class="kw">property index and the event ID
   event_id=index+(class="kw">typename(T)=="class="type">class="kw">double" ? this.m_long_prop_total : class="num">0);
   class=class="str">"cmt">//--- Reset all event flags
   for(class="type">int j=class="num">5;j<CONTROLS_TOTAL;j++)
      array[index][j]=class="kw">false;
   class=class="str">"cmt">//--- Property change value
   T value=array[index][class="num">3]-array_prev[index][class="num">3];
   array[index][class="num">4]=value;
   class=class="str">"cmt">//--- If the controlled class="kw">property increase value is set
   if(array[index][class="num">0]<LONG_MAX)
     {
      class=class="str">"cmt">//--- If the class="kw">property change value exceeds the controlled increase value - there is an event,
      class=class="str">"cmt">//--- add the event to the list, set the flag and save the new class="kw">property value size
      if(value>class="num">0 && value>array[index][class="num">0])
        {
         if(this.EventBaseAdd(event_id,BASE_EVENT_REASON_INC,value))
           {
            array[index][class="num">5]=true;
            array_prev[index][class="num">4]=value;
           }
         class=class="str">"cmt">//--- Save the current class="kw">property value as a previous one
         array_prev[index][class="num">3]=array[index][class="num">3];
        }
     }
   class=class="str">"cmt">//--- If the controlled class="kw">property decrease value is set
   if(array[index][class="num">1]<LONG_MAX)
     {
      class=class="str">"cmt">//--- If the class="kw">property change value exceeds the controlled decrease value - there is an event,
      class=class="str">"cmt">//--- add the event to the list, set the flag and save the new class="kw">property value size
      if(value<class="num">0 && fabs(value)>array[index][class="num">1])
        {

◍ 阈值穿越的事件触发与状态位落点

这段逻辑只在一个前提下跑:受控数组的索引 2 位置(控制阈值)必须小于 LONG_MAX,否则整段跳过。也就是说,没设阈值的跟踪对象不会进比较分支,只在前面做减量事件判断。 进入阈值分支后,先算 value = 当前属性值(索引3) - 控制阈值(索引2)。value>0 且上一帧属性值(索引3)还小于等于阈值,才触发「超过阈值」事件,写 BASE_EVENT_REASON_MORE_THEN 并把索引7置 true;反过来 value<0 且上一帧大于等于阈值,触发「低于阈值」事件,索引8置 true;value==0 且上一帧不等于阈值,触发「等于阈值」事件,索引9置 true。 三个分支都干同一件收尾活:把 array_prev[index][3] 刷成当前属性值。这个动作是状态机不抖的关键——它保证穿越只认「上一帧在界内、这一帧出界」的瞬间,连续多帧越界不会重复报事件。外汇与贵金属波动快,这种边沿触发能压住 MT5 回测里的事件洪水,但实盘滑点可能让穿越帧错位,仍属高风险。 代码里索引 6/7/8/9 分别锁 decrement、more、less、equals 四类事件的已触发标志,开 MT5 把这段塞进你的 CBaseObj 派生类,改 array_prev 的刷新时机就能验证「电平触发」和「边沿触发」的事件数差。

MQL5 / C++
   if(this.EventBaseAdd(event_id,BASE_EVENT_REASON_DEC,value))
      {
         array[index][class="num">6]=true;
         array_prev[index][class="num">4]=value;
      }
      class=class="str">"cmt">//--- Save the current class="kw">property value as a previous one
      array_prev[index][class="num">3]=array[index][class="num">3];
   }
   }
  class=class="str">"cmt">//--- If the controlled level value is set
  if(array[index][class="num">2]<LONG_MAX)
   {
      value=array[index][class="num">3]-array[index][class="num">2];
      class=class="str">"cmt">//--- If a class="kw">property value exceeds the control level, there is an event
      class=class="str">"cmt">//--- add the event to the list and set the flag
      if(value>class="num">0 && array_prev[index][class="num">3]<=array[index][class="num">2])
      {
         if(this.EventBaseAdd(event_id,BASE_EVENT_REASON_MORE_THEN,array[index][class="num">2]))
            array[index][class="num">7]=true;
         class=class="str">"cmt">//--- Save the current class="kw">property value as a previous one
         array_prev[index][class="num">3]=array[index][class="num">3];
      }
      class=class="str">"cmt">//--- If a class="kw">property value is less than the control level, there is an event,
      class=class="str">"cmt">//--- add the event to the list and set the flag
      else if(value<class="num">0 && array_prev[index][class="num">3]>=array[index][class="num">2])
      {
         if(this.EventBaseAdd(event_id,BASE_EVENT_REASON_LESS_THEN,array[index][class="num">2]))
            array[index][class="num">8]=true;
         class=class="str">"cmt">//--- Save the current class="kw">property value as a previous one
         array_prev[index][class="num">3]=array[index][class="num">3];
      }
      class=class="str">"cmt">//--- If a class="kw">property value is equal to the control level, there is an event,
      class=class="str">"cmt">//--- add the event to the list and set the flag
      else if(value==class="num">0 && array_prev[index][class="num">3]!=array[index][class="num">2])
      {
         if(this.EventBaseAdd(event_id,BASE_EVENT_REASON_EQUALS,array[index][class="num">2]))
            array[index][class="num">9]=true;
         class=class="str">"cmt">//--- Save the current class="kw">property value as a previous one
         array_prev[index][class="num">3]=array[index][class="num">3];
      }
   }
   class="kw">return true;
   }

「对象属性变更的重置与事件落库」

图形对象的状态机里,属性变更缓存必须在每根K线或每次重算前清空,否则旧信号会污染新判断。ResetChangesParams 先校验长短整型/双精度控制数组的尺寸(CheckControlDataArraySize 的 true/false 分别管两套数组),任一不通过直接 return,避免越界写脏数据。 清空时把 m_list_events 与 m_list_events_base 两个列表 Clear 后立刻 Sort,保证后续二分查找或遍历的事件序是稳定的。注释里标了数组单元格的语义:[3] 是属性值、[4] 是属性变化量、[5]~[9] 分别是增/减超阈值、增/减破控线、等于受控值的布尔旗。 核心归零用双层循环:外层从 m_long_prop_total-1 downto WRONG_VALUE,内层 j 从 3 到 CONTROLS_TOTAL(不含),把 m_long_prop_event[i][j] 和 m_double_prop_event[i][j] 全写 0。CONTROLS_TOTAL 若设为 10,则每个属性有 7 个状态槽被刷掉,这是 MT5 标准库里图形事件系统的典型实现。 CheckEvents 负责把 base 事件列表转成终端事件:取 total,为 0 就退;否则遍历,用 UshortToLong 把「TickTime 的毫秒」「事件原因」「对象类型」压进一个 long 的 0/1/2 段,再调 EventAdd 入列。只要有一条成功,m_is_event 置 true,盯盘面板就能知道这帧有东西动了。 外汇与贵金属品种用这套机制做属性监控时,点差跳变可能瞬间触发多次事件,属于高风险场景,参数阈值建议先在模拟盘验证。

MQL5 / C++
class="type">void CBaseObj::ResetChangesParams(class="type">void)
  {
   if(!this.CheckControlDataArraySize(true) || !this.CheckControlDataArraySize(class="kw">false))
      class="kw">return;
   this.m_list_events.Clear();
   this.m_list_events.Sort();
   this.m_list_events_base.Clear();
   this.m_list_events_base.Sort();
class=class="str">"cmt">//--- Data in the array cells
class=class="str">"cmt">//--- [Property index][class="num">3] Property value
class=class="str">"cmt">//--- [Property index][class="num">4] Property value change
class=class="str">"cmt">//--- [Property index][class="num">5] Flag of a class="kw">property change exceeding the increase value
class=class="str">"cmt">//--- [Property index][class="num">6] Flag of a class="kw">property change exceeding the decrease value
class=class="str">"cmt">//--- [Property index][class="num">7] Flag of a class="kw">property increase exceeding the control level
class=class="str">"cmt">//--- [Property index][class="num">8] Flag of a class="kw">property decrease being less than the control level
class=class="str">"cmt">//--- [Property index][class="num">9] Flag of a class="kw">property value being equal to the controlled value
   for(class="type">int i=this.m_long_prop_total-class="num">1;i>WRONG_VALUE;i--)
      for(class="type">int j=class="num">3; j<CONTROLS_TOTAL; j++)
         this.m_long_prop_event[i][j]=class="num">0;
   for(class="type">int i=this.m_double_prop_total-class="num">1;i>WRONG_VALUE;i--)
      for(class="type">int j=class="num">3; j<CONTROLS_TOTAL; j++)
         this.m_double_prop_event[i][j]=class="num">0;
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Check the list of object class="kw">property changes and create an event     |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CBaseObj::CheckEvents(class="type">void)
  {
   class="type">int total=this.m_list_events_base.Total();
   if(total==class="num">0)
      class="kw">return;

   for(class="type">int i=class="num">0;i<total;i++)
     {
      CBaseEvent *event=this.GetEventBase(i);
      if(event==NULL)
         class="kw">continue;
      class="type">long lvalue=class="num">0;
      this.UshortToLong(this.MSCfromTime(this.TickTime()),class="num">0,lvalue);
      this.UshortToLong(event.Reason(),class="num">1,lvalue);
      this.UshortToLong((class="type">class="kw">ushort)this.m_type,class="num">2,lvalue);
      if(this.EventAdd((class="type">class="kw">ushort)event.ID(),lvalue,event.Value(),this.m_name))
         this.m_is_event=true;
     }
  }

盯紧品种属性与保证金结构的底层容器

想在 MT5 里写一个能实时感知品种状态变化的监控类,先得把品种相关的所有属性塞进一组固定数组和结构体里。下面这段声明就是典型做法:用三个定长数组分别装整数、实数和字符串属性,再用 MqlMarginRateMode 把多空及挂单的保证金率一次性打包。 [CODE] struct MqlMarginRate { double Initial; double Maintenance; }; struct MqlMarginRateMode { MqlMarginRate Long; MqlMarginRate Short; MqlMarginRate BuyStop; MqlMarginRate BuyLimit; MqlMarginRate BuyStopLimit; MqlMarginRate SellStop; MqlMarginRate SellLimit; MqlMarginRate SellStopLimit; }; MqlMarginRateMode m_margin_rate; MqlBookInfo m_book_info_array[]; long m_long_prop[SYMBOL_PROP_INTEGER_TOTAL]; double m_double_prop[SYMBOL_PROP_DOUBLE_TOTAL]; string m_string_prop[SYMBOL_PROP_STRING_TOTAL]; bool m_is_change_trade_mode; virtual void InitControlsParams(void); void CheckEvents(void); template<typename T> void SetControlChangedValue(const int property,const T value); [/CODE] 逐行看:MqlMarginRate 里 Initial 是开仓保证金率,Maintenance 是维持保证金率,两者都是 double。MqlMarginRateMode 把 8 种持仓和挂单方向(多、空、四类买挂、四类卖挂)各自的保证金率都包进来,外汇和贵金属杠杆切换时这组值会直接决定占用保证金,属于高风险品种的风控关键点。 m_book_info_array 用来接市场深度(DOM)数据,三个 prop 数组按 MQL5 内置枚举总项数分配,比如 SYMBOL_PROP_DOUBLE_TOTAL 在现行 MT5 构建里通常是 20 上下,具体数以你终端的 include 文件为准。m_is_change_trade_mode 是个布尔旗标,标记品种是否切了交易模式。 InitControlsParams 做初始化,CheckEvents 扫属性变动并抛事件,SetControlChangedValue 是模板方法,能塞任意类型的属性新值。开 MT5 新建一个 CSymbol 派生类,把这套声明原样贴进私有段,基本就能跑起品种监控原型。

MQL5 / C++
class="kw">struct MqlMarginRate
  {
   class="type">class="kw">double Initial;
   class="type">class="kw">double Maintenance;
  };
class="kw">struct MqlMarginRateMode
  {
   MqlMarginRate Long;
   MqlMarginRate Short;
   MqlMarginRate BuyStop;
   MqlMarginRate BuyLimit;
   MqlMarginRate BuyStopLimit;
   MqlMarginRate SellStop;
   MqlMarginRate SellLimit;
   MqlMarginRate SellStopLimit;
  };
MqlMarginRateMode m_margin_rate;
MqlBookInfo m_book_info_array[];
class="type">long m_long_prop[SYMBOL_PROP_INTEGER_TOTAL];
class="type">class="kw">double m_double_prop[SYMBOL_PROP_DOUBLE_TOTAL];
class="type">class="kw">string m_string_prop[SYMBOL_PROP_STRING_TOTAL];
class="type">bool m_is_change_trade_mode;
class="kw">virtual class="type">void InitControlsParams(class="type">void);
class="type">void CheckEvents(class="type">void);
class="kw">template<class="kw">typename T> class="type">void SetControlChangedValue(class="kw">const class="type">int class="kw">property,class="kw">const T value);

◍ 给品种属性变动上阈值与越界标记

在盯盘逻辑里,光读品种属性不够,还得知道它相对上一刻是放大还是缩小、有没有越过你设的临界线。下面这组接口把这件事拆成了三类写入:增量控制值、减量控制值、控制水位,分别对应属性向上动、向下动、以及你认定的基准线。 template<typename T> void SetControlPropertyINC(const int property,const T value); template<typename T> void SetControlPropertyDEC(const int property,const T value); template<typename T> void SetControlPropertyLEVEL(const int property,const T value); 上面三行就是写入入口,T 可以是 long 也可以是 double,由你监控的 ENUM_SYMBOL_PROP 类型决定。比如监控 XAUUSD 的卖一量(整数属性)和点差(实数属性),就能分别给 INC/DEC/LEVEL 挂不同阈值。 template<typename T> void SetControlFlagINC(const int property,const T value); template<typename T> void SetControlFlagDEC(const int property,const T value); 这两个是越界标记写入:当属性实际变化超过 INC 或 DEC 你给的数值,内部 flag 会被置位。外汇和贵金属波动高,阈值设太窄会频繁误触发,建议先用历史 tick 回看 95 分位变动再定。 读回方面,GetControlParameterINC / DEC 按属性是整数还是实数返回 long 或 double,直接拿你之前写进去的阈值;GetControlFlagINC / DEC 则返回越界状态,整数属性版返回 long、实数属性版强转成了 bool,调用时注意类型别接错。 把这些塞进 EA 的 OnTick,配合小布跑一遍异动扫描,你就能在 MT5 里实时看见哪些品种刚踩过你的线,而不是等 K 线走完才后知后觉。

MQL5 / C++
class=class="str">"cmt">//--- Set the value of the controlled symbol class="kw">property (class="num">1) increase, (class="num">2) decrease and(class="num">3) control level
  class="kw">template<class="kw">typename T> class="type">void  SetControlPropertyINC(class="kw">const class="type">int class="kw">property,class="kw">const T value);
  class="kw">template<class="kw">typename T> class="type">void  SetControlPropertyDEC(class="kw">const class="type">int class="kw">property,class="kw">const T value);
  class="kw">template<class="kw">typename T> class="type">void  SetControlPropertyLEVEL(class="kw">const class="type">int class="kw">property,class="kw">const T value);
class=class="str">"cmt">//--- Set the flag of a symbol class="kw">property change exceeding the(class="num">1) increase and(class="num">2) decrease values
  class="kw">template<class="kw">typename T> class="type">void  SetControlFlagINC(class="kw">const class="type">int class="kw">property,class="kw">const T value);
  class="kw">template<class="kw">typename T> class="type">void  SetControlFlagDEC(class="kw">const class="type">int class="kw">property,class="kw">const T value);
  
class=class="str">"cmt">//--- Return the set value of the(class="num">1) integer and(class="num">2) real symbol class="kw">property controlled increase
  class="type">long                GetControlParameterINC(class="kw">const ENUM_SYMBOL_PROP_INTEGER class="kw">property)  class="kw">const { class="kw">return this.GetControlledValueLongINC(class="kw">property);       }
  class="type">class="kw">double              GetControlParameterINC(class="kw">const ENUM_SYMBOL_PROP_DOUBLE class="kw">property)    class="kw">const { class="kw">return this.GetControlledValueDoubleINC(class="kw">property);     }
class=class="str">"cmt">//--- Return the set value of the(class="num">1) integer and(class="num">2) real symbol class="kw">property controlled decrease
  class="type">long                GetControlParameterDEC(class="kw">const ENUM_SYMBOL_PROP_INTEGER class="kw">property)  class="kw">const { class="kw">return this.GetControlledValueLongDEC(class="kw">property);       }
  class="type">class="kw">double              GetControlParameterDEC(class="kw">const ENUM_SYMBOL_PROP_DOUBLE class="kw">property)    class="kw">const { class="kw">return this.GetControlledValueDoubleDEC(class="kw">property);     }
class=class="str">"cmt">//--- Return the flag of an(class="num">1) integer and(class="num">2) real symbol class="kw">property value change exceeding the increase value
  class="type">long                GetControlFlagINC(class="kw">const ENUM_SYMBOL_PROP_INTEGER class="kw">property)        class="kw">const { class="kw">return this.GetControlledFlagLongINC(class="kw">property);         }
  class="type">class="kw">double              GetControlFlagINC(class="kw">const ENUM_SYMBOL_PROP_DOUBLE class="kw">property)         class="kw">const { class="kw">return this.GetControlledFlagDoubleINC(class="kw">property);       }
class=class="str">"cmt">//--- Return the flag of an(class="num">1) integer and(class="num">2) real symbol class="kw">property value change exceeding the decrease value
  class="type">bool                GetControlFlagDEC(class="kw">const ENUM_SYMBOL_PROP_INTEGER class="kw">property)        class="kw">const { class="kw">return (class="type">bool)this.GetControlledFlagLongDEC(class="kw">property);   }
  class="type">bool                GetControlFlagDEC(class="kw">const ENUM_SYMBOL_PROP_DOUBLE class="kw">property)         class="kw">const { class="kw">return (class="type">bool)this.GetControlledFlagDoubleDEC(class="kw">property); }
让小布替你跑这套
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到对象属性变化与事件触发标记,不必自己编译测试 EA 来验证重构效果。

常见问题

所有衍生对象事件判定逻辑完全一致,放基类避免重复实现,后续改跟踪规则只动一处。
可能更准。LONG_MAX 会吞掉接近边界的小变化,零值让首次变化可被正常识别为事件。
倾向可以,继承体系打通后,账户属性变化走同一套受监视属性设置即可产出事件。
能,小布盯盘品种页内置了属性变化与事件标记,省去你本地跑测试 EA 的步骤。
仅在注册事件时才把当前状态写入之前状态,否则变化对比永远为零,抓不到即时报价里的大跳。