Skip to main content

lnm_sdk/shared/models/
leverage.rs

1use std::{cmp::Ordering, convert::TryFrom, fmt};
2
3use serde::{Deserialize, Serialize, de};
4
5use super::{
6    SATS_PER_BTC, error::LeverageValidationError, margin::Margin, price::Price, quantity::Quantity,
7    serde_util,
8};
9
10/// A validated leverage value for trading positions.
11///
12/// Leverage represents the multiplier applied to a trader's margin to determine the position size.
13/// This type ensures that leverage values are within acceptable bounds (1x to 100x) and can be
14/// safely used when trading futures.
15///
16/// Leverage values must be:
17/// + Greater than or equal to [`Leverage::MIN`] (1x)
18/// + Less than or equal to [`Leverage::MAX`] (100x)
19///
20/// # Examples
21///
22/// ```
23/// use lnm_sdk::api_v3::models::Leverage;
24///
25/// // Create a leverage value from a float
26/// let leverage = Leverage::try_from(10.0).unwrap();
27/// assert_eq!(leverage.as_f64(), 10.0);
28///
29/// // Values outside the valid range will fail
30/// assert!(Leverage::try_from(0.5).is_err());
31/// assert!(Leverage::try_from(150.0).is_err());
32/// ```
33#[derive(Debug, Clone, Copy, PartialEq)]
34pub struct Leverage(f64);
35
36impl Leverage {
37    /// The minimum allowed leverage value (1x).
38    pub const MIN: Self = Self(1.);
39
40    /// The maximum allowed leverage value (100x).
41    pub const MAX: Self = Self(100.);
42
43    /// Creates a `Leverage` by bounding the given value to the valid range.
44    ///
45    /// This method bounds the input to the range ([Leverage::MIN], [Leverage::MAX]).
46    /// It should be used to ensure a valid `Leverage` without error handling.
47    ///
48    /// **Note:** In order to check whether a value is a valid leverage and receive an error for
49    /// invalid values, use [`Leverage::try_from`].
50    ///
51    /// # Examples
52    ///
53    /// ```
54    /// use lnm_sdk::api_v3::models::Leverage;
55    ///
56    /// // Values within range are preserved
57    /// let l = Leverage::bounded(25.0);
58    /// assert_eq!(l.as_f64(), 25.0);
59    ///
60    /// // Values below minimum are bounded to MIN
61    /// let l = Leverage::bounded(0.5);
62    /// assert_eq!(l, Leverage::MIN);
63    ///
64    /// // Values above maximum are bounded to MAX
65    /// let l = Leverage::bounded(150.0);
66    /// assert_eq!(l, Leverage::MAX);
67    /// ```
68    pub fn bounded<T>(value: T) -> Self
69    where
70        T: Into<f64>,
71    {
72        let as_f64: f64 = value.into();
73        let clamped = as_f64.clamp(Self::MIN.0, Self::MAX.0);
74
75        Self(clamped)
76    }
77
78    /// Returns the leverage value as its underlying `f64` representation.
79    ///
80    /// # Examples
81    ///
82    /// ```
83    /// use lnm_sdk::api_v3::models::Leverage;
84    ///
85    /// let leverage = Leverage::try_from(25.0).unwrap();
86    /// assert_eq!(leverage.as_f64(), 25.0);
87    /// ```
88    pub fn as_f64(&self) -> f64 {
89        self.0
90    }
91
92    /// Calculates leverage from quantity (USD), margin (sats), and price (BTC/USD).
93    ///
94    /// The leverage is calculated using the formula:
95    ///
96    /// leverage = (quantity * SATS_PER_BTC) / (margin * price)
97    ///
98    /// # Examples
99    ///
100    /// ```
101    /// use lnm_sdk::api_v3::models::{Leverage, Quantity, Margin, Price};
102    ///
103    /// let quantity = Quantity::try_from(1_000).unwrap(); // Quantity in USD
104    /// let margin = Margin::try_from(20_000).unwrap(); // Margin in sats
105    /// let price = Price::try_from(100_000.0).unwrap(); // Price in USD/BTC
106    ///
107    /// let leverage = Leverage::try_calculate(quantity, margin, price).unwrap();
108    /// ```
109    pub fn try_calculate(
110        quantity: Quantity,
111        margin: Margin,
112        price: Price,
113    ) -> Result<Self, LeverageValidationError> {
114        let leverage_value = quantity.as_f64() * SATS_PER_BTC / (margin.as_f64() * price.as_f64());
115
116        Self::try_from(leverage_value)
117    }
118}
119
120impl From<Leverage> for f64 {
121    fn from(value: Leverage) -> f64 {
122        value.0
123    }
124}
125
126impl TryFrom<u8> for Leverage {
127    type Error = LeverageValidationError;
128
129    fn try_from(value: u8) -> Result<Self, Self::Error> {
130        Self::try_from(value as f64)
131    }
132}
133
134impl TryFrom<u16> for Leverage {
135    type Error = LeverageValidationError;
136
137    fn try_from(value: u16) -> Result<Self, Self::Error> {
138        Self::try_from(value as f64)
139    }
140}
141
142impl TryFrom<u32> for Leverage {
143    type Error = LeverageValidationError;
144
145    fn try_from(value: u32) -> Result<Self, Self::Error> {
146        Self::try_from(value as f64)
147    }
148}
149
150impl TryFrom<u64> for Leverage {
151    type Error = LeverageValidationError;
152
153    fn try_from(value: u64) -> Result<Self, Self::Error> {
154        Self::try_from(value as f64)
155    }
156}
157
158impl TryFrom<i8> for Leverage {
159    type Error = LeverageValidationError;
160
161    fn try_from(value: i8) -> Result<Self, Self::Error> {
162        Self::try_from(value as f64)
163    }
164}
165
166impl TryFrom<i16> for Leverage {
167    type Error = LeverageValidationError;
168
169    fn try_from(value: i16) -> Result<Self, Self::Error> {
170        Self::try_from(value as f64)
171    }
172}
173
174impl TryFrom<i32> for Leverage {
175    type Error = LeverageValidationError;
176
177    fn try_from(leverage: i32) -> Result<Self, Self::Error> {
178        Self::try_from(leverage as f64)
179    }
180}
181
182impl TryFrom<i64> for Leverage {
183    type Error = LeverageValidationError;
184
185    fn try_from(value: i64) -> Result<Self, Self::Error> {
186        Self::try_from(value as f64)
187    }
188}
189
190impl TryFrom<usize> for Leverage {
191    type Error = LeverageValidationError;
192
193    fn try_from(value: usize) -> Result<Self, Self::Error> {
194        Self::try_from(value as f64)
195    }
196}
197
198impl TryFrom<isize> for Leverage {
199    type Error = LeverageValidationError;
200
201    fn try_from(value: isize) -> Result<Self, Self::Error> {
202        Self::try_from(value as f64)
203    }
204}
205
206impl TryFrom<f32> for Leverage {
207    type Error = LeverageValidationError;
208
209    fn try_from(value: f32) -> Result<Self, Self::Error> {
210        Self::try_from(value as f64)
211    }
212}
213
214impl TryFrom<f64> for Leverage {
215    type Error = LeverageValidationError;
216
217    fn try_from(value: f64) -> Result<Self, Self::Error> {
218        if value < Self::MIN.0 {
219            return Err(LeverageValidationError::TooLow { value });
220        }
221
222        if value > Self::MAX.0 {
223            return Err(LeverageValidationError::TooHigh { value });
224        }
225
226        Ok(Leverage(value))
227    }
228}
229
230impl Eq for Leverage {}
231
232impl Ord for Leverage {
233    fn cmp(&self, other: &Self) -> Ordering {
234        self.0
235            .partial_cmp(&other.0)
236            .expect("`Leverage` must be finite")
237    }
238}
239
240impl PartialOrd for Leverage {
241    fn partial_cmp(&self, other: &Leverage) -> Option<Ordering> {
242        Some(self.cmp(other))
243    }
244}
245
246impl fmt::Display for Leverage {
247    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
248        self.0.fmt(f)
249    }
250}
251
252impl Serialize for Leverage {
253    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
254    where
255        S: serde::Serializer,
256    {
257        serde_util::float_without_decimal::serialize(&self.0, serializer)
258    }
259}
260
261impl<'de> Deserialize<'de> for Leverage {
262    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
263    where
264        D: serde::Deserializer<'de>,
265    {
266        let leverage_f64 = f64::deserialize(deserializer)?;
267        Leverage::try_from(leverage_f64).map_err(|e| de::Error::custom(e.to_string()))
268    }
269}