English Deutsch
preview
ラリー・ウィリアムズの『市場の秘密』(第3回):MQL5で非ランダムな市場の動きを証明する

ラリー・ウィリアムズの『市場の秘密』(第3回):MQL5で非ランダムな市場の動きを証明する

MetaTrader 5トレーディングシステム |
25 1
Chacha Ian Maroa
Chacha Ian Maroa

はじめに

「金融市場は本当にランダムなのか、それとも測定および検証が可能なパターンを持っているのか。」この問いは、これまでに考案されたあらゆる取引システムの根幹にあるものです。価格変動が完全にランダムであるならば、戦略やインジケーター、あるいは自動売買システムを構築する試みは最終的に意味を持たなくなります。そこに優位性は存在せず、あるのは純粋な偶然のみということになります。

本記事では、これらの考え方を現代的かつ実践的な視点から再検討します。(基礎となる統計的概念についてより詳しく知りたい場合は、前回の記事をご参照ください。) 単に結論を受け入れるのではなく、MQL5コードを用いてウィリアムズの実験を再現し、さらに拡張します。カスタムエキスパートアドバイザー(EA)を作成し、特定の価格挙動がランダムに期待される頻度を超えて発生しているかどうかを検証します。目的は市場を確実に予測することではなく、小さくても統計的に意味のある偏りが存在するかどうかを見極めることです。

これは重要なポイントです。わずかな統計的バイアスであっても、それがトレーディングにおける優位性の基盤となり得るからです。過去の価格変動と将来の結果の間に一切の関係がないのであれば、システマティックトレードは成立しません。しかし、市場が部分的にでも非ランダムであるならば、構造、論理、確率はトレーダーにとって有効な手段となります。


非ランダムな市場の動きをどのようにテストするか

「はじめに」では、シンプルでありながら本質的な考え方を提示しました。市場が完全にランダムであれば、価格変動はコイン投げのような振る舞いになります。各ローソク足は前のローソク足から独立しており、上昇または下落で終わる確率は、過去の値動きに関係なく、理論上は50%になるはずです。

ラリー・ウィリアムズは、この前提に対して実践的な問いを投げかけました。「価格が上昇して終わった場合、あるいは下落して終わった場合、その後はどうなるのか?」市場に記憶がないのであれば、次のローソク足の結果は変化しないはずです。しかし彼の研究では、実際にはそうではないことが示されています。

この仮説を定量的に検証するために、今回は自動化されたアプローチを採用します。主観的な解釈や都合の良い例に頼るのではなく、コードを用いて過去データをスキャンし、特定の条件がどの程度の頻度で発生するかを集計します。それぞれのテストは、プライスアクションに関する確率的な問いに答えるよう設計されています。

今回実施する実験は、大きく3つのカテゴリに分かれます。まず1つ目は、単一ローソク足における方向性バイアスの検証です。終値が始値より高くなる割合が、偶然期待される50%からどの程度ずれるかを確認します。

2つ目は、条件付き確率の検証です。ここでは、1本または複数本の連続した陽線および陰線の後に、市場の挙動が変化するかどうかを検証します。市場が完全にランダムであれば、次のローソク足が陽線や陰線となる確率は変化しないはずです。ここで確率が変化する場合、価格は直近の値動きに影響を受けている可能性があります。

3つ目は、簡単な市場構造の検証です。ラリー・ウィリアムズは短期的な安値を、3本のローソク足で構成されるパターンとして定義しています。このパターンに意味があるなら、その後のローソク足は通常よりも高い確率で上昇するはずです。

これらの考え方をもとに、以下の8つのテストケースを自動化します。

オープンからクローズへのバイアス

ローソク足が陽線で終わる頻度、つまり終値が始値より高い割合を測定します。完全にランダムな市場であれば、陽線と陰線はそれぞれおおよそ50%ずつ出現するはずです。

1本の陰線の後における陽線の確率

1本の陰線の後に、次のローソク足が陽線で終わる頻度を検証します。これにより、短期的な押し目が買い圧力を誘発する傾向があるかどうかを確認します。

2本連続した陰線の後における陽線の確率

陰線が2本連続した後に、陽線の確率が上昇するかを検証します。短期的な下落が反発につながる傾向があるかを調べます。

3本連続した陰線の後における陽線の確率

さらに深い短期下落局面の後に、価格が反発する傾向があるかを検証します。

1本の陽線の後における陽線の確率

単一の陽線の後に、その勢いが継続するのか、それとも失速するのかを検証します。

2本連続した陽線の後における陽線の確率

上昇の勢いが継続するのか、それとも短期的な上昇の後に価格が停滞するのかを調べます。

3本連続した陽線の後における陽線の確率

連続した上昇圧力が継続するのか、それとも過熱によって反転するのかを検証します。

短期的な市場安値の後における陽線の確率

ラリー・ウィリアムズの定義に基づき、3本のローソク足パターンから短期的な安値を検出し、その後のローソク足が陽線で終わる頻度を測定します。

これらのテストはすべて、「過去のローソク足の結果が次のローソク足の確率に影響を与えるのか」という単純な問いに集約されます。もし常に50%であれば、市場はコイン投げと同じ振る舞いになります。一方で、一定の偏りが継続的に観測される場合、それは非ランダムな構造の存在を示唆します。

これらのテストケースを定義できたので、次は技術的な実装に進みます。次のセクションでは、各実験を個別に実行し、異なる市場での結果を過去データから直接観測できるMQL5エキスパートアドバイザー(EA)を実装します。


MQL5での実験実装

これらの市場挙動が本当に非ランダムなのかを検証するには、単なる統計以上のものが必要です。実際の取引条件をシミュレーションする必要があります。つまり、ローソク足の始値でエントリーし、終値でクローズし、その結果を記録するという方法です。このアプローチにより、確率的な検証だけでなく、より実践的な問いにも答えられます。それは「これらの挙動は実際の取引戦略として成立するのか」という点です。

まずMetaEditorを開き、新しいEAを作成し、空のテンプレートを選択します。ファイル名はlarryWilliamsNonRandomMarketBehaviorTester.mq5とします。作成後、提供されたソースコードを貼り付けて保存します。

//+------------------------------------------------------------------+
//|                   larryWilliamsNonRandomMarketBehaviorTester.mq5 |
//|          Copyright 2025, MetaQuotes Ltd. Developer is Chacha Ian |
//|                          https://www.mql5.com/ja/users/chachaian |
//+------------------------------------------------------------------+

#property copyright   "Copyright 2025, MetaQuotes Ltd. Developer is Chacha Ian"
#property link        "https://www.mql5.com/ja/users/chachaian"
#property version     "1.00"
#property description "This Expert Advisor is designed to run one statistical experiment at a time."
#property description "The test to be executed is selected through a dropdown input when attaching the EA to the chart."

//+------------------------------------------------------------------+
//| Standard Libraries                                               |
//+------------------------------------------------------------------+
#include <Trade\Trade.mqh>

//+------------------------------------------------------------------+
//| Global Variables                                                 |
//+------------------------------------------------------------------+
//--- Create a CTrade object to handle trading operations
CTrade Trade;

