目錄
如果未做特別說明,文中的程序都是 python3 代碼。
QuantLib 金融計算——案例之普通歐式期權分析
載入 QuantLib 和其他包:
import QuantLib as ql
import numpy as np
import pandas as pd
print(ql.__version__)
1.15
概述
從金融工程中最簡單的案例——“普通歐式期權公式法定價”入手,介紹 QuantLib 中期權分析的基本組件,以及如何將這些組件拼接成為一個完整的計算流程。
普通歐式期權公式法定價
采用《期權、期貨及其他衍生產品(第 7 版)》第 17 章中的例子:
- 類型:普通歐式看漲期權
- 當前價格:49$
- 敲定價格:50$
- 無風險利率:5%
- 年化波動率:20%
- 期限:20 周
使用 BS 公式為上述期權定價,並計算希臘值。
1. 配置期權合約條款
# 配置日期計算條款
calendar = ql.UnitedStates(ql.UnitedStates.NYSE)
dayCounter = ql.Actual365Fixed(ql.Actual365Fixed.Standard)
todayDate = ql.Date(11, ql.July, 2019)
maturity = todayDate + ql.Period(20, ql.Weeks)
settlementDate = todayDate
# 配置期權參數
stock = 49
strike = 50
riskFreeRate = 0.05
volatility = 0.2
# 配置全局估值日期
ql.Settings.instance().evaluationDate = todayDate
2. 構建期權對象
# 配置行權條款
europeanExercise = ql.EuropeanExercise(maturity)
optionType = ql.Option.Call
payoff = ql.PlainVanillaPayoff(
type=optionType, strike=strike)
# 構建期權對象
europeanOption = ql.VanillaOption(
payoff=payoff,
exercise=europeanExercise)
3. 配置定價引擎
underlying = ql.SimpleQuote(stock)
underlyingH = ql.QuoteHandle(underlying)
# 無風險利率的期限結構
flatRiskFreeTS = ql.YieldTermStructureHandle(
ql.FlatForward(
settlementDate, riskFreeRate, dayCounter))
# 波動率的期限結構
flatVolTS = ql.BlackVolTermStructureHandle(
ql.BlackConstantVol(
settlementDate, calendar,
volatility, dayCounter))
# 構建 BS 過程
bsProcess = ql.BlackScholesProcess(
s0=underlyingH,
riskFreeTS=flatRiskFreeTS,
volTS=flatVolTS)
# 基於 BS 過程的公式定價引擎
pricingEngine = ql.AnalyticEuropeanEngine(
bsProcess)
europeanOption.setPricingEngine(pricingEngine)
4. 計算
# RESULTS
print("Option value =", europeanOption.NPV())
print("Delta value =", europeanOption.delta())
print("Theta value =", europeanOption.theta())
print("Theta perday =", europeanOption.thetaPerDay())
print("Gamma value =", europeanOption.gamma())
print("Vega value =", europeanOption.vega())
print("Rho value =", europeanOption.rho())
Option value = 2.395988448539984
Delta value = 0.5213970624832108
Theta value = -4.309457134907618
Theta perday = -0.011806731876459226
Gamma value = 0.06563585494066533
Vega value = 12.089225358769994
Rho value = 8.88039853654583
題外話:天數計算規則
上述例子中的計算結果和書中給出的結果略有出入,依經驗判斷,最有可能造成計算不一致的原因是“天數計算規則的不一致”。
詳細來說,書中期權的期限是 20 周,作者認為 20 周等於 0.3846 年,可能的依據有:
- \(20 \times 7 / 364(\text{not } 365) \approx 0.3846\) (即 Actual/364)或
- \(20 \times 5(\text{weekday}) / [52(\approx 365/7)\times 5(\text{weekday})] \approx 0.3846\)
目前,QuantLib 中並不支持這兩種天數計算規則。例子中出現的規則
Actual365Fixed(Actual365Fixed.Standard)
認為 20 周等於 0.38356 年:print(dayCounter.yearFraction(settlementDate, maturity)) # 0.3835616438356164
對於期權來說,天數計算規則的影響可能微不足道,但是對於固定收益類金融工具及其衍生品來說,天數計算規則的選擇至關重要,“失之毫厘,謬以千里”。
Quote
帶來的便利
QuantLib 中有相當多的組件接受 Handle
類型的參數,而這些參數通常持有一個 Quote
類型的變量。借助“觀察者模式”,用戶修改 Quote
類型變量的值將會自動通知相關組件,並使其重新進行性計算,而無需再次構建一遍計算流程。對於某些用途來講,這帶來了相當大的便利。
# USE QUOTE
stock_array = np.arange(
start=30, stop=70, step=0.01)
NPV = np.array([np.nan] * len(stock_array))
delta = np.array([np.nan] * len(stock_array))
theta = np.array([np.nan] * len(stock_array))
# thetaPerDay = np.array([np.nan] * len(stock_array))
gamma = np.array([np.nan] * len(stock_array))
vega = np.array([np.nan] * len(stock_array))
rho = np.array([np.nan] * len(stock_array))
for i, v in enumerate(stock_array):
# 重置 Quote 對象的值
underlying.setValue(v)
# 無須再次配置計算流程,直接計算
NPV[i] = europeanOption.NPV()
delta[i] = europeanOption.delta()
theta[i] = europeanOption.theta()
# thetaPerDay[i] = europeanOption.thetaPerDay()
gamma[i] = europeanOption.gamma()
vega[i] = europeanOption.vega()
rho[i] = europeanOption.rho()
result = pd.DataFrame(
data=dict(
NPV=NPV,
delta=delta,
theta=theta,
# thetaPerDay=thetaPerDay,
gamma=gamma,
vega=vega, rho=rho),
index=stock_array)
result.plot(subplots=True)
總結
下面用一副圖顯示上述例子中的若干變量如何匯聚成一個計算流程: