陰線陽線の連続で逆の足が出やすい?

陰線・陽線が連続すると相場は反転するのか?

よくブログや掲示板などの手法で「ローソク足が5本連続したら次の足は逆の足になりやすい」といった文言を目にします。

たしかに陽線や陰線が連続すると、次は逆の足が出そうな気もしますが、相場の値動きが完全にランダムだと仮定するとどれだけ陽線や陰線が連続したとしてもその次に逆のローソク足が出る確率は2分の1です。

実際のところこのロジックは有効なのでしょうか?

酒田五法

酒田五法とは、江戸自体の相場師である本間宗久が考案したローソク足の組み合わせによる相場分析手法です。

その名の通り5つの分析手法があるのですが、その中に「三兵」という分析手法があります。

三兵とは、例えば陽線が3本連続して出た場合強い上昇トレンドが発生したと考え、順張りの買いを実施する手法のことです。

つまり、冒頭の「陰線・陽線が連続すると相場は反転する」という考え方とは全く逆の考え方になっています。

どちらの考え方が正しいのでしょうか?

検証する方法

ということで、mql4のプログラムを書いて検証してみます。

チャートにセットすると、過去のローソク足の内、〇本連続で出現した後に反転する数を計測し、その確率を求めるようなスクリプトを書いていきます。

例えば以下の画像のようなチャートが対象だった場合、

1連続後の反転 2/4 50%
2連続後の反転 1/2 50%
3連続後の反転 1/1 100%

と表示することを目標にします。

mql4プログラムを書く

それではプログラムを書いていきます。

プログラムの全文は本記事の一番下に記載しております。

スクリプトを作成する

今回はティックごとに処理を実施したり、矢印を描画するわけではないので「インジケーター」ではなく「スクリプト」を作成していきます。

まずメタエディターからスクリプトを新規作成します。

インジケーターでも大丈夫

スクリプトに抵抗がある場合は、インジケーターを作成しOnInit関数に記述しても同じ実装を行うことができます。

フィールドを記述する

#property copyright "Copyright 2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property strict
#property show_inputs

input int period = 100000;           //計測期間

今回パラメータ設定計測する期間を指定できるようにします。

デフォルトの設定では過去10万本のローソク足で計測を行うようにします。

また、スクリプトの場合「property show_inputs」というプロパティを指定しないと、チャートにスクリプトをセットした時にパラメータの変更画面が出てこなくなりますので注意が必要です。

int countArray[100][4] = {0};    //countArray[連続回数][↓のコメントを参照]=カウントの回数
//0:陽線が連続後に陽線が出た回数
//1:陰線が連続後に陽線が出た回数
//2:陽線が連続後に陰線が出た回数
//3:陰線が連続後に陽線が出た回数  

計測結果を格納するための配列を作成します。

今回1次元目の添え字が「〇本連続」の〇に対応するようにし、2次元目の添え字が例えば1の時は陰線が連続後に陽線が出た回数をカウントします。

例えば計測した結果「陽線が5本連続で出た直後に陰線が出た回数が15回」だったとき、countArray[5][2]には15が格納されています。

また今回100本連続で陽線や陰線は出ないだろうと決め打ちをして、1次元目の要素数の最大を100としています。

int bullishCount = 0;               //陽線が連続した回数
int bearishCount = 0;               //陰線が連続した回数
int previousCandle = EMPTY_VALUE;   //0は陽線、1は陰線

計測に使用する変数を宣言します。

bullishCountは陽線が連続した回数をカウントし、bearishCountは陰線が連続した回数をカウントします。

previousCandleは1つ前の足が陽線なら0を、陰線なら1を格納するようにします。

これでフィールドの中身を埋めることができました。

#property copyright "Copyright 2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property strict
#property show_inputs

input int period = 100000;           //計測期間

int countArray[100][4] = {0};    //countArray[連続回数][↓のコメントを参照]=カウントの回数
//0:陽線が連続後に陽線が出た回数
//1:陰線が連続後に陽線が出た回数
//2:陽線が連続後に陰線が出た回数
//3:陰線が連続後に陽線が出た回数  

int bullishCount = 0;               //陽線が連続した回数
int bearishCount = 0;               //陰線が連続した回数
int previousCandle = EMPTY_VALUE;   //0は陽線、1は陰線

OnStart関数を記述する

続いてOnStart関数を記述していきます。OnStart関数はチャートにスクリプトをセットした瞬間に実行される標準関数です。この関数の実行が終了した時点でスクリプトはチャートから自動で除外されます。

ではOnStart関数の中身を書いていきます。

void OnStart()
{
   int startIndex = Bars <= period ? Bars - 1 : period ;
}

int型の変数startIndexに、計測を開始するインデックスを格納します。

パラメータで指定した期間(period)よりも、存在するローソク足の数(Bars)が少ない場合、startIndexの値をBars-1とします。

void OnStart()
{
   int startIndex = Bars <= period ? Bars - 1 : period ;
   //カウント
   for(int i = startIndex - 1; i >= 0 ; i--){
      if(Open[i] < Close[i]){
         if(previousCandle == 0 && previousCandle != EMPTY_VALUE){
            int v = bullishCount;
            while(v > 0){
               countArray[v][0]++;
               v--;
            }
         }
         if(previousCandle == 1 && previousCandle != EMPTY_VALUE){
            int v = bearishCount;
            while(v > 0){
               countArray[v][1]++;
               v--;
            }
         }
         bullishCount++;
         bearishCount = 0;
         previousCandle = 0;
      }
      if(Open[i] > Close[i]){
         if(previousCandle == 0 && previousCandle != EMPTY_VALUE){
            int v = bullishCount;
            while(v > 0){
               countArray[v][2]++;
               v--;
            }
         }
         if(previousCandle == 1 && previousCandle != EMPTY_VALUE){
            int v = bearishCount;
            while(v > 0){
               countArray[v][3]++;
               v--;
            }
         }
         bullishCount = 0;
         bearishCount++;
         previousCandle = 1;
      }
      if(Open[i] == Close[i]){
         bullishCount = 0; 
         bearishCount = 0;
         previousCandle = EMPTY_VALUE;
      }
   }
}

