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}