手工制图表与交易工具箱:从智能交易系统到指标的进化与全新绘图工具(基础篇)
🛠️

手工制图表与交易工具箱:从智能交易系统到指标的进化与全新绘图工具(基础篇)

(1/3)· 当 40 个图表同时跑快捷键逻辑,终端卡顿只是时间问题,本篇拆解函数库重构与交叉线工具

含代码示例实战向 第 1/3 篇
很多交易者把键盘快捷键函数库直接塞进 EA 里跑,却没意识到多图表下线程开销会悄悄拖慢终端。把同一套逻辑迁移到指标,并只在活动窗口响应击键,是更顺手的做法。

MT5 里手动画工具也能被代码接管

MetaTrader 5 自带的手动图表工具(趋势线、矩形、斐波那契)靠人拖拽,重复劳动多且容易漏。第三部分的思路是:把这些手工动作封装成脚本和指标,让优化后的工具自动落位。

  • 年 1 月 24 日那篇示例文章在站内有 2932 次浏览、5 条评论,说明手动工具自动化这个方向实操者不少。它本质是用 MQL5 把「人眼识别 + 鼠标绘制」两步压缩成「条件触发 + 对象创建」。

外汇和贵金属杠杆高、滑点随机,任何自动绘图只解决「画得准」,不解决「看得对」。先在 MT5 策略测试器外开图表手动比对,确认触发逻辑再谈批量使用。

「快捷键函数库的五个模块分工」

这套快捷键函数库目前由五个 .mqh 文件构成,不是单文件黑盒,而是按职责拆开维护。主文件 Shortcuts.mqh 是唯一需要被 EA 或指标直接 include 的入口,它内部再逐级包含其余文件并完成初始化,击键逻辑也写在这里。 GlobalVariables.mqh 集中放全局变量,主要管线条颜色、线宽、延伸系数这类显示参数;Mouse.mqh 定义鼠标移动处理类,记录光标当前的像素坐标和“价格-时间”坐标,以及正处于哪条主线。 Utilites.mqh 放辅助函数,算柱线极值、指标线交点等不一定直接绘图但能判断图形状态的量;Graphics.mqh 则依据前面文件的数据真正画图,它的主函数会被 Shortcuts.mqh 调用。作者坦言分组并不严格,部分计算就夹在绘图函数里,当前结构够用,以后可能重排。 一个可验证的落点是:在 MT5 里新建指标,只需 #include <Shortcuts.mqh> 这一个文件,其余四个由它自动拉入,你能直接看到五个文件协同跑通。

◍ 只处理活动图表就够用了

最初函数库用智能交易系统跑快捷键,是因为每个 EA 在独立线程里运行,多图表挂载时终端不易卡。但 EA 本意是交易,这个程序不下一单,反而挡着用户在图表上跑别的 EA,于是改成了指标。 改成指标后马上遇到性能疑点:如果用户开了 40 个甚至更多图表选项卡,所有图表同时响应击键,键盘检查会拖垮终端。后来想通了——没必要全图都处理,击键只在当前活动窗口发生,非活动图表直接跳过即可。 上面代码块里高亮的那段就是落地办法:函数一进来先调 ChartGetInteger(0, CHART_BRING_TO_TOP),若返回 false 说明图表不在最前,直接 return。我没测过精确的百分比提速,但用过大量选项卡的真实用户反馈,转成指标后响应反而更跟手。 这个库本身只干偶发活儿,不在每次 tick 里被调用,资源占用时间很短。真正吃资源的是极值搜索函数,目前没找到更快写法,不过它只在画直线等少数造型时触发,频率低到可以忽略。其余函数调用更少、速度够快,不在优化讨论范围内。

MQL5 / C++
class=class="str">"cmt">/* Shortcuts.mqh */
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">//...
  if(ChartGetInteger(class="num">0,CHART_BRING_TO_TOP)==class="kw">false)
   {
    class="kw">return;
   }

 class=class="str">"cmt">//...

把绘图类从外部文件里解耦出来

