A reference-grade financial calculation engine for TypeScript.
Explicit assumptions. Reproducible results. Verifiable answers.
finprecise is not another collection of financial helper functions. It is a calculation foundation where every assumption — rate convention, day count, rounding rule, solver behavior — is visible in the API and reproducible across Node.js, Bun, Deno, and browsers.
Existing TypeScript/JavaScript financial libraries have gaps:
- financial covers numpy-financial basics but explicitly excludes arbitrary-precision decimals
- financejs and xirr are aging implementations (2017, 2020) with no day-count or rounding controls
- None expose solver diagnostics ("did it converge?", "why did it fail?")
finprecise fills these gaps:
- Decimal-first: All calculations use
decimal.js— no floating-point surprises - Assumptions are explicit: Day count convention, compounding frequency, payment timing, and rounding mode are API parameters — sensible defaults exist where industry convention is clear, but nothing is hidden
- Solver transparency: IRR/XIRR/rate return
SolveResult— convergence status, iteration count, and failure reasons - Verification-ready: Ships with fixtures cross-referenced against numpy-financial and Excel
| Package | Description |
|---|---|
@finprecise/core |
Decimal, Rate, DayCount, Rounding, Solver |
@finprecise/cashflow |
PV, FV, PMT, NPER, RATE, NPV, XNPV, IRR, XIRR, MIRR |
@finprecise/loans |
Amortization schedules: level-payment, level-principal, interest-only, bullet, variable-rate, prepayment |
@finprecise/depreciation |
SLN, DB, DDB, SYD (Excel-compatible) |
@finprecise/bonds |
Bond pricing, YTM, Macaulay/modified duration, convexity |
@finprecise/format |
Display formatting via Intl.NumberFormat (separated from calculation) |
@finprecise/fixtures |
Verification data for numpy-financial and Excel compatibility |
npm install @finprecise/core @finprecise/cashflowimport { periodicRate } from "@finprecise/core";
import { pv, pmt, irr, xirr } from "@finprecise/cashflow";
// Monthly payment on a $200,000 mortgage at 6% annual for 30 years
const monthlyRate = periodicRate("0.06", "monthly"); // → 0.005
const payment = pmt(monthlyRate, "360", "200000");
// → -1199.10 (you pay $1,199.10/month)
// Present value of $1,000/year for 10 years at 5%
const presentValue = pv("0.05", "10", "-1000");
// → 7721.73
// IRR with solver diagnostics
const result = irr(["-100000", "30000", "35000", "40000", "25000"]);
if (result.ok) {
console.log(`IRR: ${result.value.toFixed(4)}, converged in ${result.iterations} iterations`);
} else {
console.log(`Failed: ${result.reason} — ${result.detail}`);
}import { xirr } from "@finprecise/cashflow";
const result = xirr({
cashflows: [
{ amount: "-100000", date: "2025-01-10" },
{ amount: "25000", date: "2025-06-30" },
{ amount: "90000", date: "2026-03-01" },
],
dayCount: "act/365-fixed",
solver: {
method: "hybrid",
guess: "0.10",
maxIterations: 128,
tolerance: "1e-12",
},
});import { loanSchedule } from "@finprecise/loans";
const schedule = loanSchedule({
principal: "350000",
periods: 360,
repayment: { kind: "level-payment" },
rateSteps: [
{ from: 1, annualRate: "0.0475" },
{ from: 37, annualRate: "0.0610" },
],
accrual: { dayCount: "30e/360", compounding: "monthly" },
rounding: {
interest: "half-up",
payment: "half-up",
balance: "half-up",
scale: 2,
},
});
// Every row shows: period, beginBalance, payment, principal, interest, endBalance, annualRate
for (const row of schedule.rows.slice(0, 3)) {
console.log(
`#${row.period} | Balance: ${row.beginBalance} | Payment: ${row.payment} | ` +
`Principal: ${row.principal} | Interest: ${row.interest} | End: ${row.endBalance}`
);
}
console.log(`Total interest: ${schedule.summary.totalInterest}`);import { sln, ddb, syd } from "@finprecise/depreciation";
// Straight-line: $30,000 asset, $7,500 salvage, 10 years
sln("30000", "7500", "10"); // → 2250
// Double-declining balance
ddb("1000000", "100000", "6", "1"); // → 333333.33
// Sum-of-years-digits
syd("30000", "7500", "10", "1"); // → 4090.91import { effectiveAnnualRate, nominalAnnualRate } from "@finprecise/core";
// 12% nominal with monthly compounding → 12.68% effective
effectiveAnnualRate("0.12", "monthly"); // → 0.126825...
// Convert back
nominalAnnualRate("0.126825", "monthly"); // → 0.12...Every function that depends on conventions exposes them as API parameters. Sensible defaults are provided where industry convention is clear, but all can be overridden explicitly:
PaymentTiming:"begin"or"end"(default"end"— ordinary annuity, matching Excel/numpy-financial)DayCountConvention:"act/365-fixed","30/360","act/act-isda", etc. (default"act/365-fixed"for XIRR/XNPV)CompoundingFrequency:"monthly","quarterly","continuous", etc.RoundingMode:"half-up","half-even","down", etc.SolverConfig: method, guess, maxIterations, tolerance (default: hybrid, 0.10, 128, 1e-12)
IRR, XIRR, and RATE return a discriminated union:
type SolveResult =
| { ok: true; value: Decimal; iterations: number }
| { ok: false; reason: "no-bracket" | "no-convergence"; detail?: string }- All calculations use
decimal.jsfor arbitrary precision - Inputs accept
string | number | Decimal(strings recommended) - Display formatting is in a separate
@finprecise/formatpackage usingIntl.NumberFormat - Computation and display are strictly separated
The @finprecise/fixtures package provides cross-referenced expected values from numpy-financial and Excel, so users can verify results against authoritative sources.
| Function | numpy-financial | Excel | Notes |
|---|---|---|---|
pv |
npf.pv |
PV |
Identical sign convention |
fv |
npf.fv |
FV |
Identical sign convention |
pmt |
npf.pmt |
PMT |
Identical sign convention |
nper |
npf.nper |
NPER |
|
rate |
npf.rate |
RATE |
Returns SolveResult |
ipmt |
npf.ipmt |
IPMT |
|
ppmt |
npf.ppmt |
PPMT |
|
npv |
npf.npv |
— | First cashflow discounted (numpy convention) |
xnpv |
— | XNPV |
Configurable day count |
irr |
npf.irr |
IRR |
Returns SolveResult |
xirr |
— | XIRR |
Returns SolveResult, configurable day count |
mirr |
npf.mirr |
MIRR |
|
yearFrac |
— | YEARFRAC |
All 6 basis types |
sln |
— | SLN |
|
ddb |
— | DDB |
|
syd |
— | SYD |
packages/
core/ — Decimal, Rate, DayCount, Rounding, Solver
cashflow/ — TVM functions, NPV, IRR, XIRR, MIRR
loans/ — Amortization schedule engine
depreciation/ — SLN, DB, DDB, SYD
bonds/ — Bond pricing, YTM, duration, convexity
format/ — Display formatting (Intl.NumberFormat)
fixtures/ — Verification data
pnpm install
pnpm build
pnpm testTests include:
- Unit tests against numpy-financial and Excel expected values
- Property-based tests with fast-check (TVM round-trips, IRR→NPV=0)
- Day count convention tests for all 6 basis types
- Solver convergence tests
- Fork the repository
- Create a feature branch
- Add tests (including fixtures for new functions)
- Ensure
pnpm testpasses - Submit a pull request