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

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

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

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

前提#

ランダムウォーク#

過程ytが定数項δを用いて

yt=δ+yt1+εt,εtiid(0,σ2)

と表現されるとき、ytはランダムウォークと呼ばれる。ただし、y0=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/494db22fb52a0b71629070475b36e1c02e664b58b148838cd1862e8478a50dfe.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/43f141d82773b22ce6c5747d8ff1d7c9a20e730f3a8efd8c53e40a84f0908109.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/819aed92a6164a74084cb6a9dfbcca7a37b1195b14e7a3e4e9d160f38464a405.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.927 125.131
2 0.96 87.960 143.529
5 0.96 85.753 221.821
7 0.96 85.488 306.133
10 0.96 85.267 431.802