English Deutsch
preview
MetaTrader 5用シグマスコアインジケーター:単純な統計的異常検出器

MetaTrader 5用シグマスコアインジケーター:単純な統計的異常検出器

MetaTrader 5インディケータ |
34 0
TECHAURORA - FZCO
Dominic Michael Frehner

はじめに

多くのトレーダーは早い段階で、価格が滑らかで予測可能な動き方をするわけではないことを学びます。市場には「静かな」局面がある一方で、突然のスパイク、急落、ショック的な値動きも発生します。実務的には、その値動きが通常のもの(直近の市場において典型的なもの)なのか、それとも異常なもの(直近の挙動と比較して稀なもの)なのかを判定するために、リターン(収益率)を標準化し「シグマ単位」で表現する方法が用いられます。

これがシグマスコアインジケーターの役割です。直近のリターンをz得点(標準得点)に変換し、別ウィンドウにプロットします。簡単に言うと、シグマスコアは次の問いに答えるものです。

「現在のバーにおけるリターンは、過去N本の平均リターンから標準偏差でどれだけ離れているか?」

そのため、初心者にとっても扱いやすい指標です。複雑な取引理論は不要で、「0付近は通常の状態」「大きな正負の値は異常に強い値動き」を意味するという点だけ理解すれば読み解けます。


シグマスコアとは何か、そして何を意味するのか

統計学においてz得点は、ある値が平均からどれだけ離れているかを標準偏差単位で測る指標です。基本式は以下の通りです。

ここでxは現在の値、μは平均値、σは標準偏差を表します。 金融分野では、この考え方をリターンに適用することが一般的です。

このインジケーターでは対数収益率を使用します。定義は以下の通りです。

この定義はクオンツ金融で非常に一般的です。対数収益率は時間方向に対して加法性を持つため、複数期間にわたって「足し合わせる」ことができ、扱いやすいという特性があります。したがってこのインジケーターでは

  • x:現在の対数収益率
  • μ:InpLookback本の平均対数収益率
  • σ:InpLookback本における対数収益率の標準偏差

なお、対数収益率の標準偏差は、過去のボラティリティを表す標準的な手法でもあります。


デフォルトの閾値が±2.0であることが多い理由

リターンが完全な正規分布に従うと仮定した場合(現実の市場はそうではありませんが、有用な近似として)、観測値の約95%は平均から±2標準偏差の範囲に収まります。

このため多くのトレーダーは次のように解釈します。

  • シグマスコア > +2:通常よりも非常に強い上昇リターン
  • シグマスコア < −2:通常よりも非常に強い下落リターン

重要な注意点として、実際の金融市場のリターンはファットテール(厚い裾)を持つことが多く、極端な値動きは理論上の正規分布よりも頻繁に発生します。そのため、これらの閾値は確率的な保証ではなく、「ストレス/異常検知ゾーン」として実務的に扱うのが適切です。

注意:実装ではローリングウィンドウに現在バーのリターンも含まれます(j = 0から開始)。そのため、現在の観測値を含んだサンプルに対してz得点が計算される点に注意してください。


インジケーターの構築

ステップ1:インジケーターの骨組み(プロパティとプロット)の作成

MetaEditorで新しいインジケーターファイルを作成した際、最初に重要となるのが#propertyブロックです。ここでは、MetaTrader 5に対してインジケーターの表示方法を定義します。

#property indicator_separate_window
#property indicator_buffers 1
#property indicator_plots   1
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrBlue
#property indicator_width1  2
#property indicator_label1  "Sigma Score"

この設定では以下をおこないます。

  • indicator_separate_window:インジケーターをメインチャートではなくサブウィンドウに表示します。
  • indicator_buffers = 1、indicator_plots = 1:シグマスコア1本のみを描画するため、バッファとプロットはいずれも1つです。
  • DRAW_LINE:提供された値を基にラインとして描画します。
  • 色/ラベル:表示されるライン名を「Sigma Score」とし、青色で描画します。

ステップ2:入力パラメータ(インジケーターの可変設定)

