exc_binance/http/response/
trading.rs

1use exc_core::{Asset, ExchangeError, Str};
2use rust_decimal::Decimal;
3use serde::Deserialize;
4use serde_with::serde_as;
5
6use crate::{
7    http::error::RestError,
8    types::trading::{OrderSide, OrderType, PositionSide, Status, TimeInForce},
9};
10
11use super::Data;
12
13/// Order.
14#[derive(Debug, Clone, Deserialize)]
15#[serde(untagged)]
16pub enum Order {
17    /// Usd-Margin Futures.
18    UsdMarginFutures(UsdMarginFuturesOrder),
19    /// Options.
20    EuropeanOptions(OptionsOrder),
21    /// Spot.
22    Spot(SpotOrder),
23}
24
25impl Order {
26    /// Get order id.
27    pub fn id(&self) -> i64 {
28        match self {
29            Self::UsdMarginFutures(order) => order.order_id,
30            Self::Spot(order) => order.ack.order_id,
31            Self::EuropeanOptions(order) => order.order_id,
32        }
33    }
34
35    /// Get symbol.
36    pub fn symbol(&self) -> &str {
37        match self {
38            Self::UsdMarginFutures(order) => order.symbol.as_str(),
39            Self::Spot(order) => order.ack.symbol.as_str(),
40            Self::EuropeanOptions(order) => order.symbol.as_str(),
41        }
42    }
43
44    /// Get client order id.
45    pub fn client_id(&self) -> &str {
46        tracing::debug!("get client id; {self:?}");
47        match self {
48            Self::UsdMarginFutures(order) => order.client_order_id.as_str(),
49            Self::Spot(order) => order.ack.client_order_id(),
50            Self::EuropeanOptions(order) => order
51                .client_order_id
52                .as_ref()
53                .map(|s| s.as_str())
54                .unwrap_or_default(),
55        }
56    }
57
58    /// Get updated time.
59    pub fn updated(&self) -> Option<i64> {
60        match self {
61            Self::UsdMarginFutures(order) => Some(order.update_time),
62            Self::Spot(order) => order.ack.transact_time,
63            Self::EuropeanOptions(order) => order.state.as_ref().map(|s| s.update_time),
64        }
65    }
66}
67
68impl TryFrom<Data> for Order {
69    type Error = RestError;
70
71    fn try_from(value: Data) -> Result<Self, Self::Error> {
72        match value {
73            Data::Order(order) => Ok(order),
74            Data::Error(msg) => match msg.code {
75                -2013 => Err(RestError::Exchange(ExchangeError::OrderNotFound)),
76                _ => Err(RestError::Exchange(ExchangeError::Api(anyhow::anyhow!(
77                    "{msg:?}"
78                )))),
79            },
80            _ => Err(RestError::UnexpectedResponseType(anyhow::anyhow!(
81                "{value:?}"
82            ))),
83        }
84    }
85}
86
87/// Usd-Margin Futures Order.
88#[derive(Debug, Clone, Deserialize)]
89#[serde(rename_all = "camelCase")]
90pub struct UsdMarginFuturesOrder {
91    /// Client id.
92    pub client_order_id: String,
93    /// FIXME: what is this?
94    pub cum_qty: Option<Decimal>,
95    /// FIXME: what is this?
96    pub cum_quote: Option<Decimal>,
97    /// Filled size.
98    pub executed_qty: Decimal,
99    /// Order id.
100    pub order_id: i64,
101    /// Cost.
102    pub avg_price: Decimal,
103    /// Size.
104    pub orig_qty: Decimal,
105    /// Price.
106    pub price: Decimal,
107    /// Reduce only.
108    pub reduce_only: bool,
109    /// Order side.
110    pub side: OrderSide,
111    /// Position side.
112    pub position_side: PositionSide,
113    /// Status.
114    pub status: Status,
115    /// Stop price.
116    pub stop_price: Decimal,
117    /// Is close position.
118    pub close_position: bool,
119    /// Symbol.
120    pub symbol: String,
121    /// Time-In-Force.
122    pub time_in_force: TimeInForce,
123    /// Order type.
124    #[serde(rename = "type")]
125    pub order_type: OrderType,
126    /// Active price.
127    pub activate_price: Option<Decimal>,
128    /// Price rate.
129    pub price_rate: Option<Decimal>,
130    /// Update timestamp.
131    pub update_time: i64,
132    /// Working type.
133    pub working_type: String,
134    /// Price protect.
135    pub price_protect: bool,
136}
137
138/// Spot Ack.
139#[serde_as]
140#[derive(Debug, Clone, Deserialize)]
141#[serde(rename_all = "camelCase")]
142pub struct SpotAck {
143    /// Symbol.
144    pub symbol: String,
145    /// Order id.
146    #[serde_as(as = "serde_with::PickFirst<(_, serde_with::DisplayFromStr)>")]
147    pub order_id: i64,
148    /// Orignal client order id.
149    orig_client_order_id: Option<String>,
150    /// Client id.
151    client_order_id: String,
152    /// Update timestamp.
153    #[serde(alias = "updateTime")]
154    pub transact_time: Option<i64>,
155}
156
157impl SpotAck {
158    /// Client order id.
159    pub fn client_order_id(&self) -> &str {
160        match &self.orig_client_order_id {
161            Some(id) => id.as_str(),
162            None => self.client_order_id.as_str(),
163        }
164    }
165}
166
167/// Spot Result.
168#[derive(Debug, Clone, Deserialize)]
169#[serde(rename_all = "camelCase")]
170pub struct SpotResult {
171    /// Price.
172    pub price: Decimal,
173    /// Size.
174    pub orig_qty: Decimal,
175    /// Filled size.
176    pub executed_qty: Decimal,
177    /// Filled quote size.
178    pub cummulative_quote_qty: Decimal,
179    /// Status.
180    pub status: Status,
181    /// Time-In-Force.
182    pub time_in_force: TimeInForce,
183    /// Order type.
184    #[serde(rename = "type")]
185    pub order_type: OrderType,
186    /// Order side.
187    pub side: OrderSide,
188}
189
190/// Spot Order.
191#[derive(Debug, Clone, Deserialize)]
192#[serde(rename_all = "camelCase")]
193pub struct SpotOrder {
194    /// Ack.
195    #[serde(flatten)]
196    pub ack: SpotAck,
197
198    /// Result.
199    #[serde(flatten)]
200    pub result: Option<SpotResult>,
201
202    /// Fills.
203    #[serde(default)]
204    pub fills: Vec<SpotFill>,
205}
206
207/// Spot fill.
208#[derive(Debug, Clone, Deserialize)]
209#[serde(rename_all = "camelCase")]
210pub struct SpotFill {
211    /// Price.
212    pub price: Decimal,
213    /// Size.
214    pub qty: Decimal,
215    /// Fee.
216    pub commission: Decimal,
217    /// Fee asset.
218    pub commission_asset: Asset,
219    /// Trade id.
220    pub trade_id: i64,
221}
222
223/// Options Order.
224#[serde_as]
225#[derive(Debug, Clone, Deserialize)]
226#[serde(rename_all = "camelCase")]
227pub struct OptionsOrder {
228    /// Order id.
229    pub(crate) order_id: i64,
230    /// Client id.
231    pub(crate) client_order_id: Option<Str>,
232    /// Symbol.
233    pub(crate) symbol: Str,
234    /// Price.
235    pub(crate) price: Decimal,
236    /// Size.
237    pub(crate) quantity: Decimal,
238    /// Side.
239    pub(crate) side: OrderSide,
240    /// Order type.
241    #[serde(rename = "type")]
242    pub(crate) order_type: OrderType,
243    /// Reduce only.
244    #[allow(unused)]
245    pub(crate) reduce_only: bool,
246    /// Post only.
247    pub(crate) post_only: bool,
248    /// Mmp.
249    #[allow(unused)]
250    pub(crate) mmp: bool,
251    /// State.
252    #[serde(flatten, default)]
253    pub(crate) state: Option<OptionsOrderState>,
254}
255
256/// Options Order State.
257#[derive(Debug, Clone, Deserialize)]
258#[serde(rename_all = "camelCase")]
259pub struct OptionsOrderState {
260    /// Create time.
261    #[allow(unused)]
262    pub(crate) create_time: i64,
263    /// Update time.
264    pub(crate) update_time: i64,
265    /// Filled size.
266    pub(crate) executed_qty: Decimal,
267    /// Average price.
268    pub(crate) avg_price: Decimal,
269    /// Quote asset.
270    pub(crate) quote_asset: Asset,
271    /// Fee.
272    pub(crate) fee: Decimal,
273    /// Time-In-Force.
274    pub(crate) time_in_force: TimeInForce,
275    /// Status.
276    pub(crate) status: Status,
277}