MQL5 酷客宝典: 读取持有锁仓仓位的属性·综合运用
🔒

MQL5 酷客宝典: 读取持有锁仓仓位的属性·综合运用

(3/3)· 锁仓账户下五种合计仓位类型与属性获取,多数 EA 在净额和对冲混用时会读错字段

进阶 第 3/3 篇
锁仓账户里一个符号下可能叠着多个反向订单,直接按 POSITION_TYPE 取属性常会漏掉净额手数。先把合计仓位拆成五类再读,才能拿到真实敞口。

「对冲账户里的锁仓净头寸怎么读」

在 MT5 对冲账户里,一组反向单合并后打印出的「锁仓净额卖出」并不是某一张单,而是账户层面的净敞口快照。上面这段日志来自 AUDNZD H1 上的测试脚本,时间停在 2018.09.03 18:51:37,显示净卖出交易量 -1.95,其中买入累计 5.55、卖出累计 7.50,开仓价 0.97159,当前价 0.96956。 注意止损和获利价格都打了 -1.00000,这代表该净头寸未挂止损止盈,不是真实报价。当笔快照利润 409.77、预付款占用 7500.00,隔夜息 -35.79,说明锁仓过周末会吃掉不小利息成本,外汇与贵金属这类高杠杆品种尤其要盯紧 swap。 四分钟后脚本切到 EURUSD 打印另一组锁仓属性:仓位总数 5,前三笔都是买入,分别是 1.75 @ 1.16329、2.55 @ 1.16329、1.25 @ 1.16322。净头寸的方向和体量,得把同品种所有单按买卖加总后才看得准,单看某张 ticket 容易误判。 开 MT5 用脚本把 PositionGetInteger / PositionGetDouble 循环打一遍,就能复现这种净头寸视图;重点核对买入合计与卖出合计的差值,那才是你真正暴露在外的方向。

对冲净仓模式下持仓聚合的字段真相

在 MT5 的 HEDGE_NETTING 账户里,单子不会像纯对冲账户那样各自独立挂账。上面这段日志是 EA 在 AUDNZD,H1 图表上打印的 EURUSD 聚合持仓:两张卖单分别 3.00 和 4.50 手,成交于 1.16323 与 1.16320,但系统对外只暴露一条 netting 记录。 聚合后交易量字段显示 -1.95,而明细里买入 5.55、卖出 7.50,差值正好 1.95 手净空。建仓价被摊成 1.16303,当前价 1.16198,浮利 206.60——注意这利润不含隔夜息,后者单独列了 -37.20。 止损获利都是 -1.00000,说明该 netting 仓位没挂止损止盈,靠的是对冲单互抵。时间戳也值得看:开仓 2018.08.31 16:38:10(毫秒 1535733490531),更新 16:38:49,EA 在 9 月 3 日才打印,延迟读历史仓位很常见。 开 MT5 随便跑个 hedge 账户回测,用 OrderGetString(ORDER_TYPE) 抓 HEDGE_NETTING_SELL 就能复现这类聚合;外汇和贵金属杠杆高,netting 下的净值跳变可能比想象快,验证时先把点差和库存费算进去。

◍ 从日志读锁仓净额的真实方向

上面这段 MT5 回测日志来自一个叫 Test_hedge_properties 的脚本,图表挂在 EURUSD(H1),但实际扫描的是 AUDNZD 的持仓。日志里打出 5 笔仓位:3 笔买入合计 5.55 手(1.75+2.55+1.25),2 笔卖出合计 7.50 手(3.00+4.50),净敞口是 -1.95 手卖出。 注意 类型: HEDGE_NETTING_SELL 这行——它表示当前这个锁仓组的净额方向为卖,和 交易量: -1.95 完全对得上。也就是说,即便存在多空双向持仓,账户在该品种上的风险暴露仍是净空头,外汇与贵金属这类高杠杆品种里,这种净额才是保证金和预付款计算的基准。 日志还顺带给出了开仓时间与更新时间的毫秒数(如 1535733581113),说明在 MQL5 里每一组锁仓属性都能精确到毫秒级追踪。开 MT5 跑一遍同样的 Print 结构,你能在策略测试器日志里直接核对自己的锁仓逻辑有没有被平台归错类。

「锁仓面板的字段与类结构」

