English Русский 中文 Español Deutsch Português
preview
ペアトレード:Zスコアの差に基づく自動最適化機能を備えたアルゴリズム取引

ペアトレード:Zスコアの差に基づく自動最適化機能を備えたアルゴリズム取引

MetaTrader 5トレーディングシステム |
41 8
Yevgeniy Koshtenko
Yevgeniy Koshtenko

はじめに

多くのトレーダーが複雑なインジケーターを追い求め、「聖杯」と呼ばれる手法を探し続ける取引の世界において、実際にはシンプルな統計的原理に基づく戦略が高い効果を発揮することがあります。本記事では、ペアトレードという興味深い分野に焦点を当て、現代的なアルゴリズムアプローチによって、このクラシックな戦略でどのように新たなレベルの効率性を実現できるのかを探っていきます。

ペアトレードは、もともと1980年代にウォール街のクオンツトレーダーによって開発され、長らく大手ヘッジファンドや機関投資家だけが利用できる高度な戦略とされてきました。しかし、技術の進歩やMetaTraderのような取引プラットフォームの普及により、現在では個人トレーダーでもこの戦略を活用できるようになっています。

本記事では、古典的なペアトレードの原則と、最新の最適化技術および市場環境への自動適応機能を組み合わせた、実践的なEAをベースに解説を進めます。このアプローチにより、相関の高い資産間に発生する短期的な価格乖離を検出できるだけでなく、市場構造の変化にも柔軟に対応することが可能になります。


ペアトレードの概念:統計学の応用

ペアトレードは、相関関係にある資産の値動きにおける統計的な乖離を利用するマーケットニュートラル型の戦略です。その基本的な考え方はシンプルかつ合理的です。すなわち、相関関係にある2つの金融商品の価格差が通常以上に拡大した場合、その関係性は将来的に過去の平均値へ回帰する可能性が高い、という前提に基づいています。

戦略の数学的基盤

この戦略は、「相関(Correlation)」と「定常性(Stationarity)」という2つの重要な統計概念に基づいています。相関とは、2つの変数間に存在する統計的な関係性を示す指標であり、一方の変数の変化が他方の変数とどれほど連動しているかを表します。金融市場において、2つの資産間の相関係数は-1(完全な負の相関)から+1(完全な正の相関)までの範囲を取ります。

一方、定常性とは、時系列データにおいて平均値、分散、自己相関といった統計的特性が時間の経過によって変化しない性質を指します。ペアトレードにおいて重要なのは、2つの資産間の価格関係が定常的であること、つまり価格差が長期的に平均値へ回帰する傾向を持つことです。

この考え方は、特にFXトレーダーにとって非常に重要です。為替ペアはしばしば強い相関性を示すためです。たとえば、EURUSDとGBPUSDは通常、同じ方向へ動く傾向がありますが、その変動幅や一時的な乖離には違いがあります。そして、この乖離こそが潜在的な利益機会となります。

実例

EURUSDとGBPUSDのペアを例に考えてみましょう。これらの通貨ペアは、ユーロ圏と英国の地理的近接性および経済的結びつきの強さから、歴史的に高い正の相関を示しています。

ある時点でEURUSDがGBPUSDよりも速いペースで上昇した場合、EURUSDとGBPUSDの価格比率も上昇します。この比率が統計的に有意なレベルで過去平均を上回った場合、平均回帰が発生する可能性が高いと考えられます。このような状況では、ペアトレード戦略では、EURUSD(割高な資産)を売り、GBPUSD(割安な資産)を買うというポジションを提案します。

マーケットニュートラル戦略の利点

ペアトレードの最大の利点は、市場全体の方向性への依存度が比較的低い点にあります。相関性のある資産に対して同時にロングとショートを建てるため、市場全体に対する実質的なポジションはほぼ中立となります。その結果、相場が上昇局面であっても下落局面であっても利益を狙うことが可能です。これは、特に高いボラティリティや市場の不確実性が高まる局面において、大きなメリットとなります。

主要指標としてのZスコア

このアルゴリズムでは、Zスコアを主要指標として使用しています。Zスコアは、現在の価格関係が過去平均からどの程度乖離しているかを、標準偏差単位で表す統計指標です。

Z-score = (Current Ratio - Average Ratio) / Standard deviation

