trd-utils 0.0.3__py3-none-any.whl → 0.0.4__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.

@@ -1,6 +1,8 @@
1
1
  from typing import Any, Optional
2
2
  from ..types_helper import BaseModel
3
3
  from decimal import Decimal
4
+ from datetime import datetime, timedelta
5
+ import pytz
4
6
 
5
7
  from .common_utils import (
6
8
  dec_to_str,
@@ -690,6 +692,7 @@ class ContractOrderInfo(BaseModel):
690
692
  close_type: int = None
691
693
  status: ContractOrderStatus = None
692
694
  open_date: str = None
695
+ close_date: str = None
693
696
  fees: Decimal = None
694
697
  lever_fee: Decimal = None
695
698
  name: str = None
@@ -730,7 +733,8 @@ class ContractOrderInfo(BaseModel):
730
733
  return self.sys_force_price
731
734
 
732
735
  def get_profit_str(self) -> str:
733
- profit_or_loss = self.current_price - self.display_price
736
+ last_price = self.current_price or self.display_close_price
737
+ profit_or_loss = last_price - self.display_price
734
738
  profit_percentage = (profit_or_loss / self.display_price) * 100
735
739
  profit_percentage *= 1 if self.is_long() else -1
736
740
  return dec_to_str(profit_percentage * self.lever_times)
@@ -755,8 +759,10 @@ class ContractOrderInfo(BaseModel):
755
759
  if self.sys_force_price:
756
760
  result_str += f"liquidation: {dec_to_normalize(self.sys_force_price)}{separator}"
757
761
 
758
- result_str += f"current price: {dec_to_normalize(self.current_price)}{separator}"
759
-
762
+ if self.current_price:
763
+ result_str += f"current price: {dec_to_normalize(self.current_price)}{separator}"
764
+ elif self.display_close_price:
765
+ result_str += f"close price: {dec_to_normalize(self.display_close_price)}{separator}"
760
766
  profit_str = self.get_profit_str()
761
767
  result_str += f"profit: {profit_str}%"
762
768
 
@@ -768,6 +774,11 @@ class ContractOrderInfo(BaseModel):
768
774
  def __repr__(self):
769
775
  return self.to_str()
770
776
 
777
+ class ClosedContractOrderInfo(ContractOrderInfo):
778
+ close_type_name: str = None
779
+ gross_earnings: Decimal = None
780
+ position_order: int = None
781
+
771
782
  class MarginStatInfo(BaseModel):
772
783
  name: str = None
773
784
  margin_coin_name: str = None
@@ -783,3 +794,87 @@ class ContractsListResult(BaseModel):
783
794
 
784
795
  class ContractsListResponse(BxApiResponse):
785
796
  data: ContractsListResult = None
797
+
798
+ class ContractOrdersHistoryResult(BaseModel):
799
+ orders: list[ClosedContractOrderInfo] = None
800
+ page_id: int = None
801
+
802
+ class ContractOrdersHistoryResponse(BxApiResponse):
803
+ data: ContractOrdersHistoryResult = None
804
+
805
+ def get_today_earnings(self, timezone: Any = pytz.UTC) -> Decimal:
806
+ """
807
+ Returns the total earnings for today.
808
+ NOTE: This function will return None if there are no orders for today.
809
+ """
810
+ found_any_for_today: bool = False
811
+ today_earnings = Decimal("0.00")
812
+ today = datetime.now(timezone).date()
813
+ for current_order in self.data.orders:
814
+ # check if the date is for today
815
+ closed_date = datetime.strptime(
816
+ current_order.close_date,
817
+ '%Y-%m-%dT%H:%M:%S.%f%z',
818
+ ).astimezone(timezone).date()
819
+ if closed_date == today:
820
+ today_earnings += current_order.gross_earnings
821
+ found_any_for_today = True
822
+
823
+ if not found_any_for_today:
824
+ return None
825
+
826
+ return today_earnings
827
+
828
+ def get_this_week_earnings(self, timezone: Any = pytz.UTC) -> Decimal:
829
+ """
830
+ Returns the total earnings for this week.
831
+ NOTE: This function will return None if there are no orders for this week.
832
+ """
833
+ found_any_for_week: bool = False
834
+ week_earnings = Decimal("0.00")
835
+ today = datetime.now(timezone).date()
836
+ week_start = today - timedelta(days=today.weekday())
837
+ for current_order in self.data.orders:
838
+ # check if the date is for this week
839
+ closed_date = datetime.strptime(
840
+ current_order.close_date,
841
+ '%Y-%m-%dT%H:%M:%S.%f%z',
842
+ ).astimezone(timezone).date()
843
+ if closed_date >= week_start:
844
+ week_earnings += current_order.gross_earnings
845
+ found_any_for_week = True
846
+
847
+ if not found_any_for_week:
848
+ return None
849
+
850
+ return week_earnings
851
+
852
+ def get_this_month_earnings(self, timezone: Any = pytz.UTC) -> Decimal:
853
+ """
854
+ Returns the total earnings for this month.
855
+ NOTE: This function will return None if there are no orders for this month.
856
+ """
857
+ found_any_for_month: bool = False
858
+ month_earnings = Decimal("0.00")
859
+ today = datetime.now(timezone).date()
860
+ month_start = today.replace(day=1)
861
+ for current_order in self.data.orders:
862
+ # check if the date is for this month
863
+ closed_date = datetime.strptime(
864
+ current_order.close_date,
865
+ '%Y-%m-%dT%H:%M:%S.%f%z',
866
+ ).astimezone(timezone).date()
867
+ if closed_date >= month_start:
868
+ month_earnings += current_order.gross_earnings
869
+ found_any_for_month = True
870
+
871
+ if not found_any_for_month:
872
+ return None
873
+
874
+ return month_earnings
875
+
876
+ def get_orders_len(self) -> int:
877
+ if not self.data or not self.data.orders:
878
+ return 0
879
+ return len(self.data.orders)
880
+
@@ -1,5 +1,6 @@
1
1
  """BxUltra exchange subclass"""
2
2
 
3
+ import asyncio
3
4
  from decimal import Decimal
4
5
  import json
5
6
  import logging
@@ -13,6 +14,7 @@ from pathlib import Path
13
14
  from .common_utils import do_ultra_ss
14
15
  from .bx_types import (
15
16
  AssetsInfoResponse,
17
+ ContractOrdersHistoryResponse,
16
18
  ContractsListResponse,
17
19
  CopyTraderTradePositionsResponse,
18
20
  HintListResponse,
@@ -241,6 +243,136 @@ class BXUltraClient:
241
243
  )
242
244
  return ContractsListResponse.deserialize(response.json(parse_float=Decimal))
243
245
 
246
+ async def get_contract_order_history(
247
+ self,
248
+ fund_type: int = 1,
249
+ paging_size: int = 10,
250
+ page_id: int = 0,
251
+ from_order_no: int = 0,
252
+ margin_coin_name: str = "USDT",
253
+ margin_type: int = 0,
254
+ ) -> ContractOrdersHistoryResponse:
255
+ params = {
256
+ "fundType": f"{fund_type}",
257
+ "pagingSize": f"{paging_size}",
258
+ "pageId": f"{page_id}",
259
+ "marginCoinName": margin_coin_name,
260
+ "marginType": f"{margin_type}",
261
+ }
262
+ if from_order_no:
263
+ params["fromOrderNo"] = f"{from_order_no}"
264
+
265
+ headers = self.get_headers(params, needs_auth=True)
266
+ response = await self.httpx_client.get(
267
+ f"{self.we_api_base_url}/v2/contract/order/history",
268
+ headers=headers,
269
+ params=params,
270
+ )
271
+ return ContractOrdersHistoryResponse.deserialize(
272
+ response.json(parse_float=Decimal)
273
+ )
274
+
275
+ async def get_today_contract_earnings(
276
+ self,
277
+ margin_coin_name: str = "USDT",
278
+ page_size: int = 10,
279
+ max_total_size: int = 500,
280
+ delay_per_fetch: float = 1.0,
281
+ ) -> Decimal:
282
+ """
283
+ Fetches today's earnings from the contract orders.
284
+ NOTE: This method is a bit slow due to the API rate limiting.
285
+ NOTE: If the user has not opened ANY contract orders today,
286
+ this method will return None.
287
+ """
288
+ return await self._get_period_contract_earnings(
289
+ period="day",
290
+ margin_coin_name=margin_coin_name,
291
+ page_size=page_size,
292
+ max_total_size=max_total_size,
293
+ delay_per_fetch=delay_per_fetch,
294
+ )
295
+
296
+ async def get_this_week_contract_earnings(
297
+ self,
298
+ margin_coin_name: str = "USDT",
299
+ page_size: int = 10,
300
+ max_total_size: int = 500,
301
+ delay_per_fetch: float = 1.0,
302
+ ) -> Decimal:
303
+ """
304
+ Fetches this week's earnings from the contract orders.
305
+ NOTE: This method is a bit slow due to the API rate limiting.
306
+ NOTE: If the user has not opened ANY contract orders this week,
307
+ this method will return None.
308
+ """
309
+ return await self._get_period_contract_earnings(
310
+ period="week",
311
+ margin_coin_name=margin_coin_name,
312
+ page_size=page_size,
313
+ max_total_size=max_total_size,
314
+ delay_per_fetch=delay_per_fetch,
315
+ )
316
+
317
+ async def get_this_month_contract_earnings(
318
+ self,
319
+ margin_coin_name: str = "USDT",
320
+ page_size: int = 10,
321
+ max_total_size: int = 500,
322
+ delay_per_fetch: float = 1.0,
323
+ ) -> Decimal:
324
+ """
325
+ Fetches this month's earnings from the contract orders.
326
+ NOTE: This method is a bit slow due to the API rate limiting.
327
+ NOTE: If the user has not opened ANY contract orders this week,
328
+ this method will return None.
329
+ """
330
+ return await self._get_period_contract_earnings(
331
+ period="month",
332
+ margin_coin_name=margin_coin_name,
333
+ page_size=page_size,
334
+ max_total_size=max_total_size,
335
+ delay_per_fetch=delay_per_fetch,
336
+ )
337
+
338
+ async def _get_period_contract_earnings(
339
+ self,
340
+ period: str,
341
+ margin_coin_name: str = "USDT",
342
+ page_size: int = 10,
343
+ max_total_size: int = 500,
344
+ delay_per_fetch: float = 1.0,
345
+ ) -> Decimal:
346
+ total_fetched = 0
347
+ total_earnings = Decimal("0.00")
348
+ has_earned_any = False
349
+ while total_fetched < max_total_size:
350
+ current_page = total_fetched // page_size
351
+ result = await self.get_contract_order_history(
352
+ page_id=current_page,
353
+ paging_size=page_size,
354
+ margin_coin_name=margin_coin_name,
355
+ )
356
+ if period == "day":
357
+ temp_earnings = result.get_today_earnings()
358
+ elif period == "week":
359
+ temp_earnings = result.get_this_week_earnings()
360
+ elif period == "month":
361
+ temp_earnings = result.get_this_month_earnings()
362
+ if temp_earnings is None:
363
+ # all ended
364
+ break
365
+ total_earnings += temp_earnings
366
+ has_earned_any = True
367
+ total_fetched += page_size
368
+ if result.get_orders_len() < page_size:
369
+ break
370
+ await asyncio.sleep(delay_per_fetch)
371
+
372
+ if not has_earned_any:
373
+ return None
374
+ return total_earnings
375
+
244
376
  # endregion
245
377
  ###########################################################
246
378
  # region copy-trade-facade
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: trd_utils
3
- Version: 0.0.3
3
+ Version: 0.0.4
4
4
  Summary: Common Basic Utils for Python3. By ALiwoto.
5
5
  Home-page: https://github.com/ALiwoto/trd_utils
6
6
  Keywords: utils,trd_utils,basic-utils,common-utils
@@ -1,7 +1,7 @@
1
1
  trd_utils/__init__.py,sha256=ajz1GSNU9xYVrFEDSz6Xwg7amWQ_yvW75tQa1ZvRIWc,3
2
2
  trd_utils/bx_ultra/__init__.py,sha256=8Ssy-eOemQR32Nv1-FoPHm87nRqRO4Fm2PU5GHEFKfQ,80
3
- trd_utils/bx_ultra/bx_types.py,sha256=avgj7JFijJpK3_WBMrV5HaK4rssBnj0eG7kToEbdvAQ,21646
4
- trd_utils/bx_ultra/bx_ultra_client.py,sha256=w2ajrBLDrSeRAPXCPV4lkUkR-r1PqE1Kwu2bxbJUy7A,15876
3
+ trd_utils/bx_ultra/bx_types.py,sha256=eaNIjGiWh9T9j9jfCdcni_vVTHS5AW1FyA3aeyCssqU,25187
4
+ trd_utils/bx_ultra/bx_ultra_client.py,sha256=4fNtu0G04ZXqy2SHaYkwbwWDTb7M-InOggRN-a-bprs,20504
5
5
  trd_utils/bx_ultra/common_utils.py,sha256=p9u3D52jCa9DNJzo-oA1yK_lrdb7_ahkGHaTuiX5wGE,1656
6
6
  trd_utils/cipher/__init__.py,sha256=V05KNuzQwCic-ihMVHlC8sENaJGc3I8MCb4pg4849X8,1765
7
7
  trd_utils/html_utils/__init__.py,sha256=1WWs8C7JszRjTkmzIRLHpxWECHur_DrulTPGIeX88oM,426
@@ -11,7 +11,7 @@ trd_utils/tradingview/tradingview_client.py,sha256=iiNSLSKr5PnDcGiVFn515gsnGtqm5
11
11
  trd_utils/tradingview/tradingview_types.py,sha256=z21MXPVdWHAduEl3gSeMIRhxtBN9yK-jPYHfZSMIbSA,6144
12
12
  trd_utils/types_helper/__init__.py,sha256=SB9_5bQkuxV059AKC4cXYwsLPT3lM4LOu2m8LpMhZlQ,60
13
13
  trd_utils/types_helper/base_model.py,sha256=J3SdOB9UNgw0cI8gaG8zjnfqa1qiVmlpz4o3aBxHqas,5631
14
- trd_utils-0.0.3.dist-info/LICENSE,sha256=msfwzd8S06fL8ORRneycnonTKmyuXzfeBT0V2figir8,1062
15
- trd_utils-0.0.3.dist-info/METADATA,sha256=uhT5sTw4F0V-uIdk-i3iz3EKSfDHq8TU8FpELH0-K-o,975
16
- trd_utils-0.0.3.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
17
- trd_utils-0.0.3.dist-info/RECORD,,
14
+ trd_utils-0.0.4.dist-info/LICENSE,sha256=msfwzd8S06fL8ORRneycnonTKmyuXzfeBT0V2figir8,1062
15
+ trd_utils-0.0.4.dist-info/METADATA,sha256=4RQOqx9CFCx1HzBbzBJsYyyRCn-EWCiupNs37429BPA,975
16
+ trd_utils-0.0.4.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
17
+ trd_utils-0.0.4.dist-info/RECORD,,