English
preview
共和分株式による統計的裁定取引(第7回):スコアリングシステム2

共和分株式による統計的裁定取引(第7回):スコアリングシステム2

MetaTrader 5トレーディングシステム |
16 2
Jocimar Lopes
Jocimar Lopes

はじめに

前回の記事では、株式バスケットに対するスコアリングシステムの提案を紹介しました。記事はバックテストの結果を示したところで終わりましたが、その結果は比較的良好な数値を示す一方で、バスケットにいくつかの欠点があることも明らかにしました。 

  • 平均保有期間が非常に長いポジションがあった
  • 少なくとも1つのポジションが16週間以上継続し、これが最大保有期間だった

そのバックテストの主な目的は、そこで述べたとおり、戦略やバスケットの収益性を評価することではありませんでした。目的は、提案していた除外基準のうち2つ、すなわちポートフォリオウェイト(比率)の安定性と平均回帰に要する時間、いわゆる半減期を欠いた状態で、スコアリングシステムがどのように機能するかを検証することでした。つまり、前回の記事の時点では、提案していた4つの除外基準のうち2つのみをスコアリングシステムで使用していました。私たちは単に、半導体業界の中から最も流動性の高い銘柄でいくつかのバスケットを構築し、その中で共和分関係が最も強いものを選択しただけでした。ジョハンセン検定から得られるポートフォリオウェイトが十分に安定しているかどうかは評価しておらず、平均回帰に要する時間も評価していませんでした。

  • 流動性 
  • 共和分ベクトルの強度 
  • 共和分ベクトル(すなわちポートフォリオウェイト)の安定性 
  • 平均回帰に要する時間(半減期) 

本記事では、この不足部分を補い、残りの2つであるポートフォリオウェイトの安定性と平均回帰に要する時間を取り上げます。前回選定した同じバスケットを用い、これら2つのランキング要因を追加することで、システムがどのように改善されるかを検証します。

本連載をまだ追っていない読者のために説明すると、私たちは一般的なノートパソコン、限られた資金、通常のインターネット回線環境を前提とした、個人トレーダー向けの統計的裁定取引フレームワークを開発しています。このプロジェクトは友人との非公式な雑談から始まり、学習の機会でありトレーダーとしてのスキルを向上させるための挑戦として、私とパートナーが受け入れたものです。その会話のきっかけは、数学者でありヘッジファンドマネージャーでもあったジェームズ・シモンズの逝去でした。彼は伝説的なメダリオンファンドにおいて、1988年から2018年の間に平均年間総リターン66.1%、平均年間純リターン39.1%という成績を達成し、30年連続で利益を上げました。その手法は統計的裁定取引と、本人の言葉を借りれば「ある種の機械学習」によるものでした。

これまでに、ペアトレーディングおよびポートフォリオグループ、すなわちバスケットに対して、代表的な相関、共和分、定常性の各種検定をどのように適用し、どのように解釈するかを見てきました。また、分析用の複数のPythonスクリプトを実装し、2つのサンプルエキスパートアドバイザー(EA)を作成しました。1つはペアトレーディング用、もう1つはバスケット用です。そして、それらを用いていくつかのバックテストを実行しました。さらに、実験を支えるために必要なデータベーススキーマを構築し、継続的に改良してきました。

統計的裁定取引のアプローチに関心がある場合は、本連載の過去記事も確認し、実際に試してみてください。本連載はトレーダーにとって非常に実践的な内容になっています。私たちは「巨人の肩の上に立つ」という立場を取っています。専門の数学者や統計学者がすでに困難な理論部分を築き上げており、私たちは難解な数学そのものではなくトレーディングの側面に焦点を当て、既存のオープンソースライブラリを広く活用することでその成果を活かしています。本連載の基礎部分も終盤に近づいているため、ここで基礎を振り返るのは良いタイミングです。

それでは、スコアリングシステムを完成させます。まず、不足している2つのランキング要因を追加できるように、coint_rankテーブルを修正するところから始めます。

図1:更新されたcoint_rankテーブルのフィールドおよびデータ型

図1:更新されたcoint_rankテーブルのフィールドおよびデータ型



平均回帰に要する時間

(まだ半分しか実装されていない)スコアリングシステムをテストするために実施したバックテストでは、平均ポジション保有時間に関して奇妙な数値が出ました。平均でほぼ4週間半です。さらに、少なくとも1つのポジションは16週間以上も保持されたままでした。これは、この戦略にとって想定しているリスク水準ではありません。 

図2:前回の記事のバックテストレポートのスクリーンショット(ポジション保有時間を表示)

図2:前回の記事のバックテストレポートのスクリーンショット(ポジション保有時間を表示)

すべてがEAおよびバックテスト環境で正常に動作していると仮定し、かつ私たちの戦略が平均回帰ベースであることを踏まえると、これらの異常な数値を説明する最も明白な要因は「平均回帰に要する時間」です。スプレッドが想定よりも大幅に長期間、平均に回帰せず、その結果、ヘッジされた全ポジションを決済するトリガーが発動しない可能性があります。