在 EURUSD H1 的测试实例里,同一毫秒(2018.09.03 18:47:25.369)打出的仓位快照显示:买入 5.55 手、卖出 7.50 手,建仓价 1.08708,当前价 1.09314;止损与获利价均为 -1.00000,说明这组锁仓单没有挂止损止盈。 该快照里隔夜息 -21.06、利润 -779.45、预付款 1136.23,手续费 0.00。外汇与贵金属属高风险品种,这类负隔夜息叠加浮亏的情况可能随持仓时间拉长而恶化,开 MT5 自己跑一遍能直观看到数字变化。 承载这些字段的 CHedgeDialog 类私有继承自 CAppDialog,内部用 CArrayString 存品种、CLabel 与 CEdit 数组管显示、CComboBox 做品种切换,并靠 m_to_refresh 标记决定是否重绘。下面这段是类声明的骨架,逐行看能弄清面板怎么搭。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Class CHedgeDialog                                                |
class=class="str">"cmt">//| 目的: 用于显示锁仓仓位信息的类.                                   |
class=class="str">"cmt">//|       派生于 CAppDialog 类。                                      |
class=class="str">"cmt">//+------------------------------------------------------------------+
class CHedgeDialog : class="kw">private CAppDialog
  {
   class=class="str">"cmt">//--- === 数据成员 === --- 
class="kw">private:
   CArrayString      m_symbols_arr;
   class=class="str">"cmt">//--- 控件
   CLabel            m_labels[FIELDS_NUM+class="num">1];
   CEdit             m_edits[FIELDS_NUM];
   CComboBox         m_combo;
   class="type">bool              m_to_refresh;
   class=class="str">"cmt">//--- === 方法 === --- 
class="kw">public:
   class=class="str">"cmt">//--- 构造函数/析构函数
   class="type">void              CHedgeDialog(class="type">void) {};
   class="type">void             ~CHedgeDialog(class="type">void) {};
   class=class="str">"cmt">//--- 初始化
   class="type">bool              Init(class="type">void);
   class="type">void              Deinit(class="kw">const class="type">int _reason);
   class=class="str">"cmt">//--- 处理
   class="type">void              OnChartEvent(class="kw">const class="type">int _id,

用计时闸门拦住面板刷新风暴

EA 在 OnTradeEvent 之后若无条件重绘面板,遇到密集成交会瞬间吃掉主线程。上面这段把刷新动作收进一个时间闸门里。 核心判断在 msc_elapsed>1000:用 GetTickCount 取当前毫秒数,减去上一次记录的 prev_cnt,只有间隔超过 1000 毫秒才把 m_to_refresh 置真、允许下一次 RefreshPanel;否则直接 return 跳过。 实盘里把阈值从 1000 改成 250,面板跟手度会明显提升,但 CPU 占用随之走高;外汇与贵金属波动剧烈时这种高频重绘叠加点差跳变,属于典型高风险操作,调参前建议在策略测试器跑一轮。

MQL5 / C++
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="type">void                OnTradeEvent(class="type">void);
   class=class="str">"cmt">//---
class="kw">private:
   class="type">int                 HedgeSymbols(class="type">void);
   class="type">void                RefreshPanel(class="type">void);
};
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//--- 检查刷新限制
if(!m_to_refresh)
  {
   class="type">uint last_cnt=GetTickCount();
   class="kw">static class="type">uint prev_cnt=class="num">0;
   class="type">uint msc_elapsed=last_cnt-prev_cnt;
   prev_cnt=last_cnt;
   if(msc_elapsed>class="num">1000)
      m_to_refresh=true;
   else
      class="kw">return;
  }

◍ 最后一句大实话

MT5 的多种仓位管理系统确实比 MT4 只支持净额结算要宽得多,迁移策略时优先确认经纪商支持的对冲规则,否则回测和实盘会出现仓位计数偏差。 读者 francisalmeida 在关闭两个仓位后反馈,Update_Hedge_Info 里 hdg_number_of_pos_total 在第二单平掉后仍显示 1 而非 0,这类边界要在自己的 EA 里加打印验证。 若用 EURCHF 这类交叉盘,CHedgePositionInfo::AveragePrice 里的 SymbolSelect(major_symbol,true) 可能因 USDEUR 不被经纪商支持而让策略测试器直接中止,可参考文末代码在算完均价后把该符号移出市场观察。 [CODE] if(SymbolSelect(major_symbol,true)) // (*) SymbolSelect(major_symbol,false); [/CODE] 外汇和贵金属杠杆高、滑点跳空频繁,任何仓位管理库都先在策略测试器跑通再上模拟盘。

把重复劳动交给小布
这些锁仓类型的判定与小布盯盘的 AIGC 已内置,打开对应品种页即可看到合计仓位归类与净手数,不必自己写枚举比对。

常见问题

前者仅含同方向订单,后者含双向订单且某一方向交易量占优,判定依据是各边交易量之差而非订单数量。
锁定仓位买卖量平衡,终端仍归为 hedge locked 而非基础枚举,需用锁仓专属属性接口读取。
可以,品种页已按本文的五类规则聚合展示,省去自己遍历订单和计算净额的代码。
需分别处理持仓与历史中的合计记录,避免把已平仓的对冲腿误计入当前净敞口。