API Reference

Instruments

Interest rate derivatives, bonds, credit, and FX instruments for pricing and calibration.

All types are available via flat import using short names:

from vade import IRS, FRA, ZCS, SBS, OIS, Deposit, IRFuture, CapFloor
from vade import FixedRateBond, Bill, FloatRateNote
from vade import ZeroCouponBond, StepUpBond, AmortizingBond, PIKBond
from vade import SubPeriodFRN
from vade import CappedFloatRateNote, AssetSwap, CallableBond
from vade import CDS, XCS, FXForward
from vade import NDF, NDIRS, NDXCS

See Conventions for all accepted string parameter values.

Contents:

Interest Rate Derivatives: IRS | FRA | ZCS | SBS | OIS | Deposit | IRFuture | CapFloor

Bonds: FixedRateBond | Bill | FloatRateNote | SubPeriodFRN | ZeroCouponBond | StepUpBond | AmortizingBond | PIKBond | CappedFloatRateNote | AssetSwap | CallableBond

Credit & FX: CDS | XCS | FXForward

Non-Deliverable Instruments: NDF | NDIRS | NDXCS


IRS

Interest Rate Swap: fixed vs floating leg. Python wrapper composing FixedLeg + FloatLeg.

See also: Pricing Guide for rates instrument pricing workflows.

Alias: IRS = InterestRateSwap

Constructor

IRS(
    *,
    spec=None,
    effective=None,
    termination=None,
    frequency="a",
    fixed_rate=0.0,
    notional=1_000_000.0,
    convention="act360",
    float_convention=None,
    fixing_method="rfr_payment_delay",
    spread=0.0,
    calendar=None,
    payment_lag=None,
    currency="USD",
    amortization=None,
    fixed_amortization=None,
    float_amortization=None,
    modifier="mf",
    stub="shortfront",
    disc_curve_id=None,
    forecast_curve_id=None,
)

Parameters

NameTypeDefaultDescription
specstr or NoneNoneInstrument spec name (overrides individual params)
effectivedatetime.date or NoneNoneStart date of the swap
terminationdate, str, or NoneNoneEnd date or tenor string (e.g., "5Y")
frequencystr"a"Payment frequency for both legs
fixed_ratefloat0.0Fixed leg rate (percentage, e.g., 3.0 for 3%)
notionalfloat1_000_000.0Notional amount
conventionstr"act360"Day count convention for the fixed leg
float_conventionstr or NoneNoneDay count convention for the float leg (defaults to convention)
fixing_methodstr"rfr_payment_delay"Rate fixing method
spreadfloat0.0Spread on the floating leg (basis points)
calendarstr or NoneNoneNamed calendar (e.g., "NYC", "LDN")
payment_lagint or NoneNonePayment lag in business days
currencystr"USD"Currency code
amortizationschedule or NoneNoneAmortization schedule for both legs
fixed_amortizationschedule or NoneNoneAmortization schedule for fixed leg only
float_amortizationschedule or NoneNoneAmortization schedule for float leg only
modifierstr"mf"Business day adjustment rule
stubstr"shortfront"Stub period preference
disc_curve_idstr or NoneNoneDiscount curve identifier for Solver lookup
forecast_curve_idstr or NoneNoneForecast curve identifier for Solver lookup

See Conventions for accepted values for frequency, convention, fixing_method, modifier, and stub.

Methods

MethodReturnsDescription
.rate(curves, *, solver=None)floatPar rate against curves
.npv(curves, *, solver=None)floatNet present value
.cashflows(curves, *, solver=None)DataFramePeriod-level cashflow table
.spread(curves, *, solver=None)floatPar spread

The curves parameter accepts a DiscountCurve, LineCurve, list, dict, or None. When using a Solver, pass the solver and curves are resolved by disc_curve_id/forecast_curve_id.

Example

import datetime
from vade import IRS, DiscountCurve

nodes = {datetime.date(2024, 1, 1): 1.0, datetime.date(2025, 1, 1): 0.97, datetime.date(2026, 1, 1): 0.94}
curve = DiscountCurve(nodes, interpolation="log_linear", convention="act365f")

irs = IRS(
    effective=datetime.date(2024, 1, 1),
    termination="1Y",
    frequency="a",
    fixed_rate=3.0,
    convention="act365f",
)
irs.npv(curve)  # 817.9264117309795
irs.rate(curve)  # 3.084091921661261

FRA

Forward Rate Agreement: single-period forward rate instrument. Python wrapper composing a single FloatPeriod.

Alias: FRA = ForwardRateAgreement

Constructor

FRA(
    *,
    spec=None,
    effective=None,
    termination=None,
    notional=1_000_000.0,
    fixed_rate=0.0,
    convention="act360",
    fixing_method="rfr_payment_delay",
    calendar=None,
    disc_curve_id=None,
    forecast_curve_id=None,
)

Parameters

NameTypeDefaultDescription
specstr or NoneNoneInstrument spec name
effectivedatetime.date or NoneNoneStart date
terminationdate, str, or NoneNoneEnd date or tenor string (e.g., "6M")
notionalfloat1_000_000.0Notional amount
fixed_ratefloat0.0Fixed rate (percentage)
conventionstr"act360"Day count convention
fixing_methodstr"rfr_payment_delay"Rate fixing method
calendarstr or NoneNoneNamed calendar
disc_curve_idstr or NoneNoneDiscount curve identifier for Solver lookup
forecast_curve_idstr or NoneNoneForecast curve identifier for Solver lookup

See Conventions for accepted values for convention and fixing_method.

Methods

MethodReturnsDescription
.rate(curves, *, solver=None)floatPar rate against curves
.npv(curves, *, solver=None)floatNet present value
.cashflows(curves, *, solver=None)DataFramePeriod-level cashflow table
.spread(curves, *, solver=None)floatPar spread

Example

import datetime
from vade import FRA, DiscountCurve

nodes = {datetime.date(2024, 1, 1): 1.0, datetime.date(2024, 7, 1): 0.985, datetime.date(2025, 1, 1): 0.97}
curve = DiscountCurve(nodes, interpolation="log_linear", convention="act360")

fra = FRA(
    effective=datetime.date(2024, 3, 1),
    termination="6M",
    fixed_rate=3.5,
    convention="act360",
)
fra.rate(curve)  # forward rate implied by the curve
fra.npv(curve)  # NPV of the FRA position

ZCS

Zero Coupon Swap: zero-coupon fixed vs floating leg. Python wrapper composing ZeroFixedLeg + ZeroFloatLeg.

Alias: ZCS = ZeroCouponSwap

Constructor

ZCS(
    *,
    spec=None,
    effective=None,
    termination=None,
    fixed_rate=0.0,
    notional=1_000_000.0,
    convention="act360",
    float_convention=None,
    fixing_method="rfr_payment_delay",
    spread=0.0,
    calendar=None,
    currency="USD",
    disc_curve_id=None,
    forecast_curve_id=None,
)

Parameters

NameTypeDefaultDescription
specstr or NoneNoneInstrument spec name
effectivedatetime.date or NoneNoneStart date
terminationdate, str, or NoneNoneEnd date or tenor string
fixed_ratefloat0.0Fixed leg rate (percentage)
notionalfloat1_000_000.0Notional amount
conventionstr"act360"Day count convention for the fixed leg
float_conventionstr or NoneNoneDay count convention for the float leg
fixing_methodstr"rfr_payment_delay"Rate fixing method
spreadfloat0.0Spread on the floating leg (basis points)
calendarstr or NoneNoneNamed calendar
currencystr"USD"Currency code
disc_curve_idstr or NoneNoneDiscount curve identifier for Solver lookup
forecast_curve_idstr or NoneNoneForecast curve identifier for Solver lookup

See Conventions for accepted values for convention and fixing_method.

Methods

MethodReturnsDescription
.rate(curves, *, solver=None)floatPar rate against curves
.npv(curves, *, solver=None)floatNet present value
.cashflows(curves, *, solver=None)DataFramePeriod-level cashflow table
.spread(curves, *, solver=None)floatPar spread

Example

import datetime
from vade import ZCS, DiscountCurve

nodes = {datetime.date(2024, 1, 1): 1.0, datetime.date(2025, 1, 1): 0.97, datetime.date(2026, 1, 1): 0.94}
curve = DiscountCurve(nodes, interpolation="log_linear", convention="act365f")

zcs = ZCS(
    effective=datetime.date(2024, 1, 1),
    termination="2Y",
    fixed_rate=3.0,
    convention="act365f",
)
zcs.rate(curve)  # par zero-coupon rate
zcs.npv(curve)  # NPV of the ZCS position

SBS

Single Basis Swap: two floating legs with different frequencies. Python wrapper composing two FloatLegs.

Alias: SBS = SingleBasisSwap

Constructor

SBS(
    *,
    spec=None,
    effective=None,
    termination=None,
    frequency="q",
    leg2_frequency="s",
    notional=1_000_000.0,
    spread=0.0,
    convention="act360",
    leg2_convention=None,
    fixing_method="rfr_payment_delay",
    leg2_fixing_method=None,
    calendar=None,
    payment_lag=None,
    currency="USD",
    modifier="mf",
    stub="shortfront",
    disc_curve_id=None,
    forecast_curve_id=None,
)

Parameters

NameTypeDefaultDescription
specstr or NoneNoneInstrument spec name
effectivedatetime.date or NoneNoneStart date
terminationdate, str, or NoneNoneEnd date or tenor string
frequencystr"q"Leg 1 payment frequency
leg2_frequencystr"s"Leg 2 payment frequency
notionalfloat1_000_000.0Notional amount
spreadfloat0.0Spread on leg 1 (basis points)
conventionstr"act360"Day count convention for leg 1
leg2_conventionstr or NoneNoneDay count convention for leg 2 (defaults to convention)
fixing_methodstr"rfr_payment_delay"Rate fixing method for leg 1
leg2_fixing_methodstr or NoneNoneRate fixing method for leg 2 (defaults to fixing_method)
calendarstr or NoneNoneNamed calendar
payment_lagint or NoneNonePayment lag in business days
currencystr"USD"Currency code
modifierstr"mf"Business day adjustment rule
stubstr"shortfront"Stub period preference
disc_curve_idstr or NoneNoneDiscount curve identifier for Solver lookup
forecast_curve_idstr or NoneNoneForecast curve identifier for Solver lookup

See Conventions for accepted values for frequency, convention, fixing_method, modifier, and stub.

Methods

MethodReturnsDescription
.rate(curves, *, solver=None)floatPar basis spread
.npv(curves, *, solver=None)floatNet present value
.cashflows(curves, *, solver=None)DataFramePeriod-level cashflow table
.spread(curves, *, solver=None)floatPar spread

Example

import datetime
from vade import SBS, DiscountCurve

nodes = {datetime.date(2024, 1, 1): 1.0, datetime.date(2025, 1, 1): 0.97, datetime.date(2026, 1, 1): 0.94}
curve = DiscountCurve(nodes, interpolation="log_linear", convention="act360")

sbs = SBS(
    effective=datetime.date(2024, 1, 1),
    termination="2Y",
    frequency="q",
    leg2_frequency="s",
    convention="act360",
)
sbs.rate(curve)  # par basis spread between the two tenors
sbs.npv(curve)  # NPV of the basis swap

OIS

Overnight Indexed Swap: fixed vs RFR floating leg with compounding. Python wrapper composing FixedLeg + FloatLeg with RFR compounding.

Alias: OIS = OvernightIndexedSwap

Constructor

