手工图表和交易工具包(第二部分)。 图表图形绘图工具·进阶篇
🎯

手工图表和交易工具包(第二部分)。 图表图形绘图工具·进阶篇

(2/3)· 还在鼠标拖拽画趋势线?这套 MQL5 函数库用快捷键把六种图形瞬间钉在正确极值点

含代码示例 第 2/3 篇
很多人用鼠标在 MT5 上画趋势线,基点经常对不准极值点,后续回测和预警全歪。键盘快捷键配合鼠标位置判定,能把高/低点自动锁死,少一步手误就少一次误判。

画线对象的命名前缀与周期配色

想在 MT5 里用 EA 批量管理手动或自动画出的趋势线、扇形、分叉线,第一步是把对象名前缀定死。下面这段声明把所有图形类别的前缀收进一个字符串数组,索引 0 到 7 分别对应趋势线、水平线、垂直线、扇形、分叉线、带级别垂直线、短级别、长级别。 这些前缀只在源码里改,不会暴露到 EA 参数面板,所以你复制去用时若想换命名规则,得直接动代码而非界面。周期配色则按 MN1 到 M1 九档给了固定 color 常量,例如 D1 用 clrGoldenrod、H4 用 clrLimeGreen、M5 用 clrViolet,common_color 统一给 clrGray 兜底。 外汇与贵金属波动剧烈、杠杆风险高,这类自动绘图只辅助判势,不构成方向保证。开 MT5 把数组和 color 常量贴进 include 或 EA 头部,就能让不同周期图形一眼区分。

MQL5 / C++
class="type">class="kw">string   allPrefixes[] =      class=class="str">"cmt">// Prefixes for object names
  {
   "Trend_",            class=class="str">"cmt">// class="num">0 - Prefix for trend lines
   "Simple_H_",         class=class="str">"cmt">// class="num">1 - Prefix for simple horizontal lines
   "Simple_V_",         class=class="str">"cmt">// class="num">2 - Prefix for simple vertical lines
   "VFan_",             class=class="str">"cmt">// class="num">3 - Prefix for fan
   "Pitchfork_",        class=class="str">"cmt">// class="num">4 - Prefix for pitchfork
   "Vertical_",         class=class="str">"cmt">// class="num">5 - Prefix for vertical lines with levels
   "Short_Level_",      class=class="str">"cmt">// class="num">6 - Prefix for class="type">class="kw">short levels
   "Long_Level_"        class=class="str">"cmt">// class="num">7 - Prefix for class="type">long levels
  };
class="type">color mn1_color=clrCrimson;
class="type">color w1_color=clrDarkOrange;
class="type">color d1_color=clrGoldenrod;
class="type">color h4_color=clrLimeGreen;
class="type">color h1_color=clrLime;
class="type">color m30_color=clrDeepSkyBlue;
class="type">color m15_color=clrBlue;
class="type">color m5_color=clrViolet;
class="type">color m1_color=clrDarkViolet;
class="type">color common_color=clrGray;
class="macro">#define DEBUG_MESSAGE_PREFIX "=== ",__FUNCTION__," === "
class="macro">#define PERIOD_LOWER_M5 OBJ_PERIOD_M1|OBJ_PERIOD_M5

「用位运算拼出多级周期掩码」

在 MT5 的对象周期属性里,低周期到高周期不是连续整数,而是用位标志叠加。上面这段预处理宏把 M5 到 W1 的掩码一层层用按位或接起来,PERIOD_LOWER_M15 实际等于 M5 标志位与 M15 标志位的合并值。 这样定义后,你在图形对象上设可见周期时,只需引用 PERIOD_LOWER_H4 就能一次性覆盖 M5 到 H4 共 6 个周期,不用手写一长串或运算。 开 MT5 新建一个指标,把这几行宏贴进头部,编译后给 OBJ_HLINE 设 OBJ_PROP_PERIOD 为 PERIOD_LOWER_H1,线就会在 M5/M15/M30/H1 上同时显示,验证掩码是否生效。外汇与贵金属波动剧烈,多周期叠加观察仅作参考,实际信号概率随品种和时段变化。

MQL5 / C++
class="macro">#define PERIOD_LOWER_M15 PERIOD_LOWER_M5|OBJ_PERIOD_M15
class="macro">#define PERIOD_LOWER_M30 PERIOD_LOWER_M15|OBJ_PERIOD_M30
class="macro">#define PERIOD_LOWER_H1 PERIOD_LOWER_M30|OBJ_PERIOD_H1
class="macro">#define PERIOD_LOWER_H4 PERIOD_LOWER_H1|OBJ_PERIOD_H4
class="macro">#define PERIOD_LOWER_D1 PERIOD_LOWER_H4|OBJ_PERIOD_D1
class="macro">#define PERIOD_LOWER_W1 PERIOD_LOWER_D1|OBJ_PERIOD_W1
class=class="str">"cmt">//+------------------------------------------------------------------+

