1use serde::Deserialize;
6
7use super::ExtraFields;
8use crate::model::NumberString;
9
10#[derive(Debug, Clone, Default, Deserialize)]
14#[serde(rename_all = "camelCase")]
15#[non_exhaustive]
16pub struct SpreadOrderUpdate {
17 #[serde(default)]
19 pub sprd_id: String,
20 #[serde(default)]
22 pub ord_id: String,
23 #[serde(default)]
25 pub cl_ord_id: String,
26 #[serde(default)]
28 pub tag: String,
29 #[serde(default)]
31 pub px: NumberString,
32 #[serde(default)]
34 pub sz: NumberString,
35 #[serde(default)]
37 pub ord_type: String,
38 #[serde(default)]
40 pub side: String,
41 #[serde(default)]
43 pub fill_sz: NumberString,
44 #[serde(default)]
46 pub fill_px: NumberString,
47 #[serde(default)]
49 pub trade_id: String,
50 #[serde(default)]
52 pub acc_fill_sz: NumberString,
53 #[serde(default)]
55 pub pending_fill_sz: NumberString,
56 #[serde(default)]
58 pub pending_settle_sz: NumberString,
59 #[serde(default)]
61 pub canceled_sz: NumberString,
62 #[serde(default)]
64 pub avg_px: NumberString,
65 #[serde(default)]
69 pub state: String,
70 #[serde(default)]
72 pub cancel_source: String,
73 #[serde(default)]
75 pub req_id: String,
76 #[serde(default)]
78 pub amend_result: String,
79 #[serde(default)]
81 pub code: String,
82 #[serde(default)]
84 pub msg: String,
85 #[serde(default)]
87 pub c_time: NumberString,
88 #[serde(default)]
90 pub u_time: NumberString,
91 #[serde(flatten, default)]
93 pub extra: ExtraFields,
94}
95
96#[derive(Debug, Clone, Default, Deserialize)]
98#[serde(rename_all = "camelCase")]
99#[non_exhaustive]
100pub struct SpreadTradeLeg {
101 #[serde(default)]
103 pub inst_id: String,
104 #[serde(default)]
106 pub px: NumberString,
107 #[serde(default)]
109 pub sz: NumberString,
110 #[serde(default)]
112 pub side: String,
113 #[serde(default)]
115 pub fill_pnl: NumberString,
116 #[serde(default)]
118 pub fee: NumberString,
119 #[serde(default)]
121 pub sz_cont: NumberString,
122 #[serde(default)]
124 pub fee_ccy: String,
125 #[serde(default)]
127 pub trade_id: String,
128 #[serde(flatten, default)]
130 pub extra: ExtraFields,
131}
132
133#[derive(Debug, Clone, Default, Deserialize)]
137#[serde(rename_all = "camelCase")]
138#[non_exhaustive]
139pub struct SpreadTradeUpdate {
140 #[serde(default)]
142 pub sprd_id: String,
143 #[serde(default)]
145 pub trade_id: String,
146 #[serde(default)]
148 pub ord_id: String,
149 #[serde(default)]
151 pub cl_ord_id: String,
152 #[serde(default)]
154 pub tag: String,
155 #[serde(default)]
157 pub fill_px: NumberString,
158 #[serde(default)]
160 pub fill_sz: NumberString,
161 #[serde(default)]
163 pub side: String,
164 #[serde(default)]
166 pub state: String,
167 #[serde(default)]
169 pub exec_type: String,
170 #[serde(default)]
172 pub ts: NumberString,
173 #[serde(default)]
175 pub legs: Vec<SpreadTradeLeg>,
176 #[serde(default)]
178 pub code: String,
179 #[serde(default)]
181 pub msg: String,
182 #[serde(flatten, default)]
184 pub extra: ExtraFields,
185}
186
187#[derive(Debug, Clone, Default, Deserialize)]
191#[serde(rename_all = "camelCase")]
192#[non_exhaustive]
193pub struct SpreadPublicTradeUpdate {
194 #[serde(default)]
196 pub sprd_id: String,
197 #[serde(default)]
199 pub trade_id: String,
200 #[serde(default)]
202 pub px: NumberString,
203 #[serde(default)]
205 pub sz: NumberString,
206 #[serde(default)]
208 pub side: String,
209 #[serde(default)]
211 pub ts: NumberString,
212 #[serde(flatten, default)]
214 pub extra: ExtraFields,
215}
216
217#[derive(Debug, Clone, Default, Deserialize)]
221#[serde(rename_all = "camelCase")]
222#[non_exhaustive]
223pub struct SpreadTickerUpdate {
224 #[serde(default)]
226 pub sprd_id: String,
227 #[serde(default)]
229 pub last: NumberString,
230 #[serde(default)]
232 pub last_sz: NumberString,
233 #[serde(default)]
235 pub ask_px: NumberString,
236 #[serde(default)]
238 pub ask_sz: NumberString,
239 #[serde(default)]
241 pub bid_px: NumberString,
242 #[serde(default)]
244 pub bid_sz: NumberString,
245 #[serde(default)]
247 pub open24h: NumberString,
248 #[serde(default)]
250 pub high24h: NumberString,
251 #[serde(default)]
253 pub low24h: NumberString,
254 #[serde(default)]
256 pub vol24h: NumberString,
257 #[serde(default)]
259 pub ts: NumberString,
260 #[serde(flatten, default)]
262 pub extra: ExtraFields,
263}
264
265#[derive(Debug, Clone, Default, Deserialize)]
269#[serde(rename_all = "camelCase")]
270#[non_exhaustive]
271pub struct SpreadPlaceOrderResult {
272 #[serde(default)]
274 pub cl_ord_id: String,
275 #[serde(default)]
277 pub ord_id: String,
278 #[serde(default)]
280 pub tag: String,
281 #[serde(default)]
283 pub s_code: String,
284 #[serde(default)]
286 pub s_msg: String,
287 #[serde(flatten, default)]
289 pub extra: ExtraFields,
290}
291
292#[derive(Debug, Clone, Default, Deserialize)]
296#[serde(rename_all = "camelCase")]
297#[non_exhaustive]
298pub struct SpreadAmendOrderResult {
299 #[serde(default)]
301 pub cl_ord_id: String,
302 #[serde(default)]
304 pub ord_id: String,
305 #[serde(default)]
307 pub req_id: String,
308 #[serde(default)]
310 pub s_code: String,
311 #[serde(default)]
313 pub s_msg: String,
314 #[serde(flatten, default)]
316 pub extra: ExtraFields,
317}
318
319#[derive(Debug, Clone, Default, Deserialize)]
323#[serde(rename_all = "camelCase")]
324#[non_exhaustive]
325pub struct SpreadCancelOrderResult {
326 #[serde(default)]
328 pub cl_ord_id: String,
329 #[serde(default)]
331 pub ord_id: String,
332 #[serde(default)]
334 pub s_code: String,
335 #[serde(default)]
337 pub s_msg: String,
338 #[serde(flatten, default)]
340 pub extra: ExtraFields,
341}
342
343#[derive(Debug, Clone, Default, Deserialize)]
347#[serde(rename_all = "camelCase")]
348#[non_exhaustive]
349pub struct SpreadMassCancelResult {
350 #[serde(default)]
352 pub result: bool,
353 #[serde(flatten, default)]
355 pub extra: ExtraFields,
356}
357
358#[cfg(test)]
359mod tests {
360 use super::*;
361
362 #[test]
363 fn parses_operation_result() {
364 let row: SpreadAmendOrderResult = serde_json::from_str(
365 r#"{"ordId":"1","clOrdId":"c","reqId":"r","sCode":"0","sMsg":""}"#,
366 )
367 .unwrap();
368 assert_eq!(row.s_code, "0");
369 }
370
371 #[test]
372 fn parses_spread_order_update() {
373 let row: SpreadOrderUpdate = serde_json::from_str(
374 r#"{
375 "sprdId":"BTC-USDT_BTC-USDT-SWAP","ordId":"312269865356374016","clOrdId":"b1",
376 "tag":"","px":"999","sz":"3","ordType":"limit","side":"buy","fillSz":"0",
377 "fillPx":"","tradeId":"","accFillSz":"0","pendingFillSz":"2","pendingSettleSz":"1",
378 "canceledSz":"1","state":"live","avgPx":"0","cancelSource":"","uTime":"1597026383085",
379 "cTime":"1597026383085","code":"0","msg":"","reqId":"","amendResult":"",
380 "instId":"ignored","pTime":"ignored"
381 }"#,
382 )
383 .unwrap();
384 assert_eq!(row.sprd_id, "BTC-USDT_BTC-USDT-SWAP");
385 assert_eq!(row.amend_result, "");
386 assert!(row.extra.contains_key("instId"));
387 assert!(row.extra.contains_key("pTime"));
388 }
389
390 #[test]
391 fn parses_spread_trade_update() {
392 let row: SpreadTradeUpdate = serde_json::from_str(
393 r#"{
394 "sprdId":"BTC-USDT-SWAP_BTC-USDT-200329","tradeId":"123","ordId":"123445",
395 "clOrdId":"b16","tag":"","fillPx":"999","fillSz":"3","state":"filled","side":"buy",
396 "execType":"M","ts":"1597026383085","legs":[],"code":"","msg":""
397 }"#,
398 )
399 .unwrap();
400 assert_eq!(row.sprd_id, "BTC-USDT-SWAP_BTC-USDT-200329");
401 assert_eq!(row.exec_type, "M");
402 assert_eq!(row.code, "");
403 assert_eq!(row.msg, "");
404 }
405
406 #[test]
407 fn parses_spread_public_trade_update() {
408 let row: SpreadPublicTradeUpdate = serde_json::from_str(
409 r#"{"sprdId":"BTC-USDT_BTC-USDT-SWAP","tradeId":"2499206329160695808","px":"-10","sz":"0.001","side":"sell","ts":"1726801105519"}"#,
410 ).unwrap();
411 assert_eq!(row.sprd_id, "BTC-USDT_BTC-USDT-SWAP");
412 assert_eq!(row.px.as_str(), "-10");
413 assert_eq!(row.sz.as_str(), "0.001");
414 }
415
416 #[test]
417 fn parses_spread_ticker_update() {
418 let row: SpreadTickerUpdate = serde_json::from_str(
419 r#"{
420 "sprdId":"BTC-USDT_BTC-USDT-SWAP","last":"4","lastSz":"0.01","askPx":"19.7",
421 "askSz":"5.79","bidPx":"5.9","bidSz":"5.79","open24h":"-7","high24h":"19.6",
422 "low24h":"-7","vol24h":"9.87","ts":"1715247061026"
423 }"#,
424 )
425 .unwrap();
426 assert_eq!(row.sprd_id, "BTC-USDT_BTC-USDT-SWAP");
427 assert_eq!(row.last.as_str(), "4");
428 assert_eq!(row.ts.as_str(), "1715247061026");
429 }
430}