CashFlows, Legs and Interest Rates


Interest Rates

Concrete interest rate class

ql.InterestRate(rate, dayCount, compounding, frequency)
rate = ql.InterestRate(0.05, ql.Actual360(), ql.Compounded, ql.Annual)

Here are some common member functions:

  • rate() : a floating point number that returns the value of the rate of return;

  • dayCounter() : DayCounter object, which returns the member variable that controls the day calculation rule;

  • compounding() : an integer that returns the interest rate method;

  • frequency() : Integer, returns the frequency of interest payments;

  • discountFactor(d1, d2) : float, d1 and d2 are both Date objects ( d1 < d2 ), returning the discount factor size from d1 to d2;

  • compoundFactor(d1, d2) : float, d1 and d2 are both Date objects ( d1 < d2 ), returning the size of the interest factor from d1 to d2;

  • equivalentRate(resultDC, comp, freq, d1, d2) : The InterestRate object returns an InterestRate object equivalent to the current object. The configuration parameters of the object include resultDC , comp , freq :

  • Both d1 and d2 are Date objects ( d1 < d2 )

  • resultDC : DayCounter object, configure the number of days calculation rules;

  • comp : integer, configuration interest rate, the value range is some reserved variables of quantlib-python;

  • freq : integer, configuration payoff frequency, the range of values ​​is some reserved variables of quantlib-python.

In some cases, it is necessary to recalculate the rate of return based on the size of the interest factor. The InterestRate class provides the function impliedRate implement this function:

  • impliedRate(compound, resultDC, comp, freq, d1, d2) : The InterestRate object returns the inverse calculated InterestRate object whose configuration parameters include resultDC , comp , freq :

  • Both d1 and d2 are Date objects ( d1 < d2 )

  • resultDC : DayCounter object, configure the number of days calculation rules;

  • comp : integer, configuration interest rate, the value range is some reserved variables of quantlib-python;

  • freq : integer, configuration payoff frequency, the range of values ​​is some reserved variables of quantlib-python.

print("Rate: ", rate.rate())
print("DayCount: ", rate.dayCounter())
print("DiscountFactor: ", rate.discountFactor(1))
print("DiscountFactor: ", rate.discountFactor(ql.Date(15,6,2020), ql.Date(15,6,2021)))
print("CompoundFactor: ", rate.compoundFactor(ql.Date(15,6,2020), ql.Date(15,6,2021)))
print("EquivalentRate: ", rate.equivalentRate(ql.Actual360(), ql.Compounded, ql.Semiannual, ql.Date(15,6,2020), ql.Date(15,6,2021)))

factor = rate.compoundFactor(ql.Date(15,6,2020), ql.Date(15,6,2021))
print("ImpliedRate: ", rate.impliedRate(factor, ql.Actual360(), ql.Continuous, ql.Annual, ql.Date(15,6,2020), ql.Date(15,6,2021)))

CashFlows

SimpleCashFlow

ql.SimpleCashFlow(amount, date)
amount = 105
date = ql.Date(15,6,2020)
cf = ql.SimpleCashFlow(amount, date)

Redemption

ql.Redemption(amount, date)
amount = 100
date = ql.Date(15,6,2020)
redemption = ql.Redemption(amount, date)

AmortizingPayment

ql.AmortizingPayment(amount, date)
amount = 100
date = ql.Date(15,6,2020)
ql.AmortizingPayment(amount, date)

Coupons

FixedRateCoupon

ql.FixedRateCoupon(paymentDate, nominal, rate, dayCounter, startDate, endDate)
amount = 105
nominal = 100.
paymentDate = ql.Date(15,6,2020)
startDate = ql.Date(15,12,2019)
rate = .05
dayCounter = ql.Actual360()
coupon = ql.FixedRateCoupon(endDate, nominal, rate, dayCounter, startDate, endDate)

IborCoupon

ql.IborCoupon(paymentDate, nominal, startDate, endDate, fixingDays, index)
nominal = 100.
startDate = ql.Date(15,12,2020)
endDate = ql.Date(15,6,2021)
rate = .05
dayCounter = ql.Actual360()
index = ql.Euribor6M()
coupon = ql.IborCoupon(endDate, nominal, startDate, endDate, 2, index)