一定の閾値(例:+2.0)を超えるZスコアは、統計的に有意な外れ値であることを示します。統計理論上、このような状態は平均値へ回帰する可能性が高いと考えられます。

Zスコアは、現在の価格関係が過去データと比較してどの程度「異常」であるかを定量化する指標です。たとえば、+2.0という値は、現在の比率が平均値から2標準偏差離れていることを意味します。正規分布を前提とした場合、このような事象は約2.3%の確率でしか発生しません。

Zスコアの解釈

Z-score > 0の場合、第一通貨ペア(Symbol1)は第二通貨ペア(Symbol2)に対して割高な状態です。Z-score < 0の場合、第一通貨ペア(Symbol1)は第二通貨ペア(Symbol2)に対して割安な状態です。|Z-score| < 1の場合、価格比率は通常レンジ内にあります。1 < |Z-score| < 2の場合、通常値から中程度の乖離が発生しています。2 < |Z-score| < 3の場合、有意な外れ値であることを示しており、潜在的なエントリーポイントとなる可能性があります。|Z-score| > 3の場合、極端な外れ値であることを示しており、平均回帰が発生する可能性が高い状態です。

最適な計算期間の選定

Zスコア計算に使用する期間の選択は、戦略パフォーマンスに大きく影響する重要なパラメータです。期間が短すぎる場合はノイズや誤シグナルが増加し、逆に長すぎる場合は短期的な取引機会を逃す可能性があります。

EAでは、この問題に対応するため、過去データに基づいてZスコア計算期間を自動最適化しています。これにより、現在の市場環境に適応したパラメータ設定を維持できます。


アルゴリズムアーキテクチャ

EAは、異常な乖離を統計的に検出する仕組み、リアルタイムでの動的パラメータ最適化、そして相関変化を考慮した適応的リスク管理という3つの主要原則に基づいています。

EAの全体構造

このアルゴリズムは、データ収集・前処理、統計解析、売買判断、自己最適化、リスク管理の各モジュールで構成されています。データ収集モジュールは通貨ペアの過去および現在の価格を取得し、フィルタリングおよび正規化をおこないます。統計解析モジュールでは価格比、Zスコア、および通貨ペア間の相関を計算します。売買判断モジュールはエントリーポイントを決定し、保有ポジションを管理します。最適化モジュールは定期的にパラメータの組み合わせをテストし、最適な設定を選択します。リスク管理モジュールは最適なポジションサイズを計算し、連続損失を制御します。

比率とZスコアの計算

アルゴリズムの中核となるのは、価格比を計算し、それをZスコアへ変換する関数です。

void CalculateRatioAndZScore()
{
    // Price ratio calculation
    for(int i = 0; i < CurrentZScorePeriod; i++)
    {
        if(prices2[i] == 0) continue;
        ratio[i] = prices1[i] / prices2[i];
    }
    
    // Calculate the average
    double mean = 0;
    for(int i = 0; i < CurrentZScorePeriod; i++)
    {
        mean += ratio[i];
    }
    mean /= CurrentZScorePeriod;
    
    // Calculation of standard deviation
    double stdDev = 0;
    for(int i = 0; i < CurrentZScorePeriod; i++)
    {
        stdDev += MathPow(ratio[i] - mean, 2);
    }
    stdDev = MathSqrt(stdDev / CurrentZScorePeriod);
    
    // Calculate Z-score
    for(int i = 0; i < CurrentZScorePeriod; i++)
    {
        if(stdDev == 0)
            zscore[i] = 0;
        else
            zscore[i] = (ratio[i] - mean) / stdDev;
    }
}

このスニペットは、価格比をどのように計算し、Zスコアへ変換するかを示しています。標準偏差がゼロに近い場合のエッジケースにも対応しており、これによりアルゴリズムの安定性が確保されています。

相関の追跡とエントリーポイントの検出

本手法における重要な革新の一つは、最適なエントリーポイントを決定するために動的な相関追跡を使用している点です。

