Index CDS
This page lists everything you need to find an index series and book and value an index CDS: the series functions, the contract's arguments and defaults, how it's priced, and an example you can run. An index CDS buys or sells protection on a standard basket of names, such as the 125 names of CDX.NA.IG, in one contract. Series, rolls and the index curve are 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 finds the CDX.NA.IG series on the run at the SOFR close, when it rolled and where its five-year contract matures. It then pins a 1.2% flat hazard for the index under the series' identity and values five years of protection at the family's coupon.
use chrono::{Datelike, NaiveDate};
use helixio_docs_sheets::{SOFR_CLOSE_DATE, USD_SOFR_CLOSE};
use helixrs::{
AnyInstrument, CollateralSpec, CreditCurveId, CreditIndexFamily, DayCountFraction, FlatHazard,
IndexCreditDefaultSwap, MarketContext, QuoteSheet, Survival,
};
type Outcome<T> = Result<T, Box<dyn std::error::Error>>;
fn date((year, month, day): (i32, u32, u32)) -> NaiveDate {
NaiveDate::from_ymd_opt(year, month, day).expect("a real calendar date")
}
const FAMILY: &str = "CDX.NA.IG";
const HAZARD: f64 = 0.012;
const RECOVERY: f64 = 0.4;
fn main() -> Outcome<()> {
let close = date(SOFR_CLOSE_DATE);
let family = FAMILY.parse::<CreditIndexFamily>()?.template();
// The series on the run at the close, the date it rolled, and where its
// five-year contract matures.
let series = family.on_the_run(close);
let rolled = family.series_roll(series);
let maturity = family.series_maturity(series, 5);
println!("series {series}");
println!("rolled {rolled}");
println!("5Y matures {maturity}");
// The index curve is pinned under the series' identity, FAMILY.SERIESvVERSION.
let identity = format!("{FAMILY}.{series}v1");
let sheet = QuoteSheet::from_rows(USD_SOFR_CLOSE.iter().copied())?;
let credit: CreditCurveId = identity.parse()?;
let survival = Survival::FlatHazard(FlatHazard::new(close, HAZARD, DayCountFraction::Act365Fixed)?);
let market = MarketContext::as_of(close)
.with_recipe_quotes(&CollateralSpec::Currency("USD".parse()?), &sheet)?
.with_credit_curve(credit, survival, Some(RECOVERY));
let index = AnyInstrument::IndexCds(Box::new(
IndexCreditDefaultSwap::standard(&family, close, series, 5, 10_000_000.0, true).with_version(1),
));
let npv = index.npv(&market)?;
let par = index.par_spread(&market)?;
println!("credit {identity}");
println!("npv {npv:.2}");
println!("par spread {par:.6}");
// The buyer pays the family's 100bp coupon for protection worth about 72bp, so
// the contract has a negative value to the buyer.
Ok(())
}import helixio as hlx
from desk_sheets import SOFR_CLOSE_DATE, USD_SOFR_CLOSE
FAMILY = "CDX.NA.IG"
HAZARD, RECOVERY = 0.012, 0.4
# The series on the run at the close, the date it rolled, and where its
# five-year contract matures.
series = hlx.credit.on_the_run(FAMILY, SOFR_CLOSE_DATE)
rolled = hlx.credit.series_roll(FAMILY, series)
maturity = hlx.credit.series_maturity(FAMILY, series, 5)
print(f"series {series}")
print(f"rolled {rolled}")
print(f"5Y matures {maturity}")
# The index curve is pinned under the series' identity, FAMILY.SERIESvVERSION.
identity = f"{FAMILY}.{series}v1"
market = (
hlx.MarketContext(SOFR_CLOSE_DATE)
.with_recipe_quotes("USD", USD_SOFR_CLOSE)
.with_credit_curve(identity, hlx.curves.SurvivalCurve.flat_hazard(SOFR_CLOSE_DATE, HAZARD), recovery=RECOVERY)
)
index = hlx.products.index_credit_default_swap(FAMILY, SOFR_CLOSE_DATE, series, 5, notional=10_000_000.0)
npv = index.npv(market)
par = index.par_spread(market)
print(f"credit {identity}")
print(f"npv {npv:.2f}")
print(f"par spread {par:.6f}")
# The buyer pays the family's 100bp coupon for protection worth about 72bp, so
# the contract has a negative value to the buyer.series 47
rolled 2026-09-20
5Y matures 2031-12-20
credit CDX.NA.IG.47v1
npv -129653.61
par spread 0.007144
The buyer pays the family's 100bp coupon for protection worth about 71bp, so the contract is worth less than nothing to the buyer.
How it's priced
For a family anchored at series rolling on , series rolls on
the series on the run at is the largest with , and its -year contract matures three months after plus years. The legs are those of a single-name CDS on the index curve, at the family's fixed coupon, on the notional times the index factor.
API
| Argument | Type | Default | Meaning |
|---|---|---|---|
family |
str |
required | the index family, "CDX.NA.IG" or "ITRAXX.EUROPE" |
trade_date |
date |
required | the trade date |
series |
int |
required | the series, such as the one credit.on_the_run returns |
tenor_years |
int |
required | whole years, matured three months after the series' roll plus the tenor |
notional |
float |
required | the protected amount before the index factor |
buys_protection |
bool |
True |
False books the seller |
version |
int |
1 |
the index version; each credit event moves the contract to the next |
index_factor |
float |
None |
the remaining notional fraction, in (0, 1], after names have defaulted |
upfront |
float |
None |
the upfront the buyer pays at settlement, as a decimal of the notional |
fixed_recovery |
float |
None |
a recovery the contract fixes; None reads the market's |
protection_start |
date |
None |
a forward start, as on the single-name contract |
The series functions take the family: credit.on_the_run(family, date),
credit.series_roll(family, series) and credit.series_maturity(family, series, years). In Rust
they are methods of CreditIndexTemplate, and the contract is IndexCreditDefaultSwap::standard.
| Symbol | Meaning | Rust | Python |
|---|---|---|---|
| on the run at | the latest series rolled by | CreditIndexTemplate::on_the_run |
credit.on_the_run |
| series 's roll | CreditIndexTemplate::series_roll |
credit.series_roll |
|
| maturity | three months after , plus the tenor | CreditIndexTemplate::series_maturity |
credit.series_maturity |
| the index curve | pinned under FAMILY.SERIESvVERSION |
MarketContext::with_credit_curve |
MarketContext.with_credit_curve |
| the contract | index CDS on a series | IndexCreditDefaultSwap::standard |
products.index_credit_default_swap |
| intrinsic | the index against its constituents | index_intrinsic |
credit.index_intrinsic |
| skew | hazard multipliers that reprice the index from its constituents | index_skew |
credit.index_skew |
Defaults and limits
- The coupon: an index contract always pays its family's standard coupon; there is no coupon argument. Each family's coupon and schedule are in the credit conventions catalog.
- The curve: the contract values on the curve pinned under
FAMILY.SERIESvVERSION, such asCDX.NA.IG.47v1. A new version after a credit event needs its own curve; see credit events. - The maturity is fixed by the series, so an off-the-run contract keeps the maturity it had when it was on the run.
See also
- Source:
crates/helixrs/src/statics/templates/credit.rs,crates/helixrs/src/instruments/credit.rs - Related pages:
- How credit products work, the ideas behind the index.
- Credit default swap, for the legs.
- CDO tranche, for slicing the index pool.
- Credit events, for a default inside the index.
From the 0.0 line at revision bf7f2e2. Every printed number is that revision's own output.