kraken_async_rs/wss/messages/
market_data_messages.rs

1use crate::crypto::secrets::Token;
2use crate::response_types::BuySell;
3use rust_decimal::Decimal;
4use serde::{Deserialize, Serialize};
5use serde_with::{serde_as, skip_serializing_none};
6
7#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Copy)]
8#[serde(rename_all = "lowercase")]
9pub enum EventTrigger {
10    Bbo,
11    Trades,
12}
13
14#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Copy)]
15#[serde(rename_all = "lowercase")]
16pub enum OrderbookEvent {
17    Add,
18    Modify,
19    Delete,
20}
21
22#[derive(Debug, Deserialize, Clone, PartialEq, Eq, Copy)]
23#[serde(rename_all = "lowercase")]
24pub enum MarketLimit {
25    Market,
26    Limit,
27}
28
29#[derive(Debug, Deserialize, Clone, PartialEq, Eq, Copy)]
30#[serde(rename_all = "lowercase")]
31pub enum AssetStatus {
32    DepositOnly,
33    Disabled,
34    Enabled,
35    FundingTemporarilyDisabled,
36    WithdrawalOnly,
37    WorkInProgress,
38}
39
40#[derive(Debug, Deserialize, Clone, PartialEq, Eq, Copy)]
41#[serde(rename_all = "snake_case")]
42pub enum PairStatus {
43    CancelOnly,
44    Delisted,
45    LimitOnly,
46    Maintenance,
47    Online,
48    PostOnly,
49    ReduceOnly,
50    WorkInProgress,
51}
52
53#[serde_as]
54#[skip_serializing_none]
55#[derive(Debug, Serialize, Clone)]
56pub struct TickerSubscription {
57    pub channel: String,
58    pub symbol: Vec<String>,
59    pub event_trigger: Option<EventTrigger>,
60    pub snapshot: Option<bool>,
61}
62
63impl TickerSubscription {
64    pub fn new(symbol: Vec<String>) -> Self {
65        TickerSubscription {
66            channel: "ticker".to_string(),
67            symbol,
68            event_trigger: None,
69            snapshot: None,
70        }
71    }
72}
73
74#[derive(Debug, Deserialize, PartialEq)]
75pub struct Ticker {
76    pub ask: Decimal,
77    #[serde(rename = "ask_qty")]
78    pub ask_quantity: Decimal,
79    pub bid: Decimal,
80    #[serde(rename = "bid_qty")]
81    pub bid_quantity: Decimal,
82    pub change: Decimal,
83    pub change_pct: Decimal,
84    pub high: Decimal,
85    pub last: Decimal,
86    pub low: Decimal,
87    pub symbol: String,
88    pub volume: Decimal,
89    pub vwap: Decimal,
90}
91
92#[serde_as]
93#[skip_serializing_none]
94#[derive(Debug, Serialize, Clone)]
95pub struct BookSubscription {
96    pub channel: String,
97    pub symbol: Vec<String>,
98    pub depth: Option<i32>,
99    pub snapshot: Option<bool>,
100    /// only needed for L3 subscription
101    pub token: Option<Token>,
102}
103
104impl BookSubscription {
105    pub fn new(symbol: Vec<String>) -> Self {
106        BookSubscription {
107            channel: "book".to_string(),
108            symbol,
109            depth: None,
110            snapshot: None,
111            token: None,
112        }
113    }
114
115    pub fn new_l3(symbol: Vec<String>, token: Token) -> Self {
116        BookSubscription {
117            channel: "level3".to_string(),
118            symbol,
119            depth: None,
120            snapshot: None,
121            token: Some(token),
122        }
123    }
124}
125
126#[derive(Debug, Deserialize, PartialEq)]
127#[serde(untagged)]
128pub enum L2 {
129    Orderbook(Orderbook),
130    Update(OrderbookUpdate),
131}
132
133#[derive(Debug, Deserialize, PartialEq)]
134pub struct BidAsk {
135    pub price: Decimal,
136    #[serde(rename = "qty")]
137    pub quantity: Decimal,
138}
139
140#[derive(Debug, Deserialize, PartialEq)]
141#[serde(deny_unknown_fields)]
142pub struct Orderbook {
143    pub symbol: String,
144    pub checksum: u32,
145    pub bids: Vec<BidAsk>,
146    pub asks: Vec<BidAsk>,
147}
148
149#[derive(Debug, Deserialize, PartialEq)]
150pub struct OrderbookUpdate {
151    pub symbol: String,
152    pub checksum: u32,
153    pub timestamp: String,
154    pub bids: Vec<BidAsk>,
155    pub asks: Vec<BidAsk>,
156}
157
158#[derive(Debug, Deserialize, PartialEq)]
159#[serde(untagged)]
160pub enum L3 {
161    Orderbook(L3Orderbook),
162    Update(L3OrderbookUpdate),
163}
164
165#[derive(Debug, Deserialize, PartialEq)]
166pub struct L3Orderbook {
167    pub symbol: String,
168    pub bids: Vec<L3BidAsk>,
169    pub asks: Vec<L3BidAsk>,
170    pub checksum: u32,
171    pub timestamp: String, // rfc3339
172}
173
174#[derive(Debug, Deserialize, PartialEq)]
175pub struct L3OrderbookUpdate {
176    pub symbol: String,
177    pub bids: Vec<L3BidAskUpdate>,
178    pub asks: Vec<L3BidAskUpdate>,
179    pub checksum: u32,
180    pub timestamp: String, // rfc3339
181}
182
183#[derive(Debug, Deserialize, PartialEq)]
184#[serde(deny_unknown_fields)]
185pub struct L3BidAsk {
186    pub order_id: String,
187    pub limit_price: Decimal,
188    #[serde(rename = "order_qty")]
189    pub order_quantity: Decimal,
190    pub timestamp: String,
191}
192
193#[derive(Debug, Deserialize, PartialEq)]
194pub struct L3BidAskUpdate {
195    pub event: OrderbookEvent,
196    pub order_id: String,
197    pub limit_price: Decimal,
198    #[serde(rename = "order_qty")]
199    pub order_quantity: Decimal,
200    pub timestamp: String,
201}
202
203#[serde_as]
204#[skip_serializing_none]
205#[derive(Debug, Serialize, Clone)]
206pub struct OhlcSubscription {
207    pub channel: String,
208    pub symbol: Vec<String>,
209    pub interval: i32,
210    pub snapshot: Option<bool>,
211}
212
213impl OhlcSubscription {
214    pub fn new(symbols: Vec<String>, interval: i32) -> Self {
215        OhlcSubscription {
216            channel: "ohlc".to_string(),
217            symbol: symbols,
218            interval,
219            snapshot: None,
220        }
221    }
222}
223
224#[derive(Debug, Deserialize)]
225pub struct SubscriptionResponse {
226    pub channel: String,
227    pub symbol: Option<String>,
228    pub snapshot: Option<bool>,
229    pub warnings: Option<Vec<String>>,
230}
231
232#[derive(Debug, Deserialize, PartialEq)]
233pub struct TradeSubscriptionResponse {
234    pub symbol: Option<String>,
235    pub snapshot: Option<bool>,
236    pub warnings: Option<Vec<String>>,
237}
238
239#[derive(Debug, Deserialize, PartialEq)]
240pub struct OhlcSubscriptionResponse {
241    pub symbol: Option<String>,
242    pub snapshot: Option<bool>,
243    pub interval: i64,
244    pub warnings: Option<Vec<String>>,
245}
246
247#[derive(Debug, Deserialize, PartialEq)]
248pub struct BookSubscriptionResponse {
249    pub symbol: String,
250    pub depth: Option<i32>,
251    pub snapshot: Option<bool>,
252    pub warnings: Option<Vec<String>>,
253}
254
255#[derive(Debug, Deserialize, PartialEq)]
256pub struct TickerSubscriptionResponse {
257    pub symbol: String,
258    pub event_trigger: Option<EventTrigger>,
259    pub snapshot: Option<bool>,
260}
261
262#[derive(Debug, Deserialize, PartialEq)]
263pub struct Ohlc {
264    pub symbol: String,
265    pub open: Decimal,
266    pub high: Decimal,
267    pub low: Decimal,
268    pub close: Decimal,
269    pub vwap: Decimal,
270    pub trades: i64,
271    pub volume: Decimal,
272    pub interval_begin: String,
273    pub interval: i32,
274}
275
276#[serde_as]
277#[skip_serializing_none]
278#[derive(Debug, Serialize, Clone)]
279pub struct TradesSubscription {
280    pub channel: String,
281    pub symbol: Vec<String>,
282    pub snapshot: Option<bool>,
283}
284
285impl TradesSubscription {
286    pub fn new(symbols: Vec<String>) -> Self {
287        TradesSubscription {
288            channel: "trade".to_string(),
289            symbol: symbols,
290            snapshot: None,
291        }
292    }
293}
294
295#[derive(Debug, Deserialize, PartialEq)]
296pub struct Trade {
297    pub symbol: String,
298    pub side: BuySell,
299    #[serde(rename = "qty")]
300    pub quantity: Decimal,
301    pub price: Decimal,
302    #[serde(rename = "ord_type")]
303    pub order_type: MarketLimit,
304    pub trade_id: i64,
305    pub timestamp: String,
306}
307
308#[serde_as]
309#[skip_serializing_none]
310#[derive(Debug, Serialize, Clone)]
311pub struct InstrumentsSubscription {
312    pub channel: String,
313    pub snapshot: Option<bool>,
314}
315
316impl InstrumentsSubscription {
317    pub fn new(snapshot: bool) -> Self {
318        InstrumentsSubscription {
319            channel: "instrument".to_string(),
320            snapshot: Some(snapshot),
321        }
322    }
323}
324
325#[derive(Debug, Deserialize, PartialEq)]
326pub struct Asset {
327    pub id: String,
328    pub margin_rate: Option<Decimal>,
329    pub precision: i64,
330    pub precision_display: i64,
331    pub status: AssetStatus,
332    pub borrowable: bool,
333    pub collateral_value: Decimal,
334}
335
336#[derive(Debug, Deserialize, PartialEq)]
337pub struct Pair {
338    pub base: String,
339    pub quote: String,
340    pub cost_min: Decimal,
341    pub cost_precision: i64,
342    pub has_index: bool,
343    pub margin_initial: Option<Decimal>,
344    pub marginable: bool,
345    pub position_limit_long: Option<i64>,
346    pub position_limit_short: Option<i64>,
347    pub price_increment: Decimal,
348    pub price_precision: i64,
349    #[serde(rename = "qty_increment")]
350    pub quantity_increment: Decimal,
351    #[serde(rename = "qty_min")]
352    pub quantity_min: Decimal,
353    #[serde(rename = "qty_precision")]
354    pub quantity_precision: i64,
355    pub status: PairStatus,
356    pub symbol: String,
357}
358
359#[derive(Debug, Deserialize, PartialEq)]
360pub struct Instruments {
361    pub assets: Vec<Asset>,
362    pub pairs: Vec<Pair>,
363}
364
365#[cfg(test)]
366mod tests {
367    use super::*;
368
369    use rust_decimal_macros::dec;
370
371    #[test]
372    fn test_deserialize_asset() {
373        let raw = r#"{"id":"XLM","status":"enabled","precision":8,"precision_display":5,"borrowable":true,"collateral_value":0.00,"margin_rate":0.020000}"#;
374        let expected = Asset {
375            id: "XLM".to_string(),
376            margin_rate: Some(dec!(0.02)),
377            precision: 8,
378            precision_display: 5,
379            status: AssetStatus::Enabled,
380            borrowable: true,
381            collateral_value: dec!(0),
382        };
383
384        let deserialized = serde_json::from_str::<Asset>(raw).unwrap();
385
386        assert_eq!(expected, deserialized);
387    }
388
389    #[test]
390    fn test_deserialize_pair() {
391        let raw = r#"{"symbol":"ETH/BTC","base":"ETH","quote":"BTC","status":"online","qty_precision":8,"qty_increment":0.00000001,"price_precision":5,"cost_precision":10,"marginable":true,"has_index":true,"cost_min":0.00002,"margin_initial":0.20,"position_limit_long":1000,"position_limit_short":600,"tick_size":0.00001,"price_increment":0.00001,"qty_min":0.00200000}"#;
392        let expected = Pair {
393            base: "ETH".to_string(),
394            quote: "BTC".to_string(),
395            cost_min: dec!(0.00002),
396            cost_precision: 10,
397            has_index: true,
398            margin_initial: Some(dec!(0.2)),
399            marginable: true,
400            position_limit_long: Some(1000),
401            position_limit_short: Some(600),
402            price_increment: dec!(0.00001),
403            price_precision: 5,
404            quantity_increment: dec!(0.00000001),
405            quantity_min: dec!(0.002),
406            quantity_precision: 8,
407            status: PairStatus::Online,
408            symbol: "ETH/BTC".to_string(),
409        };
410
411        let deserialized = serde_json::from_str::<Pair>(raw).unwrap();
412
413        assert_eq!(expected, deserialized);
414    }
415}