在 MetaTrader 5 中交易的可视评估和调整(基础篇)
「把 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 对象按品种隔离,列表按毫秒时间排序却用秒来搜成交——因为一秒内常有多笔毫秒级成交,纯秒排序会漏,用毫秒排序+秒检索能把同秒多笔都兜住。
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 头文件即可验证编译。 外汇与贵金属杠杆高,历史数据回放仅用于方法验证,实盘信号概率仍受滑点与点差扰动。
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缓冲区溢出时静默截断的实际边界。外汇与贵金属杠杆品种波动剧烈,跑这类底层结构测试也需在模拟盘环境,实盘误用可能丢单。
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 里快速倒出分组数据;外汇与贵金属杠杆品种跳空频繁,排序后仍需人工核对异常滑点。
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