// The logic for opening new positions is to enter when the correlation is at a minimum
if(!isPositionOpen)
{
    double currentCorrelation = correlationHistory[0];
    double minCorrelation = GetMinimumCorrelation();
    
    // If the current correlation is close to the minimum and the Z-score exceeds the threshold
    if(MathAbs(currentCorrelation - minCorrelation) < 0.01 && 
       MathAbs(currentZScore) >= CurrentEntryThreshold)
    {
        // Calculate lot size based on risk
        double riskLot = CalculatePositionSize();
        
        if(currentZScore > 0) // Symbol1 is overvalued, Symbol2 is undervalued
        {
            // Sell Symbol1 and buy Symbol2
            if(OpenPairPosition(POSITION_TYPE_SELL, Symbol1, 
                               POSITION_TYPE_BUY, Symbol2, riskLot))
            {
                Log("Paired position opened: SELL " + Symbol1 + ", BUY " + Symbol2);
                isPositionOpen = true;
            }
        }
        else // Symbol1 is undervalued, Symbol2 is overvalued
        {
            // Buy Symbol1 and sell Symbol2
            if(OpenPairPosition(POSITION_TYPE_BUY, Symbol1, 
                               POSITION_TYPE_SELL, Symbol2, riskLot))
            {
                Log("Paired position opened: BUY " + Symbol1 + ", SELL " + Symbol2);
                isPositionOpen = true;
            }
        }
    }
}

この関数は、2つの値の配列(各銘柄の終値)を受け取り、-1から1までの範囲の相関係数を返します。

相関履歴の更新

相関関係の変化を効果的に追跡するために、相関関係の値の履歴を保存します。

void UpdateCorrelationHistory()
{
    // Shift the correlation history
    for(int i = CorrelationPeriod-1; i > 0; i--)
    {
        correlationHistory[i] = correlationHistory[i-1];
    }
    
    // Add a new correlation
    correlationHistory[0] = CalculateCurrentCorrelation();
}

double GetMinimumCorrelation()
{
    double minCorr = 1.0;
    for(int i = 0; i < CorrelationPeriod; i++)
    {
        if(correlationHistory[i] < minCorr)
            minCorr = correlationHistory[i];
    }
    return minCorr;
}

このモジュールにより、金融商品間の相関が局所的な最小値に達する瞬間を追跡することが可能となります。これは、エントリーをおこなうための追加的なシグナルとなります。

重要機能:パラメータの自動最適化

従来のペアトレード戦略は固定されたパラメータに依存しているため、時間の経過とともに有効性が低下するという問題を抱えています。本アルゴリズムはこの問題を解決するために、重要なパラメータを定期的に自動最適化します。

void Optimize()
{
    Print("Starting optimization...");
    
    optimizationResults.Clear();
    
    // Optimization ranges
    int zScorePeriodMin = 50, zScorePeriodMax = 200, zScorePeriodStep = 25;
    double entryThresholdMin = 1.5, entryThresholdMax = 3.0, entryThresholdStep = 0.25;
    double exitThresholdMin = 0.0, exitThresholdMax = 1.0, exitThresholdStep = 0.25;
    
    // Iterate over all combinations of parameters
    for(int period = zScorePeriodMin; period <= zScorePeriodMax; period += zScorePeriodStep)
    {
        for(double entry = entryThresholdMin; entry <= entryThresholdMax; entry += entryThresholdStep)
        {
            for(double exit = exitThresholdMin; exit <= exitThresholdMax; exit += exitThresholdStep)
            {
                // Test parameters
                double profit = TestParameters(period, entry, exit);
                
                OptimizationResult* result = new OptimizationResult();
                result.zScorePeriod = period;
                result.entryThreshold = entry;
                result.exitThreshold = exit;
                result.profit = profit;
                
                optimizationResults.Add(result);
            }
        }
    }
    
    // Find the best result and apply new parameters
    OptimizationResult* bestResult = NULL;
    for(int i = 0; i < optimizationResults.Total(); i++)
    {
        OptimizationResult* currentResult = optimizationResults.At(i);
        if(bestResult == NULL || currentResult.profit > bestResult.profit)
        {
            bestResult = currentResult;
        }
    }
    
    if(bestResult != NULL)
    {
        // Update the EA internal parameters
        CurrentZScorePeriod = bestResult.zScorePeriod;
        CurrentEntryThreshold = bestResult.entryThreshold;
        CurrentExitThreshold = bestResult.exitThreshold;
        
        // Update arrays
        ArrayResize(prices1, CurrentZScorePeriod);
        ArrayResize(prices2, CurrentZScorePeriod);
        ArrayResize(ratio, CurrentZScorePeriod);
        ArrayResize(zscore, CurrentZScorePeriod);
        
        Print("Optimization complete. New parameters: ZScorePeriod = ", CurrentZScorePeriod, 
              ", EntryThreshold = ", CurrentEntryThreshold, ", ExitThreshold = ", CurrentExitThreshold);
    }
}

