一张图表上的多个指标(第 06 部分):将 MetaTrader 5 转变为 RAD 系统(II)·进阶篇
🛠️

一张图表上的多个指标(第 06 部分):将 MetaTrader 5 转变为 RAD 系统(II)·进阶篇

(2/3)· 上一页 IDE 跑通了,但缺了资产名和当日盈亏累计——本篇手把手把这两个坑填平

案例拆解新手友好 第 2/3 篇

不少人在 MT5 里画好交易面板就以为 RAD 系统完工,结果图表上连当前品种和当日盈亏都看不出,决策还要切回市场报价窗口。界面缺了这两个只读字段,所谓可视化交易其实只做了一半。

「给 IDE 对象补一个资产标签位」

往交易面板里加资产名称,本质不是大改,而是把枚举和消息表对齐。最忌讳在枚举头尾直接塞新值:一旦你引用过极值(比如用 eRESULT 或 eEDIT_STOP 做边界判断),漏改一处就会冒出看似新功能引发、实则越界导致的运行错误。 稳妥做法是把 eLABEL_SYMBOL 插在 eRESULT 和 eBTN_BUY 之间,同步在 szMsgIDE 字符串数组的对应位置加 "MSG_NAME_SYMBOL"。这样原边界判断逻辑不受扰,MT5 的 IDE 对象树也能正确识别这个新接收消息的单元。 光加枚举不够,不补消息常量会在运行时报错。把常量放在数组第二行,只是出于结构清晰——它实际只在 DispatchMessage 里被用来决定哪个对象收消息,摆哪都行。 发消息的时机选在对象类 Create 函数末尾最自然:标签文本只需在图表挂上 IDE 时发一次。下一段代码即最终落点,绿色高亮为实际新增。几乎零改动,就跑通了一条资产名消息流。 [CODE] enum eObjectsIDE {eRESULT, eLABEL_SYMBOL, eBTN_BUY, eBTN_SELL, eCHECK_DAYTRADE, eBTN_CANCEL, eEDIT_LEVERAGE, eEDIT_TAKE, eEDIT_STOP}; static const string C_Chart_IDE::szMsgIDE[] = { "MSG_RESULT", "MSG_NAME_SYMBOL", "MSG_BUY_MARKET", "MSG_SELL_MARKET", "MSG_DAY_TRADE", "MSG_CLOSE_POSITION", "MSG_LEVERAGE_VALUE", "MSG_TAKE_VALUE", "MSG_STOP_VALUE" }; void DispatchMessage(int iMsg, string szArg, double dValue = 0.0) { if (m_CountObject < eEDIT_STOP) return; switch (iMsg) { case CHARTEVENT_CHART_CHANGE: if (szArg == szMsgIDE[eRESULT]) { [/CODE]

MQL5 / C++
enum eObjectsIDE {eRESULT, eLABEL_SYMBOL, eBTN_BUY, eBTN_SELL, eCHECK_DAYTRADE, eBTN_CANCEL, eEDIT_LEVERAGE, eEDIT_TAKE, eEDIT_STOP};
class="kw">static class="kw">const class="type">class="kw">string C_Chart_IDE::szMsgIDE[] = {
    "MSG_RESULT",
    "MSG_NAME_SYMBOL",
    "MSG_BUY_MARKET",
    "MSG_SELL_MARKET",
    "MSG_DAY_TRADE",
    "MSG_CLOSE_POSITION",
    "MSG_LEVERAGE_VALUE",
    "MSG_TAKE_VALUE",
    "MSG_STOP_VALUE"
};
class="type">void DispatchMessage(class="type">int iMsg, class="type">class="kw">string szArg, class="type">class="kw">double dValue = class="num">0.0)
{
    if (m_CountObject < eEDIT_STOP) class="kw">return;
    class="kw">switch (iMsg)
    {
        case CHARTEVENT_CHART_CHANGE:
            if (szArg == szMsgIDE[eRESULT])
            {

图表对象随消息刷新颜色与文字

在 MT5 的面板类逻辑里,图表事件驱动是让 HUD 元素活起来的关键。下面这段 DispatchMessage 处理了 CHARTEVENT_CHART_CHANGE,当传入参数 szArg 匹配结果标签的标识时,就按数值正负切换背景色:小于 0 用 clrLightCoral(浅珊瑚红),否则用 clrLightGreen(浅绿),并把数值格式化为两位小数写进文本。 若 szArg 匹配品种标签标识,则把当前图表的交易品种名刷进标签文本,同时设 ALIGN_CENTER 居中对齐。这种写法让交易品种和计算结果在切换图表或品种时自动同步,不用手动重绘。 Create 函数里有个前置闸门:m_CountObject 小于 eEDIT_STOP 时 DispatchMessage 直接 return,意味着面板子对象没建够之前不响应任何消息。另外它尝试以二进制读方式打开 "Chart Trade\IDE.tpl",打不开就返回 false,模板文件路径错一处面板就起不来。 外汇与贵金属波动剧烈,这类面板只做信息呈现,信号对错不代表方向确定性,实盘前请在策略测试器里跑通对象创建与事件回调。

MQL5 / C++
class="type">void DispatchMessage(class="type">int iMsg, class="type">class="kw">string szArg, class="type">class="kw">double dValue = class="num">0.0)
{
   if (m_CountObject < eEDIT_STOP) class="kw">return;
   class="kw">switch (iMsg)
   {
      case CHARTEVENT_CHART_CHANGE:
         if (szArg == szMsgIDE[eRESULT])
         {
            ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[eRESULT].szName, OBJPROP_BGCOLOR, (dValue < class="num">0 ? clrLightCoral : clrLightGreen));
            ObjectSetString(Terminal.Get_ID(), m_ArrObject[eRESULT].szName, OBJPROP_TEXT, DoubleToString(dValue, class="num">2));
         }else if (szArg == szMsgIDE[eLABEL_SYMBOL])
         {
            ObjectSetString(Terminal.Get_ID(), m_ArrObject[eLABEL_SYMBOL].szName, OBJPROP_TEXT, Terminal.GetSymbol());
            ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[eLABEL_SYMBOL].szName, OBJPROP_ALIGN, ALIGN_CENTER);
         }
         class="kw">break;
      case CHARTEVENT_OBJECT_CLICK:
class=class="str">"cmt">// ... The rest of the code
   }
}
class="type">bool Create(class="type">int nSub)
{
   m_CountObject = class="num">0;
   if ((m_fp = FileOpen("Chart Trade\\IDE.tpl", FILE_BIN | FILE_READ)) == INVALID_HANDLE) class="kw">return class="kw">false;

◍ 从模板文件还原图表对象的循环逻辑

这段读取逻辑面向已保存的图表模板文件,先以 SHORT_VALUE 读出一个短整型头字段,随后把 eRESULT 到 eEDIT_STOP 区间内的对象名清空,为后续重载做准备。 主循环以 </chart> 作为终止标记,逐行读取;遇到 <object> 节点后再读一行,若标签为 type 则按数值分支创建图形。type=102 建 OBJ_LABEL,103 建 OBJ_BUTTON,106 建 OBJ_BITMAP_LABEL,107 建 OBJ_EDIT,110 建 OBJ_RECTANGLE_LABEL,任一步 FileReadLine 失败即返回 false。 文件读完用 FileClose 收尾,并派发 CHARTEVENT_CHART_CHANGE 消息触发重绘。在 MT5 里改这几个 type 映射,就能控制模板反序列化时生成哪类控件,外汇与贵金属图表加载自定义面板时尤其要留意事件重绘带来的闪烁风险。

MQL5 / C++
FileReadInteger(m_fp, SHORT_VALUE);
        for (m_CountObject = eRESULT; m_CountObject <= eEDIT_STOP; m_CountObject++) m_ArrObject[m_CountObject].szName = "";
        m_SubWindow = nSub;
        m_szLine = "";
        class="kw">while (m_szLine != "</chart>")
        {
            if (!FileReadLine()) class="kw">return class="kw">false;
            if (m_szLine == "<object>")
            {
                if (!FileReadLine()) class="kw">return class="kw">false;
                if (m_szLine == "type")
                {
                    if (m_szValue == "class="num">102") if (!LoopCreating(OBJ_LABEL)) class="kw">return class="kw">false;
                    if (m_szValue == "class="num">103") if (!LoopCreating(OBJ_BUTTON)) class="kw">return class="kw">false;
                    if (m_szValue == "class="num">106") if (!LoopCreating(OBJ_BITMAP_LABEL)) class="kw">return class="kw">false;
                    if (m_szValue == "class="num">107") if (!LoopCreating(OBJ_EDIT)) class="kw">return class="kw">false;
                    if (m_szValue == "class="num">110") if (!LoopCreating(OBJ_RECTANGLE_LABEL)) class="kw">return class="kw">false;
                }
            }
        }
        FileClose(m_fp);
        DispatchMessage(CHARTEVENT_CHART_CHANGE, szMsgIDE[eLABEL_SYMBOL]);
        class="kw">return true;

「把当日盈亏塞进同一条消息」

给图表 IDE 加当天累计值,逻辑和加资产名一致,但落地位置有讲究。很多人会顺手在 C_Chart_IDE 里写函数,实际为组织清晰,应放进 C_OrderView 类,和订单处理放一起。 枚举里加一个 eROOF_DIARY,消息数组对应补 "MSG_ROOF_DIARY"。这样持仓结果和当天结果用一条消息就能刷新,否则得发两条才能正确覆盖面板。 OnTrade 里调用 UpdateRoof 能感知持仓变动,但它和 OnTick 并发时会拖慢 EA。原 UpdateRoof 每次都跑完整历史扫描,改进后加静态变量 memMax 与 Accumulated:若指定时段无历史订单,两者清零并直接 return;有数据才测,且值跨调用保留。 持仓因价格自然波动触发 OnTrade 时,新 UpdateRoof 发现仓未平就立即回到控制点,少做一次完整扫描。外汇与贵金属杠杆高,这类事件频繁,省下的执行时间对实盘延迟有概率改善。

MQL5 / C++
enum eObjectsIDE {eRESULT, eLABEL_SYMBOL, eROOF_DIARY, eBTN_BUY, eBTN_SELL, eCHECK_DAYTRADE, eBTN_CANCEL, eEDIT_LEVERAGE, eEDIT_TAKE, eEDIT_STOP};
class=class="str">"cmt">// ... Rest of the code
class="kw">static class="kw">const class="type">class="kw">string C_Chart_IDE::szMsgIDE[] = {
                                             "MSG_RESULT",
                                             "MSG_NAME_SYMBOL",
                                             "MSG_ROOF_DIARY",
                                             "MSG_BUY_MARKET",
                                             "MSG_SELL_MARKET",
                                             "MSG_DAY_TRADE",
                                             "MSG_CLOSE_POSITION",
                                             "MSG_LEVERAGE_VALUE",
                                             "MSG_TAKE_VALUE",
                                             "MSG_STOP_VALUE"
                                            };
class="type">class="kw">double UpdateRoof(class="type">void)
{
            class="type">class="kw">ulong   ticket;
            class="type">int     max;
            class="type">class="kw">string  szSymbol = Terminal.GetSymbol();
            class="type">class="kw">double  Accumulated = class="num">0;
}
交给小布盯盘看盘口
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到资产标识与日内浮盈浮亏的实时着色,不必自己写 EDIT 对象对齐代码。

常见问题

它们没有关联任何事件,MetaTrader 5 只在图表上显示而不会响应交互;要生效需在 MQL5 里绑定事件或至少用代码驱动属性更新。
可以,小布的品种页已内置资产名与日累计浮亏浮盈的着色显示,省去手动建只读 EDIT 和对齐文本的代码劳动,你专注策略逻辑即可。
对象属性面板无法直接指定对齐方式,需要用 ENUM_ALIGN_MODE 相关属性在代码里设置居中对齐,否则文字默认靠左。
在更新 EDIT 01 对象时判断累计值符号,分别调用对象颜色属性设置函数,负值用警示色、正值用常规盈利色,刷新事件绑在日线闭合或定时检查上。