バスケットの選定方法を覚えていますか。「最も」共和分関係が強いバスケットを選択しました。具体的には、半導体業界で最も流動性の高い10銘柄から構成された4銘柄バスケットです。言い換えれば、流動性と共和分関係の強度だけでスコアリングしていました。しかし、「共和分ポートフォリオ」を特定した後の次の論理的ステップは、スプレッドの平均からの乖離がどの程度の速さで平均に戻るのかを評価することです。この指標には、「平均回帰の半減期」という少し仰々しい名前が付けられています。平均回帰の半減期とは、スプレッド(共和分関係から得られる残差)が平均から乖離した後、その乖離が平均に向かって半分まで戻るのに要すると期待される時間を定量化したものです。

統計的裁定取引では、利益は一時的な価格のミスプライシングに賭けることから生まれます。そのため、半減期を定量化することは、ポジション保有時間を見積もるうえで極めて重要であり、その結果として戦略全体の実行可能性を評価することにも直結します。後に資金管理やリスク管理を扱う際には、平均回帰の半減期はポジションのボリュームや取引サイズを決定するためにも有用になります。半減期が短いほど不確実性が低いため、より大きなベットを支えることができます。

短い半減期(ここでの場合、数分から数時間)は迅速な回帰を示し、ポジションを素早く解消できます。これによりリスクエクスポージャーを削減でき、資本を新たな機会に再投資できます。逆に、長い半減期(数日から数週間)は資本を拘束し、保有コスト(FXにおけるスワップや、株式空売りにおける借株料など)を増加させ、ニュースイベントや流動性の枯渇に対するリスクエクスポージャーを高めます。平均回帰の半減期を定量化しなければ、想定よりも長く市場に留まり続けるだけでなく、早まってポジションを決済してしまうリスクにもさらされます。

この導入部から一つだけ覚えておくのであれば、次のことを強く意識してください。 平均回帰は保証されていません。

ポジションが恒久的に決済されない可能性もあります。長期間の乖離はストップロスの発動やマージンコールにつながる可能性があります。半減期があなたのリスク許容度を超える場合(たとえば、高頻度戦略において30日超など)は、そのペアをフィルタリングすべきです。 

半減期の計算方法

平均回帰の半減期を計算するための数学的背景は、数学に馴染みのない人にとっては難解です。半減期は、スプレッドをオルンシュタイン=ウーレンベック過程としてモデル化することから導出されます。これは平均回帰的挙動を記述する確率微分方程式です。離散時間の場合(ここでのケース)には、これは一次自己回帰モデル(AR(1))として近似されます。

「オルンシュタイン=ウーレンベック過程は、金利のバシチェック・モデルで使用されています。この過程は、(修正を加えたうえで)金利、為替レート、商品価格を確率的にモデル化するためのいくつかの手法の一つです。(…)この過程の応用の一つが、ペアトレードとして知られる取引戦略です。」(WikipediaのOrnstein-Uhlenbeck process in financial mathematicsより)

これは数学志向の読者のための引用です。幸いにも、前述の通り、この計算の背後にある数学を完全に習得する必要はありません。公式が存在し、AR(1)モデルのフィッティングには信頼できるstatsmodels Pythonライブラリを利用できます。私たちに必要なのは、計算手順を理解することだけです。(この計算は広く用いられており、statsmodelsも広く知られた統計ライブラリなので、AIアシスタントも大いに役立ちます。)

添付のPythonファイルでは、この処理をcompute_half_life()メソッドで実装しています。

if len(spread) < 3:
            return np.nan
        lag = spread.shift(1).dropna()
        delta = (spread - lag).dropna()
        if len(delta) < 2:
            return np.nan
        res = sm.OLS(delta, sm.add_constant(lag, has_constant='add')).fit()
        # beta = res.params[1]
        beta = res.params.iloc[-1]
        if beta >= 0:
            return np.inf
        else:
            return -np.log(2) / beta

この関数に渡す前に、スプレッドは定常性が検証されていなければなりません。私たちはすでにパイプライン内でADF(拡張ディッキー–フラー)検定を実行しています。ここに渡されるデータはすでに定常です。また、過学習を避け予測力を確認するために、アウトオブサンプルデータで実行する必要があります。これは次のステップでおこないます。初期の閾値として、私たちの戦略では4〜20時間(1〜5バー)の半減期を探します(4時間足でスイングトレードをおこなっていることを思い出してください)。用途に応じてこの範囲を調整してください。たとえば、日足であれば5〜20日の範囲を検討するかもしれません。ただし繰り返しますが、変更 → バックテスト → 再調整 → 再バックテスト → 最適化というプロセスを踏んでください。半減期が無限大(平均回帰が確認できない)の場合は、そのペアは除外します。

解釈

半減期は、時間足に対応する期間数で表されます。ここでの例の戦略ではH4をテストしているため、1期間は4時間です。したがって、半減期が10であれば、スプレッドが平均からの乖離を50%縮小するまでに約10本のバー、すなわち40時間を要することを意味します。

たとえば図3では、(「half_life IS NOT NULL」以外のフィルタは未使用)coint_rankテーブルを見ることができます。最初の2行は、同一のバスケット、タイムフレーム、ルックバックについて、異なる時点(タイムスタンプ)で取得されたレコードです。推定された半減期は約6.98です。これは、このバスケットがジョハンセン法で共和分検定され、H4、180日ルックバックで評価された場合、乖離したスプレッドが平均に戻るまでに約7本の4時間バー、すなわち約48時間を要することを意味します。これは、その前の約48時間でスプレッドが平均からピークまで乖離していたことを意味します。 

