Options
為什麼比較定價方法(Python)時美式期權價格存在差異?
我已經編寫了一個 Python 腳本來使用最小二乘蒙特卡羅為美式期權定價,並在下面添加了一個 QuantLib 實現(分析/二項式/有限差分)進行比較。問題是我的 MCLS 方法似乎略微高估了看漲期權和低估了看跌期權的價格,而且我似乎無法在程式碼中找到錯誤。任何有關此/建議以最佳方式標準化基礎價格的任何幫助將不勝感激,在此先感謝!
""" AMERICAN OPTION PRICING BY LEAST SQUARES MONTE CARLO, FINITE DIFFERENCE, ANALYTICAL AND BINOMIAL METHODS """ from numpy import zeros, concatenate, sqrt, exp, maximum, polyfit, polyval, shape, where, sum, argsort, random, \ RankWarning, put, nonzero from zlib import compress import matplotlib.pyplot as plt import os import sys from QuantLib import * from pylab import * import warnings warnings.simplefilter('ignore', RankWarning) plt.style.use('seaborn') # Define global parameters S0 = 100 # Underlying price K = 90 # Strike valuation_date = Date(1, 1, 2018) # Valuation date expiry_date = Date(1, 1, 2019) # Expiry date t = ActualActual().yearFraction(valuation_date, expiry_date) # Year fraction T = 100 # Time grid dt = t / T # Delta time r = 0.01 # Interest rate sig = 0.4 # Volatility sim = 10 ** 5 # Number of MC simulations DiscountFactor = exp(-r * dt) # Discount factor """ Least Squares Monte Carlo """ def GBM(underlying, time, simulations, rate, sigma, delta_t): # Geometric Brownian Motion GBM = zeros((time + 1, simulations)) GBM[0, :] = underlying for t in range(1, time + 1): brownian = standard_normal(simulations // 2) brownian = concatenate((brownian, -brownian)) GBM[t, :] = (GBM[t - 1, :] * exp((rate - sigma ** 2 / 2.) * delta_t + sigma * brownian * sqrt(delta_t))) return GBM def Payoff(strike, paths, simulations): # Define option type and respective payoff if OptionType == 'call': po = maximum(paths - strike, zeros((T + 1, simulations))) elif OptionType == 'put': po = maximum(strike - paths, zeros((T + 1, simulations))) else: print('Incorrect input') os.execl(sys.executable, sys.executable, *sys.argv) return po def loadingBar(count, total, size): # MC progress bar percent = float(count) / float(total) * 100 sys.stdout.write("\r" + str(int(count)).rjust(3, '0') + "/" + str(int(total)).rjust(3, '0') + ' [' + '=' * int( percent / 10) * size + ' ' * (10 - int(percent / 10)) * size + ']') # Graph the regression fit and simulations OptionType = str(input('Price call or put:')) print('Plotting fitted regression at T...') GBM = GBM(S0, T, sim, r, sig, dt) payoff = Payoff(K, GBM, sim) ValueMatrix = zeros_like(payoff) ValueMatrix[T, :] = payoff[T, :] prices = GBM[T, :] value = ValueMatrix[T, :] regression = polyfit(prices, value * DiscountFactor, 4) ContinuationValue = polyval(regression, prices) sorted_index = argsort(prices) prices = prices[sorted_index] ContinuationValue = ContinuationValue[sorted_index] ValueMatrix[T, :] = where(payoff[T, :] > ContinuationValue, payoff[T, :], ValueMatrix[T, :] * DiscountFactor) ValueVector = ValueMatrix[T, :] * DiscountFactor ValueVector = ValueVector[sorted_index] plt.figure() f, axes = plt.subplots(2, 1) axes[0].set_title('American Option') axes[0].plot(prices, ContinuationValue, label='Fitted Polynomial') axes[0].plot(prices, ValueVector, label='Inner Value') axes[0].set_ylabel('Payoff') axes[0].set_xlabel('Asset Price') axes[0].legend() axes[1].set_title('Geometric Brownian Motion') axes[1].plot(GBM, lw=0.5) axes[1].set_ylabel('Asset Price') axes[1].set_xlabel('Time') f.tight_layout() plt.show() # MC results print('Pricing option...') for i in range(0, 100): loadingBar(i, 100, 2) for t in range(T - 1, 0, -1): ITM = payoff[t, :] > 0 ITMS = compress(ITM, GBM[t, :]) ITMP = compress(ITM, payoff[t + 1, :] * DiscountFactor) regression = polyval(polyfit(ITMS, ITMP, 4), ITMS) continuation = zeros(sim) put(continuation, nonzero(ITM), regression) payoff[t, :] = where(payoff[t, :] > continuation, payoff[t, :], payoff[t + 1, :] * DiscountFactor) price = sum(payoff[1, :] * DiscountFactor) / sim print('\nLeast Squares Monte Carlo Price:', price) """ QuantLib Pricing """ S0 = SimpleQuote(S0) if OptionType == 'call': OptionType = Option.Call elif OptionType == 'put': OptionType = Option.Put else: print('Incorrect input') os.execl(sys.executable, sys.executable, *sys.argv) def Process(valuation_date, r, dividend_rate, sigma, underlying): calendar = UnitedStates() day_counter = ActualActual() Settings.instance().evaluation_date = valuation_date interest_curve = FlatForward(valuation_date, r, day_counter) dividend_curve = FlatForward(valuation_date, dividend_rate, day_counter) volatility_curve = BlackConstantVol(valuation_date, calendar, sigma, day_counter) u = QuoteHandle(underlying) d = YieldTermStructureHandle(dividend_curve) r = YieldTermStructureHandle(interest_curve) v = BlackVolTermStructureHandle(volatility_curve) return BlackScholesMertonProcess(u, d, r, v) def FDAmericanOption(valuation_date, expiry_date, OptionType, K, process): # Finite difference exercise = AmericanExercise(valuation_date, expiry_date) payoff = PlainVanillaPayoff(OptionType, K) option = VanillaOption(payoff, exercise) time_steps = 100 grid_points = 100 engine = FDAmericanEngine(process, time_steps, grid_points) option.setPricingEngine(engine) return option def ANAmericanOption(valuation_date, expiry_date, OptionType, K, process): # Analytical exercise = AmericanExercise(valuation_date, expiry_date) payoff = PlainVanillaPayoff(OptionType, K) option = VanillaOption(payoff, exercise) engine = BaroneAdesiWhaleyEngine(process) option.setPricingEngine(engine) return option def BINAmericanOption(valuation_date, expiry_date, OptionType, K, process): # Binomial exercise = AmericanExercise(valuation_date, expiry_date) payoff = PlainVanillaPayoff(OptionType, K) option = VanillaOption(payoff, exercise) timeSteps = 10 ** 3 engine = BinomialVanillaEngine(process, 'crr', timeSteps) option.setPricingEngine(engine) return option def FDAmericanResults(option): print('Finite Difference Price: ', option.NPV()) print('Option Delta: ', option.delta()) print('Option Gamma: ', option.gamma()) def ANAmericanResults(option): print('Barone-Adesi-Whaley Analytical Price: ', option.NPV()) def BINAmericanResults(option): print('Binomial CRR Price: ', option.NPV()) # Quantlib results process = Process(valuation_date, r, 0, sig, S0) ANoption = ANAmericanOption(valuation_date, expiry_date, OptionType, K, process) ANAmericanResults(ANoption) BINoption = BINAmericanOption(valuation_date, expiry_date, OptionType, K, process) BINAmericanResults(BINoption) FDoption = FDAmericanOption(valuation_date, expiry_date, OptionType, K, process) FDAmericanResults(FDoption) os.system('say "completo"')
我從來沒有處理過 Python,所以我只是想從視覺/邏輯上理解發生了什麼。總體而言,它看起來不錯,除了 LS 建議僅使用實值路徑進行回歸之外,在您的程式碼中看不到這一點,還是我錯過了它?一些旁注,您的變數命名有點不標準,我會更改您的 T -> NT,t -> T。此外,您所說的折扣率實際上通常稱為折扣因子。最後,您嘗試為沒有股息的美國(百慕大)看漲期權定價,該看漲期權的價格應與同等歐洲看漲期權的價格相同。
LS 算法,如果處理得當,無論如何都會低估你的美國人。那是因為通過基函式的連續值近似,就是一個近似值。這意味著算法(其決策基於持續價值)不會總是做出正確的(最佳)決定來行使,這意味著期權價值將略低於您始終以最佳方式行使的期權價值。
現在,正如 Bob Jansen 所說,很可能是您的噪音太大了。我的意思是,現在誰使用 10.000 MC 路徑!我猜 Python 對 MC 來說太慢了。所以,我用這個工具和 8.000 個(Sobol)路徑(用於主模擬和回歸)和 6 個基函式嘗試了你的問題,我得到 C=21.32(半秒內)。然後嘗試使用 131.000 條路徑,我得到 21.02(在 12 秒內)。正確的價格是 21.046。所以,是的,很可能是你的噪音太大,路徑太少,你無法得出任何關於你是否有其他問題的結論。