Comparing curves, swap values and quote risk across engines

An independent implementation can help assess curve and valuation results when contract conventions and risk inputs are aligned. This study uses ORE/CurveEngine to reconstruct four curves and price four swaps from the historical market dated 11 November 2025.

sensitivity_config.json contains the independent market inputs, and qs_results.json contains recorded Rust outputs from that snapshot. We compare USD SOFR, TermSOFR3m, CLP ICP and USD-collateralized CLP curves, then match swap PVs and sensitivities by financial identifiers.

The contracts use NullCalendar schedules, Actual/360 accrual, USD reporting and spot of 935 CLP per USD. The historical vanilla swaps use period-forward coupons. Credit and funding adjustments are excluded.

Use ORE 1.8.14.1 and CurveEngine source revision d7e000d, which supports the input’s OvernightIborBasis helpers. The published CurveEngine 1.1.6 release lacks that helper. Install the optional dependencies with the commands in the setup guide and keep both JSON files beside the notebook. The calculation reads those local inputs and recorded outputs; it does not run the current Rust binary. Package versions are printed below.

Computational study. Canonical notebook · Companion files and setup. Download this notebook.

This page renders saved outputs; the documentation build does not execute it. Run the canonical notebook from its own directory with the checkout-built binding to reproduce the computation. Each study records its market inputs and numerical assumptions.

import json
from pathlib import Path
import importlib.metadata

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import ORE as ore
from curveengine import CurveEngine

directory = Path.cwd()
configuration = json.loads((directory / "sensitivity_config.json").read_text())
recorded = json.loads((directory / "qs_results.json").read_text())
reference = ore.DateParser.parseISO(configuration["refDate"])
ore.Settings.instance().evaluationDate = reference
engine = CurveEngine(configuration)
curve_names = ["SOFR", "TermSOFR3m", "ICP", "Collateral(CLP/USD)"]
curves = {name: engine.getCurve(name) for name in curve_names}
indices = {name: engine.getIndex(name) for name in curve_names}
recorded_products = {product["label"]: product for product in recorded["products"]}
print("Historical reference date:", reference.ISO())
print("ORE version:", getattr(ore, "__version__", "not exposed"))
try:
    print("CurveEngine version:", importlib.metadata.version("curveengine"))
except importlib.metadata.PackageNotFoundError:
    print("CurveEngine version: source checkout, no installed distribution metadata")
Historical reference date: 2025-11-11
ORE version: 1.8.14.1
CurveEngine version: 1.1.6

Comparing curves at common dates

We query the independent curves at the recorded Rust node dates. Aligning by date allows each engine to retain its own internal node representation. The signed discount-factor residuals show the resulting construction differences across maturities.

curve_rows = []
for reference_curve in recorded["curves"]:
    name = reference_curve["name"]
    for node in reference_curve["nodes"]:
        date = ore.DateParser.parseISO(node["date"])
        independent_df = curves[name].discount(date)
        curve_rows.append(
            {
                "curve": name,
                "date": node["date"],
                "recorded_df": node["discount_factor"],
                "independent_df": independent_df,
                "signed_difference": independent_df - node["discount_factor"],
            }
        )
curve_comparison = pd.DataFrame(curve_rows)
curve_comparison.groupby("curve")["signed_difference"].agg(["min", "max"]).assign(
    max_absolute_difference=lambda frame: frame.abs().max(axis=1)
)
min max max_absolute_difference
curve
Collateral(CLP/USD) -9.586776e-14 5.928591e-14 9.586776e-14
ICP -1.550982e-13 5.928591e-14 1.550982e-13
SOFR -7.505108e-14 1.508793e-13 1.508793e-13
TermSOFR3m -4.962697e-14 1.996736e-13 1.996736e-13
figure, axes = plt.subplots(1, 2, figsize=(11, 4))
for name, frame in curve_comparison.groupby("curve"):
    dates = pd.to_datetime(frame["date"])
    axes[0].plot(dates, frame["independent_df"], marker=".", label=name)
    axes[1].plot(dates, frame["signed_difference"].abs(), marker=".", label=name)
axes[0].set(
    title="Independently constructed discount factors", ylabel="Discount factor"
)
axes[1].set(
    title="Absolute DF difference against recorded Rust", ylabel="Absolute difference"
)
for axis in axes:
    axis.set_xlabel("Maturity date")
    axis.legend(fontsize=8)
