الدقة مكلفة
ينخفض الخطأ المعياري لتقدير مونت كارلو مثل 1 / sqrt(N): عشرة أضعاف الدقة تحتاج مئة ضعف المسارات. وعندما تكون المحاكاة مكلفة فالرافعة الأفضل من زيادة المسارات هي تقليل الضجيج في كل مسار. وهذه طرق تقليل التباين، وأبسطها المتغيرات المتقابلة. تستخدم الأعداد العشوائية نفسها مرتين: مع كل عينة z نشغّل أيضًا مسار -z.
import numpy as np
def terminal_price(s0, rate, sigma, t, z):
return s0 * np.exp((rate - 0.5 * sigma ** 2) * t + sigma * np.sqrt(t) * z)
z = np.array([-1.0, 0.0, 1.0])
print(np.round(terminal_price(100.0, 0.05, 0.2, 1.0, z), 3)) # the price for z = -1, 0, 1
print(np.round(terminal_price(100.0, 0.05, 0.2, 1.0, -z), 3)) # and for the opposite draws: mirror imagesلماذا يفيد الاقتران
إذا كانت z كبيرة وموجبة تنتهي المسار عاليًا ويدفع الكول كثيرًا؛ ومع -z تنتهي منخفضة ويدفع قليلًا. فمدفوعاتهما مرتبطة سلبيًا ومتوسط الزوج أثبت بكثير من كل منهما. وفي المدفوع الرتيب كالكول يكون لتباين متوسط الزوج (f(z) + f(-z)) / 2 قيمة (1 + corr) / 2 من تباين مدفوع واحد، مع corr سالب. ويبقى السعر متوسط كمية غير متحيزة فلا انحياز.
import numpy as np
def terminal_price(s0, rate, sigma, t, z):
return s0 * np.exp((rate - 0.5 * sigma ** 2) * t + sigma * np.sqrt(t) * z)
z = np.random.default_rng(1).standard_normal(20000)
up = np.maximum(terminal_price(100.0, 0.05, 0.2, 1.0, z) - 100.0, 0.0) # call payoffs for z
down = np.maximum(terminal_price(100.0, 0.05, 0.2, 1.0, -z) - 100.0, 0.0) # ... and for -z
print(round(float(np.corrcoef(up, down)[0, 1]), 3)) # negative: the payoffs offset each otherالمقدّر وهامش خطئه
عامِل كل زوج كمشاهدة واحدة: pair = (payoff(z) + payoff(-z)) / 2. التقدير هو المتوسط المخصوم لمدفوعات الأزواج، والخطأ المعياري هو الانحراف المعياري المخصوم (ddof=1) للأزواج على sqrt(عدد الأزواج). والعدد هو عدد *الأزواج* لأن عضوي الزوج غير مستقلين. ولتقدير المكسب قارن بتقدير عادي يستخدم العدد نفسه من المدفوعات من عينات مستقلة.
import numpy as np
def terminal_price(s0, rate, sigma, t, z):
return s0 * np.exp((rate - 0.5 * sigma ** 2) * t + sigma * np.sqrt(t) * z)
def mc_estimate(payoffs, discount):
return discount * payoffs.mean(), discount * payoffs.std(ddof=1) / np.sqrt(len(payoffs))
def call_payoff(z):
return np.maximum(terminal_price(100.0, 0.05, 0.2, 1.0, z) - 100.0, 0.0)
rng = np.random.default_rng(3)
z, w = rng.standard_normal(5000), rng.standard_normal(5000)
discount = np.exp(-0.05)
plain = mc_estimate(call_payoff(np.concatenate([z, w])), discount) # 10,000 independent payoffs
anti = mc_estimate((call_payoff(z) + call_payoff(-z)) / 2, discount) # 5,000 pairs: the same 10,000 evaluations
print([round(float(v), 4) for v in plain], [round(float(v), 4) for v in anti])
print(round(float(plain[1] / anti[1]), 2), "times smaller error")# A call profits from pairing; a straddle (symmetric in z) does not
import numpy as np
def terminal_price(z):
return 100.0 * np.exp((0.05 - 0.5 * 0.2 ** 2) + 0.2 * z)
rng = np.random.default_rng(4)
z = rng.standard_normal(20000)
for name, payoff in (("call", lambda p: np.maximum(p - 100.0, 0.0)), ("straddle", lambda p: np.abs(p - 100.0))):
pairs = (payoff(terminal_price(z)) + payoff(terminal_price(-z))) / 2
plain = payoff(terminal_price(np.concatenate([z, rng.standard_normal(20000)])))
ratio = (plain.std(ddof=1) / np.sqrt(len(plain))) / (pairs.std(ddof=1) / np.sqrt(len(pairs)))
print(name, round(float(ratio), 2)) # the call gains (1.41); the straddle gets worse (0.78)z كالستراددل حيث يتشابه f(z) وf(-z) فلا يضيف الزوج معلومة. لكنه مجاني: لا حاجة لأعداد عشوائية إضافية، ويمكن دمجه مع المتغيرات الضابطة في الموضوع التالي.- استخدم كل عينة
zمع-z؛ وخذ متوسط المدفوعين في مدفوع زوج أقل ضجيجًا بكثير للمدفوع الرتيب. - يُحسب الخطأ المعياري على عدد الأزواج بـ
ddof=1:discount * std(pairs) / sqrt(pairs). - قارن بتقدير عادي بالكلفة نفسها؛ لا تفيد المدفوعات المتماثلة في
z، وتُدمج مع المتغيرات الضابطة.
التالي: المتغيرات الضابطة، استخدام كمية قيمتها الدقيقة معروفة لإلغاء الضجيج.
المختبر: ما الذي يُقيَّم
- حوّل العينات إلى أسعار باستخدام
terminal_price25 نقطة - أبلغ عن التقدير وخطئه باستخدام
mc_estimate25 نقطة - قرن كل عينة بعكسها باستخدام
antithetic_call_price25 نقطة - قِس المكسب باستخدام
variance_ratio25 نقطة