Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Instruments and Trades

QuantSupport models a contract at three levels: cashflows grouped into legs, an instrument that owns one or more legs (or an option payoff), and a trade that adds the economic position. This chapter documents the building blocks and lists every instrument in the library with its builder and trade type.

Cashflows

pub trait Cashflow<T: Scalar> {
    fn amount(&self) -> Result<T>;
    fn payment_date(&self) -> Date;
}

pub enum CashflowType<T: Scalar> {
    FixedRateCoupon(FixedRateCoupon<T>),            // notional × (compound(rate, accrual) − 1)
    FloatingRateCoupon(FloatingRateCoupon<T>),      // notional × (fixing + spread) × accrual
    OptionEmbeddedCoupon(OptionEmbeddedCoupon<T>),  // floating coupon with caplet/floorlet strikes
    Redemption(SimpleCashflow<f64>),                // principal repayment
    Disbursement(SimpleCashflow<f64>),              // principal paid out at start (loans/bonds)
    ConstantAmount(SimpleCashflow<f64>),
    OptionEmbeddedCashflow(OptionEmbeddedCashflow<T>),
}

FixedRateCoupon carries an InterestRate<T> (rate + RateDefinition = day counter, compounding, frequency); its amount() is notional × (compound_factor − 1). FloatingRateCoupon stores fixing/accrual dates, the forward index and a spread; its amount is undefined until a fixing is supplied by the pricer, which is why amount() returns Result. CashflowType<f64> and CashflowType<DualFwd> convert into each other with .into(), so instruments built in f64 can be priced with AAD.

Legs

Leg<T> groups cashflows that share a currency, side and set of indices:

FieldMeaning
id: usizeleg identifier used by pricers (CashflowsTable rows, XVA leg_id)
cashflows: Vec<CashflowType<T>>ordered cashflows
currency: Currencypayment currency
discount_index: Option<MarketIndex>explicit discount curve override
forward_index: Option<MarketIndex>index fixing floating coupons
spread: Option<T>, interest_rate: Option<InterestRate<T>>floating spread / fixed rate
side: SidePayShort or LongReceive
is_linear: boolfalse when option-embedded coupons are present
asset_class: AssetClassFixedIncome, InterestRate, Equity, Fx, Credit, Other
first_payment_date, last_payment_dateused by bootstrappers to order pillars

MakeLeg

Every multi-leg instrument builder delegates to MakeLeg<T>:

let leg = MakeLeg::<DualFwd>::default()
    .with_start_date(Date::new(2024, 1, 1))
    .with_end_date(Date::new(2025, 1, 1))        // or .with_tenor(Period::from_str("1Y")?)
    .with_notional(100_000.0)
    .with_rate(InterestRate::from_rate_definition(DualFwd::new(0.05),
        RateDefinition::new(DayCounter::Actual360, Compounding::Simple, Frequency::Annual)))
    .with_rate_type(RateType::Fixed)              // or RateType::Floating + with_forward_index / with_spread
    .with_side(Side::PayShort)
    .with_currency(Currency::USD)
    .with_payment_frequency(Frequency::Semiannual)
    .with_calendar(Some(Calendar::NullCalendar))
    .with_business_day_convention(Some(BusinessDayConvention::ModifiedFollowing))
    .with_date_generation_rule(Some(DateGenerationRule::Backward))
    .with_discount_index(Some(MarketIndex::SOFR))
    .bullet()
    .build()?;

Payment structures (PaymentStructure):

MethodStructureNotes
.bullet()coupons + single redemption at maturitydefault for swaps
.equal_redemptions()principal amortised in equal amounts, coupons on outstanding notional
.equal_payments()constant coupon + principal instalmentsfixed legs only (error on floating)
.zero()one payment at maturityforces Frequency::Once
.other()custom with_disbursements(HashMap<Date,f64>) / with_redemptions(HashMap<Date,f64>)forces Frequency::OtherFrequency

Optional extras: with_first_coupon_date, with_end_of_month, with_leg_id, with_asset_class, with_caplet_strike/with_floorlet_strike (turns a floating leg into option-embedded coupons; not allowed on fixed legs). build() fails with ValueNotSetErr("Rate type") and similar messages when a required field is missing, and with InvalidValueErr for inconsistent combinations.

Instruments and trades

An instrument holds the legs and static terms; a trade wraps it with trade_date, notional and Side:

pub struct Swap<T: Scalar> { fixed_leg, floating_leg, forward_index, currency, ... }
impl<T: Scalar> Swap<T> {
    pub fn fixed_leg(&self) -> &Leg<T>;
    pub fn floating_leg(&self) -> &Leg<T>;
    pub fn forward_index(&self) -> MarketIndex;
    pub const fn currency(&self) -> Currency;
}

