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.
Files changed (44) hide show
  1. trd_utils/__init__.py +3 -0
  2. trd_utils/cipher/__init__.py +44 -0
  3. trd_utils/common_utils/float_utils.py +21 -0
  4. trd_utils/common_utils/wallet_utils.py +26 -0
  5. trd_utils/date_utils/__init__.py +8 -0
  6. trd_utils/date_utils/datetime_helpers.py +25 -0
  7. trd_utils/exchanges/README.md +203 -0
  8. trd_utils/exchanges/__init__.py +28 -0
  9. trd_utils/exchanges/base_types.py +229 -0
  10. trd_utils/exchanges/binance/__init__.py +13 -0
  11. trd_utils/exchanges/binance/binance_client.py +389 -0
  12. trd_utils/exchanges/binance/binance_types.py +116 -0
  13. trd_utils/exchanges/blofin/__init__.py +6 -0
  14. trd_utils/exchanges/blofin/blofin_client.py +375 -0
  15. trd_utils/exchanges/blofin/blofin_types.py +173 -0
  16. trd_utils/exchanges/bx_ultra/__init__.py +6 -0
  17. trd_utils/exchanges/bx_ultra/bx_types.py +1338 -0
  18. trd_utils/exchanges/bx_ultra/bx_ultra_client.py +1123 -0
  19. trd_utils/exchanges/bx_ultra/bx_utils.py +51 -0
  20. trd_utils/exchanges/errors.py +10 -0
  21. trd_utils/exchanges/exchange_base.py +301 -0
  22. trd_utils/exchanges/hyperliquid/README.md +3 -0
  23. trd_utils/exchanges/hyperliquid/__init__.py +7 -0
  24. trd_utils/exchanges/hyperliquid/hyperliquid_client.py +292 -0
  25. trd_utils/exchanges/hyperliquid/hyperliquid_types.py +183 -0
  26. trd_utils/exchanges/okx/__init__.py +6 -0
  27. trd_utils/exchanges/okx/okx_client.py +219 -0
  28. trd_utils/exchanges/okx/okx_types.py +197 -0
  29. trd_utils/exchanges/price_fetcher.py +48 -0
  30. trd_utils/html_utils/__init__.py +26 -0
  31. trd_utils/html_utils/html_formats.py +72 -0
  32. trd_utils/tradingview/__init__.py +8 -0
  33. trd_utils/tradingview/tradingview_client.py +128 -0
  34. trd_utils/tradingview/tradingview_types.py +185 -0
  35. trd_utils/types_helper/__init__.py +12 -0
  36. trd_utils/types_helper/base_model.py +350 -0
  37. trd_utils/types_helper/decorators.py +20 -0
  38. trd_utils/types_helper/model_config.py +6 -0
  39. trd_utils/types_helper/ultra_list.py +39 -0
  40. trd_utils/types_helper/utils.py +40 -0
  41. trd_utils-0.0.57.dist-info/METADATA +42 -0
  42. trd_utils-0.0.57.dist-info/RECORD +44 -0
  43. trd_utils-0.0.57.dist-info/WHEEL +4 -0
  44. 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,6 @@
1
+
2
+
3
+ class ModelConfig:
4
+ ignored_fields: list[str] = None
5
+
6
+
@@ -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,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: poetry-core 2.2.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -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.