使用MQL5轻松创建图形面板·综合运用
🖥️

使用MQL5轻松创建图形面板·综合运用

(3/3)· 新手也能照做的面板开发收尾篇,用同一案例对比裸写与调库的差异

案例拆解新手友好 第 3/3 篇
很多交易者以为面板必须找现成EA挂,其实自己写个按钮开关持仓比想得简单。但一上来就硬磕底层绘图,多半卡在坐标计算和重绘循环里放弃。本篇用同一功能演示两种做法,避开无效折腾。

◍ 用 ObjectSetInteger 把按钮摆上图表

在 MT5 里画一个可交互的 OBJ_EDIT 或按钮对象,光用 ObjectCreate 拿到句柄还不够,坐标、尺寸、字体、配色全得靠 ObjectSetInteger / ObjectSetString 后续写入,否则对象建出来也是个看不见的空壳。 创建失败时要立刻把错误码抛出来:Print(__FUNCTION__, ": failed to create \"Edit\" object! Error code = ", GetLastError()); 然后 return(false); 这套写法能让你在专家日志里直接定位是哪一行参数越界,不用瞎猜。 位置和大小走整型属性:OBJPROP_XDISTANCE / OBJPROP_YDISTANCE 定相对角落的偏移像素,OBJPROP_XSIZE / OBJPROP_YSIZE 定控件宽高;文字内容走 ObjectSetString 的 OBJPROP_TEXT,字体名和 OBJPROP_FONTSIZE 分开设,对齐方式用 OBJPROP_ALIGN 控。 交互细节容易漏:OBJPROP_READONLY 决定能否手敲改值,OBJPROP_SELECTABLE 配合 OBJPROP_SELECTED 管鼠标拖动,OBJPROP_ZORDER 抢鼠标点击事件优先级。角落基准 OBJPROP_CORNER 设错,x/y 就会从图表另一头算起——这是实盘面板错位的高频坑。 颜色三层分开:OBJPROP_COLOR 管文字、OBJPROP_BGCOLOR 管背景、OBJPROP_BORDER_COLOR 管边框;OBJPROP_BACK 切前后景,OBJPROP_HIDDEN 藏对象列表名。全部写完后 return(true),外汇与贵金属图表上挂这类控件属高风险界面操作,参数误设可能导致面板遮挡报价。

MQL5 / C++
Print(__FUNCTION__,
      ": failed to create \"Edit\" object! Error code = ", GetLastError());
      class="kw">return(class="kw">false);
   }
class=class="str">"cmt">//--- Setting the object coordinates x and y by using the ObjectSetInteger
   ObjectSetInteger(chart_ID, name, OBJPROP_XDISTANCE, x);
   ObjectSetInteger(chart_ID, name, OBJPROP_YDISTANCE, y);
class=class="str">"cmt">//--- Setting the object size by using the ObjectSetInteger function also
   ObjectSetInteger(chart_ID, name, OBJPROP_XSIZE, width);
   ObjectSetInteger(chart_ID, name, OBJPROP_YSIZE, height);
class=class="str">"cmt">//--- Setting the text by using ObjectSetString function
   ObjectSetString(chart_ID, name, OBJPROP_TEXT, text);
class=class="str">"cmt">//--- Setting the text font by using the ObjectSetString function also
   ObjectSetString(chart_ID, name, OBJPROP_FONT, font);
class=class="str">"cmt">//--- Setting the font size by using the ObjectSetInteger function
   ObjectSetInteger(chart_ID, name, OBJPROP_FONTSIZE, font_size);
class=class="str">"cmt">//--- Setting the type of text alignment in the object
   ObjectSetInteger(chart_ID, name, OBJPROP_ALIGN, align);
class=class="str">"cmt">//--- Setting the ability to edit, enable if it is(true) or cancel(class="kw">false) if you need a read-only mode
   ObjectSetInteger(chart_ID, name, OBJPROP_READONLY, read_only);
class=class="str">"cmt">//--- Setting the chart&class="macro">#x27;s corner, relative to which object coordinates are defined to set the location of the object.
   ObjectSetInteger(chart_ID, name, OBJPROP_CORNER, corner);
