fin_primitives/error.rs
1//! Error types for the fin-primitives crate.
2//!
3//! All errors are named, typed, and propagatable via `thiserror`.
4//! Every variant has at least one test that triggers it.
5
6use rust_decimal::Decimal;
7
8/// All errors that can occur in fin-primitives operations.
9#[derive(Debug, thiserror::Error)]
10pub enum FinError {
11 /// Symbol string was empty or contained whitespace.
12 #[error("Symbol '{0}' is invalid (empty or contains whitespace)")]
13 InvalidSymbol(String),
14
15 /// Price value was zero or negative.
16 #[error("Price must be positive, got {0}")]
17 InvalidPrice(Decimal),
18
19 /// Quantity value was negative.
20 #[error("Quantity must be non-negative, got {0}")]
21 InvalidQuantity(Decimal),
22
23 /// Order book delta arrived out of sequence.
24 #[error("Order book sequence mismatch: expected {expected}, got {got}")]
25 SequenceMismatch {
26 /// The next sequence number the book expected.
27 expected: u64,
28 /// The sequence number that was actually received.
29 got: u64,
30 },
31
32 /// Not enough resting liquidity to fill the requested quantity.
33 #[error("No liquidity available for requested quantity {0}")]
34 InsufficientLiquidity(Decimal),
35
36 /// OHLCV bar failed internal invariant check (high >= low, etc.).
37 #[error("OHLCV bar invariant violated: {0}")]
38 BarInvariant(String),
39
40 /// A signal has not accumulated enough bars to produce a value.
41 #[error("Signal '{name}' not ready (requires {required} periods, have {have})")]
42 SignalNotReady {
43 /// Name of the signal that is not ready.
44 name: String,
45 /// Number of bars required before the signal produces a value.
46 required: usize,
47 /// Number of bars seen so far.
48 have: usize,
49 },
50
51 /// Position lookup failed for the given symbol.
52 #[error("Position not found for symbol '{0}'")]
53 PositionNotFound(String),
54
55 /// Ledger cash balance insufficient to cover the fill cost.
56 #[error("Insufficient funds: need {need}, have {have}")]
57 InsufficientFunds {
58 /// Amount of cash required for the fill (cost + commission).
59 need: Decimal,
60 /// Current cash balance in the ledger.
61 have: Decimal,
62 },
63
64 /// Timeframe duration was zero or negative.
65 #[error("Timeframe duration must be positive")]
66 InvalidTimeframe,
67
68 /// CSV or text parse failure.
69 #[error("CSV parse error: {0}")]
70 CsvParse(String),
71
72 /// A Decimal arithmetic operation overflowed.
73 #[error("Arithmetic overflow in financial calculation")]
74 ArithmeticOverflow,
75
76 /// Order book ended up with an inverted spread after a delta was applied.
77 #[error("Inverted spread: best_bid {best_bid} >= best_ask {best_ask}")]
78 InvertedSpread {
79 /// Best bid price at the time the spread inversion was detected.
80 best_bid: Decimal,
81 /// Best ask price at the time the spread inversion was detected.
82 best_ask: Decimal,
83 },
84}