OIS(
    *,
    spec=None,
    effective=None,
    termination=None,
    frequency="a",
    fixed_rate=0.0,
    notional=1_000_000.0,
    convention="act360",
    float_convention=None,
    fixing_method="rfr_payment_delay",
    spread=0.0,
    calendar=None,
    payment_lag=None,
    currency="USD",
    amortization=None,
    fixed_amortization=None,
    float_amortization=None,
    compounding_method=None,
    lockout=None,
    lookback=None,
    modifier="mf",
    stub="shortfront",
    disc_curve_id=None,
    forecast_curve_id=None,
)

Parameters

NameTypeDefaultDescription
specstr or NoneNoneInstrument spec name
effectivedatetime.date or NoneNoneStart date
terminationdate, str, or NoneNoneEnd date or tenor string
frequencystr"a"Payment frequency
fixed_ratefloat0.0Fixed leg rate (percentage)
notionalfloat1_000_000.0Notional amount
conventionstr"act360"Day count convention for the fixed leg
float_conventionstr or NoneNoneDay count convention for the float leg
fixing_methodstr"rfr_payment_delay"Rate fixing method
spreadfloat0.0Spread on the floating leg (basis points)
calendarstr or NoneNoneNamed calendar
payment_lagint or NoneNonePayment lag in business days
currencystr"USD"Currency code
amortizationschedule or NoneNoneAmortization schedule for both legs
fixed_amortizationschedule or NoneNoneAmortization schedule for fixed leg only
float_amortizationschedule or NoneNoneAmortization schedule for float leg only
compounding_methodstr or NoneNoneRFR compounding method (e.g., "rfr_obs_shift")
lockoutint or NoneNoneNumber of lockout days before period end
lookbackint or NoneNoneNumber of lookback days for rate observation
modifierstr"mf"Business day adjustment rule
stubstr"shortfront"Stub period preference
disc_curve_idstr or NoneNoneDiscount curve identifier for Solver lookup
forecast_curve_idstr or NoneNoneForecast curve identifier for Solver lookup

See Conventions for accepted values for frequency, convention, fixing_method, modifier, and stub.

Methods

MethodReturnsDescription
.rate(curves, *, solver=None)floatPar rate against curves
.npv(curves, *, solver=None)floatNet present value
.cashflows(curves, *, solver=None)DataFramePeriod-level cashflow table
.spread(curves, *, solver=None)floatPar spread

Example

import datetime
from vade import OIS, DiscountCurve

nodes = {datetime.date(2024, 1, 1): 1.0, datetime.date(2025, 1, 1): 0.97, datetime.date(2026, 1, 1): 0.94}
curve = DiscountCurve(nodes, interpolation="log_linear", convention="act365f")

ois = OIS(
    effective=datetime.date(2024, 1, 1),
    termination="1Y",
    frequency="a",
    fixed_rate=3.0,
    convention="act365f",
)
ois.rate(curve)  # par OIS rate
ois.npv(curve)  # NPV of the OIS position

Deposit

Deposit instrument for short-end curve calibration. Rust-backed.

Constructor

Deposit(
    *,
    effective=None,
    termination=None,
    rate=0.0,
    notional=1_000_000.0,
    convention="act360",
    disc_curve_id=None,
    forecast_curve_id=None,
)

Parameters

NameTypeDefaultDescription
effectivedatetime.date or NoneNoneStart date
terminationdate, str, or NoneNoneEnd date or tenor string (e.g., "3M")
ratefloat0.0Deposit rate (percentage)
notionalfloat1_000_000.0Notional amount
conventionstr"act360"Day count convention
disc_curve_idstr or NoneNoneDiscount curve identifier for Solver lookup
forecast_curve_idstr or NoneNoneForecast curve identifier for Solver lookup

See Conventions for accepted values for convention.

Methods

MethodReturnsDescription
.rate(curves, *, solver=None)floatPar rate against curves
.npv(curves, *, solver=None)floatNet present value
.cashflows(curves, *, solver=None)DataFramePeriod-level cashflow table
.spread(curves, *, solver=None)floatPar spread

Example

import datetime
from vade import Deposit, DiscountCurve

nodes = {datetime.date(2024, 1, 1): 1.0, datetime.date(2024, 4, 1): 0.9925, datetime.date(2024, 7, 1): 0.985}
curve = DiscountCurve(nodes, interpolation="log_linear", convention="act360")

dep = Deposit(
    effective=datetime.date(2024, 1, 1),
    termination="3M",
    rate=3.0,
    convention="act360",
)
dep.rate(curve)  # par deposit rate implied by the curve
dep.npv(curve)  # NPV of the deposit

IRFuture

STIR Futures instrument with convexity adjustment. Rust-backed.

Constructor

IRFuture(
    *,
    effective=None,
    termination=None,
    price=100.0,
    notional=1_000_000.0,
    convexity_adjustment=0.0,
    convention="act360",
    quote_convention="price",
    sigma=None,
    tick_size=None,
    notional_multiplier=None,
    contract_size=None,
    disc_curve_id=None,
    forecast_curve_id=None,
)

Parameters

NameTypeDefaultDescription
effectivedatetime.date or NoneNoneContract start date
terminationdate, str, or NoneNoneContract end date or tenor string
pricefloat100.0Futures price (e.g., 96.5 implies ~3.5% rate)
notionalfloat1_000_000.0Notional amount
convexity_adjustmentfloat0.0Convexity adjustment in basis points
conventionstr"act360"Day count convention
quote_conventionstr"price"Quote convention ("price" or "rate")
sigmafloat or NoneNoneVolatility for automatic convexity calculation
tick_sizefloat or NoneNoneMinimum price increment
notional_multiplierfloat or NoneNoneNotional multiplier per tick
contract_sizefloat or NoneNoneContract size
disc_curve_idstr or NoneNoneDiscount curve identifier for Solver lookup
forecast_curve_idstr or NoneNoneForecast curve identifier for Solver lookup

See Conventions for accepted values for convention.

Methods

MethodReturnsDescription
.rate(curves, *, solver=None)floatImplied forward rate
.npv(curves, *, solver=None)floatNet present value
.cashflows(curves, *, solver=None)DataFramePeriod-level cashflow table
.spread(curves, *, solver=None)floatPar spread

Example

import datetime
from vade import IRFuture, DiscountCurve

nodes = {datetime.date(2024, 1, 1): 1.0, datetime.date(2024, 7, 1): 0.985, datetime.date(2025, 1, 1): 0.97}
curve = DiscountCurve(nodes, interpolation="log_linear", convention="act360")

irf = IRFuture(
    effective=datetime.date(2024, 6, 1),
    termination="3M",
    price=96.5,
    convention="act360",
)
irf.rate(curve)  # implied forward rate from the curve
irf.npv(curve)  # NPV based on price vs implied rate

CapFloor

Cap/Floor instrument with Black-76 or Bachelier pricing. Python wrapper composing caplet/floorlet periods.

See also: Cap & Floor Guide for cap/floor pricing and vol surface usage.

Constructor

CapFloor(
    *,
    effective=None,
    termination=None,
    strike=0.0,
    vol=0.0,
    notional=1_000_000.0,
    frequency="q",
    cap_floor_type="cap",
    model="black76",
    convention="act360",
    currency="USD",
)

Parameters

NameTypeDefaultDescription
effectivedatetime.date or NoneNoneStart date
terminationdate, str, or NoneNoneEnd date or tenor string
strikefloat0.0Strike rate (percentage)
volfloat0.0Volatility (decimal, e.g., 0.20 for 20%)
notionalfloat1_000_000.0Notional amount
frequencystr"q"Caplet/floorlet frequency
cap_floor_typestr"cap"Type: "cap" or "floor"
modelstr"black76"Pricing model: "black76" or "bachelier"
conventionstr"act360"Day count convention
currencystr"USD"Currency code

See Conventions for accepted values for frequency and convention.

Methods

MethodReturnsDescription
.npv(disc_curve, forecast_curve)floatNet present value using Black-76 or Bachelier
.cashflows(disc_curve, forecast_curve)DataFrameCaplet/floorlet-level cashflow table

Note: CapFloor does not have .rate() or solver keyword arguments. Both disc_curve and forecast_curve are required positional parameters.

Example

import datetime
from vade import CapFloor, DiscountCurve

nodes = {datetime.date(2024, 1, 1): 1.0, datetime.date(2025, 1, 1): 0.97, datetime.date(2026, 1, 1): 0.94}
disc = DiscountCurve(nodes, interpolation="log_linear", convention="act365f")
forecast = DiscountCurve(nodes, interpolation="log_linear", convention="act365f")

cap = CapFloor(
    effective=datetime.date(2024, 1, 1),
    termination="1Y",
    strike=3.0,
    vol=0.20,
    frequency="q",
    cap_floor_type="cap",
    model="black76",
    convention="act360",
)
cap.npv(disc, forecast)  # NPV of the interest rate cap

FixedRateBond

Fixed Rate Bond with full analytics suite. Python wrapper composing FixedLeg with bond analytics.

See also: Bonds Guide for bond pricing, yield analysis, and risk metrics.

Constructor

FixedRateBond(
    *,
    spec=None,
    effective=None,
    termination=None,
    coupon=0.0,
    frequency="s",
    settlement_days=1,
    ex_div_days=0,
    calendar=None,
    currency="USD",
    convention="actactisda",
    accrued_convention=None,
    face_value=100.0,
    modifier="none",
    disc_curve_id=None,
)

Parameters

NameTypeDefaultDescription
specstr or NoneNoneInstrument spec name
effectivedatetime.date or NoneNoneIssue or settlement date
terminationdate, str, or NoneNoneMaturity date or tenor string
couponfloat0.0Coupon rate (percentage, e.g., 5.0 for 5%)
frequencystr"s"Coupon payment frequency
settlement_daysint1Settlement lag in business days
ex_div_daysint0Ex-dividend days before coupon payment
calendarstr or NoneNoneNamed calendar
currencystr"USD"Currency code
conventionstr"actactisda"Day count convention for coupon accrual
accrued_conventionstr or NoneNoneSeparate day count for accrued interest (defaults to convention)
face_valuefloat100.0Face value of the bond
modifierstr"none"Business day adjustment rule
disc_curve_idstr or NoneNoneDiscount curve identifier for Solver lookup

See Conventions for accepted values for frequency, convention, and modifier.

Methods

Standard Methods

MethodReturnsDescription
.rate(curves, *, solver=None)floatPar rate against curves
.npv(curves, *, solver=None)floatNet present value
.cashflows(curves, *, solver=None)DataFramePeriod-level cashflow table
.spread(curves, *, solver=None)floatPar spread

Price and Yield

MethodReturnsDescription
.price(curves, *, solver=None, settlement=None)floatClean price
.dirty_price(curves, *, solver=None, settlement=None)floatDirty price (clean + accrued)
.accrued_interest(settlement)floatAccrued interest at settlement date
.ytm(clean_price, settlement, convention="periodic")floatYield to maturity from clean price
.price_from_ytm(ytm, settlement, convention="periodic")floatClean price from yield to maturity

YTM convention accepts "periodic", "annual", "semi_annual", or "continuous".

Risk Analytics

MethodReturnsDescription
.duration(curves, *, solver=None, metric="modified", settlement=None)floatModified or Macaulay duration
.convexity(curves, *, solver=None, settlement=None)floatPrice convexity
.dv01(curves, *, solver=None)floatDollar value of 1bp
.z_spread(curves, *, solver=None, price=None, settlement=None)floatZ-spread over curve
.asw_spread(curves, *, solver=None, price=None, method="par_par", settlement=None)floatAsset swap spread
.cs01(curves, *, solver=None)floatCredit spread sensitivity (1bp)
.price_yield_sensitivity(curves, *, solver=None)floatPrice-yield sensitivity
.analytic_delta(curves, *, solver=None)floatAnalytic delta