早先那版工具库图省事,全局设定直接写死,鼠标类也被一堆实用函数绑死。真要拿到不碰鼠标、不画图形的新项目里复用,还得把全局文件和鼠标类一起拽进去,纯属累赘。 这次重构的思路很直接:全局变量留着不动,但在主类里加一份私有字段副本,再配一对公开的 get/set 函数读写。上面那段 MQL5 片段就是样板——m_TrendLengthCoefficient 作为 static 私有字段,外部通过同名的 TrendLengthCoefficient() 重载来取或改。 代价是写起来啰嗦,光把现有设定都包一遍就挺磨人。但换来四个实在好处:类不再依赖外部文件,用哪个变量设哪个;运行时能改参,比如画扇形线时每条线长度是前一条两倍、角度岔开,每次绘制前重设 TrendLengthCoefficient 即可;内部数据可随意分组,对外接口不变;数据不一定驻内存,可以扔终端全局变量、文件甚至库里,按别的参数动态算。 重构完之后,鼠标类和实用类各自独立,绘图类只耦合这两者加一个派发器。外汇与贵金属自动化工具高风险,但这套解耦让你在 MT5 里把实用文件原样拖进别的项目验证复用,省掉不少接线活。

MQL5 / C++
<span class="keyword">class="kw">private</span>:
&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">/* Fields */</span>
&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">//---</span>
&nbsp;&nbsp;<span class="keyword">class="kw">static</span> <span class="keyword">class="type">int</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_TrendLengthCoefficient;
<span class="keyword">class="kw">public</span>:
&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">/* Methods */</span>
&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">//---</span>
&nbsp;&nbsp;<span class="keyword">class="kw">static</span> <span class="keyword">class="type">int</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TrendLengthCoefficient(<span class="keyword">class="type">void</span>) {<span class="keyword">class="kw">return</span> m_TrendLengthCoefficient;}
&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">//---</span>
&nbsp;&nbsp;<span class="keyword">class="kw">static</span> <span class="keyword">class="type">void</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TrendLengthCoefficient(<span class="keyword">class="type">int</span> _coefficient) {m_TrendLengthCoefficient=_coefficient;}

「用 X 键在任意位置打交叉线」

老版本函数库画趋势线时,末端会带一条由垂直线和水平线拼出的交叉线,用来读时间和价位。想把它放到图表任意点,得先按 H 再按 I,两步操作在盯盘时有点碍事。 新版本直接给了独立的交叉线工具:光标移到目标位,按 X 键就生成。底层没啥魔法,就是调了之前文章里写过的指示线绘制函数,坐标逻辑也简单——传了参数就用传的值,没传就抓鼠标指针的当前位置。 代码里 DrawCross 的默认参数是 _time=-1、_price=-1,命中缺省就走 CMouse::Time() 和 CMouse::Price() 取实时光标位;否则价格还会过一道 NormalizeDouble(_price,Digits()) 对齐品种小数位。最后两行 DrawSimple 分别画 OBJ_HLINE 和 OBJ_VLINE,交叉线就出来了。 触发靠 Shortcuts.mqh 里的按键事件:全局变量 Cross_Key 默认赋 "X",ChartEvent 收到 KEYDOWN 且键值匹配时调 m_graphics.DrawCross()。想换键位的,改 input string Cross_Key 即可,外汇和贵金属波动剧烈,这类辅助线只帮定位、不预示方向。

MQL5 / C++
class=class="str">"cmt">/* Graphics.mqh */
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Draws a crosshair at specified coordinates. If the coordinates  |
class=class="str">"cmt">//|  are not set, the mouse pointer coordinates are used.           |
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Parameters:                                                     |
class=class="str">"cmt">//|  class="type">class="kw">datetime _time - crosshair time                                |
class=class="str">"cmt">//|  class="type">class="kw">double _price - price level                                    |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CGraphics::DrawCross(class="type">class="kw">datetime _time=-class="num">1,class="type">class="kw">double _price=-class="num">1)
 {
  class="type">class="kw">datetime time;
  class="type">class="kw">double price;
class=class="str">"cmt">//---
  if(_time==-class="num">1)
   {
    time=CMouse::Time();
   }
  else
   {
    time=_time;
   }
  if(_price==-class="num">1)
   {
    price=CMouse::Price();
   }
  else
   {
    price=NormalizeDouble(_price,Digits());
   }
  DrawSimple(OBJ_HLINE,time,price);
  DrawSimple(OBJ_VLINE,time,price);

 }
class=class="str">"cmt">/* GlobalVariables.mqh */

  class=class="str">"cmt">// ...

  class="kw">input class="type">class="kw">string  Cross_Key="X";                     class=class="str">"cmt">// Crosshair where the mouse was clicked

  class=class="str">"cmt">// ...
