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

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

第 3/3 篇

◍ 强制重绘的调用位置

MQL5 里图表对象画完之后不会自动刷新,必须显式调用 ChartRedraw() 才会把缓冲里的图形推到界面。上面这段代码出现在自定义指标或 EA 的绘图逻辑末尾,行号 408 直接调用,409 闭合函数体,410 是文件分段注释尾。 如果你在 OnCalculate 里批量画了几十根水平线却看不到,十有八九是漏了这一次重绘。实盘贵金属波动快,漏掉可能让你对着旧图做决策,属于高风险下的低级失误。 验证方法很简单:在 MT5 策略测试器里加载一段带图形输出的脚本,注释掉 ChartRedraw() 看图表是否静止,再放开对比刷新延迟。

MQL5 / C++
class="num">408.        ChartRedraw();
class="num">409.    }
class="num">410. class=class="str">"cmt">//+------------------------------------------------------------------+

定长还是变长:消息切分的坑

把二进制数值转成字符串再发,本质是用带宽换准确率。比如二进制 0001 0011 要发成两个字符“19”,多耗一倍字节,但接收方不会解错。消息协议第一关就是:单字符串里怎么标出每段数据的起止。 最直白的办法是固定长度数组拼起来。索引好算,第 N 块永远从第 N×块大小开始;缺点是没用完的空位也占传输量。原文示意图里就有一个蓝色 NULL 收尾的空数组块,纯浪费内存和带宽。 换成可变长度数组能省空间,但发送方和接收方得额外约定边界,代码量上去,测试成本也上去。更麻烦的是:若某字段预期 2 字节却塞进 3 字节,多出的那个字节会顶掉后续组,接收方按旧预期解,整条消息就废了。 折中方案是只用 32–127 的可见字符传数据(资产名、杠杆、TP、SL 这类纯字母数字加小数点足够),把 128–255 甚至标点符号留作分隔符。这样变长块能随便扩,靠分隔符切段,EA 与图表端跨终端通信也不会因为长度漂移而失联。外汇/贵金属消息误解析可能直接触发错单,实盘前务必在 MT5 用两终端互发做边界压测。

「混用定长与变长:Chart Trade 的通信协议怎么拼」

Chart Trade 的发送协议走的是折中路线:用字母数字串传数据,中间插分隔符,同时保留索引能力。每个字段占多少字符由内容决定,不强行补齐,所以一行里的块大小可以不一样,接收方仍按既定顺序解析。 真实报文里第一个块是单个字符 D,前后都是同一种分隔符,说明这个位置被当成可索引标记。D 代表当日平仓类操作;若不是日内交易,这里会换成 S,接收方代码也得同步改,否则解析会断。 紧跟着的块是资产名,示例里是 BOVA11(五字符 ETF 符号)。不同市场符号长度不一,B3 交易所里既有四位也有五位,回放模式下名字长度更随意,所以变长块是刚需。 再往后是文字值 250,对应第 338 行里要设的杠杆。这里用三个数字字符而不是二进制,坑在分隔符:问号 ASCII 码是 63,任何含 63 的二进制杠杆值都会被接收方误判成块边界。即便给杠杆加 63 偏移,每 255 个计数仍会撞上 63 复合值——因为 MQL5 字符串按 C/C++ 的 ASCII 处理,StringFormat 不碰 Unicode,字符上限就是 2^8=255。 [CODE] // 第 338 行附近逻辑(示意,源于原文描述) // ushort action = (ushort)evChartTradeBuy; // 枚举第9项 = 9 // string symbol = "BOVA11"; // char sess = 'D'; // 日内平仓标记 // string lever = "250"; // double pts = 3.60; // 已转点数,对应 900 美元 // string msg = action + "?" + symbol + "?" + sess + "?" + lever + "?" + DoubleToString(pts); [/CODE] 报文尾部两类数值按 double/float 写成了文字,原因和杠杆一样——避开 63 冲突。它们看起来怪,是因为第 338 行把金额先转成了点数:3.60 即 900 美元,3.02 即 755 美元。EA 端直接吃点数比内部再算一次转换省事,具体好处后面章节跑代码时你能看出来。 枚举值不是拍脑袋:Defines.mqh 里 EnumEvents 从 evHideMouse=0 起算,第 9 个是 evChartTradeBuy、第 11 个是 evChartTradeCloseAll,所以买用 9、全平用 11。改协议前先去头文件核对枚举顺序,不然 EA 收到的动作类型会错位。外汇与贵金属品种叠加这类自定义协议时波动剧烈,实盘前务必在 MT5 策略测试器用模拟报文跑通。

MQL5 / C++
class=class="str">"cmt">// 第 class="num">338 行附近逻辑(示意,源于原文描述)
class=class="str">"cmt">// class="type">class="kw">ushort action = (class="type">class="kw">ushort)evChartTradeBuy; // 枚举第9项 = class="num">9
class=class="str">"cmt">// class="type">class="kw">string symbol = "BOVA11";
class=class="str">"cmt">// class="type">char sess = &class="macro">#x27;D&class="macro">#x27;; // 日内平仓标记
class=class="str">"cmt">// class="type">class="kw">string lever = "class="num">250";
class=class="str">"cmt">// class="type">class="kw">double pts = class="num">3.60; // 已转点数,对应 class="num">900 美元
class=class="str">"cmt">// class="type">class="kw">string msg = action + "?" + symbol + "?" + sess + "?" + lever + "?" + DoubleToString(pts);

◍ 先把协议设计钉死再写接收端

通信协议最怕临时补窟窿。本文只讲了发送侧的结构化封装,接收端(EA 本体)留到下一篇,但协议字段顺序、分隔符、校验方式现在不定稿,后面联调时信息错位几乎必然发生。 外汇与贵金属行情走得快、重连频繁,协议若没提前规划,断线恢复后 EA 可能读到半截指令而误操作,这类风险在实盘里代价很高。 所以别等接收模块动工才反推协议。拿本文给出的字段设计在 MT5 脚本里先发几条模拟消息,确认结构稳定,再往下写 EA 的监听逻辑。

常见问题

应放在消息处理完毕后的统一收尾处,而非每次局部更新都调用,避免重复触发导致闪烁和延迟。
混用会导致接收端解析错位、丢包或死循环,必须先在协议层钉死一种切分规则再写代码。
可以,把协议设计和小节逻辑发给小布,它能快速指出定长/变长混用、字段缺失等常见拼装错误。
因为没先钉死协议设计,正确顺序是先定消息结构、切分方式与字段含义,再写收发两端。
在协议头写明后续字节数或用特定分隔符收尾,接收端按约定动态读取,不要硬套定长缓冲。