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,72 @@
1
+ import html
2
+
3
+ def camel_to_snake(str_value: str) -> str:
4
+ """
5
+ Convert CamelCase to snake_case.
6
+ https://stackoverflow.com/a/44969381/16518789
7
+ """
8
+ return ''.join(['_'+c.lower() if c.isupper() else c for c in str_value]).lstrip('_')
9
+
10
+ def to_camel_case(snake_str: str) -> str:
11
+ """
12
+ Convert snake_case to CamelCase.
13
+ https://stackoverflow.com/a/19053800/16518789
14
+ """
15
+ return "".join(x.capitalize() for x in snake_str.lower().split("_"))
16
+
17
+ def to_lower_camel_case(snake_str: str) -> str:
18
+ # We capitalize the first letter of each component except the first one
19
+ # with the 'capitalize' method and join them together.
20
+ camel_string = to_camel_case(snake_str)
21
+ return snake_str[0].lower() + camel_string[1:]
22
+
23
+ def get_html_normal(*argv) -> str:
24
+ if argv is None or len(argv) == 0:
25
+ return ""
26
+
27
+ my_str = ""
28
+ for value in argv:
29
+ if not value:
30
+ continue
31
+ if isinstance(value, str):
32
+ my_str += value
33
+ else:
34
+ my_str += str(value)
35
+
36
+ return my_str
37
+
38
+ def html_normal(value, *argv) -> str:
39
+ my_str = html.escape(str(value))
40
+ for value in argv:
41
+ if isinstance(value, str):
42
+ my_str += value
43
+ return my_str
44
+
45
+
46
+ def html_mono(value, *argv) -> str:
47
+ return f"<code>{html.escape(str(value))}</code>" + get_html_normal(*argv)
48
+
49
+ def html_in_parenthesis(value) -> str:
50
+ if not value:
51
+ return ": "
52
+ return f" ({html.escape(str(value))}): "
53
+
54
+ def html_bold(value, *argv) -> str:
55
+ return f"<b>{html.escape(str(value))}</b>" + get_html_normal(*argv)
56
+
57
+ def html_italic(value, *argv) -> str:
58
+ return f"<i>{html.escape(str(value))}</i>" + get_html_normal(*argv)
59
+
60
+ def html_link(value, link: str, *argv) -> str:
61
+ if not isinstance(link, str) or len(link) == 0:
62
+ return html_mono(value, *argv)
63
+ return f"<a href={html.escape(link)}>{html.escape(str(value))}</a>" + get_html_normal(*argv)
64
+
65
+ def html_code_snippets(value, language: str, *argv):
66
+ return html_pre(value, language, *argv)
67
+
68
+ def html_pre(value, language: str, *argv):
69
+ return f"<pre language={html.escape(language)}>{html.escape(str(value))}</pre>" + get_html_normal(*argv)
70
+
71
+ def html_spoiler(value, *argv):
72
+ return f"<spoiler>{html.escape(str(value))}</spoiler>" + get_html_normal(*argv)
@@ -0,0 +1,8 @@
1
+ from .tradingview_client import TradingViewClient
2
+ from .tradingview_types import CoinScanInfo
3
+
4
+
5
+ __all__ = [
6
+ "TradingViewClient",
7
+ "CoinScanInfo",
8
+ ]
@@ -0,0 +1,128 @@
1
+ import json
2
+ from typing import Optional
3
+
4
+ from trd_utils.types_helper import new_list
5
+
6
+ from .tradingview_types import CoinScanInfo
7
+
8
+
9
+ class TradingViewClient:
10
+ """TradingViewClient class to interact with TradingView API."""
11
+
12
+ def __init__(self) -> None:
13
+ pass
14
+
15
+ async def get_coin_scan(
16
+ self,
17
+ coin_filter: Optional[str] = None,
18
+ limit: int = 200,
19
+ offset: int = 0,
20
+ ) -> list["CoinScanInfo"]:
21
+ import httpx
22
+
23
+ cookies = {
24
+ "cookiesSettings": '{"analytics":true,"advertising":true}',
25
+ "cookiePrivacyPreferenceBannerProduction": "accepted",
26
+ }
27
+
28
+ headers = {
29
+ "accept": "application/json",
30
+ "accept-language": "en-US,en;q=0.9",
31
+ "cache-control": "no-cache",
32
+ "content-type": "text/plain;charset=UTF-8",
33
+ "origin": "https://www.tradingview.com",
34
+ "pragma": "no-cache",
35
+ "priority": "u=1, i",
36
+ "referer": "https://www.tradingview.com/",
37
+ "sec-ch-ua": '"Not)A;Brand";v="99", "Google Chrome";v="127", "Chromium";v="127"',
38
+ "sec-ch-ua-mobile": "?0",
39
+ "sec-ch-ua-platform": '"Windows"',
40
+ "sec-fetch-dest": "empty",
41
+ "sec-fetch-mode": "cors",
42
+ "sec-fetch-site": "same-site",
43
+ "user-agent": (
44
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
45
+ + "(KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36"
46
+ ),
47
+ }
48
+
49
+ params = {
50
+ "label-product": "screener-coin",
51
+ }
52
+
53
+ data = {
54
+ "columns": [
55
+ "base_currency",
56
+ "base_currency_desc",
57
+ "base_currency_logoid",
58
+ "update_mode",
59
+ "type",
60
+ "typespecs",
61
+ "exchange",
62
+ "crypto_total_rank",
63
+ "Recommend.All",
64
+ "Recommend.MA",
65
+ "Recommend.Other",
66
+ "RSI",
67
+ "Mom",
68
+ "pricescale",
69
+ "minmov",
70
+ "fractional",
71
+ "minmove2",
72
+ "AO",
73
+ "CCI20",
74
+ "Stoch.K",
75
+ "Stoch.D",
76
+ "profit_addresses_percentage",
77
+ "sentiment",
78
+ "socialdominance",
79
+ "galaxyscore",
80
+ "social_volume_24h",
81
+ "altrank",
82
+ "large_tx_count",
83
+ "close",
84
+ "currency",
85
+ "change_abs",
86
+ "Volatility.D",
87
+ ],
88
+ "ignore_unknown_fields": False,
89
+ "options": {"lang": "en"},
90
+ "range": [
91
+ offset,
92
+ offset + limit,
93
+ ],
94
+ "sort": {"sortBy": "crypto_total_rank", "sortOrder": "asc"},
95
+ "symbols": {},
96
+ "markets": ["coin"],
97
+ }
98
+
99
+ if coin_filter:
100
+ data["filter"] = [
101
+ {
102
+ "left": "base_currency,base_currency_desc",
103
+ "operation": "match",
104
+ "right": f"{coin_filter}",
105
+ }
106
+ ]
107
+ data = json.dumps(data)
108
+ async with httpx.AsyncClient() as client:
109
+ response = await client.post(
110
+ "https://scanner.tradingview.com/coin/scan",
111
+ params=params,
112
+ cookies=cookies,
113
+ headers=headers,
114
+ data=data,
115
+ )
116
+
117
+ j_result = response.json()
118
+ if not isinstance(j_result, dict) or not j_result.get("data", None):
119
+ raise Exception("No data found")
120
+
121
+ j_data = j_result["data"]
122
+ all_infos = new_list()
123
+ for current_data in j_data:
124
+ if not isinstance(current_data, dict):
125
+ continue
126
+ all_infos.append(CoinScanInfo._parse(current_data.get("d", [])))
127
+
128
+ 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,12 @@
1
+
2
+ from .ultra_list import UltraList
3
+ from .base_model import BaseModel, new_list
4
+ from .decorators import ignore_json_fields
5
+
6
+
7
+ __all__ = [
8
+ "UltraList",
9
+ "BaseModel",
10
+ "new_list",
11
+ "ignore_json_fields",
12
+ ]