trd-utils 0.0.12__tar.gz → 0.0.14__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of trd-utils might be problematic. Click here for more details.

Files changed (24) hide show
  1. {trd_utils-0.0.12 → trd_utils-0.0.14}/PKG-INFO +1 -1
  2. {trd_utils-0.0.12 → trd_utils-0.0.14}/pyproject.toml +1 -1
  3. trd_utils-0.0.14/trd_utils/__init__.py +3 -0
  4. {trd_utils-0.0.12 → trd_utils-0.0.14}/trd_utils/exchanges/bx_ultra/bx_types.py +289 -42
  5. {trd_utils-0.0.12 → trd_utils-0.0.14}/trd_utils/exchanges/bx_ultra/bx_ultra_client.py +46 -3
  6. {trd_utils-0.0.12 → trd_utils-0.0.14}/trd_utils/types_helper/base_model.py +22 -3
  7. trd_utils-0.0.12/trd_utils/__init__.py +0 -3
  8. {trd_utils-0.0.12 → trd_utils-0.0.14}/LICENSE +0 -0
  9. {trd_utils-0.0.12 → trd_utils-0.0.14}/README.md +0 -0
  10. {trd_utils-0.0.12 → trd_utils-0.0.14}/trd_utils/cipher/__init__.py +0 -0
  11. {trd_utils-0.0.12 → trd_utils-0.0.14}/trd_utils/common_utils/float_utils.py +0 -0
  12. {trd_utils-0.0.12 → trd_utils-0.0.14}/trd_utils/exchanges/__init__.py +0 -0
  13. {trd_utils-0.0.12 → trd_utils-0.0.14}/trd_utils/exchanges/blofin/__init__.py +0 -0
  14. {trd_utils-0.0.12 → trd_utils-0.0.14}/trd_utils/exchanges/blofin/blofin_client.py +0 -0
  15. {trd_utils-0.0.12 → trd_utils-0.0.14}/trd_utils/exchanges/blofin/blofin_types.py +0 -0
  16. {trd_utils-0.0.12 → trd_utils-0.0.14}/trd_utils/exchanges/bx_ultra/__init__.py +0 -0
  17. {trd_utils-0.0.12 → trd_utils-0.0.14}/trd_utils/exchanges/bx_ultra/bx_utils.py +0 -0
  18. {trd_utils-0.0.12 → trd_utils-0.0.14}/trd_utils/exchanges/exchange_base.py +0 -0
  19. {trd_utils-0.0.12 → trd_utils-0.0.14}/trd_utils/html_utils/__init__.py +0 -0
  20. {trd_utils-0.0.12 → trd_utils-0.0.14}/trd_utils/html_utils/html_formats.py +0 -0
  21. {trd_utils-0.0.12 → trd_utils-0.0.14}/trd_utils/tradingview/__init__.py +0 -0
  22. {trd_utils-0.0.12 → trd_utils-0.0.14}/trd_utils/tradingview/tradingview_client.py +0 -0
  23. {trd_utils-0.0.12 → trd_utils-0.0.14}/trd_utils/tradingview/tradingview_types.py +0 -0
  24. {trd_utils-0.0.12 → trd_utils-0.0.14}/trd_utils/types_helper/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: trd_utils
3
- Version: 0.0.12
3
+ Version: 0.0.14
4
4
  Summary: Common Basic Utils for Python3. By ALiwoto.
5
5
  Keywords: utils,trd_utils,basic-utils,common-utils
6
6
  Author: ALiwoto
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "trd_utils"
3
- version = "0.0.12"
3
+ version = "0.0.14"
4
4
  description = "Common Basic Utils for Python3. By ALiwoto."
5
5
  authors = ["ALiwoto <aminnimaj@gmail.com>"]
