سهم لا يستطيع إلا الصعود أو الهبوط
بلاك-شولز صيغة، أما الشجرة ثنائية الحدّ فإجراء، وتستطيع تسعير ما لا تستطيعه الصيغة. نقسم الزمن حتى الانتهاء إلى خطوات صغيرة. في كل خطوة يصعد السهم بعامل u أو يهبط بعامل d، لا غير. وبعد n خطوة توجد n + 1 أسعار ممكنة. المدفوعات معروفة عند الانتهاء، وقيمة كل عقدة سابقة هي المتوسط المخصوم للعقدتين اللتين تقودان إليهما، فنملأ الشجرة رجوعًا حتى الحاضر.
# One step: a stock at 100 goes to 110 or 90. What is a call with strike 100 worth?
import math
spot, strike, rate, dt = 100, 100, 0.05, 1.0
u, d = 1.10, 0.90
p = (math.exp(rate * dt) - d) / (u - d) # the probability that makes the stock grow at the rate
up_value, down_value = max(spot * u - strike, 0), max(spot * d - strike, 0)
price = math.exp(-rate * dt) * (p * up_value + (1 - p) * down_value)
print(round(p, 4), round(up_value, 4), down_value) # 0.7564 10.0 0
print(round(price, 4)) # 7.1947الاحتمال ليس توقعًا
يُختار العدد p ليكبر السهم في المتوسط بمعدل الفائدة الخالي من المخاطر: p * u + (1 - p) * d = exp(rate * dt)، فينتج p = (exp(rate * dt) - d) / (u - d). وهو الاحتمال المحايد للمخاطر: حيلة تجعل محفظة المحاكاة تساوي توقعها المخصوم، لا اعتقادًا عن وجهة السهم. ولمطابقة التقلب المرصود تستخدم شجرة كوكس-روس-روبنشتاين (CRR) u = exp(sigma * sqrt(dt)) وd = 1 / u، فيعيد الصعود يتبعه الهبوط السعر نفسه وتتحد الفروع: n خطوة لا تعطي إلا n + 1 عقدة نهائية.
# The CRR parameters and the prices at expiry of a 3-step tree
import math
def crr_parameters(rate, sigma, t, steps):
dt = t / steps
u = math.exp(sigma * math.sqrt(dt))
d = 1 / u
p = (math.exp(rate * dt) - d) / (u - d)
return u, d, p
def terminal_prices(spot, u, d, steps):
return [spot * u ** j * d ** (steps - j) for j in range(steps + 1)]
u, d, p = crr_parameters(0.05, 0.2, 1, 3)
print(round(u, 4), round(d, 4), round(p, 4)) # 1.1224 0.8909 0.5438
print([round(price, 2) for price in terminal_prices(100, u, d, 3)])العمل رجوعًا
عند الانتهاء كل عقدة هي المدفوع فقط، max(S - K, 0) للكول. وفي العمود الأسبق تكون كل قيمة exp(-rate * dt) * (p * value_up + (1 - p) * value_down) وينقص العمود عقدة. وبعد n مسحة تبقى قيمة واحدة هي سعر اليوم. وفي بايثون العمود قائمة، والمسحة قائمة اشتمال على أزواج الجيران. ومع ازدياد عدد الخطوات يتقارب السعر من سعر بلاك-شولز، فتعيد الشجرة ما تقوله الصيغة.
# The tree price of a European call approaches the Black-Scholes price as steps grow
import math
def norm_cdf(x):
return 0.5 * (1 + math.erf(x / math.sqrt(2)))
def bs_call(spot, strike, rate, sigma, t):
d1 = (math.log(spot / strike) + (rate + 0.5 * sigma ** 2) * t) / (sigma * math.sqrt(t))
return spot * norm_cdf(d1) - strike * math.exp(-rate * t) * norm_cdf(d1 - sigma * math.sqrt(t))
def european_call_tree(spot, strike, rate, sigma, t, steps):
dt = t / steps
u = math.exp(sigma * math.sqrt(dt))
d = 1 / u
p = (math.exp(rate * dt) - d) / (u - d)
disc = math.exp(-rate * dt)
values = [max(spot * u ** j * d ** (steps - j) - strike, 0) for j in range(steps + 1)]
for _ in range(steps):
values = [disc * (p * values[j + 1] + (1 - p) * values[j]) for j in range(len(values) - 1)]
return values[0]
print("Black-Scholes", round(bs_call(100, 100, 0.05, 0.2, 1), 4))
for steps in (1, 5, 25, 100, 400):
print(steps, round(european_call_tree(100, 100, 0.05, 0.2, 1, steps), 4))التنفيذ المبكر
القوة الحقيقية للشجرة هي الخيار الذي لا تسعّره الصيغة: الخيار الأمريكي يمكن تنفيذه في أي وقت. في كل عقدة يقارن حامله بين قيمتين، التوقع المخصوم للاحتفاظ ومدفوع التنفيذ الآن، ويأخذ الأكبر. هذا max الإضافي الوحيد في كل عقدة هو كل التغيير في الخوارزمية. ففي كول على سهم بلا توزيعات لا يفيد التنفيذ المبكر أبدًا، أما في البوت فقد يفيد: فالبوت العميق داخل النقود قيمته نقدًا اليوم أكبر، ولذلك يكلف البوت الأمريكي أكثر من الأوروبي.
# An American put against a European put: the early-exercise premium
import math
def tree(spot, strike, rate, sigma, t, steps, american):
dt = t / steps
u = math.exp(sigma * math.sqrt(dt))
d = 1 / u
p = (math.exp(rate * dt) - d) / (u - d)
disc = math.exp(-rate * dt)
values = [max(strike - spot * u ** j * d ** (steps - j), 0) for j in range(steps + 1)]
for i in range(steps - 1, -1, -1):
values = [disc * (p * values[j + 1] + (1 - p) * values[j]) for j in range(i + 1)]
if american:
values = [max(values[j], strike - spot * u ** j * d ** (i - j)) for j in range(i + 1)]
return values[0]
for spot in (80, 100, 120):
european = tree(spot, 100, 0.05, 0.2, 1, 200, False)
american = tree(spot, 100, 0.05, 0.2, 1, 200, True)
print(spot, round(european, 4), round(american, 4), "premium", round(american - european, 4))n خطوة تكلف نحو n**2 / 2 تحديثًا للعقد، والتقارب ليس سلسًا (يتذبذب الخطأ بحسب وقوع سعر التنفيذ بين العقد)، ولذلك تُفحص الأشجار بمقارنة عدة أعداد من الخطوات.- في كل خطوة يصعد السهم بـ
uأو يهبط بـd؛ واختيار CRR أيu = exp(sigma * sqrt(dt))وd = 1 / uيجعل الشجرة تتحد، فتكون لـnخطوةn + 1عقدة نهائية. - التسعير رجوعًا: المدفوعات عند الانتهاء ثم
exp(-rate * dt) * (p * up + (1 - p) * down)لكل عقدة؛ وpاحتمال محايد للمخاطر لا توقع. - الخطوات الكثيرة تتقارب مع بلاك-شولز؛ والخيار الأمريكي يضيف
max(الاستمرار, التنفيذ)لكل عقدة، وهذا سبب وجود الأشجار.
التالي: طرق الفروق المنتهية، التسعير بحل المعادلة التفاضلية الجزئية للخيار على شبكة.
المختبر: ما الذي يُقيَّم
- حدّد حركتي الصعود والهبوط باستخدام
crr_parameters25 نقطة - اسرد الأسعار عند الانتهاء باستخدام
terminal_prices25 نقطة - اعمل رجوعًا عبر الشجرة باستخدام
european_call_tree25 نقطة - اسمح بالتنفيذ المبكر باستخدام
american_put_tree25 نقطة