trd-utils 0.0.13__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.
- {trd_utils-0.0.13 → trd_utils-0.0.14}/PKG-INFO +1 -1
- {trd_utils-0.0.13 → trd_utils-0.0.14}/pyproject.toml +1 -1
- trd_utils-0.0.14/trd_utils/__init__.py +3 -0
- {trd_utils-0.0.13 → trd_utils-0.0.14}/trd_utils/exchanges/bx_ultra/bx_types.py +289 -42
- {trd_utils-0.0.13 → trd_utils-0.0.14}/trd_utils/exchanges/bx_ultra/bx_ultra_client.py +46 -3
- {trd_utils-0.0.13 → trd_utils-0.0.14}/trd_utils/types_helper/base_model.py +11 -0
- trd_utils-0.0.13/trd_utils/__init__.py +0 -3
- {trd_utils-0.0.13 → trd_utils-0.0.14}/LICENSE +0 -0
- {trd_utils-0.0.13 → trd_utils-0.0.14}/README.md +0 -0
- {trd_utils-0.0.13 → trd_utils-0.0.14}/trd_utils/cipher/__init__.py +0 -0
- {trd_utils-0.0.13 → trd_utils-0.0.14}/trd_utils/common_utils/float_utils.py +0 -0
- {trd_utils-0.0.13 → trd_utils-0.0.14}/trd_utils/exchanges/__init__.py +0 -0
- {trd_utils-0.0.13 → trd_utils-0.0.14}/trd_utils/exchanges/blofin/__init__.py +0 -0
- {trd_utils-0.0.13 → trd_utils-0.0.14}/trd_utils/exchanges/blofin/blofin_client.py +0 -0
- {trd_utils-0.0.13 → trd_utils-0.0.14}/trd_utils/exchanges/blofin/blofin_types.py +0 -0
- {trd_utils-0.0.13 → trd_utils-0.0.14}/trd_utils/exchanges/bx_ultra/__init__.py +0 -0
- {trd_utils-0.0.13 → trd_utils-0.0.14}/trd_utils/exchanges/bx_ultra/bx_utils.py +0 -0
- {trd_utils-0.0.13 → trd_utils-0.0.14}/trd_utils/exchanges/exchange_base.py +0 -0
- {trd_utils-0.0.13 → trd_utils-0.0.14}/trd_utils/html_utils/__init__.py +0 -0
- {trd_utils-0.0.13 → trd_utils-0.0.14}/trd_utils/html_utils/html_formats.py +0 -0
- {trd_utils-0.0.13 → trd_utils-0.0.14}/trd_utils/tradingview/__init__.py +0 -0
- {trd_utils-0.0.13 → trd_utils-0.0.14}/trd_utils/tradingview/tradingview_client.py +0 -0
- {trd_utils-0.0.13 → trd_utils-0.0.14}/trd_utils/tradingview/tradingview_types.py +0 -0
- {trd_utils-0.0.13 → trd_utils-0.0.14}/trd_utils/types_helper/__init__.py +0 -0
|
@@ -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:
|
|
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
|
|
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 +=
|
|
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 +=
|
|
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 +=
|
|
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 =
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
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 =
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
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 =
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
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
|
-
|
|
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.
|
|
@@ -241,6 +244,14 @@ class BaseModel:
|
|
|
241
244
|
|
|
242
245
|
if ULTRA_LIST_ENABLED and isinstance(value, list):
|
|
243
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
|
+
)
|
|
244
255
|
|
|
245
256
|
# Type checking
|
|
246
257
|
elif not (is_any_type(expected_type) or isinstance(value, expected_type)):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|