[英]Monte Carlo in Python
編輯為包含 VBA 代碼以進行比較
此外,我們知道 Monte-Carlo 應該收斂的解析值 8.021,這使得比較更容易。
Excel VBA 基於平均 5 次蒙特卡羅模擬(7.989、8.187、8.045、8.034、8.075)給出 8.067
Python 基於 5 個 MC(7.913、7.915、8.203、7.739、8.095)給出 7.973 和更大的方差!
VBA 代碼甚至不是“那么好”,使用一種相當糟糕的方式從標准正常生成樣本!
我在 Python 中運行一個超級簡單的代碼,通過蒙特卡洛為歐洲看漲期權定價,我對 10,000 條“模擬路徑”的收斂有多“糟糕”感到驚訝。 通常,在 C++ 甚至 VBA 中針對這個簡單問題運行 Monte-Carlo 時,我會獲得更好的收斂性。
我展示了下面的代碼(代碼取自教科書“Python for Finance”,我在 Visual Studio Code 中的 Python 3.7.7,64 位版本下運行):我得到以下結果,例如:Run 1 = 7.913,運行 2 = 7.915,運行 3 = 8.203,運行 4 = 7.739,運行 5 = 8.095,
上述結果相差如此之大,將是不可接受的。 如何提高收斂性??? (顯然通過運行更多路徑,但正如我所說:對於 10,000 條路徑,結果應該已經收斂得更好):
#MonteCarlo valuation of European Call Option
import math
import numpy as np
#Parameter Values
S_0 = 100. # initial value
K = 105. # strike
T = 1.0 # time to maturity
r = 0.05 # short rate (constant)
sigma = 0.2 # vol
nr_simulations = 10000
#Valuation Algo:
# Notice the vectorization below, instead of a loop
z = np.random.standard_normal(nr_simulations)
# Notice that the S_T below is a VECTOR!
S_T = S_0 * np.exp((r-0.5*sigma**2)+math.sqrt(T)*sigma*z)
#Call option pay-off at maturity (Vector!)
C_T = np.maximum((S_T-K),0)
# C_0 is a scalar
C_0 = math.exp(-r*T)*np.average(C_T)
print('Value of the European Call is: ', C_0)
我還包括 VBA 代碼,它會產生更好的結果(在我看來):使用下面的 VBA 代碼,我得到 7.989、8.187、8.045、8.034、8.075。
Option Explicit
Sub monteCarlo()
' variable declaration
' stock initial & final values, option pay-off at maturity
Dim stockInitial, stockFinal, optionFinal As Double
' r = rate, sigma = volatility, strike = strike price
Dim r, sigma, strike As Double
'maturity of the option
Dim maturity As Double
' instatiate variables
stockInitial = 100#
r = 0.05
maturity = 1#
sigma = 0.2
strike = 105#
' normal is Standard Normal
Dim normal As Double
' randomNr is randomly generated nr via "rnd()" function, between 0 & 1
Dim randomNr As Double
' variable for storing the final result value
Dim result As Double
Dim i, j As Long, monteCarlo As Long
monteCarlo = 10000
For j = 1 To 5
result = 0#
For i = 1 To monteCarlo
' get random nr between 0 and 1
randomNr = Rnd()
'max(Rnd(), 0.000000001)
' standard Normal
normal = Application.WorksheetFunction.Norm_S_Inv(randomNr)
stockFinal = stockInitial * Exp((r - (0.5 * (sigma ^ 2))) + (sigma * Sqr(maturity) * normal))
optionFinal = max((stockFinal - strike), 0)
result = result + optionFinal
Next i
result = result / monteCarlo
result = result * Exp(-r * maturity)
Worksheets("sheet1").Cells(j, 1) = result
Next j
MsgBox "Done"
End Sub
Function max(ByVal number1 As Double, ByVal number2 As Double)
If number1 > number2 Then
max = number1
Else
max = number2
End If
End Function
我不認為 Python 或 numpy 內部有什么問題,無論你使用什么工具,收斂性肯定是一樣的。 我用不同的樣本大小和不同的 sigma 值運行了一些模擬。 毫不奇怪,事實證明收斂速度很大程度上受 sigma 值控制,請參見下面的 plot。 請注意,x 軸是對數刻度。 在較大的振盪消失后,在穩定之前會有更多的較小的波。 在 sigma=0.5 時最容易看到。
正如您所提到的,我絕對不是專家,但我認為最明顯的解決方案是增加樣本量。 很高興看到 C++ 或 VBA 的結果和代碼,因為我不知道您對 Z2EA9510C37F7F89E4941FF75F62F21CB756DDZEBB4934BFC 函數有多熟悉。 也許某事沒有做你認為它正在做的事情。
生成plot的代碼(不談效率,太可怕了):
import numpy as np
import matplotlib.pyplot as plt
S_0 = 100. # initial value
K = 105. # strike
T = 1.0 # time to maturity
r = 0.05 # short rate (constant)
fig = plt.figure()
ax = fig.add_subplot()
plt.xscale('log')
samplesize = np.geomspace(1000, 20000000, 64)
sigmas = np.arange(0, 0.7, 0.1)
for s in sigmas:
arr = []
for n in samplesize:
n = n.astype(int)
z = np.random.standard_normal(n)
S_T = S_0 * np.exp((r-0.5*s**2)+np.sqrt(T)*s*z)
C_T = np.maximum((S_T-K),0)
C_0 = np.exp(-r*T)*np.average(C_T)
arr.append(C_0)
ax.scatter(samplesize, arr, label=f'sigma={s:.2f}')
plt.tight_layout()
plt.xlabel('Sample size')
plt.ylabel('Value')
plt.grid()
handles, labels = ax.get_legend_handles_labels()
plt.legend(handles[::-1], labels[::-1], loc='upper left')
plt.show()
加法:
這次您使用 VBA 獲得了更接近實際值的結果。 但有時你不會。 這里隨機性的影響太大了。 事實是,從低樣本數模擬中僅平均 5 個結果是沒有意義的。 例如,在 Python(只有 n=10000,即使你不應該這樣做,如果你願意得到正確的答案)中平均 50 個不同的模擬得到 8.025167(± 0.039717,置信水平為 95%),即非常接近真正的解決方案。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.