多品种价格偏离指标开发:从零搭建跨图表分析框架(基础篇)
📊

多品种价格偏离指标开发:从零搭建跨图表分析框架(基础篇)

(1/3)· 面对 1500 行指标代码无从下手?本篇拆开多品种偏离分析的项目结构与事件处理基础

含代码示例实战向 第 1/3 篇

不少交易者在 MT5 里想同时比对多个品种的价格偏离,却卡在图表拖拽事件和缓存区配色上。直接抄旧版多货币指标往往跑不通,因为新需求要处理对象拖拽与图表属性变更。先把工程拆成三个 include 文件,比硬啃主文件更省事。

◍ 用多品种指标抓跨市场偏离

在 MT5 里做价格行为分析,单看一个品种容易漏掉关联市场的异常。把多个品种拉到同一指标窗口,能直观比对它们的相对强弱和偏离程度。 Anatoli Kazharski 在 2014 年 6 月发布的示例显示,这类多品种指标在发布后获得 2417 次查看与 21 条互动,说明跨品种比对是实盘前值得验证的思路。外汇与贵金属跨品种偏离受流动性与突发事件影响,高风险,偏离回归只是概率倾向,不是必然。 打开 MT5 新建自定义指标,把 Symbol() 参数改成可配置的字符串数组,就能在同一副图叠加 EURUSD、XAUUSD 等品种的标准化价格曲线。先跑一遍历史数据,看偏离超过 2 个标准差时后续修复倾向,再决定要不要接警报。

「多品种偏离指标的新架构与事件处理」

这一篇只啃增量:在已有的多品种波动分析框架上,重做一套能分析指定周期价格偏离的指标。老文章讲过基础编程套路,新人先去补那块,本文不重复造轮子。 新指标要落地的几个硬点:改图表属性;响应 CHARTEVENT_OBJECT_DRAG(拖拽对象)和 CHARTEVENT_CHART_CHANGE(属性框缩放/改属性);用多色渲染指标缓存区;在可视区内抓缓存区最高最低来重设图表上下轨;以及序列翻转。 代码量约 1500 行,全塞一个文件没法维护,拆成三个 .mqh 外挂:Checks.mqh 管检查和数据下载,Objects.mqh 管图形对象,Chart.mqh 管图表属性。主文件只留归类不进的杂函数,用 #include 链进来。 外汇与贵金属品种波动剧烈、杠杆风险高,多品种同屏偏离只是概率性参考,别当方向指令。

多品种价格叠加指标的缓冲区与绘制架构

