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