A reckoner seed: fin's time value of money, loans, capital budgeting, depreciation and payroll — machines/seeds/seedfin.shoddy
seedfin is a reckoner seed: a small file that bridges
one machine's words into the reckoner calculator. It bridges
fin's rates, time value of money, growth,
statistics, curve fitting, budgeting, loan amortization and payoff,
NPV/IRR/payback, CAGR/ROI, dollar-cost averaging, margin and markup,
break-even, payroll and depreciation. Per R4.16 there is no RECORD cell
at the keyboard. So every Fin* record fin.shoddy returns is
a dict here — a list of name-value pairs, the same LIST-of-PAIR shape
seeddict already established, one entry per
field.
Several words pre-flight a condition fin.shoddy itself Errors on — they test it up front rather than let fin abort:
FINNPER — and, through it, FINCARDPAYOFF
and FINEXTRAPAYOFF — refuses a payment that never clears
the interest.FINIRR refuses when the cash-flow bracket shows no
sign change.FINMIRR refuses when there is no negative cash flow to
finance.FINCARDMINPAYOFF refuses a minimum rate at or below
the periodic interest rate, which makes non-payoff certain rather than
merely possible.FINFITOF takes the two columns and a model.
The model is a word — FINLINEAR, FINLOGMODEL,
FINEXPMODEL, FINPOWERMODEL — for the same
reason the day-count bases below are. A session reads
FINLOGMODEL instead of 2. And a mistyped
model is an unknown word, rather than a different curve fitted in
silence.
Four of the guards are conditions fin.shoddy already Errors on by
name: the three model domains — a logarithm of a non-positive x, an
exponential of a non-positive y, a power model needing both — and a
model it does not recognise. The fifth is one fin does not state, and
it is the one a session meets first. LinFit divides by the
variance of the x column — variance measures how much the values spread
out. Correl divides by the product of the two standard
deviations. So a column that does not vary divides by zero, and the
runtime ends the run. The seed's test is exact equality of every
element, not a tolerance, because that is precisely when those
denominators are exactly zero. A nearly-constant column gives a very
large slope. That is fin's answer, and not this seed's to overrule.
One such test covers all four models, because the logarithm is
injective — it never turns two different inputs into the same
output.
The lengths are checked too, though nothing aborts on a mismatch —
which is why they need checking. Cov zips the two lists —
pairs them up element by element — and the zip stops at the shorter
list. So a mismatched pair would otherwise answer a fit of the overlap
against the means of the whole. That is a wrong number rather than a
refusal, and that is the failure this layer exists for.
fin carries a second subsystem — the day-count bases, and a bond
pricer built on them — and it is bridged in full. A date is a dict:
{ ("yr" n) ("mon" n) ("day" n) }, built by
seedjulian's JULDATE. The
calendar words lived here as FINDATE and friends while fin
carried the calendar itself. They moved to seedjulian, renamed, when
the calendar moved to julian. The dict shape
did not change, so a date built there flows straight into
FINDAYS360 and the bond words.
The day-count bases and coupon frequencies are words rather
than numbers to remember. fin spells them as zero-argument definitions
because Shoddy has no enum — no built-in type for a fixed set of named
choices. The same choice here means a session reads
"S" RCL "M" RCL 0.05 P FINSEMI FIN360 FINYTM rather than
… 2 1 …. A mistyped basis is an unknown word instead of a
wrong answer.
Almost nothing here aborts on bad input, which is exactly
the problem. JulSerial is integer arithmetic with no
opinion about whether a date exists. It serializes a date — turns it
into a plain day number — and it serializes a 30th of February
perfectly happily to the 2nd of March. FinYearFrac falls
through to actual/365.25 for any basis it does not recognise.
FinLastCoupon steps back by 12 / freq whole
months whether or not that divides evenly.
Every one of those is a wrong answer rather than a refusal. A wrong answer about money is the failure this whole seed layer exists to prevent. So every word that takes a date checks it on the way in — a dict can also arrive hand-built. And the basis, the frequency and the yield are each refused when they are outside what the arithmetic underneath can mean.
The one genuine abort is FinYtm's, when no yield
between 0 and 2 prices the bond. It is guarded the way
FINIRR's twin already is: the same test asked first, and
refused instead.
A five percent bond, paying twice a year, settling on a coupon date five years before it matures. At a yield equal to its coupon it must be worth exactly par — its face value. That is arithmetic anyone can check:
> 2025 1 15 JULDATE "S" STO 2030 1 15 JULDATE "M" STO
ok: S
ok: M
[ empty ]
> "S" RCL "M" RCL FINSEMI FINCOUPONSBEFORE
x: 10
> "S" RCL "M" RCL 0.05 0.05 FINSEMI FIN360 FINBONDPRICE
x: 100
> "S" RCL "M" RCL 0.05 0.08 FINSEMI FIN360 FINBONDPRICE
x: 87.83365633
> "S" RCL "M" RCL 0.05 100 FINSEMI FIN360 FINYTM
x: 0.05
> "S" RCL "M" RCL 0.05 0.05 FINSEMI FINMDUR
x: 4.376031965
> "S" RCL 2024 7 15 JULDATE 0.05 FINSEMI FIN360 FINACCRUED
x: 2.5
The day counts underneath it, where the whole point is that the bases disagree:
> 2025 1 1 JULDATE 2026 1 1 JULDATE FIN360 FINDATEDIFF
x: 360
> 2025 1 1 JULDATE 2026 1 1 JULDATE FINACTUAL FINDATEDIFF
x: 365
And the refusals, every one of which would otherwise have been a number:
> 2025 1 1 JULDATE 2026 1 1 JULDATE 9 FINYEARFRAC
?: FINYEARFRAC: THAT IS NOT A DAY-COUNT BASIS — USE FINACTUAL, FIN360 OR FIN365
> "M" RCL "S" RCL 0.05 0.05 FINSEMI FIN360 FINBONDPRICE
?: FINBONDPRICE: SETTLEMENT MUST FALL BEFORE MATURITY
> "S" RCL "M" RCL 0.05 5000 FINSEMI FIN360 FINYTM
?: FINYTM: NO YIELD BETWEEN 0 AND 2 PRICES THIS BOND AT 5000
| Group | Words |
|---|---|
| Rates, conversion | FINPCT FINASPCT FINEFF FINNOM FINRULE72 FINREAL FINPCTCHG FINTOBPS FINFROMBPS |
| Time value of money | FINPV FINFV FINPVANN FINFVANN FINPVANNDUE FINFVANNDUE FINPMT FINPMTDUE FINNPER FINSIMPLE FINCOMPOUND |
| Statistics | FINSUM FINMEAN FINSTDDEV FINSTDDEVP FINVAR FINVARP FINMEDIAN FINQUANTILE FINMINIMUM FINMAXIMUM FINRANGEOF FINSKEWNESS FINKURTOSIS FINWEIGHTEDMEAN FINGEOMEAN FINCORREL FINCOV FINSPEARMAN FINNORMP FINTDIST FINSTAT1 |
| Curve fitting | FINFITOF FINLINEAR FINLOGMODEL FINEXPMODEL FINPOWERMODEL |
| Budgeting, saving, investing | FINGOALPMT FINRETIRE FINNETWORTH FINBUDGETOF FINDCAOF |
| Loans: amortization, payoff | FINBALANCE FINAMORTAT FINSCHEDULE FINEXTRAPAYOFF FINCARDPAYOFF FINCARDMINPAYOFF |
| Capital budgeting | FINNPV FINNPVOF FINIRR FINNFV FINMIRR FINPAYBACK FINPAYBACKDISC |
| Growth, margin, break-even, payroll, depreciation | FINCAGR FINROI FINRSTAT FINYIELD FINDIVGROWTH FINMARGINOF FINMARKUPOF FINSELLFROMMARGIN FINCOSTFROMMARGIN FINBREAKEVENOF FINPAYROLLOF FINSL FINSLBOOK FINSYD FINDB FINFACT FINCOMB FINPERM |
| Bases and frequencies | FINACTUAL FIN360 FIN365 FINANNUAL FINSEMI FINQUARTERLY |
| The day counts | FINDAYS360 FINDATEDIFF FINYEARFRAC |
| Bonds | FINCOUPONSBEFORE FINLASTCOUPON FINACCRUED FINBONDPRICE FINYTM FINMDUR |
The day counts and the bonds in full, since neither reads like the rest
of the seed. The calendar words that once sat above these —
FINDATE through FINDATEADDMONTHS — are
seedjulian's now, spelled JUL*:
| Word | Description |
|---|---|
| FINDAYS360 ( a b -- n ) | Days from a to b on the NASD 30/360 rule. |
| FINDATEDIFF ( a b basis -- n ) | Days from a to b, counted on the given basis. |
| FINYEARFRAC ( a b basis -- n ) | The fraction of a year from a to b on the given basis. |
| FINCOUPONSBEFORE ( settle maturity freq -- n ) | How many coupons — the bond's regular interest payments — are still to come between settlement and maturity. |
| FINLASTCOUPON ( settle maturity freq -- date ) | The coupon date on or before settlement — what FINACCRUED accrues from. |
| FINACCRUED ( settle lastcoupon coupon freq basis -- n ) | Interest earned since the last coupon and not yet paid, per 100 of face. coupon is a rate: 0.05 for a five percent bond. |
| FINBONDPRICE ( settle maturity coupon yld freq basis -- price ) | The clean price per 100 of face — accrued interest excluded, which is what a quoted price means. FINACCRUED is the rest of it. |
| FINYTM ( settle maturity coupon price freq basis -- yield ) | The yield to maturity that prices the bond at price. Refuses when no yield between 0 and 2 does. |
| FINMDUR ( settle maturity coupon yld freq -- years ) | Modified duration in years: roughly how much the price moves for a one-point move in yield. |
| User | How | |
|---|---|---|
| halifax | The calculator's finance words — every group above. | |
| sparky | Sparky folds it too, so a model calling eval reaches the same words halifax puts at a prompt. |
A mill claims this seed by folding RckSeedFin over its
reckoner state. That is all halifax does.
| Machine | Why | |
|---|---|---|
| cuttle | The Cell type every bridged word reads its arguments from and answers into. | |
| fin | The domain this seed bridges. | |
| reckoner | RckReg and the argument readers every registered word is built from. | |
| seq | List plumbing under every dict and list converter. | |
| julian | The calendar rules every arriving date dict is checked against, and the JulDate the bond words consume. |