[検証]ランダムにトレードしたら成績はどうなる?EAでテストしてみた。

FXでランダムにエントリー/エグジットをしたら、勝率や損益はどうなるんだろう?

ふと気になったので実際に検証してみました。

さてランダムトレードの成績は?

経緯「以前は勝てていたのに」

裁量トレードでもシステムトレードでも経験がありますが、不思議と勝ちが続く時があります。そんな時には「自分には相場が読める!」と有頂天になるのですが、しばらくするとやっぱり勝てなくなります。

勝てていた手法が、なぜ通用しなくなったの?

実は偶然勝ってただけ?

じゃあ、超適当(ランダム)にトレードしたらどれくらいの成績になるの?

という経緯(?)でランダムにトレードするEAを作りました。

ランダムトレードするEAを作成

便宜上、このEAを「ランダムくん」と呼ぶことにします。ランダムくんが「いい具合にランダムにトレードするように」以下の仕様にしました。

ランダムくんの動作仕様

ロット・ポジション数
  • ロット数は0.1lot(10,000通貨)とする
  • ポジションを1つしか持たない(最大ポジション0.1lot)
エントリー
  • ロングかショートかをランダムで決める
  • 前回イグジット後0分~24時間(ランダム)で次のエントリーをする
イグジット
  • エントリー後0分~24時間(ランダム)でイグジットする
  • ホールド途中で利確または損切りはしない

ポイントとしては、連続してトレードしないように、エントリーまでの待機時間やポジション保有中のホールド時間をランダムにしているところです。この仕様を入れておかないと、一定の周期でエントリー/イグジットを繰り返してしまうので、機械的なトレード結果になってしまいます。

軽く動作チェック

ランダムくんの動作をざっとチェックしました。下図はランダムくんのトレード軌跡ですが、いい具合にランダムなトレードをしているのが確認できました。

ランダムトレードの様子

↑赤色のラインがロング、青色のラインがショートです。エントリー方向やエントリータイミング、ホールド時間、次のエントリーまでのインターバルがバラバラでランダムにトレードしていることが分かります。

補足

上のチャート表示にはBacktestViewerというインジを使っています。参考記事→「MT4 バックテスト結果を見える化するインジケーター(Mi_BackTestViewer)

ランダムトレードの成績(1年)

それではいよいよランダムくんでバックテストです!テスト期間は1年間にして、3回まわしてみました。主な条件は以下の通り。