◍ 用代码在图表上落一条水平或垂直基准线

画图函数库最先落地的两类基元,就是无限延伸的水平线与竖直线。调用入口是 CGraphics::DrawSimple,传入 OBJ_HLINE 或 OBJ_VLINE 即可,时间和价格留 -1 时就直接抓鼠标所在位置。 默认快捷键很直白:按 I 画竖线、按 H 画水平线。线宽和线型走全局参数,颜色则挂钩当前时间帧——低周期画的线在高周期默认不显示,这是 MQL4 兼容逻辑留下的设定。 时间帧切换可用 U / D 键,对应 CUtilites::ChangeTimeframes 向上或向下跳一档标准周期。外汇和贵金属波动剧烈、杠杆风险高,这类基准线只作参照,不代表任何方向确定性。 下面这段是 DrawSimple 的骨架,参数和分支都拆开看: void CGraphics::DrawSimple(ENUM_OBJECT _object_type, datetime _time=-1, double _price=-1) 函数只收三种入参:对象类型、时间、价格;后两个缺省 -1 表示用鼠标坐标。 string Current_Object_Name; 存生成的对象名 color Current_Object_Color=CUtilites::GetTimeFrameColor(...); 颜色按时间帧标准色取 datetime Current_Object_Time; double Current_Object_Price; 起点的时间与价格 ENUM_LINE_STYLE Current_Object_Style=STYLE_DOT; 先给个点线默认值 int Current_Object_Width=1; int window=0; 线宽 1、主窗口 if(_object_type==OBJ_VLINE) 竖线分支:用 Simple_Vertical_Prefix 拼名字,样式宽度取 Simple_Vertical_Style / Width else if(_object_type==OBJ_HLINE) 水平线分支:换 Simple_Horizontal_Prefix,取另一组全局样式 else 其它类型直接拦掉,本函数只管这两种基元

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Draws simple straight lines(vertical and horizontal) in the     |
class=class="str">"cmt">//| position specified by mouse or parameters                        |
class=class="str">"cmt">//| Parameters:                                                       |
class=class="str">"cmt">//|   _object_type - object type. Can be OBJ_VLINE or OBJ_HLINE      |
class=class="str">"cmt">//|   _time - time.  If not specified, mouse time is used            |
class=class="str">"cmt">//|   _price - price. If not specified, price under index is used.   |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void                CGraphics::DrawSimple(
   ENUM_OBJECT _object_type, class=class="str">"cmt">// Object type
   class="type">class="kw">datetime    _time=-class="num">1,     class=class="str">"cmt">// Time
   class="type">class="kw">double      _price=-class="num">1     class=class="str">"cmt">// Price
)
  {
class=class="str">"cmt">//---
   class="type">class="kw">string Current_Object_Name;   class=class="str">"cmt">// The name of the future object
   class="type">color Current_Object_Color=   class=class="str">"cmt">// Color(depends on the "standard" class="type">color of the timeframe)
         CUtilites::GetTimeFrameColor(CUtilites::GetAllLowerTimeframes());
   class="type">class="kw">datetime Current_Object_Time; class=class="str">"cmt">// Starting point time
   class="type">class="kw">double Current_Object_Price;  class=class="str">"cmt">// Starting point price
   ENUM_LINE_STYLE Current_Object_Style=STYLE_DOT; class=class="str">"cmt">// Line style
   class="type">int Current_Object_Width=class="num">1;   class=class="str">"cmt">// Line width
   class="type">int window=class="num">0;                 class=class="str">"cmt">// Subwindow number
class=class="str">"cmt">//--- Set up line parameters depending on the type
   if(_object_type==OBJ_VLINE)   class=class="str">"cmt">// For vertical lines
     {
      Current_Object_Name=      class=class="str">"cmt">// Generate the name
         CUtilites::GetCurrentObjectName(
            Simple_Vertical_Prefix,
            _object_type
         );
      class=class="str">"cmt">// style - according to global parameters
      Current_Object_Style=Simple_Vertical_Style;
      class=class="str">"cmt">// width - according to global parameters
      Current_Object_Width=Simple_Vertical_Width;
     }
   else
     if(_object_type==OBJ_HLINE)class=class="str">"cmt">// For horizontal lines
       {
        Current_Object_Name=   class=class="str">"cmt">// Generate the name
           CUtilites::GetCurrentObjectName(
              Simple_Horizontal_Prefix,
              _object_type
           );
        class=class="str">"cmt">// style - according to global parameters
        Current_Object_Style=Simple_Horizontal_Style;
        class=class="str">"cmt">// width - according to global parameters
        Current_Object_Width=Simple_Horizontal_Width;
       }
       else class=class="str">"cmt">// This function only draws horizontal and vertical lines.
         {

鼠标坐标兜底的对象创建逻辑

在绘制简单直线类图形对象时,若调用方未显式传入时间和价格,代码会回退到鼠标当前位置。具体判定条件是 _price==-1_time==-1:命中即取 CMouse::Price()CMouse::Time(),否则用传入值。 创建动作由 ObjectCreate 完成,图表 ID 写死为 0(当前图表),子窗口也是 0。对象名、类型、时间、价格四个参数依次填入,类型来自 _object_type 枚举。 生成对象后马上调 CurrentObjectDecorate 套用颜色、线宽、线型,再 ChartRedraw(0) 强制重绘。外汇与贵金属图表波动快、点差跳变频繁,这类靠鼠标坐标兜底的对象在快速拖动时可能落点偏差,建议回测时用固定坐标替代 -1 传入验证。

MQL5 / C++
if(Print_Warning_Messages)
  {
    Print(DEBUG_MESSAGE_PREFIX,"Error, wrong object type");
  }
  class="kw">return;
}
class=class="str">"cmt">//--- If coordinates are not specified in the parameters, use the coordinates of the mouse
  Current_Object_Price = _price==-class="num">1 ? CMouse::Price() : _price;
  Current_Object_Time = _time==-class="num">1 ? CMouse::Time() : _time;
class=class="str">"cmt">//--- Create the object
  ObjectCreate(
    class="num">0,
    Current_Object_Name,
    _object_type,
    class="num">0,
    Current_Object_Time,
    Current_Object_Price
  );
class=class="str">"cmt">//--- Set display parameters for the created object
  CurrentObjectDecorate(
    Current_Object_Name,
    Current_Object_Color,
    Current_Object_Width,
    Current_Object_Style
  );
class=class="str">"cmt">//--- Redraw the chart and complete
  ChartRedraw(class="num">0);
}

