プライスアクション分析ツールキットの開発(第42回):ボタンロジックと統計レベルを用いたインタラクティブチャートの検証
内容
はじめに
連載記事「プライスアクション分析ツールキットの開発」第42回にようこそ。本記事の目的は、プライスアクション分析を自動化し、ブラックボックス指標ではなく価格構造に依存するトレーダーにも直感的で使いやすくすることです。本記事では、前回の作業を拡張します。前回は、平均値、標準偏差、中央値、その他の分布レベルをローソク足の典型価格から計算し、市場の重要な参照レベル(サポート、レジスタンス、ピボットポイントなど)に自然にマッピングする方法を紹介しました。
パラメータをハードコーディングしたり、手動で入力を調整するのではなく、今回は、よりインタラクティブなチャート上でのアプローチを導入します。統計ダッシュボードです。このダッシュボードは、チャート上に操作ボタンや編集可能なフィールドを配置し、ユーザーが必要に応じて統計レベルを計算・可視化できるようにします。前回の実装の多くの機能は維持されていますが、このデザインはより洗練され、ユーザー中心のチャート分析を実現しています。
本記事では以下の内容を扱います。
- 統計ダッシュボードのロジックと動機の説明
- MQL5実装のハイライトの紹介
- 例となる結果と使用パターンの共有
- 結論と次のステップのまとめ
概念の理解
価格データから算出される統計指標(平均値、中央値、パーセンタイル、密度ベースのモードなど)は、最近の価格変動の中心傾向や分布の形状を捉えることができます。これらの指標は、市場で重要な反応エリアとしばしば一致します。たとえば、流動性が集中するゾーン、価格が戻りやすいゾーン、ブレイクアウトや反転が発生しやすいゾーンです。統計指標を参照レベルとして扱うことで、裁量取引にもシステマティックなアプローチにも対応できます。視覚的に解釈しやすく、自動監視にも適しています。 平均値、標準偏差、中央値などの指標については、前回の記事で詳しく解説していますので、そちらもご参照ください。
このツールのロジックは、ボタン操作による制御を中心に構築されており、市場分析を簡素化します。ダッシュボードは、開発者が設定した入力を軽量なチャート上UIに変換し、トレーダーは以下の操作をおこなうことができます。- 日付/時間範囲の選択(チャート上または入力による指定)
- 選択範囲に対する分布統計の計算
- 平均線、標準偏差バンド、パーセンタイル、中央値、モードの描画
- 参照レベルのスナップショット取得(タッチ、ブレイクアウト、反転検知用)
- スナップショットデータのCSVエクスポート
- ダッシュボードの状態を素早くリセット
このチャート上のUXにより、どの設定を使うか迷う必要がなくなり、分析作業の効率が向上します。また、コードを逐次変更する必要がないため、ライブトレードでも安全かつ使いやすくなります。トレーダーは数秒で統計を計算し、確認できます。

