开发回放系统(第30部分):EA交易项目C_Mouse类(四)·进阶篇
🖱️

开发回放系统(第30部分):EA交易项目C_Mouse类(四)·进阶篇

(2/3)· 在已测试代码中塞未验证函数常致不可预测失败,本篇给另一条并行路径

偏理论 第 2/3 篇
不少人习惯把刚写好的实验函数直接插进跑通的主代码里,以为只是临时加一行。一旦新逻辑和原系统耦合,删都比回退早期版本难,隐性崩溃概率明显上升。

◍ 用指针绕开继承的并行测试思路

第二条路径里我们不去碰继承,而是直接在 EA 里用指针把各个类系统挂起来。主类系统(鼠标下单核心)标黄,上一篇文章里扩展出来的分析类标绿,橙色则是另一套可并行跑的分析系统。因为没走继承,EA 里要手写更多声明,但好处是能同时跑多份并行代码,哪一段在实测里出故障,直接删掉指针释放就行,不会牵一发动全身。 橙色和绿色这两套分析类本身都可能写成多态,这样能在并行开发阶段把系统的更多切面测出来。多态的具体用法留到后面再拆,现在铺开讲容易把人绕晕,尤其是刚接触这种架构的交易者。 下面这段 EA 代码就是第二条路径的完整骨架。注意 OnInit 里用 new 给三个指针分配内存,OnDeinit 里必须按相反顺序 delete,否则可能漏释放。OnTimer 每 500 毫秒触发一次,只调了 extra1 的 Update,而盘口事件 OnBookEvent 里也喂给 extra1,extra2 在当前版本还没接活。

