English Русский
preview
初心者からエキスパートへ:隠れフィボナッチリトレースメントレベルの謎を解く

初心者からエキスパートへ:隠れフィボナッチリトレースメントレベルの謎を解く

MetaTrader 5トレーディングシステム |
25 0
Clemence Benjamin
Clemence Benjamin

内容

  1. はじめに
  2. 実装戦略
  3. テストと結果
  4. 結論
  5. 添付ファイル


はじめに

フィボナッチリトレースメントレベルは広く利用されていますが、実際の市場では、価格が中間的、あるいは繰り返し出現する非標準的な比率に反応する場面も少なくありません。本記事では、体系的かつデータ駆動型の手法を用いて、こうしたレベルを発見できるのか。さらに、それらが偶発的ではなく繰り返し出現することを検証し、十分に堅牢であれば、取引ツールや戦略における正式なレベルとして組み込むことが可能なのかという問いを提示します。

 従来のフィボナッチレベルが不完全な理由

23.6%、38.2%、50%、61.8%、78.6%といった古典的なフィボナッチ比率は、フィボナッチ数列および黄金比に基づいて導出されています。これらは市場参加者の間で広く受け入れられている一方、実際の取引経験からは、市場がこの標準セットに含まれない中間的あるいは代替的なリトレースメントレベルに反応するケースがしばしば観察されます。この事実は、従来のフィボナッチ枠組みだけでは、市場の価格挙動を十分に捉え切れていない可能性を示唆しています。

隠れた市場反応の観察

経験的には、価格が50%と61.8%の間、あるいはその他の非標準的なポイント付近で、停滞、反転、あるいは加速といった挙動を示すことがあります。本記事では、これらを仮に「隠れたフィボナッチレベル」と呼ぶことにします。しかし問題は、こうした観察の多くがチャートの目視確認に基づく主観的判断であり、通貨ペアや時間足が変わると一貫性を欠く可能性がある点です。

逸話的証拠と統計的検証の間にある課題

視覚的なパターン認識は、確証バイアスの影響を受けやすい手法です。価格が想定したレベルで反応した事例は強く記憶される一方、反応しなかったケースは忘れられがちです。体系的な検証がおこなわれない限り、こうした「隠れたレベル」は推測の域を出ません。とはいえ、逸話的な観察は無意味ではありません。それは、トレーダーが依拠してきた理論を、より精度の高い形で検証するための出発点となり得ます。真の課題は、ランダムなノイズと、実際に存在する構造的傾向とを明確に区別することにあります。

未知のリトレースメントレベル

図1:  非標準フィボナッチリトレースメントレベルの例

図1はMetaTrader 5上に表示したEUR/USDのM15チャートのスクリーンショットです。スイングA–Bをフィボナッチツールで計測し、標準的なリトレースメントレベルを青色で描画しラベル付けしています。注目すべき点は、価格が古典的な比率上で正確に止まっているわけではなく、38.2%と50%の間に位置する中間ポイントで、明確な価格反応が見られることです。これらの中間的な反応を、赤い破線で強調し、「?」というラベルを付けました。これは、正体不明ながらも、潜在的に意味を持つレベルであることを示しています。

これらの中間反応こそが、本記事で解明しようとする対象です。理論的には、リトレースメント値を正確に計算して描画すること自体はプログラムで可能です。しかし、MetaTrader 5に標準搭載されているフィボナッチツールは、教科書的な比率しか描画できないため、手作業による目視確認には限界があります。そこで必要となるのが、次の2段階のアプローチです。まず、正規化された大量のリトレースメント観測データを収集し、統計的手法によってフィルタリングをおこない、繰り返し市場に尊重される中間バンドを特定します。次に、検証によって有効性が確認されたレベルを、信頼度スコアとともにMetaTrader 5のフィボナッチツール上に描画するアルゴリズムを実装します。


実装戦略

バー範囲およびスイング検出手法

プロジェクトでは、各バーの高値と安値を単純な「スイング」範囲として扱います。ノイズを抑え、有意な値動きに注目するため、最小値幅フィルタ(たとえばATR倍率)を適用します。このバー範囲の代理手法は意図的に軽量化されています。実装が簡単で、大量の過去データに対して高速に処理でき、統計的発見に適した、確定済みバーごとに1つの観測値を得られるデータセットを生成します。

将来的には、複数バーを考慮したより精緻なスイング検出を導入し、より長期または構造的に重要なスイングを捉える予定です。現段階でバー範囲を用いる理由は明確で、エンジニアリング負荷を最小限に抑えつつ、大量サンプルを迅速に収集し、統計手法を検証したうえで改善を繰り返すことができます。この手法の妥当性は、実務的な市場観察に基づいています。ローソク足はしばしば前の値動きの一部をリトレースするため、正規化されたリトレースメント分布において測定可能なピークが現れます。図2はバー内リトレースメントの挙動を示しており、この手法の動機となっています。

ローソク足バー範囲リトレースメント

図2:ローソク足バー範囲リトレースメント

 データの収集と準備

まず、複数銘柄(EURUSD、GBPUSD、S&P500、XAUUSDなど)および時間足(M15、H1、H4、D1)にわたる過去のOHLCVデータを収集します。できる限り長期のサンプル(理想的には3〜5年分)を用意することで、トレンド期からレンジ期まで多様な市場局面を反映できます。データは使用前にクリーニングし、欠損バーや異常スパイク、ギャップを除外することで分析バイアスを防ぎます。こうして構築された基盤により、リトレースメントデータセットが市場構造を正しく反映するものとなります。

