fin is the money-over-time machine. What a loan costs, what a
saving comes to, whether an investment is worth making, what an asset is worth
after three years, what a bond is worth today. It carries the time value of
money (present and future value, annuities, payments, terms). It carries
loans and their amortisation schedules — the period-by-period split of
each payment into interest and principal. It carries investment appraisal
(NPV, IRR, payback, MIRR), everyday small-business arithmetic (break-even,
margin and markup, payroll, late interest), the four depreciation methods,
the day-count bases, and bond pricing with yield and duration on top of it.
Every word is pure
— it computes from its arguments and nothing else — and every word
is prefixed Fin.
The idea under every word in this machine — that money later is
worth a computable amount of money now — is medieval. Leonardo of Pisa,
better known as Fibonacci, worked comparisons of investment streams in the
Liber Abaci of 1202. He discounted future payments to put two deals
on a common footing, and scholars have argued he deserves the credit for
present value itself. For the next seven centuries the discipline ran on
printed tables — compound interest, annuities, depreciation —
because raising (1 + r) to the 360th power by hand is
nobody's idea of an afternoon. The other fossil in this machine is younger.
The 30/360 day-count basis is a rule for counting the days between two
dates. It exists because a bond desk clerk, computing accrued interest with
pen and ink, needed every month to be 30 days and every year 360 —
that way the arithmetic came out in round fractions. The clerks are gone,
but the convention is still written into bond contracts. That is why
FinDays360 must reproduce its rules exactly rather than count
real days — the real count is julian's job.
Finance arithmetic is the kind that looks easy and is not. The formulas are short, and almost every one of them has a trap in it that produces a plausible wrong number rather than an error — which is the worst thing a number can do. This machine's job is to have the traps already sprung.
A rate is a decimal fraction, per period. 5% is
0.05, never 5. Every word here follows that one rule,
so nothing has to be checked twice. Where a rate is annual the parameter says
so — apr, annualRate, yld —
and the word takes the periods-per-year alongside it. Nothing silently
annualises. FinPct(5) and FinAsPct(0.05) convert at
the edges and are the only two places a 100 appears.
Everything is a plain number, in whole currency units.
1234.56, not 123456, and no currency symbol anywhere
— this machine does not presume you are counting dollars. It deliberately
does not use money, which is the obvious
choice and the wrong one. Money holds whole cents and has no
division or exponentiation, so FinPv — a division by
(1 + rate)^n — cannot be written in it at all. And rounding
every intermediate to the cent drifts, until a schedule stops agreeing with the
balance it is supposed to describe. Use FinFmt to print, and
money when you need to settle an amount exactly.
Cash out is negative, cash in is positive in every word that
takes a list of cash flows. FinPmt is the one exception, and it is
the exception every spreadsheet also makes: a positive loan gives a positive
payment, because that is the number you print.
A rate word refuses rather than guesses. FinIrr
and FinYtm find their answer by bisection — repeatedly
halving a bracketed range — and both check that the answer is actually
in the bracket before they start looking. A series with no internal rate of
return gets an error naming the problem, not a number that looks fine in a
report.
The payment first, then the schedule:
Include "fin.shoddy"
Def Main()
' 200,000 over 30 years at 6% APR — a monthly rate of 0.005
Let pmt = FinPmt(200000, 0, 0.005, 360)
Print(FinFmt(pmt)) ' 1,199.10
' Month one is nearly all interest
Let first = FinAmortAt(200000, 0.005, pmt, 1)
Print(FinFmt(Interest(first))) ' 1,000.00
Print(FinFmt(Principal(first))) ' 199.10
' And what £200 a month extra would save
Let saved = FinExtraPayoff(200000, 0.005, pmt, 200)
Print(Periods(saved)) ' months off the term
Print(FinFmt(Interest(saved))) ' interest never paid
FinBalance and FinAmortAt are closed forms —
single formulas, not loops — so they do not walk the schedule to answer
a question about one period. FinSchedule is the walk, when you
actually want every row, and the two agree: the schedule's principal column
sums to the loan.
' The outlay is the negative first element
Let flows = { 0 - 1000, 400, 400, 400 }
Print(FinIrr(flows)) ' 0.09701...
Print(FinNpvOf(flows, 0.08)) ' positive, so worth it at 8%
Print(FinPayback(1000, { 400, 400, 400 })) ' 2.5 — it interpolates
There are two NPV words on purpose. (NPV, net present value, is every cash
flow converted into today's money and summed.) FinNpvOf takes
one list whose first element is the outlay at time zero — the form
FinIrr solves. FinNpv takes the outlay separately
and discounts the first flow by one period. Mixing those two up is the
classic off-by-one-period error, and giving them one name would have invited
it.
FinPayback interpolates inside the crossing period rather than
rounding up to it, and returns -1 when the flows never recover the
outlay — an honest answer rather than a misleading one.
A finance report wants means and medians and standard deviations, so they are here, prefixed like everything else:
Print(FinMean(returns))
Print(FinStdDev(returns)) ' sample form (n-1), like a spreadsheet's STDEV
Print(FinStdDevP(returns)) ' population form (n)
Print(FinMedian(returns))
Both standard-deviation forms are here and each is named for which it is,
because picking the wrong one gives a plausible wrong number and no error.
(Standard deviation is the typical distance of a value from the average.)
The arithmetic underneath is stats' and
math' — you do not need to include them, and
nothing on this page will send you to them. The line is drawn at the hypothesis
tests. If you want Anova or a chi-squared test you have left
finance behind, and you include stats and say so.
Growth averages geometrically. FinDivGrowth goes through the
geometric mean, never an arithmetic one. On a series that falls and recovers
the two disagree, and the geometric answer is the true one.
The calendar itself lives in julian — this
machine once spelled the leap rules as a stated stand-in, and that
arithmetic moved there unchanged. A date here is a JulDate,
built by JulOf. What stays in fin are the day-count
bases, because a basis is a financial fiction about the
calendar, not the calendar:
Include "julian.shoddy"
Print(FinDays360(JulOf(2026, 1, 31), JulOf(2026, 2, 28))) ' 28 — the NASD rule
Print(FinDateDiff(JulOf(2026, 1, 1), JulOf(2027, 1, 1), FinActual())) ' 365
Three day-count bases ship, because the conventions genuinely differ:
FinActual() counts real days, Fin360() is the 30/360
US (NASD) rule that bond markets quote in, and Fin365() is
actual/365. The basis is an argument wherever it matters rather than an
assumption baked in.
Let settle = JulOf(2026, 1, 1)
Let mature = JulOf(2031, 1, 1)
Print(FinBondPrice(settle, mature, 0.06, 0.06, FinSemi(), Fin360())) ' 100 — par
Print(FinYtm(settle, mature, 0.06, 100, FinSemi(), Fin360())) ' 0.06
FinBondPrice returns the clean price, per 100
of face — accrued interest excluded, which is what every quoting
convention means by "price". FinAccrued is right there when you
want the dirty price. Price and duration are built from the same cash-flow
walk, so they cannot drift apart.
Every word, and the records they hand back
| Word | Description |
|---|---|
| FinStat | Avg, Sd, Total — a one-variable summary. Named so rather than Mean/StdDev/Sum because those are stats' words and a machine word makes a field of the same name unreachable. |
| FinGrowth | Interest, Total — interest earned or owed, and what it comes to. |
| FinAmort | Principal, Interest, Balance — one period of a loan, or a range of them aggregated. |
| FinPayoff | Periods, Interest — how long, and how much interest. |
| FinBudget | Net, SavingsRate. |
| FinFlow | Amount, Count — one entry of an uneven cash-flow list, with its repeat count. |
| FinDca | Dca, Lump — the two ending values being compared. |
| FinReturns | AvgReturn, Sd. |
| FinBreakEven | Units, Margin. |
| FinPayroll | Net, Withheld. |
| FinDep | Amount, Book, Method — a period's depreciation, the book value after it, and which method produced it. |
| FinFit | Intercept, Slope, RFit — a fitted curve. RFit is the correlation in the fitted space, which is what a spreadsheet reports. |
A civil date is julian's JulDate
— the record this machine once carried as FinDate, retired
when the calendar moved and the tree gained one set of leap rules.
| Word | Description |
|---|---|
| FinActual() | Day count: actual days. |
| Fin360() | Day count: 30/360 US (NASD). |
| Fin365() | Day count: actual/365. |
| FinAnnual() / FinSemi() | Coupons per year: 1 or 2. |
| FinLinear() / FinLogModel() / FinExpModel() / FinPowerModel() | Which curve FinFitOf fits. |
| FinDbUsed() / FinSlUsed() | Which method a FinDep period used. |
| Word | Description |
|---|---|
| FinPct(p) | 5 becomes 0.05. One of only two places a 100 appears. |
| FinAsPct(r) | And back again, for printing. |
| FinEff(nom, m) | Nominal (APR) to effective annual, compounding m times a year. |
| FinNom(eff, m) | And the reverse. |
| FinRule72(rate) | Years to double, the back-of-an-envelope way. |
| FinReal(nominal, inflation) | Inflation-adjusted return, by the exact Fisher relation rather than subtraction — the two differ visibly when inflation is high. |
| FinPctChg(old, new) | Percentage change, as a fraction. |
| FinToBps(pct) / FinFromBps(bps) | Basis points, both ways. |
| Word | Description |
|---|---|
| FinPv(fv, rate, n) | What a future sum is worth now. |
| FinFv(pv, rate, n) | And what a present sum grows to. |
| FinPvAnn(pmt, rate, n) | Present value of a level stream paid at period end. |
| FinFvAnn(pmt, rate, n) | Its future value. |
| FinPvAnnDue / FinFvAnnDue | The same, paid at period start. |
| FinPmt(pv, fv, rate, n) | The level payment that clears pv and leaves fv. Positive for a positive loan — the sign exception. |
| FinPmtDue(...) | Paid at the start of each period. |
| FinNper(pv, fv, pmt, rate) | How many payments that takes. Errors when the payment never clears the interest. |
| FinSimple(principal, rate, years) | Simple interest → FinGrowth. |
| FinCompound(principal, rate, m, years) | Compound interest, m times a year → FinGrowth. |
Every word above that divides by the rate answers at
zero too — FinPmt(1200, 0, 0, 12) is 100. A 0%
promotional loan is a real input, not an edge case.
| Word | Description |
|---|---|
| FinGoalPmt(targetFv, pv, rate, n) | What you must put by each period to reach a target. |
| FinRetire(start, contribution, perYear, rate, years) | And what a balance plus regular contributions comes to. |
| FinNetWorth(assets, liabilities) | Assets less liabilities. |
| FinBudgetOf(income, expenses) | Net position and savings rate → FinBudget. The rate is 0 on no income, not a NaN in your report. |
| Word | Description |
|---|---|
| FinBalance(pv, rate, pmt, k) | What is still owed after k payments. Closed form. |
| FinAmortAt(pv, rate, pmt, k) | One period's split → FinAmort. Also closed form. |
| FinAmortRange(pv, rate, pmt, from, to) | A range aggregated into one FinAmort. |
| FinSchedule(pv, rate, n, pmt) | Every row, as a list. |
| FinExtraPayoff(pv, rate, pmt, extra) | What paying extra saves → FinPayoff: periods off the term, and interest never paid. |
| FinCardPayoff(balance, apr, pmt) | A card cleared at a fixed payment. |
| FinCardMinPayoff(balance, apr, minRate) | And at the minimum, which shrinks as the balance does. Errors if the minimum never clears the interest — which happens, and is worth being told about. |
| Word | Description |
|---|---|
| FinNpv(outlay, cfs, rate) | Net present value, outlay given separately, flows starting one period out. |
| FinNpvOf(cfs, rate) | One list, first element at time zero — the form FinIrr solves. |
| FinIrr(cfs) | The rate at which FinNpvOf is zero. Errors on a series with no sign change rather than returning a plausible number. |
| FinNfv(cfs, rate) | Net future value at the final period. |
| FinMirr(cfs, financeRate, reinvestRate) | IRR with separate borrowing and reinvestment rates. Closed form. |
| FinPayback(outlay, cfs) | How long to recover the outlay, interpolated. -1 if it never does. |
| FinPaybackDisc(outlay, cfs, rate) | The same on discounted flows. |
| FinCagr(begin, end, years) | Compound annual growth rate. |
| FinRoi(initial, final) | Simple return on investment. |
| FinExpand(entries) | Expands a list of FinFlow (amount plus repeat count) into the flat series the words above take. |
| Word | Description |
|---|---|
| FinStat1(data) | Mean, standard deviation and total in one FinStat. |
| FinMean / FinSum / FinMedian | The everyday three. |
| FinStdDev / FinStdDevP | Standard deviation, sample (n-1) and population (n). Named for which they are, because the wrong one is invisible. |
| FinVar / FinVarP | Variance, the same two ways. |
| FinMinimum / FinMaximum / FinRangeOf | Extremes and their spread. |
| FinQuantile(xs, p) | The value at fraction p — 0..1, like every other fraction here. |
| FinSkewness / FinKurtosis | The shape of a return distribution. Kurtosis is excess: a normal curve is 0. |
| FinWeightedMean(xs, ws) | Mean with per-item weights. |
| FinCorrel / FinCov / FinSpearman | How two series move together — linear, raw, and by rank. |
| FinGeoMean(xs) | The compound-growth average. Everything here that averages growth goes through it. |
| FinNormP(x, mean, sd) | Normal probability, taking a raw mean and standard deviation. |
| FinTDist(t, df) | Student's t. |
| FinRound(x, places) | Round to a number of decimal places. |
| Word | Description |
|---|---|
| FinRStat(returns) | Average return and volatility → FinReturns. |
| FinYield(annualDividend, price) | Dividend yield. |
| FinDivGrowth(series) | Annualised dividend growth, geometrically. |
| FinDcaOf(total, intervals, prices) | Averaging in against the lump sum → FinDca. |
| FinFitOf(xs, ys, model) | Fits linear, logarithmic, exponential or power → FinFit. Errors on non-positive inputs for the three log models rather than dropping points. |
| Word | Description |
|---|---|
| FinBreakEvenOf(fixedCosts, varCost, price, targetProfit) | Units needed → FinBreakEven. Errors when the price does not cover the variable cost. |
| FinCashProj(startingCash, inflows, outflows) | Running cash position, one entry per period. |
| FinMarginOf(cost, sell) | Margin — on the selling price. |
| FinMarkupOf(cost, sell) | Markup — on the cost. Cost 60 sold at 100 is a 40% margin and a 66.7% markup: same trade, two numbers. |
| FinSellFromMargin / FinCostFromMargin | Solve for the missing one of the three. |
| FinPayrollOf(gross, rates) | Net pay and total withheld → FinPayroll. The rates are summed, not compounded: an estimate, not a tax engine. |
| FinLateOf(amount, annualRate, daysLate, basis) | Interest on an overdue invoice → FinGrowth. |
| Word | Description |
|---|---|
| FinSl(cost, salvage, life) | Straight line, per period. |
| FinSlBook(cost, salvage, life) | Its book values, as a list. |
| FinSyd(cost, salvage, life, period) | Sum of the years' digits → FinDep. |
| FinDb(cost, salvage, life, factor, period) | Declining balance. factor is a multiple — 2 for double-declining — not a percentage. |
| FinDbx(...) | Declining balance that switches to straight line once straight line gives more, and reports which it used in Method. |
| Word | Description |
|---|---|
| FinFact(n) | Factorial. Exact to 18; past that a double no longer holds whole numbers exactly. |
| FinPerm(n, r) | Ordered arrangements. |
| FinComb(n, r) | Unordered selections, computed multiplicatively — FinComb(52, 5) is 2598960 exactly, which the factorial form cannot manage. |
The calendar itself — dates, leap rules, date
arithmetic — is julian's; these take its
JulDate
| Word | Description |
|---|---|
| FinDateDiff(a, b, basis) | Days between, under a day-count basis. |
| FinDays360(a, b) | The 30/360 US (NASD) rule, end-of-month adjustments and all. |
| FinYearFrac(a, b, basis) | The fraction of a year between two dates. |
| Word | Description |
|---|---|
| FinCouponsBefore(settle, maturity, freq) | How many coupons are left. |
| FinLastCoupon(settle, maturity, freq) | The coupon date on or before settlement. |
| FinAccrued(settle, lastCoupon, coupon, freq, basis) | Interest earned since that date and not yet paid. |
| FinBondPrice(settle, maturity, coupon, yld, freq, basis) | The clean price per 100 of face. |
| FinYtm(settle, maturity, coupon, price, freq, basis) | The yield that price implies. Errors if no yield in 0..2 prices the bond. |
| FinMdur(settle, maturity, coupon, yld, freq) | Modified duration — price sensitivity to a 1% yield move. |
| Word | Description |
|---|---|
| FinFmt(x) | 1,234.56 — grouped thousands, two places, and no currency symbol. Put your own in front; this machine does not presume a currency. |
Where the arithmetic comes from.
stats and math do the
statistical work underneath, and str formats. You do not
need to include any of them: every word you need is on this page, prefixed
Fin. If you want the hypothesis tests — Anova,
chi-squared, the t-tests — include stats yourself and
call them by their own names.
No machine and no mill includes it yet — its words are already at the reckoner's prompt through its seed, and a mill that puts them to work will appear here.
| Machine | Why | |
|---|---|---|
| julian | The calendar. A date here is a JulDate, and JulSerial, JulDiff and JulAddMonths stand under the day counts and the bond words — the leap rules this machine once spelled itself, as a stated stand-in, now spelled once for the tree. | |
| math | RoundTo under FinRound. | |
| seq | Flatten expands a cash-flow list, and Taken walks the running cash position. | |
| stats | Every descriptive statistic this machine offers, and LinFit under the four regression models. | |
| str | CommaGroup and PadZero build FinFmt. |