図3:「NOT NULL」のhalf_lifeフィールドを表示したcoint_rankテーブルのMetaEditorデータベースインターフェース

図3:「NOT NULL」のhalf_lifeフィールドを表示したcoint_rankテーブルのMetaEditorデータベースインターフェース

図4では、同じテーブル内に非常に長い半減期の例を見ることができます。このケースでは、記録された中で最大の半減期です。対象のバスケットは、120日間のルックバックでテストした場合、平均へ戻るまでに約934本の4時間足、すなわち約155日を要します。明らかに、このバスケットを取引対象にするべきではありません。ポジションをより長期間保有する必要があり、その結果、市場リスクへのエクスポージャーが増加するためです。

図4:「MAX」half_lifeフィールド(Infinity以外)を表示したcoint_rankテーブルのMetaEditorデータベースインターフェース

図4:「MAX」half_lifeフィールド(Infinity以外)を表示したcoint_rankテーブルのMetaEditorデータベースインターフェース

SQLiteにおいてInfinity値を除外するために「NOT IN」句を使用している点に注意してください。この句がなければ、返されるMAX半減期はInfinityになります。この場合、スプレッドが平均に回帰しない可能性があることを示します。つまり、他にクローズ/ストップロス条件(たとえばポジション保有時間の閾値、いわゆる時間に基づく決済)が存在しない限り、ポジションは無期限にオープンのまま残る可能性があります。

図5:「MAX」half_life(Infinityフィルタなし)を表示したcoint_rankテーブルのMetaEditorデータベースインターフェース

図5:「MAX」half_life(Infinityフィルタなし)を表示したcoint_rankテーブルのMetaEditorデータベースインターフェース

半減期が無限大であることは、平均回帰が存在しないことを示します(スプレッドが非定常、またはランダムウォーク的挙動を示している)。本記事のコードではこれをnp.infとして保存しています。これは、そのバスケットがH4足、180日ルックバックにおいて、平均回帰型取引に適していないことを示唆しています。

    def compute_half_life(self, spread: pd.Series) -> float:
        """
        Compute the half-life of mean reversion for a given spread series.
        
        Parameters
        ----------
        spread : pd.Series
            The stationary spread (residual) series from the cointegration vector.
        
        Returns
        -------
        float
            The half-life in periods (e.g., bars). Returns np.inf if no mean reversion (beta >= 0), np.nan if insufficient data.
        """
        if len(spread) < 3:
            return np.nan
        lag = spread.shift(1).dropna()
        delta = (spread - lag).dropna()
        if len(delta) < 2:
            return np.nan
        res = sm.OLS(delta, sm.add_constant(lag, has_constant='add')).fit()
        # beta = res.params[1]
        beta = res.params.iloc[-1]
        if beta >= 0:
            return np.inf
        else:
            return -np.log(2) / beta

これとは対照的に、同じサンプルデータ内において、非常に短い半減期の例もあります。テーブル上の最小半減期は約1.5本のH4足、すなわち平均回帰まで約6時間です。

図6:「MIN」half_lifeフィールドを表示したcoint_rankテーブルのMetaEditorデータベースインターフェース

図6:「MIN」half_lifeフィールドを表示したcoint_rankテーブルのMetaEditorデータベースインターフェース

これは統計的裁定取引にとって理想的です。スプレッドが乖離した際にエントリーし、平均へ回帰した際にエグジットするという取引機会が、短時間内により頻繁に発生することを示唆しているためです。

図7:NULLのhalf_lifeフィールドを表示したcoint_rankテーブルのMetaEditorデータベースインターフェース

図7:NULLのhalf_lifeフィールドを表示したcoint_rankテーブルのMetaEditorデータベースインターフェース

最後に、図7では数千件のNaN (Not a Number)半減期があることが分かります。これらはSQLiteではNULLとして保存されています。不十分なデータや、スプレッドが定数であるといった数値的問題が発生した場合に生じ、計算が正常に実行できなかったことを示します。

まとめると、収益性の観点では、統計的裁定戦略では、一般に半減期は短い方が望ましいとされます。これは、資本拘束が短く、コインテグレーション関係が崩れるリスクも低い、より迅速な取引を意味するためです。ただし、半減期の長短は相対的な概念である点を常に考慮する必要があります。スコアリングにおける戦略的基準と整合している必要があります。ここでの例では、H4足が戦略的基準であり、半減期の長短はこのH4基準に対して相対的に判断されます。

以下の点を念頭に置いてください。

  • 有限の半減期は、スプレッドが定常であることを前提としています。アウトオブサンプルでも定常性が維持されているかどうかを確認するためにstability_stat(ADF統計量)をチェックしてください。stability_statがあまり負でない場合、共和分ベクトルが将来的に維持されない可能性があります。
  • 定数、またはほぼ定数の価格系列のような問題は、NaNやinf値を引き起こす可能性があります。これはデータ品質の低さや流動性不足を示唆します。
  • 半減期は時間足に依存します。異なる時間足では、同じバスケットでも異なる半減期を示す可能性があります。半減期を比較する際は、必ず同一時間足でおこなってください。

