Skip to main content

bybit/
trade.rs

1use crate::prelude::*;
2
3#[derive(Clone)]
4pub struct Trader {
5    pub client: Client,
6    pub recv_window: u16,
7}
8
9/// Creates an order with various options for different account types and contract types.
10///
11/// # Account Coverage
12/// - Unified account: Spot, USDT perpetual, USDC contract, Inverse contract, Option
13/// - Classic account: Spot, USDT perpetual, Inverse contract
14///
15/// # Order Types
16/// - Limit Order: Must specify quantity and price.
17/// - Market Order: Executes at the best market price. Price parameter can be empty.
18/// - Conditional Order: Set `triggerPrice` to convert to a conditional order.
19///
20/// # Time In Force Strategies
21/// - GTC (Good Till Cancelled)
22/// - IOC (Immediate Or Cancel)
23/// - FOK (Fill Or Kill)
24/// - PostOnly: Cancelled if it would be filled immediately when submitted.
25///
26/// # Take Profit / Stop Loss
27/// - Can be set during order placement and modified later.
28///
29/// # Order Quantity
30/// - Only positive numbers are supported for perpetual contract orders.
31///
32/// # Order Price
33/// - Required for limit orders. Must be higher than liquidation price if in position.
34///
35/// # Order Link ID
36/// - Custom active order ID up to 36 characters.
37///
38/// # Order Limits
39/// - Futures: 500 active orders per contract, 10 conditional orders per account.
40/// - Spot: 500 total orders, 30 open TP/SL orders, 30 open conditional orders.
41/// - Option: 50 open orders.
42///
43/// # Rate Limit
44/// - Refer to the rate limit table. Contact client manager for increases.
45///
46/// # Risk Control
47/// - Bybit monitors API requests. Exceeding daily limits may lead to restrictions.
48///
49/// # Spot Stop Order in Different Account Types
50/// - Classic account: New order ID upon stop order trigger.
51/// - Unified account: Order ID remains unchanged upon stop order trigger.
52pub enum Action<'a> {
53    Order(OrderRequest<'a>, bool),
54    Amend(AmendOrderRequest<'a>, bool),
55    Cancel(CancelOrderRequest<'a>, bool),
56}
57
58impl Trader {
59    pub async fn place_custom_order<'b>(
60        &self,
61        req: OrderRequest<'_>,
62    ) -> Result<OrderResponse, BybitError> {
63        let action = Action::Order(req, false);
64        let parameters = Self::build_orders(action);
65
66        let request = build_json_request(&parameters);
67        let response: OrderResponse = self
68            .client
69            .post_signed(API::Trade(Trade::Place), self.recv_window, Some(request))
70            .await?;
71        Ok(response)
72    }
73
74    pub async fn place_futures_limit_order(
75        &self,
76        category: Category,
77        symbol: &str,
78        side: Side,
79        qty: f64,
80        price: f64,
81        mode: u8,
82    ) -> Result<OrderResponse, BybitError> {
83        let mut parameters: BTreeMap<String, String> = BTreeMap::new();
84        let req = OrderRequest {
85            category,
86            symbol: Cow::Borrowed(symbol),
87            side,
88            qty,
89            order_type: OrderType::Limit,
90            position_idx: Some(mode),
91            order_link_id: Some(generate_random_uid(36).into()),
92            price: Some(price),
93            ..Default::default()
94        };
95        parameters.insert("category".into(), req.category.as_str().into());
96        parameters.insert("symbol".into(), req.symbol.into_owned());
97        parameters.insert("orderType".into(), req.order_type.as_str().into());
98        parameters.insert("side".into(), req.side.as_str().into());
99        if let Some(v) = req.order_link_id {
100            parameters.insert("orderLinkId".into(), v.into());
101        }
102        parameters.insert("qty".into(), req.qty.to_string());
103        if let Some(v) = req.position_idx {
104            match v {
105                0..=2 => {
106                    parameters.insert("positionIdx".into(), v.to_string());
107                }
108                _ => return Err(BybitError::from("Invalid position index".to_string())),
109            }
110        }
111        if let Some(v) = req.price {
112            parameters.insert("price".into(), v.to_string());
113        }
114        parameters.insert("timeInForce".into(), "GTC".into());
115        let request = build_json_request(&parameters);
116        let response: OrderResponse = self
117            .client
118            .post_signed(API::Trade(Trade::Place), self.recv_window, Some(request))
119            .await?;
120        Ok(response)
121    }
122
123    pub async fn amend_order<'b>(
124        &self,
125        req: AmendOrderRequest<'_>,
126    ) -> Result<AmendOrderResponse, BybitError> {
127        let action = Action::Amend(req, false);
128        let parameters = Self::build_orders(action);
129        let request = build_json_request(&parameters);
130        let response: AmendOrderResponse = self
131            .client
132            .post_signed(API::Trade(Trade::Amend), self.recv_window, Some(request))
133            .await?;
134        Ok(response)
135    }
136    pub async fn cancel_order<'b>(
137        &self,
138        req: CancelOrderRequest<'_>,
139    ) -> Result<CancelOrderResponse, BybitError> {
140        let action = Action::Cancel(req, false);
141        let parameters = Self::build_orders(action);
142        let request = build_json_request(&parameters);
143        let response: CancelOrderResponse = self
144            .client
145            .post_signed(API::Trade(Trade::Cancel), self.recv_window, Some(request))
146            .await?;
147        Ok(response)
148    }
149    pub async fn get_open_orders<'b>(
150        &self,
151        req: OpenOrdersRequest<'_>,
152    ) -> Result<OpenOrdersResponse, BybitError> {
153        let mut parameters: BTreeMap<String, String> = BTreeMap::new();
154
155        parameters.insert("category".into(), req.category.as_str().into());
156        parameters.insert("symbol".into(), req.symbol.into());
157
158        if let Some(base_coin) = req.base_coin {
159            parameters.insert("baseCoin".into(), base_coin.into());
160        }
161        if let Some(settle_coin) = req.settle_coin {
162            parameters.insert("settleCoin".into(), settle_coin.into());
163        }
164        if let Some(order_id) = req.order_id {
165            parameters.insert("orderId".into(), order_id.into());
166        }
167        if let Some(order_link_id) = req.order_link_id {
168            parameters.insert("orderLinkId".into(), order_link_id.into());
169        }
170        if let Some(open_only) = req.open_only {
171            if matches!(open_only, 0..=2) {
172                parameters.insert("openOnly".into(), open_only.to_string());
173            }
174        }
175        if let Some(order_filter) = req.order_filter {
176            parameters.insert("orderFilter".into(), order_filter.into());
177        }
178        if let Some(limit) = req.limit {
179            parameters.insert("limit".into(), limit.to_string());
180        }
181
182        let request = build_request(&parameters);
183        let response: OpenOrdersResponse = self
184            .client
185            .get_signed(API::Trade(Trade::OpenOrders), 5000, Some(request))
186            .await?;
187
188        Ok(response)
189    }
190    pub async fn cancel_all_orders<'b>(
191        &self,
192        req: CancelAllRequest<'_>,
193    ) -> Result<CancelAllResponse, BybitError> {
194        let mut parameters: BTreeMap<String, String> = BTreeMap::new();
195        parameters.insert("category".into(), req.category.as_str().into());
196        parameters.insert("symbol".into(), req.symbol.into());
197        if let Some(base_coin) = req.base_coin {
198            parameters.insert("baseCoin".into(), base_coin.into());
199        }
200        if let Some(settle_coin) = req.settle_coin {
201            parameters.insert("settleCoin".into(), settle_coin.into());
202        }
203        if let Some(order_filter) = req.order_filter {
204            parameters.insert("orderFilter".into(), order_filter.into());
205        }
206        if let Some(stop_order_type) = req.stop_order_type {
207            parameters.insert("stopOrderType".into(), stop_order_type.into());
208        }
209        let request = build_json_request(&parameters);
210        let response: CancelAllResponse = self
211            .client
212            .post_signed(
213                API::Trade(Trade::CancelAll),
214                self.recv_window,
215                Some(request),
216            )
217            .await?;
218        Ok(response)
219    }
220
221    /// Retrieves the order history based on the given request parameters.
222    ///
223    /// # Arguments
224    /// * `req` - An instance of `OrderHistoryRequest` containing the request parameters.
225    ///
226    /// # Returns
227    /// A `Result` wrapping `OrderHistory` which contains the historical orders' data.
228    /// If the operation fails, it returns an error.
229    ///
230    pub async fn get_order_history<'b>(
231        &self,
232        req: OrderHistoryRequest<'_>,
233    ) -> Result<OrderHistoryResponse, BybitError> {
234        let mut parameters: BTreeMap<String, String> = BTreeMap::new();
235        parameters.insert("category".into(), req.category.as_str().into());
236        req.symbol
237            .map(|symbol| parameters.insert("symbol".into(), symbol.into()));
238        req.base_coin
239            .map(|base_coin| parameters.insert("baseCoin".into(), base_coin.into()));
240        req.settle_coin
241            .map(|settle_coin| parameters.insert("settleCoin".into(), settle_coin.into()));
242        req.order_id
243            .map(|order_id| parameters.insert("orderId".into(), order_id.into()));
244        req.order_link_id
245            .map(|order_link_id| parameters.insert("orderLinkId".into(), order_link_id.into()));
246        req.order_filter
247            .map(|order_filter| parameters.insert("orderFilter".into(), order_filter.into()));
248        req.order_status
249            .map(|order_status| parameters.insert("orderStatus".into(), order_status.into()));
250        req.start_time
251            .map(|start_time| parameters.insert("startTime".into(), start_time.to_string()));
252        req.end_time
253            .map(|end_time| parameters.insert("endTime".into(), end_time.to_string()));
254        req.limit
255            .map(|limit| parameters.insert("limit".into(), limit.to_string()));
256
257        let request = build_request(&parameters);
258        let response: OrderHistoryResponse = self
259            .client
260            .get_signed(API::Trade(Trade::History), self.recv_window, Some(request))
261            .await?;
262        Ok(response)
263    }
264
265    /// Retrieves the trade history for a specific trading pair, order, or time range.
266    ///
267    /// # Arguments
268    ///
269    /// * `req` - A `TradeHistoryRequest` containing the parameters for the trade history query.
270    ///
271    /// # Returns
272    ///
273    /// Returns a `Result<TradeHistoryResponse, BybitError>` containing the trade history data if the query is successful, or an error detailing the problem if the query fails.
274    pub async fn get_trade_history<'b>(
275        &self,
276        req: TradeHistoryRequest<'_>,
277    ) -> Result<TradeHistoryResponse, BybitError> {
278        // Create a new BTreeMap to store the parameters for the request
279        let mut parameters: BTreeMap<String, String> = BTreeMap::new();
280
281        // Add the category to the request parameters
282        parameters.insert("category".into(), req.category.as_str().into());
283
284        // Add the symbol to the request parameters if it is specified
285        req.symbol
286            .map(|symbol| parameters.insert("symbol".into(), symbol.into()));
287
288        // Add the order ID to the request parameters if it is specified
289        req.order_id
290            .map(|order_id| parameters.insert("orderId".into(), order_id.into()));
291
292        // Add the order link ID to the request parameters if it is specified
293        req.order_link_id
294            .map(|order_link_id| parameters.insert("orderLinkId".into(), order_link_id.into()));
295
296        // Add the base coin to the request parameters if it is specified
297        req.base_coin
298            .map(|base_coin| parameters.insert("baseCoin".into(), base_coin.into()));
299
300        // Add the start time to the request parameters if it is specified
301        req.start_time
302            .map(|start_time| parameters.insert("startTime".into(), start_time.to_string()));
303
304        // Add the end time to the request parameters if it is specified
305        req.end_time
306            .map(|end_time| parameters.insert("endTime".into(), end_time.to_string()));
307
308        // Add the limit to the request parameters if it is specified
309        req.limit
310            .map(|limit| parameters.insert("limit".into(), limit.to_string()));
311
312        // Add the execution type to the request parameters if it is specified
313        req.exec_type
314            .map(|exec_type| parameters.insert("execType".into(), exec_type.into()));
315
316        // Build the request from the parameters
317        let request = build_request(&parameters);
318
319        // Send the signed GET request to the Bybit API to retrieve the trade history
320        let response: TradeHistoryResponse = self
321            .client
322            .get_signed(
323                API::Trade(Trade::TradeHistory),
324                self.recv_window,
325                Some(request),
326            )
327            .await?;
328
329        // Return the response
330        Ok(response)
331    }
332
333    /// Asynchronously places a batch of orders using the Bybit API.
334    ///
335    /// # Arguments
336    ///
337    /// * `req` - The `BatchPlaceRequest` containing the orders to be placed.
338    ///
339    /// # Returns
340    ///
341    /// Returns a `Result` containing a `BatchPlaceResponse` on success or a `BybitError` on failure.
342    pub async fn batch_place_order<'b>(
343        &self,
344        req: BatchPlaceRequest<'_>,
345    ) -> Result<BatchPlaceResponse, BybitError> {
346        // Create a new BTreeMap to store the parameters for the request
347        let mut parameters: BTreeMap<String, Value> = BTreeMap::new();
348
349        // Check if the category is valid and insert it into the parameters
350        match req.category {
351            Category::Linear | Category::Inverse | Category::Option => {
352                parameters.insert("category".into(), req.category.as_str().into());
353            }
354            // If the category is invalid, print an error message
355            _ => {
356                error!("Invalid category");
357            }
358        }
359
360        // Create an empty array to store the orders
361        let mut requests_array: Vec<Value> = Vec::new();
362
363        // Iterate over each order in the requests and build the orders
364        for value in req.requests {
365            // Create an Action to represent the order
366            let action = Action::Order(value, true);
367
368            // Build the orders using the build_orders method
369            let order_object = Self::build_orders(action);
370
371            // Convert the order object to a JSON Value
372            let built_orders = json!(order_object);
373
374            // Add the built orders to the requests array
375            requests_array.push(built_orders);
376        }
377
378        // Insert the requests array into the parameters
379        parameters.insert("request".into(), Value::Array(requests_array));
380
381        // Build the request from the parameters
382        let request = build_json_request(&parameters);
383
384        // Send the signed POST request to the Bybit API to place the batch of orders
385        let response: BatchPlaceResponse = self
386            .client
387            .post_signed(
388                API::Trade(Trade::BatchPlace),
389                self.recv_window,
390                Some(request),
391            )
392            .await?;
393
394        // Return the response
395        Ok(response)
396    }
397
398    /// Sends a batch request to amend multiple orders at once.
399    ///
400    /// # Arguments
401    ///
402    /// * `req` - The `BatchAmendRequest` containing the orders to amend.
403    ///
404    /// # Returns
405    ///
406    /// A `Result` containing a `BatchAmendResponse` on success or a `BybitError` on failure.
407    pub async fn batch_amend_order<'b>(
408        &self,
409        req: BatchAmendRequest<'_>,
410    ) -> Result<BatchAmendResponse, BybitError> {
411        // Create an empty map to store the parameters
412        let mut parameters: BTreeMap<String, Value> = BTreeMap::new();
413
414        // Insert the category into the parameters
415        match req.category {
416            Category::Linear | Category::Inverse | Category::Option => {
417                parameters.insert("category".into(), req.category.as_str().into());
418            }
419            _ => {
420                // Print an error message if the category is invalid
421                error!("Invalid category");
422            }
423        }
424
425        // Create an empty array to store the requests
426        let mut requests_array: Vec<Value> = Vec::new();
427
428        // Iterate over each request in the BatchAmendRequest
429        for value in req.requests {
430            // Create an Action to represent the request
431            let action = Action::Amend(value, true);
432
433            // Build the orders using the build_orders method
434            let amend_object = Self::build_orders(action); // Assuming this returns the correct object structure
435
436            // Convert the amend object to a JSON Value
437            let built_amends = json!(amend_object);
438
439            // Add the built amends to the requests array
440            requests_array.push(built_amends);
441        }
442
443        // Insert the requests array into the parameters
444        parameters.insert("request".into(), Value::Array(requests_array));
445
446        // Build the request from the parameters
447        let request = build_json_request(&parameters);
448
449        // Send the signed POST request to the Bybit API to amend the batch of orders
450        let response: BatchAmendResponse = self
451            .client
452            .post_signed(
453                API::Trade(Trade::BatchAmend),
454                self.recv_window,
455                Some(request),
456            )
457            .await?;
458
459        // Return the response
460        Ok(response)
461    }
462
463    /// Cancel a batch of orders from the Bybit API
464    ///
465    /// This function will send a signed POST request to the Bybit API to cancel
466    /// a batch of orders. The request should contain the category, symbol, order_id,
467
468    /// and order_link_id for each order to be cancelled.
469    ///
470    /// # Arguments
471    ///
472    /// * `req` - A `BatchCancelRequest` containing the details of the orders to
473    /// be cancelled.
474    ///
475    /// # Returns
476    ///
477    /// A `BatchCancelResponse` containing the result of the request.
478    ///
479    /// # Errors
480    ///
481    /// Returns an error if the request fails or if the Bybit API returns an
482    /// error response.
483    pub async fn batch_cancel_order<'b>(
484        &self,
485        req: BatchCancelRequest<'_>,
486    ) -> Result<BatchCancelResponse, BybitError> {
487        let mut parameters: BTreeMap<String, Value> = BTreeMap::new();
488        match req.category {
489            Category::Linear | Category::Inverse | Category::Option => {
490                parameters.insert("category".into(), req.category.as_str().into());
491            }
492            _ => {
493                error!("Invalid category");
494            }
495        }
496        let mut requests_array: Vec<Value> = Vec::new();
497        for value in req.requests {
498            let action = Action::Cancel(value, true);
499            let cancel_object = Self::build_orders(action); // Assuming this returns the correct object structure
500            let built_cancels = json!(cancel_object);
501            requests_array.push(built_cancels);
502        }
503        parameters.insert("request".into(), Value::Array(requests_array));
504        let request = build_json_request(&parameters);
505        let response: BatchCancelResponse = self
506            .client
507            .post_signed(
508                API::Trade(Trade::BatchCancel),
509                self.recv_window,
510                Some(request),
511            )
512            .await?;
513        Ok(response)
514    }
515
516    /// Query the available balance for Spot trading and Margin trading.
517    ///
518    /// This endpoint returns information about maximum trade quantities and amounts
519    /// available for spot trading, including both actual balances and borrowable amounts.
520    /// It helps bots determine available trading capacity before placing orders.
521    ///
522    /// # Arguments
523    ///
524    /// * `req` - The borrow quota request containing symbol and side
525    ///
526    /// # Returns
527    ///
528    /// * `Ok(BorrowQuotaResponse)` - The borrow quota information
529    /// * `Err(BybitError)` - If the request fails or API returns an error
530    ///
531    /// # Notes
532    ///
533    /// - Only supports `category=spot`
534    /// - During periods of extreme market volatility, this interface may experience
535    ///   increased latency or temporary delays in data delivery
536    pub async fn get_borrow_quota_spot<'b>(
537        &self,
538        req: BorrowQuotaRequest<'_>,
539    ) -> Result<BorrowQuotaResponse, BybitError> {
540        let mut parameters: BTreeMap<String, String> = BTreeMap::new();
541
542        parameters.insert("category".into(), req.category.as_str().into());
543        parameters.insert("symbol".into(), req.symbol.into());
544        parameters.insert("side".into(), req.side.as_str().into());
545
546        let request = build_request(&parameters);
547        let response: BorrowQuotaResponse = self
548            .client
549            .get_signed(
550                API::Trade(Trade::SpotBorrowCheck),
551                self.recv_window,
552                Some(request),
553            )
554            .await?;
555        Ok(response)
556    }
557
558    /// Configure Disconnection Protection (DCP) parameters.
559    ///
560    /// DCP automatically cancels all active orders if the client remains disconnected
561    /// from Bybit's WebSocket for longer than the specified time window.
562    /// This helps prevent unintended order execution during connection issues.
563    ///
564    /// # Arguments
565    ///
566    /// * `req` - The DCP configuration request containing time window and product type
567    ///
568    /// # Returns
569    ///
570    /// * `Ok(DcpResponse)` - Confirmation that DCP was configured successfully
571    /// * `Err(BybitError)` - If the request fails or API returns an error
572    ///
573    /// # Notes
574    ///
575    /// - After the request is successfully sent, the system needs a certain time to take effect
576    /// - It is recommended to query or set again after 10 seconds
577    /// - Your private websocket connection **must** subscribe to "dcp" topic to trigger DCP successfully
578    /// - Valid time window range: 3 to 300 seconds
579    /// - Default product is "OPTIONS" if not specified
580    pub async fn set_dcp_options<'b>(
581        &self,
582        req: DcpRequest<'_>,
583    ) -> Result<DcpResponse, BybitError> {
584        // Validate the request parameters
585        if let Err(e) = req.validate() {
586            return Err(BybitError::from(e));
587        }
588
589        let mut parameters: BTreeMap<String, Value> = BTreeMap::new();
590
591        // Add timeWindow parameter (required)
592        parameters.insert("timeWindow".into(), req.time_window.into());
593
594        // Add product parameter (optional)
595        if let Some(product) = req.product {
596            parameters.insert("product".into(), product.into());
597        }
598
599        let request = build_json_request(&parameters);
600        let response: DcpResponse = self
601            .client
602            .post_signed(
603                API::Trade(Trade::SetDisconnectCancelall),
604                self.recv_window,
605                Some(request),
606            )
607            .await?;
608        Ok(response)
609    }
610
611    /// Pre-check an order to calculate margin impact before placement.
612    ///
613    /// This endpoint calculates the changes in Initial Margin Requirement (IMR) and
614    /// Maintenance Margin Requirement (MMR) before and after placing an order.
615    /// It supports categories: `inverse`, `linear`, and `option`.
616    ///
617    /// # Arguments
618    ///
619    /// * `req` - The order request to pre-check
620    ///
621    /// # Returns
622    ///
623    /// * `Ok(PreCheckOrderResponse)` - The pre-check results with margin calculations
624    /// * `Err(BybitError)` - If the request fails or API returns an error
625    ///
626    /// # Notes
627    ///
628    /// - Only Cross Margin mode and Portfolio Margin mode are supported
629    /// - Isolated margin mode is not supported
630    /// - Conditional orders are not supported
631    /// - For `inverse` category, Cross Margin mode is not supported
632    pub async fn pre_check_order<'b>(
633        &self,
634        req: OrderRequest<'_>,
635    ) -> Result<PreCheckOrderResponse, BybitError> {
636        let action = Action::Order(req, false);
637        let parameters = Self::build_orders(action);
638
639        let request = build_json_request(&parameters);
640        let response: PreCheckOrderResponse = self
641            .client
642            .post_signed(API::Trade(Trade::PreCheck), self.recv_window, Some(request))
643            .await?;
644        Ok(response)
645    }
646
647    pub fn build_orders<'b>(action: Action<'_>) -> BTreeMap<String, Value> {
648        let mut parameters: BTreeMap<String, Value> = BTreeMap::new();
649        match action {
650            Action::Order(req, batch) => {
651                if !batch {
652                    parameters.insert("category".into(), req.category.as_str().into());
653                }
654                parameters.insert("symbol".into(), req.symbol.into());
655                if let Some(leverage) = req.is_leverage {
656                    if leverage {
657                        // Whether to borrow. Valid for Unified spot only. 0(default): false then spot trading, 1: true then margin trading
658                        parameters.insert("leverage".into(), 1.into());
659                    }
660                }
661                parameters.insert("side".into(), req.side.as_str().into());
662                parameters.insert("orderType".into(), req.order_type.as_str().into());
663
664                parameters.insert("qty".into(), req.qty.to_string().into());
665                if let Some(market_unit) = req.market_unit {
666                    parameters.insert("marketUnit".into(), market_unit.to_string().into());
667                }
668                if let Some(price) = req.price {
669                    parameters.insert("price".into(), price.to_string().into());
670                }
671                if let Some(trigger_direction) = req.trigger_direction {
672                    if trigger_direction {
673                        parameters.insert("triggerDirection".into(), 1.into());
674                    } else {
675                        parameters.insert("triggerDirection".into(), 2.into());
676                    }
677                }
678                if let Some(order_filter) = req.order_filter {
679                    parameters.insert("orderFilter".into(), order_filter.into());
680                }
681                if let Some(trigger_price) = req.trigger_price {
682                    parameters.insert("triggerPrice".into(), trigger_price.to_string().into());
683                }
684                if let Some(trigger) = req.trigger_by {
685                    parameters.insert("triggerBy".into(), trigger.into());
686                }
687                if let Some(iv) = req.order_iv {
688                    parameters.insert("orderIv".into(), iv.to_string().into());
689                }
690                if let Some(time_in_force) = req.time_in_force {
691                    parameters.insert("timeInForce".into(), time_in_force.into());
692                }
693                if let Some(v) = req.position_idx {
694                    match v {
695                        0..=2 => {
696                            parameters.insert("positionIdx".into(), v.to_string().into());
697                        }
698                        _ => error!("Invalid position idx"),
699                    }
700                }
701                if let Some(order_link_id) = req.order_link_id {
702                    parameters.insert("orderLinkId".into(), order_link_id.into());
703                } else {
704                    let uuid = generate_random_uid(36);
705                    parameters.insert("orderLinkId".into(), uuid.into());
706                }
707                if let Some(price) = req.take_profit {
708                    parameters.insert("takeProfit".into(), price.to_string().into());
709                }
710                if let Some(price) = req.stop_loss {
711                    parameters.insert("stopLoss".into(), price.to_string().into());
712                }
713                if let Some(kind) = req.tp_trigger_by {
714                    parameters.insert("tpTriggerBy".into(), kind.into());
715                }
716                if let Some(kind) = req.sl_trigger_by {
717                    parameters.insert("slTriggerBy".into(), kind.into());
718                }
719                if let Some(reduce) = req.reduce_only {
720                    parameters.insert("reduceOnly".into(), reduce.into());
721                }
722                if let Some(close) = req.close_on_trigger {
723                    parameters.insert("closeOnTrigger".into(), close.into());
724                }
725                if let Some(v) = req.mmp {
726                    parameters.insert("mmp".into(), v.into());
727                }
728                if let Some(v) = req.tpsl_mode {
729                    parameters.insert("tpslMode".into(), v.into());
730                }
731                if let Some(v) = req.tp_limit_price {
732                    parameters.insert("tpTriggerPrice".into(), v.to_string().into());
733                }
734                if let Some(v) = req.sl_limit_price {
735                    parameters.insert("slTriggerPrice".into(), v.to_string().into());
736                }
737                if let Some(v) = req.tp_order_type {
738                    parameters.insert("tpOrderType".into(), v.into());
739                }
740                if let Some(v) = req.sl_order_type {
741                    parameters.insert("slOrderType".into(), v.into());
742                }
743                if let Some(v) = req.slippage_tolerance_type {
744                    parameters.insert("slippageToleranceType".into(), v.into());
745                }
746                if let Some(v) = req.slippage_tolerance {
747                    parameters.insert("slippageTolerance".into(), v.to_string().into());
748                }
749                if let Some(v) = req.bbo_side_type {
750                    parameters.insert("bboSideType".into(), v.into());
751                }
752                if let Some(v) = req.bbo_level {
753                    parameters.insert("bboLevel".into(), v.to_string().into());
754                }
755            }
756            Action::Amend(req, batch) => {
757                if !batch {
758                    parameters.insert("category".into(), req.category.as_str().into());
759                }
760                parameters.insert("symbol".into(), req.symbol.into());
761                if let Some(v) = req.order_id {
762                    parameters.insert("orderId".into(), v.into());
763                }
764                if let Some(v) = req.order_link_id {
765                    parameters.insert("orderLinkId".into(), v.into());
766                }
767                if let Some(v) = req.order_iv {
768                    parameters.insert("orderIv".into(), v.to_string().into());
769                }
770                if let Some(v) = req.trigger_price {
771                    parameters.insert("triggerPrice".into(), v.to_string().into());
772                }
773                parameters.insert("qty".into(), req.qty.into());
774                if let Some(v) = req.price {
775                    parameters.insert("price".into(), v.to_string().into());
776                }
777                if let Some(v) = req.tpsl_mode {
778                    parameters.insert("tpslMode".into(), v.into());
779                }
780                if let Some(v) = req.take_profit {
781                    parameters.insert("takeProfit".into(), v.to_string().into());
782                }
783                if let Some(v) = req.stop_loss {
784                    parameters.insert("stopLoss".into(), v.to_string().into());
785                }
786                if let Some(v) = req.tp_trigger_by {
787                    parameters.insert("tpTriggerBy".into(), v.into());
788                }
789                if let Some(v) = req.sl_trigger_by {
790                    parameters.insert("slTriggerBy".into(), v.into());
791                }
792                if let Some(v) = req.trigger_by {
793                    parameters.insert("triggerBy".into(), v.into());
794                }
795                if let Some(v) = req.tp_limit_price {
796                    parameters.insert("tpLimitPrice".into(), v.to_string().into());
797                }
798                if let Some(v) = req.sl_limit_price {
799                    parameters.insert("slLimitPrice".into(), v.to_string().into());
800                }
801            }
802            Action::Cancel(req, batch) => {
803                if !batch {
804                    parameters.insert("category".into(), req.category.as_str().into());
805                }
806                parameters.insert("symbol".into(), req.symbol.into());
807                if let Some(v) = req.order_id {
808                    parameters.insert("orderId".into(), v.into());
809                }
810                if let Some(v) = req.order_link_id {
811                    parameters.insert("orderLinkId".into(), v.into());
812                }
813                if let Some(v) = req.order_filter {
814                    parameters.insert("orderFilter".into(), v.into());
815                }
816            }
817        }
818        parameters
819    }
820}
821
822pub fn build_ws_orders<'a>(orders: RequestType) -> Value {
823    let mut order_array = Vec::new();
824    match orders {
825        RequestType::Create(req) => {
826            let action = Action::Order(req, false);
827            let order_object = Trader::build_orders(action); // Assuming this returns the correct object structure
828            let built_order = json!(order_object);
829            order_array.push(built_order);
830            Value::Array(order_array)
831        }
832        RequestType::CreateBatch(req) => {
833            for v in req.requests {
834                let action = Action::Order(v, true);
835                let order_object = Trader::build_orders(action); // Assuming this returns the correct object structure
836                let built_order = json!(order_object);
837                order_array.push(built_order);
838            }
839            Value::Array(order_array)
840        }
841        RequestType::Amend(req) => {
842            let action = Action::Amend(req, false);
843            let order_object = Trader::build_orders(action); // Assuming this returns the correct object structure
844            let built_order = json!(order_object);
845            order_array.push(built_order);
846            Value::Array(order_array)
847        }
848        RequestType::AmendBatch(req) => {
849            for v in req.requests {
850                let action = Action::Amend(v, true);
851                let order_object = Trader::build_orders(action); // Assuming this returns the correct object structure
852                let built_order = json!(order_object);
853                order_array.push(built_order);
854            }
855            Value::Array(order_array)
856        }
857        RequestType::Cancel(req) => {
858            let action = Action::Cancel(req, false);
859            let order_object = Trader::build_orders(action); // Assuming this returns the correct object structure
860            let built_order = json!(order_object);
861            order_array.push(built_order);
862            Value::Array(order_array)
863        }
864        RequestType::CancelBatch(req) => {
865            for v in req.requests {
866                let action = Action::Cancel(v, true);
867                let order_object = Trader::build_orders(action); // Assuming this returns the correct object structure
868                let built_order = json!(order_object);
869                order_array.push(built_order);
870            }
871            Value::Array(order_array)
872        }
873    }
874}