trd-utils 0.0.34__tar.gz → 0.0.36__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.34 → trd_utils-0.0.36}/PKG-INFO +1 -1
- {trd_utils-0.0.34 → trd_utils-0.0.36}/pyproject.toml +1 -1
- trd_utils-0.0.36/trd_utils/__init__.py +3 -0
- {trd_utils-0.0.34 → trd_utils-0.0.36}/trd_utils/exchanges/README.md +4 -2
- {trd_utils-0.0.34 → trd_utils-0.0.36}/trd_utils/exchanges/base_types.py +5 -0
- {trd_utils-0.0.34 → trd_utils-0.0.36}/trd_utils/exchanges/blofin/blofin_client.py +7 -3
- {trd_utils-0.0.34 → trd_utils-0.0.36}/trd_utils/exchanges/blofin/blofin_types.py +7 -2
- {trd_utils-0.0.34 → trd_utils-0.0.36}/trd_utils/exchanges/bx_ultra/bx_ultra_client.py +10 -1
- {trd_utils-0.0.34 → trd_utils-0.0.36}/trd_utils/exchanges/exchange_base.py +3 -0
- {trd_utils-0.0.34 → trd_utils-0.0.36}/trd_utils/exchanges/hyperliquid/hyperliquid_client.py +6 -1
- {trd_utils-0.0.34 → trd_utils-0.0.36}/trd_utils/exchanges/okx/okx_client.py +2 -0
- {trd_utils-0.0.34 → trd_utils-0.0.36}/trd_utils/exchanges/price_fetcher.py +11 -1
- {trd_utils-0.0.34 → trd_utils-0.0.36}/trd_utils/types_helper/base_model.py +0 -1
- trd_utils-0.0.34/trd_utils/__init__.py +0 -3
- {trd_utils-0.0.34 → trd_utils-0.0.36}/LICENSE +0 -0
- {trd_utils-0.0.34 → trd_utils-0.0.36}/README.md +0 -0
- {trd_utils-0.0.34 → trd_utils-0.0.36}/trd_utils/cipher/__init__.py +0 -0
- {trd_utils-0.0.34 → trd_utils-0.0.36}/trd_utils/common_utils/float_utils.py +0 -0
- {trd_utils-0.0.34 → trd_utils-0.0.36}/trd_utils/common_utils/wallet_utils.py +0 -0
- {trd_utils-0.0.34 → trd_utils-0.0.36}/trd_utils/date_utils/__init__.py +0 -0
- {trd_utils-0.0.34 → trd_utils-0.0.36}/trd_utils/date_utils/datetime_helpers.py +0 -0
- {trd_utils-0.0.34 → trd_utils-0.0.36}/trd_utils/exchanges/__init__.py +0 -0
- {trd_utils-0.0.34 → trd_utils-0.0.36}/trd_utils/exchanges/blofin/__init__.py +0 -0
- {trd_utils-0.0.34 → trd_utils-0.0.36}/trd_utils/exchanges/bx_ultra/__init__.py +0 -0
- {trd_utils-0.0.34 → trd_utils-0.0.36}/trd_utils/exchanges/bx_ultra/bx_types.py +0 -0
- {trd_utils-0.0.34 → trd_utils-0.0.36}/trd_utils/exchanges/bx_ultra/bx_utils.py +0 -0
- {trd_utils-0.0.34 → trd_utils-0.0.36}/trd_utils/exchanges/errors.py +0 -0
- {trd_utils-0.0.34 → trd_utils-0.0.36}/trd_utils/exchanges/hyperliquid/README.md +0 -0
- {trd_utils-0.0.34 → trd_utils-0.0.36}/trd_utils/exchanges/hyperliquid/__init__.py +0 -0
- {trd_utils-0.0.34 → trd_utils-0.0.36}/trd_utils/exchanges/hyperliquid/hyperliquid_types.py +0 -0
- {trd_utils-0.0.34 → trd_utils-0.0.36}/trd_utils/exchanges/okx/__init__.py +0 -0
- {trd_utils-0.0.34 → trd_utils-0.0.36}/trd_utils/exchanges/okx/okx_types.py +0 -0
- {trd_utils-0.0.34 → trd_utils-0.0.36}/trd_utils/html_utils/__init__.py +0 -0
- {trd_utils-0.0.34 → trd_utils-0.0.36}/trd_utils/html_utils/html_formats.py +0 -0
- {trd_utils-0.0.34 → trd_utils-0.0.36}/trd_utils/tradingview/__init__.py +0 -0
- {trd_utils-0.0.34 → trd_utils-0.0.36}/trd_utils/tradingview/tradingview_client.py +0 -0
- {trd_utils-0.0.34 → trd_utils-0.0.36}/trd_utils/tradingview/tradingview_types.py +0 -0
- {trd_utils-0.0.34 → trd_utils-0.0.36}/trd_utils/types_helper/__init__.py +0 -0
|
@@ -48,11 +48,13 @@ class MyExchangeClient(ExchangeBase):
|
|
|
48
48
|
fav_letter: str = "^",
|
|
49
49
|
read_session_file: bool = True,
|
|
50
50
|
sessions_dir: str = "sessions",
|
|
51
|
+
use_http1: bool = False,
|
|
52
|
+
use_http2: bool = True,
|
|
51
53
|
):
|
|
52
54
|
self.httpx_client = httpx.AsyncClient(
|
|
53
55
|
verify=http_verify,
|
|
54
|
-
|
|
55
|
-
|
|
56
|
+
http1=use_http1,
|
|
57
|
+
http2=use_http2,
|
|
56
58
|
)
|
|
57
59
|
self.account_name = account_name
|
|
58
60
|
self._fav_letter = fav_letter
|
|
@@ -37,6 +37,11 @@ class UnifiedPositionInfo(BaseModel):
|
|
|
37
37
|
# The base unit that the open-price is based on (e.g. USD, USDT, USDC)
|
|
38
38
|
open_price_unit: str | None = None
|
|
39
39
|
|
|
40
|
+
# The initial amount of open_price_unit that the trader has put to open
|
|
41
|
+
# this position.
|
|
42
|
+
# Note that not all public APIs might provide this field.
|
|
43
|
+
initial_margin: Decimal | None = None
|
|
44
|
+
|
|
40
45
|
# The last price of this pair on the target exchange.
|
|
41
46
|
# not all exchanges support this yet, so use it with caution.
|
|
42
47
|
last_price: Decimal | None = None
|
|
@@ -51,16 +51,19 @@ class BlofinClient(ExchangeBase):
|
|
|
51
51
|
fav_letter: str = "^",
|
|
52
52
|
read_session_file: bool = True,
|
|
53
53
|
sessions_dir: str = "sessions",
|
|
54
|
+
use_http1: bool = False,
|
|
55
|
+
use_http2: bool = True,
|
|
54
56
|
):
|
|
55
57
|
self.httpx_client = httpx.AsyncClient(
|
|
56
58
|
verify=http_verify,
|
|
57
|
-
|
|
58
|
-
|
|
59
|
+
http1=use_http1,
|
|
60
|
+
http2=use_http2,
|
|
59
61
|
)
|
|
60
62
|
self.account_name = account_name
|
|
61
63
|
self._fav_letter = fav_letter
|
|
62
64
|
self.sessions_dir = sessions_dir
|
|
63
|
-
|
|
65
|
+
self.exchange_name = "blofin"
|
|
66
|
+
|
|
64
67
|
super().__init__()
|
|
65
68
|
|
|
66
69
|
if read_session_file:
|
|
@@ -339,6 +342,7 @@ class BlofinClient(ExchangeBase):
|
|
|
339
342
|
unified_pos.open_time = dt_from_ts(position.open_time)
|
|
340
343
|
unified_pos.open_price = position.avg_open_price
|
|
341
344
|
unified_pos.open_price_unit = position.symbol.split("-")[-1]
|
|
345
|
+
unified_pos.initial_margin = position.get_initial_margin()
|
|
342
346
|
unified_result.positions.append(unified_pos)
|
|
343
347
|
|
|
344
348
|
return unified_result
|
|
@@ -97,8 +97,8 @@ class CopyTraderSingleOrderInfo(BaseModel):
|
|
|
97
97
|
symbol: str = None
|
|
98
98
|
leverage: int = None
|
|
99
99
|
order_side: str = None
|
|
100
|
-
avg_open_price:
|
|
101
|
-
quantity:
|
|
100
|
+
avg_open_price: Decimal = None
|
|
101
|
+
quantity: Decimal = None
|
|
102
102
|
quantity_cont: Any = None
|
|
103
103
|
open_time: int = None
|
|
104
104
|
close_time: Any = None
|
|
@@ -146,6 +146,11 @@ class CopyTraderSingleOrderInfo(BaseModel):
|
|
|
146
146
|
position_change_history: Any = None
|
|
147
147
|
user_id: Any = None
|
|
148
148
|
|
|
149
|
+
def get_initial_margin(self) -> Decimal:
|
|
150
|
+
if not self.avg_open_price or not self.quantity or not self.leverage:
|
|
151
|
+
return None
|
|
152
|
+
return (self.avg_open_price * self.quantity) / self.leverage
|
|
153
|
+
|
|
149
154
|
|
|
150
155
|
class CopyTraderOrderListResponse(BlofinApiResponse):
|
|
151
156
|
data: list[CopyTraderSingleOrderInfo] = None
|
|
@@ -114,9 +114,13 @@ class BXUltraClient(ExchangeBase, IPriceFetcher):
|
|
|
114
114
|
http_verify: bool = True,
|
|
115
115
|
fav_letter: str = "^",
|
|
116
116
|
sessions_dir: str = "sessions",
|
|
117
|
+
use_http1: bool = False,
|
|
118
|
+
use_http2: bool = True,
|
|
117
119
|
):
|
|
118
120
|
self.httpx_client = httpx.AsyncClient(
|
|
119
|
-
verify=http_verify,
|
|
121
|
+
verify=http_verify,
|
|
122
|
+
http1=use_http1,
|
|
123
|
+
http2=use_http2,
|
|
120
124
|
)
|
|
121
125
|
self.account_name = account_name
|
|
122
126
|
self.platform_id = platform_id
|
|
@@ -124,6 +128,7 @@ class BXUltraClient(ExchangeBase, IPriceFetcher):
|
|
|
124
128
|
self.app_version = app_version
|
|
125
129
|
self._fav_letter = fav_letter
|
|
126
130
|
self.sessions_dir = sessions_dir
|
|
131
|
+
self.exchange_name = "\u0062ing\u0078"
|
|
127
132
|
|
|
128
133
|
super().__init__()
|
|
129
134
|
self.read_from_session_file(
|
|
@@ -301,6 +306,8 @@ class BXUltraClient(ExchangeBase, IPriceFetcher):
|
|
|
301
306
|
await self._do_price_ws(
|
|
302
307
|
url=url,
|
|
303
308
|
)
|
|
309
|
+
except asyncio.CancelledError:
|
|
310
|
+
return
|
|
304
311
|
except Exception as ex:
|
|
305
312
|
err_str = f"{ex}"
|
|
306
313
|
if err_str.find("Event loop is closed") != -1:
|
|
@@ -308,6 +315,7 @@ class BXUltraClient(ExchangeBase, IPriceFetcher):
|
|
|
308
315
|
return
|
|
309
316
|
|
|
310
317
|
logger.warning(f"error at _do_price_ws: {err_str}")
|
|
318
|
+
await asyncio.sleep(1)
|
|
311
319
|
|
|
312
320
|
async def _do_price_ws(self, url: str):
|
|
313
321
|
async with websockets.connect(url, ping_interval=None) as ws:
|
|
@@ -914,6 +922,7 @@ class BXUltraClient(ExchangeBase, IPriceFetcher):
|
|
|
914
922
|
unified_pos.position_pair = position.symbol.replace("-", "/")
|
|
915
923
|
unified_pos.open_time = None # TODO: do something for this?
|
|
916
924
|
unified_pos.open_price = position.avg_price
|
|
925
|
+
unified_pos.initial_margin = position.margin
|
|
917
926
|
unified_pos.open_price_unit = (
|
|
918
927
|
position.valuation_coin_name or position.symbol.split("-")[-1]
|
|
919
928
|
) # TODO
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
|
|
2
|
+
from datetime import datetime
|
|
2
3
|
from decimal import Decimal
|
|
3
4
|
import json
|
|
4
5
|
import logging
|
|
@@ -6,6 +7,8 @@ import httpx
|
|
|
6
7
|
|
|
7
8
|
from pathlib import Path
|
|
8
9
|
|
|
10
|
+
import pytz
|
|
11
|
+
|
|
9
12
|
from trd_utils.cipher import AESCipher
|
|
10
13
|
from trd_utils.common_utils.wallet_utils import shorten_wallet_address
|
|
11
14
|
from trd_utils.exchanges.base_types import UnifiedPositionInfo, UnifiedTraderInfo, UnifiedTraderPositions
|
|
@@ -46,6 +49,7 @@ class HyperLiquidClient(ExchangeBase):
|
|
|
46
49
|
self.account_name = account_name
|
|
47
50
|
self._fav_letter = fav_letter
|
|
48
51
|
self.sessions_dir = sessions_dir
|
|
52
|
+
self.exchange_name = "hyperliquid"
|
|
49
53
|
|
|
50
54
|
super().__init__()
|
|
51
55
|
|
|
@@ -162,9 +166,10 @@ class HyperLiquidClient(ExchangeBase):
|
|
|
162
166
|
unified_pos.margin_mode = position.leverage.type
|
|
163
167
|
unified_pos.position_leverage = Decimal(position.leverage.value)
|
|
164
168
|
unified_pos.position_pair = f"{position.coin}/USDT"
|
|
165
|
-
unified_pos.open_time =
|
|
169
|
+
unified_pos.open_time = datetime.now(pytz.UTC) # hyperliquid doesn't provide this...
|
|
166
170
|
unified_pos.open_price = position.entry_px
|
|
167
171
|
unified_pos.open_price_unit = "USDT"
|
|
172
|
+
unified_pos.initial_margin = position.margin_used
|
|
168
173
|
unified_result.positions.append(unified_pos)
|
|
169
174
|
|
|
170
175
|
return unified_result
|
|
@@ -54,6 +54,7 @@ class OkxClient(ExchangeBase):
|
|
|
54
54
|
self.account_name = account_name
|
|
55
55
|
self._fav_letter = fav_letter
|
|
56
56
|
self.sessions_dir = sessions_dir
|
|
57
|
+
self.exchange_name = "okx"
|
|
57
58
|
|
|
58
59
|
super().__init__()
|
|
59
60
|
|
|
@@ -185,6 +186,7 @@ class OkxClient(ExchangeBase):
|
|
|
185
186
|
unified_pos.open_time = position.c_time
|
|
186
187
|
unified_pos.open_price = position.avg_px
|
|
187
188
|
unified_pos.open_price_unit = position.quote_ccy
|
|
189
|
+
unified_pos.initial_margin = position.margin
|
|
188
190
|
unified_result.positions.append(unified_pos)
|
|
189
191
|
|
|
190
192
|
return unified_result
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
|
|
2
2
|
|
|
3
|
+
from datetime import datetime
|
|
3
4
|
from decimal import Decimal
|
|
5
|
+
|
|
6
|
+
import pytz
|
|
4
7
|
from trd_utils.types_helper import BaseModel
|
|
5
8
|
|
|
6
9
|
|
|
@@ -20,6 +23,13 @@ class MinimalCandleInfo(BaseModel):
|
|
|
20
23
|
# volume in the second part of the pair (e.g. USDT).
|
|
21
24
|
quote_volume: Decimal = None
|
|
22
25
|
|
|
26
|
+
# The time this candle info was retrieved.
|
|
27
|
+
fetched_at: datetime = None
|
|
28
|
+
|
|
29
|
+
def __init__(self, **kwargs):
|
|
30
|
+
super().__init__(**kwargs)
|
|
31
|
+
self.fetched_at = datetime.now(tz=pytz.UTC)
|
|
32
|
+
|
|
23
33
|
|
|
24
34
|
class IPriceFetcher:
|
|
25
35
|
"""
|
|
@@ -35,4 +45,4 @@ class IPriceFetcher:
|
|
|
35
45
|
pass
|
|
36
46
|
|
|
37
47
|
async def get_last_candle(self, pair: str) -> MinimalCandleInfo:
|
|
38
|
-
pass
|
|
48
|
+
pass
|
|
@@ -212,7 +212,6 @@ def convert_to_ultra_list(value: Any) -> UltraList:
|
|
|
212
212
|
class BaseModel:
|
|
213
213
|
def __init__(self, **kwargs):
|
|
214
214
|
annotations = get_my_field_types(self)
|
|
215
|
-
# annotations = self.__annotations__
|
|
216
215
|
for key, value in kwargs.items():
|
|
217
216
|
corrected_key = key
|
|
218
217
|
if key not in annotations:
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|