trd-utils 0.0.1__py3-none-any.whl

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/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+
2
+
3
+
@@ -0,0 +1,6 @@
1
+
2
+ from .bx_ultra_client import BXUltraClient
3
+
4
+ __all__ = [
5
+ "BXUltraClient",
6
+ ]
@@ -0,0 +1,719 @@
1
+ from typing import Any, Optional
2
+ from ..types_helper import BaseModel
3
+ from decimal import Decimal
4
+
5
+ default_quantize = Decimal("1.00")
6
+
7
+
8
+ class BxApiResponse(BaseModel):
9
+ code: int = None
10
+ timestamp: int = None
11
+ msg: str = None
12
+
13
+ def __str__(self):
14
+ return f"code: {self.code}; timestamp: {self.timestamp}"
15
+
16
+ def __repr__(self):
17
+ return f"code: {self.code}; timestamp: {self.timestamp}"
18
+
19
+
20
+ class CoinQuotationInfo(BaseModel):
21
+ name: str = None
22
+ coin_type: int = None
23
+ valuation_coin_name: str = None
24
+ coin_name: str = None
25
+ icon_name: str = None
26
+ slug: str = None
27
+ quotation_id: int = None
28
+ open: Decimal = None
29
+ close: Decimal = None
30
+ weight: int = None
31
+ favorite_flag: Any = None # unknown
32
+ precision: int = None
33
+ coin_precision: int = None
34
+ valuation_precision: int = None
35
+ market_status: Any = None # unknown
36
+ trader_scale: Decimal = None
37
+ coin_id: int = None
38
+ valuation_coin_id: int = None
39
+ status: Any = None # unknown
40
+ open_time: Any = None # unknown
41
+ open_price: Any = None # unknown
42
+ high24: Optional[Decimal] = None
43
+ low24: Optional[Decimal] = None
44
+ volume24: Optional[Decimal] = None
45
+ amount24: Optional[Decimal] = None
46
+ market_val: Optional[Decimal] = None
47
+ full_name: str = None
48
+ biz_type: int = None
49
+
50
+ def __str__(self):
51
+ return f"{self.coin_name}/{self.valuation_coin_name}; price: {self.close}; vol: {self.market_val}"
52
+
53
+ def __repr__(self):
54
+ return f"{self.coin_name}/{self.valuation_coin_name}; price: {self.close}; vol: {self.market_val}"
55
+
56
+
57
+ ###########################################################
58
+
59
+
60
+ class ZoneModuleInfo(BaseModel):
61
+ id: int = None
62
+ name: str = None
63
+ quotation_list: list[CoinQuotationInfo] = None
64
+ zone_name: str = None
65
+ weight: int = None
66
+ biz_type: int = None
67
+
68
+ def __str__(self):
69
+ return f"{self.name} ({self.zone_name})"
70
+
71
+ def __repr__(self):
72
+ return f"{self.name} ({self.zone_name})"
73
+
74
+
75
+ class ZoneModuleListResult(BaseModel):
76
+ zone_module_list: list[ZoneModuleInfo] = None
77
+ biz_type: int = None
78
+ need_channel_type: list[int] = None
79
+ icon_url_prefix: str = None
80
+
81
+
82
+ class ZoneModuleListResponse(BxApiResponse):
83
+ data: ZoneModuleListResult = None
84
+
85
+
86
+ ###########################################################
87
+
88
+
89
+ class UserFavoriteQuotationResult(BaseModel):
90
+ usdt_margin_list: list[CoinQuotationInfo] = None
91
+ coin_margin_list: list[CoinQuotationInfo] = None
92
+ swap_list: list[CoinQuotationInfo] = None
93
+ biz_type: int = None
94
+ icon_url_prefix: str = None
95
+ recommend: bool = None
96
+
97
+
98
+ class UserFavoriteQuotationResponse(BxApiResponse):
99
+ data: UserFavoriteQuotationResult = None
100
+
101
+
102
+ ###########################################################
103
+
104
+
105
+ class QuotationRankBizItem(BaseModel):
106
+ quotation_list: list[CoinQuotationInfo] = None
107
+ biz_type: int = None
108
+ biz_name: str = None
109
+
110
+
111
+ class QuotationRankItem(BaseModel):
112
+ rank_type: int = None
113
+ rank_name: str = None
114
+ rank_biz_list: list[QuotationRankBizItem] = None
115
+
116
+
117
+ class QuotationRankResult(BaseModel):
118
+ rank_list: list[QuotationRankItem] = None
119
+ icon_prefix: str = None
120
+ icon_url_prefix: str = None
121
+ order_flag: int = None
122
+ show_favorite: bool = None
123
+
124
+
125
+ class QuotationRankResponse(BxApiResponse):
126
+ data: QuotationRankResult = None
127
+
128
+
129
+ ###########################################################
130
+
131
+ # region HotSearch types
132
+
133
+
134
+ class HotSearchItem(BaseModel):
135
+ symbol: str = None
136
+ coin_name: str = None
137
+ val_coin_name: str = None
138
+ weight: int = None
139
+
140
+ def __str__(self):
141
+ return f"{self.coin_name} ({self.symbol})"
142
+
143
+ def __repr__(self):
144
+ return f"{self.coin_name} ({self.symbol})"
145
+
146
+
147
+ class HotSearchResult(BaseModel):
148
+ result: list[HotSearchItem] = None
149
+ hint_ab_test: bool = None
150
+ page_id: int = None
151
+ total: int = None
152
+
153
+
154
+ class HotSearchResponse(BxApiResponse):
155
+ data: HotSearchResult = None
156
+
157
+ def __str__(self):
158
+ if not self.data:
159
+ return "HotSearchResponse: No data"
160
+
161
+ str_result = "HotSearchResponse: \n"
162
+ for current_item in self.data.result:
163
+ str_result += f" - {current_item}\n"
164
+
165
+ return str_result
166
+
167
+ def __repr__(self):
168
+ return self.__str__()
169
+
170
+
171
+ # endregion
172
+
173
+ ###########################################################
174
+
175
+ # region HomePage types
176
+
177
+
178
+ class CoinModuleInfoBase(BaseModel):
179
+ name: str = None
180
+ coin_name: str = None
181
+ icon_name: str = None
182
+ valuation_coin_name: str = None
183
+ open: Decimal = None
184
+ close: Decimal = None
185
+ precision: int = None
186
+ biz_type: int = None
187
+ market_val: Decimal = None
188
+ status: int = None
189
+ open_time: int = None
190
+ open_price: Decimal = None
191
+ full_name: str = None
192
+ amount24: Decimal = None
193
+ global_first_publish: bool = None
194
+ st_tag: int = None
195
+
196
+
197
+ class HomePageModuleIncreaseRankData(CoinModuleInfoBase):
198
+ pass
199
+
200
+
201
+ class HomePageModuleIncreaseRank(BaseModel):
202
+ item_name: str = None
203
+ sub_module_type: int = None
204
+ data: list[HomePageModuleIncreaseRankData] = None
205
+
206
+
207
+ class HomePageModuleHotZoneData(CoinModuleInfoBase):
208
+ zone_desc: str = None
209
+ zone_name: str = None
210
+ zone_id: int = None
211
+ zone_price_rate: Decimal = None
212
+
213
+
214
+ class HomePageModuleHotZone(BaseModel):
215
+ data: list[HomePageModuleHotZoneData] = None
216
+
217
+
218
+ class HomePageModuleRecentHotData(CoinModuleInfoBase):
219
+ pass
220
+
221
+
222
+ class HomePageModuleRecentHot(BaseModel):
223
+ data: list[HomePageModuleRecentHotData] = None
224
+
225
+
226
+ class HomePageModuleGlobalDebutData(CoinModuleInfoBase):
227
+ pass
228
+
229
+
230
+ class HomePageModuleGlobalDebut(BaseModel):
231
+ data: list[HomePageModuleGlobalDebutData] = None
232
+
233
+
234
+ class HomePageModuleMainMarketData(CoinModuleInfoBase):
235
+ pass
236
+
237
+
238
+ class HomePageModuleMainMarket(BaseModel):
239
+ data: list[HomePageModuleMainMarketData] = None
240
+
241
+
242
+ class HomePageModulePreOnlineData(CoinModuleInfoBase):
243
+ pass
244
+
245
+
246
+ class HomePageModulePreOnline(BaseModel):
247
+ data: list[HomePageModulePreOnlineData] = None
248
+
249
+
250
+ class HomePageModuleBannerData(BaseModel):
251
+ banner_title: str = None
252
+ banner_img: str = None
253
+ banner_jump_url: str = None
254
+
255
+
256
+ class HomePageModuleBanner(BaseModel):
257
+ data: list[HomePageModuleBannerData] = None
258
+
259
+
260
+ class HomePageModuleInfo(BaseModel):
261
+ module_type: int = None
262
+ module_name: str = None
263
+ module_desc: str = None
264
+ item_list: list = None
265
+
266
+ def _check_module_name(
267
+ self, j_data: dict, module_name: str, module_type: int
268
+ ) -> bool:
269
+ return (
270
+ self.module_name == module_name
271
+ or j_data.get("moduleName", None) == module_name
272
+ or self.module_type == module_type
273
+ or j_data.get("moduleType", None) == module_type
274
+ )
275
+
276
+ def _get_item_list_type(self, j_data: dict = None) -> type:
277
+ if not j_data:
278
+ # for more safety
279
+ j_data = {}
280
+
281
+ if self._check_module_name(j_data, "PRE_ONLINE", 1):
282
+ return list[HomePageModulePreOnline]
283
+
284
+ elif self._check_module_name(j_data, "MAIN_MARKET", 2):
285
+ return list[HomePageModuleMainMarket]
286
+
287
+ elif self._check_module_name(j_data, "RECENT_HOT", 3):
288
+ return list[HomePageModuleRecentHot]
289
+
290
+ elif self._check_module_name(j_data, "HOT_ZONE", 5):
291
+ return list[HomePageModuleHotZone]
292
+
293
+ elif self._check_module_name(j_data, "INCREASE_RANK", 6):
294
+ return list[HomePageModuleIncreaseRank]
295
+
296
+ elif self._check_module_name(j_data, "BANNER", 7):
297
+ return list[HomePageModuleBanner]
298
+
299
+ elif self._check_module_name(j_data, "GLOBAL_DEBUT", 8):
300
+ return list[HomePageModuleGlobalDebut]
301
+
302
+ return None
303
+
304
+ def __str__(self):
305
+ return (
306
+ f"{self.module_name} ({self.module_type}): {self.module_desc};"
307
+ + f" {len(self.item_list)} items"
308
+ )
309
+
310
+ def __repr__(self):
311
+ return (
312
+ f"{self.module_name} ({self.module_type}): {self.module_desc};"
313
+ + f" {len(self.item_list)} items"
314
+ )
315
+
316
+
317
+ class HomePageResult(BaseModel):
318
+ icon_prefix: str = None
319
+ green_amount_img_prefix: str = None
320
+ red_amount_img_prefix: str = None
321
+ module_list: list[HomePageModuleInfo] = None
322
+
323
+
324
+ class HomePageResponse(BxApiResponse):
325
+ data: HomePageResult = None
326
+
327
+
328
+ # endregion
329
+
330
+
331
+ ###########################################################
332
+
333
+
334
+ class ZenDeskABStatusResult(BaseModel):
335
+ ab_status: int = None
336
+
337
+
338
+ class ZenDeskABStatusResponse(BxApiResponse):
339
+ data: ZenDeskABStatusResult = None
340
+
341
+
342
+ ###########################################################
343
+
344
+
345
+ class HintListResult(BaseModel):
346
+ hints: list = None # unknown
347
+
348
+
349
+ class HintListResponse(BxApiResponse):
350
+ data: HintListResult = None
351
+
352
+
353
+ ###########################################################
354
+
355
+ #region CopyTrading types
356
+
357
+
358
+ class CopyTradingSymbolConfigInfo(BaseModel):
359
+ price_precision: int = None
360
+ quantity_precision: int = None
361
+
362
+
363
+ class CopyTraderPositionInfo(BaseModel):
364
+ avg_price: Decimal = None
365
+ coin_name: str = None
366
+ leverage: Decimal = None
367
+ liquidated_price: Decimal = None
368
+ margin: Decimal = None
369
+ mark_price: Decimal = None
370
+ position_earning_rate: Decimal = None
371
+ position_no: int = None
372
+ position_side: str = None
373
+ position_side_and_symbol: str = None
374
+ symbol: str = None
375
+ symbol_config: CopyTradingSymbolConfigInfo = None
376
+ unrealized_pnl: Decimal = None
377
+ valuation_coin_name: str = None
378
+ volume: Decimal = None
379
+ search_result: Optional[bool] = None
380
+ short_position_rate: Decimal = None
381
+ total: int = None
382
+
383
+ def __str__(self):
384
+ return (
385
+ f"{self.coin_name} / {self.valuation_coin_name} {self.position_side} "
386
+ + f"{self.leverage.quantize(default_quantize)}x "
387
+ + f"vol: {self.volume.quantize(default_quantize)}; "
388
+ + f"price: {self.avg_price.quantize(default_quantize)}; "
389
+ + f"margin: {self.margin.quantize(default_quantize)}; "
390
+ + f"pnl: {self.unrealized_pnl.quantize(default_quantize)}; "
391
+ + f"ROI: {(self.position_earning_rate * 100).quantize(default_quantize)}%"
392
+ )
393
+
394
+ def __repr__(self):
395
+ return self.__str__()
396
+
397
+
398
+ class CopyTraderTradePositionsResult(BaseModel):
399
+ hide: int = None
400
+ long_position_rate: Decimal = None
401
+ page_id: int = None
402
+ positions: list[CopyTraderPositionInfo] = None
403
+
404
+
405
+ class CopyTraderTradePositionsResponse(BxApiResponse):
406
+ data: CopyTraderTradePositionsResult = None
407
+
408
+
409
+ class SearchCopyTraderCondition(BaseModel):
410
+ key: str = "exchangeId"
411
+ selected: int = 2
412
+ type: str = "singleSelect"
413
+
414
+ def to_dict(self):
415
+ return {
416
+ "key": self.key,
417
+ "selected": f"{self.selected}",
418
+ "type": self.type,
419
+ }
420
+
421
+ class SearchedTraderChartItem(BaseModel):
422
+ cumulative_pnl_rate: Decimal = None
423
+
424
+ class SearchedTraderExchangeVoInfo(BaseModel):
425
+ account_enum: str = None
426
+ desc: str = None
427
+ exchange_id: int = None
428
+ exchange_name: str = None
429
+ icon: str = None
430
+
431
+ class SearchTraderInfoRankStat(BaseModel):
432
+ api_identity: int = None
433
+ avg_hold_time: str = None
434
+ avg_loss_amount: str = None
435
+ avg_loss_rate: str = None
436
+ avg_profit_amount: str = None
437
+ avg_profit_rate: str = None
438
+ being_invite: bool = None
439
+ chart: list[SearchedTraderChartItem] = None # unknown; list
440
+ copier_status: int = None
441
+ dis_play_name: str = None
442
+ equity: str = None
443
+ exchange_vo: Any = None # unknown
444
+ expand: int = None
445
+ follower_earning: str = None
446
+ follower_full: bool = None
447
+ icon: str = None
448
+ is_pro: bool = None
449
+ is_relation: bool = None
450
+ last_trade_time: str = None
451
+ latest30_days_median_lever_times: str = None
452
+ latest30_days_median_margin: str = None
453
+ loss_count: int = None
454
+ max_draw_down: str = None
455
+ pnl_rate: str = None
456
+ profit_count: int = None
457
+ recent7_day_follower_num_change: int = None
458
+ recent30_day_follower_num_change: int = None
459
+ recent90_day_follower_num_change: int = None
460
+ recent180_day_follower_num_change: int = None
461
+ risk_level7_days: str = None
462
+ risk_level30_days: str = None
463
+ risk_level90_days: str = None
464
+ risk_level180_days: str = None
465
+ risk_status: int = None
466
+ str_acc_follower_num: str = None
467
+ str_follower_num: str = None
468
+ str_recent7_days_rate: str = None
469
+ str_recent30_days_rate: str = None
470
+ str_recent90_days_rate: str = None
471
+ str_recent180_days_rate: str = None
472
+ str_recent180_days_rate: str = None
473
+ str_total_earnings_rate: str = None
474
+ total_earnings: str = None
475
+ total_transactions: int = None
476
+ trade_days: str = None
477
+ update_time: str = None
478
+ valid: int = None
479
+ vst_copier_status: int = None
480
+ weekly_trade_frequency: str = None
481
+ winRate: str = None
482
+
483
+ class SearchedTraderInfo(BaseModel):
484
+ avatar: str = None
485
+ be_trader: bool = None
486
+ channel: str = None
487
+ flag: str = None
488
+ ip_country: str = None
489
+ nation: str = None
490
+ nick_name: str = None
491
+ register_date: str = None
492
+ register_ip_country: str = None
493
+ short_uid: int = None
494
+ team_id: int = None
495
+ uid: int = None
496
+
497
+ class SearchedTraderAccountGradeVoInfo(BaseModel):
498
+ api_identity: int = None
499
+ label: int = None
500
+ trader_grade: int = None
501
+ uid: int = None
502
+
503
+ class SearchTraderInfoContainer(BaseModel):
504
+ content: str = None
505
+ has_new: bool = None
506
+ labels: list = None # unknown
507
+ rank_stat: SearchTraderInfoRankStat = None # unknown
508
+ trader: SearchedTraderInfo = None # unknown
509
+ trader_account_grade_vo: SearchedTraderAccountGradeVoInfo = None # unknown
510
+ trader_public_recommend_status: Any = None # unknown
511
+
512
+ def get_nick_name(self) -> str:
513
+ if self.trader:
514
+ return self.trader.nick_name
515
+
516
+ return
517
+
518
+ def get_uid(self) -> int:
519
+ if self.trader:
520
+ return self.trader.uid
521
+
522
+ return
523
+
524
+ def get_api_identity(self) -> int:
525
+ if self.trader_account_grade_vo:
526
+ return self.trader_account_grade_vo.api_identity
527
+
528
+ if self.rank_stat:
529
+ return self.rank_stat.api_identity
530
+
531
+ # TODO: later on add support for more cases
532
+ return None
533
+
534
+ def __str__(self):
535
+ if not self.trader:
536
+ return "No trader info"
537
+
538
+ return f"uid: {self.trader.uid}; name: {self.trader.nick_name}; country: {self.trader.nation}"
539
+
540
+ def __repr__(self):
541
+ return self.__str__()
542
+
543
+ class SearchCopyTradersResult(BaseModel):
544
+ expand_display: Any = None # unknown
545
+ fold_display: Any = None # unknown
546
+ page_id: int = None
547
+ rank_desc: str = None
548
+ rank_short_desc: str = None
549
+ rank_statistic_days: int = None
550
+ rank_tags: Any = None # unknown
551
+ rank_title: str = None
552
+ rank_type: str = None
553
+ result: list[SearchTraderInfoContainer] = None # unknown
554
+ search_result: bool = None
555
+ total: int = None
556
+
557
+ class SearchCopyTradersResponse(BxApiResponse):
558
+ data: SearchCopyTradersResult = None
559
+
560
+
561
+ #endregion
562
+
563
+ ###########################################################
564
+
565
+ # region Account Assets types
566
+
567
+ class TotalAssetsInfo(BaseModel):
568
+ amount: Any = None # unknown
569
+ currency_amount: Decimal = None
570
+ sign: str = None
571
+
572
+ def __str__(self):
573
+ return f"{self.currency_amount.quantize(default_quantize)} {self.sign}"
574
+
575
+ def __repr__(self):
576
+ return self.__str__()
577
+
578
+ class AccountOverviewItem(BaseModel):
579
+ account_name: str = None
580
+ account_type: int = None
581
+ total: TotalAssetsInfo = None # unknown
582
+ schema: str = None
583
+ order: int = None
584
+
585
+ def __str__(self):
586
+ return f"{self.account_name} ({self.account_type}): {self.total}"
587
+
588
+ class AssetsInfoResult(BaseModel):
589
+ total: TotalAssetsInfo = None
590
+ account_overviews: list[AccountOverviewItem] = None
591
+ recharge: int = None
592
+ withdraw: int = None
593
+ transfer: int = None
594
+ exchange: int = None
595
+ fault_flag: int = None
596
+ fault_accounts: Any = None # unknown
597
+
598
+ class AssetsInfoResponse(BxApiResponse):
599
+ data: AssetsInfoResult = None
600
+
601
+ # endregion
602
+
603
+ ###########################################################
604
+
605
+ # region Contracts types
606
+
607
+ class BasicCoinInfo(BaseModel):
608
+ name: str = None
609
+
610
+ def __str__(self):
611
+ return f"{self.name} CoinInfo"
612
+
613
+ def __repr__(self):
614
+ return self.__str__()
615
+
616
+ class QuotationCoinVOInfo(BaseModel):
617
+ id: int = None
618
+ coin: BasicCoinInfo = None
619
+ valuation_coin: BasicCoinInfo = None
620
+ precision: int = None
621
+ name: str = None
622
+ market_status: int = None
623
+
624
+ class OrderDebitInfo(BaseModel):
625
+ lend_coin: BasicCoinInfo = None
626
+ amount: Decimal = None
627
+
628
+ class OrderOpenTradeInfo(BaseModel):
629
+ traded_amount: Decimal = None
630
+ traded_cash_amount: Decimal = None
631
+
632
+ class ContractOrderStatus(BaseModel):
633
+ code: int = None
634
+ value: str = None
635
+
636
+
637
+ class ContractTakeProfitInfo(BaseModel):
638
+ id: int = None
639
+
640
+ # this is id of the contract that this take-profit info belongs to.
641
+ order_no: int = None
642
+ margin_coin_name: str = None
643
+ type: int = None
644
+ margin: Decimal = None
645
+ stop_rate: Decimal = None
646
+ stop_price: Decimal = None
647
+ close_style: int = None
648
+ all_close: bool = None
649
+
650
+ class ContractStopLossInfo(BaseModel):
651
+ pass
652
+
653
+ class ProfitLossInfoContainer(BaseModel):
654
+ loss_nums: int = None
655
+ profit_nums: int = None
656
+ profit_margin: Decimal = None
657
+ loss_margin: Decimal = None
658
+ profit_config: ContractTakeProfitInfo = None
659
+ loss_config: ContractStopLossInfo = None
660
+
661
+ class ContractOrderInfo(BaseModel):
662
+ order_no: int = None
663
+ quotation_coin_vo: QuotationCoinVOInfo = None
664
+ margin: Decimal = None
665
+ margin_coin_name: str = None
666
+ lever_times: Decimal = None
667
+ display_lever_times: Decimal = None
668
+ amount: Decimal = None # margin * lever_times
669
+ display_price: Decimal = None
670
+ display_close_price: Decimal = None
671
+ order_type: int = None
672
+ close_type: int = None
673
+ status: ContractOrderStatus = None
674
+ open_date: str = None
675
+ fees: Decimal = None
676
+ lever_fee: Decimal = None
677
+ name: str = None
678
+ order_create_type: int = None
679
+ hide_price: bool = None
680
+ fee_rate: Decimal = None
681
+ hide: int = None
682
+ liquidation_desc: str = None
683
+ contract_account_mode: int = None
684
+ current_price: Decimal = None
685
+ sys_force_price: Decimal = None
686
+ fund_type: int = None
687
+ interest: Decimal = None
688
+ order_open_trade: OrderOpenTradeInfo = None
689
+ order_debit: OrderDebitInfo = None
690
+ open_rate: Decimal = None
691
+ close_rate: Decimal = None
692
+ market_status: int = None
693
+ create_time: str = None
694
+ coupon_amount: Decimal = None
695
+ stop_profit_modify_time: str = None
696
+ stop_loss_modify_time: str = None
697
+ show_adjust_margin: int = None
698
+ trailing_stop: Decimal = None
699
+ trailing_close_price: Decimal = None
700
+ stop_rate: Decimal = None
701
+ profit_loss_info: ProfitLossInfoContainer = None
702
+ configs: Any = None # just dictionaries of take-profit and stop-loss configs.
703
+ stop_offset_rate: Decimal = None
704
+
705
+ class MarginStatInfo(BaseModel):
706
+ name: str = None
707
+ margin_coin_name: str = None
708
+ margin_type: int = None
709
+
710
+ # total count of open contract orders in this margin-type.
711
+ total: int = None
712
+
713
+ class ContractsListResult(BaseModel):
714
+ orders: list[ContractOrderInfo] = None
715
+ page_id: int = None
716
+ margin_stats: list[MarginStatInfo] = None
717
+
718
+ class ContractsListResponse(BxApiResponse):
719
+ data: ContractsListResult = None