QuantLib 金融計算——收益率曲線之構建曲線(2)

若是未作特別說明,文中的程序都是 Python3 代碼。算法

QuantLib 金融計算——收益率曲線之構建曲線(2)

理論和實踐上有多種方法能夠構建與市場一致的收益率曲線,背後的方法論取決於市場上的可得到金融工具的流動性。在構建收益率曲線時有兩個選項必須選定好:擬合方法和所選的金融工具。bootstrap

quantlib-python 容許構建下列兩大類收益率曲線:app

  • 第一類,根據數值和對應日期構建:
    • DiscountCurve,根據貼現因子構建
    • 若干 *ZeroCurve 型的收益率曲線,根據債券零息收益率構建(前綴表示具體的構建方法)
    • ForwardCurve,根據遠期收益率構建
  • 第二類,根據若干固定收益類對象(如 FixedRateBond)構建:
    • 若干 Piecewise** 型的收益率曲線,根據若干不一樣類型金融工具(存款收益率、收益率遠期合約和互換等等)的報價分段構建(後綴表示具體的構建方法和曲線類型)
    • FittedBondDiscountCurve,根據若干債券的價格構建

本文介紹第二種。函數

載入 QuantLib:工具

import QuantLib as ql

print(ql.__version__)
1.12

YieldTermStructure

事實上,全部上述類都派生自基類 YieldTermStructure,該基類實現了一些經常使用的功能。例如,實現了返回基準日期、天數計算規則、日曆的函數,以及返回收益率的最小或最大日期的函數。優化

YieldTermStructure 經常使用的成員函數:lua

  • discount(d, extrapolate = False):浮點數,dDate 對象, extrapolate 是布爾型。返回貼現因子大小。
  • zeroRate(d, resultDayCounter, comp, freq = Annual, extrapolate = False)InterestRatedDate 對象,resultDayCounterDayCounter 對象,compfreq 是預置整數,extrapolate 是布爾型。返回等價的零息收益率對象。
  • forwardRate(d1, d2, dc, comp, freq = Annual, extrapolate = false)InterestRated1d2Date 對象,resultDayCounterDayCounter 對象,compfreq 是 quantlib-python 預置整數(表示付息方式和頻率),extrapolate 是布爾型。返回 d1d2 之間的遠期收益率對象。

問題描述

以貨幣網 2018-07-23 發佈的國債收盤收益率曲線爲基準,構造一組樣本券:spa

  • 樣本券的期限分別爲收益率曲線的關鍵期限,如 一、五、15 年等;
  • 樣本券的付息頻率爲每一年付息一次;
  • 樣本券的票息率爲對應期限的到期收益率;
  • 樣本券的當前價格爲 100 元

此時,這些樣本券肯定的「到期」收益率曲線即爲發佈的收盤收益率曲線,由於對於每一年付息一次的固息債,全價等於 100 時票息率剛好等於到期收益率。rest

下面,從上述樣本券中構造出知足某種限制條件的「即期」收益率曲線。理論上,從新構造出的即期收益率應當與貨幣網發佈的即期收益率很是接近,甚至相等。

期限 到期收益率 即期收益率
1 3.0544 3.0544
2 3.1549 3.1565
3 3.2489 3.2531
4 3.2702 3.2744
5 3.2915 3.2964
6 3.3958 3.4092
7 3.5000 3.5237
8 3.5050 3.5264
9 3.5100 3.5298
10 3.5150 3.5337
15 3.7765 3.8517
20 3.8163 3.8884
30 3.9568 4.0943
50 3.9720 4.0720

Piecewise**

quantlib-python 提供的若干 Piecewise** 型的收益率曲線意爲「分段收益率曲線」,能夠根據若干固收類金融工具的報價推算出特按期限的收益率(或貼現因子),進而構建收益率曲線。

分段收益率曲線的原理

分段收益率曲線接受一組不一樣期限的固收類金融工具(如固息債、利率互換和遠期利率協議等等),根據具體金融工具的貼現方法、期限、價格等等因素拆解(bootstrap)出對應期限的即期收益率(或等價的貼現因子)。再經過某種插值手段——一般是樣條插值,構造出與實際即期收益率(或貼現因子)最接近的理論收益率曲線。

Piecewise** 對象的構造

PiecewiseLogCubicDiscount 從金融工具報價中拆解出貼現因子,並用對數三次樣條插值構建理論貼現因子曲線。其構造函數具備如下實現

PiecewiseLogCubicDiscount(referenceDate,
                          instruments,
                          dayCounter,
                          jumps,
                          jumpDates,
                          accuracy,
                          i)

PiecewiseLogCubicDiscount(settlementDays,
                          calendar,
                          instruments,
                          dayCounter,
                          jumps,
                          jumpDates,
                          accuracy,
                          i)

