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 BinanceKlinesParams {
53 pub symbol: String,
55 pub interval: String,
57 #[serde(skip_serializing_if = "Option::is_none")]
59 #[serde(rename = "startTime")]
60 pub start_time: Option<i64>,
61 #[serde(skip_serializing_if = "Option::is_none")]
63 #[serde(rename = "endTime")]
64 pub end_time: Option<i64>,
65 #[serde(skip_serializing_if = "Option::is_none")]
67 pub limit: Option<u32>,
68}
69
70#[derive(Clone, Debug, Deserialize, Serialize, Default, Builder)]
72#[builder(default)]
73#[builder(setter(into, strip_option))]
74pub struct BinanceTicker24hrParams {
75 #[serde(skip_serializing_if = "Option::is_none")]
77 pub symbol: Option<String>,
78}
79
80#[derive(Clone, Debug, Deserialize, Serialize, Default, Builder)]
82#[builder(default)]
83#[builder(setter(into, strip_option))]
84pub struct BinanceBookTickerParams {
85 #[serde(skip_serializing_if = "Option::is_none")]
87 pub symbol: Option<String>,
88}
89
90#[derive(Clone, Debug, Deserialize, Serialize, Default, Builder)]
92#[builder(default)]
93#[builder(setter(into, strip_option))]
94pub struct BinanceMarkPriceParams {
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 BinanceFundingRateParams {
105 #[serde(skip_serializing_if = "Option::is_none")]
107 pub symbol: Option<String>,
108 #[serde(rename = "startTime", skip_serializing_if = "Option::is_none")]
110 pub start_time: Option<i64>,
111 #[serde(rename = "endTime", skip_serializing_if = "Option::is_none")]
113 pub end_time: Option<i64>,
114 #[serde(skip_serializing_if = "Option::is_none")]
116 pub limit: Option<u32>,
117}
118
119#[derive(Clone, Debug, Deserialize, Serialize, Builder)]
121#[builder(setter(into))]
122pub struct BinanceOpenInterestParams {
123 pub symbol: String,
125}
126
127#[derive(Clone, Debug, Default, Deserialize, Serialize, Builder)]
129#[builder(setter(into, strip_option), default)]
130pub struct BinanceOpenInterestHistParams {
131 #[serde(skip_serializing_if = "Option::is_none")]
133 pub symbol: Option<String>,
134 #[serde(skip_serializing_if = "Option::is_none")]
136 pub pair: Option<String>,
137 #[serde(rename = "contractType", skip_serializing_if = "Option::is_none")]
139 pub contract_type: Option<String>,
140 pub period: String,
142 #[serde(rename = "startTime", skip_serializing_if = "Option::is_none")]
144 pub start_time: Option<i64>,
145 #[serde(rename = "endTime", skip_serializing_if = "Option::is_none")]
147 pub end_time: Option<i64>,
148 #[serde(skip_serializing_if = "Option::is_none")]
150 pub limit: Option<u32>,
151}
152
153#[derive(Clone, Debug, Deserialize, Serialize, Default, Builder)]
155#[builder(default)]
156#[builder(setter(into, strip_option))]
157pub struct BinanceFuturesBalanceParams {
158 #[serde(skip_serializing_if = "Option::is_none")]
160 pub asset: Option<String>,
161 #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
163 pub recv_window: Option<u64>,
164}
165
166#[derive(Clone, Debug, Deserialize, Serialize, Default, Builder)]
168#[builder(default)]
169#[builder(setter(into, strip_option))]
170pub struct BinancePositionRiskParams {
171 #[serde(skip_serializing_if = "Option::is_none")]
173 pub symbol: Option<String>,
174 #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
176 pub recv_window: Option<u64>,
177}
178
179#[derive(Clone, Debug, Deserialize, Serialize, Default, Builder)]
181#[builder(default)]
182#[builder(setter(into, strip_option))]
183pub struct BinanceIncomeHistoryParams {
184 #[serde(skip_serializing_if = "Option::is_none")]
186 pub symbol: Option<String>,
187 #[serde(rename = "incomeType", skip_serializing_if = "Option::is_none")]
189 pub income_type: Option<BinanceIncomeType>,
190 #[serde(rename = "startTime", skip_serializing_if = "Option::is_none")]
192 pub start_time: Option<i64>,
193 #[serde(rename = "endTime", skip_serializing_if = "Option::is_none")]
195 pub end_time: Option<i64>,
196 #[serde(skip_serializing_if = "Option::is_none")]
198 pub limit: Option<u32>,
199 #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
201 pub recv_window: Option<u64>,
202}
203
204#[derive(Clone, Debug, Default, Deserialize, Serialize, Builder)]
206#[builder(setter(into, strip_option), default)]
207pub struct BinanceUserTradesParams {
208 pub symbol: String,
210 #[serde(rename = "orderId", skip_serializing_if = "Option::is_none")]
212 pub order_id: Option<i64>,
213 #[serde(rename = "startTime", skip_serializing_if = "Option::is_none")]
215 pub start_time: Option<i64>,
216 #[serde(rename = "endTime", skip_serializing_if = "Option::is_none")]
218 pub end_time: Option<i64>,
219 #[serde(rename = "fromId", skip_serializing_if = "Option::is_none")]
221 pub from_id: Option<i64>,
222 #[serde(skip_serializing_if = "Option::is_none")]
224 pub limit: Option<u32>,
225 #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
227 pub recv_window: Option<u64>,
228}
229
230#[derive(Clone, Debug, Deserialize, Serialize, Default, Builder)]
232#[builder(default)]
233#[builder(setter(into, strip_option))]
234pub struct BinanceOpenOrdersParams {
235 #[serde(skip_serializing_if = "Option::is_none")]
237 pub symbol: Option<String>,
238 #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
240 pub recv_window: Option<u64>,
241}
242
243#[derive(Clone, Debug, Default, Deserialize, Serialize, Builder)]
245#[builder(setter(into, strip_option), default)]
246pub struct BinanceOrderQueryParams {
247 pub symbol: String,
249 #[serde(rename = "orderId", skip_serializing_if = "Option::is_none")]
251 pub order_id: Option<i64>,
252 #[serde(rename = "origClientOrderId", skip_serializing_if = "Option::is_none")]
254 pub orig_client_order_id: Option<String>,
255 #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
257 pub recv_window: Option<u64>,
258}
259
260#[derive(Clone, Debug, Deserialize, Serialize, Builder)]
262#[builder(setter(into, strip_option))]
263pub struct BinanceNewOrderParams {
264 pub symbol: String,
266 pub side: BinanceSide,
268 #[serde(rename = "type")]
270 pub order_type: BinanceFuturesOrderType,
271 #[serde(rename = "positionSide", skip_serializing_if = "Option::is_none")]
273 #[builder(default)]
274 pub position_side: Option<BinancePositionSide>,
275 #[serde(rename = "timeInForce", skip_serializing_if = "Option::is_none")]
277 #[builder(default)]
278 pub time_in_force: Option<BinanceTimeInForce>,
279 #[serde(skip_serializing_if = "Option::is_none")]
281 #[builder(default)]
282 pub quantity: Option<String>,
283 #[serde(rename = "reduceOnly", skip_serializing_if = "Option::is_none")]
285 #[builder(default)]
286 pub reduce_only: Option<bool>,
287 #[serde(skip_serializing_if = "Option::is_none")]
289 #[builder(default)]
290 pub price: Option<String>,
291 #[serde(rename = "newClientOrderId", skip_serializing_if = "Option::is_none")]
293 #[builder(default)]
294 pub new_client_order_id: Option<String>,
295 #[serde(rename = "stopPrice", skip_serializing_if = "Option::is_none")]
297 #[builder(default)]
298 pub stop_price: Option<String>,
299 #[serde(rename = "closePosition", skip_serializing_if = "Option::is_none")]
301 #[builder(default)]
302 pub close_position: Option<bool>,
303 #[serde(rename = "activationPrice", skip_serializing_if = "Option::is_none")]
305 #[builder(default)]
306 pub activation_price: Option<String>,
307 #[serde(rename = "callbackRate", skip_serializing_if = "Option::is_none")]
309 #[builder(default)]
310 pub callback_rate: Option<String>,
311 #[serde(rename = "workingType", skip_serializing_if = "Option::is_none")]
313 #[builder(default)]
314 pub working_type: Option<BinanceWorkingType>,
315 #[serde(rename = "priceProtect", skip_serializing_if = "Option::is_none")]
317 #[builder(default)]
318 pub price_protect: Option<bool>,
319 #[serde(rename = "newOrderRespType", skip_serializing_if = "Option::is_none")]
321 #[builder(default)]
322 pub new_order_resp_type: Option<String>,
323 #[serde(rename = "goodTillDate", skip_serializing_if = "Option::is_none")]
325 #[builder(default)]
326 pub good_till_date: Option<i64>,
327 #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
329 #[builder(default)]
330 pub recv_window: Option<u64>,
331 #[serde(rename = "priceMatch", skip_serializing_if = "Option::is_none")]
333 #[builder(default)]
334 pub price_match: Option<BinancePriceMatch>,
335 #[serde(
337 rename = "selfTradePreventionMode",
338 skip_serializing_if = "Option::is_none"
339 )]
340 #[builder(default)]
341 pub self_trade_prevention_mode: Option<BinanceSelfTradePreventionMode>,
342}
343
344#[derive(Clone, Debug, Default, Deserialize, Serialize, Builder)]
346#[builder(setter(into, strip_option), default)]
347pub struct BinanceCancelOrderParams {
348 pub symbol: String,
350 #[serde(rename = "orderId", skip_serializing_if = "Option::is_none")]
352 pub order_id: Option<i64>,
353 #[serde(rename = "origClientOrderId", skip_serializing_if = "Option::is_none")]
355 pub orig_client_order_id: Option<String>,
356 #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
358 pub recv_window: Option<u64>,
359}
360
361#[derive(Clone, Debug, Default, Deserialize, Serialize, Builder)]
363#[builder(setter(into, strip_option), default)]
364pub struct BinanceCancelAllOrdersParams {
365 pub symbol: String,
367 #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
369 pub recv_window: Option<u64>,
370}
371
372#[derive(Clone, Debug, Deserialize, Serialize, Builder)]
374#[builder(setter(into, strip_option))]
375pub struct BinanceModifyOrderParams {
376 pub symbol: String,
378 #[serde(rename = "orderId", skip_serializing_if = "Option::is_none")]
380 #[builder(default)]
381 pub order_id: Option<i64>,
382 #[serde(rename = "origClientOrderId", skip_serializing_if = "Option::is_none")]
384 #[builder(default)]
385 pub orig_client_order_id: Option<String>,
386 pub side: BinanceSide,
388 pub quantity: String,
390 pub price: String,
392 #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
394 #[builder(default)]
395 pub recv_window: Option<u64>,
396}
397
398#[derive(Clone, Debug, Default, Deserialize, Serialize, Builder)]
400#[builder(setter(into, strip_option), default)]
401pub struct BinanceAllOrdersParams {
402 pub symbol: String,
404 #[serde(rename = "orderId", skip_serializing_if = "Option::is_none")]
406 pub order_id: Option<i64>,
407 #[serde(rename = "startTime", skip_serializing_if = "Option::is_none")]
409 pub start_time: Option<i64>,
410 #[serde(rename = "endTime", skip_serializing_if = "Option::is_none")]
412 pub end_time: Option<i64>,
413 #[serde(skip_serializing_if = "Option::is_none")]
415 pub limit: Option<u32>,
416 #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
418 pub recv_window: Option<u64>,
419}
420
421#[derive(Clone, Debug, Deserialize, Serialize, Builder)]
423#[builder(setter(into))]
424pub struct BinanceSetLeverageParams {
425 pub symbol: String,
427 pub leverage: u32,
429 #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
431 #[builder(default)]
432 pub recv_window: Option<u64>,
433}
434
435#[derive(Clone, Debug, Deserialize, Serialize, Builder)]
437#[builder(setter(into))]
438pub struct BinanceSetMarginTypeParams {
439 pub symbol: String,
441 #[serde(rename = "marginType")]
443 pub margin_type: BinanceMarginType,
444 #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
446 #[builder(default)]
447 pub recv_window: Option<u64>,
448}
449
450#[derive(Clone, Debug, Serialize)]
452#[serde(rename_all = "camelCase")]
453pub struct BatchOrderItem {
454 pub symbol: String,
456 pub side: String,
458 #[serde(rename = "type")]
460 pub order_type: String,
461 #[serde(skip_serializing_if = "Option::is_none")]
463 pub time_in_force: Option<String>,
464 #[serde(skip_serializing_if = "Option::is_none")]
466 pub quantity: Option<String>,
467 #[serde(skip_serializing_if = "Option::is_none")]
469 pub price: Option<String>,
470 #[serde(skip_serializing_if = "Option::is_none")]
472 pub reduce_only: Option<bool>,
473 #[serde(skip_serializing_if = "Option::is_none")]
475 pub new_client_order_id: Option<String>,
476 #[serde(skip_serializing_if = "Option::is_none")]
478 pub stop_price: Option<String>,
479 #[serde(skip_serializing_if = "Option::is_none")]
481 pub position_side: Option<String>,
482 #[serde(skip_serializing_if = "Option::is_none")]
484 pub activation_price: Option<String>,
485 #[serde(skip_serializing_if = "Option::is_none")]
487 pub callback_rate: Option<String>,
488 #[serde(skip_serializing_if = "Option::is_none")]
490 pub working_type: Option<String>,
491 #[serde(skip_serializing_if = "Option::is_none")]
493 pub price_protect: Option<bool>,
494 #[serde(skip_serializing_if = "Option::is_none")]
496 pub close_position: Option<bool>,
497 #[serde(skip_serializing_if = "Option::is_none")]
499 pub good_till_date: Option<i64>,
500 #[serde(skip_serializing_if = "Option::is_none")]
502 pub price_match: Option<String>,
503 #[serde(skip_serializing_if = "Option::is_none")]
505 pub self_trade_prevention_mode: Option<String>,
506}
507
508#[derive(Clone, Debug, Serialize)]
510#[serde(rename_all = "camelCase")]
511pub struct BatchCancelItem {
512 pub symbol: String,
514 #[serde(skip_serializing_if = "Option::is_none")]
516 pub order_id: Option<i64>,
517 #[serde(skip_serializing_if = "Option::is_none")]
519 pub orig_client_order_id: Option<String>,
520}
521
522impl BatchCancelItem {
523 #[must_use]
525 pub fn by_order_id(symbol: impl Into<String>, order_id: i64) -> Self {
526 Self {
527 symbol: symbol.into(),
528 order_id: Some(order_id),
529 orig_client_order_id: None,
530 }
531 }
532
533 #[must_use]
535 pub fn by_client_order_id(
536 symbol: impl Into<String>,
537 client_order_id: impl Into<String>,
538 ) -> Self {
539 Self {
540 symbol: symbol.into(),
541 order_id: None,
542 orig_client_order_id: Some(client_order_id.into()),
543 }
544 }
545}
546
547#[derive(Clone, Debug, Serialize)]
549#[serde(rename_all = "camelCase")]
550pub struct BatchModifyItem {
551 pub symbol: String,
553 #[serde(skip_serializing_if = "Option::is_none")]
555 pub order_id: Option<i64>,
556 #[serde(skip_serializing_if = "Option::is_none")]
558 pub orig_client_order_id: Option<String>,
559 pub side: String,
561 pub quantity: String,
563 pub price: String,
565}
566
567#[derive(Debug, Clone, Serialize)]
569#[serde(rename_all = "camelCase")]
570pub struct ListenKeyParams {
571 pub listen_key: String,
573}
574
575#[derive(Clone, Debug, Serialize, Builder)]
581#[builder(setter(into, strip_option))]
582#[serde(rename_all = "camelCase")]
583pub struct BinanceNewAlgoOrderParams {
584 pub symbol: String,
586 pub side: BinanceSide,
588 #[serde(rename = "type")]
590 pub order_type: BinanceFuturesOrderType,
591 #[serde(rename = "algoType")]
593 pub algo_type: BinanceAlgoType,
594 #[serde(rename = "positionSide", skip_serializing_if = "Option::is_none")]
596 #[builder(default)]
597 pub position_side: Option<BinancePositionSide>,
598 #[serde(skip_serializing_if = "Option::is_none")]
600 #[builder(default)]
601 pub quantity: Option<String>,
602 #[serde(skip_serializing_if = "Option::is_none")]
604 #[builder(default)]
605 pub price: Option<String>,
606 #[serde(rename = "triggerPrice", skip_serializing_if = "Option::is_none")]
608 #[builder(default)]
609 pub trigger_price: Option<String>,
610 #[serde(rename = "timeInForce", skip_serializing_if = "Option::is_none")]
612 #[builder(default)]
613 pub time_in_force: Option<BinanceTimeInForce>,
614 #[serde(rename = "workingType", skip_serializing_if = "Option::is_none")]
616 #[builder(default)]
617 pub working_type: Option<BinanceWorkingType>,
618 #[serde(rename = "closePosition", skip_serializing_if = "Option::is_none")]
620 #[builder(default)]
621 pub close_position: Option<bool>,
622 #[serde(rename = "priceProtect", skip_serializing_if = "Option::is_none")]
624 #[builder(default)]
625 pub price_protect: Option<bool>,
626 #[serde(rename = "reduceOnly", skip_serializing_if = "Option::is_none")]
628 #[builder(default)]
629 pub reduce_only: Option<bool>,
630 #[serde(rename = "activatePrice", skip_serializing_if = "Option::is_none")]
632 #[builder(default)]
633 pub activation_price: Option<String>,
634 #[serde(rename = "callbackRate", skip_serializing_if = "Option::is_none")]
636 #[builder(default)]
637 pub callback_rate: Option<String>,
638 #[serde(rename = "clientAlgoId", skip_serializing_if = "Option::is_none")]
640 #[builder(default)]
641 pub client_algo_id: Option<String>,
642 #[serde(rename = "goodTillDate", skip_serializing_if = "Option::is_none")]
644 #[builder(default)]
645 pub good_till_date: Option<i64>,
646 #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
648 #[builder(default)]
649 pub recv_window: Option<u64>,
650}
651
652#[derive(Clone, Debug, Default, Serialize, Builder)]
654#[builder(setter(into, strip_option), default)]
655#[serde(rename_all = "camelCase")]
656pub struct BinanceAlgoOrderQueryParams {
657 #[serde(rename = "algoId", skip_serializing_if = "Option::is_none")]
659 pub algo_id: Option<i64>,
660 #[serde(rename = "clientAlgoId", skip_serializing_if = "Option::is_none")]
662 pub client_algo_id: Option<String>,
663 #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
665 pub recv_window: Option<u64>,
666}
667
668#[derive(Clone, Debug, Default, Serialize, Builder)]
670#[builder(setter(into, strip_option), default)]
671#[serde(rename_all = "camelCase")]
672pub struct BinanceOpenAlgoOrdersParams {
673 #[serde(skip_serializing_if = "Option::is_none")]
675 pub symbol: Option<String>,
676 #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
678 pub recv_window: Option<u64>,
679}
680
681#[derive(Clone, Debug, Serialize, Builder)]
683#[builder(setter(into, strip_option))]
684#[serde(rename_all = "camelCase")]
685pub struct BinanceAllAlgoOrdersParams {
686 pub symbol: String,
688 #[serde(rename = "startTime", skip_serializing_if = "Option::is_none")]
690 #[builder(default)]
691 pub start_time: Option<i64>,
692 #[serde(rename = "endTime", skip_serializing_if = "Option::is_none")]
694 #[builder(default)]
695 pub end_time: Option<i64>,
696 #[serde(skip_serializing_if = "Option::is_none")]
698 #[builder(default)]
699 pub page: Option<u32>,
700 #[serde(skip_serializing_if = "Option::is_none")]
702 #[builder(default)]
703 pub limit: Option<u32>,
704 #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
706 #[builder(default)]
707 pub recv_window: Option<u64>,
708}
709
710#[derive(Clone, Debug, Serialize, Builder)]
712#[builder(setter(into, strip_option))]
713#[serde(rename_all = "camelCase")]
714pub struct BinanceCancelAllAlgoOrdersParams {
715 pub symbol: String,
717 #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
719 #[builder(default)]
720 pub recv_window: Option<u64>,
721}
722
723#[cfg(test)]
724mod tests {
725 use rstest::rstest;
726
727 use super::*;
728
729 #[rstest]
730 fn test_depth_params_builder() {
731 let params = BinanceDepthParamsBuilder::default()
732 .symbol("BTCUSDT")
733 .limit(100u32)
734 .build()
735 .unwrap();
736
737 assert_eq!(params.symbol, "BTCUSDT");
738 assert_eq!(params.limit, Some(100));
739 }
740
741 #[rstest]
742 fn test_ticker_params_serialization() {
743 let params = BinanceTicker24hrParams {
744 symbol: Some("BTCUSDT".to_string()),
745 };
746
747 let serialized = serde_urlencoded::to_string(¶ms).unwrap();
748 assert_eq!(serialized, "symbol=BTCUSDT");
749 }
750
751 #[rstest]
752 fn test_order_query_params_builder() {
753 let params = BinanceOrderQueryParamsBuilder::default()
754 .symbol("BTCUSDT")
755 .order_id(12345_i64)
756 .recv_window(5_000_u64)
757 .build()
758 .unwrap();
759
760 assert_eq!(params.symbol, "BTCUSDT");
761 assert_eq!(params.order_id, Some(12345));
762 assert_eq!(params.recv_window, Some(5_000));
763 }
764
765 #[rstest]
766 fn test_income_history_params_serialization() {
767 let params = BinanceIncomeHistoryParamsBuilder::default()
768 .symbol("ETHUSDT")
769 .income_type(BinanceIncomeType::FundingFee)
770 .limit(50_u32)
771 .build()
772 .unwrap();
773
774 let serialized = serde_urlencoded::to_string(¶ms).unwrap();
775 assert_eq!(serialized, "symbol=ETHUSDT&incomeType=FUNDING_FEE&limit=50");
776 }
777
778 #[rstest]
779 fn test_open_orders_params_builder() {
780 let params = BinanceOpenOrdersParamsBuilder::default()
781 .symbol("BNBUSDT")
782 .build()
783 .unwrap();
784
785 assert_eq!(params.symbol.as_deref(), Some("BNBUSDT"));
786 assert!(params.recv_window.is_none());
787 }
788
789 #[rstest]
790 fn test_new_algo_order_params_serialization_uses_activate_price() {
791 let params = BinanceNewAlgoOrderParamsBuilder::default()
792 .symbol("ETHUSDT")
793 .side(BinanceSide::Sell)
794 .order_type(BinanceFuturesOrderType::TrailingStopMarket)
795 .algo_type(BinanceAlgoType::Conditional)
796 .quantity("0.1")
797 .activation_price("10000.00")
798 .callback_rate("0.25")
799 .build()
800 .unwrap();
801
802 let serialized = serde_urlencoded::to_string(¶ms).unwrap();
803 let query: std::collections::HashMap<String, String> =
804 serde_urlencoded::from_str(&serialized).unwrap();
805
806 assert_eq!(query.get("activatePrice"), Some(&"10000.00".to_string()));
807 assert_eq!(query.get("callbackRate"), Some(&"0.25".to_string()));
808 assert!(!query.contains_key("activationPrice"));
809 }
810
811 #[rstest]
812 fn test_new_order_params_with_price_match_serializes_correctly() {
813 let params = BinanceNewOrderParams {
814 symbol: "BTCUSDT".to_string(),
815 side: BinanceSide::Buy,
816 order_type: BinanceFuturesOrderType::Limit,
817 time_in_force: Some(BinanceTimeInForce::Gtc),
818 quantity: Some("0.001".to_string()),
819 price: None,
820 new_client_order_id: Some("test-order-001".to_string()),
821 stop_price: None,
822 reduce_only: None,
823 position_side: None,
824 close_position: None,
825 activation_price: None,
826 callback_rate: None,
827 working_type: None,
828 price_protect: None,
829 new_order_resp_type: None,
830 good_till_date: None,
831 recv_window: None,
832 price_match: Some(BinancePriceMatch::Opponent5),
833 self_trade_prevention_mode: None,
834 };
835
836 let serialized = serde_urlencoded::to_string(¶ms).unwrap();
837 let query: std::collections::HashMap<String, String> =
838 serde_urlencoded::from_str(&serialized).unwrap();
839
840 assert_eq!(query.get("priceMatch"), Some(&"OPPONENT_5".to_string()));
841 assert!(!query.contains_key("price"));
842 assert_eq!(query.get("symbol"), Some(&"BTCUSDT".to_string()));
843 assert_eq!(query.get("side"), Some(&"BUY".to_string()));
844 assert_eq!(query.get("type"), Some(&"LIMIT".to_string()));
845 }
846
847 #[rstest]
848 fn test_new_order_params_without_price_match_omits_field() {
849 let params = BinanceNewOrderParams {
850 symbol: "BTCUSDT".to_string(),
851 side: BinanceSide::Buy,
852 order_type: BinanceFuturesOrderType::Limit,
853 time_in_force: Some(BinanceTimeInForce::Gtc),
854 quantity: Some("0.001".to_string()),
855 price: Some("50000.00".to_string()),
856 new_client_order_id: Some("test-order-002".to_string()),
857 stop_price: None,
858 reduce_only: None,
859 position_side: None,
860 close_position: None,
861 activation_price: None,
862 callback_rate: None,
863 working_type: None,
864 price_protect: None,
865 new_order_resp_type: None,
866 good_till_date: None,
867 recv_window: None,
868 price_match: None,
869 self_trade_prevention_mode: None,
870 };
871
872 let serialized = serde_urlencoded::to_string(¶ms).unwrap();
873 let query: std::collections::HashMap<String, String> =
874 serde_urlencoded::from_str(&serialized).unwrap();
875
876 assert!(!query.contains_key("priceMatch"));
877 assert_eq!(query.get("price"), Some(&"50000.00".to_string()));
878 }
879}