trd-utils 0.0.57__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.
- trd_utils/__init__.py +3 -0
- trd_utils/cipher/__init__.py +44 -0
- trd_utils/common_utils/float_utils.py +21 -0
- trd_utils/common_utils/wallet_utils.py +26 -0
- trd_utils/date_utils/__init__.py +8 -0
- trd_utils/date_utils/datetime_helpers.py +25 -0
- trd_utils/exchanges/README.md +203 -0
- trd_utils/exchanges/__init__.py +28 -0
- trd_utils/exchanges/base_types.py +229 -0
- trd_utils/exchanges/binance/__init__.py +13 -0
- trd_utils/exchanges/binance/binance_client.py +389 -0
- trd_utils/exchanges/binance/binance_types.py +116 -0
- trd_utils/exchanges/blofin/__init__.py +6 -0
- trd_utils/exchanges/blofin/blofin_client.py +375 -0
- trd_utils/exchanges/blofin/blofin_types.py +173 -0
- trd_utils/exchanges/bx_ultra/__init__.py +6 -0
- trd_utils/exchanges/bx_ultra/bx_types.py +1338 -0
- trd_utils/exchanges/bx_ultra/bx_ultra_client.py +1123 -0
- trd_utils/exchanges/bx_ultra/bx_utils.py +51 -0
- trd_utils/exchanges/errors.py +10 -0
- trd_utils/exchanges/exchange_base.py +301 -0
- trd_utils/exchanges/hyperliquid/README.md +3 -0
- trd_utils/exchanges/hyperliquid/__init__.py +7 -0
- trd_utils/exchanges/hyperliquid/hyperliquid_client.py +292 -0
- trd_utils/exchanges/hyperliquid/hyperliquid_types.py +183 -0
- trd_utils/exchanges/okx/__init__.py +6 -0
- trd_utils/exchanges/okx/okx_client.py +219 -0
- trd_utils/exchanges/okx/okx_types.py +197 -0
- trd_utils/exchanges/price_fetcher.py +48 -0
- trd_utils/html_utils/__init__.py +26 -0
- trd_utils/html_utils/html_formats.py +72 -0
- trd_utils/tradingview/__init__.py +8 -0
- trd_utils/tradingview/tradingview_client.py +128 -0
- trd_utils/tradingview/tradingview_types.py +185 -0
- trd_utils/types_helper/__init__.py +12 -0
- trd_utils/types_helper/base_model.py +350 -0
- trd_utils/types_helper/decorators.py +20 -0
- trd_utils/types_helper/model_config.py +6 -0
- trd_utils/types_helper/ultra_list.py +39 -0
- trd_utils/types_helper/utils.py +40 -0
- trd_utils-0.0.57.dist-info/METADATA +42 -0
- trd_utils-0.0.57.dist-info/RECORD +44 -0
- trd_utils-0.0.57.dist-info/WHEEL +4 -0
- trd_utils-0.0.57.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
from decimal import Decimal
|
|
3
|
+
import json
|
|
4
|
+
from typing import (
|
|
5
|
+
Union,
|
|
6
|
+
Any,
|
|
7
|
+
get_args as get_type_args,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
import dateutil.parser
|
|
11
|
+
|
|
12
|
+
from trd_utils.date_utils.datetime_helpers import dt_from_ts, dt_to_ts
|
|
13
|
+
from trd_utils.html_utils.html_formats import camel_to_snake
|
|
14
|
+
from trd_utils.types_helper.model_config import ModelConfig
|
|
15
|
+
from trd_utils.types_helper.ultra_list import convert_to_ultra_list, UltraList
|
|
16
|
+
from trd_utils.types_helper.utils import AbstractModel, get_my_field_types, is_type_optional
|
|
17
|
+
|
|
18
|
+
# Whether to use ultra-list instead of normal python list or not.
|
|
19
|
+
# This might be convenient in some cases, but it is not recommended
|
|
20
|
+
# to use it in production code because of the performance overhead.
|
|
21
|
+
ULTRA_LIST_ENABLED: bool = False
|
|
22
|
+
|
|
23
|
+
# Whether to also set the camelCase attribute names for the model.
|
|
24
|
+
# This is useful when the API returns camelCase attribute names
|
|
25
|
+
# and you want to use them as is in the model; by default, the
|
|
26
|
+
# attribute names are converted to snake_case.
|
|
27
|
+
SET_CAMEL_ATTR_NAMES = False
|
|
28
|
+
|
|
29
|
+
# The _model_config is a special field which cannot get serialized
|
|
30
|
+
# nor can it get deserialized.
|
|
31
|
+
SPECIAL_FIELDS = [
|
|
32
|
+
"_model_config",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
def new_list(original: Any = None) -> list:
|
|
36
|
+
if original is None:
|
|
37
|
+
original = []
|
|
38
|
+
elif not isinstance(original, list):
|
|
39
|
+
original = [original]
|
|
40
|
+
|
|
41
|
+
if ULTRA_LIST_ENABLED:
|
|
42
|
+
return UltraList(original)
|
|
43
|
+
|
|
44
|
+
return original
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def is_base_model_type(expected_type: type) -> bool:
|
|
48
|
+
return (
|
|
49
|
+
expected_type is not None
|
|
50
|
+
and expected_type != Any
|
|
51
|
+
and issubclass(expected_type, BaseModel)
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def is_any_type(target_type: type) -> bool:
|
|
56
|
+
return target_type == Any or target_type is type(None)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
# TODO: add support for max_depth for this...
|
|
60
|
+
def value_to_normal_obj(value, omit_none: bool = False):
|
|
61
|
+
"""
|
|
62
|
+
Converts a custom value, to a corresponding "normal object" which can be used
|
|
63
|
+
in dict.
|
|
64
|
+
"""
|
|
65
|
+
if isinstance(value, BaseModel):
|
|
66
|
+
return value.to_dict(
|
|
67
|
+
omit_none=omit_none,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
if isinstance(value, list):
|
|
71
|
+
results = new_list()
|
|
72
|
+
for current in value:
|
|
73
|
+
results.append(
|
|
74
|
+
value_to_normal_obj(
|
|
75
|
+
value=current,
|
|
76
|
+
omit_none=omit_none,
|
|
77
|
+
)
|
|
78
|
+
)
|
|
79
|
+
return results
|
|
80
|
+
|
|
81
|
+
if isinstance(value, (int, str)) or value is None:
|
|
82
|
+
return value
|
|
83
|
+
|
|
84
|
+
if isinstance(value, Decimal):
|
|
85
|
+
return str(value)
|
|
86
|
+
|
|
87
|
+
if isinstance(value, dict):
|
|
88
|
+
result = {}
|
|
89
|
+
for inner_key, inner_value in value.items():
|
|
90
|
+
normalized_value = value_to_normal_obj(
|
|
91
|
+
value=inner_value,
|
|
92
|
+
omit_none=omit_none,
|
|
93
|
+
)
|
|
94
|
+
if normalized_value is None and omit_none:
|
|
95
|
+
continue
|
|
96
|
+
|
|
97
|
+
result[inner_key] = normalized_value
|
|
98
|
+
|
|
99
|
+
return result
|
|
100
|
+
|
|
101
|
+
if isinstance(value, datetime.datetime):
|
|
102
|
+
return dt_to_ts(value)
|
|
103
|
+
|
|
104
|
+
raise TypeError(f"unsupported type provided: {type(value)}")
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def convert_to_expected_type(
|
|
108
|
+
expected_type: type,
|
|
109
|
+
value: Any,
|
|
110
|
+
default_value=None,
|
|
111
|
+
):
|
|
112
|
+
try:
|
|
113
|
+
return expected_type(value)
|
|
114
|
+
except Exception:
|
|
115
|
+
if value == "":
|
|
116
|
+
try:
|
|
117
|
+
return expected_type()
|
|
118
|
+
except Exception:
|
|
119
|
+
return default_value
|
|
120
|
+
return default_value
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def generic_obj_to_value(
|
|
124
|
+
expected_type: type,
|
|
125
|
+
expected_type_args: tuple[type],
|
|
126
|
+
value: Any,
|
|
127
|
+
):
|
|
128
|
+
"""
|
|
129
|
+
Converts a normal JSON-compatible "object" to a customized python value.
|
|
130
|
+
"""
|
|
131
|
+
if not expected_type_args:
|
|
132
|
+
expected_type_args = get_type_args(expected_type)
|
|
133
|
+
|
|
134
|
+
if isinstance(value, list):
|
|
135
|
+
result = new_list()
|
|
136
|
+
for current in value:
|
|
137
|
+
result.append(
|
|
138
|
+
generic_obj_to_value(
|
|
139
|
+
expected_type=expected_type_args[0],
|
|
140
|
+
expected_type_args=expected_type_args[1:],
|
|
141
|
+
value=current,
|
|
142
|
+
)
|
|
143
|
+
)
|
|
144
|
+
return result
|
|
145
|
+
|
|
146
|
+
expected_type_name = getattr(expected_type, "__name__", None)
|
|
147
|
+
if expected_type_name == "dict" and isinstance(value, dict):
|
|
148
|
+
result = {}
|
|
149
|
+
for inner_key, inner_value in value.items():
|
|
150
|
+
result[expected_type_args[0](inner_key)] = generic_obj_to_value(
|
|
151
|
+
expected_type=expected_type_args[1],
|
|
152
|
+
expected_type_args=expected_type_args[1:],
|
|
153
|
+
value=inner_value,
|
|
154
|
+
)
|
|
155
|
+
return result
|
|
156
|
+
|
|
157
|
+
if isinstance(value, dict) and is_base_model_type(expected_type=expected_type):
|
|
158
|
+
if len(expected_type_args) > 1:
|
|
159
|
+
raise ValueError(
|
|
160
|
+
"unsupported operation: at this time we cannot have"
|
|
161
|
+
" expected type args at all...",
|
|
162
|
+
)
|
|
163
|
+
return expected_type(**value)
|
|
164
|
+
|
|
165
|
+
if not expected_type_args:
|
|
166
|
+
if value is None or isinstance(value, expected_type):
|
|
167
|
+
return value
|
|
168
|
+
return convert_to_expected_type(
|
|
169
|
+
expected_type=expected_type,
|
|
170
|
+
value=value,
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
raise TypeError(f"unsupported type: {type(value)}")
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
class BaseModel(AbstractModel):
|
|
177
|
+
_model_config: ModelConfig = None
|
|
178
|
+
|
|
179
|
+
def __init__(self, **kwargs):
|
|
180
|
+
if not self._model_config:
|
|
181
|
+
self._model_config = ModelConfig()
|
|
182
|
+
|
|
183
|
+
annotations = get_my_field_types(self)
|
|
184
|
+
for key, value in kwargs.items():
|
|
185
|
+
corrected_key = key
|
|
186
|
+
if key not in annotations:
|
|
187
|
+
# key does not exist, try converting it to snake_case
|
|
188
|
+
corrected_key = camel_to_snake(key)
|
|
189
|
+
if corrected_key not in annotations:
|
|
190
|
+
# just ignore and continue
|
|
191
|
+
annotations[key] = Any
|
|
192
|
+
annotations[corrected_key] = Any
|
|
193
|
+
|
|
194
|
+
if corrected_key in SPECIAL_FIELDS or (
|
|
195
|
+
self._model_config.ignored_fields
|
|
196
|
+
and corrected_key in self._model_config.ignored_fields
|
|
197
|
+
):
|
|
198
|
+
continue
|
|
199
|
+
|
|
200
|
+
expected_type = annotations[corrected_key]
|
|
201
|
+
if hasattr(self, "_get_" + corrected_key + "_type"):
|
|
202
|
+
try:
|
|
203
|
+
overridden_type = getattr(self, "_get_" + corrected_key + "_type")(
|
|
204
|
+
kwargs
|
|
205
|
+
)
|
|
206
|
+
if overridden_type:
|
|
207
|
+
expected_type = overridden_type
|
|
208
|
+
except Exception:
|
|
209
|
+
pass
|
|
210
|
+
|
|
211
|
+
expected_type_args = get_type_args(expected_type)
|
|
212
|
+
expected_type_name = getattr(expected_type, "__name__", None)
|
|
213
|
+
is_optional_type = is_type_optional(expected_type)
|
|
214
|
+
is_dict_type = expected_type_name == "dict"
|
|
215
|
+
# maybe in the future we can have some other usages for is_optional_type
|
|
216
|
+
# variable or something like that.
|
|
217
|
+
if is_optional_type:
|
|
218
|
+
try:
|
|
219
|
+
expected_type = expected_type_args[0]
|
|
220
|
+
except Exception:
|
|
221
|
+
# something went wrong, just ignore and continue
|
|
222
|
+
expected_type = Any
|
|
223
|
+
|
|
224
|
+
if value is None:
|
|
225
|
+
# just skip...
|
|
226
|
+
pass
|
|
227
|
+
elif isinstance(value, dict) and is_dict_type:
|
|
228
|
+
value = generic_obj_to_value(
|
|
229
|
+
expected_type=expected_type,
|
|
230
|
+
expected_type_args=expected_type_args,
|
|
231
|
+
value=value,
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
# Handle nested models
|
|
235
|
+
elif isinstance(value, dict) and is_base_model_type(
|
|
236
|
+
expected_type=expected_type
|
|
237
|
+
):
|
|
238
|
+
value = expected_type(**value)
|
|
239
|
+
|
|
240
|
+
elif isinstance(value, list):
|
|
241
|
+
if not expected_type_args:
|
|
242
|
+
# if it's Any, it means we shouldn't really care about the type
|
|
243
|
+
if expected_type != Any:
|
|
244
|
+
value = expected_type(value)
|
|
245
|
+
else:
|
|
246
|
+
value = generic_obj_to_value(
|
|
247
|
+
expected_type=expected_type,
|
|
248
|
+
expected_type_args=expected_type_args,
|
|
249
|
+
value=value,
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
if ULTRA_LIST_ENABLED and isinstance(value, list):
|
|
253
|
+
value = convert_to_ultra_list(value)
|
|
254
|
+
elif expected_type is datetime.datetime:
|
|
255
|
+
try:
|
|
256
|
+
if isinstance(value, str):
|
|
257
|
+
if value.isdigit():
|
|
258
|
+
value = dt_from_ts(int(value))
|
|
259
|
+
else:
|
|
260
|
+
value = dateutil.parser.parse(value)
|
|
261
|
+
elif isinstance(value, int):
|
|
262
|
+
value = dt_from_ts(value)
|
|
263
|
+
except Exception as ex:
|
|
264
|
+
raise ValueError(
|
|
265
|
+
f"Failed to parse the string as datetime: {value}"
|
|
266
|
+
f" Are you sure it's in correct format? inner error: {ex}"
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
# Type checking
|
|
270
|
+
elif not (is_any_type(expected_type) or isinstance(value, expected_type)):
|
|
271
|
+
try:
|
|
272
|
+
value = convert_to_expected_type(
|
|
273
|
+
expected_type=expected_type,
|
|
274
|
+
value=value,
|
|
275
|
+
)
|
|
276
|
+
except Exception:
|
|
277
|
+
raise TypeError(
|
|
278
|
+
f"Field {corrected_key} must be of type {expected_type},"
|
|
279
|
+
+ f" but it's {type(value)}"
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
setattr(self, corrected_key, value)
|
|
283
|
+
if SET_CAMEL_ATTR_NAMES and key != corrected_key:
|
|
284
|
+
setattr(self, key, value)
|
|
285
|
+
|
|
286
|
+
# Check if all required fields are present
|
|
287
|
+
# for field in self.__annotations__:
|
|
288
|
+
# if not hasattr(self, field):
|
|
289
|
+
# raise ValueError(f"Missing required field: {field}")
|
|
290
|
+
|
|
291
|
+
@classmethod
|
|
292
|
+
def deserialize(
|
|
293
|
+
cls,
|
|
294
|
+
json_data: Union[str, dict],
|
|
295
|
+
parse_float=Decimal,
|
|
296
|
+
):
|
|
297
|
+
if isinstance(json_data, str):
|
|
298
|
+
data = json.loads(
|
|
299
|
+
json_data,
|
|
300
|
+
parse_float=parse_float,
|
|
301
|
+
)
|
|
302
|
+
else:
|
|
303
|
+
data = json_data
|
|
304
|
+
return cls(**data)
|
|
305
|
+
|
|
306
|
+
def serialize(
|
|
307
|
+
self,
|
|
308
|
+
separators=(",", ":"),
|
|
309
|
+
ensure_ascii: bool = True,
|
|
310
|
+
sort_keys: bool = True,
|
|
311
|
+
omit_none: bool = False,
|
|
312
|
+
) -> bytes:
|
|
313
|
+
return json.dumps(
|
|
314
|
+
obj=self.to_dict(
|
|
315
|
+
omit_none=omit_none,
|
|
316
|
+
),
|
|
317
|
+
ensure_ascii=ensure_ascii,
|
|
318
|
+
separators=separators,
|
|
319
|
+
sort_keys=sort_keys,
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
def to_dict(
|
|
323
|
+
self,
|
|
324
|
+
omit_none: bool = False,
|
|
325
|
+
) -> dict:
|
|
326
|
+
annotations = get_my_field_types(self)
|
|
327
|
+
result_dict = {}
|
|
328
|
+
for key, _ in annotations.items():
|
|
329
|
+
if not isinstance(key, str) or key in SPECIAL_FIELDS:
|
|
330
|
+
continue
|
|
331
|
+
|
|
332
|
+
if key.startswith("__") or key.startswith(f"_{self.__class__.__name__}__"):
|
|
333
|
+
# ignore private attributes
|
|
334
|
+
continue
|
|
335
|
+
|
|
336
|
+
if (
|
|
337
|
+
self._model_config.ignored_fields
|
|
338
|
+
and key in self._model_config.ignored_fields
|
|
339
|
+
):
|
|
340
|
+
continue
|
|
341
|
+
|
|
342
|
+
normalized_value = value_to_normal_obj(
|
|
343
|
+
value=getattr(self, key),
|
|
344
|
+
omit_none=omit_none,
|
|
345
|
+
)
|
|
346
|
+
if normalized_value is None and omit_none:
|
|
347
|
+
continue
|
|
348
|
+
|
|
349
|
+
result_dict[key] = normalized_value
|
|
350
|
+
return result_dict
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
|
|
2
|
+
from typing import Type, TypeVar
|
|
3
|
+
|
|
4
|
+
from trd_utils.types_helper.model_config import ModelConfig
|
|
5
|
+
from trd_utils.types_helper.utils import AbstractModel
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
T = TypeVar('T', bound=AbstractModel)
|
|
9
|
+
|
|
10
|
+
def ignore_json_fields(fields: list[str]):
|
|
11
|
+
def wrapper(cls: Type[T]) -> Type[T]:
|
|
12
|
+
config = getattr(cls, "_model_config", None)
|
|
13
|
+
if not config:
|
|
14
|
+
config = ModelConfig()
|
|
15
|
+
|
|
16
|
+
config.ignored_fields = fields.copy()
|
|
17
|
+
setattr(cls, "_model_config", config)
|
|
18
|
+
return cls
|
|
19
|
+
return wrapper
|
|
20
|
+
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
|
|
2
|
+
from typing import Any
|
|
3
|
+
from trd_utils.types_helper.utils import get_my_field_types, get_real_attr
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class UltraList(list):
|
|
7
|
+
def __getattr__(self, attr):
|
|
8
|
+
if len(self) == 0:
|
|
9
|
+
return None
|
|
10
|
+
return UltraList([get_real_attr(item, attr) for item in self])
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def convert_to_ultra_list(value: Any = None) -> UltraList:
|
|
14
|
+
if not value:
|
|
15
|
+
return UltraList()
|
|
16
|
+
|
|
17
|
+
# Go through all fields of the value and convert them to
|
|
18
|
+
# UltraList if they are lists
|
|
19
|
+
|
|
20
|
+
try:
|
|
21
|
+
if isinstance(value, list):
|
|
22
|
+
return UltraList([convert_to_ultra_list(item) for item in value])
|
|
23
|
+
elif isinstance(value, dict):
|
|
24
|
+
return {k: convert_to_ultra_list(v) for k, v in value.items()}
|
|
25
|
+
elif isinstance(value, tuple):
|
|
26
|
+
return tuple(convert_to_ultra_list(v) for v in value)
|
|
27
|
+
elif isinstance(value, set):
|
|
28
|
+
return {convert_to_ultra_list(v) for v in value}
|
|
29
|
+
|
|
30
|
+
for attr, attr_value in get_my_field_types(value).items():
|
|
31
|
+
if isinstance(attr_value, list):
|
|
32
|
+
setattr(value, attr, convert_to_ultra_list(getattr(value, attr)))
|
|
33
|
+
|
|
34
|
+
return value
|
|
35
|
+
except Exception:
|
|
36
|
+
return value
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from typing import (
|
|
2
|
+
get_type_hints,
|
|
3
|
+
)
|
|
4
|
+
|
|
5
|
+
class AbstractModel:
|
|
6
|
+
pass
|
|
7
|
+
|
|
8
|
+
def is_type_optional(target: type) -> bool:
|
|
9
|
+
if getattr(target, "__name__", None) == "Optional":
|
|
10
|
+
# e.g: my_field: Optional[str] = None
|
|
11
|
+
return True
|
|
12
|
+
|
|
13
|
+
target_args = getattr(target, "__args__", None)
|
|
14
|
+
if target_args and len(target_args) > 1 and target_args[1] is type(None):
|
|
15
|
+
# e.g: my_field: Decimal | None = None
|
|
16
|
+
return True
|
|
17
|
+
|
|
18
|
+
return False
|
|
19
|
+
|
|
20
|
+
def get_real_attr(cls, attr_name):
|
|
21
|
+
if cls is None:
|
|
22
|
+
return None
|
|
23
|
+
|
|
24
|
+
if isinstance(cls, dict):
|
|
25
|
+
return cls.get(attr_name, None)
|
|
26
|
+
|
|
27
|
+
if hasattr(cls, attr_name):
|
|
28
|
+
return getattr(cls, attr_name)
|
|
29
|
+
|
|
30
|
+
return None
|
|
31
|
+
|
|
32
|
+
def get_my_field_types(cls):
|
|
33
|
+
type_hints = {}
|
|
34
|
+
for current_cls in cls.__class__.__mro__:
|
|
35
|
+
if current_cls is object or current_cls is AbstractModel:
|
|
36
|
+
break
|
|
37
|
+
type_hints.update(get_type_hints(current_cls))
|
|
38
|
+
return type_hints
|
|
39
|
+
|
|
40
|
+
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: trd_utils
|
|
3
|
+
Version: 0.0.57
|
|
4
|
+
Summary: Common Basic Utils for Python3. By ALiwoto.
|
|
5
|
+
License-File: LICENSE
|
|
6
|
+
Keywords: utils,trd_utils,basic-utils,common-utils
|
|
7
|
+
Author: ALiwoto
|
|
8
|
+
Author-email: aminnimaj@gmail.com
|
|
9
|
+
Requires-Python: >=3.7
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Natural Language :: English
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
22
|
+
Requires-Dist: cryptography (>=41.0.7)
|
|
23
|
+
Requires-Dist: httpx (>=0.21.0)
|
|
24
|
+
Requires-Dist: python-dateutil (>=2.9.0.post0)
|
|
25
|
+
Requires-Dist: websockets (>=15.0.1)
|
|
26
|
+
Project-URL: Homepage, https://github.com/ALiwoto/trd_utils
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
# Trd Utils
|
|
30
|
+
|
|
31
|
+
Basic common utils for Python.
|
|
32
|
+
|
|
33
|
+
## How to run tests
|
|
34
|
+
|
|
35
|
+
Use this command first:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install -e .
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Then run the tests in vscode.
|
|
42
|
+
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
trd_utils/__init__.py,sha256=Xi63N56I_0mLMvCwn1ROb6iNd47L9-f1CDY-vGa9P9s,25
|
|
2
|
+
trd_utils/cipher/__init__.py,sha256=V05KNuzQwCic-ihMVHlC8sENaJGc3I8MCb4pg4849X8,1765
|
|
3
|
+
trd_utils/common_utils/float_utils.py,sha256=aYPwJ005LmrRhXAngojwvdDdtRgeb1FfR6hKeQ5ndMU,470
|
|
4
|
+
trd_utils/common_utils/wallet_utils.py,sha256=OX9q2fymP0VfIWTRIRBP8W33cfyjLXimxMgPOsZe-3g,727
|
|
5
|
+
trd_utils/date_utils/__init__.py,sha256=Erg_E1TfKWNpiuZFm_NXRjCwoRMfxpPS2-mJK6V4lFM,77
|
|
6
|
+
trd_utils/date_utils/datetime_helpers.py,sha256=euIJBr-6PfJzLScOC9xVXd8Re_Gw5CSBPwtHX9_Il4A,596
|
|
7
|
+
trd_utils/exchanges/README.md,sha256=8egE4IPUQ3_UtiGP6GaCg50xq_dp43aGY_X1lKcO6ok,6812
|
|
8
|
+
trd_utils/exchanges/__init__.py,sha256=IRAEr4DYE8pmnYUoilpUThEhKBQ2TCupDHrhRzvcVXg,674
|
|
9
|
+
trd_utils/exchanges/base_types.py,sha256=Yl9lMvK1f13ZuhWoZRqyjGxaDAjcAbeamK-BSVJB-lA,7498
|
|
10
|
+
trd_utils/exchanges/binance/__init__.py,sha256=Yy9z_cQeECjX05IwTJHpIRmId1aLoo0tQAR8rVTrUx0,223
|
|
11
|
+
trd_utils/exchanges/binance/binance_client.py,sha256=dNY3uki68CkbRgRTwXKu-nS8m0tQwp1ZVa-BsOGbUnQ,13524
|
|
12
|
+
trd_utils/exchanges/binance/binance_types.py,sha256=SyzFDQKynCrtbFNdOt_K_6N6mxcO2HIWK7UzTr3rrIM,3396
|
|
13
|
+
trd_utils/exchanges/blofin/__init__.py,sha256=X4r9o4Nyjla4UeOBG8lrgtnGYO2aErFMKaJ7yQrFasE,76
|
|
14
|
+
trd_utils/exchanges/blofin/blofin_client.py,sha256=QRiCyUewY9KBBaCrLTyg26RNedX58zhFY6NdpEcgO40,13343
|
|
15
|
+
trd_utils/exchanges/blofin/blofin_types.py,sha256=bQx0opCgHwcuC-5TxiVA4VQr17A1x7u7QIMdcIrROAg,4315
|
|
16
|
+
trd_utils/exchanges/bx_ultra/__init__.py,sha256=8Ssy-eOemQR32Nv1-FoPHm87nRqRO4Fm2PU5GHEFKfQ,80
|
|
17
|
+
trd_utils/exchanges/bx_ultra/bx_types.py,sha256=7Ga6IYHQNRDbhWXmS1J0NxpcR9HUJ8ZwQGh-1EvNRqM,36687
|
|
18
|
+
trd_utils/exchanges/bx_ultra/bx_ultra_client.py,sha256=W85CFxSFE5lFqUvNupQf42b5aU81i-SFF23BO22kx4U,40798
|
|
19
|
+
trd_utils/exchanges/bx_ultra/bx_utils.py,sha256=PwapomwDW33arVmKIDj6cL-aP0ptu4BYy_lOCqSAPOo,1392
|
|
20
|
+
trd_utils/exchanges/errors.py,sha256=P_NTuc389XL7rFegomP59BydWmHv8ckiGyNU-_l5qNQ,167
|
|
21
|
+
trd_utils/exchanges/exchange_base.py,sha256=oXiG4Qu4GKRzrMbjZWJkUla6uYV2UrWHtYKWsGo0gjc,9271
|
|
22
|
+
trd_utils/exchanges/hyperliquid/README.md,sha256=-qaxmDt_9NTus2xRuzyFGkKgYDWgWk7ufHVTSkyn3t4,105
|
|
23
|
+
trd_utils/exchanges/hyperliquid/__init__.py,sha256=QhwGRcneGFHREM-MMdYpbcx-aWdsWsu2WznHzx7LaUM,92
|
|
24
|
+
trd_utils/exchanges/hyperliquid/hyperliquid_client.py,sha256=Fcwb5JBgZ3Sw-N_wFEOj-LUwsb7MLcLkPQcrzhHx59Y,10402
|
|
25
|
+
trd_utils/exchanges/hyperliquid/hyperliquid_types.py,sha256=PqrA3ycKpOReGLoJLMc4ZgqbQrU8xxXbshBOIvq9-jo,5186
|
|
26
|
+
trd_utils/exchanges/okx/__init__.py,sha256=OjVpvcwB9mrCTofLt14JRHV2-fMAzGz9-YkJAMwl6dM,67
|
|
27
|
+
trd_utils/exchanges/okx/okx_client.py,sha256=tIYp3RehylP1Ag9IUnqlIdgpavynchzzPka-3W5osxg,7486
|
|
28
|
+
trd_utils/exchanges/okx/okx_types.py,sha256=IkFOfgivcvvIw950jyGHAVfFFGbGqfZcYGfZLWfNLvc,5013
|
|
29
|
+
trd_utils/exchanges/price_fetcher.py,sha256=YHLkM6sCvHGFS_4t188Sl3DczqrarK-1ivGwZEVsom4,1320
|
|
30
|
+
trd_utils/html_utils/__init__.py,sha256=1WWs8C7JszRjTkmzIRLHpxWECHur_DrulTPGIeX88oM,426
|
|
31
|
+
trd_utils/html_utils/html_formats.py,sha256=unKsvOiiDmYTTaM0DYZEUNLEUzWQKKrqASJXvY54kvU,2299
|
|
32
|
+
trd_utils/tradingview/__init__.py,sha256=H0QYb-O5qvy7qC3yswtlcSWLmeBnaS6oJ3JtjvmaV_Y,154
|
|
33
|
+
trd_utils/tradingview/tradingview_client.py,sha256=D6K0Hra6WNua0ATwmK9xNDEzPZTMd-ghtdk-D19A0Mg,3970
|
|
34
|
+
trd_utils/tradingview/tradingview_types.py,sha256=z21MXPVdWHAduEl3gSeMIRhxtBN9yK-jPYHfZSMIbSA,6144
|
|
35
|
+
trd_utils/types_helper/__init__.py,sha256=_yWDkeLdgVUG92CQFyO9uoGsSyMRUq0yYXK3XgUYmDI,214
|
|
36
|
+
trd_utils/types_helper/base_model.py,sha256=bkkKdYoAmBrn0dW55icVJV062DCU38fbxr4NTNw8FTM,11481
|
|
37
|
+
trd_utils/types_helper/decorators.py,sha256=ziQGDKV0RnhMG6gBPAz244Ug3j6ayr0iKXeucAdnXB8,527
|
|
38
|
+
trd_utils/types_helper/model_config.py,sha256=uvyhdGHQZ1A_I5RUbCgzlDk6MxWL6RLV8r0cdVi6nBk,60
|
|
39
|
+
trd_utils/types_helper/ultra_list.py,sha256=pt3GGZ1F0V4DqxVNbfeqQmVkPmkOphaBA6UtA5zOl7U,1184
|
|
40
|
+
trd_utils/types_helper/utils.py,sha256=hJzxI6D1ZSmv4y4wLrv-QW5AvrDU4BARXJmLpB-CTEI,960
|
|
41
|
+
trd_utils-0.0.57.dist-info/METADATA,sha256=OeCDvhODSgzlxtjXhq5v0c2oYLJzfT6U7672MrTRbYk,1252
|
|
42
|
+
trd_utils-0.0.57.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
43
|
+
trd_utils-0.0.57.dist-info/licenses/LICENSE,sha256=J1EP2xt87RjjmsTV1jTjHDQMLIM9FjdwEftTpw8hyv4,1067
|
|
44
|
+
trd_utils-0.0.57.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024-2025 ALi.w
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|