歩幅を縮めたランダムウォーク
価格が毎秒ランダムな量だけ上下する様子を想像してください。間隔をどんどん短くし、全体のばらつきが適切になるよう各ステップを小さくした極限がブラウン運動 W(t) です。水中の花粉の動きを記述するために導入され、1900年にバシュリエが価格のモデルに採用しました。3つの性質で定義されます。W(0) = 0、重ならない区間の増分は独立、W(t) - W(s) は平均0・分散 t - s の正規分布に従う。
import numpy as np
rng = np.random.default_rng(1)
dt = 0.01
z = rng.standard_normal(5) # standard normal draws
increments = z * np.sqrt(dt) # each step over dt has standard deviation sqrt(dt)
print(np.round(increments, 4))経路は累積和
時刻 0, dt, 2 dt, ... の格子上で経路をシミュレートするには、ステップごとに標準正規乱数 z を1つ引いて sqrt(dt) を掛け、足し上げます。W(k * dt) は最初の k 個の増分の和で、np.cumsum です。経路は0から始まるので先頭に 0 を置きます。W(t) の分散は t なので、位置のばらつきは時間の平方根で増え、ボラティリティやVaRの換算と同じ平方根則です。
import numpy as np
def brownian_path(z, dt):
return np.concatenate([[0.0], np.cumsum(z * np.sqrt(dt))])
rng = np.random.default_rng(1)
n, dt = 252, 1 / 252
ends = np.array([brownian_path(rng.standard_normal(n), dt)[-1] for _ in range(4000)])
print(round(float(ends.mean()), 3), round(float(ends.var()), 3)) # about 0 and 1: W(1) is a standard normal無限に粗い経路
ブラウン運動の経路は連続ですがどこも滑らかでなく、長さは無限です。その粗さには正確な尺度があり、ステップの二乗和である二次変分です。滑らかな曲線ではこの和は格子を細かくすると0に縮みますが、ブラウン運動では格子によらず経過時間 t のままです。各ステップの二乗はおよそ dt で、それが t / dt 個あるからです。この事実 (dW)^2 = dt が確率解析を通常の解析から区別し、次のトピックの鍵になります。
import numpy as np
def quadratic_variation(path):
return float(np.sum(np.diff(path) ** 2))
rng = np.random.default_rng(2)
t = 2.0
for steps in (10, 100, 10000):
dt = t / steps
path = np.concatenate([[0.0], np.cumsum(rng.standard_normal(steps) * np.sqrt(dt))])
print(steps, round(quadratic_variation(path), 3)) # settles near 2.0 as the grid gets finer
smooth = np.linspace(0, 1, 10001) * 2
print(round(quadratic_variation(smooth), 5)) # a straight line: close to 0ドリフトとボラティリティ
価格にはトレンドとスケールがあります。ドリフト付き算術ブラウン運動は X(t) = mu * t + sigma * W(t) で、mu がドリフト(単位時間あたりのトレンド)、sigma がボラティリティ(ランダム部分のスケール)です。価格変化の最も単純なモデルですが負になりうるため、価格そのものは次のトピックで幾何ブラウン運動としてモデル化されます。金利やスプレッドの基本部品としては今も使われます。
import numpy as np
def drifted_path(z, dt, mu, sigma):
t = np.arange(len(z) + 1) * dt
w = np.concatenate([[0.0], np.cumsum(z * np.sqrt(dt))])
return mu * t + sigma * w
rng = np.random.default_rng(3)
n, dt = 252, 1 / 252
path = drifted_path(rng.standard_normal(n), dt, 0.08, 0.2) # 8% a year drift, 20% volatility
print(round(float(path[-1]), 4), "after one year; the drift alone would give 0.08")
print(np.round(drifted_path(np.zeros(4), 0.25, 0.08, 0.2), 4)) # no noise: just the trend linenp.random.default_rng(seed))から得られ、実行が再現可能になります。1本の経路では何も証明できません。W(1) の平均や分散といった統計量は数千本の経路で初めて現れ、モンテカルロ推定の誤差は 1 / sqrt(paths) で縮みます。- ブラウン運動は0から始まり、分散
dtの独立な正規増分を持つ。z * sqrt(dt)と累積和でシミュレートする W(t)の分散はt、経路の二次変分は経過時間のまま:(dW)^2 = dtX(t) = mu * t + sigma * W(t)でトレンドとスケールを加える。1本の経路では何も証明できず、統計には多数の経路が要る
次は幾何ブラウン運動です。価格が負にならないモデルです。
ラボ:採点される内容
brownian_incrementsでショックをスケールする25点brownian_pathで経路を作る25点quadratic_variationで粗さを測る25点drifted_pathでトレンドを加える25点