//--- Bid and Ask
double   askPrice;

//+------------------------------------------------------------------+
//| User input variables                                             |
//+------------------------------------------------------------------+
input group "Information"
input ulong           magicNumber                 = 254700680002;                 
input ENUM_TIMEFRAMES timeframe                   = PERIOD_CURRENT;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit(){

   //---  Assign a unique magic number to identify trades opened by this EA
   Trade.SetExpertMagicNumber(magicNumber);

   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason){

   //--- Notify why the program stopped running
   Print("Program terminated! Reason code: ", reason);

}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick(){

   //--- Retrieve current market prices for trade execution
   askPrice      = SymbolInfoDouble (_Symbol, SYMBOL_ASK);
   
}

//+------------------------------------------------------------------+
//| TradeTransaction function                                        |
//+------------------------------------------------------------------+
void OnTradeTransaction(const MqlTradeTransaction& trans,
                        const MqlTradeRequest& request,
                        const MqlTradeResult& result)
{
}
  
//+------------------------------------------------------------------+

この初期コードは、これから構築していくための土台になります。まだ実際の実験処理は実行されませんが、後続のロジックを整理された状態で実装できるよう、必要な構成を一通り準備しています。 理解を助けるために、完成版のソースコードとしてlarryWilliamsNonRandomMarketBehaviorTester.mq5を添付していますので、ダウンロードして自身の実装と比較し、正しくセットアップできているか確認してください。

ファイルヘッダとプロパティディレクティブ

//+------------------------------------------------------------------+
//|                   larryWilliamsNonRandomMarketBehaviorTester.mq5 |
//|          Copyright 2025, MetaQuotes Ltd. Developer is Chacha Ian |
//|                          https://www.mql5.com/ja/users/chachaian |
//+------------------------------------------------------------------+

#property copyright   "Copyright 2025, MetaQuotes Ltd. Developer is Chacha Ian"
#property link        "https://www.mql5.com/ja/users/chachaian"
#property version     "1.00"
#property description "This Expert Advisor is designed to run one statistical experiment at a time."
#property description "The test to be executed is selected through a dropdown input when attaching the EA to the chart."

最初のセクションでは、ファイル名、作成者情報、バージョン、そしてEAの簡単な説明を定義します。これらのプロパティは、EAをチャートに適用した際にその目的を明確に示すために重要です。この場合、EAは一度に1つの統計的実験のみを実行し、その実験は入力ドロップダウンから選択できることが説明として明記されています。これは本記事の目的と直接的に一致しています。

標準ライブラリのインクルード

//+------------------------------------------------------------------+
//| Standard Libraries                                               |
//+------------------------------------------------------------------+
#include <Trade\Trade.mqh>

次に、標準Tradeライブラリをインクルードします。このライブラリはCTradeクラスへのアクセスを提供し、安全かつ構造化された方法で注文処理をおこなえるようにします。このクラスを使用することで、取引管理がシンプルになり、実行時エラーのリスクも低減できるため、利用が推奨されます

ユーザー入力設定
//+------------------------------------------------------------------+
//| User input variables                                             |
//+------------------------------------------------------------------+
input group "Information"
input ulong           magicNumber                 = 254700680002;                 
input ENUM_TIMEFRAMES timeframe                   = PERIOD_CURRENT;

入力セクションでは、EAをチャートに適用する際にユーザーが設定できるパラメータを定義します。このEAによって開かれた取引を一意に識別するためのマジックナンバーを指定し、他の戦略の取引と干渉しないようにします。また、実験をおこなう時間足もユーザーが選択できるようにし、異なるチャート設定でも柔軟に再利用できるようにしています。

グローバルな取引オブジェクトと変数

//+------------------------------------------------------------------+
//| Global Variables                                                 |
//+------------------------------------------------------------------+
//--- Create a CTrade object to handle trading operations
CTrade Trade;

//--- Bid and Ask
double   askPrice;

次に、グローバルなCTradeオブジェクトを作成します。このオブジェクトはEA全体を通して使用され、ポジションのオープンおよびクローズ処理を担います。また、市場価格を扱うための変数も宣言します。この段階ではAsk価格のみを取得しており、実験では買いポジションを建てるだけなのでこれで十分です。

初期化ロジック

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit(){

   //---  Assign a unique magic number to identify trades opened by this EA
   Trade.SetExpertMagicNumber(magicNumber);

   return(INIT_SUCCEEDED);
}

初期化関数は、EAがチャートに適用されたときに一度だけ実行されます。ここではCTradeオブジェクトにマジックナンバーを割り当てます。このステップは重要で、EAが発注したすべての取引を後から正しく識別および管理できるようにするために必要です。

初期化解除ロジック

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason){

   //--- Notify why the program stopped running
   Print("Program terminated! Reason code: ", reason);

}

EAが削除または停止されると、初期化解除関数が呼び出されます。このケースでは、プログラムが停止した理由を示すメッセージを出力するだけです。これはデバッグ時や、EAのライフサイクルがどのタイミングでどのように終了したのかを把握するのに役立ちます。

ティック処理

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick(){

   //--- Retrieve current market prices for trade execution
   askPrice      = SymbolInfoDouble (_Symbol, SYMBOL_ASK);
   
}

ティック関数は、新しい市場データが到着するたびに継続的に実行されます。現時点では、現在のAsk価格を取得するのみですが、後にこの関数は実験全体の制御センターとして機能するようになります。具体的には、新しいローソク足の検出、テスト条件の評価、エントリー、そして適切なタイミングでのクローズ処理をここで実装します。

取引トランザクションハンドラ

//+------------------------------------------------------------------+
//| TradeTransaction function                                        |
//+------------------------------------------------------------------+
void OnTradeTransaction(const MqlTradeTransaction& trans,
                        const MqlTradeRequest& request,
                        const MqlTradeResult& result)
{
}

最後に、取引トランザクション関数を定義します。この関数は、ポジションのオープンやクローズといった取引関連のアクションが発生するたびに呼び出されます。現時点では中身は空ですが、今後は取引結果の追跡や実験データの収集に活用していきます。

この段階では、EAはチャート上で目に見える動作をまだおこないません。それは意図的なもので、ベストプラクティスに従ったクリーンで信頼性の高い土台を構築できています。次のセクションでは、新しいローソク足の検出ロジックを追加し、非ランダム性のテストケースを適用しながら、実際の市場挙動を観察するための取引実行処理を組み込んでいきます。

EAの骨組みが整ったので、次のステップは市場の挙動を認識できるようにすることです。この段階ではまだ実際の売買はおこないません。目的はシンプルですが重要で、それぞれの非ランダムテストケースに対応するパターンを正しく検出できるようにすることです。

EAが検証をおこなうためには、どの実験を実行しているのかを明確に認識している必要があります。そのため最初のステップとして、テストシナリオをクリーンかつ制御しやすい形で切り替える仕組みを定義します。

テストモードの定義