pub struct SwapTrade<T: Scalar> { instrument: Swap<T>, trade_date: Date, notional: f64, side: Side }
impl<T: Scalar> SwapTrade<T> {
    pub const fn new(instrument: Swap<T>, trade_date: Date, notional: f64, side: Side) -> Self;
    pub const fn notional(&self) -> f64;
}

Side::LongReceive means the trade receives the fixed leg (for swaps) / owns the instrument; Side::PayShort is the mirror. Side::sign() returns +1.0/-1.0 and pricers multiply by it. Trades implement Instrument (identifier()), Discountable (asset class, currency, optional discount index) and, for exposure simulation, IntoContingentClaims.

Catalogue

Asset classInstrumentBuilderTradeDeterministic pricer
RatesSwap (fixed vs float)MakeSwapSwapTradeDiscountedCashflowPricer
RatesBasisSwap (float vs float)MakeBasisSwapBasisSwapTradeDiscountedCashflowPricer
RatesFixFloatCrossCurrencySwapMakeFixFloatCrossCurrencySwapFixFloatCrossCurrencySwapTradeDiscountedCashflowPricer
RatesFloatFloatCrossCurrencySwapMakeFloatFloatCrossCurrencySwapFloatFloatCrossCurrencySwapTradeDiscountedCashflowPricer
RatesCapFloorMakeCapFloorCapFloorTradeClosedFormBlackCapPricer, ClosedFormHullWhiteCapPricer
RatesCapletFloorlet— (from quotes)CapletFloorletTradeClosedFormBlackCapletPricer, ClosedFormHullWhiteCapletPricer
RatesEuropeanSwaptionMakeSwaptionEuropeanSwaptionTrade<DualFwd>ClosedFormHullWhiteSwaptionPricer
RatesRateFuturesMakeRateFuturesRateFuturesTradeRateFuturesPricer
Fixed incomeFixedRateBondMakeFixedRateBondFixedRateBondTradeDiscountedCashflowPricer
Fixed incomeFloatingRateNoteMakeFloatingRateNoteFloatingRateNoteTradeDiscountedCashflowPricer
Fixed incomeFixedRateDepositMakeFixedRateDepositFixedRateDepositTradeDiscountedCashflowPricer
FXFxForwardMakeFxForwardFxForwardTradeFxForwardPricer
FXFxOptionMakeFxOptionFxOptionTradeFxOptionPricer (Garman–Kohlhagen)
EquityEquityForwardMakeEquityForwardEquityForwardTrade— (claims / scripting)
EquityEquityEuropeanOptionEquityEuropeanOptionTradeBlackEuropeanOptionPricer, BlackMCEuropeanOptionPricer
EquityFuturesMakeFuturesFuturesTrade— (claims / scripting)
CreditCreditDefaultSwapCdsTradeCdsPricer
AnyScriptedProductscript textScriptEngine (Monte Carlo)

The generic parameter T on rate/fixed-income instruments is f64 or DualFwd; build in f64 when you do not need rate sensitivities and convert with .into() when you do. FX, equity, cap/floor and credit instruments are non-generic and always price in DualFwd internally.

Builder conventions

All Make* builders follow the same pattern as MakeSwap (see Your First Swap):

  • Make*::new() / default() then chained with_* setters that take ownership.
  • build() returns Result<Instrument>; missing mandatory fields produce QSError::ValueNotSetErr("<Field>").
  • Defaults are conservative: Calendar::NullCalendar, BusinessDayConvention::Unadjusted, DateGenerationRule::Backward, spread = 0.0, Side::LongReceive.
  • Cross-currency builders take two currencies, two notionals (or an FX rate to derive one) and per-leg indices; MakeFxForward and MakeFxOption take a currency pair, strike/forward rate and settlement date; MakeCapFloor takes an index, strike, cap/floor flag and schedule parameters; MakeSwaption wraps a MakeSwap plus expiry and settlement type.

The per-product chapters under Pricing show each builder with its required fields and the requests its pricer supports.

Contingent claims

For simulation-based pricing every trade is decomposed into ContingentClaims—atomic payments with a payment date, currency, leg id, side and a ClaimEvaluationStrategy (fixed amount, forward-rate coupon, option payoff, scripted payoff, …). IntoContingentClaims::into_contingent_claims(&self, trade_id: &str) -> Result<Vec<ContingentClaim>> performs the decomposition; the XVA engine, the scripting engine and LgmMarketModel all consume claims rather than instruments. See Exposure.