OvernightIndexedCoupon

ql.OvernightIndexedCoupon(paymentDate, nominal, startDate, endDate, overnightIndex, gearing=1.0, spread=0.0, refPeriodStart=ql.Date(), refPeriodEnd=ql.Date(), dayCounter=ql.DayCounter(), telescopicValueDates=False)
paymentDate = ql.Date(15, 9, 2020)
nominal = 100
startDate = ql.Date(15, 6, 2002)
endDate = ql.Date(15,9,2020)
overnightIndex = ql.Eonia()
ql.OvernightIndexedCoupon(paymentDate, nominal, startDate, endDate, overnightIndex)

CappedFlooredCoupon

Capped and/or floored floating-rate coupon

ql.CappedFlooredCoupon(FloatingRateCoupon, cap, floor)

CappedFlooredIborCoupon

CmsCoupon

ql.CmsCoupon(paymentDate, nominal, startDate, endDate, fixingDays, swapIndex)
nominal = 100.
startDate = ql.Date(15,12,2020)
endDate = ql.Date(15,6,2021)
rate = .05
dayCounter = ql.Actual360()
index = ql.Euribor6M()
fixingDays = 2
swapIndex = ql.EuriborSwapIsdaFixA(ql.Period("2Y"))
cms = ql.CmsCoupon(endDate, nominal, startDate, endDate, fixingDays, swapIndex)

CappedFlooredCmsCoupon

ql.CappedFlooredCmsCoupon(paymentDate, nominal, startDate, endDate, fixingDays, swapIndex, rate, spread)

CmsSpreadCoupon

ql.CmsSpreadCoupon(paymentDate, nominal, startDate, endDate, fixingDays, spreadIndex)
ql.CmsSpreadCoupon(paymentDate, nominal, startDate, endDate, fixingDays, spreadIndex, gearing=1, spread=0, refPeriodStart=ql.Date(), refPeriodEnd=ql.Date(), dayCounter=ql.DayCounter(), isInArrears=False, exCouponDate=ql.Date())
nominal = 100.
startDate = ql.Date(15,12,2020)
endDate = ql.Date(15,6,2021)
fixingDays = 2
swapIndex1 = ql.EuriborSwapIsdaFixA(ql.Period("10Y"))
swapIndex2 = ql.EuriborSwapIsdaFixA(ql.Period("2Y"))
spreadIndex = ql.SwapSpreadIndex("CMS 10Y-2Y", swapIndex1, swapIndex2)
ql.CmsSpreadCoupon(endDate, nominal, startDate, endDate, fixingDays, spreadIndex)

CappedFlooredCmsSpreadCoupon

ql.CmsSpreadCoupon(paymentDate, nominal, startDate, endDate, fixingDays, spreadIndex, gearing=1, spread=0, cap=Null, floor=Null, refPeriodStart=ql.Date(), refPeriodEnd=ql.Date(), dayCounter=ql.DayCounter(), isInArrears=False, exCouponDate=ql.Date())
nominal = 100.
startDate = ql.Date(15,12,2020)
endDate = ql.Date(15,6,2021)
fixingDays = 2
swapIndex1 = ql.EuriborSwapIsdaFixA(ql.Period("10Y"))
swapIndex2 = ql.EuriborSwapIsdaFixA(ql.Period("2Y"))
spreadIndex = ql.SwapSpreadIndex("CMS 10Y-2Y", swapIndex1, swapIndex2)
ql.CappedFlooredCmsSpreadCoupon(endDate, nominal, startDate, endDate, fixingDays, spreadIndex)

gearing = 1
spread = 0
cap=0
floor=0

ql.CappedFlooredCmsSpreadCoupon(endDate, nominal, startDate, endDate, fixingDays, spreadIndex, gearing, spread, cap, floor)

