الأسعار تتغير بالنسبة المئوية
تضيف الحركة البراونية الحسابية الضجيج نفسه بالدولار سواء كان السهم بـ5 أو 500، ويمكن أن تنزل تحت الصفر. أما السهم فيتصرف بشكل مختلف: حركة 1% هي 1% عند أي سعر. والحركة البراونية الهندسية تضع العشوائية في التغير النسبي: dS = mu * S * dt + sigma * S * dW. وهي نموذج بلاك-شولز. وmu العائد المتوقع السنوي وsigma التقلب، وهو الكمية المعلنة في السوق.
import math
def gbm_step(s, mu, sigma, dt, z):
return s * math.exp((mu - 0.5 * sigma ** 2) * dt + sigma * math.sqrt(dt) * z)
print(round(gbm_step(100.0, 0.08, 0.2, 1 / 252, 0.0), 4)) # a calm day: only the (mu - sigma^2/2) drift acts
print(round(gbm_step(100.0, 0.08, 0.2, 1 / 252, 1.0), 4)) # a one-sigma up day
print(round(gbm_step(100.0, 0.08, 0.2, 1 / 252, -50.0), 4)) # an absurd fall: still positiveالحل الدقيق وسالب نصف سيغما تربيع
حل المعادلة يعطي S(t) = S(0) * exp((mu - sigma**2 / 2) * t + sigma * W(t)). لماذا هذا الحد الغريب - sigma**2 / 2؟ لأن الأسية محدبة: وبحسب متباينة ينسن يرفع الضجيج متوسط exp(الضجيج)، وطرح sigma**2 / 2 يلغي ذلك تمامًا، فينمو السعر *المتوقع* بمعدل mu. أما *المسارات* النموذجية فتنمو أبطأ، بمعدل mu - sigma**2 / 2، ولذلك يقع وسيط سعر السهم دون متوسطه. والموضوع التالي، مبرهنة إيتو، يشتق هذا الحد.
import numpy as np
def gbm_path(s0, mu, sigma, dt, z):
log_moves = (mu - 0.5 * sigma ** 2) * dt + sigma * np.sqrt(dt) * z
return s0 * np.exp(np.concatenate([[0.0], np.cumsum(log_moves)]))
rng = np.random.default_rng(1)
z = rng.standard_normal(252)
path = gbm_path(100.0, 0.08, 0.2, 1 / 252, z)
print(path[:4].round(3), round(float(path[-1]), 3))مسارات كثيرة: المتوسط والوسيط
حاكِ آلاف المسارات لسنة واحدة وانظر أين تنتهي. فمتوسط السعر النهائي قريب من S0 * exp(mu * T) صيغة السعر المتوقع. أما *الوسيط* فأقل، S0 * exp((mu - sigma**2/2) * T)، ومع التقلب العالي يكون الفارق كبيرًا: تنتهي معظم المسارات دون المتوسط الذي ترفعه مسارات قليلة مذهلة. وهذا الالتواء نحو اليمين، توزيع لوغاريتمي طبيعي، هو شكل أسعار الأسهم على الآفاق الطويلة.
import numpy as np
rng = np.random.default_rng(5)
s0, mu, sigma, T, steps, paths = 100.0, 0.08, 0.4, 1.0, 252, 20000
dt = T / steps
z = rng.standard_normal((paths, steps))
final = s0 * np.exp(np.sum((mu - 0.5 * sigma ** 2) * dt + sigma * np.sqrt(dt) * z, axis=1))
print(round(float(final.mean()), 1), round(s0 * np.exp(mu * T), 1)) # close to each other: the expected price
print(round(float(np.median(final)), 1), round(s0 * np.exp((mu - 0.5 * sigma ** 2) * T), 1)) # the median is lower
print(round(float(np.mean(final < s0)), 3)) # the share of paths that lose moneyتقدير mu و sigma
من أسعار تؤخذ كل dt سنة تكون العوائد اللوغاريتمية r = diff(log(prices)) طبيعية مستقلة بمتوسط (mu - sigma**2/2) * dt وانحراف معياري sigma * sqrt(dt). فـsigma = std(r) / sqrt(dt) وmu = mean(r) / dt + sigma**2 / 2. وتقدير التقلب جيد بمئات النقاط؛ أما الانجراف فمضطرب بشدة لأن خطؤه المعياري لا يتقلص إلا مع *الزمن* الكلي لا مع عدد المشاهدات.
import numpy as np
def estimate_gbm(prices, dt):
r = np.diff(np.log(prices))
sigma = r.std(ddof=1) / np.sqrt(dt)
return r.mean() / dt + 0.5 * sigma ** 2, sigma
rng = np.random.default_rng(8)
dt = 1 / 252
for years in (1, 10, 100):
n = int(years * 252)
log_moves = (0.10 - 0.5 * 0.25 ** 2) * dt + 0.25 * np.sqrt(dt) * rng.standard_normal(n)
prices = 100 * np.exp(np.concatenate([[0.0], np.cumsum(log_moves)]))
mu, sigma = estimate_gbm(prices, dt)
print(years, "years:", round(float(mu), 3), round(float(sigma), 3)) # true mu 0.10 and sigma 0.25- الحركة الهندسية:
S(t) = S(0) * exp((mu - sigma**2 / 2) * t + sigma * W(t))؛ التغيرات النسبية عشوائية والأسعار موجبة. - يلغي
- sigma**2 / 2تحدبexp: السعر المتوقعS0 * exp(mu * t)لكن المسار الوسيط ينمو بـmu - sigma**2 / 2. - قدّر من العوائد اللوغاريتمية:
sigma = std(r) / sqrt(dt)وmu = mean(r) / dt + sigma**2 / 2؛ يُقدَّر التقلب جيدًا والانجراف رديئًا.
التالي: مبرهنة إيتو، قاعدة السلسلة التي تفسر أصل الحد - sigma**2 / 2.
المختبر: ما الذي يُقيَّم
- اتخذ خطوة دقيقة واحدة باستخدام
gbm_step25 نقطة - حاكِ مسار سعر باستخدام
gbm_path25 نقطة - احسب متوسط كل المستقبلات باستخدام
gbm_expected25 نقطة - استخرج المعاملات باستخدام
estimate_gbm25 نقطة