1use super::super::{Okx, parser};
4use crate::okx::signed_request::HttpMethod;
5use ccxt_core::{
6 Error, ParseError, Result,
7 types::{Amount, Order, OrderRequest, OrderSide, OrderType, Price, TimeInForce},
8};
9use tracing::warn;
10
11impl Okx {
12 pub async fn create_order_v2(&self, request: OrderRequest) -> Result<Order> {
32 let market = self.base().market(&request.symbol).await?;
33
34 let path = Self::build_api_path("/trade/order");
35
36 let mut map = serde_json::Map::new();
37 map.insert(
38 "instId".to_string(),
39 serde_json::Value::String(market.id.clone()),
40 );
41 map.insert(
42 "tdMode".to_string(),
43 serde_json::Value::String(self.options().account_mode.clone()),
44 );
45 map.insert(
46 "side".to_string(),
47 serde_json::Value::String(match request.side {
48 OrderSide::Buy => "buy".to_string(),
49 OrderSide::Sell => "sell".to_string(),
50 }),
51 );
52 map.insert(
53 "ordType".to_string(),
54 serde_json::Value::String(match request.order_type {
55 OrderType::LimitMaker => "post_only".to_string(),
56 OrderType::StopLoss
57 | OrderType::StopMarket
58 | OrderType::TakeProfit
59 | OrderType::TrailingStop => "market".to_string(),
60 _ => "limit".to_string(),
61 }),
62 );
63 map.insert(
64 "sz".to_string(),
65 serde_json::Value::String(request.amount.to_string()),
66 );
67
68 if let Some(p) = request.price {
69 if request.order_type == OrderType::Limit || request.order_type == OrderType::LimitMaker
70 {
71 map.insert("px".to_string(), serde_json::Value::String(p.to_string()));
72 }
73 }
74
75 if request.time_in_force == Some(TimeInForce::PO) || request.post_only == Some(true) {
76 }
78
79 if let Some(client_id) = request.client_order_id {
80 map.insert("clOrdId".to_string(), serde_json::Value::String(client_id));
81 }
82
83 if let Some(reduce_only) = request.reduce_only {
84 map.insert(
85 "reduceOnly".to_string(),
86 serde_json::Value::Bool(reduce_only),
87 );
88 }
89
90 if let Some(trigger) = request.trigger_price.or(request.stop_price) {
91 map.insert(
92 "triggerPx".to_string(),
93 serde_json::Value::String(trigger.to_string()),
94 );
95 }
96
97 if let Some(pos_side) = request.position_side {
98 map.insert("posSide".to_string(), serde_json::Value::String(pos_side));
99 }
100
101 let body = serde_json::Value::Object(map);
102
103 let response = self
104 .signed_request(&path)
105 .method(HttpMethod::Post)
106 .body(body)
107 .execute()
108 .await?;
109
110 let data = response
111 .get("data")
112 .ok_or_else(|| Error::from(ParseError::missing_field("data")))?;
113
114 let orders = data.as_array().ok_or_else(|| {
115 Error::from(ParseError::invalid_format(
116 "data",
117 "Expected array of orders",
118 ))
119 })?;
120
121 if orders.is_empty() {
122 return Err(Error::exchange("-1", "No order data returned"));
123 }
124
125 parser::parse_order(&orders[0], Some(&market))
126 }
127
128 #[deprecated(
147 since = "0.2.0",
148 note = "Use create_order_v2 with OrderRequest::builder() instead"
149 )]
150 pub async fn create_order(
151 &self,
152 symbol: &str,
153 order_type: OrderType,
154 side: OrderSide,
155 amount: Amount,
156 price: Option<Price>,
157 ) -> Result<Order> {
158 let market = self.base().market(symbol).await?;
159
160 let path = Self::build_api_path("/trade/order");
161
162 let mut map = serde_json::Map::new();
163 map.insert(
164 "instId".to_string(),
165 serde_json::Value::String(market.id.clone()),
166 );
167 map.insert(
168 "tdMode".to_string(),
169 serde_json::Value::String(self.options().account_mode.clone()),
170 );
171 map.insert(
172 "side".to_string(),
173 serde_json::Value::String(match side {
174 OrderSide::Buy => "buy".to_string(),
175 OrderSide::Sell => "sell".to_string(),
176 }),
177 );
178 map.insert(
179 "ordType".to_string(),
180 serde_json::Value::String(match order_type {
181 OrderType::Market => "market".to_string(),
182 OrderType::LimitMaker => "post_only".to_string(),
183 _ => "limit".to_string(),
184 }),
185 );
186 map.insert(
187 "sz".to_string(),
188 serde_json::Value::String(amount.to_string()),
189 );
190
191 if let Some(p) = price {
192 if order_type == OrderType::Limit || order_type == OrderType::LimitMaker {
193 map.insert("px".to_string(), serde_json::Value::String(p.to_string()));
194 }
195 }
196 let body = serde_json::Value::Object(map);
197
198 let response = self
199 .signed_request(&path)
200 .method(HttpMethod::Post)
201 .body(body)
202 .execute()
203 .await?;
204
205 let data = response
206 .get("data")
207 .ok_or_else(|| Error::from(ParseError::missing_field("data")))?;
208
209 let orders = data.as_array().ok_or_else(|| {
210 Error::from(ParseError::invalid_format(
211 "data",
212 "Expected array of orders",
213 ))
214 })?;
215
216 if orders.is_empty() {
217 return Err(Error::exchange("-1", "No order data returned"));
218 }
219
220 parser::parse_order(&orders[0], Some(&market))
221 }
222
223 pub async fn cancel_order(&self, id: &str, symbol: &str) -> Result<Order> {
234 let market = self.base().market(symbol).await?;
235
236 let path = Self::build_api_path("/trade/cancel-order");
237
238 let mut map = serde_json::Map::new();
239 map.insert(
240 "instId".to_string(),
241 serde_json::Value::String(market.id.clone()),
242 );
243 map.insert(
244 "ordId".to_string(),
245 serde_json::Value::String(id.to_string()),
246 );
247 let body = serde_json::Value::Object(map);
248
249 let response = self
250 .signed_request(&path)
251 .method(HttpMethod::Post)
252 .body(body)
253 .execute()
254 .await?;
255
256 let data = response
257 .get("data")
258 .ok_or_else(|| Error::from(ParseError::missing_field("data")))?;
259
260 let orders = data.as_array().ok_or_else(|| {
261 Error::from(ParseError::invalid_format(
262 "data",
263 "Expected array of orders",
264 ))
265 })?;
266
267 if orders.is_empty() {
268 return Err(Error::exchange("-1", "No order data returned"));
269 }
270
271 parser::parse_order(&orders[0], Some(&market))
272 }
273
274 pub async fn fetch_order(&self, id: &str, symbol: &str) -> Result<Order> {
285 let market = self.base().market(symbol).await?;
286
287 let path = Self::build_api_path("/trade/order");
288
289 let response = self
290 .signed_request(&path)
291 .param("instId", &market.id)
292 .param("ordId", id)
293 .execute()
294 .await?;
295
296 let data = response
297 .get("data")
298 .ok_or_else(|| Error::from(ParseError::missing_field("data")))?;
299
300 let orders = data.as_array().ok_or_else(|| {
301 Error::from(ParseError::invalid_format(
302 "data",
303 "Expected array of orders",
304 ))
305 })?;
306
307 if orders.is_empty() {
308 return Err(Error::exchange("51400", "Order not found"));
309 }
310
311 parser::parse_order(&orders[0], Some(&market))
312 }
313
314 pub async fn fetch_open_orders(
326 &self,
327 symbol: Option<&str>,
328 since: Option<i64>,
329 limit: Option<u32>,
330 ) -> Result<Vec<Order>> {
331 let path = Self::build_api_path("/trade/orders-pending");
332
333 let actual_limit = limit.map_or(100, |l| l.min(100));
334
335 let market = if let Some(sym) = symbol {
336 let m = self.base().market(sym).await?;
337 Some(m)
338 } else {
339 None
340 };
341
342 let mut builder = self
343 .signed_request(&path)
344 .param("instType", self.get_inst_type())
345 .param("limit", actual_limit);
346
347 if let Some(ref m) = market {
348 builder = builder.param("instId", &m.id);
349 }
350
351 if let Some(start_time) = since {
352 builder = builder.param("begin", start_time);
353 }
354
355 let response = builder.execute().await?;
356
357 let data = response
358 .get("data")
359 .ok_or_else(|| Error::from(ParseError::missing_field("data")))?;
360
361 let orders_array = data.as_array().ok_or_else(|| {
362 Error::from(ParseError::invalid_format(
363 "data",
364 "Expected array of orders",
365 ))
366 })?;
367
368 let mut orders = Vec::new();
369 for order_data in orders_array {
370 match parser::parse_order(order_data, market.as_deref()) {
371 Ok(order) => orders.push(order),
372 Err(e) => {
373 warn!(error = %e, "Failed to parse open order");
374 }
375 }
376 }
377
378 Ok(orders)
379 }
380
381 pub async fn fetch_closed_orders(
393 &self,
394 symbol: Option<&str>,
395 since: Option<i64>,
396 limit: Option<u32>,
397 ) -> Result<Vec<Order>> {
398 let path = Self::build_api_path("/trade/orders-history");
399
400 let actual_limit = limit.map_or(100, |l| l.min(100));
401
402 let market = if let Some(sym) = symbol {
403 let m = self.base().market(sym).await?;
404 Some(m)
405 } else {
406 None
407 };
408
409 let mut builder = self
410 .signed_request(&path)
411 .param("instType", self.get_inst_type())
412 .param("limit", actual_limit);
413
414 if let Some(ref m) = market {
415 builder = builder.param("instId", &m.id);
416 }
417
418 if let Some(start_time) = since {
419 builder = builder.param("begin", start_time);
420 }
421
422 let response = builder.execute().await?;
423
424 let data = response
425 .get("data")
426 .ok_or_else(|| Error::from(ParseError::missing_field("data")))?;
427
428 let orders_array = data.as_array().ok_or_else(|| {
429 Error::from(ParseError::invalid_format(
430 "data",
431 "Expected array of orders",
432 ))
433 })?;
434
435 let mut orders = Vec::new();
436 for order_data in orders_array {
437 match parser::parse_order(order_data, market.as_deref()) {
438 Ok(order) => orders.push(order),
439 Err(e) => {
440 warn!(error = %e, "Failed to parse closed order");
441 }
442 }
443 }
444
445 Ok(orders)
446 }
447}