このクリーンな基盤をもとに、各確定済みバーを独立したスイング範囲として扱います。高値と安値を参照境界とし、直後のバーをリトレースメント率を確認するテストバーとして分析します。ノイズを抑えるため、ATRベースの閾値より小さい値幅は除外します。リトレースメント率は0〜100のスケールで正規化され、銘柄、時間足、方向、タイムスタンプ、出来高などのメタデータとともに記録されます。すべてのシーケンスが有効というわけではなく、テストバーが参照範囲外で始まる場合や参照方向と逆に確定する場合は収集対象から除外します。また、連続するインサイドバーや包み足、ギャップなどは拡張ウィンドウロジックでまとめ、通常のリトレースメントとして誤認されないようにします。こうした検証とメタデータの収集により、生成されるデータセットはクリーンで再現可能となり、古典的フィボナッチ比率の統計探索にも使用できます。

データ収集スクリプトの開発

次の段階では、MQL5でデータ収集用スクリプトを作成し、リトレースメントCSVデータファイルを生成します。このファイルはJupyter Notebookで解析され、パターンの探索やインサイトの抽出に用いられます。

初期化:制御パラメータとヘルパー関数の設定

スクリプトの冒頭で、解析対象バー数、ATR設定、先読み制限、出力フラグなどの制御パラメータを宣言します。スクリプトが開始されると、これらの入力値を読み込み、コードの可読性を保つために、2つの小さなヘルパー関数(フォーマッタと時間足から文字列への変換)を準備します。その後、銘柄と時間足を含む出力ファイル名を作成し、CSVファイルを開いて書き込みをおこないます。もしファイルを開けない場合は処理を停止し、エラーを報告します。これにより、実行が実際に開始されたかどうかを常に確認できます。

//--- input parameters
input int      BarsToProcess     = 20000;   // how many candidate reference bars to process
input int      StartShift        = 1;       // skip most recent N bars
input int      ATR_Period        = 14;      // ATR period
input double   ATR_Multiplier    = 0.3;     // min ATR filter
input int      MaxLookahead      = 3;       // extended-window lookahead
input bool     UsePerfectSetup   = true;    // require perfect setups
input bool     OutputOnlySameDir = false;   // require same-dir support
input bool     IncludeInvalidRows= false;   // output invalids
input string   OutFilePrefix     = "CandleRangeData"; // file prefix

//--- output file
string OutFileName = StringFormat("%s_%s_%s.csv", OutFilePrefix, _Symbol, PeriodToString(_Period));
int fh = FileOpen(OutFileName, FILE_WRITE|FILE_CSV|FILE_ANSI);
if(fh == INVALID_HANDLE) {
   PrintFormat("Error opening file %s", OutFileName);
   return;
}

ボラティリティ基準:ATRハンドルの作成

バーを走査する前に、ATRインジケーターのハンドルを作成します。各候補基準バーについて、スクリプトはそのバーのATR値を取得し、このATR値をボラティリティの指標として使用します。バーの値幅がATR × ATR_Multiplierより小さい場合、そのバーはノイズとみなし、スキップします。 小さな値幅を除外することで、ランダムな小さなバーによって誤ったリトレースメントが生成されるのを防ぎ、シグナルの品質を向上させます。

//--- prepare ATR handle
int atr_handle = iATR(_Symbol, _Period, ATR_Period);
if(atr_handle == INVALID_HANDLE) {
   Print("ATR handle invalid");
   FileClose(fh);
   return;
}

メイン走査ループ:履歴データの順次処理

スクリプトは、StartShiftから過去方向に確定済みバーを順に走査し、利用可能な履歴の終わりまで、あるいはBarsToProcess行の出力に達するまで処理を続けます。各反復(候補基準バーごと)で、基準バーの高値、安値、始値および終値を取得し、バーの値幅を計算したうえでATRフィルタを適用します。バーが条件を満たした場合、スクリプトは基準バーに続くテストバーの解析に進みます。このループは、生の過去データを候補リトレースメントイベントに変換するエンジンとして機能します。これにより、不適切なケースを早期に除外して、後続の統計分析の精度を向上させます。

int bars = iBars(_Symbol,_Period);
double atr_buf[];

for(int r = StartShift; r <= bars - 1; r++) {
   double RefTop   = iHigh(_Symbol,_Period,r);
   double RefBot   = iLow(_Symbol,_Period,r);
   double RefOpen  = iOpen(_Symbol,_Period,r);
   double RefClose = iClose(_Symbol,_Period,r);
   double Range    = RefTop - RefBot;
   
   // ATR filter
   if(CopyBuffer(atr_handle,0,r,1,atr_buf) <= 0) continue;
   if(Range < atr_buf[0] * ATR_Multiplier) continue;
   
   // process this reference...
}

基準バー方向の判定:スイングを強気、弱気、中立にラベル付け

基準バーについて、終値が始値より高ければ強気、低ければ弱気、同値であれば中立と判定します。この方向が、テストバーでどの極値をリトレースメントとして扱うかを決定します(強気参照の場合は安値を、弱気参照の場合は高値を対象)。リトレースメント率の正規化は、基準バーが上昇か下降かによって変わります。

string RefDir;
if(RefClose > RefOpen)      RefDir = "Bull";
else if(RefClose < RefOpen) RefDir = "Bear";
else                        RefDir = "Neutral";

初期テストバーとパーフェクトセットアップの検証:単純で明確なケースをまず確認

スクリプトは基準バーの直後のバー(テストバー)を読み込みます。このバーは通常リトレースメントが発生する箇所です。「パーフェクトセットアップ」フィルタを有効にしている場合、スクリプトは2つのトレーダー目線の条件を確認します。1つ目は、テストバーの始値が基準バーの値幅内であること。2つ目は、終値が基準バーの方向と逆になっていないことです(たとえば強気参照の場合、テストバーが弱気で確定してはいけない)。テストバーが条件を満たさず、診断用の行を出力しない設定であれば、この基準バーに対する出力はスキップされます。

int testIndex   = r - 1;
double testOpen = iOpen(_Symbol,_Period,testIndex);
double testClose= iClose(_Symbol,_Period,testIndex);

bool ValidSetup = true;
if(UsePerfectSetup) {
   if(testOpen < RefBot || testOpen > RefTop) ValidSetup = false;
   if(RefDir=="Bull" && testClose < testOpen) ValidSetup = false;
   if(RefDir=="Bear" && testClose > testOpen) ValidSetup = false;
}

