Fee
This page lists what you need to book and value a standalone fee: its one call, the sign of its amount, how it's valued, and what happens once its date has passed. A fee is one known amount paid or received on a date, such as an arrangement fee on a loan, an upfront on a swap or a break cost.
Quick example
The example reads its quotes from the desk sheets; load them once and it runs as shown.
This books an arrangement fee of 250,000 EUR next March, from the side that pays it and the side that receives it. It checks the value against the discount factor by hand, reads the fee's one row in the cashflow table, and values a fee whose date has already passed. The market is the EUR close built on quote sheets and recipes.
use chrono::NaiveDate;
use helixio_docs_sheets::{EUR_CLOSE, SOFR_CLOSE_DATE};
use helixrs::{AnyInstrument, CollateralSpec, Currency, Fee, MarketContext, QuoteSheet, YieldCurve};
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 fee(currency: Currency, payment: NaiveDate, amount: f64) -> AnyInstrument {
AnyInstrument::Fee(Box::new(Fee::new(currency, payment, amount)))
}
fn main() -> Outcome<()> {
let eur: Currency = "EUR".parse()?;
let market = MarketContext::as_of(date(SOFR_CLOSE_DATE)).with_recipe_quotes(
&CollateralSpec::Currency(eur),
&QuoteSheet::from_rows(EUR_CLOSE.iter().copied())?,
)?;
let payment = date((2027, 3, 15));
// An arrangement fee of 250,000 EUR paid next March, and the same fee received.
let paid = fee(eur, payment, -250_000.0);
let received = fee(eur, payment, 250_000.0);
println!("paid {:.2}", paid.npv(&market)?);
println!("received {:.2}", received.npv(&market)?);
// By hand: the amount times the discount factor to the payment date, and one
// row in the cashflow table whose pv is that value.
let (curve, _) = market
.curve(&"discount(EUR)".parse()?)
.expect("the EUR discount curve");
let df = curve.view::<f64>().discount(payment)?;
let by_hand = -250_000.0 * df;
let rows = paid.cashflow_records(&market)?;
let kind = rows["kind"].text().expect("text");
let side = rows["direction"].text().expect("text");
let pv = rows["pv"].numbers().expect("numbers");
println!("discount factor {df:.10}");
println!("by hand {by_hand:.2}");
println!("row {} {} {:.2}", kind[0], side[0], pv[0]);
// A fee paid before the valuation date has already left: it is worth nothing,
// and its table is empty.
let settled = fee(eur, date((2026, 9, 15)), -250_000.0);
println!("settled {:.2}", settled.npv(&market)?);
println!("rows {}", settled.cashflow_records(&market)?.len());
Ok(())
}import datetime as dt
import helixio as hlx
from desk_sheets import EUR_CLOSE, SOFR_CLOSE_DATE
market = hlx.MarketContext(SOFR_CLOSE_DATE).with_recipe_quotes("EUR", EUR_CLOSE)
PAYMENT = dt.date(2027, 3, 15)
# An arrangement fee of 250,000 EUR paid next March, and the same fee received.
paid = hlx.products.fee("EUR", PAYMENT, amount=-250_000.0)
received = hlx.products.fee("EUR", PAYMENT, amount=250_000.0)
print(f"paid {paid.npv(market):.2f}")
print(f"received {received.npv(market):.2f}")
# By hand: the amount times the discount factor to the payment date, and one
# row in the cashflow table whose pv is that value.
df = market.discount_factor("EUR", PAYMENT)
by_hand = -250_000.0 * df
rows = paid.cashflow_records(market).to_dict()
print(f"discount factor {df:.10f}")
print(f"by hand {by_hand:.2f}")
print(f"row {rows['kind'][0]} {rows['direction'][0]} {rows['pv'][0]:.2f}")
# A fee paid before the valuation date has already left: it is worth nothing,
# and its table is empty.
settled = hlx.products.fee("EUR", dt.date(2026, 9, 15), amount=-250_000.0)
print(f"settled {settled.npv(market):.2f}")
print(f"rows {len(settled.cashflow_records(market).to_dict()['kind'])}")paid -247797.19
received 247797.19
discount factor 0.9911887520
by hand -247797.19
row Fee Pay -247797.19
settled 0.00
rows 0
The payer and the receiver hold the same value with opposite signs, and the one row in the table carries exactly the fee's value.
How it's priced
A fee of amount paid on is worth the amount discounted from its payment date, and nothing once that date has passed:
is positive when received and negative when paid. Why a fee is booked beside its trade rather than folded into a price is covered in how financing works.
API
| Argument | Type | Default | Meaning |
|---|---|---|---|
currency |
str |
required | the fee's currency; it discounts on that currency's cash collateral curve |
payment_date |
date |
required | the day the amount changes hands |
amount |
float |
required, keyword only | the amount: positive received, negative paid |
The Python call is products.fee(currency, payment_date, amount=). In Rust it's
AnyInstrument::Fee(Box::new(Fee::new(currency, payment_date, amount))).
| Symbol | Meaning | Rust | Python |
|---|---|---|---|
| the amount, signed by side | Fee::new(.., amount) |
products.fee(.., amount=) |
|
| the discount factor to the payment date | curve.view::<f64>().discount(t_p) |
MarketContext.discount_factor |
|
| its value | AnyInstrument::npv |
Instrument.npv |
|
| the row | its one cashflow | AnyInstrument::cashflow_records |
Instrument.cashflow_records |
Defaults and limits
- What the market needs: the discount curve of the fee's currency, and nothing else. A fee has no rate, index or schedule.
- Sign: the sign of
amountis the side. There's nosideargument. - After its date: a fee whose payment date is before the valuation date is worth 0.0, and its cashflow table has no rows.
- Its row: the cashflow table holds one row of kind
Fee, whosepvis the fee's value.
See also
- Source:
crates/helixrs/src/instruments/fee.rs - Related pages:
- How financing works, the ideas behind the financing products.
- Loan, for the trade an arrangement fee usually sits beside.
- Cashflow records, for the table its one row joins.
From the 0.0 line at revision bf7f2e2. Every printed number is that revision's own output.