Accrued Interest and Settlement¶
One-line definition¶
The gap between a bond's quoted (clean) price and the cash a buyer actually pays (dirty price), made up of interest the seller has earned but not yet been paid.
Formula¶
accrued = face * coupon_rate / frequency * (elapsed / full period)
dirty = clean + accrued
invoice = face / 100 * dirty # quotes are per 100 of face
elapsed and full period are both measured with the bond's day-count
convention, so the ratio is the share of the coupon period that has passed.
Day-count conventions¶
The convention is part of the instrument's terms, not a formatting detail. The same dates and the same coupon give different accrued interest under each one:
| Convention | Elapsed days | Year basis |
|---|---|---|
| 30/360 | every month treated as 30 days | 360 |
| ACT/360 | actual calendar days | 360 |
| ACT/365 | actual calendar days | 365 |
| ACT/ACT | actual calendar days | 365, or 366 in a leap year |
30/360 carries two end-of-month adjustments, and their order matters: a
start date on the 31st collapses to the 30th first, and only then does an end
date on the 31st collapse. Adjusting the end date first gives a different
answer when both fall on a 31st — tests/test_settlement.py pins this.
Interpretation¶
Buy a 5% semi-annual bond exactly halfway through its coupon period and you pay the quoted price plus half a coupon — 1.25 per 100 of face. You get the whole 2.50 coupon at the next payment date, so the accrued payment simply compensates the seller for the part of the period they held it. Accrued is zero on a coupon date and rises linearly to a full coupon just before the next one.
Why PMs care¶
Every other page in this section works in clean prices, because that is what gets quoted. Cash does not move in clean prices. Position values, settlement amounts, and any reconciliation against a custodian or counterparty need the dirty price — and a wrong day-count convention produces a number that is confidently wrong rather than obviously broken, which is exactly the kind of error that survives review.
In this repo¶
src/pm/fixed_income/settlement.py — day_count_fraction,
accrued_interest, dirty_price, clean_price, invoice_amount. An
unknown convention raises rather than silently defaulting, on the principle
that a wrong convention is a wrong number.
ACT/ACT here is the simple year-basis approximation, not full ISDA
ACT/ACT, which splits a period across year boundaries. That matters for
periods spanning a year end; the docstring says so rather than implying
otherwise.
Common mistakes¶
- Assuming a day-count convention instead of reading it from the
instrument's terms.
day_count_fractionraises on an unknown convention for exactly this reason — a wrong convention produces a confidently wrong number rather than an obvious failure. - Applying the 30/360 end-of-month adjustments in the wrong order. The
start date collapses from the 31st to the 30th first; only then may
the end date. Reversing it changes the answer when both fall on a
31st, and
tests/test_settlement.pypins the case. - Using this module's
ACT/ACTwhere full ISDA ACT/ACT is required. This is the simple year-basis approximation and does not split a period across a year boundary. - Adding accrued interest to a price already quoted dirty, producing a double count. Check which convention the quote is on before adjusting.