bts_rs/errors.rs
1//! Error types for the BTS library.
2
3use chrono::{DateTime, Utc};
4
5/// Enum representing possible errors in the crate.
6pub type Result<T> = std::result::Result<T, Error>;
7
8/// Custom error types for the `bts` library.
9#[derive(thiserror::Error, Debug)]
10pub enum Error {
11 /// The candle data provided is empty.
12 ///
13 /// Backtesting requires at least one candle to execute.
14 #[error("Candle data is empty: backtesting requires at least one candle")]
15 CandleDataEmpty,
16
17 /// The requested candle was not found in the dataset.
18 #[error("Candle not found")]
19 CandleNotFound,
20
21 /// The Aggregator factor is invalid.
22 #[error("The Aggregator factor is invalid")]
23 InvalidFactor,
24
25 /// A required field is missing.
26 #[error("Missing required field: {0}")]
27 MissingField(&'static str),
28
29 /// Prices are not in valid order (open ≤ low ≤ high ≤ close).
30 #[error("Invalid price order: open={0}, low={1}, high={2}, close={3}")]
31 InvalidPriceOrder(f64, f64, f64, f64),
32
33 /// Volume cannot be negative.
34 #[error("Volume cannot be negative (got: {0})")]
35 NegativeVolume(f64),
36
37 /// Open time and close time are not in valid order (open time < close time).
38 #[error("Invalid time order: open={0}, close={1}")]
39 InvalideTimes(DateTime<Utc>, DateTime<Utc>),
40
41 /// The initial or current balance is not positive.
42 ///
43 /// ### Arguments
44 /// * `0` - The invalid balance value.
45 #[error("Balance must be positive (got: {0})")]
46 NegZeroBalance(f64),
47
48 /// The wallet does not have enough funds to place the order.
49 ///
50 /// ### Arguments
51 /// * `0` - The required amount.
52 /// * `1` - The available amount.
53 #[error("Insufficient funds: required {0}, available {1}")]
54 InsufficientFunds(f64, f64),
55
56 /// The free balance is negative.
57 ///
58 /// ### Arguments
59 /// * `0` - The current balance.
60 /// * `1` - The locked funds.
61 #[error("Negative free balance: balance={0}, locked={1}")]
62 NegFreeBalance(f64, f64),
63
64 /// The fees are negative.
65 #[error("Negative fees")]
66 NegZeroFees,
67
68 /// The locked funds are insufficient for the requested amount.
69 ///
70 /// ### Arguments
71 /// * `0` - The currently locked funds.
72 /// * `1` - The requested amount to unlock.
73 #[error("Locked funds {0} are insufficient for amount {1}")]
74 UnlockBalance(f64, f64),
75
76 /// The requested order was not found.
77 #[error("Order not found")]
78 OrderNotFound,
79
80 /// Failed to remove an order.
81 #[error("Failed to remove order")]
82 RemoveOrder,
83
84 /// The requested position was not found.
85 #[error("Position not found")]
86 PositionNotFound,
87
88 /// Failed to remove a position.
89 #[error("Failed to remove position")]
90 RemovePosition,
91
92 /// The exit price is invalid.
93 #[error("Invalid exit price {0}")]
94 ExitPrice(f64),
95
96 /// A generic error with a custom message.
97 ///
98 /// ### Arguments
99 /// * `0` - The error message.
100 #[error("{0}")]
101 Msg(String),
102
103 /// Take profit or stop loss values must be positive.
104 #[error("TakeProfit or StopLoss must be positive")]
105 NegTakeProfitAndStopLoss,
106
107 /// Trailing stop values must be positive.
108 #[error("TrailingStop must be positive and greater than 0")]
109 NegZeroTrailingStop,
110
111 /// The order type is not compatible with the operation.
112 ///
113 /// Use market or limit orders to open a position, and take profit, stop loss, or trailing stop to close a position.
114 #[error("Try another order type")]
115 MismatchedOrderType,
116
117 /// An I/O error occurred.
118 ///
119 /// ### Arguments
120 /// * `0` - The underlying I/O error.
121 #[error("{0}")]
122 Io(#[from] std::io::Error),
123
124 /// A formatter error occured.
125 ///
126 /// ### Arguments
127 /// * `0` - The underlying formatter error.
128 #[cfg(feature = "draws")]
129 #[error("{0}")]
130 Fmt(#[from] std::fmt::Error),
131
132 /// An error with plotters crate.
133 ///
134 /// ### Arguments
135 /// * `0` - The underlying plotters error.
136 #[cfg(feature = "draws")]
137 #[error("{0}")]
138 Plotters(String),
139
140 /// A mutex was poisoned.
141 #[error("{0}")]
142 Mutex(String),
143}
144
145#[cfg(feature = "serde")]
146impl serde::Serialize for Error {
147 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
148 where
149 S: serde::Serializer,
150 {
151 serializer.serialize_str(self.to_string().as_ref())
152 }
153}
154
155#[cfg(feature = "serde")]
156impl<'de> serde::Deserialize<'de> for Error {
157 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
158 where
159 D: serde::Deserializer<'de>,
160 {
161 let s = String::deserialize(deserializer)?;
162 //todo implements others fields
163 Ok(Error::Msg(s))
164 }
165}