この関数は、Zスコアの計算期間、エントリー閾値、エグジット閾値のさまざまな組み合わせを繰り返し実行し、過去のデータに基づいて最適な組み合わせを選択します。従来の手動最適化は一度しか実行されませんが、このアルゴリズムは変化する市場状況に継続的に適応し、結果の長期的な安定性を向上させます。

最適な最適化頻度の選択

最適化頻度は、システム性能に影響を与える重要なパラメータです。最適化を頻繁におこないすぎると、アルゴリズムが「過学習」状態に陥り、不要な計算リソースの消費につながる可能性があります。一方で、最適化の頻度が低すぎると、市場環境の変化に追従できなくなる可能性があります。

本アルゴリズムでは、一定のティック数の経過後に最適化が実行される仕組みを採用しており、適応性と安定性のバランスを確保しています。

// Auto optimization
if(AutoOptimize && ++tickCount >= OptimizationPeriod)
{
    Optimize();
    tickCount = 0;
}

OptimizationPeriodパラメータのデフォルト値は5000ティックに設定されていますが、取引対象となる銘柄の特性や戦略の時間軸に応じて変更することが可能です。

スマートパラメータテスト

各パラメータセットについて、過去データを用いたシミュレーションを実行します。

double TestParameters(int period, double entry, double exit)
{
    // Initialize test arrays
    double test_prices1[], test_prices2[], test_ratio[], test_zscore[];
    ArrayResize(test_prices1, period);
    ArrayResize(test_prices2, period);
    ArrayResize(test_ratio, period);
    ArrayResize(test_zscore, period);
    
    double close1[], close2[];
    ArraySetAsSeries(close1, true);
    ArraySetAsSeries(close2, true);
    
    int copied1 = CopyClose(Symbol1, PERIOD_CURRENT, 0, MinDataPoints, close1);
    int copied2 = CopyClose(Symbol2, PERIOD_CURRENT, 0, MinDataPoints, close2);
    
    if(copied1 < MinDataPoints || copied2 < MinDataPoints)
    {
        Print("Not enough data for testing");
        return -DBL_MAX;
    }
    
    double profit = 0;
    bool inPosition = false;
    double entryPrice1 = 0, entryPrice2 = 0;
    ENUM_POSITION_TYPE posType1 = POSITION_TYPE_BUY, posType2 = POSITION_TYPE_BUY;
    
    // Create a correlation history for testing
    double testCorrelations[];
    ArrayResize(testCorrelations, CorrelationPeriod);
    
    // Fill in the initial data
    for(int i = 0; i < period; i++)
    {
        test_prices1[i] = close1[MinDataPoints - 1 - i];
        test_prices2[i] = close2[MinDataPoints - 1 - i];
    }
    
    // Inverse simulation on historical data
    for(int i = period; i < MinDataPoints; i++)
    {
        // Update data, calculate Z-score and simulate trading decisions 
        // ...
        
        double currentZScore = test_zscore[0];
        
        // Simulate closing positions
        if(inPosition)
        {
            double currentProfit = 0;
            
            if(posType1 == POSITION_TYPE_BUY)
                currentProfit += (close1[MinDataPoints - 1 - i] - entryPrice1) * 10000;
            else
                currentProfit += (entryPrice1 - close1[MinDataPoints - 1 - i]) * 10000;
                
            if(posType2 == POSITION_TYPE_BUY)
                currentProfit += (close2[MinDataPoints - 1 - i] - entryPrice2) * 10000;
            else
                currentProfit += (entryPrice2 - close2[MinDataPoints - 1 - i]) * 10000;
                
            if(currentProfit >= ProfitTarget)
            {
                profit += currentProfit;
                inPosition = false;
            }
        }
        
        // Simulate opening new positions
        if(!inPosition && i > CorrelationPeriod)
        {
            // Check entry conditions based on Z-score and correlation
            // ...
        }
    }
    
    return profit; // Return the resulting profit for the given set of parameters
}

過学習への対処

最適化された取引システムにおける主要な課題の一つは、過学習のリスクです。これは、システムが過去データに対して過度に調整され、その結果として汎化性能を失い、新しいデータへの対応力が低下する現象です。

