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}
172
173#[derive(Debug, Deserialize, PartialEq)]
174pub struct L3OrderbookUpdate {
175    pub symbol: String,
176    pub bids: Vec<L3BidAskUpdate>,
177    pub asks: Vec<L3BidAskUpdate>,
178    pub checksum: u32,
179}
180
181#[derive(Debug, Deserialize, PartialEq)]
182#[serde(deny_unknown_fields)]
183pub struct L3BidAsk {
184    pub order_id: String,
185    pub limit_price: Decimal,
186    #[serde(rename = "order_qty")]
187    pub order_quantity: Decimal,
188    pub timestamp: String,
189}
190
191#[derive(Debug, Deserialize, PartialEq)]
192pub struct L3BidAskUpdate {
193    pub event: OrderbookEvent,
194    pub order_id: String,
195    pub limit_price: Decimal,
196    #[serde(rename = "order_qty")]
197    pub order_quantity: Decimal,
198    pub timestamp: String,
199}
200
201#[serde_as]
202#[skip_serializing_none]
203#[derive(Debug, Serialize, Clone)]
204pub struct OhlcSubscription {
205    pub channel: String,
206    pub symbol: Vec<String>,
207    pub interval: i32,
208    pub snapshot: Option<bool>,
209}
210
211impl OhlcSubscription {
212    pub fn new(symbols: Vec<String>, interval: i32) -> Self {
213        OhlcSubscription {
214            channel: "ohlc".to_string(),
215            symbol: symbols,
216            interval,
217            snapshot: None,
218        }
219    }
220}
221
222#[derive(Debug, Deserialize)]
223pub struct SubscriptionResponse {
224    pub channel: String,
225    pub symbol: Option<String>,
226    pub snapshot: Option<bool>,
227    pub warnings: Option<Vec<String>>,
228}
229
230#[derive(Debug, Deserialize, PartialEq)]
231pub struct TradeSubscriptionResponse {
232    pub symbol: Option<String>,
233    pub snapshot: Option<bool>,
234    pub warnings: Option<Vec<String>>,
235}
236
237#[derive(Debug, Deserialize, PartialEq)]
238pub struct OhlcSubscriptionResponse {
239    pub symbol: Option<String>,
240    pub snapshot: Option<bool>,
241    pub interval: i64,
242    pub warnings: Option<Vec<String>>,
243}
244
245#[derive(Debug, Deserialize, PartialEq)]
246pub struct BookSubscriptionResponse {
247    pub symbol: String,
248    pub depth: Option<i32>,
249    pub snapshot: Option<bool>,
250    pub warnings: Option<Vec<String>>,
251}
252
253#[derive(Debug, Deserialize, PartialEq)]
254pub struct TickerSubscriptionResponse {
255    pub symbol: String,
256    pub event_trigger: Option<EventTrigger>,
257    pub snapshot: Option<bool>,
258}
259
260#[derive(Debug, Deserialize, PartialEq)]
261pub struct Ohlc {
262    pub symbol: String,
263    pub open: Decimal,
264    pub high: Decimal,
265    pub low: Decimal,
266    pub close: Decimal,
267    pub vwap: Decimal,
268    pub trades: i64,
269    pub volume: Decimal,
270    pub interval_begin: String,
271    pub interval: i32,
272}
273
274#[serde_as]
275#[skip_serializing_none]
276#[derive(Debug, Serialize, Clone)]
277pub struct TradesSubscription {
278    pub channel: String,
279    pub symbol: Vec<String>,
280    pub snapshot: Option<bool>,
281}
282
283impl TradesSubscription {
284    pub fn new(symbols: Vec<String>) -> Self {
285        TradesSubscription {
286            channel: "trade".to_string(),
287            symbol: symbols,
288            snapshot: None,
289        }
290    }
291}
292
293#[derive(Debug, Deserialize, PartialEq)]
294pub struct Trade {
295    pub symbol: String,
296    pub side: BuySell,
297    #[serde(rename = "qty")]
298    pub quantity: Decimal,
299    pub price: Decimal,
300    #[serde(rename = "ord_type")]
301    pub order_type: MarketLimit,
302    pub trade_id: i64,
303    pub timestamp: String,
304}
305
306#[serde_as]
307#[skip_serializing_none]
308#[derive(Debug, Serialize, Clone)]
309pub struct InstrumentsSubscription {
310    pub channel: String,
311    pub snapshot: Option<bool>,
312}
313
314impl InstrumentsSubscription {
315    pub fn new(snapshot: bool) -> Self {
316        InstrumentsSubscription {
317            channel: "instrument".to_string(),
318            snapshot: Some(snapshot),
319        }
320    }
321}
322
323#[derive(Debug, Deserialize, PartialEq)]
324pub struct Asset {
325    pub id: String,
326    pub margin_rate: Option<Decimal>,
327    pub precision: i64,
328    pub precision_display: i64,
329    pub status: AssetStatus,
330    pub borrowable: bool,
331    pub collateral_value: Decimal,
332}
333
334#[derive(Debug, Deserialize, PartialEq)]
335pub struct Pair {
336    pub base: String,
337    pub quote: String,
338    pub cost_min: Decimal,
339    pub cost_precision: i64,
340    pub has_index: bool,
341    pub margin_initial: Option<Decimal>,
342    pub marginable: bool,
343    pub position_limit_long: Option<i64>,
344    pub position_limit_short: Option<i64>,
345    pub price_increment: Decimal,
346    pub price_precision: i64,
347    #[serde(rename = "qty_increment")]
348    pub quantity_increment: Decimal,
349    #[serde(rename = "qty_min")]
350    pub quantity_min: Decimal,
351    #[serde(rename = "qty_precision")]
352    pub quantity_precision: i64,
353    pub status: PairStatus,
354    pub symbol: String,
355}
356
357#[derive(Debug, Deserialize, PartialEq)]
358pub struct Instruments {
359    pub assets: Vec<Asset>,
360    pub pairs: Vec<Pair>,
361}
362
363#[cfg(test)]
364mod tests {
365    use super::*;
366
367    use rust_decimal_macros::dec;
368
369    #[test]
370    fn test_deserialize_asset() {
371        let raw = r#"{"id":"XLM","status":"enabled","precision":8,"precision_display":5,"borrowable":true,"collateral_value":0.00,"margin_rate":0.020000}"#;
372        let expected = Asset {
373            id: "XLM".to_string(),
374            margin_rate: Some(dec!(0.02)),
375            precision: 8,
376            precision_display: 5,
377            status: AssetStatus::Enabled,
378            borrowable: true,
379            collateral_value: dec!(0),
380        };
381
382        let deserialized = serde_json::from_str::<Asset>(raw).unwrap();
383
384        assert_eq!(expected, deserialized);
385    }
386
387    #[test]
388    fn test_deserialize_pair() {
389        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}"#;
390        let expected = Pair {
391            base: "ETH".to_string(),
392            quote: "BTC".to_string(),
393            cost_min: dec!(0.00002),
394            cost_precision: 10,
395            has_index: true,
396            margin_initial: Some(dec!(0.2)),
397            marginable: true,
398            position_limit_long: Some(1000),
399            position_limit_short: Some(600),
400            price_increment: dec!(0.00001),
401            price_precision: 5,
402            quantity_increment: dec!(0.00000001),
403            quantity_min: dec!(0.002),
404            quantity_precision: 8,
405            status: PairStatus::Online,
406            symbol: "ETH/BTC".to_string(),
407        };
408
409        let deserialized = serde_json::from_str::<Pair>(raw).unwrap();
410
411        assert_eq!(expected, deserialized);
412    }
413}