標準ライブラリのインクルードの直後に、実行したい非ランダム実験を一覧化したカスタム列挙型を定義します。それぞれの値は、それぞれ検証対象となる個別の市場挙動を表します。

//--- CUSTOM ENUMERATIONS
//+------------------------------------------------------------------+
//| Non-random market behavior test modes                            |
//+------------------------------------------------------------------+
enum ENUM_NON_RANDOM_TEST_MODE
{
   TEST_OPEN_TO_CLOSE_BIAS,  
   TEST_AFTER_ONE_DOWN_CLOSE,    
   TEST_AFTER_TWO_DOWN_CLOSES,    
   TEST_AFTER_THREE_DOWN_CLOSES, 
   TEST_AFTER_ONE_UP_CLOSE,          
   TEST_AFTER_TWO_UP_CLOSES,                 
   TEST_AFTER_THREE_UP_CLOSES,
   TEST_AFTER_SHORT_TERM_LOW 
};

この列挙型により、EAは一度に1つのテストモードのみで動作するようになります。EAをチャートに適用すると、ユーザーは入力パラメータとして用意されたドロップダウンから実行する実験を選択できます。この設計によりロジックがシンプルに保たれ、異なるテスト結果が混在するのを防ぐことができます。

この時点では、まだ確率の検証はおこなっていません。あくまで構造を定義している段階です。この構造が、以降のパターン検出や意思決定ロジックの基盤となります。

バー開始インスタンスの検出

今回のすべての実験は、新しいローソク足が形成されたタイミングで一度だけ評価されます。これは非常に重要です。すべてのティックに反応すると、ノイズや重複シグナルが発生してしまうためです。そのため、新しいバーが確定したタイミングでのみパターンを検出します。

これを実現するために、選択した時間足において新しいバーが生成されたかどうかを判定する、小さくも重要なユーティリティ関数を定義します。

//--- UTILITY FUNCTIONS
//+------------------------------------------------------------------+
//| Function to check if there's a new bar on a given chart timeframe|
//+------------------------------------------------------------------+
bool IsNewBar(string symbol, ENUM_TIMEFRAMES tf, datetime &lastTm)
{

   datetime currentTm = iTime(symbol, tf, 0);
   if(currentTm != lastTm){
      lastTm       = currentTm;
      return true;
   }  
   return false;   
}

この関数では、現在のバーの始値時刻と、最後に記録したバーの時刻を比較します。時刻が異なれば、新しいバーが形成されたと判断できます。

このロジックを支えるために、直近のバーの始値時刻を保持するグローバル変数もあわせて宣言しておきます。

//+------------------------------------------------------------------+
//| Global Variables                                                 |
//+------------------------------------------------------------------+

...

//--- To help track new bar open
datetime lastBarOpenTime;

この変数は、初期化関数内で0に初期化されます。これにより、最初のバーが必ず正しく新規バーとして検出されるようになります。

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit(){

   ...
   
   //--- Initialize global variables
   lastBarOpenTime = 0;

   return(INIT_SUCCEEDED);
}

ここから先、EA内のすべてのパターンチェックは、新しいバーが確定したタイミングでのみ実行されます。これにより、すべての実験間で一貫性が保たれるようになります。

連続バー挙動のための汎用関数

今回のテストケースの多くは、「一定本数の陽線または陰線が続いた後、次に何が起こるのか?」というシンプルな問いに基づいています。

このため、ケースごとに個別のロジックを実装するのではなく、すべてのケースに対応できる汎用的で柔軟な関数を用意します。この関数では、直近N本の確定済みローソク足が、それぞれ始値に対して同じ方向(上昇または下降)で終わっているかどうかを判定します。

//+------------------------------------------------------------------+
//| Checks whether the last N completed bars all closed              |
//| either up or down relative to their open                         |
//+------------------------------------------------------------------+
bool IsConsecutiveBarCloseState(string symbol, ENUM_TIMEFRAMES tf, int barsToCheck, ENUM_BAR_CLOSE_STATE closeState)
{
   // Start from bar index 1 (last fully closed bar)
   for(int i = 1; i <= barsToCheck; i++){
   
      double openPrice  = iOpen (symbol, timeframe, i);
      double closePrice = iClose(symbol, timeframe, i);

      // Safety check (in case of missing data)
      if(openPrice == 0.0 || closePrice == 0.0){
         return false;
      }

      // Validate close direction
      if(closeState == BAR_CLOSE_UP && closePrice <= openPrice){
         return false;
      }
         
      if(closeState == BAR_CLOSE_DOWN && closePrice >= openPrice){
         return false;
      }      
   }
   return true;
}

関数を再利用しやすく、かつ明確にするために、テスト対象の銘柄、時間足、評価する連続バーの本数、そしてそのバーの方向の4つの入力を受け取るようにします。バーの方向を明確に表現するために、陽線と陰線の終値方向を示す小さな列挙型を導入します。

//--- CUSTOM ENUMERATIONS
//+------------------------------------------------------------------+
//| Non-random market behavior test modes                            |
//+------------------------------------------------------------------+

...

//+------------------------------------------------------------------+
//| Direction of candle close relative to open                       |
//+------------------------------------------------------------------+
enum ENUM_BAR_CLOSE_STATE
{
   BAR_CLOSE_UP, 
   BAR_CLOSE_DOWN
};

この関数は、確定済みのローソク足のみを対象としてループ処理をおこないます。直近で確定したバーから過去方向へ順にチェックし、各バーについて始値と終値を比較します。そして期待される方向と一致しているかを確認します。もし途中のいずれかのバーが条件を満たさない場合は、その時点でfalseを返して処理を終了します。すべてのバーが条件を満たしている場合にのみtrueを返します。

この単一の関数は、8つのテストケースのうち6つをカバーしています。非ランダム性テストの中核となる構成要素であり、後に実際の売買ロジックにも直接利用される重要な基盤となります。

ラリー・ウィリアムズの短期安値の検出

最後に必要となるパターンは、より構造的なものです。ラリー・ウィリアムズは短期的な安値を、3本のローソク足によって構成される形状として定義しています。このとき中央のバーがスイングローを形成し、その左右のバーよりも低い安値を持つことが条件となります。

ただし彼はさらに2つの重要なフィルターを加えています。まずスイングバーはアウトサイドバーであってはならず、また直近のバーもインサイドバーであってはなりません。これらの条件により、弱いシグナルやノイズを排除します。

このパターンを検出するために、直近3本の確定済みローソク足を分析する専用関数を構築します。

