barter_data/exchange/binance/
trade.rs1use super::BinanceChannel;
2use crate::{
3 Identifier,
4 event::{MarketEvent, MarketIter},
5 exchange::ExchangeSub,
6 subscription::trade::PublicTrade,
7};
8use barter_instrument::{Side, exchange::ExchangeId};
9use barter_integration::subscription::SubscriptionId;
10use chrono::{DateTime, Utc};
11use serde::{Deserialize, Serialize};
12
13#[derive(Clone, PartialEq, PartialOrd, Debug, Deserialize, Serialize)]
55pub struct BinanceTrade {
56 #[serde(alias = "s", deserialize_with = "de_trade_subscription_id")]
57 pub subscription_id: SubscriptionId,
58 #[serde(
59 alias = "T",
60 deserialize_with = "barter_integration::de::de_u64_epoch_ms_as_datetime_utc"
61 )]
62 pub time: DateTime<Utc>,
63 #[serde(alias = "t")]
64 pub id: u64,
65 #[serde(alias = "p", deserialize_with = "barter_integration::de::de_str")]
66 pub price: f64,
67 #[serde(alias = "q", deserialize_with = "barter_integration::de::de_str")]
68 pub amount: f64,
69 #[serde(alias = "m", deserialize_with = "de_side_from_buyer_is_maker")]
70 pub side: Side,
71}
72
73impl Identifier<Option<SubscriptionId>> for BinanceTrade {
74 fn id(&self) -> Option<SubscriptionId> {
75 Some(self.subscription_id.clone())
76 }
77}
78
79impl<InstrumentKey> From<(ExchangeId, InstrumentKey, BinanceTrade)>
80 for MarketIter<InstrumentKey, PublicTrade>
81{
82 fn from((exchange_id, instrument, trade): (ExchangeId, InstrumentKey, BinanceTrade)) -> Self {
83 Self(vec![Ok(MarketEvent {
84 time_exchange: trade.time,
85 time_received: Utc::now(),
86 exchange: exchange_id,
87 instrument,
88 kind: PublicTrade {
89 id: trade.id.to_string(),
90 price: trade.price,
91 amount: trade.amount,
92 side: trade.side,
93 },
94 })])
95 }
96}
97
98pub fn de_trade_subscription_id<'de, D>(deserializer: D) -> Result<SubscriptionId, D::Error>
101where
102 D: serde::de::Deserializer<'de>,
103{
104 <&str as Deserialize>::deserialize(deserializer)
105 .map(|market| ExchangeSub::from((BinanceChannel::TRADES, market)).id())
106}
107
108pub fn de_side_from_buyer_is_maker<'de, D>(deserializer: D) -> Result<Side, D::Error>
114where
115 D: serde::de::Deserializer<'de>,
116{
117 Deserialize::deserialize(deserializer).map(|buyer_is_maker| {
118 if buyer_is_maker {
119 Side::Sell
120 } else {
121 Side::Buy
122 }
123 })
124}
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129
130 mod de {
131 use std::time::Duration;
132
133 use barter_integration::{de::datetime_utc_from_epoch_duration, error::SocketError};
134 use serde::de::Error;
135
136 use super::*;
137
138 #[test]
139 fn test_binance_trade() {
140 struct TestCase {
141 input: &'static str,
142 expected: Result<BinanceTrade, SocketError>,
143 }
144
145 let tests = vec![
146 TestCase {
147 input: r#"
149 {
150 "e":"trade","E":1649324825173,"s":"ETHUSDT","t":1000000000,
151 "p":"10000.19","q":"0.239000","b":10108767791,"a":10108764858,
152 "T":1749354825200,"m":false,"M":true
153 }
154 "#,
155 expected: Ok(BinanceTrade {
156 subscription_id: SubscriptionId::from("@trade|ETHUSDT"),
157 time: datetime_utc_from_epoch_duration(Duration::from_millis(
158 1749354825200,
159 )),
160 id: 1000000000,
161 price: 10000.19,
162 amount: 0.239000,
163 side: Side::Buy,
164 }),
165 },
166 TestCase {
167 input: r#"{
169 "e":"trade","E":1649324825173,"s":"ETHUSDT","t":1000000000,
170 "p":"10000.19000000","q":"0.239000","b":10108767791,"a":10108764858,
171 "T":1649324825173,"m":"yes","M":true
172 }"#,
173 expected: Err(SocketError::Deserialise {
174 error: serde_json::Error::custom(""),
175 payload: "".to_owned(),
176 }),
177 },
178 TestCase {
179 input: r#"
181 {
182 "e": "trade","E": 1649839266194,"T": 1749354825200,"s": "ETHUSDT",
183 "t": 1000000000,"p":"10000.19","q":"0.239000","X": "MARKET","m": true
184 }
185 "#,
186 expected: Ok(BinanceTrade {
187 subscription_id: SubscriptionId::from("@trade|ETHUSDT"),
188 time: datetime_utc_from_epoch_duration(Duration::from_millis(
189 1749354825200,
190 )),
191 id: 1000000000,
192 price: 10000.19,
193 amount: 0.239000,
194 side: Side::Sell,
195 }),
196 },
197 TestCase {
198 input: r#"
200 {
201 "e": "trade","E": 1649839266194,"T": 1749354825200,"s": "ETHUSDT",
202 "t": 1000000000,"p":"10000.19","q":"0.239000","X": "LIQUIDATION","m": false
203 }
204 "#,
205 expected: Ok(BinanceTrade {
206 subscription_id: SubscriptionId::from("@trade|ETHUSDT"),
207 time: datetime_utc_from_epoch_duration(Duration::from_millis(
208 1749354825200,
209 )),
210 id: 1000000000,
211 price: 10000.19,
212 amount: 0.239000,
213 side: Side::Buy,
214 }),
215 },
216 TestCase {
217 input: r#"{
219 "e": "trade","E": 1649839266194,"T": 1749354825200,"s": "ETHUSDT",
220 "t": 1000000000,"p":"10000.19","q":"0.239000","X": "INSURANCE_FUND","m": false
221 }"#,
222 expected: Ok(BinanceTrade {
223 subscription_id: SubscriptionId::from("@trade|ETHUSDT"),
224 time: datetime_utc_from_epoch_duration(Duration::from_millis(
225 1749354825200,
226 )),
227 id: 1000000000,
228 price: 10000.19,
229 amount: 0.239000,
230 side: Side::Buy,
231 }),
232 },
233 ];
234
235 for (index, test) in tests.into_iter().enumerate() {
236 let actual = serde_json::from_str::<BinanceTrade>(test.input);
237 match (actual, test.expected) {
238 (Ok(actual), Ok(expected)) => {
239 assert_eq!(actual, expected, "TC{} failed", index)
240 }
241 (Err(_), Err(_)) => {
242 }
244 (actual, expected) => {
245 panic!(
247 "TC{index} failed because actual != expected. \nActual: {actual:?}\nExpected: {expected:?}\n"
248 );
249 }
250 }
251 }
252 }
253 }
254}