本アルゴリズムでは、この問題に対処するために複数のアプローチを採用しています。第一に、最適化には過去データの一部のみを使用し、残りを検証用データ(アウトオブサンプルテスト)として保持します。第二に、探索対象とするパラメータを重要なものに限定することで、探索空間の次元を削減します。第三に、すべての値を網羅的に試すのではなく、適切な刻み幅(例:zScorePeriodStep = 25)を用いることで、過去データの特定の特徴に過度に適合することを防ぎます。最後に、定期的なパラメータ更新により、市場環境の変化に適応できるようにしています。

ポジション平均化の改良

本アルゴリズムにおける重要な革新の一つは、取引対象となる金融商品間の相関が低下した際における適応的なポジション管理です。

// Logic for averaging positions when correlation drops
if(isPositionOpen && EnableAveraging)
{
    double currentCorrelation = correlationHistory[0];
    
    // Check for a drop in correlation from the moment a position is opened
    if(averagingCount == 0)
    {
        // If this is the first check after opening a position
        initialCorrelation = currentCorrelation;
        averagingCount++;
    }
    else if(initialCorrelation - currentCorrelation > CorrelationDropThreshold)
    {
        // If the correlation has fallen below the threshold, add an averaging counter trade 
        double averagingLot = lastLotSize * AveragingLotMultiplier;
        
        // Check the type of our current positions
        ENUM_POSITION_TYPE posType1 = POSITION_TYPE_BUY;
        string posSymbol = "";
        bool foundPos1 = false, foundPos2 = false;
        ENUM_POSITION_TYPE posType2 = POSITION_TYPE_BUY;
        
        // Check our open positions
        for(int i = 0; i < ArraySize(posTickets); i++)
        {
            if(PositionSelectByTicket(posTickets[i]))
            {
                string symbol = PositionGetString(POSITION_SYMBOL);
                ENUM_POSITION_TYPE type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
                
                if(symbol == Symbol1)
                {
                    posType1 = type;
                    foundPos1 = true;
                }
                else if(symbol == Symbol2)
                {
                    posType2 = type;
                    foundPos2 = true;
                }
                
                if(foundPos1 && foundPos2) break;
            }
        }
        
        if(foundPos1 && foundPos2)
        {
            // Open counter trades with an increased lot
            ENUM_POSITION_TYPE reverseType1 = (posType1 == POSITION_TYPE_BUY) ? POSITION_TYPE_SELL : POSITION_TYPE_BUY;
            ENUM_POSITION_TYPE reverseType2 = (posType2 == POSITION_TYPE_BUY) ? POSITION_TYPE_SELL : POSITION_TYPE_BUY;
            
            if(OpenPairPosition(reverseType1, Symbol1, reverseType2, Symbol2, averagingLot))
            {
                Log("Averaging counter position opened: " + 
                    (reverseType1 == POSITION_TYPE_BUY ? "BUY " : "SELL ") + Symbol1 + ", " +
                    (reverseType2 == POSITION_TYPE_BUY ? "BUY " : "SELL ") + Symbol2);
                
                // Update the initial correlation for the next check
                initialCorrelation = currentCorrelation;
                averagingCount++;
            }
        }
    }
}

このアプローチにより、相関が低下した場合の損失を防ぐだけでなく、市場環境が有利な方向に進展した場合には利益を増加させる可能性も得られます。

平均化メカニズムとその正当性

ポジションのナンピン(平均化)は、価格が不利な方向に動いた際にポジションサイズを増加させるため、従来、非常に危険かつリスクの高い手法と考えられています。しかし、ペアトレードの文脈では状況が異なります。ここでは価格の変動ではなく、金融商品間の相関変化に基づいてポジションの平均化をおこないます。

2つの金融商品間の相関が低下した場合、それは2つの可能性を示唆します。第一に、これは一時的な乖離であり、近い将来に修正される可能性があること。第二に、市場構造に変化が生じている可能性です。前者の場合、追加のポジションを逆方向に(ロットを増加させながら)建てることで、相関が元の水準に戻れば、大きな利益を得ることが可能となります。後者の場合でも、初期リスクをヘッジするポジションを追加で構築することになるため、さらなる損失から保護されます。

