程式

自舉 OIS 曲線

  • September 29, 2020

我正在嘗試使用 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

我看到了幾個可以解釋這些差異的問題:

  1. EONIA 掉期固定腿的頻率是每年而不是半年
  2. 存款便利利率不是 EONIA 曲線的一部分。使用 Eonia 匯率。
  3. 您正在計算簡單複利而不是年度複利的利率

這是一個替代實現:

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%

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