6
6
  packages = [
@@ -0,0 +1,3 @@
1
+
2
+ __version__ = "0.0.14"
3
+
@@ -10,12 +10,21 @@ from trd_utils.common_utils.float_utils import (
10
10
  dec_to_normalize,
11
11
  )
12
12
 
13
+ ###########################################################
14
+
15
+ # region constant variables
16
+
13
17
  ORDER_TYPES_MAP = {
14
18
  0: "LONG",
15
19
  1: "SHORT",
16
20
  }
17
21
 
22
+ # endregion
23
+
24
+ ###########################################################
25
+
18
26
 
27
+ # region Common types
19
28
  class BxApiResponse(BaseModel):
20
29
  code: int = None
21
30
  timestamp: int = None
@@ -65,8 +74,26 @@ class CoinQuotationInfo(BaseModel):
65
74
  return f"{self.coin_name}/{self.valuation_coin_name}; price: {self.close}; vol: {self.market_val}"
66
75
 
67
76
 
77
+ class ExchangeVo(BaseModel):
78
+ exchange_id: int = None
79
+ exchange_name: str = None
80
+ icon: str = None
81
+ account_enum: str = None
82
+ desc: str = None
83
+
84
+ def __str__(self):
85
+ return f"{self.exchange_name} ({self.exchange_id}) - {self.account_enum}"
86
+
87
+ def __repr__(self):
88
+ return f"{self.exchange_name} ({self.exchange_id}) - {self.account_enum}"
89
+
90
+
91
+ # endregion
92
+
68
93
  ###########################################################
69
94
 
95
+ # region ZoneModule types
96
+
70
97
 
71
98
  class ZoneModuleInfo(BaseModel):
72
99
  id: int = None
@@ -94,8 +121,12 @@ class ZoneModuleListResponse(BxApiResponse):
94
121
  data: ZoneModuleListResult = None
95
122
 
96
123
 
124
+ # endregion
125
+
97
126
  ###########################################################
98
127
 
128
+ # region UserFavorite types
129
+
99
130
 
100
131
  class UserFavoriteQuotationResult(BaseModel):
101
132
  usdt_margin_list: list[CoinQuotationInfo] = None
@@ -110,8 +141,12 @@ class UserFavoriteQuotationResponse(BxApiResponse):
110
141
  data: UserFavoriteQuotationResult = None
111
142
 
112
143
 
144
+ # endregion
145
+
113
146
  ###########################################################
114
147
 
148
+ # region QuotationRank types
149
+
115
150
 
116
151
  class QuotationRankBizItem(BaseModel):
117
152
  quotation_list: list[CoinQuotationInfo] = None
@@ -137,6 +172,8 @@ class QuotationRankResponse(BxApiResponse):
137
172
  data: QuotationRankResult = None
138
173
 
139
174
 
175
+ # endregion
176
+
140
177
  ###########################################################
141
178
 
142
179
  # region HotSearch types
@@ -150,7 +187,7 @@ class HotSearchItem(BaseModel):
150
187
 
151
188
  def __str__(self):
152
189
  return f"{self.coin_name} ({self.symbol})"
153
-
190
+
154
191
  def __repr__(self):
155
192
  return f"{self.coin_name} ({self.symbol})"
156
193
 
@@ -168,13 +205,13 @@ class HotSearchResponse(BxApiResponse):
168
205
  def __str__(self):
169
206
  if not self.data:
170
207
  return "HotSearchResponse: No data"
171
-
208
+
172
209
  str_result = "HotSearchResponse: \n"
173
210
  for current_item in self.data.result:
174
211
  str_result += f" - {current_item}\n"
175
-
212
+
176
213
  return str_result
177
-
214
+
178
215
  def __repr__(self):
179
216
  return self.__str__()
180
217
 
@@ -338,9 +375,10 @@ class HomePageResponse(BxApiResponse):
338
375
 
339
376
  # endregion
340
377
 
341
-
342
378
  ###########################################################
343
379
 
380
+ # region ZenDesk types
381
+
344
382
 
345
383
  class ZenDeskABStatusResult(BaseModel):
346
384
  ab_status: int = None
@@ -349,9 +387,18 @@ class ZenDeskABStatusResult(BaseModel):
349
387
  class ZenDeskABStatusResponse(BxApiResponse):
350
388
  data: ZenDeskABStatusResult = None
351
389
 
390
+ class ZenDeskAuthResult(BaseModel):
391
+ jwt: str = None
392
+
393
+ class ZenDeskAuthResponse(BxApiResponse):
394
+ data: ZenDeskAuthResult = None
395
+
396
+ # endregion
352
397
 
353
398
  ###########################################################
354
399
 
400
+ # region HintList types
401
+
355
402
 
356
403
  class HintListResult(BaseModel):
357
404
  hints: list = None # unknown
@@ -361,9 +408,11 @@ class HintListResponse(BxApiResponse):
361
408
  data: HintListResult = None
362
409
 
363
410
 
411
+ # endregion
412
+
364
413
  ###########################################################
365
414
 
366
- #region CopyTrading types
415
+ # region CopyTrading types
367
416
 
368
417
 
369
418
  class CopyTradingSymbolConfigInfo(BaseModel):
@@ -417,6 +466,155 @@ class CopyTraderTradePositionsResponse(BxApiResponse):
417
466
  data: CopyTraderTradePositionsResult = None
418
467
 
419
468
 
469
+ class CopyTraderFuturesStatsResult(BaseModel):
470
+ api_identity: int = None
471
+ dis_play_name: str = None
472
+ icon: str = None
473
+ valid: int = None
474
+ risk_status: int = None
475
+ is_relation: int = None
476
+ copier_status: int = None
477
+ vst_copier_status: int = None
478
+ follower_full: bool = None
479
+ being_invite: bool = None
480
+ str_follower_num: int = None
481
+ equity: str = None
482
+ total_earnings: str = None
483
+ follower_earning: str = None
484
+ max_draw_down: str = None
485
+ str_total_earnings_rate: str = None
486
+ str_recent30_days_rate: str = None
487
+ str_recent7_days_rate: str = None
488
+ str_recent90_days_rate: str = None
489
+ str_recent180_days_rate: str = None
490
+ exchange_vo: ExchangeVo = None
491
+ update_time: datetime = None
492
+ commission_rate: float = None
493
+ risk_level7_days: int = None
494
+ risk_level30_days: int = None
495
+ risk_level90_days: int = None
496
+ risk_level180_days: int = None
497
+ str_acc_follower_num: int = None
498
+ win_rate: str = None
499
+ total_transactions: int = None
500
+ profit_count: int = None
501
+ avg_profit_amount: str = None
502
+ avg_profit_rate: str = None
503
+ loss_count: int = None
504
+ avg_loss_amount: str = None
505
+ avg_loss_rate: str = None
506
+ pnl_rate: str = None
507
+ avg_hold_time: int = None
508
+ weekly_trade_frequency: str = None
509
+ trade_days: int = None
510
+ last_trade_time: datetime = None
511
+ expand: int = None
512
+ recent7_day_follower_num_change: int = None
513
+ recent30_day_follower_num_change: int = None
514
+ recent90_day_follower_num_change: int = None
515
+ recent180_day_follower_num_change: int = None
516
+ latest30_days_median_margin: str = None
517
+ latest30_days_median_lever_times: str = None
518
+ cumulative_profit_loss7_d: float = None
519
+ cumulative_profit_loss30_d: float = None
520
+ cumulative_profit_loss90_d: float = None
521
+ cumulative_profit_loss180_d: float = None
522
+ maximum_draw_down: int = None
523
+ max_draw_down7_d: float = None
524
+ max_draw_down30_d: float = None
525
+ max_draw_down90_d: float = None
526
+ max_draw_down180_d: float = None
527
+ total_position_count: int = None
528
+ profitable_position_count: int = None
529
+ loss_position_count: int = None
530
+ profit_realized_pnl_u: float = None
531
+ loss_realized_pnl_u: float = None
532
+ pnl_rate_u: str = None
533
+ avg_profit: float = None
534
+ avg_loss: float = None
535
+ is_pro: int = None
536
+
537
+
538
+ class CopyTraderFuturesStatsResponse(BxApiResponse):
539
+ data: CopyTraderFuturesStatsResult = None
540
+
541
+
542
+ class CopyTraderInfo(BaseModel):
543
+ nick_name: str = None
544
+ avatar: str = None
545
+ brief: str = None
546
+ uid: int = None
547
+ register_date: datetime = None
548
+ calling_code: str = None
549
+ team_id: int = None
550
+ short_uid: int = None
551
+ identity_type: int = None
552
+
553
+ class CopyTraderVo(BaseModel):
554
+ audit_status: int = None
555
+ trader_status: int = None
556
+ profit_share_rate: float = None
557
+ trader_role: int = None
558
+ recent_avg_margin: int = None
559
+ min_basic_copy_trade_unit: int = None
560
+ max_basic_copy_trade_unit: int = None
561
+ basic_copy_trade_unit: int = None
562
+ copy_trade_rate_on: bool = None
563
+ trader_protect_status: int = None
564
+ trader_public_recommend_status: int = None
565
+ rank_account_id: int = None
566
+ last_trader_time: datetime = None
567
+
568
+ class CopyTraderAccountGradeVO(BaseModel):
569
+ uid: int = None
570
+ api_identity: int = None
571
+ trader_grade: int = None
572
+ label: int = None
573
+ uid_and_api: str = None
574
+
575
+ class CopyTraderSharingAccount(BaseModel):
576
+ category: int = None
577
+ trader: int = None
578
+ api_identity: int = None
579
+ display_name: str = None
580
+ icon: str = None
581
+ valid: int = None
582
+ copier_status: int = None
583
+ vst_copier_status: int = None
584
+ follower_full: bool = None
585
+ copy_trade_account_enum: str = None
586
+ order: int = None
587
+ trader_account_grade_vo: Optional[CopyTraderAccountGradeVO] = None
588
+ hide_info: int = None
589
+ copy_trade_label_type: Optional[int] = None
590
+
591
+ class CopyTraderResumeResult(BaseModel):
592
+ trader_info: CopyTraderInfo = None
593
+ trader_vo: CopyTraderVo = None
594
+ tags: list = None
595
+ has_new: int = None
596
+ labels: list = None
597
+ has_subscribed: int = None
598
+ fans_num: int = None
599
+ follower_num: int = None
600
+ subscriber_num: int = None
601
+ category: int = None
602
+ api_identity: int = None
603
+ trader_sharing_accounts: list[CopyTraderSharingAccount] = None
604
+ latest30_days_earning_ratio: str = None
605
+ swap_copy_trade_label_type: int = None
606
+ is_pro: int = None
607
+
608
+ class CopyTraderResumeResponse(BxApiResponse):
609
+ data: CopyTraderResumeResult = None
610
+
611
+ # endregion
612
+
613
+ ###########################################################
614
+
615
+ # region SearchCopyTrading types
616
+
617
+
420
618
  class SearchCopyTraderCondition(BaseModel):
421
619
  key: str = "exchangeId"
422
620
  selected: int = 2
@@ -429,9 +627,11 @@ class SearchCopyTraderCondition(BaseModel):
429
627
  "type": self.type,
430
628
  }
