#![allow(dead_code)]
use super::{parse_decimal_multi, parse_f64, value_to_hashmap};
use ccxt_core::{
Result,
error::{Error, ParseError},
types::{
Market, OrderSide, TakerOrMaker, Trade,
financial::{Amount, Cost, Price},
},
};
use rust_decimal::Decimal;
use serde_json::Value;
pub fn parse_trade(data: &Value, market: Option<&Market>) -> Result<Trade> {
let symbol = if let Some(m) = market {
m.symbol.clone()
} else {
data["symbol"]
.as_str()
.ok_or_else(|| Error::from(ParseError::missing_field("symbol")))?
.to_string()
};
let id = data["id"]
.as_u64()
.or_else(|| data["a"].as_u64())
.map(|v| v.to_string());
let timestamp = data["time"].as_i64().or_else(|| data["T"].as_i64());
let side = if data["isBuyerMaker"].as_bool().unwrap_or(false)
|| data["m"].as_bool().unwrap_or(false)
{
OrderSide::Sell
} else {
OrderSide::Buy
};
let price = parse_decimal_multi(data, &["price", "p"])
.ok_or_else(|| Error::from(ParseError::missing_field("price")))?;
let amount = parse_decimal_multi(data, &["qty", "q"])
.ok_or_else(|| Error::from(ParseError::missing_field("amount")))?;
let cost = Some(price * amount);
Ok(Trade {
id,
order: data["orderId"]
.as_u64()
.or_else(|| data["orderid"].as_u64())
.map(|v| v.to_string()),
timestamp: timestamp.unwrap_or(0),
datetime: timestamp.map(|t| {
chrono::DateTime::from_timestamp(t / 1000, 0)
.map(|dt| dt.to_rfc3339())
.unwrap_or_default()
}),
symbol,
trade_type: None,
side,
taker_or_maker: if data["isBuyerMaker"].as_bool().unwrap_or(false) {
Some(TakerOrMaker::Maker)
} else {
Some(TakerOrMaker::Taker)
},
price: Price::new(price),
amount: Amount::new(amount),
cost: cost.map(Cost::new),
fee: None,
info: value_to_hashmap(data),
})
}
pub fn parse_ws_trade(data: &Value, market: Option<&Market>) -> Result<Trade> {
let market_id = data["s"]
.as_str()
.or_else(|| data["symbol"].as_str())
.ok_or_else(|| Error::from(ParseError::missing_field("symbol")))?;
let symbol = if let Some(m) = market {
m.symbol.clone()
} else {
market_id.to_string()
};
let id = data["t"]
.as_u64()
.or_else(|| data["a"].as_u64())
.map(|v| v.to_string());
let timestamp = data["T"].as_i64().unwrap_or(0);
let price = parse_f64(data, "L")
.or_else(|| parse_f64(data, "p"))
.and_then(Decimal::from_f64_retain);
let amount = parse_f64(data, "q").and_then(Decimal::from_f64_retain);
let cost = match (price, amount) {
(Some(p), Some(a)) => Some(p * a),
_ => None,
};
let side = if data["m"].as_bool().unwrap_or(false) {
OrderSide::Sell
} else {
OrderSide::Buy
};
let taker_or_maker = if data["m"].as_bool().unwrap_or(false) {
Some(TakerOrMaker::Maker)
} else {
Some(TakerOrMaker::Taker)
};
Ok(Trade {
id,
order: data["orderId"]
.as_u64()
.or_else(|| data["orderid"].as_u64())
.map(|v| v.to_string()),
timestamp,
datetime: Some(
chrono::DateTime::from_timestamp_millis(timestamp)
.map(|dt| dt.to_rfc3339())
.unwrap_or_default(),
),
symbol,
trade_type: None,
side,
taker_or_maker,
price: Price::from(price.unwrap_or(Decimal::ZERO)),
amount: Amount::from(amount.unwrap_or(Decimal::ZERO)),
cost: cost.map(Cost::from),
fee: None,
info: value_to_hashmap(data),
})
}
pub fn parse_agg_trade(data: &Value, symbol: Option<String>) -> Result<ccxt_core::types::AggTrade> {
use ccxt_core::types::AggTrade;
use rust_decimal::prelude::FromStr;
let agg_id = data["a"]
.as_i64()
.ok_or_else(|| Error::from(ParseError::missing_field("a")))?;
let price = data["p"]
.as_str()
.and_then(|s| Decimal::from_str(s).ok())
.ok_or_else(|| Error::from(ParseError::missing_field("p")))?;
let quantity = data["q"]
.as_str()
.and_then(|s| Decimal::from_str(s).ok())
.ok_or_else(|| Error::from(ParseError::missing_field("q")))?;
let first_trade_id = data["f"]
.as_i64()
.ok_or_else(|| Error::from(ParseError::missing_field("f")))?;
let last_trade_id = data["l"]
.as_i64()
.ok_or_else(|| Error::from(ParseError::missing_field("l")))?;
let timestamp = data["T"]
.as_i64()
.ok_or_else(|| Error::from(ParseError::missing_field("T")))?;
let is_buyer_maker = data["m"].as_bool().unwrap_or(false);
let is_best_match = data["M"].as_bool();
Ok(AggTrade {
agg_id,
price,
quantity,
first_trade_id,
last_trade_id,
timestamp,
is_buyer_maker,
is_best_match,
symbol,
})
}
pub fn parse_order_trades(
data: &Value,
market: Option<&Market>,
) -> Result<Vec<ccxt_core::types::Trade>> {
if let Some(array) = data.as_array() {
array.iter().map(|item| parse_trade(item, market)).collect()
} else {
Ok(vec![parse_trade(data, market)?])
}
}