ただし、本アルゴリズムには平均化の過度な拡大を防ぐための保護機構が組み込まれています。AveragingLotMultiplierパラメータは、各平均化におけるロットサイズの増加倍率を決定し、また平均化の総回数は相関履歴の追跡によって制御されます。

// If the correlation has fallen below the threshold, we add an averaging counter trade
double averagingLot = lastLotSize * AveragingLotMultiplier;

// Open counter trades with an increased lot
ENUM_POSITION_TYPE reverseType1 = (posType1 == POSITION_TYPE_BUY) ? POSITION_TYPE_SELL : POSITION_TYPE_BUY;
ENUM_POSITION_TYPE reverseType2 = (posType2 == POSITION_TYPE_BUY) ? POSITION_TYPE_SELL : POSITION_TYPE_BUY;

保護的なストップ注文およびテイクプロフィットの使用

コアとなる取引ロジックに加えて、本アルゴリズムは個々のポジションレベルでリスクを管理するために、ストップロスおよびテイクプロフィットを設定する機能を備えています。

// Calculate protective stop losses and take profits, if enabled
double sl1 = 0, sl2 = 0, tp1 = 0, tp2 = 0;
double point1 = SymbolInfoDouble(symbol1, SYMBOL_POINT);
double point2 = SymbolInfoDouble(symbol2, SYMBOL_POINT);

if(EnableProtectiveStops)
{
    if(type1 == POSITION_TYPE_BUY)
    {
        double ask1 = SymbolInfoDouble(symbol1, SYMBOL_ASK);
        sl1 = ask1 - ProtectiveStopPips * point1;
    }
    else
    {
        double bid1 = SymbolInfoDouble(symbol1, SYMBOL_BID);
        sl1 = bid1 + ProtectiveStopPips * point1;
    }
    
    // Similar calculations for the second instrument
    // ...
}

// Take profit calculation
if(EnableTakeProfit)
{
    // Calculations for take profits
    // ...
}

これらの保護メカニズムは、特に高いボラティリティを持つ市場や経済的に不安定な期間において重要です。そのような状況では、通常は高い相関を持つ金融商品であっても、値動きに大きな乖離が生じる可能性があります。

動的リスク管理

あらゆる取引システムの長期的な成功において重要な要素は、適切なリスク管理です。本アルゴリズムでは、口座残高に対する指定されたリスク割合に基づいて、ポジションサイズを自動的に計算します。

double CalculatePositionSize()
{
    double balance = AccountInfoDouble(ACCOUNT_BALANCE);
    double riskAmount = balance * RiskPercent / 100.0;
    
    double tickValue1 = SymbolInfoDouble(Symbol1, SYMBOL_TRADE_TICK_VALUE);
    double tickSize1 = SymbolInfoDouble(Symbol1, SYMBOL_TRADE_TICK_SIZE);
    double point1 = SymbolInfoDouble(Symbol1, SYMBOL_POINT);
    
    double lotStep = SymbolInfoDouble(Symbol1, SYMBOL_VOLUME_STEP);
    double minLot = SymbolInfoDouble(Symbol1, SYMBOL_VOLUME_MIN);
    double maxLot = SymbolInfoDouble(Symbol1, SYMBOL_VOLUME_MAX);
    
    // Risk-based lot calculation (using an approximate stop loss of 100 pips for calculation)
    double virtualStopLoss = 100;
    double riskPerPoint = tickValue1 * (point1 / tickSize1);
    double lotSizeByRisk = riskAmount / (virtualStopLoss * riskPerPoint);
    
    // Round to the nearest lot step
    lotSizeByRisk = MathFloor(lotSizeByRisk / lotStep) * lotStep;
    
    // Check for minimum and maximum lot size
    lotSizeByRisk = MathMax(minLot, MathMin(maxLot, lotSizeByRisk));
    
    return lotSizeByRisk;
}

このアプローチにより、資金が増加した際にはポジションサイズが比例的に増加し、ドローダウン時には減少するため、健全な資金管理の原則に合致した運用が可能となります。

連続損失制限メカニズム

さらに、本アルゴリズムには連続損失トレードの最大回数を制限するための保護機構が組み込まれています。

if(totalProfit < 0)
    consecutiveLosses++;
else
    consecutiveLosses = 0;

if(consecutiveLosses >= MaxConsecutiveLosses)
{
    Log("Reached maximum number of consecutive losses. Trading suspended.");
    // Implementation of logic for temporary suspension of trading
}