431
629
 
630
+
432
631
  class SearchedTraderChartItem(BaseModel):
433
632
  cumulative_pnl_rate: Decimal = None
434
633
 
634
+
435
635
  class SearchedTraderExchangeVoInfo(BaseModel):
436
636
  account_enum: str = None
437
637
  desc: str = None
@@ -439,6 +639,7 @@ class SearchedTraderExchangeVoInfo(BaseModel):
439
639
  exchange_name: str = None
440
640
  icon: str = None
441
641
 
642
+
442
643
  class SearchTraderInfoRankStat(BaseModel):
443
644
  api_identity: int = None
444
645
  avg_hold_time: str = None
@@ -451,7 +652,7 @@ class SearchTraderInfoRankStat(BaseModel):
451
652
  copier_status: int = None
452
653
  dis_play_name: str = None
453
654
  equity: str = None
454
- exchange_vo: Any = None # unknown
655
+ exchange_vo: ExchangeVo = None # unknown
455
656
  expand: int = None
456
657
  follower_earning: str = None
457
658
  follower_full: bool = None
@@ -491,6 +692,7 @@ class SearchTraderInfoRankStat(BaseModel):
491
692
  weekly_trade_frequency: str = None
492
693
  winRate: str = None
493
694
 
695
+
494
696
  class SearchedTraderInfo(BaseModel):
