在 MetaTrader 5 中交易的可视评估和调整(基础篇)
📘

在 MetaTrader 5 中交易的可视评估和调整(基础篇)

第 1/3 篇

「把 MT5 成交历史落盘再说调整」

在 MetaTrader 5 里做交易的可视评估,第一步是把成交历史从终端里导出来存成文件,而不是只盯着账户历史标签页看。这样你才能把历史丢进策略测试器,用真实滑点和点差重放,看清自己止损到底被扫得合不合理。 原文示例发布于 2025 年 11 月 5 日 08:48,截至撰写已被查看 574 次、评论 16 条,说明把成交落盘这件事本身就有不少人在琢磨。外汇和贵金属品种点差跳空频繁,单纯看账户报表容易低估滑点成本,存文件回放更贴近真实。 落地动作很简单:开 MT5 → 打开「EA 交易测试」→ 载入能写成交历史的脚本 → 跑完得到 csv/bin 历史文件 → 再切到测试器用「来自文件的成交历史」模式加载。下一步才是调止损位,但没这步落盘,调整就是盲调。

把真实账户历史搬进策略测试器

实盘跑了一段之后,想复盘“如果当时止损再宽一点会怎样”“尾随改成 15 点会不会少亏”,终端里按 Alt+E 看标准报告只能看静态结果,把成交图标丢到图表上也只能逐品种翻入场出场时间。要一次性横看所有品种的开平、挂单位置和仓位大小,并且动态重演,就得换条路。 办法已经现成:把账户里已完结的成交导出成一个文件,再写一个 EA 在策略测试器里读这个文件,按记录重开仓、平仓。MQL5 的策略测试器本来就支持用历史数据回放,这类 EA 只是把“你的真实成交”当剧本喂进去。 具体分工可以这样定:EA 挂在任意图表上运行时,只干一件事——扫当前账户全部成交历史,写进文件,然后静默;EA 在测试器里运行时,读那个文件,把每一笔成交在测试环境里重做一遍。两套行为靠运行上下文区分,不冲突。 文件备好之后,就能往测试器版 EA 里加改动:比如给所有重演的持仓强行套不同的止损止盈、移动到盈亏平衡、或接一段尾随逻辑,跑完直接比曲线。外汇和贵金属杠杆高、滑点跳空频繁,这种重演只是概率层面的“如果”推演,不等于实盘会复现。

◍ 把真实成交落盘到文件供回放

在 MT5 的 \MQL5\Experts\ 下建 TradingByHistoryDeals 文件夹,放一个同名 EA 和 SymbolTrade.mqh 包含文件。EA 输入项留三个:品种名(默认空串)、魔术码(默认 -1)、日志开关。都取默认时不对历史做筛选,整个账户成交全量写入;填了品种或魔术码就只落对应子集,多 EA 同跑也不会串。 落盘逻辑很直白:遍历历史成交→逐笔读属性写进结构→塞进结构数组→循环结束把数组 FileWriteArray 一次性写文件。注意 MQL5 只能把简单类型结构写文件,所以原结构里的 string 字段要换成 ushort 数组,并配专门的编解码方法。 除了写文件,EA 在 OnInit 里还会弹一条提示,按首次余额成交日期反推测试器该设的初始余额、杠杆和起始时间。实测这样回测的最终曲线最接近真实账户表现,偏差主要来源于点差和延迟,外汇与贵金属品种本身波动剧烈,回放结果仅作概率参考。 SymbolTrade.mqh 里另写了成交类和交易品种类。成交类多了小数位、点数和虚拟 Compare() 方法;品种类用 CTrade 对象按品种隔离,列表按毫秒时间排序却用秒来搜成交——因为一秒内常有多笔毫秒级成交,纯秒排序会漏,用毫秒排序+秒检索能把同秒多笔都兜住。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                    TradingByHistoryDeals.mq5 |
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."

「把历史成交落盘成二进制文件」

做复盘类 EA 时,第一道坎不是策略,而是怎么把策略测试器里的成交记录原样留下来。下面这段代码用结构体 + 文件宏,把每笔 deal 的关键字段序列化进本地 bin 文件,后续可离线重放。 代码里定义了三个宏:DIRECTORY 为 "TradingByHistoryDeals",FILE_NAME 为 "HistoryDealsData.bin",PATH 则是两者用反斜杠拼出的相对路径,文件默认落在 MT5 的 Files 子目录下。 SDeal 结构体是落盘的最小单元,包含 ticket(成交票号)、order(来源订单号)、pos_id(持仓 ID)、time_msc(毫秒级时间戳)和 time(秒级时间)。这 5 个字段足够还原成交顺序与持仓归属,开 MT5 按 F4 把这段塞进 include 头文件即可验证编译。 外汇与贵金属杠杆高,历史数据回放仅用于方法验证,实盘信号概率仍受滑点与点差扰动。

MQL5 / C++
class="macro">#class="kw">property link        "[MQL5官方文档]
class="macro">#class="kw">property version   "class="num">1.00"
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Expert                                                            |
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//--- input parameters
input    class="type">class="kw">string   InpTestedSymbol   =  "";       class=class="str">"cmt">/* The symbol being tested in the tester       */ 
input    class="type">long     InpTestedMagic     =  -class="num">1;       class=class="str">"cmt">/* The magic number being tested in the tester */ 
input  class="type">bool     InpShowDataInLog   =  false;   class=class="str">"cmt">/* Show collected data in the log              */ 
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                            SymbolTrade.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="macro">#define   DIRECTORY  "TradingByHistoryDeals"
class="macro">#define   FILE_NAME  "HistoryDealsData.bin"
class="macro">#define   PATH       DIRECTORY+"\\"+FILE_NAME
class="macro">#include <Arrays\ArrayObj.mqh>
class="macro">#include <Trade\Trade.mqh>
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|  Deal structure. Used to create a deal history file              |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="kw">struct SDeal
  {
   class="type">ulong                ticket;             class=class="str">"cmt">// Deal ticket
   class="type">long                order;              class=class="str">"cmt">// Order the deal is based on
   class="type">long                pos_id;             class=class="str">"cmt">// Position ID
   class="type">long                time_msc;           class=class="str">"cmt">// Time in milliseconds
   class="type">class="kw">datetime            time;               class=class="str">"cmt">// Time
  };

