手工图表和交易工具包(第二部分)。 图表图形绘图工具·进阶篇
(2/3)· 还在鼠标拖拽画趋势线?这套 MQL5 函数库用快捷键把六种图形瞬间钉在正确极值点
画线对象的命名前缀与周期配色
想在 MT5 里用 EA 批量管理手动或自动画出的趋势线、扇形、分叉线,第一步是把对象名前缀定死。下面这段声明把所有图形类别的前缀收进一个字符串数组,索引 0 到 7 分别对应趋势线、水平线、垂直线、扇形、分叉线、带级别垂直线、短级别、长级别。 这些前缀只在源码里改,不会暴露到 EA 参数面板,所以你复制去用时若想换命名规则,得直接动代码而非界面。周期配色则按 MN1 到 M1 九档给了固定 color 常量,例如 D1 用 clrGoldenrod、H4 用 clrLimeGreen、M5 用 clrViolet,common_color 统一给 clrGray 兜底。 外汇与贵金属波动剧烈、杠杆风险高,这类自动绘图只辅助判势,不构成方向保证。开 MT5 把数组和 color 常量贴进 include 或 EA 头部,就能让不同周期图形一眼区分。
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 上同时显示,验证掩码是否生效。外汇与贵金属波动剧烈,多周期叠加观察仅作参考,实际信号概率随品种和时段变化。
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 其它类型直接拦掉,本函数只管这两种基元
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 传入验证。
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 画水平线,位置默认落在当前光标处。
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">//--- } }
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 出扇形。我曾看某扇形配置倾向价格回落,后来确实跌了——外汇与贵金属波动剧烈,这类形态只作概率参考,仓位需自担高风险。
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 |