2
$\begingroup$

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))
$\endgroup$

1 Answer 1

1
$\begingroup$

The CapFloor class provides some limited information (see the SWIG wrappers). You can call capfloor.optionletsPrice() to get a list of the PVs of the caplets/floorlets and other methods such as capfloor.optionletsAtmForward() and capfloor.optionletsDiscountFactor() for more data.

$\endgroup$
1
  • $\begingroup$ thanks so much for this, I'll try it now. really apppreciate it. $\endgroup$ Commented Aug 31, 2023 at 13:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.