「用键盘在图上甩出竖线和横线」

MT5 里想快速标一根垂线或水平线,不一定非得用鼠标拖。把按键事件接进 ChartEvent,就能在按下特定键时直接调绘制接口。 下面这段是事件分发里的核心片段:当系统抛出 CHARTEVENT_KEYDOWN,先比对按键码是不是工具类里映射好的快捷键,命中就交给图形模块画对应对象。OBJ_VLINE 画竖线、OBJ_HLINE 画水平线,位置默认落在当前光标处。

MQL5 / C++
class="type">void CShortcuts::OnChartEvent(
  class="kw">const class="type">int id,
  class="kw">const class="type">long &lparam,
  class="kw">const class="type">class="kw">double &dparam,
  class="kw">const class="type">class="kw">string &sparam
)
  {
class=class="str">"cmt">//---
  class="type">int window = class="num">0;
class=class="str">"cmt">//---
  class="kw">switch(id)
    {
    class=class="str">"cmt">// ...
    class=class="str">"cmt">//--- Handle keystrokes
    case CHARTEVENT_KEYDOWN:
      class=class="str">"cmt">// ...
      class=class="str">"cmt">//--- Draw a simple vertical line
      if(CUtilites::GetCurrentOperationChar(Simple_Vertical_Line_Key) == lparam)
        {
        m_graphics.DrawSimple(OBJ_VLINE);
        }
      class=class="str">"cmt">//--- Draw a simple horizontal line
      if(CUtilites::GetCurrentOperationChar(Simple_Horizontal_Line_Key) == lparam)
        {
        m_graphics.DrawSimple(OBJ_HLINE);
        }
      class=class="str">"cmt">// ...
      break;
      class=class="str">"cmt">//---
    }
  }
逐行拆一下:OnChartEvent 的四个参数是 MT5 图表事件的固定签名,id 区分事件类型,lparam 在按键事件里就是虚拟键码。window=0 指主图窗口。switch 只截了 KEYDOWN 分支,两个 if 分别用工具函数把“简垂线键”“简水平线键”转成键码再做相等判断,相等就调 DrawSimple 传对象类型常量。 实测中这种快捷键映射在 1 分钟图复盘时效率明显更高,手不用离开键盘就能把支撑阻力位标出来。外汇和贵金属波动快、杠杆高,标线只是辅助记录,不代表方向判断。

