Wednesday, November 21, 2018

QuantLib-Python: Builder for Piecewise Term Structure

This post is presenting one possible implementation for Python builder class for constructing QuantLib piecewise yield term structure. The purpose is simple: one can assemble piecewise yield curve by adding arbitrary amount of different quote types and finally request handle for the curve. This curve can then be used in other part of the program. Effectively, this class is wrapping some of the required tedious administrative code work away from a client and also offering a nice compact package for all corresponding purposes. Now, it is not my intention to be object-oriented in Python world just for the sake of being object-oriented, but .. this is just a perfect place for Python class implementation. Corresponding C++ version has been presented in here.

Thanks for reading my blog.
-Mike

from QuantLib import *
import numpy as Numpy

# create piecewise yield term structure
class PiecewiseCurveBuilder:
    def __init__(self, settlementDate, dayCounter):
        self.helpers = []
        self.settlementDate = settlementDate
        self.dayCounter = dayCounter

    # 4th constructor: DepositRateHelper(Rate rate, const shared_ptr<IborIndex> &iborIndex)
    def AddDeposit(self, rate, iborIndex):
        helper = DepositRateHelper(rate, iborIndex)
        self.helpers.append(helper)

    # 4th constructor: FraRateHelper(Rate rate, Natural monthsToStart, const shared_ptr<IborIndex> &iborIndex)
    def AddFRA(self, rate, monthsToStart, iborIndex):
        helper = FraRateHelper(rate, monthsToStart, iborIndex)
        self.helpers.append(helper)
    
    # 6th constructor (Real price, const Date &iborStartDate, const ext::shared_ptr<IborIndex> &iborIndex) 
    def AddFuture(self, price, iborStartDate, iborIndex):
        helper = FuturesRateHelper(price, iborStartDate, iborIndex)
        self.helpers.append(helper)
    
    # 4th constructor: SwapRateHelper(Rate rate, const Period &tenor, const Calendar &calendar, 
    # Frequency fixedFrequency, BusinessDayConvention fixedConvention, const DayCounter &fixedDayCount, 
    # const shared_ptr<IborIndex> &iborIndex)
    def AddSwap(self, rate, periodLength, fixedCalendar, fixedFrequency, fixedConvention, fixedDayCount, floatIndex):
        helper = SwapRateHelper(rate, periodLength, fixedCalendar, fixedFrequency, 
            fixedConvention, fixedDayCount, floatIndex)
        self.helpers.append(helper)
    
    # PiecewiseYieldCurve <ZeroYield, Linear>
    def GetCurveHandle(self):  
        yieldTermStructure = PiecewiseLinearZero(self.settlementDate, self.helpers, self.dayCounter)
        return RelinkableYieldTermStructureHandle(yieldTermStructure)


# general parameters    
tradeDate = Date(4, February, 2008)
calendar = TARGET()
dayCounter = Actual360()
convention = ModifiedFollowing
settlementDate = calendar.advance(tradeDate, Period(2, Days), convention)  
swapIndex = USDLibor(Period(3, Months))
frequency = Annual

# create curve builder object
Settings.instance().evaluationDate = tradeDate
builder = PiecewiseCurveBuilder(settlementDate, dayCounter)

# cash deposit
depos = []
depos.append((0.032175, USDLibor(Period(1, Weeks))))
depos.append((0.0318125, USDLibor(Period(1, Months))))
depos.append((0.03145, USDLibor(Period(3, Months))))
[builder.AddDeposit(d[0], d[1]) for d in depos]

# futures
futures = []
futures.append((97.41, IMM.nextDate(settlementDate + Period(3, Months)), swapIndex))
futures.append((97.52, IMM.nextDate(settlementDate + Period(6, Months)), swapIndex))
futures.append((97.495, IMM.nextDate(settlementDate + Period(9, Months)), swapIndex))
futures.append((97.395, IMM.nextDate(settlementDate + Period(12, Months)), swapIndex))
[builder.AddFuture(f[0], f[1], f[2]) for f in futures]

# swaps
swaps = []
swaps.append((0.02795, Period(2, Years), calendar, frequency, convention, dayCounter, swapIndex))
swaps.append((0.03035, Period(3, Years), calendar, frequency, convention, dayCounter, swapIndex))
swaps.append((0.03275, Period(4, Years), calendar, frequency, convention, dayCounter, swapIndex))
swaps.append((0.03505, Period(5, Years), calendar, frequency, convention, dayCounter, swapIndex))
swaps.append((0.03715, Period(6, Years), calendar, frequency, convention, dayCounter, swapIndex))
swaps.append((0.03885, Period(7, Years), calendar, frequency, convention, dayCounter, swapIndex))
swaps.append((0.04025, Period(8, Years), calendar, frequency, convention, dayCounter, swapIndex))
swaps.append((0.04155, Period(9, Years), calendar, frequency, convention, dayCounter, swapIndex))
swaps.append((0.04265, Period(10, Years), calendar, frequency, convention, dayCounter, swapIndex))
swaps.append((0.04435, Period(12, Years), calendar, frequency, convention, dayCounter, swapIndex))
[builder.AddSwap(s[0], s[1], s[2], s[3], s[4], s[5], s[6]) for s in swaps]

# get relinkable curve handle from builder
curve = builder.GetCurveHandle()
curve.enableExtrapolation()

# create and print array of discount factors for every 3M up to 15Y
times = Numpy.linspace(0.0, 15.0, 61)
dfs = Numpy.array([curve.discount(t) for t in times])
print(dfs)

No comments:

Post a Comment