このメカニズムは、通貨ペア間の相関関係に構造的変化が生じている局面など、市場環境が本戦略の前提と合致しない期間において、継続的な取引をおこなうことを防ぐ役割を果たします。

適応型リスク管理

本手法の重要な特徴は、過去のボラティリティおよび直近の取引結果に応じてリスク水準を動的に調整する点にあります。直近の取引が好調な場合、アルゴリズムはポジションサイズをわずかに増加させる可能性があり、逆に損失が続いた場合にはポジションサイズを減少させます。

これは、RiskPercentパラメータを動的に調整することによって実現されます。

// Example of implementing adaptive risk management
double GetAdaptiveRiskPercent()
{
    double baseRisk = RiskPercent;
    
    // Reduce risk in case of consecutive losses
    if(consecutiveLosses > 0)
        baseRisk = baseRisk * (1.0 - 0.1 * consecutiveLosses);
    
    // Limit the minimum and maximum risk
    return MathMax(0.2, MathMin(baseRisk, 2.0));
}


実データを用いたペアトレードの効率性の評価

提案アルゴリズムの有効性を客観的に評価するため、複数の通貨ペアの過去データを用いた一連のテストを実施しました。ここでは、代表的な3つの通貨ペアにおけるテスト結果を示します。

EURUSDとAUDUSDのペアのテスト結果

この代表的な通貨ペアの組み合わせは、過去において高い相関関係を示します。過去5年間のテスト結果は以下の通りです。

  • 総利益:初期資金に対して+14%
  • 最大ドローダウン:0.33%
  • 勝率:59%
  • 平均保有時間:3時間42分
  • シャープレシオ:5.3

戦略は特にボラティリティが高まる局面で高いパフォーマンスを示しました。たとえば、BrexitやCOVID-19パンデミックのような期間では、一時的な相関の乖離が多数の取引機会を生み出しています。

AUDUSDとNZDUSDのペアのテスト結果

オーストラリアドルとニュージーランドドルの通貨ペアも、コモディティ依存型経済構造の類似性により高い相関を持ちます。テスト結果は以下の通りです。

  • 総利益:初期資金に対して+17%
  • 最大ドローダウン:0.21%
  • 勝率:56%
  • 平均保有時間:3時間26分
  • シャープレシオ:7.82

このペアの興味深い特徴は平均保有時間が短い点であり、これは相関の平均回帰がより迅速に発生することによって説明されます。

時間足に応じたパラメータ設定

最適な時間足の選択は、取引の時間的ホライゾンに依存します。中期取引(数日から数週間のポジション保有)では、日足または4時間足の使用が推奨されます。短期取引(数時間から数日)は、1時間足または15分足が適しています。

時間足を変更する際には、以下のようにパラメータを調整する必要があります。

  • 高時間足(D1、H4)では、Zスコアの計算期間を増加させ、エントリーおよびエグジットの閾値を減少させます。
  • 低時間足(H1、M15)では、Zスコアの計算期間を減少させ、エントリーおよびエグジットの閾値を増加させることで、市場ノイズをフィルタリングします。


発展の見通しとさらなる改善

現在のアルゴリズムのバージョンはすでに良好な結果を示していますが、さらなる改善のためにはいくつかの領域が存在します。

機械学習手法の応用

有望な方向性の一つは、金融商品間の相関ダイナミクスを予測するために機械学習手法を統合することです。たとえば、LSTM(長・短期記憶)ニューラルネットワークのようなアルゴリズムは、相関関係における複雑なパターンを効果的に捉え、その変化を高い精度で予測することが可能です。

多通貨ポートフォリオへの拡張

現在のアルゴリズムは2つの通貨ペアを対象としていますが、その概念は3つ以上のペア間の相関を同時に追跡する多通貨ポートフォリオへ拡張することが可能です。このアプローチにより、分散投資の効果をより効率的に活用し、より広範な市場環境においてトレーディング機会を発見することができます。

ファンダメンタル分析との統合

もう一つの有望な方向性は、意思決定アルゴリズムにファンダメンタル経済指標を統合することです。たとえば、金利差、インフレ率、その他のマクロ経済指標を考慮することで、通貨ペア間の長期的な相関変化をより正確に予測することが可能になります。


結論

