1use serde::Deserialize;
6use serde_json::Value;
7
8use super::ExtraFields;
9use crate::model::NumberString;
10
11#[derive(Debug, Clone, Default, Deserialize)]
15#[serde(rename_all = "camelCase")]
16#[non_exhaustive]
17pub struct OrderUpdate {
18 #[serde(default)]
20 pub inst_type: String,
21 #[serde(default)]
23 pub inst_id: String,
24 #[serde(default)]
29 pub tgt_ccy: String,
30 #[serde(default)]
32 pub ccy: String,
33 #[serde(default)]
35 pub ord_id: String,
36 #[serde(default)]
38 pub cl_ord_id: String,
39 #[serde(default)]
41 pub tag: String,
42 #[serde(default)]
44 pub px: NumberString,
45 #[serde(default)]
47 pub sz: NumberString,
48 #[serde(default)]
50 pub notional_usd: NumberString,
51 #[serde(default)]
56 pub ord_type: String,
57 #[serde(default)]
59 pub side: String,
60 #[serde(default)]
62 pub pos_side: String,
63 #[serde(default)]
65 pub td_mode: String,
66 #[serde(default)]
68 pub acc_fill_sz: NumberString,
69 #[serde(default)]
71 pub fill_px: NumberString,
72 #[serde(default)]
74 pub trade_id: String,
75 #[serde(default)]
77 pub fill_sz: NumberString,
78 #[serde(default)]
80 pub fill_time: NumberString,
81 #[serde(default)]
83 pub fill_pnl: NumberString,
84 #[serde(default)]
86 pub fill_fee: NumberString,
87 #[serde(default)]
89 pub fill_fee_ccy: String,
90 #[serde(default)]
92 pub fill_notional_usd: NumberString,
93 #[serde(default)]
95 pub fill_px_vol: NumberString,
96 #[serde(default)]
98 pub fill_px_usd: NumberString,
99 #[serde(default)]
101 pub fill_mark_vol: NumberString,
102 #[serde(default)]
104 pub fill_fwd_px: NumberString,
105 #[serde(default)]
107 pub fill_mark_px: NumberString,
108 #[serde(default)]
110 pub fill_idx_px: NumberString,
111 #[serde(default)]
113 pub exec_type: String,
114 #[serde(default)]
116 pub avg_px: NumberString,
117 #[serde(default)]
121 pub state: String,
122 #[serde(default)]
124 pub lever: NumberString,
125 #[serde(default)]
127 pub attach_algo_cl_ord_id: String,
128 #[serde(default)]
130 pub tp_trigger_px: NumberString,
131 #[serde(default)]
133 pub tp_trigger_px_type: String,
134 #[serde(default)]
136 pub tp_ord_px: NumberString,
137 #[serde(default)]
139 pub sl_trigger_px: NumberString,
140 #[serde(default)]
142 pub sl_trigger_px_type: String,
143 #[serde(default)]
145 pub sl_ord_px: NumberString,
146 #[serde(default)]
148 pub fee_ccy: String,
149 #[serde(default)]
151 pub fee: NumberString,
152 #[serde(default)]
154 pub rebate_ccy: String,
155 #[serde(default)]
157 pub rebate: NumberString,
158 #[serde(default)]
160 pub pnl: NumberString,
161 #[serde(default)]
163 pub source: String,
164 #[serde(default)]
169 pub category: String,
170 #[serde(default)]
172 pub reduce_only: String,
173 #[serde(default)]
175 pub is_tp_limit: String,
176 #[serde(default)]
178 pub cancel_source: String,
179 #[serde(default)]
181 pub algo_cl_ord_id: String,
182 #[serde(default)]
184 pub algo_id: String,
185 #[serde(default)]
187 pub amend_source: String,
188 #[serde(default)]
192 pub amend_result: String,
193 #[serde(default)]
195 pub req_id: String,
196 #[serde(default)]
198 pub code: String,
199 #[serde(default)]
201 pub msg: String,
202 #[serde(default)]
204 pub px_type: String,
205 #[serde(default)]
207 pub px_usd: NumberString,
208 #[serde(default)]
210 pub px_vol: NumberString,
211 #[serde(default)]
213 pub linked_algo_ord: Value,
214 #[serde(default)]
216 pub attach_algo_ords: Vec<Value>,
217 #[serde(default)]
219 pub stp_mode: String,
220 #[serde(default)]
222 pub trade_quote_ccy: String,
223 #[serde(default)]
225 pub last_px: NumberString,
226 #[serde(default)]
228 pub outcome: String,
229 #[serde(default)]
231 pub c_time: NumberString,
232 #[serde(default)]
234 pub u_time: NumberString,
235 #[serde(flatten, default)]
237 pub extra: ExtraFields,
238}
239
240#[derive(Debug, Clone, Default, Deserialize)]
244#[serde(rename_all = "camelCase")]
245#[non_exhaustive]
246pub struct FillUpdate {
247 #[serde(default)]
249 pub inst_id: String,
250 #[serde(default)]
252 pub fill_sz: NumberString,
253 #[serde(default)]
255 pub fill_px: NumberString,
256 #[serde(default)]
258 pub side: String,
259 #[serde(default)]
261 pub ts: NumberString,
262 #[serde(default)]
264 pub ord_id: String,
265 #[serde(default)]
267 pub cl_ord_id: String,
268 #[serde(default)]
270 pub trade_id: String,
271 #[serde(default)]
273 pub exec_type: String,
274 #[serde(default)]
276 pub count: NumberString,
277 #[serde(flatten, default)]
279 pub extra: ExtraFields,
280}
281
282#[derive(Debug, Clone, Default, Deserialize)]
286#[serde(rename_all = "camelCase")]
287#[non_exhaustive]
288pub struct PlaceOrderResult {
289 #[serde(default)]
291 pub ord_id: String,
292 #[serde(default)]
294 pub cl_ord_id: String,
295 #[serde(default)]
297 pub tag: String,
298 #[serde(default)]
300 pub ts: NumberString,
301 #[serde(default)]
303 pub s_code: String,
304 #[serde(default)]
306 pub s_msg: String,
307 #[serde(default)]
309 pub sub_code: String,
310 #[serde(flatten, default)]
312 pub extra: ExtraFields,
313}
314
315#[derive(Debug, Clone, Default, Deserialize)]
319#[serde(rename_all = "camelCase")]
320#[non_exhaustive]
321pub struct CancelOrderResult {
322 #[serde(default)]
324 pub ord_id: String,
325 #[serde(default)]
327 pub cl_ord_id: String,
328 #[serde(default)]
330 pub ts: NumberString,
331 #[serde(default)]
333 pub s_code: String,
334 #[serde(default)]
336 pub s_msg: String,
337 #[serde(flatten, default)]
339 pub extra: ExtraFields,
340}
341
342#[derive(Debug, Clone, Default, Deserialize)]
346#[serde(rename_all = "camelCase")]
347#[non_exhaustive]
348pub struct AmendOrderResult {
349 #[serde(default)]
351 pub ord_id: String,
352 #[serde(default)]
354 pub cl_ord_id: String,
355 #[serde(default)]
357 pub ts: NumberString,
358 #[serde(default)]
360 pub req_id: String,
361 #[serde(default)]
363 pub s_code: String,
364 #[serde(default)]
366 pub s_msg: String,
367 #[serde(default)]
369 pub sub_code: String,
370 #[serde(flatten, default)]
372 pub extra: ExtraFields,
373}
374
375#[derive(Debug, Clone, Default, Deserialize)]
379#[serde(rename_all = "camelCase")]
380#[non_exhaustive]
381pub struct MassCancelOperationResult {
382 #[serde(default)]
384 pub result: bool,
385 #[serde(flatten, default)]
387 pub extra: ExtraFields,
388}
389
390#[cfg(test)]
391mod tests {
392 use super::*;
393
394 #[test]
395 fn parses_order_update() {
396 let row: OrderUpdate = serde_json::from_str(
397 r#"{
398 "accFillSz":"0.001","algoClOrdId":"","algoId":"","amendResult":"","amendSource":"",
399 "avgPx":"31527.1","cancelSource":"","category":"normal","ccy":"","clOrdId":"",
400 "code":"0","cTime":"1654084334977","execType":"M","fee":"-0.02522168","feeCcy":"USDT",
401 "fillFee":"-0.02522168","fillFeeCcy":"USDT","fillNotionalUsd":"31.50818374",
402 "fillPx":"31527.1","fillSz":"0.001","fillPnl":"0.01","fillTime":"1654084353263",
403 "fillPxVol":"","fillPxUsd":"","fillMarkVol":"","fillFwdPx":"","fillMarkPx":"",
404 "fillIdxPx":"","instId":"BTC-USDT","instType":"SPOT","lever":"0","msg":"",
405 "notionalUsd":"31.50818374","ordId":"452197707845865472","ordType":"limit","pnl":"0",
406 "posSide":"","px":"31527.1","pxUsd":"","pxVol":"","pxType":"","quickMgnType":"",
407 "rebate":"0","rebateCcy":"BTC","reduceOnly":"false","reqId":"","side":"sell",
408 "attachAlgoClOrdId":"","slOrdPx":"","slTriggerPx":"","slTriggerPxType":"last",
409 "source":"","state":"filled","stpId":"","stpMode":"","sz":"0.001","tag":"",
410 "tdMode":"cash","tgtCcy":"","tpOrdPx":"","tpTriggerPx":"","tpTriggerPxType":"last",
411 "attachAlgoOrds":[],"tradeId":"242589207","tradeQuoteCcy":"USDT","lastPx":"38892.2",
412 "uTime":"1654084353264","isTpLimit":"false","linkedAlgoOrd":{"algoId":""}
413 }"#,
414 )
415 .unwrap();
416 assert_eq!(row.ord_id, "452197707845865472");
417 assert_eq!(row.state, "filled");
418 assert_eq!(row.fill_notional_usd.as_str(), "31.50818374");
419 assert_eq!(row.last_px.as_str(), "38892.2");
420 assert_eq!(row.is_tp_limit, "false");
421 assert!(row.extra.contains_key("stpId"));
423 assert!(row.extra.contains_key("quickMgnType"));
424 }
425
426 #[test]
427 fn parses_fill_update() {
428 let row: FillUpdate = serde_json::from_str(
429 r#"{
430 "instId":"BTC-USDT-SWAP","fillSz":"100","fillPx":"70000","side":"buy",
431 "ts":"1705449605015","ordId":"680800019749904384","clOrdId":"1234567890",
432 "tradeId":"12345","execType":"T","count":"10"
433 }"#,
434 )
435 .unwrap();
436 assert_eq!(row.inst_id, "BTC-USDT-SWAP");
437 assert_eq!(row.exec_type, "T");
438 assert_eq!(row.count.as_str(), "10");
439 }
440
441 #[test]
442 fn parses_place_order_result() {
443 let row: PlaceOrderResult = serde_json::from_str(
444 r#"{"clOrdId":"","ordId":"12345689","tag":"","ts":"1695190491421","sCode":"0","sMsg":"","subCode":""}"#,
445 )
446 .unwrap();
447 assert_eq!(row.ord_id, "12345689");
448 assert_eq!(row.s_code, "0");
449 assert_eq!(row.ts.as_str(), "1695190491421");
450 }
451
452 #[test]
453 fn parses_cancel_order_result() {
454 let row: CancelOrderResult = serde_json::from_str(
455 r#"{"clOrdId":"","ordId":"2510789768709120","ts":"1695190491421","sCode":"0","sMsg":""}"#,
456 )
457 .unwrap();
458 assert_eq!(row.ord_id, "2510789768709120");
459 assert_eq!(row.s_code, "0");
460 }
461
462 #[test]
463 fn parses_amend_order_result() {
464 let row: AmendOrderResult = serde_json::from_str(
465 r#"{"clOrdId":"","ordId":"2510789768709120","ts":"1695190491421","reqId":"b12344","sCode":"0","sMsg":"","subCode":""}"#,
466 )
467 .unwrap();
468 assert_eq!(row.ord_id, "2510789768709120");
469 assert_eq!(row.req_id, "b12344");
470 assert_eq!(row.s_code, "0");
471 }
472}