K_DM Book
用語集
対象者 標準
対象者別の入口 →

対象者の設定はこのブラウザの中にだけ保存されます。

言語 JA

ピーク検出

05.1.5

ピーク検出

最終更新 2020-03-25 読了時間 4 分
まとめ
ピーク検出は、時系列の中で「両隣より高い点」を機械的に見つける処理で、scipy.signal.find_peaks に系列を渡すとピークの位置(添字)が返ってきます。このページでは FRED から取得したハイイールド債のオプション調整後スプレッド BAMLH0A0HYM2(2017 年以降の日次)に distance=30、threshold=0.03、width=3 の条件を付けてピークを検出し、細かな揺れを除いた「信用不安が高まった日」だけを抜き出します。検出した日付を S&P 500 の折れ線に axvline で縦線として重ね、スプレッドのピークと株価の底が重なる 2018 年末や 2020 年 3 月を確認します。読み終えると、3 つの引数がピークの数をどう絞るかを図で説明できるようになります。

直感 #

ハイイールド債スプレッドは低格付け社債の利回りが国債をどれだけ上回っているかで、投資家が不安になると急に広がり、落ち着くと縮みます。「不安が頂点に達した日」を知りたくても、日次の線は細かく上下するので、両隣より高い点を全部拾うと何百個もの「ピーク」が出てしまいます。find_peaks はそこに「隣のピークと 30 日以上離れていること」「両隣より 0.03 以上高いこと」「山の幅が 3 日以上あること」という条件を付けて、本当に目立つ山だけを残します。残ったピークの日に S&P 500 がどうなっていたかを見れば、「信用市場の悲鳴が株価の底と重なるか」を検証できます。

FRED からデータを取り出す準備 #

スプレッドと株価指数はどちらも FRED から取得します。関数 get_fred_data() は FRED データベース のページと同じもので、系列 ID を渡すと日付索引の DataFrame を返します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import japanize_matplotlib
import matplotlib.pyplot as plt
import os
import pandas as pd
import seaborn as sns
from full_fred.fred import Fred

# FRED_API_KEY = os.getenv('FRED_API_KEY')
fred = Fred()
print(f"FRED APIキーが環境変数に設定されている:{fred.env_api_key_found()}")

def get_fred_data(name, start="2013-01-01", end=""):
    df = fred.get_series_df(name)[["date", "value"]].copy()
    df["date"] = pd.to_datetime(df["date"])
    df["value"] = pd.to_numeric(df["value"], errors="coerce")
    df = df.set_index("date")

    if end == "":
        df = df.loc[f"{start}":]
    else:
        df = df.loc[f"{start}":f"{end}"]

    return df
FRED APIキーが環境変数に設定されている:True

ハイイールド債スプレッド(BAMLH0A0HYM2)を取得する #

ソース:https://fred.stlouisfed.org/series/BAMLH0A0HYM2

ICE BofA US High Yield Index Option-Adjusted Spread を 2017 年 1 月以降の日次で取得します。

1
2
df_BAMLH0A0HYM2 = get_fred_data("BAMLH0A0HYM2", start="2017-01-01")
df_BAMLH0A0HYM2.head(5)

value
date
2017-01-02NaN
2017-01-034.13
2017-01-044.02
2017-01-054.04
2017-01-063.98

S&P 500 を取得する #

ソース:https://fred.stlouisfed.org/series/SP500

比較対象の S&P 500 も get_fred_data("SP500") で取得します。FRED の SP500 は直近 10 年分だけ配信されている点に注意してください。

1
2
df_SP500 = get_fred_data("SP500", start="2017-01-01")
df_SP500.head(5)

value
date
2017-01-02NaN
2017-01-032257.83
2017-01-042270.75
2017-01-052269.00
2017-01-062276.98

スプレッドを左軸、S&P 500 を右軸にして 1 枚に重ねます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
sns.set(rc={"figure.figsize": (15, 8)})
japanize_matplotlib.japanize()

fig = plt.figure()
ax1 = fig.add_subplot(111)
sns.lineplot(data=df_BAMLH0A0HYM2, x="date", y="value", label="ハイイールド債スプレッド", color="b")
plt.legend(loc="lower right", fontsize="20")