本記事に添付されているcoint_ranker_auto.pyスクリプトは、上位バスケットのスプレッドをプロットすることで半減期の挙動を理解する助けになります。これにより、回帰速度が計算された半減期と一致しているかを視覚的に確認できます。

図8:4銘柄のNasdaq株式バスケットにおける共和分スプレッドと平均回帰半減期のプロット

図8:4銘柄のNasdaq株式バスケットにおける共和分スプレッドと平均回帰半減期のプロット

私たちのスコアリングシステムでは、rank_scoreおよびstability_stat(後述)と並んで半減期を使用し、バスケットをランク付けします。具体的には、高いrank_score(強い共和分関係)、短いhalf_life(速い回帰)、より負のstability_stat(安定したベクトル)を持つバスケットを優先します。


ポートフォリオのウェイトの安定性

ポートフォリオウェイトは、共和分を用いた統計的裁定取引における中核的な要素です。これらは同時に建てるヘッジ付きポジションのサイズだけでなく、その方向も定義します。ジョハンセン共和分検定の導入時に詳しく説明したので、ここでは繰り返しませんが、重要なのは、これらのウェイトが平均回帰スプレッド計算の出発点であるという点です。

これらのウェイトは、複数の株価系列(非定常時系列)間の共和分関係を特定するために用いている多変量統計手法であるジョハンセン検定から得られる固有ベクトルに由来します。この検定は共和分ベクトルの本数を推定し、対応する固有ベクトルを提供します。これらの固有ベクトルは、定常かつ取引可能なポートフォリオを形成する資産の線形結合を表します。 

たとえば、直近のバックテストでは、「NVDA」「INTC」「AVGO」「ASX」から構成されるバスケットに対して、{1.0, -1.9598590335874817, -0.36649674991957104, 21.608207065113874}をポートフォリオウェイトとして使用しました。

NVDA INTC AVGO ASX
1.0
-1.9598590335874817
-0.36649674991957104
21.608207065113874

表1:それぞれのポートフォリオのウェイトを持つ株式バスケット(共和分ベクトル)

これは、NVDAを1単位ロングするごとに、以下のポジションを同時に建てることを意味します。 

  • INTCを約1.96単位ショート 
  • AVGOを約0.37単位ショート
  • ASXを約21.6単位ロング

このような加重ヘッジ構造により、理想的には統計的アービトラージにおける市場中立性が確保されることが期待されます(「理想的に」と記載したのは、市場中立は完全に達成できるものではなく、常に残余リスクが存在するためです。本記事ではその詳細には立ち入りません)。しかしながら、これらのウェイトが時間を通じて不変であると仮定することは、動的な金融市場においてはリスクを伴います。スコアリングをおこなう際には、少なくとも以下の3つの理由から、ジョハンセン固有ベクトルの安定性を検証する必要があります。

第一に、共和分は長期的な均衡関係を意味しますが、政策変更、市場暴落、セクターローテーションなどにより経済レジームは変化します。ローリング推定ウィンドウ間で固有ベクトルが大きく変動する場合、基礎となる関係が安定していない可能性を示唆します。ウェイトが不安定であれば、平均回帰が機能しなくなる恐れがあります。その結果、本来は収益性のあるアービトラージ戦略が、持続的な乖離と損失を生む戦略へと変質する可能性があります。

第二に、安定したウェイトは、市場ベータなどの共通因子に対するヘッジ状態を維持するために不可欠です。不安定性は、ボラティリティの急騰や共和分関係の崩壊など、意図しないリスクへのエクスポージャーを拡大させる可能性があります。たとえば、2008年の金融危機時には、多くの金融株ペアにおける共和分関係が崩れ、従来の固有ベクトルは無効となりました。安定性を定期的に検証することで、過去データに基づく推定値への過信を防ぎ、ウェイトの再推定やポジション解消といった戦略調整を適時に実行できます。これが、リアルタイムでモデルを更新するために「データベースを単一の信頼できる情報源」として実装した主な理由です。

第三に、ポートフォリオウェイトが不安定であれば、ドローダウンの拡大やシャープレシオの低下につながる可能性が高くなります。不安定性が検出された場合には、当該バスケットをスコアリングシステム上で積極的に格下げする必要があります。

stability_statとは何か

名称のとおり、coint_rankテーブルにあるstability_statフィールドは、ポートフォリオウェイトの安定性統計量を格納するためのものです。その値は、以下の2つの方法のいずれかで算出できます。

ローリングウィンドウにおける固有ベクトル比較:これは直接的な方法であり、連続するローリングウィンドウ間で共和分ベクトルを比較します。ポートフォリオウェイトが安定していると判断するためには、コサイン距離が事前に設定した閾値以内に収まることを期待します。 

インサンプル/アウトオブサンプルADF検証これは4段階のプロセスです。まず価格データをインサンプル(IS)とアウトオブサンプル(OOS)に分割し、ISデータ上で共和分ベクトルを推定します。次に、その共和分ベクトルを用いてOOSデータ上でスプレッドを計算します。最後に、そのOOSスプレッドに対してADF統計量を評価します。ポートフォリオウェイトが安定していると判断するためには、ISとOOSの両方のADF統計量が定常性を示すことを期待します。