//+------------------------------------------------------------------+
//| Detects a Larry Williams short-term low on the last three bars   |
//| Bar index 2 must be a swing low with higher lows on both sides   |
//| Bar 2 must NOT be an outside bar                                 |
//| Bar 1 must NOT be an inside bar                                  |
//+------------------------------------------------------------------+
bool IsLarryWilliamsShortTermLow(string symbol, ENUM_TIMEFRAMES tf){

   //--- Price data for the three bars
   double high1 = iHigh(symbol, tf, 1);
   double low1  = iLow (symbol, tf, 1);

   double high2 = iHigh(symbol, tf, 2);
   double low2  = iLow (symbol, tf, 2);

   double high3 = iHigh(symbol, tf, 3);
   double low3  = iLow (symbol, tf, 3);

   //--- Condition 1: Bar 2 must be a swing low
   bool isSwingLow =
      (low2 < low1) &&
      (low2 < low3);

   if(!isSwingLow){
      return false;
   }
      
   //--- Condition 2: Bar 2 must NOT be an outside bar relative to bar 3
   bool isOutsideBar =
      (high2 > high3) &&
      (low2  < low3);

   if(isOutsideBar){
      return false;
   }

   //--- Condition 3: Bar 1 must NOT be an inside bar relative to bar 2
   bool isInsideBar =
      (high1 < high2) &&
      (low1  > low2);

   if(isInsideBar){
      return false;
   }

   //--- All conditions satisfied
   return true;
}

まず最初に、中央のバーが最安値を持っているかどうかを確認します。次に、その中央バーが直前のバーに対してアウトサイドバーになっていないかを検証します。最後に、直近のバーがスイングバーに対してインサイドバーになっていないかを確認します。

すべての条件が満たされた場合にのみ、関数はtrueを返します。

アクティブな実験の選択

テストを簡単にするために、ユーザーが実行する非ランダムテストモードを選択できる入力パラメータを導入します。この入力は先ほど定義した列挙型を使用しており、EAをチャートに適用した際にドロップダウン形式で表示されます。

//+------------------------------------------------------------------+
//| User input variables                                             |
//+------------------------------------------------------------------+

...

input group "Trade And Risk Management"
input ENUM_NON_RANDOM_TEST_MODE nonRandomTestMode = TEST_OPEN_TO_CLOSE_BIAS;

このアプローチにより、毎回コードを変更することなく、すべての実験に同一のEAを再利用できるようになります。

OnTick内でのパターン検出の検証

すべての検出ロジックが揃ったので、これらをティック関数の中で統合します。

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick(){

   ...
   
   //--- Run this block only when a new bar is detected on the selected timeframe
   if(IsNewBar(_Symbol, timeframe, lastBarOpenTime)){
      
      //--- Execute a buy trade when the Open-to-Close bias test mode is selected
      if(nonRandomTestMode == TEST_OPEN_TO_CLOSE_BIAS    ){
         Print("EA should open a long position");
      }
      
      //--- Enter a buy position to test bullish bias following a single down close
      if(nonRandomTestMode == TEST_AFTER_ONE_DOWN_CLOSE   ){
         if(IsConsecutiveBarCloseState(_Symbol, timeframe, 1, BAR_CLOSE_DOWN)){
            Print("Previous bearish bar");
         }
      }
      
      //--- Enter a buy position to test bullish bias following two consecutive down closes
      if(nonRandomTestMode == TEST_AFTER_TWO_DOWN_CLOSES  ){
         if(IsConsecutiveBarCloseState(_Symbol, timeframe, 2, BAR_CLOSE_DOWN)){
            Print("Two consecutive bearish bars");
         }
      }
      
      //--- Enter a buy position to test bullish bias following three consecutive down closes
      if(nonRandomTestMode == TEST_AFTER_THREE_DOWN_CLOSES){
         if(IsConsecutiveBarCloseState(_Symbol, timeframe, 3, BAR_CLOSE_DOWN)){
            Print("Three consecutive bearish bars");
         }
      }
      
      //--- Enter a buy position to test bullish bias following a single up close
      if(nonRandomTestMode == TEST_AFTER_ONE_UP_CLOSE     ){
         if(IsConsecutiveBarCloseState(_Symbol, timeframe, 1, BAR_CLOSE_UP)){
            Print("Previous bullish bar");
         }
      }
      
      //--- Enter a buy position to test bullish bias following two consecutive up closes
      if(nonRandomTestMode == TEST_AFTER_TWO_UP_CLOSES    ){
         if(IsConsecutiveBarCloseState(_Symbol, timeframe, 2, BAR_CLOSE_UP)){
            Print("Two consecutive bullish bars");
         }
      }
      
      //--- Enter a buy position to test bullish bias following three consecutive up closes
      if(nonRandomTestMode == TEST_AFTER_THREE_UP_CLOSES  ){
         if(IsConsecutiveBarCloseState(_Symbol, timeframe, 3, BAR_CLOSE_UP)){
            Print("Three consecutive bullish bars");
         }
      }
      
      //---  Enter a buy position when a Larry Williams-defined short-term low pattern is detected
      if(nonRandomTestMode == TEST_AFTER_SHORT_TERM_LOW   ){
         if(IsLarryWilliamsShortTermLow(_Symbol, timeframe)){
            Print("Short-term low");
         }
      }      
   }   
}

EAはまず、新しいバーが形成されたかどうかを確認します。形成されていない場合は、何も処理をおこないません。新しいバーが検出された場合のみ、選択されているテストモードに応じた評価を実行します。各ケースでは対応する検出関数を呼び出し、パターンが検出された際にはメッセージを出力します。

この段階では実際の取引はおこないません。出力されるメッセージは確認用のシグナルとして機能し、パターンが正しいタイミングかつ正しい条件で検出されているかを視覚的に検証するためのものです。

このステップは極めて重要です。実際の取引ロジックを追加する前に、パターン検出が正確に動作していることを保証する必要があります。誤ったロジックのまま進めてしまうと、その後のすべての結果の信頼性が失われてしまいます。

次のセクションでは、これらのprint文を実際の注文実行ロジックに置き換えます。その段階でEAは「観察」から「実験」へと移行し、非ランダムな挙動が実際の市場結果に変換可能かどうかを検証できるようになります。

この時点で、EAは非ランダム市場テストに必要なすべてのパターン検出能力を備えています。次のステップは、それらのシグナルを実際の市場アクションへと変換することです。ここからは観察から実験へと移行します。

ラリー・ウィリアムズの元のロジックとの整合性を保つため、このEAはロングポジションのみを扱います。各取引は新しいバーの開始時にエントリーし、そのバーの終了時にクローズします。ストップロスやテイクプロフィットは設定しません。目的は取引管理ではなく計測であり、特定の条件が出現した直後の価格挙動を観察することにあります。

この設計により、EAは以下の3つを確実に実行できる必要があります。第一に、すでにポジションが存在するかどうかを判断すること。第二に、そのポジションを安全にクローズできること。第三に、選択されたテスト条件が成立したときのみ新規ポジションを開くことです。

アクティブポジションの確認

新しいポジションを開く前に、EAはすでに自身のロングポジションが存在するかどうかを確認する必要があります。同一口座上には他のEAや手動取引によって開かれたポジションが存在する可能性があるため、このEAが開いたポジションを識別するためにマジックナンバーを使用します。

この処理のために、すべての保有ポジションを走査し、マジックナンバーが一致するものが存在するかを確認する関数を定義します。一致するポジションが存在すればtrueを返し、それ以外の場合はfalseを返します。