成交结构体里的字符串字段怎么塞进MT5

在 MQL5 的成交(deal)结构体定义里,symbol、comment、external_id 这三样没有用 string,而是用 ushort 数组承载,长度分别锁死在 16、64、256。这是 MT5 内核为了跨模块内存对齐做的取舍,直接赋值 string 会编译报错。 写入时靠 StringToShortArray 把 UTF-16 字符拆进数组,函数返回值等于原字符串长度才说明装下了。例如 SetSymbol 里写 ::StringToShortArray(deal_symbol, symbol)==deal_symbol.Length(),若符号名超过 16 个 ushort 就会返回 false,调用方得自己判错。 开 MT5 新建一个 struct 试这三行 Set 方法,用 "EURUSD" 和一段 70 字 comment 分别喂进去,后者必然写不满——这能帮你确认comment缓冲区溢出时静默截断的实际边界。外汇与贵金属杠杆品种波动剧烈,跑这类底层结构测试也需在模拟盘环境,实盘误用可能丢单。

MQL5 / C++
  class="type">class="kw">ushort            symbol[class="num">16];           class=class="str">"cmt">// Symbol
  class="type">class="kw">ushort            comment[class="num">64];          class=class="str">"cmt">// Deal comment
  class="type">class="kw">ushort            external_id[class="num">256];     class=class="str">"cmt">// Deal ID in an external trading system(on the exchange)

class=class="str">"cmt">//--- Set class="type">class="kw">string properties
  class="type">bool              SetSymbol(class="kw">const class="type">class="kw">string deal_symbol)     { class="kw">return(::StringToShortArray(deal_symbol, symbol)==deal_symbol.Length()); }
  class="type">bool              SetComment(class="kw">const class="type">class="kw">string deal_comment)   { class="kw">return(::StringToShortArray(deal_comment, comment)==deal_comment.Length()); }
  class="type">bool              SetExternalID(class="kw">const class="type">class="kw">string deal_external_id) { class="kw">return(::StringToShortArray(deal_external_id, external_id)==deal_external_id.Length()); }

◍ 成交对象的字符串属性与排序枚举

在封装成交(Deal)结构时,symbol、comment、external_id 这类字段常以 short 数组存放,对外暴露时需转成 string。下面三个方法直接调用平台内置的 ShortArrayToString 完成转换,返回当前成交的品种名、注释与外部 ID,调用方无需关心底层数组长度。 //--- Return string properties string Symbol(void) { return(::ShortArrayToString(symbol)); } string Comment(void) { return(::ShortArrayToString(comment)); } string ExternalID(void) { return(::ShortArrayToString(external_id)); } //--- Return string properties 是注释,提示接下来都是返回字符串属性的方法。 string Symbol(void) 定义 Symbol 方法,无参,调用 ::ShortArrayToString(symbol) 把品种短数组转字符串并返回。 string Comment(void) 定义 Comment 方法,无参,将 comment 短数组转成可读注释字符串。 string ExternalID(void) 定义 ExternalID 方法,无参,把 external_id 短数组转为外部标识字符串。 真正拉取历史成交做统计分析时,往往要先决定按什么维度排序。ENUM_DEAL_SORT_MODE 枚举给出了 15 种比较模式,从 SORT_MODE_DEAL_TICKET(按成交票号)到 SORT_MODE_DEAL_SL(按止损位),覆盖了票号、关联订单、时间、毫秒时间、类型、方向、魔术码、来源、持仓 ID、成交量、价格、佣金、库存费、盈亏、手续费与止损。 实盘回测里若想算某魔术码下每笔盈亏分布,选 SORT_MODE_DEAL_MAGIC 再套 SORT_MODE_DEAL_PROFIT 二次排,就能在 MT5 里快速倒出分组数据;外汇与贵金属杠杆品种跳空频繁,排序后仍需人工核对异常滑点。

MQL5 / C++
class=class="str">"cmt">//--- Return class="type">class="kw">string properties
  class="type">class="kw">string        Symbol(class="type">void)        { class="kw">return(::ShortArrayToString(symbol)); }
  class="type">class="kw">string        Comment(class="type">void)       { class="kw">return(::ShortArrayToString(comment)); }
  class="type">class="kw">string        ExternalID(class="type">void)    { class="kw">return(::ShortArrayToString(external_id)); }
  };
class=class="str">"cmt">//--- Deal sorting types
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
   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

常见问题

用脚本把真实成交按时间顺序写入文件,再在回放工具里逐笔对照进出场位置,比直接看账户历史更直观。
按成交时间戳排序后写入,回放时严格按文件顺序读取,避免按其他字段重排导致顺序错乱。
小布可自动读取你的成交记录并生成可视化复盘视图,标出可疑进出场点,省去手动落盘和比对。
字符串长度不固定,写入时需先记长度再写内容,读取时按长度截取,否则容易错位丢数据。
多是字符串字段编码或排序枚举不匹配,检查写入时用的属性顺序和读取端定义是否完全一致。