figure.autofmt_xdate()
figure.tight_layout()
plt.show()
../../../_images/d49bfbbe4fc49a5fe3e4a228a9718ef86a56c0ccc3a24ea045600082931e3ec0.png

Valuing the four positions

For a single-currency receive-fixed swap, \(V=N[K\sum_i\tau_iD_i-\sum_jF_j\delta_jD_j]\). ICP projects CLP coupons, while Collateral(CLP/USD) discounts them under USD collateral. Dividing local CLP PV by 935 expresses it in USD.

The cross-currency swap receives CLP ICP plus 50 bp and pays USD SOFR. We match each valuation to the recorded position by its identifier.

calendar = ore.NullCalendar()
start = calendar.advance(reference, ore.Period("2D"))
maturity = calendar.advance(start, ore.Period("5Y"))
basis = ore.Actual360()


def schedule(frequency):
    return ore.Schedule(
        start,
        maturity,
        ore.Period(frequency),
        calendar,
        ore.Following,
        ore.Following,
        ore.DateGeneration.Forward,
        False,
    )


sofr_handle = ore.YieldTermStructureHandle(curves["SOFR"])
collateral_handle = ore.YieldTermStructureHandle(curves["Collateral(CLP/USD)"])
positions = {}
definitions = [
    ("SOFR OIS 5Y Swap", "SOFR", 10_000_000, 0.0378, sofr_handle, 1.0),
    ("TermSOFR3m 5Y Swap", "TermSOFR3m", 10_000_000, 0.04, sofr_handle, 1.0),
    (
        "CLP ICP OIS 5Y Swap (USD collateral)",
        "ICP",
        5_000_000_000,
        0.0440,
        collateral_handle,
        1 / 935,
    ),
]
for label, index, notional, coupon, discount_handle, usd_conversion in definitions:
    swap = ore.VanillaSwap(
        ore.VanillaSwap.Receiver,
        notional,
        schedule(ore.Semiannual),
        coupon,
        basis,
        schedule(ore.Quarterly),
        indices[index],
        0.0,
        basis,
    )
    swap.setPricingEngine(ore.DiscountingSwapEngine(discount_handle))
    positions[label] = {"instrument": swap, "usd_conversion": usd_conversion}

spot_clp_per_usd = 935.0
fx_clp_to_usd = ore.SimpleQuote(1 / spot_clp_per_usd)
xccy = ore.CrossCcyBasisSwap(
    10_000_000,
    ore.USDCurrency(),
    schedule(ore.Quarterly),
    indices["SOFR"],
    0.0,
    1.0,
    10_000_000 * spot_clp_per_usd,
    ore.CLPCurrency(),
    schedule(ore.Quarterly),
    indices["ICP"],
    0.005,
    1.0,
)
xccy.setPricingEngine(
    ore.CrossCcySwapEngine(
        ore.USDCurrency(),
        sofr_handle,
        ore.CLPCurrency(),
        collateral_handle,
        ore.QuoteHandle(fx_clp_to_usd),
    )
)
xccy_label = "Cross-Currency Swap CLP/USD 5Y (receive CLP ICP, pay USD SOFR)"
positions[xccy_label] = {"instrument": xccy, "usd_conversion": 1.0}


def position_pv_usd(label):
    position = positions[label]
    return position["instrument"].NPV() * position["usd_conversion"]


valuation = pd.DataFrame(
    [
        {
            "position": label,
            "independent_pv_usd": position_pv_usd(label),
            "recorded_pv_usd": recorded_products[label]["npv"],
        }
        for label in positions
    ]
).set_index("position")
valuation["difference_usd"] = (
    valuation["independent_pv_usd"] - valuation["recorded_pv_usd"]
)
valuation
independent_pv_usd recorded_pv_usd difference_usd
position
SOFR OIS 5Y Swap 342.595511 342.595511 -7.479684e-09
TermSOFR3m 5Y Swap 88754.684511 88754.684511 -1.411536e-09
CLP ICP OIS 5Y Swap (USD collateral) 4.440637 4.440638 -8.615045e-07
Cross-Currency Swap CLP/USD 5Y (receive CLP ICP, pay USD SOFR) 158661.258109 158661.258108 1.702545e-06

Matching risk inputs and units

Calibration-helper metadata identifies the quote type and tenor. We use it to construct labels matching the recorded Rust sensitivities. Rate and spread quotes are decimals; FX-forward points are CLP per USD.