本ペアトレードアルゴリズムは、自動最適化を備えており、比較的単純な統計的原理を現代的なアルゴリズム取引手法によって効率的な取引システムへと変換する方法を示しています。

本手法の最大の利点は適応性にあります。固定パラメータを持つシステムとは異なり、本アルゴリズムは市場環境の変化に継続的に適応し、長期的な有効性を維持します。

ただし、取引プロセスが自動化されているとはいえ、アルゴリズムの動作原理に対する深い理解と定期的な監視は、この戦略を成功させるために依然として必要な条件であることに注意が必要です。

次回の記事では、相関ダイナミクス予測のための機械学習手法や、遺伝的アルゴリズムによるパラメータ最適化を含むさらなる改善について解説します。

MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/17800

添付されたファイル |
PairsTradingOpt.mq5 (71.85 KB)
最後のコメント | ディスカッションに移動 (8)
Alamgir Malek
Alamgir Malek | 1 4月 2026 において 10:05
どのチャート、どのペアでも動作しますか?
Solomon Anietie Sunday
Solomon Anietie Sunday | 1 4月 2026 において 17:03
親愛なる作者様、私は平均回帰戦略についてよく知りません。このストラテジーはどのようなタイムフレームでも使用できるのでしょうか?
anandpandya
anandpandya | 5 4月 2026 において 09:00
これはカーブにフィットしている。
Ryan L Johnson
Ryan L Johnson | 7 4月 2026 において 19:17
Solomon Anietie Sunday #:
[この記事の戦略は、Renkoタイプのチャートに最適です。

Renkoチャートは時代を超越したチャートです。各レンコ・ブリックにタイムスタンプが残っていても、カスタムレンコ・チャートの時間スケールは均等に増分されません。したがって、Renkoを使用することで、取引プロセスから最適なタイムフレームを手動で選択する手間を省くことができます。その代わりに、最適なブリック・サイズを選択することになります。また、Renkoは本質的にチャートのノイズを平滑化します。

Solomon Anietie Sunday#:
[W]上の画像は何についてですか?私たちに何を伝えようとしているのですか?

中国語は読めないが、絵文字なら読める。このコードの "profit in pips for faster calculation "を有効にするとTesterでは利益が出るが、無効にすると利益が出ないようだ。

QuantGlobal
QuantGlobal | 20 4月 2026 において 06:07

インデックスで試してみました。

取引回数を減らすにはどうしたらいいですか?Zスコアが2を下回ったり上回ったりするまで、取引回数が多すぎます。

トレンドの基準:結論 トレンドの基準:結論
本記事では、実務におけるいくつかのトレンド判定基準の適用について検討します。また、それらの基準を基にしていくつかの新しい判定基準の開発も試みます。特に、市場データ解析および取引への適用効率に焦点を当てます。
サンゴ礁最適化(CRO) サンゴ礁最適化(CRO)
サンゴ礁の形成および発展過程に着想を得たメタヒューリスティクス手法であるサンゴ礁最適化(CRO, Coral Reef Optimization)アルゴリズムを包括的に解析します。このアルゴリズムは、放卵放精、体内発生、幼生定着、無性生殖、ならびに限られた礁空間を巡る競争といった、サンゴの進化過程における主要な生物学的現象をモデル化したものです。特に、本記事では改良版アルゴリズムに重点を置いて説明します。
一次元特異スペクトル解析 一次元特異スペクトル解析
本記事では、特異スペクトル解析(SSA, Singular Spectrum Analysis)法の理論的および実践的側面について考察します。SSAは時系列解析の有効な手法の一つであり、時系列の複雑な構造を、トレンド、季節性(周期的)変動、ノイズなどの単純な成分へ分解して表現することを可能にします。
市場シミュレーション(第17回):ソケット(XI) 市場シミュレーション(第17回):ソケット(XI)
MetaTrader 5上で実行されるコードの実装自体は、それほど難しいものではありません。しかし、いくつか考慮すべき重要な点があります。これはシステムを正しく動作させるために必要です。ここで重要な点を1つ覚えておいてください。実際には1つのプログラムだけが動作するわけではありません。現実には、3つのプログラムを同時に実行する必要があります。それぞれのプログラムが相互に連携し、通信できるように設計して構造化することが重要です。また、それぞれが他のプログラムの処理内容を認識できる必要があります。