exc_binance/http/response/
trading.rs1use 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#[derive(Debug, Clone, Deserialize)]
15#[serde(untagged)]
16pub enum Order {
17 UsdMarginFutures(UsdMarginFuturesOrder),
19 EuropeanOptions(OptionsOrder),
21 Spot(SpotOrder),
23}
24
25impl Order {
26 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 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 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 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#[derive(Debug, Clone, Deserialize)]
89#[serde(rename_all = "camelCase")]
90pub struct UsdMarginFuturesOrder {
91 pub client_order_id: String,
93 pub cum_qty: Option<Decimal>,
95 pub cum_quote: Option<Decimal>,
97 pub executed_qty: Decimal,
99 pub order_id: i64,
101 pub avg_price: Decimal,
103 pub orig_qty: Decimal,
105 pub price: Decimal,
107 pub reduce_only: bool,
109 pub side: OrderSide,
111 pub position_side: PositionSide,
113 pub status: Status,
115 pub stop_price: Decimal,
117 pub close_position: bool,
119 pub symbol: String,
121 pub time_in_force: TimeInForce,
123 #[serde(rename = "type")]
125 pub order_type: OrderType,
126 pub activate_price: Option<Decimal>,
128 pub price_rate: Option<Decimal>,
130 pub update_time: i64,
132 pub working_type: String,
134 pub price_protect: bool,
136}
137
138#[serde_as]
140#[derive(Debug, Clone, Deserialize)]
141#[serde(rename_all = "camelCase")]
142pub struct SpotAck {
143 pub symbol: String,
145 #[serde_as(as = "serde_with::PickFirst<(_, serde_with::DisplayFromStr)>")]
147 pub order_id: i64,
148 orig_client_order_id: Option<String>,
150 client_order_id: String,
152 #[serde(alias = "updateTime")]
154 pub transact_time: Option<i64>,
155}
156
157impl SpotAck {
158 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#[derive(Debug, Clone, Deserialize)]
169#[serde(rename_all = "camelCase")]
170pub struct SpotResult {
171 pub price: Decimal,
173 pub orig_qty: Decimal,
175 pub executed_qty: Decimal,
177 pub cummulative_quote_qty: Decimal,
179 pub status: Status,
181 pub time_in_force: TimeInForce,
183 #[serde(rename = "type")]
185 pub order_type: OrderType,
186 pub side: OrderSide,
188}
189
190#[derive(Debug, Clone, Deserialize)]
192#[serde(rename_all = "camelCase")]
193pub struct SpotOrder {
194 #[serde(flatten)]
196 pub ack: SpotAck,
197
198 #[serde(flatten)]
200 pub result: Option<SpotResult>,
201
202 #[serde(default)]
204 pub fills: Vec<SpotFill>,
205}
206
207#[derive(Debug, Clone, Deserialize)]
209#[serde(rename_all = "camelCase")]
210pub struct SpotFill {
211 pub price: Decimal,
213 pub qty: Decimal,
215 pub commission: Decimal,
217 pub commission_asset: Asset,
219 pub trade_id: i64,
221}
222
223#[serde_as]
225#[derive(Debug, Clone, Deserialize)]
226#[serde(rename_all = "camelCase")]
227pub struct OptionsOrder {
228 pub(crate) order_id: i64,
230 pub(crate) client_order_id: Option<Str>,
232 pub(crate) symbol: Str,
234 pub(crate) price: Decimal,
236 pub(crate) quantity: Decimal,
238 pub(crate) side: OrderSide,
240 #[serde(rename = "type")]
242 pub(crate) order_type: OrderType,
243 #[allow(unused)]
245 pub(crate) reduce_only: bool,
246 pub(crate) post_only: bool,
248 #[allow(unused)]
250 pub(crate) mmp: bool,
251 #[serde(flatten, default)]
253 pub(crate) state: Option<OptionsOrderState>,
254}
255
256#[derive(Debug, Clone, Deserialize)]
258#[serde(rename_all = "camelCase")]
259pub struct OptionsOrderState {
260 #[allow(unused)]
262 pub(crate) create_time: i64,
263 pub(crate) update_time: i64,
265 pub(crate) executed_qty: Decimal,
267 pub(crate) avg_price: Decimal,
269 pub(crate) quote_asset: Asset,
271 pub(crate) fee: Decimal,
273 pub(crate) time_in_force: TimeInForce,
275 pub(crate) status: Status,
277}