For quote \(q\), central revaluation estimates \(\partial V/\partial q\approx[V(q+h)-V(q-h)]/(2h)\). We use \(h=10^{-4}\) for rates and \(h=10^{-3}\) CLP per USD for forward points, then report P&L for +1 bp or +1 CLP per USD respectively. A finally block restores every quote after its bump.

quote_book = []
for specification in configuration["curves"]:
    name = specification["curveName"]
    helpers = specification["curveConfig"]["rateHelpers"]
    native_quotes = engine.getQuotes(name)
    for helper, native_quote in zip(helpers, native_quotes):
        kind = helper["helperType"]
        tenor = helper["helperConfig"]["tenor"]
        if kind == "FxSwap":
            label = f"FxForward_USDCLP_{tenor}_AnchorForwardPoints"
            quote = native_quote["fxPoints"]
            family, bump, report_move = "FX forward +1 CLP per USD", 1e-3, 1.0
        else:
            if kind == "Deposit":
                currency = "CLP" if name == "ICP" else "USD"
                label = f"FixedRateDeposit_{currency}_{name}_{tenor}"
            elif kind == "OIS":
                currency = "CLP" if name == "ICP" else "USD"
                label = f"OIS_{currency}_{name}_{tenor}"
            elif kind == "OvernightIborBasis":
                label = f"BasisSwap_USD_SOFR_TermSOFR3m_{tenor}"
            elif kind == "Xccy":
                label = f"FixFloatCrossCurrencySwap_CLP_SOFR_USD_{tenor}"
            else:
                raise ValueError(f"Unmapped calibration helper: {kind}")
            quote = (
                native_quote["spread"]
                if kind == "OvernightIborBasis"
                else native_quote["rate"]
            )
            family, bump, report_move = "rate/spread +1 bp", 1e-4, 1e-4
        quote_book.append(
            {
                "pillar": label,
                "quote": quote,
                "family": family,
                "difference_step": bump,
                "report_move": report_move,
            }
        )
pd.DataFrame(quote_book).drop(columns="quote").groupby("family").size().rename(
    "quote_count"
)
family
FX forward +1 CLP per USD     3
rate/spread +1 bp            42
Name: quote_count, dtype: int64
risk_rows = []
for label in positions:
    recorded_risk = {
        row["pillar"]: row["exposure"]
        for row in recorded_products[label]["sensitivities"]
    }
    for item in quote_book:
        if item["pillar"] not in recorded_risk:
            continue
        quote, step = item["quote"], item["difference_step"]
        original = quote.value()
        try:
            quote.setValue(original + step)
            up = position_pv_usd(label)
            quote.setValue(original - step)
            down = position_pv_usd(label)
        finally:
            quote.setValue(original)
        derivative = (up - down) / (2 * step)
        risk_rows.append(
            {
                "position": label,
                "pillar": item["pillar"],
                "family": item["family"],
                "independent_pnl_usd": derivative * item["report_move"],
                "recorded_pnl_usd": recorded_risk[item["pillar"]] * item["report_move"],
            }
        )
risk_comparison = pd.DataFrame(risk_rows)
risk_comparison["difference_usd"] = (
    risk_comparison["independent_pnl_usd"] - risk_comparison["recorded_pnl_usd"]
)
risk_comparison.groupby(["position", "family"])[
    ["independent_pnl_usd", "recorded_pnl_usd", "difference_usd"]
].sum()
independent_pnl_usd recorded_pnl_usd difference_usd
position family
CLP ICP OIS 5Y Swap (USD collateral) FX forward +1 CLP per USD 34.205693 34.205664 0.000029
rate/spread +1 bp -2388.545942 -2388.545933 -0.000009
Cross-Currency Swap CLP/USD 5Y (receive CLP ICP, pay USD SOFR) FX forward +1 CLP per USD 629.037427 629.037376 0.000051
rate/spread +1 bp -37.644554 -37.644537 -0.000017
SOFR OIS 5Y Swap rate/spread +1 bp -4567.243079 -4567.243071 -0.000008
TermSOFR3m 5Y Swap rate/spread +1 bp -9185.547780 -9185.547764 -0.000016

FX spot orientation and propagation

The recorded USD/CLP pillar is spot in CLP per USD. ORE’s reporting conversion uses the reciprocal. We bump USD/CLP spot, update the collateral-helper inputs and set the conversion quote to its reciprocal, with contractual notionals fixed. The resulting derivative is USD per one CLP-per-USD spot move.

