Why do rate datasets disagree about what the number 4 means?
Across the endpoints in this course, a field called rate, value_bps, default_spread, yield_value or market_cmdi can hold a percent, a basis point count, a decimal fraction, an index level or a dollar amount in millions. All of them are numbers. Getting one wrong by a factor of 100 is the most common bug in rates code, and it never throws.
Five conventions, one course
Every figure below is real, read on the same day.
Percent. /ust/yield-rates 10Y on 27 July 2026: rate = 4.65. That means 4.65%, or 0.0465. The whole Treasury family and /rates/policy-rates use this convention, and so does every overnight and average row in /rates/reference-rates — every row there except SOFRINDEX, which the index-level paragraph below picks up.
Basis points. /spreads/funding-stress SOFR_TARGET_LOWER on the same date: value_bps = 14. That means 0.14%, or 0.0014. Note that its own leg_a_rate field, 3.64, is in percent — two units inside a single row, correctly named by the field names.
Decimal fraction. /credit-risk/sovereign/cds-spreads for the United States: cds_spread = 0.0044. That is 0.44%, or 44 basis points. Every numeric spread, premium and tax rate in the /credit-risk/sovereign/* family is stored as a fraction — the ratings in the same family are strings, with no arithmetic defined on them at all. Brazil's corporate_tax_rate there is 0.34, meaning 34%.
Index level. /credit-risk/corporate/cmdi on 19 June 2026: market_cmdi = 0.13. This is not 13 basis points and not 13%. It is a distress index on a 0-to-1 scale. And SOFRINDEX at 1.25249067 from /rates/reference-rates is a cumulative accrual index, not a rate either.
Millions of dollars. /credit-risk/cds-market/aggregates: usd_notional_mn = 2,429,758. That is $2.43 trillion, not $2.4 million.
The conversions, written once
- percent → basis points: × 100 (4.65% = 465 bps)
- percent → decimal: ÷ 100 (4.65% = 0.0465)
- decimal → basis points: × 10,000 (0.0044 = 44 bps)
- basis points → percent: ÷ 100 (14 bps = 0.14%)
A basis point is one hundredth of a percentage point. The reason the market uses it at all is that saying "the spread widened 1%" is genuinely ambiguous — did it go from 4% to 5%, or from 4% to 4.04%? "Widened 100 basis points" and "widened 1%" answer different questions and basis points remove the ambiguity permanently.
Read meta — it is documentation that ships with the payload
The credit endpoints attach a meta block that answers the units question directly. A real one, from /credit-risk/cds-market/aggregates:
"dataset": "cds_market_aggregates", "source": "cftc", "frequency": "weekly",
"units": "millions USD",
"attribution": "Source: CFTC Weekly Swaps Report (public domain).
Values in millions USD. Lag: T+17 days."
Four facts you would otherwise have to guess: what the dataset is, who published it, how often it updates, and how stale the newest row is. The default-spreads endpoint goes further and states the normalisation it applied: "values normalised from basis points to fractions (e.g. 60bp -> 0.006)". That is the provider telling you exactly which of the five conventions above it chose.
The rates and Treasury endpoints have a thinner meta — usually just total and page — but they carry source_series_id per row instead, which points at the upstream publisher's own definition.
The habit
Name your columns with the unit in them. yield_pct, spread_bps, notional_musd, tax_frac. It is ugly and it makes the factor-of-100 bug impossible to write, because the mismatch is visible in the expression itself rather than three charts downstream.
Try it now
- Here are the newest
/ust/yield-ratescurve (the 10Y isdata[-3]) and the US row of/credit-risk/sovereign/cds-spreads. Express the 10Y andcds_spreadboth in basis points. One needs × 100, the other × 10,000.
- Here is the whole
metablock of/credit-risk/cds-market/aggregates, and the two dates on its first data row. Write down itsfrequencyand its stated lag, then check the lag againstas_of_dateandrelease_date.