Skip to main content

ccxt_exchanges/binance/parser/
trade.rs

1#![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
15/// Parse trade data from Binance trade response.
16pub 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
76/// Parse WebSocket trade data from Binance streams.
77pub 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
143/// Parse aggregated trade from Binance API response.
144pub 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
190/// Parse order trade history from Binance myTrades endpoint.
191pub 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}