ax2 = ax1.twinx()
sns.lineplot(data=df_SP500, x="date", y="value", label="S&P 500", color="r")
plt.legend(loc="upper left", fontsize="20")
<matplotlib.legend.Legend at 0x13735de10>

S&P 500 figure

実行結果の読み方 #

青のスプレッドが跳ねた 2018 年 12 月と 2020 年 3 月に、赤の S&P 500 は大きく下げています。特に 2020 年 3 月はスプレッドが 10% 超まで広がった日とほぼ同じ日に株価が底を打っています。この「青の山」を機械的に拾うのが次の find_peaks です。

ピークを検出する #

ハイイールド債スプレッドのピーク #

scipy.signal.find_peaks は、両隣より高い点をすべて候補にし、引数で与えた条件を満たすものだけを残して添字の配列で返します。

1
2
3
4
5
6
7
8
import numpy as np
from scipy.signal import find_peaks

data = df_BAMLH0A0HYM2["value"]
peaks, _ = find_peaks(data, distance=30, threshold=0.03, width=3)
plt.plot(data)
plt.plot(data.iloc[peaks].index, data.iloc[peaks], "o", markersize=10)
plt.show()

find_peaksを用いて、ピークとなっている時点を検出してみます。 figure

実行結果の読み方 #

丸印は 2018 年 12 月末、2020 年 3 月下旬、2022 年前半などスプレッドの山の頂点に付いています。2020 年 3 月の急騰部分に丸が 1 つしか無いのは distance=30 の効果で、distance=5 にすると同じ山の中に複数の丸が並びます。

S&P 500 とハイイールド債のピークを比較する #

検出したピークの日付を S&P 500 の図に黒い縦線として引き、株価の底と重なるかを見ます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
sns.set(rc={"figure.figsize": (15, 8)})
japanize_matplotlib.japanize()
sns.lineplot(data=df_SP500, x="date", y="value", label="S&P 500", color="r")
for peak_date in data.iloc[peaks].index:
    plt.axvline(peak_date, alpha=0.5, linewidth=0.75, color="k")
    plt.text(
        peak_date + pd.DateOffset(5),
        3300,
        f'{peak_date.strftime("%Y/%m/%d")}',
        rotation=90,
    )
plt.legend(loc="upper left", fontsize="20")
<matplotlib.legend.Legend at 0x136f9a020>

SP500とハイイールド債券のピークを比較してみる figure

実行結果の読み方 #

2018/12 と 2020/03 の縦線は S&P 500 の谷底とほぼ一致しています。信用市場の不安が頂点を打った日が株価の底と重なる、という関係が 2 回の大きな下落で成り立っていることが分かります。一方で小さなピークの縦線は株価の小さな押し目に対応するだけで、スプレッドのピークがすべて買い場ではありません。

使いどころと落とし穴 #

find_peaks の distance と width は「点の数」で指定するので、日次データでの 30 は約 1.5 か月、月次データでは 2.5 年と意味が変わります。頻度を変えたら引数も見直してください。threshold は両隣との差だけを見るため、なだらかに上がって高い水準で揺れている山では弱く、「ベースラインからの高さ」で絞りたいときは prominence 引数のほうが適しています。ピークは「その後に下がった」ことが確定してから分かる後知恵の指標なので、リアルタイムの売買シグナルとして使うには、直近の点をピークと判定できない遅れがあることを前提にします。

まとめ #

ピーク検出は find_peaks(data, distance=30, threshold=0.03, width=3) の 1 行で、両隣より高い候補点を 3 つの条件で絞り込んで添字を返します。distance が近接する山を 1 つにまとめ、threshold が微小な凸を、width が 1 日だけの跳ねを除くので、ハイイールド債スプレッドの 1,300 点から数個の「不安の頂点」が残ります。その日付を axvline で S&P 500 に重ねると、2018 年 12 月と 2020 年 3 月の株価の底に縦線が立ち、信用市場のピークと株式市場の底が同時に来ることを図で確認できます。