refPeriodStart = ql.Date()
refPeriodEnd = ql.Date()
dayCounter = ql.Actual360()
isInArrears = False
exCouponDate = ql.Date()
ql.CappedFlooredCmsSpreadCoupon(endDate, nominal, startDate, endDate, fixingDays, spreadIndex, gearing, spread, cap, floor, refPeriodStart, refPeriodEnd, dayCounter, isInArrears, exCouponDate)

Legs

Leg

date = ql.Date().todaysDate()
cf1 = ql.SimpleCashFlow(5.0, date+365)
cf2 = ql.SimpleCashFlow(5.0, date+365*2)
cf3 = ql.SimpleCashFlow(105.0, date+365*3)
leg = ql.Leg([cf1, cf2, cf3])

FixedRateLeg

helper class building a sequence of fixed rate coupons

ql.FixedRateLeg(schedule, dayCount, nominals, fixedRate, BusinessDayConvention, FirstPeriodDayCounter, ExCouponPeriod, PaymentCalendar)
schedule = ql.MakeSchedule(ql.Date(15,6,2020), ql.Date(15,6,2021), ql.Period('6M'))
dayCount = ql.Actual360()
leg = ql.FixedRateLeg(schedule, dayCount, [100.], [0.05])
leg = ql.FixedRateLeg(schedule, ql.Actual360(), [100.], [0.05], ql.Following, ql.Actual360(), ql.Period('3M'), ql.TARGET())

IborLeg

helper class building a sequence of capped/floored ibor-rate coupon

ql.IborLeg(nominals, schedule, index, paymentDayCounter = DayCounter(), paymentConvention = Following, fixingDays = 0, gearings = 1, spreads, caps, floors, isInArrears, exCouponPeriod, exCouponCalendar, exCouponConvention = Unadjusted, exCouponEndOfMonth = False)
schedule = ql.MakeSchedule(ql.Date(15,6,2020), ql.Date(15,6,2021), ql.Period('6M'))
index = ql.Euribor3M()
leg = ql.IborLeg([100], schedule, index)
leg = ql.IborLeg([100], schedule, index, ql.Actual360())
leg = ql.IborLeg([100], schedule, index, ql.Actual360(), ql.ModifiedFollowing)
leg = ql.IborLeg([100], schedule, index, ql.Actual360(), ql.ModifiedFollowing, [2])
leg = ql.IborLeg([100], schedule, index, ql.Actual360(), ql.ModifiedFollowing, fixingDays=[2], gearings=[1])

leg = ql.IborLeg([100], schedule, index, ql.Actual360(), ql.ModifiedFollowing, fixingDays=[2], gearings=[1], spreads=[0])
leg = ql.IborLeg([100], schedule, index, ql.Actual360(), ql.ModifiedFollowing, fixingDays=[2], gearings=[1], spreads=[0], caps=[0])
leg = ql.IborLeg([100], schedule, index, ql.Actual360(), ql.ModifiedFollowing, fixingDays=[2], gearings=[1], spreads=[0], floors=[0])

OvernightLeg

helper class building a sequence of overnight coupons

ql.OvernightLeg(nominals, schedule, overnightIndex, dayCount, BusinessDayConvention, gearing, spread, TelescopicValueDates)
nominal = 100
schedule = ql.MakeSchedule(ql.Date(15,6,2020), ql.Date(15,6,2021), ql.Period('3M'))
overnightIndex = ql.OvernightIndex('CNYRepo7D', 1, ql.CNYCurrency(), ql.China(), ql.Actual365Fixed())
ql.OvernightLeg([nominal], schedule, overnightIndex, ql.Actual360(), ql.Following, [1],[0], True)

Pricers

BlackIborCouponPricer

ql.BlackIborCouponPricer(OptionletVolatilityStructureHandle)
volatility = 0.10;
vol = ql.ConstantOptionletVolatility(2, ql.TARGET(), ql.Following, volatility, ql.Actual360())
pricer = ql.BlackIborCouponPricer(ql.OptionletVolatilityStructureHandle(vol))

Example: In arrears coupon

crv = ql.FlatForward(0, ql.TARGET(), -0.01, ql.Actual360())
yts = ql.YieldTermStructureHandle(crv)
index = ql.Euribor3M(yts)