495
697
  avatar: str = None
496
698
  be_trader: bool = None
@@ -505,12 +707,14 @@ class SearchedTraderInfo(BaseModel):
505
707
  team_id: int = None
506
708
  uid: int = None
507
709
 
710
+
508
711
  class SearchedTraderAccountGradeVoInfo(BaseModel):
509
712
  api_identity: int = None
510
713
  label: int = None
511
714
  trader_grade: int = None
512
715
  uid: int = None
513
716
 
717
+
514
718
  class SearchTraderInfoContainer(BaseModel):
515
719
  content: str = None
516
720
  has_new: bool = None
@@ -523,34 +727,35 @@ class SearchTraderInfoContainer(BaseModel):
523
727
  def get_nick_name(self) -> str:
524
728
  if self.trader:
525
729
  return self.trader.nick_name
526
-
730
+
527
731
  return
528
732
 
529
733
  def get_uid(self) -> int:
530
734
  if self.trader:
531
735
  return self.trader.uid
532
-
736
+
533
737
  return
534
738
 
535
739
  def get_api_identity(self) -> int:
536
740
  if self.trader_account_grade_vo:
537
741
  return self.trader_account_grade_vo.api_identity
538
-
742
+
539
743
  if self.rank_stat:
540
744
  return self.rank_stat.api_identity
541
-
745
+
542
746
  # TODO: later on add support for more cases
543
747
  return None
544
748
 
545
749
  def __str__(self):
546
750
  if not self.trader:
547
751
  return "No trader info"
548
-
752
+
549
753
  return f"uid: {self.trader.uid}; name: {self.trader.nick_name}; country: {self.trader.nation}"
550
-
754
+
551
755
  def __repr__(self):
552
756
  return self.__str__()
553
757
 