MQL5 / C++
class="macro">#class="kw">property copyright "Daniel Jose"
class="macro">#class="kw">property description "Generic EA for use on Demo account, replay system/simulator and Real account."
class="macro">#class="kw">property description "This system has means of sending orders using the mouse and keyboard combination."
class="macro">#class="kw">property description "For more information see the article about the system."
class="macro">#class="kw">property version   "class="num">1.30"
class="macro">#class="kw">property icon "../../Images/Icons/Replay - EA.ico"
class="macro">#class="kw">property link "[MQL5官方文档]
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#include <Market Replay\System EA\Auxiliar\C_Mouse.mqh>
class="macro">#include <Market Replay\System EA\Auxiliar\Study\C_StudyS1.mqh>
class="macro">#include <Market Replay\System EA\Auxiliar\Study\C_StudyS2.mqh>
class=class="str">"cmt">//+------------------------------------------------------------------+
class="kw">input group "Mouse";
class="kw">input class="type">color    user00 = clrBlack;       class=class="str">"cmt">//Price Line
class="kw">input class="type">color    user01 = clrPaleGreen;  class=class="str">"cmt">//Positive Study
class="kw">input class="type">color    user02 = clrLightCoral; class=class="str">"cmt">//Negative Study
class=class="str">"cmt">//+------------------------------------------------------------------+
C_Mouse *mouse = NULL;
C_StudyS1 *extra1 = NULL;
C_StudyS2 *extra2 = NULL;
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnInit()
{
   mouse = new C_Mouse(user00, user01, user02);
   extra1 = new C_StudyS1(mouse, user01, user02);
   extra2 = new C_StudyS2(mouse, user01, user02);
           
   MarketBookAdd((*mouse).GetInfoTerminal().szSymbol);
   OnBookEvent((*mouse).GetInfoTerminal().szSymbol);
   EventSetMillisecondTimer(class="num">500);
   class="kw">return INIT_SUCCEEDED;
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnDeinit(class="kw">const class="type">int reason)
{
   MarketBookRelease((*mouse).GetInfoTerminal().szSymbol);
   EventKillTimer();
       
   class="kw">delete extra1;
   class="kw">delete extra2;
   class="kw">delete mouse;
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnTick() {}
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnTimer()
{
   (*extra1).Update();
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnBookEvent(class="kw">const class="type">class="kw">string &symbol)
{
   MqlBookInfo book[];

   if ((*mouse).GetInfoTerminal().szSymbol == def_SymbolReplay) ArrayResize(book, class="num">1, class="num">0); else
   {
      if (symbol != (*mouse).GetInfoTerminal().szSymbol) class="kw">return;
      MarketBookGet((*mouse).GetInfoTerminal().szSymbol, book);
   }
   (*extra1).Update(book);
}
逐行拆解几个关键点:第 19–21 行声明三个类指针并置 NULL,这是 MT5 里防野指针的常规起手;OnInit 第 26 行 EventSetMillisecondTimer(500) 意味着定时器精度半秒,做高频盘口快照够用但别指望亚毫秒级;OnDeinit 第 38–40 行删除顺序和 new 相反,先析构分析类再析构鼠标类,避免分析类内部还引用鼠标对象。 开 MT5 把这段塞进 EA 跑复盘,你会发现 extra2 不接 Update 调用就等于空转,想加并行测试直接把 OnTimer 里补一行 (*extra2).Update() 就能对比两套分析信号。外汇与贵金属杠杆高,复盘通过不代表实盘概率占优,并行测试也只是排查架构故障,不涉及任何收益暗示。

MQL5 / C++
class="macro">#class="kw">property copyright "Daniel Jose"
class="macro">#class="kw">property description "Generic EA for use on Demo account, replay system/simulator and Real account."
class="macro">#class="kw">property description "This system has means of sending orders using the mouse and keyboard combination."
class="macro">#class="kw">property description "For more information see the article about the system."
class="macro">#class="kw">property version   "class="num">1.30"
class="macro">#class="kw">property icon "../../Images/Icons/Replay - EA.ico"
class="macro">#class="kw">property link "[MQL5官方文档]
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#include <Market Replay\System EA\Auxiliar\C_Mouse.mqh>
class="macro">#include <Market Replay\System EA\Auxiliar\Study\C_StudyS1.mqh>
class="macro">#include <Market Replay\System EA\Auxiliar\Study\C_StudyS2.mqh>
class=class="str">"cmt">//+------------------------------------------------------------------+
class="kw">input group "Mouse";
class="kw">input class="type">color    user00 = clrBlack;       class=class="str">"cmt">//Price Line
class="kw">input class="type">color    user01 = clrPaleGreen;  class=class="str">"cmt">//Positive Study
class="kw">input class="type">color    user02 = clrLightCoral; class=class="str">"cmt">//Negative Study
class=class="str">"cmt">//+------------------------------------------------------------------+
C_Mouse *mouse = NULL;
C_StudyS1 *extra1 = NULL;
C_StudyS2 *extra2 = NULL;
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnInit()
{
   mouse = new C_Mouse(user00, user01, user02);
   extra1 = new C_StudyS1(mouse, user01, user02);
   extra2 = new C_StudyS2(mouse, user01, user02);
           
   MarketBookAdd((*mouse).GetInfoTerminal().szSymbol);
   OnBookEvent((*mouse).GetInfoTerminal().szSymbol);
   EventSetMillisecondTimer(class="num">500);
   class="kw">return INIT_SUCCEEDED;
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnDeinit(class="kw">const class="type">int reason)
{
   MarketBookRelease((*mouse).GetInfoTerminal().szSymbol);
   EventKillTimer();
       
   class="kw">delete extra1;
   class="kw">delete extra2;
   class="kw">delete mouse;
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnTick() {}
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnTimer()
{
   (*extra1).Update();
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnBookEvent(class="kw">const class="type">class="kw">string &symbol)
{
   MqlBookInfo book[];

   if ((*mouse).GetInfoTerminal().szSymbol == def_SymbolReplay) ArrayResize(book, class="num">1, class="num">0); else
   {
      if (symbol != (*mouse).GetInfoTerminal().szSymbol) class="kw">return;
      MarketBookGet((*mouse).GetInfoTerminal().szSymbol, book);
   }
   (*extra1).Update(book);
}

「把鼠标事件分流给多个绘制对象」

在 MT5 自定义指标或 EA 里接管 OnChartEvent,是做交互式图形的前提。下面这段把图表事件统一派发给三个对象:mouse、extra1、extra2,各自内部再决定怎么响应。 代码里用指针解引用 (*mouse).DispatchMessage(...) 把 id、lparam、dparam、sparam 原样传进去,意味着鼠标点击、移动、键盘输入都会同时触达这三个实例。extra1 和 extra2 是扩展层,可挂不同的悬浮标签或辅助线,不干扰主鼠标逻辑。 每次派发完立刻调 ChartRedraw(),保证多个对象的状态变更在同一帧重绘,避免界面撕裂。实盘加载这类指标时,若对象超过 3 个且都接事件,CPU 占用可能随鼠标移动陡增,建议在 OnChartEvent 里加 id==CHARTEVENT_MOUSE_MOVE 的节流判断。外汇与贵金属图表事件密集,高频重绘有卡顿风险,参数需自测。

MQL5 / C++
class="type">void OnChartEvent(class="kw">const class="type">int id, class="kw">const class="type">long &lparam, class="kw">const class="type">class="kw">double &dparam, class="kw">const class="type">class="kw">string &sparam)
{
   (*mouse).DispatchMessage(id, lparam, dparam, sparam);
   (*extra1).DispatchMessage(id, lparam, dparam, sparam);
   (*extra2).DispatchMessage(id, lparam, dparam, sparam);
   
   ChartRedraw();
}

拆开 C_StudyS2 的斐波那契分析骨架

C_StudyS2 类不是从零搭系统,而是复用 C_Mouse 已经验证过的鼠标交互与对象管理逻辑,再叠加一套基于 OBJ_FIBO 的分析呈现。头文件用相对路径 include(如 "..\C_Mouse.mqh"),项目整体迁移目录时不用改引用,结构维护成本低。 类里先用宏定义对象前缀与别名:def_ExpansionPrefix 拼出 "Expansion2_",再派生按钮与斐波那契对象名;def_InfoTerminal、def_InfoMousePos 直接映射到鼠标类的实时数据,写代码时少敲很多字。私有结构 st00 通过 m_Info 全局变量存分析状态——是否执行、是否清除、记忆价格与时间、正负偏差颜色。 真正画分析分三段:第一段监听到用户开始分析事件后,删掉 C_Mouse 创建的临时对象,但只删“研究不需要看见”的,避免误伤图表关键元素;此时旧分析对象也被清掉,方便按 OBJ_FIBOTIMES、OBJ_FIBOFAN、OBJ_FIBOARC、OBJ_FIBOCHANNEL、OBJ_EXPANSION 等变体重建。第二段创建 OBJ_FIBO 并设属性,级别用静态定义,颜色与线形可改,目的是一眼读懂。第三段上图表绘制,并备好后续函数要用的变量。 显示文本的小数位数跟着品种跑:有的品种报价值要 4 位,有的 5 位,股票可能只 2 位。代码用 StringFormat 配合 <%.2f> / <%.4f> 在运行时拼格式,差值(分析起点价与鼠标当前价偏移)按对应位数转字符串,不会写死。 柱数统计也值得注意:用平台函数数分析区间的 bar,若在空白区会返回 -1,单 bar 返回 0;代码里把 -1 剔掉,让数值恒等于“含起点柱”的实际根数。偏差百分比另算一并显示。外汇与贵金属波动大、杠杆高,这类自定义分析对象仅辅助判读,信号失效概率不低,上 MT5 跑前先开模拟盘验证。

MQL5 / C++
<span class="preprocessor">class="macro">#class="kw">property </span><span class="macro">copyright</span> <span class="class="type">class="kw">string">"Daniel Jose"</span>
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="preprocessor">class="macro">#include </span><span class="class="type">class="kw">string">"..\C_Mouse.mqh"</span>
<span class="preprocessor">class="macro">#include </span><span class="class="type">class="kw">string">"..\..\..\Service Graphics\Support\Interprocess.mqh"</span>
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="preprocessor">class="macro">#define </span>def_ExpansionPrefix <span class="class="type">class="kw">string">"Expansion2_"</span>
<span class="preprocessor">class="macro">#define </span>def_ExpansionBtn1 def_ExpansionPrefix + <span class="class="type">class="kw">string">"B1"</span>
<span class="preprocessor">class="macro">#define </span>def_ExpansionFibo def_ExpansionPrefix + <span class="class="type">class="kw">string">"FB"</span>
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="preprocessor">class="macro">#define </span>def_InfoTerminal(*mouse).GetInfoTerminal()
<span class="preprocessor">class="macro">#define </span>def_InfoMousePos(*mouse).GetInfoMouse().Position
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="keyword">class</span> C_StudyS2
{
&nbsp;&nbsp;&nbsp;<span class="keyword">class="kw">protected</span>:
&nbsp;&nbsp;&nbsp;<span class="keyword">class="kw">private</span> :
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;C_Mouse *mouse;
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">class="kw">struct</span> st00
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">class="type">bool</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ExecStudy,
&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;ClearStudy;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">class="type">class="kw">double</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MemPrice;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">class="type">class="kw">datetime</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MemDT;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">class="type">color</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; corP,
&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;corN;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}m_Info;
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="preprocessor">class="macro">#define </span>def_FontName <span class="class="type">class="kw">string">"Lucida Console"</span>
<span class="preprocessor">class="macro">#define </span>def_FontSize <span class="number">class="num">10</span>

◍ 用 Fibo 数组和按钮对象搭信息面板

在 MT5 指标里做交互式提示,先得量出文字占位。GetDimensionText 用 TextSetFont 设字体(字号取负向 -10*def_FontSize 表示像素单位),TextGetSize 拿宽高后各加 5 像素留白,避免文字贴边。 CreateBTNInfo 负责生成 OBJ_BUTTON:通过 ObjectSetInteger 把状态锁为 true、边框与文字都设 clrBlack,背景交 backColor 参数控制;字体名与字号沿用全局 def_FontName / def_FontSize,锚点固定 CORNER_LEFT_UPPER 并按 x 给水平偏移。 CreateStudy 才是主装配:FiboLevels 写死 0 / 0.236 / 0.382 / 0.50 / 0.618 / 1 / 1.618 / 2 共 8 档,先删掉旧扩展斐波那契与三个 MOUSE_ 前缀对象,再建 OBJ_FIBO 并关掉向左射线。OBJPROP_LEVELS 直接用 ArraySize(FiboLevels) 动态填层数,外汇与贵金属波动剧烈,这类画线仅作参考、实际破位概率随流动性变化。 循环从 c0=0 跑到数组长度,逐档写层级样式——这一段若接你自己的面板逻辑,改 FiboLevels 数组就能换一套扩展位。

MQL5 / C++
class="type">void GetDimensionText(class="kw">const class="type">class="kw">string szArg, class="type">int &w, class="type">int &h)
  {
    TextSetFont(def_FontName, -class="num">10 * def_FontSize, FW_NORMAL);
    TextGetSize(szArg, w, h);
    h += class="num">5;
    w += class="num">5;
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
  class="type">void CreateBTNInfo(class="type">int x, class="type">int w, class="type">int h, class="type">class="kw">string szName, class="type">color backColor)
  {
    (*mouse).CreateObjectGraphics(szName, OBJ_BUTTON, clrNONE);
    ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_STATE, true);
    ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_BORDER_COLOR, clrBlack);
    ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_COLOR, clrBlack);
    ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_BGCOLOR, backColor);
    ObjectSetString(def_InfoTerminal.ID, szName, OBJPROP_FONT, def_FontName);
    ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_FONTSIZE, def_FontSize);
    ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_CORNER, CORNER_LEFT_UPPER);
    ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_XDISTANCE, x);
  }
