1use std::fmt::Display;
19
20use nautilus_model::enums::{OrderSide, TimeInForce};
21use serde::{Deserialize, Serialize};
22
23#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
28#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
29#[cfg_attr(
30 feature = "python",
31 pyo3::pyclass(module = "nautilus_trader.core.nautilus_pyo3.binance", eq)
32)]
33pub enum BinanceProductType {
34 #[default]
36 Spot,
37 Margin,
39 UsdM,
41 CoinM,
43 Options,
45}
46
47impl BinanceProductType {
48 #[must_use]
50 pub const fn as_str(self) -> &'static str {
51 match self {
52 Self::Spot => "SPOT",
53 Self::Margin => "MARGIN",
54 Self::UsdM => "USD_M",
55 Self::CoinM => "COIN_M",
56 Self::Options => "OPTIONS",
57 }
58 }
59
60 #[must_use]
62 pub const fn suffix(self) -> &'static str {
63 match self {
64 Self::Spot => "-SPOT",
65 Self::Margin => "-MARGIN",
66 Self::UsdM => "-LINEAR",
67 Self::CoinM => "-INVERSE",
68 Self::Options => "-OPTION",
69 }
70 }
71
72 #[must_use]
74 pub const fn is_spot(self) -> bool {
75 matches!(self, Self::Spot | Self::Margin)
76 }
77
78 #[must_use]
80 pub const fn is_futures(self) -> bool {
81 matches!(self, Self::UsdM | Self::CoinM)
82 }
83
84 #[must_use]
86 pub const fn is_linear(self) -> bool {
87 matches!(self, Self::Spot | Self::Margin | Self::UsdM)
88 }
89
90 #[must_use]
92 pub const fn is_inverse(self) -> bool {
93 matches!(self, Self::CoinM)
94 }
95
96 #[must_use]
98 pub const fn is_options(self) -> bool {
99 matches!(self, Self::Options)
100 }
101}
102
103impl Display for BinanceProductType {
104 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
105 write!(f, "{}", self.as_str())
106 }
107}
108
109#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
111#[cfg_attr(
112 feature = "python",
113 pyo3::pyclass(module = "nautilus_trader.core.nautilus_pyo3.binance", eq)
114)]
115pub enum BinanceEnvironment {
116 #[default]
118 Mainnet,
119 Testnet,
121}
122
123impl BinanceEnvironment {
124 #[must_use]
126 pub const fn is_testnet(self) -> bool {
127 matches!(self, Self::Testnet)
128 }
129}
130
131#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
133#[serde(rename_all = "UPPERCASE")]
134pub enum BinanceSide {
135 Buy,
137 Sell,
139}
140
141impl TryFrom<OrderSide> for BinanceSide {
142 type Error = anyhow::Error;
143
144 fn try_from(value: OrderSide) -> Result<Self, Self::Error> {
145 match value {
146 OrderSide::Buy => Ok(Self::Buy),
147 OrderSide::Sell => Ok(Self::Sell),
148 _ => anyhow::bail!("Unsupported `OrderSide` for Binance: {value:?}"),
149 }
150 }
151}
152
153#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
155#[serde(rename_all = "UPPERCASE")]
156pub enum BinancePositionSide {
157 Both,
159 Long,
161 Short,
163 #[serde(other)]
165 Unknown,
166}
167
168#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
170#[serde(rename_all = "lowercase")]
171pub enum BinanceMarginType {
172 Cross,
174 Isolated,
176 #[serde(other)]
178 Unknown,
179}
180
181#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
183#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
184pub enum BinanceWorkingType {
185 ContractPrice,
187 MarkPrice,
189 #[serde(other)]
191 Unknown,
192}
193
194#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
196#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
197pub enum BinanceOrderStatus {
198 New,
200 PartiallyFilled,
202 Filled,
204 Canceled,
206 PendingCancel,
208 Rejected,
210 Expired,
212 ExpiredInMatch,
214 #[serde(other)]
216 Unknown,
217}
218
219#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
221#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
222pub enum BinanceFuturesOrderType {
223 Limit,
225 Market,
227 Stop,
229 StopMarket,
231 TakeProfit,
233 TakeProfitMarket,
235 TrailingStopMarket,
237 Liquidation,
239 Adl,
241 #[serde(other)]
243 Unknown,
244}
245
246#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
248#[serde(rename_all = "UPPERCASE")]
249pub enum BinanceTimeInForce {
250 Gtc,
252 Ioc,
254 Fok,
256 Gtx,
258 Gtd,
260 #[serde(other)]
262 Unknown,
263}
264
265impl TryFrom<TimeInForce> for BinanceTimeInForce {
266 type Error = anyhow::Error;
267
268 fn try_from(value: TimeInForce) -> Result<Self, Self::Error> {
269 match value {
270 TimeInForce::Gtc => Ok(Self::Gtc),
271 TimeInForce::Ioc => Ok(Self::Ioc),
272 TimeInForce::Fok => Ok(Self::Fok),
273 TimeInForce::Gtd => Ok(Self::Gtd),
274 _ => anyhow::bail!("Unsupported `TimeInForce` for Binance: {value:?}"),
275 }
276 }
277}
278
279#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
281#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
282pub enum BinanceIncomeType {
283 Transfer,
285 WelcomeBonus,
287 RealizedPnl,
289 FundingFee,
291 Commission,
293 InsuranceClear,
295 ReferralKickback,
297 #[serde(other)]
299 Unknown,
300}
301
302#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
304#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
305pub enum BinancePriceMatch {
306 Opponent,
308 Opponent5,
310 Opponent10,
312 Opponent20,
314 Queue,
316 Queue5,
318 Queue10,
320 Queue20,
322 #[serde(other)]
324 Unknown,
325}
326
327#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
329#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
330pub enum BinanceSelfTradePreventionMode {
331 ExpireMaker,
333 ExpireTaker,
335 ExpireBoth,
337 #[serde(other)]
339 Unknown,
340}
341
342#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
344#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
345pub enum BinanceTradingStatus {
346 Trading,
348 PendingTrading,
350 PreTrading,
352 PostTrading,
354 EndOfDay,
356 Halt,
358 AuctionMatch,
360 Break,
362 #[serde(other)]
364 Unknown,
365}
366
367#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
369#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
370pub enum BinanceContractStatus {
371 Trading,
373 PendingTrading,
375 PreDelivering,
377 Delivering,
379 Delivered,
381 PreDelisting,
383 Delisting,
385 Down,
387 #[serde(other)]
389 Unknown,
390}
391
392#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
396#[serde(rename_all = "camelCase")]
397pub enum BinanceWsEventType {
398 AggTrade,
400 Trade,
402 BookTicker,
404 DepthUpdate,
406 MarkPriceUpdate,
408 Kline,
410 ForceOrder,
412 #[serde(rename = "24hrTicker")]
414 Ticker24Hr,
415 #[serde(rename = "24hrMiniTicker")]
417 MiniTicker24Hr,
418 #[serde(other)]
420 Unknown,
421}
422
423impl BinanceWsEventType {
424 #[must_use]
426 pub const fn as_str(self) -> &'static str {
427 match self {
428 Self::AggTrade => "aggTrade",
429 Self::Trade => "trade",
430 Self::BookTicker => "bookTicker",
431 Self::DepthUpdate => "depthUpdate",
432 Self::MarkPriceUpdate => "markPriceUpdate",
433 Self::Kline => "kline",
434 Self::ForceOrder => "forceOrder",
435 Self::Ticker24Hr => "24hrTicker",
436 Self::MiniTicker24Hr => "24hrMiniTicker",
437 Self::Unknown => "unknown",
438 }
439 }
440}
441
442impl Display for BinanceWsEventType {
443 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
444 write!(f, "{}", self.as_str())
445 }
446}
447
448#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
452#[serde(rename_all = "UPPERCASE")]
453pub enum BinanceWsMethod {
454 Subscribe,
456 Unsubscribe,
458}
459
460#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
462#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
463pub enum BinanceFilterType {
464 PriceFilter,
466 PercentPrice,
468 LotSize,
470 MarketLotSize,
472 Notional,
474 MinNotional,
476 MaxNumOrders,
478 MaxNumAlgoOrders,
480 #[serde(other)]
482 Unknown,
483}
484
485impl Display for BinanceEnvironment {
486 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
487 match self {
488 Self::Mainnet => write!(f, "Mainnet"),
489 Self::Testnet => write!(f, "Testnet"),
490 }
491 }
492}
493
494#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
496pub enum BinanceRateLimitType {
497 RequestWeight,
498 Orders,
499}
500
501#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
503pub enum BinanceRateLimitInterval {
504 Second,
505 Minute,
506 Day,
507}
508
509#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
514pub enum BinanceKlineInterval {
515 #[serde(rename = "1s")]
517 Second1,
518 #[default]
520 #[serde(rename = "1m")]
521 Minute1,
522 #[serde(rename = "3m")]
524 Minute3,
525 #[serde(rename = "5m")]
527 Minute5,
528 #[serde(rename = "15m")]
530 Minute15,
531 #[serde(rename = "30m")]
533 Minute30,
534 #[serde(rename = "1h")]
536 Hour1,
537 #[serde(rename = "2h")]
539 Hour2,
540 #[serde(rename = "4h")]
542 Hour4,
543 #[serde(rename = "6h")]
545 Hour6,
546 #[serde(rename = "8h")]
548 Hour8,
549 #[serde(rename = "12h")]
551 Hour12,
552 #[serde(rename = "1d")]
554 Day1,
555 #[serde(rename = "3d")]
557 Day3,
558 #[serde(rename = "1w")]
560 Week1,
561 #[serde(rename = "1M")]
563 Month1,
564}
565
566impl BinanceKlineInterval {
567 #[must_use]
569 pub const fn as_str(&self) -> &'static str {
570 match self {
571 Self::Second1 => "1s",
572 Self::Minute1 => "1m",
573 Self::Minute3 => "3m",
574 Self::Minute5 => "5m",
575 Self::Minute15 => "15m",
576 Self::Minute30 => "30m",
577 Self::Hour1 => "1h",
578 Self::Hour2 => "2h",
579 Self::Hour4 => "4h",
580 Self::Hour6 => "6h",
581 Self::Hour8 => "8h",
582 Self::Hour12 => "12h",
583 Self::Day1 => "1d",
584 Self::Day3 => "3d",
585 Self::Week1 => "1w",
586 Self::Month1 => "1M",
587 }
588 }
589}
590
591#[cfg(test)]
592mod tests {
593 use rstest::rstest;
594
595 use super::*;
596
597 #[rstest]
598 fn test_product_type_as_str() {
599 assert_eq!(BinanceProductType::Spot.as_str(), "SPOT");
600 assert_eq!(BinanceProductType::Margin.as_str(), "MARGIN");
601 assert_eq!(BinanceProductType::UsdM.as_str(), "USD_M");
602 assert_eq!(BinanceProductType::CoinM.as_str(), "COIN_M");
603 assert_eq!(BinanceProductType::Options.as_str(), "OPTIONS");
604 }
605
606 #[rstest]
607 fn test_product_type_suffix() {
608 assert_eq!(BinanceProductType::Spot.suffix(), "-SPOT");
609 assert_eq!(BinanceProductType::Margin.suffix(), "-MARGIN");
610 assert_eq!(BinanceProductType::UsdM.suffix(), "-LINEAR");
611 assert_eq!(BinanceProductType::CoinM.suffix(), "-INVERSE");
612 assert_eq!(BinanceProductType::Options.suffix(), "-OPTION");
613 }
614
615 #[rstest]
616 fn test_product_type_predicates() {
617 assert!(BinanceProductType::Spot.is_spot());
618 assert!(BinanceProductType::Margin.is_spot());
619 assert!(!BinanceProductType::UsdM.is_spot());
620
621 assert!(BinanceProductType::UsdM.is_futures());
622 assert!(BinanceProductType::CoinM.is_futures());
623 assert!(!BinanceProductType::Spot.is_futures());
624
625 assert!(BinanceProductType::CoinM.is_inverse());
626 assert!(!BinanceProductType::UsdM.is_inverse());
627
628 assert!(BinanceProductType::Options.is_options());
629 assert!(!BinanceProductType::Spot.is_options());
630 }
631}