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