schedule = ql.MakeSchedule(ql.Date(15,6,2021), ql.Date(15,6,2023), ql.Period('6M'))

leg = ql.IborLeg([100], schedule, index, ql.Actual360(), ql.ModifiedFollowing, isInArrears=True)

volatility = 0.10;
vol = ql.ConstantOptionletVolatility(2, ql.TARGET(), ql.Following, volatility, ql.Actual360())
pricer = ql.BlackIborCouponPricer(ql.OptionletVolatilityStructureHandle(vol))
ql.setCouponPricer(leg, pricer)

npv = ql.CashFlows.npv(leg, yts, True)
print(f"LEG NPV: {npv:,.2f}")

LinearTsrPricer

ql.LinearTsrPricer(swaptionVolatilityStructure, meanReversion)
volQuote = ql.QuoteHandle(ql.SimpleQuote(0.2))
swaptionVol = ql.ConstantSwaptionVolatility(0, ql.TARGET(), ql.ModifiedFollowing, volQuote, ql.Actual365Fixed())
swvol_handle = ql.SwaptionVolatilityStructureHandle(swaptionVol)

mean_reversion = ql.QuoteHandle(ql.SimpleQuote(0.01))
cms_pricer = ql.LinearTsrPricer(swvol_handle, mean_reversion)

LognormalCmsSpreadPricer

NumericHaganPricer

AnalyticHaganPricer


Cashflow Analysis Functions

Date Inspectors

ql.CashFlows.startDate(leg)
ql.CashFlows.maturityDate(leg)

Cashflow Inspectors

the last cashflow paying before or at the given date

ql.CashFlows.previousCashFlowDate(leg, includeSettlementDateFlows, settlementDate=ql.Date())
ql.CashFlows.previousCashFlowDate(leg, True)
ql.CashFlows.previousCashFlowDate(leg, True, ql.Date(15,12,2020))

the first cashflow paying after the given date

ql.CashFlows.nextCashFlowDate(leg, includeSettlementDateFlows, settlementDate=ql.Date())

YieldTermstructure

NPV of the cash flows

ql.CashFlows.npv(leg, discountCurve, includeSettlementDateFlows, settlementDate=ql.Date(), npvDate=ql.Date())
yts = ql.YieldTermStructureHandle(ql.FlatForward(ql.Date(15,1,2020), 0.04, ql.Actual360()))
ql.CashFlows.npv(leg, yts, True)
ql.CashFlows.npv(leg, yts, True, ql.Date(15,6,2020))
ql.CashFlows.npv(leg, yts, True, ql.Date(15,6,2020), ql.Date(15,12,2020))

Basis-point sensitivity of the cash flows

ql.CashFlows.bps(leg, discountCurve, includeSettlementDateFlows, settlementDate=ql.Date(), npvDate=ql.Date())
yts = ql.YieldTermStructureHandle(ql.FlatForward(ql.Date(15,1,2020), 0.04, ql.Actual360()))
ql.CashFlows.bps(leg, yts, True)

At-the-money rate of the cash flows

ql.CashFlows.atmRate(leg, discountCurve, includeSettlementDateFlows, settlementDate=ql.Date(), ql.npvDate=Date(), npv=Null< Real >())
crv = ql.FlatForward(ql.Date(15,1,2020), 0.04, ql.Actual360())
ql.CashFlows.atmRate(leg, crv, True, ql.Date(15,6,2020))

Yield (a.k.a. Internal Rate of Return, i.e. IRR)

