Functional Weave
Code in Python

lending.daily-interest@1.0.0

README.md

2,390 bytes · view raw

# lending.daily-interest

Interest on an account whose balance changes during the period: a savings
account with deposits, a loan with a payment mid-month, an overdraft that goes
in and out. Each balance accrues balance × rate × (the year fraction for the
days it was held), under the day-count convention the contract names, and the
pieces are added up.

## How it is computed

- `balances` is the balance history: each entry holds from its date until
  the next entry's date. The first entry must start on or before `fromIso`
  (the opening balance); entries after `toIso` are ignored.
- The period is `fromIso` included to `toIso` excluded, the usual accrual
  convention: 1 January to 31 January is 30 days of interest.
- Each piece's year fraction comes from dates.day-count-fraction (ACT/365F,
  ACT/360, 30/360, 30E/360, ACT/ACT ISDA), as an exact fraction.
- The pieces are summed exactly with math.rational, and the total is rounded
  once, with the mode you pass. `exact` is that unrounded total in minor
  units, for audit and for carrying fractions of a penny into the next period
  if your ledger does that.

Rounding once is the point. Rounding each day (or each balance change) and
adding the rounded amounts drifts: three days at 0.23p each would post 0p
where the true total is 0.68p, and more balance changes would mean more
drift. Some ledgers do post whole pence daily; if yours does, call this once
per day instead.

## Signs

A negative balance (overdrawn, or a loan held as a debt) accrues negative
interest, and a negative rate charges a positive balance. The rounding modes
are those of math.round-div, symmetric about zero: `down` is towards zero,
`up` away from it.

## Splitting and 30/360

Under ACT/365F, ACT/360 and ACT/ACT the pieces add up to the whole period
exactly. Under 30/360 each piece is measured on its own, as a contract that
names 30/360 per balance period would, so a piece ending on the 31st can
differ by a day from measuring the whole month at once.

## Limits

The exact running total must fit math.rational (numerator and denominator
within 2^53 - 1); that holds for balances into the hundreds of millions of
pounds at ordinary rates, and a larger one is an error ("rational overflow"),
never a wrong answer. The rate is -100000 to 100000 basis points, and the rate
is fixed for the period: for a rate change, call once per rate and add.