图表上的历史仓位及其盈利/亏损图指标(基础篇)
◍ 把历史仓位画进价格图
MT5 里有一类指标不画均线也不画通道,只干一件事:把账户里已经平掉的历史仓位,连同每笔的盈利亏损,直接投影到对应 K 线位置上。 这类工具属于交易类扩展,核心由三个对象构成:单笔仓位数据、历史仓位列表、以及把浮盈浮亏随时间连成曲线的绘图层。 以 2024 年 7 月 24 日发布的某公开版本为例,该指标在社区累计被加载 1184 次、评论 10 条,说明手动复盘人群对它有一定刚需。 外汇与贵金属杠杆高、滑点随机,历史盈利分布不代表未来;用这类图做验证时,先开 MT5 的「账户历史」核对成交时间,再叠加指标看盈亏曲线是否和你的记忆一致。
「历史仓位没有现成函数可查」
在 MT5 里,未平仓位和对冲账户的独立仓位都有直接接口可取:净额账户用 PositionSelect() 按品种名选中,对冲账户则先用 PositionsTotal() 拿数量,再靠 PositionGetTicket(i) 取索引对应的单号,最后 PositionSelectByTicket() 定位。这套逻辑跑通很容易,但已平掉的历史仓位就断了——平台没有专门处理历史仓位的函数。 破局点在于每个仓位都有唯一 ID,且这个 ID 被记在影响该仓位的每笔成交上(开仓、改仓、平仓都带)。用 HistorySelect(0, TimeCurrent()) 拉出成交列表,配合 HistoryDealsTotal() 与 HistoryDealGetTicket(i) 遍历,再从每笔成交用 HistoryDealGetInteger(ticket, DEAL_POSITION_ID) 取出参与的仓位 ID,就能反向拼出历史仓位。 具体做法是在遍历成交时,检查该仓位 ID 是否已在自建列表:没有就新建仓位对象,有就追加当前成交到它的成交清单。跑完整个 HistoryDealsTotal() 循环,所有历史仓位及其对应成交就还原了。 落地时建议建三个类:成交类(存仓位标识与属性)、仓位类(存参与成交列表与自身属性)、历史仓位列表类(按属性筛选)。借助标准库的 CObject 派生指针数组,重载 Compare() 并按 m_sort_mode 切换排序字段,搜索效率会高很多。外汇与贵金属杠杆高,历史回测仅反映过去,实盘仍可能因滑点、点差突变而偏离。 下面这段是从原文抽出的核心调用骨架,直接在 MT5 新建脚本粘进去就能看现象:
<span class="keyword">if</span>(<span class="functions">PositionSelect</span>(symbol_name)) { <span class="comment">class=class="str">"cmt">/* Work with selected position data <a href="/zh/docs/trading/positiongetdouble" target="_blank">PositionGetDouble()</a>; <a href="/zh/docs/trading/positiongetinteger" target="_blank">PositionGetInteger()</a>; <a href="/zh/docs/trading/positiongetstring" target="_blank">PositionGetString()</a>; */</span> } <span class="keyword">class="type">int</span> total=<span class="functions">PositionsTotal</span>(); <span class="keyword">for</span>(<span class="keyword">class="type">int</span> i=total-<span class="number">class="num">1</span>;i>=<span class="number">class="num">0</span>;i--) { <span class="keyword">class="type">ulong</span> ticket=<span class="functions">PositionGetTicket</span>(i); <span class="keyword">if</span>(ticket==<span class="number">class="num">0</span> || !<span class="functions">PositionSelectByTicket</span>(ticket)) <span class="keyword">class="kw">continue</span>; <span class="comment">class=class="str">"cmt">/* Work with selected position data <a href="/zh/docs/trading/positiongetdouble" target="_blank">PositionGetDouble()</a>; <a href="/zh/docs/trading/positiongetinteger" target="_blank">PositionGetInteger()</a>; <a href="/zh/docs/trading/positiongetstring" target="_blank">PositionGetString()</a>; */</span> } <span class="keyword">if</span>(<span class="functions">HistorySelect</span>(<span class="number">class="num">0</span>,<span class="functions">TimeCurrent</span>())) { <span class="keyword">class="type">int</span> total=<span class="functions">HistoryDealsTotal</span>(); <span class="keyword">for</span>(<span class="keyword">class="type">int</span> i=<span class="number">class="num">0</span>;i<total;i++) { <span class="keyword">class="type">ulong</span> ticket=<span class="functions">HistoryDealGetTicket</span>(i); <span class="keyword">if</span>(ticket==<span class="number">class="num">0</span>) <span class="keyword">class="kw">continue</span>; <span class="keyword">class="type">long</span> pos_id=<span class="functions">HistoryDealGetInteger</span>(ticket,<span class="macro">DEAL_POSITION_ID</span>); <span class="comment">class=class="str">"cmt">// ID of a position a deal participated in</span> <span class="comment">class=class="str">"cmt">/* Work with selected deal data HistoryDealGetDouble(); HistoryDealGetInteger(); HistoryDealGetString(); */</span> } } <span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span> <span class="comment">class=class="str">"cmt">//| PositionInfoIndicator.mq5 |</span> <span class="comment">class=class="str">"cmt">//| Copyright class="num">2023, MetaQuotes Ltd. |</span>
用填充图把持仓盈亏画进副图
想在 MT5 副窗直接看当前持仓的浮动盈亏带,而不是切到交易面板盯数字,可以写一个独立指标,把盈利区填绿、亏损区填红。 下面这段声明决定了指标骨架:用 indicator_separate_window 放到副图,indicator_buffers 设 2 个缓冲区但只 plot 1 个图,类型选 DRAW_FILLING,颜色对是 clrGreen 和 clrRed,标签写 Profit;ZeroLine,意味着两条缓冲线之间的区域会被填充,零轴就是盈亏分界。 代码里还引了 ArrayObj.mqh,并在 CArrayObj::Search 里做有序数组定位:若数组空、指针无效或排序模式为 -1 直接返回 -1;否则走 QuickSearch,再比 Compare 是否等于 0,相等才返回位置,否则视作未找到。这套查找逻辑在你后面按开仓时间排序持仓对象时很关键,能避免线性扫描拖慢刷新。 实际加载后,若某根 K 线期间持仓净值为正,副图绿块向上撑;转负则红块向下吞。外汇与贵金属杠杆高,填充带只反映当下敞口盈亏,不预示走势,用它辅助平仓决策时仍需自担风险。
class=class="str">"cmt">//| [MQL5官方文档] | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#class="kw">property copyright "Copyright class="num">2023, MetaQuotes Ltd." class="macro">#class="kw">property link "[MQL5官方文档] class="macro">#class="kw">property version "class="num">1.00" class="macro">#class="kw">property indicator_separate_window class="macro">#class="kw">property indicator_buffers class="num">2 class="macro">#class="kw">property indicator_plots class="num">1 class=class="str">"cmt">//--- plot Fill class="macro">#class="kw">property indicator_label1 "Profit;ZeroLine" class="macro">#class="kw">property indicator_type1 DRAW_FILLING class="macro">#class="kw">property indicator_color1 clrGreen,clrRed class="macro">#class="kw">property indicator_style1 STYLE_SOLID class="macro">#class="kw">property indicator_width1 class="num">1 class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| PositionInfoIndicator.mq5 | class=class="str">"cmt">//| Copyright class="num">2023, MetaQuotes Ltd. | class=class="str">"cmt">//| [MQL5官方文档] | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#class="kw">property copyright "Copyright class="num">2023, MetaQuotes Ltd." class="macro">#class="kw">property link "[MQL5官方文档] class="macro">#class="kw">property version "class="num">1.00" class="macro">#class="kw">property indicator_separate_window class="macro">#class="kw">property indicator_buffers class="num">2 class="macro">#class="kw">property indicator_plots class="num">1 class=class="str">"cmt">//--- plot Fill class="macro">#class="kw">property indicator_label1 "Profit;ZeroLine" class="macro">#class="kw">property indicator_type1 DRAW_FILLING class="macro">#class="kw">property indicator_color1 clrGreen,clrRed class="macro">#class="kw">property indicator_style1 STYLE_SOLID class="macro">#class="kw">property indicator_width1 class="num">1 class=class="str">"cmt">//--- includes class="macro">#include <Arrays\ArrayObj.mqh> class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Search of position of element in a sorted array | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int CArrayObj::Search(class="kw">const CObject *element) class="kw">const { class="type">int pos; class=class="str">"cmt">//--- check if(m_data_total==class="num">0 || !CheckPointer(element) || m_sort_mode==-class="num">1) class="kw">return(-class="num">1); class=class="str">"cmt">//--- search pos=QuickSearch(element); if(m_data[pos].Compare(element,m_sort_mode)==class="num">0) class="kw">return(pos); class=class="str">"cmt">//--- not found
◍ 成交与持仓的排序枚举怎么定义
在 MT5 里做持仓盈亏可视化指标时,先把底层数据对象的排序维度固化成枚举,后续数组排序才不会乱。下面这段来自一个独立窗口指标的工程代码,用两个 enum 把「成交」和「持仓」的可排字段全部列了出来。 DEAL_SORT_MODE 覆盖了 14 种成交排序键:从毫秒级执行时间、票号、持仓 ID、魔术码,到类型、开平方向、成交量、价格、佣金、库存费、盈亏、手续费、品种名。POS_SORT_MODE 则区分了开仓/平仓的毫秒时间与普通时间,以及开仓单号与平仓单号。 实际用处很直接:当你用 CArrayObj 装载历史成交或持仓,调用 Sort() 时传入对应枚举值,就能按盈利大小或平仓时间轴重排。外汇与贵金属杠杆高,重排后做权益曲线填充图,能更直观看出哪段行情的回撤概率偏大。 别把枚举当装饰。若你要接小布盯盘做批量回测,少写一个 SWAP 或 FEE 键,跨品种贵金属的隔夜成本就可能被漏算,回测净值会偏乐观。
class=class="str">"cmt">//--- enums class=class="str">"cmt">//--- Deal object sorting modes enum ENUM_DEAL_SORT_MODE { DEAL_SORT_MODE_TIME_MSC, class=class="str">"cmt">// Deal execution time in milliseconds DEAL_SORT_MODE_TIME, class=class="str">"cmt">// Deal execution time DEAL_SORT_MODE_TIKET, class=class="str">"cmt">// Deal ticket DEAL_SORT_MODE_POS_ID, class=class="str">"cmt">// Position ID DEAL_SORT_MODE_MAGIC, class=class="str">"cmt">// Magic number for a deal DEAL_SORT_MODE_TYPE, class=class="str">"cmt">// Deal type DEAL_SORT_MODE_ENTRY, class=class="str">"cmt">// Deal entry - entry in, entry out, reverse DEAL_SORT_MODE_VOLUME, class=class="str">"cmt">// Deal volume DEAL_SORT_MODE_PRICE, class=class="str">"cmt">// Deal price DEAL_SORT_MODE_COMISSION, class=class="str">"cmt">// Deal commission DEAL_SORT_MODE_SWAP, class=class="str">"cmt">// Accumulated swap when closing DEAL_SORT_MODE_PROFIT, class=class="str">"cmt">// Deal financial result DEAL_SORT_MODE_FEE, class=class="str">"cmt">// Deal fee DEAL_SORT_MODE_SYMBOL, class=class="str">"cmt">// Name of the symbol for which the deal is executed }; class=class="str">"cmt">//--- Position object sorting modes enum ENUM_POS_SORT_MODE { POS_SORT_MODE_TIME_IN_MSC, class=class="str">"cmt">// Open time in milliseconds POS_SORT_MODE_TIME_OUT_MSC,class=class="str">"cmt">// Close time in milliseconds POS_SORT_MODE_TIME_IN, class=class="str">"cmt">// Open time POS_SORT_MODE_TIME_OUT, class=class="str">"cmt">// Close time POS_SORT_MODE_DEAL_IN, class=class="str">"cmt">// Open deal ticket POS_SORT_MODE_DEAL_OUT, class=class="str">"cmt">// Close deal ticket };
「持仓排序枚举的字段含义」
在 MT5 的持仓管理逻辑里,排序模式靠一组枚举常量来指定,直接决定后续遍历持仓时按什么键排。 下面这段枚举把可选的排序维度列得很清楚:POS_SORT_MODE_ID 按持仓唯一 ID 排;POS_SORT_MODE_MAGIC 按 EA 的 magic 号排;POS_SORT_MODE_PRICE_IN 按开仓价排;POS_SORT_MODE_PRICE_OUT 按平仓价排;POS_SORT_MODE_VOLUME 按手数排;POS_SORT_MODE_SYMBOL 按交易品种排。 实盘里若同时跑多个黄金 EA,用 MAGIC 排序能快速隔离各自持仓,避免误平别人的单;纯手工多品种盯盘则用 SYMBOL 排序更顺手。外汇与贵金属杠杆高,批量操作前务必在策略测试器用小资金验证排序结果。
POS_SORT_MODE_ID, class=class="str">"cmt">// Position ID POS_SORT_MODE_MAGIC, class=class="str">"cmt">// Position magic POS_SORT_MODE_PRICE_IN, class=class="str">"cmt">// Open price POS_SORT_MODE_PRICE_OUT, class=class="str">"cmt">// Close price POS_SORT_MODE_VOLUME, class=class="str">"cmt">// Position volume POS_SORT_MODE_SYMBOL, class=class="str">"cmt">// Position symbol };
交易类如何把每笔成交归档成仓位
交易类的结构很直白:私有段只放成交属性变量,公有段给每个属性配了 get/set 方法。Compare() 是个虚函数,按调用时传入的比较模式挑属性比对——当前对象该属性值更大返回 1,更小返回 -1,相等返回 0,方便后续按某一维度排序。 构造函数里默认这笔成交已被选中,直接把属性写进私有变量。这样每从终端历史里捞一笔成交,就 new 一个对象,里面已经装好了 ticket、magic、position_id、time_msc 等 13 个字段。 关键动作在 position_id:从对象里取出仓位 ID,再去建历史仓位类的对象,把同一 ID 下的所有成交挂到它的交易列表里。跑完之后,一个历史仓位对象就聚合了它全生命周期的成交,提取仓位历史只需遍历这个列表。 下面这段是成交类的骨架声明,私有变量和注释一一对应,复制进 MT5 就能编译看结构。外汇与贵金属历史回测含跳空与滑点风险,实盘统计结果可能偏离历史样本。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Deal class | class=class="str">"cmt">//+------------------------------------------------------------------+ class CDeal : class="kw">public CObject { class="kw">private: class="type">long m_ticket; class=class="str">"cmt">// Deal ticket class="type">long m_magic; class=class="str">"cmt">// Magic number for a deal class="type">long m_position_id; class=class="str">"cmt">// Position ID class="type">long m_time_msc; class=class="str">"cmt">// Deal execution time in milliseconds class="type">class="kw">datetime m_time; class=class="str">"cmt">// Deal execution time ENUM_DEAL_TYPE m_type; class=class="str">"cmt">// Deal type ENUM_DEAL_ENTRY m_entry; class=class="str">"cmt">// Deal entry - entry in, entry out, reverse class="type">class="kw">double m_volume; class=class="str">"cmt">// Deal volume class="type">class="kw">double m_price; class=class="str">"cmt">// Deal price class="type">class="kw">double m_comission; class=class="str">"cmt">// Deal commission class="type">class="kw">double m_swap; class=class="str">"cmt">// Accumulated swap when closing class="type">class="kw">double m_profit; class=class="str">"cmt">// Deal financial result class="type">class="kw">double m_fee; class=class="str">"cmt">// Deal fee class="type">class="kw">string m_symbol; class=class="str">"cmt">// Name of the symbol for which the deal is executed class=class="str">"cmt">//--- Return the deal direction description class="type">class="kw">string EntryDescription(class="type">void) class="kw">const {