MQL5 中的绘图风格(基础篇)
📘

MQL5 中的绘图风格(基础篇)

第 1/3 篇

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 更不容易在休市段画出假连接,这是实盘里容易忽略的点。

MQL5 / C++
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。

MQL5 / C++
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 处理,这点调参时容易踩坑。 外汇与贵金属波动剧烈、杠杆风险高,这类实验性指标仅用于熟悉渲染机制,切勿直接用于实盘信号判断。

MQL5 / C++
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 两种跑法,图表上漏画或误画的概率差异立刻可见。外汇与贵金属报价在高波动时段易出现这类脏数据,属高风险环境下的细节雷。

MQL5 / C++
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);
}

常见问题

指标绘图风格通过缓冲区和画图类型设定,EA通常用OBJ_*对象手动画;先确认你要画的是指标线还是图形对象再选方法。
多半是空值常量没处理好,空值要用EMPTY_VALUE而不是0,否则会被当成有效坐标导致断裂或错位。
可以,小布能按你给的参数在对应品种页直接渲染多种风格预览,省去手动挨个切换测试。
决定画线是向下延伸还是向上延伸以及起点索引方向,写错会让线画反或只显示一半,对着文档核对排列再改。
常规策略够用,同时开太多缓冲线会占资源;高风险品种建议只留必要线,多余风格关掉保流畅。