The Machines · Money & finance

fin

Finance — machines/fin.shoddy

the fin machine's icon

Summary

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.

A Brief History of Present Value

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.

Why It's Useful

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.

User's Guide

What a loan costs

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.

Whether an investment is worth making

' 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.

Statistics, without leaving

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.

Day counts, because bonds need them

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.

Bonds

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.

Word Reference

Every word, and the records they hand back

The records

WordDescription
FinStatAvg, 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.
FinGrowthInterest, Total — interest earned or owed, and what it comes to.
FinAmortPrincipal, Interest, Balance — one period of a loan, or a range of them aggregated.
FinPayoffPeriods, Interest — how long, and how much interest.
FinBudgetNet, SavingsRate.
FinFlowAmount, Count — one entry of an uneven cash-flow list, with its repeat count.
FinDcaDca, Lump — the two ending values being compared.
FinReturnsAvgReturn, Sd.
FinBreakEvenUnits, Margin.
FinPayrollNet, Withheld.
FinDepAmount, Book, Method — a period's depreciation, the book value after it, and which method produced it.
FinFitIntercept, 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.

Choices

WordDescription
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.

Rates

WordDescription
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.

Time value of money

WordDescription
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 / FinFvAnnDueThe 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.

Goals and projections

WordDescription
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.

Loans

WordDescription
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.

Investment appraisal

WordDescription
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.

Statistics

WordDescription
FinStat1(data)Mean, standard deviation and total in one FinStat.
FinMean / FinSum / FinMedianThe everyday three.
FinStdDev / FinStdDevPStandard deviation, sample (n-1) and population (n). Named for which they are, because the wrong one is invisible.
FinVar / FinVarPVariance, the same two ways.
FinMinimum / FinMaximum / FinRangeOfExtremes and their spread.
FinQuantile(xs, p)The value at fraction p — 0..1, like every other fraction here.
FinSkewness / FinKurtosisThe shape of a return distribution. Kurtosis is excess: a normal curve is 0.
FinWeightedMean(xs, ws)Mean with per-item weights.
FinCorrel / FinCov / FinSpearmanHow 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.

Returns, dividends and fit

WordDescription
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.

Small business

WordDescription
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 / FinCostFromMarginSolve 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.

Depreciation

WordDescription
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.

Combinatorics

WordDescription
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.

Day counts

The calendar itself — dates, leap rules, date arithmetic — is julian's; these take its JulDate

WordDescription
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.

Bonds

WordDescription
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.

Printing

WordDescription
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.

Who Uses It

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.

The Machines It Uses

MachineWhy
julianThe 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.
mathRoundTo under FinRound.
seqFlatten expands a cash-flow list, and Taken walks the running cash position.
statsEvery descriptive statistic this machine offers, and LinFit under the four regression models.
strCommaGroup and PadZero build FinFmt.