758
+
554
759
  class SearchCopyTradersResult(BaseModel):
555
760
  expand_display: Any = None # unknown
556
761
  fold_display: Any = None # unknown
@@ -565,16 +770,18 @@ class SearchCopyTradersResult(BaseModel):
565
770
  search_result: bool = None
566
771
  total: int = None
567
772
 
773
+
568
774
  class SearchCopyTradersResponse(BxApiResponse):
569
775
  data: SearchCopyTradersResult = None
570
776
 
571
777
 
572
- #endregion
778
+ # endregion
573
779
 
574
780
  ###########################################################
575
781
 
576
782
  # region Account Assets types
577
783
 
784
+
578
785
  class TotalAssetsInfo(BaseModel):
579
786
  amount: Any = None # unknown
580
787
  currency_amount: Decimal = None
@@ -582,10 +789,11 @@ class TotalAssetsInfo(BaseModel):
582
789
 
583
790
  def __str__(self):
584
791
  return f"{dec_to_str(self.currency_amount)} {self.sign}"
585
-
792
+
586
793
  def __repr__(self):
587
794
  return self.__str__()
588
795
 
796
+
589
797
  class AccountOverviewItem(BaseModel):
590
798
  account_name: str = None
591
799
  account_type: int = None
@@ -596,6 +804,7 @@ class AccountOverviewItem(BaseModel):
596
804
  def __str__(self):
597
805
  return f"{self.account_name} ({self.account_type}): {self.total}"
598
806
 
807
+
599
808
  class AssetsInfoResult(BaseModel):
600
809
  total: TotalAssetsInfo = None
601
810
  account_overviews: list[AccountOverviewItem] = None
@@ -606,24 +815,28 @@ class AssetsInfoResult(BaseModel):
606
815
  fault_flag: int = None
607
816
  fault_accounts: Any = None # unknown
608
817
 
818
+
609
819
  class AssetsInfoResponse(BxApiResponse):
610
820
  data: AssetsInfoResult = None
611
821
 
822
+
612
823
  # endregion
613
824
 
614
825
  ###########################################################
615
826
 
616
827
  # region Contracts types
617
828
 
829
+
618
830
  class BasicCoinInfo(BaseModel):
619
831
  name: str = None
620
832
 
621
833
  def __str__(self):
622
834
  return f"{self.name} CoinInfo"
623
-
835
+
624
836
  def __repr__(self):
625
837
  return self.__str__()
626
838
 
839
+
627
840
  class QuotationCoinVOInfo(BaseModel):
628
841
  id: int = None
629
842
  coin: BasicCoinInfo = None
@@ -632,14 +845,17 @@ class QuotationCoinVOInfo(BaseModel):
632
845
  name: str = None
633
846
  market_status: int = None
634
847
 
848
+
635
849
  class OrderDebitInfo(BaseModel):
636
850
  lend_coin: BasicCoinInfo = None
637
851
  amount: Decimal = None
638
852
 
853
+
639
854
  class OrderOpenTradeInfo(BaseModel):
640
855
  traded_amount: Decimal = None
641
856
  traded_cash_amount: Decimal = None
642
857
 
858
+
643
859
  class ContractOrderStatus(BaseModel):
644
860
  code: int = None
645
861
  value: str = None
@@ -658,6 +874,7 @@ class ContractTakeProfitInfo(BaseModel):
658
874
  close_style: int = None
659
875
  all_close: bool = None
660
876
 
877
+
661
878
  class ContractStopLossInfo(BaseModel):
662
879
  id: int = None
663
880
 
@@ -671,6 +888,7 @@ class ContractStopLossInfo(BaseModel):
671
888
  close_style: int = None
672
889
  all_close: bool = None
673
890
 
891
+
674
892
  class ProfitLossInfoContainer(BaseModel):
675
893
  loss_nums: int = None
676
894
  profit_nums: int = None
@@ -679,6 +897,7 @@ class ProfitLossInfoContainer(BaseModel):
679
897
  profit_config: ContractTakeProfitInfo = None
680
898
  loss_config: ContractStopLossInfo = None
681
899
 
900
+
682
901
  class ContractOrderInfo(BaseModel):
683
902
  order_no: int = None
684
903
  quotation_coin_vo: QuotationCoinVOInfo = None
@@ -686,7 +905,7 @@ class ContractOrderInfo(BaseModel):
686
905
  margin_coin_name: str = None
687
906
  lever_times: Decimal = None
688
907
  display_lever_times: Decimal = None
689
- amount: Decimal = None # margin * lever_times
908
+ amount: Decimal = None # margin * lever_times
690
909
  display_price: Decimal = None
