Skip to main content

nautilus_binance/spot/http/
query.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2026 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16//! Query parameter builders for Binance Spot HTTP requests.
17
18use serde::Serialize;
19
20use crate::{
21    common::enums::{BinanceSelfTradePreventionMode, BinanceSide, BinanceTimeInForce},
22    spot::enums::{BinanceCancelReplaceMode, BinanceOrderResponseType, BinanceSpotOrderType},
23};
24
25/// Query parameters for the depth endpoint.
26#[derive(Debug, Clone, Serialize)]
27pub struct DepthParams {
28    /// Trading pair symbol (e.g., "BTCUSDT").
29    pub symbol: String,
30    /// Number of price levels to return (default 100, max 5000).
31    #[serde(skip_serializing_if = "Option::is_none")]
32    pub limit: Option<u32>,
33}
34
35impl DepthParams {
36    /// Create new depth query params.
37    #[must_use]
38    pub fn new(symbol: impl Into<String>) -> Self {
39        Self {
40            symbol: symbol.into(),
41            limit: None,
42        }
43    }
44
45    /// Set the limit.
46    #[must_use]
47    pub fn with_limit(mut self, limit: u32) -> Self {
48        self.limit = Some(limit);
49        self
50    }
51}
52
53/// Query parameters for the trades endpoint.
54#[derive(Debug, Clone, Serialize)]
55pub struct TradesParams {
56    /// Trading pair symbol (e.g., "BTCUSDT").
57    pub symbol: String,
58    /// Number of trades to return (default 500, max 1000).
59    #[serde(skip_serializing_if = "Option::is_none")]
60    pub limit: Option<u32>,
61}
62
63impl TradesParams {
64    /// Create new trades query params.
65    #[must_use]
66    pub fn new(symbol: impl Into<String>) -> Self {
67        Self {
68            symbol: symbol.into(),
69            limit: None,
70        }
71    }
72
73    /// Set the limit.
74    #[must_use]
75    pub fn with_limit(mut self, limit: u32) -> Self {
76        self.limit = Some(limit);
77        self
78    }
79}
80
81/// Query parameters for new order submission.
82#[derive(Debug, Clone, Serialize)]
83pub struct NewOrderParams {
84    /// Trading pair symbol.
85    pub symbol: String,
86    /// Order side (BUY or SELL).
87    pub side: BinanceSide,
88    /// Order type.
89    #[serde(rename = "type")]
90    pub order_type: BinanceSpotOrderType,
91    /// Time in force.
92    #[serde(skip_serializing_if = "Option::is_none", rename = "timeInForce")]
93    pub time_in_force: Option<BinanceTimeInForce>,
94    /// Order quantity.
95    #[serde(skip_serializing_if = "Option::is_none")]
96    pub quantity: Option<String>,
97    /// Quote order quantity (for market orders).
98    #[serde(skip_serializing_if = "Option::is_none", rename = "quoteOrderQty")]
99    pub quote_order_qty: Option<String>,
100    /// Limit price.
101    #[serde(skip_serializing_if = "Option::is_none")]
102    pub price: Option<String>,
103    /// Client order ID.
104    #[serde(skip_serializing_if = "Option::is_none", rename = "newClientOrderId")]
105    pub new_client_order_id: Option<String>,
106    /// Stop price for stop orders.
107    #[serde(skip_serializing_if = "Option::is_none", rename = "stopPrice")]
108    pub stop_price: Option<String>,
109    /// Trailing delta for trailing stop orders.
110    #[serde(skip_serializing_if = "Option::is_none", rename = "trailingDelta")]
111    pub trailing_delta: Option<i64>,
112    /// Iceberg quantity.
113    #[serde(skip_serializing_if = "Option::is_none", rename = "icebergQty")]
114    pub iceberg_qty: Option<String>,
115    /// Response type (ACK, RESULT, or FULL).
116    #[serde(skip_serializing_if = "Option::is_none", rename = "newOrderRespType")]
117    pub new_order_resp_type: Option<BinanceOrderResponseType>,
118    /// Self-trade prevention mode.
119    #[serde(
120        skip_serializing_if = "Option::is_none",
121        rename = "selfTradePreventionMode"
122    )]
123    pub self_trade_prevention_mode: Option<BinanceSelfTradePreventionMode>,
124    /// Strategy ID for order tracking.
125    #[serde(skip_serializing_if = "Option::is_none", rename = "strategyId")]
126    pub strategy_id: Option<i64>,
127    /// Strategy type for order tracking.
128    #[serde(skip_serializing_if = "Option::is_none", rename = "strategyType")]
129    pub strategy_type: Option<i64>,
130}
131
132impl NewOrderParams {
133    /// Create new order params for a limit order.
134    #[must_use]
135    pub fn limit(
136        symbol: impl Into<String>,
137        side: BinanceSide,
138        quantity: impl Into<String>,
139        price: impl Into<String>,
140    ) -> Self {
141        Self {
142            symbol: symbol.into(),
143            side,
144            order_type: BinanceSpotOrderType::Limit,
145            time_in_force: Some(BinanceTimeInForce::Gtc),
146            quantity: Some(quantity.into()),
147            quote_order_qty: None,
148            price: Some(price.into()),
149            new_client_order_id: None,
150            stop_price: None,
151            trailing_delta: None,
152            iceberg_qty: None,
153            new_order_resp_type: Some(BinanceOrderResponseType::Full),
154            self_trade_prevention_mode: None,
155            strategy_id: None,
156            strategy_type: None,
157        }
158    }
159
160    /// Create new order params for a market order.
161    #[must_use]
162    pub fn market(
163        symbol: impl Into<String>,
164        side: BinanceSide,
165        quantity: impl Into<String>,
166    ) -> Self {
167        Self {
168            symbol: symbol.into(),
169            side,
170            order_type: BinanceSpotOrderType::Market,
171            time_in_force: None,
172            quantity: Some(quantity.into()),
173            quote_order_qty: None,
174            price: None,
175            new_client_order_id: None,
176            stop_price: None,
177            trailing_delta: None,
178            iceberg_qty: None,
179            new_order_resp_type: Some(BinanceOrderResponseType::Full),
180            self_trade_prevention_mode: None,
181            strategy_id: None,
182            strategy_type: None,
183        }
184    }
185
186    /// Set the client order ID.
187    #[must_use]
188    pub fn with_client_order_id(mut self, id: impl Into<String>) -> Self {
189        self.new_client_order_id = Some(id.into());
190        self
191    }
192
193    /// Set the time in force.
194    #[must_use]
195    pub fn with_time_in_force(mut self, tif: BinanceTimeInForce) -> Self {
196        self.time_in_force = Some(tif);
197        self
198    }
199
200    /// Set the stop price.
201    #[must_use]
202    pub fn with_stop_price(mut self, price: impl Into<String>) -> Self {
203        self.stop_price = Some(price.into());
204        self
205    }
206
207    /// Set the self-trade prevention mode.
208    #[must_use]
209    pub fn with_stp_mode(mut self, mode: BinanceSelfTradePreventionMode) -> Self {
210        self.self_trade_prevention_mode = Some(mode);
211        self
212    }
213}
214
215/// Query parameters for canceling an order.
216#[derive(Debug, Clone, Serialize)]
217pub struct CancelOrderParams {
218    /// Trading pair symbol.
219    pub symbol: String,
220    /// Order ID to cancel.
221    #[serde(skip_serializing_if = "Option::is_none", rename = "orderId")]
222    pub order_id: Option<i64>,
223    /// Original client order ID.
224    #[serde(skip_serializing_if = "Option::is_none", rename = "origClientOrderId")]
225    pub orig_client_order_id: Option<String>,
226    /// New client order ID for the cancel request.
227    #[serde(skip_serializing_if = "Option::is_none", rename = "newClientOrderId")]
228    pub new_client_order_id: Option<String>,
229}
230
231impl CancelOrderParams {
232    /// Create cancel params by order ID.
233    #[must_use]
234    pub fn by_order_id(symbol: impl Into<String>, order_id: i64) -> Self {
235        Self {
236            symbol: symbol.into(),
237            order_id: Some(order_id),
238            orig_client_order_id: None,
239            new_client_order_id: None,
240        }
241    }
242
243    /// Create cancel params by client order ID.
244    #[must_use]
245    pub fn by_client_order_id(
246        symbol: impl Into<String>,
247        client_order_id: impl Into<String>,
248    ) -> Self {
249        Self {
250            symbol: symbol.into(),
251            order_id: None,
252            orig_client_order_id: Some(client_order_id.into()),
253            new_client_order_id: None,
254        }
255    }
256}
257
258/// Query parameters for canceling all open orders on a symbol.
259#[derive(Debug, Clone, Serialize)]
260pub struct CancelOpenOrdersParams {
261    /// Trading pair symbol.
262    pub symbol: String,
263}
264
265impl CancelOpenOrdersParams {
266    /// Create new cancel open orders params.
267    #[must_use]
268    pub fn new(symbol: impl Into<String>) -> Self {
269        Self {
270            symbol: symbol.into(),
271        }
272    }
273}
274
275/// Query parameters for cancel and replace order.
276#[derive(Debug, Clone, Serialize)]
277pub struct CancelReplaceOrderParams {
278    /// Trading pair symbol.
279    pub symbol: String,
280    /// Order side.
281    pub side: BinanceSide,
282    /// Order type.
283    #[serde(rename = "type")]
284    pub order_type: BinanceSpotOrderType,
285    /// Cancel/replace mode.
286    #[serde(rename = "cancelReplaceMode")]
287    pub cancel_replace_mode: BinanceCancelReplaceMode,
288    /// Time in force.
289    #[serde(skip_serializing_if = "Option::is_none", rename = "timeInForce")]
290    pub time_in_force: Option<BinanceTimeInForce>,
291    /// Order quantity.
292    #[serde(skip_serializing_if = "Option::is_none")]
293    pub quantity: Option<String>,
294    /// Quote order quantity.
295    #[serde(skip_serializing_if = "Option::is_none", rename = "quoteOrderQty")]
296    pub quote_order_qty: Option<String>,
297    /// Limit price.
298    #[serde(skip_serializing_if = "Option::is_none")]
299    pub price: Option<String>,
300    /// Order ID to cancel.
301    #[serde(skip_serializing_if = "Option::is_none", rename = "cancelOrderId")]
302    pub cancel_order_id: Option<i64>,
303    /// Client order ID to cancel.
304    #[serde(
305        skip_serializing_if = "Option::is_none",
306        rename = "cancelOrigClientOrderId"
307    )]
308    pub cancel_orig_client_order_id: Option<String>,
309    /// New client order ID.
310    #[serde(skip_serializing_if = "Option::is_none", rename = "newClientOrderId")]
311    pub new_client_order_id: Option<String>,
312    /// Stop price.
313    #[serde(skip_serializing_if = "Option::is_none", rename = "stopPrice")]
314    pub stop_price: Option<String>,
315    /// Trailing delta.
316    #[serde(skip_serializing_if = "Option::is_none", rename = "trailingDelta")]
317    pub trailing_delta: Option<i64>,
318    /// Iceberg quantity.
319    #[serde(skip_serializing_if = "Option::is_none", rename = "icebergQty")]
320    pub iceberg_qty: Option<String>,
321    /// Response type.
322    #[serde(skip_serializing_if = "Option::is_none", rename = "newOrderRespType")]
323    pub new_order_resp_type: Option<BinanceOrderResponseType>,
324    /// Self-trade prevention mode.
325    #[serde(
326        skip_serializing_if = "Option::is_none",
327        rename = "selfTradePreventionMode"
328    )]
329    pub self_trade_prevention_mode: Option<BinanceSelfTradePreventionMode>,
330}
331
332/// Query parameters for querying a single order.
333#[derive(Debug, Clone, Serialize)]
334pub struct QueryOrderParams {
335    /// Trading pair symbol.
336    pub symbol: String,
337    /// Order ID.
338    #[serde(skip_serializing_if = "Option::is_none", rename = "orderId")]
339    pub order_id: Option<i64>,
340    /// Original client order ID.
341    #[serde(skip_serializing_if = "Option::is_none", rename = "origClientOrderId")]
342    pub orig_client_order_id: Option<String>,
343}
344
345impl QueryOrderParams {
346    /// Create query params by order ID.
347    #[must_use]
348    pub fn by_order_id(symbol: impl Into<String>, order_id: i64) -> Self {
349        Self {
350            symbol: symbol.into(),
351            order_id: Some(order_id),
352            orig_client_order_id: None,
353        }
354    }
355
356    /// Create query params by client order ID.
357    #[must_use]
358    pub fn by_client_order_id(
359        symbol: impl Into<String>,
360        client_order_id: impl Into<String>,
361    ) -> Self {
362        Self {
363            symbol: symbol.into(),
364            order_id: None,
365            orig_client_order_id: Some(client_order_id.into()),
366        }
367    }
368}
369
370/// Query parameters for querying open orders.
371#[derive(Debug, Clone, Default, Serialize)]
372pub struct OpenOrdersParams {
373    /// Trading pair symbol (optional, omit for all symbols).
374    #[serde(skip_serializing_if = "Option::is_none")]
375    pub symbol: Option<String>,
376}
377
378impl OpenOrdersParams {
379    /// Create new open orders params for all symbols.
380    #[must_use]
381    pub fn all() -> Self {
382        Self { symbol: None }
383    }
384
385    /// Create new open orders params for a specific symbol.
386    #[must_use]
387    pub fn for_symbol(symbol: impl Into<String>) -> Self {
388        Self {
389            symbol: Some(symbol.into()),
390        }
391    }
392}
393
394/// Query parameters for querying all orders (includes filled/canceled).
395#[derive(Debug, Clone, Serialize)]
396pub struct AllOrdersParams {
397    /// Trading pair symbol.
398    pub symbol: String,
399    /// Filter by order ID (returns orders >= this ID).
400    #[serde(skip_serializing_if = "Option::is_none", rename = "orderId")]
401    pub order_id: Option<i64>,
402    /// Filter by start time.
403    #[serde(skip_serializing_if = "Option::is_none", rename = "startTime")]
404    pub start_time: Option<i64>,
405    /// Filter by end time.
406    #[serde(skip_serializing_if = "Option::is_none", rename = "endTime")]
407    pub end_time: Option<i64>,
408    /// Maximum number of orders to return (default 500, max 1000).
409    #[serde(skip_serializing_if = "Option::is_none")]
410    pub limit: Option<u32>,
411}
412
413impl AllOrdersParams {
414    /// Create new all orders params.
415    #[must_use]
416    pub fn new(symbol: impl Into<String>) -> Self {
417        Self {
418            symbol: symbol.into(),
419            order_id: None,
420            start_time: None,
421            end_time: None,
422            limit: None,
423        }
424    }
425
426    /// Set the limit.
427    #[must_use]
428    pub fn with_limit(mut self, limit: u32) -> Self {
429        self.limit = Some(limit);
430        self
431    }
432
433    /// Set the time range.
434    #[must_use]
435    pub fn with_time_range(mut self, start: i64, end: i64) -> Self {
436        self.start_time = Some(start);
437        self.end_time = Some(end);
438        self
439    }
440}
441
442/// Query parameters for new OCO order.
443#[derive(Debug, Clone, Serialize)]
444pub struct NewOcoOrderParams {
445    /// Trading pair symbol.
446    pub symbol: String,
447    /// Order side.
448    pub side: BinanceSide,
449    /// Order quantity.
450    pub quantity: String,
451    /// Limit price (above-market for sell, below-market for buy).
452    pub price: String,
453    /// Stop price trigger.
454    #[serde(rename = "stopPrice")]
455    pub stop_price: String,
456    /// Stop limit price (optional, creates stop-limit if provided).
457    #[serde(skip_serializing_if = "Option::is_none", rename = "stopLimitPrice")]
458    pub stop_limit_price: Option<String>,
459    /// Client order ID for the entire list.
460    #[serde(skip_serializing_if = "Option::is_none", rename = "listClientOrderId")]
461    pub list_client_order_id: Option<String>,
462    /// Client order ID for the limit order.
463    #[serde(skip_serializing_if = "Option::is_none", rename = "limitClientOrderId")]
464    pub limit_client_order_id: Option<String>,
465    /// Client order ID for the stop order.
466    #[serde(skip_serializing_if = "Option::is_none", rename = "stopClientOrderId")]
467    pub stop_client_order_id: Option<String>,
468    /// Iceberg quantity for the limit leg.
469    #[serde(skip_serializing_if = "Option::is_none", rename = "limitIcebergQty")]
470    pub limit_iceberg_qty: Option<String>,
471    /// Iceberg quantity for the stop leg.
472    #[serde(skip_serializing_if = "Option::is_none", rename = "stopIcebergQty")]
473    pub stop_iceberg_qty: Option<String>,
474    /// Time in force for the stop-limit leg.
475    #[serde(
476        skip_serializing_if = "Option::is_none",
477        rename = "stopLimitTimeInForce"
478    )]
479    pub stop_limit_time_in_force: Option<BinanceTimeInForce>,
480    /// Response type.
481    #[serde(skip_serializing_if = "Option::is_none", rename = "newOrderRespType")]
482    pub new_order_resp_type: Option<BinanceOrderResponseType>,
483    /// Self-trade prevention mode.
484    #[serde(
485        skip_serializing_if = "Option::is_none",
486        rename = "selfTradePreventionMode"
487    )]
488    pub self_trade_prevention_mode: Option<BinanceSelfTradePreventionMode>,
489}
490
491impl NewOcoOrderParams {
492    /// Create new OCO order params.
493    #[must_use]
494    pub fn new(
495        symbol: impl Into<String>,
496        side: BinanceSide,
497        quantity: impl Into<String>,
498        price: impl Into<String>,
499        stop_price: impl Into<String>,
500    ) -> Self {
501        Self {
502            symbol: symbol.into(),
503            side,
504            quantity: quantity.into(),
505            price: price.into(),
506            stop_price: stop_price.into(),
507            stop_limit_price: None,
508            list_client_order_id: None,
509            limit_client_order_id: None,
510            stop_client_order_id: None,
511            limit_iceberg_qty: None,
512            stop_iceberg_qty: None,
513            stop_limit_time_in_force: None,
514            new_order_resp_type: Some(BinanceOrderResponseType::Full),
515            self_trade_prevention_mode: None,
516        }
517    }
518
519    /// Set stop limit price (makes stop leg a stop-limit order).
520    #[must_use]
521    pub fn with_stop_limit_price(mut self, price: impl Into<String>) -> Self {
522        self.stop_limit_price = Some(price.into());
523        self.stop_limit_time_in_force = Some(BinanceTimeInForce::Gtc);
524        self
525    }
526}
527
528/// Query parameters for new OCO order list.
529#[derive(Debug, Clone, Serialize)]
530#[serde(rename_all = "camelCase")]
531pub struct NewOcoOrderListParams {
532    /// Trading pair symbol.
533    pub symbol: String,
534    /// Client order ID for the entire list.
535    #[serde(skip_serializing_if = "Option::is_none")]
536    pub list_client_order_id: Option<String>,
537    /// Order side.
538    pub side: BinanceSide,
539    /// Quantity for both legs.
540    pub quantity: String,
541    /// Above leg order type.
542    pub above_type: BinanceSpotOrderType,
543    /// Client order ID for the above leg.
544    #[serde(skip_serializing_if = "Option::is_none")]
545    pub above_client_order_id: Option<String>,
546    /// Iceberg quantity for the above leg.
547    #[serde(skip_serializing_if = "Option::is_none")]
548    pub above_iceberg_qty: Option<String>,
549    /// Limit price for the above leg.
550    #[serde(skip_serializing_if = "Option::is_none")]
551    pub above_price: Option<String>,
552    /// Stop price for the above leg.
553    #[serde(skip_serializing_if = "Option::is_none")]
554    pub above_stop_price: Option<String>,
555    /// Time in force for the above leg.
556    #[serde(skip_serializing_if = "Option::is_none")]
557    pub above_time_in_force: Option<BinanceTimeInForce>,
558    /// Below leg order type.
559    pub below_type: BinanceSpotOrderType,
560    /// Client order ID for the below leg.
561    #[serde(skip_serializing_if = "Option::is_none")]
562    pub below_client_order_id: Option<String>,
563    /// Iceberg quantity for the below leg.
564    #[serde(skip_serializing_if = "Option::is_none")]
565    pub below_iceberg_qty: Option<String>,
566    /// Limit price for the below leg.
567    #[serde(skip_serializing_if = "Option::is_none")]
568    pub below_price: Option<String>,
569    /// Stop price for the below leg.
570    #[serde(skip_serializing_if = "Option::is_none")]
571    pub below_stop_price: Option<String>,
572    /// Time in force for the below leg.
573    #[serde(skip_serializing_if = "Option::is_none")]
574    pub below_time_in_force: Option<BinanceTimeInForce>,
575    /// Response type.
576    #[serde(skip_serializing_if = "Option::is_none")]
577    pub new_order_resp_type: Option<BinanceOrderResponseType>,
578    /// Self-trade prevention mode.
579    #[serde(skip_serializing_if = "Option::is_none")]
580    pub self_trade_prevention_mode: Option<BinanceSelfTradePreventionMode>,
581}
582
583/// Query parameters for canceling an order list (OCO).
584#[derive(Debug, Clone, Serialize)]
585pub struct CancelOrderListParams {
586    /// Trading pair symbol.
587    pub symbol: String,
588    /// Order list ID.
589    #[serde(skip_serializing_if = "Option::is_none", rename = "orderListId")]
590    pub order_list_id: Option<i64>,
591    /// List client order ID.
592    #[serde(skip_serializing_if = "Option::is_none", rename = "listClientOrderId")]
593    pub list_client_order_id: Option<String>,
594    /// New client order ID for the cancel request.
595    #[serde(skip_serializing_if = "Option::is_none", rename = "newClientOrderId")]
596    pub new_client_order_id: Option<String>,
597}
598
599impl CancelOrderListParams {
600    /// Create cancel params by order list ID.
601    #[must_use]
602    pub fn by_order_list_id(symbol: impl Into<String>, order_list_id: i64) -> Self {
603        Self {
604            symbol: symbol.into(),
605            order_list_id: Some(order_list_id),
606            list_client_order_id: None,
607            new_client_order_id: None,
608        }
609    }
610
611    /// Create cancel params by list client order ID.
612    #[must_use]
613    pub fn by_list_client_order_id(
614        symbol: impl Into<String>,
615        list_client_order_id: impl Into<String>,
616    ) -> Self {
617        Self {
618            symbol: symbol.into(),
619            order_list_id: None,
620            list_client_order_id: Some(list_client_order_id.into()),
621            new_client_order_id: None,
622        }
623    }
624}
625
626/// Query parameters for querying an order list (OCO).
627#[derive(Debug, Clone, Serialize)]
628pub struct QueryOrderListParams {
629    /// Order list ID.
630    #[serde(skip_serializing_if = "Option::is_none", rename = "orderListId")]
631    pub order_list_id: Option<i64>,
632    /// List client order ID.
633    #[serde(skip_serializing_if = "Option::is_none", rename = "origClientOrderId")]
634    pub orig_client_order_id: Option<String>,
635}
636
637impl QueryOrderListParams {
638    /// Create query params by order list ID.
639    #[must_use]
640    pub fn by_order_list_id(order_list_id: i64) -> Self {
641        Self {
642            order_list_id: Some(order_list_id),
643            orig_client_order_id: None,
644        }
645    }
646
647    /// Create query params by list client order ID.
648    #[must_use]
649    pub fn by_client_order_id(client_order_id: impl Into<String>) -> Self {
650        Self {
651            order_list_id: None,
652            orig_client_order_id: Some(client_order_id.into()),
653        }
654    }
655}
656
657/// Query parameters for querying all order lists (OCOs).
658#[derive(Debug, Clone, Default, Serialize)]
659pub struct AllOrderListsParams {
660    /// Filter by start time.
661    #[serde(skip_serializing_if = "Option::is_none", rename = "startTime")]
662    pub start_time: Option<i64>,
663    /// Filter by end time.
664    #[serde(skip_serializing_if = "Option::is_none", rename = "endTime")]
665    pub end_time: Option<i64>,
666    /// Maximum number of results (default 500, max 1000).
667    #[serde(skip_serializing_if = "Option::is_none")]
668    pub limit: Option<u32>,
669}
670
671/// Query parameters for querying open order lists (OCOs).
672#[derive(Debug, Clone, Default, Serialize)]
673pub struct OpenOrderListsParams {}
674
675/// Query parameters for account information.
676#[derive(Debug, Clone, Default, Serialize)]
677pub struct AccountInfoParams {
678    /// Omit zero balances from response.
679    #[serde(skip_serializing_if = "Option::is_none", rename = "omitZeroBalances")]
680    pub omit_zero_balances: Option<bool>,
681}
682
683impl AccountInfoParams {
684    /// Create new account info params.
685    #[must_use]
686    pub fn new() -> Self {
687        Self::default()
688    }
689
690    /// Omit zero balances from response.
691    #[must_use]
692    pub fn omit_zero_balances(mut self) -> Self {
693        self.omit_zero_balances = Some(true);
694        self
695    }
696}
697
698/// Query parameters for account trades.
699#[derive(Debug, Clone, Serialize)]
700pub struct AccountTradesParams {
701    /// Trading pair symbol.
702    pub symbol: String,
703    /// Filter by order ID.
704    #[serde(skip_serializing_if = "Option::is_none", rename = "orderId")]
705    pub order_id: Option<i64>,
706    /// Filter by start time.
707    #[serde(skip_serializing_if = "Option::is_none", rename = "startTime")]
708    pub start_time: Option<i64>,
709    /// Filter by end time.
710    #[serde(skip_serializing_if = "Option::is_none", rename = "endTime")]
711    pub end_time: Option<i64>,
712    /// Filter by trade ID (returns trades >= this ID).
713    #[serde(skip_serializing_if = "Option::is_none", rename = "fromId")]
714    pub from_id: Option<i64>,
715    /// Maximum number of trades to return (default 500, max 1000).
716    #[serde(skip_serializing_if = "Option::is_none")]
717    pub limit: Option<u32>,
718}
719
720impl AccountTradesParams {
721    /// Create new account trades params.
722    #[must_use]
723    pub fn new(symbol: impl Into<String>) -> Self {
724        Self {
725            symbol: symbol.into(),
726            order_id: None,
727            start_time: None,
728            end_time: None,
729            from_id: None,
730            limit: None,
731        }
732    }
733
734    /// Filter by order ID.
735    #[must_use]
736    pub fn for_order(mut self, order_id: i64) -> Self {
737        self.order_id = Some(order_id);
738        self
739    }
740
741    /// Set the limit.
742    #[must_use]
743    pub fn with_limit(mut self, limit: u32) -> Self {
744        self.limit = Some(limit);
745        self
746    }
747
748    /// Set the time range.
749    #[must_use]
750    pub fn with_time_range(mut self, start: i64, end: i64) -> Self {
751        self.start_time = Some(start);
752        self.end_time = Some(end);
753        self
754    }
755}
756
757/// Query parameters for klines (candlestick) data.
758#[derive(Debug, Clone, Serialize)]
759pub struct KlinesParams {
760    /// Trading pair symbol (e.g., "BTCUSDT").
761    pub symbol: String,
762    /// Kline interval (e.g., "1m", "1h", "1d").
763    pub interval: String,
764    /// Filter by start time (milliseconds).
765    #[serde(skip_serializing_if = "Option::is_none", rename = "startTime")]
766    pub start_time: Option<i64>,
767    /// Filter by end time (milliseconds).
768    #[serde(skip_serializing_if = "Option::is_none", rename = "endTime")]
769    pub end_time: Option<i64>,
770    /// Kline time zone offset (+/- hours, default 0 UTC).
771    #[serde(skip_serializing_if = "Option::is_none", rename = "timeZone")]
772    pub time_zone: Option<String>,
773    /// Maximum number of klines to return (default 500, max 1000).
774    #[serde(skip_serializing_if = "Option::is_none")]
775    pub limit: Option<u32>,
776}
777
778/// Query parameters for listen key operations (extend/close).
779#[derive(Debug, Clone, Serialize)]
780pub struct ListenKeyParams {
781    /// The listen key to extend or close.
782    #[serde(rename = "listenKey")]
783    pub listen_key: String,
784}
785
786impl ListenKeyParams {
787    /// Creates new listen key params.
788    #[must_use]
789    pub fn new(listen_key: impl Into<String>) -> Self {
790        Self {
791            listen_key: listen_key.into(),
792        }
793    }
794}
795
796/// Query parameters for ticker endpoints.
797#[derive(Debug, Clone, Default, Serialize)]
798pub struct TickerParams {
799    /// Trading pair symbol (optional, omit for all symbols).
800    #[serde(skip_serializing_if = "Option::is_none")]
801    pub symbol: Option<String>,
802}
803
804impl TickerParams {
805    /// Creates ticker params for all symbols.
806    #[must_use]
807    pub fn all() -> Self {
808        Self { symbol: None }
809    }
810
811    /// Creates ticker params for a specific symbol.
812    #[must_use]
813    pub fn for_symbol(symbol: impl Into<String>) -> Self {
814        Self {
815            symbol: Some(symbol.into()),
816        }
817    }
818}
819
820/// Query parameters for average price endpoint.
821#[derive(Debug, Clone, Serialize)]
822pub struct AvgPriceParams {
823    /// Trading pair symbol (required).
824    pub symbol: String,
825}
826
827impl AvgPriceParams {
828    /// Creates average price params.
829    #[must_use]
830    pub fn new(symbol: impl Into<String>) -> Self {
831        Self {
832            symbol: symbol.into(),
833        }
834    }
835}
836
837/// Query parameters for trade fee endpoint.
838#[derive(Debug, Clone, Default, Serialize)]
839pub struct TradeFeeParams {
840    /// Trading pair symbol (optional, omit for all symbols).
841    #[serde(skip_serializing_if = "Option::is_none")]
842    pub symbol: Option<String>,
843}
844
845impl TradeFeeParams {
846    /// Creates trade fee params for all symbols.
847    #[must_use]
848    pub fn all() -> Self {
849        Self { symbol: None }
850    }
851
852    /// Creates trade fee params for a specific symbol.
853    #[must_use]
854    pub fn for_symbol(symbol: impl Into<String>) -> Self {
855        Self {
856            symbol: Some(symbol.into()),
857        }
858    }
859}
860
861/// Single order in a batch order request (JSON format for batchOrders param).
862#[derive(Debug, Clone, Serialize)]
863#[serde(rename_all = "camelCase")]
864pub struct BatchOrderItem {
865    /// Trading pair symbol.
866    pub symbol: String,
867    /// Order side (BUY or SELL).
868    pub side: String,
869    /// Order type.
870    #[serde(rename = "type")]
871    pub order_type: String,
872    /// Time in force.
873    #[serde(skip_serializing_if = "Option::is_none")]
874    pub time_in_force: Option<String>,
875    /// Order quantity.
876    #[serde(skip_serializing_if = "Option::is_none")]
877    pub quantity: Option<String>,
878    /// Limit price.
879    #[serde(skip_serializing_if = "Option::is_none")]
880    pub price: Option<String>,
881    /// Client order ID.
882    #[serde(skip_serializing_if = "Option::is_none")]
883    pub new_client_order_id: Option<String>,
884    /// Stop price for stop orders.
885    #[serde(skip_serializing_if = "Option::is_none")]
886    pub stop_price: Option<String>,
887}
888
889impl BatchOrderItem {
890    /// Creates a batch order item from NewOrderParams.
891    #[must_use]
892    pub fn from_params(params: &NewOrderParams) -> Self {
893        Self {
894            symbol: params.symbol.clone(),
895            side: format!("{:?}", params.side).to_uppercase(),
896            order_type: format!("{:?}", params.order_type).to_uppercase(),
897            time_in_force: params
898                .time_in_force
899                .map(|t| format!("{t:?}").to_uppercase()),
900            quantity: params.quantity.clone(),
901            price: params.price.clone(),
902            new_client_order_id: params.new_client_order_id.clone(),
903            stop_price: params.stop_price.clone(),
904        }
905    }
906}
907
908/// Single cancel in a batch cancel request.
909#[derive(Debug, Clone, Serialize)]
910#[serde(rename_all = "camelCase")]
911pub struct BatchCancelItem {
912    /// Trading pair symbol.
913    pub symbol: String,
914    /// Order ID to cancel.
915    #[serde(skip_serializing_if = "Option::is_none")]
916    pub order_id: Option<i64>,
917    /// Original client order ID.
918    #[serde(skip_serializing_if = "Option::is_none")]
919    pub orig_client_order_id: Option<String>,
920}
921
922impl BatchCancelItem {
923    /// Creates a batch cancel item by order ID.
924    #[must_use]
925    pub fn by_order_id(symbol: impl Into<String>, order_id: i64) -> Self {
926        Self {
927            symbol: symbol.into(),
928            order_id: Some(order_id),
929            orig_client_order_id: None,
930        }
931    }
932
933    /// Creates a batch cancel item by client order ID.
934    #[must_use]
935    pub fn by_client_order_id(
936        symbol: impl Into<String>,
937        client_order_id: impl Into<String>,
938    ) -> Self {
939        Self {
940            symbol: symbol.into(),
941            order_id: None,
942            orig_client_order_id: Some(client_order_id.into()),
943        }
944    }
945}