开发回放系统(第 48 部分):了解服务的概念·进阶篇
📘

开发回放系统(第 48 部分):了解服务的概念·进阶篇

第 2/2 篇

用服务锁住图表上的指标

用预设模板统一 MT5 图表风格,最怕手滑删了某个指标或改了参数。与其事后一个个恢复,不如写个后台服务,让特定指标在终端里删不掉、关不掉。 下面这段服务代码跑起来后,会每秒扫 4 次(循环间隔 250 毫秒)所有已打开图表,发现缺了 9 周期 EMA 就自动补回去。对外汇、贵金属这类高波动品种,图表上控盘均线突然消失可能让你误判节奏,这种自动兜底能降低人为操作风险。 代码里第 13 行用 while (!_StopFlag) 做主循环,保证服务收到停止信号才退。第 15 行 ChartFirst() 拿终端里第一个窗口 ID,存进 id;之后第 16 行内层循环靠 ChartNext(id) 顺着窗口链往下走,直到返回 -1 才停。 第 18 行判断当前图表第一个指标名是否等于 "MA(9)",不等就在第 20 行建 iMA 句柄(周期 9、EMA、收盘价),第 21 行 ChartIndicatorAdd 贴回图表,第 22 行 IndicatorRelease 释放句柄。哪怕你当场删了线,服务下一轮扫描就会把它加回来。 别把正态当圣经 这套逻辑只保证「指标在」,不保证「参数对你当前品种合适」。9 周期 EMA 是示例值,黄金 5 分钟和欧美日线该用哪条均线,得自己回测定。

MQL5 / C++
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="type">void OnStart()
{
   class="type">long id;
   class="type">class="kw">string szSymbol;
   class="type">int handle;
   
   Print("Waiting for the chart of the first asset to be opened...");
   while (!_StopFlag)
   {
      while (((id = ChartFirst()) < class="num">0) && (!_StopFlag));
      while ((id > class="num">0) && (!_StopFlag))
      {
         if (ChartIndicatorName(id, class="num">0, class="num">0) != "MA(class="num">9)")
         {
            handle = iMA(szSymbol = ChartSymbol(id), ChartPeriod(id), class="num">9, class="num">0, MODE_EMA, PRICE_CLOSE);
            ChartIndicatorAdd(id, class="num">0, handle);
            IndicatorRelease(handle);
         }
         id = ChartNext(id);
      }
   }
   Print("Service removed");
}

「用 Sleep 控制服务脚本的轮询节奏」

上面这段代码是一个 MQL5 服务脚本的收尾片段,核心在循环里用 Sleep(250) 让线程暂停 250 毫秒再进入下一轮。对于常驻型 EA 或指标服务,不加 Sleep 会让 CPU 在空转循环里吃满,MT5 终端容易卡顿甚至被券商断连。 把 250 这个值调小到 50,轮询更密、对 tick 响应更快,但日志和对象访问频率会翻倍;调大到 1000 则更省资源,适合只做分钟级检查的场景。外汇与贵金属杠杆高,脚本卡死可能漏掉止损触发,实盘前务必在策略测试器里跑一遍观察资源占用。 Print("Service finish..."); 在循环退出后输出,说明服务收到了停止信号并正常收尾,和终端里手动移除脚本的日志能对上。

MQL5 / C++
class="num">26.       Sleep(class="num">250);
class="num">27.   }
class="num">28.   Print("Service finish...");
class="num">29. }
class="num">30. class=class="str">"cmt">//+------------------------------------------------------------------+

◍ 用服务锁死图表配置

MT5 服务(service)能把图表状态变成终端服务配置的一部分,而不是依赖模板。好处很直接:你给 WDO(巴西 B3 的迷你美元期货)挂好随机振荡指标后,哪怕手动删掉,服务也会立刻把它加回来,配置不会被意外改掉。 代码里第 19、20 行先抓图表的交易品种名和周期,塞进 szSymbol 与 tf 两个变量。第 21 行做双重判断:品种名前三个字母是不是 WDO,以及图表里有没有叫 "Stoch(8,3,3)" 的指标子窗口。两个条件同时满足才往下走。 用 StringSubstr 只比前 3 个字母,是因为期货每个月换合约(WDOZ24、WDOF25 之类),通用前缀能覆盖全部到期版本,不用月月改代码。指标名必须写短名称(short name),拿不准就先手动挂指标,用个小脚本把 ChartWindowFind 能识别的字符串打印出来对照。 第 23 行用 iStochastic(szSymbol, tf, 8, 3, 3, MODE_SMA, STO_CLOSECLOSE) 建句柄,第 24 行 ChartIndicatorAdd 挂到 ChartGetInteger(id, CHART_WINDOWS_TOTAL) 返回的新子窗口——也就是 MT5 自动开一个干净窗口放指标,不会塞进你已有的别的子窗口造成错位。第 25 行 IndicatorRelease 释放句柄,之后服务只观察不再干预。 原文第 27 行附近按索引查指标在多个指标同图时容易误判,实际改成按名称查更稳:无论指标在第几个窗口,都只保证服务放的那一个存在。外汇和贵金属市场杠杆高、滑点大,这类自动重挂逻辑先在模拟盘验证再上实盘。

