trd-utils 0.0.1__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/__init__.py +3 -0
- trd_utils/bx_ultra/__init__.py +6 -0
- trd_utils/bx_ultra/bx_types.py +719 -0
- trd_utils/bx_ultra/bx_ultra_client.py +436 -0
- trd_utils/bx_ultra/common_utils.py +51 -0
- trd_utils/cipher/__init__.py +44 -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 +130 -0
- trd_utils/tradingview/tradingview_types.py +185 -0
- trd_utils/types_helper/__init__.py +4 -0
- trd_utils/types_helper/base_model.py +150 -0
- trd_utils-0.0.1.dist-info/LICENSE +21 -0
- trd_utils-0.0.1.dist-info/METADATA +28 -0
- trd_utils-0.0.1.dist-info/RECORD +17 -0
- trd_utils-0.0.1.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
import json
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
from .tradingview_types import CoinScanInfo
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class TradingViewClient:
|
|
11
|
+
"""TradingViewClient class to interact with TradingView API."""
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def __init__(self) -> None:
|
|
15
|
+
pass
|
|
16
|
+
|
|
17
|
+
async def get_coin_scan(self,
|
|
18
|
+
coin_filter: Optional[str] = None,
|
|
19
|
+
limit: int = 200,
|
|
20
|
+
offset: int = 0) -> list['CoinScanInfo']:
|
|
21
|
+
import httpx
|
|
22
|
+
cookies = {
|
|
23
|
+
'cookiesSettings': '{"analytics":true,"advertising":true}',
|
|
24
|
+
'cookiePrivacyPreferenceBannerProduction': 'accepted',
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
headers = {
|
|
28
|
+
'accept': 'application/json',
|
|
29
|
+
'accept-language': 'en-US,en;q=0.9',
|
|
30
|
+
'cache-control': 'no-cache',
|
|
31
|
+
'content-type': 'text/plain;charset=UTF-8',
|
|
32
|
+
'origin': 'https://www.tradingview.com',
|
|
33
|
+
'pragma': 'no-cache',
|
|
34
|
+
'priority': 'u=1, i',
|
|
35
|
+
'referer': 'https://www.tradingview.com/',
|
|
36
|
+
'sec-ch-ua': '"Not)A;Brand";v="99", "Google Chrome";v="127", "Chromium";v="127"',
|
|
37
|
+
'sec-ch-ua-mobile': '?0',
|
|
38
|
+
'sec-ch-ua-platform': '"Windows"',
|
|
39
|
+
'sec-fetch-dest': 'empty',
|
|
40
|
+
'sec-fetch-mode': 'cors',
|
|
41
|
+
'sec-fetch-site': 'same-site',
|
|
42
|
+
'user-agent': ('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ' +
|
|
43
|
+
'(KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36'),
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
params = {
|
|
47
|
+
'label-product': 'screener-coin',
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
data = {
|
|
51
|
+
"columns": [
|
|
52
|
+
"base_currency",
|
|
53
|
+
"base_currency_desc",
|
|
54
|
+
"base_currency_logoid",
|
|
55
|
+
"update_mode",
|
|
56
|
+
"type",
|
|
57
|
+
"typespecs",
|
|
58
|
+
"exchange",
|
|
59
|
+
"crypto_total_rank",
|
|
60
|
+
"Recommend.All",
|
|
61
|
+
"Recommend.MA",
|
|
62
|
+
"Recommend.Other",
|
|
63
|
+
"RSI",
|
|
64
|
+
"Mom",
|
|
65
|
+
"pricescale",
|
|
66
|
+
"minmov",
|
|
67
|
+
"fractional",
|
|
68
|
+
"minmove2",
|
|
69
|
+
"AO",
|
|
70
|
+
"CCI20",
|
|
71
|
+
"Stoch.K",
|
|
72
|
+
"Stoch.D",
|
|
73
|
+
"profit_addresses_percentage",
|
|
74
|
+
"sentiment",
|
|
75
|
+
"socialdominance",
|
|
76
|
+
"galaxyscore",
|
|
77
|
+
"social_volume_24h",
|
|
78
|
+
"altrank",
|
|
79
|
+
"large_tx_count",
|
|
80
|
+
"close",
|
|
81
|
+
"currency",
|
|
82
|
+
"change_abs",
|
|
83
|
+
"Volatility.D",
|
|
84
|
+
],
|
|
85
|
+
"ignore_unknown_fields": False,
|
|
86
|
+
"options": {
|
|
87
|
+
"lang": "en"
|
|
88
|
+
},
|
|
89
|
+
"range":[
|
|
90
|
+
offset,
|
|
91
|
+
offset + limit,
|
|
92
|
+
],
|
|
93
|
+
"sort": {
|
|
94
|
+
"sortBy": "crypto_total_rank",
|
|
95
|
+
"sortOrder": "asc"
|
|
96
|
+
},
|
|
97
|
+
"symbols": {},
|
|
98
|
+
"markets": ["coin"]
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if coin_filter:
|
|
102
|
+
data["filter"] = [
|
|
103
|
+
{
|
|
104
|
+
"left": "base_currency,base_currency_desc",
|
|
105
|
+
"operation": "match",
|
|
106
|
+
"right": f"{coin_filter}"
|
|
107
|
+
}
|
|
108
|
+
]
|
|
109
|
+
data = json.dumps(data)
|
|
110
|
+
async with httpx.AsyncClient() as client:
|
|
111
|
+
response = await client.post(
|
|
112
|
+
'https://scanner.tradingview.com/coin/scan',
|
|
113
|
+
params=params,
|
|
114
|
+
cookies=cookies,
|
|
115
|
+
headers=headers,
|
|
116
|
+
data=data,
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
j_result = response.json()
|
|
120
|
+
if not isinstance(j_result, dict) or not j_result.get("data", None):
|
|
121
|
+
raise Exception("No data found")
|
|
122
|
+
|
|
123
|
+
j_data = j_result["data"]
|
|
124
|
+
all_infos = []
|
|
125
|
+
for current_data in j_data:
|
|
126
|
+
if not isinstance(current_data, dict):
|
|
127
|
+
continue
|
|
128
|
+
all_infos.append(CoinScanInfo._parse(current_data.get("d", [])))
|
|
129
|
+
|
|
130
|
+
return all_infos
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class CoinScanInfo:
|
|
6
|
+
base_currency: str = ""
|
|
7
|
+
base_currency_desc: str = ""
|
|
8
|
+
base_currency_logoid: str = ""
|
|
9
|
+
update_mode: str = ""
|
|
10
|
+
type: str = ""
|
|
11
|
+
typespecs: str = ""
|
|
12
|
+
exchange: str = ""
|
|
13
|
+
crypto_total_rank: Optional[int] = None
|
|
14
|
+
recommend_all: Optional[float] = None
|
|
15
|
+
recommend_ma: Optional[float] = None
|
|
16
|
+
recommend_other: Optional[float] = None
|
|
17
|
+
RSI: Optional[float] = None
|
|
18
|
+
Mom: Optional[float] = None
|
|
19
|
+
pricescale: Optional[int] = None
|
|
20
|
+
minmov: Optional[int] = None
|
|
21
|
+
fractional: Optional[bool] = None
|
|
22
|
+
minmove2: Optional[int] = None
|
|
23
|
+
AO: Optional[float] = None
|
|
24
|
+
CCI20: Optional[float] = None
|
|
25
|
+
stoch_k: Optional[float] = None
|
|
26
|
+
stoch_d: Optional[float] = None
|
|
27
|
+
profit_addresses_percentage: Optional[float] = None
|
|
28
|
+
sentiment: Optional[float] = None
|
|
29
|
+
socialdominance: Optional[float] = None
|
|
30
|
+
galaxyscore: Optional[int] = None
|
|
31
|
+
social_volume_24h: Optional[int] = None
|
|
32
|
+
altrank: Optional[int] = None
|
|
33
|
+
large_tx_count: Optional[int] = None
|
|
34
|
+
close_price: Optional[float] = None
|
|
35
|
+
currency: Optional[str] = None
|
|
36
|
+
change_abs: Optional[float] = None
|
|
37
|
+
volatility_d: Optional[float] = None
|
|
38
|
+
|
|
39
|
+
def get_tech_rating_str(self) -> str:
|
|
40
|
+
if self.recommend_all is None:
|
|
41
|
+
return "N/A"
|
|
42
|
+
|
|
43
|
+
is_negative = False
|
|
44
|
+
emoji = "🟢"
|
|
45
|
+
if self.recommend_all < 0:
|
|
46
|
+
is_negative = True
|
|
47
|
+
self.recommend_all = abs(self.recommend_all)
|
|
48
|
+
emoji = "🔴"
|
|
49
|
+
|
|
50
|
+
if self.recommend_all > 0.5:
|
|
51
|
+
return f"{emoji} Strong {'Sell' if is_negative else 'Buy'}"
|
|
52
|
+
|
|
53
|
+
if self.recommend_all > 0.1:
|
|
54
|
+
return f"{emoji} {'Sell' if is_negative else 'Buy'}"
|
|
55
|
+
|
|
56
|
+
return "Neutral"
|
|
57
|
+
|
|
58
|
+
def get_ma_rating_str(self) -> str:
|
|
59
|
+
if self.recommend_ma is None:
|
|
60
|
+
return "N/A"
|
|
61
|
+
|
|
62
|
+
is_negative = False
|
|
63
|
+
emoji = "🟢"
|
|
64
|
+
if self.recommend_ma < 0:
|
|
65
|
+
is_negative = True
|
|
66
|
+
self.recommend_ma = abs(self.recommend_ma)
|
|
67
|
+
emoji = "🔴"
|
|
68
|
+
|
|
69
|
+
if self.recommend_ma > 0.5:
|
|
70
|
+
return f"{emoji} Strong {'Sell' if is_negative else 'Buy'}"
|
|
71
|
+
|
|
72
|
+
if self.recommend_ma > 0.1:
|
|
73
|
+
return f"{emoji} {'Sell' if is_negative else 'Buy'}"
|
|
74
|
+
|
|
75
|
+
return "Neutral"
|
|
76
|
+
|
|
77
|
+
def get_os_rating_str(self) -> str:
|
|
78
|
+
if self.recommend_other is None:
|
|
79
|
+
return "N/A"
|
|
80
|
+
|
|
81
|
+
is_negative = False
|
|
82
|
+
emoji = "🟢"
|
|
83
|
+
if self.recommend_other < 0:
|
|
84
|
+
is_negative = True
|
|
85
|
+
self.recommend_other = abs(self.recommend_other)
|
|
86
|
+
emoji = "🔴"
|
|
87
|
+
|
|
88
|
+
if self.recommend_other > 0.5:
|
|
89
|
+
return f"{emoji} Strong {'Sell' if is_negative else 'Buy'}"
|
|
90
|
+
|
|
91
|
+
if self.recommend_other > 0.1:
|
|
92
|
+
return f"{emoji} {'Sell' if is_negative else 'Buy'}"
|
|
93
|
+
|
|
94
|
+
return "Neutral"
|
|
95
|
+
|
|
96
|
+
def get_addresses_in_profit_str(self) -> str:
|
|
97
|
+
if self.profit_addresses_percentage is None:
|
|
98
|
+
return "N/A"
|
|
99
|
+
return f"{self.profit_addresses_percentage:.2f}%"
|
|
100
|
+
|
|
101
|
+
def get_sentiment_str(self) -> str:
|
|
102
|
+
if self.sentiment is None:
|
|
103
|
+
return "N/A"
|
|
104
|
+
return f"{self.sentiment:.2f}%"
|
|
105
|
+
|
|
106
|
+
def get_socialdominance_str(self) -> str:
|
|
107
|
+
if self.socialdominance is None:
|
|
108
|
+
return "N/A"
|
|
109
|
+
return f"{self.socialdominance:.2f}%"
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def parse_as_markdown(self) -> str:
|
|
113
|
+
text = f"*{self.crypto_total_rank}. {self.base_currency_desc} ({self.base_currency})*:\n"
|
|
114
|
+
text += f" *• Tech Rating:* {self.get_tech_rating_str()}\n"
|
|
115
|
+
text += f" *• MA Rating:* {self.get_ma_rating_str()}\n"
|
|
116
|
+
text += f" *• Os Rating:* {self.get_os_rating_str()}\n"
|
|
117
|
+
|
|
118
|
+
if self.profit_addresses_percentage:
|
|
119
|
+
text += f" *• In Profit:* {self.get_addresses_in_profit_str()}\n"
|
|
120
|
+
|
|
121
|
+
if self.sentiment:
|
|
122
|
+
text += f" *• Sentiment:* {self.get_sentiment_str()}\n"
|
|
123
|
+
|
|
124
|
+
if self.socialdominance:
|
|
125
|
+
text += f" *• Social Dominance:* {self.get_socialdominance_str()}\n"
|
|
126
|
+
|
|
127
|
+
if self.galaxyscore:
|
|
128
|
+
text += f" *• Galaxy Score:* {self.galaxyscore}\n"
|
|
129
|
+
|
|
130
|
+
if self.social_volume_24h:
|
|
131
|
+
text += f" *• Social Volume 24h:* {self.social_volume_24h}\n"
|
|
132
|
+
|
|
133
|
+
if self.altrank:
|
|
134
|
+
text += f" *• AltRank:* {self.altrank}\n"
|
|
135
|
+
|
|
136
|
+
if self.large_tx_count:
|
|
137
|
+
text += f" *• Large TX Count:* {self.large_tx_count}\n"
|
|
138
|
+
|
|
139
|
+
if self.close_price and self.currency:
|
|
140
|
+
text += f" *• Price:* `{self.close_price} {self.currency}`"
|
|
141
|
+
text += f" (`{self.change_abs:+.2f}`)\n"
|
|
142
|
+
|
|
143
|
+
if self.volatility_d:
|
|
144
|
+
text += f" *• Volatility:* {self.volatility_d:.2f}%\n"
|
|
145
|
+
|
|
146
|
+
return text
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
@staticmethod
|
|
150
|
+
def _parse(data: list) -> "CoinScanInfo":
|
|
151
|
+
instance = CoinScanInfo()
|
|
152
|
+
instance.base_currency = data[0]
|
|
153
|
+
instance.base_currency_desc = data[1]
|
|
154
|
+
instance.base_currency_logoid = data[2]
|
|
155
|
+
instance.update_mode = data[3]
|
|
156
|
+
instance.type = data[4]
|
|
157
|
+
instance.typespecs = data[5]
|
|
158
|
+
instance.exchange = data[6]
|
|
159
|
+
instance.crypto_total_rank = data[7]
|
|
160
|
+
instance.recommend_all = data[8]
|
|
161
|
+
instance.recommend_ma = data[9]
|
|
162
|
+
instance.recommend_other = data[10]
|
|
163
|
+
instance.RSI = data[11]
|
|
164
|
+
instance.Mom = data[12]
|
|
165
|
+
instance.pricescale = data[13]
|
|
166
|
+
instance.minmov = data[14]
|
|
167
|
+
instance.fractional = data[15]
|
|
168
|
+
instance.minmove2 = data[16]
|
|
169
|
+
instance.AO = data[17]
|
|
170
|
+
instance.CCI20 = data[18]
|
|
171
|
+
instance.stoch_k = data[19]
|
|
172
|
+
instance.stoch_d = data[20]
|
|
173
|
+
instance.profit_addresses_percentage = data[21]
|
|
174
|
+
instance.sentiment = data[22]
|
|
175
|
+
instance.socialdominance = data[23]
|
|
176
|
+
instance.galaxyscore = int(data[24] or 0)
|
|
177
|
+
instance.social_volume_24h = int(data[25] or 0)
|
|
178
|
+
instance.altrank = int(data[26] or 0)
|
|
179
|
+
instance.large_tx_count = int(data[27] or 0)
|
|
180
|
+
instance.close_price = data[28]
|
|
181
|
+
instance.currency = data[29]
|
|
182
|
+
instance.change_abs = data[30]
|
|
183
|
+
instance.volatility_d = data[31]
|
|
184
|
+
|
|
185
|
+
return instance
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from typing import (
|
|
3
|
+
Optional,
|
|
4
|
+
Union,
|
|
5
|
+
get_type_hints,
|
|
6
|
+
Any,
|
|
7
|
+
get_args as get_type_args
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
from trd_utils.html_utils.html_formats import camel_to_snake
|
|
11
|
+
|
|
12
|
+
# Whether to use ultra-list instead of normal python list or not.
|
|
13
|
+
# This might be convenient in some cases, but it is not recommended
|
|
14
|
+
# to use it in production code because of the performance overhead.
|
|
15
|
+
ULTRA_LIST_ENABLED: bool = False
|
|
16
|
+
|
|
17
|
+
# Whether to also set the camelCase attribute names for the model.
|
|
18
|
+
# This is useful when the API returns camelCase attribute names
|
|
19
|
+
# and you want to use them as is in the model; by default, the
|
|
20
|
+
# attribute names are converted to snake_case.
|
|
21
|
+
SET_CAMEL_ATTR_NAMES = False
|
|
22
|
+
|
|
23
|
+
def get_my_field_types(cls):
|
|
24
|
+
type_hints = {}
|
|
25
|
+
for current_cls in cls.__class__.__mro__:
|
|
26
|
+
if current_cls is object or current_cls is BaseModel:
|
|
27
|
+
break
|
|
28
|
+
type_hints.update(get_type_hints(current_cls))
|
|
29
|
+
return type_hints
|
|
30
|
+
|
|
31
|
+
def get_real_attr(cls, attr_name):
|
|
32
|
+
if cls is None:
|
|
33
|
+
return None
|
|
34
|
+
|
|
35
|
+
if isinstance(cls, dict):
|
|
36
|
+
return cls.get(attr_name, None)
|
|
37
|
+
|
|
38
|
+
if hasattr(cls, attr_name):
|
|
39
|
+
return getattr(cls, attr_name)
|
|
40
|
+
|
|
41
|
+
return None
|
|
42
|
+
|
|
43
|
+
class UltraList(list):
|
|
44
|
+
def __getattr__(self, attr):
|
|
45
|
+
if len(self) == 0:
|
|
46
|
+
return None
|
|
47
|
+
return UltraList([get_real_attr(item, attr) for item in self])
|
|
48
|
+
|
|
49
|
+
def convert_to_ultra_list(value: Any) -> UltraList:
|
|
50
|
+
if not value:
|
|
51
|
+
return UltraList()
|
|
52
|
+
|
|
53
|
+
# Go through all fields of the value and convert them to
|
|
54
|
+
# UltraList if they are lists
|
|
55
|
+
|
|
56
|
+
try:
|
|
57
|
+
if isinstance(value, list):
|
|
58
|
+
return UltraList([convert_to_ultra_list(item) for item in value])
|
|
59
|
+
elif isinstance(value, dict):
|
|
60
|
+
return {k: convert_to_ultra_list(v) for k, v in value.items()}
|
|
61
|
+
elif isinstance(value, tuple):
|
|
62
|
+
return tuple(convert_to_ultra_list(v) for v in value)
|
|
63
|
+
elif isinstance(value, set):
|
|
64
|
+
return {convert_to_ultra_list(v) for v in value}
|
|
65
|
+
|
|
66
|
+
for attr, attr_value in get_my_field_types(value).items():
|
|
67
|
+
if isinstance(attr_value, list):
|
|
68
|
+
setattr(value, attr, convert_to_ultra_list(getattr(value, attr)))
|
|
69
|
+
|
|
70
|
+
return value
|
|
71
|
+
except Exception:
|
|
72
|
+
return value
|
|
73
|
+
|
|
74
|
+
class BaseModel:
|
|
75
|
+
def __init__(self, **kwargs):
|
|
76
|
+
annotations = get_my_field_types(self)
|
|
77
|
+
# annotations = self.__annotations__
|
|
78
|
+
for key, value in kwargs.items():
|
|
79
|
+
corrected_key = key
|
|
80
|
+
if key not in annotations:
|
|
81
|
+
# key does not exist, try converting it to snake_case
|
|
82
|
+
corrected_key = camel_to_snake(key)
|
|
83
|
+
if corrected_key not in annotations:
|
|
84
|
+
# just ignore and continue
|
|
85
|
+
annotations[key] = Any
|
|
86
|
+
annotations[corrected_key] = Any
|
|
87
|
+
|
|
88
|
+
expected_type = annotations[corrected_key]
|
|
89
|
+
if hasattr(self, "_get_" + corrected_key + "_type"):
|
|
90
|
+
try:
|
|
91
|
+
overridden_type = getattr(self, "_get_" + corrected_key + "_type")(kwargs)
|
|
92
|
+
if overridden_type:
|
|
93
|
+
expected_type = overridden_type
|
|
94
|
+
except Exception:
|
|
95
|
+
pass
|
|
96
|
+
|
|
97
|
+
is_optional_type = getattr(expected_type, '_name', None) == 'Optional'
|
|
98
|
+
# maybe in the future we can have some other usages for is_optional_type
|
|
99
|
+
# variable or something like that.
|
|
100
|
+
if is_optional_type:
|
|
101
|
+
try:
|
|
102
|
+
expected_type = get_type_args(expected_type)[0]
|
|
103
|
+
except Exception:
|
|
104
|
+
# something went wrong, just ignore and continue
|
|
105
|
+
expected_type = Any
|
|
106
|
+
|
|
107
|
+
# Handle nested models
|
|
108
|
+
if isinstance(value, dict) and issubclass(expected_type, BaseModel):
|
|
109
|
+
value = expected_type(**value)
|
|
110
|
+
|
|
111
|
+
elif isinstance(value, list):
|
|
112
|
+
type_args = get_type_args(expected_type)
|
|
113
|
+
if not type_args:
|
|
114
|
+
# if it's Any, it means we shouldn't really care about the type
|
|
115
|
+
if expected_type != Any:
|
|
116
|
+
value = expected_type(value)
|
|
117
|
+
else:
|
|
118
|
+
# Handle list of nested models
|
|
119
|
+
nested_type = type_args[0]
|
|
120
|
+
if issubclass(nested_type, BaseModel):
|
|
121
|
+
value = [nested_type(**item) for item in value]
|
|
122
|
+
|
|
123
|
+
if ULTRA_LIST_ENABLED and isinstance(value, list):
|
|
124
|
+
value = convert_to_ultra_list(value)
|
|
125
|
+
|
|
126
|
+
# Type checking
|
|
127
|
+
elif expected_type != Any and not isinstance(value, expected_type):
|
|
128
|
+
try:
|
|
129
|
+
value = expected_type(value)
|
|
130
|
+
except Exception:
|
|
131
|
+
raise TypeError(f"Field {corrected_key} must be of type {expected_type}," +
|
|
132
|
+
f" but it's {type(value)}")
|
|
133
|
+
|
|
134
|
+
setattr(self, corrected_key, value)
|
|
135
|
+
if SET_CAMEL_ATTR_NAMES and key != corrected_key:
|
|
136
|
+
setattr(self, key, value)
|
|
137
|
+
|
|
138
|
+
# Check if all required fields are present
|
|
139
|
+
# for field in self.__annotations__:
|
|
140
|
+
# if not hasattr(self, field):
|
|
141
|
+
# raise ValueError(f"Missing required field: {field}")
|
|
142
|
+
|
|
143
|
+
@classmethod
|
|
144
|
+
def deserialize(cls, json_data: Union[str, dict]):
|
|
145
|
+
if isinstance(json_data, str):
|
|
146
|
+
data = json.loads(json_data)
|
|
147
|
+
else:
|
|
148
|
+
data = json_data
|
|
149
|
+
return cls(**data)
|
|
150
|
+
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 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.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: trd_utils
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Common Basic Utils for Python3. By ALiwoto.
|
|
5
|
+
Home-page: https://github.com/ALiwoto/trd_utils
|
|
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
|
+
Requires-Dist: cryptography (>=41.0.7)
|
|
22
|
+
Requires-Dist: httpx (>=0.21.0)
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
|
|
25
|
+
# Trd Utils
|
|
26
|
+
|
|
27
|
+
Basic common utils for Python.
|
|
28
|
+
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
trd_utils/__init__.py,sha256=ajz1GSNU9xYVrFEDSz6Xwg7amWQ_yvW75tQa1ZvRIWc,3
|
|
2
|
+
trd_utils/bx_ultra/__init__.py,sha256=8Ssy-eOemQR32Nv1-FoPHm87nRqRO4Fm2PU5GHEFKfQ,80
|
|
3
|
+
trd_utils/bx_ultra/bx_types.py,sha256=fILyRGxdPyQOwqMiVOJK9GtVD5eV116GrADKfnye6OI,19414
|
|
4
|
+
trd_utils/bx_ultra/bx_ultra_client.py,sha256=w2ajrBLDrSeRAPXCPV4lkUkR-r1PqE1Kwu2bxbJUy7A,15876
|
|
5
|
+
trd_utils/bx_ultra/common_utils.py,sha256=aHvtaGelyUB10p0FWMX5y-snO0ZuKYrAW2rqNzyxEUw,1392
|
|
6
|
+
trd_utils/cipher/__init__.py,sha256=V05KNuzQwCic-ihMVHlC8sENaJGc3I8MCb4pg4849X8,1765
|
|
7
|
+
trd_utils/html_utils/__init__.py,sha256=1WWs8C7JszRjTkmzIRLHpxWECHur_DrulTPGIeX88oM,426
|
|
8
|
+
trd_utils/html_utils/html_formats.py,sha256=unKsvOiiDmYTTaM0DYZEUNLEUzWQKKrqASJXvY54kvU,2299
|
|
9
|
+
trd_utils/tradingview/__init__.py,sha256=H0QYb-O5qvy7qC3yswtlcSWLmeBnaS6oJ3JtjvmaV_Y,154
|
|
10
|
+
trd_utils/tradingview/tradingview_client.py,sha256=iiNSLSKr5PnDcGiVFn515gsnGtqm5Htu99IwShHr2wM,4007
|
|
11
|
+
trd_utils/tradingview/tradingview_types.py,sha256=z21MXPVdWHAduEl3gSeMIRhxtBN9yK-jPYHfZSMIbSA,6144
|
|
12
|
+
trd_utils/types_helper/__init__.py,sha256=SB9_5bQkuxV059AKC4cXYwsLPT3lM4LOu2m8LpMhZlQ,60
|
|
13
|
+
trd_utils/types_helper/base_model.py,sha256=J3SdOB9UNgw0cI8gaG8zjnfqa1qiVmlpz4o3aBxHqas,5631
|
|
14
|
+
trd_utils-0.0.1.dist-info/LICENSE,sha256=msfwzd8S06fL8ORRneycnonTKmyuXzfeBT0V2figir8,1062
|
|
15
|
+
trd_utils-0.0.1.dist-info/METADATA,sha256=efwVikXUz2_0VeUw8f184Fh28tAQ3w-vpvdosENtuqI,975
|
|
16
|
+
trd_utils-0.0.1.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
|
17
|
+
trd_utils-0.0.1.dist-info/RECORD,,
|