入力パラメータは、MetaTrader 5上でユーザーが変更可能な設定値です。

input int    InpLookback = 20;
input double InpUpperThreshold = 2.0;
input double InpLowerThreshold = -2.0;

各パラメータの意味は以下の通りです。

  • InpLookback:平均およびボラティリティ(標準偏差)の計算において「直近」とみなす期間のバー数
  • InpUpperThreshold、InpLowerThreshold:閾値ライン(一般的には±2付近が「大きな変動ゾーン」として用いられる)

ステップ3:バッファと変数

インジケーターは配列(バッファ)に値を書き込むことで描画されます。1本のラインのみを描画するため、出力バッファは1つです。

double SigmaBuffer[];
SigmaScoreは単一の時系列データとしてプロットされるため、バッファも1つで十分です。

ステップ4:初期化

OnInit()はインジケーターをチャートに適用した際に一度だけ実行される初期化関数です。MetaTrader 5がプロットを作成するために必要なすべてのものを準備します。

int OnInit()
{
    // Indicator buffers mapping
    SetIndexBuffer(0, SigmaBuffer, INDICATOR_DATA);
    ArraySetAsSeries(SigmaBuffer, true);
    
    // Set horizontal levels
    IndicatorSetInteger(INDICATOR_LEVELS, 3);
    IndicatorSetDouble(INDICATOR_LEVELVALUE, 0, 0.0);
    IndicatorSetDouble(INDICATOR_LEVELVALUE, 1, InpUpperThreshold);
    IndicatorSetDouble(INDICATOR_LEVELVALUE, 2, InpLowerThreshold);
    
    IndicatorSetInteger(INDICATOR_LEVELCOLOR, 0, clrGray);
    IndicatorSetInteger(INDICATOR_LEVELCOLOR, 1, clrRed);
    IndicatorSetInteger(INDICATOR_LEVELCOLOR, 2, clrGreen);
    
    IndicatorSetInteger(INDICATOR_LEVELSTYLE, 0, STYLE_DOT);
    IndicatorSetInteger(INDICATOR_LEVELSTYLE, 1, STYLE_DASH);
    IndicatorSetInteger(INDICATOR_LEVELSTYLE, 2, STYLE_DASH);
    
    // Set indicator labels
    PlotIndexSetString(0, PLOT_LABEL, "Sigma Score");
    IndicatorSetString(INDICATOR_SHORTNAME, "Sigma Score (" + IntegerToString(InpLookback) + ")");
    IndicatorSetInteger(INDICATOR_DIGITS, 2);
    
    return(INIT_SUCCEEDED);
}

このセクションでは以下をおこないます。

  1. バッファのマッピングと配列設定
    SetIndexBuffer(0, SigmaBuffer, INDICATOR_DATA)SigmaBuffer[]をプロット0に紐付けます。この時点から、SigmaBuffer[i] = ...のような値を割り当てると、MetaTraderはそれをインジケーターラインとして描画します。
    ArraySetAsSeries(SigmaBuffer, true)バッファを価格配列のように扱うようにします。インデックス0は最新のバー、インデックス1は1本前のバー、というように並びます。これにより、close[0]close[1]といった価格配列と一貫したインデックス管理ができるようになります。

  2. 水平ガイドレベル(スタイル設定)
    IndicatorSetInteger(INDICATOR_LEVELS, 3):3本の水平基準線を作成します。これらは0.0、上側の閾値、下側の閾値に設定します。これによって、ゼロ付近の「通常範囲」と、異常に大きい正負の値を視覚的に明確に分けることができます。
    さらに、見やすさのためにこれらのレベルにスタイルを設定します。ゼロラインはグレーの点線、閾値ラインは赤と緑の破線にすることで、極端な値が一目で分かるようにしています。

  3. 名称と表示形式
    プロットのラベルを「Sigma Score」に設定し、さらにルックバック期間を含む短い名前を定義します。たとえば「Sigma Score (20)」「Sigma Score (50)」のように表示されます。これにより、異なる期間設定で複数のインジケーターをチャートに追加した場合でも区別しやすくなります。
    最後に、IndicatorSetInteger(INDICATOR_DIGITS, 2)を設定し、表示を小数点以下2桁に整えて、見た目をすっきりさせています。