691
910
  display_close_price: Decimal = None
692
911
  order_type: int = None
@@ -729,10 +948,10 @@ class ContractOrderInfo(BaseModel):
729
948
 
730
949
  def get_open_price(self) -> Decimal:
731
950
  return self.display_price
732
-
951
+
733
952
  def get_liquidation_price(self) -> Decimal:
734
953
  return self.sys_force_price
735
-
954
+
736
955
  def get_profit_str(self) -> str:
737
956
  last_price = self.current_price or self.display_close_price
738
957
  profit_or_loss = last_price - self.display_price
@@ -756,14 +975,20 @@ class ContractOrderInfo(BaseModel):
756
975
  sl_config = self.profit_loss_info.loss_config
757
976
  result_str += f"SL: {dec_to_normalize(sl_config.stop_price)}"
758
977
  result_str += f"{sl_config.margin_coin_name}{separator}"
759
-
978
+
760
979
  if self.sys_force_price:
761
- result_str += f"liquidation: {dec_to_normalize(self.sys_force_price)}{separator}"
762
-
980
+ result_str += (
981
+ f"liquidation: {dec_to_normalize(self.sys_force_price)}{separator}"
982
+ )
983
+
763
984
  if self.current_price:
764
- result_str += f"current price: {dec_to_normalize(self.current_price)}{separator}"
985
+ result_str += (
986
+ f"current price: {dec_to_normalize(self.current_price)}{separator}"
987
+ )
765
988
  elif self.display_close_price:
766
- result_str += f"close price: {dec_to_normalize(self.display_close_price)}{separator}"
989
+ result_str += (
990
+ f"close price: {dec_to_normalize(self.display_close_price)}{separator}"
991
+ )
767
992
  profit_str = self.get_profit_str()
768
993
  result_str += f"profit: {profit_str}%"
769
994
 
@@ -771,15 +996,17 @@ class ContractOrderInfo(BaseModel):
771
996
 
772
997
  def __str__(self):
773
998
  return self.to_str()
774
-
999
+
775
1000
  def __repr__(self):
776
1001
  return self.to_str()
777
1002
 
1003
+
778
1004
  class ClosedContractOrderInfo(ContractOrderInfo):
779
1005
  close_type_name: str = None
780
1006
  gross_earnings: Decimal = None
781
1007
  position_order: int = None
782
1008
 
1009
+
783
1010
  class MarginStatInfo(BaseModel):
784
1011
  name: str = None
785
1012
  margin_coin_name: str = None
@@ -788,18 +1015,22 @@ class MarginStatInfo(BaseModel):
788
1015
  # total count of open contract orders in this margin-type.
789
1016
  total: int = None
790
1017
 
1018
+
791
1019
  class ContractsListResult(BaseModel):
792
1020
  orders: list[ContractOrderInfo] = None
793
1021
  page_id: int = None
794
1022
  margin_stats: list[MarginStatInfo] = None
795
1023
 
1024
+
796
1025
  class ContractsListResponse(BxApiResponse):
797
1026
  data: ContractsListResult = None
798
1027
 
1028
+
799
1029
  class ContractOrdersHistoryResult(BaseModel):
800
1030
  orders: list[ClosedContractOrderInfo] = None
801
1031
  page_id: int = None
802
1032
 
1033
+
803
1034
  class ContractOrdersHistoryResponse(BxApiResponse):
804
1035
  data: ContractOrdersHistoryResult = None
805
1036
 
@@ -813,19 +1044,23 @@ class ContractOrdersHistoryResponse(BxApiResponse):
813
1044
  today = datetime.now(timezone).date()
814
1045
  for current_order in self.data.orders:
815
1046
  # check if the date is for today
816
- closed_date = datetime.strptime(
817
- current_order.close_date,
818
- '%Y-%m-%dT%H:%M:%S.%f%z',
819
- ).astimezone(timezone).date()
1047
+ closed_date = (
1048
+ datetime.strptime(
1049
+ current_order.close_date,
1050
+ "%Y-%m-%dT%H:%M:%S.%f%z",
1051
+ )
1052
+ .astimezone(timezone)
1053
+ .date()
1054
+ )
820
1055
  if closed_date == today:
821
1056
  today_earnings += current_order.gross_earnings
822
1057
  found_any_for_today = True
823
-
1058
+
824
1059
  if not found_any_for_today:
825
1060
  return None
826
1061
 
827
1062
  return today_earnings
828
-
1063
+
829
1064
  def get_this_week_earnings(self, timezone: Any = pytz.UTC) -> Decimal:
