如何不通过翻找历史交易记录直接在图表上查看交易情况·进阶篇
(2/3)·图表被历史标签糊满看不清?本篇教你用光标键逐一调出平仓单并居中显示
成交对象里的字段与赋值入口
在 MT5 自建交易类里,一笔 deal 的基础属性通常拆成数值、字符串和行情三类成员。下面这段声明把止盈价、品种名、外部系统 ID、点值、买卖盘口都塞进同一个结构,方便后续在图表上还原当时上下文。 double m_tp; // 止盈价位 double m_bid; // 成交时的买价 double m_ask; // 成交时的卖价 int m_spread; // 成交时的点差(以整数点计) string m_symbol; // 成交品种名称 string m_external_id; // 交易所/外部系统里的成交编号 这些字段不是摆设。m_point 和 m_digits 决定了挂单偏移要不要按品种精度四舍五入;m_bid/m_ask 留痕后,你回看箭头标记就能算出当时真实滑点可能偏向哪边。 公开区的 Set 方法是一对一写字段,例如 SetTicket(long) 直接改 m_ticket,SetTypeDeal(ENUM_DEAL_TYPE) 定买卖类别。复制进 EA 头文件,把 DrawArrow() 留作虚函数,子类重写就能换箭头样式,不用碰核心数据层。 外汇与贵金属杠杆高,m_spread 在消息行情里可能瞬间扩到日常 5~10 倍,用这类结构做复盘时别把样本期的点差当常态。
class="type">class="kw">double m_tp; class=class="str">"cmt">// Take Profit level class=class="str">"cmt">//--- String properties class="type">class="kw">string m_symbol; class=class="str">"cmt">// Name of the symbol for which the deal is executed class="type">class="kw">string m_comment; class=class="str">"cmt">// Deal comment class="type">class="kw">string m_external_id; class=class="str">"cmt">// Deal ID in an external trading system(on the exchange) class=class="str">"cmt">//--- Additional properties class="type">int m_digits; class=class="str">"cmt">// Symbol digits class="type">class="kw">double m_point; class=class="str">"cmt">// Symbol point class="type">class="kw">double m_bid; class=class="str">"cmt">// Bid when performing a deal class="type">class="kw">double m_ask; class=class="str">"cmt">// Ask when performing a deal class="type">int m_spread; class=class="str">"cmt">// Spread when performing a deal class="type">class="kw">color m_color_arrow; class=class="str">"cmt">// Deal label class="type">class="kw">color class=class="str">"cmt">//--- Draws an arrow corresponding to the deal type. It can be redefined in the inherited classes class="kw">virtual class="type">void DrawArrow(class="type">void); class="kw">public: class=class="str">"cmt">//--- Set the properties class=class="str">"cmt">//--- Integer properties class="type">void SetTicket(const class="type">long ticket) { this.m_ticket=ticket; } class=class="str">"cmt">// Ticket class="type">void SetOrder(const class="type">long order) { this.m_order=order; } class=class="str">"cmt">// Order class="type">void SetTime(const class="type">class="kw">datetime time) { this.m_time=time; } class=class="str">"cmt">// Time class="type">void SetTimeMsc(const class="type">long value) { this.m_time_msc=value; } class=class="str">"cmt">// Time in milliseconds class="type">void SetTypeDeal(const ENUM_DEAL_TYPE type) { this.m_type=type; } class=class="str">"cmt">// Type class="type">void SetEntry(const ENUM_DEAL_ENTRY entry) { this.m_entry=entry; } class=class="str">"cmt">// Direction
◍ 订单结构里的字段写入接口
在 MT5 的自定义交易类里,这组 Set 方法负责把外部计算好的参数写进私有成员,相当于给一笔挂单或成交对象逐字段赋值。 从代码看,标识类字段(magic、reason、position_id)与真实数值字段(volume、price、commission、swap、profit、fee、sl、tp)是分开声明的,字符串类还有 symbol 与 comment。这种分层在回测日志排查时很有用:比如同一 magic 下若 swap 累计异常,可直接定位 SetSwap 的调用链。 [CODE] 逐行拆解: void SetMagic(const long magic) { this.m_magic=magic; } // 写入魔数,区分 EA 交易批次 void SetReason(const ENUM_DEAL_REASON reason) { this.m_reason=reason; } // 写入成交来源(脚本/EA/手动) void SetPositionID(const long id) { this.m_position_id=id; } // 绑定持仓 ID void SetVolume(const double volume) { this.m_volume=volume; } // 手数 void SetPrice(const double price) { this.m_price=price; } // 挂单或成交价 void SetCommission(const double value) { this.m_commission=value; } // 佣金 void SetSwap(const double value) { this.m_swap=value; } // 平仓时累计掉期 void SetProfit(const double value) { this.m_profit=value; } // 财务结果 void SetFee(const double value) { this.m_fee=value; } // 交易费 void SetSL(const double value) { this.m_sl=value; } // 止损位 void SetTP(const double value) { this.m_tp=value; } // 止盈位 void SetSymbol(const string symbol) { this.m_symbol=symbol; } // 品种名 void SetComment(const string comment) { this.m_comment=comment; } // 备注 实盘或复盘时,外汇与贵金属波动受杠杆与消息面影响大,高风险特征明显;建议你在 MT5 里新建一个 test script,依次调用这组 Set 方法后打印结构体,确认每字段写入无误再接入下单逻辑。
class="type">void SetMagic(const class="type">long magic) { this.m_magic=magic; } class=class="str">"cmt">// Magic number class="type">void SetReason(const ENUM_DEAL_REASON reason) { this.m_reason=reason; } class=class="str">"cmt">// Deal execution reason or source class="type">void SetPositionID(const class="type">long id) { this.m_position_id=id; } class=class="str">"cmt">// Position ID class=class="str">"cmt">//--- Real properties class="type">void SetVolume(const class="type">class="kw">double volume) { this.m_volume=volume; } class=class="str">"cmt">// Volume class="type">void SetPrice(const class="type">class="kw">double price) { this.m_price=price; } class=class="str">"cmt">// Price class="type">void SetCommission(const class="type">class="kw">double value) { this.m_commission=value; } class=class="str">"cmt">// Commission class="type">void SetSwap(const class="type">class="kw">double value) { this.m_swap=value; } class=class="str">"cmt">// Accumulated swap when closing class="type">void SetProfit(const class="type">class="kw">double value) { this.m_profit=value; } class=class="str">"cmt">// Financial result class="type">void SetFee(const class="type">class="kw">double value) { this.m_fee=value; } class=class="str">"cmt">// Deal fee class="type">void SetSL(const class="type">class="kw">double value) { this.m_sl=value; } class=class="str">"cmt">// Stop Loss level class="type">void SetTP(const class="type">class="kw">double value) { this.m_tp=value; } class=class="str">"cmt">// Take Profit level class=class="str">"cmt">//--- String properties class="type">void SetSymbol(const class="type">class="kw">string symbol) { this.m_symbol=symbol; } class=class="str">"cmt">// Symbol name class="type">void SetComment(const class="type">class="kw">string comment) { this.m_comment=comment; } class=class="str">"cmt">// Comment
「从成交对象里直接抠出 Ticket 和毫秒时间」
在 MT5 的 CDeal 类封装里,成交记录不是靠 HistoryDealGetXXX 一个个字段去查,而是把属性做成成员函数直接返回。下面这段是类里取属性的一段典型实现,开 MT5 新建个 include 文件就能原样编译。 代码里分了整数属性和实数属性两块。整数块里 Ticket() 返回 m_ticket,这是成交在交易历史里的唯一编号;TimeMsc() 返回 m_time_msc,精度到毫秒,比 Time() 的秒级 datetime 更适合做高频去重。 实数块目前只露出 Volume() 返回 m_volume,即成交手数。注意所有 getter 都带 const 修饰,说明它们不改动对象内部状态,可以放心在回测或实盘事件里反复调用。 外汇和贵金属杠杆高,用 PositionID() 和 Magic() 交叉过滤自己的 EA 成交,能避免把手动单误判成策略单,从而降低统计偏差的概率。
class="type">void SetExternalID(const class="type">class="kw">string ext_id) { this.m_external_id=ext_id; } class=class="str">"cmt">// Deal ID in an external trading system class=class="str">"cmt">//--- Get the properties class=class="str">"cmt">//--- Integer properties class="type">long Ticket(class="type">void) const { class="kw">return(this.m_ticket); } class=class="str">"cmt">// Ticket class="type">long Order(class="type">void) const { class="kw">return(this.m_order); } class=class="str">"cmt">// Order class="type">class="kw">datetime Time(class="type">void) const { class="kw">return(this.m_time); } class=class="str">"cmt">// Time class="type">long TimeMsc(class="type">void) const { class="kw">return(this.m_time_msc); } class=class="str">"cmt">// Time in milliseconds ENUM_DEAL_TYPE TypeDeal(class="type">void) const { class="kw">return(this.m_type); } class=class="str">"cmt">// Type ENUM_DEAL_ENTRY Entry(class="type">void) const { class="kw">return(this.m_entry); } class=class="str">"cmt">// Direction class="type">long Magic(class="type">void) const { class="kw">return(this.m_magic); } class=class="str">"cmt">// Magic number ENUM_DEAL_REASON Reason(class="type">void) const { class="kw">return(this.m_reason); } class=class="str">"cmt">// Deal execution reason or source class="type">long PositionID(class="type">void) const { class="kw">return(this.m_position_id); } class=class="str">"cmt">// Position ID class=class="str">"cmt">//--- Real properties class="type">class="kw">double Volume(class="type">void) const { class="kw">return(this.m_volume); } class=class="str">"cmt">// Volume
持仓对象的只读取值接口
在自定义持仓类里,这一组方法全部声明为 const,意味着调用时不会改动对象内部状态,只把私有成员吐出来供外部读取。
Price() 返回 m_price,即开仓或标记价格;Commission() 与 Fee() 分别给出佣金和单笔成交手续费,两者在 MT5 真实账户中常不同步计入浮盈。Swap() 返回平仓时累计的库存费,隔夜持仓超过 24 小时才可能显著。
Profit() 是财务结果,未扣除 fee 前它可能为正但净值仍亏,验证时要把 Profit() 减 Fee() 再减 Commission()。SL() / TP() 直接给止损止盈价位,若返回 0 表示该侧未挂。
Bid() / Ask() / Spread() 保留成交瞬间的市场快照:Spread() 为 int 类型,通常是点差点数,比如 EURUSD 在流动性正常时可能为 8~12 点。Symbol() 返回品种名字符串,便于在多品种回测里区分持仓来源。
把这些方法直接贴进你的 .mqh 结构体,编译后可在 EA 里写 double net = pos.Profit() - pos.Fee() - pos.Commission(); 做净收益判断,比只看 Profit() 更接近实盘。
class="type">class="kw">double Price(class="type">void) const { class="kw">return(this.m_price); } class=class="str">"cmt">// Price class="type">class="kw">double Commission(class="type">void) const { class="kw">return(this.m_commission); } class=class="str">"cmt">// Commission class="type">class="kw">double Swap(class="type">void) const { class="kw">return(this.m_swap); } class=class="str">"cmt">// Accumulated swap when closing class="type">class="kw">double Profit(class="type">void) const { class="kw">return(this.m_profit); } class=class="str">"cmt">// Financial result class="type">class="kw">double Fee(class="type">void) const { class="kw">return(this.m_fee); } class=class="str">"cmt">// Deal fee class="type">class="kw">double SL(class="type">void) const { class="kw">return(this.m_sl); } class=class="str">"cmt">// Stop Loss level class="type">class="kw">double TP(class="type">void) const { class="kw">return(this.m_tp); } class=class="str">"cmt">// Take Profit level class="type">class="kw">double Bid(class="type">void) const { class="kw">return(this.m_bid); } class=class="str">"cmt">// Bid when performing a deal class="type">class="kw">double Ask(class="type">void) const { class="kw">return(this.m_ask); } class=class="str">"cmt">// Ask when performing a deal class="type">int Spread(class="type">void) const { class="kw">return(this.m_spread); } class=class="str">"cmt">// Spread when performing a deal class=class="str">"cmt">//--- String properties class="type">class="kw">string Symbol(class="type">void) const { class="kw">return(this.m_symbol); } class=class="str">"cmt">// Symbol name
◍ 扒开 CDeal 类的存取与绘制接口
在 MT5 标准库的交易历史封装里,CDeal 类把每笔成交的元数据拆成了可直接调用的成员函数。想在自己的 EA 里把某笔单的备注和外部系统 ID 捞出来,直接读 Comment() 与 ExternalID() 即可,两者都带 const 修饰,不会改动底层结构。 下面这段声明展示了标签颜色与图表显隐的控制入口:SetColorArrow 负责改箭头色,HideArrow / ShowArrow 的入参 chart_redraw 默认 false,意味着你批量隐藏十笔成交时若都走默认,可能只在最后一次手动 Refresh() 才整图重绘,CPU 占用倾向更低。 TypeDescription、EntryDescription、ReasonDescription 三个函数把枚举型的成交类型、开平方式和触发原因转成人类可读字符串,做日志或悬浮提示时比硬查 DEAL_TYPE 枚举省事。Description 与虚函数 Tooltip 则分别给出简描和鼠标悬停文本,Print 一键把属性喷到专家日志。 构造函数重载值得注意:无参版 CDeal(void) 仅把 m_ticket 置 0,带 ulong ticket 的版本才真正绑定某笔历史成交。若你传了不存在的 ticket,后续取属性大概率返回空或零值,开 MT5 用 HistoryDealGetTicket 先校验再 new 才稳。
class="type">class="kw">string Comment(class="type">void) const { class="kw">return(this.m_comment); } class=class="str">"cmt">// Comment class="type">class="kw">string ExternalID(class="type">void) const { class="kw">return(this.m_external_id); } class=class="str">"cmt">// Deal ID in an external trading system class=class="str">"cmt">//--- Set the class="type">class="kw">color of the deal label class="type">void SetColorArrow(const class="type">class="kw">color clr); class=class="str">"cmt">//--- (class="num">1) Hide, (class="num">2) display the deal label on a chart class="type">void HideArrow(const class="type">bool chart_redraw=false); class="type">void ShowArrow(const class="type">bool chart_redraw=false); class=class="str">"cmt">//--- Return the description of a(class="num">1) deal type, (class="num">2) position change method and(class="num">3) deal reason class="type">class="kw">string TypeDescription(class="type">void) const; class="type">class="kw">string EntryDescription(class="type">void) const; class="type">class="kw">string ReasonDescription(class="type">void) const; class=class="str">"cmt">//--- Return(class="num">1) a class="type">short description and(class="num">2) a tooltip text of a deal class="type">class="kw">string Description(class="type">void); class="kw">virtual class="type">class="kw">string Tooltip(class="type">void); class=class="str">"cmt">//--- Print deal properties in the journal class="type">void Print(class="type">void); class=class="str">"cmt">//--- Compare two objects by the class="kw">property specified in &class="macro">#x27;mode&class="macro">#x27; class="kw">virtual class="type">int Compare(const CObject *node, const class="type">int mode=class="num">0) const; class=class="str">"cmt">//--- Constructors/destructor CDeal(class="type">void) { this.m_ticket=class="num">0; } CDeal(const class="type">ulong ticket); ~CDeal(); }; CDeal::CDeal(const class="type">ulong ticket) { this.m_ticket = (class="type">long)ticket; class=class="str">"cmt">// Deal ticket
「从成交单号回填一笔历史交易的全字段」
在 EA 回测或实盘复盘里,拿到一个 deal ticket 后,第一件事就是把它对应的整数、双精度、字符串属性全部读出来塞进结构体。下面这段就是典型的回填逻辑,整数类用 HistoryDealGetInteger,双精度类用 HistoryDealGetDouble。 整数属性里最容易被忽略的是 DEAL_TIME_MSC,它给出毫秒级成交时间,在做高频出场归因时比 DEAL_TIME 的秒级精度更有用;DEAL_POSITION_ID 则用来把多笔平仓 deal 归并到同一个持仓上。 双精度属性中 DEAL_FEE 和 DEAL_SWAP 要分开看:前者是经纪商额外手续费,后者是隔夜利息,二者都会直接吃掉 DEAL_PROFIT 的净值。外汇与贵金属杠杆高,swap 在周三翻三倍时可能让隔夜仓成本陡增,复盘时务必单独拉出来核对。 代码只列到字符串属性之前,但读法一致:字符串类用 HistoryDealGetString 取品种名、comment 等。开 MT5 按 F4 把这段粘进你的 CDeal 类,改一下成员变量名就能直接跑。
this.m_order = ::HistoryDealGetInteger(ticket, DEAL_ORDER); class=class="str">"cmt">// Order this.m_time = (class="type">class="kw">datetime)::HistoryDealGetInteger(ticket, DEAL_TIME); class=class="str">"cmt">// Deal execution time this.m_time_msc = ::HistoryDealGetInteger(ticket, DEAL_TIME_MSC); class=class="str">"cmt">// Deal execution time in milliseconds this.m_type = (ENUM_DEAL_TYPE)::HistoryDealGetInteger(ticket, DEAL_TYPE); class=class="str">"cmt">// Type this.m_entry = (ENUM_DEAL_ENTRY)::HistoryDealGetInteger(ticket, DEAL_ENTRY); class=class="str">"cmt">// Direction this.m_magic = ::HistoryDealGetInteger(ticket, DEAL_MAGIC); class=class="str">"cmt">// Magic number this.m_reason = (ENUM_DEAL_REASON)::HistoryDealGetInteger(ticket, DEAL_REASON); class=class="str">"cmt">// Deal execution reason or source this.m_position_id= ::HistoryDealGetInteger(ticket, DEAL_POSITION_ID); class=class="str">"cmt">// Position ID class=class="str">"cmt">//--- Real properties this.m_volume = ::HistoryDealGetDouble(ticket, DEAL_VOLUME); class=class="str">"cmt">// Volume this.m_price = ::HistoryDealGetDouble(ticket, DEAL_PRICE); class=class="str">"cmt">// Price this.m_commission = ::HistoryDealGetDouble(ticket, DEAL_COMMISSION); class=class="str">"cmt">// Commission this.m_swap = ::HistoryDealGetDouble(ticket, DEAL_SWAP); class=class="str">"cmt">// Accumulated swap when closing this.m_profit = ::HistoryDealGetDouble(ticket, DEAL_PROFIT); class=class="str">"cmt">// Financial result this.m_fee = ::HistoryDealGetDouble(ticket, DEAL_FEE); class=class="str">"cmt">// Deal fee this.m_sl = ::HistoryDealGetDouble(ticket, DEAL_SL); class=class="str">"cmt">// Stop Loss level this.m_tp = ::HistoryDealGetDouble(ticket, DEAL_TP); class=class="str">"cmt">// Take Profit level class=class="str">"cmt">//--- String properties