Skip to main content

bulk_client/msgs/
responses.rs

1use serde::{Deserialize};
2use serde_json::Value;
3
4// ─────────────────────────────────────────────────────────────────────────────
5// Order responses (mirrors trade.py OrderResponse)
6// ─────────────────────────────────────────────────────────────────────────────
7
8#[derive(Debug, Clone, Deserialize)]
9#[allow(unused)]
10pub struct Response {
11    pub order_id: Option<String>,
12    pub status: String,
13    pub message: Option<String>,
14    #[serde(skip)]
15    pub raw: Value,
16}
17
18#[allow(unused)]
19impl Response {
20    /// Indicate whether response is an error
21    pub fn is_error(&self) -> bool {
22        matches!(
23            self.status.as_str(),
24            "error"
25                | "rejectedRiskLimit"
26                | "rejectedInvalid"
27                | "rejectedDuplicate"
28                | "rejectedCrossing"
29        )
30    }
31
32    /// Indicate whether response is a placement
33    pub fn is_placement(&self) -> bool {
34        matches!(
35            self.status.as_str(),
36            "resting" | "working" | "filled"
37        )
38    }
39
40    /// Parse the list of statuses from a post response (same logic as Python).
41    pub(crate) fn parse_responses(data: &Value) -> Vec<Self> {
42        // WS: data.data.payload.response.data.statuses
43        // HTTP: data.response.data.statuses
44        let statuses = data["data"]["payload"]["response"]["data"]["statuses"]
45            .as_array()
46            .or_else(|| data["response"]["data"]["statuses"].as_array());
47
48        let Some(arr) = statuses else {
49            return vec![];
50        };
51
52        arr.iter()
53            .map(|entry| {
54                if let Some(body) = entry.get("error") {
55                    Response {
56                        order_id: None,
57                        status: "error".into(),
58                        message: body["message"].as_str().map(Into::into),
59                        raw: body.clone(),
60                    }
61                } else {
62                    // First key is the status string
63                    let status_key = entry
64                        .as_object()
65                        .and_then(|m| m.keys().next())
66                        .unwrap_or(&String::new())
67                        .clone();
68                    let body = &entry[&status_key];
69                    if let Some(oid) = body.get("oid") {
70                        Response {
71                            order_id: body["oid"].as_str().map(Into::into),
72                            status: status_key,
73                            message: None,
74                            raw: body.clone(),
75                        }
76                    } else {
77                        Response {
78                            order_id: None,
79                            status: status_key,
80                            message: None,
81                            raw: body.clone(),
82                        }
83                    }
84                }
85            })
86            .collect()
87    }
88}