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