【投資】相場の勢いを表すストキャスティクスをデータ分析して分かったこと

＜あらすじ＞
「買われすぎ」や「売られすぎ」といった相場の勢いを表す過熱感
その過熱感を数値で判断できるストキャスティクスについて検証しました。

TOPIX500銘柄の過去5年分のデータを使って
ストキャスティクスの買いサインの数日後に、株価が上がっているのかを調査します。

＜結論＞
・市場の過熱感を知りたいときはストキャスティクスよりRSIを使うべきである

# きっかけ
「買われすぎ」や「売られすぎ」といった相場の勢いを表す過熱感
前回、その過熱感を数値で判断できるRSIについて検証しました。

[投資タイミングを判断するときに必ず見てほしい指標RSIをデータ分析](https://qiita.com/MandT500/items/37c2d357e89ca6354d13)

【結論】
・RSIの使い方次第で確実に勝率を上げられる
・投資タイミングの判断材料として有効である

RSIの検証結果が予想以上の成績であったため
同じ過熱感を表す指標として有名な**ストキャスティクス**についても検証してみたいと思います。

※この記事だけでも内容は完結していますが、先にRSIの記事を読んでいただいた方が理解しやすいかもです

# ストキャスティクス（stochastics）
ストキャスティクスは一定期間の一番高かった値段と安かった値段の値幅に対して、現在の株価がどのくらいの位置いるのかということを数値化したもので、「売られすぎ」なのか「買われすぎ」なのかを知りたいときに役立ちます。

過熱感を表すテクニカル指標として、RSIと並んで個人投資家に人気があります。

ストキャスティクスの使い方を簡単に紹介します。
「%K」「%D」「slow%D」の3つのラインを組み合わせて使用するテクニカル分析で
それぞれの算出方法は以下の通りです。（$n$は5, 9, 14が使用されることが多い）
▼ストキャスティクの算出方法
![ストキャスティクス.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/906836/bf5d51a4-f93a-0cc2-8899-8539e33e52c4.png)
ここで重要なことは
・3つのラインは全て一定期間の一番高かった株価と一番安かった株価を使って、現在の株価の過熱感を表す
・3つのラインは全て0～100％の値をとり、一般的に30％以下が売られすぎ、70％以上が買われすぎ
・株価への反応の速さは「**%K > %D > slow%D**」の順である

これらの3つのラインを個別で使用する場合や、2つを組み合わせて使用する場合があり
%Kと%Dを使用するストキャスティクスを「ファストストキャスティクス」
%Dとslow%Dを使用するストキャスティクスを「スローストキャスティクス」といいます。
「ファスト」は株価の動きに敏感に反応し、「スロー」は反応は遅いがだましは少なくなります。
自分の投資スタイルや銘柄に合わせて使い分ける必要があります。

ストキャスティクスは様々な使い方があるため、少し難易度が高いです。
そこで最も良い使い方を見つけるべく検証したいと思います。

## 買いサイン
ストキャスティクスの買いサインは、3つのラインのうち1つだけを使う場合と2つを組み合わせて使用する場合で異なります。

1つのライン：基準値を下から上に突き抜けたとき
2つのライン：基準値以下でゴールデンクロスしたとき

▼ストキャスティクスの買いサイン（**基準値=30%**の場合）
![sto_buy_timing.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/906836/f34b4a5d-4bcc-0766-82fe-c32d54086e3f.png)

1つだけを使う場合は「%K」「%D」「slow%D」の3種類
2つを組み合わせて使用する場合は「%Kと%D」「%Dとslow%D」の2種類
が、使用されます。

一般的に30％以下が売られすぎと言われるため、今回の検証では基準値に30%,20%,10％を使用します。

# 検証方法
大まかな流れはTOPIX500銘柄を対象に5年分のデータからストキャスティクスの買いサインを見つけ、その数日後に株価が上がっているのかを調査します。また、買いサインの基準値を変化させて結果を比較します。

買いサインから何％株価が変動したのかを表す、株価の変化率（変動率）の分布も求めました。

一旦、検証パラメータを羅列しますが、後ほど例を使って説明します。
また、検証プログラムは記事の最後に記載します。

【検証期間】
2015年1月1日～2021年2月12日の約5年間

【対象銘柄】
TOPIX500銘柄

【ストキャスティクスの設定期間】
1. 5日
2. 9日（最も使われる）
3. 14日

【株価の上昇を確認する日】
1. 1日後
2. 3日後
3. 5日後
4. 10日後

【基準値】
＜基準値を下から上に突き抜けたとき＞
1. 30％
2. 20％
3. 10％
＜基準値以下でゴールデンクロスしたとき＞
1. 30％
2. 20％
3. 10％

## 例
ストキャスティクスの買いサインが生じた日から見て、数日後に上昇してるかを確認します。
▼検証方法の概要（基準値：30％を下から上に突き抜けたとき）
![sto_example1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/906836/f6409fc2-4def-17f0-5f88-fa2ee63c57b1.png)
▼検証方法の概要（基準値：30％以下でゴールデンクロスしたとき）
![sto_example2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/906836/53c4276c-e7ba-9144-8d40-899d5cc07529.png)

上の例では基準値を30%としましたが、基準値を30,20,10％の3通りで検証します。
前回検証したRSIでは基準値を30,25,20％の3通りで検証しましたが、ストキャスティクスは比較的簡単に30%以下になるため基準値を下げました。

実際にトヨタ自動車のデータを使って、買いサインの**基準値を20％**とした場合を説明します。
まず、1つのラインを使用した買いサイン「基準値=20%を下から上に突き抜けたとき」の例です。（**slow%D**を使用）
見やすさのために2019年1月～2021年2月の約2年分のデータを用いました。
青丸はストキャスティクスのslow%Dが20%を下から上に突き抜けた日を示しています。
この時の株価から1,3,5,10日後に上がっているのかを調べます。
▼ストキャスティクス「slow%D」（設定期間：14日,基準値：20％）の買いサイン
![sto_example_toyota1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/906836/be205e14-e0b8-bc0c-cf6a-c332c56f706a.png)

次に、2つのラインを使用した買いサイン「基準値=20%以下でゴールデンクロスしたとき」の例です。（**%D**と**slow%D**を使用）
見やすさのために2020年6月～2021年2月の約8ヵ月分のデータを用いました。
緑丸はストキャスティクスの%Dとslow%Dが20%以下でゴールデンクロスした日を示しています。
※ ゴールデンクロスの前後で%Dとslow%Dがともに基準値以下のタイミングのみを選択
この時の株価から1,3,5,10日後に上がっているのかを調べます。
▼ストキャスティクス「%Dとslow%D」（設定期間：14日,基準値：20％）の買いサイン
![sto_example_toyota2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/906836/1a3ff457-7e0e-bbbb-70bf-9c408376f4b3.png)

また、それぞれ買いサインから1,3,5,10日後に株価がどれだけ変動したか（変化率）を求め、分布図も作成します。

# 検証結果
ここでは1,3,5,10日後に上昇している確率を基準値30,10％の2通りと、分布図を一部記載します。
残りの結果は記事の最後にまとめて記載します。
使用するデータは前述の通りTOPIX500銘柄の5年間です。

【ストキャスティクスの設定期間】
1. 5日
2. 9日（最も使われる）
3. 14日

【基準値】
＜基準値を下から上に突き抜けたとき＞
1. 30％
2. 10％
＜基準値以下でゴールデンクロスしたとき＞
1. 30％
2. 10％

---
早速ですが結果を表にまとめました。
上昇確率が**53％以上**の場合に**太字**となっています。
▼基準値が30%のとき
![sto_result1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/906836/dcad4cae-116a-6525-5ca5-045d232a0ba0.png)

▼基準値が10%のとき
![sto_result2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/906836/2df50e1c-98b4-43d0-16b4-a5a59afa609b.png)

【表からわかること】
・設定期間によるが買いサインの総数が多い（RSIと比較すると同様の条件で5倍程度）
・上昇確率が50%を下回る場合も多い
・基準値を小さく（条件を厳しく）しても結果が明確に上昇しない
・ラインを1つ使用した場合も2つ使用した場合もどちらの方が良いとは言えない

次に株価の変化率（買いサインから何％株価が変動したか）の分布をいくつか示します。
青が上昇，赤が下落を表します。
以下は上昇確率が54%で最も成績が良い条件での変化率分布です。
▼3日後の株価の変化率分布（設定期間：5日, 10%以下で％Dとslow%Dがゴールデンクロスしたとき）
![%D%slowD_below10 (period=5, x day later=3).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/906836/188302e4-7a5f-1961-4ace-7beaef0e6a27.png)

以下は１つのラインを使った場合で最も成績が良い条件での変化率分布です。
▼10日後の株価の変化率分布（設定期間：5日, %Kが30%を下から上に突き抜けたとき）
![%K_below30 (period=5, x day later=10).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/906836/5a6b99cc-e19a-d277-3e0f-af1854e79c4f.png)

【表からわかること】
・どちらも少しだけではあるが、山が右にずれている
・「-2~0」と「0~2」の数は同程度だが「-10~-2」よりも「2~10」の数が少しづつ多い
・10%以上の上昇など大きな上昇は見込めない

ストキャスティクスの結果は[前回検証したRSI](https://qiita.com/MandT500/items/37c2d357e89ca6354d13)よりも明らかに悪いです。(RSIでは上昇確率55%以上が多数)
同じ**過熱感**を表す指標ですが、算出方法が異なると勝率も大きく変わります。

# まとめ
「買われすぎ」や「売られすぎ」といった相場の勢いを表す過熱感
その過熱感を数値で判断できるストキャスティクスについて検証しました。
ストキャスティクスは一定期間の一番高かった値段と安かった値段の値幅に対して、現在の株価がどのくらいの位置いるのかで数値化します。
株価への反応の速さが異なる3つのラインの買いサインを検証したところ、一部の条件で数日後の上昇確率が53%を超える買いサインが存在しました。しかし、この結果は同じ過熱感を表す**RSI**と比べると良い結果とは言えません。

＜ストキャスティクスとRSIの算出方法の違い＞
ストキャスティクス：ある期間の株価の最高値と最安値から算出
RSI：ある期間の株価の上がり幅と下がり幅から算出

この算出方法の違いにより、同じ設定期間でも算出に使う株価のデータ数が異なります。
＜例：設定期間が9日＞
ストキャスティクスは9日間の株価のうち最高値，最安値，現在の株価の3日分です。（9分の3）
RSIは9日間の株価のうち全ての日にちの株価から算出した値動き幅を使います。（9分の9）

この違いが検証結果の違いに影響を与えているのではないかと考えます。
RSIでは設定期間を長くすると動きが滑らかになるため、だましの数が減り上昇確率が上がりやすいです。
一方でストキャスティクスは設定期間を長くしても、その傾向が見られません。
これはストキャスティクスの場合、設定期間を長くすると使用する株価の割合が小さくなるためと考えられます。

以上より、「**市場の過熱感を知りたいときはストキャスティクスよりRSIを使うべき**」と結論付けます。

今回の検証ではストキャスティクスのシンプルな買いサインについて検証しましたが、ひょっとするともっと有効な買いサインの判断方法が存在するのかもしれません。ただ、初心者にはRSIの方が使いやすいとはっきり言えます。

# 次は
「RSI」「ストキャスティクス」と検証しましたが、過熱感を表す有名な指標として「移動平均線乖離率」があります。
次回はこの移動平均線乖離率について検証したいと思います。
また、いずれは過熱感を表す指標とMACDなどのトレンドを表すテクニカル分析を組み合わせた検証も考えています。

<font color="DarkGray" size=1>
＜Youtube＞
分析動画を投稿しています。
検証結果を多くの方に見てもらえると嬉しいです。
[https://www.youtube.com/channel/UCKM_EhOxMfXkcLFOwAdEKcQ](https://www.youtube.com/channel/UCKM_EhOxMfXkcLFOwAdEKcQ)
↑チャンネル登録していただけますと励みになります。
</font>

<font color="DarkGray" size=1>
＜過去記事＞
・[日経225全銘柄の投資効率を検証](https://qiita.com/MandT500/items/f4aaafa0de824dab92c2)
・[【5年分データ分析】ゴールデンクロスの数日後に株価は上がっているのか](https://qiita.com/MandT500/items/1f2ed62922de3037fe3c)
・[【5年分データ分析】MACDの買いサインから上がる確率を検証しました](https://qiita.com/MandT500/items/52f253bffb14306611c6)
・[コロナ・ショック後から株価上昇し続けている１５銘柄](https://qiita.com/MandT500/items/ede92d7c322d0bbf32dd)
</font>

# 検証結果の追加
数が多く全てを載せきれないため
基準値20%での1,3,5,10日後に上昇している確率と以下の変化率の分布を記載します。

【検証期間】
2015年1月1日～2021年2月12日の約5年間

【対象銘柄】
TOPIX500銘柄

【ストキャスティクスの設定期間】
1. 5日
2. 9日（最も使われる）
3. 14日

【株価の上昇を確認する日】
1. 1日後
2. 3日後
3. 5日後
4. 10日後

【基準値】
＜基準値を下から上に突き抜けたとき＞
1. 30％
＜基準値以下でゴールデンクロスしたとき＞
1. 10％

▼基準値が20%のとき
![sto_result_20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/906836/0ace2974-2086-48b1-6ec6-910901a22b34.png)

▼%Kが基準値30%を下から上に突き抜けたとき（設定期間：5，9，14日）
![%K_30_all.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/906836/bb29ad57-4cb6-6a06-336e-df0cf2e46785.png)

▼%Dとslow%Dが基準値10%以下でゴールデンクロスしたとき（設定期間：5，9，14日）
![%D%slowD_10_all.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/906836/f35ccfee-ee5a-f27e-ebee-5c665a52a7ab.png)



# 検証プログラム
プログラム内で使用している自作の関数や株価データの取得方法は以下の記事をご参照ください。（執筆中）
・[株分析ツールの使い方（備忘録）](https://qiita.com/MandT500/items/f845abb8ce5a3a0729b0)

```Python:
import trade_package as tp # 株価分析用に自作した関数をまとめたもの
import pandas as pd
import matplotlib.pyplot as plt
from math import ceil 

# 銘柄コードの読み込み
stocks = tp.get.topix500()

# 条件と保存ファイル名の設定
point = 10
line = "slowk"
line2 = "slowd"
case = "%D%slowD_below"+str(point)

# resultデータフレーム作成
day = [1,3,5,10]  # 株価を確認する日（〇日後の株価）
col=["buy_sign_count"]
period = [5,9,14]   # 設定期間
for d in day:
    col.append("roc_d"+str(d)+"_plus")
result = pd.DataFrame(data=0,index=range(len(period)),columns=col)
# rocマップのデータフレーム
col=[]
for p in period:
    for d in day:
        col.append("roc_p"+str(p)+"_d"+str(d))
index=[]
# 分布図の設定(-40~40%を2%間隔)
for i in range(-42,42,2):
    if(i==40):
        index.append(str(i)+"~")
    elif(i==-42):
        index.append("~"+str(i+2))
    else:
        index.append(str(i)+"~"+str(i+2))
roc_map = pd.DataFrame(data=0,index=index,columns=col)

for code in stocks.code:
    print(code)
    # 用意した株価データの読み込み
    read_data = tp.get.price(code)
    
    for p in range(len(period)):
        data = read_data.copy()

        # ファーストストキャスティクス
        tp.tech.stochf(data,K=period[p],D=3)
        # スローストキャスティクス
        tp.tech.stoch(data,fastK=period[p],slowK=3,slowD=3)
        data["buy_sign"] = False

        # タイミングを取得
        for i in range(len(data.index)-1):
            # 下から上へ突破したとき
            # if(data[line].iat[i]<point and data[line].iat[i+1]>=point):
            #     data["buy_sign"].iat[i+1] = True
            # ゴールデンクロス
            if(data[line].iat[i]<point and data[line2].iat[i]<point \
                and data[line].iat[i]<data[line2].iat[i] and data[line].iat[i+1]>data[line2].iat[i+1]):
                data["buy_sign"].iat[i+1] = True

        # 〇日後に上昇しているか確認    
        for bs in data.index[data.buy_sign]:
            for d in day:
                if(len(data.Close[:bs])+d<=len(data.Close)):
                    data.at[bs,"roc_d"+str(d)] = (data.Close[len(data.Close[:bs])+d-1]-data.Close[bs])/data.Close[bs]*100
                else:
                    # 〇日後の株価がない場合は最新の株価
                    data.at[bs,"roc_d"+str(d)] = (data.Close[-1]-data.Close[bs])/data.Close[bs]*100
                # 分布に振り分け
                roc_index = 20+ceil(data.at[bs,"roc_d"+str(d)]/2)
                if(roc_index<0):
                    roc_index=0
                elif(roc_index>41):
                    roc_index=41   
                roc_map.at[index[roc_index],"roc_p"+str(period[p])+"_d"+str(d)] += 1

       
# 上昇した数をカウント/roc分布を画像出力
for p in range(len(period)):
    result["buy_sign_count"].iat[p] = sum(roc_map.iloc[:,p*len(day)])
    for d in range(len(day)):
            result["roc_d"+str(day[d])+"_plus"].iat[p]=sum(roc_map.iloc[21:,p*len(day)+d])
            
            # roc分布を画像出力
            fig = plt.figure(figsize=(16, 12))
            ax = fig.add_subplot(111)
            bar_list = ax.bar(roc_map.index,roc_map["roc_p"+str(period[p])+"_d"+str(day[d])], width=1, color="#8ac6d1")
            [bar_list[i].set_color("#ffb6b9") for i in range(21)]
            ax.set_xticklabels(roc_map.index,rotation=90)
            ax.tick_params(labelsize=20)
            ax.grid(axis="y",c="lightgray")
            title = case+" (period="+str(period[p])+", x day later="+str(day[d])+")"
            ax.set_title(title,fontsize=24)
            ax.set_xlabel("Rate of Change [%]", fontsize=24)
            ax.set_ylabel("counts", fontsize=24)
            win = round(result.at[p,"roc_d"+str(day[d])+"_plus"]/result.at[p,"buy_sign_count"]*100,1)
            fig.text(0.75,0.5,"{:}%".format(win),size=40,color="#7dc4d1")
            fig.text(0.17,0.5,"{:}%".format(100-win),size=40,color="#ffa8ac")
            fig.savefig(title+".png", bbox_inches='tight')
        
print(result)
```