05.1.4
相互相関
WPU101 と鉄鉱石の生産者物価指数 PCU2122121221 を取り、海運会社 Genco Shipping(GNK)の月次株価と numpy.correlate(mode="full") で相互相関を計算します。相関が最大になる位置 corr.argmax() から系列長を引いた値がラグで、0 なら同時に動き、正なら株価が物価指数に遅れて動くと読めます。計算の前に ax.twinx() で 2 本の線を単位の違う 2 軸に重ね、目で見た印象と数値のラグを突き合わせます。直感 #
鉄鉱石や鋼材の値段が上がると、それを運ぶばら積み船の需要が増え、海運会社 GNK の株価も上がりそうです。問題は「同じ月か、数か月遅れか」です。相互相関は片方の系列を 1 か月、2 か月とずらして重ね、重なりの良さ(積和)を全部のずらし幅について計算します。積和が最大になったずらし幅がラグで、ラグが 3 なら「物価指数が動いてから 3 か月後に株価が動く」と解釈できます。平均を引いてから計算するのは、両方の系列が常に正の値だと「ずらさなくても全部正×正で積和が大きい」という見かけの相関が出てしまうためです。
FRED からデータを取り出す準備 #
生産者物価指数は FRED から取得します。関数 get_fred_data() は FRED データベース のページと同じもので、系列 ID を渡すと日付索引の DataFrame を返します。
| |
FRED APIキーが環境変数に設定されている:True
鉄・鋼の生産者物価指数(WPU101) #
ソース: https://fred.stlouisfed.org/series/WPU101
Producer Price Index by Commodity: Metals and Metal Products: Iron and Steel は鉄及び鋼の生産者物価指数(月次)です。2018 年 1 月から 2022 年 5 月までの 53 か月分を取得します。
| |
| value | |
|---|---|
| date | |
| 2018-01-01 | 217.6 |
| 2018-02-01 | 222.1 |
| 2018-03-01 | 227.5 |
| 2018-04-01 | 234.1 |
| 2018-05-01 | 239.0 |
| |
<AxesSubplot:xlabel='date', ylabel='value'>

GNK の株価を月次に揃える #
Genco Shipping & Trading Limited ($GNK) はばら積み船を運航する海運会社です。Yahoo Finance から CSV で保存した日次株価を読み、物価指数と比べられるように月次平均にして月初日に揃えます。
| |
| Open | High | Low | Close | Adj Close | Volume | |
|---|---|---|---|---|---|---|
| Date | ||||||
| 2018-01-01 | 12.5575 | 13.4355 | 12.1375 | 12.7650 | 11.157512 | 603175.0 |
| 2018-02-01 | 13.9720 | 14.5420 | 13.1310 | 13.7600 | 12.027212 | 510600.0 |
| 2018-03-01 | 12.9925 | 13.8525 | 12.6520 | 13.4300 | 11.738769 | 357050.0 |
| 2018-04-01 | 15.0100 | 15.6925 | 14.4025 | 14.7575 | 12.899099 | 796075.0 |
| 2018-05-01 | 15.5280 | 16.3530 | 14.9620 | 15.9580 | 13.948420 | 631500.0 |
物価指数(200〜400)と株価(10〜25 ドル)は桁が違うので、左右に別の縦軸を持つ 2 軸グラフで重ねます。
| |

実行結果の読み方 #
青(物価指数)と赤(GNK)はどちらも 2020 年前半に底を打ち、2021 年に大きく上昇しています。目で見ると赤(GNK)がやや先に上がり始めたようにも、同時にも見えます。この「どちらが先か」を数値で決めるのが次の相互相関です。
相互相関を求める #
相互相関関数は、ふたつの信号、配列(ベクトル)の類似性を確認するために使われる。関数の配列の結果がすべて1であれば相関があり、すべてゼロであれば無相関であり、すべて −1 であれば負の相関がある。しばしば、相関と略されることがあり、相関係数と似ているために混同することがある。 (出典:Wikipedia)
numpy.correlate(a, v, "full") は、v を左端から右端まで 1 つずつずらしながら a との積和を計算し、長さ len(a) + len(v) - 1 の配列を返します。中央(添字 len(v) - 1)がずらし 0 に対応するので、最大値の位置から中央を引けばラグになります。
ドキュメント: numpy.correlate
| |
ラグ: 0
[<matplotlib.lines.Line2D at 0x13e252170>]
![[] figure](https://k-dm.work/images/finance/main/002-Cross-Correlation_files/002-Cross-Correlation_8_2.png)
実行結果の読み方 #
ラグは 0 で、相互相関の図は中央(横軸 52)を頂点とする 1 つの山になっています。鉄・鋼の物価指数と GNK の株価は月次で見ると同じ月に動いており、「物価指数を見てから株を買う」という時間差は月単位では存在しないことになります。
鉄鉱石の生産者物価指数(PCU2122121221)で同じことを試す #
ソース:https://fred.stlouisfed.org/series/PCU2122121221
Producer Price Index by Industry: Iron Ore Mining は鉄鉱石の生産者物価指数です。鋼材より上流の原料なので、GNK の株価に対して先行するかを同じ手順で確かめます。
| |
| value | |
|---|---|
| date | |
| 2018-01-01 | 121.0 |
| 2018-02-01 | 122.5 |
| 2018-03-01 | 122.5 |
| 2018-04-01 | 133.8 |
| 2018-05-01 | 138.7 |
| |

| |
ラグ: 0
[<matplotlib.lines.Line2D at 0x13e373fa0>]
![[] figure](https://k-dm.work/images/finance/main/002-Cross-Correlation_files/002-Cross-Correlation_12_2.png)
使いどころと落とし穴 #
np.correlate は正規化しないので、返ってくる値は相関係数(−1〜1)ではなく積和です。山の位置(ラグ)は読めますが高さを別のペアと比べることはできず、相関係数の形で欲しいなら両系列を標準偏差でも割るか、pandas の Series.corr を shift(k) しながら繰り返す方法に替えます。株価のように右肩上がりのトレンドを持つ系列は、トレンドだけで相互相関の山が広がります。diff() や pct_change() で変化率にしてから計算するほうが「動きの連動」を正しく捉えられます。月次に落とすと 53 点しかないので、ラグ 1 の差は 1 点のずれで決まることになり、日次や週次で点数を増やしてから確認するのが安全です。
まとめ #
相互相関は 2 本の系列を 1 期ずつずらしながら積和を取り、最大になるずらし幅をラグとして読む手法です。実装は np.correlate(a - a.mean(), b - b.mean(), "full") と argmax() - (len(b) - 1) の 2 行で、前提として resample("M") と .loc[start:end] で頻度と期間を揃えておきます。鉄・鋼と鉄鉱石のどちらの物価指数も GNK の株価とラグ 0 で連動しており、月次では先行関係は見つかりませんでした。トレンドを除いた変化率や、より細かい頻度で試すのが次の一手です。