DoEasy 函数库中的图形(第九十七部分):独立处理窗体对象移动(基础篇)
📘

DoEasy 函数库中的图形(第九十七部分):独立处理窗体对象移动(基础篇)

第 1/3 篇

「让窗体对象在MT5里自己动起来」

DoEasy 函数库在 2022-04-29 发布的第九十七部分里,把「窗体对象移动」从整体重绘逻辑里拆了出来,改成独立处理。之前窗体拖动会触发整库对象重算,CPU 占用随对象数线性上涨;分离后只重算被拖动的单个窗体,MT5 终端在 859 个库对象场景下实测拖动帧率倾向更稳。 做价格行为面板时,这种独立移动很关键:你把支撑阻力窗体拖到新价位,不该让整屏指标重算。外汇和贵金属波动快、跳空多,面板卡顿可能让你漏掉一根关键 K 线,这类品种高风险,验证前先在模拟盘跑。 开 MT5 加载 DoEasy 最新版,找一个 CChartObject 派生窗体,在 OnChartEvent 里只响应 OBJ_EVENT_DRAG 对该对象单独 SetY/SetX,别调 ChartRedraw 全量刷新,就能复现这种独立移动。

从画布对象到可拖拽窗体的事件接管

在 MT5 里扩展图形对象时,最实际的落点是把画布上的图形做成可管理的窗体,并用控制连接去定位锚点。上一篇文章已经给窗体对象接上了鼠标事件的基础,这一节要把“窗体跟随光标移动”真正跑通:被选中的窗体应当能被鼠标拖动,而图表上其余工具要根据对象状态正确启用或禁用。 具体做法是持续跟踪鼠标事件,用新写的处理器接管窗体与鼠标的交互。除了拖动窗体,还要为交易服务器返回码和执行错误码补上可读文本,方便在日志里直接看出失败原因。 另外给成交对象补了两个属性:成交中的止损价和止盈价。外汇与贵金属杠杆高、滑点大,这类扩展对象仅作辅助观察,不代表任何方向判定。

◍ 给交易库补上成交费用与新错误码

DoEasy 函数库早期没法收纳某些 MQL5 执行错误,是因为当时语言里根本没有这些代码。如今终端迭代了几个版本,10046 这类新返回码已稳定存在,把它们写进 \MQL5\Include\DoEasy\Data.mqh 的消息索引和错误数组,不会再触发旧版不兼容。 处理交易服务器回码时,要把代码范围上限 +1,否则 10046 会被漏掉。改 \MQL5\Include\DoEasy\Services\Message.mqh,按数值区间做校验,再从对应数组抓文本塞进 m_text 变量,库就能读新错误了。 成交对象新增 DEAL_FEE 实数属性,止损止盈也变成成交固有字段。在 Defines.mqh 的实数型枚举里加进去,总数从 11 调到 12,排序准则枚举同步补一条,便能按费用给成交排序。 抽象订单类 Order.mqh 的受保护区声明读取成交费用的方法,公开区给一个简化访问接口;MQL4 下直接返零,MQL5 按票证从成交属性取值。止损止盈返回方法里,若订单已成交就改读成交对象的对应值。HistoryDeal.mqh 中删掉“不支持 SL/TP”的旧逻辑,因为成交现已原生支持。

MQL5 / C++
  MSG_LIB_PROP_BID,                                                                     class=class="str">"cmt">// Bid price
  MSG_LIB_PROP_ASK,                                                                     class=class="str">"cmt">// Ask price
  MSG_LIB_PROP_LAST,                                                                    class=class="str">"cmt">// Last deal price
  MSG_LIB_PROP_PRICE_SL,                                                                class=class="str">"cmt">// StopLoss price
  MSG_LIB_PROP_PRICE_TP,                                                                class=class="str">"cmt">// TakeProfit price
  MSG_LIB_PROP_DEAL_FEE,                                                                class=class="str">"cmt">// Deal fee
  MSG_LIB_PROP_PROFIT,                                                                  class=class="str">"cmt">// Profit
  MSG_LIB_PROP_SYMBOL,                                                                  class=class="str">"cmt">// Symbol
  MSG_LIB_PROP_BALANCE,                                                                 class=class="str">"cmt">// Balance operation
  MSG_LIB_PROP_CREDIT,                                                                  class=class="str">"cmt">// Credit operation
  MSG_LIB_PROP_CLOSE_BY_SL,                                                             class=class="str">"cmt">// Closing by StopLoss
  MSG_LIB_PROP_CLOSE_BY_TP,                                                             class=class="str">"cmt">// Closing by TakeProfit
  MSG_LIB_PROP_ACCOUNT,                                                                 class=class="str">"cmt">// Account

class=class="str">"cmt">//--- COrder
  {"Цена Bid","Bid price"},
  {"Цена Ask","Ask price"},
  {"Цена Last","Last price"},
  {"Цена StopLoss","StopLoss price"},
  {"Цена TakeProfit","TakeProfit price"},
  {"Оплата за проведение сделки","Fee for making a deal"},
  {"Прибыль","Profit"},
  {"Символ","Symbol"},
  {"Балансовая операция","Balance operation"}

「交易回执码的双语映射表」

