【FX検証】仕掛けの優位性を評価するMQL4検証について

MQLプログラミング

前回までは仕掛けと手仕舞いの両方を同時に評価していました。

しかし、それでは仕掛けと手仕舞いのどちらに問題があったのかを知ることができず、潜在的な優位性を見逃すことになってしまいます。

そこで、仕掛けと手仕舞いの検証をバラバラに行うことで、より優位性を発見しやすくしたのが今回行った検証です。

仕掛け検証の概要

仕掛けの検証では、手仕舞いを以下のように設定することでおこないます。

  1. 固定pipsでの手仕舞い
  2. 固定本数での手仕舞い
  3. 完全にランダムな手仕舞い

以上の3パターンのいずれかを使って、仕掛けの優位性を検証していくわけですが、今回は2番の固定本数での手仕舞いを採用しました。

つまり、優位性のある仕掛けであれば、ローソク足数本以内に利益を出すものだろうというのがこの検証に込められたメッセージです。

たしかに、仕掛けてすぐに含み益を出すのであれば、仕掛けのタイミングとしては正しいと言え、仕掛けてもしばらく含み損か、とんとんの状態なら仕掛けが早すぎたか、誤っていると判断できます。

今回検証する仕掛け

今回検証する仕掛けは以下の通りです。

【ロング条件】
・X日間での最高値を30分足の終値で更新
・Y日単純移動平均線が上向き
【ショート条件】
・X日間で最安値を30分足の終値で更新
・Y日単純移動平均線が下向き

【決済戦略】
ローソク足10本後に決済(固定本数での決済)

仕掛け検証のコード


input int slippage = 10;
input int day_count = 40;
input int ma_period=50;
input ENUM_TIMEFRAMES timeframe = PERIOD_M30; // 時間足のパラメータを追加
extern int magicnumber = 123;


int time =300*60;//単位は秒 300分なら300×60 30分足10本分

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
 
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
   // バックテストかどうかチェック
   if(!IsTesting()){
      return;
   }
  
    double highest = iHigh(NULL, timeframe, iHighest(NULL, timeframe, MODE_HIGH, day_count, 1));
    double lowest = iLow(NULL, timeframe, iLowest(NULL, timeframe, MODE_LOW, day_count, 1));
    double close = iClose(NULL, timeframe, 0);
    double now_ma=iMA(NULL,timeframe,ma_period,0,MODE_SMA,PRICE_CLOSE,0);
    double before_ma=iMA(NULL,timeframe,ma_period,0,MODE_SMA,PRICE_CLOSE,1);
    
    int order = OrdersTotal();


    if (order>0)
    {
        DoExit();
        
    }
    
    if (order<1 && (close > highest) && (now_ma>before_ma)) // ロングの条件
    {
        printf("ロング");  
        
        OrderSend(Symbol(), OP_BUY, 1, Ask, slippage, 0, 0, NULL, magicnumber, 0, clrRed);
      
    }
    else if (order<1 && (close < lowest) && (now_ma<before_ma)) // ショートの条件
    {
        printf("ショート");
       
        OrderSend(Symbol(), OP_SELL, 1, Bid, slippage, 0, 0, NULL, magicnumber, 0, clrBlue);
        
    }
    

    Comment(day_count + 1, "期間の最高値は", highest, "|",
            day_count + 1, "期間の最安値は", lowest);
}
//+------------------------------------------------------------------+


//+------------------------------------------------------------------+
//|関数 イグジット処理                                                 |
//+------------------------------------------------------------------+
void DoExit(){
   
   for(int i=0; i < OrdersTotal(); i++){
      OrderSelect(i, SELECT_BY_POS, MODE_TRADES);
      if(OrderMagicNumber() != magicnumber || OrderSymbol() != Symbol()){
         continue;
      }
      if(TimeCurrent()>OrderOpenTime()+time)
      {
      
      
         if(OrderType() == OP_BUY){
            OrderClose(OrderTicket(), OrderLots(), Bid, 3, clrWhite);
            break;
         }
         if(OrderType() == OP_SELL){
            OrderClose(OrderTicket(), OrderLots(), Ask, 3, clrWhite);
            break;
         }
      }
   }
}

仕掛けの検証の流れ

  1. 2015.1-2016.12の最適化→パラメータの組み合わせの70%以上がプラス利益なら合格
    不合格の場合は、EAの設計自体をやり直す
  2. 2015-2016で最適化したEAを2017.1-2017.6でフォワードテスト
  3. モンキーEAを作って2017.1-2017.6を集計し、EAのフォワードテストの結果と比較
    モンキーEAより作成したEAが優れていたら合格、不合格の場合は設計からやり直し
  4. 2015.7-2017.6が最適化期間、2017.7-2017.12がアウト期間のウォークフォワードテスト→最適化期間でのパラメータの組み合わせで堅牢性テスト、不合格の場合設計やり直し
  5. これを2021-2022が最適化期間、2023.1-2023.6がアウト期間のウォークフォワードテストまで繰り返す
  6. ウォークフォワードテストでは、2017-2023.6までの13回分のフォワードテスト結果を得られる
    この中の70%-80%つまり、10回分で利益が出ていればウォークフォワードテスト合格

2015.1-2016.12の期間で最適化

まずは、2015年の期間で当該トレード手法を最適化していきます。

【ロング条件】
・X日間での最高値を30分足の終値で更新
・Y日単純移動平均線が上向き
【ショート条件】
・X日間で最安値を30分足の終値で更新
・Y日単純移動平均線が下向き

【決済戦略】
ローソク足10本後に決済(固定本数での決済)

最適化では、以上のXとYを対象としていきます。

最適化を行う前に決めておかなければいけないのは、最適化のスキャンレンジです。

Xのスキャンレンジは前回の検証と同じく1から47までとし、1ずつステップを刻んでいくこととします。

Yの移動平均線の期間に関するスキャンレンジですが、中期から長期の期間でなければ相場全体の傾きを見る意味がありません。

したがって、50から200くらいが妥当なスキャンレンジといえるのではないでしょうか。

また、中期から長期の移動平均線なので、ステップを1ずつ刻んでいっても仕方ないですよね。ただし、50期間移動平均線と75期間移動平均線ではかなり違ってくるイメージがあるので、スキャンステップは5でいいのではないでしょうか。

最適化の結果が以下の通りです。

最適化結果のほとんどで元本割れをしており、見るに堪えないゴミシステムといえます。

最適化結果の70%以上がプラス利益であれば次のステップに進むところですが、明らかに70%もないので、ここで終了です。

この場合、利益を上げているパラメータの組み合わせを利用してしまうと、「過剰最適化」と呼ばれる状態に陥ってしまいます。

開発者視点で考えるのであれば、この仕掛けのシステムは全く優位性がなく、あらたなシステムの根本から作り変える必要があります。