手工图表和交易工具包(第一部分)。 准备:结构描述和助手类·进阶篇
📘

手工图表和交易工具包(第一部分)。 准备:结构描述和助手类·进阶篇

第 2/3 篇

「光标状态被收进一个静态类」

做 MT5 自定义指标或 EA 时,想实时拿到鼠标在图上的位置,最省事的办法是把光标参数集中到一个类里。这里有一套实现把 X/Y 像素坐标、对应柱线序号、光标处的时间与价格,以及光标相对当前柱高低的位置标记,全部存进 Mouse.mqh。 这个类的方法全声明为 static,意味着你不用 new 一个实例,直接在任意 .mq5 文件里写 CMouse::X() 就能取数。对价格行为交易者来说,这等于随时能知道「用户正悬停在哪根 K 线、哪个价位」,做悬停标注或交互式画线都够用。 外汇和贵金属波动快、杠杆高,这类交互只辅助看盘,不能单独作为下单依据,信号误读可能放大亏损。 下面这段是类的骨架,逐行看: //+------------------------------------------------------------------+

//Mouse.mqh
//Copyright 2020, MetaQuotes Software Corp.
//[MQL5官方文档]

//+------------------------------------------------------------------+ // 文件头注释,标明出处与链接,仅作版权声明 #property copyright "Copyright 2020, MetaQuotes Software Corp." // 编译器属性:版权信息 #property link "[MQL5官方文档] // 编译器属性:相关文章链接 //+------------------------------------------------------------------+

//Mouse movement handling class

