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.
- trd_utils/bx_ultra/bx_types.py +98 -3
- trd_utils/bx_ultra/bx_ultra_client.py +132 -0
- {trd_utils-0.0.3.dist-info → trd_utils-0.0.4.dist-info}/METADATA +1 -1
- {trd_utils-0.0.3.dist-info → trd_utils-0.0.4.dist-info}/RECORD +6 -6
- {trd_utils-0.0.3.dist-info → trd_utils-0.0.4.dist-info}/LICENSE +0 -0
- {trd_utils-0.0.3.dist-info → trd_utils-0.0.4.dist-info}/WHEEL +0 -0
trd_utils/bx_ultra/bx_types.py
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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,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=
|
|
4
|
-
trd_utils/bx_ultra/bx_ultra_client.py,sha256=
|
|
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.
|
|
15
|
-
trd_utils-0.0.
|
|
16
|
-
trd_utils-0.0.
|
|
17
|
-
trd_utils-0.0.
|
|
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,,
|
|
File without changes
|
|
File without changes
|