Quantlib

QuantLib - 在負利率上校準 Hull White 單因素

  • November 10, 2020

幾天來,我一直在使用 QuantLib Python 包。目前,我正在為短期利率校準 Hull White 單因素模型。我正在根據收益率曲線和掉期期權波動率來校準模型。

鑑於目前的市場情況,收益率曲線的很大一部分是負數。由於對數正態分佈不能為負,這會導致校準 Hull White 單因素模型時出現一些問題。經過一番搜尋,我發現了指定班次的可能性。在下面的程式碼中,我的 SwaptionHelper 初始化顯示,包括班次。

請注意,我目前使用的是收益率曲線,但是,當使用 -0.00478 的平正時,我得到了類似的結果。

但是,使用移位會導致 a (a=0.0000389, sigma=0.0222) 的輸出不切實際。

我一直在尋找答案,但找不到適合負利率情況的資訊。如果有人能夠解釋輪班的用法或快速檢查我的程式碼以查看是否有任何問題,那將非常有幫助。

helper = ql.SwaptionHelper(
       ql.Period(int(maturity), ql.Years),
       ql.Period(int(tenor), ql.Years),
       volatility,
       index,
       fixedLegTenor,
       fixedLegDayCounter,
       floatingLegDayCounter,
       term_structure,
       ql.BlackCalibrationHelper.RelativePriceError,
       ql.nullDouble(),
       1.0,
       ql.ShiftedLognormal,
       0.05 #shift to make rates non-negative
   )

完整程式碼:

import csv
from QuantLib.QuantLib import SwaptionHelper
import matplotlib.pyplot as plt
import pandas as pd
import QuantLib as ql

def load_csv_input():
   zero_curve = pd.read_csv('ZeroCurve.csv', delimiter=';')
   dates = []
   rates = []

   for i in range(0,len(zero_curve)):
       rates.append(float(zero_curve['Rate'][i]))
       dates.append(ql.Date(int(zero_curve['Mat_Day'][i]),int(zero_curve['Mat_Month'][i]),int(zero_curve['Mat_Year'][i])))

   return dates, rates

# Read the swaption volatilities from the csv file.
swaption_vols = pd.read_csv('SwaptionVol.csv', delimiter=';', index_col=0)
dates, rates = load_csv_input()

curve = ql.ZeroCurve(dates, rates, ql.Actual365Fixed())
term_structure = ql.YieldTermStructureHandle(curve)

model = ql.HullWhite(term_structure)
#engine = ql.TreeSwaptionEngine(model, 25)
engine = ql.JamshidianSwaptionEngine(model)
#engine = ql.G2SwaptionEngine(model, 10, 400)

index = ql.Euribor1Y(term_structure)
fixedLegTenor = ql.Period('1Y')
fixedLegDayCounter = ql.Actual360()
floatingLegDayCounter = ql.Actual360()

swaptions = []
ql.Settings.instance().evaluationDate = ql.Date(1, 10, 2020)
for maturity in swaption_vols.index:
   for tenor in swaption_vols.columns:
       volatility = ql.QuoteHandle(ql.SimpleQuote(swaption_vols.at[maturity,tenor]))
       helper = ql.SwaptionHelper(
           ql.Period(int(maturity), ql.Years),
           ql.Period(int(tenor), ql.Years),
           volatility,
           index,
           fixedLegTenor,
           fixedLegDayCounter,
           floatingLegDayCounter,
           term_structure,
           ql.BlackCalibrationHelper.RelativePriceError,
           ql.nullDouble(),
           1.0,
           ql.ShiftedLognormal,
           0.2 #shift to make rates non-negative
       )
       helper.setPricingEngine(engine)
       swaptions.append(helper)

optimization_method = ql.LevenbergMarquardt(1.0e-8,1.0e-8,1.0e-8)
end_criteria = ql.EndCriteria(500000, 1000, 1e-6, 1e-8, 1e-8)
model.calibrate(swaptions, optimization_method, end_criteria)
params = model.params()
print(params)

更新

我已將波動率類型從對數正態更​​改為正態,因為數據包含正態波動率。所以這應該能夠處理負值。

現在我遇到了一個問題,即僅當我使用少量波動率時,校準才有效。我的完整波動率矩陣是 10x10(1、2、3、4、5、10、15、20、25 和 30Y 期限和期限)。但是,如果我使用超過第一個 5x5,我會收到“根未括起來”錯誤。

在建構 SwaptionHelper 時,您必須告訴 QuantLib 您輸入的是哪種波動率。共有三個選項:Black Vol、Shifted Black Vol 和 Normal Vol。

由於負前鋒,您在大多數交換面 (EUR) 中沒有黑色音量,因此您可以使用移位黑色音量或正常音量。

在範例中,您使用的是移位的黑色音量,但您的移位為 20%!您的移位卷應該有一個相應的移位值,但我懷疑它是 20%。例如,ICAP 報價以 2% 的幅度移動歐元匯率。

不做任何程式碼,我猜如果你將你的轉變糾正到適當的值,你會得到更好的結果。您可以將模型值與助手的市場值進行比較。

引用自:https://quant.stackexchange.com/questions/59269