//+------------------------------------------------------------------+
//| To verify whether this EA currently has an active buy position.  |                                 |
//+------------------------------------------------------------------+
bool IsThereAnActiveBuyPosition(ulong magic){
   
   for(int i = PositionsTotal() - 1; i >= 0; i--){
      ulong ticket = PositionGetTicket(i);
      if(ticket == 0){
         Print("Error while fetching position ticket ", _LastError);
         continue;
      }else{
         if(PositionGetInteger(POSITION_MAGIC) == magic && PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY){
            return true;
         }
      }
   }
   
   return false;
}

このチェックにより、EAは規律を保った状態で動作します。どの時点においても、各テストにつきアクティブポジションは1つだけという制約が維持されます。これにより結果の整合性が保たれ、重複したポジションによって統計が歪むことを防ぐことができます。

既存ポジションのクローズ

新しいバーが形成された時点で、直前のバーは正式に確定します。これは、直前のトレードに対するテストウィンドウが終了したことを意味します。その時点でポジションがまだ残っている場合は、直ちにクローズする必要があります。

これを実現するために、すべてのオープンポジションを走査し、マジックナンバーに基づいてこのEAが保有しているポジションを特定し、それらをクローズする関数を定義します。この関数は利益や損失の管理は一切おこないません。ただ市場価格で即座にポジションを終了させるだけのシンプルな処理です。

//+------------------------------------------------------------------+
//| To close all position with a specified magic number              |   
//+------------------------------------------------------------------+
void ClosePositionsByMagic(ulong magic) {
    
    for (int i = PositionsTotal() - 1; i >= 0; i--) {
        ulong ticket = PositionGetTicket(i);
        if (PositionSelectByTicket(ticket)) {
            if (PositionGetInteger(POSITION_MAGIC) == magic) {
                ulong positionType = PositionGetInteger(POSITION_TYPE);
                double volume = PositionGetDouble(POSITION_VOLUME);
                if (positionType == POSITION_TYPE_BUY) {
                    Trade.PositionClose(ticket);
                } else if (positionType == POSITION_TYPE_SELL) {
                    Trade.PositionClose(ticket);
                }
            }
        }
    }    
}

このアプローチにより、すべてのテストが同一のルールに従うようになります。つまり「1本のバーでエントリーし、1本のバーでクローズする」というシンプルな構造です。それ以上でも以下でもありません。

新規買いポジションのオープン

ポジション制御が整ったので、次にシンプルな成行買い注文を出す関数を定義します。この関数は2つの入力を受け取ります。1つ目は建値で、これはEA内で既に管理している現在のAsk価格です。2つ目はポジションサイズです。

//+------------------------------------------------------------------+
//| Function to open a market buy position                           |
//+------------------------------------------------------------------+
bool OpenBuy(double entryPrice, double lotSize){
   if(!Trade.Buy(NormalizeDouble(lotSize, 2), _Symbol, entryPrice)){
      Print("Error while executing a market buy order: ", GetLastError());
      Print(Trade.ResultRetcode());
      Print(Trade.ResultComment());
      return false;
   }
   return true;
}

何らかの理由で取引が失敗した場合は、その原因を特定するための診断情報を出力します。一方で正常に注文が通った場合には、trueを返してエントリーが成功したことを明示します。

この仕組みを柔軟にするため、、ロットサイズは入力パラメータとしてユーザーが指定できるようにします。これにより、EAはさまざまな口座サイズに対応しつつ、実験ロジック自体は一貫して維持できるようになります。

//+------------------------------------------------------------------+
//| User input variables                                             |
//+------------------------------------------------------------------+

...

input double positionSize                         = 0.01;

OnTick内でのロジック実行

これですべてが繋がります。

新しいバーが検出されるたびに、EAは以下の処理を順番に実行します。まず、このEAによって開かれている既存のポジションをすべてクローズします。これで前回のテストサイクルが終わります。その後、プラットフォームがクローズ処理を正しく反映するように、短い待機を入れます。

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick(){
   
   ...
   
   //--- Run this block only when a new bar is detected on the selected timeframe
   if(IsNewBar(_Symbol, timeframe, lastBarOpenTime)){
      
      //--- Close any existing buy positions for this EA before opening a new one
      if(IsThereAnActiveBuyPosition(magicNumber)){
         ClosePositionsByMagic(magicNumber);
         Sleep(100);
      }      

      ...     
 
   }
}

次に、EAは現在選択されているテストモードを確認します。その選択に基づいて対応する条件を評価し、条件が満たされた場合は新しいバーの開始時点で即座に買いポジションを開きます。

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick(){

   ...
   
   //--- Run this block only when a new bar is detected on the selected timeframe
   if(IsNewBar(_Symbol, timeframe, lastBarOpenTime)){
      
      ...
      
      //--- Execute a buy trade when the Open-to-Close bias test mode is selected
      if(nonRandomTestMode == TEST_OPEN_TO_CLOSE_BIAS    ){
         OpenBuy(askPrice, positionSize);
      }
      
      //--- Enter a buy position to test bullish bias following a single down close
      if(nonRandomTestMode == TEST_AFTER_ONE_DOWN_CLOSE   ){
         if(IsConsecutiveBarCloseState(_Symbol, timeframe, 1, BAR_CLOSE_DOWN)){
            OpenBuy(askPrice, positionSize);
         }
      }
      
      //--- Enter a buy position to test bullish bias following two consecutive down closes
      if(nonRandomTestMode == TEST_AFTER_TWO_DOWN_CLOSES  ){
         if(IsConsecutiveBarCloseState(_Symbol, timeframe, 2, BAR_CLOSE_DOWN)){
            OpenBuy(askPrice, positionSize);
         }
      }
      
      //--- Enter a buy position to test bullish bias following three consecutive down closes
      if(nonRandomTestMode == TEST_AFTER_THREE_DOWN_CLOSES){
         if(IsConsecutiveBarCloseState(_Symbol, timeframe, 3, BAR_CLOSE_DOWN)){
            OpenBuy(askPrice, positionSize);
         }
      }
      
      //--- Enter a buy position to test bullish bias following a single up close
      if(nonRandomTestMode == TEST_AFTER_ONE_UP_CLOSE     ){
         if(IsConsecutiveBarCloseState(_Symbol, timeframe, 1, BAR_CLOSE_UP)){
            OpenBuy(askPrice, positionSize);
         }
      }
      
      //--- Enter a buy position to test bullish bias following two consecutive up closes
      if(nonRandomTestMode == TEST_AFTER_TWO_UP_CLOSES    ){
         if(IsConsecutiveBarCloseState(_Symbol, timeframe, 2, BAR_CLOSE_UP)){
            OpenBuy(askPrice, positionSize);
         }
      }
      
      //--- Enter a buy position to test bullish bias following three consecutive up closes
      if(nonRandomTestMode == TEST_AFTER_THREE_UP_CLOSES  ){
         if(IsConsecutiveBarCloseState(_Symbol, timeframe, 3, BAR_CLOSE_UP)){
            OpenBuy(askPrice, positionSize);
         }
      }
      
      //---  Enter a buy position when a Larry Williams-defined short-term low pattern is detected
      if(nonRandomTestMode == TEST_AFTER_SHORT_TERM_LOW   ){
         if(IsLarryWilliamsShortTermLow(_Symbol, timeframe)){
            OpenBuy(askPrice, positionSize);
         }
      }      
   }
}

