rustrade_data/exchange/binance/book/
l2.rs1use super::{super::channel::BinanceChannel, BinanceLevel};
2use crate::{
3 Identifier, books::OrderBook, event::MarketEvent, exchange::subscription::ExchangeSub,
4 subscription::book::OrderBookEvent,
5};
6use chrono::{DateTime, Utc};
7use derive_more::Constructor;
8use rustrade_instrument::exchange::ExchangeId;
9use rustrade_integration::subscription::SubscriptionId;
10use serde::{Deserialize, Serialize};
11
12#[derive(Debug, Constructor)]
13pub struct BinanceOrderBookL2Meta<InstrumentKey, Sequencer> {
14 pub key: InstrumentKey,
15 pub sequencer: Sequencer,
16}
17
18#[derive(Clone, PartialEq, PartialOrd, Debug, Deserialize, Serialize)]
54pub struct BinanceOrderBookL2Snapshot {
55 #[serde(rename = "lastUpdateId")]
56 pub last_update_id: u64,
57 #[serde(default, rename = "E", with = "chrono::serde::ts_milliseconds_option")]
58 pub time_exchange: Option<DateTime<Utc>>,
59 #[serde(default, rename = "T", with = "chrono::serde::ts_milliseconds_option")]
60 pub time_engine: Option<DateTime<Utc>>,
61 pub bids: Vec<BinanceLevel>,
62 pub asks: Vec<BinanceLevel>,
63}
64
65impl<InstrumentKey> From<(ExchangeId, InstrumentKey, BinanceOrderBookL2Snapshot)>
66 for MarketEvent<InstrumentKey, OrderBookEvent>
67{
68 fn from(
69 (exchange, instrument, snapshot): (ExchangeId, InstrumentKey, BinanceOrderBookL2Snapshot),
70 ) -> Self {
71 let time_received = Utc::now();
72 Self {
73 time_exchange: snapshot.time_exchange.unwrap_or(time_received),
74 time_received,
75 exchange,
76 instrument,
77 kind: OrderBookEvent::from(snapshot),
78 }
79 }
80}
81
82impl From<BinanceOrderBookL2Snapshot> for OrderBookEvent {
83 fn from(snapshot: BinanceOrderBookL2Snapshot) -> Self {
84 Self::Snapshot(OrderBook::new(
85 snapshot.last_update_id,
86 snapshot.time_engine,
87 snapshot.bids,
88 snapshot.asks,
89 ))
90 }
91}
92
93pub fn de_ob_l2_subscription_id<'de, D>(deserializer: D) -> Result<SubscriptionId, D::Error>
100where
101 D: serde::de::Deserializer<'de>,
102{
103 <&str as Deserialize>::deserialize(deserializer)
104 .map(|market| ExchangeSub::from((BinanceChannel::ORDER_BOOK_L2, market)).id())
105}
106
107#[cfg(test)]
108#[allow(clippy::unwrap_used)] mod tests {
110 use super::*;
111
112 mod de {
113 use super::*;
114 use rust_decimal_macros::dec;
115
116 #[test]
117 fn test_binance_order_book_l2_snapshot() {
118 struct TestCase {
119 input: &'static str,
120 expected: BinanceOrderBookL2Snapshot,
121 }
122
123 let tests = vec![
124 TestCase {
125 input: r#"
127 {
128 "lastUpdateId": 1027024,
129 "bids": [
130 [
131 "4.00000000",
132 "431.00000000"
133 ]
134 ],
135 "asks": [
136 [
137 "4.00000200",
138 "12.00000000"
139 ]
140 ]
141 }
142 "#,
143 expected: BinanceOrderBookL2Snapshot {
144 last_update_id: 1027024,
145 time_exchange: Default::default(),
146 time_engine: Default::default(),
147 bids: vec![BinanceLevel {
148 price: dec!(4.00000000),
149 amount: dec!(431.00000000),
150 }],
151 asks: vec![BinanceLevel {
152 price: dec!(4.00000200),
153 amount: dec!(12.00000000),
154 }],
155 },
156 },
157 TestCase {
158 input: r#"
160 {
161 "lastUpdateId": 1027024,
162 "E": 1589436922972,
163 "T": 1589436922959,
164 "bids": [
165 [
166 "4.00000000",
167 "431.00000000"
168 ]
169 ],
170 "asks": [
171 [
172 "4.00000200",
173 "12.00000000"
174 ]
175 ]
176 }
177 "#,
178 expected: BinanceOrderBookL2Snapshot {
179 last_update_id: 1027024,
180 time_exchange: Some(
181 DateTime::from_timestamp_millis(1589436922972).unwrap(),
182 ),
183 time_engine: Some(DateTime::from_timestamp_millis(1589436922959).unwrap()),
184 bids: vec![BinanceLevel {
185 price: dec!(4.0),
186 amount: dec!(431.0),
187 }],
188 asks: vec![BinanceLevel {
189 price: dec!(4.00000200),
190 amount: dec!(12.0),
191 }],
192 },
193 },
194 ];
195
196 for (index, test) in tests.into_iter().enumerate() {
197 assert_eq!(
198 serde_json::from_str::<BinanceOrderBookL2Snapshot>(test.input).unwrap(),
199 test.expected,
200 "TC{} failed",
201 index
202 );
203 }
204 }
205 }
206}