Skip to content
All library documents

Using the GSR Model for Callable Bond OAS in QuantLib

Article Quant Q&A · Author: P Bemelmans

Summary

The document asks whether QuantLib’s GSR short-rate model can be used with TreeCallableFixedRateBondEngine to calculate option-adjusted spreads for callable or puttable bonds. The motivation is that Hull–White and Black–Karasinski work with this engine but do not provide the time-varying volatility and mean reversion the author needs. The author shows a calibrated GSR setup that prices swaptions through Gaussian1dSwaptionEngine, then reports a type error when passing GSR to the tree engine.

The document provides no answer to whether the engine supports GSR and no alternative OAS method. Its useful content is the distinction between a model that calibrates and prices one instrument class and compatibility with a particular callable-bond engine. The error lists accepted constructor signatures but does not identify which argument or model type causes the failure. Readers should treat this as an unresolved implementation question rather than evidence that GSR is incompatible; the example alone does not establish a fix or pricing result.

Key ideas

  • GSR offers time-varying volatility and mean reversion in the author’s setup.
  • The example calibrates GSR using swaption helpers and Gaussian1dSwaptionEngine.
  • The author encounters a type error when passing GSR to TreeCallableFixedRateBondEngine.
  • The document does not establish whether GSR is compatible with that engine or how to obtain OAS otherwise.

Tags

Full text
# Can I use the Gsr short-rate model in quantlib to find the OAS of callable bonds


# Can I use the Gsr short-rate model in quantlib to find the OAS of callable bonds












I am trying to find the OAS of callable/puttable bonds using TreeCallableFixedRateBondEngine. This works when I use the HullWhite or BlackKarasinski short-rate models, but they do not allow for time-varying volatility and mean-reversion. when I try using the Gsr short-rate model, I get this error:

```
TypeError: Wrong number or type of arguments for overloaded function 'new_TreeCallableFixedRateBondEngine'.
  Possible C/C++ prototypes are:
  TreeCallableFixedRateBondEngine::TreeCallableFixedRateBondEngine(ext::shared_ptr< ShortRateModel > const &,Size,Handle< YieldTermStructure > const &)
  TreeCallableFixedRateBondEngine::TreeCallableFixedRateBondEngine(ext::shared_ptr< ShortRateModel > const &,Size)
  TreeCallableFixedRateBondEngine::TreeCallableFixedRateBondEngine(ext::shared_ptr< ShortRateModel > const &,TimeGrid const &,Handle< YieldTermStructure > const &)
  TreeCallableFixedRateBondEngine::TreeCallableFixedRateBondEngine(ext::shared_ptr< ShortRateModel > const &,TimeGrid const &)
```

Does this mean that the Gsr model is not compatible with the TreeCallableFixedRateBondEngine? Are there other methods to find the OAS of bonds with the Gsr model?

My Gsr model is calibrated and I am able to use it in the Gaussian1dSwaptionEngine. This is the code I am using:

```
import QuantLib as ql
import numpy as np
```

## Set Up the Evaluation Environment

```
today = ql.Date(2, ql.October, 2024)
ql.Settings.instance().evaluationDate = today
```

## Define the Yield Term Structure

```
flat_forward_rate = 0.02  # 2% flat rate
day_count = ql.Actual365Fixed()
calendar = ql.TARGET()
settlement_days = 2

spot_curve = ql.FlatForward(today, flat_forward_rate, day_count)
term_structure_handle = ql.YieldTermStructureHandle(spot_curve)
```

## Set Up Market Data for Swaptions

```
swaption_maturities = [ql.Period(i, ql.Years) for i in range(1, 6)]  # 1Y to 5Y
swap_tenors = [ql.Period(5, ql.Years)] * 5
market_vols = [0.20, 0.19, 0.18, 0.17, 0.16]
```

## Create Swaption Helpers

```
swaption_helpers = []

fixed_leg_frequency = ql.Annual
fixed_leg_convention = ql.ModifiedFollowing
fixed_leg_daycount = ql.Thirty360(ql.Thirty360.BondBasis)
floating_leg_frequency = ql.Semiannual
floating_leg_convention = ql.ModifiedFollowing
floating_leg_daycount = ql.Actual360()
index = ql.Euribor6M(term_structure_handle)

for maturity, tenor, vol in zip(swaption_maturities, swap_tenors, market_vols):
    helper = ql.SwaptionHelper(
        maturity,
        tenor,
        ql.QuoteHandle(ql.SimpleQuote(vol)),
        index,
        index.tenor(),
        fixed_leg_daycount,
        floating_leg_daycount,
        term_structure_handle
    )
    swaption_helpers.append(helper)
```

## Initialize the GSR Model

```
vol_times = [today + m for m in swaption_maturities[:-2]]
initial_vols = [ql.QuoteHandle(ql.SimpleQuote(0.01)) for _ in range(len(vol_times)+1)]
reversions = [ql.QuoteHandle(ql.SimpleQuote(0.01))]
short_rate_model = ql.Gsr(term_structure_handle, vol_times, initial_vols, reversions)
```

## Set Up the Swaption Pricing Engine

```
engine = ql.Gaussian1dSwaptionEngine(short_rate_model, 64, 7.0)
for helper in swaption_helpers:
    helper.setPricingEngine(engine)
```

## Calibrate the GSR Model

```
optimization_method = ql.LevenbergMarquardt()
end_criteria = ql.EndCriteria(
    1000,    # maxIterations
    500,     # maxStationaryStateIterations
    1e-8,    # rootEpsilon
    1e-8,    # functionEpsilon
    1e-8     # gradientNormEpsilon
)
short_rate_model.calibrate(swaption_helpers, optimization_method, end_criteria)

steps=100
callable_bond_engine = ql.TreeCallableFixedRateBondEngine(short_rate_model, steps)
```

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.