ここでの変更点は以前の検証フェーズと比べてシンプルですが非常に重要です。すべてのPrint出力を実際の取引実行処理に置き換えています。ただし、パターン検出ロジック自体は変更していません。これにより、これまでおこなってきた検証が正しかったことが確認できます。

この時点でEAは完全に動作する状態になっています。パターンの検出、エントリー、1バー後のクローズ、そしてこのサイクルの繰り返しが、すべてのテストケースで一貫して実行されます。

明確な視覚テストのためのチャート設定

このEAは、視覚的および統計的な実験を目的としているため、テスト実行中にチャート上で何が起きているかを明確に把握できることが重要です。陽線と陰線の判別、エントリーとエグジットのタイミングの識別、そして観察の妨げになる不要な視覚的ノイズの排除が求められます。

そのため、EAがチャートに適用された直後にチャートの表示を整える小さなヘルパー関数を導入します。

//+------------------------------------------------------------------+
//| This function configures the chart's appearance.                 |
//+------------------------------------------------------------------+
bool ConfigureChartAppearance()
{
   if(!ChartSetInteger(0, CHART_COLOR_BACKGROUND, clrWhite)){
      Print("Error while setting chart background, ", GetLastError());
      return false;
   }
   
   if(!ChartSetInteger(0, CHART_SHOW_GRID, false)){
      Print("Error while setting chart grid, ", GetLastError());
      return false;
   }
   
   if(!ChartSetInteger(0, CHART_MODE, CHART_CANDLES)){
      Print("Error while setting chart mode, ", GetLastError());
      return false;
   }

   if(!ChartSetInteger(0, CHART_COLOR_FOREGROUND, clrBlack)){
      Print("Error while setting chart foreground, ", GetLastError());
      return false;
   }

   if(!ChartSetInteger(0, CHART_COLOR_CANDLE_BULL, clrSeaGreen)){
      Print("Error while setting bullish candles color, ", GetLastError());
      return false;
   }
      
   if(!ChartSetInteger(0, CHART_COLOR_CANDLE_BEAR, clrBlack)){
      Print("Error while setting bearish candles color, ", GetLastError());
      return false;
   }
   
   if(!ChartSetInteger(0, CHART_COLOR_CHART_UP, clrSeaGreen)){
      Print("Error while setting bearish candles color, ", GetLastError());
      return false;
   }
   
   if(!ChartSetInteger(0, CHART_COLOR_CHART_DOWN, clrBlack)){
      Print("Error while setting bearish candles color, ", GetLastError());
      return false;
   }
   
   return true;
}

実験においては、視覚的フィードバックが重要な役割を果たします。最終的な結論はデータと統計に基づいて導かれますが、各テスト条件における価格の挙動を視覚的に確認することで、仮定の妥当性を検証したり、異常値を早期に発見したりすることができます。

この関数によって適用される設定は、主に3つの目的を満たします。

  • 視認性の向上:白背景と黒系の前景要素を採用することで、最大限のコントラストを確保します。
  • 方向性の強調:陽線と陰線を直感的かつ明確に識別できるよう、モダンな配色を用いています。
  • 集中の維持:グリッドラインを非表示にすることで、プライスアクションと実際の取引に意識を集中できるようにします。

このチャート設定関数は、OnInit関数の中で呼び出されます。

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit(){

   ...
   
   //--- To configure the chart's appearance
   if(!ConfigureChartAppearance()){
      Print("Error while configuring chart appearance", GetLastError());
      return INIT_FAILED;
   }

   return(INIT_SUCCEEDED);
}

これにより、EAが起動した瞬間にチャート設定が即座に適用されるようになります。何らかの理由でプラットフォーム側が設定を適用できなかった場合は、その問題を報告し、初期化処理を中断します。

この追加によって、テスト用EAの開発は完了となります。次のセクションでは、これらの実験をさまざまな市場で実際に実行します。結果を観察し、アセットクラスごとの挙動を比較しながら、この一連の検証の出発点となった「市場は本当にランダムなのか、それとも測定可能な痕跡を残しているのか」という問いに、最終的に答えていきます。


テスト実行と市場動向に関する観察

ここまでの段階では、主にアイデアの整理と準備に焦点を当ててきました。ラリー・ウィリアムズが主張した「価格挙動は完全にはランダムではない」という考え方を検証し、その仮説を明確なテストケースへと落とし込み、それらを一貫して実行できるEAを構築しました。

このEAは意図的にシンプルに設計されています。最適化はされず、ストップロスやテイクプロフィットも設定していません。また動的なポジション管理もおこないません。各ポジションは新しいバーの開始時にエントリーし、その同じバーの終了時にクローズされます。常に同時保有は1トレードのみで、各テストは互いに独立して実行されます。

ここでの目的は、利益を出す取引システムを構築することでも、エントリーやエグジット、リスク管理を最適化することでもありません。よりシンプルで本質的な、

特定の価格挙動はランダム性が想定する頻度よりも高い頻度で発生するのか、そしてそれは異なる市場でも一貫して観測できるのか」という問いに答えることです。

テスト環境

実験結果の一貫性、比較可能性、再現性を確保するため、すべてのテストは同一の固定条件下で実施されます。市場ごとにパラメータ調整はおこなわず、どの段階でも最適化は実行しません。

すべての実験は日足(D1)で実行されます。より高い時間足を使用することで、マーケットマイクロストラクチャーによるノイズを低減し、各取引が短期的な価格変動ではなく1日の取引セッション全体を反映するようにしています。

テスト期間はすべての銘柄に対して2025年01月01日から2025年11月30日までに統一しています。この同一期間設定により、時間的なバイアスを排除しながら市場間の比較が可能になります。 

ロットサイズはすべての実験を通して固定(0.1)とします。EAは常に1ポジションのみを保有し、各取引は必ず1バーで終了します。ピラミッディング、スケーリング、ポジションの重複は一切おこないません。これにより、すべての結果は独立したものとなり、評価対象となる条件に直接帰属する形になります。

初期資金はすべてのテストで10,000USDに統一し、レバレッジは1:500を使用します。取引ルール、執行ロジック、エグジットの挙動はすべての市場およびテストケースで共通です。この統一された設定は、市場の特性そのものを抽出するために不可欠です。

再現を容易にするため、設定ファイルconfigurations.iniを用意しています。MetaTrader5のストラテジーテスターでEAを実行する際、[設定]タブから[読み込み]を選択し、この.iniファイルを読み込むことで、同一のテスト条件を適用できます。これにより、読者は同一環境で検証を再現できます。

テスト対象金融商品

非ランダムな市場挙動が市場タイプごとにどのように現れるかを評価するため、以下の銘柄を対象にテストを実施します。

  • XAUUSD:コモディティ(ゴールド)
  • BTCUSD:仮想通貨(ビットコイン)
  • GBP/USD:通貨ペア(英ポンド対米ドル)
  • NAS100:株価指数(Nasdaq 100)

