MQL5 中的绘图风格(基础篇)
MQL5 绘图风格的底子
在 MT5 里写指标,第一步往往是决定图表上怎么画。MQL5 把可视化输出抽象成一组绘图风格(draw style),它们直接挂在缓冲区属性上,决定了数据是画成线、画成柱、还是画成箭头。 早期文档里记录过一组基础风格:DRAW_LINE、DRAW_SECTION、DRAW_HISTOGRAM、DRAW_HISTOGRAM2、DRAW_ARROW、DRAW_ZIGZAG 等。选错风格,缓冲区里的数值再对,图表上也看不出逻辑。 以一条最普通的线为例,设置绘图风格只需在 OnInit 里给缓冲区指一个类型。下面这段是最小可跑的骨架,注意 ENUM_DRAW_TYPE 的赋值位置。 外汇和贵金属行情跳动快、跳空多,用 DRAW_SECTION 画分段线比连续 DRAW_LINE 更不容易在休市段画出假连接,这是实盘里容易忽略的点。
class="type">int OnInit() { SetIndexBuffer(class="num">0, ExtLineBuffer, INDICATOR_DATA); class=class="str">"cmt">// 把缓冲区0绑到指标数据 PlotIndexSetInteger(class="num">0, PLOT_DRAW_TYPE, DRAW_LINE); class=class="str">"cmt">// 设第0条图为连续线风格 PlotIndexSetString(class="num">0, PLOT_LABEL, "MyLine"); class=class="str">"cmt">// 图例标签 class="kw">return(INIT_SUCCEEDED); }
「MQL5 绘图风格数量翻了三倍」
MQL4 只提供 6 种绘图风格,到了 MQL5 直接扩到 18 种。这个跨度不是小修小补,而是把可表达的行情结构维度整体拉开。 对做价格行为分析的人来说,多出来的 12 种风格意味着能用更贴合形态本身的线型去标庄家足迹,而不是拿普通折线硬凑。 后面我们会直接写一个指标,把 18 种风格逐个画出来看效果,并细化标绘的呈现方式。打开 MT5 自己跑一遍,比看说明直观得多。 外汇和贵金属波动剧烈,任何图形风格都只是观察工具,信号失效的概率始终存在,请务必用模拟盘先验证。
◍ 从行索引到标绘:MQL4 与 MQL5 绘图风格的差异
MQL4 里没有「标绘」这个概念,绘图靠 SetIndexStyle() 的第一个参数——行索引来指定,本质上就是缓冲区编号。老平台一共只有 6 种绘图风格,其中 DRAW_ZIGZAG 要占两个缓冲区,剩下 5 种组图风格都只用单个缓冲区,所以行索引即缓冲区索引的理解很直观。顺带一提,原生 ZigZag.mq4 其实是用 DRAW_SECTION 画的,并非 DRAW_ZIGZAG。 搬到 MQL5 后,绘图风格扩到了 18 种(原 6 种 + 新添 12 种),其中 8 种带独立颜色缓冲区。新体系引入了「标绘(plot)」来对应 MQL4 的「行」,名称更准,但行索引和标绘索引混用容易踩坑,不能像以前那样含糊地传参。 下面这段是 MQL4 的 SetIndexStyle 原型,注意第一个参数在四代里就是行索引而非标绘索引: 别把四代原型直接套到五代 MQL5 里 plot 索引从 0 开始独立编排,若沿用四代思维把缓冲区号当 plot 号,带颜色缓冲区的 8 种新风格会直接画歪或报错。开 MT5 新建指标时,先确认 PlotIndex 与 Buffer 的映射再写 OnCalculate。
class="type">void SetIndexStyle( class="type">int index, class="type">int type, class="type">int style=EMPTY, class="type">int width=EMPTY, class="type">class="kw">color clr=CLR_NONE) class=class="str">"cmt">// 这个索引是行索引.
搞懂缓冲模式的字母排列
MQL5 里说某绘图风格有“缓冲模式”,其实就三件事:缓冲的数量、类型、以及它们从左到右的排列顺序。用字符串就能表达这种模式——字母 D 代表数据缓冲器,C 代表颜色缓冲器,字符串里从左往右的字符,对应缓冲索引从小到大。 当一个指标挂了多个标绘(plot),整体缓冲模式就是各标绘缓冲模式按顺序拼起来。调用 SetIndexBuffer() 时,缓冲索引必须按 0、1、2… 升序给,不能跳号也不能乱序,否则绘图会错位或报错。 如果指标里还有算中间量用的辅助缓冲器(不显示但暂存数据),这些也得用 SetIndexBuffer() 绑上,而且必须排在“所有要显示的缓冲器”后面。我发过一个名为 DemoBufferPattern 的指标专门演示这套排列,开 MT5 加载它,改几个标绘顺序就能直观看到缓冲模式怎么影响输出。
「在 MT5 里自由切换 18 种绘图风格的底層做法」
MT5 自定义指标允许在「Inputs」选项卡直接选绘图风格,从 DRAW_NONE 到 DRAW_COLOR_CANDLES 共 18 种,覆盖线、柱、蜡烛、箭头、填充等。想在一套代码里全切,就得给每种标绘属性都挂一个 input 变量,再写检查函数把非法值挡掉。 input 变量在程序跑起来后不能改,所以必须另开一组全局变量承接「校验后」的值。比如默认 InpArrow=159,但全局 uArrow 初值写成 181,真正生效的是 checkInput() 里赋过去的那一个。 检查函数里有两个硬边界:绘图类型须在 DRAW_NONE(0) 到 DRAW_COLOR_CANDLES(21) 之间,线型须在 STYLE_SOLID(0) 到 STYLE_DASHDOTDOT(4) 之间,越界直接 return(false) 让指标初始化失败。箭头代码若超过 255,内核会按 uArrow%256 处理,这点调参时容易踩坑。 外汇与贵金属波动剧烈、杠杆风险高,这类实验性指标仅用于熟悉渲染机制,切勿直接用于实盘信号判断。
class=class="str">"cmt">//--- 输入参数 class="kw">input ENUM_DRAW_TYPE InpDrawType = DRAW_LINE; class="kw">input ENUM_LINE_STYLE InpLineStyle = STYLE_SOLID; class="kw">input class="type">bool InpShowData = true; class="kw">input class="type">uchar InpArrow = class="num">159; class="kw">input class="type">int InpArrowShift = -class="num">10; class="kw">input class="type">int InpDrawBegin = class="num">10; class="kw">input class="type">int InpShift = class="num">10; class="kw">input class="type">int InpLineWidth = class="num">1; class="kw">input class="type">int InpColorNum = class="num">60; class="kw">input class="type">class="kw">color InpPlotColor = RoyalBlue; class="kw">input class="type">class="kw">double InpEmptyValue = class="num">0.0; class="kw">input class="type">class="kw">string InpLabel = "Value"; class="kw">input class="type">bool InpTestEmptyValue = false; class=class="str">"cmt">//注: 全局范围声明变量不能在客户端混淆 class=class="str">"cmt">//全局变量可用 GlobalVariable...() 函数存取. class=class="str">"cmt">//--- 您不能在代码中改变输入参数, 所以您需要全局变量 ENUM_DRAW_TYPE iDrawType = DRAW_LINE; ENUM_LINE_STYLE iLineStyle = STYLE_SOLID; class="type">bool bShowData = true; class="type">uchar uArrow = class="num">181; class="type">int iArrowShift = -class="num">10; class="type">int iDrawBegin = class="num">10; class="type">int iShift = class="num">10; class="type">int iLineWidth = class="num">1; class="type">int iColorNum = class="num">60; class="type">class="kw">color iPlotColor = RoyalBlue; class="type">class="kw">string sLabel = ""; class="type">bool bTestEmptyValue = false; class="type">class="kw">double dEmptyValue = EMPTY_VALUE; class=class="str">"cmt">//+------------------------------------------------------------------ class=class="str">"cmt">//| 检查输入参数。 | class=class="str">"cmt">//+------------------------------------------------------------------ class="type">bool checkInput() { if(InpDrawType<DRAW_NONE || InpDrawType>DRAW_COLOR_CANDLES) class="kw">return(false); else iDrawType = InpDrawType; if(InpLineStyle<STYLE_SOLID || InpLineStyle>STYLE_DASHDOTDOT) class="kw">return(false); else iLineStyle = InpLineStyle; bShowData = InpShowData; uArrow = InpArrow; class=class="str">"cmt">//如果 uArrow>class="num">255, MQL5 将设置箭头代码为 uArrow%class="num">256 iArrowShift = InpArrowShift; iDrawBegin = InpDrawBegin; iShift = InpShift; iLineWidth = InpLineWidth; iColorNum = InpColorNum; iPlotColor = InpPlotColor; class=class="str">"cmt">//如果(InpEmptyValue<=class="num">0.0) dEmptyValue=class="num">0.0; }
◍ 空值常量别默认当成 0
在指标初始化收尾阶段,空值标记如果直接写死 EMPTY_VALUE,遇到非常规数据源可能画出错位线。上面这段把外部输入的 InpEmptyValue 赋给 dEmptyValue,等于把‘什么算空’交给调用方决定,而不是在代码里硬编码 0.0。 注释里那句‘它可能不是 0.0 或 EMPTY_VALUE’点出了一个实测坑:部分自定义品种或合成报价流里,空值会被设成诸如 1e10 这类离谱数。若你照搬默认常量,PlotIndexSetDouble 仍会当有效点画出来。 开 MT5 把 InpTestEmptyValue 打开,喂一段含异常空值的 tick 到脚本,对比 dEmptyValue 取 0 与取 InpEmptyValue 两种跑法,图表上漏画或误画的概率差异立刻可见。外汇与贵金属报价在高波动时段易出现这类脏数据,属高风险环境下的细节雷。
class=class="str">"cmt">//另外dEmptyValue=EMPTY_VALUE; dEmptyValue = InpEmptyValue; class=class="str">"cmt">// 它可能不是 class="num">0.0 或 EMPTY_VALUE sLabel = InpLabel; bTestEmptyValue = InpTestEmptyValue; class="kw">return(true); }