barter_data/exchange/bitfinex/
trade.rs

1use crate::{
2    event::{MarketEvent, MarketIter},
3    subscription::trade::PublicTrade,
4};
5use barter_instrument::{Side, exchange::ExchangeId};
6use barter_integration::de::{datetime_utc_from_epoch_duration, extract_next};
7use chrono::{DateTime, Utc};
8use serde::Serialize;
9
10/// [`Bitfinex`](super::Bitfinex) real-time trade message.
11///
12/// ### Raw Payload Examples
13/// Format: \[ID, TIME, AMOUNT, PRICE\], <br> where +/- of amount indicates Side
14///
15/// See docs: <https://docs.bitfinex.com/reference/ws-public-trades>
16///
17/// #### Side::Buy Trade
18/// See docs: <https://docs.bitfinex.com/reference/ws-public-trades>
19/// ```json
20/// [420191,"te",[1225484398,1665452200022,0.08980641,19027.02807752]]
21/// ```
22///
23/// #### Side::Sell Trade
24/// See docs: <https://docs.bitfinex.com/reference/ws-public-trades>
25/// ```json
26/// [420191,"te",[1225484398,1665452200022,-0.08980641,19027.02807752]]
27/// ```
28///
29/// ## Notes:
30/// - [`Bitfinex`](super::Bitfinex) trades subscriptions results in receiving tag="te" & tag="tu"
31///   trades, both of which are identical.
32/// - "te" trades arrive marginally faster.
33/// - Therefore, tag="tu" trades are filtered out and considered only as additional Heartbeats.
34///
35/// See docs: <https://docs.bitfinex.com/reference/ws-public-trades>
36#[derive(Clone, Copy, PartialEq, PartialOrd, Debug, Serialize)]
37pub struct BitfinexTrade {
38    pub id: u64,
39    pub time: DateTime<Utc>,
40    pub side: Side,
41    pub price: f64,
42    pub amount: f64,
43}
44
45impl<InstrumentKey> From<(ExchangeId, InstrumentKey, BitfinexTrade)>
46    for MarketIter<InstrumentKey, PublicTrade>
47{
48    fn from((exchange_id, instrument, trade): (ExchangeId, InstrumentKey, BitfinexTrade)) -> Self {
49        Self(vec![Ok(MarketEvent {
50            time_exchange: trade.time,
51            time_received: Utc::now(),
52            exchange: exchange_id,
53            instrument,
54            kind: PublicTrade {
55                id: trade.id.to_string(),
56                price: trade.price,
57                amount: trade.amount,
58                side: trade.side,
59            },
60        })])
61    }
62}
63
64impl<'de> serde::Deserialize<'de> for BitfinexTrade {
65    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
66    where
67        D: serde::de::Deserializer<'de>,
68    {
69        struct SeqVisitor;
70
71        impl<'de> serde::de::Visitor<'de> for SeqVisitor {
72            type Value = BitfinexTrade;
73
74            fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
75                formatter.write_str("BitfinexTrade struct from the Bitfinex WebSocket API")
76            }
77
78            fn visit_seq<SeqAccessor>(
79                self,
80                mut seq: SeqAccessor,
81            ) -> Result<Self::Value, SeqAccessor::Error>
82            where
83                SeqAccessor: serde::de::SeqAccess<'de>,
84            {
85                // Trade: [ID, TIME, AMOUNT,PRICE]
86                let id = extract_next(&mut seq, "id")?;
87                let time_millis = extract_next(&mut seq, "time")?;
88                let amount: f64 = extract_next(&mut seq, "amount")?;
89                let price = extract_next(&mut seq, "price")?;
90                let side = match amount.is_sign_positive() {
91                    true => Side::Buy,
92                    false => Side::Sell,
93                };
94
95                // Ignore any additional elements or SerDe will fail
96                //  '--> Bitfinex may add fields without warning
97                while seq.next_element::<serde::de::IgnoredAny>()?.is_some() {}
98
99                Ok(BitfinexTrade {
100                    id,
101                    time: datetime_utc_from_epoch_duration(std::time::Duration::from_millis(
102                        time_millis,
103                    )),
104                    price,
105                    amount: amount.abs(),
106                    side,
107                })
108            }
109        }
110
111        // Use Visitor implementation to deserialise the BitfinexTrade message
112        deserializer.deserialize_seq(SeqVisitor)
113    }
114}