if(!ValidSetup && !IncludeInvalidRows) continue;

拡張ウィンドウ処理:スクリプトで現実的な複数バーのリトレースメントを捉える

この機能を有効にすると、スクリプトはさらに過去方向に(設定可能なバー数分)さかのぼり、短い連続シーケンスを統合して真のリトレースメント極値を特定します。ルックアヘッドバーを走査する際に、スクリプトは以下の3つをおこないます。

  1. ギャップの検出:バーの始値が基準バーの値幅外であれば、ギャップとしてフラグを立て、そのサイズを記録します。
  2. インサイドバーの統合:小さな連続バーがすべて基準バーの値幅内に収まる場合、極値(Ext)をそのバー群の最安値(強気参照)または最高値(弱気参照)に更新し、InsideCountを増加させます。
  3. 包み足の検出:後続バーが基準バーを完全に包む場合、シーケンスをEngulfと分類し、HighMomentumフラグを設定します。

この統合処理により、観測データは未完成の部分的なタッチではなく、完了したリトレースメントエピソードを正確に反映するようになります。

double Ext = (RefDir=="Bull") ? testLow : testHigh;
string SeqType = "Single";
bool HighMomentum = false;
int InsideCount = 0;

for(int k=1; k<=MaxLookahead; k++) {
   int idx = r - k;
   if(idx < 0) break;
   double kOpen = iOpen(_Symbol,_Period,idx);
   double kHigh = iHigh(_Symbol,_Period,idx);
   double kLow  = iLow(_Symbol,_Period,idx);
   
   if(kOpen > RefTop || kOpen < RefBot) { SeqType="Gap"; break; }
   if(kHigh <= RefTop && kLow >= RefBot) {
      if(RefDir=="Bull") Ext = MathMin(Ext,kLow);
      if(RefDir=="Bear") Ext = MathMax(Ext,kHigh);
      InsideCount++;
      continue;
   }
   if(kHigh >= RefTop && kLow <= RefBot) {
      SeqType="Engulf";
      HighMomentum=true;
      break;
   }
   // if retrace detected, stop
   break;
}

リトレースメント率の計算:解析用に結果を正規化

最終的に記録された極値(Ext)を用いて、スクリプトはリトレースメント率(RetracePct)を0〜100のスケールで計算します。

  • 強気参照:RetracePct = (RefTop - Ext) / Range * 100
  • 弱気参照:RetracePct = (Ext - RefBot) / Range * 100

次に、このイベントをラベル付けします。

  • RetracePctが負の場合:NoRetrace(価格が参照方向から離れた)
  • 0〜100の場合:Retracement
  • 100を超える場合:Extension(基準バーを超えた動き)

double Rpct = EMPTY_VALUE;
string Type = "Undefined";

if(RefDir=="Bull") Rpct = (RefTop - Ext) / Range * 100.0;
if(RefDir=="Bear") Rpct = (Ext - RefBot) / Range * 100.0;

if(Rpct < 0)       Type="NoRetrace";
else if(Rpct<=100) Type="Retracement";
else               Type="Extension";

実務上の診断:取引可能な距離と古典的フィボナッチレベルへの近接度の計算

スクリプトは、リトレースメントが表す絶対値(RetracePips)を計算し、スプレッドより小さいなど取引に適さない些細なタッチを除外できるようにします。また、古典的フィボナッチレベルのうち最も近いレベルとその差(NearestFibPct、NearestFibDistPct)も算出します。最後に、代表バー(統合後のシーケンスの最後のバー)の終値が基準バーと同じ方向で確定したかを確認し、SameDirSupportを設定します。

double RetracePips = (RefDir=="Bull") ? (RefTop-Ext)/_Point : (Ext-RefBot)/_Point;

// Same-dir support
bool SameDirSupport = (RefDir=="Bull") ? (testClose >= testOpen) : (testClose <= testOpen);

// Nearest Fibonacci comparison
double fibLevels[] = {0,23.6,38.2,50.0,61.8,78.6,100.0};
double nearest = fibLevels[0];
double minDist = fabs(Rpct - fibLevels[0]);
for(int i=1;i<ArraySize(fibLevels);i++) {
   double d = fabs(Rpct - fibLevels[i]);
   if(d < minDist) { minDist = d; nearest = fibLevels[i]; }
}

出力ルール

フラグ(OutputOnlySameDir、IncludeInvalidRows)の設定に応じて、スクリプトは該当行をスキップするか、書き込みます。書き込む場合、その行にはすべてのメタデータ(時刻、銘柄、RefTop/RefBot、Ext、RetracePips、RetracePct、SeqType、SameDirSupport、最寄りフィボナッチレベル、出来高およびスプレッド)が含まれます。ファイルは決定的であり、同じ銘柄、時間足、パラメータであれば常に同じCSVが生成されます。

if(OutputOnlySameDir && !SameDirSupport) continue;

FileWrite(fh,
   _Symbol,
   PeriodToString(_Period),
   TimeToString(iTime(_Symbol,_Period,r),TIME_DATE|TIME_SECONDS),
   RefTop, RefBot, Range, RefDir,
   Ext, RetracePips, Rpct, Type,
   SeqType, HighMomentum, InsideCount,
   SameDirSupport, nearest, minDist
);

クリーンアップと報告:実行を終了し、何が起こったかをチームに報告する

ループの後、スクリプトはATRハンドルを解放し、ファイルを閉じます。そして、書き込まれた行数や無効な候補がいくつスキップされたかを短くまとめて表示します。この即時のフィードバックによって、次にどのような対応を取るべきか(例:バーの本数を増やす、フィルターを緩める、ATRの倍率を変更する)を判断することができます。

IndicatorRelease(atr_handle);
FileClose(fh);
PrintFormat("CandleRangeData_v2: finished. Wrote %d rows to %s", written, OutFileName);