これらは流動性、参加者構造、取引時間、ニュースやセンチメントへの感応度といった点で大きく異なる、意図的に分散されたアセットクラスから選定されています。

コモディティ、仮想通貨、通貨、株価指数に同一のテストロジックを適用することで、検出された非ランダム性が特定市場に依存するものなのか、それともアセットクラスを横断して共通して現れるのかを判断できます。

この比較は、市場構造由来の効果と、個別商品固有の特性を切り分けるために不可欠です。これにより、後続のテスト結果を正しく解釈するための基盤が形成されます。それぞれの銘柄は、同一のルール下で独立して評価されることになります。

実験の実施と結果の記録

データの整合性を保ち、結果の混在を防ぐため、各テストケースは独立して実行されます。それぞれの結果は専用のテーブルに分離して記録し、可読性を最大限に確保します。各実験について、以下の項目を記録します。

  • 銘柄:対象市場(XAUUSD、BTCUSD、GBP/USD、NAS100)
  • 取引回数:当該シナリオで発生した総取引数
  • 勝率(%):利益確定で終了した取引の割合
  • 純結果:トータルでプラスかマイナスかの判定

この構造化されたアプローチにより、市場間およびシナリオ間の比較が容易になり、データの解析が明確かつ一貫した形でおこなえるようになります。

実験1:オープンからクローズへのバイアス

このテストは、ローソク足が始値よりも高く終わる(陽線になる)傾向があるか、それとも低く終わる(陰線になる)傾向があるかを検証するものです。これは最も基本的なシナリオであり、対象期間における各市場の方向性バイアスを把握するための出発点になります。


取引件数 勝率(%) 純結果
XAUUSD 236 56.36 +
BTCUSD 331 51.06 -
GBP/USD 238 45.38 -
NASDAQ 236 55.93 +

実験2:1本の陰線後の強気バイアス

このテストでは、単一の陰線の後に陽線が出現しやすいかどうかを検証します。つまり、1本の下落後に市場が反転しやすい傾向があるかを確認するものです。陽線が陰線の後に続くかどうかをチェックします。


取引件数 勝率(%) 純結果
XAUUSD 101 62.38 +
BTCUSD 161 53.42 +
GBP/USD 106 46.23 -
NASDAQ 106 62.26 +

実験3:2本連続した陰線の後における強気バイアス

このテストでは、2本連続で陰線が出現した場合に、その後の陽線発生確率が上昇するかどうかを検証します。


取引件数 勝率(%) 純結果
XAUUSD 35 57.14 +
BTCUSD 73 50.68 +
GBP/USD 47 44.68 -
NASDAQ 40 57.50 -

実験4:3本連続した陰線の後における強気バイアス

このテストはさらに拡張し、陰線が3本連続した後に反発確率がどのように変化するかを検証します。より強い下落トレンドが反転確率に影響するかを確認します。


取引件数 勝率(%) 純結果
XAUUSD 14 71.43 +
BTCUSD 35 60.00 +
GBP/USD 20 35.00 -
NASDAQ 19 52.63 +

実験5:1本の陽線後の強気バイアス

このテストでは、陽線の後に再び陽線が続く傾向があるかを検証し、短期的なモメンタムの存在を確認します。


取引件数 勝率(%) 純結果
XAUUSD 135 51.85 +
BTCUSD 170 48.82 -
GBP/USD 131 45.04 -
NASDAQ 130 50.77 -

実験6:2本連続した陽線の後における強気バイアス

このテストでは、陽線が2本連続した後に勢いが継続するか、それとも失速するかを検証します。


取引件数 勝率(%) 純結果
XAUUSD 69 53.62 +
BTCUSD 82 43.90 -
GBP/USD 72 41.67 -
NASDAQ 64 54.69 -

実験7:3本連続した陽線の後における強気バイアス

このテストでは、陽線が3本連続した後にさらに続伸する確率を検証し、強いモメンタムの持続性を評価します。


取引件数 勝率(%) 純結果
XAUUSD 37 37.84 -
BTCUSD 34 52.94 +
GBP/USD 37 40.54 -
NASDAQ 34 50.00 -

実験8:ラリー・ウィリアムズの短期安値後の強気バイアス

このテストでは、ラリー・ウィリアムズが定義した短期スイングロー(短期安値)の後に陽線が出現しやすいかを検証します。プライスアクションとスイング構造を組み合わせた、より構造的な予測可能性の評価です。


取引件数 勝率(%) 純結果
XAUUSD 21 61.90 +
BTCUSD 26 53.85 +
GBP/USD 23 65.22 -
NASDAQ 29 62.07 +

非ランダムな市場挙動の証拠

セクションでは、8つの実験結果をまとめ、最も重要な観点である「勝率の観測値」に焦点を当てます。市場が完全にランダムであれば、ローソク足の上昇や下落は前後の条件に関係なく、およそ50%付近に均等に分布するはずです。しかし実際の結果はそうなっていません。

複数の銘柄と複数のテストケースにおいて、価格挙動はそれ以前に何が起きたかによって変化しています。これは、各ローソク足が記憶を持たない独立した事象であるという仮定に対して、明確な疑問を投げかけます。

1. オープンからクローズへのバイアスは一様ではない

オープンからクローズのテストは基準となる指標です。市場がランダムであれば、各銘柄の結果は50%付近に収束するはずです。しかし実際にはばらつきが見られます。ゴールドNASDAQは55%以上の勝率を示し、いずれも収益はプラスになっています。ビットコインはランダムに近い水準で、結果として損失側に傾いています。GBP/USDは50%を大きく下回り、パフォーマンスも弱いです。これは重要なことを示唆しています。最も基本的な価格挙動でさえ市場ごとに異なっており、構造や参加者の特性が影響しているのです。

2. 下落後の動きにはより強い予測性が見られる

ランダム性に対する最も強い反証の一つは、陰線の後に現れます。1本の陰線の後では、ゴールドNasdaqが60%以上の勝率を示します。ビットコインも50%を超え、収益はプラスになります。GBP/USDは依然として弱い結果です。

陰線が2本連続した後でも、ゴールドビットコインは50%以上の水準を維持しています。NASDAQは収益性こそ失いますが、勝率は依然として高いままです。

陰線が3本連続した後では、さらに明確になります。ゴールドは70%を超え、ビットコインも強い水準を維持します。NASDAQも50%以上を維持しています。唯一GBP/USDのみが一貫して弱い結果です。

この流れは重要です。ランダムなシステムであれば、過去の陰線の本数が増えても次の陽線の確率が上昇することはありません。しかしここでは、特にゴールドビットコインが実際に上昇しています。

これはノイズではなく、条件付きの挙動です。

3. 上昇後の挙動は大きく異なる

同じ分析を陽線の連続に適用すると、結果はより弱く不安定になります。

1本の陽線の後では、多くの銘柄が50%を下回ります。陽線が2本連続した後では、ゴールドのみがわずかな優位性を維持します。3本連続ではさらに結果が悪化します。