具体的には、次の場合です。

  1. データをインサンプルとアウトオブサンプルに分割する
  2. インサンプルで共和分ベクトル、スプレッド、ADF統計量を算出する
  3. 同じ共和分ベクトルを用いてアウトオブサンプルでスプレッドとADF統計量を算出する

ここで用いるADF統計量は、本連載開始当初から使用しているADF検定です。ADF検定の詳細については既に解説済みですので、導入記事をご参照ください。

どちらか一方が常に優れている、あるいは推奨されるというわけではありません。両者は用途が異なります。ローリングウィンドウによる固有ベクトル比較は、オンライン取引を監視しながらリアルタイムでモデル更新やポートフォリオのリバランスを行う場合に適しています。一方、インサンプル/アウトオブサンプルADF検証は、スコアリングフェーズのような取引前のシナリオや、バックテストにおけるポートフォリオの妥当性評価に適しています。ポートフォリオ安定性を推定する目的では、こちらの方法を採用すべきです。そのため、本スコアリングシステムではこの方法を使用しています。

(ポートフォリオウェイトの継続的な再評価が口座保全にとって極めて重要であることを踏まえ、将来的には両手法を異なるシナリオでベンチマークした結果をまとめた記事を公開する予定です。)

添付のPythonスクリプトには、実際に使用しているcompute_stability関数が含まれています。前述のとおり、この関数は上記の第二の方法に基づいています。

1. 価格データをインサンプル(デフォルトでは最初の70%、split_ratio=0.7で制御)とアウトオブサンプル(残りの30%)に分割します。

def compute_stability(
        self,
        prices: pd.DataFrame,
        method: str,
        split_ratio: float = 0.7,
        det_order: int = 0,
        k_ar_diff: int = 1
    ) -> float:
        """
        Check the stability of the cointegration vector by computing it on in-sample data
        and evaluating the ADF statistic on the out-of-sample spread.
        
        Parameters
        ----------
        prices : pd.DataFrame
            DataFrame with columns as asset tickers and rows as time-indexed prices.
        method : str
            'Engle-Granger' for pairs or 'Johansen' for baskets.
        split_ratio : float
            Fraction of data to use as in-sample (default 0.7).
        det_order : int
            Deterministic order for Johansen (default 0).
        k_ar_diff : int
            Lag order for Johansen (default 1).
        
        Returns
        -------
        float
            The out-of-sample ADF statistic (more negative indicates stronger stability/stationarity).
            Returns np.nan if insufficient data.
        """
        n = len(prices)
        if n < 50:
            return np.nan
        split = int(n * split_ratio)
        if n - split < 30:
            return np.nan
        train = prices.iloc[:split]
        test = prices.iloc[split:]

2. インサンプルデータに対してget_coint_vectorを用い、共和分ベクトルを算出します。ペアの場合はEngle-Granger法、バスケットの場合はジョハンセン法を使用します。

try:
      vec_is = self.get_coint_vector(train, method, det_order, k_ar_diff)

3. この共和分ベクトルをアウトオブサンプルの価格データに適用し、スプレッドを構築します。

spread_oos = pd.Series(np.dot(test.values, vec_is), index=test.index)
 if spread_oos.std() < 1e-10 or spread_oos.isnull().any():
     self.logger.debug("Out-of-sample spread is effectively constant or contains NaNs")
      raise ValueError("Out-of-sample spread is effectively constant or contains NaNs")

4. statsmodelsのadfullerを用いて、このアウトオブサンプルスプレッド(spread_oos)に対してADF検定を実行し、結果の最初の要素であるテスト統計量(adfuller(spread_oos)[0])を抽出します。

adf_stat = adfuller(spread_oos)[0]

5. このADF統計量をstability_statとして返します。計算に失敗した場合(データ不足や数値的不安定など)はnp.nanを返します。

    return float(adf_stat)
except Exception as e:
    self.logger.warning(f"Stability computation error: {e}")
    return np.nan

最終的におこなっていることは単純です。インサンプルで推定した共和分ベクトルを、未知データであるアウトオブサンプルに適用したとき、その関係がどれだけ頑健かを測定しています。すなわち、アウトオブサンプルのスプレッドに対するADF統計量によって、インサンプル由来の共和分ベクトルが将来データに対しても定常なスプレッドを生成するかを検証しています。これは、共和分関係が今後の取引期間にも持続する可能性を評価するものです。

ADF統計量は、より負の値であるほど強い定常性を示します。すなわち、アウトオブサンプルにおける平均回帰の証拠が強いことを意味します。5%有意水準における一般的な臨界値はおおよそ-2.86から-3.5の範囲です(サンプルサイズやモデル仕様に依存します)。stability_statがこれらよりも十分に負であれば、95%信頼水準でスプレッドは定常である可能性が高いと判断できます。

一方、ゼロ付近または正の値は、アウトオブサンプルスプレッドが非定常である可能性を示します。これは、インサンプルで得られた共和分ベクトルが未知データ上で平均回帰スプレッドを生成していないことを意味します。すなわち、共和分関係が不安定であり、取引戦略として信頼性に欠ける可能性があります。

この手法は、十分なアウトオブサンプルデータを必要とします。ここでは最低30本のバーを確保する制約を設けています(n - split < 30 の場合は無効)。たとえば、H4足で180日のルックバックを用いる場合、実効取引時間は約1080時間、約270本のバーとなり、アウトオブサンプル期間は約81本です。より短いルックバックでは信頼性が低下する可能性があります。

