finmoney
A precise, panic-free FinMoney library for Rust. finmoney provides safe monetary arithmetic, currency-aware values, configurable rounding strategies, and exchange-grade tick handling. Designed for trading systems, bots, and financial apps where correctness and determinism matter.
Features
- Precise arithmetic: Built on
rust_decimalfor exact decimal calculations with checked overflow detection - Currency safety: Prevents mixing different currencies in operations
- Configurable rounding: Multiple rounding strategies for different use cases
- Tick handling: Exchange-grade price/quantity rounding to valid tick sizes
- Zero panics: All operations return
Resulttypes for error handling - 11 predefined currencies: USD, EUR, BTC, ETH, GBP, JPY, CHF, CNY, RUB, USDT, SOL
- Money allocation: Split amounts by weights with zero-loss remainder distribution
- Currency conversion:
convert_toandexchange_rate_tofor multi-currency workflows - Iterator support:
Sumtrait andtry_sumfor idiomatic collection operations - Flexible formatting:
format_with_separatorandformat_paddedfor display - Convenient constructors:
from_i64,from_f64,TryFrom<(f64, FinMoneyCurrency)> - Error predicates:
is_currency_mismatch(),is_division_by_zero(),is_overflow() - Serde support: Optional serialization/deserialization (feature-gated)
- Modern Rust: Uses Rust 2024 edition, no unsafe code
Requirements
- Rust 1.90 or later (Rust 2024 edition)
Rust 2024 Edition Benefits
This library uses the Rust 2024 edition, which provides:
- Improved error messages and diagnostics
- Better async/await ergonomics
- Enhanced pattern matching capabilities
- More consistent and intuitive syntax
- Latest language features and optimizations
Quick Start
Add this to your Cargo.toml:
[]
= "3.0.0"
# For serialization support
= { = "3.0.0", = ["serde"] }
Basic Usage
use ;
use dec;
// Create currencies
let usd = new?;
let btc = new?;
// Or use predefined currencies
let usd = USD;
let eur = EUR;
// Create FinMoney values
let price = new;
let tax = new;
// Perform arithmetic (returns Result for safety)
let total = ?;
println!; // 11.55 USD
// Multiply by decimal (returns Result, both directions work)
let doubled = ?;
let also_doubled = ?;
println!; // 21.00 USD
// Division with rounding
let divided = price.divided_by_decimal?;
println!; // 3.50 USD
Performance-Optimized Currency Creation
For performance-critical applications, use new_from_tiny with pre-calculated TinyAsciiStr values:
use FinMoneyCurrency;
use TinyAsciiStr;
// Pre-calculate TinyAsciiStr values (more efficient)
let code: = "USD".parse.unwrap;
let name: = "US Dollar".parse.unwrap;
// Create currency without string parsing overhead
let usd = new_from_tiny?;
Currency Safety
finmoney prevents mixing different currencies:
let usd_amount = new;
let eur_amount = new;
// This will return an error
match usd_amount + eur_amount
Tick Handling for Trading
Perfect for exchange trading where prices must conform to specific tick sizes:
let price = new;
// Round to nearest 0.25 tick
let rounded = price.to_tick_nearest?;
println!; // 10.50 USD
// Round down (floor)
let floor_price = price.to_tick_down?;
println!; // 10.50 USD
// Round up (ceiling)
let ceil_price = price.to_tick_up?;
println!; // 10.75 USD
// Check if price is valid for tick size
if price.is_multiple_of_tick
Rounding Strategies
Multiple rounding strategies are available:
let amount = new;
// Banker's rounding (default)
let rounded1 = amount.round_dp_with_strategy;
// Always round away from zero
let rounded2 = amount.round_dp_with_strategy;
// Always round toward zero
let rounded3 = amount.round_dp_with_strategy;
Percentage Calculations
let initial = new;
let current = new;
// Calculate percentage change
let change = current.percent_change_from?;
println!; // Change: 10%
// Or use static method
let change = percent_change?;
Comparison Operations
let price1 = new;
let price2 = new;
// Safe comparisons (returns Result)
if price1.is_greater_than?
// Min/max operations
let lower = price1.min?;
let higher = price1.max?;
// Direct decimal comparisons (no Result needed)
if price1.is_greater_than_decimal
Properties and Utilities
let money = new;
println!;
println!;
println!;
println!;
println!;
// Mathematical operations
let abs_money = money.abs; // 15.75 USD
let neg_money = -money; // 15.75 USD
let floor_money = money.floor; // -16.00 USD
let ceil_money = money.ceil; // -15.00 USD
Error Handling
All operations that can fail return Result<T, FinMoneyError>:
use FinMoneyError;
let result = money1.divided_by_decimal;
match result
// Convenient error predicates (no pattern matching needed)
if let Err = result
Predefined Currencies
11 common currencies are available as constants:
let usd = new; // precision 2
let eur = new; // precision 2
let btc = new; // precision 8
let eth = new; // precision 18
let gbp = new; // precision 2
let jpy = new; // precision 0
let chf = new; // precision 2
let cny = new; // precision 2
let rub = new; // precision 2
let usdt = new; // precision 6
let sol = new; // precision 9
// Get all predefined currencies
let all = all_predefined; // &[FinMoneyCurrency] with 11 entries
// Display trait
println!; // "USD (US Dollar)"
println!; // "BTC"
Convenient Constructors
// From i64
let money = from_i64;
// From f64 (returns Result — NaN/Infinity produce errors)
let money = from_f64?;
// Via TryFrom
let money = try_from?;
Money Allocation
Split a monetary amount by weights with zero-loss remainder distribution:
let total = new;
let parts = total.allocate?;
// [33.34 USD, 33.33 USD, 33.33 USD] — remainder goes to first parts
assert_eq!;
Iterator Support
let amounts = vec!;
// Sum trait (panics on currency mismatch or empty iterator)
let total: FinMoney = amounts.clone.into_iter.sum;
// Safe alternative
let total = try_sum?;
Currency Conversion
let usd = new;
// Convert at a given rate
let eur = usd.convert_to?;
println!; // 92.00 EUR
// Calculate exchange rate between two amounts
let rate = usd.exchange_rate_to?;
println!; // 0.92
Formatting
let money = new;
// Custom separators
println!; // "1,234,567.89 USD"
println!; // "1.234.567,89 USD"
// Pad to fixed decimal places
let m = new;
println!; // "10.5000 USD"
Serde Support
Enable the serde feature for serialization support:
[]
= { = "3.0.0", = ["serde"] }
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize)] struct Order { price: FinMoney, quantity: FinMoney, }
let order = Order { price: FinMoney::new(dec!(10.50), FinMoneyCurrency::USD), quantity: FinMoney::new(dec!(5), FinMoneyCurrency::USD), };
let json = serde_json::to_string(&order)?; let deserialized: Order = serde_json::from_str(&json)?;
## Migration Guide
This section covers breaking changes introduced in v2.x. Update your code accordingly.
### 1. Checked arithmetic: `plus_decimal` / `minus_decimal`
These methods now return `Result<FinMoney, FinMoneyError>` instead of `FinMoney` to detect overflow.
Before:
```rust
let result = money.plus_decimal(dec!(5)); // FinMoney
let result = money.minus_decimal(dec!(3)); // FinMoney
After:
let result = money.plus_decimal?; // Result<FinMoney, FinMoneyError>
let result = money.minus_decimal?; // Result<FinMoney, FinMoneyError>
2. Mul<Decimal> for FinMoney
The * operator with Decimal now returns Result<FinMoney, FinMoneyError> instead of FinMoney.
Before:
let doubled = money * dec!; // FinMoney
After:
let doubled = ?; // Result<FinMoney, FinMoneyError>
3. Mul<FinMoney> for Decimal
Same change — multiplying Decimal * FinMoney now returns Result.
Before:
let doubled = dec! * money; // FinMoney
After:
let doubled = ?; // Result<FinMoney, FinMoneyError>
4. multiplied_by_decimal
Before:
let result = money.multiplied_by_decimal; // FinMoney
After:
let result = money.multiplied_by_decimal?; // Result<FinMoney, FinMoneyError>
All arithmetic operations now consistently return Result, making overflow detection explicit. The ArithmeticOverflow error variant is returned when the result exceeds the Decimal range.
Performance
finmoney is built on rust_decimal which provides excellent performance for financial calculations. All operations are designed to be allocation-free where possible.
License
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.