830
1065
  """
831
1066
  Returns the total earnings for this week.
@@ -837,14 +1072,18 @@ class ContractOrdersHistoryResponse(BxApiResponse):
837
1072
  week_start = today - timedelta(days=today.weekday())
838
1073
  for current_order in self.data.orders:
839
1074
  # check if the date is for this week
840
- closed_date = datetime.strptime(
841
- current_order.close_date,
842
- '%Y-%m-%dT%H:%M:%S.%f%z',
843
- ).astimezone(timezone).date()
1075
+ closed_date = (
1076
+ datetime.strptime(
1077
+ current_order.close_date,
1078
+ "%Y-%m-%dT%H:%M:%S.%f%z",
1079
+ )
1080
+ .astimezone(timezone)
1081
+ .date()
1082
+ )
844
1083
  if closed_date >= week_start:
845
1084
  week_earnings += current_order.gross_earnings
846
1085
  found_any_for_week = True
847
-
1086
+
848
1087
  if not found_any_for_week:
849
1088
  return None
850
1089
 
@@ -861,14 +1100,18 @@ class ContractOrdersHistoryResponse(BxApiResponse):
861
1100
  month_start = today.replace(day=1)
862
1101
  for current_order in self.data.orders:
863
1102
  # check if the date is for this month
864
- closed_date = datetime.strptime(
865
- current_order.close_date,
866
- '%Y-%m-%dT%H:%M:%S.%f%z',
867
- ).astimezone(timezone).date()
1103
+ closed_date = (
1104
+ datetime.strptime(
1105
+ current_order.close_date,
1106
+ "%Y-%m-%dT%H:%M:%S.%f%z",
1107
+ )
1108
+ .astimezone(timezone)
1109
+ .date()
1110
+ )
868
1111
  if closed_date >= month_start:
869
1112
  month_earnings += current_order.gross_earnings
870
1113
  found_any_for_month = True
871
-
1114
+
872
1115
  if not found_any_for_month:
873
1116
  return None
874
1117
 
@@ -878,4 +1121,8 @@ class ContractOrdersHistoryResponse(BxApiResponse):
878
1121
  if not self.data or not self.data.orders:
879
1122
  return 0
880
1123
  return len(self.data.orders)
881
-
1124
+
1125
+
1126
+ # endregion
1127
+
1128
+ ###########################################################
@@ -17,6 +17,8 @@ from trd_utils.exchanges.bx_ultra.bx_types import (
17
17
  AssetsInfoResponse,
18
18
  ContractOrdersHistoryResponse,
19
19
  ContractsListResponse,
20
+ CopyTraderFuturesStatsResponse,
21
+ CopyTraderResumeResponse,
20
22
  CopyTraderTradePositionsResponse,
21
23
  HintListResponse,
22
24
  HomePageResponse,
@@ -26,6 +28,7 @@ from trd_utils.exchanges.bx_ultra.bx_types import (
26
28
  SearchCopyTradersResponse,
27
29
  UserFavoriteQuotationResponse,
28
30
  ZenDeskABStatusResponse,
31
+ ZenDeskAuthResponse,
29
32
  ZoneModuleListResponse,
30
33
  BxApiResponse,
31
34
  )
@@ -45,6 +48,8 @@ ANDROID_APP_VERSION = "4.28.3"
45
48
  WEB_APP_VERSION = "4.78.12"
46
49
  TG_APP_VERSION = "5.0.15"
47
50
 
51
+ ACCEPT_ENCODING_HEADER = "gzip, deflate, br, zstd"
52
+
48
53
  logger = logging.getLogger(__name__)
49
54
 
50
55
 
@@ -195,7 +200,13 @@ class BXUltraClient(ExchangeBase):
195
200
  headers=headers,
196
201
  model=ZenDeskABStatusResponse,
197
202
  )
198
-
203
+ async def do_zendesk_auth(self) -> ZenDeskAuthResponse:
204
+ headers = self.get_headers(needs_auth=True)
205
+ return await self.invoke_get(
206
+ f"{self.we_api_base_url}/customer/v1/zendesk/auth/jwt",
207
+ headers=headers,
208
+ model=ZenDeskAuthResponse,
209
+ )
199
210
  # endregion
200
211
  ###########################################################
201
212
  # region platform-tool
@@ -380,7 +391,7 @@ class BXUltraClient(ExchangeBase):
380
391
  # region copy-trade-facade
381
392
  async def get_copy_trade_trader_positions(
382
393
  self,
383
- uid: str,
394
+ uid: int | str,
384
395
  api_identity: str,
385
396
  page_size: int = 20,
386
397
  page_id: int = 0,
@@ -438,6 +449,38 @@ class BXUltraClient(ExchangeBase):
438
449
  model=SearchCopyTradersResponse,
439
450
  )
440
451
 
452
+ async def get_copy_trader_futures_stats(
453
+ self,
454
+ uid: int | str,
455
+ api_identity: str,
456
+ ) -> CopyTraderFuturesStatsResponse:
457
+ params = {
458
+ "uid": f"{uid}",
459
+ "apiIdentity": f"{api_identity}",
460
+ }
461
+ headers = self.get_headers(params)
462
+ return await self.invoke_get(
463
+ f"{self.we_api_base_url}/copy-trade-facade/v4/trader/account/futures/stat",
464
+ headers=headers,
465
+ params=params,
466
+ model=CopyTraderFuturesStatsResponse,
467
+ )
468
+
469
+ async def get_copy_trader_resume(
470
+ self,
471
+ uid: int | str,
472
+ ) -> CopyTraderResumeResponse:
473
+ params = {
474
+ "uid": f"{uid}",
475
+ }
476
+ headers = self.get_headers(params)
477
+ return await self.invoke_get(
478
+ f"{self.we_api_base_url}/copy-trade-facade/v1/trader/resume",
479
+ headers=headers,
480
+ params=params,
481
+ model=CopyTraderResumeResponse,
482
+ )
483
+
441
484
  # endregion
442
485
  ###########################################################
443
486
  # region welfare
@@ -484,7 +527,7 @@ class BXUltraClient(ExchangeBase):
484
527
  payload_data=payload,
485
528
  ),
486
529
  "Timestamp": f"{the_timestamp}",
487
- 'Accept-Encoding': 'gzip, deflate',
530
+ "Accept-Encoding": ACCEPT_ENCODING_HEADER,
488
531
  "User-Agent": self.user_agent,
489
532
  "Connection": "close",
490
533
  "appsiteid": "0",
@@ -1,3 +1,4 @@
1
+ import datetime
1
2
  from decimal import Decimal
2
3
  import json
3
4
  from typing import (
@@ -7,6 +8,8 @@ from typing import (
7
8
  get_args as get_type_args,
8
9
  )
9
10
 
11
+ import dateutil.parser
12
+
10
13
  from trd_utils.html_utils.html_formats import camel_to_snake
11
14
 
12
15
  # Whether to use ultra-list instead of normal python list or not.
@@ -61,7 +64,12 @@ def value_to_normal_obj(value, omit_none: bool = False):
61
64
  if isinstance(value, list):
62
65
  results = []
63
66
  for current in value:
64
- results.append(value_to_normal_obj(current))
67
+ results.append(
68
+ value_to_normal_obj(
69
+ value=current,
70
+ omit_none=omit_none,
71
+ )
72
+ )
65
73
  return results
66
74
 
67
75
  if isinstance(value, (int, str)) or value is None:
@@ -73,7 +81,10 @@ def value_to_normal_obj(value, omit_none: bool = False):
73
81
  if isinstance(value, dict):
74
82
  result = {}
75
83
  for inner_key, inner_value in value.items():
76
- normalized_value = value_to_normal_obj(inner_value)
84
+ normalized_value = value_to_normal_obj(
85
+ value=inner_value,
86
+ omit_none=omit_none,
87
+ )
77
88
  if normalized_value is None and omit_none:
78
89
  continue
79
90
 
@@ -233,6 +244,14 @@ class BaseModel:
233
244
 
234
245
  if ULTRA_LIST_ENABLED and isinstance(value, list):
235
246
  value = convert_to_ultra_list(value)
247
+ elif expected_type is datetime.datetime and isinstance(value, str):
248
+ try:
249
+ value = dateutil.parser.parse(value)
250
+ except Exception as ex:
251
+ raise ValueError(
252
+ f"Failed to parse the string as datetime: {value}"
253
+ f" Are you sure it's in correct format? inner error: {ex}"
254
+ )
236
255
 
237
256
  # Type checking
238
257
  elif not (is_any_type(expected_type) or isinstance(value, expected_type)):
@@ -263,7 +282,7 @@ class BaseModel:
263
282
 
264
283
  def serialize(
265
284
  self,
266
- separators = (",", ":"),
285
+ separators=(",", ":"),
267
286
  ensure_ascii: bool = True,
268
287
  sort_keys: bool = True,
269
288
  omit_none: bool = False,
@@ -1,3 +0,0 @@
1
-
2
- __version__ = "0.0.12"
3
-
File without changes
File without changes