なお、stability_statは時間足に依存します。本ケースではH4を前提としています。H4で安定しているバスケットが、D1やM15でも安定しているとは限りません。必ず同一時間足内で比較してください。また、ポートフォリオウェイトの安定性のみでは十分ではありません。stability_statが非常に負であっても、half_lifeが長すぎれば、安定ではあっても実運用には遅すぎる可能性があります。常にhalf_lifeと、ペア取引ではp_value、バスケットではeigen_strengthと組み合わせて評価すべきです。

coint_rankテーブルでは、5%有意水準の基準としてstability_stat < -2.86を優先し、ペア取引ではhalf_lifeとp_valueを、バスケットではhalf_lifeとeigen_strengthを併用しています。

以下に、ジョハンセン検定のeigen_strengthを用いた株式バスケット向けフィルタを示します。

SELECT timeframe as tf, lookback as lb, assets as basket, 
coint_vector as weights, eigen_strength as strength, 
stability_stat as stability, half_life as rev_bars
FROM 'coint_rank'
WHERE stability_stat < -2.86
ORDER BY eigen_strength DESC;

図9:MetaEditorのデータベースインターフェースに表示されたcoint_rankテーブル(バックテスト対象として選択されたバスケット)

図9:MetaEditorのデータベースインターフェースに表示されたcoint_rankテーブル(バックテスト対象として選択されたバスケット)

私たちは、stability_statが-2.86未満という閾値を満たすバスケットの中から、共和分強度が最も高いものを選択しています。薄い青色で、検討対象となる上位5つを強調表示しています。ただし、上位5つのうち最初の4つは、正規化されたウェイト1.0と比較して、少なくとも1つのウェイトが極端に大きいことに注意してください。すなわち、共和分ベクトルの一成分が過度に拡大しています。 

例として、3番目のバスケットを見てみます。

ティッカー(銘柄) AVGO LAES MRVL ASX
ポートフォリオの丸め後ウェイト
1.0 47.6 -3.1 -78.4

表2:非常に空売り比率の高い株式バスケット

これは、AVGOを1株保有するごとに、LAESを47株買い、同時にMRVLを3株、ASXを78株空売りする必要があることを意味します。個人投資家としては、このようなポートフォリオは避けるべきです。十分な資金やブローカーへの高速・優先的アクセスがない場合、流動性の低い銘柄をこれだけの数量で空売りしようとすると、執行リスクや高い取引コストに直面する可能性があります。繰り返しになりますが、ここでいう「低流動性」は、NVDA、INTC、AVGOのような半導体業界の高流動性株と比較した相対的な意味です。

そのため、より均質なポートフォリオ、たとえば5行目のバスケットのようなものを選ぶ方が望ましいです。 

ティッカー(銘柄) INTC WOLF NVTS ASX
ポートフォリオの丸め後ウェイト 1.0 -0.6 1.46 -0.46

表3:ポートフォリオウェイトが比較的均等な株式バスケット

わずかに共和分強度が弱くても、安定性統計量が-3.72と良好であるため十分に補われています。さらに、平均回帰までの半減期はH4でわずか3本、すなわち約12時間と推定されており、統計的裁定取引のスイングトレードで想定している保有期間と非常に整合しています。

上位ランキングのバスケットにおける不均衡を避けるために、原因となるティッカーをフィルタリングすることが可能です。この例ではLAESとASXが候補です。ただし、表3においてASXは不均衡を引き起こしていません。これはASXがNVDAに対して大きく偏っているものの、NVDAは私たちのコア戦略、すなわち初期仮説の中心銘柄であるため、除外すべきではないからです。

SELECT timeframe as tf, lookback as lb, assets as basket, 
coint_vector as weights, eigen_strength as strength, 
stability_stat as stability, half_life as rev_bars
FROM 'coint_rank'
WHERE stability < -2.86 
AND basket NOT LIKE '%LAES%'
AND basket NOT LIKE '%ASX%'
ORDER BY strength DESC;

図10:MetaEditorのデータベースインターフェースに表示されたcoint_rankテーブル(LAESとASXを除外したバックテスト対象バスケット)

図10:MetaEditorのデータベースインターフェースに表示されたcoint_rankテーブル(LAESとASXを除外したバックテスト対象バスケット)

これにより、上位5つのバスケットがより均衡の取れたものになりました。特に2行目は、NVDAを含まないものの、共和分の安定性が高く(-4.29)、平均回帰までの推定期間(half_life)は約48時間(H4 12本)と見積もられており、私たちのスイングトレードスタイルに非常に適しています。

このバスケットをバックテストして、これまでの追加ランキング条件なしでおこなったバックテストより改善があるか確認してみましょう。(後で、3行目のNVDAを含むバスケットでも同様に確認できます。)


バックテスト

前回のバックテストと同様に、今回もEAに取引パラメータをハードコーディングしてバックテストをおこないます。なぜこの方法を取るのか理解するためには、前回の記事のバックテスト内容をご参照ください。

