Credit-linked swap
This page lists everything you need to book and value a credit-linked swap: the function's arguments and defaults, the roles a leg can take, how it's priced, and an example you can run. A credit-linked swap is a set of legs whose cash depends on whether a reference entity survives. What each role means is covered in how credit products work.
Quick example
The example reads its rates from the desk sheets; load them once and it runs as shown.
This books five annual 5% coupons on ten million as one leg against ACME. It values the leg as independent and as contingent on a 2% hazard, then contingent where default can't happen, then under each rule for the accrual a default interrupts.
use std::sync::Arc;
use chrono::NaiveDate;
use helixio_docs_sheets::{SOFR_CLOSE_DATE, USD_SOFR_CLOSE};
use helixrs::{
AccrualOnDefault, AnyInstrument, CollateralSpec, CreditLinkedSwap, Currency, DayCountFraction,
DefaultPaymentTime, Direction, FlatHazard, LegRole, LegTerms, MarketContext, PaymentRule,
QuoteSheet, RoleLeg, Schedule, Survival, fixed_rate_leg,
};
type Outcome<T> = Result<T, Box<dyn std::error::Error>>;
const ENTITY: &str = "ACME/SeniorUnSec/XR14/USD";
fn date((year, month, day): (i32, u32, u32)) -> NaiveDate {
NaiveDate::from_ymd_opt(year, month, day).expect("a real calendar date")
}
fn main() -> Outcome<()> {
let close = date(SOFR_CLOSE_DATE);
let usd: Currency = "USD".parse()?;
let rates = MarketContext::as_of(close).with_recipe_quotes(
&CollateralSpec::Currency(usd),
&QuoteSheet::from_rows(USD_SOFR_CLOSE.iter().copied())?,
)?;
// The SOFR market with ACME on a flat hazard curve, recovering 40%.
let credit = |hazard: f64| -> Outcome<MarketContext> {
let curve = Survival::FlatHazard(FlatHazard::new(close, hazard, DayCountFraction::Act365Fixed)?);
Ok(rates
.clone()
.with_credit_curve(ENTITY.parse()?, Arc::new(curve), Some(0.40)))
};
// Five annual 5% coupons on ten million, received.
let schedule = Schedule::from_dates((0..6).map(|i| date((2026 + i, 10, 5))).collect())?;
let terms = LegTerms {
notional: 10_000_000.0,
day_count: DayCountFraction::Thirty360,
currency: usd,
direction: Direction::Receive,
payment: PaymentRule::none(),
};
let coupons = fixed_rate_leg(&schedule, &terms, 0.05);
// The coupons as one leg of a credit-linked swap on ACME, in `role`.
let swap = |role: LegRole, accrual: AccrualOnDefault| -> Outcome<AnyInstrument> {
Ok(AnyInstrument::CreditLinkedSwap(Box::new(CreditLinkedSwap::new(
ENTITY.parse()?,
usd,
CollateralSpec::Currency(usd),
vec![RoleLeg::new(coupons.clone(), role, CollateralSpec::Currency(usd))],
accrual,
None,
DefaultPaymentTime::AtDefault,
)?)))
};
// On a 2% hazard, the independent leg is worth what it is without credit, and
// the contingent leg less: each coupon is weighted by the chance ACME survives.
let (risky, riskless) = (credit(0.02)?, credit(0.0)?);
let independent = swap(LegRole::Independent, AccrualOnDefault::Halfway)?.npv(&risky)?;
let contingent = swap(LegRole::Contingent, AccrualOnDefault::Halfway)?.npv(&risky)?;
println!("independent {independent:.2}");
println!("contingent {contingent:.2}");
// Where default cannot happen, the contingent leg is the independent one.
let certain = swap(LegRole::Contingent, AccrualOnDefault::Halfway)?.npv(&riskless)?;
println!("contingent {certain:.2}");
// Settling a coupon's accrual to the default date, by the halfway rule, pays
// part of the coupon a default interrupts; settling none pays nothing of it.
let halfway = swap(LegRole::Contingent, AccrualOnDefault::Halfway)?.npv(&risky)?;
let none = swap(LegRole::Contingent, AccrualOnDefault::None)?.npv(&risky)?;
println!("halfway {halfway:.2}");
println!("none {none:.2}");
let worth = halfway - none;
println!("accrual worth {worth:.2}");
Ok(())
}import datetime as dt
import helixio as hlx
from desk_sheets import SOFR_CLOSE_DATE, USD_SOFR_CLOSE
ENTITY = "ACME/SeniorUnSec/XR14/USD"
rates = hlx.MarketContext(SOFR_CLOSE_DATE).with_recipe_quotes("USD", USD_SOFR_CLOSE)
def credit(hazard):
"""The SOFR market with ACME on a flat hazard curve, recovering 40%."""
return rates.with_credit_curve(ENTITY, hlx.curves.SurvivalCurve.flat_hazard(SOFR_CLOSE_DATE, hazard), 0.40)
# Five annual 5% coupons on ten million, received.
schedule = hlx.dates.Schedule.from_dates([dt.date(2026 + i, 10, 5) for i in range(6)])
coupons = hlx.legs.fixed_rate_leg(
schedule, schedule.dates()[1:], "USD", notional=10_000_000.0, fixed_rate=0.05, day_count="30/360"
)
def swap(role, **kw):
"""The coupons as one leg of a credit-linked swap on ACME, in `role`."""
return hlx.products.credit_linked_swap(ENTITY, "USD", [(hlx.Leg(coupons, "Receive", "USD"), role)], **kw)
# On a 2% hazard, the independent leg is worth what it is without credit, and
# the contingent leg less: each coupon is weighted by the chance ACME survives.
risky, riskless = credit(0.02), credit(0.0)
independent = swap("independent").npv(risky)
contingent = swap("contingent").npv(risky)
print(f"independent {independent:.2f}")
print(f"contingent {contingent:.2f}")
# Where default cannot happen, the contingent leg is the independent one.
certain = swap("contingent").npv(riskless)
print(f"contingent {certain:.2f}")
# Settling a coupon's accrual to the default date, by the halfway rule, pays
# part of the coupon a default interrupts; settling none pays nothing of it.
halfway = swap("contingent", settles_accrual=True).npv(risky)
none = swap("contingent", settles_accrual=False).npv(risky)
print(f"halfway {halfway:.2f}")
print(f"none {none:.2f}")
worth = halfway - none
print(f"accrual worth {worth:.2f}")independent 2179956.34
contingent 2078431.05
contingent 2179956.34
halfway 2078431.05
none 2057191.45
accrual worth 21239.60
Credit leaves the independent leg alone and lowers the contingent one. Where default can't happen, the two agree. Settling the interrupted coupon's accrual by the halfway rule is the default, and it is worth more than settling nothing.
How it's priced
An independent leg is valued as it is. A contingent leg's flow at is weighted by the survival probability . With accrual settled on default, the period in which default falls also pays half its coupon, weighted by the probability of default in the period, . A default leg pays on default scaled by the loss , and a recovery leg scaled by .
API
| Argument | Type | Default | Meaning |
|---|---|---|---|
reference |
str |
required | the entity's full identity, such as "ACME/SeniorUnSec/XR14/USD" |
currency |
str |
required | the trade's currency |
legs |
list[tuple[Leg, LegRole or str]] |
required | each leg as built, with its role: "independent", "contingent", "default" or "recovery" |
settles_accrual |
bool |
None, read as True |
True settles a contingent coupon's accrual to default by the halfway rule, False settles none |
accrual_on_default |
AccrualOnDefault or str |
None |
names the accrual rule instead, "proportional" among them; not with settles_accrual |
fixed_recovery |
float |
None |
a recovery the contract fixes; None reads the market's |
default_payment_time |
DefaultPaymentTime or str |
"at_default" |
when a default payment is made: "at_default", "at_period_end" or "at_maturity" |
In Rust the trade is CreditLinkedSwap::new(credit, currency, collateral, legs, accrual, recovery, payment_time), each leg a RoleLeg::new(leg, LegRole::Contingent, collateral).
| Symbol | Meaning | Rust | Python |
|---|---|---|---|
| the trade | legs tagged by role against one entity | CreditLinkedSwap::new |
products.credit_linked_swap |
| the role | how a leg depends on default | RoleLeg::new(leg, LegRole::Contingent, ..) |
(leg, "contingent") |
| , | the entity's survival and recovery | MarketContext::with_credit_curve |
MarketContext.with_credit_curve |
| its value | AnyInstrument::npv |
Instrument.npv |
Defaults and limits
- Accrual: give
settles_accrualoraccrual_on_default, not both. With neither, a contingent coupon settles its accrual by the halfway rule. - Currencies: a leg in another currency prices on the entity's curve in that currency and converts at spot.
- Conventions: the accrual and recovery conventions a credit trade states are in the credit conventions catalog.
- The legs: build each leg as on legs and coupons; the swap takes it as built, on its own discount key.
See also
- Source:
crates/helixrs/src/instruments/credit_linked.rs - Related pages:
- How credit products work, the ideas behind the roles.
- Credit-linked note, for the funded form.
- Credit default swap, for the protection a contingent leg sells.
- Survival curves, for the entity's curve.
From the 0.0 line at revision bf7f2e2. Every printed number is that revision's own output.