开发回放系统(第 75 部分):新 Chart Trade(二)·综合运用
📘

开发回放系统(第 75 部分):新 Chart Trade(二)·综合运用

第 3/3 篇

◍ 浮动面板的状态恢复与析构清理

当 RestoreState() 返回 false 时,说明没有可复用的历史布局,代码会强制写入一套默认参数:杠杆取当前账户值,日交易标记置 true,财务止盈止损沿用传入量,窗口最大化开启,左上角坐标锁在 x=115、y=64。随后以 170×210 的尺寸创建 RAD 浮动窗,并套用模板调整。 析构函数 ~C_ChartFloatingRAD() 负责收尾:先 ChartRedraw() 刷新,再 ObjectsDeleteAll() 按终端 ID 和对象名清理图表附属物件;若本次运行未触发保存状态,就 FileDelete() 删掉临时模板文件,最后 delete 鼠标事件对象,避免 MT5 退出时残留句柄。 SaveState() 用了一个联合体技巧:uCast_Double 把 4 个 16 位坐标值(x/y/minx/miny)压进一个 double,通过 macro_GlobalVariable 写进终端全局变量,键名由 macro_NameGlobalVariable("POST") 生成。这样下次加载时 RestoreState() 读回该 double 就能还原窗口位置——外汇与贵金属品种的高杠杆下,面板坐标错乱可能让你误触重仓,建议在 MT5 里跑一遍并改 m_Info.minx 的 115 看吸附表现。

MQL5 / C++
if (!RestoreState())
{
   m_Info.Leverage     = Leverage;
   m_Info.IsDayTrade   = true;
   m_Info.FinanceTake = FinanceTake;
   m_Info.FinanceStop = FinanceStop;
   m_Info.IsMaximized = true;
   m_Info.minx = m_Info.x = class="num">115;
   m_Info.miny = m_Info.y = class="num">64;
}
CreateWindowRAD(class="num">170, class="num">210);
AdjustTemplate(true);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
   ~C_ChartFloatingRAD()
   {
    ChartRedraw();
    ObjectsDeleteAll(GetInfoTerminal().ID, m_Info.szObj_Chart);
    if (!m_Info.IsSaveState)
    FileDelete(m_Info.szFileNameTemplate);
    class="kw">delete m_Mouse;
   }
class=class="str">"cmt">//+------------------------------------------------------------------+
   class="type">void SaveState(class="type">void)
   {
class="macro">#define macro_GlobalVariable(A, B) if (GlobalVariableTemp(A)) GlobalVariableSet(A, B);
    uCast_Double info;
    info._16b[class="num">0] = m_Info.x;
    info._16b[class="num">1] = m_Info.y;
    info._16b[class="num">2] = m_Info.minx;
    info._16b[class="num">3] = m_Info.miny;
    macro_GlobalVariable(macro_NameGlobalVariable("POST"), info.dValue);
    m_Info.IsSaveState = true;
class="macro">#undef macro_GlobalVariable
   }
class=class="str">"cmt">//+------------------------------------------------------------------+

卸载时的清理与状态保存分支

指标或 EA 被移除时,OnDeinit 拿到的 reason 参数决定了该做哪种收尾。上面这段 MT5 代码把初始化失败和图表属性变更分成了两个独立分支处理。 当 reason 等于 REASON_INITFAILED,直接调 ChartIndicatorDelete 按 def_ShortName 删掉当前图表零号子窗口里的指标实例,避免半初始化对象留在图上。若是 REASON_CHARTCHANGE,则触发 chart 指针指向对象的 SaveState 方法把界面状态落盘,再退出 switch。 两个 case 都 break 后,第 53 行无条件 delete chart 释放堆内存。注意宏 macro_NameGlobalVariable 与 macro_CloseIndicator 在文件尾用 #undef 取消定义,防止污染其他包含单元——这套写法在 260~392 行类定义收尾后紧接着出现,说明全局宏生命周期被严格框在单文件内。 DispatchMessage 函数体在原文里只留了「内部代码下篇见」的占位,说明消息分发逻辑尚未展开,当前节重点就是搞清卸载路径不漏资源。

MQL5 / C++
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)
{
.... The internal code of this procedure will be seen in the next article
}
class=class="str">"cmt">//+------------------------------------------------------------------+
};
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#undef macro_NameGlobalVariable
class="macro">#undef macro_CloseIndicator
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnDeinit(class="kw">const class="type">int reason)
{
  class="kw">switch (reason)
  {
    case REASON_INITFAILED:
      ChartIndicatorDelete(ChartID(), class="num">0, def_ShortName);
      class="kw">break;
    case REASON_CHARTCHANGE:
      (*chart).SaveState();
      class="kw">break;
  }

  class="kw">delete chart;
}

「先把能跑的类收进自己的工具箱」

第 260 行声明、第 386 行实现的 DispatchMessage 过程这次先不拆,它负责把用户和图表上浮动面板的交互事件转成开平仓指令,单独讲太占篇幅,留到和下一篇的另一个项目对照看更清楚。 眼下你能做的实事:把 C_ChartFloatingRAD 类的代码存成独立文件,直接挂进 MT5 图表跑一遍。按 CTRL+B 打开对象列表,只会看到 1 个对象,但图表上那些可点的按钮都是它隐性管理的——这就是 RAD 思路的妙处。 顺手把附件 Anexo.zip(163.48 KB)下下来,里面指标能直接加载交互,配合视频看一遍比读文档快。外汇和贵金属这类品种波动大、杠杆高,跑任何第三方面板前先在模拟账户验一遍事件响应,别拿真仓试未知逻辑。 下一篇会填第 260 到 386 行之间的坑,在那之前,把这套已经能跑的代码摸熟就够了。

常见问题

在面板析构函数里把挂单、画线对象句柄和窗口坐标写入本地缓存,下次初始化时读取并重建,就能还原上次状态。
在 OnDeinit 里遍历并删除自己创建的所有图形对象与事件钩子,再释放类实例,避免 MT5 终端里留下垃圾。
可以,小布能读取你的面板逻辑并标出未释放的资源与未保存的状态分支,给出修改建议。
多半是状态只存在内存没落盘,把配置存到品种级缓存并在 ChartChange 事件里重载即可解决。
常漏了在 OnInit 调构造、OnChartEvent 转发行情事件,确认事件路由和生命周期挂钩才真能用。