finance-solution
finance-solution is a Rust library for time-value-of-money (TVM), cashflow, amortization, and related finance formulas β with detailed solution structs, period-by-period series, and pretty-printed tables. Scalar math executes in nanoseconds, making it suitable for quant systems. Solution structs execute in microseconds, providing observability with speed.
finance-solution is a financial library for solving time-value-of-money problems and financial math, including common quant calculations. πΈ
People who will find this crate helpful include:
- Students of Finance who want to solve their financial problems using something better than a crude handheld calculator. Using this library also reduces the chance of human error, and provides better output and data displays than Excel in less time thereby providing a better surface for internalizing financial math concepts.
- New developers who want to learn Rust using Finance as a topic.
- Experienced Rust developers who want to learn more about finance and/or incorporate safe financial calculations into their projects.
- Serious Rust developers who want to build financial software, and prefer to rely on a rigourously tested library instead of reinventing the wheel and spending hundreds of hours to develop and test their own library of financial calculations.
In the v0.0.0 release, this library was geared only towards the basic financial equations, regarding:
- Simple Time-Value-of-Money formulas -- this includes
present_value,future_value,rate, andperiods(known as NPER in Excel). - Cashflow Time-Value-of-Money formulas -- this includes
present_value_annuity,future_value_annuity,net_present_value, andpayment(known as PMT in Excel). - Rate conversions -- this includes all conversions between
apr,ear, andepr, and also includes conversion for continuous compounding (apr_continuous,ear_continuous).
Current Status: revived as 0.1.0 (edition 2021). Previous v0.0.0 release relied on panicking APIs (by design, asserts were placed in the functions to immediately inform the user of incorrect input values) which was great for teaching finance to students, but not ideal for library use in applications. In
v0.1all methods return onlyResult. This is a breaking change from v0.0.0 which provided both panicking APIs (a compromise made for teaching) andtry_<methodname>as a non-panicking approach, but the dual API leads to duplication, documentation bloat, and could confuse crate users and AI Agents implementing the finance-solution crate. By consolidating all library features to returnResult, we separate "bad data inputs" from "true bugs" and allows serious rust engineers to utilize the library ergnomically and decide how they want their system to handle theResult(log, skip and report, etc., without crashing the whole system).v0.1also addsamortizationandstocksmodules.
Modules (0.1 layout)
| Module | Contents |
|---|---|
tvm |
Present/future value, rate, periods (simple + continuous, fixed + schedule) |
cashflow |
Payment (PMT), annuities, NPV, NPER |
convert_rate |
APR β EPR β EAR (+ continuous) |
amortization |
Solution/series/tables + PPMT, IPMT, CUMPRINC, CUMIPMT |
returns |
Doubling rules: solution + comparison tables (72/70/69 vs exact) |
stocks |
Price-path solution/series/tables; returns, vol, Sharpe, Sortino, beta, drawdowns |
util |
FinanceError, FinanceResult |
round |
Rounding + test assert helpers |
Quick start
use *;
// Present value of $4,000 in 3 years at 5% (simple compounding)
// Fixture rates/amounts are valid; production code should use `?` or match.
let pv = present_value.expect;
assert_rounded_2!; // sign: opposite of future value
// Prefer the solution API for formulas + period series
let answer = present_value_solution.expect;
println!;
answer.series.print_table;
// Handle bad input without panicking
match present_value
Payment + amortization table
use *;
let rate = 0.08 / 12.0;
let periods = 60;
let principal = 13_000.0;
let solution = payment_solution.expect;
solution.print_table;
// Excel-style period components (1-based period index)
let interest_m1 = ipmt.expect;
let principal_m1 = ppmt.expect;
let year1_interest = cumipmt.expect;
Rate conversion
use *;
let r = apr.expect;
println!;
Doubling rules & price paths
use *;
let d = doubling_solution.expect;
assert_rounded_2!;
d.print_table;
let prices = ;
let path = price_path_solution.expect;
path.print_summary;
path.series.print_table;
Design notes
- Result-only public math β domain failures are
FinanceResult/FinanceError. There is no dual panicking +try_*pair; the ordinary name is the fallible function. - Solution + series β
*_solutiontypes carry inputs, symbolic/numeric formulas, and.series()for period detail. - Excel-compatible signs β loans/payments follow spreadsheet conventions (positive principal β negative payment, etc.).
- Enums for flags β prefer [
Compounding] and [PaymentTiming];boolstill converts viaFromfor ergonomics. - Tables β
.print_table()/.print_table_locale()for teaching and debugging (copy/paste friendly). - Hot path vs solution path β scalar APIs (
payment,present_value, β¦) are the nanosecond-class production path (FinanceResult<f64>). Solution/series APIs are the microsecond teaching/observability path. Same formulas; different packaging. Seebenches/RESULTS.md.
Parameter order (when possible)
rate, periods, present_value, future_value, β¦
Money arguments often accept any Into<f64> (i32, f32, β¦). Rates stay f64; period counts are u32 (NPER returns f64 for fractional periods).
Error handling
Why. Libraries used from web handlers, batch jobs, or CLIs must not abort when a user types -150% interest or period 0.
Canonical pattern (structured match + ?):
use ;
match loan_payment
Affordability example (compose with ?):
use ;
// Call from a fallible main or handler:
// let pmt = affordability(50_000.0, 0.05, 0.06, 10, 60)?;
// pmt β -593.43
| Quantity | Value | Meaning |
|---|---|---|
pv |
β β30,695.66 | Todayβs value of $50k in 10 years at 5% |
| Loan principal | β +30,695.66 | pv.abs() |
| Monthly payment | β β593.43 | Negative = cash you pay |
| 60 Γ payment | β β35,606 | Total cash paid over the loan |
Early payoff / mortgage what-ifs use the same Result APIs β see cargo run --example early_payoff_what_if and the amortization tables below.
Benefits:
- No surprise panics on bad external input
- Matchable variants for UI / metrics (
FinanceError::code()) std::error::Error+Displayfor logging and?into app error types- One name per formula β fallibility lives in the return type
Amortization: solution / series / tables
amortization_solution β .series() β .print_table()
β β
formulas + payment period rows (principal, interest, balance, formulas)
use *;
let rate = 0.08 / 12.0;
let periods = 12;
let principal = 10_000.0;
let solution = amortization_solution
.expect;
println!;
let series = solution.series;
assert_eq!;
series.print_table;
assert_approx_equal!;
assert_approx_equal!;
let _first_half = solution.cumipmt.unwrap;
Sample print_table(true, true) (12-month $10k loan at 8% APR monthly):
period payment principal interest balance principal_to_date interest_to_date payments_to_date principal_remaining interest_remaining payments_remaining
------ --------- --------- -------- ---------- ----------------- ---------------- ---------------- ------------------- ------------------ ------------------
1 -869.8843 -803.2176 -66.6667 9_196.7824 -803.2176 -66.6667 -869.8843 -9_196.7824 -371.9448 -9_568.7272
2 -869.8843 -808.5724 -61.3119 8_388.2100 -1_611.7900 -127.9785 -1_739.7686 -8_388.2100 -310.6329 -8_698.8429
...
12 -869.8843 -864.1235 -5.7608 -0.0000 -10_000.0000 -438.6115 -10_438.6115 0.0000 -0.0000 0.0000
| Need | API |
|---|---|
One Excel cell (IPMT month 3) |
ipmt(...)? / solution.ipmt(3)? |
| Full schedule | amortization_solution(...)?.series() |
| Terminal table | .print_table() / .print_table_locale() |
| Safe construction | amortization_solution β FinanceResult |
Returns & stocks
use *;
let s = doubling_solution.expect;
s.print_table;
doubling_compare_rates
.expect
.print_table;
let prices = ;
let path = price_path_solution
.expect;
path.print_summary;
path.series.print_table;
Examples
Testing & CI
Symmetry tests, Excel golden values, and integration tests cover core TVM, cashflow, amortization, returns, and stocks paths. CI runs build, tests, doctests, clippy, and fmt on stable and MSRV 1.70.
License
MIT β see LICENSE.