同步多个相同交易品种而时段不同的图表(基础篇)
同品种多周期图表如何保持联动
在 MT5 里经常遇到一个场景:同一个交易品种,你开了 M5、H1、D1 三张图,想看价格在不同周期上的同步反应,但默认情况下这几张图各看各的,切换品种时也不会一起跳。 手动逐个改符号效率低,且容易漏掉某张图,导致分析时周期视角错位。Dmitriy Gizlyk 在 2018-06-12 发布的该示例帖获得 8962 次查看、30 条讨论,说明多图同步是实盘中的高频痛点。 解决思路是用一段 MQL5 脚本监听当前激活图表的品种,再把符号名广播给同品种的其他周期图表,让它们自动跟随。下面这段代码演示了基础实现逻辑。
class="type">int OnInit() { EventSetSymbol(GetSymbolName()); class="kw">return(INIT_SUCCEEDED); } class="type">void OnSymbolChange(class="kw">const class="type">class="kw">string symbol) { class="type">long chartIDs[]; class="type">int total=ChartGetInteger(class="num">0,CHART_WINDOWS_TOTAL); for(class="type">int i=class="num">0;i<total;i++) { class="type">long id=ChartId(); if(ChartSymbol(id)==symbol) ChartSetSymbolPeriod(id,symbol,ChartPeriod(id)); } }
◍ 跨周期盯盘的对象同步痛点
老手都习惯先看高周期定方向,再切低周期找入场,但手动切周期时,画好的趋势线、框选区域常常跟着变或丢失。MT5 本身支持单图表内改周期保留对象,可一旦要同时盯多个品种或多个周期,麻烦就来了。 最原始的办法是用模板:改一根线,得重新存模板,再挨个图表应用一遍。这种重复操作在盯盘黄金和欧美的联动时尤其耗神,且外汇和贵金属波动快,手动同步容易漏掉关键位。 本文的思路是把同步逻辑塞进一个指标里,让多图表的对象改动自动广播,而不是人肉搬模板。这类自动化对跨周期价格行为分析有直接帮助,但外汇贵金属属高风险品类,任何工具都不消除方向误判的概率。
「用事件驱动避开指标同步的两个坑」
要让 MT5 里多图表共享同一套图形对象,核心任务很直接:指定一个交易品种,把它的对象变化实时搬到其他同品种图表上。但跟踪方式选错,指标就会变成终端负担。 第一种思路是定时轮询,按固定周期扫一遍所有图的对象再同步。优点是每个图表只需一个实例,但有两个硬伤:轮询间隔天然带来同步延迟;你没法干净地判断哪个图表里的对象才是最新真相。对象一多,每次循环耗时和存储量都线性膨胀,而指标跑在 MT5 总流程里,过重会拖垮同终端其他指标的整体效率。 第二种走终端事件,在 OnChartEvent 里当场处理创建和修改。对象一动就立刻反应,延迟压到最低,也不用存全量状态、不用定时全扫,负载明显更轻。代价是 OnChartEvent 只在指标所在图表触发——想自由在任何图表改对象,就得每图都挂实例,人工或靠 ChartIndicatorAdd 自动都行。 我选了事件驱动来实现。指标启动后先按交易品种归类已开图表,查重并把当前图对象克隆进目标图;之后只处理图表事件,把改动对象的数据推给已建好的图表列表。运行时图表会开关,所以用定时器隔一阵重跑一次初始化过程来维持列表准确。外汇与贵金属图表多开时终端负载波动大,实盘前请在模拟环境验证定时器间隔。
把指标克隆到多图表上跑
做跨周期或多品种联动时,最麻烦的是把同一个指标手动挂到一堆图表上。写个 CCloneIndy 类,把品种、指标名、调用路径存进私有变量,初始化时顺手用 MQL_PROGRAM_PATH 截出 Indicators 之后的相对路径,并给指标设个短名,后面靠这个短名去目标图表抓参数。 SearchCharts 是公有的入口:传入源图表 ID,返回选中图表的 ID 数组。ID 为 -1 直接 false;为 0 就取当前 ChartID();若保存的品种和源图表不一致,就重写 s_Symbol 对齐。接着用 ChartFirst/ChartNext 遍历所有打开图表,跳过自身 ID 和品种不匹配的,命中后看指标在不在,不在就调 AddIndicator 挂上,成功才把 ID 写进数组。 AddIndicator 不能直接拿主图表句柄去贴,因为主图和接收图的品种或时段可能不同——我们任务里时段本来就是故意错开的。所以要先取主图指标句柄,拿到参数后,在接收图 ID 和对应时段上重新 iCustom 调用,失败就返回 false,成功再把对象克隆过去。 下面这段是类的骨架和 SearchCharts 前半部分的真实代码,注意 case -1 的提前返回和 s_Symbol 的重写逻辑,都是为了防止后面遍历时错位。外汇与贵金属图表切换频繁,这类自动克隆在实盘前务必在 MT5 策略测试器外先手测一遍,品种不匹配可能导致指标静默失败。
class CCloneIndy { class="kw">private: class="type">class="kw">string s_Symbol; class="type">class="kw">string s_IndyName; class="type">class="kw">string s_IndyPath; class="kw">public: CCloneIndy(); ~CCloneIndy(); class="type">bool SearchCharts(class="type">long chart,class="type">long &charts[]); class="kw">protected: class="type">bool AddChartToArray(class="kw">const class="type">long chart,class="type">long &charts[]); class="type">bool AddIndicator(class="kw">const class="type">long master_chart,class="kw">const class="type">long slave_chart); }; CCloneIndy::CCloneIndy() { s_Symbol=_Symbol; s_IndyName=MQLInfoString(MQL_PROGRAM_NAME); s_IndyPath=MQLInfoString(MQL_PROGRAM_PATH); class="type">int pos=StringFind(s_IndyPath,"\\Indicators\\",class="num">0); if(pos>=class="num">0) { pos+=class="num">12; s_IndyPath=StringSubstr(s_IndyPath,pos); } IndicatorSetString(INDICATOR_SHORTNAME,s_IndyName); } class="type">bool CCloneIndy::SearchCharts(class="type">long chart,class="type">long &charts[]) { class="kw">switch((class="type">int)chart) { case -class="num">1: class="kw">return class="kw">false; class="kw">break; case class="num">0: chart=ChartID(); class="kw">break; class="kw">default: if(s_Symbol!=ChartSymbol(chart)) s_Symbol=ChartSymbol(chart); class="kw">break; } class="type">long check_chart=ChartFirst(); class="kw">while(check_chart!=-class="num">1) { if(check_chart==chart) { check_chart=ChartNext(check_chart); class="kw">continue; } if(ChartSymbol(check_chart)!=s_Symbol) { check_chart=ChartNext(check_chart); class="kw">continue; }