The Problem
Standard f64 floating-point causes rounding errors:
let a: f64 = 0.1;
let b: f64 = 0.2;
let sum = a + b;
println!("{}", sum); // 0.30000000000000004
println!("{}", sum == 0.3); // false
In trading systems, these errors compound over thousands of calculations:
- Incorrect P&L
- Failed reconciliation
- Checksum mismatches
- Audit failures
The Solution
Havklo uses rust_decimal throughout:
use rust_decimal::Decimal;
use rust_decimal_macros::dec;
let a = dec!(0.1);
let b = dec!(0.2);
let sum = a + b;
println!("{}", sum); // 0.3
println!("{}", sum == dec!(0.3)); // true
rust_decimal provides 96-bit decimal representation with up to 28 significant digits - the same precision as .NET’s decimal type.
Where It Matters
Every price and quantity in Havklo is Decimal:
pub struct Level {
pub price: Decimal,
pub qty: Decimal,
}
impl OrderbookSnapshot {
pub fn spread(&self) -> Option<Decimal>;
pub fn mid_price(&self) -> Option<Decimal>;
}
// All SDK methods return Decimal
let spread: Option<Decimal> = client.spread("BTC/USD");
let bid: Option<Decimal> = client.best_bid("BTC/USD");
Scientific Notation
Kraken sometimes sends very small numbers in scientific notation:
{"price": "1.5e-8", "qty": "1000000"}
Havklo parses these correctly:
// Parsed automatically to Decimal
let price = dec!(0.000000015); // 1.5e-8
Decimal is slower than f64, but still fast:
| Operation | f64 | Decimal | Overhead |
|---|
| Addition | ~1 ns | ~5 ns | 5x |
| Multiplication | ~1 ns | ~10 ns | 10x |
| Division | ~5 ns | ~30 ns | 6x |
A 5x slowdown on a 1ns operation is still only 5ns - orders of magnitude faster than network latency.
Best Practices
Use the dec! macro
// Good - compile-time parsing
let price = dec!(100.50);
// Avoid - runtime parsing
let price = Decimal::from_str("100.50")?;
Keep calculations in Decimal
// Good
let pnl = (exit_price - entry_price) * qty;
// Bad - converting back and forth
let pnl = (exit.to_f64()? - entry.to_f64()?) * qty.to_f64()?;
Format for display, don't convert
// Good
println!("Price: {:.8}", price);
// Bad
println!("Price: {}", price.to_f64().unwrap());
Avoid to_f64() unless required by external APIs. Each conversion loses precision.