MQL5 / C++
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="type">void OnStart()
{
   class="type">long id;
   class="type">class="kw">string szSymbol;
   class="type">int handle;
   ENUM_TIMEFRAMES tf;
   
   Print("Waiting for the chart of the first asset to be opened...");
   while (!_StopFlag)
   {
      while (((id = ChartFirst()) < class="num">0) && (!_StopFlag));
      while ((id > class="num">0) && (!_StopFlag))
      {
         szSymbol = ChartSymbol(id);
         tf = ChartPeriod(id);
         if ((StringSubstr(szSymbol, class="num">0, class="num">3) == "WDO") && (ChartWindowFind(id, "Stoch(class="num">8,class="num">3,class="num">3)") < class="num">0))
         {
            handle = iStochastic(szSymbol, tf, class="num">8, class="num">3, class="num">3, MODE_SMA, STO_CLOSECLOSE);
            ChartIndicatorAdd(id, (class="type">int)ChartGetInteger(id, CHART_WINDOWS_TOTAL), handle);
            IndicatorRelease(handle);
         }

遍历图表自动补齐 9 周期均线

这段逻辑跑在一个常驻服务脚本里,目标是把当前终端所有图表都挂上一条 9 周期 EMA(收盘价)。外层用 ChartNext 不断拿下一个图表 ID,直到返回 0 结束遍历。 判断某图表是否已有该指标,有两种写法:其一是取第 0 号指标名比对是否为 "MA(9)";其二是直接用 ChartIndicatorGet 按名称查句柄,若返回 INVALID_HANDLE 就说明没挂。后者更稳,不会因为指标排序变化而漏判。 确认缺失后,iMA 以 szSymbol、tf、9、0、MODE_EMA、PRICE_CLOSE 创建句柄,ChartIndicatorAdd 把它加到图表主窗口,随后 IndicatorRelease 释放本地引用计数——句柄仍由图表持有,不会失效。每次循环尾部的 Sleep(250) 让出 250 毫秒 CPU,避免扫太多图表时卡死终端。 外汇与贵金属品种波动快,这类自动挂指标脚本仅作辅助观察,不代表任何方向判断;实盘前务必在策略测试器或模拟盘验证遍历逻辑不会重复添加。

MQL5 / C++
if(ChartIndicatorName(id, class="num">0, class="num">0) != "MA(class="num">9)")
  {
   handle = iMA(szSymbol, tf, class="num">9, class="num">0, MODE_EMA, PRICE_CLOSE);
   ChartIndicatorAdd(id, class="num">0, handle);
   IndicatorRelease(handle);
  }
id = ChartNext(id);
}
Sleep(class="num">250);
}
Print("Service finish...");
}
class=class="str">"cmt">//+------------------------------------------------------------------+
if(ChartIndicatorGet(id, class="num">0, "MA(class="num">9)") == INVALID_HANDLE)

「一点提醒」

MT5 的回放与自定义符号能力很少被用透,有读者在讨论区提到用真实账户拉取 GBPUSD 脱欧前后各一天的历史,以重播模式灌进 repGBPUSD 这类自定义品种,再挂 RSI(14)、MA(50) 做事件重演,这类玩法值得自己搭一遍。 学习别停在看文章,独立把数据库、套接字、重播这几块拼起来才有进展;碰到瓶颈就找更熟的人碰,而不是重复造轮子。 外汇和贵金属波动剧烈、杠杆风险高,任何回放验证都只是训练手段,别当成实盘依据。

常见问题

用后台服务持有指标句柄并持续监测,一旦发现被移除就立即重新创建,配置写在服务里比写在脚本里更稳。
在轮询循环里加 Sleep 控制节奏,比如每次检查后歇 500 毫秒,既能及时补回配置又不占满 CPU。
小布可接管这类重复盯盘活,自动巡检图表并在指标缺失或参数被改时提醒你,你只管交易决策。
写个遍历图表的循环,对每张图检查是否已有该均线,没有就按统一参数挂上,省去手动挨个加。
会,服务发现变动就复原,所以临时调整前先停服务,改完再启,避免刚改完又被锁回去。