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 = "orderId")]
83 pub order_id: String,
84 #[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 = "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}×tamp={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}×tamp={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}×tamp={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}×tamp={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}×tamp={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}