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 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, }
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, }
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}