Skip to main content

nautilus_binance/futures/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//! Binance Futures HTTP query parameter builders.
17
18use derive_builder::Builder;
19#[cfg(feature = "python")]
20use pyo3::prelude::*;
21use serde::{Deserialize, Serialize};
22
23use crate::common::enums::{
24    BinanceAlgoType, BinanceFuturesOrderType, BinanceIncomeType, BinanceMarginType,
25    BinancePositionSide, BinancePriceMatch, BinanceSelfTradePreventionMode, BinanceSide,
26    BinanceTimeInForce, BinanceWorkingType,
27};
28
29/// Query parameters for `GET /fapi/v1/depth` or `GET /dapi/v1/depth`.
30#[derive(Clone, Debug, Default, Deserialize, Serialize, Builder)]
31#[builder(setter(into, strip_option), default)]
32pub struct BinanceDepthParams {
33    /// Trading symbol (required).
34    pub symbol: String,
35    /// Depth limit (default 100, max 1000).
36    #[serde(skip_serializing_if = "Option::is_none")]
37    pub limit: Option<u32>,
38}
39
40/// Query parameters for `GET /fapi/v1/trades` or `GET /dapi/v1/trades`.
41#[derive(Clone, Debug, Default, Deserialize, Serialize, Builder)]
42#[builder(setter(into, strip_option), default)]
43pub struct BinanceTradesParams {
44    /// Trading symbol (required).
45    pub symbol: String,
46    /// Number of trades to return (default 500, max 1000).
47    #[serde(skip_serializing_if = "Option::is_none")]
48    pub limit: Option<u32>,
49}
50
51/// Query parameters for `GET /fapi/v1/klines` or `GET /dapi/v1/klines`.
52#[derive(Clone, Debug, Default, Deserialize, Serialize, Builder)]
53#[builder(setter(into, strip_option), default)]
54pub struct BinanceKlinesParams {
55    /// Trading symbol (required).
56    pub symbol: String,
57    /// Kline interval (e.g., "1m", "5m", "1h", "1d").
58    pub interval: String,
59    /// Start time in milliseconds.
60    #[serde(skip_serializing_if = "Option::is_none")]
61    #[serde(rename = "startTime")]
62    pub start_time: Option<i64>,
63    /// End time in milliseconds.
64    #[serde(skip_serializing_if = "Option::is_none")]
65    #[serde(rename = "endTime")]
66    pub end_time: Option<i64>,
67    /// Number of klines to return (default 500, max 1500).
68    #[serde(skip_serializing_if = "Option::is_none")]
69    pub limit: Option<u32>,
70}
71
72/// Query parameters for `GET /fapi/v1/ticker/24hr` or `GET /dapi/v1/ticker/24hr`.
73#[derive(Clone, Debug, Deserialize, Serialize, Default, Builder)]
74#[builder(default)]
75#[builder(setter(into, strip_option))]
76pub struct BinanceTicker24hrParams {
77    /// Filter by single symbol.
78    #[serde(skip_serializing_if = "Option::is_none")]
79    pub symbol: Option<String>,
80}
81
82/// Query parameters for `GET /fapi/v1/ticker/bookTicker` or `GET /dapi/v1/ticker/bookTicker`.
83#[derive(Clone, Debug, Deserialize, Serialize, Default, Builder)]
84#[builder(default)]
85#[builder(setter(into, strip_option))]
86pub struct BinanceBookTickerParams {
87    /// Filter by single symbol.
88    #[serde(skip_serializing_if = "Option::is_none")]
89    pub symbol: Option<String>,
90}
91
92/// Query parameters for `GET /fapi/v1/premiumIndex` or `GET /dapi/v1/premiumIndex`.
93#[derive(Clone, Debug, Deserialize, Serialize, Default, Builder)]
94#[builder(default)]
95#[builder(setter(into, strip_option))]
96pub struct BinanceMarkPriceParams {
97    /// Filter by single symbol.
98    #[serde(skip_serializing_if = "Option::is_none")]
99    pub symbol: Option<String>,
100}
101
102/// Query parameters for `GET /fapi/v1/fundingRate` or `GET /dapi/v1/fundingRate`.
103#[derive(Clone, Debug, Deserialize, Serialize, Default, Builder)]
104#[builder(default)]
105#[builder(setter(into, strip_option))]
106pub struct BinanceFundingRateParams {
107    /// Trading symbol.
108    #[serde(skip_serializing_if = "Option::is_none")]
109    pub symbol: Option<String>,
110    /// Start time in milliseconds.
111    #[serde(rename = "startTime", skip_serializing_if = "Option::is_none")]
112    pub start_time: Option<i64>,
113    /// End time in milliseconds.
114    #[serde(rename = "endTime", skip_serializing_if = "Option::is_none")]
115    pub end_time: Option<i64>,
116    /// Number of results (default 100, max 1000).
117    #[serde(skip_serializing_if = "Option::is_none")]
118    pub limit: Option<u32>,
119}
120
121/// Query parameters for `GET /fapi/v1/openInterest` or `GET /dapi/v1/openInterest`.
122#[derive(Clone, Debug, Deserialize, Serialize, Builder)]
123#[builder(setter(into))]
124pub struct BinanceOpenInterestParams {
125    /// Trading symbol (required).
126    pub symbol: String,
127}
128
129/// Query parameters for `GET /fapi/v2/balance` or `GET /dapi/v1/balance`.
130#[derive(Clone, Debug, Deserialize, Serialize, Default, Builder)]
131#[builder(default)]
132#[builder(setter(into, strip_option))]
133pub struct BinanceFuturesBalanceParams {
134    /// Filter by asset (e.g., "USDT").
135    #[serde(skip_serializing_if = "Option::is_none")]
136    pub asset: Option<String>,
137    /// Recv window override (ms).
138    #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
139    pub recv_window: Option<u64>,
140}
141
142/// Query parameters for `GET /fapi/v2/positionRisk` or `GET /dapi/v1/positionRisk`.
143#[derive(Clone, Debug, Deserialize, Serialize, Default, Builder)]
144#[builder(default)]
145#[builder(setter(into, strip_option))]
146pub struct BinancePositionRiskParams {
147    /// Filter by symbol.
148    #[serde(skip_serializing_if = "Option::is_none")]
149    pub symbol: Option<String>,
150    /// Recv window override (ms).
151    #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
152    pub recv_window: Option<u64>,
153}
154
155/// Query parameters for `GET /fapi/v1/income` or `GET /dapi/v1/income`.
156#[derive(Clone, Debug, Deserialize, Serialize, Default, Builder)]
157#[builder(default)]
158#[builder(setter(into, strip_option))]
159pub struct BinanceIncomeHistoryParams {
160    /// Filter by symbol.
161    #[serde(skip_serializing_if = "Option::is_none")]
162    pub symbol: Option<String>,
163    /// Income type filter (e.g., FUNDING_FEE).
164    #[serde(rename = "incomeType", skip_serializing_if = "Option::is_none")]
165    pub income_type: Option<BinanceIncomeType>,
166    /// Start time in milliseconds.
167    #[serde(rename = "startTime", skip_serializing_if = "Option::is_none")]
168    pub start_time: Option<i64>,
169    /// End time in milliseconds.
170    #[serde(rename = "endTime", skip_serializing_if = "Option::is_none")]
171    pub end_time: Option<i64>,
172    /// Maximum number of rows (default 100, max 1000).
173    #[serde(skip_serializing_if = "Option::is_none")]
174    pub limit: Option<u32>,
175    /// Recv window override (ms).
176    #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
177    pub recv_window: Option<u64>,
178}
179
180/// Query parameters for `GET /fapi/v1/userTrades` or `GET /dapi/v1/userTrades`.
181#[derive(Clone, Debug, Default, Deserialize, Serialize, Builder)]
182#[builder(setter(into, strip_option), default)]
183pub struct BinanceUserTradesParams {
184    /// Trading symbol (required).
185    pub symbol: String,
186    /// Order ID to filter trades for a specific order.
187    #[serde(rename = "orderId", skip_serializing_if = "Option::is_none")]
188    pub order_id: Option<i64>,
189    /// Start time in milliseconds.
190    #[serde(rename = "startTime", skip_serializing_if = "Option::is_none")]
191    pub start_time: Option<i64>,
192    /// End time in milliseconds.
193    #[serde(rename = "endTime", skip_serializing_if = "Option::is_none")]
194    pub end_time: Option<i64>,
195    /// Trade ID to fetch from (inclusive).
196    #[serde(rename = "fromId", skip_serializing_if = "Option::is_none")]
197    pub from_id: Option<i64>,
198    /// Number of trades to return (default 500, max 1000).
199    #[serde(skip_serializing_if = "Option::is_none")]
200    pub limit: Option<u32>,
201    /// Recv window override (ms).
202    #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
203    pub recv_window: Option<u64>,
204}
205
206/// Query parameters for `GET /fapi/v1/openOrders` or `GET /dapi/v1/openOrders`.
207#[derive(Clone, Debug, Deserialize, Serialize, Default, Builder)]
208#[builder(default)]
209#[builder(setter(into, strip_option))]
210pub struct BinanceOpenOrdersParams {
211    /// Filter by symbol.
212    #[serde(skip_serializing_if = "Option::is_none")]
213    pub symbol: Option<String>,
214    /// Recv window override (ms).
215    #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
216    pub recv_window: Option<u64>,
217}
218
219/// Query parameters for `GET /fapi/v1/order` or `GET /dapi/v1/order`.
220#[derive(Clone, Debug, Default, Deserialize, Serialize, Builder)]
221#[builder(setter(into, strip_option), default)]
222pub struct BinanceOrderQueryParams {
223    /// Trading symbol (required).
224    pub symbol: String,
225    /// Order ID.
226    #[serde(rename = "orderId", skip_serializing_if = "Option::is_none")]
227    pub order_id: Option<i64>,
228    /// Orig client order ID.
229    #[serde(rename = "origClientOrderId", skip_serializing_if = "Option::is_none")]
230    pub orig_client_order_id: Option<String>,
231    /// Recv window override (ms).
232    #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
233    pub recv_window: Option<u64>,
234}
235
236/// Query parameters for `POST /fapi/v1/order` (new order).
237#[derive(Clone, Debug, Deserialize, Serialize, Builder)]
238#[builder(setter(into, strip_option))]
239pub struct BinanceNewOrderParams {
240    /// Trading symbol (required).
241    pub symbol: String,
242    /// Order side (required).
243    pub side: BinanceSide,
244    /// Order type (required).
245    #[serde(rename = "type")]
246    pub order_type: BinanceFuturesOrderType,
247    /// Position side (required for hedge mode).
248    #[serde(rename = "positionSide", skip_serializing_if = "Option::is_none")]
249    #[builder(default)]
250    pub position_side: Option<BinancePositionSide>,
251    /// Time in force.
252    #[serde(rename = "timeInForce", skip_serializing_if = "Option::is_none")]
253    #[builder(default)]
254    pub time_in_force: Option<BinanceTimeInForce>,
255    /// Order quantity.
256    #[serde(skip_serializing_if = "Option::is_none")]
257    #[builder(default)]
258    pub quantity: Option<String>,
259    /// Reduce only flag.
260    #[serde(rename = "reduceOnly", skip_serializing_if = "Option::is_none")]
261    #[builder(default)]
262    pub reduce_only: Option<bool>,
263    /// Limit price.
264    #[serde(skip_serializing_if = "Option::is_none")]
265    #[builder(default)]
266    pub price: Option<String>,
267    /// Client order ID.
268    #[serde(rename = "newClientOrderId", skip_serializing_if = "Option::is_none")]
269    #[builder(default)]
270    pub new_client_order_id: Option<String>,
271    /// Stop price.
272    #[serde(rename = "stopPrice", skip_serializing_if = "Option::is_none")]
273    #[builder(default)]
274    pub stop_price: Option<String>,
275    /// Close position flag.
276    #[serde(rename = "closePosition", skip_serializing_if = "Option::is_none")]
277    #[builder(default)]
278    pub close_position: Option<bool>,
279    /// Activation price for trailing stop.
280    #[serde(rename = "activationPrice", skip_serializing_if = "Option::is_none")]
281    #[builder(default)]
282    pub activation_price: Option<String>,
283    /// Callback rate for trailing stop.
284    #[serde(rename = "callbackRate", skip_serializing_if = "Option::is_none")]
285    #[builder(default)]
286    pub callback_rate: Option<String>,
287    /// Working type (MARK_PRICE or CONTRACT_PRICE).
288    #[serde(rename = "workingType", skip_serializing_if = "Option::is_none")]
289    #[builder(default)]
290    pub working_type: Option<BinanceWorkingType>,
291    /// Price protect flag.
292    #[serde(rename = "priceProtect", skip_serializing_if = "Option::is_none")]
293    #[builder(default)]
294    pub price_protect: Option<bool>,
295    /// Response type (ACK, RESULT, FULL).
296    #[serde(rename = "newOrderRespType", skip_serializing_if = "Option::is_none")]
297    #[builder(default)]
298    pub new_order_resp_type: Option<String>,
299    /// Good till date (for GTD orders).
300    #[serde(rename = "goodTillDate", skip_serializing_if = "Option::is_none")]
301    #[builder(default)]
302    pub good_till_date: Option<i64>,
303    /// Recv window override (ms).
304    #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
305    #[builder(default)]
306    pub recv_window: Option<u64>,
307    /// Price match mode for algorithmic price matching.
308    #[serde(rename = "priceMatch", skip_serializing_if = "Option::is_none")]
309    #[builder(default)]
310    pub price_match: Option<BinancePriceMatch>,
311    /// Self-trade prevention mode.
312    #[serde(
313        rename = "selfTradePreventionMode",
314        skip_serializing_if = "Option::is_none"
315    )]
316    #[builder(default)]
317    pub self_trade_prevention_mode: Option<BinanceSelfTradePreventionMode>,
318}
319
320/// Query parameters for `DELETE /fapi/v1/order` (cancel order).
321#[derive(Clone, Debug, Default, Deserialize, Serialize, Builder)]
322#[builder(setter(into, strip_option), default)]
323pub struct BinanceCancelOrderParams {
324    /// Trading symbol (required).
325    pub symbol: String,
326    /// Order ID.
327    #[serde(rename = "orderId", skip_serializing_if = "Option::is_none")]
328    pub order_id: Option<i64>,
329    /// Orig client order ID.
330    #[serde(rename = "origClientOrderId", skip_serializing_if = "Option::is_none")]
331    pub orig_client_order_id: Option<String>,
332    /// Recv window override (ms).
333    #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
334    pub recv_window: Option<u64>,
335}
336
337/// Query parameters for `DELETE /fapi/v1/allOpenOrders` (cancel all open orders).
338#[derive(Clone, Debug, Default, Deserialize, Serialize, Builder)]
339#[builder(setter(into, strip_option), default)]
340pub struct BinanceCancelAllOrdersParams {
341    /// Trading symbol (required).
342    pub symbol: String,
343    /// Recv window override (ms).
344    #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
345    pub recv_window: Option<u64>,
346}
347
348/// Query parameters for `PUT /fapi/v1/order` (modify order).
349#[derive(Clone, Debug, Deserialize, Serialize, Builder)]
350#[builder(setter(into, strip_option))]
351pub struct BinanceModifyOrderParams {
352    /// Trading symbol (required).
353    pub symbol: String,
354    /// Order ID.
355    #[serde(rename = "orderId", skip_serializing_if = "Option::is_none")]
356    #[builder(default)]
357    pub order_id: Option<i64>,
358    /// Orig client order ID.
359    #[serde(rename = "origClientOrderId", skip_serializing_if = "Option::is_none")]
360    #[builder(default)]
361    pub orig_client_order_id: Option<String>,
362    /// Order side (required).
363    pub side: BinanceSide,
364    /// Order quantity (required).
365    pub quantity: String,
366    /// Limit price (required).
367    pub price: String,
368    /// Recv window override (ms).
369    #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
370    #[builder(default)]
371    pub recv_window: Option<u64>,
372}
373
374/// Query parameters for `GET /fapi/v1/allOrders` (all orders history).
375#[derive(Clone, Debug, Default, Deserialize, Serialize, Builder)]
376#[builder(setter(into, strip_option), default)]
377pub struct BinanceAllOrdersParams {
378    /// Trading symbol (required).
379    pub symbol: String,
380    /// Order ID to start from.
381    #[serde(rename = "orderId", skip_serializing_if = "Option::is_none")]
382    pub order_id: Option<i64>,
383    /// Start time in milliseconds.
384    #[serde(rename = "startTime", skip_serializing_if = "Option::is_none")]
385    pub start_time: Option<i64>,
386    /// End time in milliseconds.
387    #[serde(rename = "endTime", skip_serializing_if = "Option::is_none")]
388    pub end_time: Option<i64>,
389    /// Number of results (default 500, max 1000).
390    #[serde(skip_serializing_if = "Option::is_none")]
391    pub limit: Option<u32>,
392    /// Recv window override (ms).
393    #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
394    pub recv_window: Option<u64>,
395}
396
397/// Query parameters for `POST /fapi/v1/leverage` (set leverage).
398#[derive(Clone, Debug, Deserialize, Serialize, Builder)]
399#[builder(setter(into))]
400pub struct BinanceSetLeverageParams {
401    /// Trading symbol (required).
402    pub symbol: String,
403    /// Target leverage (required).
404    pub leverage: u32,
405    /// Recv window override (ms).
406    #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
407    #[builder(default)]
408    pub recv_window: Option<u64>,
409}
410
411/// Query parameters for `POST /fapi/v1/marginType` (set margin type).
412#[derive(Clone, Debug, Deserialize, Serialize, Builder)]
413#[builder(setter(into))]
414pub struct BinanceSetMarginTypeParams {
415    /// Trading symbol (required).
416    pub symbol: String,
417    /// Margin type (required).
418    #[serde(rename = "marginType")]
419    pub margin_type: BinanceMarginType,
420    /// Recv window override (ms).
421    #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
422    #[builder(default)]
423    pub recv_window: Option<u64>,
424}
425
426/// Single order item for batch submit operations.
427#[derive(Clone, Debug, Serialize)]
428#[serde(rename_all = "camelCase")]
429#[cfg_attr(
430    feature = "python",
431    pyclass(
432        module = "nautilus_trader.core.nautilus_pyo3.binance",
433        name = "FuturesBatchOrderItem",
434        get_all,
435        from_py_object,
436    )
437)]
438pub struct BatchOrderItem {
439    /// Trading symbol.
440    pub symbol: String,
441    /// Order side.
442    pub side: String,
443    /// Order type.
444    #[serde(rename = "type")]
445    pub order_type: String,
446    /// Time in force.
447    #[serde(skip_serializing_if = "Option::is_none")]
448    pub time_in_force: Option<String>,
449    /// Order quantity.
450    #[serde(skip_serializing_if = "Option::is_none")]
451    pub quantity: Option<String>,
452    /// Limit price.
453    #[serde(skip_serializing_if = "Option::is_none")]
454    pub price: Option<String>,
455    /// Reduce-only flag.
456    #[serde(skip_serializing_if = "Option::is_none")]
457    pub reduce_only: Option<bool>,
458    /// Client order ID.
459    #[serde(skip_serializing_if = "Option::is_none")]
460    pub new_client_order_id: Option<String>,
461    /// Stop price for stop orders.
462    #[serde(skip_serializing_if = "Option::is_none")]
463    pub stop_price: Option<String>,
464    /// Position side.
465    #[serde(skip_serializing_if = "Option::is_none")]
466    pub position_side: Option<String>,
467    /// Activation price for trailing stop orders.
468    #[serde(skip_serializing_if = "Option::is_none")]
469    pub activation_price: Option<String>,
470    /// Callback rate for trailing stop orders (percentage).
471    #[serde(skip_serializing_if = "Option::is_none")]
472    pub callback_rate: Option<String>,
473    /// Working type (MARK_PRICE or CONTRACT_PRICE).
474    #[serde(skip_serializing_if = "Option::is_none")]
475    pub working_type: Option<String>,
476    /// Price protection flag.
477    #[serde(skip_serializing_if = "Option::is_none")]
478    pub price_protect: Option<bool>,
479    /// Close position flag.
480    #[serde(skip_serializing_if = "Option::is_none")]
481    pub close_position: Option<bool>,
482    /// Good till date for GTD orders (ms).
483    #[serde(skip_serializing_if = "Option::is_none")]
484    pub good_till_date: Option<i64>,
485    /// Price match mode.
486    #[serde(skip_serializing_if = "Option::is_none")]
487    pub price_match: Option<String>,
488    /// Self-trade prevention mode.
489    #[serde(skip_serializing_if = "Option::is_none")]
490    pub self_trade_prevention_mode: Option<String>,
491}
492
493#[cfg(feature = "python")]
494#[pymethods]
495impl BatchOrderItem {
496    #[new]
497    #[pyo3(signature = (symbol, side, order_type, time_in_force=None, quantity=None, price=None, reduce_only=None, new_client_order_id=None, stop_price=None, position_side=None, activation_price=None, callback_rate=None, working_type=None, price_protect=None, close_position=None, good_till_date=None, price_match=None, self_trade_prevention_mode=None))]
498    #[allow(clippy::too_many_arguments)]
499    fn py_new(
500        symbol: String,
501        side: String,
502        order_type: String,
503        time_in_force: Option<String>,
504        quantity: Option<String>,
505        price: Option<String>,
506        reduce_only: Option<bool>,
507        new_client_order_id: Option<String>,
508        stop_price: Option<String>,
509        position_side: Option<String>,
510        activation_price: Option<String>,
511        callback_rate: Option<String>,
512        working_type: Option<String>,
513        price_protect: Option<bool>,
514        close_position: Option<bool>,
515        good_till_date: Option<i64>,
516        price_match: Option<String>,
517        self_trade_prevention_mode: Option<String>,
518    ) -> Self {
519        Self {
520            symbol,
521            side,
522            order_type,
523            time_in_force,
524            quantity,
525            price,
526            reduce_only,
527            new_client_order_id,
528            stop_price,
529            position_side,
530            activation_price,
531            callback_rate,
532            working_type,
533            price_protect,
534            close_position,
535            good_till_date,
536            price_match,
537            self_trade_prevention_mode,
538        }
539    }
540}
541
542/// Single cancel item for batch cancel operations.
543#[derive(Clone, Debug, Serialize)]
544#[serde(rename_all = "camelCase")]
545#[cfg_attr(
546    feature = "python",
547    pyclass(
548        module = "nautilus_trader.core.nautilus_pyo3.binance",
549        name = "FuturesBatchCancelItem",
550        get_all,
551        from_py_object,
552    )
553)]
554pub struct BatchCancelItem {
555    /// Trading symbol.
556    pub symbol: String,
557    /// Order ID to cancel.
558    #[serde(skip_serializing_if = "Option::is_none")]
559    pub order_id: Option<i64>,
560    /// Original client order ID.
561    #[serde(skip_serializing_if = "Option::is_none")]
562    pub orig_client_order_id: Option<String>,
563}
564
565impl BatchCancelItem {
566    /// Creates a batch cancel item by order ID.
567    #[must_use]
568    pub fn by_order_id(symbol: impl Into<String>, order_id: i64) -> Self {
569        Self {
570            symbol: symbol.into(),
571            order_id: Some(order_id),
572            orig_client_order_id: None,
573        }
574    }
575
576    /// Creates a batch cancel item by client order ID.
577    #[must_use]
578    pub fn by_client_order_id(
579        symbol: impl Into<String>,
580        client_order_id: impl Into<String>,
581    ) -> Self {
582        Self {
583            symbol: symbol.into(),
584            order_id: None,
585            orig_client_order_id: Some(client_order_id.into()),
586        }
587    }
588}
589
590#[cfg(feature = "python")]
591#[pymethods]
592impl BatchCancelItem {
593    #[new]
594    #[pyo3(signature = (symbol, order_id=None, orig_client_order_id=None))]
595    fn py_new(symbol: String, order_id: Option<i64>, orig_client_order_id: Option<String>) -> Self {
596        Self {
597            symbol,
598            order_id,
599            orig_client_order_id,
600        }
601    }
602
603    /// Creates a batch cancel item by order ID.
604    #[staticmethod]
605    #[pyo3(name = "by_order_id")]
606    fn py_by_order_id(symbol: String, order_id: i64) -> Self {
607        Self::by_order_id(symbol, order_id)
608    }
609
610    /// Creates a batch cancel item by client order ID.
611    #[staticmethod]
612    #[pyo3(name = "by_client_order_id")]
613    fn py_by_client_order_id(symbol: String, client_order_id: String) -> Self {
614        Self::by_client_order_id(symbol, client_order_id)
615    }
616}
617
618/// Single modify item for batch modify operations.
619#[derive(Clone, Debug, Serialize)]
620#[serde(rename_all = "camelCase")]
621#[cfg_attr(
622    feature = "python",
623    pyclass(
624        module = "nautilus_trader.core.nautilus_pyo3.binance",
625        name = "FuturesBatchModifyItem",
626        get_all,
627        from_py_object,
628    )
629)]
630pub struct BatchModifyItem {
631    /// Trading symbol.
632    pub symbol: String,
633    /// Order ID to modify.
634    #[serde(skip_serializing_if = "Option::is_none")]
635    pub order_id: Option<i64>,
636    /// Original client order ID.
637    #[serde(skip_serializing_if = "Option::is_none")]
638    pub orig_client_order_id: Option<String>,
639    /// New order side.
640    pub side: String,
641    /// New quantity.
642    pub quantity: String,
643    /// New price.
644    pub price: String,
645}
646
647#[cfg(feature = "python")]
648#[pymethods]
649impl BatchModifyItem {
650    #[new]
651    #[pyo3(signature = (symbol, side, quantity, price, order_id=None, orig_client_order_id=None))]
652    fn py_new(
653        symbol: String,
654        side: String,
655        quantity: String,
656        price: String,
657        order_id: Option<i64>,
658        orig_client_order_id: Option<String>,
659    ) -> Self {
660        Self {
661            symbol,
662            order_id,
663            orig_client_order_id,
664            side,
665            quantity,
666            price,
667        }
668    }
669}
670
671/// Listen key request parameters.
672#[derive(Debug, Clone, Serialize)]
673#[serde(rename_all = "camelCase")]
674pub struct ListenKeyParams {
675    /// The listen key to extend or close.
676    pub listen_key: String,
677}
678
679/// Query parameters for `POST /fapi/v1/algoOrder` (new algo order).
680///
681/// # References
682///
683/// - <https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/New-Algo-Order>
684#[derive(Clone, Debug, Serialize, Builder)]
685#[builder(setter(into, strip_option))]
686#[serde(rename_all = "camelCase")]
687pub struct BinanceNewAlgoOrderParams {
688    /// Trading symbol (required).
689    pub symbol: String,
690    /// Order side (required).
691    pub side: BinanceSide,
692    /// Order type (required): STOP_MARKET, STOP, TAKE_PROFIT, TAKE_PROFIT_MARKET, TRAILING_STOP_MARKET.
693    #[serde(rename = "type")]
694    pub order_type: BinanceFuturesOrderType,
695    /// Algo type (required). Currently only `Conditional` is supported.
696    #[serde(rename = "algoType")]
697    pub algo_type: BinanceAlgoType,
698    /// Position side (required for hedge mode).
699    #[serde(rename = "positionSide", skip_serializing_if = "Option::is_none")]
700    #[builder(default)]
701    pub position_side: Option<BinancePositionSide>,
702    /// Order quantity.
703    #[serde(skip_serializing_if = "Option::is_none")]
704    #[builder(default)]
705    pub quantity: Option<String>,
706    /// Limit price (for STOP/TAKE_PROFIT limit orders).
707    #[serde(skip_serializing_if = "Option::is_none")]
708    #[builder(default)]
709    pub price: Option<String>,
710    /// Trigger price for conditional order (required).
711    #[serde(rename = "triggerPrice", skip_serializing_if = "Option::is_none")]
712    #[builder(default)]
713    pub trigger_price: Option<String>,
714    /// Time in force.
715    #[serde(rename = "timeInForce", skip_serializing_if = "Option::is_none")]
716    #[builder(default)]
717    pub time_in_force: Option<BinanceTimeInForce>,
718    /// Working type for trigger price calculation (MARK_PRICE or CONTRACT_PRICE).
719    #[serde(rename = "workingType", skip_serializing_if = "Option::is_none")]
720    #[builder(default)]
721    pub working_type: Option<BinanceWorkingType>,
722    /// Close all position flag.
723    #[serde(rename = "closePosition", skip_serializing_if = "Option::is_none")]
724    #[builder(default)]
725    pub close_position: Option<bool>,
726    /// Price protection flag.
727    #[serde(rename = "priceProtect", skip_serializing_if = "Option::is_none")]
728    #[builder(default)]
729    pub price_protect: Option<bool>,
730    /// Reduce-only flag.
731    #[serde(rename = "reduceOnly", skip_serializing_if = "Option::is_none")]
732    #[builder(default)]
733    pub reduce_only: Option<bool>,
734    /// Activation price for TRAILING_STOP_MARKET orders.
735    #[serde(rename = "activationPrice", skip_serializing_if = "Option::is_none")]
736    #[builder(default)]
737    pub activation_price: Option<String>,
738    /// Callback rate for TRAILING_STOP_MARKET orders (0.1 to 10, where 1 = 1%).
739    #[serde(rename = "callbackRate", skip_serializing_if = "Option::is_none")]
740    #[builder(default)]
741    pub callback_rate: Option<String>,
742    /// Client algo order ID for idempotency.
743    #[serde(rename = "clientAlgoId", skip_serializing_if = "Option::is_none")]
744    #[builder(default)]
745    pub client_algo_id: Option<String>,
746    /// Good till date for GTD orders (milliseconds).
747    #[serde(rename = "goodTillDate", skip_serializing_if = "Option::is_none")]
748    #[builder(default)]
749    pub good_till_date: Option<i64>,
750    /// Recv window override (ms).
751    #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
752    #[builder(default)]
753    pub recv_window: Option<u64>,
754}
755
756/// Query parameters for `GET /fapi/v1/algoOrder` and `DELETE /fapi/v1/algoOrder`.
757#[derive(Clone, Debug, Default, Serialize, Builder)]
758#[builder(setter(into, strip_option), default)]
759#[serde(rename_all = "camelCase")]
760pub struct BinanceAlgoOrderQueryParams {
761    /// Algo order ID.
762    #[serde(rename = "algoId", skip_serializing_if = "Option::is_none")]
763    pub algo_id: Option<i64>,
764    /// Client algo order ID.
765    #[serde(rename = "clientAlgoId", skip_serializing_if = "Option::is_none")]
766    pub client_algo_id: Option<String>,
767    /// Recv window override (ms).
768    #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
769    pub recv_window: Option<u64>,
770}
771
772/// Query parameters for `GET /fapi/v1/openAlgoOrders`.
773#[derive(Clone, Debug, Default, Serialize, Builder)]
774#[builder(setter(into, strip_option), default)]
775#[serde(rename_all = "camelCase")]
776pub struct BinanceOpenAlgoOrdersParams {
777    /// Filter by symbol (optional).
778    #[serde(skip_serializing_if = "Option::is_none")]
779    pub symbol: Option<String>,
780    /// Recv window override (ms).
781    #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
782    pub recv_window: Option<u64>,
783}
784
785/// Query parameters for `GET /fapi/v1/allAlgoOrders`.
786#[derive(Clone, Debug, Serialize, Builder)]
787#[builder(setter(into, strip_option))]
788#[serde(rename_all = "camelCase")]
789pub struct BinanceAllAlgoOrdersParams {
790    /// Trading symbol (required).
791    pub symbol: String,
792    /// Start time in milliseconds.
793    #[serde(rename = "startTime", skip_serializing_if = "Option::is_none")]
794    #[builder(default)]
795    pub start_time: Option<i64>,
796    /// End time in milliseconds.
797    #[serde(rename = "endTime", skip_serializing_if = "Option::is_none")]
798    #[builder(default)]
799    pub end_time: Option<i64>,
800    /// Page number (1-indexed).
801    #[serde(skip_serializing_if = "Option::is_none")]
802    #[builder(default)]
803    pub page: Option<u32>,
804    /// Number of results per page (default 100, max 100).
805    #[serde(skip_serializing_if = "Option::is_none")]
806    #[builder(default)]
807    pub limit: Option<u32>,
808    /// Recv window override (ms).
809    #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
810    #[builder(default)]
811    pub recv_window: Option<u64>,
812}
813
814/// Query parameters for `DELETE /fapi/v1/algoOpenOrders` (cancel all open algo orders).
815#[derive(Clone, Debug, Serialize, Builder)]
816#[builder(setter(into, strip_option))]
817#[serde(rename_all = "camelCase")]
818pub struct BinanceCancelAllAlgoOrdersParams {
819    /// Trading symbol (required).
820    pub symbol: String,
821    /// Recv window override (ms).
822    #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
823    #[builder(default)]
824    pub recv_window: Option<u64>,
825}
826
827#[cfg(test)]
828mod tests {
829    use rstest::rstest;
830
831    use super::*;
832
833    #[rstest]
834    fn test_depth_params_builder() {
835        let params = BinanceDepthParamsBuilder::default()
836            .symbol("BTCUSDT")
837            .limit(100u32)
838            .build()
839            .unwrap();
840
841        assert_eq!(params.symbol, "BTCUSDT");
842        assert_eq!(params.limit, Some(100));
843    }
844
845    #[rstest]
846    fn test_ticker_params_serialization() {
847        let params = BinanceTicker24hrParams {
848            symbol: Some("BTCUSDT".to_string()),
849        };
850
851        let serialized = serde_urlencoded::to_string(&params).unwrap();
852        assert_eq!(serialized, "symbol=BTCUSDT");
853    }
854
855    #[rstest]
856    fn test_order_query_params_builder() {
857        let params = BinanceOrderQueryParamsBuilder::default()
858            .symbol("BTCUSDT")
859            .order_id(12345_i64)
860            .recv_window(5_000_u64)
861            .build()
862            .unwrap();
863
864        assert_eq!(params.symbol, "BTCUSDT");
865        assert_eq!(params.order_id, Some(12345));
866        assert_eq!(params.recv_window, Some(5_000));
867    }
868
869    #[rstest]
870    fn test_income_history_params_serialization() {
871        let params = BinanceIncomeHistoryParamsBuilder::default()
872            .symbol("ETHUSDT")
873            .income_type(BinanceIncomeType::FundingFee)
874            .limit(50_u32)
875            .build()
876            .unwrap();
877
878        let serialized = serde_urlencoded::to_string(&params).unwrap();
879        assert_eq!(serialized, "symbol=ETHUSDT&incomeType=FUNDING_FEE&limit=50");
880    }
881
882    #[rstest]
883    fn test_open_orders_params_builder() {
884        let params = BinanceOpenOrdersParamsBuilder::default()
885            .symbol("BNBUSDT")
886            .build()
887            .unwrap();
888
889        assert_eq!(params.symbol.as_deref(), Some("BNBUSDT"));
890        assert!(params.recv_window.is_none());
891    }
892}