class="macro">#undef def_FontSize
class="macro">#undef def_FontName
class="type">void CreateStudy(class="type">void)
  {
    class="kw">const class="type">class="kw">double FiboLevels[] = {class="num">0, class="num">0.236, class="num">0.382, class="num">0.50, class="num">0.618, class="num">1, class="num">1.618, class="num">2};
    ENUM_LINE_STYLE ls;
    class="type">color cor;
    
    ObjectDelete(def_InfoTerminal.ID, def_ExpansionFibo);
    ObjectDelete(def_InfoTerminal.ID, "MOUSE_TB");
    ObjectDelete(def_InfoTerminal.ID, "MOUSE_TI");
    ObjectDelete(def_InfoTerminal.ID, "MOUSE_TT");
    (*mouse).CreateObjectGraphics(def_ExpansionFibo, OBJ_FIBO, clrNONE);
    ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_HIDDEN, class="kw">false);
    ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_RAY_LEFT, class="kw">false);
    ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_LEVELS, ArraySize(FiboLevels));
    for (class="type">int c0 = class="num">0, c1 = ArraySize(FiboLevels); c0 < c1; c0++)
    {

「斐波那契扩展线的样式与交互收尾」

上面这段把扩展斐波那契的各档位线型与配色做了区分:0、1、2 档用实线,其余用点划线,0.5 档单独点线。switch 里把线型映射到颜色——点线蓝紫、点划紫、默认靛蓝,视觉上能把关键回调位和延伸位拉开层次。 循环里用 ObjectSetInteger 逐个写 OBJPROP_LEVELSTYLE、OBJPROP_LEVELCOLOR、OBJPROP_LEVELWIDTH(固定 1),再把档位值乘 100 后 NormalizeDouble 到两位小数写进 OBJPROP_LEVELTEXT,图表上直接显示百分比而非小数。 对象收尾靠析构里的 ObjectsDeleteAll 按前缀 def_ExpansionPrefix 清空,避免重复画线堆积。键盘事件里若按下 ESC 且已执行分析,就把 ClearStudy 置真,鼠标移动事件留作后续触发刷新——外汇与贵金属波动剧烈,这类对象若不清会干扰后续手动划线,实盘前务必在 MT5 策略测试器或图表手动验证一遍生命周期。

MQL5 / C++
ls = ((FiboLevels[c0] == class="num">0) || (FiboLevels[c0] == class="num">1) || (FiboLevels[c0] == class="num">2) ? STYLE_SOLID : STYLE_DASHDOT);
ls = (FiboLevels[c0] == class="num">0.5 ? STYLE_DOT : ls);
class="kw">switch (ls)
{
   case STYLE_DOT     : cor = clrBlueViolet;  break;
   case STYLE_DASHDOT: cor = clrViolet;      break;
   class="kw">default            : cor = clrIndigo;
}
ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_LEVELSTYLE, c0, ls);
ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_LEVELCOLOR, c0, cor);
ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_LEVELWIDTH, c0, class="num">1);
ObjectSetString(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_LEVELTEXT, c0, (class="type">class="kw">string)NormalizeDouble(FiboLevels[c0] * class="num">100, class="num">2));
}
ObjectSetDouble(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_PRICE, class="num">1, m_Info.MemPrice = def_InfoMousePos.Price);
ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_TIME, class="num">1, m_Info.MemDT = def_InfoMousePos.dt);
CreateBTNInfo(def_InfoMousePos.X, class="num">50, class="num">18, def_ExpansionBtn1, clrNONE);
m_Info.ExecStudy = true;
m_Info.ClearStudy = class="kw">false;
}
C_StudyS2(C_Mouse *arg, class="type">color corP, class="type">color corN)
   {                   
   mouse = arg;
   ZeroMemory(m_Info);
   m_Info.corP = corP;
   m_Info.corN = corN;
}
class=class="str">"cmt">//+------------------------------------------------------------------+
~C_StudyS2()
   {
   ObjectsDeleteAll(def_InfoTerminal.ID, def_ExpansionPrefix);
}
class="kw">virtual class="type">void DispatchMessage(class="kw">const class="type">int id, class="kw">const class="type">long &lparam, class="kw">const class="type">class="kw">double &dparam, class="kw">const class="type">class="kw">string &sparam)
   {
   class="type">class="kw">double v1, v2;
   class="type">int w, h;
   class="type">class="kw">string sz1;
           
   class="kw">switch (id)
   {
      case CHARTEVENT_KEYDOWN:
         if (TerminalInfoInteger(TERMINAL_KEYSTATE_ESCAPE) && (m_Info.ExecStudy)) m_Info.ClearStudy = true;
         break;
      case CHARTEVENT_MOUSE_MOVE:
把并行分支交给小布盯盘记录
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到不同开发分支的调用轨迹,你专注决策哪条路径进主类。

常见问题

未验证函数嵌入稳定代码后,可能因耦合产生不可预测失败,且后期剥离难度高于回退早期阶段,并行开发更稳妥。
可以,小布盯盘内置的 AIGC 能呈现各并行分支的调用与状态,减少人工翻代码的重复劳动。
不是必须,是否继承偏个人选择;两条路径可并行工作,最终稳定有趣的代码才纳入主类系统。
指针方案避免早期过度继承卡顿,适合测试期并行扩展,但生产环境需更严控制以防奇怪错误。
关于主类系统整合与实战技巧的完整讨论见《开发回放系统(第30部分):EA交易项目C_Mouse类》·实战篇。