// check if we are backtesting
   if(MQLInfoInteger(MQL_TESTER))
     {
      Print("Running on Tester");
      ArrayResize(symbols, 4);
      ArrayResize(weights, 4);
      // "H4",120,"INTC,AMD,AVGO,MU"
      // "[1.0, -0.5005745550348311, 0.7458706676501435, -0.3108921739081775]"
      symbols[0] = "INTC";
      symbols[1] = "AMD";
      symbols[2] = "AVGO";
      symbols[3] = "MU";
      //---
      weights[0] = 1.0;
      weights[1] = -0.5005745550348311;
      weights[2] = 0.7458706676501435;
      weights[3] = -0.3108921739081775;
      timeframe = PERIOD_H4;
      //InpLookbackPeriod = 120;
     }
   else
     {
      // Load strategy parameters from database
      if(!LoadStrategyFromDB(InpDbFilename,
                             InpStrategyName,
                             symbols,
                             weights,
                             timeframe,
                             InpLookbackPeriod))

今回のバックテストでは、ルックバック期間をユーザー入力(InpLookbackPeriod)として含めています。これにより、ポジションのエントリー/エグジットに用いる平均スプレッドや標準偏差の計算用ルックバックと、共和分評価用のルックバックを分離しました。また、平均と標準偏差のルックバックは最適化にも利用可能になっています。

図11:バックテストで使用した入力パラメータのスクリーンショット

図11:バックテストで使用した入力パラメータのスクリーンショット

図12には、シャープレシオでソートした最適化結果のスクリーンショットを示しています。

図12:シャープレシオでソートした最適化結果のスクリーンショット

図12:シャープレシオでソートした最適化結果のスクリーンショット

今回は上位の2番目(薄青色でハイライト)に対して単独でバックテストを実施しました。1行目と比較すると、シャープレシオはわずかに低い(6.87)ものの、リカバリーファクター(5.72)の向上、ドローダウンの縮小(3.36)、取引回数がほぼ半分で期待ペイオフが倍以上という点で十分に補われており、取引コストの低減にもつながっています。

この最適化では、平均から3.0標準偏差でポジションをエントリーし、0.8標準偏差でエグジットする設定です。また、共和分ルックバックは120日、平均スプレッド・標準偏差計算用ルックバックは90日でバックテストしています。

バックテスト期間は4か月です。

図13:バックテスト設定のスクリーンショット

図13: バックテスト設定のスクリーンショット

統合レポートは有望な数字を示しています。

図14:バックテストの統合レポートの統計スクリーンショット

図14:バックテストの統合レポートの統計スクリーンショット

履歴データの品質は良くない点に注意し、ここでの結果よりも統計的裁定取引の基本原則に注目してください。

前述の統計情報に加えて、利益取引(51.67%)と損失取引(48.33%)の比率にも注目してください。このわずかな優位性は、統計的裁定取引の特徴です。 

バランス/エクイティのグラフを見ると、資本曲線は持続可能な形で推移しており、ドローダウンも小さく抑えられています(図15)。

図15:バックテストのバランス/エクイティグラフを示すスクリーンショット

図15:バックテストのバランス/エクイティグラフを示すスクリーンショット

ポートフォリオウェイトの安定性と平均回帰半減期をスコアリングに組み込んだことで、前回のバックテストで見られた取引の偏った集中は解消されたようです。

図16:バックテストのポジション時間、曜日、月ごとの分布のスクリーンショット

図16:バックテストのポジション時間、曜日、月ごとの分布のスクリーンショット

現在、取引は週の全日および選択した4か月すべてにわたって均等に分布しています。唯一集中しているのは米国市場の取引セッションであり、Nasdaq銘柄を対象にバックテストしているため予想通りです。

MFE(最大有利方向値)およびMAE(最大不利方向値)の利益分布を見ると、まだ改善の余地があることがわかります。 

図17:バックテストのMFEおよびMAE利益分布のスクリーンショット

図17:バックテストのMFEおよびMAE利益分布のスクリーンショット

おそらく、次回の記事で扱う動的ポートフォリオリバランスを実装すれば、さらに良い結果が得られるでしょう。

最後に、前回のバックテストでの最大の課題は、ポジションの保有時間でした。

図18:バックテストのポジション保有時間のスクリーンショット

図18:バックテストのポジション保持時間を示すスクリーンショット

最大および平均のポジション保有時間を見ると、スコアリングに平均回帰半減期を組み込んだことが、今回の全体的な改善に寄与していることがわかります。前回は平均保有時間が約1か月で、少なくとも1つのポジションは15週間以上保有されていました。今回は平均約48時間で、1週間以上保有されたポジションはわずか3件のみです。まさにクラシカルなスイングトレードの形です。


結論

本記事では、共和分を用いた統計的裁定取引のための株式バスケットをランク付けする、最小限のスコアリングシステム向けに、2つのランキング基準を説明、実装、バックテストしました。これら2つの基準は、前回の記事で紹介・開発を開始したスコアリングシステムの一部です。つまり、今回説明した2つの基準と前回の記事で紹介した3つの基準を組み合わせることで、バックテスト用の株式バスケットを評価する際に一度に適用できる統合スコアリングシステムが完成します。

最初の基準は共和分ベクトルの安定性であり、ポートフォリオウェイトの時間的安定性を評価します。ポートフォリオウェイトはヘッジ構造を定義するため、統計的裁定取引戦略の市場中立性の達成はウェイトの安定性に強く依存します。この基準により、ウェイトが定義した閾値を超えて変動するバスケットを除外できます。

2つ目の基準は平均回帰に要する時間(半減期)であり、平均的なポジション保有期間を推定します。この基準は、前回の記事で述べた戦略上の時間足(短期/中期/長期)に直接関係しています。平均回帰に要する時間を考慮することで、市場からの退出に過剰な時間を要するバスケットを除外し、リスクエクスポージャーを許容範囲内に維持できます。

最後に、これら2つの基準に加えて前回紹介した3つの基準を組み合わせた上でバックテストをおこなった結果、平均回帰時間とポートフォリオウェイトの安定性をスコアリングシステムに組み込むことで、バスケット選定が大幅に改善されることが示唆されました。

ここでは、実験を再現するために必要なすべてのファイルを含めています。バックテスト設定(.iniファイル)や最適化設定(.setファイル)も同梱しています。

ファイル 説明
config\CointNasdaq.INTC.H4.20250701_20251031.020.ini バックテスト設定ファイル
config\CointNasdaq.set 最適化パラメータSETファイル
Experts\StatArb\CointNasdaq.mq5 サンプルEA
Files\StatArb\schema-0.5.sql 更新されたデータベーススキーマ(DDL)
Include\StatArb\CointNasdaq.mqh サンプルEAヘッダ
Python\coint_ranker_auto.py 共和分テストのランク付けと保存をおこなうPythonクラス

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

添付されたファイル |
最後のコメント | ディスカッションに移動 (2)
Cyberdude
Cyberdude | 9 12月 2025 において 15:50

まず、このトピックを簡単に説明しようとする試みには感謝します。

しかし、あなたのバックテストは現実離れしていると思います。

遅延0と「毎ティック」のモデリングはどちらも非現実的です。遅延0は実際には存在しません。少なくとも100ミリ秒に設定してください。
また、'every tick' のモデリングは、MT5 によって製造されたティックです。本物のティック」が必要です。
私があなただったら、これはリテールMT5ユーザーにとって確実に損をする戦略であることを明確にします。
Jocimar Lopes
Jocimar Lopes | 10 12月 2025 において 09:05
Cyberdude 「毎ティック」のモデリングはどちらも非現実的です。遅延0は実際には存在しません。少なくとも100ミリ秒に設定してください。
また、'every tick' のモデリングは、MT5 によって製造されたティックです。本物のティック」が必要です。
私があなただったら、これはリテールMT5ユーザーにとって確実に損をする戦略であることを明確にします。
遅延0は非現実的であり、「毎ティック」の選択も同様です。しかし、これらの選択には理由があります。

'Every tick'は、Exchangeサブスクリプションなしのデフォルトのデモ口座で、銘柄シンボルの非常に質の低い履歴データを扱わなければならないためです。

遅延が 0 というのは、この記事では提案したスコアリング・システムの骨格を説明することに重点を置いており、実際の取引におけるストラテジーのパフォーマンスについては考えていなかったからです。

誤解しないでほしい。あなたは正しいし、**読者への警告は正しい**。この「戦略」は、引用符の間を読まなければならない。

ありがとう。
オンチャートUIを使用したリスクベースの取引執行EA(第1回):ユーザーインターフェースの設計 オンチャートUIを使用したリスクベースの取引執行EA(第1回):ユーザーインターフェースの設計
MQL5でリスクベース取引執行エキスパートアドバイザー(EA)用の、クリーンでプロフェッショナルなオンチャートコントロールパネルを構築する方法を解説します。このステップバイステップガイドでは、トレーダーが取引パラメータを入力し、ロットサイズを計算し、自動発注に備えることができる機能的なGUIの設計方法を説明します。
長期取引の最適化:包み足と流動性戦略 長期取引の最適化:包み足と流動性戦略
高時間足(W1、D1、MN)に基づいて長期的な分析と取引判断をおこなうEAです。このEAは、短期的な値動きに翻弄されることなく、利確目標に到達するまで自分のトレンドの方向性(バイアス)を頻繁に変えずにポジションを保持できる、忍耐強い長期トレーダー向けに設計されています。
取引戦略の開発:バタフライオシレーター法 取引戦略の開発:バタフライオシレーター法
魅力的な数学概念であるバタフライ曲線を、実践的な取引ツールへと応用する方法を紹介します。バタフライオシレーターを構築し、それを基盤とした基本的な取引戦略を開発します。この戦略は、オシレーター特有の周期的シグナルと移動平均による従来型のトレンド確認を効果的に組み合わせることで、潜在的な市場エントリーポイントを特定するための体系的なアプローチを実現します。
MQL5での取引戦略の自動化(第38回):傾斜角フィルタ付き隠れRSIダイバージェンス取引 MQL5での取引戦略の自動化(第38回):傾斜角フィルタ付き隠れRSIダイバージェンス取引
スイングポイントを用いて隠れRSIダイバージェンスを検出するMQL5 EAを構築します。これは、価格とRSIに対して、スイング強度、バー間隔、許容誤差、傾き角度のフィルタを適用し、検証済みのシグナルで固定ロット、SL/TP(pips単位)、およびオプションのトレーリングストップを用いて売買を実行するシステムです。