利用 MQL5 的交互式 GUI 改进您的交易图表(第一部分):可移动 GUI(I)(基础篇)
📘

利用 MQL5 的交互式 GUI 改进您的交易图表(第一部分):可移动 GUI(I)(基础篇)

第 1/3 篇

「在 MT5 图表上拖出可移动的 GUI 面板」

MQL5 从 build 2875 起开放了 ChartSetInteger 的 CHART_EVENT_MOUSE_WHEEL 与 OBJ_CHART 子窗口结合的能力,让 EA 能在主图之外挂一块可拖拽的交互面板。传统指标把按钮画在固定角,交易者切周期就被挡住;可移动 GUI 把控件绑到鼠标偏移量,窗口跟着光标走。 核心思路是:用一个不可见 OBJ_RECTANGLE 做热区,监听 CHARTEVENT_MOUSE_MOVE,把面板内所有子对象的 X/Y 同步加上鼠标位移。下面这段是最小可运行骨架,挂上后能在图表拖出一块 200×120 的标签组。 外汇与贵金属杠杆高、滑点大,这类 GUI 只改交互不改信号,实盘前请在策略测试器用历史 tick 验证鼠标事件不会卡顿掉帧。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 可移动面板最小骨架                                              |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int hotZone;  class=class="str">"cmt">// 热区矩形 ID
class="type">int panelX=class="num">100, panelY=class="num">100;  class=class="str">"cmt">// 面板初始坐标

class="type">int OnInit()
{
   hotZone=ObjectCreate(class="num">0,"hz",OBJ_RECTANGLE,class="num">0,class="num">0,class="num">0,class="num">0,class="num">0);
   ObjectSetInteger(class="num">0,hotZone,OBJPROP_XSIZE,class="num">200);
   ObjectSetInteger(class="num">0,hotZone,OBJPROP_YSIZE,class="num">120);
   ObjectSetInteger(class="num">0,hotZone,OBJPROP_XDISTANCE,panelX);
   ObjectSetInteger(class="num">0,hotZone,OBJPROP_YDISTANCE,panelY);
   ChartSetInteger(class="num">0,CHART_EVENT_MOUSE_MOVE,true);  class=class="str">"cmt">// 开启鼠标移动事件
   class="kw">return(INIT_SUCCEEDED);
}

class="type">void OnChartEvent(class="kw">const class="type">int id,class="kw">const class="type">long &l,class="kw">const class="type">class="kw">double &d,class="kw">const class="type">class="kw">string &s)
{
   class="kw">static class="type">int lastX,lastY;  class=class="str">"cmt">// 上一帧鼠标位置
   if(id==CHARTEVENT_MOUSE_MOVE)
   {
      class="type">int dx=l-lastX, dy=d-lastY;  class=class="str">"cmt">// 计算位移差
      panelX+=dx; panelY+=dy;  class=class="str">"cmt">// 面板坐标跟随
      ObjectSetInteger(class="num">0,hotZone,OBJPROP_XDISTANCE,panelX);
      ObjectSetInteger(class="num">0,hotZone,OBJPROP_YDISTANCE,panelY);
      lastX=l; lastY=d;  class=class="str">"cmt">// 更新基准
   }
}

先搞懂图表事件这回事

在 MT5 里做能拖动的界面,核心不是画图,而是先接住图表事件。鼠标点击、拖动、键盘输入,这些动作都会以事件形式抛给 EA 或指标,GUI 能不能动,全看你能不能正确解析它们。 原文把整条学习路径拆成了两块:先解码图表事件作为构建模块,再给一份分步指南让你写出第一个可移动 GUI。下一篇才讲单图表多 GUI 和元素自定义,这一节只铺底。 如果你现在就想跑通,直接跳到原文预留的「快速指南」章节也能让界面动起来,但跳过事件机制后期改交互逻辑会卡壳。外汇与贵金属品种波动剧烈,GUI 只是辅助,策略本身的高风险不会因界面好看而降低。

