为智能系统制定品质因数·综合运用
📊

为智能系统制定品质因数·综合运用

(3/3)· 从随机模型到双品质因数返回,补齐策略测试器只给一个数值的能力缺口

实战向进阶 第 3/3 篇

多数交易者在策略测试器里只盯着 OnTester result 一个零值发呆,却没意识到那是因为 EA 根本没写返回逻辑。把系统品质压缩成单一数字,等于主动放弃了第二把尺子。

按月切分交易历史算 SQN 均值

这段逻辑干的事很直接:把 MT5 账户全部已平仓交易按自然月切块,逐月算 SQN 再取简单平均。外汇与贵金属杠杆高、滑点跳空频繁,历史统计只能反映过去样本,对未来的指向是概率性的,不能直接当仓位依据。 代码先用 HistorySelect(0, TimeCurrent()) 拉全量成交历史,失败直接返回 0.0;HistoryDealsTotal() 拿到总单数。循环里用 HistoryDealGetTicket 取每笔 ticket,再读 DEAL_TIME 与 DEAL_PROFIT,利润为 0 的单子跳过不计入。 eh_um_novo_mes 检测到跨月时,若上月有交易就调用 sqn() 把该月 SQN 累加到 sqn_acumulado,同时 sqn_n 加 1,并重置累计利润与笔数。循环结束后再补一次月末残留计算,避免最后一个月漏掉。 最终 return NormalizeDouble(sqn_acumulado / sqn_n, 2) 输出两位小数月度 SQN 均值。SQN_TESTER_ON_TESTER 里则把 sqn_mes() 与 CPCIndex() 相乘保留 5 位,并在日志打印 rr_medio、CPCIndex、sqn_mes 三项,方便你开 MT5 策略测试器跑完直接核对数字。

MQL5 / C++
  class="type">class="kw">double lucro_acumulado = class="num">0.0;
  class="type">class="kw">double quantidade_negocios = class="num">0.0;
  class="type">int sqn_n = class="num">0;
  class="type">int mes = -class="num">1;
  class="type">uint primeiro_negocio = class="num">0;
  class="type">uint total_negocios;
class=class="str">"cmt">//--- request the history of trades
  if(HistorySelect(class="num">0,TimeCurrent()) == class="kw">false)
    class="kw">return class="num">0.0;
  total_negocios = HistoryDealsTotal();
class=class="str">"cmt">//--- the average for each month is calculated for each trade
  for(class="type">uint i=primeiro_negocio; i < total_negocios; i++)
    {
      class="type">class="kw">ulong    ticket=class="num">0;
      class=class="str">"cmt">//--- Select the required ticket to pick up data
      if((ticket=HistoryDealGetTicket(i))>class="num">0)
        {
         class="type">class="kw">datetime time = (class="type">class="kw">datetime)HistoryDealGetInteger(ticket, DEAL_TIME);
         class="type">class="kw">double   lucro = HistoryDealGetDouble(ticket,DEAL_PROFIT);
         if(lucro == class="num">0)
           {
            class=class="str">"cmt">//--- If there is no result, move on to the next trade.
            class="kw">continue;
           }
         if(eh_um_novo_mes(time, mes))
           {
            class=class="str">"cmt">//--- If we have trades, then we calculate sqn, otherwise it will be equal to zero...
            if(quantidade_negocios>class="num">0)
              {
               sqn_acumulado += sqn(primeiro_negocio, i, lucro_acumulado,
                                    quantidade_negocios);
              }
            class=class="str">"cmt">//--- The calculated amount sqns is always updated!
            sqn_n++;
            primeiro_negocio=i;
            lucro_acumulado = class="num">0.0;
            quantidade_negocios = class="num">0;
           }
         lucro_acumulado += lucro;
         quantidade_negocios++;
        }
    }
class=class="str">"cmt">//--- when exiting "for", we can have undesired result
  if(quantidade_negocios>class="num">0)
    {
     sqn_acumulado += sqn(primeiro_negocio, total_negocios,
                           lucro_acumulado, quantidade_negocios);
     sqn_n++;
    }
class=class="str">"cmt">//--- take the simple average of sqns
  class="kw">return NormalizeDouble(sqn_acumulado / sqn_n, class="num">2);
  }
class="type">class="kw">double SQN_TESTER_ON_TESTER()
  {
   PrintFormat("%G,%G,%G", rr_medio(), CPCIndex(), sqn_mes());
   class="kw">return NormalizeDouble(sqn_mes() * CPCIndex(), class="num">5);
  }

◍ 用宏切换绕开 OnTester 重定义

EA 主文件和被 include 的 ARTICLE_METRICS.mq5 里若各写一个 OnTester,编译器会直接报函数重复。常见做法是注释掉一大片代码,但在 MT5 里可以用预处理宏做轻量切换。 上面的示例里,先在 EA 中定义 OnTester 返回 __LINE__,随后 #include 指标/库文件。紧跟着用 #define OnTester disable 把名字改写,再写第二个 OnTester 并通过 #undef 还原宏,第二次 include 时文件内的 OnTester 已被重命名为 disable,主文件里的仍叫 OnTester,重名冲突消失。 实测这种写法不用动 include 文件内容,只改 EA 里的两行宏就能在「用主函数」和「用库函数」之间切。代价是宏侵入性较强,团队接手时容易看懵:明明写的是 OnTester,实际编译进去的可能是 disable。外汇与贵金属 EA 回测高风险,任何函数切换都建议在策略测试器里跑一遍确认入口没接错。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">class="kw">double OnTester()
  {
   class="kw">return __LINE__;
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#include "ARTICLE_METRICS.mq5"
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#define OnTester disable
class=class="str">"cmt">//class="macro">#define SQN_TESTER_ON_TESTER disable
class="type">class="kw">double OnTester()
  {
   class="kw">return __LINE__;
  }
class="macro">#undef OnTester
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#include "ARTICLE_METRICS.mq5"

「把工具请下神坛」

前面两节把随机操作的智能系统模型拆开,用 Van Tharp 和 Sunny Harris 两套公式算了品质因数,又引入了新的“风险-回报”关系因数。代码层面靠 includes 机制切换不同衡量函数,文后 zip 里附了 ARTICLE_METRICS.mq5(10.33 KB)和 ARTICLE_MT5.mq5(11.87 KB),直接拖进 MT5 就能改参数跑。 这些衡量度只是给策略打分的尺子,不是圣杯。外汇和贵金属杠杆高、跳空频繁,同一套因数在样本内漂亮,换周期可能就塌了。 真要验证,就把 zip 里的 mq5 编译后接上自己的历史数据,看“风险-回报”因数在实盘滑点下掉多少。工具用熟了,决策还得多留一分怀疑。

把双因数记录交给小布盯盘
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到回测品质的辅助视图,你专注决策而非抄数值。

常见问题

若 EA 未在 OnTester 中显式返回数值,测试器会取默认零值;需自行计算并 return 品质得分才会变化。
该模型仅用于演示品质因数构建,外汇贵金属属高风险,随机开仓实盘亏损概率倾向极高,不可直接用于真实账户。
按定义周期的新烛条触发即可,避免每笔成交跑大函数;休市期也能防止测试器因空交易卡暂停。
可以,小布盯盘的 AIGC 模块支持把回测返回的两个数值做横向视图,省去手动记录多版本差异的重复劳动。
可用全局变量或文件写出第二值,测试后读取;或把两值编码进一个 double 再拆开,具体见本篇代码小节。