程式
自舉 OIS 曲線
我正在嘗試使用 Quantlib 從一系列基於 EONIA 的 OIS 速率中獲得零曲線。在將我的輸出與彭博社進行比較時,我發現了一些差異(請參閱問題的末尾),並且我沒有發現我做錯了什麼(這是我第一次引導 OIS 費率)。
我瀏覽了 Quantlib 文件,並嘗試按照他們的方法進行操作,但沒有運氣。然後,如果有人發現問題所在並告訴我,我將非常感激!
today = ql.Date(22, 9, 2020) ql.Settings.instance().evaluationDate = today OIS_rate = [-0.47, -0.472, -0.4755, -0.481, -0.485, -0.489, -0.505, -0.519, -0.54, -0.552, -0.559, -0.5502, -0.5308, -0.5, -0.462, -0.4257, -0.382, -0.337, -0.2435, -0.1385, -0.056, -0.049, -0.078, -0.128, -0.1723] terms = [1, 2, 3, 4, 5, 6, 9, 12, 18, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 15, 20, 25, 30, 40, 50] calendar = ql.TARGET() bussiness_convention = ql.Following day_count = ql.Actual360() #Overnigth Rate depo_facility = -0.5 depo_helper = [ql.DepositRateHelper(ql.QuoteHandle(ql.SimpleQuote(depo_facility/100)), ql.Period(1,ql.Days), 1, calendar, ql.Unadjusted, False, day_count)] settlement_days_EONIA = 1 EONIA = ql.OvernightIndex("EONIA", settlement_days_EONIA, ql.EURCurrency(),ql.TARGET(), day_count) # Build OIS helpers OIS_helpers = [] for i in range(len(terms)): if i < 8: coupon_frequency = ql.Once tenor = ql.Period(terms[i],ql.Months) rate = OIS_rate[i] #OIS_helpers.append(ql.SwapRateHelper(ql.QuoteHandle(ql.SimpleQuote(rate/100.0)),tenor, calendar,coupon_frequency, bussiness_convention,day_count,ql.Euribor3M())) OIS_helpers.append(ql.OISRateHelper(settlement_days_EONIA, tenor, ql.QuoteHandle(ql.SimpleQuote(rate/100)), EONIA)) elif i == 8: coupon_frequency = ql.Semiannual tenor = ql.Period(terms[i],ql.Months) rate = OIS_rate[i] OIS_helpers.append(ql.OISRateHelper(settlement_days_EONIA, tenor, ql.QuoteHandle(ql.SimpleQuote(rate/100)), EONIA)) else: coupon_frequency = ql.Semiannual tenor = ql.Period(terms[i],ql.Years) rate = OIS_rate[i] OIS_helpers.append(ql.OISRateHelper(settlement_days_EONIA, tenor, ql.QuoteHandle(ql.SimpleQuote(rate/100)), EONIA)) rate_helpers = depo_helper + OIS_helpers yieldcurve = ql.PiecewiseLogCubicDiscount(today,rate_helpers,day_count) spots = [] tenors = [] for d in yieldcurve.dates(): yrs = day_count.yearFraction(today, d) compounding = ql.Simple freq = ql.Semiannual zero_rate = yieldcurve.zeroRate(yrs, compounding, freq) tenors.append(yrs) eq_rate = zero_rate.equivalentRate(day_count,compounding,freq,today,d).rate() spots.append(100*eq_rate) datatable = {'Dates':yieldcurve.dates(),'Tenors':tenors,'spots':spots} df = pd.DataFrame.from_dict(datatable)
以下是我得到的即期匯率,與 Bloomberg <SWDF 133 8> 中的匯率相比
Tenor Bloomberg Output 1 MO -0.469 -0.47102 2 MO -0.4711 -0.472474 3 MO -0.4747 -0.475779 4 MO -0.4802 -0.48116 5 MO -0.4843 -0.485102 6 MO -0.4884 -0.489064 9 MO -0.5047 -0.504981 12 MO -0.519 -0.518946 18 MO -0.5397 -0.538899 2 YR -0.5519 -0.550289 3 YR -0.5589 -0.555675 4 YR -0.5503 -0.545571 5 YR -0.531 -0.525296 6 YR -0.5006 -0.494237 7 YR -0.463 -0.456508 8 YR -0.4271 -0.420624 9 YR -0.3837 -0.377845 10 YR -0.3391 -0.333913 12 YR -0.2459 -0.242628 15 YR -0.1405 -0.139172 20 YR -0.057 -0.056707 25 YR -0.0497 -0.049446 30 YR -0.0787 -0.077762 40 YR -0.1278 -0.124637 50 YR -0.1705 -0.163544
我看到了幾個可以解釋這些差異的問題:
- EONIA 掉期固定腿的頻率是每年而不是半年
- 存款便利利率不是 EONIA 曲線的一部分。使用 Eonia 匯率。
- 您正在計算簡單複利而不是年度複利的利率
這是一個替代實現:
tenors = [ '1D', '1W', '2W', '1M', '2M', '3M', '4M', '5M', '6M', '7M', '8M', '9M', '10M', '11M', '1Y', '18M', '2Y', '30M', '3Y', '4Y', '5Y', '6Y', '7Y', '8Y', '9Y', '10Y', '11Y', '12Y', '15Y', '20Y', '25Y', '30Y', '35Y', '40Y', '50Y'] rates = [ -0.467, -0.472, -0.47, -0.46, -0.471, -0.47, -0.481, -0.487, -0.5, -0.495, -0.5, -0.506, -0.51, -0.515, -0.52, -0.541, -0.551, -0.556, -0.56, -0.551, -0.531, -0.5, -0.462, -0.426, -0.379, -0.337, -0.293, -0.251, -0.147, -0.068, -0.055, -0.09, -0.099, -0.134, -0.172] eonia = ql.Eonia() helpers = [] for tenor, rate in zip(tenors,rates): if tenor == '1D': helpers.append( ql.DepositRateHelper(rate / 100, eonia ) ) else: helpers.append( ql.OISRateHelper(2, ql.Period(tenor), ql.QuoteHandle(ql.SimpleQuote(rate/100)), eonia) ) eonia_curve = ql.PiecewiseLogCubicDiscount(0, ql.TARGET(), helpers, ql.Actual365Fixed()) discount_curve = ql.YieldTermStructureHandle(eonia_curve) swapEngine = ql.DiscountingSwapEngine(discount_curve)
然後,您可以創建 OIS 交換工具以獲得公平的匯率。
overnightIndex = ql.Eonia(discount_curve) for tenor, rate in zip(tenors, rates): if tenor == '1D': continue ois_swap = ql.MakeOIS(ql.Period(tenor), overnightIndex, 0.01, pricingEngine=swapEngine) print(f"{tenor}\t{ois_swap.fairRate():.4%}\t{rate:.4f}%")
1W -0.4720% -0.4720%
2W -0.4700% -0.4700%
1M -0.4600% -0.4600%
2M -0.4710% -0.4710%
3M -0.4700% -0.4700%
4M -0.4810% -0.4810%
5M -0.486M% -0.
% -0.5000% -0.5000%
7M -0.4950% -0.4950%
8M -0.5000% -0.5000%
9M -0.5060% -0.5060%
(…)
10Y -0.3370% -0.3370%
11Y -0.2930% -0.2930%
12Y -0.250% -0.2510%
15 年 -0.1470% -0.1470%
20 年 -0.0680% -0.0680
% 25 年 -0.0550% -0.0550% 30 年 -0.0900% -0.0900% 35 年 -0.0990% -0.0990% 40 年 -0.1340%
-0.17340 % -0.1340% -0.1340% 0.1720%