ccxt_exchanges/binance/parser/
trade.rs1#![allow(dead_code)]
2
3use super::{parse_decimal_multi, parse_f64, value_to_hashmap};
4use ccxt_core::{
5 Result,
6 error::{Error, ParseError},
7 types::{
8 Market, OrderSide, TakerOrMaker, Trade,
9 financial::{Amount, Cost, Price},
10 },
11};
12use rust_decimal::Decimal;
13use serde_json::Value;
14
15pub fn parse_trade(data: &Value, market: Option<&Market>) -> Result<Trade> {
17 let symbol = if let Some(m) = market {
18 m.symbol.clone()
19 } else {
20 data["symbol"]
21 .as_str()
22 .ok_or_else(|| Error::from(ParseError::missing_field("symbol")))?
23 .to_string()
24 };
25
26 let id = data["id"]
27 .as_u64()
28 .or_else(|| data["a"].as_u64())
29 .map(|v| v.to_string());
30
31 let timestamp = data["time"].as_i64().or_else(|| data["T"].as_i64());
32
33 let side = if data["isBuyerMaker"].as_bool().unwrap_or(false)
34 || data["m"].as_bool().unwrap_or(false)
35 {
36 OrderSide::Sell
37 } else {
38 OrderSide::Buy
39 };
40
41 let price = parse_decimal_multi(data, &["price", "p"])
42 .ok_or_else(|| Error::from(ParseError::missing_field("price")))?;
43 let amount = parse_decimal_multi(data, &["qty", "q"])
44 .ok_or_else(|| Error::from(ParseError::missing_field("amount")))?;
45
46 let cost = Some(price * amount);
47
48 Ok(Trade {
49 id,
50 order: data["orderId"]
51 .as_u64()
52 .or_else(|| data["orderid"].as_u64())
53 .map(|v| v.to_string()),
54 timestamp: timestamp.unwrap_or(0),
55 datetime: timestamp.map(|t| {
56 chrono::DateTime::from_timestamp(t / 1000, 0)
57 .map(|dt| dt.to_rfc3339())
58 .unwrap_or_default()
59 }),
60 symbol,
61 trade_type: None,
62 side,
63 taker_or_maker: if data["isBuyerMaker"].as_bool().unwrap_or(false) {
64 Some(TakerOrMaker::Maker)
65 } else {
66 Some(TakerOrMaker::Taker)
67 },
68 price: Price::new(price),
69 amount: Amount::new(amount),
70 cost: cost.map(Cost::new),
71 fee: None,
72 info: value_to_hashmap(data),
73 })
74}
75
76pub fn parse_ws_trade(data: &Value, market: Option<&Market>) -> Result<Trade> {
78 let market_id = data["s"]
79 .as_str()
80 .or_else(|| data["symbol"].as_str())
81 .ok_or_else(|| Error::from(ParseError::missing_field("symbol")))?;
82
83 let symbol = if let Some(m) = market {
84 m.symbol.clone()
85 } else {
86 market_id.to_string()
87 };
88
89 let id = data["t"]
90 .as_u64()
91 .or_else(|| data["a"].as_u64())
92 .map(|v| v.to_string());
93
94 let timestamp = data["T"].as_i64().unwrap_or(0);
95
96 let price = parse_f64(data, "L")
97 .or_else(|| parse_f64(data, "p"))
98 .and_then(Decimal::from_f64_retain);
99
100 let amount = parse_f64(data, "q").and_then(Decimal::from_f64_retain);
101
102 let cost = match (price, amount) {
103 (Some(p), Some(a)) => Some(p * a),
104 _ => None,
105 };
106
107 let side = if data["m"].as_bool().unwrap_or(false) {
108 OrderSide::Sell
109 } else {
110 OrderSide::Buy
111 };
112
113 let taker_or_maker = if data["m"].as_bool().unwrap_or(false) {
114 Some(TakerOrMaker::Maker)
115 } else {
116 Some(TakerOrMaker::Taker)
117 };
118
119 Ok(Trade {
120 id,
121 order: data["orderId"]
122 .as_u64()
123 .or_else(|| data["orderid"].as_u64())
124 .map(|v| v.to_string()),
125 timestamp,
126 datetime: Some(
127 chrono::DateTime::from_timestamp_millis(timestamp)
128 .map(|dt| dt.to_rfc3339())
129 .unwrap_or_default(),
130 ),
131 symbol,
132 trade_type: None,
133 side,
134 taker_or_maker,
135 price: Price::from(price.unwrap_or(Decimal::ZERO)),
136 amount: Amount::from(amount.unwrap_or(Decimal::ZERO)),
137 cost: cost.map(Cost::from),
138 fee: None,
139 info: value_to_hashmap(data),
140 })
141}
142
143pub fn parse_agg_trade(data: &Value, symbol: Option<String>) -> Result<ccxt_core::types::AggTrade> {
145 use ccxt_core::types::AggTrade;
146 use rust_decimal::prelude::FromStr;
147
148 let agg_id = data["a"]
149 .as_i64()
150 .ok_or_else(|| Error::from(ParseError::missing_field("a")))?;
151
152 let price = data["p"]
153 .as_str()
154 .and_then(|s| Decimal::from_str(s).ok())
155 .ok_or_else(|| Error::from(ParseError::missing_field("p")))?;
156
157 let quantity = data["q"]
158 .as_str()
159 .and_then(|s| Decimal::from_str(s).ok())
160 .ok_or_else(|| Error::from(ParseError::missing_field("q")))?;
161
162 let first_trade_id = data["f"]
163 .as_i64()
164 .ok_or_else(|| Error::from(ParseError::missing_field("f")))?;
165
166 let last_trade_id = data["l"]
167 .as_i64()
168 .ok_or_else(|| Error::from(ParseError::missing_field("l")))?;
169
170 let timestamp = data["T"]
171 .as_i64()
172 .ok_or_else(|| Error::from(ParseError::missing_field("T")))?;
173
174 let is_buyer_maker = data["m"].as_bool().unwrap_or(false);
175 let is_best_match = data["M"].as_bool();
176
177 Ok(AggTrade {
178 agg_id,
179 price,
180 quantity,
181 first_trade_id,
182 last_trade_id,
183 timestamp,
184 is_buyer_maker,
185 is_best_match,
186 symbol,
187 })
188}
189
190pub fn parse_order_trades(
192 data: &Value,
193 market: Option<&Market>,
194) -> Result<Vec<ccxt_core::types::Trade>> {
195 if let Some(array) = data.as_array() {
196 array.iter().map(|item| parse_trade(item, market)).collect()
197 } else {
198 Ok(vec![parse_trade(data, market)?])
199 }
200}