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

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

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

  • 「株式投資で元本割れを経験する割合が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/68f96ca1f1db20dbeb429dc529ed21a1364dc5f6b635bf854e859b00345fa0d4.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/d212e2569668a9225aa5db6ebdcedf1f1a6532c267eac04770f54d31c4195182.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/0f874ef67b65b6e3da0998e390e226a4e6641e254a631c1811f3d98d4a715f2b.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.084 124.967
2 0.96 86.516 142.486
5 0.98 83.698 214.026
7 0.98 83.244 289.848
10 0.98 82.947 396.254