Skip to main content

quant_primitives/
fraction.rs

1//! Validated fraction value object.
2//!
3//! Represents a value in the range [0, 1] — the result of converting
4//! percentages or basis points to a multiplicative factor.
5//!
6//! `Fraction(0.05)` means 5% — used directly in arithmetic:
7//! `capital * fraction.value()` gives the position size.
8
9use std::fmt;
10
11use rust_decimal::Decimal;
12use serde::{Deserialize, Serialize};
13
14/// A fraction value validated to the range [0, 1].
15///
16/// Produced by `Percentage::to_fraction()` and `Bps::to_fraction()`.
17/// Used directly in multiplication: `notional * fraction.value()`.
18#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
19pub struct Fraction(pub(crate) Decimal);
20
21/// Error for invalid fraction construction.
22#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
23pub enum FractionError {
24    /// Value is outside the valid [0, 1] range.
25    #[error("fraction {0} out of range [0, 1]")]
26    OutOfRange(Decimal),
27}
28
29impl Fraction {
30    /// Create a new fraction, validating that the value is in [0, 1].
31    pub fn new(value: Decimal) -> Result<Self, FractionError> {
32        if value < Decimal::ZERO || value > Decimal::ONE {
33            return Err(FractionError::OutOfRange(value));
34        }
35        Ok(Self(value))
36    }
37
38    /// The raw fraction value (0-1).
39    pub fn value(&self) -> Decimal {
40        self.0
41    }
42
43    /// The zero fraction.
44    pub fn zero() -> Self {
45        Self(Decimal::ZERO)
46    }
47
48    /// The full fraction (1.0 = 100%).
49    pub fn one() -> Self {
50        Self(Decimal::ONE)
51    }
52}
53
54impl fmt::Display for Fraction {
55    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56        write!(f, "{}", self.0.normalize())
57    }
58}
59
60#[cfg(test)]
61#[path = "fraction_tests.rs"]
62mod tests;