在 MT5 里做多品种叠加指标,第一步是在 MQL5\Indicators 下建同名文件夹,主文件用 *.mq5 或向导模板生成;除 OnInit / OnDeinit / OnCalculate 外,本例还挂了 OnChartEvent 与 OnTimer 来处理拖拽垂直线和定时刷新。 只画单色折线时,缓冲区数量等于品种数就够了;但本指标要支持柱线/蜡烛双色模式,每个序列需两个缓冲:一个画序列、一个按条件给元素上色。原文示例用 5 个品种、25 个缓冲区(#property indicator_buffers 25)和 5 个绘图序列(indicator_plots 5),双色靠逗号分隔的第二色渲染阴线。 外部参数用 ENUM_DRAWTYPE(折线/柱线/蜡烛)和 ENUM_START_POINT 做下拉选择。起始点有五种:垂直线、月、周、日、小时。垂直线模式在加载时插一根绿线,拖到某根 K 线后所有品种以该 K 线开盘价汇聚;其余四种则按对应周期开头自动汇聚。 品种数下限是 2(含当前图表的 EURUSD)。对每个非当前品种可设翻转,把 USDCHF 这类同基准货币对上下颠倒后,和 EURUSD 同屏对比更直观。OnInit 里用 CheckTesterMode 拦掉策略测试器(终端尚未支持测市中改图表属性),InitStartPointTF 按选择分配 timeframe_start_point,SetDivergenceLine 在垂线模式放操控线。 OnCalculate 改为单循环填充,并新增 LoadAndFormDataHighTF 取高周期起点时间;FillIndicatorBuffers 拆成取数、定汇聚位、算值填缓冲、校验四步,CorrectChartMaxMin 借可视柱线数重设纵轴最大最小,避免多品种尺度错位。外汇与贵金属叠加对比属高风险分析,结果仅作概率参考。

MQL5 / C++
class="macro">#class="kw">property indicator_chart_window class=class="str">"cmt">// Indicator is in the main window
class="macro">#class="kw">property indicator_buffers class="num">25   class=class="str">"cmt">// Number of buffers for indicator calculation
class="macro">#class="kw">property indicator_plots   class="num">5    class=class="str">"cmt">// Number of plotting series
class=class="str">"cmt">//--- Indicator buffers colors
class="macro">#class="kw">property indicator_color1   clrDodgerBlue,C&class="macro">#x27;class="num">0,class="num">50,class="num">100&class="macro">#x27;
class="macro">#class="kw">property indicator_color2   clrLimeGreen,C&class="macro">#x27;class="num">20,class="num">80,class="num">20&class="macro">#x27;
class="macro">#class="kw">property indicator_color3   clrGold,C&class="macro">#x27;class="num">160,class="num">140,class="num">0&class="macro">#x27;
class="macro">#class="kw">property indicator_color4   clrAqua,C&class="macro">#x27;class="num">0,class="num">140,class="num">140&class="macro">#x27;
class="macro">#class="kw">property indicator_color5   clrMagenta,C&class="macro">#x27;class="num">130,class="num">0,class="num">130&class="macro">#x27;
class=class="str">"cmt">//--- Constants 
class="macro">#define RESET          class="num">0 class=class="str">"cmt">// Returning the indicator recalculation command to the terminal
class="macro">#define SYMBOLS_COUNT  class="num">5 class=class="str">"cmt">// Number of symbols
class=class="str">"cmt">//--- Include the class for working with the canvas
class="macro">#include <Canvas\Canvas.mqh>
class=class="str">"cmt">//--- Include the class for working with the canvas
class="macro">#include "Include/Checks.mqh"
class="macro">#include "Include/Chart.mqh"
class="macro">#include "Include/Objects.mqh"
class=class="str">"cmt">//--- Drawing type of the price data
enum ENUM_DRAWTYPE
  {
   LINE   =class="num">0,  class=class="str">"cmt">// Line
   BARS   =class="num">1,  class=class="str">"cmt">// Bars
   CANDLES=class="num">2   class=class="str">"cmt">// Candlesticks
  };
class=class="str">"cmt">//--- Mode of the price divergence starting point
enum ENUM_START_POINT
  {

◍ 多品种叠加的参数与缓冲结构

这段声明定义了指标如何把多个外汇品种塞进同一画面。枚举里 VERTICAL_LINE 到 HOUR 分别对应 0~4 的整数,用来标记价格背离的起始参考粒度,从竖线、月、周、日一直到小时,实战中若只看日内背离,把 StartPriceDivergence 设成 4 即可。 外部参数里 DrawType 控制蜡烛或线形绘制,TwoColor 决定是否用双色区分涨跌;后面从 Symbol02 到 Symbol06 一口气挂了 GBPUSD、AUDUSD、NZDUSD、USDCAD、USDCHF 五个副品种,每个都带 Inverse 开关,置 true 会把该品种走势反向叠加,适合观察负相关性。外汇与贵金属叠加多品种属高风险操作,参数误设可能放大误判。 缓冲结构用 struct 把 open、high、low 三个 double 数组打包,后续每个品种的价格数据就按这个模板写入指标缓冲区,方便统一调度。 让小布替你跑这套 把 Symbol02 改成你常看的 XAUUSD,Inverse02 设 true,加载后若黄金与主线反向运动明显,说明对冲参考有效,可继续加品种验证。

MQL5 / C++
  VERTICAL_LINE=class="num">0,  class=class="str">"cmt">// Vertical line
  MONTH          =class="num">1,  class=class="str">"cmt">// Month
  WEEK           =class="num">2,  class=class="str">"cmt">// Week
  DAY            =class="num">3,  class=class="str">"cmt">// Day
  HOUR           =class="num">4   class=class="str">"cmt">// Hour
  };
class=class="str">"cmt">//--- External parameters
input   ENUM_DRAWTYPE       DrawType                =CANDLES;       class=class="str">"cmt">// Drawing type
input   ENUM_START_POINT StartPriceDivergence =VERTICAL_LINE; class=class="str">"cmt">// Start of price divergence
input   class="type">bool                TwoColor                =false;         class=class="str">"cmt">// Two-class="type">color bars/candlesticks
sinput class="type">class="kw">string dlm01=""; class=class="str">"cmt">//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
input   class="type">class="kw">string              Symbol02                ="GBPUSD";      class=class="str">"cmt">// Symbol class="num">2
input   class="type">bool                Inverse02               =false;         class=class="str">"cmt">// Inverse symbol class="num">2
input   class="type">class="kw">string              Symbol03                ="AUDUSD";      class=class="str">"cmt">// Symbol class="num">3
input   class="type">bool                Inverse03               =false;         class=class="str">"cmt">// Inverse symbol class="num">3
input   class="type">class="kw">string              Symbol04                ="NZDUSD";      class=class="str">"cmt">// Symbol class="num">4
input   class="type">bool                Inverse04               =false;         class=class="str">"cmt">// Inverse symbol class="num">4
input   class="type">class="kw">string              Symbol05                ="USDCAD";      class=class="str">"cmt">// Symbol class="num">5
input   class="type">bool                Inverse05               =false;         class=class="str">"cmt">// Inverse symbol class="num">5
input   class="type">class="kw">string              Symbol06                ="USDCHF";      class=class="str">"cmt">// Symbol class="num">6
input   class="type">bool                Inverse06               =false;         class=class="str">"cmt">// Inverse symbol class="num">6
class=class="str">"cmt">//--- Structure of the indicator buffers arrays
class="kw">struct buffers
  {
   class="type">class="kw">double             open[];   class=class="str">"cmt">// Open prices buffer
   class="type">class="kw">double             high[];   class=class="str">"cmt">// High prices buffer
   class="type">class="kw">double             low[];    class=class="str">"cmt">// Low prices buffer

「多品种指标的内存结构怎么摆」

写跨品种指标时,先把每个交易品种的缓冲区和状态变量按数组组织好,否则 OnCalculate 里很容易乱套。下面这段声明定义了每个符号各自的收盘价缓冲、颜色缓冲,以及统一存一批符号数据的结构体数组 buffer_data[SYMBOLS_COUNT],SYMBOLS_COUNT 决定最多能同时画几个品种。 从 OnCalculate 往全局搬数据,靠的是 OC_ 前缀那组变量:OC_rates_total 记录输入序列长度,OC_prev_calculated 记上次已处理柱数,OC_open/high/low/close 等分别接开高低收和量。用它们做增量计算,能少跑无用循环。 顺手定义了一些易漏的细节:series_first_date 与 series_first_date_last 用来核对终端里首根 K 线的真实时间,limit_time 控制从哪根柱开始画;line_colors 直接给了 5 个颜色(clrDodgerBlue、clrLimeGreen、clrGold、clrAqua、clrMagenta),对应前 5 个品种线色;empty_symbol 设成 "EMPTY" 标记缺品种。外汇贵金属多品种叠加波动相关性强,实盘前请在 MT5 用 3~5 个品种验证内存占用与重绘表现,相关风险偏高。

MQL5 / C++
  class="type">class="kw">double              close[];   class=class="str">"cmt">// Close prices buffer
  class="type">class="kw">double              icolor[]; class=class="str">"cmt">// Buffer to determine the class="type">color of element
  };
buffers                buffer_data[SYMBOLS_COUNT];
class=class="str">"cmt">//--- Load the class
CCanvas                canvas;
class=class="str">"cmt">//--- Variables/arrays for copying data from OnCalculate()
class="type">int                    OC_rates_total      =class="num">0; class=class="str">"cmt">// Size of input time series
class="type">int                    OC_prev_calculated =class="num">0; class=class="str">"cmt">// Bars processed at the previous call
class="type">class="kw">datetime               OC_time[];             class=class="str">"cmt">// Opening time
class="type">class="kw">double                 OC_open[];             class=class="str">"cmt">// Open prices
class="type">class="kw">double                 OC_high[];             class=class="str">"cmt">// High prices
class="type">class="kw">double                 OC_low[];              class=class="str">"cmt">// Low prices
class="type">class="kw">double                 OC_close[];            class=class="str">"cmt">// Close prices
class="type">long                   OC_tick_volume[];     class=class="str">"cmt">// Tick volumes
class="type">long                   OC_volume[];          class=class="str">"cmt">// Real volumes
class="type">int                    OC_spread[];          class=class="str">"cmt">// Spread
class=class="str">"cmt">//--- For the purpose of storing and checking the time of the first bar in the terminal
class="type">class="kw">datetime               series_first_date[SYMBOLS_COUNT];
class="type">class="kw">datetime               series_first_date_last[SYMBOLS_COUNT];
class=class="str">"cmt">//--- Time array of the bar from which we will start drawing
class="type">class="kw">datetime               limit_time[SYMBOLS_COUNT];
class=class="str">"cmt">//--- Symbol names array
class="type">class="kw">string                 symbol_names[SYMBOLS_COUNT];
class=class="str">"cmt">//--- Array of symbol inverse flags
class="type">bool                   inverse[SYMBOLS_COUNT];
class=class="str">"cmt">//--- Colors of indicator lines
class="type">color                  line_colors[SYMBOLS_COUNT]={clrDodgerBlue,clrLimeGreen,clrGold,clrAqua,clrMagenta};
class=class="str">"cmt">//--- String representing the lack of the symbol
class="type">class="kw">string                 empty_symbol="EMPTY";
class=class="str">"cmt">//--- Chart properties
class="type">int                    window_number                      =WRONG_VALUE;          class=class="str">"cmt">// Indicator window number
class="type">int                    chart_width                        =class="num">0;                    class=class="str">"cmt">// Chart width
class="type">int                    chart_height                       =class="num">0;                    class=class="str">"cmt">// Chart height

把指标参数和画布底子先钉死

写自定义指标时,先把图表尺寸、中心坐标和配色这类全局变量在文件顶部一次性声明,后续重绘逻辑就不用反复算。下面这段就是某背离指标里实际用的初始化块,last_chart_width 和 last_chart_height 初始为 0,意味着首次 OnCalculate 必然要走一次尺寸重捕。 canvas_opacity 取 190(0~255 区间),黑底叠加在报价图上还留了约 25% 的透,不会完全挡掉裸 K。font_size 给 16、font_name 写死 Calibri,是为了在 1920 宽屏下标签不挤;你换 4K 屏时这个数可能要调到 22 才顺眼。 prefix 用指标短名加下划线拼出来,所有图形对象名都挂这个前缀,MT5 里删对象时一句 ObjectDelete 通配就干净,不会误伤别的 EA 画的线。 别把 clr_format 当摆设 COLOR_FORMAT_ARGB_RAW 要求你自己在颜色里排好 Alpha 通道顺序,用 clrBlack 这种宏没问题,但一旦手填 C'100,0,0' 就得确认顺序是 R-G-B 还是 B-G-R,填反了下跌柱线会渲染成怪色。开 MT5 把下面代码贴进 mq5 头部编译,看导航栏里 MS_PriceDivergence 前缀对象是否随加载自动建出来,就能验证这套命名有没有撞车。

MQL5 / C++
class="type">int                 last_chart_width         =class="num">0;                class=class="str">"cmt">// Last saved chart width
class="type">int                 last_chart_height        =class="num">0;                class=class="str">"cmt">// Last saved chart height
class="type">int                 chart_center_x           =class="num">0;                class=class="str">"cmt">// Horizontal center of chart
class="type">int                 chart_center_y           =class="num">0;                class=class="str">"cmt">// Vertical center of chart
class="type">color               color_bar_up             =clrRed;           class=class="str">"cmt">// Up bar class="type">color
class="type">color               color_bar_down           =C&class="macro">#x27;class="num">100,class="num">0,class="num">0&class="macro">#x27;;       class=class="str">"cmt">// Down bar class="type">color
class="type">class="kw">string              indicator_shortname     ="MS_PriceDivergence"; class=class="str">"cmt">// Short name of the indicator
class="type">class="kw">string              prefix                   =indicator_shortname+"_"; class=class="str">"cmt">// Prefix for objects
class=class="str">"cmt">//--- Name of vertical line of the price divergence starting point
class="type">class="kw">string              start_price_divergence=prefix+"start_price_divergence";
class=class="str">"cmt">//--- Canvas properties
class="type">class="kw">string              canvas_name              =prefix+"canvas";   class=class="str">"cmt">// Canvas name
class="type">color               canvas_background        =clrBlack;         class=class="str">"cmt">// Canvas background class="type">color
class="type">uchar               canvas_opacity           =class="num">190;              class=class="str">"cmt">// Opacity
class="type">int                 font_size                =class="num">16;               class=class="str">"cmt">// Font size
class="type">class="kw">string              font_name                ="Calibri";        class=class="str">"cmt">// Font
ENUM_COLOR_FORMAT   clr_format               =COLOR_FORMAT_ARGB_RAW; class=class="str">"cmt">// Color components should be correctly set by the user
class=class="str">"cmt">//--- Canvas messages
class="type">class="kw">string              msg_prepare_data         ="Preparing data! Please wait...";
让小布替你跑这套
这些多品种偏离诊断逻辑小布盯盘已内置,打开对应品种页即可看到跨图表偏离提示,你只需专注判断背离是否具交易意义。

常见问题

前者在手动拖拽图表对象时触发,后者在缩放或修改图表属性对话框设置时触发,指标里需分别处理以避免重绘逻辑冲突。
每个序列除价格缓存外还需一个缓存区按条件指定阳线阴线颜色,否则无法在指标中渲染双向蜡烛。
按功能拆出 Checks.mqh、Objects.mqh、Chart.mqh 等包含文件,主文件仅留核心回调,便于维护与调试。
可以,小布盯盘的 AIGC 模块已内置跨品种偏离计算,无需自行编译指标即可在品种页查看。
未正确处理时间轴方向会导致缓存区赋值错位,需在 OnCalculate 中确认索引顺序与可见区域边界。