Duration metric accepts "modified" or "macaulay". ASW method accepts "par_par" or "proceeds".

Example

import datetime
from vade import FixedRateBond, DiscountCurve

nodes = {
    datetime.date(2024, 1, 1): 1.0,
    datetime.date(2025, 1, 1): 0.97,
    datetime.date(2026, 1, 1): 0.94,
    datetime.date(2027, 1, 1): 0.91,
}
curve = DiscountCurve(nodes, interpolation="log_linear", convention="act365f")

bond = FixedRateBond(
    effective=datetime.date(2024, 1, 1),
    termination="2Y",
    coupon=5.0,
    frequency="s",
    convention="actactisda",
)
bond.npv(curve)  # net present value
bond.price(curve, settlement=datetime.date(2024, 1, 2))  # clean price
bond.dirty_price(curve, settlement=datetime.date(2024, 1, 2))  # dirty price
bond.accrued_interest(datetime.date(2024, 3, 1))  # accrued at settlement
bond.ytm(99.5, datetime.date(2024, 1, 2))  # yield to maturity
bond.duration(curve, metric="modified", settlement=datetime.date(2024, 1, 2))  # modified duration

Bill

Zero-coupon bill (Treasury bill). Python wrapper composing zero-coupon cashflow with bill analytics.

Constructor

Bill(
    *,
    spec=None,
    effective=None,
    termination=None,
    settlement_days=1,
    calendar=None,
    currency="USD",
    convention="act360",
    face_value=100.0,
    disc_curve_id=None,
)

Parameters

NameTypeDefaultDescription
specstr or NoneNoneInstrument spec name
effectivedatetime.date or NoneNoneIssue date
terminationdate, str, or NoneNoneMaturity date or tenor string (e.g., "6M")
settlement_daysint1Settlement lag in business days
calendarstr or NoneNoneNamed calendar
currencystr"USD"Currency code
conventionstr"act360"Day count convention
face_valuefloat100.0Face value
disc_curve_idstr or NoneNoneDiscount curve identifier for Solver lookup

See Conventions for accepted values for convention.

Methods

MethodReturnsDescription
.rate(curves, *, solver=None)floatPar rate against curves
.npv(curves, *, solver=None)floatNet present value
.cashflows(curves, *, solver=None)DataFramePeriod-level cashflow table
.price(curves, *, solver=None)floatBill price
.dirty_price(curves, *, solver=None)floatDirty price
.ytm(clean_price, settlement)floatYield to maturity from price

Example

import datetime
from vade import Bill, DiscountCurve

nodes = {datetime.date(2024, 1, 1): 1.0, datetime.date(2024, 7, 1): 0.985, datetime.date(2025, 1, 1): 0.97}
curve = DiscountCurve(nodes, interpolation="log_linear", convention="act360")

bill = Bill(
    effective=datetime.date(2024, 1, 1),
    termination="6M",
    convention="act360",
)
bill.rate(curve)  # implied discount rate
bill.price(curve)  # bill price
bill.ytm(99.0, datetime.date(2024, 1, 2))  # yield from price

Advanced Example

import datetime, math
from vade import Bill, DiscountCurve, LineCurve

# 6-month US Treasury Bill
bill = Bill(
    effective=datetime.date(2024, 6, 15),
    termination="6m",
    convention="act360",
    currency="usd",
)

# Flat 4% discount curve
base = datetime.date(2024, 6, 15)
nodes = {base: 1.0}
for y in range(1, 6):
    nodes[datetime.date(2024 + y, 6, 15)] = math.exp(-0.04 * y)
curve = DiscountCurve(nodes, interpolation="log_linear", convention="act360")

# Pricing
price = bill.price(curve)
assert 97.0 < float(price) < 100.0

dirty = bill.dirty_price(curve)
assert 97.0 < float(dirty) < 100.0

# NPV
npv = bill.npv(curve)
assert isinstance(float(npv), float)

# Cashflows -- single redemption row
cf = bill.cashflows(curve)
assert len(cf) >= 1

# Yield to maturity (from price and settlement date)
settlement = datetime.date(2024, 6, 16)
ytm = bill.ytm(float(price), settlement)
assert 0.02 < float(ytm) < 0.06

# Rate (discount rate)
rate = bill.rate(curve)
assert isinstance(float(rate), float)

# I-spread against a benchmark swap curve
benchmark = LineCurve({base: 0.035, datetime.date(2030, 6, 15): 0.04})
i_spr = bill.i_spread(settlement, benchmark)
assert isinstance(float(i_spr), float)

FloatRateNote

Floating Rate Note (FRN). Python wrapper composing FloatLeg with FRN analytics.

Constructor

FloatRateNote(
    *,
    spec=None,
    effective=None,
    termination=None,
    spread=0.0,
    frequency="q",
    settlement_days=2,
    ex_div_days=0,
    calendar=None,
    currency="USD",
    convention="act360",
    accrued_convention=None,
    face_value=100.0,
    modifier="mf",
    disc_curve_id=None,
)

Parameters

NameTypeDefaultDescription
specstr or NoneNoneInstrument spec name
effectivedatetime.date or NoneNoneIssue date
terminationdate, str, or NoneNoneMaturity date or tenor string
spreadfloat0.0Spread over floating rate (basis points)
frequencystr"q"Coupon payment frequency
settlement_daysint2Settlement lag in business days
ex_div_daysint0Ex-dividend days before coupon payment
calendarstr or NoneNoneNamed calendar
currencystr"USD"Currency code
conventionstr"act360"Day count convention
accrued_conventionstr or NoneNoneSeparate day count for accrued interest (defaults to convention)
face_valuefloat100.0Face value
modifierstr"mf"Business day adjustment rule
disc_curve_idstr or NoneNoneDiscount curve identifier for Solver lookup

See Conventions for accepted values for frequency, convention, and modifier.

Methods

MethodReturnsDescription
.rate(curves, *, solver=None)floatPar rate against curves
.npv(curves, *, solver=None)floatNet present value
.cashflows(curves, *, solver=None)DataFramePeriod-level cashflow table
.price(curves, *, solver=None)floatClean price
.dirty_price(curves, *, solver=None)floatDirty price
.analytic_delta(curves, *, solver=None)floatAnalytic delta

Example

import datetime
from vade import FloatRateNote, DiscountCurve

nodes = {datetime.date(2024, 1, 1): 1.0, datetime.date(2025, 1, 1): 0.97, datetime.date(2026, 1, 1): 0.94}
curve = DiscountCurve(nodes, interpolation="log_linear", convention="act360")

frn = FloatRateNote(
    effective=datetime.date(2024, 1, 1),
    termination="1Y",
    frequency="q",
    spread=50.0,
    convention="act360",
)
frn.rate(curve)  # par spread
frn.npv(curve)  # net present value
frn.price(curve)  # clean price

SubPeriodFRN

A floating rate note where the coupon payment frequency differs from the fixing frequency. Each coupon period compounds multiple sub-period fixings: coupon = Product(1 + r_i * dcf_i) - 1 + spread. Python wrapper composing FloatLeg with sub-period compounding.

For example, a quarterly-pay / monthly-fix SubPeriodFRN has 4 coupon periods per year, but the floating rate within each coupon is compounded from ~3 monthly fixings. This is common in leveraged loan and CLO markets.

Constructor

SubPeriodFRN(
    *,
    effective=None,
    termination=None,
    spread=0.0,
    frequency="q",
    fixing_frequency="m",
    settlement_days=2,
    ex_div_days=0,
    calendar=None,
    currency="USD",
    convention="act360",
    accrued_convention=None,
    face_value=100.0,
    modifier="mf",
    disc_curve_id=None,
    index=None,
)

Parameters

NameTypeDefaultDescription
effectivedatetime.date or NoneNoneIssue date
terminationdate, str, or NoneNoneMaturity date or tenor string
spreadfloat0.0Spread over floating rate (decimal, e.g., 0.001 = 10bp)
frequencystr"q"Coupon payment frequency
fixing_frequencystr"m"Sub-period fixing frequency (must be higher than frequency)
settlement_daysint2Settlement lag in business days
ex_div_daysint0Ex-dividend days before coupon payment
calendarstr or NoneNoneNamed calendar
currencystr"USD"Currency code
conventionstr"act360"Day count convention
accrued_conventionstr or NoneNoneSeparate day count for accrued interest (defaults to convention)
face_valuefloat100.0Face value
modifierstr"mf"Business day adjustment rule
disc_curve_idstr or NoneNoneDiscount curve identifier for Solver lookup
indexstr or NoneNoneFloating rate index name for fixing lookups

See Conventions for accepted values for frequency, convention, and modifier.

Methods

Standard Methods

MethodReturnsDescription
.rate(curves, *, solver=None)floatPar rate against curves
.npv(curves, *, solver=None)floatNet present value
.cashflows(curves, *, solver=None)DataFramePeriod-level cashflow table
.spread(curves, *, solver=None)floatPar spread

Price and Yield

MethodReturnsDescription
.price(curves, *, solver=None, settlement=None)floatClean price
.dirty_price(curves, *, solver=None, settlement=None)floatDirty price (clean + accrued)
.accrued_interest(settlement)floatAccrued interest at settlement date
.ytm(clean_price, settlement, convention="periodic")floatYield to maturity from clean price
.price_from_ytm(ytm, settlement, convention="periodic")floatClean price from yield to maturity

Risk Analytics

MethodReturnsDescription
.duration(curves, *, solver=None, metric="modified", settlement=None)floatModified or Macaulay duration
.convexity(curves, *, solver=None, settlement=None)floatPrice convexity
.dv01(curves, *, solver=None)floatDollar value of 1bp
.z_spread(curves, *, solver=None, price=None, settlement=None)floatZ-spread over curve
.asw_spread(curves, *, solver=None, price=None, method="par_par", settlement=None)floatAsset swap spread
.analytic_delta(curves, *, solver=None)floatAnalytic delta
.i_spread(settlement, swap_curve)floatInterpolated spread vs swap curve

SubPeriodFRN-Specific

MethodReturnsDescription
.sub_period_schedule()list[tuple[date, date, date, list[tuple]]]Nested schedule showing sub-period structure

Each element of the outer list is a coupon period tuple: (accrual_start, accrual_end, payment_date, sub_periods). Each sub-period is a tuple: (start, end, dcf, fixing).

Example

import datetime
from vade import SubPeriodFRN, DiscountCurve

# Build a discount curve
nodes = {
    datetime.date(2024, 1, 15): 1.0,
    datetime.date(2025, 1, 15): 0.965,
    datetime.date(2026, 1, 15): 0.93,
    datetime.date(2030, 1, 15): 0.85,
}
curve = DiscountCurve(nodes, interpolation="log_linear")

# 2Y quarterly-pay / monthly-fix SubPeriodFRN with 10bp spread
frn = SubPeriodFRN(
    effective=datetime.date(2024, 1, 15),
    termination=datetime.date(2026, 1, 15),
    spread=0.001,
    frequency="q",
    fixing_frequency="m",
    convention="act360",
)

# Price using [discount_curve, forecast_curve] list
dp = frn.dirty_price(curves=[curve, curve])
assert isinstance(dp, float)
assert 90 < dp < 110  # near par

npv = frn.npv(curves=[curve, curve])
assert isinstance(npv, float)