テスト条件(1年)

  • テスト期間:2018年1月~2018年12月(1年間
  • 通関ペア:ドル円
  • スプレッド:1.0pips(=1.0銭)
  • ロット:1万通貨
  • 初期残高:100万円

テスト1回目

下図は、1回目のバックテスト結果です。

1年1回目のグラフ

1年1回目の成績

↑あれ?意外と頑張ってる!?勝率は51.6%で、スプレッドに負けず5万円ほどプラスの結果に。後半は右肩上がりに見えなくもないです。

テスト2回目

下図は、2回目のバックテスト結果です。

1年2回目のグラフ

1年2回目の成績

↑2回目の勝率は、46.7%でした。残高グラフは1回目とは全然違う形です。勝ち傾向が続いたり、負け傾向が続いたりで、山や谷が生じました。最終損益は3万円弱のプラスです。

テスト3回目

下図は、3回目のバックテスト結果です。

1年3回目のグラフ

1年3回目の成績

↑3回目の勝率は48.1%でした。またもやグラフは違う形に。なだらかな丘のような形になりました。最終損益はトントンです。

1年だと想像より偏る

テストをする前は、もっとダラダラとなだらかに負けていくグラフを予想していました。ところが実際にテストしてみたら、山あり谷ありで一時的ではあるが想像していたよりも勝ち負けが偏ることがわかりました。

ランダムトレードの成績(10年)

1年では予想していたよりも勝ち負けが偏ることが分かりました。では10年なら?ということで10年でテストしてみました。

テスト条件(10年)

  • テスト期間:2009年1月~2018年12月(10年間
  • 通関ペア:ドル円
  • スプレッド:1.0pips(=1.0銭)
  • ロット:1万通貨
  • 初期残高:100万円

テスト結果(10年)

下図はランダムくんの10年間のバックテスト結果です。

10年のグラフ

10年の成績

これならさすがにランダムくんの実力が見えてきますね。右肩下がりのグラフで安定してダメなトレードです。(それでも途中には小さいながらも山谷が発生します。)

10年だと実力どおり

10年の長期間でバックテストをするとEAの実力がはっきりわかります。1年でテストした場合は勝ち負けが多少偏りましたが、10年でみるとEA本来の成績に収束していきます。ランダムくんの場合は、長期間で見るとスプレッド分だけ負けていくという傾向が分かります。

参考:長期のヒストリーデータを作るには

長期のヒストリーデータを作る方法はこちらの記事にまとめてあります。→「長期のヒストリーデータを取得・作成する方法(MT4)

ランダムトレードのコード

参考としてランダムくんのコードを記載しておきます。自分でバックテストしてみたい方は良かったら参考にしてください。ただしリアルトレードには使わないでくださいね。エラー回避等の処理が全く入れてありませんし、そもそもランダムくん理屈上は勝てないロジックです。

参考記事

ランダム関数の使いかたはこちら→「[MQL]ランダムな値を発生させるMathRand()の使い方


//+------------------------------------------------------------------+
//|                                                 Mi_Randomkun.mq4 |
//|                                                     minagachi FX |
//|                                            https://minagachi.com |
//+------------------------------------------------------------------+
#property copyright "minagachi FX"
#property link      "https://minagachi.com"
#property version   "1.00"
#property strict

#define MAGICRANDOM 10000008  //マジックナンバー
input double Lot = 0.1;       // ロット数
input int Wait_Max = 1440;    // 最大待機時間(分)

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   if(!IsTesting()){
      MessageBox("ランダムくんはバックテストでないとトレードしません。");
   }
   MathSrand(GetTickCount()); // 乱数シード
   return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   //処理なし
}
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
   // バックテストかどうかチェック
   if(!IsTesting()){
      return;
   }

   // 変数宣言
   static bool initialized = false; // 初回処理判定
   static datetime time_trade;      // トレード時刻

   // 初回処理のみ
   if(!initialized){
      // トレード時刻を算出
      time_trade = CalcTimeTrade();
      initialized = true;
   }

   // トレード時刻になったら
   if(TimeCurrent() > time_trade){

      // ポジションなしの場合は新規エントリー
      if(CalcOrders() == 0){
         DoEntry();
         // トレード時刻を更新
         time_trade = CalcTimeTrade();
      }

      // ポジション保有中の場合はイグジット
      else{
         DoExit();
         // トレード時刻を更新
         time_trade = CalcTimeTrade();
      }
   }
}
//+------------------------------------------------------------------+
//|関数 次回のトレード時刻をランダムで算出                               |
//+------------------------------------------------------------------+
datetime CalcTimeTrade(){
   // 待機時間(0~1440分)をランダムで決める
   int time_wait = MathRand()%Wait_Max;
   // 秒に換算
   time_wait = time_wait * 60;

   // トレード時刻を計算(現在時刻+待機時間)
   datetime time_next = TimeCurrent() + (datetime)time_wait;
   return time_next;
}

//+------------------------------------------------------------------+
//|関数 ポジション数計算                                               |
//+------------------------------------------------------------------+
int CalcOrders(){
   bool res;
   int num = 0;
   for(int i = 0; i < OrdersTotal(); i++){
      res = OrderSelect(i, SELECT_BY_POS, MODE_TRADES);
      if(OrderSymbol() == Symbol() && OrderMagicNumber() == MAGICRANDOM){
         num++;
      }
   }
   return(num);
}

//+------------------------------------------------------------------+
//|関数 エントリー処理                                                 |
//+------------------------------------------------------------------+
void DoEntry(){
   bool res;
   // エントリー方向をランダムに決定
   int direction = MathRand()%2; // 0または1

   // 0の場合はショート
   if(direction == 0){
      res = OrderSend(Symbol(), OP_SELL, Lot, Bid, 0, 0, 0, "", MAGICRANDOM, 0, clrRed);
      return;
   }

   // 1の場合はロング
   if(direction == 1){
      res = OrderSend(Symbol(), OP_BUY, Lot, Ask, 0, 0, 0, "", MAGICRANDOM, 0, clrBlue);
      return;
   }
}

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

まとめ

ランダムトレードって思ったよりも勝ち負けが偏るなぁ」というのが一番の感想です。ランダムならもっと勝ち負けが均等にばらつくと思っていたのですが、テスト結果では「勝ちが続く期間や負けが続く期間」が見て取れました。

裁量トレードにしろ、システムトレードにしろ、こういった偏りには気を付けたいですね。一定期間勝ちまくっていたとしても単なる偶然の可能性が十分あるということです。浮かれずに冷静に検証していきたいと改めて思いました。

コメント

この記事へのコメント(0 件)