MetaTrader 5 已具备锁仓账户系统·进阶篇
◍ 锁仓账户里的反向平仓能省一次点差
MT5 锁仓账户近年加了一种叫 Close By 的操作,专门用来一次性了结同一品种的两个反向持仓。它和手动分别平多、平空不同:分别平仓时,买入仓按卖价走、卖出仓按买价走,交易者要付两次点差;而 Close By 直接用对方的开盘价去抵掉自己这一边,只占用一次点差。 如果两边手数不一样,Close By 之后会留下一个单向仓,其交易量等于两手数之差,方向和开盘价继承手数较大的那一侧。比如 0.3 手多和 0.1 手空做 Close By,剩下 0.2 手多,开盘价按原 0.3 手多的价格。 成交记录上,这一对反向仓会由两笔 out by 类型的交易闭合,但总盈亏只写在一笔里,不像分开平那样拆成两条独立损益。开 MT5 找个锁仓账户,随便开一多一空比对一下成交里的点差成本,就能验证省的那一次 spread 是不是真的。外汇和贵金属杠杆高,点差虽小但频繁锁仓来回 Close By 也可能吃掉利润,动手前先算清频次。
「锁仓账户里预付款怎么被拆开算」
在锁仓账户系统下,同品种多空持仓的预付款不是简单相加。经纪商可能采用两种对冲计算方式,具体由合约规格里的“对冲保证金”字段决定。 若未指定“使用较大分支计算”,则走基本三步:先算未覆盖交易量(大分支减掉小分支),再算覆盖交易量(若指定了对冲保证金值),最后单独算挂单。每一步都按品种类型公式出保证金,再求和。 未覆盖部分要算分支加权开盘价:(开仓价1×量1 + … + 开仓价N×量N) / (量1 + … + 量N),保证金率和货币换算也用权重平均。覆盖部分同理,若“对冲”字段填了100,000、合约大小也是100,000,那1手买+1手卖EURUSD就各按1手收,填0则不收覆盖保证金。 如果“对冲保证金”字段勾了“使用较大分支计算”,就分别汇总多空两条分支(持仓+市价单+同方向挂单),取两者保证金的最大值作为最终结果。外汇和贵金属杠杆高,实际占用预付款以经纪商终端显示为准,开MT5账户里的“交易”窗口能逐笔核对。
持仓标签化后 EA 该怎么改请求结构
MT5 更新之后,每个持仓都带上了独立标签,通常和它建仓时用的订单标签一致。程序端会自动把标签铺到所有现存持仓上,但你自己的 EA 在锁仓系统里改仓或平仓时,必须显式填 MqlTradeRequest::ticket,否则可能动错单子。 在锁仓账户里一个品种能叠多个持仓,PositionSelect 只会挑标签最小的那个;想精准锁定某一仓,得用新增的 PositionSelectByTicket,或者先 PositionGetTicket 按列表索引捞出标签再选。净额系统虽也能填标签,但底层是按品种名定仓的,逻辑不一样。 保证金计算也分了三种账户模式:ACCOUNT_MARGIN_MODE_RETAIL_NETTING 走场外净额、按品种类型算预付;ACCOUNT_MARGIN_MODE_EXCHANGE 走交易所折扣率,券商设的值不能低于交易所下限;ACCOUNT_MARGIN_MODE_RETAIL_HEDGING 才是锁仓独立持仓,会考虑同品种多仓。配合 SYMBOL_MARGIN_HEDGED 能直接读某品种锁仓预付值。 Close By 反向平仓带来一串新常量:TRADE_ACTION_CLOSE_BY、ORDER_TYPE_CLOSE_BY、ORDER_POSITION_BY_ID、DEAL_ENTRY_OUT_BY。做对冲平仓脚本时,position_by 字段填反向仓标签,只在 close by / out by 成交里生效。外汇与贵金属杠杆高,标签填错可能误平,先在策略测试器跑一遍再上实盘。
class="kw">struct class="type">MqlTradeRequest { ENUM_TRADE_REQUEST_ACTIONS action; class=class="str">"cmt">// 执行操作类型 class="type">class="kw">ulong magic; class=class="str">"cmt">// EA交易神奇数字 class="type">class="kw">ulong order; class=class="str">"cmt">// 订单标签 class="type">class="kw">string symbol; class=class="str">"cmt">// 交易品种名称 class="type">class="kw">double volume; class=class="str">"cmt">// 按手数请求的交易量 class="type">class="kw">double price; class=class="str">"cmt">// 价格 class="type">class="kw">double stoplimit; class=class="str">"cmt">// 止损限价订单水平 class="type">class="kw">double sl; class=class="str">"cmt">// 止损订单水平 class="type">class="kw">double tp; class=class="str">"cmt">// 获利订单水平 class="type">class="kw">ulong deviation; class=class="str">"cmt">//要求价格中最大允许偏差 ENUM_ORDER_TYPE type; class=class="str">"cmt">// 订单类型 ENUM_ORDER_TYPE_FILLING type_filling; class=class="str">"cmt">//订单填充类型 ENUM_ORDER_TYPE_TIME type_time; class=class="str">"cmt">// 订单时间类型 class="type">class="kw">datetime expiration; class=class="str">"cmt">// 订单截止日期 ( ORDER_TIME_SPECIFIED 类型订单) class="type">class="kw">string comment; class=class="str">"cmt">// 订单说明
◍ 交易事务与持仓标签的结构拆解
在 MT5 的 EA 事件驱动模型里,每一笔成交、挂单、持仓变动都会以 MqlTradeTransaction 结构推送到 OnTradeTransaction。这个结构体把订单、成交、持仓三套标识全部摊开,方便你在回调里按标签追状态。 position 与 position_by 是两个容易混淆的字段:前者是当前事务关联的持仓标签,后者在对冲账户里指向被反向平仓的那条持仓。净头寸账户下 position_by 通常为 0,别拿它当通用持仓 ID 去查。 PositionGetTicket(int index) 按列表序号取持仓 ticket,返回 ulong;配合 PositionSelectByTicket(ulong ticket) 可以把该持仓置为当前上下文,再读持仓属性。实盘里建议先数 PositionTotal(),再循环取 ticket,避免索引越界。 外汇与贵金属杠杆高,持仓标签失效或选错上下文可能导致重复平仓,回测与实盘前务必在策略测试器用 EURUSD 1 分钟数据跑一遍事件打印。
class="kw">struct MqlTradeTransaction { class="type">class="kw">ulong deal; class=class="str">"cmt">// 交易标签 class="type">class="kw">ulong order; class=class="str">"cmt">// 订单标签 class="type">class="kw">string symbol; class=class="str">"cmt">// 交易品种名称 ENUM_TRADE_TRANSACTION_TYPE type; class=class="str">"cmt">// 交易事务类型 ENUM_ORDER_TYPE order_type; class=class="str">"cmt">// 订单类型 ENUM_ORDER_STATE order_state; class=class="str">"cmt">// 订单状态 ENUM_DEAL_TYPE deal_type; class=class="str">"cmt">// 交易类型 ENUM_ORDER_TYPE_TIME time_type; class=class="str">"cmt">// 订单时间类型 class="type">class="kw">datetime time_expiration; class=class="str">"cmt">// 订单截止日期 class="type">class="kw">double price; class=class="str">"cmt">// 价格 class="type">class="kw">double price_trigger; class=class="str">"cmt">// 止损限价订单激活价格 class="type">class="kw">double price_sl; class=class="str">"cmt">// 止损水平 class="type">class="kw">double price_tp; class=class="str">"cmt">// 获利水平 class="type">class="kw">double volume; class=class="str">"cmt">// 交易量手数 class="type">class="kw">ulong position; class=class="str">"cmt">// 持仓标签 class="type">class="kw">ulong position_by; class=class="str">"cmt">// 反向持仓标签 }; class="type">class="kw">ulong PositionGetTicket( class="type">int index class=class="str">"cmt">// 持仓列表索引 ); class="type">bool PositionSelectByTicket( class="type">class="kw">ulong ticket class=class="str">"cmt">// 持仓标签 );
「锁仓账户下用通用遍历平掉所有仓位」
MT5 现在同时支持股票市场与零售外汇锁仓账户,写自动系统的好处不只是多线程回测,连 MQL5 云网络的分布式算力也能直接调用,跑大规模优化不再受本机限制。 锁仓账户里没有 OrderClose() 这种净额结算时代的写法,很多老 EA 直接移植会卡住。下方 GalacticExplosion 示例里的 CloseAllPositions 用通用遍历,对锁仓和净额账户都有效,是更稳的平仓入口。 [CODE] <span class="comment">//+------------------------------------------------------------------+</span>
| <span class="comment">// | 关闭所有位置 | </span> |
|---|
<span class="comment">//+------------------------------------------------------------------+</span> <span class="keyword">void</span> CloseAllPositions() { <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="functions">PositionsTotal</span>()-<span class="number">1</span>;i>=<span class="number">0</span>;i--) <span class="comment">// 返回当前位置的数量</span> <span class="keyword">if</span>(m_position.SelectByIndex(i)) <span class="comment">// 通过索引选择位置,以便进一步访问其属性</span> <span class="keyword">if</span>(m_position.<span class="functions">Symbol</span>()==m_symbol.Name() && m_position.Magic()==m_magic) m_trade.PositionClose(m_position.Ticket()); <span class="comment">// 用指定的符号关闭一个位置</span> } [/CODE] 代码逐行拆解:函数从 PositionsTotal()-1 倒序取到 0,避免平仓后索引错位;SelectByIndex 按序号锁定某仓,再比对交易品种与 Magic 值过滤属于自己的单;最后用 PositionClose 按 ticket 平掉。外汇与贵金属属高风险品类,锁仓只是风控手段之一,不意味着回撤可控,上云网络跑优化前先在 demo 验证逻辑。 有交易者反馈:同品种一买一卖各平一次,点差或佣金可能付两遍;若经纪商只加宽点差,成本同样翻倍。把 CloseAllPositions 接进自己的 EA 前,先想清楚平仓顺序对净值曲线的概率影响。
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span> <span class="comment">class=class="str">"cmt">//| 关闭所有位置|</span> <span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span> <span class="keyword">class="type">void</span> CloseAllPositions() { <span class="keyword">for</span>(<span class="keyword">class="type">int</span> i=<span class="functions">PositionsTotal</span>()-<span class="number">class="num">1</span>;i>=<span class="number">class="num">0</span>;i--) <span class="comment">class=class="str">"cmt">// 返回当前位置的数量</span> <span class="keyword">if</span>(m_position.SelectByIndex(i)) <span class="comment">class=class="str">"cmt">// 通过索引选择位置,以便进一步访问其属性</span> <span class="keyword">if</span>(m_position.<span class="functions">Symbol</span>()==m_symbol.Name() && m_position.Magic()==m_magic) m_trade.PositionClose(m_position.Ticket()); <span class="comment">class=class="str">"cmt">// 用指定的符号关闭一个位置</span> }