MQL5 细则手册:保存基于指定标准的EA 交易的优化结果·综合运用
📘

MQL5 细则手册:保存基于指定标准的EA 交易的优化结果·综合运用

第 3/3 篇

「把优化结果写进文件前的条件闸门」

EA 在把每轮优化参数落盘前,先要判断根目录与自身子目录是否都存在。若 root_folder_exists && expert_folder_exists 为真,才返回 root_folder+EXPERT_NAME 作为结果文件路径;否则打印错误并 return(""),后续写文件逻辑直接拿不到句柄。 写盘动作集中在 WriteOptimizationResults():先用 CriterionSelectionRule 决定走 OR 还是 AND 逻辑——RULE_ORAccessCriterionOR()RULE_ANDAccessCriterionAND()。只有 condition 为真且 OptimizationFileHandle!=INVALID_HANDLE 时,才通过 GetStringsCount() 拿到当前行数并把 IntegerToString(strings_count) 与待写串一起 FileWrite 出去。 AND 模式下的 AccessCriterionAND()for 循环扫 criteria 数组,遇到 C_NO_CRITERIONcontinue 跳过;以 C_STAT_PROFIT 为例,当 stat_values[0] > criteria_values[i]count++。只有全部启用条件都满足,count 才会等于有效条件数,闸门才放开。外汇与贵金属品种波动大、滑点不确定,这套过滤仅降低无效参数入库概率,不保证实盘收益。

MQL5 / C++
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 同步调大,否则新条件永远不参与联合判定。外汇与贵金属品种波动剧烈,这类统计筛选只能提高样本质量,不保证后续实盘概率优势。

MQL5 / C++
        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 用历史数据自验。

MQL5 / C++
  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 方便后续追加写,返回统计出的行数。注意外汇与贵金属市场高风险,这类文件解析脚本仅用于本地分析,不代表任何交易信号。

MQL5 / C++
  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 语法下编译会报一堆错,优化时也不出报告,说明环境差异得自己修一遍才能验证。 外汇和贵金属品种做参数优化回测波动极大,历史拟合亮眼不代表实盘能复现,任何优化结论都只是提高概率,杠杆风险始终在那里。 把代码跑起来、把报告字段看明白,比盯着别人贴的曲线更有用;后面我们还会就优化结果分析接着拆。

常见问题

在 OnTester 或优化结束回调里加条件闸门,只有多指标联合通过才写文件,其余直接丢弃。
用 if(condA || condB) 包住文件写入语句,任一条件为真即执行落盘,注意括号别漏。
小布可接入你的 EA 输出,按你设定的阈值自动标红淘汰项,只把联合通过的组推到看板。
用文件指针从头逐行读,遇到换行累加计数,跑完返回行数即可,不必全载内存。
先原样编译跑通确认能写文件,再改条件闸门,避免边改逻辑边排环境错。