1.4.2 — 2026-07-05

Fixed

  • The rate solver no longer reports a non-root as converged. Newton's step-size termination could accept a rate where the NPV was still large — near a steep NPV the step f / f' shrinks below the tolerance even while f does not — so convergence now rests solely on the NPV threshold, and a stalled Newton falls through to bisection.
  • amortization_schedule keeps the balance retiring monotonically within [0, opening]. Once (1 + rate)^nper is large a cent-rounded level payment can no longer tame the balance: rounded a hair high it overshot below zero, a hair low it grew the balance back (negative amortization) before the final row absorbed the difference. Each period now clamps so the schedule stays monotonic and bounded, with a final row that clears whatever remains.

Added

  • Property-based stress tests for the solver and TVM: IRR root-finding across extreme rates and long horizons, a no-crash / real-root contract on arbitrary cash-flow signs, TVM.rate round-trips, pv/fv inversion, and amortization balance invariants. These surfaced the two fixes above.

1.4.1 — 2026-07-05

Fixed

  • The solver now converges for long-maturity, low-rate flows — for example the ytm of a deep-discount 28-year bond, or a 30-year monthly amortization rate. Previously a Newton step could overflow on such flows and abort the whole solve instead of falling through to bisection, and the bisection bracket itself overflowed for long-dated flows. Now a Newton overflow falls through to bisection, the bracket floor adapts to the longest flow, and the bracketing sign check compares signs instead of multiplying (which could overflow).

1.4.0 — 2026-07-04

Added

  • Finance.Returns return metrics: cagr/4 (compound annual growth rate), payback_period/2 and discounted_payback_period/3 (with fractional-period interpolation), profitability_index/3 (reusing CashFlow.npv), and twr/2 (time-weighted return, with an optional :periods_per_year to annualise) — each with a ! variant.

1.3.0 — 2026-07-04

Added

  • Finance.TVM.ipmt/6 and ppmt/6 — the interest and principal portions of a given period's payment; together they add up to pmt/5.
  • Finance.TVM.amortization_schedule/3,4 — the full repayment schedule as a list of %{period, payment, interest, principal, balance} rows, with the final row absorbing rounding so the balance ends at exactly 0. Computed in integer minor units (10^:precision), so every row is exact to the requested precision; returns Decimal rows when given Decimal inputs, floats otherwise.
  • Finance.Rateseffective_annual_rate/2, nominal_rate/2, and continuous_to_periodic/2 for converting between rate quotations.
  • Finance.Bonds — fixed income: price/5, ytm/5 (reuses the Finance.Solver seam, mirroring TVM.rate), duration/4 (Macaulay), modified_duration/4, and convexity/4, each with a ! variant. Maturity is given in years with a configurable coupon frequency (freq, default 2); settlement is assumed to fall on a coupon date (clean price, no accrued interest).

1.2.0 — 2026-07-04

Reorganised the flat Finance module into domain modules. No behaviour change — every function keeps its signature and result.

Added

Deprecated

  • The flat Finance.foo functions (e.g. Finance.xirr/1) now delegate to their domain module and are deprecated. They still work in the 1.x line and will be removed in 2.0.

1.1.0 — 2026-07-04

Added

  • volatility/2 and volatility!/2 — annualised volatility of a price series, the standard deviation of its period returns scaled by √periods_per_year. Supports simple or log returns and a configurable period count. Resolves the long-standing volatility request (issue #7), with the submitted snippet's crashes on short input and zero prices fixed.

1.0.0 — 2026-07-04

A complete rewrite of the library. Breaking changes — some return values differ from 0.x because intermediate rounding was removed and the solver was replaced, and errors are now atoms rather than strings.

Rate of return

  • xirr/1,2,3 and xirr! — internal rate of return for dated cash flows, given as {date, amount} pairs or two parallel lists. Dates may be Date structs or {year, month, day} tuples.
  • irr/1,2 and irr! — internal rate of return for periodic (equally spaced) flows.
  • mirr/3,4 and mirr! — modified internal rate of return.
  • Newton-Raphson solver with an analytic derivative, a bracketing bisection fallback, and a hard iteration cap.

Present / future value

  • xnpv/2,3 and xnpv! — net present value of dated cash flows.
  • npv/2,3 and npv! — net present value of periodic flows. The first amount sits at period 0 (so npv(irr(a), a) ≈ 0), which differs from spreadsheet NPV (first amount at period 1).

Time value of money

  • fv/5, pv/5, pmt/5, nper/5, rate/6 and their ! variants — solve the annuity equation for one unknown, with annuity-due (type: 1) support.

Depreciation

  • sln/3, syd/4, ddb/5, db/5 and their ! variants — straight-line, sum-of-years'-digits, double-declining and fixed-declining depreciation.

Amounts and options

  • Amounts may be integers, floats, or Decimal values (via the optional decimal dependency); results are always floats.
  • Options (:guess, :tolerance, :max_iterations, :precision) are validated with nimble_options; unknown keys and out-of-type values raise.

Removed

  • The timex runtime dependency (now uses the standard-library Date).
  • The spawn_link-per-cash-flow "parallel" mapping, which was slower and unsafe.

Tooling

  • Requires Elixir ~> 1.18; tested on Elixir 1.18/OTP 27 and 1.20/OTP 29.
  • Travis CI replaced with GitHub Actions.