PythonとJupyter Notebookによるフィボナッチリトレースメントデータの統計解析と可視化

研究を進めるために、Pythonを用いてデータを精緻化し、統計レポートを作成します。この目的のために、Jupyter Notebookを選びました。Jupyter NotebookはPythonでの作業効率を向上させ、私たちが目指す結果を得るのに適した環境を提供してくれます。

Jupyter Notebookは、科学計算、データ解析、可視化のために設計された対話型のウェブ環境です。コード、可視化結果、ドキュメントを同じ作業スペース上で共存させることができるため、研究中心の作業に非常に適しています。私たちの場合、この環境を使うことで、リトレースメントデータの実験、統計手法の検証、ヒストグラムや密度プロットなどの結果の即時可視化が可能になります。静的なスクリプトのように最初から最後まで一度に実行する必要はなく、Jupyterではセルごとに個別に実行できます。このため、計算を調整したり、特定のステップだけを再実行したりする場合に非常に便利です。この対話型のワークフローは、金融データにおけるパターン探索の反復的かつ探索的な性質に非常によく合っています。

以下に、Windows上でJupyter Notebookをセットアップする手順を示します。

セルを実行するには、WindowsでJupyter(NotebookまたはLab)を使用してください。手順は以下の通りです。

1. python.orgからPython 3.10以上をインストールします([Add Python to PATH]を選択してください)。
2. コマンドプロンプト(PowerShellまたはCMD)を開き、仮想環境を作成します(任意ですが推奨です)。

python -m venv venv
venv\Scripts\activate

3. Jupyterと必要なライブラリをインストールします。

pip install jupyter pandas numpy matplotlib scipy scikit-learn

4. Jupyterを起動します。

特定のフォルダ内で作業したい場合は、cd コマンドでディレクトリを変更します。たとえば、CSVファイルをエクスポートしたフォルダ(一般的にはC:\Users\YourComputerName\MQL5\Files)に移動する場合は、以下のように入力します。

cd C:\Users\YourComputerName\TerminalDataFolder\MQL5\Files

その後、以下のコマンドでJupyterを起動できます。

jupyter notebook

ライブラリとその用途

  • pandas:CSVテーブル(行/列)の読み込みや操作
  • numpy:数値配列や数学的な補助計算
  • matplotlib:ヒストグラムやカーネル密度推定(KDE)のプロット
  • scipy:統計関数やKDE
  • scikit-learn (sklearn):1次元データのクラスタリングに使うガウス混合モデルに使用 

セル1:Python環境の設定

ノートブックの最初では、分析に必要なPythonライブラリをインポートして環境を準備します。それぞれのライブラリは、MQL5スクリプトからエクスポートされたデータを扱うために特化した役割を持っています。

  • Pandas (pd):構造化データの操作に使用します。リトレースメントの記録を含むCSVファイルを読み込み、行や列を操作したり、統計値を簡単に計算したりできます。
  • Numpy (np):数値配列や線形代数などの数学的ツールを提供し、多くの統計計算の基盤となります。
  • Matplotlib.pyplot (plt):ヒストグラムや密度プロットなどのチャート作成の基盤となります。リトレースメントの挙動を可視化するのに役立ちます。
  • Seaborn (sns):Matplotlibを拡張し、より美しくスタイル調整しやすいプロットを作成できます。統計的パターンをより明確に示すのに便利です。
  • Scipy.stats (stats):カーネル密度推定(KDE)、仮説検定、確率分布などの高度な統計手法を提供します。
  • Sklearn.mixture.GaussianMixture:scikit-learnの機械学習ライブラリからのもので、ガウス混合モデルを適用できます。リトレースメントレベルをクラスタリングし、隠れたフィボナッチのようなレベルが集中している箇所を検出する際に役立ちます。

このセルを実行することで、分析に必要なすべてのツールがロードされます。

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from scipy import stats
from sklearn.mixture import GaussianMixture

セル2:CSVの読み込みとValidSetup_bool列の作成

この段階では、ノートブックはMQL5からエクスポートされたリトレースメントデータセットの読み込みから始まります。CSVファイルはpandasのDataFrameに読み込まれ、読み込まれた行数と利用可能な列がすぐに表示されます。この確認は非常に重要で、ファイルパスが正しいことや、RetracementPct、Type、ValidSetupなどの期待されるフィールドが存在することを確認できます。また、データの構造を可視化するため、最初の数行が表示され、MetaTraderからエクスポートしたデータと整合しているかを素早くチェックできます。

データセットをメモリに読み込んだ後、次の作業はどの行をさらに分析に使用するかを決定することです。MQL5スクリプトからのリトレースメント計算すべてが信頼できるわけではなく、未完成のものや無効なセットアップとしてフラグが立てられたものもあります。データセットによっては、ValidSetup、Valid、validsetupのように有効性を示す列名が微妙に異なる場合があります。そのため、スクリプトは複数の一般的な列名を確認し、見つかった場合は新しいブール型列ValidSetup_boolに標準化します。True、1、Yesなどの値は有効と解釈されます。有効性を示す列がまったく見つからなかった場合は、すべての行を有効として扱うことで、分析に必要なデータを確保します。最後に、有効な行だけを含むフィルタ済みデータセットdf_validが作成され、全体の行数と有効な行数の統計が簡単に表示されます。

# Robust Cell 2: load CSV and create a boolean ValidSetup_bool column
import pandas as pd
from IPython.display import display

csv_file = "CandleRangeData_NZDUSD_H4.csv"   # <- set your filename here
df = pd.read_csv(csv_file, sep=None, engine="python")
print("Loaded rows:", len(df))
print("Columns found:", list(df.columns))

# Show first 5 rows
display(df.head())

# Try to detect a 'valid setup' column (several common name variants)
candidates = ["ValidSetup", "validsetup", "Valid_Setup", "Valid", "valid", 
              "ValidSetup_bool", "Valid_Setup_bool"]
found = None
for c in candidates:
    if c in df.columns:
        found = c
        break

