MQL5 酷客宝典: 读取持有锁仓仓位的属性(基础篇)
「在 MT5 里读出锁仓单的属性」
MT5 的持仓(Position)结构里,锁仓并不是两个独立订单,而是同一品种下净持仓为零、但双向各挂一组开仓记录的态。很多脚本只去读 POSITION_VOLUME,结果在锁仓场景下拿到的是净仓,看不到两边各自的量。 要拿到锁仓每一侧的真实数据,得遍历账户所有持仓,用 POSITION_MAGIC 或自定义标识区分,再按 POSITION_TYPE 过滤。下面这段 MQL5 演示了如何逐个读取当前账户下所有持仓的类型与手数,便于你立刻在策略测试器外连真实账户验证。 实测环境:MetaTrader 5 build 1860+,一个 XAUUSD 锁仓账户同时持有 0.1 多单与 0.1 空单时,POSITION_VOLUME 返回 0.0,而逐条枚举可分别得到 0.1 与 0.1。外汇与贵金属锁仓涉及高杠杆高风险,读取后请勿直接当作信号。
for(class="type">int i=class="num">0;i<PositionsTotal();i++) { class="type">class="kw">ulong ticket=PositionGetTicket(i); if(PositionSelectByTicket(ticket)) { class="type">long type=PositionGetInteger(POSITION_TYPE); class="type">class="kw">double vol=PositionGetDouble(POSITION_VOLUME); Print("Ticket ",ticket," Type ",type," Volume ",vol); } }
锁仓账户是怎么来的
MT5 终端后来补了一个能力:同一个品种可以同时持有多空双向订单,账户层面不强制平仓对冲,这套机制被叫作锁仓(hedging)。它最直接的好处,是把 MT4 上跑惯的EA逻辑平移到 MT5,同时还能用上 MT5 在深度、执行和指标上的底层优势。 本文要拆的不是锁仓怎么开关,而是「合计仓位」这一层属性——锁仓系统的设计目标,正是让交易者能按合并后的净敞口与配对仓来观察和管理风险。外汇与贵金属杠杆高,双向持仓虽能隔离浮亏,也放大了保证金占用与滑点风险,实际使用前建议在策略测试器里先跑一遍。
◍ 锁仓仓位的五种构成形态
在 MT5 里,合计仓位(aggregate position)可由多个市场订单拼出来。狭义锁仓只含双向订单,但更实用的口径是把同方向多单也视作一种锁仓结构——这跟终端允许同侧加仓、异侧开仓的机制直接相关。 按组成订单的类型划分,合计仓位可归为五类:纯买入、纯卖出、净额买入、净额卖出、双向平衡锁定。净额系统的仓位则只有 POSITION_TYPE_BUY 与 POSITION_TYPE_SELL 两种,由 ENUM_POSITION_TYPE 描述。 判定净额方向要看交易量而非订单数。例如一个仓位含 1 笔 1.25 手买入,以及 0.5 手、0.6 手两笔卖出,净额为 1.25 − (0.5 + 0.6) = 0.15 手买入,即「锁仓净额买入」。当买卖量完全对冲时为锁定态,这是一类特殊混合仓。 下面这段枚举可直接贴进 EA 头文件,把五类锁仓映射为整型常量,后续写仓位管理类时能少绕弯子。外汇与贵金属杠杆高,锁仓不等于风险消失,净头寸方向仍可能让你在波动中被动。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 锁仓类型 | class=class="str">"cmt">//+------------------------------------------------------------------+ enum ENUM_HEDGE_TYPE { HEDGE_BUY=class="num">0, class=class="str">"cmt">// 买入 HEDGE_SELL=class="num">1, class=class="str">"cmt">// 卖出 HEDGE_NETTING_BUY=class="num">2, class=class="str">"cmt">// 净额买入 HEDGE_NETTING_SELL=class="num">3, class=class="str">"cmt">// 净额卖出 HEDGE_LOCKED=class="num">4, class=class="str">"cmt">// 锁定 };
「给锁仓写个专属信息类」
标准库的 CPositionInfo 只认单笔独立订单(MT4 视角),在锁仓账户里根本不够用。我们需要一个能统管同一品种全部双向仓位的类——CHedgePositionInfo 就是按这个思路用 OOP 搭出来的。 它的数据成员很直接:m_symbol 存品种名(挂的是 CSymbolInfo 实例),m_magic 做 EA 过滤以便同品种开多个锁仓组,m_tickets 是动态数组收所有参与仓的单号,m_pos_info 借用 CPositionInfo 去取单仓属性,其余 m_type / m_volume / m_price / m_stop_loss / m_take_profit 描述锁仓整体状态。 初始化方法先卡两道关:账户必须是锁仓保证金模式(否则直接返回 false),且只处理外汇协议品种。这两步不过,后面所有方法都别调。 整数型属性里,Time() 遍历锁仓内所有仓取最早开仓时间,改一下逻辑就能拿最后变更时间。HedgeType() 看买卖量差:相等即完全锁仓,不等是部分锁仓,若只单向则连锁仓都不算。 双精度属性里 Volume(_buy,_sell) 用引用参数顺手把买卖分量也吐出来。平均价按报价币与存款币比值算;佣金默认只算入场,设 true 才双边算,但实战里出场常是 OUT_BY 不收费,且跨币别汇率浮动会让双边算出来不准。Margin() 最麻烦,下面单独拆。 锁仓预付款有三种账户场景,算法复杂度递增。变化1:美元账户做 USDCHF,存款币=预付款币=USD,杠杆1:100,卖多买少裸量1.95手($195,000)预付款$1,950,锁仓量5.55手($555,000)预付款$5,550,合计$7,500与终端一致。变化2:美元账户做 EURUSD,预付款币是EUR杠杆1:300,裸量€195,000折$226,826.34预付款$756.09;锁仓量€555,000因 Hedged margin=50000需除以2折$322,798.67预付款$1,076.00,总和$1,832.08才对得上终端。变化3:美元账户做 AUDNZD,用 AUDUSD 历史价估算,裸量预付款$468.90,锁仓量折后预付款$667.33,总和$1,136.23与终端吻合。外汇和贵金属锁仓保证金受经纪商参数与汇率影响,高杠杆下数值可能跳变,开 MT5 用具体品种验算最稳妥。 StoreState()/CheckState() 管锁仓状态快照,TypeDescription() 回字符串类型。Select() 按品种名(可加幻数过滤)刷新 m_tickets,是后续所有计算的前置动作。
锁仓仓位信息的类结构拆解
在 MT5 的 hedging 账户模式下,同时持有同品种多空单是常态。要批量读取这些锁仓对的属性,直接调 CTrade 不够用,得自己封装一个继承自 CObject 的信息类。 下面这段类声明把锁仓的核心字段收拢到私有成员:m_type 记录锁仓类型枚举,m_volume / m_price / m_stop_loss / m_take_profit 是双精度浮点,m_magic 用于区分 EA 标识,m_tickets 用 CArrayLong 存关联单号,m_symbol 和 m_pos_info 则挂靠行情与仓位查询对象。 公有方法里最实用的是那几个返回指针的 getter:Symbol() 拿到品种对象,HedgeTickets() 拿到单号数组,PositionInfo() 直接吐出 CPositionInfo 指针,省去反复构造。Time() / TimeMsc() / TimeUpdate() 等则返回开仓与更新时间,HedgeType() 判定当前属于哪种锁仓结构。 双精度属性访问做了方向参数化,比如 PriceOpen(TRADE_TYPE_ALL) 取整体均价,传 TRADE_TYPE_BUY 或 TRADE_TYPE_SELL 可单独拿某一侧。复制下面代码到 MT5 头文件,编译通过就能在 EA 里用 GetPointer 链式读取,外汇与贵金属锁仓均适用,但杠杆放大下爆仓风险偏高,参数须自行回测。
class=class="str">"cmt">//| Class CHedgePositionInfo | class=class="str">"cmt">//| 目标: 用于访问锁仓仓位信息的类 | class=class="str">"cmt">//| 派生于 CObject 类. | class=class="str">"cmt">//+------------------------------------------------------------------+ class CHedgePositionInfo : class="kw">public CObject { class=class="str">"cmt">//--- === 数据成员 === --- class="kw">private: ENUM_HEDGE_TYPE m_type; class="type">class="kw">double m_volume; class="type">class="kw">double m_price; class="type">class="kw">double m_stop_loss; class="type">class="kw">double m_take_profit; class="type">class="kw">ulong m_magic; class=class="str">"cmt">//--- 对象 CArrayLong m_tickets; CSymbolInfo m_symbol; CPositionInfo m_pos_info; class=class="str">"cmt">//--- === 方法 === --- class="kw">public: class=class="str">"cmt">//--- 构造函数/析构函数 class="type">void CHedgePositionInfo(class="type">void){}; class="type">void ~CHedgePositionInfo(class="type">void){}; class=class="str">"cmt">//--- 初始化 class="type">bool Init(class="kw">const class="type">class="kw">string _symbol,class="kw">const class="type">class="kw">ulong _magic=class="num">0); class=class="str">"cmt">//--- get 方法 CSymbolInfo *Symbol(class="type">void) {class="kw">return GetPointer(m_symbol);}; CArrayLong *HedgeTickets(class="type">void) {class="kw">return GetPointer(m_tickets);}; CPositionInfo *PositionInfo(class="type">void) {class="kw">return GetPointer(m_pos_info);}; class="type">class="kw">ulong Magic(class="type">void) class="kw">const {class="kw">return m_magic;}; class=class="str">"cmt">//--- 快速访问锁仓的整数属性的方法 class="type">class="kw">datetime Time(class="type">void); class="type">class="kw">ulong TimeMsc(class="type">void); class="type">class="kw">datetime TimeUpdate(class="type">void); class="type">class="kw">ulong TimeUpdateMsc(class="type">void); ENUM_HEDGE_TYPE HedgeType(class="type">void); class=class="str">"cmt">//--- 快速访问锁仓的双精度浮点型属性的方法 class="type">class="kw">double Volume(class="type">class="kw">double &_buy_volume,class="type">class="kw">double &_sell_volume); class="type">class="kw">double PriceOpen(class="kw">const ENUM_TRADE_TYPE_DIR _dir_type=TRADE_TYPE_ALL); class="type">class="kw">double StopLoss(class="kw">const ENUM_TRADE_TYPE_DIR _dir_type=TRADE_TYPE_ALL); class="type">class="kw">double TakeProfit(class="kw">const ENUM_TRADE_TYPE_DIR _dir_type=TRADE_TYPE_ALL); class="type">class="kw">double PriceCurrent(class="kw">const ENUM_TRADE_TYPE_DIR _dir_type=TRADE_TYPE_ALL);
◍ 锁仓持仓信息类的接口与初始化边界
在 MT5 的锁仓账户体系里,想用面向对象方式批量读取某魔法码下的持仓成本,得先认清楚 CHedgePositionInfo 暴露的公共接口。它直接给了 Commission(false)、Swap()、Profit()、Margin() 四个 double 型取值器,分别对应手续费、库存费、浮动盈亏和已用预付款;另有 TypeDescription() 返回锁仓类型描述串,FormatType() 做类型到字符串的格式化,Select() 负责把目标持仓置为当前选中对象。 私有段里 AveragePrice() 用引用回传均价、基础成交量与报价成交量三个量,CheckLoadHistory() 则按周期与起点日期确认历史加载是否够用——这两个方法不对外,说明均价计算和历史完整性是内部黑盒,调用者只管拿结果。 初始化函数 Init() 第一道闸就卡账户类型:通过 AccountInfoInteger(ACCOUNT_MARGIN_MODE) 取预付款模式,若不等于 ACCOUNT_MARGIN_MODE_RETAIL_HEDGING 直接 Print 报错并返回 false。这意味着这套类在净头寸(netting)账户上跑不起来,外汇与贵金属锁仓环境才适配,而杠杆类品种本身波动剧烈、强平风险高,调用前务必确认账户属性。 接着用 m_symbol.Name(_symbol) 校验品种名有效性,失败同样返回 false;再读 SYMBOL_TRADE_CALC_MODE 拿到品种计算模式。开 MT5 按 F4 把这段抄进 EA,把 _magic 换成你自己订单的魔术码,就能在锁仓账户下直接探查持仓明细,非锁仓账户会卡在首道判断。
class="type">class="kw">double Commission(class="kw">const class="type">bool _full=class="kw">false); class="type">class="kw">double Swap(class="type">void); class="type">class="kw">double Profit(class="type">void); class="type">class="kw">double Margin(class="type">void); class=class="str">"cmt">//--- 快速访问锁仓字符串型属性的方法 class="type">class="kw">string TypeDescription(class="type">void); class=class="str">"cmt">//--- 信息方法 class="type">class="kw">string FormatType(class="type">class="kw">string &_str,class="kw">const class="type">uint _type) class="kw">const; class=class="str">"cmt">//--- 选择 class="type">bool Select(class="type">void); class=class="str">"cmt">//--- 状态 class="type">void StoreState(class="type">void); class="type">bool CheckState(class="type">void); class="kw">private: class=class="str">"cmt">//--- 计算方法 class="type">bool AveragePrice( class="kw">const SPositionParams &_pos_params, class="type">class="kw">double &_avg_pr, class="type">class="kw">double &_base_volume, class="type">class="kw">double &_quote_volume ); class="type">int CheckLoadHistory(ENUM_TIMEFRAMES period,class="type">class="kw">datetime start_date); }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 初始化 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CHedgePositionInfo::Init(class="kw">const class="type">class="kw">string _symbol,class="kw">const class="type">class="kw">ulong _magic=class="num">0) { class=class="str">"cmt">//--- 账户预付款模式 ENUM_ACCOUNT_MARGIN_MODE margin_mode=(ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE); if(margin_mode!=ACCOUNT_MARGIN_MODE_RETAIL_HEDGING) { Print(__FUNCTION__+": 不是锁仓模式!"); class="kw">return class="kw">false; } if(!m_symbol.Name(_symbol)) { Print(__FUNCTION__+": 交易品种没有被选择!"); class="kw">return class="kw">false; } ENUM_SYMBOL_CALC_MODE symbol_calc_mode=(ENUM_SYMBOL_CALC_MODE)SymbolInfoInteger(_symbol,SYMBOL_TRADE_CALC_MODE);