use exc_core::{Asset, ExchangeError};
use rust_decimal::Decimal;
use serde::Deserialize;
use serde_with::serde_as;
use crate::{
http::error::RestError,
types::trading::{OrderSide, OrderType, PositionSide, Status, TimeInForce},
};
use super::Data;
#[derive(Debug, Clone, Deserialize)]
#[serde(untagged)]
pub enum Order {
UsdMarginFutures(UsdMarginFuturesOrder),
Spot(SpotOrder),
}
impl Order {
pub fn id(&self) -> i64 {
match self {
Self::UsdMarginFutures(order) => order.order_id,
Self::Spot(order) => order.ack.order_id,
}
}
pub fn symbol(&self) -> &str {
match self {
Self::UsdMarginFutures(order) => order.symbol.as_str(),
Self::Spot(order) => order.ack.symbol.as_str(),
}
}
pub fn client_id(&self) -> &str {
tracing::debug!("get client id; {self:?}");
match self {
Self::UsdMarginFutures(order) => order.client_order_id.as_str(),
Self::Spot(order) => order.ack.client_order_id(),
}
}
pub fn updated(&self) -> Option<i64> {
match self {
Self::UsdMarginFutures(order) => Some(order.update_time),
Self::Spot(order) => order.ack.transact_time,
}
}
}
impl TryFrom<Data> for Order {
type Error = RestError;
fn try_from(value: Data) -> Result<Self, Self::Error> {
match value {
Data::Order(order) => Ok(order),
Data::Error(msg) => match msg.code {
-2013 => Err(RestError::Exchange(ExchangeError::OrderNotFound)),
_ => Err(RestError::Exchange(ExchangeError::Api(anyhow::anyhow!(
"{msg:?}"
)))),
},
_ => Err(RestError::UnexpectedResponseType(anyhow::anyhow!(
"{value:?}"
))),
}
}
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UsdMarginFuturesOrder {
pub client_order_id: String,
pub cum_qty: Option<Decimal>,
pub cum_quote: Option<Decimal>,
pub executed_qty: Decimal,
pub order_id: i64,
pub avg_price: Decimal,
pub orig_qty: Decimal,
pub price: Decimal,
pub reduce_only: bool,
pub side: OrderSide,
pub position_side: PositionSide,
pub status: Status,
pub stop_price: Decimal,
pub close_position: bool,
pub symbol: String,
pub time_in_force: TimeInForce,
#[serde(rename = "type")]
pub order_type: OrderType,
pub activate_price: Option<Decimal>,
pub price_rate: Option<Decimal>,
pub update_time: i64,
pub working_type: String,
pub price_protect: bool,
}
#[serde_as]
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SpotAck {
pub symbol: String,
#[serde_as(as = "serde_with::PickFirst<(_, serde_with::DisplayFromStr)>")]
pub order_id: i64,
orig_client_order_id: Option<String>,
client_order_id: String,
#[serde(alias = "updateTime")]
pub transact_time: Option<i64>,
}
impl SpotAck {
pub fn client_order_id(&self) -> &str {
match &self.orig_client_order_id {
Some(id) => id.as_str(),
None => self.client_order_id.as_str(),
}
}
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SpotResult {
pub price: Decimal,
pub orig_qty: Decimal,
pub executed_qty: Decimal,
pub cummulative_quote_qty: Decimal,
pub status: Status,
pub time_in_force: TimeInForce,
#[serde(rename = "type")]
pub order_type: OrderType,
pub side: OrderSide,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SpotOrder {
#[serde(flatten)]
pub ack: SpotAck,
#[serde(flatten)]
pub result: Option<SpotResult>,
#[serde(default)]
pub fills: Vec<SpotFill>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SpotFill {
pub price: Decimal,
pub qty: Decimal,
pub commission: Decimal,
pub commission_asset: Asset,
pub trade_id: i64,
}