Advanced Example

import datetime
from vade import SubPeriodFRN, DiscountCurve

# Build curve with several nodes for accurate forward rates
nodes = {
    datetime.date(2024, 1, 15): 1.0,
    datetime.date(2024, 7, 15): 0.982,
    datetime.date(2025, 1, 15): 0.965,
    datetime.date(2025, 7, 15): 0.948,
    datetime.date(2026, 1, 15): 0.93,
}
curve = DiscountCurve(nodes, interpolation="log_linear")

# 1Y SubPeriodFRN: quarterly coupon, monthly sub-period compounding
frn = SubPeriodFRN(
    effective=datetime.date(2024, 1, 15),
    termination=datetime.date(2025, 1, 15),
    spread=0.0,
    frequency="q",
    fixing_frequency="m",
    convention="act360",
)

# Inspect sub-period structure: 4 quarterly periods, each with ~3 monthly fixings
schedule = frn.sub_period_schedule()
assert len(schedule) == 4  # 4 quarterly coupon periods

for accrual_start, accrual_end, payment_date, sub_periods in schedule:
    assert accrual_start < accrual_end  # valid period
    assert len(sub_periods) >= 2  # at least 2 sub-period fixings per quarter
    for sub_start, sub_end, dcf, fixing in sub_periods:
        assert sub_start < sub_end
        assert 0 < dcf < 1  # day count fraction for a monthly period

# Pricing and analytics
dp = frn.dirty_price(curves=[curve, curve])
assert 90 < dp < 110  # near par for zero-spread FRN

npv = frn.npv(curves=[curve, curve])
assert isinstance(npv, float)
assert abs(npv - dp) < 1e-6  # NPV equals dirty price for FRN

ad = frn.analytic_delta(curves=[curve, curve])
assert isinstance(ad, float)
assert ad < 0  # negative delta (price falls as rates rise)

ZeroCouponBond

Zero-coupon bond with OID (Original Issue Discount) accrued interest. Python wrapper composing zero-coupon cashflow with compound discounting.

Accrued interest uses the OID compound method: at any settlement date, accrued equals the amortized cost (issue price compounded at yield) minus the issue price, prorated within each semi-annual period.

Constructor

ZeroCouponBond(
    *,
    effective=None,
    termination=None,
    issue_price=100.0,
    frequency="s",
    settlement_days=1,
    calendar=None,
    currency="USD",
    convention="actactisda",
    face_value=100.0,
    disc_curve_id=None,
)

Parameters

NameTypeDefaultDescription
effectivedatetime.date or NoneNoneIssue date
terminationdate, str, or NoneNoneMaturity date or tenor string
issue_pricefloat100.0Original issue price (OID basis for accrued calculation)
frequencystr"s"Accrual frequency for OID calculation
settlement_daysint1Settlement lag in business days
calendarstr or NoneNoneNamed calendar
currencystr"USD"Currency code
conventionstr"actactisda"Day count convention
face_valuefloat100.0Face value at maturity
disc_curve_idstr or NoneNoneDiscount curve identifier for Solver lookup

See Conventions for accepted values for frequency and convention.

Methods

Standard Methods

MethodReturnsDescription
.rate(curves, *, solver=None)floatPar rate against curves
.npv(curves, *, solver=None)floatNet present value
.cashflows(curves, *, solver=None)DataFramePeriod-level cashflow table

Price and Yield

MethodReturnsDescription
.price(curves, *, solver=None, settlement=None)floatClean price
.dirty_price(curves, *, solver=None, settlement=None)floatDirty price (clean + OID accrued)
.accrued_interest(settlement)floatOID accrued interest at settlement date
.ytm(clean_price, settlement, convention="periodic")floatYield to maturity from clean price
.price_from_ytm(ytm, settlement, convention="periodic")floatClean price from yield to maturity

Risk Analytics

MethodReturnsDescription
.duration(curves, *, solver=None, metric="modified", settlement=None)floatModified or Macaulay duration
.convexity(curves, *, solver=None, settlement=None)floatPrice convexity
.z_spread(curves, *, solver=None, price=None, settlement=None)floatZ-spread over curve

Example

import math
import datetime
from vade import ZeroCouponBond, DiscountCurve

# Flat 4% continuously compounded curve
base = datetime.date(2024, 1, 1)
nodes = {base: 1.0}
for y in range(1, 11):
    nodes[datetime.date(2024 + y, 1, 1)] = math.exp(-0.04 * y)
curve = DiscountCurve(nodes, interpolation="log_linear", convention="act365f")

zcb = ZeroCouponBond(
    effective=datetime.date(2024, 1, 1),
    termination=datetime.date(2029, 1, 1),
    issue_price=80.0,
    face_value=100.0,
    frequency="s",
    convention="actactisda",
)
settlement = datetime.date(2024, 1, 2)

price = zcb.price(curve, settlement=settlement)
assert 50.0 < price < 80.0  # deep discount zero-coupon bond

accrued = zcb.accrued_interest(settlement)
assert accrued >= 0.0  # OID accrued is non-negative

ytm = zcb.ytm(float(price), settlement)
assert 0.03 < ytm < 0.06  # yield in reasonable range

dur = zcb.duration(curve, metric="modified", settlement=settlement)
assert dur > 4.0  # near maturity duration for 5Y zero

Advanced Example

import datetime, math
from vade import ZeroCouponBond, DiscountCurve

# 5-year zero coupon bond
zcb = ZeroCouponBond(
    effective=datetime.date(2024, 1, 15),
    termination="5y",
    face_value=100.0,
    frequency="s",
    convention="act365f",
    currency="usd",
)

# Flat 4% discount curve
base = datetime.date(2024, 1, 15)
nodes = {base: 1.0}
for y in range(1, 11):
    nodes[datetime.date(2024 + y, 1, 15)] = math.exp(-0.04 * y)
curve = DiscountCurve(nodes, interpolation="log_linear", convention="act365f")

settlement = datetime.date(2024, 1, 16)

# Pricing and yield
price = zcb.price(curve, settlement=settlement)
assert 75.0 < float(price) < 95.0

ytm = zcb.ytm(float(price), settlement)
assert 0.02 < float(ytm) < 0.06

# Price-from-YTM roundtrip
price_rt = zcb.price_from_ytm(ytm, settlement)
assert abs(float(price_rt) - float(price)) < 0.01

# OID accrued interest grows over time (requires issue_price < face_value)
zcb_oid = ZeroCouponBond(
    effective=datetime.date(2024, 1, 15),
    termination="5y",
    issue_price=80.0,
    face_value=100.0,
    frequency="s",
    convention="act365f",
    currency="usd",
)
ai_early = zcb_oid.accrued_interest(datetime.date(2025, 1, 15))
ai_mid = zcb_oid.accrued_interest(datetime.date(2027, 1, 15))
assert float(ai_mid) > float(ai_early)  # accrued grows over time

# Cashflows -- single redemption
cf = zcb.cashflows(curve)
assert len(cf) >= 1

# Risk analytics
dur = zcb.duration(curve, settlement=settlement)
assert 3.0 < float(dur) < 6.0

conv = zcb.convexity(curve, settlement=settlement)
assert float(conv) > 0

z_spr = zcb.z_spread(curve, price=float(price), settlement=settlement)
assert abs(float(z_spr)) < 0.01  # near zero when priced off same curve

StepUpBond

Fixed-rate bond with coupon rates that change at specified dates. Python wrapper composing FixedLeg with dated rate resolution.

The coupon_rates parameter replaces the single coupon used by FixedRateBond, allowing step-up (or step-down) coupon schedules where the rate changes at predefined dates.

Constructor

StepUpBond(
    *,
    effective=None,
    termination=None,
    coupon_rates=None,
    frequency="s",
    settlement_days=1,
    ex_div_days=0,
    calendar=None,
    currency="USD",
    convention="actactisda",
    accrued_convention=None,
    face_value=100.0,
    modifier="none",
    disc_curve_id=None,
)

Parameters

NameTypeDefaultDescription
effectivedatetime.date or NoneNoneIssue date
terminationdate, str, or NoneNoneMaturity date or tenor string
coupon_rateslist[tuple[date, float]] or NoneNoneDated coupon rate schedule as [(date, rate), ...]
frequencystr"s"Coupon payment frequency
settlement_daysint1Settlement lag in business days
ex_div_daysint0Ex-dividend days before coupon payment
calendarstr or NoneNoneNamed calendar
currencystr"USD"Currency code
conventionstr"actactisda"Day count convention for coupon accrual
accrued_conventionstr or NoneNoneSeparate day count for accrued interest (defaults to convention)
face_valuefloat100.0Face value of the bond
modifierstr"none"Business day adjustment rule
disc_curve_idstr or NoneNoneDiscount curve identifier for Solver lookup

See Conventions for accepted values for frequency, convention, and modifier.

Methods

Standard Methods

MethodReturnsDescription
.rate(curves, *, solver=None)floatPar rate against curves
.npv(curves, *, solver=None)floatNet present value
.cashflows(curves, *, solver=None)DataFramePeriod-level cashflow table
.spread(curves, *, solver=None)floatPar spread

Price and Yield

MethodReturnsDescription
.price(curves, *, solver=None, settlement=None)floatClean price
.dirty_price(curves, *, solver=None, settlement=None)floatDirty price (clean + accrued)
.accrued_interest(settlement)floatAccrued interest at settlement date
.ytm(clean_price, settlement, convention="periodic")floatYield to maturity from clean price
.price_from_ytm(ytm, settlement, convention="periodic")floatClean price from yield to maturity

Risk Analytics

MethodReturnsDescription
.duration(curves, *, solver=None, metric="modified", settlement=None)floatModified or Macaulay duration
.convexity(curves, *, solver=None, settlement=None)floatPrice convexity
.dv01(curves, *, solver=None)floatDollar value of 1bp
.z_spread(curves, *, solver=None, price=None, settlement=None)floatZ-spread over curve
.asw_spread(curves, *, solver=None, price=None, method="par_par", settlement=None)floatAsset swap spread
.analytic_delta(curves, *, solver=None)floatAnalytic delta

Example

import math
import datetime
from vade import StepUpBond, DiscountCurve

# Flat 4% continuously compounded curve
base = datetime.date(2024, 1, 1)
nodes = {base: 1.0}
for y in range(1, 11):
    nodes[datetime.date(2024 + y, 1, 1)] = math.exp(-0.04 * y)
curve = DiscountCurve(nodes, interpolation="log_linear", convention="act365f")

bond = StepUpBond(
    effective=datetime.date(2024, 1, 1),
    termination=datetime.date(2029, 1, 1),
    coupon_rates=[
        (datetime.date(2024, 1, 1), 0.03),   # 3% for first 3 years
        (datetime.date(2027, 1, 1), 0.05),   # steps up to 5%
    ],
    frequency="s",
    face_value=100.0,
    convention="actactisda",
)
settlement = datetime.date(2024, 1, 2)

price = bond.price(curve, settlement=settlement)
assert 90.0 < price < 110.0  # near par

ytm = bond.ytm(float(price), settlement)
assert 0.03 < ytm < 0.06  # yield between step-up rates

dur = bond.duration(curve, metric="modified", settlement=settlement)
assert 3.0 < dur < 5.5  # reasonable duration for 5Y bond

Advanced Example

import datetime, math
from vade import StepUpBond, DiscountCurve

