You implement a swap pricer in QuantLib. The schedule looks right. The market quotes came from the same screen. Your par rate is close. And yet the NPV still does not match Bloomberg.
The instinctive reaction is usually: there must be a bug in the pricing formula. In practice, that is often the wrong place to start.
A pricing system is not just a formula. It is a stack of choices: valuation date, settlement rules, calendars, day counts, fixing treatment, curve instruments, bootstrapping order, interpolation space, discounting curve, projection curve, quote conventions, and numerical tolerances. Two systems can implement the same financial model and still disagree because they are solving slightly different specifications.
This article gives a desk-style reconciliation framework for diagnosing that disagreement. The goal is not to reverse-engineer proprietary Bloomberg internals. The goal is to make your own pricer explicit enough that, when two numbers differ, you can isolate the exact layer responsible.
1. The key idea: reconcile objects, not headline NPVs
Suppose your engine returns a present value PV_Q and the reference system returns PV_B. Looking only at
tells you almost nothing about the cause. A 5 bp difference could come from one wrong holiday, a different fixing, a projection-curve interpolation choice, or a numerical integration setting.
The correct approach is hierarchical. Reconcile the lowest-level objects first and move upward only after each layer matches.
| Layer | What to reconcile | Typical failure signal |
|---|---|---|
| 0. Snapshot | Valuation timestamp, market-data timestamp, trade state | Everything is slightly off |
| 1. Contract | Effective date, maturity, notional, coupon, pay/receive, conventions | Different economics before pricing starts |
| 2. Schedule | Accrual dates, payment dates, fixing dates, stubs, lags | Cashflow dates differ |
| 3. Fixings | Historical and same-day fixings, fallback rules | One or a few coupons differ sharply |
| 4. Curves | Discount curve, projection curve, bootstrap instruments | Forward rates or discount factors differ |
| 5. Interpolation | Discount, zero, or forward interpolation and extrapolation | Differences concentrate between pillars |
| 6. Cashflows | Coupon rates, accrual factors, amounts, discount factors | Leg-level PV mismatch becomes identifiable |
| 7. Model/numerics | Vol surface, model parameters, solver, integration, tolerances | Inputs match but price still differs |
Do not start at layer 7. In production debugging, most wasted time comes from investigating model mathematics before proving that the two systems are pricing the same contractual cashflows with the same market state.
2. Freeze the snapshot before comparing anything
Before debugging the model, make the comparison reproducible. Record:
- valuation date and exact market-data timestamp;
- trade effective date, maturity, notional, fixed rate/spread and side;
- all historical fixings used by the trade;
- the exact curve quotes and their timestamps;
- the reference system settings used for the valuation;
- whether the trade is clean, dirty, settled, partially fixed, amended, or forward-starting.
This sounds administrative, but it is quantitative discipline. If one system uses a 10:00 curve and another uses a 10:03 curve, the discrepancy may be real market movement, not model error.
3. Reconcile the contract specification
For a vanilla fixed-vs-floating interest-rate swap, the fixed-leg PV is approximately
where N is notional, K is the fixed coupon, alpha_i is the fixed-leg accrual fraction, D(0,T_i) is the discount factor, and s is the pay/receive sign.
The floating leg is
with beta_j the floating accrual fraction and F_j the applicable fixing or forward rate.
That formula is simple. The specification behind the symbols is not. You must match:
- fixed and floating frequencies;
- business-day convention;
- holiday calendar;
- day-count convention on each leg;
- date-generation rule;
- end-of-month handling;
- spot lag and payment lag;
- stub convention;
- index tenor and fixing lag.
A one-day shift can alter both the accrual factor and the discounting date. A different stub can alter an entire coupon. A different calendar can move multiple payment dates. The model can be perfectly coded and still produce the wrong number because the contract was instantiated differently.
4. Reconcile schedules before rates
The fastest test is brutally simple: export the full schedule from both systems and compare row by row.
| Field | Your engine | Reference | Match? |
|---|---|---|---|
| Accrual start | ... | ... | Yes/No |
| Accrual end | ... | ... | Yes/No |
| Payment date | ... | ... | Yes/No |
| Fixing date | ... | ... | Yes/No |
| Accrual fraction | ... | ... | Yes/No |
If dates differ, stop. Do not inspect interpolation yet. A curve cannot explain a contractual schedule mismatch.
Day-count conventions deserve special care. A convention designed for coupon accrual is not automatically a good time coordinate for a generic term structure. QuantLib's own practitioner documentation demonstrates that some day-count choices can behave unexpectedly when used to convert arbitrary dates into curve times. Treat the day counter used by the instrument and the one used internally by the term structure as separate modelling decisions.
5. Fixings can change more than one coupon
A common debugging mistake is to think historical fixings matter only for the coupon that directly consumes them. In a bootstrapped framework, a fixing can also affect the curve calibration itself.
Why? If quoted calibration instruments contain a coupon whose fixing is now known, their projected cashflows change. The bootstrapped curve must then move so those calibration instruments still reproduce their market quotes.
This creates a non-obvious chain:
new fixing
-> calibration-instrument cashflow changes
-> bootstrap recalculates
-> forward curve changes
-> future projected coupons change
-> trade NPV changes
Therefore, when one system contains today's fixing and another forecasts it from the curve, you can see a difference even in trades that do not directly consume that fixing in the obvious place.
When reconciling, explicitly compare:
- all past fixings;
- today's fixing treatment;
- whether same-day fixing is considered known or forecast;
- the timestamp at which the fixing became available;
- fallback or synthetic-index rules, where relevant.
6. The most important rates concept: discount and projection are different jobs
Post-crisis interest-rate pricing is generally a multi-curve problem. The curve used to discount cashflows is not necessarily the same curve used to project floating coupons.
For an Ibor-style coupon, a simple forward can be written as
where P_f comes from the projection curve and delta is the index accrual fraction. The resulting cashflow is then discounted with the appropriate discount curve D.
This separation matters enormously in debugging.
If your cashflow amount already differs before discounting, the first suspect is the projection side: fixings, forward curve, index conventions, or interpolation. If cashflow amounts match but present values differ, the first suspect is the discount curve.
That distinction alone can cut a debugging session from hours to minutes.
7. Bootstrap order matters
A classic reconciliation error is to build a projection curve as though it were self-contained, when the market instruments used to calibrate it are themselves valued using an already-built discount curve.
A robust workflow is:
1. Build the collateral / OIS discount curve.
2. Hold that discount curve fixed.
3. Build the projection curve using instruments valued with that discount curve.
4. Verify every bootstrap instrument reprices to its market quote.
5. Only then price the target trade.
This is often called curve stacking. The conceptual point is more important than the name: calibration instruments must be valued using the same discounting architecture assumed by the market quotes.
A strong invariant is:
for par instruments, within a deliberately chosen numerical tolerance. If the instruments used to construct your curve do not reprice, debugging the target swap is premature.
8. Interpolation is a pricing assumption, not a charting preference
Suppose two adjacent curve pillars match perfectly in both systems. You may still get different forward rates between them because the systems interpolate different objects.
Common choices include:
- log-linear interpolation of discount factors;
- linear or cubic interpolation of zero rates;
- linear interpolation of instantaneous or simple forwards;
- monotone cubic variants designed to avoid oscillatory forwards.
These choices can produce nearly identical-looking zero curves while producing materially different local forward rates.
This is especially visible when:
- the curve is steep;
- the target fixing lies between sparse pillars;
- the trade is forward-starting;
- FRAs/futures bridge the short end;
- the interpolation method changes the slope sharply near a node.
If most coupons match and one interpolated reset differs significantly, do not rebuild the whole model. Zoom in on the two curve pillars surrounding that fixing period and compare the forward calculation under each interpolation rule.
9. Decompose the PV error at cashflow level
For one cashflow, let the reference amount and discount factor be CF_B and D_B, while your engine gives CF_Q and D_Q. Then
An exact decomposition is
This is a useful debugging identity because it separates the discrepancy into:
- cashflow effect — usually projection, fixing, accrual, notional, or coupon logic;
- discounting effect — usually discount curve, payment date, or compounding;
- interaction effect — usually small, but exact accounting keeps the reconciliation honest.
Aggregate these terms across the leg. Now the headline NPV difference becomes an explainable sum rather than a mystery.
10. A practical worked diagnosis
Consider a 10-year swap where:
- fixed-leg dates match the reference;
- fixed coupon amounts match;
- floating-leg dates match;
- most floating coupon amounts match;
- one forward coupon differs materially;
- the final NPV is therefore different.
What should you conclude?
First: the contract schedule is probably not the main problem, because the dates match.
Second: discounting cannot be the primary cause of the differing coupon amount, because discount factors affect PV, not the undiscounted floating coupon itself.
Third: inspect the fixing/forward used for that coupon. Determine whether it is historical, same-day, or projected.
Fourth: if projected, inspect the two projection-curve pillars surrounding the reset interval and calculate the forward manually.
Fifth: run the same curve through alternative interpolation methods. If the discrepant reset moves while already-matching coupons remain stable, you have localized the source.
Sixth: only after the floating amount matches should you reconcile discount factors and total PV.
This is much more reliable than changing five curve settings at once and hoping the final NPV gets closer.
11. QuantLib diagnostic code: export the objects that matter
For a floating leg, create a table containing the coupon mechanics and discounting inputs. The exact API may vary slightly by QuantLib version, but the diagnostic pattern is stable:
import pandas as pd
import QuantLib as ql
def floating_leg_table(swap, discount_curve):
rows = []
for cf in swap.floatingLeg():
c = ql.as_floating_rate_coupon(cf)
pay_date = c.date()
rows.append({
"accrual_start": c.accrualStartDate().ISO(),
"accrual_end": c.accrualEndDate().ISO(),
"fixing_date": c.fixingDate().ISO(),
"pay_date": pay_date.ISO(),
"accrual_fraction": c.accrualPeriod(),
"rate": c.rate(),
"amount": c.amount(),
"discount_factor": discount_curve.discount(pay_date),
"pv_contribution": c.amount() * discount_curve.discount(pay_date),
})
return pd.DataFrame(rows)
Do the same for the fixed leg. Export the reference system's schedule/cashflows if available. Then merge the two tables by payment date and compare columns.
The important debugging order is:
dates
-> accrual fractions
-> fixing dates
-> coupon rates
-> coupon amounts
-> discount factors
-> PV contributions
-> total NPV
At each stage, the first mismatch tells you where to investigate next.
12. What if all cashflows and curves match?
Only now should you move to model and numerical settings.
For vanilla swaps, there may be little left. For options and exotics, however, you still need to reconcile:
- forward construction and dividend treatment;
- volatility quote convention and surface interpolation;
- sticky-strike vs sticky-delta assumptions for scenarios;
- model parameterization;
- integration domain and quadrature method;
- PDE grid size and boundary conditions;
- Monte Carlo seed, time discretization and variance reduction;
- root-finding tolerance for implied volatility;
- rounding and reporting conventions.
Numerical tolerance should be the last explanation, not the first. Increasing solver precision cannot repair a wrong calendar or a wrong projection curve.
13. Build an error budget instead of asking for an exact match
Production reconciliation needs a tolerance policy. “The prices should be identical” is not always meaningful when two systems use different numerical representations.
A better framework is to define an error budget:
| Source | Diagnostic | Expected treatment |
|---|---|---|
| Market-data timestamp | Revalue on frozen snapshot | Must match before testing |
| Contract conventions | Schedule diff | Must match exactly |
| Bootstrap repricing | Par-instrument residuals | Near numerical tolerance |
| Interpolation | Compare forwards between pillars | Documented methodology difference |
| Numerical solver | Convergence study | Residual shrinks under refinement |
| Reporting/rounding | Compare full precision | Ignore only after quantified |
For any material unexplained difference, ask whether it is:
specification risk
market-data risk
curve-construction risk
model risk
numerical risk
reporting risk
That classification turns a one-off debugging exercise into a reusable control framework.
14. A 12-check reconciliation ladder
- Freeze valuation and market-data timestamps.
- Confirm pay/receive sign, notional, rate/spread, effective date and maturity.
- Match calendars, business-day rules, spot lag and payment lag.
- Match fixed and floating day-count conventions.
- Compare every accrual, fixing and payment date.
- Load identical historical and same-day fixings.
- Verify the discount curve independently.
- Build projection curves using the correct discounting architecture.
- Reprice every curve calibration instrument.
- Compare forwards between curve pillars under the chosen interpolation.
- Compare undiscounted cashflows, then discount factors, then PV contributions.
- Only then investigate model parameters and numerical tolerances.
If you follow this order, “QuantLib vs Bloomberg” stops being a vague software problem and becomes a deterministic reconciliation exercise.
15. The interview version
If an interviewer asks, “Your swap price differs from Bloomberg. What do you do?”, a strong answer is not “I would change the interpolation until it matches.”
A better answer is:
I would reconcile from the bottom up: first freeze the same market snapshot and contract conventions, then compare schedules and fixings, then separate projection from discounting curves, verify that bootstrap instruments reprice, compare interpolation-driven forwards, and finally compare cashflow amounts and discount factors. Only if those inputs match would I inspect model and numerical settings.
That answer signals something more valuable than library familiarity: you understand that a pricing engine is a controlled chain of financial specifications.
16. Why this problem matters beyond QuantLib
The framework applies equally to an in-house C++ library, a Python prototype, a vendor risk system, or an independent model-validation implementation.
Two implementations do not validate each other merely because they produce close NPVs. A proper comparison asks whether they agree for the right reasons. The strongest validation artifact is therefore not a screenshot showing two matching numbers. It is a reconciliation table showing that schedules, fixings, forwards, discount factors and cashflows match independently.
That is how you turn pricing from “my number versus your number” into auditable quantitative engineering.
Sources and further reading
- Quant StackExchange: Replicating Bloomberg Swap Prices with QuantLib — a public example of a dual-curve reconciliation problem.
- Quant StackExchange: QuantLib mismatch with BBG Swap — an example where floating cashflows and forwards are the key diagnostic.
- Quant StackExchange: FRA/interpolated-rate mismatch — illustrates how curve interpolation can materially change a projected reset.
- A QuantLib Guide: Dangerous day-count conventions — why coupon day counts and term-structure time coordinates should not be conflated.
- A QuantLib Guide: The effect of today's fixing — shows how a fixing can feed back into a bootstrapped forecast curve.
Continue on Desk2Quant
If you want to go deeper, the Numerical Methods for Quants resource covers curve construction, interpolation, numerical stability and calibration, while the Derivatives Products & Pricing Master Pack connects those numerical choices to market conventions across asset classes.
Educational content only. Bloomberg is referenced as a widely used market-data and analytics platform; Desk2Quant is not affiliated with Bloomberg Finance L.P.
