Skip to main content

rustrade_data/exchange/binance/futures/
liquidation.rs

1use super::super::BinanceChannel;
2use crate::{
3    Identifier,
4    event::{MarketEvent, MarketIter},
5    subscription::liquidation::Liquidation,
6};
7use chrono::{DateTime, Utc};
8use rustrade_instrument::{Side, exchange::ExchangeId};
9use rustrade_integration::subscription::SubscriptionId;
10use serde::{Deserialize, Serialize};
11
12/// [`BinanceFuturesUsd`](super::BinanceFuturesUsd) Liquidation order message.
13///
14/// ### Raw Payload Examples
15/// See docs: <https://binance-docs.github.io/apidocs/futures/en/#liquidation-order-streams>
16/// ```json
17/// {
18///     "e": "forceOrder",
19///     "E": 1665523974222,
20///     "o": {
21///         "s": "BTCUSDT",
22///         "S": "SELL",
23///         "o": "LIMIT",
24///         "f": "IOC",
25///         "q": "0.009",
26///         "p": "18917.15",
27///         "ap": "18990.00",
28///         "X": "FILLED",
29///         "l": "0.009",
30///         "z": "0.009",
31///         "T": 1665523974217
32///     }
33/// }
34/// ```
35#[derive(Clone, PartialEq, PartialOrd, Debug, Deserialize, Serialize)]
36pub struct BinanceLiquidation {
37    #[serde(alias = "o")]
38    pub order: BinanceLiquidationOrder,
39}
40
41/// [`BinanceFuturesUsd`](super::BinanceFuturesUsd) Liquidation order.
42///
43/// ### Raw Payload Examples
44/// ```json
45/// {
46///     "s": "BTCUSDT",
47///     "S": "SELL",
48///     "o": "LIMIT",
49///     "f": "IOC",
50///     "q": "0.009",
51///     "p": "18917.15",
52///     "ap": "18990.00",
53///     "X": "FILLED",
54///     "l": "0.009",
55///     "z": "0.009",
56///     "T": 1665523974217
57/// }
58/// ```
59///
60/// See docs: <https://binance-docs.github.io/apidocs/futures/en/#liquidation-order-streams>
61#[derive(Clone, PartialEq, PartialOrd, Debug, Deserialize, Serialize)]
62pub struct BinanceLiquidationOrder {
63    #[serde(alias = "s", deserialize_with = "de_liquidation_subscription_id")]
64    pub subscription_id: SubscriptionId,
65    #[serde(alias = "S")]
66    pub side: Side,
67    #[serde(
68        alias = "p",
69        deserialize_with = "rustrade_integration::serde::de::de_str"
70    )]
71    pub price: f64,
72    #[serde(
73        alias = "q",
74        deserialize_with = "rustrade_integration::serde::de::de_str"
75    )]
76    pub quantity: f64,
77    #[serde(
78        alias = "T",
79        deserialize_with = "rustrade_integration::serde::de::de_u64_epoch_ms_as_datetime_utc"
80    )]
81    pub time: DateTime<Utc>,
82}
83
84impl Identifier<Option<SubscriptionId>> for BinanceLiquidation {
85    fn id(&self) -> Option<SubscriptionId> {
86        Some(self.order.subscription_id.clone())
87    }
88}
89
90impl<InstrumentKey> From<(ExchangeId, InstrumentKey, BinanceLiquidation)>
91    for MarketIter<InstrumentKey, Liquidation>
92{
93    fn from(
94        (exchange_id, instrument, liquidation): (ExchangeId, InstrumentKey, BinanceLiquidation),
95    ) -> Self {
96        Self(vec![Ok(MarketEvent {
97            time_exchange: liquidation.order.time,
98            time_received: Utc::now(),
99            exchange: exchange_id,
100            instrument,
101            kind: Liquidation {
102                side: liquidation.order.side,
103                price: liquidation.order.price,
104                quantity: liquidation.order.quantity,
105                time: liquidation.order.time,
106            },
107        })])
108    }
109}
110
111/// Deserialize a [`BinanceLiquidationOrder`] "s" (eg/ "BTCUSDT") as the associated
112/// [`SubscriptionId`].
113///
114/// eg/ "forceOrder|BTCUSDT"
115pub fn de_liquidation_subscription_id<'de, D>(deserializer: D) -> Result<SubscriptionId, D::Error>
116where
117    D: serde::de::Deserializer<'de>,
118{
119    Deserialize::deserialize(deserializer).map(|market: String| {
120        SubscriptionId::from(format!("{}|{}", BinanceChannel::LIQUIDATIONS.0, market))
121    })
122}
123
124#[cfg(test)]
125#[allow(clippy::unwrap_used)] // Test code: panics on bad input are acceptable
126mod tests {
127    use super::*;
128
129    mod de {
130        use super::*;
131        use rustrade_integration::serde::de::datetime_utc_from_epoch_duration;
132        use std::time::Duration;
133
134        #[test]
135        fn test_binance_liquidation() {
136            let input = r#"
137            {
138                "e": "forceOrder",
139                "E": 1665523974222,
140                "o": {
141                    "s": "BTCUSDT",
142                    "S": "SELL",
143                    "o": "LIMIT",
144                    "f": "IOC",
145                    "q": "0.009",
146                    "p": "18917.15",
147                    "ap": "18990.00",
148                    "X": "FILLED",
149                    "l": "0.009",
150                    "z": "0.009",
151                    "T": 1665523974217
152                }
153            }
154            "#;
155
156            assert_eq!(
157                serde_json::from_str::<BinanceLiquidation>(input).unwrap(),
158                BinanceLiquidation {
159                    order: BinanceLiquidationOrder {
160                        subscription_id: SubscriptionId::from("@forceOrder|BTCUSDT"),
161                        side: Side::Sell,
162                        price: 18917.15,
163                        quantity: 0.009,
164                        time: datetime_utc_from_epoch_duration(Duration::from_millis(
165                            1665523974217,
166                        )),
167                    },
168                }
169            );
170        }
171    }
172}