# 7-year step-up bond with 3 coupon steps
sub = StepUpBond(
    effective=datetime.date(2024, 3, 15),
    termination="7y",
    coupon_rates=[
        (datetime.date(2024, 3, 15), 0.03),    # 3.0% initial
        (datetime.date(2026, 3, 15), 0.035),   # steps to 3.5% at year 2
        (datetime.date(2028, 3, 15), 0.04),    # steps to 4.0% at year 4
        (datetime.date(2030, 3, 15), 0.045),   # steps to 4.5% at year 6
    ],
    frequency="s",
    convention="act365f",
    currency="usd",
)

# Flat 4% discount curve
base = datetime.date(2024, 3, 15)
nodes = {base: 1.0}
for y in range(1, 11):
    nodes[datetime.date(2024 + y, 3, 15)] = math.exp(-0.04 * y)
curve = DiscountCurve(nodes, interpolation="log_linear", convention="act365f")

# Pricing
price = sub.price(curve)
assert 90.0 < float(price) < 110.0

dirty = sub.dirty_price(curve)
assert 90.0 < float(dirty) < 115.0

# Yield to maturity
settlement = datetime.date(2024, 3, 16)
ytm = sub.ytm(float(price), settlement)
assert 0.02 < float(ytm) < 0.06

# Cashflows -- shows varying coupon amounts across steps
cf = sub.cashflows(curve)
assert len(cf) > 10  # semi-annual over 7 years

# Risk analytics
dur = sub.duration(curve)
assert 3.0 < float(dur) < 8.0

conv = sub.convexity(curve)
assert float(conv) > 0

dv01 = sub.dv01(curve)
assert float(dv01) > 0

z_spr = sub.z_spread(curve, price=float(price))
assert abs(float(z_spr)) < 0.01  # near zero when priced off same curve

AmortizingBond

Fixed-rate bond with declining notional via scheduled principal repayments. Python wrapper composing FixedLeg with amortization schedule.

Principal repayment amounts are specified as absolute values. Coupon payments are computed on the remaining (reduced) notional after each principal repayment.

Constructor

AmortizingBond(
    *,
    effective=None,
    termination=None,
    coupon=0.0,
    amortization_schedule=None,
    frequency="s",
    settlement_days=1,
    ex_div_days=0,
    calendar=None,
    currency="USD",
    convention="actactisda",
    accrued_convention=None,
    face_value=100.0,
    modifier="none",
    disc_curve_id=None,
)

Parameters

NameTypeDefaultDescription
effectivedatetime.date or NoneNoneIssue date
terminationdate, str, or NoneNoneMaturity date or tenor string
couponfloat0.0Coupon rate (percentage, e.g., 0.05 for 5%)
amortization_schedulelist[tuple[date, float]] or NoneNonePrincipal repayment schedule as [(date, amount), ...]
frequencystr"s"Coupon payment frequency
settlement_daysint1Settlement lag in business days
ex_div_daysint0Ex-dividend days before coupon payment
calendarstr or NoneNoneNamed calendar
currencystr"USD"Currency code
conventionstr"actactisda"Day count convention for coupon accrual
accrued_conventionstr or NoneNoneSeparate day count for accrued interest (defaults to convention)
face_valuefloat100.0Initial face value of the bond
modifierstr"none"Business day adjustment rule
disc_curve_idstr or NoneNoneDiscount curve identifier for Solver lookup

See Conventions for accepted values for frequency, convention, and modifier.

Methods

Standard Methods

MethodReturnsDescription
.rate(curves, *, solver=None)floatPar rate against curves
.npv(curves, *, solver=None)floatNet present value
.cashflows(curves, *, solver=None)DataFramePeriod-level cashflow table
.spread(curves, *, solver=None)floatPar spread

Price and Yield

MethodReturnsDescription
.price(curves, *, solver=None, settlement=None)floatClean price
.dirty_price(curves, *, solver=None, settlement=None)floatDirty price (clean + accrued)
.accrued_interest(settlement)floatAccrued interest at settlement date
.ytm(clean_price, settlement, convention="periodic")floatYield to maturity from clean price
.price_from_ytm(ytm, settlement, convention="periodic")floatClean price from yield to maturity

Risk Analytics

MethodReturnsDescription
.duration(curves, *, solver=None, metric="modified", settlement=None)floatModified or Macaulay duration
.convexity(curves, *, solver=None, settlement=None)floatPrice convexity
.dv01(curves, *, solver=None)floatDollar value of 1bp
.z_spread(curves, *, solver=None, price=None, settlement=None)floatZ-spread over curve
.asw_spread(curves, *, solver=None, price=None, method="par_par", settlement=None)floatAsset swap spread
.analytic_delta(curves, *, solver=None)floatAnalytic delta

Example

import math
import datetime
from vade import AmortizingBond, DiscountCurve

# Flat 4% continuously compounded curve
base = datetime.date(2024, 1, 1)
nodes = {base: 1.0}
for y in range(1, 11):
    nodes[datetime.date(2024 + y, 1, 1)] = math.exp(-0.04 * y)
curve = DiscountCurve(nodes, interpolation="log_linear", convention="act365f")

bond = AmortizingBond(
    effective=datetime.date(2024, 1, 1),
    termination=datetime.date(2029, 1, 1),
    coupon=0.05,
    amortization_schedule=[
        (datetime.date(2026, 1, 1), 25.0),   # repay 25 at year 2
        (datetime.date(2028, 1, 1), 25.0),   # repay 25 at year 4
    ],
    frequency="a",
    face_value=100.0,
    convention="actactisda",
)
settlement = datetime.date(2024, 1, 2)

price = bond.price(curve, settlement=settlement)
assert 95.0 < price < 115.0  # above par when coupon > yield

ytm = bond.ytm(float(price), settlement)
assert 0.03 < ytm < 0.06  # reasonable yield range

# Accrued interest reflects current notional
ai_early = bond.accrued_interest(datetime.date(2024, 7, 1))
ai_late = bond.accrued_interest(datetime.date(2026, 7, 1))
assert ai_early > ai_late  # lower notional after amortization

Advanced Example

import datetime, math
from vade import AmortizingBond, DiscountCurve

# 5-year amortizing bond: 25% principal at Y2 and Y4
ab = AmortizingBond(
    effective=datetime.date(2024, 3, 15),
    termination="5y",
    coupon=0.05,
    frequency="s",
    convention="act365f",
    currency="usd",
    amortization_schedule=[
        (datetime.date(2026, 3, 15), 25.0),
        (datetime.date(2028, 3, 15), 25.0),
    ],
)

# Flat 4% discount curve
base = datetime.date(2024, 3, 15)
nodes = {base: 1.0}
for y in range(1, 11):
    nodes[datetime.date(2024 + y, 3, 15)] = math.exp(-0.04 * y)
curve = DiscountCurve(nodes, interpolation="log_linear", convention="act365f")

# Pricing and yield
settlement = datetime.date(2024, 3, 16)
price = ab.price(curve)
assert 100.0 < float(price) < 110.0  # above par, coupon > discount rate

ytm = ab.ytm(float(price), settlement)
assert 0.03 < float(ytm) < 0.06

# Cashflows -- inspect types: "Amortizing" and "Principal" rows present
cf = ab.cashflows(curve)
assert len(cf) > 5
cf_types = cf["Type"].to_list()
assert "Amortizing" in cf_types
assert "Principal" in cf_types  # amortization creates principal repayment rows

# Accrued interest declines as notional amortizes
ai_before = ab.accrued_interest(datetime.date(2025, 6, 15))  # before any amort
ai_after = ab.accrued_interest(datetime.date(2026, 6, 15))   # after first 25% amort
assert float(ai_before) > float(ai_after)  # lower notional -> lower accrued

# Risk analytics -- amortization reduces effective duration
dur = ab.duration(curve)
assert 2.0 < float(dur) < 5.0  # shorter than 5Y bullet

conv = ab.convexity(curve)
assert float(conv) > 0

dv01 = ab.dv01(curve)
assert float(dv01) > 0

z_spr = ab.z_spread(curve, price=float(price))
assert abs(float(z_spr)) < 0.01  # near zero when priced off same curve

PIKBond

Payment-in-kind bond where accrued interest compounds into a growing notional. Python wrapper composing FixedLeg with PIK notional accretion.

With pik_fraction=1.0 (full PIK), no cash coupons are paid; all interest accrues to principal. With pik_fraction=0.5, half the coupon is paid in cash and half compounds into the notional. The notional_schedule() method returns the growing notional at each period boundary.

Constructor

PIKBond(
    *,
    effective=None,
    termination=None,
    coupon=0.0,
    pik_fraction=1.0,
    frequency="s",
    settlement_days=1,
    ex_div_days=0,
    calendar=None,
    currency="USD",
    convention="actactisda",
    accrued_convention=None,
    face_value=100.0,
    modifier="none",
    disc_curve_id=None,
)

Parameters

NameTypeDefaultDescription
effectivedatetime.date or NoneNoneIssue date
terminationdate, str, or NoneNoneMaturity date or tenor string
couponfloat0.0Coupon rate (percentage, e.g., 0.06 for 6%)
pik_fractionfloat1.0Fraction of coupon paid in kind (1.0 = full PIK, 0.0 = all cash)
frequencystr"s"Coupon payment frequency
settlement_daysint1Settlement lag in business days
ex_div_daysint0Ex-dividend days before coupon payment
calendarstr or NoneNoneNamed calendar
currencystr"USD"Currency code
conventionstr"actactisda"Day count convention for coupon accrual
accrued_conventionstr or NoneNoneSeparate day count for accrued interest (defaults to convention)
face_valuefloat100.0Initial face value of the bond
modifierstr"none"Business day adjustment rule
disc_curve_idstr or NoneNoneDiscount curve identifier for Solver lookup

See Conventions for accepted values for frequency, convention, and modifier.

Methods

Standard Methods

MethodReturnsDescription
.rate(curves, *, solver=None)floatPar rate against curves
.npv(curves, *, solver=None)floatNet present value
.cashflows(curves, *, solver=None)DataFramePeriod-level cashflow table
.spread(curves, *, solver=None)floatPar spread

Price and Yield

MethodReturnsDescription
.price(curves, *, solver=None, settlement=None)floatClean price
.dirty_price(curves, *, solver=None, settlement=None)floatDirty price (clean + accrued)
.accrued_interest(settlement)floatAccrued interest at settlement date
.ytm(clean_price, settlement, convention="periodic")floatYield to maturity from clean price
.price_from_ytm(ytm, settlement, convention="periodic")floatClean price from yield to maturity

Risk Analytics

MethodReturnsDescription
.duration(curves, *, solver=None, metric="modified", settlement=None)floatModified or Macaulay duration
.convexity(curves, *, solver=None, settlement=None)floatPrice convexity
.dv01(curves, *, solver=None)floatDollar value of 1bp
.z_spread(curves, *, solver=None, price=None, settlement=None)floatZ-spread over curve
.asw_spread(curves, *, solver=None, price=None, method="par_par", settlement=None)floatAsset swap spread
.analytic_delta(curves, *, solver=None)floatAnalytic delta

PIK-Specific

MethodReturnsDescription
.notional_schedule()list[tuple[date, float]]Growing notional at each period boundary

Example

import math
import datetime
from vade import PIKBond, DiscountCurve

# Flat 4% continuously compounded curve
base = datetime.date(2024, 1, 1)
nodes = {base: 1.0}
for y in range(1, 11):
    nodes[datetime.date(2024 + y, 1, 1)] = math.exp(-0.04 * y)
curve = DiscountCurve(nodes, interpolation="log_linear", convention="act365f")

