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}
125
126impl NewOrderParams {
127    /// Create new order params for a limit order.
128    #[must_use]
129    pub fn limit(
130        symbol: impl Into<String>,
131        side: BinanceSide,
132        quantity: impl Into<String>,
133        price: impl Into<String>,
134    ) -> Self {
135        Self {
136            symbol: symbol.into(),
137            side,
138            order_type: BinanceSpotOrderType::Limit,
139            time_in_force: Some(BinanceTimeInForce::Gtc),
140            quantity: Some(quantity.into()),
141            quote_order_qty: None,
142            price: Some(price.into()),
143            new_client_order_id: None,
144            stop_price: None,
145            trailing_delta: None,
146            iceberg_qty: None,
147            new_order_resp_type: Some(BinanceOrderResponseType::Full),
148            self_trade_prevention_mode: None,
149        }
150    }
151
152    /// Create new order params for a market order.
153    #[must_use]
154    pub fn market(
155        symbol: impl Into<String>,
156        side: BinanceSide,
157        quantity: impl Into<String>,
158    ) -> Self {
159        Self {
160            symbol: symbol.into(),
161            side,
162            order_type: BinanceSpotOrderType::Market,
163            time_in_force: None,
164            quantity: Some(quantity.into()),
165            quote_order_qty: None,
166            price: None,
167            new_client_order_id: None,
168            stop_price: None,
169            trailing_delta: None,
170            iceberg_qty: None,
171            new_order_resp_type: Some(BinanceOrderResponseType::Full),
172            self_trade_prevention_mode: None,
173        }
174    }
175
176    /// Set the client order ID.
177    #[must_use]
178    pub fn with_client_order_id(mut self, id: impl Into<String>) -> Self {
179        self.new_client_order_id = Some(id.into());
180        self
181    }
182
183    /// Set the time in force.
184    #[must_use]
185    pub fn with_time_in_force(mut self, tif: BinanceTimeInForce) -> Self {
186        self.time_in_force = Some(tif);
187        self
188    }
189
190    /// Set the stop price.
191    #[must_use]
192    pub fn with_stop_price(mut self, price: impl Into<String>) -> Self {
193        self.stop_price = Some(price.into());
194        self
195    }
196
197    /// Set the self-trade prevention mode.
198    #[must_use]
199    pub fn with_stp_mode(mut self, mode: BinanceSelfTradePreventionMode) -> Self {
200        self.self_trade_prevention_mode = Some(mode);
201        self
202    }
203}
204
205/// Query parameters for canceling an order.
206#[derive(Debug, Clone, Serialize)]
207pub struct CancelOrderParams {
208    /// Trading pair symbol.
209    pub symbol: String,
210    /// Order ID to cancel.
211    #[serde(skip_serializing_if = "Option::is_none", rename = "orderId")]
212    pub order_id: Option<i64>,
213    /// Original client order ID.
214    #[serde(skip_serializing_if = "Option::is_none", rename = "origClientOrderId")]
215    pub orig_client_order_id: Option<String>,
216    /// New client order ID for the cancel request.
217    #[serde(skip_serializing_if = "Option::is_none", rename = "newClientOrderId")]
218    pub new_client_order_id: Option<String>,
219}
220
221impl CancelOrderParams {
222    /// Create cancel params by order ID.
223    #[must_use]
224    pub fn by_order_id(symbol: impl Into<String>, order_id: i64) -> Self {
225        Self {
226            symbol: symbol.into(),
227            order_id: Some(order_id),
228            orig_client_order_id: None,
229            new_client_order_id: None,
230        }
231    }
232
233    /// Create cancel params by client order ID.
234    #[must_use]
235    pub fn by_client_order_id(
236        symbol: impl Into<String>,
237        client_order_id: impl Into<String>,
238    ) -> Self {
239        Self {
240            symbol: symbol.into(),
241            order_id: None,
242            orig_client_order_id: Some(client_order_id.into()),
243            new_client_order_id: None,
244        }
245    }
246}
247
248/// Query parameters for canceling all open orders on a symbol.
249#[derive(Debug, Clone, Serialize)]
250pub struct CancelOpenOrdersParams {
251    /// Trading pair symbol.
252    pub symbol: String,
253}
254
255impl CancelOpenOrdersParams {
256    /// Create new cancel open orders params.
257    #[must_use]
258    pub fn new(symbol: impl Into<String>) -> Self {
259        Self {
260            symbol: symbol.into(),
261        }
262    }
263}
264
265/// Query parameters for cancel and replace order.
266#[derive(Debug, Clone, Serialize)]
267pub struct CancelReplaceOrderParams {
268    /// Trading pair symbol.
269    pub symbol: String,
270    /// Order side.
271    pub side: BinanceSide,
272    /// Order type.
273    #[serde(rename = "type")]
274    pub order_type: BinanceSpotOrderType,
275    /// Cancel/replace mode.
276    #[serde(rename = "cancelReplaceMode")]
277    pub cancel_replace_mode: BinanceCancelReplaceMode,
278    /// Time in force.
279    #[serde(skip_serializing_if = "Option::is_none", rename = "timeInForce")]
280    pub time_in_force: Option<BinanceTimeInForce>,
281    /// Order quantity.
282    #[serde(skip_serializing_if = "Option::is_none")]
283    pub quantity: Option<String>,
284    /// Quote order quantity.
285    #[serde(skip_serializing_if = "Option::is_none", rename = "quoteOrderQty")]
286    pub quote_order_qty: Option<String>,
287    /// Limit price.
288    #[serde(skip_serializing_if = "Option::is_none")]
289    pub price: Option<String>,
290    /// Order ID to cancel.
291    #[serde(skip_serializing_if = "Option::is_none", rename = "cancelOrderId")]
292    pub cancel_order_id: Option<i64>,
293    /// Client order ID to cancel.
294    #[serde(
295        skip_serializing_if = "Option::is_none",
296        rename = "cancelOrigClientOrderId"
297    )]
298    pub cancel_orig_client_order_id: Option<String>,
299    /// New client order ID.
300    #[serde(skip_serializing_if = "Option::is_none", rename = "newClientOrderId")]
301    pub new_client_order_id: Option<String>,
302    /// Stop price.
303    #[serde(skip_serializing_if = "Option::is_none", rename = "stopPrice")]
304    pub stop_price: Option<String>,
305    /// Trailing delta.
306    #[serde(skip_serializing_if = "Option::is_none", rename = "trailingDelta")]
307    pub trailing_delta: Option<i64>,
308    /// Iceberg quantity.
309    #[serde(skip_serializing_if = "Option::is_none", rename = "icebergQty")]
310    pub iceberg_qty: Option<String>,
311    /// Response type.
312    #[serde(skip_serializing_if = "Option::is_none", rename = "newOrderRespType")]
313    pub new_order_resp_type: Option<BinanceOrderResponseType>,
314    /// Self-trade prevention mode.
315    #[serde(
316        skip_serializing_if = "Option::is_none",
317        rename = "selfTradePreventionMode"
318    )]
319    pub self_trade_prevention_mode: Option<BinanceSelfTradePreventionMode>,
320}
321
322/// Query parameters for querying a single order.
323#[derive(Debug, Clone, Serialize)]
324pub struct QueryOrderParams {
325    /// Trading pair symbol.
326    pub symbol: String,
327    /// Order ID.
328    #[serde(skip_serializing_if = "Option::is_none", rename = "orderId")]
329    pub order_id: Option<i64>,
330    /// Original client order ID.
331    #[serde(skip_serializing_if = "Option::is_none", rename = "origClientOrderId")]
332    pub orig_client_order_id: Option<String>,
333}
334
335impl QueryOrderParams {
336    /// Create query params by order ID.
337    #[must_use]
338    pub fn by_order_id(symbol: impl Into<String>, order_id: i64) -> Self {
339        Self {
340            symbol: symbol.into(),
341            order_id: Some(order_id),
342            orig_client_order_id: None,
343        }
344    }
345
346    /// Create query params by client order ID.
347    #[must_use]
348    pub fn by_client_order_id(
349        symbol: impl Into<String>,
350        client_order_id: impl Into<String>,
351    ) -> Self {
352        Self {
353            symbol: symbol.into(),
354            order_id: None,
355            orig_client_order_id: Some(client_order_id.into()),
356        }
357    }
358}
359
360/// Query parameters for querying open orders.
361#[derive(Debug, Clone, Default, Serialize)]
362pub struct OpenOrdersParams {
363    /// Trading pair symbol (optional, omit for all symbols).
364    #[serde(skip_serializing_if = "Option::is_none")]
365    pub symbol: Option<String>,
366}
367
368impl OpenOrdersParams {
369    /// Create new open orders params for all symbols.
370    #[must_use]
371    pub fn all() -> Self {
372        Self { symbol: None }
373    }
374
375    /// Create new open orders params for a specific symbol.
376    #[must_use]
377    pub fn for_symbol(symbol: impl Into<String>) -> Self {
378        Self {
379            symbol: Some(symbol.into()),
380        }
381    }
382}
383
384/// Query parameters for querying all orders (includes filled/canceled).
385#[derive(Debug, Clone, Serialize)]
386pub struct AllOrdersParams {
387    /// Trading pair symbol.
388    pub symbol: String,
389    /// Filter by order ID (returns orders >= this ID).
390    #[serde(skip_serializing_if = "Option::is_none", rename = "orderId")]
391    pub order_id: Option<i64>,
392    /// Filter by start time.
393    #[serde(skip_serializing_if = "Option::is_none", rename = "startTime")]
394    pub start_time: Option<i64>,
395    /// Filter by end time.
396    #[serde(skip_serializing_if = "Option::is_none", rename = "endTime")]
397    pub end_time: Option<i64>,
398    /// Maximum number of orders to return (default 500, max 1000).
399    #[serde(skip_serializing_if = "Option::is_none")]
400    pub limit: Option<u32>,
401}
402
403impl AllOrdersParams {
404    /// Create new all orders params.
405    #[must_use]
406    pub fn new(symbol: impl Into<String>) -> Self {
407        Self {
408            symbol: symbol.into(),
409            order_id: None,
410            start_time: None,
411            end_time: None,
412            limit: None,
413        }
414    }
415
416    /// Set the limit.
417    #[must_use]
418    pub fn with_limit(mut self, limit: u32) -> Self {
419        self.limit = Some(limit);
420        self
421    }
422
423    /// Set the time range.
424    #[must_use]
425    pub fn with_time_range(mut self, start: i64, end: i64) -> Self {
426        self.start_time = Some(start);
427        self.end_time = Some(end);
428        self
429    }
430}
431
432/// Query parameters for new OCO order.
433#[derive(Debug, Clone, Serialize)]
434pub struct NewOcoOrderParams {
435    /// Trading pair symbol.
436    pub symbol: String,
437    /// Order side.
438    pub side: BinanceSide,
439    /// Order quantity.
440    pub quantity: String,
441    /// Limit price (above-market for sell, below-market for buy).
442    pub price: String,
443    /// Stop price trigger.
444    #[serde(rename = "stopPrice")]
445    pub stop_price: String,
446    /// Stop limit price (optional, creates stop-limit if provided).
447    #[serde(skip_serializing_if = "Option::is_none", rename = "stopLimitPrice")]
448    pub stop_limit_price: Option<String>,
449    /// Client order ID for the entire list.
450    #[serde(skip_serializing_if = "Option::is_none", rename = "listClientOrderId")]
451    pub list_client_order_id: Option<String>,
452    /// Client order ID for the limit order.
453    #[serde(skip_serializing_if = "Option::is_none", rename = "limitClientOrderId")]
454    pub limit_client_order_id: Option<String>,
455    /// Client order ID for the stop order.
456    #[serde(skip_serializing_if = "Option::is_none", rename = "stopClientOrderId")]
457    pub stop_client_order_id: Option<String>,
458    /// Iceberg quantity for the limit leg.
459    #[serde(skip_serializing_if = "Option::is_none", rename = "limitIcebergQty")]
460    pub limit_iceberg_qty: Option<String>,
461    /// Iceberg quantity for the stop leg.
462    #[serde(skip_serializing_if = "Option::is_none", rename = "stopIcebergQty")]
463    pub stop_iceberg_qty: Option<String>,
464    /// Time in force for the stop-limit leg.
465    #[serde(
466        skip_serializing_if = "Option::is_none",
467        rename = "stopLimitTimeInForce"
468    )]
469    pub stop_limit_time_in_force: Option<BinanceTimeInForce>,
470    /// Response type.
471    #[serde(skip_serializing_if = "Option::is_none", rename = "newOrderRespType")]
472    pub new_order_resp_type: Option<BinanceOrderResponseType>,
473    /// Self-trade prevention mode.
474    #[serde(
475        skip_serializing_if = "Option::is_none",
476        rename = "selfTradePreventionMode"
477    )]
478    pub self_trade_prevention_mode: Option<BinanceSelfTradePreventionMode>,
479}
480
481impl NewOcoOrderParams {
482    /// Create new OCO order params.
483    #[must_use]
484    pub fn new(
485        symbol: impl Into<String>,
486        side: BinanceSide,
487        quantity: impl Into<String>,
488        price: impl Into<String>,
489        stop_price: impl Into<String>,
490    ) -> Self {
491        Self {
492            symbol: symbol.into(),
493            side,
494            quantity: quantity.into(),
495            price: price.into(),
496            stop_price: stop_price.into(),
497            stop_limit_price: None,
498            list_client_order_id: None,
499            limit_client_order_id: None,
500            stop_client_order_id: None,
501            limit_iceberg_qty: None,
502            stop_iceberg_qty: None,
503            stop_limit_time_in_force: None,
504            new_order_resp_type: Some(BinanceOrderResponseType::Full),
505            self_trade_prevention_mode: None,
506        }
507    }
508
509    /// Set stop limit price (makes stop leg a stop-limit order).
510    #[must_use]
511    pub fn with_stop_limit_price(mut self, price: impl Into<String>) -> Self {
512        self.stop_limit_price = Some(price.into());
513        self.stop_limit_time_in_force = Some(BinanceTimeInForce::Gtc);
514        self
515    }
516}
517
518/// Query parameters for canceling an order list (OCO).
519#[derive(Debug, Clone, Serialize)]
520pub struct CancelOrderListParams {
521    /// Trading pair symbol.
522    pub symbol: String,
523    /// Order list ID.
524    #[serde(skip_serializing_if = "Option::is_none", rename = "orderListId")]
525    pub order_list_id: Option<i64>,
526    /// List client order ID.
527    #[serde(skip_serializing_if = "Option::is_none", rename = "listClientOrderId")]
528    pub list_client_order_id: Option<String>,
529    /// New client order ID for the cancel request.
530    #[serde(skip_serializing_if = "Option::is_none", rename = "newClientOrderId")]
531    pub new_client_order_id: Option<String>,
532}
533
534impl CancelOrderListParams {
535    /// Create cancel params by order list ID.
536    #[must_use]
537    pub fn by_order_list_id(symbol: impl Into<String>, order_list_id: i64) -> Self {
538        Self {
539            symbol: symbol.into(),
540            order_list_id: Some(order_list_id),
541            list_client_order_id: None,
542            new_client_order_id: None,
543        }
544    }
545
546    /// Create cancel params by list client order ID.
547    #[must_use]
548    pub fn by_list_client_order_id(
549        symbol: impl Into<String>,
550        list_client_order_id: impl Into<String>,
551    ) -> Self {
552        Self {
553            symbol: symbol.into(),
554            order_list_id: None,
555            list_client_order_id: Some(list_client_order_id.into()),
556            new_client_order_id: None,
557        }
558    }
559}
560
561/// Query parameters for querying an order list (OCO).
562#[derive(Debug, Clone, Serialize)]
563pub struct QueryOrderListParams {
564    /// Order list ID.
565    #[serde(skip_serializing_if = "Option::is_none", rename = "orderListId")]
566    pub order_list_id: Option<i64>,
567    /// List client order ID.
568    #[serde(skip_serializing_if = "Option::is_none", rename = "origClientOrderId")]
569    pub orig_client_order_id: Option<String>,
570}
571
572impl QueryOrderListParams {
573    /// Create query params by order list ID.
574    #[must_use]
575    pub fn by_order_list_id(order_list_id: i64) -> Self {
576        Self {
577            order_list_id: Some(order_list_id),
578            orig_client_order_id: None,
579        }
580    }
581
582    /// Create query params by list client order ID.
583    #[must_use]
584    pub fn by_client_order_id(client_order_id: impl Into<String>) -> Self {
585        Self {
586            order_list_id: None,
587            orig_client_order_id: Some(client_order_id.into()),
588        }
589    }
590}
591
592/// Query parameters for querying all order lists (OCOs).
593#[derive(Debug, Clone, Default, Serialize)]
594pub struct AllOrderListsParams {
595    /// Filter by start time.
596    #[serde(skip_serializing_if = "Option::is_none", rename = "startTime")]
597    pub start_time: Option<i64>,
598    /// Filter by end time.
599    #[serde(skip_serializing_if = "Option::is_none", rename = "endTime")]
600    pub end_time: Option<i64>,
601    /// Maximum number of results (default 500, max 1000).
602    #[serde(skip_serializing_if = "Option::is_none")]
603    pub limit: Option<u32>,
604}
605
606/// Query parameters for querying open order lists (OCOs).
607#[derive(Debug, Clone, Default, Serialize)]
608pub struct OpenOrderListsParams {}
609
610/// Query parameters for account information.
611#[derive(Debug, Clone, Default, Serialize)]
612pub struct AccountInfoParams {
613    /// Omit zero balances from response.
614    #[serde(skip_serializing_if = "Option::is_none", rename = "omitZeroBalances")]
615    pub omit_zero_balances: Option<bool>,
616}
617
618impl AccountInfoParams {
619    /// Create new account info params.
620    #[must_use]
621    pub fn new() -> Self {
622        Self::default()
623    }
624
625    /// Omit zero balances from response.
626    #[must_use]
627    pub fn omit_zero_balances(mut self) -> Self {
628        self.omit_zero_balances = Some(true);
629        self
630    }
631}
632
633/// Query parameters for account trades.
634#[derive(Debug, Clone, Serialize)]
635pub struct AccountTradesParams {
636    /// Trading pair symbol.
637    pub symbol: String,
638    /// Filter by order ID.
639    #[serde(skip_serializing_if = "Option::is_none", rename = "orderId")]
640    pub order_id: Option<i64>,
641    /// Filter by start time.
642    #[serde(skip_serializing_if = "Option::is_none", rename = "startTime")]
643    pub start_time: Option<i64>,
644    /// Filter by end time.
645    #[serde(skip_serializing_if = "Option::is_none", rename = "endTime")]
646    pub end_time: Option<i64>,
647    /// Filter by trade ID (returns trades >= this ID).
648    #[serde(skip_serializing_if = "Option::is_none", rename = "fromId")]
649    pub from_id: Option<i64>,
650    /// Maximum number of trades to return (default 500, max 1000).
651    #[serde(skip_serializing_if = "Option::is_none")]
652    pub limit: Option<u32>,
653}
654
655impl AccountTradesParams {
656    /// Create new account trades params.
657    #[must_use]
658    pub fn new(symbol: impl Into<String>) -> Self {
659        Self {
660            symbol: symbol.into(),
661            order_id: None,
662            start_time: None,
663            end_time: None,
664            from_id: None,
665            limit: None,
666        }
667    }
668
669    /// Filter by order ID.
670    #[must_use]
671    pub fn for_order(mut self, order_id: i64) -> Self {
672        self.order_id = Some(order_id);
673        self
674    }
675
676    /// Set the limit.
677    #[must_use]
678    pub fn with_limit(mut self, limit: u32) -> Self {
679        self.limit = Some(limit);
680        self
681    }
682
683    /// Set the time range.
684    #[must_use]
685    pub fn with_time_range(mut self, start: i64, end: i64) -> Self {
686        self.start_time = Some(start);
687        self.end_time = Some(end);
688        self
689    }
690}