class=class="str">"cmt">/* Shortcuts.mqh */

  class="type">void CShortcuts::OnChartEvent( class=class="str">"cmt">/* ... */ )
   class="kw">switch(id)
    {
     case CHARTEVENT_KEYDOWN:
     
     class=class="str">"cmt">// ... 
     
     class=class="str">"cmt">//--- Draw a crosshair
     if(CUtilites::GetCurrentOperationChar(Cross_Key) == lparam)
      {
       m_graphics.DrawCross();
      }
    }

◍ 用 Q 键在 MT5 上拉任意极值趋势线

在 MT5 图表里,除了按固定左右柱数取极值画线,还能用 Q 键(对应外部变量 Free_Line_Key)直接以任意极值拉一条指示线。按一次 Q 进入绘制模式并标出第一个点;若点的位置不对,再按 Q 可取消,不会留下半截线。 确认第一点后,在第二个极值附近单击,标记消失、趋势线成型。这条线分两步完成,模式状态由类内 m_Clicks_Count 区分首次按键和二次单击,存活期与对象实例一致。 Q 线参数独立于 T 模式:比如 T 可设 4px 粗体、长度为极值间距四倍,Q 则能设细线、长度为间距两倍,互不干扰。若手绘线在 D1 与极值重合,切到 H4 往往左缘偏移——因为当日极值不总在日 K 开盘。该程序用 DeepPointSearch 函数在绘制时自动按小周期校正,省去手动切图调边的麻烦,盯 20 个以上图表时尤其减负。 外汇与贵金属波动剧烈、杠杆风险高,这类辅助画线只帮你定位概率性结构,不等于方向确认。打开 MT5 把 Free_Line_Key 设成习惯按键,按 Q 再单击就能验证这套两步绘制逻辑。

MQL5 / C++
class=class="str">"cmt">/* Shortcuts.mqh */
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="kw">switch(id)
  {
  class=class="str">"cmt">//...
  case CHARTEVENT_KEYDOWN:
    if(CUtilites::GetCurrentOperationChar(Free_Line_Key) == lparam)
    {
      m_graphics.ToggleFreeLineMode();  class=class="str">"cmt">// 切换任意线绘制模式(开/关)
      if(m_graphics.IsFreeLineMode()){  class=class="str">"cmt">// 若模式已开启
        m_graphics.DrawFreeLine(CMouse::Bar(),CMouse::Above());  class=class="str">"cmt">// 以当前鼠标位画第一点标记
      }
    }
  class=class="str">"cmt">//...
class=class="str">"cmt">/* Shortcuts.mqh */
      class=class="str">"cmt">//...
  case CHARTEVENT_CLICK:
      ChartClick_Handler();  class=class="str">"cmt">// 图表单击统一进处理函数
      break;
      class=class="str">"cmt">//...
  }
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Processing a click on a free chart field                         |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CShortcuts::ChartClick_Handler()
{
class=class="str">"cmt">//---
  if(m_graphics.IsFreeLineMode()){  class=class="str">"cmt">// 仅当处于自由线模式才响应
    m_graphics.DrawFreeLine(
      CMouse::Bar(),CMouse::Above()  class=class="str">"cmt">// 取单击处柱号与价格,完成第二点并连线
    );
  }
}
class=class="str">"cmt">/* Graphics.mqh */
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|  Draws a line by arbitrary specified extrema. In the current     |
class=class="str">"cmt">//|    implementation, the first extremum is set by a hot key        |
class=class="str">"cmt">//|    (Q by class="kw">default), the second is set by clicking near the       |
class=class="str">"cmt">//|    required top                                                 |
class=class="str">"cmt">//+------------------------------------------------------------------+
让小布替你跑这套
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到键盘交互与极值绘图的辅助视图,把重复劳动交给小布,你专注决策。

常见问题

每个 EA 在独立线程运行,互不干扰,多图表下键盘检查不会明显互相阻塞;但 EA 本意是交易,不交易的逻辑放指标里更轻巧。
Shortcuts.mqh 管击键主逻辑并包含其余文件;GlobalVariables.mqh 存颜色线宽等全局设置;Mouse.mqh 处理鼠标坐标与主线编号;Utilites.mqh 算极值和交点;Graphics.mqh 负责按数据绘图。
小布盯盘内置的 AIGC 看盘页已覆盖极值识别和交叉线辅助,无需手动移植 mqh,打开品种页就能用类似视图。
作者未给精确百分比,但多选项卡真实用户反馈改为指标并加活动窗口判断后,大量图表下操作明显更跟手。
它把光标在像素和价格-时间坐标的映射标准化,后续画趋势线和读极值都能复用同一套坐标,减少重复计算。