# Case-insensitive detection if exact not found
if found is None:
    lower_map = {col.lower(): col for col in df.columns}
    for c in candidates:
        if c.lower() in lower_map:
            found = lower_map[c.lower()]
            break

# Create boolean column 'ValidSetup_bool' using detected column or fallback
if found is not None:
    print("Using column for validity:", found)
    series = df[found].astype(str).str.strip().str.lower()
    df["ValidSetup_bool"] = series.isin(["true", "1", "yes", "y", "t"])
else:
    print("No ValidSetup-like column found. Creating ValidSetup_bool=True for all rows (no filtering).")
    df["ValidSetup_bool"] = True

# Quick stats
total = len(df)
valid_count = df["ValidSetup_bool"].sum()
print(f"Total rows = {total}, ValidSetup_bool True = {valid_count}")

# Create df_valid for downstream cells (the rest of notebook expects df_valid)
df_valid = df[df["ValidSetup_bool"] == True].copy()
print("df_valid rows:", len(df_valid))

# Display preview
display(df_valid.head())

セル3:コレクターのスキーマに基づいたリトレースメント値の計算

セル2で有効なデータセットを準備した後、ノートブックはリトレースメント値が分析に一貫して利用できるように整える作業に移ります。MetaTraderからエクスポートされたデータセットでは、すでに計算済みのフィールドもあれば、生の価格データから導出する必要があるものもあります。このステップでは、リトレースメントの計算プロセスを統一します。

まず最初に、Range列が数値型であることを確認します。この列はMQL5コレクターからエクスポートされたもので、基準バーの全体のサイズ(上限と下限の差)を表します。float型に変換することで、以降の数学的な計算が正しく行われることを保証します。

次に、MetaTraderのエクスポートにRetracementPct列が含まれているかを確認します。存在する場合、この値はパーセンテージでのリトレースメント(例:50は50%)と解釈され、100で割ることで0〜1の正規化された分数に変換します。この方法により、計算の一貫性が保たれ、すでに計算済みの値を再利用できます。この列が存在しない場合は、以下の式を使ってリトレースメントを手動で計算します。

リトレースメントの計算

ここで、Extはバーが確定する前に到達した極端なリトレースメントレベル、RefBotは基準バーの下限を表します。ExtとRefBotの差をRangeで割ることで、比例的なリトレースメント値を求めます。

リトレースメント値は常に0%〜100%の範囲に収まるべきなので、clip操作を適用してすべての値を[0,1]の範囲に制限します。これにより、データの不規則性や計算上のオーバーシュートによる異常値を防ぎます。

最後に確認のため、RefTop、RefBot、Ext、Range、Retracementといった主要列の最初の数行を表示し、計算済みまたはインポート済みのリトレースメント値が妥当であることを確認します。このプレビューにより、下流の統計解析に使える一貫した正規化リトレースメント値が得られていることを安心して確認できます。

# Cell 3: Compute retracement values based on the collector's schema

# In our MT5 output:
# - RefTop = top of the reference bar
# - RefBot = bottom of the reference bar
# - Ext    = the extreme retracement reached before close
# - RetracementPct = retracement % already computed in MT5

# 1. Use the collector's "Range" directly
df_valid["Range"] = df_valid["Range"].astype(float)

# 2. Use the already provided retracement percentage (if available)
if "RetracementPct" in df_valid.columns:
    df_valid["Retracement"] = df_valid["RetracementPct"].astype(float) / 100.0
    print("Using MT5-calculated RetracementPct column.")
else:
    # fallback: compute from RefTop/RefBot and Ext
    df_valid["Retracement"] = (df_valid["Ext"].astype(float) - df_valid["RefBot"].astype(float)) / df_valid["Range"]
    print("No RetracementPct column found — computed from RefTop/RefBot/Ext.")

# 3. Clip between 0 and 1 (0%–100%)
df_valid["Retracement"] = df_valid["Retracement"].clip(0, 1)

# Quick check
print("Preview of retracement values:")
display(df_valid[["RefTop","RefBot","Ext","Range","Retracement"]].head())

図3:計算済みの値の表

セル4:リトレースメント値の分布の可視化

リトレースメント値が検証され標準化されたので、このセルではそれらの分布を可視化します。しかしプロットを作成する前に、スクリプトは追加の安全措置を講じます。MetaTraderのエクスポートに含まれる列に関わらず、使用可能なRetracement列が存在することを確認します。

まず、Retracement列がすでに存在するかを確認します。存在しない場合は、新たに構築します。最初にMetaTrader 5からエクスポートされたパーセンテージベースのRetracementPctを探し、0〜1の正規化スケールに変換します。この列がない場合は、RefTop、RefBot、Ext、RefDirを用いたカスタム計算にフォールバックします。RefDir(基準バーの方向)の情報を使うことで、計算がより堅牢になります。というのも、リトレースメントは強気バーと弱気バーで解釈が異なるためです。

  • 強気バーの場合、リトレースメントはRefTopから極値まで測定する
  • 弱気バーの場合、リトレースメントはRefBotから極値まで測定する

必要な列がまったく存在しない場合は、どのフィールドが欠落しているかを明示したエラーメッセージが表示されます。

リトレースメント値が揃ったら、すべての値を[0,1]の範囲に収めるためにclip操作をおこない、NaNを削除します。クリーニング後に有効な値が残らない場合は、誤解を招くプロットを避けるためにスクリプトは停止します。それ以外の場合は、使用可能な観測値の数が表示されます。

 Seabornライブラリが利用可能な場合は、sns.histplotを使ってヒストグラムを描き、滑らかなカーネル密度推定(KDE)曲線を重ねて視覚的に分かりやすくします。Seabornがインストールされていない場合は、Matplotlibとscipy.stats.gaussian_kdeを用いた手動計算によるKDEを使うフォールバックがおこなわれます。この方法により、最小環境でも整ったプロットを生成できます。

