开发回放系统 市场模拟(第 07 部分):首次改进(II)·综合运用
(3/3)·从指标驻留到退出拦截,把前两篇埋下的不稳定隐患逐一堵死
很多交易者在市场开放时跑回放练手,却没意识到控制指标一旦被误删,回放服务可能干扰平台正常功能。本篇接上篇,继续把这类细微崩溃点逐个消除,让系统更接近可实盘伴随的状态。
回放指标的事件接管与防重入初始化
在 MT5 里做行情回放工具,第一步是把图表事件开关打开。ChartSetInteger 对 CHART_EVENT_MOUSE_MOVE 和 CHART_EVENT_OBJECT_DELETE 置 true 后,鼠标位移和对象删除才会触发 OnChartEvent,否则你的播放按钮点了没反应。 CHARTEVENT_OBJECT_CLICK 分支里靠 sparam 比对按钮名来分流:点播放键读 OBJPROP_STATE 拿到布尔状态,暂停时 CreteCtrlSlider 拉出进度滑条,播放时 RemoveCtrlSlider 并清空 szBtnPin。左右步进按钮直接对 posPinSlider 做 ±1 位移,这个粒度就是单根 K 线步进。 OnInit 用全局变量 def_GlobalVariableReplay 做跨实例握手。若已存在且 IsUsing 为真,说明另一副本在跑,ChartIndicatorDelete 自杀并返回 INIT_FAILED,避免两个回放实例抢同一图表。外汇与贵金属波动剧烈,这类自管全局态的工具在跳空时段可能丢事件,上机前先在模拟盘验证。 初始化末尾 Control.Init(isPlay) 把状态交给控制器,返回 INIT_SUCCEEDED 才算挂载成功。全局变量首次不存在时 Info.Value 清零,保证新会话从 0 帧开始。
ChartSetInteger(m_id, CHART_EVENT_MOUSE_MOVE, true); ChartSetInteger(m_id, CHART_EVENT_OBJECT_DELETE, true); CreateBtnPlayPause(state); GlobalVariableTemp(def_GlobalVariableReplay); if (!state) CreteCtrlSlider(); ChartRedraw(); Info.Value = GlobalVariableGet(def_GlobalVariableReplay); Info.s_Infos.IsUsing = true; GlobalVariableSet(def_GlobalVariableReplay, Info.Value); } case CHARTEVENT_OBJECT_CLICK: if (sparam == m_szBtnPlay) { Info.s_Infos.isPlay =(class="type">bool) ObjectGetInteger(m_id, m_szBtnPlay, OBJPROP_STATE); if (!Info.s_Infos.isPlay) CreteCtrlSlider(); else { RemoveCtrlSlider(); m_Slider.szBtnPin = NULL; } Info.s_Infos.IsUsing = true; Info.s_Infos.iPosShift = m_Slider.posPinSlider; GlobalVariableSet(def_GlobalVariableReplay, Info.Value); ChartRedraw(); }else if (sparam == m_Slider.szBtnLeft) PositionPinSlider(m_Slider.posPinSlider - class="num">1); else if (sparam == m_Slider.szBtnRight) PositionPinSlider(m_Slider.posPinSlider + class="num">1); class="kw">break; class="type">int OnInit() { class="macro">#define def_ShortName "Market Replay" u_Interprocess Info; IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName); if(GlobalVariableCheck(def_GlobalVariableReplay)) { Info.Value = GlobalVariableGet(def_GlobalVariableReplay); if (Info.s_Infos.IsUsing) { ChartIndicatorDelete(ChartID(), class="num">0, def_ShortName); class="kw">return INIT_FAILED; } } else Info.Value = class="num">0; Control.Init(Info.s_Infos.isPlay); class="kw">return INIT_SUCCEEDED; class="macro">#undef def_ShortName } class="type">void OnDeinit(class="kw">const class="type">int reason) { u_Interprocess Info;
「用全局变量接管图表重绘与销毁」
在 EA 的 OnChartEvent 回调里,图表属性变动(REASON_CHARTCHANGE)不该触发重播逻辑重置。下面这段处理把回放状态从全局变量里读出来,避免界面抖动把历史重播进度洗掉。 当检测到 def_GlobalVariableReplay 这个全局变量存在时,直接把它的数值赋给 Info.Value,并把 IsUsing 标记置为 false,再原样写回全局变量。这样即便 MT5 图表被缩放、拖动导致 CHARTCHANGE 事件频发,回放游标也不会归零。 图表被移除或关闭(REASON_REMOVE / REASON_CHARTCLOSE)时,必须先 GlobalVariableDel 清掉回放全局变量,再 ChartClose 关掉当前图表 ID。顺序反了可能在终端残留死全局变量,下次加载 EA 会读到脏数据。 外汇与贵金属品种波动剧烈,这类全局变量回放机制仅用于本地复盘验证,实盘加载前请在策略测试器跑通事件分支,高风险品种勿直接挂真仓。
class="kw">switch (reason) { case REASON_CHARTCHANGE: if(GlobalVariableCheck(def_GlobalVariableReplay)) { Info.Value = GlobalVariableGet(def_GlobalVariableReplay); Info.s_Infos.IsUsing = false; GlobalVariableSet(def_GlobalVariableReplay, Info.Value); } class="kw">break; case REASON_REMOVE: case REASON_CHARTCLOSE: GlobalVariableDel(def_GlobalVariableReplay); ChartClose(ChartID()); class="kw">break; }
◍ 记住这一条就够了
这套用控制指标约束回放系统的思路,能挡掉一部分因指标滥用导致的异常,但覆盖不了全部故障类型,实际跑起来仍可能撞上更隐蔽的问题。 视频里暴露出的那个比表面更复杂的坑,大概率和指标状态在重放节奏下的同步有关,建议直接解压 Market_Replay.zip(约 12.8 MB)在 MT5 里单步复现。 外汇与贵金属回放测试本身高风险,结论只作技术验证参考,别当成实盘依据。