bond = PIKBond(
    effective=datetime.date(2024, 1, 1),
    termination=datetime.date(2029, 1, 1),
    coupon=0.06,
    pik_fraction=1.0,   # full PIK: all interest compounds
    frequency="a",
    face_value=100.0,
    convention="actactisda",
)

# Full PIK means no cash coupon accrues
assert bond.accrued_interest(datetime.date(2024, 7, 1)) == 0.0

# Notional grows each period
schedule = bond.notional_schedule()
assert schedule[0][1] == 100.0    # initial face value
assert schedule[1][1] > 100.0     # notional grows after first period

price = bond.price(curve, settlement=datetime.date(2024, 1, 2))
assert 100.0 < price < 120.0  # above par for 6% PIK vs 4% discount

Advanced Example

import datetime, math
from vade import PIKBond, DiscountCurve

# Full PIK bond: 6% coupon, all interest capitalised
pik_full = PIKBond(
    effective=datetime.date(2024, 1, 15),
    termination="5y",
    coupon=0.06,
    pik_fraction=1.0,
    frequency="a",
    convention="act365f",
    currency="usd",
)

# Partial PIK bond: same terms, 50% cash / 50% PIK
pik_partial = PIKBond(
    effective=datetime.date(2024, 1, 15),
    termination="5y",
    coupon=0.06,
    pik_fraction=0.5,
    frequency="a",
    convention="act365f",
    currency="usd",
)

# Flat 4% discount curve
base = datetime.date(2024, 1, 15)
nodes = {base: 1.0}
for y in range(1, 11):
    nodes[datetime.date(2024 + y, 1, 15)] = math.exp(-0.04 * y)
curve = DiscountCurve(nodes, interpolation="log_linear", convention="act365f")

# notional_schedule() shows compounding growth for full PIK
ns = pik_full.notional_schedule()
assert len(ns) > 1
# First entry is par, subsequent entries grow as interest is capitalised
assert ns[0][1] == 100.0
assert ns[-1][1] > 100.0  # notional has grown

# Full PIK has zero accrued (all capitalised)
ai_full = pik_full.accrued_interest(datetime.date(2024, 7, 15))
assert float(ai_full) == 0.0

# Partial PIK has positive accrued (cash portion)
ai_partial = pik_partial.accrued_interest(datetime.date(2024, 7, 15))
assert float(ai_partial) > 0.0

# Full PIK price is higher (more future cashflows from compounding)
price_full = pik_full.price(curve)
price_partial = pik_partial.price(curve)
assert float(price_full) > float(price_partial)

# Cashflows
cf = pik_full.cashflows(curve)
assert len(cf) > 0

# Risk analytics
dur = pik_full.duration(curve)
assert 3.0 < float(dur) < 6.0

conv = pik_full.convexity(curve)
assert float(conv) > 0

CappedFloatRateNote

Floating rate note with embedded cap and/or floor on the coupon rate. Python wrapper composing FloatLeg with Black-76 or Bachelier cap/floor pricing.

Requires vol (volatility) -- there is no intrinsic-only fallback. At least one of cap_rate or floor_rate must be specified. When both are set, the instrument is a collared FRN. Optionally supports sub-period compounding via fixing_frequency.

Constructor

CappedFloatRateNote(
    *,
    effective=None,
    termination=None,
    spread=0.0,
    frequency="q",
    fixing_frequency=None,
    cap_rate=None,
    floor_rate=None,
    vol=None,
    vol_model="black76",
    settlement_days=2,
    ex_div_days=0,
    calendar=None,
    currency="USD",
    convention="act360",
    accrued_convention=None,
    face_value=100.0,
    modifier="mf",
    disc_curve_id=None,
)

Parameters

NameTypeDefaultDescription
effectivedatetime.date or NoneNoneIssue date
terminationdate, str, or NoneNoneMaturity date or tenor string
spreadfloat0.0Spread over floating rate (decimal)
frequencystr"q"Coupon payment frequency
fixing_frequencystr or NoneNoneSub-period fixing frequency (e.g., "m" for monthly within quarterly)
cap_ratefloat or NoneNoneCap strike rate (decimal). At least one of cap_rate/floor_rate required.
floor_ratefloat or NoneNoneFloor strike rate (decimal)
volfloat or NoneNoneVolatility for option pricing. Required -- no intrinsic fallback.
vol_modelstr"black76"Pricing model: "black76" or "bachelier"
settlement_daysint2Settlement lag in business days
ex_div_daysint0Ex-dividend days before coupon payment
calendarstr or NoneNoneNamed calendar
currencystr"USD"Currency code
conventionstr"act360"Day count convention
accrued_conventionstr or NoneNoneSeparate day count for accrued interest (defaults to convention)
face_valuefloat100.0Face value
modifierstr"mf"Business day adjustment rule
disc_curve_idstr or NoneNoneDiscount curve identifier for Solver lookup

See Conventions for accepted values for frequency, convention, and modifier.

Methods

Standard Methods

MethodReturnsDescription
.rate(curves, *, solver=None)floatPar rate against curves
.npv(curves, *, solver=None)floatNet present value
.cashflows(curves, *, solver=None)DataFramePeriod-level cashflow table
.spread(curves, *, solver=None)floatPar spread

Price and Yield

MethodReturnsDescription
.price(curves, *, solver=None, settlement=None)floatClean price
.dirty_price(curves, *, solver=None, settlement=None)floatDirty price (clean + accrued)
.accrued_interest(settlement)floatAccrued interest at settlement date
.ytm(clean_price, settlement, convention="periodic")floatYield to maturity from clean price
.price_from_ytm(ytm, settlement, convention="periodic")floatClean price from yield to maturity

Risk Analytics

MethodReturnsDescription
.duration(curves, *, solver=None, metric="modified", settlement=None)floatModified or Macaulay duration
.convexity(curves, *, solver=None, settlement=None)floatPrice convexity
.dv01(curves, *, solver=None)floatDollar value of 1bp
.z_spread(curves, *, solver=None, price=None, settlement=None)floatZ-spread over curve
.asw_spread(curves, *, solver=None, price=None, method="par_par", settlement=None)floatAsset swap spread
.analytic_delta(curves, *, solver=None)floatAnalytic delta

CappedFRN-Specific

MethodReturnsDescription
.sub_period_schedule()DataFrameSub-period schedule when fixing_frequency set

Example

import math
import datetime
from vade import CappedFloatRateNote, FloatRateNote, DiscountCurve

# Steep discount curve (rates ~5-6%)
base = datetime.date(2024, 1, 1)
nodes = {base: 1.0}
for y in range(1, 11):
    nodes[datetime.date(2024 + y, 1, 1)] = math.exp(-0.055 * y)
curve = DiscountCurve(nodes, interpolation="log_linear", convention="act365f")

# Uncapped FRN for comparison
frn = FloatRateNote(
    effective=datetime.date(2024, 1, 1),
    termination=datetime.date(2026, 1, 1),
    spread=0.0,
    frequency="q",
    convention="act360",
)
frn_price = frn.dirty_price(curves=[curve, curve])

# Capped FRN with 2% cap (well below market rates)
capped = CappedFloatRateNote(
    effective=datetime.date(2024, 1, 1),
    termination=datetime.date(2026, 1, 1),
    spread=0.0,
    frequency="q",
    cap_rate=0.02,
    vol=0.20,
    convention="act360",
)
capped_price = capped.dirty_price(curves=[curve, curve])

assert isinstance(capped_price, float)
assert capped_price < frn_price  # cap reduces value to investor

Advanced Example

import datetime, math
from vade import CappedFloatRateNote, DiscountCurve

# Base discount/forecast curve (flat 4%)
base = datetime.date(2024, 6, 15)
nodes = {base: 1.0}
for y in range(1, 6):
    nodes[datetime.date(2024 + y, 6, 15)] = math.exp(-0.04 * y)
curve = DiscountCurve(nodes, interpolation="log_linear", convention="act360")

# Capped FRN: floating rate capped at 5%
capped = CappedFloatRateNote(
    effective=datetime.date(2024, 6, 15),
    termination="3y",
    frequency="q",
    convention="act360",
    currency="usd",
    cap_rate=0.05,
    vol=0.50,
)

# Floored FRN: floating rate floored at 2%
floored = CappedFloatRateNote(
    effective=datetime.date(2024, 6, 15),
    termination="3y",
    frequency="q",
    convention="act360",
    currency="usd",
    floor_rate=0.02,
    vol=0.50,
)

# Collared FRN: both cap and floor
collared = CappedFloatRateNote(
    effective=datetime.date(2024, 6, 15),
    termination="3y",
    frequency="q",
    convention="act360",
    currency="usd",
    cap_rate=0.05,
    floor_rate=0.02,
    vol=0.50,
)

# Pricing -- pass curves as list [discount, forecast] (required format)
price_capped = capped.dirty_price(curves=[curve, curve])
price_floored = floored.dirty_price(curves=[curve, curve])
price_collared = collared.dirty_price(curves=[curve, curve])

assert isinstance(float(price_capped), float)
assert isinstance(float(price_floored), float)
assert isinstance(float(price_collared), float)

# Floor adds value (guarantees minimum coupon)
# Cap reduces value (limits upside)
# So: capped < collared < floored
assert float(price_capped) < float(price_floored)

# Bachelier model comparison (vol_model="bachelier")
capped_bach = CappedFloatRateNote(
    effective=datetime.date(2024, 6, 15),
    termination="3y",
    frequency="q",
    convention="act360",
    currency="usd",
    cap_rate=0.05,
    vol=0.01,  # Bachelier vol in absolute terms
    vol_model="bachelier",
)
price_bach = capped_bach.dirty_price(curves=[curve, curve])
assert isinstance(float(price_bach), float)
# Bachelier and Black-76 produce different prices
assert float(price_bach) != float(price_capped)

# Cashflows
cf = capped.cashflows(curves=[curve, curve])
assert len(cf) > 0

# Risk analytics -- near-zero for FRN is expected (resets to par)
dur = capped.duration(curves=[curve, curve])
assert isinstance(float(dur), float)

AssetSwap

Asset swap decomposing a bond into a floating-rate equivalent. Python wrapper composing bond + floating leg.

Supports "par" (par-par) and "non_par" asset swap types. The par asset swap computes the spread over floating that makes the package NPV zero at par. The non-par variant requires a dirty_price input.

See also: Spread Analytics Guide for asset swap workflows and par vs non-par comparison.

Constructor

AssetSwap(bond, *, asw_type="par", dirty_price=None, spread=None, disc_curve_id=None)

Parameters

NameTypeDefaultDescription
bondbond instrumentrequiredUnderlying bond (FixedRateBond, FloatRateNote, StepUpBond, etc.). Positional.
asw_typestr"par"Asset swap type: "par" or "non_par"
dirty_pricefloat or NoneNoneDirty price of the bond (required for "non_par")
spreadfloat or NoneNoneFixed spread override
disc_curve_idstr or NoneNoneDiscount curve identifier for Solver lookup

Methods

MethodReturnsDescription
.rate(curves, *, solver=None)floatAsset swap spread
.npv(curves, *, solver=None)floatNet present value of the package
.cashflows(curves, *, solver=None)DataFramePeriod-level cashflow table

Example

import math
import datetime
from vade import AssetSwap, FixedRateBond, DiscountCurve

# Flat 3% continuously compounded curve
base = datetime.date(2024, 1, 1)
nodes = {base: 1.0}
for y in range(1, 11):
    nodes[datetime.date(2024 + y, 1, 1)] = math.exp(-0.03 * y)
curve = DiscountCurve(nodes, interpolation="log_linear", convention="act365f", id="disc")

