Skip to content
All library documents

Matching QuantLib Swap Quotes by Aligning Calibration and Pricing Conventions

Article Quant Q&A · Author: VexedIntern

Summary

The document explains why swaps manually priced from a QuantLib curve may fail to reproduce the market quotes used to build that curve. In the example, the discrepancies arise because the manually constructed swaps do not exactly match the swap rate helpers’ conventions. The helper’s swap index uses a standard USD LIBOR index, while the manual floating index has different day-count and fixing-calendar settings. The manual swap also relies on defaults for fixed-leg tenor and calendar conventions that differ from those used by the helper.

The proposed remedy is to construct the floating index using the same standard index as the helper, link it to the curve, and specify matching fixed and floating leg conventions when creating each swap. With those conventions aligned, the example reports that the manually priced fair rates reproduce the input quotes. The lesson is about consistency in instrument definitions during curve construction and validation; it does not establish that a curve is well calibrated beyond the instruments and conventions in the example.

Key ideas

  • Curve helpers and manually priced instruments must use matching conventions for a valid quote comparison.
  • The standard USD LIBOR swap helper uses a floating index that may differ from a custom-created index.
  • Fixed-leg tenor, day count, calendars, and index linkage can affect swap fair rates.
  • The example reports exact quote reproduction after aligning the instrument definitions.

Tags

Full text
# USD Swap curve prices do not line up with inputs


# USD Swap curve prices do not line up with inputs












As mentioned in the title, i'm having trouble with pricing USD swaps in quantlib.

I wanted to take some inputs (Prices of swaps in the market) and be able to construct a yield term structure that allows me to price say a 1y1y swap. However, it seems that that when constructing a yield curve and then pricing a swap, my prices do not line with with the exact instruments im using and im wondering if how i can reconcile the two such that the model outputs == inputs for the tenors below

```
import QuantLib as ql
today = ql.Date().todaysDate()
calendar = ql.UnitedStates()
spot = calendar.advance(today, 2, ql.Days)
helpers = ql.RateHelperVector()

index = ql.IborIndex('MyIndex',
                     ql.Period('3m'),
                     2,
                     ql.USDCurrency(),
                     ql.UnitedStates(),
                     ql.ModifiedFollowing,
                     True,
                     ql.Thirty360())

helpers.append( ql.DepositRateHelper(0.01, index) )

swaps = [
    ('1Y', 0.015),
    ('2Y', 0.02),
    ('3Y', 0.025)
]
for tenor, rate in swaps:
    swapIndex = ql.UsdLiborSwapIsdaFixAm(ql.Period(tenor))
    helpers.append( ql.SwapRateHelper(rate, swapIndex) )

curve = ql.PiecewiseSplineCubicDiscount(spot, helpers, ql.Thirty360())
yts = ql.YieldTermStructureHandle(curve)
engine = ql.DiscountingSwapEngine(yts)

index = ql.IborIndex('MyIndex',
                     ql.Period('3m'),
                     2,
                     ql.USDCurrency(),
                     ql.UnitedStates(),
                     ql.ModifiedFollowing,
                     True,
                     ql.Thirty360(),
                     yts)

print("maturity, market, model")
for tenor, rate in swaps:
    swap = ql.MakeVanillaSwap(ql.Period(tenor), index, 0.01, ql.Period('0D'), pricingEngine=engine)    
    print(f"{tenor}, {rate:.6f}, {swap.fairRate():.6f}")
```

| maturity | market | model |
| 1Y | 0.015000 | 0.014831 |
| 2Y | 0.020000 | 0.019809 |
| 3Y | 0.025000 | 0.024755 |

## Answer by Francis (score 5)

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

I updated your example to show what needs to change so that the fair rates on the manually created swaps match the input quotes. The main thing is that the manually created swaps need to match the rate helpers that are created exactly. A few things in your setup meant that this was not happening:

- The helpers created with the `ql.SwapRateHelper(rate, swapIndex)` call where the `swapIndex` is `ql.UsdLiborSwapIsdaFixAm` use `ql.USDLibor(ql.Period('3m'))` as the floating leg index. You can see this here in the underlying C++ code. This does not match the `index` that you create manually for your swap e.g. different day counter and fixing calendar. So, I just used `ql.USDLibor` directly.

- I added a few additional parameters to the call to `ql.MakeVanillaSwap` so that it matches the call that occurs in the swap rate helper constructor under the hood in the C++ code i.e. here and then here.

```
import QuantLib as Ql
ql = Ql

# Dates
today = ql.Date().todaysDate()
ql.Settings.instance().evaluationDate = today

# Helper container
helpers = ql.RateHelperVector()

# Deposit helper
index = ql.USDLibor(ql.Period('3m'))
helpers.append(ql.DepositRateHelper(0.01, index))

# Swap helpers
swaps = [
    ('1Y', 0.015),
    ('2Y', 0.02),
    ('3Y', 0.025)
]
for tenor, rate in swaps:
    swapIndex = ql.UsdLiborSwapIsdaFixAm(ql.Period(tenor))
    helpers.append(ql.SwapRateHelper(rate, swapIndex))

# Yield term structure
curve = ql.PiecewiseSplineCubicDiscount(today, helpers, ql.Thirty360())
yts = ql.YieldTermStructureHandle(curve)

# Link index to discount curve
index = index.clone(yts)

# Create engine with yield term structure
engine = ql.DiscountingSwapEngine(yts)

# Check the swaps reprice
print("maturity |  market  |   model")
for tenor, rate in swaps:
    swap = ql.MakeVanillaSwap(ql.Period(tenor),
                              index, 0.01,
                              ql.Period('0D'),
                              fixedLegTenor=ql.Period('6M'),
                              fixedLegDayCount=ql.Thirty360(),
                              fixedLegCalendar=ql.UnitedStates(),
                              floatingLegCalendar=ql.UnitedStates(),
                              pricingEngine=engine)

    print(f"   {tenor}    | {rate:.6f} | {swap.fairRate():.6f}")
```

The output from this is now:

```
maturity |  market  |   model
   1Y    | 0.015000 | 0.015000
   2Y    | 0.020000 | 0.020000
   3Y    | 0.025000 | 0.025000
```

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.