class=class="str">"cmt">//--- Setting the text class="type">color
   ObjectSetInteger(chart_ID, name, OBJPROP_COLOR, clr);
class=class="str">"cmt">//--- Setting the background class="type">color
   ObjectSetInteger(chart_ID, name, OBJPROP_BGCOLOR, back_clr);
class=class="str">"cmt">//--- Setting the border class="type">color of the object
   ObjectSetInteger(chart_ID, name, OBJPROP_BORDER_COLOR, border_clr);
class=class="str">"cmt">//--- Displaying in the foreground by(class="kw">false) or in the background by(true)
   ObjectSetInteger(chart_ID, name, OBJPROP_BACK, back);
class=class="str">"cmt">//--- Setting(true) to enable or(class="kw">false) to disable the mode of moving the label by mouse
   ObjectSetInteger(chart_ID, name, OBJPROP_SELECTABLE, selection);
   ObjectSetInteger(chart_ID, name, OBJPROP_SELECTED, selection);
class=class="str">"cmt">//--- Setting(true) if you need hiding or(class="kw">false) if you need the display of graphical object name in the object list
   ObjectSetInteger(chart_ID, name, OBJPROP_HIDDEN, hidden);
class=class="str">"cmt">//--- Setting the priority for receiving the event of a mouse click in the chart
   ObjectSetInteger(chart_ID, name, OBJPROP_ZORDER, z_order);
class=class="str">"cmt">//--- Returning(true) if successful execution
   class="kw">return(true);
   }
