ccxt_exchanges/bitget/rest/
trading.rs1use super::super::{Bitget, parser};
4use ccxt_core::{
5 Error, ParseError, Result,
6 types::{Amount, Order, OrderRequest, OrderSide, OrderType, Price, TimeInForce},
7};
8use tracing::warn;
9
10impl Bitget {
11 pub async fn create_order_v2(&self, request: OrderRequest) -> Result<Order> {
13 let market = self.base().market(&request.symbol).await?;
14
15 let path = self.build_api_path("/trade/place-order");
16
17 let mut map = serde_json::Map::new();
18 map.insert(
19 "symbol".to_string(),
20 serde_json::Value::String(market.id.clone()),
21 );
22 map.insert(
23 "side".to_string(),
24 serde_json::Value::String(match request.side {
25 OrderSide::Buy => "buy".to_string(),
26 OrderSide::Sell => "sell".to_string(),
27 }),
28 );
29 map.insert(
30 "orderType".to_string(),
31 serde_json::Value::String(match request.order_type {
32 OrderType::LimitMaker => "limit_maker".to_string(),
33 OrderType::Market
34 | OrderType::StopLoss
35 | OrderType::StopMarket
36 | OrderType::TakeProfit
37 | OrderType::TrailingStop => "market".to_string(),
38 _ => "limit".to_string(),
39 }),
40 );
41 map.insert(
42 "size".to_string(),
43 serde_json::Value::String(request.amount.to_string()),
44 );
45
46 let force = if let Some(tif) = request.time_in_force {
47 match tif {
48 TimeInForce::GTC => "gtc",
49 TimeInForce::IOC => "ioc",
50 TimeInForce::FOK => "fok",
51 TimeInForce::PO => "post_only",
52 }
53 } else if request.post_only == Some(true) {
54 "post_only"
55 } else {
56 "gtc"
57 };
58 map.insert(
59 "force".to_string(),
60 serde_json::Value::String(force.to_string()),
61 );
62
63 if let Some(p) = request.price {
64 if request.order_type == OrderType::Limit || request.order_type == OrderType::LimitMaker
65 {
66 map.insert(
67 "price".to_string(),
68 serde_json::Value::String(p.to_string()),
69 );
70 }
71 }
72
73 if let Some(client_id) = request.client_order_id {
74 map.insert(
75 "clientOid".to_string(),
76 serde_json::Value::String(client_id),
77 );
78 }
79
80 if let Some(reduce_only) = request.reduce_only {
81 map.insert(
82 "reduceOnly".to_string(),
83 serde_json::Value::Bool(reduce_only),
84 );
85 }
86
87 if let Some(trigger) = request.trigger_price.or(request.stop_price) {
88 map.insert(
89 "triggerPrice".to_string(),
90 serde_json::Value::String(trigger.to_string()),
91 );
92 }
93
94 if let Some(tp) = request.take_profit_price {
95 map.insert(
96 "presetTakeProfitPrice".to_string(),
97 serde_json::Value::String(tp.to_string()),
98 );
99 }
100
101 if let Some(sl) = request.stop_loss_price {
102 map.insert(
103 "presetStopLossPrice".to_string(),
104 serde_json::Value::String(sl.to_string()),
105 );
106 }
107
108 let body = serde_json::Value::Object(map);
109
110 let response = self
111 .signed_request(&path)
112 .method(crate::bitget::signed_request::HttpMethod::Post)
113 .body(body)
114 .execute()
115 .await?;
116
117 let data = response
118 .get("data")
119 .ok_or_else(|| Error::from(ParseError::missing_field("data")))?;
120
121 parser::parse_order(data, Some(&market))
122 }
123
124 #[deprecated(
126 since = "0.2.0",
127 note = "Use create_order_v2 with OrderRequest::builder() instead"
128 )]
129 pub async fn create_order(
130 &self,
131 symbol: &str,
132 order_type: OrderType,
133 side: OrderSide,
134 amount: Amount,
135 price: Option<Price>,
136 ) -> Result<Order> {
137 let market = self.base().market(symbol).await?;
138
139 let path = self.build_api_path("/trade/place-order");
140
141 let mut map = serde_json::Map::new();
142 map.insert(
143 "symbol".to_string(),
144 serde_json::Value::String(market.id.clone()),
145 );
146 map.insert(
147 "side".to_string(),
148 serde_json::Value::String(match side {
149 OrderSide::Buy => "buy".to_string(),
150 OrderSide::Sell => "sell".to_string(),
151 }),
152 );
153 map.insert(
154 "orderType".to_string(),
155 serde_json::Value::String(match order_type {
156 OrderType::Market => "market".to_string(),
157 OrderType::LimitMaker => "limit_maker".to_string(),
158 _ => "limit".to_string(),
159 }),
160 );
161 map.insert(
162 "size".to_string(),
163 serde_json::Value::String(amount.to_string()),
164 );
165 map.insert(
166 "force".to_string(),
167 serde_json::Value::String("gtc".to_string()),
168 );
169
170 if let Some(p) = price {
171 if order_type == OrderType::Limit || order_type == OrderType::LimitMaker {
172 map.insert(
173 "price".to_string(),
174 serde_json::Value::String(p.to_string()),
175 );
176 }
177 }
178 let body = serde_json::Value::Object(map);
179
180 let response = self
181 .signed_request(&path)
182 .method(crate::bitget::signed_request::HttpMethod::Post)
183 .body(body)
184 .execute()
185 .await?;
186
187 let data = response
188 .get("data")
189 .ok_or_else(|| Error::from(ParseError::missing_field("data")))?;
190
191 parser::parse_order(data, Some(&market))
192 }
193
194 pub async fn cancel_order(&self, id: &str, symbol: &str) -> Result<Order> {
196 let market = self.base().market(symbol).await?;
197
198 let path = self.build_api_path("/trade/cancel-order");
199
200 let mut map = serde_json::Map::new();
201 map.insert(
202 "symbol".to_string(),
203 serde_json::Value::String(market.id.clone()),
204 );
205 map.insert(
206 "orderId".to_string(),
207 serde_json::Value::String(id.to_string()),
208 );
209 let body = serde_json::Value::Object(map);
210
211 let response = self
212 .signed_request(&path)
213 .method(crate::bitget::signed_request::HttpMethod::Post)
214 .body(body)
215 .execute()
216 .await?;
217
218 let data = response
219 .get("data")
220 .ok_or_else(|| Error::from(ParseError::missing_field("data")))?;
221
222 parser::parse_order(data, Some(&market))
223 }
224
225 pub async fn fetch_order(&self, id: &str, symbol: &str) -> Result<Order> {
227 let market = self.base().market(symbol).await?;
228
229 let path = self.build_api_path("/trade/orderInfo");
230
231 let response = self
232 .signed_request(&path)
233 .param("symbol", &market.id)
234 .param("orderId", id)
235 .execute()
236 .await?;
237
238 let data = response
239 .get("data")
240 .ok_or_else(|| Error::from(ParseError::missing_field("data")))?;
241
242 let order_data = if data.is_array() {
243 data.as_array()
244 .and_then(|arr| arr.first())
245 .ok_or_else(|| Error::exchange("40007", "Order not found"))?
246 } else {
247 data
248 };
249
250 parser::parse_order(order_data, Some(&market))
251 }
252
253 pub async fn fetch_open_orders(
255 &self,
256 symbol: Option<&str>,
257 since: Option<i64>,
258 limit: Option<u32>,
259 ) -> Result<Vec<Order>> {
260 let path = self.build_api_path("/trade/unfilled-orders");
261
262 let market = if let Some(sym) = symbol {
263 Some(self.base().market(sym).await?)
264 } else {
265 None
266 };
267
268 let actual_limit = limit.map_or(100, |l| l.min(500));
269
270 let mut builder = self.signed_request(&path).param("limit", actual_limit);
271
272 if let Some(m) = &market {
273 builder = builder.param("symbol", &m.id);
274 }
275
276 if let Some(start_time) = since {
277 builder = builder.param("startTime", start_time);
278 }
279
280 let response = builder.execute().await?;
281
282 let data = response
283 .get("data")
284 .ok_or_else(|| Error::from(ParseError::missing_field("data")))?;
285
286 let orders_array = data.as_array().ok_or_else(|| {
287 Error::from(ParseError::invalid_format(
288 "data",
289 "Expected array of orders",
290 ))
291 })?;
292
293 let mut orders = Vec::new();
294 for order_data in orders_array {
295 match parser::parse_order(order_data, market.as_deref()) {
296 Ok(order) => orders.push(order),
297 Err(e) => {
298 warn!(error = %e, "Failed to parse open order");
299 }
300 }
301 }
302
303 Ok(orders)
304 }
305
306 pub async fn fetch_closed_orders(
308 &self,
309 symbol: Option<&str>,
310 since: Option<i64>,
311 limit: Option<u32>,
312 ) -> Result<Vec<Order>> {
313 let path = self.build_api_path("/trade/history-orders");
314
315 let market = if let Some(sym) = symbol {
316 Some(self.base().market(sym).await?)
317 } else {
318 None
319 };
320
321 let actual_limit = limit.map_or(100, |l| l.min(500));
322
323 let mut builder = self.signed_request(&path).param("limit", actual_limit);
324
325 if let Some(m) = &market {
326 builder = builder.param("symbol", &m.id);
327 }
328
329 if let Some(start_time) = since {
330 builder = builder.param("startTime", start_time);
331 }
332
333 let response = builder.execute().await?;
334
335 let data = response
336 .get("data")
337 .ok_or_else(|| Error::from(ParseError::missing_field("data")))?;
338
339 let orders_array = data.as_array().ok_or_else(|| {
340 Error::from(ParseError::invalid_format(
341 "data",
342 "Expected array of orders",
343 ))
344 })?;
345
346 let mut orders = Vec::new();
347 for order_data in orders_array {
348 match parser::parse_order(order_data, market.as_deref()) {
349 Ok(order) => orders.push(order),
350 Err(e) => {
351 warn!(error = %e, "Failed to parse closed order");
352 }
353 }
354 }
355
356 Ok(orders)
357 }
358}