ペアトレードと共和分をわかりやすく解説
ペアトレードに必要な共和分と相関の違いを解説します。実際の終値からヘッジ比率、スプレッド、zスコアを手計算し、手法が機能しない条件も説明します。
ペアトレードと共和分は密接に関係しています。この取引は、二つの株価の差が安定した水準へ繰り返し戻ることを前提とします。共和分は、そのような差が実際に存在することを示す統計的性質です。
相関は別のものを測定します。二つの銘柄は高い相関を示しながら、長期的に乖離し続けることがあります。一方、日々の値動きがほとんど連動しない二銘柄でも、何年にもわたって一定の価格差を維持することがあります。
このページでは、ヘッジ比率、スプレッド、zスコアを手作業で算出します。実際の終値を使って三つすべてを測定します。そのうえで、構築方法がどのように機能しなくなるかについても、同じ分量を割いて説明します。
ペアトレードにおける共和分とは
相関は、二つの銘柄の日次騰落率の動きがどの程度連動しているかを示します。値はマイナス1からプラス1までで、プラス1は、サンプル期間のすべてのセッションで両銘柄が同時に上昇・下落したことを意味します。ただし、二つの価格の距離については何も示しません。
共和分は、その距離に関する概念です。銘柄Aに一定の倍率を掛けた値を銘柄Bから差し引き、残った値を確認します。この残差系列、つまりスプレッドが際限なく離れていかず、一定の範囲内にとどまるなら、二つの銘柄は共和分関係にあります。価格自体はどこまでも動く可能性があります。しかし、その組み合わせは離れていきません。
教科書で使われるイメージは、リードにつながれた犬です。飼い主と犬は道の上を予測できない経路で歩きますが、リードが二者間の距離を一定範囲に保ちます。相関が問うのは、二つが同じタイミングで歩くかどうかです。共和分が問うのは、リードが距離を保てるかどうかです。
以下の表では、2024年と2025年における、よく知られた五つのペアについて、前者の考え方を測定しています。return_corr列は日次騰落率の相関で、通常「相関」という言葉で指すのはこちらです。price_corr列は終値そのものの相関です。両銘柄がともに上昇した場合、ほぼどの銘柄ペアでも実態以上に高く見える数値です。
各数値の背後にある正確なSQL
WITH
px AS (
SELECT
ticker,
date,
toFloat64(any(close)) AS close_px
FROM global_markets.stocks_daily_aggs
WHERE ticker IN ('KO', 'PEP', 'HD', 'LOW', 'XOM', 'CVX', 'V', 'MA', 'AAPL', 'MSFT')
AND date BETWEEN '2024-01-01' AND '2025-12-31'
GROUP BY ticker, date
),
rets AS (
SELECT
ticker,
date,
close_px,
close_px / lagInFrame(close_px) OVER (
PARTITION BY ticker ORDER BY date
ROWS BETWEEN 1 PRECEDING AND CURRENT ROW
) - 1 AS ret
FROM px
)
SELECT
p.pair AS pair,
round(corr(a.ret, b.ret), 3) AS return_corr,
round(corr(a.close_px, b.close_px), 3) AS price_corr
FROM
(
SELECT
tupleElement(t, 1) AS leg_a,
tupleElement(t, 2) AS leg_b,
tupleElement(t, 3) AS pair
FROM
(
SELECT arrayJoin([
('KO', 'PEP', 'KO / PEP'),
('HD', 'LOW', 'HD / LOW'),
('XOM', 'CVX', 'XOM / CVX'),
('V', 'MA', 'V / MA'),
('AAPL', 'MSFT', 'AAPL / MSFT')
]) AS t
)
) AS p
INNER JOIN rets AS a ON a.ticker = p.leg_a
INNER JOIN rets AS b ON b.ticker = p.leg_b AND b.date = a.date
WHERE isFinite(a.ret) AND isFinite(b.ret)
GROUP BY pair
ORDER BY return_corr DESC日次騰落率の相関で順位付けすると、HD / LOWが0.868で表の最上位となります。同じ期間における二つの価格水準の相関は0.849でした。最下段では、AAPL / MSFTの日次騰落率相関が0.479で、価格相関は0.508です。どちらの列も、ペアトレーダーが問うている点には答えていません。いずれも連動性を測る指標であり、ペアトレードには距離を測る指標が必要です。
ヘッジ比率とスプレッドの計算方法
終値リスト二つに対して、五つの手順を順番に実行します。mathだけをインポートすれば、通常のPythonですべて処理できます。パッケージもデータベンダーもネットワーク接続も不要です。
- 各系列を中心化する。
mean_a = sum(a) / len(a)。リストbも同様に処理します。 - ヘッジ比率を推定する。 これはBをAで説明する直線の傾きです。
beta = sum((a[i] - mean_a) * (b[i] - mean_b) for i in range(len(a))) / sum((x - mean_a) ** 2 for x in a)。B一単位を相殺するAの単位数として読み取ります。 - スプレッドを作る。
spread = [b[i] - beta * a[i] for i in range(len(a))]。共和分するペアでは、この系列はトレンドを形成せず、一定の水準付近で推移します。 - 後方移動ウィンドウを取る。
w = spread[i - win + 1 : i + 1]を使うと、ウィンドウ平均はmu = sum(w) / len(w)、標本標準偏差はsd = math.sqrt(sum((x - mu) ** 2 for x in w) / (len(w) - 1))です。後方移動とは、評価対象日の終値までをウィンドウに含め、それ以降のデータは使わないという意味です。 - スコアを算出して出力する。
z = (spread[i] - mu) / sdを計算し、abs(z) > 1.5の間はエントリー行を、abs(z) < 0.5になったらエグジット行を出力します。zが2なら、スプレッドが直近平均を標準偏差二つ分上回っていることを示します。
この処理を確認するには、ハードコードした価格リスト二つで十分です。winを5にすると、簡単な例を読みやすくできます。実際の分析では、約四分の一に相当する63セッション程度の期間を使うことが多いです。エントリーの1.5とエグジットの0.5という閾値は、チュートリアルで引き継がれてきた慣例にすぎません。これらは分析結果ではなく、以下のパネル内のデータから導かれたものでもありません。
実際の価格で見るペアトレードのzスコアとは
ここでは、生活必需品の飲料大手であるKOとPEPについて同じ計算を行いました。ヘッジ比率は2023年の終値だけで算出し、チャートが対象とする2024年と2025年を通じて固定しています。スコアの算出にも同じサンプルで傾きを当てはめると、バックテストにおける先読みバイアスが生じます。フィッティング期間とスコア算出期間を分けることが、これを防ぐ最も簡単な方法です。
各数値の背後にある正確なSQL
WITH
daily AS (
SELECT
date,
anyIf(toFloat64(close), ticker = 'PEP') AS pep,
anyIf(toFloat64(close), ticker = 'KO') AS ko
FROM global_markets.stocks_daily_aggs
WHERE ticker IN ('KO', 'PEP')
AND date BETWEEN '2023-01-01' AND '2025-12-31'
GROUP BY date
HAVING pep > 0 AND ko > 0
),
fitted AS (
SELECT covarSamp(pep, ko) / varSamp(ko) AS beta
FROM daily
WHERE date < '2024-01-01'
),
spread AS (
SELECT
daily.date AS date,
daily.pep - fitted.beta * daily.ko AS spread_usd
FROM daily
CROSS JOIN fitted
),
scored AS (
SELECT
date,
(spread_usd - avg(spread_usd) OVER (
ORDER BY date ROWS BETWEEN 62 PRECEDING AND CURRENT ROW))
/ stddevSampStable(spread_usd) OVER (
ORDER BY date ROWS BETWEEN 62 PRECEDING AND CURRENT ROW) AS z
FROM spread
)
SELECT
toString(toMonday(date)) AS week,
round(argMax(z, date), 2) AS z_score
FROM scored
WHERE date >= '2024-01-01'
AND isFinite(z)
GROUP BY week
ORDER BY weekこの推移には105週の観測値が含まれます。ウィンドウはzスコア-1.53で始まり、-0.68で終わります。単一の値ではなく、形状を見てください。戦略の前提どおりに動くペアは、しきい値を超えた後、数週間かけてゼロへ戻り、再びしきい値を超えます。平均回帰が止まったペアは、しきい値の外側にとどまり続けます。チャートでは、中央から離れた場所に長く平坦な推移が続く形になります。
共整合したスプレッドは、必ず戻るのでしょうか?
いいえ。これを正直に確認するには、あらゆる起点の後に何が起きたかを測定する必要があります。下のパネルでは、2019年から2025年末にかけての各セッションを、その日のzスコア別に分類し、同じスプレッドの20セッション後の平均zスコアを示しています。回帰分析で推定したスプレッドではなく、価格比の対数を使っているため、事後的に推定した回帰がチャートへ影響することはありません。
各数値の背後にある正確なSQL
WITH
daily AS (
SELECT
date,
anyIf(toFloat64(close), ticker = 'PEP') AS pep,
anyIf(toFloat64(close), ticker = 'KO') AS ko
FROM global_markets.stocks_daily_aggs
WHERE ticker IN ('KO', 'PEP')
AND date BETWEEN '2018-01-01' AND '2025-12-31'
GROUP BY date
HAVING pep > 0 AND ko > 0
),
spread AS (
SELECT
date,
log(pep / ko) AS log_ratio
FROM daily
),
scored AS (
SELECT
date,
(log_ratio - avg(log_ratio) OVER (
ORDER BY date ROWS BETWEEN 62 PRECEDING AND CURRENT ROW))
/ stddevSampStable(log_ratio) OVER (
ORDER BY date ROWS BETWEEN 62 PRECEDING AND CURRENT ROW) AS z
FROM spread
),
horizon AS (
SELECT
date,
z,
leadInFrame(z, 20) OVER (
ORDER BY date ROWS BETWEEN CURRENT ROW AND 20 FOLLOWING
) AS z_fwd
FROM scored
WHERE isFinite(z)
)
SELECT
multiIf(z < -2, 'z below -2',
z < -1, 'z -2 to -1',
z < 0, 'z -1 to 0',
z < 1, 'z 0 to 1',
z < 2, 'z 1 to 2',
'z above 2') AS z_bucket,
round(avg(z), 2) AS avg_z_start,
round(avg(z_fwd), 2) AS avg_z_20d_later,
count() AS episode_count
FROM horizon
WHERE date >= '2019-01-01'
AND date <= '2025-11-15'
AND isFinite(z_fwd)
GROUP BY z_bucket
ORDER BY avg_z_startz below -2バケットで始まったセッションは、当日の平均zスコアが-2.42でした。同じスプレッドは20セッション後、138セッションを通じて平均-0.84となりました。反対側では、z above 2バケットは2.64で始まり、20セッション後には0.92を記録しました。両端からゼロへ近づく曲線が、平均回帰によって生じる形です。
件数の列を確認しながら読んでください。外側のバケットほどセッション数は少なく、同じバケット内のセッションは大きく重複しています。そのため、少数の長い期間が行の大半を占めています。平均値は、個々のエントリーについての保証ではありません。
ペアトレードが機能しなくなるのはなぜか
ヘッジ比率は一定ではありません。 暦年ごとに再推定すると、数値は動きます。
各数値の背後にある正確なSQL
WITH daily AS (
SELECT
date,
anyIf(toFloat64(close), ticker = 'PEP') AS pep,
anyIf(toFloat64(close), ticker = 'KO') AS ko,
anyIf(toFloat64(close), ticker = 'LOW') AS lowes,
anyIf(toFloat64(close), ticker = 'HD') AS hd
FROM global_markets.stocks_daily_aggs
WHERE ticker IN ('KO', 'PEP', 'HD', 'LOW')
AND date BETWEEN '2019-01-01' AND '2025-12-31'
GROUP BY date
HAVING ko > 0 AND pep > 0 AND hd > 0 AND lowes > 0
)
SELECT
toYear(date) AS year,
round(covarSamp(pep, ko) / varSamp(ko), 3) AS pep_on_ko_beta,
round(covarSamp(lowes, hd) / varSamp(hd), 3) AS lowes_on_hd_beta
FROM daily
GROUP BY year
ORDER BY year2.338から2019までと、-0.543から2025までについて測定した、KOに対するPEPの傾きです。比較用に、二つ目のセクター・ペアも併記しています。全期間のデータで一つの傾きを推定し、初日からその数値で取引するバックテストでは、初期のすべての取引に、当時は誰も保有できなかった数値を割り当てることになります。解決策は地味ですが有効です。後方移動窓で再推定し、各日について、その日に利用可能だったパラメーターで評価します。再現可能なバックテストにすれば、その規律を第三者が検証できます。
コーポレートアクションは、一方の銘柄のデータを一晩で書き換えます。 4対1の株式分割では、一株が四株になり、表示価格は四分の一になります。この日付をまたいで未調整価格から算出したスプレッドは、実際には取引されなかった水準へ急変します。合併、スピンオフ、指数銘柄の入れ替えも、より目立たない形で同じ影響を及ぼします。まず株式分割調整済みの価格履歴を使う必要があります。また、ティッカーがまったく別の会社に割り当て直されることもあります。これがティッカーシンボルによってデータセットが壊れる理由です。
ブレイクと投資機会は、発生中には見分けがつきません。 ペアがこれまで示した最大のzスコアは、過去最高のエントリー機会かもしれません。一方で、関係性が崩れた最初の週かもしれません。当日には、どちらも拡大したスプレッドに見えます。統計で判断できない部分は、ポジションサイズが担います。ケリー基準によるポジションサイジングとボラティリティ・ターゲティングによるポジションサイジングは、一つのスプレッドに口座のどれだけを動かすことを許容するかを決める二つの枠組みです。最大ドローダウンは、平均の反対側に長期間とどまった場合に、資産曲線へどのような影響が及ぶかを測ります。
ローリングzスコアは、共和分検定ではありません。 形式的な検定は存在します。Engle-Granger手順では回帰を実行した後、残差スプレッドの定常性を検定します。Johansen検定は、この考え方を二銘柄を超える組み合わせに拡張します。zスコアは、これらの検定が確認する性質を前提としています。しかし、安定した平均を持たないスプレッドに対しても、大きな数値を問題なく表示します。トレンドを持つ系列は、常に自身の後方移動平均から大きく乖離するためです。
データに関する注記と、これらの数値に含まれない要素
相関パネルでは、2024-01-01から2025-12-31までの日次終値を使い、連続する終値からリターンを算出しています。各銘柄の初回セッションは除外しています。zスコアの推移では、2023年の終値だけを使ってヘッジ比率を推定し、2024年と2025年については、63セッションの後方移動平均と標本標準偏差で評価しています。データは各週の最終観測値にサンプリングしています。平均回帰パネルでは2018年からの履歴を読み込み、2019年以降を表示しています。そのため、評価対象となる各日には完全な後方移動窓があります。サンプル内で前方に20セッションを確保できるよう、2025年11月中旬で停止しています。
これらのパネルはいずれも戦略ではありません。売買コスト、ビッド・アスク・スプレッド、ショート legの借株料、配当、資金調達コストを含んでいません。終値だけを使っているため、終値でエントリーした場合、その価格で約定できたとは限りません。
よくある質問
相関と共和分の違いは何ですか?
相関は、二つの銘柄が同じ日に同じ方向へ動くかどうかを、-1から+1の範囲で測る指標です。共和分は、両銘柄の価格差が安定した範囲に収まるかどうかを測ります。前者の数値が高くても後者を満たさない組み合わせはあり、相関でスクリーニングしたペアがその後乖離する主な理由となります。
ペアトレーディングのヘッジ比率はどのように計算しますか?
一方の銘柄の価格をもう一方の価格で回帰し、直線の傾きを求めます。その傾きが、一方の銘柄一単位を相殺するために必要なもう一方の銘柄の単位数です。ヘッジ比率は変動するため、多くの実装では全期間で一度だけ推定せず、直近の一定期間を使って再推定します。
ペアトレーダーは、どのzスコアでエントリーとエグジットを行いますか?
チュートリアルでは通常、直近平均から標準偏差で1.5から2離れた水準でエントリーし、0.5付近でエグジットします。これらの数値は、過去の解説記事から受け継がれた慣行であり、検証された最適値ではありません。数値を変えると、取引回数と各ポジションの保有期間の両方が変わります。
相関が低い二つの銘柄でも共和分することはありますか?
あります。相関は日々の変化幅について計算し、共和分は価格差の水準について計算します。そのため、日々の値動きがほとんど一致しない銘柄同士でも、長期的な価格差が安定することがあります。逆も起こります。日次相関が高い一方で、価格差が年を追うごとに拡大するケースです。
共和分するペアが機能しなくなるのはなぜですか?
価格差を安定させていた関係が変化することがあります。例えば、合併、スピンオフ、一方の企業の事業構成の変化、保有者を変える指数構成の変更などです。乖離直前の統計は、通常の平均回帰直前の統計と同じように見えます。そのため、乖離を予測するのではなく、ポジションサイズで対応する必要があります。
ここにある各パネルには、基になったSQLが正確な形で付いています。そのため、対象期間、推定期間、バケットの境界をすべて確認できます。フォローしているペアについて同じspreadとzスコアを実行するには、Strasmore terminalで質問を平易な英語で入力してください。