Skip to main content

bybit/
enums.rs

1//! Enums Definitions
2//!
3//! Ref: https://bybit-exchange.github.io/docs/v5/enum
4
5use serde::{Deserialize, Deserializer, Serialize, Serializer};
6use serde_repr::*;
7use std::fmt;
8
9#[derive(Debug, Deserialize, Serialize)]
10pub enum Locale {
11    #[serde(rename = "de-DE")]
12    DeDe,
13    #[serde(rename = "en-US")]
14    EnUs,
15    #[serde(rename = "es-AR")]
16    EsAr,
17    #[serde(rename = "es-ES")]
18    EsEs,
19    #[serde(rename = "es-MX")]
20    EsMx,
21    #[serde(rename = "fr-FR")]
22    FrFr,
23    #[serde(rename = "kk-KZ")]
24    KkKz,
25    #[serde(rename = "id-ID")]
26    IdId,
27    #[serde(rename = "uk-UA")]
28    UkUa,
29    #[serde(rename = "ja-JP")]
30    JaJp,
31    #[serde(rename = "ru-RU")]
32    RuRu,
33    #[serde(rename = "th-TH")]
34    ThTh,
35    #[serde(rename = "pt-BR")]
36    PtBr,
37    #[serde(rename = "tr-TR")]
38    TrTr,
39    #[serde(rename = "vi-VN")]
40    ViVn,
41    #[serde(rename = "zh-TW")]
42    ZhTw,
43    #[serde(rename = "ar-SA")]
44    ArSa,
45    #[serde(rename = "hi-IN")]
46    HiIn,
47    #[serde(rename = "fil-PH")]
48    FilPh,
49}
50
51#[derive(Debug, Deserialize, Serialize)]
52pub enum AnnouncementType {
53    #[serde(rename = "new_crypto")]
54    NewCrypto,
55    #[serde(rename = "latest_bybit_news")]
56    LatestBybitNews,
57    #[serde(rename = "delistings")]
58    Delistings,
59    #[serde(rename = "latest_activities")]
60    LatestActivities,
61    #[serde(rename = "product_updates")]
62    ProductUpdates,
63    #[serde(rename = "maintenance_updates")]
64    MaintenanceUpdates,
65    #[serde(rename = "new_fiat_listings")]
66    NewFiatListings,
67    #[serde(rename = "other")]
68    Other,
69}
70
71/// Unified Account: spot | linear | inverse | option
72/// Classic Account: linear | inverse | spot
73#[derive(PartialEq, Eq, Hash, Debug, Deserialize, Serialize, Clone, Copy)]
74#[serde(rename_all = "lowercase")]
75pub enum Category {
76    /// Inverse contract, including Inverse perp, Inverse futures.
77    Inverse,
78    /// USDT perpetual, and USDC contract, including USDC perp, USDC futures.
79    Linear,
80    Option,
81    Spot,
82}
83
84impl fmt::Display for Category {
85    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
86        let value = match self {
87            Self::Inverse => "inverse",
88            Self::Linear => "linear",
89            Self::Option => "option",
90            Self::Spot => "spot",
91        };
92        write!(f, "{value}")
93    }
94}
95
96#[derive(Debug, Deserialize, Serialize, PartialEq, Clone, Copy)]
97pub enum OrderStatus {
98    // open status
99    /// order has been placed successfully
100    New,
101    PartiallyFilled,
102    /// Conditional orders are created
103    Untriggered,
104    // closed status
105    Rejected,
106    /// Only spot has this order status
107    PartiallyFilledCanceled,
108    Filled,
109    /// In derivatives, orders with this status may have an executed qty
110    Cancelled,
111    /// instantaneous state for conditional orders from Untriggered to New
112    Triggered,
113    /// UTA: Spot tp/sl order, conditional order, OCO order are cancelled before they are triggered
114    Deactivated,
115}
116
117impl OrderStatus {
118    pub fn is_open(&self) -> bool {
119        matches!(self, Self::New | Self::PartiallyFilled | Self::Untriggered)
120    }
121    pub fn is_closed(&self) -> bool {
122        matches!(
123            self,
124            Self::Rejected
125                | Self::PartiallyFilledCanceled
126                | Self::Filled
127                | Self::Cancelled
128                | Self::Triggered
129                | Self::Deactivated
130        )
131    }
132}
133
134#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
135pub enum TimeInForce {
136    /// GoodTillCancel
137    GTC,
138    /// ImmediateOrCancel
139    IOC,
140    /// FillOrKill
141    FOK,
142    PostOnly,
143    /// features:
144    /// Exclusive Matching: Only match non-algorithmic users; no execution against orders from Open API.
145    /// Post-Only Mechanism: Act as maker orders, adding liquidity
146    /// Lower Priority: Execute after non-RPI orders at the same price level.
147    /// Limited Access: Initially for select market makers across multiple pairs.
148    /// Order Book Updates: Excluded from API but displayed on the GUI.
149    RPI,
150}
151
152#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
153pub enum CreateType {
154    CreateByUser,
155    /// Spread order
156    CreateByFutureSpread,
157    CreateByAdminClosing,
158    /// USDC Futures delivery; position closed as a result of the delisting of a contract. This is recorded as a trade but not an order.
159    CreateBySettle,
160    /// Futures conditional order
161    CreateByStopOrder,
162    /// Futures take profit order
163    CreateByTakeProfit,
164    /// Futures partial take profit order
165    CreateByPartialTakeProfit,
166    /// Futures stop loss order
167    CreateByStopLoss,
168    /// Futures partial stop loss order
169    CreateByPartialStopLoss,
170    /// Futures trailing stop order
171    CreateByTrailingStop,
172    /// Laddered liquidation to reduce the required maintenance margin
173    CreateByLiq,
174    /// If the position is still subject to liquidation (i.e., does not meet the required maintenance margin level), the position shall be taken over by the liquidation engine and closed at the bankruptcy price.
175    #[serde(rename = "CreateByTakeOver_PassThrough")]
176    CreateByTakeOverPassThrough,
177    /// Auto-Deleveraging(ADL)
178    #[serde(rename = "CreateByAdl_PassThrough")]
179    CreateByAdlPassThrough,
180    /// Order placed via Paradigm
181    #[serde(rename = "CreateByBlock_PassThrough")]
182    CreateByBlockPassThrough,
183    /// Order created by move position
184    #[serde(rename = "CreateByBlockTradeMovePosition_PassThrough")]
185    CreateByBlockTradeMovePositionPassThrough,
186    /// The close order placed via web or app position area - web/app
187    CreateByClosing,
188    /// Order created via grid bot - web/app
189    CreateByFGridBot,
190    /// Order closed via grid bot - web/app
191    CloseByFGridBot,
192    /// Order created by TWAP - web/app
193    CreateByTWAP,
194    /// Order created by TV webhook - web/app
195    CreateByTVSignal,
196    /// Order created by Mm rate close function - web/app
197    CreateByMmRateClose,
198    /// Order created by Martingale bot - web/app
199    CreateByMartingaleBot,
200    /// Order closed by Martingale bot - web/app
201    CloseByMartingaleBot,
202    /// Order created by Ice berg strategy - web/app
203    CreateByIceBerg,
204    /// Order created by arbitrage - web/app
205    CreateByArbitrage,
206    /// Option dynamic delta hedge order - web/app
207    CreateByDdh,
208}
209
210#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
211pub enum ExecType {
212    Trade,
213    /// Auto-Deleveraging
214    AdlTrade,
215    /// Funding fee
216    Funding,
217    /// Takeover liquidation
218    BustTrade,
219    /// USDC futures delivery; Position closed by contract delisted
220    Delivery,
221    /// Inverse futures settlement; Position closed due to delisting
222    Settle,
223    BlockTrade,
224    MovePosition,
225    /// Spread leg execution
226    FutureSpread,
227    /// May be returned by a classic account. Cannot query by this type
228    UNKNOWN,
229}
230
231#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
232pub enum OrderType {
233    Market,
234    Limit,
235    /// is not a valid request parameter value. Is only used in some responses. Mainly, it is used when execType is Funding.
236    UNKNOWN,
237}
238
239#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
240pub enum StopOrderType {
241    TakeProfit,
242    StopLoss,
243    TrailingStop,
244    Stop,
245    PartialTakeProfit,
246    PartialStopLoss,
247    /// spot TP/SL order
248    #[serde(rename = "tpslOrder")]
249    TpslOrder,
250    /// spot Oco order
251    OcoOrder,
252    /// On web or app can set MMR to close position
253    MmRateClose,
254    /// Spot bidirectional tpsl order
255    BidirectionalTpslOrder,
256    UNKNOWN,
257}
258
259#[derive(Debug, PartialEq, Deserialize, Clone, Copy)]
260pub enum TickDirection {
261    /// price rise
262    PlusTick,
263    /// trade occurs at the same price as the previous trade, which occurred at a price higher than that for the trade preceding it
264    ZeroPlusTick,
265    /// price drop
266    MinusTick,
267    /// trade occurs at the same price as the previous trade, which occurred at a price lower than that for the trade preceding it
268    ZeroMinusTick,
269}
270
271#[derive(Debug, Deserialize, Serialize, PartialEq, Clone, Copy)]
272pub enum Interval {
273    #[serde(rename = "1")]
274    Minute1,
275    #[serde(rename = "3")]
276    Minute3,
277    #[serde(rename = "5")]
278    Minute5,
279    #[serde(rename = "15")]
280    Minute15,
281    #[serde(rename = "30")]
282    Minute30,
283    #[serde(rename = "60")]
284    Hour1,
285    #[serde(rename = "120")]
286    Hour2,
287    #[serde(rename = "240")]
288    Hour4,
289    #[serde(rename = "360")]
290    Hour6,
291    #[serde(rename = "720")]
292    Hour12,
293    #[serde(rename = "D")]
294    Day1,
295    #[serde(rename = "W")]
296    Week1,
297    #[serde(rename = "M")]
298    Month1,
299}
300
301impl fmt::Display for Interval {
302    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
303        let value = match self {
304            Self::Minute1 => "1",
305            Self::Minute3 => "3",
306            Self::Minute5 => "5",
307            Self::Minute15 => "15",
308            Self::Minute30 => "30",
309            Self::Hour1 => "60",
310            Self::Hour2 => "120",
311            Self::Hour4 => "240",
312            Self::Hour6 => "360",
313            Self::Hour12 => "720",
314            Self::Day1 => "D",
315            Self::Week1 => "W",
316            Self::Month1 => "M",
317        };
318        write!(f, "{value}")
319    }
320}
321
322#[derive(Debug, Deserialize, Serialize, PartialEq, Clone, Copy)]
323pub enum IntervalTime {
324    #[serde(rename = "5min")]
325    Minute5,
326    #[serde(rename = "15min")]
327    Minute15,
328    #[serde(rename = "30min")]
329    Minute30,
330    #[serde(rename = "1h")]
331    Hour1,
332    #[serde(rename = "4h")]
333    Hour4,
334    #[serde(rename = "1d")]
335    Day1,
336}
337
338#[derive(Serialize_repr, Deserialize_repr, Debug, PartialEq, Eq, Hash, Clone, Copy)]
339#[repr(u8)]
340pub enum PositionIdx {
341    /// 0:one-way mode position
342    OneWay = 0,
343    /// 1:Buy side of hedge-mode position
344    Buy = 1,
345    /// 2:Sell side of hedge-mode position
346    Sell = 2,
347}
348
349#[derive(Serialize_repr, Deserialize_repr, Debug, PartialEq, Clone, Copy)]
350#[repr(u8)]
351pub enum PositionMode {
352    /// 0: Merged Single (one-way)
353    OneWay = 0,
354    /// 3: Both Sides (hedge)
355    Hedge = 3,
356}
357
358#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
359pub enum PositionStatus {
360    Normal,
361    /// in the liquidation progress
362    Liq,
363    /// in the auto-deleverage progress
364    Adl,
365}
366
367#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
368pub enum RejectReason {
369    #[serde(rename = "EC_NoError")]
370    EcNoError,
371    #[serde(rename = "EC_Others")]
372    EcOthers,
373    #[serde(rename = "EC_UnknownMessageType")]
374    EcUnknownMessageType,
375    #[serde(rename = "EC_MissingClOrdID")]
376    EcMissingClOrdId,
377    #[serde(rename = "EC_MissingOrigClOrdID")]
378    EcMissingOrigClOrdId,
379    #[serde(rename = "EC_ClOrdIDOrigClOrdIDAreTheSame")]
380    EcClOrdIdorigClOrdIdareTheSame,
381    #[serde(rename = "EC_DuplicatedClOrdID")]
382    EcDuplicatedClOrdId,
383    #[serde(rename = "EC_OrigClOrdIDDoesNotExist")]
384    EcOrigClOrdIddoesNotExist,
385    #[serde(rename = "EC_TooLateToCancel")]
386    EcTooLateToCancel,
387    #[serde(rename = "EC_UnknownOrderType")]
388    EcUnknownOrderType,
389    #[serde(rename = "EC_UnknownSide")]
390    EcUnknownSide,
391    #[serde(rename = "EC_UnknownTimeInForce")]
392    EcUnknownTimeInForce,
393    #[serde(rename = "EC_WronglyRouted")]
394    EcWronglyRouted,
395    #[serde(rename = "EC_MarketOrderPriceIsNotZero")]
396    EcMarketOrderPriceIsNotZero,
397    #[serde(rename = "EC_LimitOrderInvalidPrice")]
398    EcLimitOrderInvalidPrice,
399    #[serde(rename = "EC_NoEnoughQtyToFill")]
400    EcNoEnoughQtyToFill,
401    #[serde(rename = "EC_NoImmediateQtyToFill")]
402    EcNoImmediateQtyToFill,
403    #[serde(rename = "EC_PerCancelRequest")]
404    EcPerCancelRequest,
405    #[serde(rename = "EC_MarketOrderCannotBePostOnly")]
406    EcMarketOrderCannotBePostOnly,
407    #[serde(rename = "EC_PostOnlyWillTakeLiquidity")]
408    EcPostOnlyWillTakeLiquidity,
409    #[serde(rename = "EC_CancelReplaceOrder")]
410    EcCancelReplaceOrder,
411    #[serde(rename = "EC_InvalidSymbolStatus")]
412    EcInvalidSymbolStatus,
413    #[serde(rename = "EC_CancelForNoFullFill")]
414    EcCancelForNoFullFill,
415    #[serde(rename = "EC_BySelfMatch")]
416    EcBySelfMatch,
417    /// used for pre-market order operation, e.g., during 2nd phase of call auction, cancel order is not allowed, when the cancel request is failed to be rejected by trading server, the request will be rejected by matching box finally
418    #[serde(rename = "EC_InCallAuctionStatus")]
419    EcInCallAuctionStatus,
420    #[serde(rename = "EC_QtyCannotBeZero")]
421    EcQtyCannotBeZero,
422    #[serde(rename = "EC_MarketOrderNoSupportTIF")]
423    EcMarketOrderNoSupportTif,
424    #[serde(rename = "EC_ReachMaxTradeNum")]
425    EcReachMaxTradeNum,
426    #[serde(rename = "EC_InvalidPriceScale")]
427    EcInvalidPriceScale,
428    #[serde(rename = "EC_BitIndexInvalid")]
429    EcBitIndexInvalid,
430    #[serde(rename = "EC_StopBySelfMatch")]
431    EcStopBySelfMatch,
432    #[serde(rename = "EC_InvalidSmpType")]
433    EcInvalidSmpType,
434    #[serde(rename = "EC_CancelByMMP")]
435    EcCancelByMmp,
436    #[serde(rename = "EC_InvalidUserType")]
437    EcInvalidUserType,
438    #[serde(rename = "EC_InvalidMirrorOid")]
439    EcInvalidMirrorOid,
440    #[serde(rename = "EC_InvalidMirrorUid")]
441    EcInvalidMirrorUid,
442    #[serde(rename = "EC_EcInvalidQty")]
443    EcEcInvalidQty,
444    #[serde(rename = "EC_InvalidAmount")]
445    EcInvalidAmount,
446    #[serde(rename = "EC_LoadOrderCancel")]
447    EcLoadOrderCancel,
448    #[serde(rename = "EC_MarketQuoteNoSuppSell")]
449    EcMarketQuoteNoSuppSell,
450    #[serde(rename = "EC_DisorderOrderID")]
451    EcDisorderOrderId,
452    #[serde(rename = "EC_InvalidBaseValue")]
453    EcInvalidBaseValue,
454    #[serde(rename = "EC_LoadOrderCanMatch")]
455    EcLoadOrderCanMatch,
456    #[serde(rename = "EC_SecurityStatusFail")]
457    EcSecurityStatusFail,
458    #[serde(rename = "EC_ReachRiskPriceLimit")]
459    EcReachRiskPriceLimit,
460    #[serde(rename = "EC_OrderNotExist")]
461    EcOrderNotExist,
462    #[serde(rename = "EC_CancelByOrderValueZero")]
463    EcCancelByOrderValueZero,
464    #[serde(rename = "EC_CancelByMatchValueZero")]
465    EcCancelByMatchValueZero,
466    #[serde(rename = "EC_ReachMarketPriceLimit")]
467    EcReachMarketPriceLimit,
468}
469
470#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone, Copy)]
471pub enum AccountType {
472    /// Inverse Derivatives Account | Derivatives Account
473    CONTRACT,
474    /// Unified Trading Account
475    UNIFIED,
476    /// Funding Account
477    FUND,
478    /// Spot Account
479    SPOT,
480}
481
482impl AccountType {
483    pub fn is_uta_2(&self) -> bool {
484        matches!(self, Self::UNIFIED | Self::FUND)
485    }
486    pub fn is_uta_1(&self) -> bool {
487        matches!(self, Self::CONTRACT | Self::UNIFIED | Self::FUND)
488    }
489    pub fn is_classic(&self) -> bool {
490        matches!(self, Self::SPOT | Self::CONTRACT | Self::FUND)
491    }
492}
493
494#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
495pub enum TransferStatus {
496    SUCCESS,
497    PENDING,
498    FAILED,
499}
500
501#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
502pub enum DepositStatus {
503    #[serde(rename = "0")]
504    Unknown,
505    #[serde(rename = "1")]
506    ToBeConfirmed,
507    #[serde(rename = "2")]
508    Processing,
509    /// (finalised status of a success deposit)
510    #[serde(rename = "3")]
511    Success,
512    #[serde(rename = "4")]
513    DepositFailed,
514    #[serde(rename = "10011")]
515    PendingToBeCreditedToFundingPool,
516    #[serde(rename = "10012")]
517    CreditedToFundingPoolSuccessfully,
518}
519
520#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
521pub enum WithdrawStatus {
522    SecurityCheck,
523    Pending,
524    #[serde(rename = "success")]
525    Success,
526    CancelByUser,
527    Reject,
528    Fail,
529    BlockchainConfirmed,
530    MoreInformationRequired,
531    /// a rare status
532    Unknown,
533}
534
535#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
536pub enum TriggerBy {
537    LastPrice,
538    IndexPrice,
539    MarkPrice,
540    UNKNOWN,
541}
542
543#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
544pub enum CancelType {
545    CancelByUser,
546    /// cancelled by reduceOnly
547    CancelByReduceOnly,
548    /// cancelled in order to attempt liquidation prevention by freeing up margin
549    CancelByPrepareLiq,
550    /// cancelled in order to attempt liquidation prevention by freeing up margin
551    CancelAllBeforeLiq,
552    /// cancelled due to ADL
553    CancelByPrepareAdl,
554    /// cancelled due to ADL
555    CancelAllBeforeAdl,
556    CancelByAdmin,
557    /// cancelled due to delisting contract
558    CancelBySettle,
559    /// TP/SL order cancelled when the position is cleared
560    CancelByTpSlTsClear,
561    /// cancelled by SMP
562    CancelBySmp,
563    /// cancelled by DCP triggering
564    CancelByDCP,
565    /// Spread trading: the order price of a single leg order is outside the limit price range.
566    CancelByRebalance,
567
568    // Options:
569    CancelByCannotAffordOrderCost,
570    CancelByPmTrialMmOverEquity,
571    CancelByAccountBlocking,
572    CancelByDelivery,
573    CancelByMmpTriggered,
574    CancelByCrossSelfMuch,
575    CancelByCrossReachMaxTradeNum,
576
577    /// Not documented
578    UNKNOWN,
579}
580
581#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
582pub enum OptionPeriod {
583    #[serde(rename = "7")]
584    Day7,
585    #[serde(rename = "14")]
586    Day14,
587    #[serde(rename = "21")]
588    Day21,
589    #[serde(rename = "30")]
590    Day30,
591    #[serde(rename = "60")]
592    Day60,
593    #[serde(rename = "90")]
594    Day90,
595    #[serde(rename = "180")]
596    Day180,
597    #[serde(rename = "270")]
598    Day270,
599}
600
601#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
602pub enum DataRecordingPeriod {
603    #[serde(rename = "5min")]
604    Minute5,
605    #[serde(rename = "15min")]
606    Minute15,
607    #[serde(rename = "30min")]
608    Minute30,
609    #[serde(rename = "1h")]
610    Hour1,
611    #[serde(rename = "4h")]
612    Hour4,
613    #[serde(rename = "4d")]
614    Day4,
615}
616
617#[derive(Debug, Deserialize, Serialize, PartialEq, Clone, Copy)]
618pub enum ContractType {
619    InversePerpetual,
620    LinearPerpetual,
621    /// USDT/USDC Futures
622    LinearFutures,
623    InverseFutures,
624}
625
626#[derive(Debug, Deserialize, Serialize, PartialEq, Clone, Copy)]
627pub enum Status {
628    PreLaunch,
629    Trading,
630    Delivering,
631    Closed,
632}
633
634#[derive(Debug, Deserialize, Serialize, PartialEq, Clone, Copy)]
635#[serde(rename_all = "camelCase")]
636pub enum MarginTrading {
637    /// Regardless of normal account or UTA account, this trading pair does not support margin trading
638    None,
639    /// For both normal account and UTA account, this trading pair supports margin trading
640    Both,
641    /// Only for UTA account,this trading pair supports margin trading
642    UtaOnly,
643    /// Only for normal account, this trading pair supports margin trading
644    NormalSpotOnly,
645}
646
647#[derive(Debug, Deserialize, Serialize, PartialEq, Clone, Copy)]
648#[serde(rename_all = "camelCase")]
649pub enum CopyTrading {
650    /// Regardless of normal account or UTA account, this trading pair does not support copy trading
651    None,
652    /// For both normal account and UTA account, this trading pair supports copy trading
653    Both,
654    /// Only for UTA account,this trading pair supports copy trading
655    UtaOnly,
656    /// Only for normal account, this trading pair supports copy trading
657    NormalOnly,
658}
659
660#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
661#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
662pub enum Type {
663    /// Assets that transferred into Unified | (inverse) derivatives wallet
664    TransferIn,
665    /// Assets that transferred out from Unified | (inverse) derivatives wallet
666    TransferOut,
667    Trade,
668    /// USDT Perp funding settlement, and USDC Perp funding settlement + USDC 8-hour session settlement
669    /// USDT / Inverse Perp funding settlement
670    Settlement,
671    /// USDC Futures, Option delivery
672    Delivery,
673    /// Inverse Futures delivery
674    Liquidation,
675    /// Auto-Deleveraging
676    ADL,
677    Airdrop,
678    /// Bonus claimed
679    Bonus,
680    /// Bonus expired
681    BonusRecollect,
682    /// Trading fee refunded
683    FeeRefund,
684    /// Interest occurred due to borrowing
685    Interest,
686    /// Currency convert, and the liquidation for borrowing asset(UTA loan)
687    CurrencyBuy,
688    /// Currency convert, and the liquidation for borrowing asset(UTA loan)
689    CurrencySell,
690    BorrowedAmountInsLoan,
691    PrincipleRepaymentInsLoan,
692    InterestRepaymentInsLoan,
693    /// the liquidation for borrowing asset(INS loan)
694    AutoSoldCollateralInsLoan,
695    /// the liquidation for borrowing asset(INS loan)
696    AutoBuyLiabilityInsLoan,
697    AutoPrincipleRepaymentInsLoan,
698    AutoInterestRepaymentInsLoan,
699    /// Transfer In when in the liquidation of OTC loan
700    TransferInInsLoan,
701    /// Transfer Out when in the liquidation of OTC loan
702    TransferOutInsLoan,
703    /// One-click repayment currency sell
704    SpotRepaymentSell,
705    /// One-click repayment currency buy
706    SpotRepaymentBuy,
707    /// Spot leverage token subscription
708    TokensSubscription,
709    /// Spot leverage token redemption
710    TokensRedemption,
711    /// Asset auto deducted by system (roll back)
712    AutoDeduction,
713    /// Byfi flexible stake subscription
714    FlexibleStakingSubscription,
715    /// Byfi flexible stake redemption
716    FlexibleStakingRedemption,
717    /// Byfi fixed stake subscription
718    FixedStakingSubscription,
719    PremarketTransferOut,
720    PremarketDeliverySellNewCoin,
721    PremarketDeliveryBuyNewCoin,
722    PremarketDeliveryPledgePaySeller,
723    PremarketDeliveryPledgeBack,
724    PremarketRollbackPledgeBack,
725    PremarketRollbackPledgePenaltyToBuyer,
726    /// fireblocks business
727    CustodyNetworkFee,
728    /// fireblocks business
729    CustodySettleFee,
730    /// fireblocks / copper business
731    CustodyLock,
732    /// fireblocks business
733    CustodyUnlock,
734    /// fireblocks business
735    CustodyUnlockRefund,
736    /// crypto loan
737    LoansBorrowFunds,
738    /// crypto loan repayment
739    LoansPledgeAsset,
740    BonusTransferIn,
741    BonusTransferOut,
742    PefTransferIn,
743    PefTransferOut,
744    PefProfitShare,
745    #[serde(rename = "Others")]
746    Others,
747}
748
749impl Type {
750    pub fn is_uta(&self) -> bool {
751        matches!(
752            self,
753            Self::TransferIn
754                | Self::TransferOut
755                | Self::Trade
756                | Self::Settlement
757                | Self::Delivery
758                | Self::Liquidation
759                | Self::ADL
760                | Self::Airdrop
761                | Self::Bonus
762                | Self::BonusRecollect
763                | Self::FeeRefund
764                | Self::Interest
765                | Self::CurrencyBuy
766                | Self::CurrencySell
767                | Self::BorrowedAmountInsLoan
768                | Self::PrincipleRepaymentInsLoan
769                | Self::InterestRepaymentInsLoan
770                | Self::AutoSoldCollateralInsLoan
771                | Self::AutoBuyLiabilityInsLoan
772                | Self::AutoPrincipleRepaymentInsLoan
773                | Self::AutoInterestRepaymentInsLoan
774                | Self::TransferInInsLoan
775                | Self::TransferOutInsLoan
776                | Self::SpotRepaymentSell
777                | Self::SpotRepaymentBuy
778                | Self::TokensSubscription
779                | Self::TokensRedemption
780                | Self::AutoDeduction
781                | Self::FlexibleStakingSubscription
782                | Self::FlexibleStakingRedemption
783                | Self::FixedStakingSubscription
784                | Self::PremarketTransferOut
785                | Self::PremarketDeliverySellNewCoin
786                | Self::PremarketDeliveryBuyNewCoin
787                | Self::PremarketDeliveryPledgePaySeller
788                | Self::PremarketDeliveryPledgeBack
789                | Self::PremarketRollbackPledgeBack
790                | Self::PremarketRollbackPledgePenaltyToBuyer
791                | Self::CustodyNetworkFee
792                | Self::CustodySettleFee
793                | Self::CustodyLock
794                | Self::CustodyUnlock
795                | Self::CustodyUnlockRefund
796                | Self::LoansBorrowFunds
797                | Self::LoansPledgeAsset
798                | Self::BonusTransferIn
799                | Self::BonusTransferOut
800                | Self::PefTransferIn
801                | Self::PefTransferOut
802                | Self::PefProfitShare
803        )
804    }
805    pub fn is_contract(&self) -> bool {
806        matches!(
807            self,
808            Self::TransferIn
809                | Self::TransferOut
810                | Self::Trade
811                | Self::Settlement
812                | Self::Delivery
813                | Self::Liquidation
814                | Self::ADL
815                | Self::Airdrop
816                | Self::Bonus
817                | Self::BonusRecollect
818                | Self::FeeRefund
819                | Self::CurrencyBuy
820                | Self::CurrencySell
821                | Self::AutoDeduction
822                | Self::Others
823        )
824    }
825}
826
827#[derive(Serialize_repr, Deserialize_repr, Debug, PartialEq, Clone, Copy)]
828#[repr(u8)]
829pub enum UnifiedMarginStatus {
830    ClassicAccount = 1,
831    /// 1.0
832    UnifiedTradingAccount1 = 3,
833    /// 1.0 (pro version)
834    UnifiedTradingAccount1Pro = 4,
835    /// 2.0
836    UnifiedTradingAccount2 = 5,
837    /// 2.0 (pro version)
838    UnifiedTradingAccount2Pro = 6,
839}
840
841#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
842#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
843pub enum MarginMode {
844    IsolatedMargin,
845    RegularMargin,
846    PortfolioMargin,
847}
848
849/// Whether Spot Margin Trade (UTA) is turned on for the account.
850#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
851pub enum SpotMarginMode {
852    #[serde(rename = "0")]
853    Disabled,
854    #[serde(rename = "1")]
855    Enabled,
856}
857
858#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
859#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
860pub enum SpotHedgingStatus {
861    On,
862    Off,
863}
864
865#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
866pub enum LtStatus {
867    /// LT can be purchased and redeemed
868    #[serde(rename = "1")]
869    CanBePurchasedAndRedeemed,
870    /// LT can be purchased, but not redeemed
871    #[serde(rename = "2")]
872    CanBePurchasedButNotRedeemed,
873    /// LT can be redeemed, but not purchased
874    #[serde(rename = "3")]
875    CanBeRedeemedButNotPurchased,
876    /// LT cannot be purchased nor redeemed
877    #[serde(rename = "4")]
878    CannotBePurchasedNorRedeemed,
879    /// Adjusting position
880    #[serde(rename = "5")]
881    AdjustingPosition,
882}
883
884/// Status of a leveraged token purchase or redemption order.
885#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
886pub enum LtOrderStatus {
887    #[serde(rename = "1")]
888    Completed,
889    #[serde(rename = "2")]
890    InProgress,
891    #[serde(rename = "3")]
892    Failed,
893}
894
895/// Whether a leveraged token order record is a purchase or a redemption.
896#[derive(Serialize_repr, Deserialize_repr, Debug, PartialEq, Clone, Copy)]
897#[repr(u8)]
898pub enum LtOrderType {
899    Purchase = 1,
900    Redemption = 2,
901}
902
903#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
904pub enum ConvertAccountType {
905    /// Unified Trading Account
906    #[serde(rename = "eb_convert_uta")]
907    Uta,
908    /// Funding Account
909    #[serde(rename = "eb_convert_funding")]
910    Funding,
911    /// Inverse Derivatives Account (no USDT in this wallet))
912    #[serde(rename = "eb_convert_inverse")]
913    Inverse,
914    /// Spot Account
915    #[serde(rename = "eb_convert_spot")]
916    Spot,
917    /// Derivatives Account (contain USDT in this wallet)
918    #[serde(rename = "eb_convert_contract")]
919    Contract,
920}
921
922#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
923pub enum VipLevel {
924    #[serde(rename = "No VIP")]
925    NoVIP,
926    #[serde(rename = "VIP-1")]
927    VIP1,
928    #[serde(rename = "VIP-2")]
929    VIP2,
930    #[serde(rename = "VIP-3")]
931    VIP3,
932    #[serde(rename = "VIP-4")]
933    VIP4,
934    #[serde(rename = "VIP-5")]
935    VIP5,
936    #[serde(rename = "VIP-Supreme")]
937    VIPSupreme,
938    #[serde(rename = "PRO-1")]
939    PRO1,
940    #[serde(rename = "PRO-2")]
941    PRO2,
942    #[serde(rename = "PRO-3")]
943    PRO3,
944    #[serde(rename = "PRO-4")]
945    PRO4,
946    #[serde(rename = "PRO-5")]
947    PRO5,
948}
949
950#[derive(Serialize_repr, Deserialize_repr, Debug, PartialEq, Clone, Copy)]
951#[repr(u8)]
952pub enum AdlRankIndicator {
953    /// default value of empty position
954    Zero = 0,
955    One = 1,
956    Two = 2,
957    Three = 3,
958    Four = 4,
959    Five = 5,
960}
961
962#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
963pub enum SmpType {
964    /// default
965    None,
966    CancelMaker,
967    CancelTaker,
968    CancelBoth,
969}
970
971#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
972#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
973pub enum ExtraFeeType {
974    Unknown,
975    /// Government tax. Only for Indonesian site
976    Tax,
977    /// Indonesian foreign exchange tax. Only for Indonesian site
978    Cfx,
979    /// EU withholding tax. Only for EU site
980    Wht,
981    /// Indian GST tax. Only for kyc=Indian users
982    Gst,
983    /// ARE VAT tax. Only for kyc=ARE users
984    Vat,
985}
986
987#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
988#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
989pub enum ExtraSubFeeType {
990    Unknown,
991    /// Tax fee, fiat currency to digital currency. Only for Indonesian site
992    TaxPnn,
993    /// Tax fee, digital currency to fiat currency. Only for Indonesian site
994    TaxPph,
995    /// CFX fee, fiat currency to digital currency. Only for Indonesian site
996    CfxFiee,
997    /// EU site withholding tax. Only for EU site
998    AutWithholdingTax,
999    /// Indian GST tax. Only for kyc=Indian users
1000    IndGst,
1001    /// ARE VAT tax. Only for kyc=ARE users
1002    AreVat,
1003}
1004
1005#[derive(Debug, Deserialize, Serialize, PartialEq, Clone, Copy)]
1006#[serde(rename_all = "lowercase")]
1007pub enum State {
1008    Scheduled,
1009    Ongoing,
1010    Completed,
1011    Canceled,
1012}
1013
1014#[derive(Serialize_repr, Deserialize_repr, Debug, PartialEq, Clone, Copy)]
1015#[repr(u8)]
1016pub enum ServiceTypes {
1017    /// Trading service
1018    TradingService = 1,
1019    /// Trading service via http request
1020    TradingServiceViaHttpRequest = 2,
1021    /// Trading service via websocket
1022    TradingServiceViaWebsocket = 3,
1023    /// Private websocket stream
1024    PrivateWebsocketStream = 4,
1025    /// Market data service
1026    MarketDataService = 5,
1027}
1028
1029#[derive(Serialize_repr, Deserialize_repr, Debug, PartialEq, Clone, Copy)]
1030#[repr(u8)]
1031pub enum Product {
1032    Futures = 1,
1033    Spot = 2,
1034    Option = 3,
1035    Spread = 4,
1036}
1037
1038#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
1039#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
1040pub enum DCPProduct {
1041    Spot,
1042    Derivatives,
1043    Option,
1044}
1045
1046/// Outcome of an Upgrade to Unified Account (Pro) request.
1047#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
1048#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
1049pub enum UnifiedUpdateStatus {
1050    Fail,
1051    Process,
1052    Success,
1053}
1054
1055#[derive(Serialize_repr, Deserialize_repr, Debug, PartialEq, Clone, Copy)]
1056#[repr(u8)]
1057pub enum MaintainType {
1058    PlannedMaintenance = 1,
1059    TemporaryMaintenance = 2,
1060    Incident = 3,
1061}
1062
1063#[derive(Serialize_repr, Deserialize_repr, Debug, PartialEq, Clone, Copy)]
1064#[repr(u8)]
1065pub enum Env {
1066    Product = 1,
1067    ProductDemoService = 2,
1068}
1069
1070#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
1071pub enum TpslMode {
1072    Full,
1073    Partial,
1074    UNKNOWN,
1075}
1076
1077#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
1078pub enum OcoTriggerBy {
1079    #[serde(rename = "OcoTriggerByUnknown")]
1080    Unknown,
1081    #[serde(rename = "OcoTriggerByTp")]
1082    Tp,
1083    #[serde(rename = "OcoTriggerByBySl")]
1084    BySl,
1085}
1086
1087#[derive(Serialize_repr, Deserialize_repr, Debug, PartialEq, Clone, Copy)]
1088#[repr(u8)]
1089pub enum TriggerDirection {
1090    UNKNOWN = 0,
1091    Rise = 1,
1092    Fall = 2,
1093}
1094
1095#[derive(Debug, Deserialize, Serialize, PartialEq, Clone, Copy)]
1096pub enum CurAuctionPhase {
1097    /// Pre-market trading is not started
1098    NotStarted,
1099    /// Pre-market trading is finished
1100    /// After the auction, if the pre-market contract fails to enter continues trading phase, it will be delisted and phase="Finished"
1101    /// After the continuous trading, if the pre-market contract fails to be converted to official contract, it will be delisted and phase="Finished"
1102    Finished,
1103    /// Auction phase of pre-market trading
1104    /// only timeInForce=GTC, orderType=Limit order is allowed to submit
1105    /// TP/SL are not supported; Conditional orders are not supported
1106    /// cannot modify the order at this stage
1107    /// order price range: [preOpenPrice x 0.5, maxPrice]
1108    CallAuction,
1109    /// Auction no cancel phase of pre-market trading
1110    /// only timeInForce=GTC, orderType=Limit order is allowed to submit
1111    /// TP/SL are not supported; Conditional orders are not supported
1112    /// cannot modify and cancel the order at this stage
1113    /// order price range: Buy [lastPrice x 0.5, markPrice x 1.1], Sell [markPrice x 0.9, maxPrice]
1114    CallAuctionNoCancel,
1115    /// cross matching phase
1116    /// cannot create, modify and cancel the order at this stage
1117    /// Candle data is released from this stage
1118    CrossMatching,
1119    /// Continuous trading phase
1120    /// There is no restriction to create, amend, cancel orders
1121    /// orderbook, public trade data is released from this stage
1122    ContinuousTrading,
1123}
1124
1125#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
1126pub enum PlaceType {
1127    #[serde(rename = "option")]
1128    Option,
1129    #[serde(rename = "iv")]
1130    Iv,
1131    #[serde(rename = "price")]
1132    Price,
1133}
1134
1135#[derive(Debug, Deserialize, Serialize, PartialEq, Clone, Copy)]
1136pub enum Side {
1137    Buy,
1138    Sell,
1139}
1140impl Side {
1141    pub fn reverse(&self) -> Self {
1142        match self {
1143            Side::Buy => Self::Sell,
1144            Side::Sell => Self::Buy,
1145        }
1146    }
1147}
1148
1149#[derive(Debug, PartialEq, Clone, Copy)]
1150pub enum Pair {
1151    // example of BTCUSDT
1152    Base,  // BTC
1153    Quote, // USDT
1154}
1155
1156#[derive(Debug, Deserialize, PartialEq, Clone, Copy)]
1157pub enum SlippageToleranceType {
1158    TickSize,
1159    Percent,
1160    /// default
1161    UNKNOWN,
1162}
1163
1164#[derive(Serialize_repr, Deserialize_repr, Debug, PartialEq, Clone, Copy)]
1165#[repr(u8)]
1166pub enum TradeMode {
1167    CrossMargin = 0,
1168    IsolatedMargin = 1,
1169}
1170
1171#[derive(Debug, Clone, PartialEq)]
1172pub enum Topic {
1173    Orderbook {
1174        symbol: String,
1175        depth: DepthLevel,
1176    },
1177    Trade(String),
1178    Ticker(String),
1179    Kline {
1180        symbol: String,
1181        interval: Interval,
1182    },
1183    AllLiquidation(String),
1184    Position(Category),
1185    PositionAllCategory,
1186    Execution(Category),
1187    ExecutionAllCategory,
1188    FastExecution(Category),
1189    FastExecutionAllCategory,
1190    Order(Category),
1191    OrderAllCategory,
1192    Wallet,
1193    /// option only.
1194    Greek,
1195    Dcp(DcpFunction),
1196}
1197
1198impl fmt::Display for Topic {
1199    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1200        let value = match self {
1201            Self::Orderbook { symbol, depth } => format!("orderbook.{depth}.{symbol}"),
1202            Self::Trade(symbol) => format!("publicTrade.{symbol}"),
1203            Self::Ticker(symbol) => format!("tickers.{symbol}"),
1204            Self::Kline { symbol, interval } => format!("kline.{interval}.{symbol}"),
1205            Self::AllLiquidation(symbol) => format!("allLiquidation.{symbol}"),
1206            Self::Position(category) => format!("position.{category}"),
1207            Self::PositionAllCategory => "position".to_string(),
1208            Self::Execution(category) => format!("execution.{category}"),
1209            Self::ExecutionAllCategory => "execution".to_string(),
1210            Self::FastExecution(category) => format!("execution.fast.{category}"),
1211            Self::FastExecutionAllCategory => "execution.fast".to_string(),
1212            Self::Order(category) => format!("order.{category}"),
1213            Self::OrderAllCategory => "order".to_string(),
1214            Self::Wallet => "wallet".to_string(),
1215            Self::Greek => "greek".to_string(),
1216            Self::Dcp(function) => format!("dcp.{function}"),
1217        };
1218        write!(f, "{value}")
1219    }
1220}
1221
1222impl Serialize for Topic {
1223    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1224    where
1225        S: Serializer,
1226    {
1227        serializer.collect_str(self)
1228    }
1229}
1230
1231impl<'de> Deserialize<'de> for Topic {
1232    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1233    where
1234        D: Deserializer<'de>,
1235    {
1236        let s: std::borrow::Cow<'de, str> = Deserialize::deserialize(deserializer)?;
1237        let s = s.as_ref();
1238        if let Some((kind, args)) = s.split_once('.') {
1239            match kind {
1240                "orderbook" => {
1241                    if let Some((depth, symbol)) = args.split_once('.') {
1242                        let depth =
1243                            serde_json::from_str(&format!("\"{depth}\"")).map_err(|err| {
1244                                serde::de::Error::custom(format!(
1245                                    "DepthLevel: {depth}, error: {err}"
1246                                ))
1247                            })?;
1248                        Ok(Self::Orderbook {
1249                            depth,
1250                            symbol: symbol.to_owned(),
1251                        })
1252                    } else {
1253                        Err(serde::de::Error::custom("invalid stream format"))
1254                    }
1255                }
1256                "publicTrade" => Ok(Self::Trade(args.to_owned())),
1257                "tickers" => Ok(Self::Ticker(args.to_owned())),
1258                "kline" => {
1259                    if let Some((interval, symbol)) = args.split_once('.') {
1260                        let interval = serde_json::from_str(&format!("\"{interval}\""))
1261                            .map_err(|err| serde::de::Error::custom(format!("Interval: {err}")))?;
1262                        Ok(Self::Kline {
1263                            symbol: symbol.to_owned(),
1264                            interval,
1265                        })
1266                    } else {
1267                        Err(serde::de::Error::custom("invalid stream format"))
1268                    }
1269                }
1270                "allLiquidation" => Ok(Self::AllLiquidation(args.to_owned())),
1271                "position" => {
1272                    let category = serde_json::from_str(&format!("\"{args}\""))
1273                        .map_err(|err| serde::de::Error::custom(format!("category: {err}")))?;
1274                    Ok(Self::Position(category))
1275                }
1276                "execution" => {
1277                    if args == "fast" {
1278                        Ok(Self::FastExecutionAllCategory)
1279                    } else if let Some((fast, category)) = args.split_once('.') {
1280                        let category = serde_json::from_str(&format!("\"{category}\""))
1281                            .map_err(|err| serde::de::Error::custom(format!("category: {err}")))?;
1282                        if fast == "fast" {
1283                            Ok(Self::FastExecution(category))
1284                        } else {
1285                            Ok(Self::Execution(category))
1286                        }
1287                    } else {
1288                        let category = serde_json::from_str(&format!("\"{args}\""))
1289                            .map_err(|err| serde::de::Error::custom(format!("category: {err}")))?;
1290                        Ok(Self::Execution(category))
1291                    }
1292                }
1293                "order" => {
1294                    let category = serde_json::from_str(&format!("\"{args}\""))
1295                        .map_err(|err| serde::de::Error::custom(format!("category: {err}")))?;
1296                    Ok(Self::Order(category))
1297                }
1298                "dcp" => {
1299                    let function = serde_json::from_str(&format!("\"{args}\""))
1300                        .map_err(|err| serde::de::Error::custom(format!("DcpFunction: {err}")))?;
1301                    Ok(Self::Dcp(function))
1302                }
1303                _ => Err(serde::de::Error::custom("invalid stream format")),
1304            }
1305        } else {
1306            match s {
1307                "position" => Ok(Self::PositionAllCategory),
1308                "execution" => Ok(Self::ExecutionAllCategory),
1309                "order" => Ok(Self::OrderAllCategory),
1310                "wallet" => Ok(Self::Wallet),
1311                "greek" => Ok(Self::Greek),
1312                _ => {
1313                    let msg = String::from("invalid stream format");
1314                    Err(serde::de::Error::custom(msg))
1315                }
1316            }
1317        }
1318    }
1319}
1320
1321#[derive(Debug, Deserialize, Serialize, PartialEq, Clone, Copy)]
1322#[serde(rename_all = "lowercase")]
1323pub enum DcpFunction {
1324    Future,
1325    Option,
1326    Spot,
1327}
1328
1329impl fmt::Display for DcpFunction {
1330    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1331        let value = match self {
1332            Self::Future => "future",
1333            Self::Option => "option",
1334            Self::Spot => "spot",
1335        };
1336        write!(f, "{value}")
1337    }
1338}
1339
1340#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
1341pub enum DepthLevel {
1342    #[serde(rename = "1")]
1343    Level1,
1344    #[serde(rename = "25")]
1345    Level25,
1346    #[serde(rename = "50")]
1347    Level50,
1348    #[serde(rename = "100")]
1349    Level100,
1350    #[serde(rename = "200")]
1351    Level200,
1352    /// Perpetuals and futures only.
1353    #[serde(rename = "500")]
1354    Level500,
1355    /// Spot only.
1356    #[serde(rename = "1000")]
1357    Level1000,
1358}
1359
1360impl fmt::Display for DepthLevel {
1361    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1362        let value = match self {
1363            Self::Level1 => "1",
1364            Self::Level25 => "25",
1365            Self::Level50 => "50",
1366            Self::Level100 => "100",
1367            Self::Level200 => "200",
1368            Self::Level500 => "500",
1369            Self::Level1000 => "1000",
1370        };
1371        write!(f, "{value}")
1372    }
1373}
1374
1375pub fn spot_fee_currency(side: Side, is_maker_order: bool, maker_fee_rate: f64) -> Pair {
1376    if maker_fee_rate >= 0.0 {
1377        match side {
1378            Side::Buy => Pair::Base,
1379            Side::Sell => Pair::Quote,
1380        }
1381    } else if is_maker_order {
1382        match side {
1383            Side::Buy => Pair::Quote,
1384            Side::Sell => Pair::Base,
1385        }
1386    } else {
1387        match side {
1388            Side::Buy => Pair::Base,
1389            Side::Sell => Pair::Quote,
1390        }
1391    }
1392}
1393
1394#[cfg(test)]
1395mod tests {
1396    use crate::serde::deserialize_json;
1397
1398    use super::*;
1399
1400    #[test]
1401    fn serialize_category() {
1402        let cases = [
1403            (Category::Inverse, r#""inverse""#),
1404            (Category::Linear, r#""linear""#),
1405            (Category::Option, r#""option""#),
1406            (Category::Spot, r#""spot""#),
1407        ];
1408        cases.iter().for_each(|(category, expected)| {
1409            let json = serde_json::to_string(category).unwrap();
1410            assert_eq!(json, *expected);
1411        });
1412    }
1413
1414    #[test]
1415    fn deserialize_category() {
1416        let cases = [
1417            (r#""inverse""#, Category::Inverse),
1418            (r#""linear""#, Category::Linear),
1419            (r#""option""#, Category::Option),
1420            (r#""spot""#, Category::Spot),
1421        ];
1422        cases.iter().for_each(|(json, expected)| {
1423            let message: Category = deserialize_json(json).unwrap();
1424            assert_eq!(message, *expected);
1425        });
1426    }
1427
1428    #[test]
1429    fn serialize_interval() {
1430        let cases = vec![
1431            (Interval::Minute1, r#""1""#),
1432            (Interval::Minute3, r#""3""#),
1433            (Interval::Minute5, r#""5""#),
1434            (Interval::Minute15, r#""15""#),
1435            (Interval::Minute30, r#""30""#),
1436            (Interval::Hour1, r#""60""#),
1437            (Interval::Hour2, r#""120""#),
1438            (Interval::Hour4, r#""240""#),
1439            (Interval::Hour6, r#""360""#),
1440            (Interval::Hour12, r#""720""#),
1441            (Interval::Day1, r#""D""#),
1442            (Interval::Week1, r#""W""#),
1443            (Interval::Month1, r#""M""#),
1444        ];
1445        cases.iter().for_each(|(category, expected)| {
1446            let json = serde_json::to_string(category).unwrap();
1447            assert_eq!(json, *expected);
1448        });
1449    }
1450
1451    #[test]
1452    fn deserialize_interval() {
1453        let cases = vec![
1454            (r#""1""#, Interval::Minute1),
1455            (r#""3""#, Interval::Minute3),
1456            (r#""5""#, Interval::Minute5),
1457            (r#""15""#, Interval::Minute15),
1458            (r#""30""#, Interval::Minute30),
1459            (r#""60""#, Interval::Hour1),
1460            (r#""120""#, Interval::Hour2),
1461            (r#""240""#, Interval::Hour4),
1462            (r#""360""#, Interval::Hour6),
1463            (r#""720""#, Interval::Hour12),
1464            (r#""D""#, Interval::Day1),
1465            (r#""W""#, Interval::Week1),
1466            (r#""M""#, Interval::Month1),
1467        ];
1468        cases.iter().for_each(|(json, expected)| {
1469            let message: Interval = deserialize_json(json).unwrap();
1470            assert_eq!(message, *expected);
1471        });
1472    }
1473
1474    #[test]
1475    fn deserialize_topic_with_escaped_characters() {
1476        let topic: Topic = serde_json::from_str(r#""tickers.BTC\u0055SDT""#).unwrap();
1477
1478        assert_eq!(topic, Topic::Ticker(String::from("BTCUSDT")));
1479    }
1480
1481    #[test]
1482    fn serialize_topic() {
1483        let symbol = String::from("BTCUSDT");
1484        let cases = vec![
1485            (
1486                Topic::Orderbook {
1487                    symbol: symbol.clone(),
1488                    depth: DepthLevel::Level1,
1489                },
1490                r#""orderbook.1.BTCUSDT""#,
1491            ),
1492            (Topic::Trade(symbol.clone()), r#""publicTrade.BTCUSDT""#),
1493            (Topic::Ticker(symbol.clone()), r#""tickers.BTCUSDT""#),
1494            (
1495                Topic::Kline {
1496                    symbol: symbol.clone(),
1497                    interval: Interval::Minute1,
1498                },
1499                r#""kline.1.BTCUSDT""#,
1500            ),
1501            (
1502                Topic::AllLiquidation(symbol.clone()),
1503                r#""allLiquidation.BTCUSDT""#,
1504            ),
1505            (Topic::Position(Category::Linear), r#""position.linear""#),
1506            (Topic::PositionAllCategory, r#""position""#),
1507            (Topic::Execution(Category::Linear), r#""execution.linear""#),
1508            (Topic::ExecutionAllCategory, r#""execution""#),
1509            (
1510                Topic::FastExecution(Category::Linear),
1511                r#""execution.fast.linear""#,
1512            ),
1513            (Topic::FastExecutionAllCategory, r#""execution.fast""#),
1514            (Topic::Order(Category::Linear), r#""order.linear""#),
1515            (Topic::OrderAllCategory, r#""order""#),
1516            (Topic::Wallet, r#""wallet""#),
1517            (Topic::Greek, r#""greek""#),
1518            (Topic::Dcp(DcpFunction::Future), r#""dcp.future""#),
1519        ];
1520        cases.iter().for_each(|(category, expected)| {
1521            let json = serde_json::to_string(category).unwrap();
1522            assert_eq!(*expected, json);
1523        });
1524    }
1525
1526    #[test]
1527    fn deserialize_topic() {
1528        let symbol = String::from("BTCUSDT");
1529        let cases = vec![
1530            (
1531                r#""orderbook.1.BTCUSDT""#,
1532                Topic::Orderbook {
1533                    symbol: symbol.clone(),
1534                    depth: DepthLevel::Level1,
1535                },
1536            ),
1537            (r#""publicTrade.BTCUSDT""#, Topic::Trade(symbol.clone())),
1538            (r#""tickers.BTCUSDT""#, Topic::Ticker(symbol.clone())),
1539            (
1540                r#""kline.1.BTCUSDT""#,
1541                Topic::Kline {
1542                    symbol: symbol.clone(),
1543                    interval: Interval::Minute1,
1544                },
1545            ),
1546            (
1547                r#""allLiquidation.BTCUSDT""#,
1548                Topic::AllLiquidation(symbol.clone()),
1549            ),
1550            (r#""position.linear""#, Topic::Position(Category::Linear)),
1551            (r#""position""#, Topic::PositionAllCategory),
1552            (r#""execution.linear""#, Topic::Execution(Category::Linear)),
1553            (r#""execution""#, Topic::ExecutionAllCategory),
1554            (
1555                r#""execution.fast.linear""#,
1556                Topic::FastExecution(Category::Linear),
1557            ),
1558            (r#""execution.fast""#, Topic::FastExecutionAllCategory),
1559            (r#""order.linear""#, Topic::Order(Category::Linear)),
1560            (r#""order""#, Topic::OrderAllCategory),
1561            (r#""wallet""#, Topic::Wallet),
1562            (r#""greek""#, Topic::Greek),
1563            (r#""dcp.future""#, Topic::Dcp(DcpFunction::Future)),
1564        ];
1565        cases.iter().for_each(|(json, expected)| {
1566            let message = deserialize_json(json).unwrap();
1567            assert_eq!(*expected, message);
1568        });
1569    }
1570}