1use std::fmt::Display;
19
20use nautilus_model::enums::{OrderSide, OrderType, 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(
32 module = "nautilus_trader.core.nautilus_pyo3.binance",
33 eq,
34 from_py_object,
35 rename_all = "SCREAMING_SNAKE_CASE"
36 )
37)]
38pub enum BinanceProductType {
39 #[default]
41 Spot,
42 Margin,
44 UsdM,
46 CoinM,
48 Options,
50}
51
52impl BinanceProductType {
53 #[must_use]
55 pub const fn as_str(self) -> &'static str {
56 match self {
57 Self::Spot => "SPOT",
58 Self::Margin => "MARGIN",
59 Self::UsdM => "USD_M",
60 Self::CoinM => "COIN_M",
61 Self::Options => "OPTIONS",
62 }
63 }
64
65 #[must_use]
67 pub const fn suffix(self) -> &'static str {
68 match self {
69 Self::Spot => "-SPOT",
70 Self::Margin => "-MARGIN",
71 Self::UsdM => "-LINEAR",
72 Self::CoinM => "-INVERSE",
73 Self::Options => "-OPTION",
74 }
75 }
76
77 #[must_use]
79 pub const fn is_spot(self) -> bool {
80 matches!(self, Self::Spot | Self::Margin)
81 }
82
83 #[must_use]
85 pub const fn is_futures(self) -> bool {
86 matches!(self, Self::UsdM | Self::CoinM)
87 }
88
89 #[must_use]
91 pub const fn is_linear(self) -> bool {
92 matches!(self, Self::Spot | Self::Margin | Self::UsdM)
93 }
94
95 #[must_use]
97 pub const fn is_inverse(self) -> bool {
98 matches!(self, Self::CoinM)
99 }
100
101 #[must_use]
103 pub const fn is_options(self) -> bool {
104 matches!(self, Self::Options)
105 }
106}
107
108impl Display for BinanceProductType {
109 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
110 write!(f, "{}", self.as_str())
111 }
112}
113
114#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
116#[cfg_attr(
117 feature = "python",
118 pyo3::pyclass(
119 module = "nautilus_trader.core.nautilus_pyo3.binance",
120 eq,
121 from_py_object,
122 rename_all = "SCREAMING_SNAKE_CASE"
123 )
124)]
125pub enum BinanceEnvironment {
126 #[default]
128 Mainnet,
129 Testnet,
131 Demo,
133}
134
135impl BinanceEnvironment {
136 #[must_use]
138 pub const fn is_testnet(self) -> bool {
139 matches!(self, Self::Testnet)
140 }
141
142 #[must_use]
144 pub const fn is_sandbox(self) -> bool {
145 matches!(self, Self::Testnet | Self::Demo)
146 }
147}
148
149#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
151#[serde(rename_all = "UPPERCASE")]
152pub enum BinanceSide {
153 Buy,
155 Sell,
157}
158
159impl TryFrom<OrderSide> for BinanceSide {
160 type Error = anyhow::Error;
161
162 fn try_from(value: OrderSide) -> Result<Self, Self::Error> {
163 match value {
164 OrderSide::Buy => Ok(Self::Buy),
165 OrderSide::Sell => Ok(Self::Sell),
166 _ => anyhow::bail!("Unsupported `OrderSide` for Binance: {value:?}"),
167 }
168 }
169}
170
171impl From<BinanceSide> for OrderSide {
172 fn from(value: BinanceSide) -> Self {
173 match value {
174 BinanceSide::Buy => Self::Buy,
175 BinanceSide::Sell => Self::Sell,
176 }
177 }
178}
179
180#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
182#[serde(rename_all = "UPPERCASE")]
183#[cfg_attr(
184 feature = "python",
185 pyo3::pyclass(
186 module = "nautilus_trader.core.nautilus_pyo3.binance",
187 eq,
188 from_py_object
189 )
190)]
191pub enum BinancePositionSide {
192 Both,
194 Long,
196 Short,
198 #[serde(other)]
200 Unknown,
201}
202
203#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
205#[serde(rename_all = "lowercase")]
206pub enum BinanceMarginType {
207 Cross,
209 Isolated,
211 #[serde(other)]
213 Unknown,
214}
215
216#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
218#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
219pub enum BinanceWorkingType {
220 ContractPrice,
222 MarkPrice,
224 #[serde(other)]
226 Unknown,
227}
228
229#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
231#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
232pub enum BinanceOrderStatus {
233 New,
235 PartiallyFilled,
237 Filled,
239 Canceled,
241 PendingCancel,
243 Rejected,
245 Expired,
247 ExpiredInMatch,
249 #[serde(other)]
251 Unknown,
252}
253
254#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
260#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
261pub enum BinanceAlgoStatus {
262 New,
264 Triggering,
266 Triggered,
268 Finished,
270 Canceled,
272 Expired,
274 Rejected,
276 #[serde(other)]
278 Unknown,
279}
280
281#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
285#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
286pub enum BinanceAlgoType {
287 #[default]
289 Conditional,
290 #[serde(other)]
292 Unknown,
293}
294
295#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
297#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
298pub enum BinanceFuturesOrderType {
299 Limit,
301 Market,
303 Stop,
305 StopMarket,
307 TakeProfit,
309 TakeProfitMarket,
311 TrailingStopMarket,
313 Liquidation,
315 Adl,
317 #[serde(other)]
319 Unknown,
320}
321
322impl From<BinanceFuturesOrderType> for OrderType {
323 fn from(value: BinanceFuturesOrderType) -> Self {
324 match value {
325 BinanceFuturesOrderType::Limit => Self::Limit,
326 BinanceFuturesOrderType::Market => Self::Market,
327 BinanceFuturesOrderType::Stop => Self::StopLimit,
328 BinanceFuturesOrderType::StopMarket => Self::StopMarket,
329 BinanceFuturesOrderType::TakeProfit => Self::LimitIfTouched,
330 BinanceFuturesOrderType::TakeProfitMarket => Self::MarketIfTouched,
331 BinanceFuturesOrderType::TrailingStopMarket => Self::TrailingStopMarket,
332 BinanceFuturesOrderType::Liquidation
333 | BinanceFuturesOrderType::Adl
334 | BinanceFuturesOrderType::Unknown => Self::Market, }
336 }
337}
338
339#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
341#[serde(rename_all = "UPPERCASE")]
342pub enum BinanceTimeInForce {
343 Gtc,
345 Ioc,
347 Fok,
349 Gtx,
351 Gtd,
353 #[serde(other)]
355 Unknown,
356}
357
358impl TryFrom<TimeInForce> for BinanceTimeInForce {
359 type Error = anyhow::Error;
360
361 fn try_from(value: TimeInForce) -> Result<Self, Self::Error> {
362 match value {
363 TimeInForce::Gtc => Ok(Self::Gtc),
364 TimeInForce::Ioc => Ok(Self::Ioc),
365 TimeInForce::Fok => Ok(Self::Fok),
366 TimeInForce::Gtd => Ok(Self::Gtd),
367 _ => anyhow::bail!("Unsupported `TimeInForce` for Binance: {value:?}"),
368 }
369 }
370}
371
372#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
374#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
375pub enum BinanceIncomeType {
376 Transfer,
378 WelcomeBonus,
380 RealizedPnl,
382 FundingFee,
384 Commission,
386 InsuranceClear,
388 ReferralKickback,
390 #[serde(other)]
392 Unknown,
393}
394
395#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
397#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
398pub enum BinancePriceMatch {
399 Opponent,
401 Opponent5,
403 Opponent10,
405 Opponent20,
407 Queue,
409 Queue5,
411 Queue10,
413 Queue20,
415 #[serde(other)]
417 Unknown,
418}
419
420#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
422#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
423pub enum BinanceSelfTradePreventionMode {
424 ExpireMaker,
426 ExpireTaker,
428 ExpireBoth,
430 #[serde(other)]
432 Unknown,
433}
434
435#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
437#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
438pub enum BinanceTradingStatus {
439 Trading,
441 PendingTrading,
443 PreTrading,
445 PostTrading,
447 EndOfDay,
449 Halt,
451 AuctionMatch,
453 Break,
455 #[serde(other)]
457 Unknown,
458}
459
460#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
462#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
463pub enum BinanceContractStatus {
464 Trading,
466 PendingTrading,
468 PreDelivering,
470 Delivering,
472 Delivered,
474 PreDelisting,
476 Delisting,
478 Down,
480 #[serde(other)]
482 Unknown,
483}
484
485#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
489#[serde(rename_all = "camelCase")]
490pub enum BinanceWsEventType {
491 AggTrade,
493 Trade,
495 BookTicker,
497 DepthUpdate,
499 MarkPriceUpdate,
501 Kline,
503 ForceOrder,
505 #[serde(rename = "24hrTicker")]
507 Ticker24Hr,
508 #[serde(rename = "24hrMiniTicker")]
510 MiniTicker24Hr,
511
512 #[serde(rename = "ACCOUNT_UPDATE")]
515 AccountUpdate,
516 #[serde(rename = "ORDER_TRADE_UPDATE")]
518 OrderTradeUpdate,
519 #[serde(rename = "ALGO_UPDATE")]
521 AlgoUpdate,
522 #[serde(rename = "MARGIN_CALL")]
524 MarginCall,
525 #[serde(rename = "ACCOUNT_CONFIG_UPDATE")]
527 AccountConfigUpdate,
528 #[serde(rename = "listenKeyExpired")]
530 ListenKeyExpired,
531
532 #[serde(other)]
534 Unknown,
535}
536
537impl BinanceWsEventType {
538 #[must_use]
540 pub const fn as_str(self) -> &'static str {
541 match self {
542 Self::AggTrade => "aggTrade",
543 Self::Trade => "trade",
544 Self::BookTicker => "bookTicker",
545 Self::DepthUpdate => "depthUpdate",
546 Self::MarkPriceUpdate => "markPriceUpdate",
547 Self::Kline => "kline",
548 Self::ForceOrder => "forceOrder",
549 Self::Ticker24Hr => "24hrTicker",
550 Self::MiniTicker24Hr => "24hrMiniTicker",
551 Self::AccountUpdate => "ACCOUNT_UPDATE",
552 Self::OrderTradeUpdate => "ORDER_TRADE_UPDATE",
553 Self::AlgoUpdate => "ALGO_UPDATE",
554 Self::MarginCall => "MARGIN_CALL",
555 Self::AccountConfigUpdate => "ACCOUNT_CONFIG_UPDATE",
556 Self::ListenKeyExpired => "listenKeyExpired",
557 Self::Unknown => "unknown",
558 }
559 }
560}
561
562impl Display for BinanceWsEventType {
563 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
564 write!(f, "{}", self.as_str())
565 }
566}
567
568#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
572#[serde(rename_all = "UPPERCASE")]
573pub enum BinanceWsMethod {
574 Subscribe,
576 Unsubscribe,
578}
579
580#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
582#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
583pub enum BinanceFilterType {
584 PriceFilter,
586 PercentPrice,
588 LotSize,
590 MarketLotSize,
592 Notional,
594 MinNotional,
596 MaxNumOrders,
598 MaxNumAlgoOrders,
600 #[serde(other)]
602 Unknown,
603}
604
605impl Display for BinanceEnvironment {
606 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
607 match self {
608 Self::Mainnet => write!(f, "Mainnet"),
609 Self::Testnet => write!(f, "Testnet"),
610 Self::Demo => write!(f, "Demo"),
611 }
612 }
613}
614
615#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
617pub enum BinanceRateLimitType {
618 RequestWeight,
619 Orders,
620}
621
622#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
624pub enum BinanceRateLimitInterval {
625 Second,
626 Minute,
627 Day,
628}
629
630#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
635pub enum BinanceKlineInterval {
636 #[serde(rename = "1s")]
638 Second1,
639 #[default]
641 #[serde(rename = "1m")]
642 Minute1,
643 #[serde(rename = "3m")]
645 Minute3,
646 #[serde(rename = "5m")]
648 Minute5,
649 #[serde(rename = "15m")]
651 Minute15,
652 #[serde(rename = "30m")]
654 Minute30,
655 #[serde(rename = "1h")]
657 Hour1,
658 #[serde(rename = "2h")]
660 Hour2,
661 #[serde(rename = "4h")]
663 Hour4,
664 #[serde(rename = "6h")]
666 Hour6,
667 #[serde(rename = "8h")]
669 Hour8,
670 #[serde(rename = "12h")]
672 Hour12,
673 #[serde(rename = "1d")]
675 Day1,
676 #[serde(rename = "3d")]
678 Day3,
679 #[serde(rename = "1w")]
681 Week1,
682 #[serde(rename = "1M")]
684 Month1,
685}
686
687impl BinanceKlineInterval {
688 #[must_use]
690 pub const fn as_str(&self) -> &'static str {
691 match self {
692 Self::Second1 => "1s",
693 Self::Minute1 => "1m",
694 Self::Minute3 => "3m",
695 Self::Minute5 => "5m",
696 Self::Minute15 => "15m",
697 Self::Minute30 => "30m",
698 Self::Hour1 => "1h",
699 Self::Hour2 => "2h",
700 Self::Hour4 => "4h",
701 Self::Hour6 => "6h",
702 Self::Hour8 => "8h",
703 Self::Hour12 => "12h",
704 Self::Day1 => "1d",
705 Self::Day3 => "3d",
706 Self::Week1 => "1w",
707 Self::Month1 => "1M",
708 }
709 }
710}
711
712#[cfg(test)]
713mod tests {
714 use rstest::rstest;
715
716 use super::*;
717
718 #[rstest]
719 fn test_product_type_as_str() {
720 assert_eq!(BinanceProductType::Spot.as_str(), "SPOT");
721 assert_eq!(BinanceProductType::Margin.as_str(), "MARGIN");
722 assert_eq!(BinanceProductType::UsdM.as_str(), "USD_M");
723 assert_eq!(BinanceProductType::CoinM.as_str(), "COIN_M");
724 assert_eq!(BinanceProductType::Options.as_str(), "OPTIONS");
725 }
726
727 #[rstest]
728 fn test_product_type_suffix() {
729 assert_eq!(BinanceProductType::Spot.suffix(), "-SPOT");
730 assert_eq!(BinanceProductType::Margin.suffix(), "-MARGIN");
731 assert_eq!(BinanceProductType::UsdM.suffix(), "-LINEAR");
732 assert_eq!(BinanceProductType::CoinM.suffix(), "-INVERSE");
733 assert_eq!(BinanceProductType::Options.suffix(), "-OPTION");
734 }
735
736 #[rstest]
737 fn test_product_type_predicates() {
738 assert!(BinanceProductType::Spot.is_spot());
739 assert!(BinanceProductType::Margin.is_spot());
740 assert!(!BinanceProductType::UsdM.is_spot());
741
742 assert!(BinanceProductType::UsdM.is_futures());
743 assert!(BinanceProductType::CoinM.is_futures());
744 assert!(!BinanceProductType::Spot.is_futures());
745
746 assert!(BinanceProductType::CoinM.is_inverse());
747 assert!(!BinanceProductType::UsdM.is_inverse());
748
749 assert!(BinanceProductType::Options.is_options());
750 assert!(!BinanceProductType::Spot.is_options());
751 }
752}