図はダッシュボードのシンプルな操作モデルを示しています。ボタンを1回クリックするだけで、リセット、計算、表示切替の3つの明確なアクションのいずれかにルーティングされます。各アクションは決定論的に操作を実行します(データクリア、統計計算と描画、表示の切替)、チャートを即座に更新し、EAはアイドル状態に戻ります。このチャート上ワークフローにより、日常的な分析でコードを編集する必要がなくなり、意思決定が高速化され、端末上に不要なオブジェクトやグローバル変数が残るリスクも低減します。
実装
まず、インジケーターの基礎となるメタデータを設定することから始めます。ヘッダーコメントには、ファイル名、作成者、著作権情報を記載し、スクリプトの識別や所有権の明示に役立てます。次に、プラットフォーム固有のディレクティブを設定します。具体的には、#property copyright、#property version、#property strictなどのプロパティです。これらのディレクティブにより、MetaTraderがスクリプトを正しく認識し、厳格な構文ルールを適用して安全なコーディングをサポートし、保守のためのバージョン管理も可能になります。この初期設定は非常に重要です。MetaTraderとコードを円滑に統合し、ベストプラクティスに従った開発をおこなうための基盤となるからです。
//+------------------------------------------------------------------+ //| Statistical Dashboard.mq5| //| Copyright 2025, Christian Benjamin.| //| https://www.mql5.com/ja/users/lynnchris| //+------------------------------------------------------------------+ #property copyright "https://www.mql5.com/ja/users/lynnchris" #property version "1.36" #property strict
次に、スクリプトの機能を拡張するために外部ライブラリを組み込みます。ここではArrayObj.mqhをインクルードしており、高度な配列管理機能を利用できるようにしています。これにより、複数のレベルやシグナル、UIコンポーネントを扱う際に、オブジェクトやデータの動的なコレクションを効率的に管理できるようになります。モジュール化されたこのアプローチは、コードを整理された状態に保ち、グラフィカルオブジェクトやグローバル変数などの機能を追加しても拡張性を維持することができます。
// Include array utility library #include <Arrays/ArrayObj.mqh>
次に、トレーダーがインジケーターの挙動をカスタマイズできるユーザー入力パラメータを定義します。これらの入力変数には、ルックバック期間(Lookback)、シグナルの閾値、表示設定、操作用トグルなどが含まれます。たとえば、Lookbackは一度に分析するバーの数を決定し、ZScoreSignalEnterは市場の偏差に対する感度を制御します。これらのパラメータを公開することで、トレーダーは異なる市場や時間足、個々の取引戦略に応じてインジケーターを調整できるようになり、実装の柔軟性とユーザーフレンドリーさが向上します。
// === Inputs === input int Lookback = 1000; input bool ExcludeCurrent = true; input bool UseWeightedByVol = true; input int ModeBins = 30; input int KDEGridPoints = 100; input double KDEBandwidthFactor = 1.0; input int RefreshEveryXTicks = 1; input double ZScoreSignalEnter = 2.0; input double ZScoreSignalExit = 0.8; input bool AllowLongSignals = true; input bool AllowShortSignals = true; input bool SendAlertOnSignal = false; input bool PlaySoundOnSignal = false; input string SoundFileOnSignal = "alert.wav"; input bool SendPushOnSignal = false; input ENUM_TIMEFRAMES TF = PERIOD_CURRENT; input int TimerIntervalSeconds = 60; input int CleanupIntervalSeconds = 3600; input bool AutoSnapshotLevels = false; input datetime InputRefStart = 0; input datetime InputRefEnd = 0; input int MonitorBars = 20; input double TouchTolerancePips = 3.0; input double BreakoutPips = 5.0; input double ReversalPips = 5.0; input bool UseCloseForConfirm = true; input bool UseATRforThresholds = true; input double ATRMultiplier = 0.5; input int ATRperiod = 14; input bool ClearSnapshotOnStart = false; input int LabelOffset_Mean_Sec = 0; input int LabelOffset_Median_Sec = -60; input int LabelOffset_ModeB_Sec = -120; input int LabelOffset_ModeK_Sec = -180; input int LabelOffset_Pct_Sec = -240; input bool DebugMode = false; input string InputRefStartStr = ""; input string InputRefEndStr = "";
次に、インジケーターの動作状況を管理する内部状態変数を設定します。awaitingSetStartやrefStartChartのような変数は、ユーザーの操作に関する情報、たとえば参照点を設定しているかどうかを保持したり、参照のタイムスタンプを保存したりします。また、currentSignalのような変数は、システムが現在ロング、ショート、または中立の姿勢を示しているかどうかを追跡します。これらの内部変数は非常に重要です。なぜなら、ティックごとの状況やユーザーのコマンドに対して、スクリプトが文脈を維持し、一貫性のある応答を可能にするからです。
// Internal variables to track state bool awaitingSetStart = false; bool awaitingSetEnd = false; datetime refStartChart = 0; datetime refEndChart = 0; int currentSignal = 0; // 1 for long, -1 for short, 0 for neutral
複数の参照レベルを扱うために、RefLevelという構造化データ型を定義します。この構造体には、レベルの名前、価格、到達したかどうか、到達回数、観測された最高値と最安値、その他の指標などの詳細がまとめられています。その後、複数のインスタンスを格納するために、refLevels[]という配列を作成し、複数のレベルを同時に監視し分析できるようにします。このような構造化されたアプローチを使用することで、明確で拡張性の高い設計が可能になります。トレーダーがレベルを追加しても、コードは体系的かつ効率的に対応できます。
struct RefLevel { string name; double price; bool touched; datetime touchTime; int touchCount; double highest; double lowest; double avgTouchVol; int recentTouches; double persistence; int result; // -1, 0, 1 datetime resolvedTime; }; RefLevel refLevels[]; // Array to hold multiple levels
実装全体を通して、ロジックをモジュール化するために多数の関数を宣言しています。たとえば、ボタンやラベル、グラフィカルオブジェクトを作成する関数(CreateButton()、CreateHLine_Pro()、DrawArrowAt())、データを出力する関数(ExportSnapshotCSV())、統計計算をおこなう関数(ComputeLevelScore()、Median()、Variance())などがあります。このモジュール化された設計は非常に重要です。なぜなら、UIの管理と分析ルーチンを分離することで、コードの理解やデバッグ、拡張が容易になるからです。また、スクリプトの異なる部分でコードのスニペットを再利用することも可能になります。
// Forward declarations void CreateToolbar(); void DeleteToolbar(); void CreateButton(string name,int corner,int xdist,int ydist,int xsize,int ysize,string text); void CreateButtonStatLabel(string labName,int corner,int xdist,int ydist,string text); void CreateEditField(string name,int corner,int xdist,int ydist,int xsize,int ysize,string text); void ExportSnapshotCSV(); void CreateHLine_Pro(string name,double price,double score,string friendlyLabel); double ComputeLevelScore(int touchCount,double avgTouchVolume,int recentTouches,double persistenceBars,datetime lastTouchTime); void DrawArrowAt(string name, datetime when, double price, bool isBuy); void CreatePanel(); void CreateOrUpdateLineText(string name, datetime t, double price, string text); void RemoveOldObjects(int ageSec); void ClearSnapshot(); void ClearSnapshotVisuals(); void SnapshotReferenceLevels(double mean_val,double p25,double p75,double median_val,double mode_b,double mode_k,double stddev); void MonitorReferenceLevels(const MqlRates &rates[], int copied); double pipToPointMultiplier(); void DeleteObjectIfExists(string name); void RemoveHistogramObjects(); void SetObjTimestamp(string name); datetime GetObjTimestamp(string name); void CleanupMetaForObject(string name); void CleanupAllMetaGlobals(); double Mean(const double &a[], int n); double WeightedMean(const double &a[], const double &w[], int n); double WeightedMeanFromRates(const MqlRates &rates[], int copied); double Variance(const double &a[], int n, bool sample); double Median(const double &a[], int n); double Percentile(const double &a[], int n, double q); double ModeBinned(const double &a[], int n, int bins); double ModeKDE(const double &a[], int n, int gridPts, double bwFactor); double ArrayMin(const double &a[], int n); double ArrayMax(const double &a[], int n); bool ComputeStatsFromGlobals(double &mean,double &stddev,double &median,double &modeb,double &modek,double &p25,double &p75,double &zscore);
次に、よく使う作業を簡略化するユーティリティ関数を作成します。たとえば、UpdateLabelText()はUIのラベルを動的に更新し、TrimString()はユーザー入力の文字列を整形し、pipToPointMultiplier()は正確な計算のためにピップ単位をプラットフォーム固有のポイントに変換します。これらのヘルパー関数により、コードの堅牢性が向上し、冗長なコードを防ぎ、データの一貫した取り扱いが可能になります。これらの特徴は、プロフェッショナル向けのインジケーターには欠かせません。
// Example: update label text void UpdateLabelText(string name, string text) { if(ObjectFind(0, name) >= 0) ObjectSetString(0, name, OBJPROP_TEXT, text); else CreateButtonStatLabel(name, 0, 0, 0, text); } // Example: trim strings string TrimString(string s) { // Implementation omitted for brevity }
次に、ResetAll()という包括的なリセット処理を実装します。この関数は、スナップショットをクリアし、すべてのグラフィカルオブジェクトと関連メタデータを削除し、内部変数をリセットし、ユーザーインターフェースを再構築します。この機能は非常に重要です。なぜなら、ユーザーがパラメータを変更したり、予期しない状態に遭遇した場合でも、MetaTraderを再起動せずに分析をクリーンにやり直すことができるからです。これにより、ユーザー体験が向上し、進行中の分析の整合性が保たれます。
void ResetAll() { // Clear snapshots, delete objects, reset globals, rebuild UI ClearSnapshot(); RemoveExistingEAObjects(); CleanupAllMetaGlobals(); RemoveHistogramObjects(); DeleteToolbar(); // Reset internal variables currentSignal = 0; refStartChart = 0; refEndChart = 0; refSnapshotTaken = false; snapshotTakenTime = 0; // Recreate UI CreatePanel(); CreateToolbar(); }
OnInit()関数では、インジケーターがロードされた際に初期化をおこないます。ここでは、銘柄や時間足に基づいたユニークな識別子を生成し、ビジュアルパネルやツールバーを作成し、定期的なクリーンアップのためのタイマーを設定し、ユーザー入力やチャート上の注釈に基づいて参照点を初期化します。このセットアップ段階により、リアルタイムデータの処理が始まる前に、UIコンポーネント、グローバル変数、内部状態など、必要なリソースが正しく構成されることが保証されます。適切な初期化は、エラーを防ぎ、スムーズな動作を確保するために非常に重要です。
int OnInit() { // Set base strings for global variables S_base = StringFormat("CSTATS_%s_%d", _Symbol, (int)TF); CreatePanel(); if(ClearSnapshotOnStart) ClearSnapshot(); EventSetTimer(TimerIntervalSeconds); CreateToolbar(); // Additional setup... return INIT_SUCCEEDED; }
それに対応して、OnDeinit()ではインジケーターが削除された際のクリーンアップをおこないます。タイマーを停止し、グラフィカルオブジェクトを削除し、グローバル変数をクリアし、環境をリセットします。このステップは非常に重要です。なぜなら、リソースのリークを防ぎ、チャート上の不要な情報を避け、後続のインジケーターやスクリプトが干渉なく動作できるようにするからです。これにより、取引環境全体の健全性とパフォーマンスが維持されます。
void OnDeinit(const int reason) { EventKillTimer(); // Delete graphical objects DeleteObjectIfExists(S_mean); DeleteObjectIfExists(S_panel); // Clear global variables CleanupAllMetaGlobals(); }
次に、OnTimerというタイマーハンドラを実装します。この関数は指定された間隔で実行され、古くなったグラフィカルオブジェクトや有効期限の切れたスナップショットを削除します。これにより、チャートが散らかることを防ぎ、表示されるデータの関連性を保つことができます。この定期的なクリーンアップは、特に長時間の取引セッションで連続的にデータが流れる場合に、チャートの見やすさとパフォーマンスを維持する上で重要です。
void OnTimer() { // Periodic cleanup of old objects RemoveOldObjects(CleanupIntervalSeconds); // Clear expired snapshots if(refSnapshotTaken && snapshotTakenTime > 0 && (TimeCurrent() - snapshotTakenTime) >= CleanupIntervalSeconds) { ClearSnapshot(); } }
リアルタイム分析の核心はOnTick()にあります。市場のティックが到着するたびに、この関数はインジケーターが一時停止中かどうか、またはリフレッシュレートに基づいて処理をスキップすべきかを確認します。その後、GetRatesForSelection()を使って、ユーザーが指定した範囲や過去の期間内の履歴価格データを取得します。このデータを基に、ComputeStatsFromRates()のような関数を用いて平均、中央値、最頻値、標準偏差といった統計指標を計算します。これらの指標は、市場のレジームや偏差、潜在的なシグナルを特定する基盤となります。
さらに、最新価格が平均からどれだけ離れているかを示すzスコアを計算し、これをシグナルのトリガーとして使用します。閾値に基づき、現在のシグナル状態を更新し、矢印で視覚的にシグナルを表示することで、トレーダーが市場状況を即座に把握できるようにしています。
void OnTick() { if(GlobalVariableCheck(S_base + "_PAUSED") && GlobalVariableGet(S_base + "_PAUSED") == 1.0) return; // Paused // Throttle refresh rate tick_count++; if(tick_count < RefreshEveryXTicks) return; tick_count = 0; // Gather data MqlRates rates[]; int copied=0; if(!GetRatesForSelection(rates, copied)) return; // Compute stats double mean_val, stddev, median_val, mode_b, mode_k, p25, p75; if(!ComputeStatsFromRates(rates, copied, mean_val, stddev, median_val, mode_b, mode_k, p25, p75)) return; // Calculate z-score double latest = (rates[0].high + rates[0].low + rates[0].close) / 3.0; double zscore = (stddev > 0) ? (latest - mean_val) / stddev : 0; // Store global variables GlobalVariableSet(S_base + "_mean", mean_val); GlobalVariableSet(S_base + "_zscore", zscore); // Generate signals int newSignal = 0; if(zscore >= ZScoreSignalEnter && AllowLongSignals) newSignal = 1; else if(zscore <= -ZScoreSignalEnter && AllowShortSignals) newSignal = -1; // Update visual signals if(newSignal != currentSignal) { if(newSignal == 1) DrawArrowAt(S_arrow_long, iTime(_Symbol, TF, 0), latest, true); else if(newSignal == -1) DrawArrowAt(S_arrow_short, iTime(_Symbol, TF, 0), latest, false); currentSignal = newSignal; } }
データ分析を容易にするために、GetRatesForSelection()やComputeStatsFromRates()といった関数を構築します。前者は、ユーザーが指定した日付範囲やルックバック期間を考慮し、分析対象をトレーダーの関心領域に絞った関連市場データを取得します。後者は、平均、中央値、最頻値などの統計計算を行い、配列のソートやパーセンタイル計算といった堅牢なアルゴリズムを使用します。これらの関数はインジケーターの分析機能の中核を成しており、生の市場データを意思決定に役立つ有意義なインサイトへと変換します。
bool GetRatesForSelection(MqlRates &rates[], int &copied) { // Fetch data based on date range or lookback if(UseDateRangeOnChart && refStartChart > 0 && refEndChart > 0) { int shiftStart = iBarShift(_Symbol, TF, refEndChart, false); int shiftEnd = iBarShift(_Symbol, TF, refStartChart, false); int startShift = MathMin(shiftStart, shiftEnd); int endShift = MathMax(shiftStart, shiftEnd); int count = endShift - startShift + 1; ArrayResize(rates, count); copied = CopyRates(_Symbol, TF, startShift, count, rates); return copied > 0; } else { int startShift = ExcludeCurrent ? 1 : 0; int needed = Lookback; ArrayResize(rates, needed); copied = CopyRates(_Symbol, TF, startShift, needed, rates); return copied > 0; } }
インタラクティブ性は重要な要素であるため、ユーザー入力や操作を管理するための関数を作成します。たとえば、CreateButton()、CreateEditField()、CreateToolbar()などは、チャート上にUIコントロールを生成し、トレーダーがパラメータを調整したり、アクションを実行したりできるようにします。OnChartEvent()関数は、ユーザーのクリック、ボタン操作、オブジェクトの変更を処理し、それに応じて内部変数や参照点を更新します。この設計により、インジケーターは非常に柔軟なものとなり、トレーダーが分析パラメータをその場でカスタマイズできるようになります。これは、変化の激しい取引環境において不可欠な要素です。
bool ComputeStatsFromRates(const MqlRates &rates[], int copied, double &mean, double &stddev, double &median, double &modeb, double &modek, double &p25, double &p75) { // Extract data double vals[]; ArrayResize(vals, copied); for(int i=0; i<copied; i++) { double tp = (rates[i].high + rates[i].low + rates[i].close) / 3.0; vals[i] = tp; } // Compute measures mean = Mean(vals, copied); stddev = MathSqrt(Variance(vals, copied, true)); median = Median(vals, copied); p25 = Percentile(vals, copied, 0.25); p75 = Percentile(vals, copied, 0.75); modeb = ModeBinned(vals, copied, ModeBins); modek = ModeKDE(vals, copied, KDEGridPoints, KDEBandwidthFactor); return true; }
[Mean]ボタンをクリックすると、OnChartEvent()関数がCHARTEVENT_OBJECT_CLICKイベントを通じてこの操作を検知します。次に、オブジェクト名を確認することで、クリックされたオブジェクトが[Mean]ボタンであることを特定します。確認が取れると、EAはGetRatesForSelection()を呼び出し、該当する市場データの範囲をサンプリングします。このデータ取得が失敗した場合、つまりデータが存在しない、またはエラーが発生した場合には、該当するラベルを更新してユーザーにデータが見つからなかったことを通知し、その時点で処理を終了します。
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { if(id != CHARTEVENT_OBJECT_CLICK) return; string objName = sparam; if(objName == S_base + "_BTN_SHOWMEAN") { // Step 1: Sample data for the selected range MqlRates rates[]; int copied=0; if(!GetRatesForSelection(rates, copied) || copied <= 0) { // Failure: No data available UpdateLabelText(S_base + "_LBL_BTN_MEAN", "No data for selection"); return; } // Step 2: Compute statistics (mean, stddev, etc.) double mean_val, stddev, median_val, mode_b, mode_k, p25, p75; if(!ComputeStatsFromRates(rates, copied, mean_val, stddev, median_val, mode_b, mode_k, p25, p75)) { // Failure: Calculation failed UpdateLabelText(S_base + "_LBL_BTN_MEAN", "Compute failed"); return; } // Step 3: Draw horizontal line at mean CreateHLine_Pro(S_mean, mean_val, 0.85, "Mean"); // Step 4: Update label with computed mean string rangeTxt = ""; if(UseDateRangeOnChart && refStartChart > 0 && refEndChart > 0) rangeTxt = StringFormat("%s -> %s", TimeToString(refStartChart, TIME_DATE|TIME_MINUTES), TimeToString(refEndChart, TIME_DATE|TIME_MINUTES)); else rangeTxt = StringFormat("Lookback %d bars", copied); CreateOrUpdateLineText(S_mean + "_TXT", iTime(_Symbol, TF, 0), mean_val, StringFormat("Mean: %s | %s | N=%d", DoubleToString(mean_val, _Digits), rangeTxt, copied)); UpdateLabelText(S_base + "_LBL_BTN_MEAN", "Mean: " + DoubleToString(mean_val, _Digits)); // End of process for button press return; } // Similar structure applies for other buttons like Std, Mode, Draw Levels, etc. }
データの取得に成功した場合、EAはサンプリングしたデータをComputeStatsFromRates()に渡し、平均値の計算に進みます。何らかの理由で計算に失敗した場合には、ラベルを更新して失敗したことを示し、その時点で処理を終了します。一方、計算が正常に完了した場合には、CreateHLine_Pro()を使用して算出された平均レベルに水平線を描画し、見やすさを考慮して外観やラベルをカスタマイズします。同時に、ボタンの横にある統計ラベルを更新し、新しい平均値を表示することで、即時的な視覚フィードバックを提供します。これらの処理が完了すると、一連の流れは終了し、次のユーザー操作に備えます。このようなフローにより、各ボタン操作がデータのサンプリング、分析、視覚的な更新という一連の処理を確実に実行し、ツールをインタラクティブかつ有益なものにしています。

視覚的な補助は迅速な判断において重要な要素であるため、CreateHLine_Pro()、DrawArrowAt()、CreateOrUpdateLineText()といった関数を実装します。これらの関数は、平均値や中央値などの統計レベルを示す水平線を描画し、ブレイクアウトや反転を示す矢印をプロットし、さらに詳細な情報を含むテキスト注釈を表示します。
void CreateHLine_Pro(string name, double price, double score, string friendlyLabel) { int width = 1 + (int)MathRound(score * 3.0); color col = clrDodgerBlue; // Determine color/style based on label if(ObjectFind(0, name) >= 0) { ObjectSetDouble(0, name, OBJPROP_PRICE, price); ObjectSetInteger(0, name, OBJPROP_COLOR, col); ObjectSetInteger(0, name, OBJPROP_WIDTH, width); ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID); } else { ObjectCreate(0, name, OBJ_HLINE, 0, 0, price); ObjectSetDouble(0, name, OBJPROP_PRICE, price); ObjectSetInteger(0, name, OBJPROP_COLOR, col); ObjectSetInteger(0, name, OBJPROP_WIDTH, width); ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID); } // Add label CreateOrUpdateLineText(name + "_TXT", iTime(_Symbol, TF, 0), price, friendlyLabel); }
これらの視覚的マーカーにより、トレーダーは数値を一つ一つ確認することなく、重要なレベルやシグナルを瞬時に認識できるようになり、判断のスピードと正確性が向上します。
void DrawArrowAt(string name, datetime when, double price, bool isBuy) { if(ObjectFind(0, name) >= 0) ObjectDelete(0, name); color col = isBuy ? clrLime : clrMaroon; int arrowCode = isBuy ? 233 : 234; // Up or down arrow ObjectCreate(0, name, OBJ_ARROW, 0, when, price); ObjectSetInteger(0, name, OBJPROP_ARROWCODE, arrowCode); ObjectSetInteger(0, name, OBJPROP_COLOR, col); ObjectSetInteger(0, name, OBJPROP_WIDTH, 1); }
参照レベルとの市場の相互作用の監視はMonitorReferenceLevels()によっておこなわれます。この関数は、価格があらかじめ定義されたレベルとどのように相互作用するかを追跡し、タッチ、ブレイクアウト、反転を検出します。また、タッチ回数、観測された最高値および最安値、持続性に関する指標を更新することで、各レベルの重要度を評価します。特定の条件、たとえば同じレベルに複数回タッチした場合や閾値を突破した場合には、そのレベルを確定させ、視覚的な通知やアラートを発生させます。これにより、トレーダーは市場の転換点を捉えやすくなります。
void MonitorReferenceLevels(const MqlRates &rates[], int copied) { // For each level, check touch, breakout, or reversal conditions for(int i=0; i<ArraySize(refLevels); i++) { RefLevel &L = refLevels[i]; // Touch detection if(!L.touched) { if(rates[0].high >= L.price - touchTol && rates[0].low <= L.price + touchTol) { L.touched = true; L.touchTime = rates[0].time; L.touchCount++; // Update visual CreateOrUpdateLineText(...); } } else { // Check for breakout or reversal if(rates[0].high >= L.price + breakoutThreshold) L.result = 1; // Breakout else if(rates[0].low <= L.price - reversalThreshold) L.result = -1; // Reversal // Draw outcome DrawOutcome(L, L.result == 1); } } }
このインジケーターは、SnapshotReferenceLevels()を通じて現在の市場状態のスナップショットを取得する機能もサポートしています。この関数は、各レベルの現在値を記録し、タッチの活動状況に基づいてスコアを算出し、将来の参照用として保存します。
void SnapshotReferenceLevels(double mean_val, double p25, double p75, double median_val, double mode_b, double mode_k, double stddev) { // Store snapshot data snapshot_mean = mean_val; snapshot_p25 = p25; snapshot_p75 = p75; snapshot_median = median_val; snapshot_modeb = mode_b; snapshot_modek = mode_k; // Visualize snapshot ClearSnapshotVisuals(); // Create lines CreateHLine_Pro(...); CreateOrUpdateLineText(...); refSnapshotTaken = true; snapshotTakenTime = TimeCurrent(); }
スナップショットデータはExportSnapshotCSV()を使用してCSVファイルとしてエクスポートでき、トレーダーは過去のレベルを分析したり、異なる市場レジームを比較したり、データを外部と共有したりすることが可能になります。この機能により、市場分析にさらなる深みが加わり、オフラインでのレビューや戦略立案を支援します。
void ExportSnapshotCSV() { // Save snapshot data to CSV file string filename = StringFormat("CSTATS_SNAPSHOT_%s_%d.csv", _Symbol, (int)TimeCurrent()); int handle = FileOpen(filename, FILE_WRITE | FILE_CSV); // Write headers and data FileWrite(handle, "symbol", _Symbol); // ... FileClose(handle); }
実装全体を通じて、グラフィカルオブジェクトとメタデータの管理に一貫したアプローチを維持しています。DeleteObjectIfExists()、SetObjTimestamp()、CleanupMetaForObject()といった関数は、オブジェクトが正しく作成、更新、削除されることを保証し、チャートの混雑を防ぎ、データの整合性を確保します。適切なタイムスタンプ管理により、オブジェクトが最後に修正または作成された時刻を追跡できるため、古くなった視覚要素のクリーンアップや正確なチャートオーバーレイの維持に役立ちます。
void CreatePanel() { if(ObjectFind(0, S_panel) >= 0) ObjectDelete(0, S_panel); if(!ObjectCreate(0, S_panel, OBJ_LABEL, 0, 0, 0)) { if(DebugMode) Print("CreatePanel: ObjectCreate failed: ", GetLastError()); return; } ObjectSetInteger(0, S_panel, OBJPROP_CORNER, CORNER_LEFT_UPPER); ObjectSetInteger(0, S_panel, OBJPROP_XDISTANCE, 6); ObjectSetInteger(0, S_panel, OBJPROP_YDISTANCE, 24); ObjectSetString(0, S_panel, OBJPROP_TEXT, "Statistical — Dashboard"); ObjectSetInteger(0, S_panel, OBJPROP_FONTSIZE, 11); ObjectSetInteger(0, S_panel, OBJPROP_SELECTABLE, false); #ifdef __MQL5__ ObjectSetInteger(0, S_panel, OBJPROP_BACK, false); #endif SetObjTimestamp(S_panel); }
結果
このセクションでは、EAが実際に生成するものと、その結果の読み方を説明します。 下の図は、EAがチャートにアタッチされた後、静かに待機している様子を示しています。左側には、統計ダッシュボードパネルとツールバーが表示されており、「Mean」「Std」「Mode」「Draw Levels」「Snapshot」「Apply Dates」「Reset All」とラベル付けされたボタンがあります。各ボタンの隣にはプレースホルダーのラベルがあり、ボタンを押すと計算された統計値で更新されます。右上には、カスタムの日付範囲や参照点を入力できるコントロールがあります。現時点では統計レベルは計算されていないため、チャートにはいくつかのターミナルプリミティブと、前回実行時の残りの水平線だけが表示されています。
[Mean]や[Draw Levels]などのボタンをクリックすると、EAは選択された範囲(ルックバック期間または特定のチャート日付に基づく)をサンプリングし、平均値、標準偏差、パーセンタイル、中央値、最頻値などの指標を計算します。その後、関連する水平線とラベルを描画し、統計フィールドを埋めます。さらに、スナップショットを取得する場合は、これらのレベルをタッチ、持続性、ブレイクアウトや反転などのシグナルについて監視し始めます。
以下では、分析の開始日と終了日を指定して、期間範囲を設定する方法を示します。

次に、全体的なパフォーマンスを確認します。

EAは日付範囲を設定した状態でアタッチされており(Start:2025.08.14 11:00—End:2025.09.23 04:00)、チャート上には2つのタイムスタンプマーカーが表示されています。各ダッシュボードボタンは即座に動作し、[Mean]、[Std]、[Mode]、[Median]、[P75/P25]などを押すと、選択した期間の指標を計算し、水平線とテキストラベルとしてチャート上に描画します。Draw Levelsは選択した複数のレベルを一度に描画し、Remove Levelsはそれらを削除します。また、SnapshotやSave Snapshotは現在の設定を保存し、後で参照できるようにします。
結論
このツールの強みは、ボタン駆動のロジックにあり、チャート上でのテストや分析を迅速かつインタラクティブにおこなえる点です。ボタンを一度クリックするだけで、平均値、標準偏差、最頻値、中央値、パーセンタイルなどの統計レベルが即座に計算され、ラベル付きの水平線としてチャート上に表示されます。これにより手動で計算する必要がなくなり、テスト中にレベルを素早く比較、描画、削除することが可能になります。範囲の適用やリセット、スナップショットの保存、レベルの直接操作などをダッシュボードから行えるため、価格が異なる統計ゾーンにどのように反応するかを探る際の強力なアシスタントとなり、リサーチおよびリアルタイム分析の効率化に役立ちます。
追加の図は、計算されたレベルが実際の市場の動きとどのように整合しているかを示すことで、この結論をさらに裏付けています。紫色の破線(25パーセンタイル)や黄色の実線(平均値+偏差)は、これらの統計出力が実際にサポートおよびレジスタンスゾーンとして一貫して機能することを示しています。価格がこれらのレベル付近でどのように反応するかを見ることで、ツールが重要なバウンスやリトレースメントのエリアを定義し、ブレイクアウトや反転を予測する信頼できるポイントを特定できることが明確になります。
このツールは、統計計算を通じて価格レベルの分析を支援するために設計されています。取引を自動で実行するものではなく、最終的な判断はトレーダー自身がおこないます。自身の取引戦略と併用することで最も効果を発揮するレベル補助EAと考えてください。目的は、価格が統計的に算出されたレベルとどのように相互作用するかを明確にし、ブレイクアウト、反転、リトレースメントの判断に役立つコンテキストを提供することです。今後は、プライスアクション分析をさらに深め、情報に基づいた取引判断をサポートするツールの開発を進めていく予定です。
MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/19697
警告: これらの資料についてのすべての権利はMetaQuotes Ltd.が保有しています。これらの資料の全部または一部の複製や再プリントは禁じられています。
この記事はサイトのユーザーによって執筆されたものであり、著者の個人的な見解を反映しています。MetaQuotes Ltdは、提示された情報の正確性や、記載されているソリューション、戦略、または推奨事項の使用によって生じたいかなる結果についても責任を負いません。
MQL5での取引戦略の自動化(第35回):ブレーカーブロック取引システムの作成
MQL5での取引戦略の自動化(第34回):R²適合度を用いたトレンドラインブレイクアウトシステム
初心者からエキスパートへ:MQL5を使用したバックエンド操作モニター
共和分株式による統計的裁定取引(第5回):スクリーニング
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索