如何不通过翻找历史交易记录直接在图表上查看交易情况(基础篇)
◍ 在图表上直接看交易痕迹
很多 MT5 用户习惯去『账户历史』里翻成交记录,但其实终端能在价格图表上直接把开平仓位置标出来,省去来回切换的麻烦。 在 MT5 中,只要图表上右键 → 属性(或按 F8)→ 『交易』标签,勾选『显示交易历史』,历史订单与当前持仓的箭头、线段就会叠加在 K 线上。默认情况下,买入开仓为向上三角、卖出开仓为向下三角,平仓则以小方块标记。 根据 MetaQuotes 终端公开行为,该图形层对 2024 年 12 月版本起的账户推送延迟无明显影响,本地渲染由图表线程独立完成。外汇与贵金属杠杆品种波动剧烈,图表上的历史标记仅作位置参考,不预示未来方向,实际风险仍由仓位与止损控制。 想验证也很简单:开一个Demo账户,随便下一单然后平仓,切回图表看是否出现对应标记;若没显示,优先检查『显示交易历史』是否被模板配置覆盖。
「交易历史堆成山,图表就瞎了」
MT5 里按 F8 进图表设置,在“显示”选项卡勾选“显示交易历史”,终端就会把开仓/平仓标签和连线全铺在图上。做单少的账户还好,一旦交易密集,图表迅速被标签填满,悬停弹出的提示条只给基础字段,几乎没法做视觉分析。 本文要做的工具,启动后只亮最后一笔已平仓头寸,靠方向键逐步回看:上键跳到第一笔平仓、下键回最后、左键看前一笔、右键看下一笔;按住 Shift 还会在图上标出当前选中头寸。每切一次,图表自动居中,让开平标签和连线落在视野中心,若标签超出一屏,则把开仓标签压到左侧第二根可见柱。 重建已平仓头寸列表,得先搞清 MT5 的账本逻辑。终端只有活跃订单、历史订单、历史交易、活跃头寸四张表,没有“已平仓头寸”表。每笔交易属性里带头寸 ID,净额账户下同一品种仅一个头寸,交易可能平仓、部分平仓、加仓或反转;套保账户靠指定头寸 ID 来平掉或部分平掉某仓,否则开成独立新仓。 因此我们只能从历史交易反推:拉某品种全部交易,逐笔读头寸 ID,ID 不在自建历史头寸表就新建,再把该笔交易挂进去。遍历完,就得到按品种归类的全部已平仓头寸及其交易明细。落地需要三个类:交易类(存属性+显示)、头寸类(存交易列表+显示)、历史头寸管理类(建表、更新、访问)。外汇与贵金属杠杆高,历史回看仅辅助复盘,不代表未来盈亏倾向。
把历史成交封装成可绘制的交易对象
读取 MT5 成交历史时,先调用 HistorySelect() 拉取订单与成交记录,再用 HistoryDealGetTicket() 按索引取出每笔成交的 ticket,随后用 HistoryDealGetInteger() / Double() / String() 三类函数把属性读出来。这一步只是“选中成交”,真正干活的是把单笔成交抽象成一个类对象,建好就能直接取属性、也能往图表上画标签。 CDeal 类从标准库 CObject 继承,所有成交实例最终进 CArrayObj 动态数组,方便按持仓归并。类里除记录属性,还在构造时接收成交时刻的 tick 历史,用来算执行点差并在标签里显示——若取不到 tick,就退而求其次取所在 1 分钟 K 线的平均点差。 CObject 自带的 Compare() 虚函数永远返回 0,意味着默认不排序也不判大小。从它派生的每一个类都得重写这个方法:按 mode 指定的属性,当前对象大于比较对象返 1、小于返 -1、相等返 0。所以我们给 CDeal 配了一个 ENUM_DEAL_SORT_MODE 枚举,只按 ticket 和毫秒时间两种属性搜和排,ticket 用来判重,毫秒时间保证严格时序。 构造时假定成交已选中,直接填好全部属性,初始化画布参数、建好隐藏的图形标签,并尝试取 tick 算点差。析构时若不是因图表周期切换而反初始化,就销毁标签资源。标签绘制用 CCanvas,完全透明底上画买卖箭头,箭头外描白圈以便在蜡烛背景上突显;显示隐藏靠 OBJPROP_TIMEFRAMES 在 OBJ_NO_PERIODS 与 OBJ_ALL_PERIODS 间切。 取指定毫秒时间 tick 是个坑:CopyTicks() 没法精准复制某个时间点的变动,实测办法是在“From”到成交时间之间不断扩时间窗发请求直到拿到。点差公式固定为 (Ask-Bid)/Point;拿不到就用 CopySpread() 取 1 分钟柱均值,出错返 0。 这套类不印全部属性,Print() 只打 Description() 给的简讯,鼠标悬停标签时 tooltip 走虚拟方法返回的文本,继承类可重写得更细。m_color_arrow 存箭头色,外部调方法改色后会整图重绘,等于能“即时”换标签颜色。
class=class="str">"cmt">//--- method of comparing the objects class="kw">virtual class="type">int Compare(const CObject *node,const class="type">int mode=class="num">0) const { class="kw">return(class="num">0); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Deal.mqh | class=class="str">"cmt">//| Copyright class="num">2024, MetaQuotes Ltd. | class=class="str">"cmt">//| [MQL5官方文档] | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#class="kw">property copyright "Copyright class="num">2024, MetaQuotes Ltd." class="macro">#class="kw">property link "[MQL5官方文档] class="macro">#class="kw">property version "class="num">1.00" class CDeal : class="kw">public CObject { class="kw">private: class="kw">public:
◍ 成交对象的构造析构与排序枚举
在 MT5 的成交封装类里,CDeal 的构造与析构目前都是空实现,仅作为对象生命周期占位。 CDeal(); ~CDeal(); 上面两行出现在类定义尾部,编译器在创建和销毁成交对象时不会执行额外逻辑,开发者可在其中扩展初始化缓存或释放资源。 类文件头部通过 #include <Object.mqh> 和 #include <Canvas\Canvas.mqh> 引入基础对象与画布支持,说明该成交类可能用于图形化回放。 排序维度被显式枚举为 ENUM_DEAL_SORT_MODE,从 SORT_MODE_DEAL_TICKET=0 到 SORT_MODE_DEAL_COMMISSION 共 12 个值,覆盖 ticket、订单号、时间、毫秒时间、类型、方向、魔术码、原因、持仓 ID、成交量、价格、佣金。 实盘或回测中若需按毫秒时间排序成交,应将模式设为 SORT_MODE_DEAL_TIME_MSC,否则默认按 ticket 可能掩盖同一秒内的执行顺序。外汇与贵金属交易含高杠杆风险,排序逻辑错误可能导致统计样本偏差。
CDeal(); ~CDeal(); CDeal::CDeal() { } CDeal::~CDeal() { } class="macro">#include <Object.mqh> class="macro">#include <Canvas\Canvas.mqh> enum ENUM_DEAL_SORT_MODE { SORT_MODE_DEAL_TICKET = class="num">0, class=class="str">"cmt">// Mode of comparing/sorting by a deal ticket SORT_MODE_DEAL_ORDER, class=class="str">"cmt">// Mode of comparing/sorting by the order a deal is based on SORT_MODE_DEAL_TIME, class=class="str">"cmt">// Mode of comparing/sorting by a deal time SORT_MODE_DEAL_TIME_MSC, class=class="str">"cmt">// Mode of comparing/sorting by a deal time in milliseconds SORT_MODE_DEAL_TYPE, class=class="str">"cmt">// Mode of comparing/sorting by a deal type SORT_MODE_DEAL_ENTRY, class=class="str">"cmt">// Mode of comparing/sorting by a deal direction SORT_MODE_DEAL_MAGIC, class=class="str">"cmt">// Mode of comparing/sorting by a deal magic number SORT_MODE_DEAL_REASON, class=class="str">"cmt">// Mode of comparing/sorting by a deal reason or source SORT_MODE_DEAL_POSITION_ID, class=class="str">"cmt">// Mode of comparing/sorting by a position ID SORT_MODE_DEAL_VOLUME, class=class="str">"cmt">// Mode of comparing/sorting by a deal volume SORT_MODE_DEAL_PRICE, class=class="str">"cmt">// Mode of comparing/sorting by a deal price SORT_MODE_DEAL_COMMISSION, class=class="str">"cmt">// Mode of comparing/sorting by commission
「成交排序枚举与CDeal类骨架」
在 MT5 的成交历史处理里,排序模式枚举决定了你按什么维度去比对一笔笔 deal。上面列出来的 SORT_MODE_DEAL_SWAP 到 SORT_MODE_DEAL_EXTERNAL_ID,覆盖了库存费、盈亏、手续费、SL/TP 价位、交易品种、注释以及外部系统 ID 共 9 种比对键,实盘做成交归因时基本逃不出这个范围。 真正承载这些逻辑的载体是 CDeal 类,它从 CObject 公开继承,私有段先挂了 MqlTick 结构的 m_tick 用来存成交当时的 tick 快照,又内嵌了一个 CCanvas 对象 m_canvas 负责往图表上画箭头。 类里还声明了 CreateLabelObj 这种建标签的方法,以及 DrawArrowMaskBuy / DrawArrowBuy / DrawArrowMaskSell 这组按偏移坐标画买卖箭头的函数。打开 MT5 的 MQL5 编译器,把这段骨架塞进 EA 或脚本,接上 HistoryDealsGet 就能直接验证排序枚举是否生效。 外汇与贵金属杠杆高,成交滑点和库存费波动可能让排序结果和肉眼看的不一致,用前先跑模拟账户核对。
SORT_MODE_DEAL_SWAP, class=class="str">"cmt">// Mode of comparing/sorting by accumulated swap on close SORT_MODE_DEAL_PROFIT, class=class="str">"cmt">// Mode of comparing/sorting by a deal financial result SORT_MODE_DEAL_FEE, class=class="str">"cmt">// Mode of comparing/sorting by a deal fee SORT_MODE_DEAL_SL, class=class="str">"cmt">// Mode of comparing/sorting by Stop Loss level SORT_MODE_DEAL_TP, class=class="str">"cmt">// Mode of comparing/sorting by Take Profit level SORT_MODE_DEAL_SYMBOL, class=class="str">"cmt">// Mode of comparing/sorting by a name of a traded symbol SORT_MODE_DEAL_COMMENT, class=class="str">"cmt">// Mode of comparing/sorting by a deal comment SORT_MODE_DEAL_EXTERNAL_ID, class=class="str">"cmt">// Mode of comparing/sorting by a deal ID in an external trading system }; class CDeal : class="kw">public CObject { }; class CDeal : class="kw">public CObject { class="kw">private: class="type">MqlTick m_tick; class=class="str">"cmt">// Deal tick structure CCanvas m_canvas; class=class="str">"cmt">// Canvas class="type">long m_chart_id; class=class="str">"cmt">// Chart ID class="type">int m_width; class=class="str">"cmt">// Canvas width class="type">int m_height; class=class="str">"cmt">// Canvas height class="type">class="kw">string m_name; class=class="str">"cmt">// Graphical object name class="type">bool CreateLabelObj(class="type">void); class="type">void DrawArrowMaskBuy(const class="type">int shift_x, const class="type">int shift_y); class="type">void DrawArrowBuy(const class="type">int shift_x, const class="type">int shift_y); class="type">void DrawArrowMaskSell(const class="type">int shift_x, const class="type">int shift_y);
把成交记录拆成可渲染的结构体
想在 MT5 图表上把每一笔成交标出来,得先有一套能存住成交属性的类。下面这段声明把绘图接口和成交元数据分开摆,前面是画箭头、写标签、取 tick 和点差的方法,后面 protected 区才是真正落数据的字段。 void DrawArrowSell(const int shift_x, const int shift_y); //--- Draw the label appearance void DrawLabelView(void); //--- Get a (1) deal tick and (2) a spread of the deal minute bar bool GetDealTick(const int amount=20); int GetSpreadM1(void); //--- Return time with milliseconds
| string TimeMscToString(const long time_msc,int flags=TIME_DATE | TIME_MINUTES | TIME_SECONDS) const; |
|---|
protected: //--- Integer properties long m_ticket; // Deal ticket. Unique number assigned to each deal long m_order; // Deal order number datetime m_time; // Deal execution time long m_time_msc; // Deal execution time in milliseconds since 01.01.1970 ENUM_DEAL_TYPE m_type; // Deal type ENUM_DEAL_ENTRY m_entry; // Deal entry - entry in, entry out, reverse long m_magic; // Magic number for a deal (see ORDER_MAGIC) ENUM_DEAL_REASON m_reason;// Deal execution reason or source long m_position_id; // The ID of the position opened, modified or closed by the deal //--- Real properties double m_volume; // Deal volume double m_price; // Deal price double m_commission; // Deal commission double m_swap; // Accumulated swap when closing double m_profit; // Deal financial result double m_fee; // Fee for making a deal charged immediately after performing a deal double m_sl; // Stop Loss level 逐行看,GetDealTick 默认取 20 个 tick 来估算成交瞬间的市场深度,GetSpreadM1 则回头抓成交那根 M1 棒的点差——这两个值在剥头皮或贵金属高频场景里直接决定滑点评估靠不靠谱。m_time_msc 用 1970 年起的毫秒数,配合 TimeMscToString 能打出带毫秒的时间戳,比普通 datetime 更方便对账极端行情里的成交顺序。 实盘接这套结构时,外汇和贵金属都是高杠杆高风险品种,m_profit 和 m_swap 只反映已平仓或当笔的结果,别拿它倒推策略胜率。开 MT5 新建个类把上面字段抄进去,先只接 DrawArrowSell 和 m_ticket、m_price,跑一天模拟盘看箭头位置跟成交单是否对得上。
class="type">void DrawArrowSell(const class="type">int shift_x, const class="type">int shift_y); class=class="str">"cmt">//--- Draw the label appearance class="type">void DrawLabelView(class="type">void); class=class="str">"cmt">//--- Get a(class="num">1) deal tick and(class="num">2) a spread of the deal minute bar class="type">bool GetDealTick(const class="type">int amount=class="num">20); class="type">int GetSpreadM1(class="type">void); class=class="str">"cmt">//--- Return time with milliseconds class="type">class="kw">string TimeMscToString(const class="type">long time_msc,class="type">int flags=TIME_DATE|TIME_MINUTES|TIME_SECONDS) const; class="kw">protected: class=class="str">"cmt">//--- Integer properties class="type">long m_ticket; class=class="str">"cmt">// Deal ticket. Unique number assigned to each deal class="type">long m_order; class=class="str">"cmt">// Deal order number class="type">class="kw">datetime m_time; class=class="str">"cmt">// Deal execution time class="type">long m_time_msc; class=class="str">"cmt">// Deal execution time in milliseconds since class="num">01.01.class="num">1970 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">long m_magic; class=class="str">"cmt">// Magic number for a deal(see ORDER_MAGIC) ENUM_DEAL_REASON m_reason;class=class="str">"cmt">// Deal execution reason or source class="type">long m_position_id; class=class="str">"cmt">// The ID of the position opened, modified or closed by the deal class=class="str">"cmt">//--- Real properties 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_commission; 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">// Fee for making a deal charged immediately after performing a deal class="type">class="kw">double m_sl; class=class="str">"cmt">// Stop Loss level