The recorded FX derivative equals the conversion-rescaling expression \(-V/S\). The independent experiment additionally propagates the spot move through collateral curves and the cross-currency engine. The table exposes this risk-definition discrepancy separately from the curve, PV and rate-risk comparisons.

spot_inputs = [
    quote["fxSpot"]
    for specification in configuration["curves"]
    for quote in engine.getQuotes(specification["curveName"])
    if "fxSpot" in quote
]
original_spots = [quote.value() for quote in spot_inputs]
spot_rows = []
for label in [definitions[2][0], xccy_label]:
    original_conversion = positions[label]["usd_conversion"]
    prices = {}
    try:
        for step in [-0.01, 0.01]:
            shocked_spot = spot_clp_per_usd + step
            for quote in spot_inputs:
                quote.setValue(shocked_spot)
            fx_clp_to_usd.setValue(1 / shocked_spot)
            if label == definitions[2][0]:
                positions[label]["usd_conversion"] = 1 / shocked_spot
            prices[step] = position_pv_usd(label)
    finally:
        for quote, value in zip(spot_inputs, original_spots):
            quote.setValue(value)
        fx_clp_to_usd.setValue(1 / spot_clp_per_usd)
        positions[label]["usd_conversion"] = original_conversion
    independent = (prices[0.01] - prices[-0.01]) / 0.02
    recorded_derivative = next(
        row["exposure"]
        for row in recorded_products[label]["sensitivities"]
        if row["pillar"] == "USD/CLP"
    )
    spot_rows.append(
        {
            "position": label,
            "independent_usd_per_1_clp_per_usd": independent,
            "recorded_usd_per_1_clp_per_usd": recorded_derivative,
            "difference": independent - recorded_derivative,
        }
    )
spot_comparison = pd.DataFrame(spot_rows).set_index("position")
# The historical oracle records a conversion-rescaling FX derivative.
rescaling = -valuation.loc[spot_comparison.index, "recorded_pv_usd"] / spot_clp_per_usd
spot_comparison["simple_rescaling_derivative"] = rescaling
spot_comparison["full_spot_risk_matches_record"] = np.isclose(
    spot_comparison.iloc[:, 0], spot_comparison.iloc[:, 1], rtol=1e-5, atol=1e-5
)
spot_comparison
independent_usd_per_1_clp_per_usd recorded_usd_per_1_clp_per_usd difference simple_rescaling_derivative full_spot_risk_matches_record
position
CLP ICP OIS 5Y Swap (USD collateral) 0.084539 -0.004749 0.089288 -0.004749 False
Cross-Currency Swap CLP/USD 5Y (receive CLP ICP, pay USD SOFR) -170.849614 -169.691185 -1.158429 -169.691185 False
sofr_risk = risk_comparison.loc[
    risk_comparison["position"] == "SOFR OIS 5Y Swap"
].set_index("pillar")
sofr_risk = sofr_risk.loc[
    sofr_risk[["independent_pnl_usd", "recorded_pnl_usd"]].abs().max(axis=1) > 0.01
]
axis = sofr_risk[["independent_pnl_usd", "recorded_pnl_usd"]].plot.barh(figsize=(9, 4))
axis.set(
    title="SOFR swap quote risk: independent vs recorded Rust",
    xlabel="USD P&L for +1 bp",
    ylabel="",
)
plt.tight_layout()
plt.show()
print(f"Maximum PV difference: USD {valuation['difference_usd'].abs().max():.6g}.")
print(
    f"Maximum rate/forward-point scaled-risk difference: USD {risk_comparison['difference_usd'].abs().max():.6g}."
)
../../../_images/2bf1e84e4db315cce699cb8238984223a9b9c0fa5b641793af0d6233af38542f.png
Maximum PV difference: USD 1.70254e-06.
Maximum rate/forward-point scaled-risk difference: USD 0.000105289.

The comparison uses explicit tolerances for curves, PVs and scaled quote risks. Restoring bumped inputs reproduces the independent base values. The FX table retains the discrepancy between recorded conversion-rescaling risk and the propagated market-spot shock; resolving it requires reviewing the historical FX-risk calculation and its conventions.

These results apply to the recorded 2025 snapshot. For derivatives and revaluation using the current binding, see the portfolio-risk study.