<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Shilpi Daswani</title>
    <description>The latest articles on DEV Community by Shilpi Daswani (@shilpi_daswani).</description>
    <link>https://dev.to/shilpi_daswani</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4238474%2Fc38d9bb7-5357-4947-9019-682b5e2c0337.png</url>
      <title>DEV Community: Shilpi Daswani</title>
      <link>https://dev.to/shilpi_daswani</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/shilpi_daswani"/>
    <language>en</language>
    <item>
      <title>A zero-dependency financial math library for TypeScript</title>
      <dc:creator>Shilpi Daswani</dc:creator>
      <pubDate>Wed, 11 Nov 2026 06:48:16 +0000</pubDate>
      <link>https://dev.to/shilpi_daswani/a-zero-dependency-financial-math-library-for-typescript-3dg7</link>
      <guid>https://dev.to/shilpi_daswani/a-zero-dependency-financial-math-library-for-typescript-3dg7</guid>
      <description>&lt;h2&gt;
  
  
  A zero-dependency financial math library for TypeScript
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;So I built finlytic: a small, dependency-free, fully typed library for the financial calculations themselves.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm install finlytic&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The gap
&lt;/h2&gt;

&lt;p&gt;Two things are genuinely hard to find in JS/TS finance libraries:&lt;/p&gt;

&lt;p&gt;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.&lt;br&gt;
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.&lt;/p&gt;

&lt;p&gt;finlytic covers both, alongside the usual TVM, loan amortization, depreciation, returns, perpetuities, and day-count conventions.&lt;/p&gt;

&lt;p&gt;A few examples&lt;/p&gt;

&lt;p&gt;Internal rate of return for cash flows on real dates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="n"&gt;ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;xirr&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="s1"&gt;'finlytic'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;xirr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'2024-01-01'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;6000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'2024-06-15'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;6000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'2025-01-10'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// the annual rate that makes the dated NPV zero&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Price a bond and measure its interest-rate risk:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="n"&gt;ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;bondPrice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;modifiedDuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;convexity&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="s1"&gt;'finlytic'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;bond&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;couponRate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.08&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;yearsToMaturity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// face 100, semi-annual&lt;/span&gt;

&lt;span class="nf"&gt;bondPrice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bond&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;        &lt;span class="c1"&gt;// ≈ 87.54  (priced to yield 10%)&lt;/span&gt;
&lt;span class="nf"&gt;modifiedDuration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bond&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// ≈ 6.51 years&lt;/span&gt;
&lt;span class="nf"&gt;convexity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bond&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;        &lt;span class="c1"&gt;// ≈ 56.49&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A full loan amortization schedule that ends at exactly zero:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="n"&gt;ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;amortizationSchedule&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="s1"&gt;'finlytic'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;schedule&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;amortizationSchedule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;principal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nper&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;schedule&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// { period: 1, payment: 856.07, interest: 41.67, principal: 814.41, balance: 9185.59 }&lt;/span&gt;
&lt;span class="n"&gt;schedule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;at&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  On correctness
&lt;/h2&gt;

&lt;p&gt;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:&lt;/p&gt;

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

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Some other deliberate choices:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Zero dependencies, ESM + CJS, tree-shakeable.&lt;/li&gt;
&lt;li&gt;Spreadsheet-compatible results, so numbers match what finance people expect.&lt;/li&gt;
&lt;li&gt;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.&lt;/li&gt;
&lt;li&gt;Correct handling of the edge cases implementations tend to miss: zero-interest loans, annuity-due payments, balloon residuals, and the rate === growth limit.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;npm: &lt;a href="https://www.npmjs.com/package/finlytic" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/finlytic&lt;/a&gt;&lt;br&gt;
Source &amp;amp; docs: &lt;a href="https://github.com/shilpidaswani/finkit" rel="noopener noreferrer"&gt;https://github.com/shilpidaswani/finkit&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>javascript</category>
      <category>opensource</category>
      <category>finance</category>
    </item>
  </channel>
</rss>