最終的なチャートには軸ラベルが明確に付けられ、x軸は0〜1の範囲に制約されます。これにより、すべての有効なセットアップにおけるリトレースメント比率の分布が一目で分かり、浅い、普通、深いリトレースメントがどの程度の頻度で発生するかを即座に把握できます。必要に応じて、この図はドキュメントやプレゼンテーション用に適切なサイズのPNGファイルとして保存することも可能です。

# Plotting cell 4 (robust): ensures 'Retracement' exists and draws a 750px-wide plot
import numpy as np
import matplotlib.pyplot as plt

# --- Build 'Retracement' column if needed ---
if "Retracement" not in df_valid.columns:
    if "RetracementPct" in df_valid.columns:
        # MT5 already computed it as percent (0-100)
        df_valid["Retracement"] = pd.to_numeric(df_valid["RetracementPct"], errors="coerce") / 100.0
        print("Using existing RetracementPct -> created Retracement (0-1).")
    else:
        # try to compute from RefTop/RefBot/Ext with direction awareness
        required = {"RefTop","RefBot","Ext","RefDir"}
        if required.issubset(set(df_valid.columns)):
            def compute_r(row):
                try:
                    rng = float(row["RefTop"]) - float(row["RefBot"])
                    if rng == 0: 
                        return np.nan
                    if str(row["RefDir"]).strip().lower().startswith("b") :  # Bull
                        return (float(row["RefTop"]) - float(row["Ext"])) / rng
                    elif str(row["RefDir"]).strip().lower().startswith("be"): # Bear
                        return (float(row["Ext"]) - float(row["RefBot"])) / rng
                    else:
                        return np.nan
                except Exception:
                    return np.nan
            df_valid["Retracement"] = df_valid.apply(compute_r, axis=1).astype(float)
            print("Computed Retracement from RefTop/RefBot/Ext/RefDir.")
        else:
            raise KeyError("No 'Retracement' or 'RetracementPct' column, and required columns for computation are missing. "
                           "Found columns: " + ", ".join(df_valid.columns))

# Clip to 0..1
df_valid["Retracement"] = df_valid["Retracement"].clip(lower=0.0, upper=1.0)

# Drop NaNs
vals = df_valid["Retracement"].dropna().values
if len(vals) == 0:
    raise ValueError("No valid retracement values to plot after preprocessing.")

print(f"Plotting {len(vals)} retracement observations (0..1 scale).")

# --- Plot size: target ~750 px width ---
# Use figsize such that width_inches * dpi = 750. We'll choose dpi=100, width=7.5in.
fig_w, fig_h = 7.5, 4.0
fig, ax = plt.subplots(figsize=(fig_w, fig_h), dpi=100)

# Prefer seaborn if available for nice KDE overlay, otherwise fallback
try:
    import seaborn as sns
    sns.histplot(vals, bins=50, stat="density", kde=True, ax=ax)
except Exception:
    # fallback to matplotlib
    ax.hist(vals, bins=50, density=True, alpha=0.6)
    # manual KDE overlay
    from scipy.stats import gaussian_kde
    kde = gaussian_kde(vals)
    xgrid = np.linspace(0,1,500)
    ax.plot(xgrid, kde(xgrid), linewidth=2)

ax.set_title("Retracement Ratio Distribution")
ax.set_xlabel("Retracement (0 = 0%, 1 = 100%)")
ax.set_ylabel("Density")
ax.set_xlim(0,1)
plt.tight_layout()

# Optionally save a sized PNG (uncomment to save)
# plt.savefig("retracement_distribution_750px.png", dpi=100)

plt.show()

図4:リトレースメント比の分布

セル5:カーネル密度推定(KDE)

このステップでは、基本的な可視化を超えて、より高度な統計解析をおこないます。具体的には、カーネル密度推定(KDE)とピーク検出を組み合わせます。この手法により、ヒストグラムの単なる出現回数ではなく、分布の形状を解析することで、価格が停滞したり反転したりしやすい「隠れたゾーン」、つまり一般的なリトレースメントレベルを明らかにできます。

スクリプトはまず、Retracement列が正規化された形式(0〜1)で存在することを確認します。存在しない場合は、前述と同じ強気/弱気のロジックを使って、RetracementPctから、あるいは必要に応じてRefTop、RefBot、Ext、RefDirから再構築します。値を[0,1]の範囲に収め、NaNを削除した後、少なくとも10個の有効なリトレースメント値があることを確認します。この安全策により、データセットが小さすぎる場合にノイズの多い意味のないKDE推定がおこなわれることを防ぎます。

次に、KDEを0〜1の範囲で1001ポイントの細かいグリッド上で計算します。この高解像度により、分布の微妙な構造や複数の局所最大値を捉えることができます。局所最大値を特定するために、密度曲線を正規化し、scipy.signal.find_peaksを使用して、最小プロミネンスや間隔を設定して小さな揺らぎを無視します。得られたピークのインデックスは、局所的に密度が最も高いリトレースメントレベルに対応しており、データ中に隠れた「好ましい」リトレースメントレベルを示します。

可視化では、KDE曲線の下を塗りつぶし、検出された各ピークを赤い点で強調し、リトレースメント比(例:38.20%)を注釈として表示します。前のプロットセルとは異なり、このセルでは固定ピクセル幅を強制しないため、環境に応じて図のサイズが柔軟に調整されます。軸ラベル、範囲、グリッドを整えることで、プロットは見やすく解釈しやすくなっています。

最後に、検出されたリトレースメントレベルのリストがパーセンテージで表示され、それぞれの相対プロミネンス値も出力されます。これにより、視覚的および数値的に、最も強い隠れたリトレースメントレベルの位置を把握できます。KDEとピーク検出を組み合わせることで、生のリトレースメント観測値を実践的な統計的知見に変換できます。

# Cell 5: KDE + peak detection (robust, flexible sizing)
import numpy as np
import matplotlib.pyplot as plt
from scipy import stats
from scipy.signal import find_peaks

