Skip to content
All library documents

Modeling a TIPS Deflation Floor in QuantLib

Article Quant Q&A · Author: Thomas David

Summary

The document discusses how to represent the principal deflation floor on a Treasury Inflation-Protected Security (TIPS) when using QuantLib's CPIBond. The requested payoff protects redemption at maturity so it cannot fall below face value, while coupons remain adjusted for inflation. The response states that QuantLib does not currently include a floor in its inflation-linked cash flows and that a full valuation would require pricing the embedded option, which entails nontrivial library work.

For the specific example, where inflation is held at zero and the final CPI is known to be below its initial level, the suggested workaround is to replace the inflation-linked final redemption with a plain redemption at face value and rebuild the bond. This allows yield and DV01 calculations for that assumed scenario. The workaround is limited: if inflation assumptions change and the redemption should become inflation-adjusted, the rebuilt bond will not reflect that, so it is not a general implementation of the floor.

Key ideas

  • The discussed QuantLib CPIBond cash flows do not include a TIPS principal deflation floor.
  • A full treatment requires valuing the embedded floor option.
  • For a known deflation scenario, the final indexed redemption can be replaced with face-value redemption.
  • The workaround does not update correctly if assumptions imply an inflation-adjusted redemption.

Tags

Full text
# Implementing deflation floor in QuantLib


# Implementing deflation floor in QuantLib












TIPS have deflation protection, so even if there is deflation across the life of the TIPS, the TIPS pays out the face value at maturity. I am having trouble implementing this deflation floor in QuantLib with the CPIBond object. I would like to have the value paid out at maturity bet at minimum the face value of the bond. But, I can't seem to find a way to manually adjust the CPIBond to both adjust for deflation in the coupon payments but then also pay at minimum the face value at maturity?

```
from QuantLib import *

base_cpi = 315.58677

cpi_vals = [
    315.698,  # October 2024
    315.483,  # November 2024
    315.602,  # December 2024
    317.726,  # January 2025
    319.103,  # February 2025
    319.756,  # March 2025
    320.795,  # April 2025
    321.46,   # May 2025
    base_cpi-50    # June 2025
]

cpi_dates = [Date(1,1,2025), Date(1,2,2025), Date(1,3,2025), Date(1,4,2025), Date(1,5,2025), Date(1,6,2025), Date(1,7,2025), Date(1,8,2025), Date(1,9,2025)]

# this code actually shifts the dates back by 3 months to be attached to the CPIIndex
for i in range(len(cpi_dates)):
    cpi_dates[i] = cpi_dates[i] - Period(3, Months)

settle_date = Date(17,7,2025)
trade_date = Date(16,7,2025)

valuation_date = trade_date

base_date = cpi_dates[-1]

end_date = base_date + Period(100, Years)

rates=[0.0,0.0]

zero_inflation = ZeroInflationCurve(valuation_date, [base_date, end_date], rates, Monthly, ActualActual(ActualActual.ISDA))
zero_inflation_ts_handle = ZeroInflationTermStructureHandle(zero_inflation)

cpi_index = USCPI(zero_inflation_ts_handle)

for i in range(len(cpi_dates)):
   cpi_index.addFixing(cpi_dates[i], cpi_vals[i], True)

issue_date = Date(31, 1, 2025)
maturity_date = Date(15, 1, 2035)

tenor = Period(Semiannual)

calendar = NullCalendar()

business_convention = Following

date_generation = DateGeneration.Backward
day_count = ActualActual(ActualActual.ISDA)

month_end = False
schedule = Schedule (issue_date, maturity_date, tenor,
                    calendar, business_convention,
                    business_convention , date_generation,
                    month_end)
coupon_rate = [.02125]
settlement_days = 1
face_value = 100

growth_only = False

observation_interpolation = CPI.Linear

Settings.instance().evaluationDate = valuation_date

cpi_bond = CPIBond(settlement_days, face_value, growth_only, base_cpi, Period(3, Months), cpi_index,
                  observation_interpolation, schedule, coupon_rate, day_count)

for cf in cpi_bond.cashflows():
    print(cf.date().ISO(), cf.amount())
```
```

## Answer by Luigi Ballabio (score 5, accepted)

https://quant.stackexchange.com/a/83873

There's no floor in the inflation cash flows as of now. As Dimitri said, the right way to price it would be to evaluate the option and that requires some non-trivial work to be done in the library. I suggest you open an issue for this.

In your particular case, since (as I'm gathering from your comments) you're keeping inflation null and you know that the final CPI value will be lower than the initial one, you can work around this by replacing the final cashflow with a simple non-inflated redemption and rebuild the bond. Something like:

```
cpi_bond = CPIBond(settlement_days, face_value, growth_only, base_cpi, Period(3, Months), cpi_index,
                  observation_interpolation, schedule, coupon_rate, day_count)

cfs = bond.cashflows()
inflated_redemption = cfs[-1] # the last one
coupons = cfs[:-1]            # the others
redemption = ql.Redemption(face_value, cfs[-1].date())
new_cfs = coupons + [redemption]

new_bond = ql.Bond(settlement_days, calendar, face_value,
                   maturity_date, issue_date, new_cfs)
```

You can then use the new bond to calculate yield and DV01.

Again: this is a work around for this particular case, and might not always work. For instance, if you bump inflation so that the redemption turns out to be inflated after all, this bond won't show it. You might want to keep the old bond around for comparison.

Shown in full with attribution under the source's licence. Licence: CC BY-SA 4.0 (Stack Exchange)

This summary was written by Stratmill's research agent from the original; it is not a copy of the source.