交易员的正则表达式·进阶篇
(2/3)· 当成交记录和 EA 回测表堆成乱麻,几个元字符就能替你筛出想要的那一行
◍ 用字典与枚举器搭一个可遍历容器
在 MT5 的 MQL5 里,标准模板库(STL)的 Dictionary 和 List 能直接拿来管键值对与顺序集合。上面这段演示了先建一个以字符串为键、整型为值的字典,并指定用 StringEqualityComparer 做键比较,避免默认比较器在中文或特殊字符上出问题。 紧接着新建一个 List<int>,依次压入 0、1、2 三个元素,再通过 GetEnumerator() 拿枚举器。用 while(en.MoveNext()) 逐位前进,Print(en.Current()) 把当前值打到专家日志,输出顺序就是 0、1、2——这是一个可验证的确定现象。 注意枚举器和 List 本身都是堆上 new 出来的指针,跑完必须手动 delete en 和 delete list,否则 MT5 终端会报内存泄漏。外汇与贵金属 EA 跑长周期时,这种泄漏可能拖慢盯盘终端,属高风险运维点。 下面附上可直接贴进 MT5 脚本区验证的代码,改一下 list.Add 里的数字就能看不同遍历结果。
Dictionary<class="type">class="kw">string,class="type">int> *dictionary= new Dictionary<class="type">class="kw">string,class="type">int>(new StringEqualityComparer); List<class="type">int>* list = new List<class="type">int>(); list.Add(class="num">0); list.Add(class="num">1); list.Add(class="num">2); IEnumerator<class="type">int> *en = list.GetEnumerator(); while(en.MoveNext()) { Print(en.Current()); } class="kw">delete en; class="kw">delete list; class="macro">#include <RegularExpressions\Regex.mqh>
用正则从 HTML 历史里抠出订单与成交
要把 MT5 导出的 HTML 交易报告变成可筛选的数据,核心是先按标签拆文本。实操时进终端「工具箱 → 历史」,右键导出一个 HTML(IE 格式)报告,存到 \MetaTrader 5\MQL5\Files 沙箱,EA 的 file_name 参数填这个文件名即可跑。 TradeHistory 构造器只用一个正则范式 "(>)([^<>]*)(<)" 扫描整份 HTML:它抓出所有以 '>' 开头、'<' 结尾、中间不含尖括号的子串,也就是标签之间的纯文本。匹配数若等于 23,判定为订单表一行;等于 27 则是成交表一行;其余直接丢弃。 拆出的偶数位匹配去掉首尾括号后写进数组,订单填 11 字段、成交填 13 字段,分别进 List<OrderRecord*> 和 List<DealRecord*>。每读一行都要 delete matches,读完关文件、清正则缓存,否则内存会慢慢漏。 过滤不想自己写循环也行,List<T> 的 FindAll(Predicate match) 直接返回子表。关键是 match 函数怎么知道按哪一列、比什么值——下面代码里用两个静态变量 s_index、s_value 承接 SetIndex/SetValue 的设置,再在 FindRecord 里拿 record.m_data[s_index] == s_value 做判断,越界就 Print 报错并返回 false。 过滤完的表可绘到 EA 界面,也能存 Result.csv。注意:保存时文件名写死会覆盖,要留多列就得手动改名逐次存,不然前一次白筛。外汇和贵金属交易历史波动大、滑点复杂,回测结论仅代表历史样本,实盘复制需自担高风险。
TradeHistory(const class="type">class="kw">string path) { m_file_name=path; m_handel= FileOpen(path,FILE_READ|FILE_TXT); m_list1 = new List<OrderRecord*>(); m_list2 = new List<DealRecord*>(); Regex *rgx=new Regex("(>)([^<>]*)(<)"); while(!FileIsEnding(m_handel)) { class="type">class="kw">string str=FileReadString(m_handel); MatchCollection *matches=rgx.Matches(str); if(matches.Count()==class="num">23) { class="type">class="kw">string in[class="num">11]; for(class="type">int i=class="num">0,j=class="num">1; i<class="num">11; i++,j+=class="num">2) { in[i]=StringSubstr(matches[j].Value(),class="num">1,StringLen(matches[j].Value())-class="num">2); } m_list1.Add(new OrderRecord(in)); } else if(matches.Count()==class="num">27) { class="type">class="kw">string in[class="num">13]; for(class="type">int i=class="num">0,j=class="num">1; i<class="num">13; i++,j+=class="num">2) { in[i]=StringSubstr(matches[j].Value(),class="num">1,StringLen(matches[j].Value())-class="num">2); } m_list2.Add(new DealRecord(in)); } class="kw">delete matches; } FileClose(m_handel); class="kw">delete rgx; Regex::ClearCache(); } in[i]=StringSubstr(matches[j].Value(),class="num">1,StringLen(matches[j].Value())-class="num">2); typedef class="type">bool (*Predicate)(IComparable*); class="kw">static class="type">bool FindRecord(IComparable *value) { Record *record=class="kw">dynamic_cast<Record*>(value); if(s_index>=ArraySize(record.m_data) || s_index<class="num">0) { Print("Iindex 超出范围。"); class="kw">return(false); } class="kw">return (record.m_data[s_index] == s_value); }
「用 FindAll 按列筛出目标记录」
在 MQL5 里处理 Record 集合时,先给单条记录写入数值并绑定列索引,再拿 source_list 做条件过滤,是常见做法。 下面这段代码片段展示了具体调用链:先 SetValue 写入待匹配值,SetIndex 指定查哪一列,最后用 FindAll 配合 Record::FindRecord 回调,从原列表里捞出符合条件的 Record 指针集合。 [CODE] Record::SetValue(value); Record::SetIndex(columnIndex); List<Record*> *new_list = source_list.FindAll(Record::FindRecord); [/CODE] 逐行拆解:第一行把 value 写进当前 Record 实例,作为后续查找的参照值;第二行设定 columnIndex,决定 FindRecord 比对的是第几列;第三行对 source_list 执行 FindAll,返回的新链表 new_list 只含匹配项,原列表不被改动。 在 MT5 里跑这段前,先确认 Record::FindRecord 的匹配逻辑(等于 / 包含 / 区间),否则可能返回空链。外汇与贵金属数据高频跳动,用此方式筛 tick 级记录时需注意内存与耗时,属高风险环境下的工具性操作。
Record::SetValue(value); Record::SetIndex(columnIndex); List<Record*> *new_list = source_list.FindAll(Record::FindRecord);
◍ 用过滤器筛 EA 优化结果 XML
MetaTrader 5 导出的 EA 优化结果 .xml 文件,可以用一个带图形界面的工具直接读入。数据被拆成两张表:测试器结果表装回测统计,输入参数表存所有输入参数取值,后者最多只显示十个参数,超出的部分直接不渲染。 过滤逻辑很直接——选列名再给数值区间。比如对“通过”列限定 [0; 10]、对“盈利因子”列限定 [0.4; 0.5],界面就会只留下落在这两个区间里的跑次。 开 MT5 跑完优化后导出 xml,用这类过滤器先卡盈利因子 0.4~0.5 再卡通过次数,能快速排掉明显过拟合的批次。外汇与贵金属回测高风险,区间筛选只是降噪手段,不代表实盘概率。