MQL5 细则手册:保存基于指定标准的EA 交易的优化结果·综合运用
「把优化结果写进文件前的条件闸门」
EA 在把每轮优化参数落盘前,先要判断根目录与自身子目录是否都存在。若 root_folder_exists && expert_folder_exists 为真,才返回 root_folder+EXPERT_NAME 作为结果文件路径;否则打印错误并 return(""),后续写文件逻辑直接拿不到句柄。
写盘动作集中在 WriteOptimizationResults():先用 CriterionSelectionRule 决定走 OR 还是 AND 逻辑——RULE_OR 调 AccessCriterionOR(),RULE_AND 调 AccessCriterionAND()。只有 condition 为真且 OptimizationFileHandle!=INVALID_HANDLE 时,才通过 GetStringsCount() 拿到当前行数并把 IntegerToString(strings_count) 与待写串一起 FileWrite 出去。
AND 模式下的 AccessCriterionAND() 用 for 循环扫 criteria 数组,遇到 C_NO_CRITERION 就 continue 跳过;以 C_STAT_PROFIT 为例,当 stat_values[0] > criteria_values[i] 时 count++。只有全部启用条件都满足,count 才会等于有效条件数,闸门才放开。外汇与贵金属品种波动大、滑点不确定,这套过滤仅降低无效参数入库概率,不保证实盘收益。
Print("The Expert Advisor folder ..\Files\OPTIMIZATION_DATA\ has been created"+expert_folder); } else { Print("Error when creating the Expert Advisor folder ..\Files\"+expert_folder+"\: ", ErrorDescription(GetLastError())); class="kw">return(""); } } class=class="str">"cmt">//--- If the necessary folders exist if(root_folder_exists && expert_folder_exists) { class=class="str">"cmt">//--- Return the location for creating the file of optimization results class="kw">return(root_folder+EXPERT_NAME); } class=class="str">"cmt">//--- class="kw">return(""); } class=class="str">"cmt">//+--------------------------------------------------------------------+ class=class="str">"cmt">//| Writing the results of the optimization by criteria | class=class="str">"cmt">//+--------------------------------------------------------------------+ class="type">void WriteOptimizationResults(class="type">class="kw">string string_to_write) { class="type">bool condition=class="kw">false; class=class="str">"cmt">// To check the condition class=class="str">"cmt">//--- If at least one criterion is satisfied if(CriterionSelectionRule==RULE_OR) condition=AccessCriterionOR(); class=class="str">"cmt">//--- If all criteria are satisfied if(CriterionSelectionRule==RULE_AND) condition=AccessCriterionAND(); class=class="str">"cmt">//--- If the conditions for criteria are satisfied if(condition) { class=class="str">"cmt">//--- If the file of optimization results is opened if(OptimizationFileHandle!=INVALID_HANDLE) { class="type">int strings_count=class="num">0; class=class="str">"cmt">// String counter class=class="str">"cmt">//--- Get the number of strings in the file and move the pointer to the end strings_count=GetStringsCount(); class=class="str">"cmt">//--- Write the class="type">class="kw">string with criteria FileWrite(OptimizationFileHandle,IntegerToString(strings_count),string_to_write); } else Print("Invalid optimization file handle!"); } } class=class="str">"cmt">//+--------------------------------------------------------------------+ class=class="str">"cmt">//| Checking multiple conditions for writing to the file | class=class="str">"cmt">//+--------------------------------------------------------------------+ class="type">bool AccessCriterionAND() { class="type">int count=class="num">0; class=class="str">"cmt">// Criterion counter class=class="str">"cmt">//--- Iterate over the array of criteria in a loop and see class=class="str">"cmt">// if all the conditions for writing parameters to the file are met for(class="type">int i=class="num">0; i<ArraySize(criteria); i++) { class=class="str">"cmt">//--- Move to the next iteration, if the criterion is not determined if(criteria[i]==C_NO_CRITERION) class="kw">continue; class=class="str">"cmt">//--- PROFIT if(criteria[i]==C_STAT_PROFIT) { if(stat_values[class="num">0]>criteria_values[i]) { count++; } } } }
◍ 多指标联合通过的判定写法
这段逻辑在跑一组交易统计筛选:只有当被启用的所有条件都命中,函数才返回 true。stat_values 数组按固定下标存了不同指标,下标 1 是总成交数、2 是盈利因子、3 是期望收益、4 是权益回撤百分比、5 是恢复因子、6 是夏普比率。 注意回撤那一项是反向判断——stat_values[4] 小于阈值才算通过,其余五项都是大于阈值。每满足一个 criteria[i] 对应的判断,count 就加一,一旦 count 等于 UsedCriteriaCount 立刻 return(true),不再往后比。 如果你在 MT5 里想加一条新标准(比如最大连亏次数),得先在枚举里补一个 C_STAT_ 常量,再在 stat_values 里占一个下标,并把 UsedCriteriaCount 同步调大,否则新条件永远不参与联合判定。外汇与贵金属品种波动剧烈,这类统计筛选只能提高样本质量,不保证后续实盘概率优势。
if(count==UsedCriteriaCount) class="kw">return(true); } } class=class="str">"cmt">//--- TOTAL DEALS if(criteria[i]==C_STAT_DEALS) { if(stat_values[class="num">1]>criteria_values[i]) { count++; if(count==UsedCriteriaCount) class="kw">return(true); } } class=class="str">"cmt">//--- PROFIT FACTOR if(criteria[i]==C_STAT_PROFIT_FACTOR) { if(stat_values[class="num">2]>criteria_values[i]) { count++; if(count==UsedCriteriaCount) class="kw">return(true); } } class=class="str">"cmt">//--- EXPECTED PAYOFF if(criteria[i]==C_STAT_EXPECTED_PAYOFF) { if(stat_values[class="num">3]>criteria_values[i]) { count++; if(count==UsedCriteriaCount) class="kw">return(true); } } class=class="str">"cmt">//--- EQUITY DD REL PERC if(criteria[i]==C_STAT_EQUITY_DDREL_PERCENT) { if(stat_values[class="num">4]<criteria_values[i]) { count++; if(count==UsedCriteriaCount) class="kw">return(true); } } class=class="str">"cmt">//--- RECOVERY FACTOR if(criteria[i]==C_STAT_RECOVERY_FACTOR) { if(stat_values[class="num">5]>criteria_values[i]) { count++; if(count==UsedCriteriaCount) class="kw">return(true); } } class=class="str">"cmt">//--- SHARPE RATIO if(criteria[i]==C_STAT_SHARPE_RATIO) { if(stat_values[class="num">6]>criteria_values[i]) { count++; if(count==UsedCriteriaCount) class="kw">return(true); } } } class=class="str">"cmt">//--- Conditions for writing are not met
用或逻辑筛选回测落盘条件
在 MT5 EA 里做参数寻优时,经常需要把满足条件的回测结果写进文件。AccessCriterionOR 这个函数走的是「或」逻辑:只要数组里任意一条已启用的统计指标越过阈值,就返回 true 允许写盘,否则返回 false。 代码里 criteria 数组存放指标类型,criteria_values 存对应阈值,stat_values 则是本次回测实际算出的 7 个统计量。索引 0~6 依次对应:净利润、总成交数、盈利因子、期望收益、权益回撤百分比、恢复因子、夏普比率。注意权益回撤那一项用的是小于号(stat_values[4]<criteria_values[i]),其余都是大于号——回撤当然是越低越好。 C_NO_CRITERION 的分支直接 continue,说明该槽位未启用,不参加判定。这种结构让你在 EA 输入参数里勾选多个过滤条件时,不必全部满足也能落盘,适合粗筛候选参数集。外汇与贵金属品种波动大,回测达标不代表实盘概率同向,务必上 MT5 用历史数据自验。
class="kw">return(class="kw">false); } class=class="str">"cmt">//+--------------------------------------------------------------------+ class=class="str">"cmt">//| Checking for meeting one of the conditions for writing to the file | class=class="str">"cmt">//+--------------------------------------------------------------------+ class="type">bool AccessCriterionOR() { class=class="str">"cmt">//--- Iterate over the array of criteria in a loop and see class=class="str">"cmt">// if all the conditions for writing parameters to the file are met for(class="type">int i=class="num">0; i<ArraySize(criteria); i++) { class=class="str">"cmt">//--- if(criteria[i]==C_NO_CRITERION) class="kw">continue; class=class="str">"cmt">//--- PROFIT if(criteria[i]==C_STAT_PROFIT) { if(stat_values[class="num">0]>criteria_values[i]) class="kw">return(true); } class=class="str">"cmt">//--- TOTAL DEALS if(criteria[i]==C_STAT_DEALS) { if(stat_values[class="num">1]>criteria_values[i]) class="kw">return(true); } class=class="str">"cmt">//--- PROFIT FACTOR if(criteria[i]==C_STAT_PROFIT_FACTOR) { if(stat_values[class="num">2]>criteria_values[i]) class="kw">return(true); } class=class="str">"cmt">//--- EXPECTED PAYOFF if(criteria[i]==C_STAT_EXPECTED_PAYOFF) { if(stat_values[class="num">3]>criteria_values[i]) class="kw">return(true); } class=class="str">"cmt">//--- EQUITY DD REL PERC if(criteria[i]==C_STAT_EQUITY_DDREL_PERCENT) { if(stat_values[class="num">4]<criteria_values[i]) class="kw">return(true); } class=class="str">"cmt">//--- RECOVERY FACTOR if(criteria[i]==C_STAT_RECOVERY_FACTOR) { if(stat_values[class="num">5]>criteria_values[i]) class="kw">return(true); } class=class="str">"cmt">//--- SHARPE RATIO if(criteria[i]==C_STAT_SHARPE_RATIO) { if(stat_values[class="num">6]>criteria_values[i]) class="kw">return(true); } } class=class="str">"cmt">//--- Conditions for writing are not met class="kw">return(class="kw">false); } class=class="str">"cmt">//+--------------------------------------------------------------------+ class=class="str">"cmt">//| Counting the number of strings in the file | class=class="str">"cmt">//+--------------------------------------------------------------------+ class="type">int GetStringsCount() { class="type">int strings_count =class="num">0; class=class="str">"cmt">// String counter
「逐行扫文件指针数清优化记录行数」
做批量优化结果后处理时,经常要先搞清楚优化文件里到底落了多少条参数组合。下面这段逻辑不依赖外部计数,纯靠文件指针位移自己数出来,MT5 里直接拖去测就能看到 strings_count 随文件增长。 核心思路是先 FileSeek 把指针甩到 SEEK_SET 0 位,再用 FileIsEnding 当外层循环条件,内层靠 FileIsLineEnding 判断一行是否读完。每读一段用 FileTell 抓当前偏移存进 offset,遇行尾且未到文件尾就 offset++ 并重新 FileSeek 定位,strings_count 自增。 外层循环每次底部再判一次 FileIsEnding,真到尾就 break;最后 FileSeek 到 SEEK_END 方便后续追加写,返回统计出的行数。注意外汇与贵金属市场高风险,这类文件解析脚本仅用于本地分析,不代表任何交易信号。
class="type">class="kw">ulong offset =class="num">0; class=class="str">"cmt">// Offset for determining the position of the file pointer class=class="str">"cmt">//--- Move the file pointer to the beginning FileSeek(OptimizationFileHandle,class="num">0,SEEK_SET); class=class="str">"cmt">//--- Read until the current position of the file pointer reaches the end of the file class="kw">while(!FileIsEnding(OptimizationFileHandle) || !IsStopped()) { class=class="str">"cmt">//--- Read the whole class="type">class="kw">string class="kw">while(!FileIsLineEnding(OptimizationFileHandle) || !IsStopped()) { class=class="str">"cmt">//--- Read the class="type">class="kw">string FileReadString(OptimizationFileHandle); class=class="str">"cmt">//--- Get the position of the pointer offset=FileTell(OptimizationFileHandle); class=class="str">"cmt">//--- If it&class="macro">#x27;s the end of the class="type">class="kw">string if(FileIsLineEnding(OptimizationFileHandle)) { class=class="str">"cmt">//--- Move to the next class="type">class="kw">string class=class="str">"cmt">// if it&class="macro">#x27;s not the end of the file, increase the pointer counter if(!FileIsEnding(OptimizationFileHandle)) offset++; class=class="str">"cmt">//--- Move the pointer FileSeek(OptimizationFileHandle,offset,SEEK_SET); class=class="str">"cmt">//--- Increase the class="type">class="kw">string counter strings_count++; class="kw">break; } } class=class="str">"cmt">//--- If it&class="macro">#x27;s the end of the file, exit the loop if(FileIsEnding(OptimizationFileHandle)) class="kw">break; } class=class="str">"cmt">//--- Move the pointer to the end of the file for writing FileSeek(OptimizationFileHandle,class="num">0,SEEK_END); class=class="str">"cmt">//--- Return the number of strings class="kw">return(strings_count); }
◍ 随包代码先跑通再谈优化
这套可视化优化结果的分析思路就先聊到这儿,真正要上手还是得把随文附的 EA 包(writeoptimizationresults.zip,约 21 KB)下到本地,在 MT5 里先编译跑通。有读者反馈新版本 MQL5 语法下编译会报一堆错,优化时也不出报告,说明环境差异得自己修一遍才能验证。 外汇和贵金属品种做参数优化回测波动极大,历史拟合亮眼不代表实盘能复现,任何优化结论都只是提高概率,杠杆风险始终在那里。 把代码跑起来、把报告字段看明白,比盯着别人贴的曲线更有用;后面我们还会就优化结果分析接着拆。