1use derive_builder::Builder;
22use serde::{Deserialize, Serialize};
23
24use crate::common::enums::BinanceIncomeType;
25
26#[derive(Clone, Debug, Deserialize, Serialize, Default, Builder)]
31#[builder(default)]
32#[builder(setter(into, strip_option))]
33pub struct BinanceSpotExchangeInfoParams {
34 #[serde(skip_serializing_if = "Option::is_none")]
36 pub symbol: Option<String>,
37 #[serde(skip_serializing_if = "Option::is_none")]
39 pub symbols: Option<String>,
40 #[serde(skip_serializing_if = "Option::is_none")]
42 pub permissions: Option<String>,
43}
44
45#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
50pub enum BinanceKlineInterval {
51 #[serde(rename = "1s")]
53 Second1,
54 #[default]
56 #[serde(rename = "1m")]
57 Minute1,
58 #[serde(rename = "3m")]
60 Minute3,
61 #[serde(rename = "5m")]
63 Minute5,
64 #[serde(rename = "15m")]
66 Minute15,
67 #[serde(rename = "30m")]
69 Minute30,
70 #[serde(rename = "1h")]
72 Hour1,
73 #[serde(rename = "2h")]
75 Hour2,
76 #[serde(rename = "4h")]
78 Hour4,
79 #[serde(rename = "6h")]
81 Hour6,
82 #[serde(rename = "8h")]
84 Hour8,
85 #[serde(rename = "12h")]
87 Hour12,
88 #[serde(rename = "1d")]
90 Day1,
91 #[serde(rename = "3d")]
93 Day3,
94 #[serde(rename = "1w")]
96 Week1,
97 #[serde(rename = "1M")]
99 Month1,
100}
101
102impl BinanceKlineInterval {
103 #[must_use]
105 pub const fn as_str(self) -> &'static str {
106 match self {
107 Self::Second1 => "1s",
108 Self::Minute1 => "1m",
109 Self::Minute3 => "3m",
110 Self::Minute5 => "5m",
111 Self::Minute15 => "15m",
112 Self::Minute30 => "30m",
113 Self::Hour1 => "1h",
114 Self::Hour2 => "2h",
115 Self::Hour4 => "4h",
116 Self::Hour6 => "6h",
117 Self::Hour8 => "8h",
118 Self::Hour12 => "12h",
119 Self::Day1 => "1d",
120 Self::Day3 => "3d",
121 Self::Week1 => "1w",
122 Self::Month1 => "1M",
123 }
124 }
125
126 #[must_use]
128 pub const fn as_millis(self) -> i64 {
129 match self {
130 Self::Second1 => 1_000,
131 Self::Minute1 => 60_000,
132 Self::Minute3 => 180_000,
133 Self::Minute5 => 300_000,
134 Self::Minute15 => 900_000,
135 Self::Minute30 => 1_800_000,
136 Self::Hour1 => 3_600_000,
137 Self::Hour2 => 7_200_000,
138 Self::Hour4 => 14_400_000,
139 Self::Hour6 => 21_600_000,
140 Self::Hour8 => 28_800_000,
141 Self::Hour12 => 43_200_000,
142 Self::Day1 => 86_400_000,
143 Self::Day3 => 259_200_000,
144 Self::Week1 => 604_800_000,
145 Self::Month1 => 2_592_000_000, }
147 }
148}
149
150impl std::fmt::Display for BinanceKlineInterval {
151 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
152 write!(f, "{}", self.as_str())
153 }
154}
155
156#[derive(Clone, Debug, Deserialize, Serialize, Builder)]
161#[builder(setter(into, strip_option), default)]
162pub struct BinanceKlinesParams {
163 pub symbol: String,
165 pub interval: BinanceKlineInterval,
167 #[serde(rename = "startTime", skip_serializing_if = "Option::is_none")]
169 pub start_time: Option<i64>,
170 #[serde(rename = "endTime", skip_serializing_if = "Option::is_none")]
172 pub end_time: Option<i64>,
173 #[serde(rename = "timeZone", skip_serializing_if = "Option::is_none")]
175 pub time_zone: Option<String>,
176 #[serde(skip_serializing_if = "Option::is_none")]
178 pub limit: Option<u32>,
179}
180
181impl Default for BinanceKlinesParams {
182 fn default() -> Self {
183 Self {
184 symbol: String::new(),
185 interval: BinanceKlineInterval::Minute1,
186 start_time: None,
187 end_time: None,
188 time_zone: None,
189 limit: None,
190 }
191 }
192}
193
194#[derive(Clone, Debug, Default, Deserialize, Serialize, Builder)]
199#[builder(setter(into, strip_option), default)]
200pub struct BinanceTradesParams {
201 pub symbol: String,
203 #[serde(skip_serializing_if = "Option::is_none")]
205 pub limit: Option<u32>,
206}
207
208#[derive(Clone, Debug, Default, Deserialize, Serialize, Builder)]
213#[builder(setter(into, strip_option), default)]
214pub struct BinanceAggTradesParams {
215 pub symbol: String,
217 #[serde(rename = "fromId", skip_serializing_if = "Option::is_none")]
219 pub from_id: Option<i64>,
220 #[serde(rename = "startTime", skip_serializing_if = "Option::is_none")]
222 pub start_time: Option<i64>,
223 #[serde(rename = "endTime", skip_serializing_if = "Option::is_none")]
225 pub end_time: Option<i64>,
226 #[serde(skip_serializing_if = "Option::is_none")]
228 pub limit: Option<u32>,
229}
230
231#[derive(Clone, Debug, Default, Deserialize, Serialize, Builder)]
236#[builder(setter(into, strip_option), default)]
237pub struct BinanceDepthParams {
238 pub symbol: String,
240 #[serde(skip_serializing_if = "Option::is_none")]
244 pub limit: Option<u32>,
245}
246
247#[derive(Clone, Debug, Deserialize, Serialize, Default, Builder)]
252#[builder(default)]
253#[builder(setter(into, strip_option))]
254pub struct BinanceTicker24hrParams {
255 #[serde(skip_serializing_if = "Option::is_none")]
257 pub symbol: Option<String>,
258 #[serde(skip_serializing_if = "Option::is_none")]
260 pub symbols: Option<String>,
261 #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
263 pub response_type: Option<String>,
264}
265
266#[derive(Clone, Debug, Deserialize, Serialize, Default, Builder)]
271#[builder(default)]
272#[builder(setter(into, strip_option))]
273pub struct BinancePriceTickerParams {
274 #[serde(skip_serializing_if = "Option::is_none")]
276 pub symbol: Option<String>,
277 #[serde(skip_serializing_if = "Option::is_none")]
279 pub symbols: Option<String>,
280}
281
282#[derive(Clone, Debug, Deserialize, Serialize, Default, Builder)]
287#[builder(default)]
288#[builder(setter(into, strip_option))]
289pub struct BinanceBookTickerParams {
290 #[serde(skip_serializing_if = "Option::is_none")]
292 pub symbol: Option<String>,
293 #[serde(skip_serializing_if = "Option::is_none")]
295 pub symbols: Option<String>,
296}
297
298#[derive(Clone, Debug, Deserialize, Serialize, Default, Builder)]
303#[builder(default)]
304#[builder(setter(into, strip_option))]
305pub struct BinanceMarkPriceParams {
306 #[serde(skip_serializing_if = "Option::is_none")]
308 pub symbol: Option<String>,
309}
310
311#[derive(Clone, Debug, Deserialize, Serialize, Default, Builder)]
316#[builder(default)]
317#[builder(setter(into, strip_option))]
318pub struct BinanceFundingRateParams {
319 #[serde(skip_serializing_if = "Option::is_none")]
321 pub symbol: Option<String>,
322 #[serde(rename = "startTime", skip_serializing_if = "Option::is_none")]
324 pub start_time: Option<i64>,
325 #[serde(rename = "endTime", skip_serializing_if = "Option::is_none")]
327 pub end_time: Option<i64>,
328 #[serde(skip_serializing_if = "Option::is_none")]
330 pub limit: Option<u32>,
331}
332
333#[derive(Clone, Debug, Deserialize, Serialize, Builder)]
338#[builder(setter(into))]
339pub struct BinanceOpenInterestParams {
340 pub symbol: String,
342}
343
344#[derive(Clone, Debug, Deserialize, Serialize, Default, Builder)]
349#[builder(default)]
350#[builder(setter(into, strip_option))]
351pub struct BinanceFuturesBalanceParams {
352 #[serde(skip_serializing_if = "Option::is_none")]
354 pub asset: Option<String>,
355 #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
357 pub recv_window: Option<u64>,
358}
359
360#[derive(Clone, Debug, Deserialize, Serialize, Default, Builder)]
365#[builder(default)]
366#[builder(setter(into, strip_option))]
367pub struct BinancePositionRiskParams {
368 #[serde(skip_serializing_if = "Option::is_none")]
370 pub symbol: Option<String>,
371 #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
373 pub recv_window: Option<u64>,
374}
375
376#[derive(Clone, Debug, Deserialize, Serialize, Default, Builder)]
381#[builder(default)]
382#[builder(setter(into, strip_option))]
383pub struct BinanceIncomeHistoryParams {
384 #[serde(skip_serializing_if = "Option::is_none")]
386 pub symbol: Option<String>,
387 #[serde(rename = "incomeType", skip_serializing_if = "Option::is_none")]
389 pub income_type: Option<BinanceIncomeType>,
390 #[serde(rename = "startTime", skip_serializing_if = "Option::is_none")]
392 pub start_time: Option<i64>,
393 #[serde(rename = "endTime", skip_serializing_if = "Option::is_none")]
395 pub end_time: Option<i64>,
396 #[serde(skip_serializing_if = "Option::is_none")]
398 pub limit: Option<u32>,
399 #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
401 pub recv_window: Option<u64>,
402}
403
404#[derive(Clone, Debug, Default, Deserialize, Serialize, Builder)]
409#[builder(setter(into, strip_option), default)]
410pub struct BinanceUserTradesParams {
411 pub symbol: String,
413 #[serde(rename = "startTime", skip_serializing_if = "Option::is_none")]
415 pub start_time: Option<i64>,
416 #[serde(rename = "endTime", skip_serializing_if = "Option::is_none")]
418 pub end_time: Option<i64>,
419 #[serde(rename = "fromId", skip_serializing_if = "Option::is_none")]
421 pub from_id: Option<i64>,
422 #[serde(skip_serializing_if = "Option::is_none")]
424 pub limit: Option<u32>,
425 #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
427 pub recv_window: Option<u64>,
428}
429
430#[derive(Clone, Debug, Deserialize, Serialize, Default, Builder)]
435#[builder(default)]
436#[builder(setter(into, strip_option))]
437pub struct BinanceOpenOrdersParams {
438 #[serde(skip_serializing_if = "Option::is_none")]
440 pub symbol: Option<String>,
441 #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
443 pub recv_window: Option<u64>,
444}
445
446#[derive(Clone, Debug, Default, Deserialize, Serialize, Builder)]
451#[builder(setter(into, strip_option), default)]
452pub struct BinanceOrderQueryParams {
453 pub symbol: String,
455 #[serde(rename = "orderId", skip_serializing_if = "Option::is_none")]
457 pub order_id: Option<i64>,
458 #[serde(rename = "origClientOrderId", skip_serializing_if = "Option::is_none")]
460 pub orig_client_order_id: Option<String>,
461 #[serde(rename = "recvWindow", skip_serializing_if = "Option::is_none")]
463 pub recv_window: Option<u64>,
464}
465
466#[cfg(test)]
467mod tests {
468 use rstest::rstest;
469
470 use super::*;
471
472 #[rstest]
473 fn test_kline_interval_as_str() {
474 assert_eq!(BinanceKlineInterval::Second1.as_str(), "1s");
475 assert_eq!(BinanceKlineInterval::Minute1.as_str(), "1m");
476 assert_eq!(BinanceKlineInterval::Hour1.as_str(), "1h");
477 assert_eq!(BinanceKlineInterval::Day1.as_str(), "1d");
478 assert_eq!(BinanceKlineInterval::Week1.as_str(), "1w");
479 assert_eq!(BinanceKlineInterval::Month1.as_str(), "1M");
480 }
481
482 #[rstest]
483 fn test_kline_interval_as_millis() {
484 assert_eq!(BinanceKlineInterval::Second1.as_millis(), 1_000);
485 assert_eq!(BinanceKlineInterval::Minute1.as_millis(), 60_000);
486 assert_eq!(BinanceKlineInterval::Hour1.as_millis(), 3_600_000);
487 assert_eq!(BinanceKlineInterval::Day1.as_millis(), 86_400_000);
488 }
489
490 #[rstest]
491 fn test_klines_params_builder() {
492 let params = BinanceKlinesParamsBuilder::default()
493 .symbol("BTCUSDT")
494 .interval(BinanceKlineInterval::Hour1)
495 .start_time(1_700_000_000_000i64)
496 .limit(100u32)
497 .build()
498 .unwrap();
499
500 assert_eq!(params.symbol, "BTCUSDT");
501 assert_eq!(params.interval, BinanceKlineInterval::Hour1);
502 assert_eq!(params.start_time, Some(1_700_000_000_000));
503 assert_eq!(params.limit, Some(100));
504 }
505
506 #[rstest]
507 fn test_trades_params_builder() {
508 let params = BinanceTradesParamsBuilder::default()
509 .symbol("ETHUSDT")
510 .limit(500u32)
511 .build()
512 .unwrap();
513
514 assert_eq!(params.symbol, "ETHUSDT");
515 assert_eq!(params.limit, Some(500));
516 }
517
518 #[rstest]
519 fn test_depth_params_builder() {
520 let params = BinanceDepthParamsBuilder::default()
521 .symbol("BTCUSDT")
522 .limit(100u32)
523 .build()
524 .unwrap();
525
526 assert_eq!(params.symbol, "BTCUSDT");
527 assert_eq!(params.limit, Some(100));
528 }
529
530 #[rstest]
531 fn test_ticker_params_serialization() {
532 let params = BinanceTicker24hrParams {
533 symbol: Some("BTCUSDT".to_string()),
534 symbols: None,
535 response_type: None,
536 };
537
538 let serialized = serde_urlencoded::to_string(¶ms).unwrap();
539 assert_eq!(serialized, "symbol=BTCUSDT");
540 }
541
542 #[rstest]
543 fn test_order_query_params_builder() {
544 let params = BinanceOrderQueryParamsBuilder::default()
545 .symbol("BTCUSDT")
546 .order_id(12345_i64)
547 .recv_window(5_000_u64)
548 .build()
549 .unwrap();
550
551 assert_eq!(params.symbol, "BTCUSDT");
552 assert_eq!(params.order_id, Some(12345));
553 assert_eq!(params.recv_window, Some(5_000));
554 }
555
556 #[rstest]
557 fn test_income_history_params_serialization() {
558 let params = BinanceIncomeHistoryParamsBuilder::default()
559 .symbol("ETHUSDT")
560 .income_type(BinanceIncomeType::FundingFee)
561 .limit(50_u32)
562 .build()
563 .unwrap();
564
565 let serialized = serde_urlencoded::to_string(¶ms).unwrap();
566 assert_eq!(serialized, "symbol=ETHUSDT&incomeType=FUNDING_FEE&limit=50");
567 }
568
569 #[rstest]
570 fn test_open_orders_params_builder() {
571 let params = BinanceOpenOrdersParamsBuilder::default()
572 .symbol("BNBUSDT")
573 .build()
574 .unwrap();
575
576 assert_eq!(params.symbol.as_deref(), Some("BNBUSDT"));
577 assert!(params.recv_window.is_none());
578 }
579}