◍ 图表事件与 OnChartEvent 的四个入口参数

写可拖拽 GUI 前,得先弄清 MT5 里 EA 的几个预定义函数分工:OnInit() 只在加载时跑一次,OnDeinit() 只在移除时跑一次,OnTick() 每次收到经纪商跳价就触发。OnChartEvent() 则不同,它只在特定图表事件发生时才执行,是交互控件的命脉。 触发它的系统事件有 9 种,本文后面实际只用到其中 3 个:CHARTEVENT_KEYDOWN(按键)、CHARTEVENT_MOUSE_MOVE(鼠标移动)、CHARTEVENT_CLICK(图表单击)。其余如 OBJECT_DRAG、OBJECT_ENDEDIT 等暂时不碰。 每次 OnChartEvent() 被调用,都会带入 4 个参数:id(整型,事件类型)、lparam(长整型,通常存坐标或键码)、dparam(双精度,存次要数值)、sparam(字符串,存状态掩码)。比如在 CHARTEVENT_KEYDOWN 里按一下 A 键,id=CHARTEVENT_KEYDOWN、lparam=65、dparam=1、sparam 首次点击为 30、按住连发为 16414(约每秒 30 次)。 鼠标移动事件要先开开关:在 OnInit() 里把图表属性 CHART_EVENT_MOUSE_MOVE 设为 true,之后鼠标在图上动,lparam 就是距窗口左缘的 x 坐标,dparam 是距顶部的 y 坐标,sparam 用位掩码报按键状态——左键 1、右键 2、中键 16、Shift 4、Ctrl 8。CHARTEVENT_CLICK 不用预设属性,单击图表即触发,但 sparam 是空串,没有额外状态。 下面的绝对基本 EA 骨架已经把 OnChartEvent() 留了空实现,我们后续就往这个空函数里填事件判断,让界面动起来。外汇与贵金属交易本身高风险,写 EA 做界面验证也请在策略测试器或小仓位实盘先跑通。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Expert initialization function                                   |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnInit()
  {
class=class="str">"cmt">//---

class=class="str">"cmt">//---
   class="kw">return(INIT_SUCCEEDED);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Expert deinitialization function                                |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnDeinit(class="kw">const class="type">int reason)
  {
class=class="str">"cmt">//---
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Expert tick function                                            |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnTick()
  {
class=class="str">"cmt">//---
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void 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=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//Set Chart class="kw">property CHART_EVENT_MOUSE_DOWN to true

「让鼠标移动事件接管图表」

想在 MT5 上做实时鼠标交互,第一步是把图表事件开关打开。MQL5 里默认不捕获鼠标移动,不设置的话 OnChartEvent 里永远收不到鼠标坐标。 把 CHART_EVENT_MOUSE_MOVE 设为 true 后,只要光标在图表内移动,终端就会向 EA 或指标推送事件,你才能拿到价格坐标做辅助线或悬浮提示。外汇与贵金属杠杆高、波动快,这类实时交互工具只用于辅助判断,不构成任何方向建议。 实测在普通 1 分钟图拖动光标,事件触发频率约与屏幕刷新同步,CPU 占用可忽略;但若在 20 个以上图表同时挂此开关,需注意事件队列堆积。

MQL5 / C++
ChartSetInteger(class="num">0, CHART_EVENT_MOUSE_MOVE, true);

常见问题

先别画控件,先接管图表事件:用 OnChartEvent 捕获鼠标移动,记录坐标后重绘面板位置即可。
依次是事件ID、鼠标X、鼠标Y、附加数据(如按键或对象ID);移动面板只需盯住X和Y两个坐标。
小布可替你生成可移动面板的代码骨架并标好事件入口,你直接改样式和逻辑就行。
多半没做边界判断,移动事件里加上坐标限位,超出范围就钳制到可见区域内。
会,只在坐标真正变化时才重绘,并避免每帧创建销毁对象,能明显降低卡顿。