這些變量的類型和解釋以下:

  • referenceDateDate 對象,構造麴線的基準日期
  • settlementDays:整數,結算日天數
  • calendarCalendar 對象,市場對應的日曆表
  • instruments:一列 *Helper 類的對象,特定金融工具的輔助類,用於拆解計算。
  • dayCounterDayCounter 對象,市場對應的天數計算規則
  • jumps:一列 RelinkableQuoteHandle 對象,收益率跳躍的幅度,默認是空的
  • jumpDates:一列 Date 對象,收益率跳躍的日期,默認是空的
  • accuracy:浮點數,收斂精度,默認是 1e-12
  • iMonotonicLogCubic 對象,插值方法,默認是 MonotonicLogCubic()

PiecewiseLogCubicDiscount 經常使用的成員函數均繼承自基類 YieldTermStructure

PiecewiseLogCubicDiscount 對收益率曲線的形狀沒有「結構性」的限制,能夠很好的擬合實際獲得的收益率數據,代價是缺乏理論性的解釋能力。

FittedBondDiscountCurve

PiecewiseLogCubicDiscount 相反,FittedBondDiscountCurve 對收益率曲線的形狀提出了「結構性」的限制,最終擬合曲線的理論解釋性更強,曲線也更光滑,但代價是擬合程度會下降。

FittedBondDiscountCurve 的原理

FittedBondDiscountCurve 接受一組不一樣期限的固息債對象,同時假設理論即期收益率(或貼現因子)知足某種特定的函數形式——一般由幾個參數控制。用理論曲線對算出債券的理論價格,尋找最佳參數,使得理論價格和實際價格的距離最小。最佳參數肯定的曲線即是擬合出的理論曲線。

FittedBondDiscountCurve 的構造

FittedBondDiscountCurve 從一列固息債對象中拆解出貼現因子,並用要求理論貼現因子曲線知足某種參數形式。其構造函數具備如下實現

FittedBondDiscountCurve(referenceDate,
                        helpers,
                        dayCounter,
                        fittingMethod,
                        accuracy,
                        maxEvaluations,
                        guess,
                        simplexLambda)

FittedBondDiscountCurve(settlementDays,
                        calendar,
                        helpers,
                        dayCounter,
                        fittingMethod,
                        accuracy,
                        maxEvaluations,
                        guess,
                        simplexLambda)

這些變量的類型和解釋以下:

  • referenceDateDate 對象,構造麴線的基準日期
  • settlementDays:整數,結算日天數
  • calendarCalendar 對象,市場對應的日曆表
  • helps:一列 *Helper 類的對象,固息債的輔助類,用於拆解計算。
  • dayCounterDayCounter 對象,市場對應的天數計算規則
  • fittingMethodFittingMethod 對象,規定理論曲線的函數形式。
  • accuracy:浮點數,收斂精度,默認是 1e-10
  • maxEvaluations:整數,數值優化計算中的最大迭代次數
  • guess:一列浮點數,數值優化計算的初始參數,默認是空的
  • simplexLambda:浮點數,單純型算法中的尺度,默認是 1.0

FittedBondDiscountCurve 經常使用的成員函數均繼承自基類 YieldTermStructure

FittingMethod

FittingMethod 是一個基類,quantlib-python 具體提供的 FittingMethod 子類有如下幾個:

  • ExponentialSplinesFitting:指數樣條模型,貼現因子的形式爲 \(d(t) = \sum_{i=1}^9 c_i \exp^{-\kappa \cdot i \cdot t}​\),其中 \(c_i​\)\(\kappa​\) 是須要肯定的參數。
  • CubicBSplinesFitting:三次 B-樣條模型,貼現因子的形式爲一組三次 B-樣條 \(N_{i,3}(t)\) 的組合,\(d(t) = \sum_{i=0}^{n} c_i \cdot N_{i,3}(t)\),其中 \(c_i\) 是須要肯定的參數。
  • SimplePolynomialFitting:簡單多項式模型,貼現因子的形式爲多項式
  • NelsonSiegelFitting:Nelson-Siegel 模型,即期收益率的形式爲 \(r(t) = c_0 + (c_1 + c_2) \cdot (1 - exp^{-\kappa \cdot t})/(\kappa \cdot t) - c_2 exp^{- \kappa \cdot t}\),其中 \(c_i\)\(\kappa\) 是須要肯定的參數。
  • SvenssonFitting:Svensson-Nelson-Siegel 模型,即期收益率的形式爲 \(r(t) = c_0 + (c_0 + c_1)(\frac {1 - exp^{-\kappa \cdot t}}{\kappa \cdot t}) - c_2exp^{ - \kappa \cdot t} + c_3{(\frac{1 - exp^{-\kappa_1 \cdot t}}{\kappa_1 \cdot t} -exp^{-\kappa_1 \cdot t})}\),其中 \(c_i\)\(\kappa\)\(\kappa_1\) 是須要肯定的參數。

擬合曲線

下面分別用 PiecewiseLogCubicDiscountFittedBondDiscountCurve 擬合「問題描述」中構造出的樣本券對應的即期收益率曲線,FittedBondDiscountCurve 方法使用 Svensson-Nelson-Siegel 模型。

理論上,PiecewiseLogCubicDiscount 擬合出的曲線應該很是接近貨幣網發佈的即期收益率曲線,極可能是鋸齒狀的,不光滑的(因爲市場的深度和廣度有限)。FittedBondDiscountCurve 擬合出的曲線應該很是光滑,而且足夠接近貨幣網發佈的曲線。

