簡體   English   中英

Python 中的蒙特卡洛

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM