ccxt_exchanges/binance/parser/
order.rs1#![allow(dead_code)]
2
3use super::{parse_decimal, value_to_hashmap};
4use ccxt_core::{
5 Result,
6 error::{Error, ParseError},
7 types::{
8 Market, OcoOrder, OcoOrderInfo, Order, OrderReport, OrderSide, OrderStatus, OrderType,
9 TimeInForce,
10 },
11};
12use serde_json::Value;
13
14pub fn parse_order(data: &Value, market: Option<&Market>) -> Result<Order> {
16 let symbol = if let Some(m) = market {
17 m.symbol.clone()
18 } else {
19 data["symbol"]
20 .as_str()
21 .ok_or_else(|| Error::from(ParseError::missing_field("symbol")))?
22 .to_string()
23 };
24
25 let id = data["orderId"]
26 .as_u64()
27 .ok_or_else(|| Error::from(ParseError::missing_field("orderId")))?
28 .to_string();
29
30 let timestamp = data["time"]
31 .as_i64()
32 .or_else(|| data["transactTime"].as_i64());
33
34 let status_str = data["status"]
35 .as_str()
36 .ok_or_else(|| Error::from(ParseError::missing_field("status")))?;
37
38 let status = match status_str {
39 "FILLED" => OrderStatus::Closed,
40 "CANCELED" => OrderStatus::Cancelled,
41 "EXPIRED" => OrderStatus::Expired,
42 "REJECTED" => OrderStatus::Rejected,
43 _ => OrderStatus::Open,
44 };
45
46 let side = match data["side"].as_str() {
47 Some("BUY") => OrderSide::Buy,
48 Some("SELL") => OrderSide::Sell,
49 _ => return Err(Error::from(ParseError::invalid_format("data", "side"))),
50 };
51
52 let order_type = match data["type"].as_str() {
53 Some("MARKET") => OrderType::Market,
54 Some("STOP_LOSS") => OrderType::StopLoss,
55 Some("STOP_LOSS_LIMIT") => OrderType::StopLossLimit,
56 Some("TAKE_PROFIT") => OrderType::TakeProfit,
57 Some("TAKE_PROFIT_LIMIT" | "TAKE_PROFIT_MARKET") => OrderType::TakeProfitLimit,
58 Some("STOP_MARKET" | "STOP") => OrderType::StopMarket,
59 Some("TRAILING_STOP_MARKET") => OrderType::TrailingStop,
60 Some("LIMIT_MAKER") => OrderType::LimitMaker,
61 _ => OrderType::Limit,
62 };
63
64 let time_in_force = match data["timeInForce"].as_str() {
65 Some("GTC") => Some(TimeInForce::GTC),
66 Some("IOC") => Some(TimeInForce::IOC),
67 Some("FOK") => Some(TimeInForce::FOK),
68 Some("GTX") => Some(TimeInForce::PO),
69 _ => None,
70 };
71
72 let price = parse_decimal(data, "price");
73 let amount = parse_decimal(data, "origQty");
74 let filled = parse_decimal(data, "executedQty");
75 let remaining = match (&amount, &filled) {
76 (Some(a), Some(f)) => Some(*a - *f),
77 _ => None,
78 };
79
80 let cost = parse_decimal(data, "cummulativeQuoteQty");
81
82 let average = match (&cost, &filled) {
83 (Some(c), Some(f)) if !f.is_zero() => Some(*c / *f),
84 _ => None,
85 };
86
87 Ok(Order {
88 id,
89 client_order_id: data["clientOrderId"].as_str().map(ToString::to_string),
90 timestamp,
91 datetime: timestamp.map(|t| {
92 chrono::DateTime::from_timestamp(t / 1000, 0)
93 .map(|dt| dt.to_rfc3339())
94 .unwrap_or_default()
95 }),
96 last_trade_timestamp: data["updateTime"].as_i64(),
97 status,
98 symbol,
99 order_type,
100 time_in_force: time_in_force.map(|t| t.to_string()),
101 side,
102 price,
103 average,
104 amount: amount.ok_or_else(|| Error::from(ParseError::missing_field("amount")))?,
105 filled,
106 remaining,
107 cost,
108 trades: None,
109 fee: None,
110 post_only: None,
111 reduce_only: data["reduceOnly"].as_bool(),
112 trigger_price: parse_decimal(data, "triggerPrice"),
113 stop_price: parse_decimal(data, "stopPrice"),
114 take_profit_price: parse_decimal(data, "takeProfitPrice"),
115 stop_loss_price: parse_decimal(data, "stopLossPrice"),
116 trailing_delta: parse_decimal(data, "trailingDelta"),
117 trailing_percent: super::parse_decimal_multi(data, &["trailingPercent", "callbackRate"]),
118 activation_price: super::parse_decimal_multi(data, &["activationPrice", "activatePrice"]),
119 callback_rate: parse_decimal(data, "callbackRate"),
120 working_type: data["workingType"].as_str().map(ToString::to_string),
121 fees: Some(Vec::new()),
122 info: value_to_hashmap(data),
123 })
124}
125
126pub fn parse_oco_order(data: &Value) -> Result<OcoOrder> {
128 let order_list_id = data["orderListId"]
129 .as_i64()
130 .ok_or_else(|| Error::from(ParseError::missing_field("orderListId")))?;
131
132 let list_client_order_id = data["listClientOrderId"].as_str().map(ToString::to_string);
133
134 let symbol = data["symbol"]
135 .as_str()
136 .ok_or_else(|| Error::from(ParseError::missing_field("symbol")))?
137 .to_string();
138
139 let list_status = data["listStatusType"]
140 .as_str()
141 .ok_or_else(|| Error::from(ParseError::missing_field("listStatusType")))?
142 .to_string();
143
144 let list_order_status = data["listOrderStatus"]
145 .as_str()
146 .ok_or_else(|| Error::from(ParseError::missing_field("listOrderStatus")))?
147 .to_string();
148
149 let transaction_time = data["transactionTime"]
150 .as_i64()
151 .ok_or_else(|| Error::from(ParseError::missing_field("transactionTime")))?;
152
153 let datetime = chrono::DateTime::from_timestamp(transaction_time / 1000, 0)
154 .map(|dt| dt.to_rfc3339())
155 .unwrap_or_default();
156
157 let mut orders = Vec::new();
158 if let Some(orders_array) = data["orders"].as_array() {
159 for order in orders_array {
160 let order_info = OcoOrderInfo {
161 symbol: order["symbol"].as_str().unwrap_or(&symbol).to_string(),
162 order_id: order["orderId"]
163 .as_i64()
164 .ok_or_else(|| Error::from(ParseError::missing_field("orderId")))?,
165 client_order_id: order["clientOrderId"].as_str().map(ToString::to_string),
166 };
167 orders.push(order_info);
168 }
169 }
170
171 let order_reports = if let Some(reports_array) = data["orderReports"].as_array() {
172 let mut reports = Vec::new();
173 for report in reports_array {
174 let order_report = OrderReport {
175 symbol: report["symbol"].as_str().unwrap_or(&symbol).to_string(),
176 order_id: report["orderId"]
177 .as_i64()
178 .ok_or_else(|| Error::from(ParseError::missing_field("orderId")))?,
179 order_list_id: report["orderListId"].as_i64().unwrap_or(order_list_id),
180 client_order_id: report["clientOrderId"].as_str().map(ToString::to_string),
181 transact_time: report["transactTime"].as_i64().unwrap_or(transaction_time),
182 price: report["price"].as_str().unwrap_or("0").to_string(),
183 orig_qty: report["origQty"].as_str().unwrap_or("0").to_string(),
184 executed_qty: report["executedQty"].as_str().unwrap_or("0").to_string(),
185 cummulative_quote_qty: report["cummulativeQuoteQty"]
186 .as_str()
187 .unwrap_or("0")
188 .to_string(),
189 status: report["status"].as_str().unwrap_or("NEW").to_string(),
190 time_in_force: report["timeInForce"].as_str().unwrap_or("GTC").to_string(),
191 type_: report["type"].as_str().unwrap_or("LIMIT").to_string(),
192 side: report["side"].as_str().unwrap_or("SELL").to_string(),
193 stop_price: report["stopPrice"].as_str().map(ToString::to_string),
194 };
195 reports.push(order_report);
196 }
197 Some(reports)
198 } else {
199 None
200 };
201
202 Ok(OcoOrder {
203 info: Some(data.clone()),
204 order_list_id,
205 list_client_order_id,
206 symbol,
207 list_status,
208 list_order_status,
209 transaction_time,
210 datetime,
211 orders,
212 order_reports,
213 })
214}
215
216pub fn parse_edit_order_result(data: &Value, market: Option<&Market>) -> Result<Order> {
218 let new_order_data = data.get("newOrderResponse").ok_or_else(|| {
219 Error::from(ParseError::invalid_format(
220 "data",
221 "Missing newOrderResponse field",
222 ))
223 })?;
224
225 parse_order(new_order_data, market)
226}