例子,擬合曲線並輸出 Svensson-Nelson-Siegel 模型的參數。

import QuantLib as ql
import pandas as pd
import seaborn as sb

def testingYields3():
    maturities = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20, 30, 50]
    pars = [0.030544, 0.031549, 0.032489, 0.032702, 0.032915, 0.033958, 0.03500,
            0.035050, 0.035100, 0.035150, 0.037765, 0.038163, 0.039568, 0.03972]
    spots = [0.030544, 0.031565, 0.032531, 0.032744, 0.032964, 0.034092, 0.035237,
             0.035264, 0.035298, 0.035337, 0.038517, 0.038884, 0.040943, 0.040720]

    numberOfBonds = len(maturities)
    cleanPrice = [100.0] * numberOfBonds
    quote = [ql.SimpleQuote(c) for c in cleanPrice]
    quoteHandle = [ql.RelinkableQuoteHandle()] * numberOfBonds
    for i in range(len(quoteHandle)):
        quoteHandle[i].linkTo(quote[i])

    frequency = ql.Annual
    dc = ql.ActualActual(ql.ActualActual.ISMA)
    accrualConvention = ql.ModifiedFollowing
    convention = ql.ModifiedFollowing
    redemption = 100.0
    calendar = ql.China(ql.China.IB)

    today = calendar.adjust(ql.Date(23, 7, 2018))
    ql.Settings.evaluationDate = today

    bondSettlementDays = 0
    bondSettlementDate = calendar.advance(
        today,
        ql.Period(bondSettlementDays, ql.Days))

    instruments = []

    for j in range(len(maturities)):
        maturity = calendar.advance(
            bondSettlementDate,
            ql.Period(maturities[j], ql.Years))
        schedule = ql.Schedule(
            bondSettlementDate,
            maturity,
            ql.Period(frequency),
            calendar,
            accrualConvention,
            accrualConvention,
            ql.DateGeneration.Backward,
            False)

        helper = ql.FixedRateBondHelper(
            quoteHandle[j],
            bondSettlementDays,
            100.0,
            schedule,
            [pars[j]],
            dc,
            convention,
            redemption)

        instruments.append(helper)

    tolerance = 1.0e-10
    max = 5000
    svensson = ql.SvenssonFitting()

    ts0 = ql.PiecewiseLogCubicDiscount(
        bondSettlementDate,
        instruments,
        dc)
    ts1 = ql.FittedBondDiscountCurve(
        bondSettlementDate,
        instruments,
        dc,
        svensson,
        tolerance,
        max)

    spline = []
    sv = []

    print('{0:>9}{1:>9}{2:>9}{3:>9}'.format(
        "tenor", "spot", "spline", "svensson"))

    for i in range(len(instruments)):
        cfs = instruments[i].bond().cashflows()
        cfSize = len(instruments[i].bond().cashflows())

        tenor = dc.yearFraction(today, cfs[cfSize - 1].date())

        print(
            '{0:9.3f}{1:9.3f}{2:9.3f}{3:9.3f}'.format(
                tenor,
                100.0 * spots[i],
                100.0 * ts0.zeroRate(cfs[cfSize - 1].date(), dc, ql.Compounded, frequency).rate(),
                100.0 * ts1.zeroRate(cfs[cfSize - 1].date(), dc, ql.Compounded, frequency).rate()))

        spline.append(ts0.zeroRate(cfs[cfSize - 1].date(), dc, ql.Compounded, frequency).rate())
        sv.append(ts1.zeroRate(cfs[cfSize - 1].date(), dc, ql.Compounded, frequency).rate())

    df = pd.DataFrame(
        dict(
            mat=maturities * 4,
            rate=pars + spots + spline + sv,
            type=['par'] * 14 + ['spot'] * 14 + ['spline'] * 14 + ['sv'] * 14))

    print(ts1.fitResults().solution())
    return df

rs = testingYields3()

sb.relplot(
    x='mat', y='rate', kind='line', hue='type',
    data=rs, height=5, aspect=1.6)

結果以下:

tenor     spot   spline svensson
    1.000    3.054    2.993    3.011
    2.000    3.157    3.121    3.104
    3.000    3.253    3.226    3.187
    4.000    3.274    3.258    3.262
    5.000    3.296    3.281    3.330
    6.000    3.409    3.393    3.392
    7.000    3.524    3.507    3.448
    8.000    3.526    3.512    3.499
    9.000    3.530    3.517    3.547
   10.000    3.534    3.523    3.591
   15.000    3.852    3.843    3.771
   20.000    3.888    3.877    3.906
   30.000    4.094    4.085    4.084
   50.000    4.072    4.044    4.025
[ -0.190874; 0.219538; 0.0953552; 0.586634; 0.0896221; 0.0226007 ]

將結果可視化。藍色線表示到期曲線,橙色線表示即期曲線,綠色線是 PiecewiseLogCubicDiscount 擬合的結果,紅色線是 FittedBondDiscountCurve 擬合的 Svensson-Nelson-Siegel 模型,與預期徹底一致。

相關文章
相關標籤/搜索