ステップ5:OnCalculate()での実際のシグマスコアの計算

OnCalculate()は、MetaTrader 5が各バーのインジケーター値を要求してくる場所です。この場合は、各バーごとにSigma Scoreを計算し、それをSigmaBuffer[]に書き込むことで、チャート上にラインとして描画できるようにします。まず最初に、最低限のデータがあるかを確認します。

if(rates_total < InpLookback + 1) 
{
    return(0);
}

InpLookback分のバーが揃っていないとローリング統計が計算できませんし、さらに対数収益率の計算にはclose[i]とclose[i+1]の2本のバーが必要です。そのため、履歴が不足している場合はそのまま終了し、何も描画しません。次に、MetaTrader 5に対して配列を時系列として扱うよう指定します。

ArraySetAsSeries(close, true);
ArraySetAsSeries(time, true);

time[]も同様に時系列として設定しています(このバージョンでは直接使いませんが、インデックスの一貫性を保つためです)。 これにより、close[0] が最新のバー、close[1]が1本前という形になります。これは重要なポイントです。なぜなら、ここでのループは「過去のデータを前方参照する形」、つまりi + j i + 1を使ってインデックスが大きい方向(古いバー側)へ参照する構造に依存しているからです。次に、実際にどれだけのバーを再計算する必要があるかを決めます。MetaTrader 5ではインジケーターが何度も繰り返し呼び出されるため、毎回チャート全体を再計算してしまうと動作が遅くなってしまいます。そのため、初回実行とその後の更新処理を分けて扱います。

int start;
if(prev_calculated == 0)
{
    start = rates_total - InpLookback - 1;
    ArrayInitialize(SigmaBuffer, EMPTY_VALUE);
}
else
{
    start = rates_total - prev_calculated;
}

最初の呼び出し(prev_calculated == 0)のときは、まずバッファをEMPTY_VALUEで初期化します。これは重要です。 EMPTY_VALUEはMetaTrader 5に対して「ここは描画しない」という意味を持つため、まだ計算できない領域に誤ったラインが表示されるのを防ぐことができます。また、startもここで設定し、十分なLookbackウィンドウが確保できる最も古いバーから処理を開始するようにします。一方で2回目以降の呼び出しでは、新しく生成されたバーだけを再計算するようにします。そしていよいよ、実際のσスコアの計算に入ります。更新が必要なバーを対象にループを回していきます。

for(int i = start; i >= 0; i--)
{
    if(i + InpLookback >= rates_total)
    {
        SigmaBuffer[i] = EMPTY_VALUE;
        continue;
    }

    double sum_returns = 0;
    double sum_squared = 0;
    int valid_count = 0;

    for(int j = 0; j < InpLookback; j++)
    {
        int idx1 = i + j;
        int idx2 = i + j + 1;

        if(idx2 < rates_total && close[idx1] > 0 && close[idx2] > 0)
        {
            double log_return = MathLog(close[idx1] / close[idx2]);
            sum_returns += log_return;
            sum_squared += log_return * log_return;
            valid_count++;
        }
    }

    if(i + 1 < rates_total && close[i] > 0 && close[i + 1] > 0)
    {
        double current_return = MathLog(close[i] / close[i + 1]);

        if(valid_count >= InpLookback)
        {
            double mean = sum_returns / valid_count;
            double variance = (sum_squared / valid_count) - (mean * mean);
            if(variance < 0) variance = 0;

            double stdev = MathSqrt(variance);

            if(stdev > 0.0000001)
                SigmaBuffer[i] = (current_return - mean) / stdev;
            else
                SigmaBuffer[i] = 0;
        }
        else
        {
            SigmaBuffer[i] = EMPTY_VALUE;
        }
    }
    else
    {
        SigmaBuffer[i] = EMPTY_VALUE;
    }
}

このセクションでは、全体の処理を堅牢な形で実行しています。