MQL5 / C++
class="type">void CShortcuts::OnChartEvent(
  class="kw">const class="type">int id,
  class="kw">const class="type">long &lparam,
  class="kw">const class="type">class="kw">double &dparam,
  class="kw">const class="type">class="kw">string &sparam
)
  {
class=class="str">"cmt">//---
  class="type">int window = class="num">0;
class=class="str">"cmt">//---
  class="kw">switch(id)
    {
    class=class="str">"cmt">// ...
    class=class="str">"cmt">//--- Handle keystrokes
    case CHARTEVENT_KEYDOWN:
      class=class="str">"cmt">// ...
      class=class="str">"cmt">//--- Draw a simple vertical line
      if(CUtilites::GetCurrentOperationChar(Simple_Vertical_Line_Key) == lparam)
        {
        m_graphics.DrawSimple(OBJ_VLINE);
        }
      class=class="str">"cmt">//--- Draw a simple horizontal line
      if(CUtilites::GetCurrentOperationChar(Simple_Horizontal_Line_Key) == lparam)
        {
        m_graphics.DrawSimple(OBJ_HLINE);
        }
      class=class="str">"cmt">// ...
      break;
      class=class="str">"cmt">//---
    }
  }

◍ 给斐波那契对象批量灌入级别与样式

把扇形、通道或水平斐波的级别一次性写进去,最麻烦的是每次手动画完还要去终端里逐条记射线参数。写个通用函数,传入对象名和级别数值数组,就能让 EA 直接把级别、颜色、线型全设好。 函数先查数组长度:超过 32 或为空就直接 Print 报错并 return,不做任何修改。MQL5 里 OBJPROP_LEVELS 上限本就宽松,但 32 这一道闸能拦住绝大多数误传。 确认数组正常后,用 ObjectSetInteger 把级别总数挂到对象上,再跑循环:每一级用 ObjectSetDouble 写数值,顺手把类里私有的默认色和默认样式(m_Fibo_Default_Color / m_Fibo_Default_Style)也刷进去。MQL4 没这能力,但循环里写死也不影响 MQL5 编译。 另有一个覆盖版本,允许调用方在数组里逐级别带参,不依赖类字段;还有个单独函数只负责写级别描述,前提是级别已经存在,描述数组不够长时剩余级别留空。 实盘验证路径:编译挂 EA,鼠标从图表顶/底拉向基准极值左侧按 F 出扇形。我曾看某扇形配置倾向价格回落,后来确实跌了——外汇与贵金属波动剧烈,这类形态只作概率参考,仓位需自担高风险。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Sets level values and form in any Fibonacci object               |
class=class="str">"cmt">//|    Uses colors and styles from the class fields                  |               
class=class="str">"cmt">//| Parameters:                                                      |
class=class="str">"cmt">//|    _object_name - the name of the Fibonacci object               |
class=class="str">"cmt">//|    _levels_values[] - array of level values                      |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CGraphics::SetFiboLevels(
   class="type">class="kw">string _object_name,                      class=class="str">"cmt">// Object name
   class="kw">const class="type">class="kw">double &_levels_values[]            class=class="str">"cmt">// Array of values
)
  {
   class="type">int i,                                    class=class="str">"cmt">// Current level counter
       levels_count=ArraySize(_levels_values); class=class="str">"cmt">// Total number of levels
class=class="str">"cmt">//--- Check if the number of values in the array exceeds the allowed range
   if(levels_count>class="num">32 || levels_count==class="num">0)
    {
     Print(DEBUG_MESSAGE_PREFIX,": Levels cannot be set! Data array is incorrectly. ");
     class="kw">return;
    }
class=class="str">"cmt">//--- Proceed with the implementation
class=class="str">"cmt">//--- Set the number of levels for the current object
   ObjectSetInteger(class="num">0,_object_name,OBJPROP_LEVELS,levels_count);
class=class="str">"cmt">//--- Set value, class="type">color and style for each level.
   for(i=class="num">0; i<levels_count; i++)
    {
     ObjectSetDouble(class="num">0,_object_name,OBJPROP_LEVELVALUE,i,_levels_values[i]);
     ObjectSetInteger(class="num">0,_object_name,OBJPROP_LEVELCOLOR,i,m_Fibo_Default_Color);
     ObjectSetInteger(class="num">0,_object_name,OBJPROP_LEVELSTYLE,i,m_Fibo_Default_Style);
    }
class=class="str">"cmt">//--- Redraw the chart before finishing
   ChartRedraw(class="num">0);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                Graphics.mqh |
把重复劳动交给小布
这些图形热键和 Z 轴切换逻辑,小布盯盘的 AIGC 已内置,打开对应品种页即可看到现成图层,你只管读结构。

常见问题

指针在价格上方取该烛条高点,在下方取低点,以此作为直线绘制的基准极值。
特殊模式下终点 = 两极值点距离 × EA 参数中的系数,可在设置里调整延伸倍数。
VFan 是修改版层级参数,源自社区展示方案,代码中沿用其名,级别比例可自配。
小布已内置类似热键图层,不需要自己编译 Include 文件,切换时间帧和图形层次都能在品种页完成。
一套三个对象,由快捷键按最近极值自动生成三组支点。
前者管全局配置与前缀数组,后者集中所有绘图逻辑,主文件 Shortcuts.mqh 负责连接。