开发回放系统(第29部分):EA 交易项目C_Mouse类(三)·综合运用
「用鼠标事件驱动重绘订单簿面板」
下面这段 EA 骨架把深度图渲染完全挂到图表鼠标消息和 500 毫秒定时器上,而不是每笔 tick 去刷屏。OnChartEvent 里收到 CHARTEVENT_MOUSE_MOVE 就调 Draw() 并 ChartRedraw(),意味着你移动鼠标才会重画价格线,CPU 占用倾向更低。 OnInit 中 EventSetMillisecondTimer(500) 设定了半秒级轮询,OnTimer 里只做 (*extra).Update(),即每 500ms 刷新一次附属 study 图层。对比 OnTick() 留空,说明设计者有意避开 tick 高频回调,外汇与贵金属品种在剧烈波动时这种写法可能减少卡顿。 OnBookEvent 根据当前品种是否等于 def_SymbolReplay 决定 book 数组长度:回放品种只开 1 个元素,实盘则走 MarketBookGet 取真实深度。注意 mouse 与 extra 两个指针在 OnDeinit 中必须 delete,否则 MT5 终端退出时倾向报内存泄漏。 代码里 input group "Mouse" 暴露了三根颜色线:user00 黑线做价格基准、user01 浅绿表正向 study、user02 珊瑚红表负向 study,复制时若改了头文件路径 <Market Replay\System EA\Auxiliar\...> 需同步调整 include 才能编译过。
class="kw">switch (id) { case CHARTEVENT_MOUSE_MOVE: Draw(); class="kw">break; } } 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_Studys.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_Studys *extra = NULL; class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { mouse = new C_Mouse(user00, user01, user02); extra = new C_Studys(mouse, user01, user02); OnBookEvent(_Symbol); 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(_Symbol); EventKillTimer(); class="kw">delete extra; class="kw">delete mouse; } class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnTick() {} class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnTimer() { (*extra).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); } (*extra).Update(book); } class=class="str">"cmt">//+------------------------------------------------------------------+ 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); (*extra).DispatchMessage(id, lparam, dparam, sparam); ChartRedraw(); } class=class="str">"cmt">//+------------------------------------------------------------------+ C_Studys(C_Mouse *arg, class="type">color corP, class="type">color corN) { class="macro">#define def_FontName "Lucida Console" class="macro">#define def_FontSize class="num">10 class="type">int x; mouse = arg; ZeroMemory(m_Info);
收盘态下的状态赋值与按钮绘制
这段逻辑跑在日线收盘判定之后,先把面板状态切到 eCloseMarket,再取 D1 收盘价填进结构体。取价时用了一个三元判断:当品种是回放符号,或当前日期与 D1 最新 K 线日期不一致时取 0 号柱,否则取 1 号柱——这能避免在非换日时刻误读昨收。 corP 与 corN 直接透传进 m_Info,随后用 TextSetFont 设字体,高度为 -10 * def_FontSize、字重 FW_NORMAL。接着连续建三个按钮:Closed Market 用淡蓝底色,后两个 99.99% 用透明底,位置靠 x+5 错开。 最后 Draw() 一次性刷新,并用 #undef 撤掉 def_FontSize 与 def_FontName 宏,防止后续编译命名污染。开 MT5 把这段塞进你的 CPanel::Update 尾段,改 def_ExpansionBtn2/3 的文本就能快速验证面板布局。
m_Info.Status = eCloseMarket; m_Info.Rate.close = iClose(def_InfoTerminal.szSymbol, PERIOD_D1, ((def_InfoTerminal.szSymbol == def_SymbolReplay) || (macroGetDate(TimeCurrent()) != macroGetDate(iTime(def_InfoTerminal.szSymbol, PERIOD_D1, class="num">0))) ? class="num">0 : class="num">1)); m_Info.corP = corP; m_Info.corN = corN; TextSetFont(def_FontName, -class="num">10 * def_FontSize, FW_NORMAL); CreateBTNInfo("Closed Market", class="num">2, def_ExpansionBtn1, clrPaleTurquoise, def_FontName, def_FontSize); x = CreateBTNInfo("class="num">99.99%", class="num">2, def_ExpansionBtn2, clrNONE, def_FontName, def_FontSize); CreateBTNInfo("class="num">99.99%", x + class="num">5, def_ExpansionBtn3, clrNONE, def_FontName, def_FontSize); Draw(); class="macro">#undef def_FontSize class="macro">#undef def_FontName }
◍ 收束
把附加的 Market_Replay 压缩包(56.76 KB)解进 MT5 的 MQL5/Files 目录,先在重放模式跑通,再切到 DEMO 账户做实盘环境验证,比只看代码更能摸清撮合与滑点的边界。 主类逻辑作者明确未改一行,意味着你拿到的回放框架是可溯源的;外汇与贵金属品种在高杠杆下波动剧烈,重放结论只代表历史序列概率,不预示未来。 ZIP 里 FUTUROS(11.4 MB)、FOREX(3.7 MB)、BOLSA(1.3 MB)三套样本覆盖不同市场节奏,建议按品种分开回测,避免混仓干扰信号。