まず、十分なルックバック期間を確保できないバーはスキップします。i + InpLookbackが利用可能な履歴を超えてしまう場合、そのバーにはSigmaBuffer[i] = EMPTY_VALUEを設定し、処理を続行します。これにより、不完全なデータに基づいた値がチャートに描画されることを防ぎます。次に、ルックバック期間内の対数収益率の統計量を計算します。ここではリターンを別配列に保存するのではなく、その場で計算し、2つの合計値として累積します。

  • sum_returns:対数収益率の合計(平均計算用)
  • sum_squared:対数収益率の二乗の合計(分散)

また、valid_countで有効なデータ数も同時にカウントします。対数収益率の計算には価格が正である必要があるため、close > 0を条件としてチェックし、無効なデータは単純にスキップします。次に、標準化対象となる現在バーの対数収益率を計算します。この値が有効であり、かつ十分なルックバックデータ(valid_count >= InpLookback)が揃っている場合にのみ統計計算をおこないます。

  • 平均:sum_returns / valid_count
  • 分散:(sum_squared / valid_count) - mean²
  • 標準偏差:sqrt(variance)

最後に、シグマスコア(z得点)を計算します。

コードでは以下のようになります。

SigmaBuffer[i] = (current_return - mean) / stdev;

また、ゼロに非常に近い値での割り算による不安定なスパイクを防ぐために、小さな安全閾値(stdev > 0.0000001)を設けています。ボラティリティが実質的に0の場合は、値は0として扱います。最後に、MetaTrader 5に対してどこまで計算が完了したかを知らせるためにrates_total を返します。

return(rates_total);

このようにして、完全なローリングz得点(シグマスコア)エンジンを実装しています。この実装では、以下の点に対応しています。

  • InpLookback + 1本以上のデータがあることを保証
  • prev_calculatedを使った効率的な部分更新
  • EMPTY_VALUEによる未計算領域の非表示処理
  • 対数収益率、ローリング平均・標準偏差の計算
  • 最終的なシグマスコアを単一のプロットバッファに安全に書き込む(ゼロ除算によるアーティファクトは発生しない)

シグマスコアのデモンストレーション


取引におけるシグマスコアの活用方法

シグマスコアは、それ単体で売買戦略を完成させるものではなく、「相場の状態認識」や「タイミング判断」のためのツールとして使うのが適しています。極端な値が出た瞬間に、それを平均回帰として見るのか、それともモメンタムとして見るのかを判断する材料になります。

平均回帰取引をおこなう場合、極端なシグマ値は「行き過ぎ」を示すシグナルとして扱います。たとえば、シグマスコアが-2を下回る場合、直近20本に対して異常に強い下落が起きている状態です。レンジ相場では、トレーダーはまず価格が安定するパターンを探すことが多いです。たとえば、価格が新しい安値を更新しなくなったり、強気のローソク足パターンが出現したりする場合です。そのような状況を確認したうえで、最近の平均水準へ戻る動きを想定する、いわゆる平均回帰を期待します。

一方でモメンタムを重視する取引では、極端なシグマ値を「ショックの継続シグナル」として捉えます。たとえば、シグマスコアが+2を上回り、かつ市場が明確な上昇トレンドにある場合、それは単なる行き過ぎではなく、強いインパルス的な値動きである可能性があります。このような場合には、すぐに逆張りでショートを狙うのではなく、早すぎるエントリーを避けたり、押し目を待って順張りで入るタイミングを計るといった使い方になります。

どちらの場合においても、シグマスコアはリスク管理の面でも有効です。極端なシグマの値は市場が急速に動いている状態を示しており、その結果、スプレッドが拡大したり、ストップロスに必要な余裕が大きくなったりすることがあります。そのため、シグナルとして直接売買に使わない場合でも、ポジションサイズの調整や期待する値動きの前提を見直すための指標として役立ちます。


結論

本記事では、ある共通の取引上の問題から出発しました。それは、価格の動きが本当に「極端」なのか、それとも現在の相場環境における単なるノイズなのかを判断することが難しいという点です。シグマスコアインジケーターを段階的に構築することで、その問いに対してシンプルかつ客観的に答える方法を得ることができます。