//+------------------------------------------------------------------+ class CMouse // 定义鼠标处理类 CMouse { //--- Members private: // 私有成员区 static int m_x; // X // 静态整型:光标 X 像素坐标 static int m_y; // Y // 静态整型:光标 Y 像素坐标 static int m_barNumber; // Bar number // 静态整型:光标所在柱线序号 static bool m_below; // Indication of a cursor above the price // 静态布尔:标记光标是否在价格下方 static bool m_above; // Indication of a cursor below the price // 静态布尔:标记光标是否在价格上方 static datetime m_currentTime; // Current time // 静态时间:光标处对应的 K 线时间 static double m_currentPrice;// Current price // 静态双精度:光标处对应的价格 //--- Methods public: // 公有方法区 //--- Remembers the main parameters of the mouse cursor static void SetCurrentParameters( // 静态方法:把鼠标事件参数写进上述静态成员 const int id, // 事件 ID const long &lparam, // 长整型参数(通常含坐标) const double &dparam, // 双精度参数 const string &sparam // 字符串参数 ); //--- Returns the X coordinate (in pixels) of the current cursor position static int X(void) {return m_x;} // 取光标 X 像素坐标 //--- Returns the Y coordinate (in pixels) of the current cursor position static int Y(void) {return m_y;} // 取光标 Y 像素坐标 //--- Returns the price of the current cursor position static double Price(void) {return m_currentPrice;} // 取光标处价格 //--- Returns the time of the current cursor position static datetime Time(void) {return m_currentTime;} // 取光标处时间 //--- Returns the bar number of the current cursor position static int Bar(void) {return m_barNumber;} // 取光标处柱线序号 //--- Returns a flag showing that the price is below the Low of the current bar // 后续还有判断光标与柱体低/高关系的标志方法(此处截断)

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                  Mouse.mqh |
class=class="str">"cmt">//|                       Copyright class="num">2020, MetaQuotes Software Corp. |
class=class="str">"cmt">//|                         [MQL5官方文档] |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#class="kw">property copyright "Copyright class="num">2020, MetaQuotes Software Corp."
class="macro">#class="kw">property link      "[MQL5官方文档]
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Mouse movement handling class                                    |
class=class="str">"cmt">//+------------------------------------------------------------------+
class CMouse
  {
  class=class="str">"cmt">//--- Members
class="kw">private:
  class="kw">static class="type">int       m_x;          class=class="str">"cmt">// X
  class="kw">static class="type">int       m_y;          class=class="str">"cmt">// Y
  class="kw">static class="type">int       m_barNumber;  class=class="str">"cmt">// Bar number
  class="kw">static class="type">bool      m_below;      class=class="str">"cmt">// Indication of a cursor above the price
  class="kw">static class="type">bool      m_above;      class=class="str">"cmt">// Indication of a cursor below the price
  class="kw">static class="type">class="kw">datetime  m_currentTime; class=class="str">"cmt">// Current time
  class="kw">static class="type">class="kw">double    m_currentPrice;class=class="str">"cmt">// Current price
  class=class="str">"cmt">//--- Methods
class="kw">public:
  class=class="str">"cmt">//--- Remembers the main parameters of the mouse cursor
  class="kw">static class="type">void      SetCurrentParameters(
    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">//--- Returns the X coordinate(in pixels) of the current cursor position
  class="kw">static class="type">int       X(class="type">void) {class="kw">return m_x;}
  class=class="str">"cmt">//--- Returns the Y coordinate(in pixels) of the current cursor position
  class="kw">static class="type">int       Y(class="type">void) {class="kw">return m_y;}
  class=class="str">"cmt">//--- Returns the price of the current cursor position
  class="kw">static class="type">class="kw">double    Price(class="type">void) {class="kw">return m_currentPrice;}
  class=class="str">"cmt">//--- Returns the time of the current cursor position
  class="kw">static class="type">class="kw">datetime  Time(class="type">void) {class="kw">return m_currentTime;}
  class=class="str">"cmt">//--- Returns the bar number of the current cursor position
  class="kw">static class="type">int       Bar(class="type">void) {class="kw">return m_barNumber;}
  class=class="str">"cmt">//--- Returns a flag showing that the price is below the Low of the current bar

◍ 鼠标坐标到价格时间的静态映射

CMouse 类用一组 static 成员变量缓存鼠标状态:m_x、m_y 存像素坐标,m_currentTime 与 m_currentPrice 存由坐标反推的图表时间价格,m_below / m_above 标记光标相对当前 K 线高低点的位置。类外初始化里这些变量全部置零或 false,意味着指标加载后、首次鼠标移动事件前,读到的都是默认值。 SetCurrentParameters 是核心:它接收 Windows 消息的 lparam(X 像素)和 dparam(Y 像素),调用 ChartXYToTimePrice 把像素转成时间价格,写回 m_currentTime / m_currentPrice,同时把原始像素存进 m_x / m_y。 随后用 iBarShift 按 m_currentTime 找到对应 K 线序号 m_barNumber,再用 iLow / iHigh 比较光标价格与这根 K 线的最低最高价,得出 m_below 与 m_above。外汇与贵金属市场跳空频繁,这种比较只基于当前周期 PERIOD_CURRENT 的单根 K 线,跨周期或跳空缺口下 m_above/m_below 可能偏离视觉预期,开 MT5 挂个鼠标事件打印这几个值就能验证。

MQL5 / C++
class="kw">static class="type">bool      Below(class="type">void) {class="kw">return m_below;}
  class=class="str">"cmt">//--- Returns a flag showing that the price is above the High of the current bar
  class="kw">static class="type">bool      Above(class="type">void) {class="kw">return m_above;}
  };
class=class="str">"cmt">//---
class="type">int CMouse::m_x=class="num">0;
class="type">int CMouse::m_y=class="num">0;
class="type">int CMouse::m_barNumber=class="num">0;
class="type">bool CMouse::m_below=class="kw">false;
class="type">bool CMouse::m_above=class="kw">false;
class="type">class="kw">datetime CMouse::m_currentTime=class="num">0;
class="type">class="kw">double CMouse::m_currentPrice=class="num">0;
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Remembers the main parameters for a mouse movement: coordinates  |
class=class="str">"cmt">//| of cursor in pixels and price/time, whether the cursor is        |
class=class="str">"cmt">//| above or below the price.                                        |
class=class="str">"cmt">//|+-----------------------------------------------------------------+
class="kw">static class="type">void CMouse::SetCurrentParameters(
  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">//---
  ChartXYToTimePrice(
    class="num">0,
    (class="type">int)lparam,
    (class="type">int)dparam,
    window,
    m_currentTime,
    m_currentPrice
   );
  m_x=(class="type">int)lparam;
  m_y=(class="type">int)dparam;
  m_barNumber=iBarShift(
             Symbol(),
             PERIOD_CURRENT,
             m_currentTime
             );
  m_below=m_currentPrice<iLow(Symbol(),PERIOD_CURRENT,m_barNumber);
  m_above=m_currentPrice>iHigh(Symbol(),PERIOD_CURRENT,m_barNumber);
  }

用户参数全塞在 GlobalVariables 里

这套 MT5 工具的对外可调项,目前统一收口在 GlobalVariables.mqh 这一个头文件里,没有散落到各 EA 或指标主体中。后续篇章会随图形对象增多继续补设置项,但结构不会变:所有 input 常量都在此集中声明。 当前版本已经暴露了两组关键映射:键盘热键与画线参数。比如周期切换绑定 U(升周期)和 D(降周期),趋势线用 T 拉、射线模式用 R 切、置顶图表用 Z;画线宽度写死为 2,线型默认 STYLE_SOLID。 直接在 MT5 的 MetaEditor 里打开该文件改键值就能重绑操作习惯,不用碰主逻辑。外汇与贵金属品种波动剧烈、杠杆风险高,热键误触可能瞬间下错单,建议先在模拟盘验证映射。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                 GlobalVariables.mqh |
class=class="str">"cmt">//|                     Copyright class="num">2020, MetaQuotes Software Corp. |
class=class="str">"cmt">//|                     [MQL5官方文档] |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#class="kw">property copyright "Copyright class="num">2020, MetaQuotes Software Corp."
class="macro">#class="kw">property link      "[MQL5官方文档]
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| File describing parameters available to the user                  |
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Key settings                                                      |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="kw">input class="type">class="kw">string  Keys="=== Key settings ===";
class="kw">input class="type">class="kw">string  Up_Key="U";                 class=class="str">"cmt">// Switch timeframe up
class="kw">input class="type">class="kw">string  Down_Key="D";               class=class="str">"cmt">// Switch timeframe down
class="kw">input class="type">class="kw">string  Trend_Line_Key="T";         class=class="str">"cmt">// Trend line
class="kw">input class="type">class="kw">string  Switch_Trend_Ray_Key="R";   class=class="str">"cmt">// Indication of a trend line ray
class="kw">input class="type">class="kw">string  Z_Index_Key="Z";            class=class="str">"cmt">// Indication of the chart on top
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Size settings                                                     |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="kw">input class="type">class="kw">string  Dimensions="=== Size settings ===";
class="kw">input class="type">int     Trend_Line_Width=class="num">2;         class=class="str">"cmt">// Trend line width
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Display styles                                                   |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="kw">input class="type">class="kw">string  Styles="=== Display styles ===";
class="kw">input ENUM_LINE_STYLE    Trend_Line_Style=STYLE_SOLID;     class=class="str">"cmt">// Trend line style

class=class="str">"cmt">//+------------------------------------------------------------------+

「辅助参数与隐藏前缀的取舍」

这段配置决定了 EA 在画趋势线时的「脾气」:是否射线延伸、跨周期显隐、创建即选中,以及左右分形臂长。Is_Trend_Ray 默认 false,代表趋势线以线段收口而非无限射线;若做日内突破跟踪,改成 true 可能更贴合视觉。 Is_Change_Timeframe_On_Create 默认 true,意即较高周期上自动隐藏本周期对象,避免图表被低周期线糊满;Is_Select_On_Create 与 Is_Different_Colors 同为 true,创建时自动选中且按周期换色,方便立刻拖拽微调。 Fractal_Size_Left / Right 都是 1,说明极值判定只看相邻 1 根 K 线,属于最紧的刺透判定;遇到滑点大的贵金属品种,可能需调到 2~3 才不易被毛刺误触。外汇与贵金属杠杆高,参数改动前建议先开 MT5 策略测试器用历史数据验证。 前缀与配色写死在代码里不进输入项:Trend_Line_Prefix 为 "Trend_",周期色从 mn1 的 Crimson 到 m1 的 DarkViolet 共 10 档。想统一改色直接动代码里的 color 常量即可,不用碰 EA 面板。

MQL5 / C++
class=class="str">"cmt">//| Other parameters   
class=class="str">"cmt">//+------------------------------------------------------------------+
class="kw">input class="type">class="kw">string      Others="=== Other parameters ===";

class="kw">input class="type">bool        Is_Trend_Ray=class="kw">false;             class=class="str">"cmt">// Trend line - ray
class="kw">input class="type">bool        Is_Change_Timeframe_On_Create = true;     class=class="str">"cmt">// Hide objects on higher timeframes?
                                                                    class=class="str">"cmt">//  (true - hide, class="kw">false - show)
class="kw">input class="type">bool        Is_Select_On_Create=true;             class=class="str">"cmt">// Select upon creation
class="kw">input class="type">bool        Is_Different_Colors=true;             class=class="str">"cmt">// Change colors for times
class=class="str">"cmt">// Number of bars on the left and on the right
class=class="str">"cmt">// for trend line and fan extreme points
class="kw">input class="type">int         Fractal_Size_Left=class="num">1;             class=class="str">"cmt">// Size of the left fractal
class="kw">input class="type">int         Fractal_Size_Right=class="num">1;             class=class="str">"cmt">// Size of the right fractal
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Name prefixes of drawn shapes(can be change only in code,        |
class=class="str">"cmt">//| not visible in EA parameters)                                     |
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">// class="type">class="kw">string   Prefixes="=== Prefixes ===";

class="type">class="kw">string   Trend_Line_Prefix="Trend_";             class=class="str">"cmt">// Trend line prefix
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Colors for objects of one timeframe(can be changed only in code,|
class=class="str">"cmt">//| not visible in EA parameters)                                     |
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">// class="type">class="kw">string TimeframeColors="=== Time frame colors ===";
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=class="str">"cmt">//--- Auxiliary constant for displaying error messages
class="macro">#define DEBUG_MESSAGE_PREFIX "=== ",__FUNCTION__," === "
class=class="str">"cmt">//--- Constants for describing the main timeframes when drawing

◍ 用位掩码拼出周期包含关系

在 MT5 里给图表对象设定时间周期可见性时,逐个写周期常量既啰嗦又难维护。把 OBJ_PERIOD_* 系列常量做按位或,就能用一条宏表达「某周期及其以下所有小周期」的可见集合。 上面这段预处理定义从 M1 起步,逐级把更小周期用位或叠进去:PERIOD_LOWER_M5 实际等于 M1|M5 的位掩码,PERIOD_LOWER_H1 则覆盖了 M1 到 H1 共 6 个周期位。这样在创建对象时传 PERIOD_LOWER_H1,对象便只会在 H1 及更短周期图表上显示。 外汇与贵金属杠杆品种波动剧烈,用这类宏控制对象显示范围时,仍要在多周期回测里确认对象不会被误隐藏,高风险环境下显示逻辑错漏可能直接干扰信号判断。

MQL5 / C++
class="macro">#define PERIOD_LOWER_M5 OBJ_PERIOD_M1|OBJ_PERIOD_M5
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">//+------------------------------------------------------------------+

支撑绘图的非绘图工具函数

在 MT5 里写交互式画线脚本,真正麻烦的往往不是 ObjectCreate 本身,而是周期切换、极值定位、唯一命名这些杂活。这些逻辑通常塞进一个 Utilities.mqh 之类的头文件,和绘图代码解耦,方便在不同 EA/脚本间复用。 顺序切周期是最常被调用的小函数:先在建好的时间帧数组里用 Period() 抓当前周期,定位下标后把下一个元素丢给 ChartSetSymbolPeriod。注意数组若写死 MT5 全部周期常量,在 MQL4 环境会丢失兼容性,跨平台项目别这么干。 找极值靠 GetNearestExtremumBarNumber,参数能控制向左还是向右搜、极值左右各比几根 Bar。画趋势线时一般取鼠标右侧最近两个极值,套这个函数就能拿到 Bar 序号。对象命名则建议用类型前缀加自增数字,GlobalVariables.mqh 里能查前缀表;编号搜索有两条路,主算法扫同前缀对象填“空隙”,另一算法按总数递增避让不同后缀,草叉集合那种多后缀场景得用第二条。 算像素距离时,最稳的是先测相邻两根 Bar 的像素差再乘系数,比直接读窗口坐标抗缩放。斐波那契级别若想用逗号字符串在 EA 参数里配,就用字符串转 double 数组的函数喂给 MQL 接口。外汇和贵金属波动大、杠杆高,这类辅助函数只解决坐标与命名,不构成任何方向判断。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                        Utilites.mqh |
class=class="str">"cmt">//|                   Copyright class="num">2020, MetaQuotes Software Corp. |
class=class="str">"cmt">//|                     [MQL5官方文档] |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#class="kw">property copyright "Copyright class="num">2020, MetaQuotes Software Corp."
class="macro">#class="kw">property link      "[MQL5官方文档]
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Class describing auxiliary functions                            |
class=class="str">"cmt">//+------------------------------------------------------------------+
class CUtilites
  {
class="kw">public:
  class=class="str">"cmt">//--- Changes the timeframe to the next one on the toolbar
  class="kw">static class="type">void       ChangeTimeframes(class="type">bool isUp);
  class=class="str">"cmt">//--- Converts class="type">class="kw">string command constants to keycodes
  class="kw">static class="type">int        GetCurrentOperationChar(class="type">class="kw">string keyString);
  class=class="str">"cmt">//--- Switches layers in charts(the chart is on top of all objects)
  class="kw">static class="type">void       ChangeChartZIndex(class="type">void);
  class=class="str">"cmt">//--- Returns the number of the nearest extreme bar
  class="kw">static class="type">int        GetNearestExtremumBarNumber(
     class="type">int starting_number=class="num">0,
     class="type">bool is_search_right=class="kw">false,
     class="type">bool is_up=class="kw">false,
     class="type">int left_side_bars=class="num">1,
     class="type">int right_side_bars=class="num">1,
     class="type">class="kw">string symbol=NULL,
     ENUM_TIMEFRAMES timeframe=PERIOD_CURRENT
  );
  class=class="str">"cmt">//--- Returns the class="type">color for the current timeframe
  class="kw">static class="type">color      GetTimeFrameColor(class="type">long allDownPeriodsValue);
  class=class="str">"cmt">//--- Returns a list of all timeframes lower than the current one(including the current one)
  class="kw">static class="type">long       GetAllLowerTimeframes(class="type">int NeededTimeframe=PERIOD_CURRENT);
  class=class="str">"cmt">//--- Coordinates of the straight line. Writes numbers of extreme bars to points p1 and p2
  class="kw">static class="type">void       SetExtremumsBarsNumbers(class="type">bool _is_up,class="type">int &p1, class="type">int &p2);

常见问题

用静态映射函数把光标屏幕坐标换算成时间索引与价格刻度,避免每次重画都重新计算,直接调类方法即可。
原文把用户参数塞进 GlobalVariables 并加隐藏前缀区分,实战中建议用统一前缀+命名规范防止冲突,别裸写变量名。
可以,小布能读取你的工具配置与坐标逻辑,提示静态类映射异常或全局变量前缀冲突,省去手动排错。
位掩码让多周期组合用一个整数表达,判断包含只做按位与,比枚举数组更省内存且判断更快。
至少备好坐标变换、范围裁剪和对象存在性检查,缺了这些绘图时会重复创建对象或越界报错。