# --- ensure Retracement column exists (0..1 scale) ---
if "Retracement" not in df_valid.columns:
    if "RetracementPct" in df_valid.columns:
        df_valid["Retracement"] = pd.to_numeric(df_valid["RetracementPct"], errors="coerce") / 100.0
    else:
        required = {"RefTop","RefBot","Ext","RefDir"}
        if required.issubset(set(df_valid.columns)):
            def compute_r(row):
                try:
                    rng = float(row["RefTop"]) - float(row["RefBot"])
                    if rng == 0:
                        return np.nan
                    rd = str(row["RefDir"]).strip().lower()
                    if rd.startswith("b"):  # Bull
                        return (float(row["RefTop"]) - float(row["Ext"])) / rng
                    elif rd.startswith("be") or rd.startswith("bear"):  # Bear
                        return (float(row["Ext"]) - float(row["RefBot"])) / rng
                    else:
                        return np.nan
                except Exception:
                    return np.nan
            df_valid["Retracement"] = df_valid.apply(compute_r, axis=1).astype(float)
        else:
            raise KeyError("Cannot build 'Retracement' — missing required columns. Found: " + ", ".join(df_valid.columns))

# Clip and drop NaNs
df_valid["Retracement"] = df_valid["Retracement"].clip(0,1)
vals = df_valid["Retracement"].dropna().values
n = len(vals)
if n < 10:
    raise ValueError(f"Too few retracement observations to compute KDE/peaks reliably (n={n}).")

# --- KDE on a fine grid ---
grid = np.linspace(0, 1, 1001)   # 0.001 (0.1%) resolution
kde = stats.gaussian_kde(vals)
dens = kde(grid)

# --- peak detection on normalized density ---
dens_norm = dens / dens.max()
peaks_idx, props = find_peaks(dens_norm, prominence=0.02, distance=8)  # tweak params as needed
peak_levels = grid[peaks_idx]
peak_heights = dens[peaks_idx]

# --- Plot (flexible sizing, no fixed pixel restriction) ---
fig, ax = plt.subplots(figsize=(8, 4))  # default flexible size
ax.plot(grid, dens, label="KDE", linewidth=2)
ax.fill_between(grid, dens, alpha=0.2)

# annotate peaks
for lvl, h in zip(peak_levels, peak_heights):
    ax.plot(lvl, h, "o", color="red")
    ax.text(lvl, h, f" {lvl*100:.2f}%", va="bottom", ha="left", fontsize=9)

ax.set_title("Kernel Density of Retracement Ratios")
ax.set_xlabel("Retracement (0 = 0%, 1 = 100%)")
ax.set_ylabel("Density")
ax.set_xlim(0,1)
ax.grid(True, linestyle="--", alpha=0.5)
plt.tight_layout()
plt.show()

# --- print candidate levels ---
print("Candidate Hidden Retracement Levels (%):")
print(np.round(peak_levels*100, 2))
print("Peak prominences (relative):", np.round(props["prominences"], 4) if "prominences" in props else "n/a")

KDE

図5:リトレースメント比のカーネル密度



テストと結果

Jupyter Notebookのすべてのセルは、統計的に解釈可能な可視化結果を生成しました。各セルの下には、コードとそれに対応する出力が表示されており、Jupyterで作業する大きな利点の1つである「計算と可視化をシームレスに組み合わせられる点」を示しています。

最後に、解決された隠れリトレースメント値を保存するための出力セルを実行したところ、NZDUSDペアのH4時間足では、2つの有意なレベルのみが検出されました。これはデータ構造に関する有用な洞察を提供しましたが、仮説検定の結果は私たちの期待を支持するものではありませんでした。

Detected peaks at (pct): [29.7 58.3]
Bootstrap 200/1000...
Bootstrap 400/1000...
Bootstrap 600/1000...
Bootstrap 800/1000...
Bootstrap 1000/1000...
Bootstrap done in 4.5s

Peak testing results (window ±0.40%):
Level 29.700%  mass=9.909890e-03  p=0.4960   significant=False
Level 58.300%  mass=9.729858e-03  p=0.4960   significant=False

Accepted (FDR<0.05) candidate levels (pct): []

アルゴリズムは、データ分布の中で2つの候補ピークを検出しました。それぞれの位置は29.7%と58.3%です。これらのピークは、アルゴリズムが局所的にデータが集中していると初期的に観測したポイントを示しており、潜在的な隠れ構造や繰り返しパターンの存在を示唆しています。

これらのピークが統計的に有意であるか、単なるランダムな変動であるかを評価するため、モデルは1000回のリサンプルによるブートストラップテストを実施しました。ブートストラップの過程では、データをランダムにシャッフルした場合に同様のピークがどの程度出現するかを推定し、統計的有意性の指標を得ます。

検出された2つのレベルについての結果は以下の通りです。

  • レベル 29.7% → mass = 0.0099、p = 0.4960 → 有意ではない
  • レベル 58.3% → mass = 0.0097、p = 0.4960 → 有意ではない

p値(約0.50)は、これらのピークが実際のデータと同じくらい頻繁にランダムなブートストラップサンプルでも出現することを示しており、統計的にノイズと区別できないことを意味します。FDR(偽発見率)閾値0.05を超えるピークがなかったため、アルゴリズムは選択した時間足(NZDUSDのH4)において有意な隠れレベルは存在しないと結論付けました。

 発見されたレベルを用いたバックテスト

MetaTrader 5では、デフォルトのフィボナッチセットに対して、2つのカスタムリトレースメントレベル(29.7%と58.3%)を追加して実験しました。Jupyterによる分析結果では、両レベルとも29.7% → mass = 0.0099、p = 0.496058.3% → mass = 0.0097、p = 0.4960となり、統計的には有意ではありませんでした。しかし、チャート上にプロットしてみると、過去の価格の動きにおいてどちらかのレベルが実際に意識されていた可能性がありました。これは、統計的な検定では有意性が確認されなかったものの、実務上の参考になる場合があることを示唆しています。

