开发回放系统 市场模拟(第 05 部分):加入预览(基础篇)
「给回放系统加一个预览窗口」
在 MT5 里做市场回放模拟,前几篇已经能把历史报价按节奏推出来。这一节要补的是预览:在正式回放前,先把一段行情缩略地跑一遍,确认数据区间和速度没选错。 不加预览直接全量回放,很容易在 1 分钟 K 线里卡半小时才发现选了错误品种。预览的本质是用极低成本先验证输入参数。 实现上,预览和正式回放共用同一套 OnTick 驱动逻辑,只是把总帧数砍到原来的 1/20 左右,并在图表左上角用 Comment() 打出当前模拟时间戳。读者打开 MT5 新建 EA,把下面这段接进主循环之前跑一次即可。
为什么裸回放给不出可用信号
上一篇文章里我们搭了一套市场回放系统,每根 1 分钟柱线在一分钟内生成,初步能跑通模拟。但很快发现,这种只从“当下”开始吐数据的回放,没法对更早的分钟、小时或天数做预分析。 所谓预分析,是指系统从某个时间点开始生成柱线时,前面应该已经存在历史柱线。没有这些早期柱线,任何指标都拿不到可靠输入。 举个具体例子:哪怕只用 3 周期移动平均线(JOE DI NAPOLI 系统采用的那种),也得等至少 3 根柱线生成后,均线才会画出来。在只含某一天逐笔跳价的文件里,从开盘起算,前 3 分钟图表上根本没有任何均线信号,这段时间里系统完全不可操作。 如果放到 5 分钟周期做研究,得干等 15 分钟才能看到 3 周期均线,再往后才有勉强能用的信号。回放系统必须补上历史预载,否则实战验证价值很低,外汇与贵金属这类高波动品种上误判风险尤其大。 本文接下来要解决的,就是怎么给回放系统接上这段缺失的历史数据。
◍ 回放前到底要备多少根历史柱
做市场回放前先别急着写代码,第一个要拍板的是:用哪个图表周期,以及每个指标吐出有用信号最少要吃多少根柱。这没有标准模型,完全看你拿回放来干什么。把这件事想清楚,后面不只能做回放,还能顺手搭一个市场模拟器——两者唯一实质区别是数据源,回放吃真实 TICKS/BARS,模拟器靠数学合成或纯随机生成。 判定回放启动前最少要预载多少根 1 分钟柱,公式很直白:N = P × T + T。P 是指标里最长的那个周期参数,T 是你要用的图表周期分钟数。比如 5 分钟图上跑 200 周期均线,N = 200×5 + 5 = 1005,也就是回放开始前硬盘里至少得有 1005 根 1 分钟柱,均线才能从第一帧就画出来。 复杂点的情况:5 分和 15 分图上研究 21/50 周期双均线加 14 周期 RSI 和日内 VWAP,偶尔切 30 分图确认进出场。最大指标周期是 50,VWAP 与时间无关不用管;时间帧取最长的 30 分。N = 50×30 + 30 = 1530 根 1 分钟柱。这数看着吓人,但比极端情形还差得远,核心思路是:历史数据量永远要比你直觉里“够用”多一大截。 外汇贵金属高风险,B3 这类交易所还分交易时段,美元期货 9:00 开盘 18:30 收盘,单日 1 分钟柱数 = (18-9)×60 + 30 = 570 根。把所需总柱数除以每日平均柱数,就得到最短要捕多少天的文件。之所以死磕 1 分钟柱,是因为 MT5 能据此任意合成其它周期,换用 15 分柱就失去平台原生支持了。 代码里第一处改动是指定历史数据文件:input string user00 装前期柱线、user01 装回放跳价。服务启动后依次 InitSymbolReplay 建品种、LoadPrevBars 载旧柱、LoadTicksReplay 载跳价,打印等待授权,靠 GlobalVariableCheck 轮询控制指标是否挂上。下面这段是核心骨架,逐行拆完你就能在 MT5 里照抄验证。
class="macro">#class="kw">property service class="macro">#class="kw">property copyright "Daniel Jose" class="macro">#class="kw">property version "class="num">1.00" class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#include <Market Replay\C_Replay.mqh> class=class="str">"cmt">//+------------------------------------------------------------------+ input class="type">class="kw">string user00 = "WIN$N_M1_202108030900_202108051754"; class=class="str">"cmt">//File with bars( Prev. ) input class="type">class="kw">string user01 = "WINQ21_202108060900_202108061759"; class=class="str">"cmt">//File with ticks( Replay ) class=class="str">"cmt">//+------------------------------------------------------------------+ C_Replay Replay; class="type">void OnStart() { class="type">ulong t1; class="type">int delay = class="num">3; class="type">long id; u_Interprocess Info; class="type">bool bTest = false; Replay.InitSymbolReplay(); Replay.LoadPrevBars(user00); if (!Replay.LoadTicksReplay(user01)) class="kw">return; Print("Aguardando permissão para iniciar replay ..."); id = Replay.ViewReplay(); while (!GlobalVariableCheck(def_GlobalVariableReplay)) Sleep(class="num">750);
「拿到回放许可后的计时起点」
在 MT5 终端里调用回放服务前,先确认权限已放行。代码里用 Print 输出一条许可确认信息,这条日志只起标记作用,不影响后续逻辑,但能让你在专家日志里快速定位回放会话的起始位置。 紧接着用 GetTickCount64 给变量 t1 赋值,拿到的是系统启动后到当前的毫秒数(64 位无符号),精度足够区分同秒内多次回放触发。开 MT5 把这段塞进 OnStart,跑一次就能在日志看到 'Permission granted' 与 t1 的具体数值,方便后续和回放结束时的计时做差。 外汇与贵金属品种的历史回放涉及高频 tick 重演,属于高杠杆高风险环境,计时差值仅反映本地执行耗时,不预示任何行情走向。
Print("Permission granted. Replay service can now be used..."); t1 = GetTickCount64();