Pricing
Price interest rate instruments against calibrated curves using .rate(),
.npv(), and .cashflows(). This guide covers
IRS, FRA, ZCS, OIS, and Deposit pricing against a
DiscountCurve calibrated from USD SOFR market
data.
Setup -- Calibrate a Curve
Calibrate the shared USD SOFR dataset to produce a discount curve for pricing.
import datetime
from vade import DiscountCurve, IRS, FRA, ZCS, OIS, Deposit, Solver
effective = datetime.date(2025, 6, 16)
nodes = {
effective: 1.0,
datetime.date(2025, 7, 16): 1.0,
datetime.date(2025, 9, 16): 1.0,
datetime.date(2025, 12, 16): 1.0,
datetime.date(2026, 6, 16): 1.0,
datetime.date(2027, 6, 16): 1.0,
datetime.date(2028, 6, 16): 1.0,
datetime.date(2030, 6, 16): 1.0,
datetime.date(2032, 6, 16): 1.0,
datetime.date(2035, 6, 16): 1.0,
}
curve = DiscountCurve(
nodes, interpolation="log_linear", convention="act360", id="sofr"
)
dep_1m = Deposit(effective=effective, termination="1m", rate=0.0, convention="act360")
fra_3m = FRA(effective=effective, termination="3m", fixed_rate=0.0, convention="act360")
fra_6m = FRA(effective=effective, termination="6m", fixed_rate=0.0, convention="act360")
irs_1y = IRS(effective=effective, termination="1y", frequency="a", fixed_rate=0.0, convention="act360", float_convention="act360")
irs_2y = IRS(effective=effective, termination="2y", frequency="a", fixed_rate=0.0, convention="act360", float_convention="act360")
irs_3y = IRS(effective=effective, termination="3y", frequency="a", fixed_rate=0.0, convention="act360", float_convention="act360")
irs_5y = IRS(effective=effective, termination="5y", frequency="a", fixed_rate=0.0, convention="act360", float_convention="act360")
irs_7y = IRS(effective=effective, termination="7y", frequency="a", fixed_rate=0.0, convention="act360", float_convention="act360")
irs_10y = IRS(effective=effective, termination="10y", frequency="a", fixed_rate=0.0, convention="act360", float_convention="act360")
instruments = [
(dep_1m, 0.0430), (fra_3m, 0.0425), (fra_6m, 0.0415),
(irs_1y, 4.00), (irs_2y, 3.85), (irs_3y, 3.75),
(irs_5y, 3.70), (irs_7y, 3.75), (irs_10y, 3.85),
]
solver = Solver(curves=[curve], instruments=instruments)
result = solver.iterate()
assert result.converged
calibrated = solver.get_curve(0)IRS Pricing
Price a 5-year interest rate swap with a slightly off-market fixed rate. The
.rate() method returns the par rate implied by the curve (in percentage form),
and .npv() shows the mark-to-market value.
irs = IRS(
effective=effective,
termination="5y",
frequency="a",
fixed_rate=3.80,
convention="act360",
float_convention="act360",
)
float(irs.rate(calibrated)) # 3.7000000000002484
float(irs.npv(calibrated)) # -4542.843986271881The par rate is 3.70% -- the curve-implied fair fixed rate. Because our swap pays 3.80% fixed (above par), its NPV is negative from the fixed-rate payer's perspective.
FRA Pricing
Price a forward rate agreement covering the 3-month to 6-month period.
FRA .rate() returns the implied forward rate in
decimal form.
fra = FRA(
effective=datetime.date(2025, 9, 16),
termination=datetime.date(2025, 12, 16),
fixed_rate=4.20,
convention="act360",
)
float(fra.rate(calibrated)) # 0.040488991946056214
float(fra.npv(calibrated)) # -323.57645004154983ZCS Pricing
Price a 3-year zero coupon swap. ZCS .rate()
returns the par zero-coupon rate in percentage form.
zcs = ZCS(
effective=effective,
termination="3y",
fixed_rate=3.80,
convention="act360",
float_convention="act360",
)
float(zcs.rate(calibrated)) # 7.777073194074961
float(zcs.npv(calibrated)) # -1666.5413047417533OIS Pricing
Price a 2-year overnight index swap. OIS is structurally similar to IRS but designed for overnight rate compounding.
ois = OIS(
effective=effective,
termination="2y",
frequency="a",
fixed_rate=3.90,
convention="act360",
float_convention="act360",
)
float(ois.rate(calibrated)) # 3.8500000000003856
float(ois.npv(calibrated)) # -956.7775507331098Deposit Pricing
Price a 1-month deposit. Deposit .rate()
returns the implied deposit rate in decimal form.
dep = Deposit(
effective=effective,
termination="1m",
rate=0.0440,
convention="act360",
)
float(dep.rate(calibrated)) # 0.04299999999999926
float(dep.npv(calibrated)) # -83.03578842494552Cashflow Analysis
The .cashflows() method returns a Polars DataFrame
with period-level detail for each leg of the instrument. Each row represents
one accrual period with its notional, rate, cashflow amount, discount factor,
and NPV contribution.
irs = IRS(
effective=effective,
termination="5y",
frequency="a",
fixed_rate=3.80,
convention="act360",
float_convention="act360",
)
df = irs.cashflows(calibrated)
df.columns # ['Type', 'start', 'end', 'payment', 'Ccy', 'Notional', 'fixing_rate', 'Rate', 'Spread', 'DCF', 'Cashflow', 'DF', 'NPV']
df.shape # (14, 13)Next Steps
- Calibration -- multi-curve calibration and solver algorithms
- Risk -- delta, gamma, and bucket-level sensitivities
Bootstrap & Parametric Curves
Use [bootstrap](../../api/solver.md#bootstrap) for node-by-node curve stripping
Risk
Compute delta, gamma, and bucket-level risk sensitivities from calibrated curves using [automatic differentiation](../../getting-started/type-system.md). The [Solver](../../api/solver.md#solver) provides AD-based delta and gamma, while [IRImpliedCurve](../../api/curves.md#irimpliedcurve) offers tenor-bucketed risk reporting.