bond = FixedRateBond(
    effective=datetime.date(2024, 1, 1),
    termination="5Y",
    coupon=0.04,
    frequency="s",
    face_value=100.0,
)

# Par asset swap
asw = AssetSwap(bond=bond, asw_type="par")
spread = asw.rate(curves=curve)
assert isinstance(spread, float)
assert -0.5 < spread < 0.5  # spread in reasonable range

# NPV is zero at fair spread (no fixed spread override)
npv = asw.npv(curves=curve)
assert abs(npv) < 1e-8

# Non-par asset swap with explicit dirty price
asw_np = AssetSwap(bond=bond, asw_type="non_par", dirty_price=102.5)
spread_np = asw_np.rate(curves=curve)
assert isinstance(spread_np, float)

CallableBond

Bond with embedded call and/or put options, priced via Hull-White trinomial tree. Python wrapper composing bond + option schedule.

Tree-based methods require Hull-White model parameters: a (mean reversion speed) and sigma (short-rate volatility). Standard bond methods (rate, npv, cashflows) delegate to the underlying bond.

See also: Callable Bonds Guide for OAS analysis and effective duration workflows.

Constructor

CallableBond(bond, *, call_schedule=None, put_schedule=None, disc_curve_id=None)

Parameters

NameTypeDefaultDescription
bondbond instrumentrequiredUnderlying bond (typically FixedRateBond). Positional.
call_schedulelist[tuple[date, float]] or NoneNoneCall dates and prices as [(date, strike_price), ...]
put_schedulelist[tuple[date, float]] or NoneNonePut dates and prices as [(date, strike_price), ...]
disc_curve_idstr or NoneNoneDiscount curve identifier for Solver lookup

Methods

Standard Methods (delegated to underlying bond)

MethodReturnsDescription
.rate(curves, *, solver=None)floatPar rate against curves
.npv(curves, *, solver=None)floatNet present value
.cashflows(curves, *, solver=None)DataFramePeriod-level cashflow table

Tree-Based Methods (Hull-White)

MethodReturnsDescription
.tree_price(curves, a, sigma, *, spread=0.0)floatOption-adjusted price via trinomial tree
.oas(curves, market_price, a, sigma)floatOption-adjusted spread from market price
.effective_duration(curves, a, sigma, *, oas=0.0, bump=0.0001)floatEffective duration (curve bump)
.effective_convexity(curves, a, sigma, *, oas=0.0, bump=0.0001)floatEffective convexity (curve bump)
.vega(curves, a, sigma, *, oas=0.0, bump=0.0001)floatVega (price sensitivity to volatility)

Tree-based methods take a (mean reversion speed, e.g., 0.1) and sigma (short-rate vol, e.g., 0.01).

Example

import math
import datetime
from vade import CallableBond, FixedRateBond, DiscountCurve

# Flat 3% continuously compounded curve
base = datetime.date(2024, 1, 1)
nodes = {base: 1.0}
for y in range(1, 11):
    nodes[datetime.date(2024 + y, 1, 1)] = math.exp(-0.03 * y)
curve = DiscountCurve(nodes, interpolation="log_linear", convention="act365f", id="disc")

bond = FixedRateBond(
    effective=datetime.date(2024, 1, 1),
    termination="5Y",
    coupon=0.05,
    frequency="a",
    face_value=100.0,
)

cb = CallableBond(
    bond=bond,
    call_schedule=[
        (datetime.date(2026, 1, 1), 100.0),
        (datetime.date(2027, 1, 1), 100.0),
        (datetime.date(2028, 1, 1), 100.0),
    ],
)

# Tree price with Hull-White parameters
tree_px = cb.tree_price(curves=curve, a=0.1, sigma=0.01)
assert 80.0 < tree_px < 120.0  # reasonable price range

# OAS: use tree_price as market price => OAS should be ~0
oas = cb.oas(curves=curve, market_price=tree_px, a=0.1, sigma=0.01)
assert abs(oas) < 1e-4  # OAS ~0 when market = model price

# Effective duration and convexity
eff_dur = cb.effective_duration(curves=curve, a=0.1, sigma=0.01)
assert eff_dur > 0  # positive duration

eff_cvx = cb.effective_convexity(curves=curve, a=0.1, sigma=0.01)
assert isinstance(eff_cvx, float)

CDS

Credit Default Swap with hazard rate curve pricing. Rust-backed.

See also: Credit Curves & CDS Guide for CDS-based credit curve calibration.

Alias: CDS = CreditDefaultSwap

Constructor

CDS(
    *,
    spec=None,
    effective=None,
    termination=None,
    frequency="q",
    notional=10_000_000.0,
    recovery_rate=0.4,
    fixed_rate=100.0,
    convention="act360",
    currency="USD",
    premium_accrued=True,
    modifier="mf",
    stub="shortfront",
)

Parameters

NameTypeDefaultDescription
specstr or NoneNoneInstrument spec name
effectivedatetime.date or NoneNoneProtection start date
terminationdate, str, or NoneNoneProtection end date or tenor string (e.g., "5Y")
frequencystr"q"Premium payment frequency
notionalfloat10_000_000.0Notional amount
recovery_ratefloat0.4Assumed recovery rate (decimal, e.g., 0.4 for 40%)
fixed_ratefloat100.0Fixed premium rate (basis points)
conventionstr"act360"Day count convention
currencystr"USD"Currency code
premium_accruedboolTrueWhether premium accrues to default date
modifierstr"mf"Business day adjustment rule
stubstr"shortfront"Stub period preference

See Conventions for accepted values for frequency, convention, modifier, and stub.

Methods

CDS methods take two separate curve arguments: a discount curve and a CreditImpliedCurve for survival probabilities.

MethodReturnsDescription
.rate(disc_curve, credit_curve)floatPar spread (same as .spread)
.npv(disc_curve, credit_curve)floatNet present value
.spread(disc_curve, credit_curve)floatPar spread
.cashflows(disc_curve, credit_curve)DataFramePeriod-level cashflow table
.accrued(today=None)floatAccrued premium
.analytic_rec_risk(disc_curve, credit_curve)floatRecovery risk
.jtd(today=None)floatJump-to-default exposure
.ead()floatExposure at default
.upfront(disc_curve, credit_curve)floatUpfront payment amount
.to_upfront_spread(disc_curve, credit_curve)floatConvert running to upfront spread

Example

import datetime
from vade import CDS, DiscountCurve, CreditImpliedCurve

disc_nodes = {
    datetime.date(2024, 1, 1): 1.0,
    datetime.date(2025, 1, 1): 0.97,
    datetime.date(2026, 1, 1): 0.94,
    datetime.date(2029, 1, 1): 0.85,
}
disc_curve = DiscountCurve(disc_nodes, interpolation="log_linear", convention="act365f")

credit_nodes = {
    datetime.date(2024, 1, 1): 1.0,
    datetime.date(2025, 1, 1): 0.99,
    datetime.date(2026, 1, 1): 0.98,
    datetime.date(2029, 1, 1): 0.95,
}
credit_curve = CreditImpliedCurve(credit_nodes, convention="act365f", recovery_rate=0.4)

cds = CDS(
    effective=datetime.date(2024, 1, 1),
    termination="5Y",
    fixed_rate=100.0,
    frequency="q",
    convention="act360",
    recovery_rate=0.4,
)
cds.npv(disc_curve, credit_curve)  # net present value
cds.spread(disc_curve, credit_curve)  # par spread in basis points
cds.ead()  # exposure at default

XCS

Cross Currency Swap with 4-curve pricing model. Python wrapper composing legs across currencies.

See also: Cross-Currency Swaps Guide for cross-currency pricing workflows.

Alias: XCS = CrossCurrencySwap

Constructor

XCS(
    *,
    effective=None,
    termination=None,
    frequency="q",
    leg1_currency="EUR",
    leg2_currency="USD",
    leg1_notional=1_000_000.0,
    leg2_notional=1_100_000.0,
    initial_fx_rate=1.1,
    fixed_rate=None,
    notional_exchange=True,
    convention="act360",
    leg2_convention=None,
    fixing_method="rfr_payment_delay",
    spread=0.0,
    modifier="mf",
    stub="shortfront",
    mtm_leg=None,
)

Parameters

NameTypeDefaultDescription
effectivedatetime.date or NoneNoneStart date
terminationdate, str, or NoneNoneEnd date or tenor string
frequencystr"q"Payment frequency
leg1_currencystr"EUR"Leg 1 currency code
leg2_currencystr"USD"Leg 2 currency code
leg1_notionalfloat1_000_000.0Leg 1 notional amount
leg2_notionalfloat1_100_000.0Leg 2 notional amount
initial_fx_ratefloat1.1Initial FX rate (leg2/leg1)
fixed_ratefloat or NoneNoneFixed rate on one leg (if applicable)
notional_exchangeboolTrueWhether to exchange notionals at start and end
conventionstr"act360"Day count convention for leg 1
leg2_conventionstr or NoneNoneDay count convention for leg 2 (defaults to convention)
fixing_methodstr"rfr_payment_delay"Rate fixing method
spreadfloat0.0Spread on leg 2 (basis points)
modifierstr"mf"Business day adjustment rule
stubstr"shortfront"Stub period preference
mtm_legint or NoneNoneMark-to-market reset leg (1 or 2)

See Conventions for accepted values for frequency, convention, fixing_method, modifier, and stub.

Methods

XCS methods require four separate curves (discount and forecast for each currency) plus FXRates.

MethodReturnsDescription
.rate(leg1_disc, leg1_forecast, leg2_disc, leg2_forecast, fx_rates, metric="leg2")floatCross-currency basis spread
.npv(leg1_disc, leg1_forecast, leg2_disc, leg2_forecast, fx_rates)floatNet present value
.cashflows(leg1_disc, leg1_forecast, leg2_disc, leg2_forecast)DataFramePeriod-level cashflow table

Example

import datetime
from vade import XCS, DiscountCurve, FXRates

eur_disc = DiscountCurve(
    {datetime.date(2024, 1, 1): 1.0, datetime.date(2025, 1, 1): 0.97, datetime.date(2026, 1, 1): 0.94},
    interpolation="log_linear", convention="act365f", id="eur_disc",
)
eur_fcast = DiscountCurve(
    {datetime.date(2024, 1, 1): 1.0, datetime.date(2025, 1, 1): 0.97, datetime.date(2026, 1, 1): 0.94},
    interpolation="log_linear", convention="act365f", id="eur_fcast",
)
usd_disc = DiscountCurve(
    {datetime.date(2024, 1, 1): 1.0, datetime.date(2025, 1, 1): 0.96, datetime.date(2026, 1, 1): 0.92},
    interpolation="log_linear", convention="act365f", id="usd_disc",
)
usd_fcast = DiscountCurve(
    {datetime.date(2024, 1, 1): 1.0, datetime.date(2025, 1, 1): 0.96, datetime.date(2026, 1, 1): 0.92},
    interpolation="log_linear", convention="act365f", id="usd_fcast",
)
fx = FXRates({"eurusd": 1.10}, settlement=datetime.date(2024, 1, 1))

xcs = XCS(
    effective=datetime.date(2024, 1, 1),
    termination="2Y",
    frequency="q",
    leg1_currency="EUR",
    leg2_currency="USD",
    leg1_notional=1_000_000.0,
    leg2_notional=1_100_000.0,
    initial_fx_rate=1.10,
    convention="act360",
)
float(xcs.rate(eur_disc, eur_fcast, usd_disc, usd_fcast, fx))  # cross-currency basis spread
float(xcs.npv(eur_disc, eur_fcast, usd_disc, usd_fcast, fx))  # NPV in base currency

