rustrade_data/exchange/binance/book/
l1.rs1use crate::{
2 Identifier,
3 books::Level,
4 event::{MarketEvent, MarketIter},
5 exchange::{binance::channel::BinanceChannel, subscription::ExchangeSub},
6 subscription::book::OrderBookL1,
7};
8use chrono::{DateTime, Utc};
9use rust_decimal::Decimal;
10use rustrade_instrument::exchange::ExchangeId;
11use rustrade_integration::subscription::SubscriptionId;
12use serde::{Deserialize, Serialize};
13
14#[derive(Clone, PartialEq, PartialOrd, Debug, Deserialize, Serialize)]
43pub struct BinanceOrderBookL1 {
44 #[serde(alias = "s", deserialize_with = "de_ob_l1_subscription_id")]
45 pub subscription_id: SubscriptionId,
46 #[serde(
47 alias = "T",
48 deserialize_with = "rustrade_integration::serde::de::de_u64_epoch_ms_as_datetime_utc",
49 default = "Utc::now"
50 )]
51 pub time: DateTime<Utc>,
52 #[serde(alias = "b", with = "rust_decimal::serde::str")]
53 pub best_bid_price: Decimal,
54 #[serde(alias = "B", with = "rust_decimal::serde::str")]
55 pub best_bid_amount: Decimal,
56 #[serde(alias = "a", with = "rust_decimal::serde::str")]
57 pub best_ask_price: Decimal,
58 #[serde(alias = "A", with = "rust_decimal::serde::str")]
59 pub best_ask_amount: Decimal,
60}
61
62impl Identifier<Option<SubscriptionId>> for BinanceOrderBookL1 {
63 fn id(&self) -> Option<SubscriptionId> {
64 Some(self.subscription_id.clone())
65 }
66}
67
68impl<InstrumentKey> From<(ExchangeId, InstrumentKey, BinanceOrderBookL1)>
69 for MarketIter<InstrumentKey, OrderBookL1>
70{
71 fn from(
72 (exchange_id, instrument, book): (ExchangeId, InstrumentKey, BinanceOrderBookL1),
73 ) -> Self {
74 let best_ask = if book.best_ask_price.is_zero() {
75 None
76 } else {
77 Some(Level::new(book.best_ask_price, book.best_ask_amount))
78 };
79
80 let best_bid = if book.best_bid_price.is_zero() {
81 None
82 } else {
83 Some(Level::new(book.best_bid_price, book.best_bid_amount))
84 };
85
86 Self(vec![Ok(MarketEvent {
87 time_exchange: book.time,
88 time_received: Utc::now(),
89 exchange: exchange_id,
90 instrument,
91 kind: OrderBookL1 {
92 last_update_time: book.time,
93 best_bid,
94 best_ask,
95 },
96 })])
97 }
98}
99
100pub fn de_ob_l1_subscription_id<'de, D>(deserializer: D) -> Result<SubscriptionId, D::Error>
104where
105 D: serde::de::Deserializer<'de>,
106{
107 <&str as Deserialize>::deserialize(deserializer)
108 .map(|market| ExchangeSub::from((BinanceChannel::ORDER_BOOK_L1, market)).id())
109}
110
111#[cfg(test)]
112#[allow(clippy::unwrap_used)] mod tests {
114 use super::*;
115
116 mod de {
117 use super::*;
118 use rust_decimal_macros::dec;
119
120 #[test]
121 fn test_binance_order_book_l1() {
122 struct TestCase {
123 input: &'static str,
124 expected: BinanceOrderBookL1,
125 }
126
127 let time = Utc::now();
128
129 let tests = vec![
130 TestCase {
131 input: r#"
133 {
134 "u":22606535573,
135 "s":"ETHUSDT",
136 "b":"1215.27000000",
137 "B":"32.49110000",
138 "a":"1215.28000000",
139 "A":"13.93900000"
140 }
141 "#,
142 expected: BinanceOrderBookL1 {
143 subscription_id: SubscriptionId::from("@bookTicker|ETHUSDT"),
144 time,
145 best_bid_price: dec!(1215.27000000),
146 best_bid_amount: dec!(32.49110000),
147 best_ask_price: dec!(1215.28000000),
148 best_ask_amount: dec!(13.93900000),
149 },
150 },
151 TestCase {
152 input: r#"
154 {
155 "e":"bookTicker",
156 "u":2286618712950,
157 "s":"BTCUSDT",
158 "b":"16858.90",
159 "B":"13.692",
160 "a":"16859.00",
161 "A":"30.219",
162 "T":1671621244670,
163 "E":1671621244673
164 }"#,
165 expected: BinanceOrderBookL1 {
166 subscription_id: SubscriptionId::from("@bookTicker|BTCUSDT"),
167 time,
168 best_bid_price: dec!(16858.90),
169 best_bid_amount: dec!(13.692),
170 best_ask_price: dec!(16859.00),
171 best_ask_amount: dec!(30.219),
172 },
173 },
174 ];
175
176 for (index, test) in tests.into_iter().enumerate() {
177 let actual = serde_json::from_str::<BinanceOrderBookL1>(test.input).unwrap();
178 let actual = BinanceOrderBookL1 { time, ..actual };
179 assert_eq!(actual, test.expected, "TC{} failed", index);
180 }
181 }
182 }
183}