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