今回作成したMetaTrader 5のインジケーターでは、対数収益率を計算し、それに対して設定可能なルックバック期間でローリング平均と標準偏差を求め、最新のリターンをz得点(シグマスコア)へ変換しています。その結果、相場の動きを「シグマ単位」で解釈できるようになります。0付近の値は通常の値動きを示し、±2といった閾値を超えるような値は、直近の環境と比較して異常に強い上昇または下落のインパルスを示します。

さらに重要なのは、この出力を実際の取引にどう活用するかという点です。シグマスコアは市場のストレスメーターとして機能し、平均回帰戦略とモメンタム戦略のどちらにも判断材料を提供します。また、相場が異常な状態にあることを認識することで、ポジションサイズの調整やストップ幅の見直しといったリスク管理にも役立ちます。すべてのコードが揃ったことで、今後はアラート機能やトレンドフィルター、マルチタイムフレームのロジックなどを追加しながら拡張していくことができますが、中心となる考え方は変わりません。それは、最新の値動きが過去と比べてどれだけ異常なのかを定量化するというシンプルなアイデアです。


参照文献

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

添付されたファイル |
Sigma_Score.mq5 (5.83 KB)
ラリー・ウィリアムズの『市場の秘密』(第5回):MQL5におけるボラティリティブレイクアウト戦略の自動化 ラリー・ウィリアムズの『市場の秘密』(第5回):MQL5におけるボラティリティブレイクアウト戦略の自動化
ラリー・ウィリアムズのボラティリティブレイクアウト戦略をMQL5で自動化する方法を、実践的なステップで解説します。日次のレンジ拡張の計算方法、買いと売りレベルの導出、値幅に基づくストップロスとリスクリワードに基づく利益目標によるリスク管理、そしてMetaTrader 5で動作するプロフェッショナルなエキスパートアドバイザー(EA)の構造まで学ぶことができます。これは、ラリー・ウィリアムズの市場概念を完全にテスト可能かつ実運用できる自動売買システムへと変換したいトレーダーや開発者向けに設計されています。
データベースは簡単(第1回):SQLiteを用いたMQL5向け軽量ORMフレームワーク データベースは簡単(第1回):SQLiteを用いたMQL5向け軽量ORMフレームワーク
MQL5においてSQLiteデータをORMレイヤーを通して管理する方法を体系的に紹介します。エンティティモデリングとデータベースアクセスのためのコアクラス、フルエントなCRUD API、OnGet/OnSetによるリフレクション的フック、そしてモデルを簡単に定義するためのマクロなどを取り上げます。実用的なコード例として、テーブル作成、フィールドのバインド、データの挿入、更新、取得、削除を示します。これにより開発者は、SQLを繰り返し記述することなく、再利用可能で型安全なコンポーネントを利用できるようになります。
MQL5入門(第33回):MQL5のAPIとWebRequest関数の習得(VII) MQL5入門(第33回):MQL5のAPIとWebRequest関数の習得(VII)
本記事では、MQL5を使用してGoogle Generative AI APIをMetaTrader 5に統合する方法を解説します。APIリクエストの構築、サーバー応答の処理、AI生成コンテンツの抽出、レート制限の管理、そして結果をテキストファイルに保存して簡単に参照できるようにする方法を学びます。
ラリー・ウィリアムズの『市場の秘密』(第4回):MQL5における短期的スイングハイとスイングローの自動化 ラリー・ウィリアムズの『市場の秘密』(第4回):MQL5における短期的スイングハイとスイングローの自動化
MQL5を使って、ラリー・ウィリアムズの短期スイングパターンの自動化を習得していきます。このガイドでは、非ランダムな市場構造を活用する、完全に設定可能なエキスパートアドバイザー(EA)を開発します。堅牢なリスク管理と柔軟なエグジットロジックの統合方法も解説し、システマティックな戦略開発とバックテストのための確かな基盤を提供します。