ql.CashFlows.npv(leg, rate, includeSettlementDateFlows, settlementDate=ql.Date(), npvDate=ql.Date())
rate = ql.InterestRate(.03, ql.ActualActual(), ql.Compounded, ql.Annual)
ql.CashFlows.npv(leg, rate, True)
ql.CashFlows.bps(leg, rate, includeSettlementDateFlows, settlementDate=ql.Date(), npvDate=ql.Date())
rate = ql.InterestRate(.03, ql.ActualActual(), ql.Compounded, ql.Annual)
ql.CashFlows.bps(leg, rate, True)
ql.CashFlows.basisPointValue(leg, InterestRate, includeSettlementDateFlows, settlementDate=ql.Date(), ql.npvDate=Date())
rate = ql.InterestRate(.03, ql.ActualActual(), ql.Compounded, ql.Annual)
ql.CashFlows.basisPointValue(leg, rate, True)
ql.CashFlows.basisPointValue(leg, rate, dayCounter, compounding, frequency, includeSettlementDateFlows, settlementDate=ql.Date(), ql.npvDate=Date())
ql.CashFlows.basisPointValue(leg, 0.05, ql.Actual360(), ql.Compounded, ql.Annual, True)
ql.CashFlows.duration(leg, InterestRate, ql.Duration.Type, includeSettlementDateFlows, settlementDate=ql.Date(), npvDate=ql.Date())
rate = ql.InterestRate(.03, ql.ActualActual(), ql.Compounded, ql.Annual)

ql.CashFlows.duration(leg, rate, ql.Duration.Simple, False)
ql.CashFlows.duration(leg, rate, ql.Duration.Macaulay, False)
ql.CashFlows.duration(leg, rate, ql.Duration.Modified, False)
ql.CashFlows.duration(leg, rate, dayCounter, compounding, frequency, ql.Duration.Type, includeSettlementDateFlows, settlementDate=ql.Date(), npvDate=ql.Date())
rate = 0.05
ql.CashFlows.duration(leg, rate, ql.Actual360(), ql.Compounded, ql.Annual, ql.Duration.Simple, False)
ql.CashFlows.convexity(leg, InterestRate, includeSettlementDateFlows, settlementDate=ql.Date(), npvDate=ql.Date())
rate = ql.InterestRate(.03, ql.ActualActual(), ql.Compounded, ql.Annual)
ql.CashFlows.convexity(leg, rate, False)
ql.CashFlows.convexity(leg, rate, dayCounter, compounding, frequency, includeSettlementDateFlows, settlementDate=ql.Date(), npvDate=ql.Date())
rate = 0.05
ql.CashFlows.convexity(leg, rate, ql.Actual360(), ql.Compounded, ql.Annual, False)
ql.CashFlows.yieldRate(leg, rate, dayCounter, compounding, frequency, includeSettlementDateFlows, settlementDate=ql.Date(), npvDate=ql.Date(), accuracy=1e-10, maxIterations=100, guess=0.0)
ql.CashFlows.yieldRate(leg, 5, ql.Actual360(), ql.Compounded, ql.Annual, True)
ql.CashFlows.yieldRate(leg, 5, ql.Actual360(), ql.Compounded, ql.Annual, True, ql.Date(15,6,2020))
ql.CashFlows.yieldRate(leg, 5, ql.Actual360(), ql.Compounded, ql.Annual, True, ql.Date(15,6,2020), ql.Date(15,12,2020))
ql.CashFlows.yieldRate(leg, 5, ql.Actual360(), ql.Compounded, ql.Annual, True, ql.Date(15,6,2020), ql.Date(15,12,2020), 1e-5)
ql.CashFlows.yieldRate(leg, 5, ql.Actual360(), ql.Compounded, ql.Annual, True, ql.Date(15,6,2020), ql.Date(15,12,2020), 1e-5, 100)
ql.CashFlows.yieldRate(leg, 5, ql.Actual360(), ql.Compounded, ql.Annual, True, ql.Date(15,6,2020), ql.Date(15,12,2020), 1e-5, 100, 0.04)

Z-spread

implied Z-spread.

ql.CashFlows.zSpread(leg, npv, YieldTermStructure, dayCounter, compounding, frequency, includeSettlementDateFlows, settlementDate=ql.Date(), npvDate=ql.Date(), accuracy=1e-10, maxIterations=100, guess=0.0)
crv = ql.FlatForward(ql.Date(15,1,2020), 0.04, ql.Actual360())
ql.CashFlows.zSpread(leg, 5.5, crv, ql.Actual360(), ql.Compounded, ql.Annual, True)