プログラムのカウント部分を追記しました。

1つ前のローソク足が陽線か陰線か、陽線と陰線がどれくらい続いているか、といった情報をもとに条件分岐を作成し、countArray配列をどんどん更新しています。

また今回終値と始値の値が完全に同じ、つまり陽線でも陰線でもない場合は連続回数をリセットして再度カウントをやり直すようにしています。

最後に計測結果を出力するプログラムを追記します。

void OnStart()
{
   int startIndex = Bars <= period ? Bars - 1 : period ;
   //カウント
   for(int i = startIndex - 1; i >= 0 ; i--){
      if(Open[i] < Close[i]){
         if(previousCandle == 0 && previousCandle != EMPTY_VALUE){
            int v = bullishCount;
            while(v > 0){
               countArray[v][0]++;
               v--;
            }
         }
         if(previousCandle == 1 && previousCandle != EMPTY_VALUE){
            int v = bearishCount;
            while(v > 0){
               countArray[v][1]++;
               v--;
            }
         }
         bullishCount++;
         bearishCount = 0;
         previousCandle = 0;
      }
      if(Open[i] > Close[i]){
         if(previousCandle == 0 && previousCandle != EMPTY_VALUE){
            int v = bullishCount;
            while(v > 0){
               countArray[v][2]++;
               v--;
            }
         }
         if(previousCandle == 1 && previousCandle != EMPTY_VALUE){
            int v = bearishCount;
            while(v > 0){
               countArray[v][3]++;
               v--;
            }
         }
         bullishCount = 0;
         bearishCount++;
         previousCandle = 1;
      }
      if(Open[i] == Close[i]){
         bullishCount = 0; 
         bearishCount = 0;
         previousCandle = EMPTY_VALUE;
      }
   }
   //結果表示
   for(int i = 0; i < 100 ; i++){
      if(countArray[i][0] != 0 || countArray[i][1] != 0 || countArray[i][2] != 0 || countArray[i][3] != 0){
         double hanten = countArray[i][1] + countArray[i][2];
         double notHanten = countArray[i][0] + countArray[i][3];
         Print((string)i + "連続後の反転 " + (string)hanten + "/" + (string)(hanten + notHanten) + " " +string(int(hanten * 100 / (hanten + notHanten))) + "%");
      }
   }
}

このスクリプトをチャートに追加すると、ターミナルのエキスパートには以下の用に表示されます。(USDJPY5分足)

結果まとめ(USDJPY)

1分足

5分足

15分足

30分足

1時間足

4時間足

日足

いかがでしょうか。

今回は始値と終値が全く同じのローソク足の場合は負けと判定していますが、全体的に見て陰線・陽線が連続すると逆の足が出やすいとは言えないですね。

特定の通貨ペアや時間帯では有効なロジックになるかもしれません。

プログラム全文

#property copyright "Copyright 2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property strict
#property show_inputs

input int period = 100000;           //計測期間

int countArray[100][4] = {0};  
//0:陽線が連続後に陽線が出た回数
//1:陰線が連続後に陽線が出た回数
//2:陽線が連続後に陰線が出た回数
//3:陰線が連続後に陽線が出た回数     

int bullishCount = 0;
int bearishCount = 0;
int previousCandle = EMPTY_VALUE;   //0は陽線、1は陰線

void OnStart()
{
   int startIndex = Bars <= period ? Bars - 1 : period ;

   //カウント
   for(int i = startIndex - 1; i >= 0 ; i--){
      if(Open[i] < Close[i]){
         if(previousCandle == 0 && previousCandle != EMPTY_VALUE){
            int v = bullishCount;
            while(v > 0){
               countArray[v][0]++;
               v--;
            }
         }
         if(previousCandle == 1 && previousCandle != EMPTY_VALUE){
            int v = bearishCount;
            while(v > 0){
               countArray[v][1]++;
               v--;
            }
         }
         bullishCount++;
         bearishCount = 0;
         previousCandle = 0;
      }
      if(Open[i] > Close[i]){
         if(previousCandle == 0 && previousCandle != EMPTY_VALUE){
            int v = bullishCount;
            while(v > 0){
               countArray[v][2]++;
               v--;
            }
         }
         if(previousCandle == 1 && previousCandle != EMPTY_VALUE){
            int v = bearishCount;
            while(v > 0){
               countArray[v][3]++;
               v--;
            }
         }
         bullishCount = 0;
         bearishCount++;
         previousCandle = 1;
      }
      if(Open[i] == Close[i]){
         bullishCount = 0; 
         bearishCount = 0;
         previousCandle = EMPTY_VALUE;
      }
   }

   //結果表示
   for(int i = 0; i < 100 ; i++){
      if(countArray[i][0] != 0 || countArray[i][1] != 0 || countArray[i][2] != 0 || countArray[i][3] != 0){
         double hanten = countArray[i][1] + countArray[i][2];
         double notHanten = countArray[i][0] + countArray[i][3];
         Print((string)i + "連続後の反転 " + (string)hanten + "/" + (string)(hanten + notHanten) + " " +string(int(hanten * 100 / (hanten + notHanten))) + "%");
      }
   }
}