Speeding Up Swap Scenario Pricing with Relinkable Curve Handles
Summary
The note describes a way to reduce setup work when valuing many interest rate swaps across many simulated yield curves. Instead of rebuilding each swap and pricing engine for every scenario, create the swaps and engines once, with relinkable handles for the forecast and discount curves. For each scenario, link those handles to the new curves and recalculate each swap’s net present value. If swaps share a discount curve, they can also share one pricing engine.
This approach avoids repeated construction of objects, but it does not eliminate the work of pricing each swap. The response notes that updating links can trigger notifications between objects, so the actual speed gain depends on the implementation and workload. It offers a design suggestion, not measured performance results or a benchmark.
Key ideas
- Create swap objects and pricing engines once before processing scenarios.
- Use relinkable handles to update forecast and discount curves between scenarios.
- Swaps with a common discount curve can share a discounting engine.
- Recalculating each swap’s NPV is still required for every scenario.
- Curve relinking may trigger object notifications, and the note gives no benchmark of the speed gain.
Tags
Full text
# Optimize interest rate swap calculations in Monte Carlo Simulation
# Optimize interest rate swap calculations in Monte Carlo Simulation
I’m running a simulation in which I want to calculate the NPV of 100 swaps over 1000 (or even much more) different interest rate curves.
It looks like Quantlib is not really fast in performing these calculations or my code is just not optimal.
Script does the following (for all curves):
- Create Quantlib curve objects (based on these simulated interest rate curves)
- Create swap object per swap
- Set swappriceEngine per swap object
- Calculate NPV per swap
Code example for every swap over all simulated interest rate curves:
```
fixed_schedule = ql.Schedule(settle_date, maturity_date,
fixed_leg_tenor, calendar,
ql.ModifiedFollowing, ql.ModifiedFollowing,
ql.DateGeneration.Forward, False
float_schedule = ql.Schedule (settle_date, maturity_date,
float_leg_tenor, calendar,
ql.ModifiedFollowing, ql.ModifiedFollowing,
ql.DateGeneration.Forward, False
swap = ql.VanillaSwap(ql.VanillaSwap.Payer, notional, fixed_schedule,
fixed_rate, fixed_leg_daycount, float_schedule,
6M_index, float_spread, float_leg_daycount)
swap.setPricingEngine(DiscountingSwapEngine(YieldTermStructureHandle(discount_curve)))
swap.NPV()
```
I’m wondering if it is possible to create only 100 different swap objects and just update the interest rate curves needed to calculate the NPV instead of creating swap objects in every loop. So only update the 6M_index in swap object and update discountcurve in swap.setPricingEngine.
Is that possible?
## Answer by Luigi Ballabio (score 5, accepted)
https://quant.stackexchange.com/a/72231
Yes, it's possible. You can create the 100 swaps and their engines beforehand and only change the curves. If you're using the same discount curve for all swaps, you can even create just one engine and share it between swaps.
You can write something like:
```
forecast_handle = ql.RelinkableYieldTermStructureHandle()
discount_handle = ql.RelinkableYieldTermStructureHandle()
index_6M = ql.SomeIndex(..., forecast_handle)
engine = ql.DiscountingSwapEngine(discount_handle)
```
and then for every swap:
```
fixed_schedule = ...
float_schedule = ...
swap = ...
swap.setPricingEngine(engine)
```
After this setup phase, for each scenario you can price the swaps with:
```
forecast_handle.linkTo(forecast_curve)
discount_handle.linkTo(discount_curve)
for s in swaps:
s.NPV()
```
You'll still spend time in pricing, and there might be some time spent in notifications between objects, but at least you should save the time spent in building objects. Let me know how this works.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.