この非対称性は重要です。市場は上昇よりも下落の後により強く反応する傾向があります。つまり、上昇は続きにくく、下落後の反発の方が構造的に強い傾向があります。

ランダムなシステムでは、このような非対称性は発生しません。

4. 短期安値は構造的な挙動を示す

短期的な安値のテストは、最も一貫性のある結果の一つです。

ゴールドビットコインNASDAQははいずれも60%以上の勝率を示し、収益もプラスです。GBP/USDも高い勝率を示しますが、収益にはつながっていません。

これは、シンプルな構造パターンが意味を持つことを示しています。明確に定義された短期安値は、その後のローソク足の確率を変化させます。

これは、ラリー・ウィリアムズ氏が主張した「価格は単なるランダムな推移ではない」という考えを支持する結果です。

5. アセットクラスごとの違いが主張を裏付ける

結果は銘柄ごとに異なりますが、その違い自体が重要です。

ゴールドは一貫して最も強い上昇バイアスを示します。ビットコインは下落後に条件付きの強さを見せます。NASDAQは短期的な弱さや構造に反応します。一方でGBP/USDはこのルールでは一貫性が弱いです。

市場が完全にランダムであれば、アセットによる違いは現れないはずです。しかし実際には明確に存在しています。

最終的な考察

これらの実験は確実性を主張するものではありませんし、予測を保証するものでもありません。示しているのは「確率の歪み」です。

特定の条件下では、勝率が50%から明確に乖離します。この事実だけでも、純粋なランダム性の仮説は成立しにくくなります。

市場は完全に予測可能ではありません。しかし、完全に記憶を持たないわけでもありません。価格は過去の挙動に対して統計的に反応しています。これがエッジの本質です。

これはまさにラリー・ウィリアムズが提案した考え方であり、現在では自動化とデータによってそれを実際に観測できるようになっています。


結論

本記事は、ひとつのシンプルで重要な、「市場の動きは完全にランダムなのか、それとも過去の価格アクションが次の値動きに影響を与えているのか」という問いに答えることを目的としてきました。

制御されたテスト、自動化、そして実際の市場データを用いた検証を通じて、価格挙動は一様に分布していないことが確認できました。特定の条件下では、確率が明確に50%から乖離するケースが繰り返し観測されました。この事実だけでも、市場が完全にランダムであるという仮説は成立しにくいと言えます。

ただし結果は、確実な予測や必勝性を示すものではありませんでした。むしろ示されたのは、より実務的な性質です。市場は下落後と上昇後で異なる反応を示し、特定の構造は偶然以上の頻度で繰り返され、また銘柄ごとにその傾向の強さも異なります。これらは偶然ではなく、測定可能な挙動です。

結果と同じくらい重要なのは、その結果に至るまでのプロセスです。本記事では、テスト用EAの設計から実装までを段階的に解説しました。このEAは意図的にシンプルで透明性が高く、モジュール化されています。利益最適化ではなく、仮説検証そのものを目的として設計されています。この姿勢は、実際のエッジを探す上で不可欠です。

この基盤により、読者は再現可能な検証フレームワークを手にすることになります。ロジックの変更、新しい条件の追加、時間足の変更、あるいは別の価格パターンの検証なども容易におこなうことができます。同じ手法は、研究ツール、売買システム、戦略モジュールへと応用可能であり、推測ではなく実証に基づいた開発が可能になります。

この結論は、ラリー・ウィリアムズの基本的な考え方を裏付けるものです。市場は完全に予測可能ではありませんが、同時に記憶を持たないわけでもありません。この理解に立つことで、システム開発は初めて意味のあるものになります。

次回以降では、これらの結果をさらに発展させ、非ランダム性を捉えることを目的とした取引システムの構築へと進んでいきます。これにより、より構造化され、再現可能な戦略設計を目指します。

参考のため、本記事で使用した再現用ファイルを以下に添付します。実験の再現や検証に活用できます。

  ファイル名 説明
1. larryWilliamsNonRandomMarketBehaviorTester.mq5 本記事で作成したEAの完全ソースコード
2. configurations.ini すべてのストラテジーテスター実行で使用される設定ファイル

MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/20510

最後のコメント | ディスカッションに移動 (1)
Hezekiah Bukola Oyetunde
Hezekiah Bukola Oyetunde | 13 1月 2026 において 14:31
ソースコードを 共有してくれてありがとう。ENUM_NON_RANDOM_TEST_MODE をどのように構成して実験を分離した状態に保つか、とても気に入っています。結果に'スプレッド'または'手数料'フィルタを追加することは検討されましたか?これらの統計的バイアスが、現実世界の取引コストが考慮された後も利益をもたらすかどうかを確認するのは興味深いことです。
MQL5でのAI搭載取引システムの構築(第8回):アニメーション、タイミング指標、応答管理ツールによるUIの改善 MQL5でのAI搭載取引システムの構築(第8回):アニメーション、タイミング指標、応答管理ツールによるUIの改善
本記事では、MQL5におけるAI駆動取引システムを、ユーザーインターフェースの改善によって強化します。具体的には、リクエストの準備フェーズおよび思考フェーズにおけるローディングアニメーションの追加や、レスポンスに表示される処理時間(タイミングメトリクス)による応答の向上などを実装します。さらに、AIへの再クエリを行うための再生成ボタンや、最新の応答をファイルとして保存できるエクスポート機能などのレスポンス管理ツールを追加し、操作性を向上させます。
MQL5でカスタムインジケーターを作成する(第3回):扇形と円形によるマルチゲージの強化 MQL5でカスタムインジケーターを作成する(第3回):扇形と円形によるマルチゲージの強化
本記事では、MQL5のゲージ型インジケーターを拡張し、複数のオシレーターに対応できるようにします。列挙型を使うことで、単体表示だけでなく複合表示もユーザーが選択できるようになります。また、基盤となるゲージフレームワークを拡張し、扇形と円形の2つのスタイルを派生クラスとして実装します。円弧、直線、多角形を組み合わせた枠(ケース)の描画により、見た目もより洗練されたものになります。
エラー 146 (「トレードコンテキスト ビジー」) と、その対処方法 エラー 146 (「トレードコンテキスト ビジー」) と、その対処方法
この記事では、MT4において複数のEAの衝突をさける方法を扱います。ターミナルの操作、MQL4の基本的な使い方がわかる人にとって、役に立つでしょう。
MQL5で他の言語の実用的なモジュールを実装する(第6回):MQL5におけるPython風ファイルI/O操作 MQL5で他の言語の実用的なモジュールを実装する(第6回):MQL5におけるPython風ファイルI/O操作
複雑なMQL5ファイル操作を簡素化するために、読み書きを容易にするPythonスタイルのインターフェースを構築する方法を紹介します。カスタム関数とクラスを用いて、Pythonの直感的なファイル処理パターンを再現する方法を解説します。その結果、MQL5のファイルI/Oにおいて、よりクリーンで信頼性の高いアプローチが実現しました。