1use derive_builder::Builder;
19use serde::{Deserialize, Serialize};
20
21use crate::common::enums::{
22 BinanceAlgoType, BinanceFuturesOrderType, BinanceIncomeType, BinanceMarginType,
23 BinancePositionSide, BinancePriceMatch, BinanceSelfTradePreventionMode, BinanceSide,
24 BinanceTimeInForce, BinanceWorkingType,
25};
26
27#[derive(Clone, Debug, Default, Deserialize, Serialize, Builder)]
29#[builder(setter(into, strip_option), default)]
30pub struct BinanceDepthParams {
31 pub symbol: String,
33 #[serde(skip_serializing_if = "Option::is_none")]
35 pub limit: Option<u32>,
36}
37
38#[derive(Clone, Debug, Default, Deserialize, Serialize, Builder)]
40#[builder(setter(into, strip_option), default)]
41pub struct BinanceTradesParams {
42 pub symbol: String,
44 #[serde(skip_serializing_if = "Option::is_none")]
46 pub limit: Option<u32>,
47}
48
49#[derive(Clone, Debug, Default, Deserialize, Serialize, Builder)]
51#[builder(setter(into, strip_option), default)]
52pub struct BinanceAggTradesParams {
53 pub symbol: String,
55 #[serde(rename = "fromId", skip_serializing_if = "Option::is_none")]
57 pub from_id: Option<i64>,
58 #[serde(rename = "startTime", skip_serializing_if = "Option::is_none")]
60 pub start_time: Option<i64>,
61 #[serde(rename = "endTime", skip_serializing_if = "Option::is_none")]
63 pub end_time: Option<i64>,
64 #[serde(skip_serializing_if = "Option::is_none")]
66 pub limit: Option<u32>,
67}
68
69#[derive(Clone, Debug, Default, Deserialize, Serialize, Builder)]
71#[builder(setter(into, strip_option), default)]
72pub struct BinanceKlinesParams {
73 pub symbol: String,
75 pub interval: String,
77 #[serde(skip_serializing_if = "Option::is_none")]
79 #[serde(rename = "startTime")]
80 pub start_time: Option<i64>,
81 #[serde(skip_serializing_if = "Option::is_none")]
83 #[serde(rename = "endTime")]
84 pub end_time: Option<i64>,
85 #[serde(skip_serializing_if = "Option::is_none")]
87 pub limit: Option<u32>,
88}
89
90#[derive(Clone, Debug, Deserialize, Serialize, Default, Builder)]
92#[builder(default)]
93#[builder(setter(into, strip_option))]
94pub struct BinanceTicker24hrParams {
95 #[serde(skip_serializing_if = "Option::is_none")]
97 pub symbol: Option<String>,
98}
99
100#[derive(Clone, Debug, Deserialize, Serialize, Default, Builder)]
102#[builder(default)]
103#[builder(setter(into, strip_option))]
104pub struct BinanceBookTickerParams {
105 #[serde(skip_serializing_if = "Option::is_none")]
107 pub symbol: Option<String>,
108}
109
110#[derive(Clone, Debug, Deserialize, Serialize, Default, Builder)]
112#[builder(default)]
113#[builder(setter(into, strip_option))]
114pub struct BinanceMarkPriceParams {
115 #[serde(skip_serializing_if = "Option::is_none")]
117 pub symbol: Option<String>,
118}
119
120#[derive(Clone, Debug, Deserialize, Serialize, Default, Builder)]
122#[builder(default)]
123#[builder(setter(into, strip_option))]
124pub struct BinanceFundingRateParams {
125 #[serde(skip_serializing_if = "Option::is_none")]
127 pub symbol: Option<String>,
128 #[serde(rename = "startTime", skip_serializing_if = "Option::is_none")]
130 pub start_time: Option<i64>,
131 #[serde(rename = "endTime", skip_serializing_if = "Option::is_none")]
133 pub end_time: Option<i64>,
134 #[serde(skip_serializing_if = "Option::is_none")]
136 pub limit: Option<u32>,
137}
138
139#[derive(Clone, Debug, Deserialize, Serialize, Builder)]
141#[builder(setter(into))]
142pub struct BinanceOpenInterestParams {
143 pub symbol: String,
145}
146
147#[derive(Clone, Debug, Default, Deserialize, Serialize, Builder)]
149#[builder(setter(into, strip_option), default)]
150pub struct BinanceOpenInterestHistParams {
151 #[serde(skip_serializing_if = "Option::is_none")]
153 pub symbol: Option<String>,
154 #[serde(skip_serializing_if = "Option::is_none")]
156 pub pair: Option<String>,
157 #[serde(rename = "contractType", skip_serializing_if = "Option::is_none")]
159 pub contract_type: Option<String>,
160 pub period: String,
162 #[serde(rename = "startTime", skip_serializing_if = "Option::is_none")]
164 pub start_time: Option<i64>,
165 #[serde(rename = "endTime", skip_serializing_if = "Option::is_none")]
167 pub end_time: Option<i64>,
168 #[serde(skip_serializing_if = "Option::is_none")]
170 pub limit: Option<u32>,
171}
172
173#[derive(Clone, Debug, Deserialize, Serialize, Default, Builder)]
175#[builder(default)]
176#[builder(setter(into, strip_option))]
177pub struct BinanceFuturesBalanceParams {
178 #[serde(skip_serializing_if = "Option::is_none")]
180 pub asset: Option<String>,
181 #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
183 pub recv_window: Option<u64>,
184}
185
186#[derive(Clone, Debug, Deserialize, Serialize, Default, Builder)]
188#[builder(default)]
189#[builder(setter(into, strip_option))]
190pub struct BinancePositionRiskParams {
191 #[serde(skip_serializing_if = "Option::is_none")]
193 pub symbol: Option<String>,
194 #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
196 pub recv_window: Option<u64>,
197}
198
199#[derive(Clone, Debug, Deserialize, Serialize, Builder)]
201#[builder(setter(into))]
202pub struct BinanceCommissionRateParams {
203 pub symbol: String,
205}
206
207#[derive(Clone, Debug, Deserialize, Serialize, Default, Builder)]
209#[builder(default)]
210#[builder(setter(into, strip_option))]
211pub struct BinanceIncomeHistoryParams {
212 #[serde(skip_serializing_if = "Option::is_none")]
214 pub symbol: Option<String>,
215 #[serde(rename = "incomeType", skip_serializing_if = "Option::is_none")]
217 pub income_type: Option<BinanceIncomeType>,
218 #[serde(rename = "startTime", skip_serializing_if = "Option::is_none")]
220 pub start_time: Option<i64>,
221 #[serde(rename = "endTime", skip_serializing_if = "Option::is_none")]
223 pub end_time: Option<i64>,
224 #[serde(skip_serializing_if = "Option::is_none")]
226 pub limit: Option<u32>,
227 #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
229 pub recv_window: Option<u64>,
230}
231
232#[derive(Clone, Debug, Default, Deserialize, Serialize, Builder)]
234#[builder(setter(into, strip_option), default)]
235pub struct BinanceUserTradesParams {
236 pub symbol: String,
238 #[serde(rename = "orderId", skip_serializing_if = "Option::is_none")]
240 pub order_id: Option<i64>,
241 #[serde(rename = "startTime", skip_serializing_if = "Option::is_none")]
243 pub start_time: Option<i64>,
244 #[serde(rename = "endTime", skip_serializing_if = "Option::is_none")]
246 pub end_time: Option<i64>,
247 #[serde(rename = "fromId", skip_serializing_if = "Option::is_none")]
249 pub from_id: Option<i64>,
250 #[serde(skip_serializing_if = "Option::is_none")]
252 pub limit: Option<u32>,
253 #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
255 pub recv_window: Option<u64>,
256}
257
258#[derive(Clone, Debug, Deserialize, Serialize, Default, Builder)]
260#[builder(default)]
261#[builder(setter(into, strip_option))]
262pub struct BinanceOpenOrdersParams {
263 #[serde(skip_serializing_if = "Option::is_none")]
265 pub symbol: Option<String>,
266 #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
268 pub recv_window: Option<u64>,
269}
270
271#[derive(Clone, Debug, Default, Deserialize, Serialize, Builder)]
273#[builder(setter(into, strip_option), default)]
274pub struct BinanceOrderQueryParams {
275 pub symbol: String,
277 #[serde(rename = "orderId", skip_serializing_if = "Option::is_none")]
279 pub order_id: Option<i64>,
280 #[serde(rename = "origClientOrderId", skip_serializing_if = "Option::is_none")]
282 pub orig_client_order_id: Option<String>,
283 #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
285 pub recv_window: Option<u64>,
286}
287
288#[derive(Clone, Debug, Deserialize, Serialize, Builder)]
290#[builder(setter(into, strip_option))]
291pub struct BinanceNewOrderParams {
292 pub symbol: String,
294 pub side: BinanceSide,
296 #[serde(rename = "type")]
298 pub order_type: BinanceFuturesOrderType,
299 #[serde(rename = "positionSide", skip_serializing_if = "Option::is_none")]
301 #[builder(default)]
302 pub position_side: Option<BinancePositionSide>,
303 #[serde(rename = "timeInForce", skip_serializing_if = "Option::is_none")]
305 #[builder(default)]
306 pub time_in_force: Option<BinanceTimeInForce>,
307 #[serde(skip_serializing_if = "Option::is_none")]
309 #[builder(default)]
310 pub quantity: Option<String>,
311 #[serde(rename = "reduceOnly", skip_serializing_if = "Option::is_none")]
313 #[builder(default)]
314 pub reduce_only: Option<bool>,
315 #[serde(skip_serializing_if = "Option::is_none")]
317 #[builder(default)]
318 pub price: Option<String>,
319 #[serde(rename = "newClientOrderId", skip_serializing_if = "Option::is_none")]
321 #[builder(default)]
322 pub new_client_order_id: Option<String>,
323 #[serde(rename = "stopPrice", skip_serializing_if = "Option::is_none")]
325 #[builder(default)]
326 pub stop_price: Option<String>,
327 #[serde(rename = "closePosition", skip_serializing_if = "Option::is_none")]
329 #[builder(default)]
330 pub close_position: Option<bool>,
331 #[serde(rename = "activationPrice", skip_serializing_if = "Option::is_none")]
333 #[builder(default)]
334 pub activation_price: Option<String>,
335 #[serde(rename = "callbackRate", skip_serializing_if = "Option::is_none")]
337 #[builder(default)]
338 pub callback_rate: Option<String>,
339 #[serde(rename = "workingType", skip_serializing_if = "Option::is_none")]
341 #[builder(default)]
342 pub working_type: Option<BinanceWorkingType>,
343 #[serde(rename = "priceProtect", skip_serializing_if = "Option::is_none")]
345 #[builder(default)]
346 pub price_protect: Option<bool>,
347 #[serde(rename = "newOrderRespType", skip_serializing_if = "Option::is_none")]
349 #[builder(default)]
350 pub new_order_resp_type: Option<String>,
351 #[serde(rename = "goodTillDate", skip_serializing_if = "Option::is_none")]
353 #[builder(default)]
354 pub good_till_date: Option<i64>,
355 #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
357 #[builder(default)]
358 pub recv_window: Option<u64>,
359 #[serde(rename = "priceMatch", skip_serializing_if = "Option::is_none")]
361 #[builder(default)]
362 pub price_match: Option<BinancePriceMatch>,
363 #[serde(
365 rename = "selfTradePreventionMode",
366 skip_serializing_if = "Option::is_none"
367 )]
368 #[builder(default)]
369 pub self_trade_prevention_mode: Option<BinanceSelfTradePreventionMode>,
370}
371
372#[derive(Clone, Debug, Default, Deserialize, Serialize, Builder)]
374#[builder(setter(into, strip_option), default)]
375pub struct BinanceCancelOrderParams {
376 pub symbol: String,
378 #[serde(rename = "orderId", skip_serializing_if = "Option::is_none")]
380 pub order_id: Option<i64>,
381 #[serde(rename = "origClientOrderId", skip_serializing_if = "Option::is_none")]
383 pub orig_client_order_id: Option<String>,
384 #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
386 pub recv_window: Option<u64>,
387}
388
389#[derive(Clone, Debug, Default, Deserialize, Serialize, Builder)]
391#[builder(setter(into, strip_option), default)]
392pub struct BinanceCancelAllOrdersParams {
393 pub symbol: String,
395 #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
397 pub recv_window: Option<u64>,
398}
399
400#[derive(Clone, Debug, Deserialize, Serialize, Builder)]
402#[builder(setter(into, strip_option))]
403pub struct BinanceModifyOrderParams {
404 pub symbol: String,
406 #[serde(rename = "orderId", skip_serializing_if = "Option::is_none")]
408 #[builder(default)]
409 pub order_id: Option<i64>,
410 #[serde(rename = "origClientOrderId", skip_serializing_if = "Option::is_none")]
412 #[builder(default)]
413 pub orig_client_order_id: Option<String>,
414 pub side: BinanceSide,
416 pub quantity: String,
418 pub price: String,
420 #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
422 #[builder(default)]
423 pub recv_window: Option<u64>,
424}
425
426#[derive(Clone, Debug, Default, Deserialize, Serialize, Builder)]
428#[builder(setter(into, strip_option), default)]
429pub struct BinanceAllOrdersParams {
430 pub symbol: String,
432 #[serde(rename = "orderId", skip_serializing_if = "Option::is_none")]
434 pub order_id: Option<i64>,
435 #[serde(rename = "startTime", skip_serializing_if = "Option::is_none")]
437 pub start_time: Option<i64>,
438 #[serde(rename = "endTime", skip_serializing_if = "Option::is_none")]
440 pub end_time: Option<i64>,
441 #[serde(skip_serializing_if = "Option::is_none")]
443 pub limit: Option<u32>,
444 #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
446 pub recv_window: Option<u64>,
447}
448
449#[derive(Clone, Debug, Deserialize, Serialize, Builder)]
451#[builder(setter(into))]
452pub struct BinanceSetLeverageParams {
453 pub symbol: String,
455 pub leverage: u32,
457 #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
459 #[builder(default)]
460 pub recv_window: Option<u64>,
461}
462
463#[derive(Clone, Debug, Deserialize, Serialize, Builder)]
465#[builder(setter(into))]
466pub struct BinanceSetMarginTypeParams {
467 pub symbol: String,
469 #[serde(rename = "marginType")]
471 pub margin_type: BinanceMarginType,
472 #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
474 #[builder(default)]
475 pub recv_window: Option<u64>,
476}
477
478#[derive(Clone, Debug, Serialize)]
480#[serde(rename_all = "camelCase")]
481pub struct BatchOrderItem {
482 pub symbol: String,
484 pub side: String,
486 #[serde(rename = "type")]
488 pub order_type: String,
489 #[serde(skip_serializing_if = "Option::is_none")]
491 pub time_in_force: Option<String>,
492 #[serde(skip_serializing_if = "Option::is_none")]
494 pub quantity: Option<String>,
495 #[serde(skip_serializing_if = "Option::is_none")]
497 pub price: Option<String>,
498 #[serde(skip_serializing_if = "Option::is_none")]
500 pub reduce_only: Option<bool>,
501 #[serde(skip_serializing_if = "Option::is_none")]
503 pub new_client_order_id: Option<String>,
504 #[serde(skip_serializing_if = "Option::is_none")]
506 pub stop_price: Option<String>,
507 #[serde(skip_serializing_if = "Option::is_none")]
509 pub position_side: Option<String>,
510 #[serde(skip_serializing_if = "Option::is_none")]
512 pub activation_price: Option<String>,
513 #[serde(skip_serializing_if = "Option::is_none")]
515 pub callback_rate: Option<String>,
516 #[serde(skip_serializing_if = "Option::is_none")]
518 pub working_type: Option<String>,
519 #[serde(skip_serializing_if = "Option::is_none")]
521 pub price_protect: Option<bool>,
522 #[serde(skip_serializing_if = "Option::is_none")]
524 pub close_position: Option<bool>,
525 #[serde(skip_serializing_if = "Option::is_none")]
527 pub good_till_date: Option<i64>,
528 #[serde(skip_serializing_if = "Option::is_none")]
530 pub price_match: Option<String>,
531 #[serde(skip_serializing_if = "Option::is_none")]
533 pub self_trade_prevention_mode: Option<String>,
534}
535
536#[derive(Clone, Debug, Serialize)]
538#[serde(rename_all = "camelCase")]
539pub struct BatchCancelItem {
540 pub symbol: String,
542 #[serde(skip_serializing_if = "Option::is_none")]
544 pub order_id: Option<i64>,
545 #[serde(skip_serializing_if = "Option::is_none")]
547 pub orig_client_order_id: Option<String>,
548}
549
550impl BatchCancelItem {
551 #[must_use]
553 pub fn by_order_id(symbol: impl Into<String>, order_id: i64) -> Self {
554 Self {
555 symbol: symbol.into(),
556 order_id: Some(order_id),
557 orig_client_order_id: None,
558 }
559 }
560
561 #[must_use]
563 pub fn by_client_order_id(
564 symbol: impl Into<String>,
565 client_order_id: impl Into<String>,
566 ) -> Self {
567 Self {
568 symbol: symbol.into(),
569 order_id: None,
570 orig_client_order_id: Some(client_order_id.into()),
571 }
572 }
573}
574
575#[derive(Clone, Debug, Serialize)]
577#[serde(rename_all = "camelCase")]
578pub struct BatchModifyItem {
579 pub symbol: String,
581 #[serde(skip_serializing_if = "Option::is_none")]
583 pub order_id: Option<i64>,
584 #[serde(skip_serializing_if = "Option::is_none")]
586 pub orig_client_order_id: Option<String>,
587 pub side: String,
589 pub quantity: String,
591 pub price: String,
593}
594
595#[derive(Debug, Clone, Serialize)]
597#[serde(rename_all = "camelCase")]
598pub struct ListenKeyParams {
599 pub listen_key: String,
601}
602
603#[derive(Clone, Debug, Serialize, Builder)]
609#[builder(setter(into, strip_option))]
610#[serde(rename_all = "camelCase")]
611pub struct BinanceNewAlgoOrderParams {
612 pub symbol: String,
614 pub side: BinanceSide,
616 #[serde(rename = "type")]
618 pub order_type: BinanceFuturesOrderType,
619 #[serde(rename = "algoType")]
621 pub algo_type: BinanceAlgoType,
622 #[serde(rename = "positionSide", skip_serializing_if = "Option::is_none")]
624 #[builder(default)]
625 pub position_side: Option<BinancePositionSide>,
626 #[serde(skip_serializing_if = "Option::is_none")]
628 #[builder(default)]
629 pub quantity: Option<String>,
630 #[serde(skip_serializing_if = "Option::is_none")]
632 #[builder(default)]
633 pub price: Option<String>,
634 #[serde(rename = "triggerPrice", skip_serializing_if = "Option::is_none")]
636 #[builder(default)]
637 pub trigger_price: Option<String>,
638 #[serde(rename = "timeInForce", skip_serializing_if = "Option::is_none")]
640 #[builder(default)]
641 pub time_in_force: Option<BinanceTimeInForce>,
642 #[serde(rename = "workingType", skip_serializing_if = "Option::is_none")]
644 #[builder(default)]
645 pub working_type: Option<BinanceWorkingType>,
646 #[serde(rename = "closePosition", skip_serializing_if = "Option::is_none")]
648 #[builder(default)]
649 pub close_position: Option<bool>,
650 #[serde(rename = "priceProtect", skip_serializing_if = "Option::is_none")]
652 #[builder(default)]
653 pub price_protect: Option<bool>,
654 #[serde(rename = "reduceOnly", skip_serializing_if = "Option::is_none")]
656 #[builder(default)]
657 pub reduce_only: Option<bool>,
658 #[serde(rename = "activatePrice", skip_serializing_if = "Option::is_none")]
660 #[builder(default)]
661 pub activation_price: Option<String>,
662 #[serde(rename = "callbackRate", skip_serializing_if = "Option::is_none")]
664 #[builder(default)]
665 pub callback_rate: Option<String>,
666 #[serde(rename = "clientAlgoId", skip_serializing_if = "Option::is_none")]
668 #[builder(default)]
669 pub client_algo_id: Option<String>,
670 #[serde(rename = "goodTillDate", skip_serializing_if = "Option::is_none")]
672 #[builder(default)]
673 pub good_till_date: Option<i64>,
674 #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
676 #[builder(default)]
677 pub recv_window: Option<u64>,
678}
679
680#[derive(Clone, Debug, Default, Serialize, Builder)]
682#[builder(setter(into, strip_option), default)]
683#[serde(rename_all = "camelCase")]
684pub struct BinanceAlgoOrderQueryParams {
685 #[serde(rename = "algoId", skip_serializing_if = "Option::is_none")]
687 pub algo_id: Option<i64>,
688 #[serde(rename = "clientAlgoId", skip_serializing_if = "Option::is_none")]
690 pub client_algo_id: Option<String>,
691 #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
693 pub recv_window: Option<u64>,
694}
695
696#[derive(Clone, Debug, Default, Serialize, Builder)]
698#[builder(setter(into, strip_option), default)]
699#[serde(rename_all = "camelCase")]
700pub struct BinanceOpenAlgoOrdersParams {
701 #[serde(skip_serializing_if = "Option::is_none")]
703 pub symbol: Option<String>,
704 #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
706 pub recv_window: Option<u64>,
707}
708
709#[derive(Clone, Debug, Serialize, Builder)]
711#[builder(setter(into, strip_option))]
712#[serde(rename_all = "camelCase")]
713pub struct BinanceAllAlgoOrdersParams {
714 pub symbol: String,
716 #[serde(rename = "algoId", skip_serializing_if = "Option::is_none")]
718 #[builder(default)]
719 pub algo_id: Option<i64>,
720 #[serde(rename = "startTime", skip_serializing_if = "Option::is_none")]
722 #[builder(default)]
723 pub start_time: Option<i64>,
724 #[serde(rename = "endTime", skip_serializing_if = "Option::is_none")]
726 #[builder(default)]
727 pub end_time: Option<i64>,
728 #[serde(skip_serializing_if = "Option::is_none")]
730 #[builder(default)]
731 pub page: Option<u32>,
732 #[serde(skip_serializing_if = "Option::is_none")]
734 #[builder(default)]
735 pub limit: Option<u32>,
736 #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
738 #[builder(default)]
739 pub recv_window: Option<u64>,
740}
741
742#[derive(Clone, Debug, Serialize, Builder)]
744#[builder(setter(into, strip_option))]
745#[serde(rename_all = "camelCase")]
746pub struct BinanceCancelAllAlgoOrdersParams {
747 pub symbol: String,
749 #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
751 #[builder(default)]
752 pub recv_window: Option<u64>,
753}
754
755#[cfg(test)]
756mod tests {
757 use rstest::rstest;
758
759 use super::*;
760
761 #[rstest]
762 fn test_depth_params_builder() {
763 let params = BinanceDepthParamsBuilder::default()
764 .symbol("BTCUSDT")
765 .limit(100u32)
766 .build()
767 .unwrap();
768
769 assert_eq!(params.symbol, "BTCUSDT");
770 assert_eq!(params.limit, Some(100));
771 }
772
773 #[rstest]
774 fn test_ticker_params_serialization() {
775 let params = BinanceTicker24hrParams {
776 symbol: Some("BTCUSDT".to_string()),
777 };
778
779 let serialized = serde_urlencoded::to_string(¶ms).unwrap();
780 assert_eq!(serialized, "symbol=BTCUSDT");
781 }
782
783 #[rstest]
784 fn test_agg_trades_params_serialization() {
785 let params = BinanceAggTradesParams {
786 symbol: "BTCUSDT".to_string(),
787 from_id: Some(123),
788 start_time: Some(1_700_000_000_001),
789 end_time: Some(1_700_000_000_999),
790 limit: Some(456),
791 };
792
793 let serialized = serde_urlencoded::to_string(¶ms).unwrap();
794
795 assert_eq!(
796 serialized,
797 "symbol=BTCUSDT&fromId=123&startTime=1700000000001&endTime=1700000000999&limit=456"
798 );
799 }
800
801 #[rstest]
802 fn test_order_query_params_builder() {
803 let params = BinanceOrderQueryParamsBuilder::default()
804 .symbol("BTCUSDT")
805 .order_id(12345_i64)
806 .recv_window(5_000_u64)
807 .build()
808 .unwrap();
809
810 assert_eq!(params.symbol, "BTCUSDT");
811 assert_eq!(params.order_id, Some(12345));
812 assert_eq!(params.recv_window, Some(5_000));
813 }
814
815 #[rstest]
816 fn test_income_history_params_serialization() {
817 let params = BinanceIncomeHistoryParamsBuilder::default()
818 .symbol("ETHUSDT")
819 .income_type(BinanceIncomeType::FundingFee)
820 .limit(50_u32)
821 .build()
822 .unwrap();
823
824 let serialized = serde_urlencoded::to_string(¶ms).unwrap();
825 assert_eq!(serialized, "symbol=ETHUSDT&incomeType=FUNDING_FEE&limit=50");
826 }
827
828 #[rstest]
829 fn test_open_orders_params_builder() {
830 let params = BinanceOpenOrdersParamsBuilder::default()
831 .symbol("BNBUSDT")
832 .build()
833 .unwrap();
834
835 assert_eq!(params.symbol.as_deref(), Some("BNBUSDT"));
836 assert!(params.recv_window.is_none());
837 }
838
839 #[rstest]
840 fn test_new_algo_order_params_serialization_uses_activate_price() {
841 let params = BinanceNewAlgoOrderParamsBuilder::default()
842 .symbol("ETHUSDT")
843 .side(BinanceSide::Sell)
844 .order_type(BinanceFuturesOrderType::TrailingStopMarket)
845 .algo_type(BinanceAlgoType::Conditional)
846 .quantity("0.1")
847 .activation_price("10000.00")
848 .callback_rate("0.25")
849 .build()
850 .unwrap();
851
852 let serialized = serde_urlencoded::to_string(¶ms).unwrap();
853 let query: std::collections::HashMap<String, String> =
854 serde_urlencoded::from_str(&serialized).unwrap();
855
856 assert_eq!(query.get("activatePrice"), Some(&"10000.00".to_string()));
857 assert_eq!(query.get("callbackRate"), Some(&"0.25".to_string()));
858 assert!(!query.contains_key("activationPrice"));
859 }
860
861 #[rstest]
862 fn test_new_order_params_with_price_match_serializes_correctly() {
863 let params = BinanceNewOrderParams {
864 symbol: "BTCUSDT".to_string(),
865 side: BinanceSide::Buy,
866 order_type: BinanceFuturesOrderType::Limit,
867 time_in_force: Some(BinanceTimeInForce::Gtc),
868 quantity: Some("0.001".to_string()),
869 price: None,
870 new_client_order_id: Some("test-order-001".to_string()),
871 stop_price: None,
872 reduce_only: None,
873 position_side: None,
874 close_position: None,
875 activation_price: None,
876 callback_rate: None,
877 working_type: None,
878 price_protect: None,
879 new_order_resp_type: None,
880 good_till_date: None,
881 recv_window: None,
882 price_match: Some(BinancePriceMatch::Opponent5),
883 self_trade_prevention_mode: None,
884 };
885
886 let serialized = serde_urlencoded::to_string(¶ms).unwrap();
887 let query: std::collections::HashMap<String, String> =
888 serde_urlencoded::from_str(&serialized).unwrap();
889
890 assert_eq!(query.get("priceMatch"), Some(&"OPPONENT_5".to_string()));
891 assert!(!query.contains_key("price"));
892 assert_eq!(query.get("symbol"), Some(&"BTCUSDT".to_string()));
893 assert_eq!(query.get("side"), Some(&"BUY".to_string()));
894 assert_eq!(query.get("type"), Some(&"LIMIT".to_string()));
895 }
896
897 #[rstest]
898 fn test_new_order_params_without_price_match_omits_field() {
899 let params = BinanceNewOrderParams {
900 symbol: "BTCUSDT".to_string(),
901 side: BinanceSide::Buy,
902 order_type: BinanceFuturesOrderType::Limit,
903 time_in_force: Some(BinanceTimeInForce::Gtc),
904 quantity: Some("0.001".to_string()),
905 price: Some("50000.00".to_string()),
906 new_client_order_id: Some("test-order-002".to_string()),
907 stop_price: None,
908 reduce_only: None,
909 position_side: None,
910 close_position: None,
911 activation_price: None,
912 callback_rate: None,
913 working_type: None,
914 price_protect: None,
915 new_order_resp_type: None,
916 good_till_date: None,
917 recv_window: None,
918 price_match: None,
919 self_trade_prevention_mode: None,
920 };
921
922 let serialized = serde_urlencoded::to_string(¶ms).unwrap();
923 let query: std::collections::HashMap<String, String> =
924 serde_urlencoded::from_str(&serialized).unwrap();
925
926 assert!(!query.contains_key("priceMatch"));
927 assert_eq!(query.get("price"), Some(&"50000.00".to_string()));
928 }
929}