Pythonで株価をシミュレーションしてみた

Pythonを使うことで、未来の株価をシミュレーションしてみようと思います。

Python、株の話ですが、そこまで難しい内容までは踏み込まないので気軽に読んでもらえればと思います。

## 背景
前回の記事で、以下のことが分かりました。

* 上場インデックスファンドTOPIX(1308)の2011/7/22～2021/7/21のデータについて分析
* 全データの5%を異常、95%を正常と仮定し、OneClassSVMで正常状態と異常状態に分けることで、それぞれの状態内では株価のリターン（前日比率）が正規分布していることが確認できた
* 正常状態では、株価の前日比率は
  * 平均：1.0005478168389164
  * 標準偏差：0.01024285501985374
* 異常状態では、株価の前日比率は
  * 平均：0.9971137713072488
  * 標準偏差：0.029152921438795287
* 全データの5%が異常状態


https://qiita.com/Mukai1/items/4d06436d0495cca2dd12


この知見をもとに、上場インデックスファンドTOPIXの未来の株価のシミュレーションを行います。

## シミュレーションのための関数を作成

まずは、上記の知見をもとに、シミュレーションのための関数を作成します。この関数を実行することで、分布に従った株価の前日比率が返ってきます。

```python
# 関数を定義
def make_return():
    loc1 = 1.0005478168389164
    loc2 = 0.9971137713072488
    scale1 = 0.01024285501985374
    scale2 = 0.029152921438795287
    
    # 5%の確率で異常状態の正規分布からサンプリング
    if np.random.rand() < 0.05:
        a  =np.random.normal(loc2,scale2)
        svm_result = -1
    # 95%の確率で正常状態の正規分布からサンプリング
    else:
        a = np.random.normal(loc1, scale1)
        svm_result = 1
    return a, svm_result
```

この関数を10000回実行した結果を可視化します。正常・異常の2つの正規分布を作成できていることが確認できました。

```python
# 10000回シミュレーションを実行
return_list = []
svm_list = []
for i in range(10000):
    a, svm_result = make_return()
    return_list.append(a)
    svm_list.append(svm_result)
    
return_list = np.array(return_list)
df_sim  =pd.DataFrame({'return':return_list, 'svm':svm_list})

# シミュレーションの結果確認
plt.hist(df_sim[df_sim['svm'] == 1]['return'], bins = 30)
plt.hist(df_sim[df_sim['svm'] == -1]['return'], bins = 30)
plt.show()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1951451/d5c8fda3-1630-7802-c6d9-f51e7f6aba3d.png)

## シミュレーションを実行

20年間のシミュレーションを1000回実行します。

```python
# 結果の箱を作成
df_sim1 = pd.DataFrame()

from tqdm import tqdm

# 1000回シミュレーションを実行
for j in tqdm(range(1000)):
    return_list = []
    
    #1年間を240日と考えて、20年後までをシミュレーションする
    for i in range(240 * 20):
        a,svm_result = make_return()
        return_list.append(a)
    return_list = np.array(return_list)
    df_sim1[j] = return_list
    
# 累積リターンを計算
df_sim2 = df_sim1.cumprod()
df_sim2.head()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1951451/a8fa085d-474b-234f-80eb-43ed038a0b29.png)

## シミュレーション結果の可視化

まずは1年間のシミュレーション結果を可視化します。横軸が何日後かを表しており、縦軸が株価が何倍になるかを表しています。1年後に1倍を下回ってしまい、損失となってしまうグラフの色は赤色にしています。

```python
# 結果を可視化
plt.figure(figsize = (15, 4))
num = 0
for i in df_sim2.columns:
    # 1年後までを可視化
    temp=df_sim2[i].iloc[:240 * 1]
    
    # 最終結果が1以下の場合はグラフを赤色に変更
    if temp.iloc[-1] < 1:
        plt.plot(temp.index, temp, alpha = 0.2, c = 'r')
        num += 1
    else:
        plt.plot(temp.index, temp, alpha = 0.2, c = 'g')
        
print("損失が出る確率：{}/1000".format(num)) 
print("中央値:{:.3f}倍".format(df_sim2.iloc[240 * 1 - 1].median()))
plt.title('シミュレーション結果(1年間)')
plt.ylabel('倍')
plt.xlabel('日')
plt.show()
```
1000回シミュレーションの結果、1年後に損をしている確率は34.1%、中央値では株価が1.078倍、つまりリターンが7.8%あることが分かります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1951451/7326fda8-fc5c-f9e0-f16d-b47708548800.png)

同様に5年後、10年後、20年後までのシミュレーションをグラフにすると以下のようになります。
20年後には損をしている確率は5.2%まで下がり、1000回シミュレーションした場合の中央値では、株価が4.125倍、つまりリターンが312.5%あることが分かります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1951451/e79acd16-cad0-2871-373e-70283e760936.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1951451/637a149d-96cd-5d27-27dd-2024f8fbd9ee.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1951451/69832458-f626-e38a-f5ad-1376deeaf309.png)

## まとめ
Pythonと機械学習を使うことで、長期投資のメリットを示すことができました！

* 株の期待リターンは、異常状態ではマイナスになるが、それを加味しても、トータルでは期待リターンがプラスになっている
* よって、購入して長期保有しておくだけで、損をする確率が減っていく
* 20年保有し続けておけば、損をする確率は5.2%で、価格が4倍になっている確率が高い