barter_data/exchange/kraken/book/
l1.rs1use super::super::KrakenMessage;
2use crate::{
3 Identifier,
4 books::Level,
5 event::{MarketEvent, MarketIter},
6 exchange::{kraken::channel::KrakenChannel, subscription::ExchangeSub},
7 subscription::book::OrderBookL1,
8};
9use barter_instrument::exchange::ExchangeId;
10use barter_integration::{de::extract_next, subscription::SubscriptionId};
11use chrono::{DateTime, Utc};
12use rust_decimal::Decimal;
13use serde::{Deserialize, Serialize};
14
15pub type KrakenOrderBookL1 = KrakenMessage<KrakenOrderBookL1Inner>;
18
19#[derive(Clone, PartialEq, PartialOrd, Debug, Serialize)]
26pub struct KrakenOrderBookL1Inner {
27 pub subscription_id: SubscriptionId,
28 pub spread: KrakenSpread,
29}
30
31#[derive(Clone, Copy, PartialEq, PartialOrd, Debug, Deserialize, Serialize)]
37pub struct KrakenSpread {
38 #[serde(with = "rust_decimal::serde::str")]
39 pub best_bid_price: Decimal,
40 #[serde(with = "rust_decimal::serde::str")]
41 pub best_ask_price: Decimal,
42 #[serde(deserialize_with = "barter_integration::de::de_str_f64_epoch_s_as_datetime_utc")]
43 pub time: DateTime<Utc>,
44 #[serde(with = "rust_decimal::serde::str")]
45 pub best_bid_amount: Decimal,
46 #[serde(with = "rust_decimal::serde::str")]
47 pub best_ask_amount: Decimal,
48}
49
50impl Identifier<Option<SubscriptionId>> for KrakenOrderBookL1Inner {
51 fn id(&self) -> Option<SubscriptionId> {
52 Some(self.subscription_id.clone())
53 }
54}
55
56impl<InstrumentKey> From<(ExchangeId, InstrumentKey, KrakenOrderBookL1)>
57 for MarketIter<InstrumentKey, OrderBookL1>
58{
59 fn from(
60 (exchange_id, instrument, book): (ExchangeId, InstrumentKey, KrakenOrderBookL1),
61 ) -> Self {
62 match book {
63 KrakenOrderBookL1::Data(book) => {
64 let best_ask = if book.spread.best_ask_price.is_zero() {
65 None
66 } else {
67 Some(Level::new(
68 book.spread.best_ask_price,
69 book.spread.best_ask_amount,
70 ))
71 };
72
73 let best_bid = if book.spread.best_bid_price.is_zero() {
74 None
75 } else {
76 Some(Level::new(
77 book.spread.best_bid_price,
78 book.spread.best_bid_amount,
79 ))
80 };
81
82 Self(vec![Ok(MarketEvent {
83 time_exchange: book.spread.time,
84 time_received: Utc::now(),
85 exchange: exchange_id,
86 instrument,
87 kind: OrderBookL1 {
88 last_update_time: book.spread.time,
89 best_bid,
90 best_ask,
91 },
92 })])
93 }
94 KrakenOrderBookL1::Event(_) => MarketIter(vec![]),
95 }
96 }
97}
98
99impl<'de> serde::de::Deserialize<'de> for KrakenOrderBookL1Inner {
100 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
101 where
102 D: serde::Deserializer<'de>,
103 {
104 struct SeqVisitor;
105
106 impl<'de> serde::de::Visitor<'de> for SeqVisitor {
107 type Value = KrakenOrderBookL1Inner;
108
109 fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
110 formatter.write_str("KrakenOrderBookL1Inner struct from the Kraken WebSocket API")
111 }
112
113 fn visit_seq<SeqAccessor>(
114 self,
115 mut seq: SeqAccessor,
116 ) -> Result<Self::Value, SeqAccessor::Error>
117 where
118 SeqAccessor: serde::de::SeqAccess<'de>,
119 {
120 let _: serde::de::IgnoredAny = extract_next(&mut seq, "channelID")?;
126
127 let spread = extract_next(&mut seq, "spread")?;
129
130 let _: serde::de::IgnoredAny = extract_next(&mut seq, "channelName")?;
132
133 let subscription_id = extract_next::<SeqAccessor, String>(&mut seq, "pair")
135 .map(|market| ExchangeSub::from((KrakenChannel::ORDER_BOOK_L1, market)).id())?;
136
137 while seq.next_element::<serde::de::IgnoredAny>()?.is_some() {}
140
141 Ok(KrakenOrderBookL1Inner {
142 subscription_id,
143 spread,
144 })
145 }
146 }
147
148 deserializer.deserialize_seq(SeqVisitor)
150 }
151}
152
153#[cfg(test)]
154mod tests {
155 use super::*;
156
157 mod de {
158 use super::*;
159 use barter_integration::{
160 de::datetime_utc_from_epoch_duration, error::SocketError, subscription::SubscriptionId,
161 };
162 use rust_decimal_macros::dec;
163
164 #[test]
165 fn test_kraken_message_order_book_l1() {
166 struct TestCase {
167 input: &'static str,
168 expected: Result<KrakenOrderBookL1, SocketError>,
169 }
170
171 let tests = vec![TestCase {
172 input: r#"
174 [
175 0,
176 [
177 "5698.40000",
178 "5700.00000",
179 "1542057299.545897",
180 "1.01234567",
181 "0.98765432"
182 ],
183 "spread",
184 "XBT/USD"
185 ]
186 "#,
187 expected: Ok(KrakenOrderBookL1::Data(KrakenOrderBookL1Inner {
188 subscription_id: SubscriptionId::from("spread|XBT/USD"),
189 spread: KrakenSpread {
190 best_bid_price: dec!(5698.40000),
191 best_bid_amount: dec!(1.01234567),
192 time: datetime_utc_from_epoch_duration(std::time::Duration::from_secs_f64(
193 1542057299.545897,
194 )),
195 best_ask_price: dec!(5700.00000),
196 best_ask_amount: dec!(0.98765432),
197 },
198 })),
199 }];
200
201 for (index, test) in tests.into_iter().enumerate() {
202 let actual = serde_json::from_str::<KrakenOrderBookL1>(test.input);
203 match (actual, test.expected) {
204 (Ok(actual), Ok(expected)) => {
205 assert_eq!(actual, expected, "TC{} failed", index)
206 }
207 (Err(_), Err(_)) => {
208 }
210 (actual, expected) => {
211 panic!(
213 "TC{index} failed because actual != expected. \nActual: {actual:?}\nExpected: {expected:?}\n"
214 );
215 }
216 }
217 }
218 }
219 }
220}