Cashflow records
This page lists the cashflow table every instrument and every leg reports: its seventeen columns, what each holds and when it's empty, and the basis points by flow that read off the same rows. One row is one coupon, exchange or payment still owed.
Quick example
This reads the table of a seasoned two-year SOFR swap, receiving 4.50% fixed, adds up its rows, and reads the fixed leg's basis points flow by flow. The example reads its quotes and prints from the desk sheets; load them once and it runs as shown.
use chrono::NaiveDate;
use helixio_docs_sheets::{SOFR_CLOSE_DATE, USD_SOFR_CLOSE, USD_SOFR_PRINTS};
use helixrs::{
CollateralSpec, FixingKey, FixingStore, MarketContext, QuoteSheet, Termination, template,
};
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")
}
fn main() -> Outcome<()> {
let close = date(SOFR_CLOSE_DATE);
let sheet = QuoteSheet::from_rows(USD_SOFR_CLOSE.iter().copied())?;
let prints: Vec<(FixingKey, NaiveDate, f64)> = USD_SOFR_PRINTS
.iter()
.map(|(index, day, value)| (FixingKey::Typed((*index).to_owned()), date(*day), *value))
.collect();
let market = MarketContext::as_of(close)
.with_fixings(FixingStore::from_observations_checked(&prints)?)
.with_recipe_quotes(&CollateralSpec::Currency("USD".parse()?), &sheet)?;
// A two-year SOFR swap traded a week before the close, receiving fixed.
let swap_at = |fixed_rate: f64| -> Outcome<_> {
Ok(template("USD-SOFR-OIS")?
.swap(date((2026, 9, 24)))?
.with_maturity("2Y".parse::<Termination>()?)?
.with_notional(10_000_000.0)
.with_fixed_rate(fixed_rate)
.build()?)
};
let swap = swap_at(0.0450)?;
let table = swap.cashflow_records(&market)?;
let columns: Vec<&str> = table.columns().collect();
println!("{}", columns.join(", "));
let text = |name: &str| table[name].text().expect("text");
let numbers = |name: &str| table[name].numbers().expect("numbers");
let dates = |name: &str| table[name].dates().expect("dates");
let (kind, side, pay) = (text("kind"), text("direction"), dates("payment_date"));
let (rate, amount, pv) = (numbers("rate"), numbers("amount"), numbers("pv"));
for i in 0..table.len() {
println!(
"{:<9} {:<7} {} {:.6} {:12.2} {:12.2}",
kind[i],
side[i],
pay[i].expect("a payment date"),
rate[i],
amount[i],
pv[i]
);
}
// The rows add up to the value.
let total: f64 = pv.iter().sum();
let npv = swap.npv(&market)?;
println!("sum of pv {total:.2}");
println!("npv {npv:.2}");
// What one basis point on each row's rate is worth, for the fixed leg. The leg is
// linear in its rate, so its flows add up to the swap restruck a basis point
// higher, less the swap.
let usd = CollateralSpec::Currency("USD".parse()?);
let bps = swap.legs().expect("a swap's legs")[0].bps_by_flow(&market, &usd)?;
let one_bp = swap_at(0.0451)?.npv(&market)? - npv;
let shown: Vec<String> = bps.iter().map(|b| format!("{b:.4}")).collect();
println!("bps by flow {}", shown.join(" "));
println!("sum {:.4}", bps.iter().sum::<f64>());
println!("restruck {one_bp:.4}");
Ok(())
}import datetime as dt
import helixio as hlx
from desk_sheets import SOFR_CLOSE_DATE, USD_SOFR_CLOSE, USD_SOFR_PRINTS
market = (
hlx.MarketContext(SOFR_CLOSE_DATE)
.with_fixings(USD_SOFR_PRINTS)
.with_recipe_quotes("USD", USD_SOFR_CLOSE)
)
def swap_at(fixed_rate):
"""A two-year SOFR swap traded a week before the close, receiving fixed."""
return (
hlx.templates.get("USD-SOFR-OIS")
.swap(dt.date(2026, 9, 24))
.with_maturity("2Y")
.with_notional(10_000_000.0)
.with_fixed_rate(fixed_rate)
.build()
)
swap = swap_at(0.0450)
table = swap.cashflow_records(market)
print(", ".join(table.columns))
rows = table.to_dict()
for kind, side, pay, rate, amount, pv in zip(
rows["kind"], rows["direction"], rows["payment_date"], rows["rate"], rows["amount"], rows["pv"]
):
print(f"{kind:<9} {side:<7} {pay} {rate:.6f} {amount:12.2f} {pv:12.2f}")
# The rows add up to the value, summed in row order.
total = 0.0
for pv in rows["pv"]:
total += pv
npv = swap.npv(market)
print(f"sum of pv {total:.2f}")
print(f"npv {npv:.2f}")
# What one basis point on each row's rate is worth, for the fixed leg. The leg is
# linear in its rate, so its flows add up to the swap restruck a basis point
# higher, less the swap.
bps = swap.legs()[0].bps_by_flow(market)
one_bp = swap_at(0.0451).npv(market) - npv
print("bps by flow " + " ".join(f"{b:.4f}" for b in bps))
print(f"sum {sum(bps):.4f}")
print(f"restruck {one_bp:.4f}")kind, direction, currency, accrual_start, accrual_end, payment_date, fixing_date, notional, year_fraction, rate, amount, discount_factor, pv, settlement_currency, fx_fixing_date, leg, index
Fixed Receive 2027-09-30 0.045000 456250.00 436885.13
Fixed Receive 2028-10-02 0.045000 457500.00 417609.42
Overnight Pay 2027-09-30 0.043800 444085.44 -425236.88
Overnight Pay 2028-10-02 0.047934 487333.97 -444842.09
sum of pv -15584.41
npv -15584.41
bps by flow 970.8558 928.0209
sum 1898.8768
restruck 1898.8768
The rows add up to the swap's value. The fixed leg's flows add up to what restriking the swap one basis point higher is worth.
How it's priced
A row carries an amount , the discount factor to its payment date and the side of its leg ( receive, pay). Its present value and the instrument's value are
A flow's basis-point value is what one basis point on its rate is worth, , and a leg's flows sum to the leg's.
API
| Symbol | Meaning | Rust | Python |
|---|---|---|---|
| the table | an instrument's rows | AnyInstrument::cashflow_records |
Instrument.cashflow_records |
| the table | a leg's rows | leg_cashflow_records inside MarketContext::priced, or Leg::cashflow_records |
Leg.cashflow_records |
| column names | the seventeen columns, in order | CASHFLOW_COLUMNS |
Records.columns |
| basis points by flow | Leg::bps_by_flow |
Leg.bps_by_flow |
|
| the leg's basis points | Leg::bps |
Leg.bps |
Instrument.cashflow_records(...), Leg.cashflow_records(...) and Leg.bps_by_flow(...):
| Argument | Type | Default | Meaning |
|---|---|---|---|
market |
MarketContext |
required | the market the rows are projected and discounted on |
In Rust a leg's reads also take the discount key, as Leg::npv_on does.
The columns
Read the table with Records.to_dict() in Python, or by column name in Rust (table["pv"], then
.numbers(), .dates() or .text()). An empty date is None; an empty number is NaN.
| Column | Type | Unit or values | Sign | Empty when |
|---|---|---|---|---|
kind |
text | Fixed, Ibor, Overnight, Redemption, MultiReset, Cms, CmsSpread, Cpi, CpiRedemption, YoyInflation, RangeAccrual, Settlement |
— | never |
direction |
text | Receive or Pay: the row's leg |
— | never |
currency |
text | the currency the row pays in | — | never |
accrual_start |
date | the start of the accrual period | — | a redemption |
accrual_end |
date | the end of the accrual period | — | a redemption |
payment_date |
date | the day the row pays | — | a reset inside a leg that pays once at the end |
fixing_date |
date | the day a floating rate fixes | — | a fixed coupon, a redemption, an overnight coupon (it fixes daily) |
notional |
number | in currency |
unsigned | never |
year_fraction |
number | the accrual fraction under the coupon's day count | — | the settlement row of a leg that pays once |
rate |
number | decimal: the contract rate, the fixing, or its projection | — | a redemption, a settlement row |
amount |
number | in currency: what this row pays |
unsigned | a reset inside a leg that pays once |
discount_factor |
number | to payment_date; for a converted row, the conversion and the discount in one factor |
— | a reset inside a leg that pays once |
pv |
number | today's value, in the settlement currency where there is one | signed by direction |
a reset inside a leg that pays once |
settlement_currency |
text | the currency a non-deliverable leg settles in | — | "" for a trade settled in its own currency |
fx_fixing_date |
date | the day the FX fixing converting the row is taken | — | no conversion, or one at an agreed rate |
leg |
number | the row's leg, by its position in the order the instrument declares them: 0, 1, … |
— | never; a leg on its own is 0 |
index |
text | the index code the row resets on | — | "" for a fixed coupon or a redemption |
Defaults and limits
- What's owed: a coupon paying on or before the valuation date is left out, as the valuation leaves it out, so a seasoned trade's table is shorter rather than missing.
- A leg that pays once: a zero-coupon leg reports each reset as a row with its money columns
empty, then one
Settlementrow carrying the payment. - All or nothing: a missing historical fixing refuses the whole table, rather than leaving one row projected.
- Sign:
amountis unsigned andpvis signed, so the side is never lost. A paid leg's basis points are negative. - A redemption gives zero basis points, because it accrues no rate.
See also
- Source:
crates/helixrs/src/cashflows/records.rs,crates/helixrs/src/market/store.rs - Related pages:
- Legs and coupons, the coupons the rows come from.
- Fixings: prints and projections, which prints a row's rate reads.
- How cashflows work, why one table serves every product.
From the 0.0 line at revision bf7f2e2. Every printed number is that revision's own output.