FXForward

FX Forward instrument. Rust-backed.

See also: FX Rates & Forwards Guide for FX forward pricing.

Constructor

FXForward(
    *,
    pair="eurusd",
    notional=1_000_000.0,
    delivery=None,
    currency="USD",
)

Parameters

NameTypeDefaultDescription
pairstr"eurusd"Currency pair (e.g., "eurusd", "gbpusd")
notionalfloat1_000_000.0Notional amount
deliverydatetime.date or NoneNoneDelivery date
currencystr"USD"Settlement currency

Methods

FXForward methods take a discount curve, forecast curve, and FXRates.

MethodReturnsDescription
.rate(disc_curve, forecast_curve, fx_rates)floatForward FX rate
.npv(disc_curve, forecast_curve, fx_rates)floatNet present value
.cashflows(disc_curve, forecast_curve, fx_rates)DataFramePeriod-level cashflow table

Example

import datetime
from vade import FXForward, DiscountCurve, FXRates

eur_curve = DiscountCurve(
    {datetime.date(2024, 1, 1): 1.0, datetime.date(2025, 1, 1): 0.97},
    interpolation="log_linear", convention="act365f", id="eur_curve",
)
usd_curve = DiscountCurve(
    {datetime.date(2024, 1, 1): 1.0, datetime.date(2025, 1, 1): 0.96},
    interpolation="log_linear", convention="act365f", id="usd_curve",
)
fx = FXRates({"eurusd": 1.10}, settlement=datetime.date(2024, 1, 1))

fxf = FXForward(
    pair="eurusd",
    notional=1_000_000.0,
    delivery=datetime.date(2024, 7, 1),
    currency="USD",
)
float(fxf.rate(usd_curve, eur_curve, fx))  # forward FX rate
float(fxf.npv(usd_curve, eur_curve, fx))  # NPV of the forward position

Non-Deliverable Instruments

Non-deliverable (ND) instruments settle in a convertible currency rather than the restricted currency in which cashflows accrue. FX conversion uses per-period fixings (known or IRP-implied).


NDF

Non-Deliverable Forward: single-currency net settlement of an FX forward. Subclass of FXForward.

See also: Non-Deliverable Instruments Guide for NDF, NDIRS, and NDXCS workflows.

Constructor

NDF(
    *,
    spec=None,           # Market convention ("usdbrl_ndf", "usdkrw_ndf", ...)
    effective=None,      # Effective / trade date
    delivery=None,       # Settlement date
    pair="usdbrl",       # FX pair (restricted vs settlement)
    notional=1_000_000.0,
    currency="USD",      # Notional currency
    settlement_currency="usd",
    contracted_rate=None,  # Agreed FX rate (None = at-market)
    fx_fixings=None,     # dict[date, float] for known fixings
)

Methods

MethodSignatureReturns
rate(disc_curve, forecast_curve, fx_rates)Implied forward rate (IRP)
npv(disc_curve, forecast_curve, fx_rates)Net settlement NPV in settlement currency
cashflows(disc_curve, forecast_curve, fx_rates)Polars DataFrame
spread(disc_curve, forecast_curve, fx_rates)Spread to contracted rate
pillar_date()Delivery date
to_json()JSON-serializable dict
from_json(data)Reconstruct NDF from dict

Example

import datetime
from vade import NDF, DiscountCurve, FXRates

ndf = NDF(
    spec="usdbrl_ndf",
    effective=datetime.date(2025, 6, 16),
    delivery=datetime.date(2025, 9, 16),
    notional=1_000_000.0,
    contracted_rate=5.25,
)

usd = DiscountCurve(
    {datetime.date(2025, 6, 16): 1.0, datetime.date(2026, 6, 16): 0.96},
    interpolation="log_linear", convention="act360",
)
brl = DiscountCurve(
    {datetime.date(2025, 6, 16): 1.0, datetime.date(2026, 6, 16): 0.88},
    interpolation="log_linear", convention="act360",
)
fxr = FXRates({"usdbrl": 5.20}, settlement=datetime.date(2025, 6, 16))

fair_rate = ndf.rate(usd, brl, fxr)
npv = ndf.npv(usd, brl, fxr)
assert abs(float(fair_rate)) < 10
assert abs(float(npv)) < 1e8

NDIRS

Non-Deliverable Interest Rate Swap: IRS where cashflows accrue in a restricted currency but settle in a convertible currency via per-period FX conversion. Subclass of InterestRateSwap.

Uses a 3-curve model: settlement discount, restricted forecast, restricted discount.

Constructor

NDIRS(
    *,
    spec=None,             # Market convention ("usdbrl_ndirs", ...)
    effective=None,
    termination=None,      # date or tenor string ("5Y")
    frequency="s",
    fixed_rate=0.0,
    notional=1_000_000.0,
    convention="act360",
    settlement_currency="usd",
    pair="usdbrl",
    fx_fixings=None,       # dict[date, float] known FX fixings
)

Methods

MethodSignatureReturns
rate(curves=[disc, forecast, leg2_disc], fx_rates=fxr)Par fixed rate
npv(curves=[disc, forecast, leg2_disc], fx_rates=fxr)NPV in settlement currency
cashflows(curves=[disc, forecast, leg2_disc], fx_rates=fxr)Polars DataFrame with settlement columns
spread(curves=[disc, forecast, leg2_disc], fx_rates=fxr)Par spread on float leg
pillar_date()Maturity date
to_json()JSON-serializable dict
from_json(data)Reconstruct NDIRS from dict

Curve arguments: disc_curve = settlement discount, forecast_curve = restricted forecast, leg2_disc_curve = restricted discount (for IRP forwards).

Example

import datetime
from vade import NDIRS, DiscountCurve, FXRates

ndirs = NDIRS(
    spec="usdbrl_ndirs",
    effective=datetime.date(2025, 6, 16),
    termination="2Y",
    fixed_rate=10.0,
    notional=1_000_000.0,
)

usd = DiscountCurve(
    {datetime.date(2025, 6, 16): 1.0, datetime.date(2028, 6, 16): 0.89},
    interpolation="log_linear", convention="act360",
)
brl = DiscountCurve(
    {datetime.date(2025, 6, 16): 1.0, datetime.date(2028, 6, 16): 0.71},
    interpolation="log_linear", convention="act360",
)
fxr = FXRates({"usdbrl": 5.20}, settlement=datetime.date(2025, 6, 16))

par_rate = ndirs.rate(curves=[usd, brl, brl], fx_rates=fxr)
npv = ndirs.npv(curves=[usd, brl, brl], fx_rates=fxr)
assert 0 < float(par_rate) < 30
assert abs(float(npv)) < 1e8

NDXCS

Non-Deliverable Cross-Currency Swap: one leg is non-deliverable with cashflows converted via per-period FX fixings and settled in a convertible currency. Supports notional exchanges and MTM resets on the ND leg. Subclass of CrossCurrencySwap.

Constructor

NDXCS(
    *,
    spec=None,               # Market convention ("usdbrl_ndxcs", ...)
    effective=None,
    termination=None,        # date or tenor string ("5Y")
    frequency="q",
    leg1_currency="USD",
    leg2_currency="BRL",
    leg1_notional=1_000_000.0,
    leg2_notional=5_000_000.0,
    initial_fx_rate=5.0,
    fixed_rate=None,         # None = float-float
    notional_exchange=True,
    settlement_currency="usd",
    nd_leg=2,                # Which leg is non-deliverable (1 or 2)
    pair=None,               # FX pair (derived from currencies if None)
    fx_fixings=None,         # dict[date, float] for ALL FX fixing needs
    mtm_leg=None,            # Optional MTM notional reset leg
)

Methods

MethodSignatureReturns
rate(leg1_disc, leg1_forecast, leg2_disc, leg2_forecast, fx_rates)Par basis spread on ND leg
npv(leg1_disc, leg1_forecast, leg2_disc, leg2_forecast, fx_rates)NPV in settlement currency
cashflows(leg1_disc, leg1_forecast, leg2_disc, leg2_forecast, fx_rates)Polars DataFrame with settlement columns
spread(leg1_disc, leg1_forecast, leg2_disc, leg2_forecast, fx_rates)Par floating spread
pillar_date()Maturity date
to_json()JSON-serializable dict
from_json(data)Reconstruct NDXCS from dict

Example

import datetime
from vade import NDXCS, DiscountCurve, FXRates

ndxcs = NDXCS(
    effective=datetime.date(2025, 6, 16),
    termination="2Y",
    leg1_currency="USD",
    leg2_currency="BRL",
    settlement_currency="usd",
    nd_leg=2,
    pair="usdbrl",
    leg1_notional=1_000_000.0,
    leg2_notional=5_000_000.0,
    initial_fx_rate=5.0,
    frequency="q",
    convention="act360",
)

usd = DiscountCurve(
    {datetime.date(2025, 6, 16): 1.0, datetime.date(2028, 6, 16): 0.89},
    interpolation="log_linear", convention="act360",
)
brl = DiscountCurve(
    {datetime.date(2025, 6, 16): 1.0, datetime.date(2028, 6, 16): 0.71},
    interpolation="log_linear", convention="act360",
)
fxr = FXRates({"usdbrl": 5.20}, settlement=datetime.date(2025, 6, 16))

rate = ndxcs.rate(usd, usd, brl, brl, fxr)
npv = ndxcs.npv(usd, usd, brl, brl, fxr)
assert abs(float(rate)) < 500
assert abs(float(npv)) < 1e8

See Curves for curve types used in pricing, Solver for multi-curve calibration, and FX for FX rate framework.

On this page

IRSConstructorParametersMethodsExampleFRAConstructorParametersMethodsExampleZCSConstructorParametersMethodsExampleSBSConstructorParametersMethodsExampleOISConstructorParametersMethodsExampleDepositConstructorParametersMethodsExampleIRFutureConstructorParametersMethodsExampleCapFloorConstructorParametersMethodsExampleFixedRateBondConstructorParametersMethodsStandard MethodsPrice and YieldRisk AnalyticsExampleBillConstructorParametersMethodsExampleAdvanced ExampleFloatRateNoteConstructorParametersMethodsExampleSubPeriodFRNConstructorParametersMethodsStandard MethodsPrice and YieldRisk AnalyticsSubPeriodFRN-SpecificExampleAdvanced ExampleZeroCouponBondConstructorParametersMethodsStandard MethodsPrice and YieldRisk AnalyticsExampleAdvanced ExampleStepUpBondConstructorParametersMethodsStandard MethodsPrice and YieldRisk AnalyticsExampleAdvanced ExampleAmortizingBondConstructorParametersMethodsStandard MethodsPrice and YieldRisk AnalyticsExampleAdvanced ExamplePIKBondConstructorParametersMethodsStandard MethodsPrice and YieldRisk AnalyticsPIK-SpecificExampleAdvanced ExampleCappedFloatRateNoteConstructorParametersMethodsStandard MethodsPrice and YieldRisk AnalyticsCappedFRN-SpecificExampleAdvanced ExampleAssetSwapConstructorParametersMethodsExampleCallableBondConstructorParametersMethodsStandard Methods (delegated to underlying bond)Tree-Based Methods (Hull-White)ExampleCDSConstructorParametersMethodsExampleXCSConstructorParametersMethodsExampleFXForwardConstructorParametersMethodsExampleNon-Deliverable InstrumentsNDFConstructorMethodsExampleNDIRSConstructorMethodsExampleNDXCSConstructorMethodsExample