ランダムウォークによる株価のシミュレーション

ランダムウォークによる株価のシミュレーション#

  • ランダムウォークにより株式時系列データの簡単なシミュレーションを行う

  • 「株式投資で元本割れを経験する割合が9割」という話を再現

前提#

ランダムウォーク#

過程\(y_t\)が定数項\(\delta\)を用いて

\[ y_t=\delta+y_{t-1}+\varepsilon_t, \quad \varepsilon_t \sim \operatorname{iid}\left(0, \sigma^2\right) \]

と表現されるとき、\(y_t\)はランダムウォークと呼ばれる。ただし、\(y_0=0\)とする。

ランダムウォークは代表的な単位根過程、すなわち、その差分系列が定常過程となる確率過程である。

データ#

Hide code cell source
import matplotlib.pyplot as plt
import japanize_matplotlib
import seaborn as sns
import yfinance as yf

ticker = yf.Ticker("^N225")
df = ticker.history(period="5y")
prices = df["Close"]
returns = prices.pct_change(1)

fig, axes = plt.subplots(ncols=2, figsize=[8, 2])
axes[0].hist(prices, bins=30)
axes[0].set(xlabel="価格", title="日経平均の分布")

axes[1].hist(returns, bins=30)
axes[1].set(xlabel="価格変化率",
            title=f"日経平均の日次リターン \n(平均={returns.mean():.1%}, 標準偏差={returns.std():.1%})")
fig.show()
../_images/8c635ffab1ab8ff91932fc2ca9aff15089f1a2388611d2329e6b89cdb1fb536b.png
Hide code cell source
fig, axes = plt.subplots(nrows=2, figsize=[4, 3], sharex=True)
axes[0].plot(prices)
axes[0].set(ylabel="日経平均")
axes[1].plot(returns)
axes[1].set(ylabel="日次リターン")
fig.show()
../_images/1e038fd4c34328d3f00d7741dca6f9aeda7eab6c9be64da2f005e6f9d69b7a38.png

ランダムウォークを10個生成するとつぎのようになる。

Hide code cell source
import numpy as np
base_price = 100

   
def gen_prices(seed, n = 250 * 3):
    np.random.seed(seed)
    returns_ = np.random.normal(loc=returns.mean(), scale=returns.std(), size=n)

    prev_price = base_price
    prices_ = [prev_price]
    for ret in returns_:
        price_ = prev_price * (1 + ret)
        prices_.append(price_)
        prev_price = price_
    return prices_

fig, ax = plt.subplots()
for seed in range(10):
    prices_ = gen_prices(seed)
    ax.plot(prices_, label=f"seed={seed}, 最安値={min(prices_):.1f}")
ax.set(ylabel="シミュレーション")
ax.legend()
fig.show()
../_images/852a4905a9fd2aeeedc75ecb5a73b02bc4b9b7591c587fa258ab1e5a3c1b0db1.png

元本割れが9割#

もっと試行回数を増やし、最安値が初期値を下回った(元本割れした)率を計算する

import pandas as pd

results = []
for n_years in [1, 2, 5, 7, 10]:
    for seed in range(100):
        prices_ = gen_prices(seed, n=250*n_years)
        results.append({
            "期間(年数)": n_years,
            "元本割れ率": min(prices_) < base_price,
            "最安値": min(prices_),
            "最高値": max(prices_),
        })

pd.DataFrame(results).groupby("期間(年数)").mean().round(3)
元本割れ率 最安値 最高値
期間(年数)
1 0.96 89.199 125.327
2 0.96 86.768 143.337
5 0.97 84.103 218.000
7 0.97 83.700 297.879
10 0.97 83.428 412.265