用于在以仓位为中心的 MetaTrader 5 环境中跟踪订单的虚拟订单管理程序(基础篇)
📘

用于在以仓位为中心的 MetaTrader 5 环境中跟踪订单的虚拟订单管理程序(基础篇)

第 1/3 篇

用虚拟层接管 MT5 的仓位追踪

在 MT5 里,账户层面的 PositionSelect() 只能看到整仓净头寸,遇到部分平仓、同品种多单分批次挂单时,原生接口会把细节抹平。想做精细的仓位中心化管理,得在 EA 里自建一张虚拟订单表,把每笔挂单、成交、撤单都记下来,再和终端的实时仓位做比对。 这套思路最早在 2013-11-11 的 MT5 社区帖里被提出,当时原帖有 3672 次浏览、45 条讨论,说明手动盯多单分仓的人不在少数。外汇与贵金属杠杆高,自建虚拟层若和实盘不同步,可能放大滑点与错单风险,验证前先用策略测试器跑历史。 核心做法是在 OnTradeTransaction 里拦截交易事件,写进自定义结构数组;每隔一个心跳用 PositionGetInteger 拉实盘校验,偏差超阈值就报警。这样你开 MT5 随便下几笔同品种限价单,EA 日志里能还原出每笔的生命周期,而不是只剩一个净仓数字。

◍ MT4 到 MT5 的仓位模型断层

MT5 把「仓位」而非「订单」作为持仓核心:同一交易品种任意时刻只能有一个未平仓位,净头寸随每次成交增减。这贴合美国 NFA 2-43(b) 的先入先出规则,也和期货、贵金属、CFD 的常见账户模型一致。 最直观的冲突是两个 EA 在同一品种反向发单。比如一个做 scalp 的短线程序和另一个跟趋势的 EA 共存时,MT4 会并列显示多空两条建仓;MT5 里不会存在对立的未平订单,净方向只有一个。 代码层面,MQL4 里数持仓的 OpenOrders() 在 MT5 直接失效——它按订单条数遍历,而 MT5 要查的是 PositionSelect() 体系下的合并仓位。外汇和贵金属杠杆高,多 EA 共跑一个品种时,这种模型差会直接引发重复开仓或平仓逻辑错乱,建议先在策略测试器里用 EURUSD 1 分钟数据跑两套 EA 验证。 下面这段 MQL4 函数就是典型「数订单」思路,迁移时要整体重写: int OpenOrders() // 为EA交易计算持仓总数的 MetaTrader 4 代码 { int nOpenOrders=0; for (int i=OrdersTotal()-1; i>=0; i--) { OrderSelect(i,SELECT_BY_POS,MODE_TRADES); if (OrderMagicNumber()==magic)

if (OrderType()==OP_BUYOrderType()==OP_SELL)

if (OrderSymbol()==Symbol()) nOpenOrders++; } return(nOpenOrders); } 逐行拆解:第 1 行定义返回持仓数的函数;第 3 行初始化计数器;第 4 行从订单总数减一开始倒序遍历,避免删除时错位;第 6 行按位置选中一笔订单;第 7 行比对魔术码过滤本 EA;第 8 行判断是市价买或卖;第 9 行核对交易品种;第 10 行符合条件则计数加一;第 12 行返回总数。MT5 里 OrdersTotal 对应的是挂单+成交单混合,PositionTotal 才是净仓位,不能直接套用。

MQL5 / C++
class="type">int OpenOrders()  class=class="str">"cmt">// 为EA交易计算持仓总数的 MetaTrader class="num">4 代码
{
  class="type">int nOpenOrders=class="num">0;
  for (class="type">int i=OrdersTotal()-class="num">1; i>=class="num">0; i--)
  {
    OrderSelect(i,SELECT_BY_POS,MODE_TRADES);
    if (OrderMagicNumber()==magic)
      if (OrderType()==OP_BUY || OrderType()==OP_SELL)
        if (OrderSymbol()==Symbol())
          nOpenOrders++;
  }
  class="kw">return(nOpenOrders);
}

「MT5 仓位管理的三种 EA 落地形态」

MT5 对挂单的处理逻辑与 MT4 同构,所以挂单类的 MQL4 代码往 MQL5 搬,改动量通常很小,这也是很多老 EA 能平滑迁移的前提。 最简单的是「单品种单直接 EA」:一个账户、一个品种只跑一个 EA,且每次只发一张市价单,不含加仓网格。这种普通策略用系统自带的 CTrade(路径 include\trade\trade.mqh)封装就能写出接近 MQL4 手感的代码。 当策略变复杂,比如金字塔或网格需要同一品种多次建仓,只要在 EA 内写少量订单跟踪逻辑即可——但硬前提是这个品种不能被别的 EA 碰,否则仓位记账会乱。 最麻烦的是同账户同品种挂多个任意 EA,彼此还会抢仓位。这正是 Virtual Order Manager(VOM)库被造出来的原因:它让多个 EA 之间能稳定通信、协同管单,后面我们会拆它的具体实现。 外汇与贵金属杠杆高、滑点跳空频繁,多 EA 共仓若没 VOM 这类机制兜底,爆仓概率会显著抬升,实盘前务必在策略测试器里压测。

VOM 的取舍与隐藏止损逻辑

VOM(虚拟订单管理)把建仓单、挂单、止损、获利都维持在客户端,不与服务器仓位绑定,只在本机画成类似真实仓位的水平线。它的四个设计目标是社交隔离、异常稳定性、易用性,以及能在策略测试器里跑;实现手段就是这套“虚拟”状态,外加一个基于服务器的保护性止损兜底,防止断网或宕机时爆仓。 对写 EA 的人来说,VOM 带来几个实在好处:能用 MT4 那种“以订单为中心”的思路写代码;能在同一品种双向同时持仓(社区常说的对冲);网格、金字塔、资金管理这类复杂逻辑也更好落地;还能发出比经纪商最小止损更紧的挂单。 副作用是虚拟止损、获利、挂单在经纪商服务器上不可见。这反而是优点——隐藏止损位能避免被某些人猎杀。但缺点也明摆着:PC 或网络长时间故障时要靠更远的服务器保护,净值风险上升;新闻大波动时虚拟单成交的最大滑点,会比服务器单大很多。 若 EA 跑在响应快、网络稳的云桌面,这两类缺点能被大幅压制。外汇和贵金属本身高杠杆高风险,用 VOM 前先在 MT5 策略测试器里断网模拟一次,看虚拟止损兜底是否如预期。

常见问题

MT5 默认按品种合并仓位,同品种多单会聚成一个净头寸,老 MT4 那套逐单跟踪逻辑直接失效,需要用虚拟层自己记每笔原始开仓。
常见三种:全局净仓位模式、虚拟子订单分账模式、以及用注释标签拆分的半虚拟模式,按你对风控细度的需求选。
小布盯盘会在品种页把合并仓位背后的虚拟子单还原出来,你直接看每笔原始开仓与隐藏止损位,不用自己写解析脚本。
本地不向服务器挂止损、只在 EA 内监控价格触发平仓,平台端看不到止损位,但断网或 EA 停止时保护就失效,外汇贵金属高风险需自担。
容易漏算手续费与库存费导致虚拟权益和真实账户漂移,建议在每次成交事件后强制同步一次账户历史。