class="type">bool ButtonCreate(const class="type">long          chart_ID=class="num">0,
                  const class="type">class="kw">string        name="Button",
                  const class="type">int           sub_window=class="num">0,
                  const class="type">int           x=class="num">0,

「按钮对象的参数与创建落点」

在 MT5 自定义指标或 EA 里画一个可点击按钮,先得把位置、尺寸和样式参数定死。下面这段函数头把坐标原点锁在左上角(CORNER_LEFT_UPPER),按钮宽 50 像素、高 18 像素,字体用 Arial 10 号,文字写死成 "Button",背景填 RGB(236,233,216),边框设为 clrNONE 无描边。 这些常量直接写进函数签名,调用时不用再传,适合做标准控件模板。注意 hidden=true 代表该对象不参与常规对象列表显示,z_order=0 表示层级垫底,不会被后续图形盖住交互区。 真正落对象靠 ObjectCreate:图表 ID 和名字由外部传入,类型写 OBJ_BUTTON,子窗口编号 sub_window,初始 x/y 坐标先填 0,0,再由后面逻辑重定位。创建前 ResetLastError 清掉旧错误码,失败就用 Print(__FUNCTION__ 输出函数名方便排查——外汇与贵金属图表高频刷新,对象创建失败往往和图表重绘冲突有关,属于高风险环境下的常见坑。

MQL5 / C++
const class="type">int       y=class="num">0,
const class="type">int       width=class="num">50,
const class="type">int       height=class="num">18,
const ENUM_BASE_CORNER  corner=CORNER_LEFT_UPPER,
const class="type">class="kw">string        text="Button",
const class="type">class="kw">string        font="Arial",
const class="type">int       font_size=class="num">10,
const class="type">color        clr=clrBlack,
const class="type">color        back_clr=C&class="macro">#x27;class="num">236,class="num">233,class="num">216&class="macro">#x27;,
const class="type">color        border_clr=clrNONE,
const class="type">bool        state=class="kw">false,
const class="type">bool        back=class="kw">false,
const class="type">bool        selection=class="kw">false,
const class="type">bool        hidden=true,
const class="type">long        z_order=class="num">0)
  {
  ResetLastError();
  if(!ObjectCreate(chart_ID,name,OBJ_BUTTON,sub_window,class="num">0,class="num">0))
    {
    Print(__FUNCTION__,

用控件类拼一个下单面板

在 MT5 里手搓交易面板,最省事的办法是直接引用标准库里的控件类,而不是从头写 ObjectCreate。上面这段先引入了 Dialog、Button、Edit 和 Trade 四个头文件,用 CAppDialog 做容器,CButton 做买卖按钮,CEdit 做手数输入,CTrade 发单。 宏定义把面板尺寸钉死了:PANEL_WIDTH 是 116 像素,PANEL_HIEIGHT 写成 100(原文拼写如此,编译能过但建议改名),ROW_HEIGHT 为 20,按钮宽 50、高 20,买卖键横向间距靠 X 坐标 3 和 63 错开。 OnInit 里 panel.Create 在坐标 (10,20) 起手,宽高按宏算出来是 10+116+8=134 宽、20+100=120 高。buyBtn 和 sellBtn 分别用绿色背景配黑字“Buy”、红色背景配黑字“Sell”,字体 Arial 10 号,加进 panel 后就能在图表左上角看到两个并排按键。 外汇和贵金属杠杆高、滑点凶,这类面板只是把下单动作图形化,不替你判断方向;跑之前先在策略测试器用模拟账户验证按钮事件有没有接对 CTrade。

MQL5 / C++
class="macro">#include <Controls\Dialog.mqh>
class="macro">#include <Controls\Button.mqh>
class="macro">#include <Controls\Edit.mqh>
class="macro">#include <Trade\Trade.mqh>
class="macro">#define PANEL_NAME "Order Panel"
class="macro">#define PANEL_WIDTH class="num">116
class="macro">#define PANEL_HIEIGHT class="num">100
class="macro">#define ROW_HEIGHT class="num">20
class="macro">#define BUY_BTN_NAME "Buy BTN"
class="macro">#define SELL_BTN_NAME "Sell BTN"
class="macro">#define CLOSE_BTN_NAME "Close BTN"
class="macro">#define EDIT_NAME "Lot Size"
CAppDialog panel;
CButton buyBtn;
CButton sellBtn;
CButton closeBtn;
CEdit lotSize;
CTrade trade;
panel.Create(class="num">0,PANEL_NAME,class="num">0,class="num">10,class="num">20,class="num">10+PANEL_WIDTH+class="num">8,class="num">20+PANEL_HIEIGHT);
class="type">int OnInit()
  {
  class=class="str">"cmt">//buy button
  panel.Create(class="num">0,PANEL_NAME,class="num">0,class="num">10,class="num">20,class="num">10+PANEL_WIDTH+class="num">8,class="num">20+PANEL_HIEIGHT);
  buyBtn.Create(class="num">0,BUY_BTN_NAME,class="num">0,class="num">3,class="num">40,class="num">0,class="num">0);
  buyBtn.Width(class="num">50);
  buyBtn.Height(ROW_HEIGHT);
  buyBtn.ColorBackground(clrGreen);
  buyBtn.Text("Buy");
  buyBtn.Font("Arial");
  buyBtn.FontSize(class="num">10);
  buyBtn.Color(clrBlack);
  buyBtn.ColorBorder(clrBlack);  
  panel.Add(buyBtn);
  class=class="str">"cmt">//sell button
  sellBtn.Create(class="num">0,SELL_BTN_NAME,class="num">0,class="num">63,class="num">40,class="num">0,class="num">0);
  sellBtn.Width(class="num">50);
  sellBtn.Height(ROW_HEIGHT);
  sellBtn.ColorBackground(clrRed);
  sellBtn.Text("Sell");
  sellBtn.Font("Arial");
  sellBtn.FontSize(class="num">10);
  sellBtn.Color(clrBlack);
  sellBtn.ColorBorder(clrBlack);
  panel.Add(sellBtn);
  class=class="str">"cmt">//lotSize

◍ 把手填手数接进买卖按钮

上面这段是面板初始化收尾与图表事件联动的核心。lotSize 编辑框在坐标 (0,4)、宽 108 像素、高用 ROW_HEIGHT 常量,默认文本 "0.01",Arial 10 号字,白底黑字黑边,最后 panel.Add 挂进容器,panel.Run() 启动,Init 返回 INIT_SUCCEEDED。 OnDeinit 里只做一件事:panel.Destroy() 释放资源,避免指标/EA 重载时残留图形对象。 真正下单发生在 OnChartEvent:当 id 等于 CHARTEVENT_OBJECT_CLICK 且 sparam 是 buyBtn 名称时,把 lotSize.Text() 用 StringToDouble 转成 double,调 trade.Buy(lot);点 sellBtn 则走 trade.Sell(lot)。手数完全取决于编辑框里填的字符串,默认 0.01。 开 MT5 新建 EA 把这段贴进工程,改 EDIT_NAME 和按钮 Name(),就能在图表上点按钮按框内手数市价进出。外汇和贵金属杠杆高,实盘前务必先在策略测试器用 0.01 验证点击逻辑。

MQL5 / C++
  lotSize.Create(class="num">0,EDIT_NAME,class="num">0,class="num">4,class="num">10,class="num">0,class="num">0);
  lotSize.Width(class="num">108);
  lotSize.Height(ROW_HEIGHT);
  lotSize.Text("class="num">0.01");
  lotSize.Font("Arial");
  lotSize.FontSize(class="num">10);
  lotSize.ColorBackground(clrWhite);
  lotSize.Color(clrBlack);
  lotSize.ColorBorder(clrBlack);
  panel.Add(lotSize);
  class=class="str">"cmt">//run the panel
  panel.Run();
  class="kw">return(INIT_SUCCEEDED);
  }
class="type">void OnDeinit(const class="type">int reason)
  {
  panel.Destroy();
  }
class="type">void  OnChartEvent(const class="type">int id, const class="type">long &lparam, const class="type">class="kw">double &dparam, const class="type">class="kw">string &sparam)
  {
  panel.ChartEvent(id,lparam,dparam,sparam);
  if(id==CHARTEVENT_OBJECT_CLICK)
    {
      if(sparam==buyBtn.Name())
        {
         class="type">class="kw">double lot=StringToDouble(lotSize.Text());
         trade.Buy(lot);
        }
      else
       if(sparam==sellBtn.Name())
         {
          class="type">class="kw">double lot=StringToDouble(lotSize.Text());
          trade.Sell(lot);
         }
    }
  }

「记住这一条就够了」

回看附件里的两个 mq5 文件就能直观比对:用 Panels/Dialog 类写的 simpleOrderPanel.mq5 只有 2.91 KB,而不靠类、纯手写控件的 simpleOrderPanelWithoutClass.mq5 体积飙到 18.2 KB。省下的 15 KB 代码量,基本就是你在重复画按钮、管坐标、接事件上少掉的头发出量。 面板类真正的价值不在炫技,而是把下单、撤单这类机械活儿封装掉,让你把注意力放回价格行为和盘口本身。外汇与贵金属保证金交易波动剧烈、杠杆风险高,任何面板都只是执行辅助,不替你判断方向。 想给按钮加悬停提示,别去调控件对象没有的 ToolTip 方法,直接用原生函数最稳: ObjectSetString(0, "Your object name", OBJPROP_TOOLTIP, "Your text"); 这行在 MT5 里对图表对象一律有效,比绕进类成员碰壁省事。开 MT5 把那两个文件都编译跑一遍,手感比读十篇文都实在。

MQL5 / C++
ObjectSetString(class="num">0, "Your object name", OBJPROP_TOOLTIP, "Your text");
把重复劳动交给小布
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到面板交互逻辑如何映射到实盘提醒;把重复劳动交给小布,你专注决策。

常见问题

按文中划分,按钮、数字、字符串、形状、颜色都属面板可交互对象,开发者决定它们触发什么事件,交易员看的是呈现的信息与操作入口。
当你需要完全控制绘制顺序或做极简指示面板时,裸写仍有价值;它耗时但能反推你对终端绘图机制的理解。
小布目前以内置诊断与提醒为主,不代写完整面板源码;但你能把它的信号结构作为面板字段设计的参考,减少试错。
对比能显出类库封装省下的精力在哪,也保留新手从零写以练编程逻辑的余地,两者互补而非互斥。
终端卡顿或网络延迟可能让面板按钮响应滞后,高频操作仍倾向快捷键;贵金属与外汇杠杆高,任何面板仅作辅助,风险自担。