現状ではこれらのレベルは手動で追加していますが、将来的にはこれらの値をプログラムでMetaTrader 5に統合し、複数の通貨ペアやタイムフレームで自動テストをおこなうことも可能です。下図(図6)はその例を示しています。

フィボナッチツールに計算済みレベルを追加する

図6:新しいレベルの反映


結論

本プロジェクトは、野心と好奇心に突き動かされて始まりました。フィボナッチリトレースメントの枠組みにおける価格挙動をチャート上で観察することから出発し、既知のレベルに対する価格反応の不規則性に気づき、さらには従来のリトレースメント・ポイントの間に、まだ明確に定義されていない「隠れたレベル」が存在するのではないかという問いへと発展しました。これらの仮説を検証するため、本記事ではMQL5による自動データ収集と、Jupyter上で動作するPython環境を組み合わせ、データ解析、可視化、ならびに複数言語の統合を可能にする強力な分析基盤を構築しました。

その結果、初期的な成果は得られたものの、いくつかの課題も明らかになりました。データセットの規模には制約があり、算出したリトレースメント値を手動でチャートに適用する試みは一定の可能性を示したものの、当初の仮説とは必ずしも一致しませんでした。これは、データ収集プロセスそのものや、スイングおよびリトレースメント検出手法に、さらなる改善の余地があることを示唆しています。分析対象を複数の通貨ペアや時間足へと拡張し、検出アルゴリズムを再設計することで、精度と信頼性の向上が期待されます。

こうした制約があるにもかかわらず、本プロジェクトで構築した基盤は十分に価値あるものです。MQL5とPythonを組み合わせることで、定量的な取引研究を実践できることを示しており、取引プラットフォームの自動化とデータサイエンスを結び付けたいと考える初学者にとって、現実的かつ有用な出発点となります。初期結果は期待を完全に満たすものではありませんでしたが、チャート上には引き続き興味深い挙動が確認されており、さらなる検証に値する可能性を示しています。より堅牢なテストと洗練された手法を用いることで、本研究のアプローチは、リトレースメントにおける隠れたダイナミクスについて、新たな洞察をもたらす余地があります。

推測に基づいてカスタムリトレースメントレベルを追加するのとは異なり、本アプローチは、データサイエンスと既存のMQL5ツールを組み合わせることで、効率的かつ体系的な検証プロセスを実現します。以下に関連リソースをまとめていますので、ぜひ各資料を参照し、実際に検証をおこなったうえで、ご意見や考察を共有していただければ幸いです。次回の公開も、ぜひご期待ください。



添付ファイル

ファイル名 バージョン  説明
CandleRangeData.mq5
 1.0 MetaTrader 5のチャートからローソク足のレンジとリトレースメントデータを収集し、分析用にCSV形式でエクスポートするMQL5スクリプト
HiddenFiboLevels.ipynb
該当なし エクスポートされたCSVを読み込み、データを整形、クリーニングし、潜在的なフィボナッチリトレースメントレベルを検証、可視化するPythonコードを含むJupyter Notebook
CandleRangeData_NZDUSD_H4.csv
該当なし MQL5スクリプトによって生成されたサンプルデータセットで、NZDUSD通貨ペアのH4時間足用。Pythonによる解析の入力として使用。

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

添付されたファイル |
CandleRangeData.mq5 (13.94 KB)
MQL5でのAI搭載取引システムの構築(第3回):複数行入力の克服、チャットの持続性の確保、シグナル生成 MQL5でのAI搭載取引システムの構築(第3回):複数行入力の克服、チャットの持続性の確保、シグナル生成
本記事では、ChatGPTを統合したMQL5プログラムを拡張し、改良されたテキストレンダリングにより複数行入力の制限を克服します。さらに、AES256暗号化およびZIP圧縮で保存された永続的なチャット履歴をナビゲートするサイドバーを導入し、チャートデータの統合による初期売買シグナルの生成もおこないます。
知っておくべきMQL5ウィザードのテクニック(第82回):DQN強化学習でTRIXとWPRのパターンを使用する 知っておくべきMQL5ウィザードのテクニック(第82回):DQN強化学習でTRIXとWPRのパターンを使用する
前回の記事では、推論学習の枠組みにおける一目均衡表とADXの組み合わせを検証しました。本記事では、第68回で最後に取り上げたインジケーターの組み合わせ、すなわちTRIXとWilliams Percent Range (WPR)を対象に、強化学習を再度取り上げます。今回使用するアルゴリズムは、QR-DQN (Quantile Regression DQN)です。これまでと同様に、MQL5ウィザードでの実装を前提としたカスタムシグナルクラスとして提示します。
取引システムの構築(第5回):構造化された取引決済による利益管理 取引システムの構築(第5回):構造化された取引決済による利益管理
利益目標まであとわずかというところで価格が反転し、ストップロスにかかってしまう。トレーリングストップによって建値で決済された直後に、市場が元の方向へ大きく動き、当初の目標を超えていく。多くのトレーダーにとって、これはおなじみの悩みでしょう。本記事では、異なるリスクリワードレシオ(RRR)で複数のエントリーを配置する手法に焦点を当て、利益を体系的に確保しながら、全体のリスク曝露を抑えるアプローチを解説します。
MetaTrader 5機械学習の設計図(第3回):トレンドスキャンラベリング法 MetaTrader 5機械学習の設計図(第3回):トレンドスキャンラベリング法
私たちは、データリーケージを排除するために適切なティックベースバーを用いた堅牢な特徴量設計パイプラインを構築し、さらにメタラベル付きトリプルバリア法によるラベリングという重要な課題を解決してきました。本記事では、その発展的内容として、適応的な予測期間を実現する高度なラベリング手法である「トレンドスキャニング」を取り上げます。理論の解説に続き、トレンドスキャニングによるラベルをメタラベリングと組み合わせることで、従来の移動平均交差戦略を改善する具体例を示します。