DoEasy 函数库中的图形(第九十七部分):独立处理窗体对象移动(基础篇)
「让窗体对象在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”的旧逻辑,因为成交现已原生支持。
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 即可编译,不需要额外头文件。
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 精度——外汇贵金属点值敏感,超最小步长就可能被拒,属于高频踩坑点。
{"Заявка выполнена частично","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 就能验证。
{"Рынок закрыт","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