I have the following code to price a floor (can also be used for cap), and have been computing the payoff myself, but I think QL can already do this. Since the cashflows I print out aren't correct as they don't take the tenor (here, I'm only entitled to 6 months of the notional but the cashflows assume 1 year) into account, so I want to get the NPV, intrinsic and time value from each caplet/floorlet if possible using QL functions from the cap pricing engine. I have searched and can't see a way to do this.
...
ibor_leg = ql.IborLeg([Notional], schedule, ibor_index)
#use ql.Floor for floor
if Instrument == 'Cap':
capfloor = ql.Cap(ibor_leg, [strike])
elif Instrument == 'Floor':
capfloor = ql.Floor(ibor_leg, [strike])
vols = ql.QuoteHandle(ql.SimpleQuote(volatility))
engine = ql.BlackCapFloorEngine(ts_handle, vols)
capfloor.setPricingEngine(engine)
print("Price:", "${:,.2f}".format(capfloor.NPV()))
#print table of cashflows to check against benchmark
# Compute the cashflows and discounted cashflows for each caplet
cashflows = capfloor.floatingLeg()
intrinsic_value = capfloor.NPV()
discountFactors = [term_structure.discount(cashflow.date()) for cashflow in cashflows]
discountedCashflows = [cashflow.amount() * discountFactor for cashflow,
discountFactor in zip(cashflows,
discountFactors)]
def Payoff(option_Type,Notional,F,K,year_frac,discount_rate):
if option_Type=="Cap":
return max(0,F-K)*Notional*year_frac*discount_rate
elif option_Type == "Floor":
return max(0,K-F)*Notional*year_frac*discount_rate
# Construct the table data
# Print out the NPV, intrinsic and time value for each caplet/floorlet at each fixing date
tableData = []
for i in range(len(schedule)-1):
periodStart = schedule[i]
periodEnd = schedule[i+1]
period = f"{periodStart} - {periodEnd}"
discountRate = term_structure.zeroRate(periodEnd, day_count,compounding,compounding_frequency).rate()
spotRate = ibor_index.fixing(periodStart)
forwardRate = term_structure.forwardRate(periodStart, periodEnd,
day_count,
compounding,
compounding_frequency).rate()
cashflow = cashflows[i].amount()
discountFactor = discountFactors[i]
year_frac = ql.Actual360().yearFraction(periodStart,periodEnd)
if Instrument=='Cap':
intrinsic_value = Payoff('Cap',Notional,spotRate,strike,year_frac,discountFactor)
elif Instrument == 'Floor':
intrinsic_value = Payoff('Floor',Notional,spotRate,strike,year_frac,discountFactor)
discountedCashflow = discountedCashflows[i]
tableData.append([periodStart.ISO(), periodEnd.ISO(), discountRate,
forwardRate, spotRate,
"{:,.2f}".format(cashflow),
discountFactor,
"{:,.2f}".format(discountedCashflow),"{:,.2f}".format(intrinsic_value) ])
# Print the table using tabulate
headers = ["Period Start", "Period End", "Discount Rate", "Forward Rate",
"Spot Rate", "Cashflow", "Discount Factor", "Discounted Cashflow","Value","Intrinsic Value", "Time Value"]
print(tabulate(tableData, headers=headers))