خيارات بباب سري
الخيار ذو الحاجز كول أو بوت له مفتاح تشغيل وإيقاف. فخيار الإلغاء (knock-out) يزول إذا لمس السعر الحاجز، وخيار التفعيل (knock-in) لا يوجد إلا إذا لمسه. ويكون الحاجز فوق نقطة البداية (up) أو تحتها (down). ولأن المشتري يتنازل عن جزء من المدفوع فهذه الخيارات أرخص من العادية، ولهذا تشيع، وفي المنتجات المهيكلة أيضًا. وهي كالآسيوية تعتمد على المسار كله، والأداة الطبيعية المحاكاة.
import numpy as np
paths = np.array([[100.0, 105.0, 112.0, 108.0], # stays below 115
[100.0, 98.0, 103.0, 110.0], # stays below 115
[100.0, 120.0, 95.0, 90.0]]) # touches 115 on day 1, then falls
hit_up = paths.max(axis=1) >= 115.0 # the maximum of each path against the barrier
print(hit_up) # [False False True]
print(paths.min(axis=1) <= 96.0) # a down barrier at 96: [False False True]# hit_barrier for both directions, with an error for anything else
import numpy as np
def hit_barrier(paths, barrier, direction):
if direction == "up":
return paths.max(axis=1) >= barrier
if direction == "down":
return paths.min(axis=1) <= barrier
raise ValueError("direction must be 'up' or 'down'")
paths = np.array([[100.0, 110.0, 105.0], [100.0, 109.9, 105.0]])
print(hit_barrier(paths, 110.0, "up")) # [ True False]: touching the barrier exactly counts
try:
hit_barrier(paths, 110.0, "sideways")
except ValueError as error:
print("rejected:", error)الإلغاء والتفعيل ومجموعهما
احسب على كل مسار مدفوع الكول العادي max(S_T - K, 0) وعلامة الحاجز. يُبقي كول الإلغاء المدفوع حيث *لم* يُلمس الحاجز، ويُبقيه كول التفعيل حيث لُمس. وكل مسار أحد الحالين، فـ*التفعيل + الإلغاء = الكول العادي* مسارًا مسارًا، وهي متطابقة مفيدة: سعّر الأسهل واحصل على الآخر مجانًا واستخدمها لاختبار شيفرتك. وnp.where(hit, a, b) يختار بين مصفوفتين عنصرًا عنصرًا.
import numpy as np
paths = np.array([[100.0, 105.0, 112.0, 108.0],
[100.0, 98.0, 103.0, 110.0],
[100.0, 120.0, 95.0, 130.0]])
strike, barrier = 100.0, 115.0
vanilla = np.maximum(paths[:, -1] - strike, 0.0)
hit = paths.max(axis=1) >= barrier
knock_out = np.where(hit, 0.0, vanilla)
knock_in = np.where(hit, vanilla, 0.0)
print(vanilla, knock_out, knock_in) # [ 8. 10. 30.] [ 8. 10. 0.] [ 0. 0. 30.]
print(np.allclose(knock_in + knock_out, vanilla)) # Trueالتسعير وانحياز الرصد المتقطع
السعر هو متوسط المدفوع المخصوم كما سبق. لكن انتبه لفحص الحاجز: فالمحاكاة تنظر إلى السعر في تواريخ شبكتها فقط، بينما يُخترق الحاجز المستمر الحقيقي *بين* التواريخ أيضًا. ومع خطوات قليلة تعبر المسارات المحاكاة الحاجز دون أن يُرى، فيخرج كول الإلغاء أغلى مما يجب وكول التفعيل أرخص. وتقلل الخطوات الأكثر هذا الانحياز، وكذلك التصحيحات التي تزيح الحاجز بنحو 0.5826 * sigma * sqrt(dt).
import numpy as np
def simulate_paths(s0, rate, sigma, t, steps, n_paths, rng):
dt = t / steps
z = rng.standard_normal((n_paths, steps))
moves = (rate - 0.5 * sigma ** 2) * dt + sigma * np.sqrt(dt) * z
return s0 * np.exp(np.hstack([np.zeros((n_paths, 1)), np.cumsum(moves, axis=1)]))
def knock_out_price(paths, strike, barrier, rate, t):
vanilla = np.maximum(paths[:, -1] - strike, 0.0)
payoff = np.where(paths.max(axis=1) >= barrier, 0.0, vanilla)
return float(np.exp(-rate * t) * payoff.mean())
rng = np.random.default_rng(1)
for steps in (12, 52, 252):
paths = simulate_paths(100.0, 0.05, 0.25, 1.0, steps, 40000, rng)
print(steps, round(knock_out_price(paths, 100.0, 130.0, 0.05, 1.0), 3)) # the price falls as monitoring gets finer- يُلمس الحاجز إذا بلغه أعلى المسار (up) أو أدناه (down) في أي تاريخ؛ فخيار الإلغاء يموت عند اللمس وخيار التفعيل يولد عنده.
- في كل مسار: التفعيل + الإلغاء = مدفوع الكول العادي؛ و
np.where(hit, a, b)يختار؛ والخيارات ذات الحاجز أرخص من العادية. - المحاكاة على شبكة تفوّت العبور بين التواريخ فيبدو كول الإلغاء غاليًا جدًا: استخدم خطوات أكثر أو أزح الحاجز؛ واحذر مخاطر الفجوة قرب الحاجز.
التالي: المتغيرات المتقابلة، طريقة للحصول على دقة مونت كارلو نفسها بمحاكيات أقل.
المختبر: ما الذي يُقيَّم
- اكتشف اللمس باستخدام
hit_barrier25 نقطة - اقتل الخيار باستخدام
knock_out_call_payoff25 نقطة - أحيِه باستخدام
knock_in_call_payoff25 نقطة - حوّل المدفوعات إلى سعر باستخدام
barrier_price25 نقطة