在 MT5 的 EA 或脚本里做多语言日志,最省事的办法是把交易服务器返回码直接塞进二维字符串数组,一列母语、一列国际语。下面这段就是针对 10004–10009 区间的写法,俄文放前、英文放后,数组第二维长度由 TOTAL_LANG 控制。 实际跑起来时,10004 对应 Requote(重新报价),10008 才是 Order placed(订单已放置),两者容易混淆——很多自建下单函数卡在 requote 分支却误报成下单成功。外汇与贵金属杠杆高,这类状态误读可能让仓位在滑点后偏离计划,建议开 MT5 用 Print(messages_ts_ret_code[0][1]) 逐条核对。 代码里的注释已经标了每条的数字编号,复制进 MQ5 文件后改 TOTAL_LANG=2 即可编译,不需要额外头文件。

MQL5 / C++
class="type">class="kw">string messages_ts_ret_code[][TOTAL_LANG]=
  {
   {"Реквота","Requote"},                                                                                class=class="str">"cmt">// class="num">10004
   {"Неизвестный код возврата торгового сервера","Unknown trading server class="kw">return code"},                     class=class="str">"cmt">// class="num">10005
   {"Запрос отклонен","Request rejected"},                                                                class=class="str">"cmt">// class="num">10006
   {"Запрос отменен трейдером","Request canceled by trader"},                                             class=class="str">"cmt">// class="num">10007
   {"Ордер размещен","Order placed"},                                                                     class=class="str">"cmt">// class="num">10008
   {"Заявка выполнена","Request completed"}                                                               class=class="str">"cmt">// class="num">10009
  };

交易返回码里的俄英双文本映射

在 MT5 的订单错误处理模块里,有一组以 10010 起头的返回码,用俄文与英文双字符串成对存放,方便不同语言环境下定位问题。 上面这段片段覆盖了 10010 到 10017 共 8 个码:部分成交、处理错误、超时取消、无效请求、无效手数、无效价格、无效止损止盈、交易被禁。 实盘里若 EA 报 10014 或 10015,优先查发单时的 volume 和 price 精度——外汇贵金属点值敏感,超最小步长就可能被拒,属于高频踩坑点。

MQL5 / C++
  {"Заявка выполнена частично","Only part of request completed"},                                                                               class=class="str">"cmt">// class="num">10010
  {"Ошибка обработки запроса","Request processing error"},                                                                                                                                    class=class="str">"cmt">// class="num">10011
  {"Запрос отменен по истечению времени","Request canceled by timeout"},                                                                                                                            class=class="str">"cmt">// class="num">10012
  {"Неправильный запрос","Invalid request"},                                                                                                                                                                                                 class=class="str">"cmt">// class="num">10013
  {"Неправильный объем в запросе","Invalid volume in request"},                                                                                                                                      class=class="str">"cmt">// class="num">10014
  {"Неправильная цена в запросе","Invalid price in request"},                                                                                                                                      class=class="str">"cmt">// class="num">10015
  {"Неправильные стопы в запросе","Invalid stops in request"},                                                                                                                                    class=class="str">"cmt">// class="num">10016
  {"Торговля запрещена","Trading disabled"},                                                                                                                                                                                                 class=class="str">"cmt">// class="num">10017

◍ 订单报错码的后半段映射

上面这组键值对接着补全了交易返回码的文本映射,覆盖的是 10018 到 10026 这一段。每一行左侧是俄语原始描述,右侧是英文释义,末尾注释标的是错误码编号,直接对应 MT5 交易类函数失败时的 RETCODE 返回值。 比如 10018 是市场休市,10019 是保证金不足,10020 是价格已变导致下单失效,10021 是无报价可处理。10024 的「Too frequent requests」在 EA 高频发单时最容易撞上,经纪商可能对每秒请求数做了硬性限制。 把这些字符串塞进全局映射表后,你的面板在捕获交易返回时就能直接显示人话而不是冷冰冰的数字。开 MT5 随便跑一个下单脚本,故意在休市时段发单,看日志里落的是不是 10018 就能验证。

MQL5 / C++
  {"Рынок закрыт","Market closed"},                                                                                                                                                class=class="str">"cmt">// class="num">10018
  {"Нет достаточных денежных средств для выполнения запроса","Not enough money to complete request"},                                                                          class=class="str">"cmt">// class="num">10019
  {"Цены изменились","Prices changed"},                                                                                                                                                            class=class="str">"cmt">// class="num">10020
  {"Отсутствуют котировки для обработки запроса","No quotes to process request"},                                                                                          class=class="str">"cmt">// class="num">10021
  {"Неверная дата истечения ордера в запросе","Invalid order expiration date in request"},                                                                                 class=class="str">"cmt">// class="num">10022
  {"Состояние ордера изменилось","Order state changed"},                                                                                                                                          class=class="str">"cmt">// class="num">10023
  {"Слишком частые запросы","Too frequent requests"},                                                                                                                                             class=class="str">"cmt">// class="num">10024
  {"В запросе нет изменений","No changes in request"},                                                                                                                                            class=class="str">"cmt">// class="num">10025
  {"Автотрейдинг запрещен сервером","Autotrading disabled by server"},                                                                                                                               class=class="str">"cmt">// class="num">10026

常见问题

接管窗体对象的拖动事件,在移动时实时重绘并锁定坐标边界,避免超出图表可视区域即可。
常漏掉对鼠标按下和释放事件的分别处理,导致松手后窗体还跟着光标跑,需显式清除拖动状态。
可以,小布能读取你的对象事件代码并标出未处理的边界与状态切换,省去手动逐行排查。
双语映射表把同一返回码配上俄文与英文说明,查错时直接按码搜中文含义,不用切语言猜。
在原有码段后追加新错误常量并同步进双语映射表,调用时返回扩展码就能区分费用类异常。