mexc_rust_sdk/
orders.rs

1use crate::{Mexc, PROD_API_URL, utils::{parse_string_to_f64, get_timestamp, serialize_f64_as_string}};
2use anyhow::{anyhow, bail};
3use reqwest::{StatusCode, Response};
4use serde::{Deserialize, Serialize};
5use hmac::{Hmac, Mac};
6use sha2::Sha256;
7
8pub const DEFAULT_RECV_WINDOW: u64 = 5000;
9
10
11
12#[allow(non_camel_case_types)]
13#[derive(Serialize, Deserialize, Debug)]
14pub enum OrderType {
15    LIMIT,
16    MARKET,
17    LIMIT_MAKER,
18    IMMEDIATE_OR_CANCEL,
19    FILL_OR_KILL
20}
21impl std::fmt::Display for OrderType {
22    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23        match self {
24            OrderType::LIMIT => write!(f, "LIMIT"),
25            OrderType::MARKET => write!(f, "MARKET"),
26            OrderType::LIMIT_MAKER => write!(f, "LIMIT_MAKER"),
27            OrderType::IMMEDIATE_OR_CANCEL => write!(f, "IMMEDIATE_OR_CANCEL"),
28            OrderType::FILL_OR_KILL => write!(f, "FILL_OR_KILL"),
29        }
30    }
31}
32
33#[derive(Serialize, Deserialize, Debug, PartialEq)]
34pub enum OrderSide {
35    BUY,
36    SELL
37}
38impl std::fmt::Display for OrderSide {
39    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40        match self {
41            OrderSide::BUY => write!(f, "BUY"),
42            OrderSide::SELL => write!(f, "SELL"),
43        }
44    }
45}
46
47#[allow(non_camel_case_types)]
48#[derive(Deserialize, Debug)]
49pub enum OrderStatus {
50    NEW,
51    FILLED,
52    PARTIALLY_FILLED,
53    CANCELED,
54    PARTIALLY_CANCELED
55}
56
57#[allow(dead_code)]
58#[derive(Deserialize, Debug)]
59pub struct OrderReceipt {
60    pub symbol: String,
61    #[serde(rename = "orderId")]
62    pub order_id: String,
63    #[serde(rename = "orderListId")]
64    pub order_list_id: i64,
65    #[serde(deserialize_with = "parse_string_to_f64")]
66    pub price: f64,
67    #[serde(rename = "origQty", deserialize_with = "parse_string_to_f64")]
68    pub orig_qty: f64,
69    #[serde(rename = "type")]
70    pub order_type: OrderType,
71    pub side: OrderSide,
72    #[serde(rename = "transactTime")]
73    pub transact_time: u128,
74}
75
76#[allow(dead_code)]
77#[derive(Deserialize, Debug)]
78pub struct CancelledOrder {
79    pub symbol: String,
80    //#[serde(rename = "origClientOrderId")]
81    //pub orig_client_order_id: String,
82    #[serde(rename = "orderId")]
83    pub order_id: String,
84    //#[serde(rename = "clientOrderId")]
85    //pub client_order_id: String,
86    #[serde(deserialize_with = "parse_string_to_f64")]
87    pub price: f64,
88    #[serde(rename = "origQty", deserialize_with = "parse_string_to_f64")]
89    pub orig_qty: f64,
90    #[serde(rename = "executedQty", deserialize_with = "parse_string_to_f64")]
91    pub exec_qty: f64,
92    #[serde(rename = "cummulativeQuoteQty", deserialize_with = "parse_string_to_f64")]
93    pub cum_quote_qty: f64,
94    //#[serde(rename = "timeInForce")]
95    //pub time_in_force: String,
96    #[serde(rename = "type")]
97    pub order_type: OrderType,
98    pub side: OrderSide,
99}
100
101
102#[derive(Serialize,Deserialize, Debug)]
103pub struct Order {
104    pub symbol: String,
105    #[serde(serialize_with = "serialize_f64_as_string")]
106    pub price: f64,
107    #[serde(serialize_with = "serialize_f64_as_string")]
108    pub quantity: f64,
109    pub side: OrderSide,
110    #[serde(rename = "type")]
111    pub order_type: OrderType
112}
113
114#[allow(dead_code)]
115#[derive(Deserialize, Debug)]
116pub struct OrderQuery {
117    pub symbol: String,
118    #[serde(rename = "orderId")]
119    pub order_id: String,
120
121    #[serde(deserialize_with = "parse_string_to_f64")]
122    pub price: f64,
123
124    #[serde(rename = "origQty", deserialize_with = "parse_string_to_f64")]
125    pub orig_qty: f64,
126
127    #[serde(rename = "executedQty", deserialize_with = "parse_string_to_f64")]
128    pub exec_qty: f64,
129
130    #[serde(rename = "type")]
131    pub order_type: OrderType,
132    pub side: OrderSide,
133
134    #[serde(rename = "time")]
135    pub created_time: u128,
136
137    #[serde(rename = "updateTime")]
138    pub last_update: Option<u128>,
139}
140
141impl Mexc {
142
143    pub fn sign_request(&self, order_details: String) -> anyhow::Result<String> {
144        let secret_key = self.api_secret.as_ref().ok_or_else(|| anyhow!("Missing secret key"))?;
145        let mut signed_key = Hmac::<Sha256>::new_from_slice(secret_key.as_bytes())?;
146        signed_key.update(order_details.as_bytes());
147        let signature = hex::encode(signed_key.finalize().into_bytes());
148        let signed_order_details: String = format!("{}&signature={}", order_details, signature);
149        Ok(signed_order_details)
150    }
151
152    pub async fn post_signed(&self, url: &str) -> anyhow::Result<Response> {
153        let api_key = self.api_key.as_ref().ok_or_else(|| anyhow!("Missing api key"))?;
154
155        let resp = self.client
156        .post(url)
157        .header("X-MEXC-APIKEY", api_key)
158        .send().await?;
159        Ok(resp)
160    }
161
162    pub async fn put_signed(&self, url: &str) -> anyhow::Result<Response> {
163        let api_key = self.api_key.as_ref().ok_or_else(|| anyhow!("Missing api key"))?;
164
165        let resp = self.client
166        .put(url)
167        .header("X-MEXC-APIKEY", api_key)
168        .send().await?;
169        Ok(resp)
170    }
171
172    pub async fn delete_signed(&self, url: &str) -> anyhow::Result<Response> {
173        let api_key = self.api_key.as_ref().ok_or_else(|| anyhow!("Missing api key"))?;
174        
175        let resp = self.client
176        .delete(url)
177        .header("X-MEXC-APIKEY", api_key)
178        .send().await?;
179        Ok(resp)
180    }
181
182    pub async fn submit_order(&self, symbol: &str, side: OrderSide, order_type: OrderType, price: f64, quantity: f64, recv_window: Option<u64>) -> anyhow::Result<OrderReceipt> {
183        let recv_window = recv_window.unwrap_or(DEFAULT_RECV_WINDOW);
184        let timestamp = get_timestamp();
185
186        let order_request = format!("symbol={symbol}&side={side}&type={order_type}&quantity={quantity}&price={price}&recvWindow={recv_window}&timestamp={timestamp}");
187        let signed_order = self.sign_request(order_request)?;
188        let url = format!("{PROD_API_URL}/api/v3/order?{signed_order}");
189        let resp: Response = self.post_signed(&url).await?;
190
191        if resp.status() == StatusCode::OK {
192            let receipe: OrderReceipt = resp.json().await?;
193            Ok(receipe)
194        } else {
195            let err = resp.text().await?;
196            bail!(err);
197        }
198    }
199
200    pub async fn batch_orders(&self, orders: Vec<Order>, recv_window: Option<u64>) -> anyhow::Result<Vec<OrderReceipt>> {
201        if orders.is_empty() {
202            bail!("No orders in vector");
203        }
204
205        let recv_window = recv_window.unwrap_or(DEFAULT_RECV_WINDOW);
206        let timestamp = get_timestamp();
207
208        let json = serde_json::to_string(&orders)?;
209
210        let encoded_orders = url::form_urlencoded::Serializer::new(String::new())
211        .append_pair("batchOrders", &json)
212        .finish();
213
214        let order_request = format!("{encoded_orders}&recvWindow={recv_window}&timestamp={timestamp}");
215
216        let signed_order = self.sign_request(order_request)?;
217        let url = format!("{PROD_API_URL}/api/v3/batchOrders?{signed_order}");
218
219        let resp: Response = self.post_signed(&url).await?;
220
221        if resp.status() == StatusCode::OK {
222
223
224            let txt = resp.text().await?;
225        
226            let res: Result<Vec<OrderReceipt>, _> = serde_json::from_str(&txt);
227
228            match res {
229                Ok(receipts) => Ok(receipts),
230                Err(err) => {
231                    bail!("Err: {err} on {txt}");
232                }
233            }
234        } else {
235            let err = resp.text().await?;
236            bail!(err);
237        }
238    }
239
240    pub async fn cancel_all_orders(&self, symbol: &str, recv_window: Option<u64>) -> anyhow::Result<Vec<CancelledOrder>> {
241        let recv_window = recv_window.unwrap_or(DEFAULT_RECV_WINDOW);
242        let timestamp = get_timestamp();
243
244        let order_request = format!("symbol={symbol}&recvWindow={recv_window}&timestamp={timestamp}");
245        let signed_order = self.sign_request(order_request)?;
246        let url = format!("{PROD_API_URL}/api/v3/openOrders?{signed_order}");
247        let resp: Response = self.delete_signed(&url).await?;
248
249        if resp.status() == StatusCode::OK {
250            let cancelled_orders: Vec<CancelledOrder> = resp.json().await?;
251            Ok(cancelled_orders)
252        } else {
253            let err = resp.text().await?;
254            bail!(err);
255        }
256    }
257
258    pub async fn cancel_order(&self, symbol: &str, order_id: &str,recv_window: Option<u64>) -> anyhow::Result<CancelledOrder> {
259        let recv_window = recv_window.unwrap_or(DEFAULT_RECV_WINDOW);
260        let timestamp = get_timestamp();
261
262        let order_request = format!("symbol={symbol}&orderId={order_id}&recvWindow={recv_window}&timestamp={timestamp}");
263        let signed_order = self.sign_request(order_request)?;
264        let url = format!("{PROD_API_URL}/api/v3/order?{signed_order}");
265        let resp: Response = self.delete_signed(&url).await?;
266
267        if resp.status() == StatusCode::OK {
268            let cancelled_order: CancelledOrder = resp.json().await?;
269            Ok(cancelled_order)
270        } else {
271            let err = resp.text().await?;
272            bail!(err);
273        }
274    }
275
276    pub async fn get_open_orders(&self, symbol: &str,recv_window: Option<u64>) -> anyhow::Result<Vec<OrderQuery>> {
277
278        let recv_window = recv_window.unwrap_or(DEFAULT_RECV_WINDOW);
279        let timestamp = get_timestamp();
280
281        let order_request = format!("symbol={symbol}&recvWindow={recv_window}&timestamp={timestamp}");
282        let signed_order = self.sign_request(order_request)?;
283        let url = format!("{PROD_API_URL}/api/v3/openOrders?{signed_order}");
284        let resp: Response = self.get_signed(&url).await?;
285
286        if resp.status() == StatusCode::OK {
287            let orders: Vec<OrderQuery> = resp.json().await?;
288            Ok(orders)
289        } else {
290            let err = resp.text().await?;
291            bail!(err);
292        }
293    }
294}