DEV Community

Shilpi Daswani
Shilpi Daswani

Posted on

A zero-dependency financial math library for TypeScript

A zero-dependency financial math library for TypeScript

If you've ever needed to do real financial math in JavaScript, you've probably noticed the ecosystem thins out fast. The basic time-value-of-money functions — PMT, FV, IRR — are everywhere. But the moment you reach for bond duration, convexity, XIRR over irregular dates, or a proper day-count convention, you're left choosing between a heavyweight Excel-formula port and writing it yourself.

So I built finlytic: a small, dependency-free, fully typed library for the financial calculations themselves.

npm install finlytic

The gap

Two things are genuinely hard to find in JS/TS finance libraries:

Bond analytics — price, yield to maturity, and especially interest-rate risk: Macaulay duration, modified duration, convexity. These are standard in a fixed-income context and almost absent from zero-dependency JS packages.
Dated cash flows — XNPV / XIRR, where the flows fall on arbitrary calendar dates rather than neat equal periods. Easy to get subtly wrong, especially around time zones and leap years.

finlytic covers both, alongside the usual TVM, loan amortization, depreciation, returns, perpetuities, and day-count conventions.

A few examples

Internal rate of return for cash flows on real dates:

ts
import { xirr } from 'finlytic';

xirr([
  { amount: -10000, date: new Date('2024-01-01') },
  { amount: 6000,  date: new Date('2024-06-15') },
  { amount: 6000,  date: new Date('2025-01-10') },
]);
// the annual rate that makes the dated NPV zero
Enter fullscreen mode Exit fullscreen mode

Price a bond and measure its interest-rate risk:

ts
import { bondPrice, modifiedDuration, convexity } from 'finlytic';

const bond = { couponRate: 0.08, yearsToMaturity: 10 }; // face 100, semi-annual

bondPrice(bond, 0.10);        // ≈ 87.54  (priced to yield 10%)
modifiedDuration(bond, 0.10); // ≈ 6.51 years
convexity(bond, 0.10);        // ≈ 56.49
Enter fullscreen mode Exit fullscreen mode

A full loan amortization schedule that ends at exactly zero:

ts
import { amortizationSchedule } from 'finlytic';

const schedule = amortizationSchedule({ principal: 10000, rate: 0.05 / 12, nper: 12 });
schedule[0];
// { period: 1, payment: 856.07, interest: 41.67, principal: 814.41, balance: 9185.59 }
schedule.at(-1)!.balance; // 0
Enter fullscreen mode Exit fullscreen mode

On correctness

For a finance library, "looks about right" isn't good enough — the output is money. So the test suite (116 tests) doesn't check results against the implementation itself. Each reference value is cross-checked against an independent method:

xirr roots verified by bisection
bond duration and convexity verified against finite differences of the price
growing-annuity formulas verified against brute-force summation
mirr, depreciation, and day-counts verified against spreadsheet results

That process actually caught a few wrong constants while I was writing it — including a reference IRR I'd misremembered — which is exactly the point.

Some other deliberate choices:

  • Zero dependencies, ESM + CJS, tree-shakeable.
  • Spreadsheet-compatible results, so numbers match what finance people expect.
  • Time-zone-independent day counts — dates are read by their UTC calendar day, so the same inputs give the same answer regardless of where the code runs.
  • Correct handling of the edge cases implementations tend to miss: zero-interest loans, annuity-due payments, balloon residuals, and the rate === growth limit.

Try it

npm: https://www.npmjs.com/package/finlytic
Source & docs: https://github.com/shilpidaswani/finkit

It's early (v0.2.0), so feedback, bug reports, and PRs are all welcome — especially from anyone who works with fixed income or financial modelling and can stress-test the numbers.

Top comments (0)