程式

使用 QuantLib 引導:公平交換或零 NPV

  • June 30, 2020

簡而言之

  1. QuantLib 的曲線自舉中使用的終止條件是什麼?
  2. 我可以根據我的需要修改這個設置嗎,例如我可以把它調整到更高的精度嗎?

背景

當引導貼現和投影曲線時,隱含曲線的必要條件是在市場上對基準工具進行定價,即對最初提供給曲線建構算法的工具進行估值,使其盡可能接近其輸入價格。對於使用局部插值機制(迭代引導)的曲線,我會假設所有基準工具都以最小的誤差重新定價到市場價格,即

假設使用局部插值的曲線/引導機制,例如對數折扣因子的線性插值。給定市場報價,並給定“到目前為止”​​建構的曲線,引導機制選擇“下一個”參考工具並通過在節點點移動利率來更新曲線,直到參考工具按市場定價。

我的觀察和問題

我在剛剛引導的曲線上對我的基準工具重新定價,發現這些工具的價格不像我假設的那樣接近零,請參見下面的最小範例:

import QuantLib as ql

today = ql.Date(23,ql.June,2020)
ql.Settings.instance().evaluationDate = today
eonia = ql.Eonia()

假設市場上“平坦”的 OIS 報價為每個期限 1%,並收集曲線建構助手:

quotes = {str(k)+'Y' : ql.SimpleQuote(0.01) for k in range(1,21)}
ois_helpers = []    
for k,v in quotes.items():
   ois_helpers.append(ql.OISRateHelper(
       settlementDays = 2, 
       tenor = ql.Period(k), 
       rate = ql.QuoteHandle(v), 
       index = eonia, 
       telescopicValueDates =True))

eonia_curve = ql.PiecewiseLinearZero(2,ql.TARGET(),ois_helpers,ql.Actual365Fixed())
val_curve   = ql.YieldTermStructureHandle(eonia_curve)

創建另一個eonia_index,這次附上投影曲線;和估值引擎:

eonia_index = ql.Eonia(val_curve)
swap_engine = ql.DiscountingSwapEngine(val_curve)

現在我將基準工具設置為“真實”工具並獲得它們的 NPV。請注意,我假設1 millioncurrs 的概念:

print('TENOR \t PV \t fairrate% \t fairrate% + fairspread%')
for p in quotes.keys():
   schedule = ql.MakeSchedule(today, today + ql.Period(p), ql.Period('1d'), calendar=ql.TARGET())
   fixedRate = quotes[p].value()
   ois_swap = ql.OvernightIndexedSwap(
       ql.OvernightIndexedSwap.Receiver, 
       1E6, 
       schedule, 
       fixedRate, 
       ql.Actual360(),
       eonia_index)
   ois_swap.setPricingEngine(swap_engine)
   print(p + "\t" + 
       str(round(ois_swap.NPV(),2)) + " \t " + 
       str(round(ois_swap.fairRate()*100,4)) + "\t\t" +
       str(100*(ois_swap.fairRate()+ois_swap.fairSpread())))

導致

TENOR    NPV     fairrate%   fairrate% + fairspread%
1Y  50.25    0.995      1.0
2Y  100.55   0.995      1.0
3Y  149.95   0.995      1.0
4Y  199.23   0.995      1.0
5Y  247.63   0.995      1.0
6Y  295.67   0.995      1.0
7Y  343.23   0.995      1.0
8Y  390.7    0.995      1.0
9Y  437.44   0.995      1.0
10Y 483.46   0.995      1.0
11Y 529.01   0.995      1.0
12Y 574.48   0.995      1.0
13Y 619.49   0.995      1.0
14Y 663.69   0.995      1.0
15Y 707.68   0.995      1.0
16Y 751.11   0.995      1.0
17Y 794.1    0.995      1.0
18Y 836.66   0.995      1.0
19Y 879.03   0.995      1.0
20Y 920.98   0.995      1.0

顯然,隱含公平利率不完全是 1%,但隱含公平利率加上隱含利差得出 1%。此外,每個互換的 NPV 都很接近,但不是“非常”接近於零。

我想知道,是否

  1. 我的儀器設置有問題?
  2. QuantLib 的引導方法在此處應用哪種終端條件,以及
  3. 我是否可以為該機制設置更嚴格的界限。

非常感謝您的任何輸入/想法/指示。

我的設置

截至 2019 年 6 月 1 日,我按照 Luigi 和 Goutham 的 QuantLib Python Cookbook 建立了貼現曲線;我正在使用 QuantLib Python SWIG;1.19 版。

問題是您沒有為同一件事定價,原因有兩個:

  1. 您定價的普通工具應從現貨日期開始,並以該開始作為參考的到期日
  2. OIS 掉期固定腿的頻率應該是每年一次。

如果您將程式碼更改為:

print('TENOR \t PV \t fairrate% \t fairrate% + fairspread%')
calendar = ql.TARGET()
for p in quotes.keys():
   start = calendar.advance(today, 2, ql.Days)
   schedule = ql.MakeSchedule(start, calendar.advance(start, ql.Period(p)), ql.Period('1Y'), calendar=calendar)
   fixedRate = quotes[p].value()
   ois_swap = ql.OvernightIndexedSwap(
       ql.OvernightIndexedSwap.Receiver, 
       1E6, 
       schedule, 
       fixedRate, 
       ql.Actual360(),
       eonia_index)
   
   ois_swap.setPricingEngine(swap_engine)
   print(p + "\t" + 
       str(round(ois_swap.NPV(),2)) + " \t \t" + 
       str(round(ois_swap.fairRate()*100,4)) + "\t\t" +
       str(100*(ois_swap.fairRate()+ois_swap.fairSpread())))

你會得到正確的輸出。

這也可以通過從助手中提取工具來檢查:

print('TENOR \t PV \t fairrate% \t fairrate% + fairspread%')
for idx, p in enumerate(quotes.keys()):
   ois_swap = ois_helpers[idx].swap()
   ois_swap.setPricingEngine(swap_engine)
   print(p + "\t" + 
       str(round(ois_swap.NPV(),2)) + " \t \t" + 
       str(round(ois_swap.fairRate()*100,4)) + "\t\t" +
       str(100*(ois_swap.fairRate()+ois_swap.fairSpread()))) 

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