crypticorn 1.0.0__py3-none-any.whl → 1.0.2rc1__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.
- crypticorn/__init__.py +3 -3
- crypticorn/client.py +722 -0
- crypticorn/{api.py → hive/main.py} +6 -6
- crypticorn/hive/requirements.txt +4 -0
- crypticorn/{utils.py → hive/utils.py} +2 -2
- crypticorn/klines/client/__init__.py +62 -0
- crypticorn/klines/client/api/__init__.py +9 -0
- crypticorn/klines/client/api/funding_rates_api.py +362 -0
- crypticorn/klines/client/api/health_check_api.py +281 -0
- crypticorn/klines/client/api/ohlcv_data_api.py +409 -0
- crypticorn/klines/client/api/symbols_api.py +308 -0
- crypticorn/klines/client/api/udf_api.py +1929 -0
- crypticorn/klines/client/api_client.py +797 -0
- crypticorn/klines/client/api_response.py +21 -0
- crypticorn/klines/client/configuration.py +565 -0
- crypticorn/klines/client/exceptions.py +216 -0
- crypticorn/klines/client/models/__init__.py +41 -0
- crypticorn/klines/client/models/base_response_health_check_response.py +108 -0
- crypticorn/klines/client/models/base_response_list_funding_rate_response.py +112 -0
- crypticorn/klines/client/models/base_response_list_str.py +104 -0
- crypticorn/klines/client/models/base_response_ohlcv_response.py +108 -0
- crypticorn/klines/client/models/error_response.py +101 -0
- crypticorn/klines/client/models/exchange.py +91 -0
- crypticorn/klines/client/models/funding_rate_response.py +92 -0
- crypticorn/klines/client/models/health_check_response.py +89 -0
- crypticorn/klines/client/models/history_error_response.py +89 -0
- crypticorn/klines/client/models/history_no_data_response.py +99 -0
- crypticorn/klines/client/models/history_success_response.py +99 -0
- crypticorn/klines/client/models/http_validation_error.py +95 -0
- crypticorn/klines/client/models/market.py +37 -0
- crypticorn/klines/client/models/ohlcv_response.py +98 -0
- crypticorn/klines/client/models/resolution.py +40 -0
- crypticorn/klines/client/models/response_get_history_udf_history_get.py +149 -0
- crypticorn/klines/client/models/search_symbol_response.py +97 -0
- crypticorn/klines/client/models/sort_direction.py +37 -0
- crypticorn/klines/client/models/symbol_group_response.py +87 -0
- crypticorn/klines/client/models/symbol_info_response.py +115 -0
- crypticorn/klines/client/models/symbol_type.py +89 -0
- crypticorn/klines/client/models/timeframe.py +40 -0
- crypticorn/klines/client/models/udf_config_response.py +121 -0
- crypticorn/klines/client/models/validation_error.py +99 -0
- crypticorn/klines/client/models/validation_error_loc_inner.py +138 -0
- crypticorn/klines/client/py.typed +0 -0
- crypticorn/klines/client/rest.py +257 -0
- crypticorn/klines/main.py +42 -0
- crypticorn/klines/requirements.txt +4 -0
- crypticorn/klines/test/__init__.py +0 -0
- crypticorn/klines/test/test_base_response_health_check_response.py +56 -0
- crypticorn/klines/test/test_base_response_list_funding_rate_response.py +59 -0
- crypticorn/klines/test/test_base_response_list_str.py +56 -0
- crypticorn/klines/test/test_base_response_ohlcv_response.py +72 -0
- crypticorn/klines/test/test_error_response.py +57 -0
- crypticorn/klines/test/test_exchange.py +56 -0
- crypticorn/klines/test/test_funding_rate_response.py +56 -0
- crypticorn/klines/test/test_funding_rates_api.py +38 -0
- crypticorn/klines/test/test_health_check_api.py +38 -0
- crypticorn/klines/test/test_health_check_response.py +52 -0
- crypticorn/klines/test/test_history_error_response.py +53 -0
- crypticorn/klines/test/test_history_no_data_response.py +69 -0
- crypticorn/klines/test/test_history_success_response.py +87 -0
- crypticorn/klines/test/test_http_validation_error.py +58 -0
- crypticorn/klines/test/test_market.py +33 -0
- crypticorn/klines/test/test_ohlcv_data_api.py +38 -0
- crypticorn/klines/test/test_ohlcv_response.py +86 -0
- crypticorn/klines/test/test_resolution.py +33 -0
- crypticorn/klines/test/test_response_get_history_udf_history_get.py +89 -0
- crypticorn/klines/test/test_search_symbol_response.py +62 -0
- crypticorn/klines/test/test_sort_direction.py +33 -0
- crypticorn/klines/test/test_symbol_group_response.py +53 -0
- crypticorn/klines/test/test_symbol_info_response.py +84 -0
- crypticorn/klines/test/test_symbol_type.py +54 -0
- crypticorn/klines/test/test_symbols_api.py +38 -0
- crypticorn/klines/test/test_timeframe.py +33 -0
- crypticorn/klines/test/test_udf_api.py +80 -0
- crypticorn/klines/test/test_udf_config_response.py +95 -0
- crypticorn/klines/test/test_validation_error.py +60 -0
- crypticorn/klines/test/test_validation_error_loc_inner.py +50 -0
- crypticorn/trade/client/__init__.py +63 -0
- crypticorn/trade/client/api/__init__.py +13 -0
- crypticorn/trade/client/api/api_keys_api.py +1468 -0
- crypticorn/trade/client/api/bots_api.py +1211 -0
- crypticorn/trade/client/api/exchanges_api.py +297 -0
- crypticorn/trade/client/api/futures_trading_panel_api.py +1463 -0
- crypticorn/trade/client/api/notifications_api.py +1767 -0
- crypticorn/trade/client/api/orders_api.py +331 -0
- crypticorn/trade/client/api/status_api.py +278 -0
- crypticorn/trade/client/api/strategies_api.py +331 -0
- crypticorn/trade/client/api/trading_actions_api.py +898 -0
- crypticorn/trade/client/api_client.py +797 -0
- crypticorn/trade/client/api_response.py +21 -0
- crypticorn/trade/client/configuration.py +574 -0
- crypticorn/trade/client/exceptions.py +216 -0
- crypticorn/trade/client/models/__init__.py +38 -0
- crypticorn/trade/client/models/action_model.py +202 -0
- crypticorn/trade/client/models/api_error_identifier.py +83 -0
- crypticorn/trade/client/models/api_key_model.py +135 -0
- crypticorn/trade/client/models/bot_model.py +122 -0
- crypticorn/trade/client/models/exchange.py +37 -0
- crypticorn/trade/client/models/execution_ids.py +91 -0
- crypticorn/trade/client/models/futures_balance.py +109 -0
- crypticorn/trade/client/models/futures_trading_action.py +198 -0
- crypticorn/trade/client/models/http_validation_error.py +95 -0
- crypticorn/trade/client/models/margin_mode.py +37 -0
- crypticorn/trade/client/models/market_type.py +37 -0
- crypticorn/trade/client/models/notification_model.py +113 -0
- crypticorn/trade/client/models/notification_type.py +39 -0
- crypticorn/trade/client/models/order_model.py +263 -0
- crypticorn/trade/client/models/order_status.py +40 -0
- crypticorn/trade/client/models/post_futures_action.py +93 -0
- crypticorn/trade/client/models/strategy_exchange_info.py +90 -0
- crypticorn/trade/client/models/strategy_model.py +119 -0
- crypticorn/trade/client/models/tpsl.py +116 -0
- crypticorn/trade/client/models/trading_action_type.py +39 -0
- crypticorn/trade/client/models/update_notification.py +91 -0
- crypticorn/trade/client/models/validation_error.py +99 -0
- crypticorn/trade/client/models/validation_error_loc_inner.py +138 -0
- crypticorn/trade/client/py.typed +0 -0
- crypticorn/trade/client/rest.py +257 -0
- crypticorn/trade/main.py +38 -0
- crypticorn/trade/requirements.txt +4 -0
- crypticorn/trade/test/__init__.py +0 -0
- crypticorn/trade/test/test_action_model.py +87 -0
- crypticorn/trade/test/test_api_error_identifier.py +33 -0
- crypticorn/trade/test/test_api_key_model.py +61 -0
- crypticorn/trade/test/test_api_keys_api.py +66 -0
- crypticorn/trade/test/test_bot_model.py +64 -0
- crypticorn/trade/test/test_bots_api.py +59 -0
- crypticorn/trade/test/test_exchange.py +33 -0
- crypticorn/trade/test/test_exchanges_api.py +38 -0
- crypticorn/trade/test/test_execution_ids.py +68 -0
- crypticorn/trade/test/test_futures_balance.py +62 -0
- crypticorn/trade/test/test_futures_trading_action.py +86 -0
- crypticorn/trade/test/test_futures_trading_panel_api.py +66 -0
- crypticorn/trade/test/test_http_validation_error.py +58 -0
- crypticorn/trade/test/test_margin_mode.py +33 -0
- crypticorn/trade/test/test_market_type.py +33 -0
- crypticorn/trade/test/test_notification_model.py +59 -0
- crypticorn/trade/test/test_notification_type.py +33 -0
- crypticorn/trade/test/test_notifications_api.py +73 -0
- crypticorn/trade/test/test_order_model.py +75 -0
- crypticorn/trade/test/test_order_status.py +33 -0
- crypticorn/trade/test/test_orders_api.py +38 -0
- crypticorn/trade/test/test_post_futures_action.py +72 -0
- crypticorn/trade/test/test_status_api.py +38 -0
- crypticorn/trade/test/test_strategies_api.py +38 -0
- crypticorn/trade/test/test_strategy_exchange_info.py +54 -0
- crypticorn/trade/test/test_strategy_model.py +73 -0
- crypticorn/trade/test/test_tpsl.py +56 -0
- crypticorn/trade/test/test_trading_action_type.py +33 -0
- crypticorn/trade/test/test_trading_actions_api.py +52 -0
- crypticorn/trade/test/test_update_notification.py +54 -0
- crypticorn/trade/test/test_validation_error.py +60 -0
- crypticorn/trade/test/test_validation_error_loc_inner.py +50 -0
- crypticorn-1.0.2rc1.dist-info/METADATA +47 -0
- crypticorn-1.0.2rc1.dist-info/RECORD +158 -0
- {crypticorn-1.0.0.dist-info → crypticorn-1.0.2rc1.dist-info}/WHEEL +1 -1
- crypticorn-1.0.0.dist-info/METADATA +0 -34
- crypticorn-1.0.0.dist-info/RECORD +0 -8
- {crypticorn-1.0.0.dist-info → crypticorn-1.0.2rc1.dist-info}/LICENSE.md +0 -0
- {crypticorn-1.0.0.dist-info → crypticorn-1.0.2rc1.dist-info}/top_level.txt +0 -0
crypticorn/client.py
ADDED
@@ -0,0 +1,722 @@
|
|
1
|
+
import httpx
|
2
|
+
import pandas as pd
|
3
|
+
from pandas import DataFrame
|
4
|
+
from pydantic import BaseModel
|
5
|
+
from typing import Optional, Union, List
|
6
|
+
from urllib.parse import urljoin
|
7
|
+
from datetime import datetime, timedelta
|
8
|
+
|
9
|
+
from crypticorn.hive.main import HiveClient
|
10
|
+
from crypticorn.trade.main import TradeClient
|
11
|
+
from crypticorn.klines.main import KlinesClient
|
12
|
+
|
13
|
+
class PredictionData(BaseModel):
|
14
|
+
id: Optional[int] = None
|
15
|
+
action: Optional[str] = None
|
16
|
+
course_change: Optional[float]
|
17
|
+
symbol: str
|
18
|
+
timestamp: int
|
19
|
+
version: str
|
20
|
+
base_price: Optional[float]
|
21
|
+
p10: list[float]
|
22
|
+
p30: list[float]
|
23
|
+
p50: list[float]
|
24
|
+
p70: list[float]
|
25
|
+
p90: list[float]
|
26
|
+
|
27
|
+
|
28
|
+
class TrendData(BaseModel):
|
29
|
+
timestamps: Optional[list[int]]
|
30
|
+
positive_prob: list[float]
|
31
|
+
symbol: str
|
32
|
+
version: Optional[str]
|
33
|
+
|
34
|
+
|
35
|
+
class TrendQuery(BaseModel):
|
36
|
+
symbol: str
|
37
|
+
limit: int
|
38
|
+
offset: int
|
39
|
+
sort: str
|
40
|
+
dir: str
|
41
|
+
from_ts: int
|
42
|
+
to_ts: int
|
43
|
+
version: str = "1"
|
44
|
+
|
45
|
+
default_version = "1.5"
|
46
|
+
|
47
|
+
class CrypticornClient:
|
48
|
+
def __init__(
|
49
|
+
self, base_url: str = "https://api.crypticorn.com", api_key: str = None, jwt: str = None
|
50
|
+
):
|
51
|
+
self.base_url = base_url
|
52
|
+
self.api_key = api_key
|
53
|
+
self.jwt = jwt
|
54
|
+
self.client = httpx.Client()
|
55
|
+
|
56
|
+
# Initialize service clients
|
57
|
+
self.hive = HiveClient(base_url, api_key, jwt)
|
58
|
+
self.trade = TradeClient(base_url, api_key, jwt)
|
59
|
+
self.klines = KlinesClient(base_url, api_key, jwt)
|
60
|
+
|
61
|
+
def get_response(
|
62
|
+
self, endpoint: str, params: dict = None, dict_key: str = None
|
63
|
+
) -> Union[DataFrame, dict]:
|
64
|
+
full_url = urljoin(self.base_url, "/v1/miners" + endpoint)
|
65
|
+
print(full_url)
|
66
|
+
print(params)
|
67
|
+
try:
|
68
|
+
response = self.client.get(full_url, params=params, timeout=None)
|
69
|
+
response.raise_for_status()
|
70
|
+
except httpx.HTTPStatusError as e:
|
71
|
+
print(f"HTTP error occurred: {e}")
|
72
|
+
return {}
|
73
|
+
except httpx.RequestError as e:
|
74
|
+
print(f"Request error occurred: {e}")
|
75
|
+
return {}
|
76
|
+
|
77
|
+
formatted_response = response.json()
|
78
|
+
|
79
|
+
if dict_key:
|
80
|
+
return formatted_response.get(dict_key, {})
|
81
|
+
|
82
|
+
return DataFrame(formatted_response)
|
83
|
+
|
84
|
+
# -------------------- START OF DATA PLATFORM SERVICE ------------------------ #
|
85
|
+
def get_economics_news(self, entries: int, reverse: bool = False) -> DataFrame:
|
86
|
+
class NewsData(BaseModel):
|
87
|
+
timestamp: int
|
88
|
+
country: Union[str, None]
|
89
|
+
event: Union[str, None]
|
90
|
+
currency: Union[str, None]
|
91
|
+
previous: Union[float, None]
|
92
|
+
estimate: Union[float, None]
|
93
|
+
actual: Union[float, None]
|
94
|
+
change: Union[float, None]
|
95
|
+
impact: Union[str, None]
|
96
|
+
changePercentage: Union[float, None]
|
97
|
+
|
98
|
+
res = self.get_response("/ec", {"entries": entries, "reverse": reverse}, "data")
|
99
|
+
df = DataFrame(res)
|
100
|
+
df.columns = NewsData.__annotations__
|
101
|
+
df.sort_values(by="timestamp", ascending=False, inplace=True)
|
102
|
+
return df
|
103
|
+
|
104
|
+
def get_bc_historical(
|
105
|
+
self, ticker: str, interval: str, entries: int, reverse: bool = False
|
106
|
+
) -> DataFrame:
|
107
|
+
"""
|
108
|
+
|
109
|
+
get: ticker + open_unix + OHLC + Volume data , as pandas dataframe
|
110
|
+
|
111
|
+
--- structure reference: ---
|
112
|
+
|
113
|
+
(column name: data type)
|
114
|
+
|
115
|
+
timestamp: int,
|
116
|
+
ticker: str
|
117
|
+
open_interval: float,
|
118
|
+
high_interval: float,
|
119
|
+
low_interval: float,
|
120
|
+
close_interval: float,
|
121
|
+
volume_interval: float,
|
122
|
+
"""
|
123
|
+
df = self.get_response(
|
124
|
+
"/historical",
|
125
|
+
{"ticker": ticker + "@" + interval, "entries": entries, "reverse": reverse},
|
126
|
+
)
|
127
|
+
desired_order = [
|
128
|
+
"timestamp",
|
129
|
+
"ticker",
|
130
|
+
"open",
|
131
|
+
"high",
|
132
|
+
"low",
|
133
|
+
"close",
|
134
|
+
"volume",
|
135
|
+
]
|
136
|
+
df = df[desired_order]
|
137
|
+
df.rename(
|
138
|
+
columns={
|
139
|
+
"ticker": "coin",
|
140
|
+
"open": f"open_{interval}",
|
141
|
+
"high": f"high_{interval}",
|
142
|
+
"low": f"low_{interval}",
|
143
|
+
"close": f"close_{interval}",
|
144
|
+
"volume": f"volume_{interval}",
|
145
|
+
},
|
146
|
+
inplace=True,
|
147
|
+
)
|
148
|
+
df[["coin", "interval"]] = df["coin"].str.split("@", expand=True)
|
149
|
+
df.pop("interval")
|
150
|
+
df.sort_values(by="timestamp", ascending=False, inplace=True)
|
151
|
+
df["timestamp"] = df["timestamp"] // 1000
|
152
|
+
return df
|
153
|
+
|
154
|
+
def get_fgi_historical(self, days: int) -> DataFrame:
|
155
|
+
"""
|
156
|
+
|
157
|
+
get: unix_time + value , as pandas dataframe
|
158
|
+
|
159
|
+
--- structure reference: ---
|
160
|
+
|
161
|
+
(column name: data type)
|
162
|
+
|
163
|
+
unix_time: int,
|
164
|
+
value: int,
|
165
|
+
|
166
|
+
"""
|
167
|
+
df = self.get_response(endpoint="/historical/fgi", params={"days": days})
|
168
|
+
return df
|
169
|
+
|
170
|
+
def post_prediction(self, data: PredictionData) -> dict:
|
171
|
+
response = self.client.post(
|
172
|
+
urljoin(self.base_url, "/v1/predictions"),
|
173
|
+
json=data.dict(),
|
174
|
+
headers={"Authorization": f"Bearer {self.api_key}"},
|
175
|
+
)
|
176
|
+
return response.json()
|
177
|
+
|
178
|
+
def get_latest_predictions(self, version: str = default_version) -> DataFrame:
|
179
|
+
response = self.client.get(
|
180
|
+
urljoin(self.base_url, f"/v1/predictions/latest?version={version}"),
|
181
|
+
headers={"Authorization": f"Bearer {self.api_key}"},
|
182
|
+
)
|
183
|
+
arr = response.json()
|
184
|
+
flatarr = []
|
185
|
+
for i in arr:
|
186
|
+
for index, _ in enumerate(i["p50"]):
|
187
|
+
interval = 900
|
188
|
+
ts = i["timestamp"]
|
189
|
+
ts = ts - (ts % interval)
|
190
|
+
ts = ts + (index * interval)
|
191
|
+
pred_dict = {
|
192
|
+
"id": i["id"],
|
193
|
+
"action": i["action"],
|
194
|
+
"course_change": i["course_change"],
|
195
|
+
"symbol": i["symbol"],
|
196
|
+
"timestamp": ts,
|
197
|
+
"version": i["version"],
|
198
|
+
# "base_price": i["base_price"],
|
199
|
+
"p10": i["p10"][index],
|
200
|
+
"p30": i["p30"][index],
|
201
|
+
"p50": i["p50"][index],
|
202
|
+
"p70": i["p70"][index],
|
203
|
+
"p90": i["p90"][index],
|
204
|
+
}
|
205
|
+
flatarr.append(pred_dict)
|
206
|
+
df = DataFrame(flatarr)
|
207
|
+
df["timestamp"] = pd.to_datetime(df["timestamp"], unit="s")
|
208
|
+
return df
|
209
|
+
|
210
|
+
def get_prediction(self, pair: str, version: str = default_version, limit: int = 1) -> PredictionData:
|
211
|
+
response = self.client.get(
|
212
|
+
urljoin(self.base_url, f"/v1/predictions/symbol/{pair}?version={version}&limit={limit}"),
|
213
|
+
headers={"Authorization": f"Bearer {self.api_key}"},
|
214
|
+
)
|
215
|
+
return response.json()
|
216
|
+
|
217
|
+
def get_prediction_time(self, id) -> DataFrame:
|
218
|
+
response = self.client.get(
|
219
|
+
urljoin(self.base_url, f"/v1/predictions/time/{id}"),
|
220
|
+
headers={"Authorization": f"Bearer {self.api_key}"},
|
221
|
+
)
|
222
|
+
arr = response.json()
|
223
|
+
return DataFrame(arr)
|
224
|
+
|
225
|
+
def get_udf_history(self, symbol: str, entries: int) -> DataFrame:
|
226
|
+
now = int(pd.Timestamp.now().timestamp())
|
227
|
+
response = self.client.get(
|
228
|
+
urljoin(self.base_url, "/v1/udf/history"),
|
229
|
+
headers={"Authorization": f"Bearer {self.api_key}"},
|
230
|
+
params={
|
231
|
+
"from": now - (entries * 900),
|
232
|
+
"to": now,
|
233
|
+
"symbol": symbol,
|
234
|
+
"resolution": "15",
|
235
|
+
"countback": entries,
|
236
|
+
},
|
237
|
+
)
|
238
|
+
# # {'s': 'ok', 't': [1710982800.0, 1710983700.0], 'c': [67860.61, 67930.01], 'o': [67656.01, 67860.6], 'h': [67944.69, 67951.15], 'l': [67656.0, 67792.06], 'v': [448.61539, 336.9907]}
|
239
|
+
result = response.json()
|
240
|
+
# construct dataframe for t, c, o, h, l, v arrays
|
241
|
+
df = DataFrame(result)
|
242
|
+
df["t"] = pd.to_datetime(df["t"], unit="s")
|
243
|
+
df.pop("s")
|
244
|
+
return df
|
245
|
+
|
246
|
+
def post_trend(self, data: TrendData) -> dict:
|
247
|
+
response = self.client.post(
|
248
|
+
urljoin(self.base_url, "/v1/trends"),
|
249
|
+
json=data.dict(),
|
250
|
+
headers={"Authorization": f"Bearer {self.api_key}"},
|
251
|
+
)
|
252
|
+
return response.json()
|
253
|
+
|
254
|
+
def get_trends(self, query: TrendQuery):
|
255
|
+
response = self.client.get(
|
256
|
+
urljoin(self.base_url, "/v1/trends"),
|
257
|
+
headers={"Authorization": f"Bearer {self.api_key}"},
|
258
|
+
params=query.dict(),
|
259
|
+
)
|
260
|
+
df = DataFrame(response.json())
|
261
|
+
return df
|
262
|
+
|
263
|
+
# -------------------- END OF DATA PLATFORM SERVICE ------------------------ #
|
264
|
+
|
265
|
+
# -------------------- START OF KLINE SERVICE ------------------------ #
|
266
|
+
def get_symbols(self, market: str) -> DataFrame:
|
267
|
+
"""
|
268
|
+
get: symbol for futures or spot, as pandas dataframe
|
269
|
+
market: futures or spot
|
270
|
+
"""
|
271
|
+
response = self.klines.symbols.symbols_symbols_market_get(market=market)
|
272
|
+
if response.status_code == 200:
|
273
|
+
df = DataFrame(response.json())
|
274
|
+
return df
|
275
|
+
else:
|
276
|
+
raise Exception(f"Failed to get symbols: {response.json()}")
|
277
|
+
|
278
|
+
def get_klines(self, market: str, symbol: str, interval: str, limit: int, start_timestamp: int = None, end_timestamp: int = None, sort: str = "desc") -> DataFrame:
|
279
|
+
"""
|
280
|
+
get: unix_time + OHLCV data , as pandas dataframe
|
281
|
+
symbol have to be in capital case e.g. (BTCUSDT)
|
282
|
+
market: futures or spot
|
283
|
+
interval: 1m, 3m, 5m, 15m, 30m, 1h, 4h, 1d
|
284
|
+
"""
|
285
|
+
params = {"limit": limit}
|
286
|
+
if start_timestamp is not None:
|
287
|
+
params["start"] = start_timestamp
|
288
|
+
if end_timestamp is not None:
|
289
|
+
params["end"] = end_timestamp
|
290
|
+
if sort is not None:
|
291
|
+
params["sort_direction"] = sort
|
292
|
+
|
293
|
+
response = self.client.get(
|
294
|
+
urljoin(self.base_url, f"/v1/klines/{market}/{interval}/{symbol}"),
|
295
|
+
params=params, timeout=None
|
296
|
+
)
|
297
|
+
if response.status_code == 200:
|
298
|
+
df = DataFrame(response.json())
|
299
|
+
df['timestamp'] = pd.to_datetime(df['timestamp'])
|
300
|
+
df['timestamp'] = df['timestamp'].astype("int64") // 10 ** 9 # use int64 instead of int for windows
|
301
|
+
return df
|
302
|
+
else:
|
303
|
+
raise Exception(f"Failed to get klines: {response.json()}")
|
304
|
+
|
305
|
+
def get_funding_rate(self, symbol: str, start_timestamp: int = None, end_timestamp: int = None, limit: int = 2000) -> DataFrame:
|
306
|
+
"""
|
307
|
+
get: unix_time + funding rate data , as pandas dataframe
|
308
|
+
symbol have to be in capital case e.g. (BTCUSDT)
|
309
|
+
start_timestamp and end_timestamp are optional
|
310
|
+
"""
|
311
|
+
params = {"limit": limit}
|
312
|
+
if start_timestamp is not None:
|
313
|
+
params["start"] = start_timestamp
|
314
|
+
if end_timestamp is not None:
|
315
|
+
params["end"] = end_timestamp
|
316
|
+
|
317
|
+
response = self.client.get(
|
318
|
+
urljoin(self.base_url, f"/v1/klines/funding_rates/{symbol}"),
|
319
|
+
params=params, timeout=None
|
320
|
+
)
|
321
|
+
if response.status_code == 200:
|
322
|
+
df = DataFrame(response.json())
|
323
|
+
return df
|
324
|
+
else:
|
325
|
+
raise Exception(f"Failed to get funding rates: {response.json()}")
|
326
|
+
|
327
|
+
# -------------------- END OF KLINE SERVICE ------------------------ #
|
328
|
+
|
329
|
+
# -------------------- START OF TRADE SERVICE ------------------------ #
|
330
|
+
def list_orders(self) -> DataFrame:
|
331
|
+
response = self.client.get(
|
332
|
+
urljoin(self.base_url, "/v1/trade/orders"),
|
333
|
+
headers={"Authorization": f"Bearer {self.api_key}"},
|
334
|
+
)
|
335
|
+
return DataFrame(response.json())
|
336
|
+
|
337
|
+
def get_enabled_bots(self) -> DataFrame:
|
338
|
+
response = self.client.get(
|
339
|
+
urljoin(self.base_url, "/v1/trade/bots/enabled"),
|
340
|
+
headers={"Authorization": f"Bearer {self.api_key}"},
|
341
|
+
)
|
342
|
+
data = response.json()
|
343
|
+
return {
|
344
|
+
"bots": DataFrame(data["bots"]),
|
345
|
+
"api_keys": DataFrame(data["api_keys"])
|
346
|
+
}
|
347
|
+
|
348
|
+
# -------------------- END OF TRADE SERVICE ------------------------ #
|
349
|
+
|
350
|
+
# -------------------- START OF GOOGLE TRENDS ------------------------ #
|
351
|
+
# Get all keywords available for Google Trends
|
352
|
+
def get_google_trend_keywords_available(self) -> DataFrame:
|
353
|
+
response = self.client.get(
|
354
|
+
urljoin(self.base_url, "/v1/google/keywords"),
|
355
|
+
)
|
356
|
+
if response.status_code == 200:
|
357
|
+
df = pd.DataFrame(response.json())
|
358
|
+
return df
|
359
|
+
else:
|
360
|
+
raise Exception(f"Failed to get google trends keywords available: {response.json()}")
|
361
|
+
|
362
|
+
# Get Google Trends data for a specific keyword
|
363
|
+
def get_google_trend_keyword(self, keyword: str, timeframe: str = '8m', limit: int = 100) -> DataFrame:
|
364
|
+
"""
|
365
|
+
Retrieves Google Trends data for a specific keyword.
|
366
|
+
|
367
|
+
Args:
|
368
|
+
keyword (str): The keyword to retrieve Google Trends data for.
|
369
|
+
timeframe (str, optional): The timeframe for the data. Defaults to '8m'.
|
370
|
+
limit (int, optional): The maximum number of data points to retrieve. Defaults to 100.
|
371
|
+
|
372
|
+
Returns:
|
373
|
+
DataFrame: A pandas DataFrame containing the Google Trends data.
|
374
|
+
|
375
|
+
"""
|
376
|
+
response = self.client.get(
|
377
|
+
urljoin(self.base_url, f"/v1/google/trends/{keyword}"),
|
378
|
+
params={"timeframe": timeframe, "limit": limit}, timeout=None
|
379
|
+
)
|
380
|
+
if response.status_code == 200:
|
381
|
+
df = pd.DataFrame(response.json()['data'])
|
382
|
+
df.rename(columns={"values": "trend_val", "timestamps": "timestamp"}, inplace=True)
|
383
|
+
return df
|
384
|
+
else:
|
385
|
+
raise Exception(f"Failed to get google trends: {response.json()}")
|
386
|
+
|
387
|
+
# -------------------- END OF GOOGLE TRENDS ------------------------ #
|
388
|
+
|
389
|
+
# -------------------- START OF MARKET SERVICE ------------------------ #
|
390
|
+
def get_exchange_all_symbols(self, exchange_name: str) -> DataFrame:
|
391
|
+
"""Exchange names to be added as follows:
|
392
|
+
Binance, KuCoin, Gate.io, Bybit, Bingx, Bitget
|
393
|
+
"""
|
394
|
+
if exchange_name not in ['Binance', 'KuCoin', 'Gate.io', 'Bybit', 'Bingx', 'Bitget']:
|
395
|
+
raise ValueError("Invalid exchange name it needs to be one of the following: Binance, KuCoin, Gate.io, Bybit, Bingx, Bitget")
|
396
|
+
response = self.client.get(
|
397
|
+
urljoin(self.base_url, f"/v1/market/exchange-data/{exchange_name}"), timeout=None
|
398
|
+
)
|
399
|
+
if response.status_code == 200:
|
400
|
+
df = pd.DataFrame(response.json())
|
401
|
+
return df
|
402
|
+
else:
|
403
|
+
raise Exception(f"Failed to get exchange all symbols: {response.json()}")
|
404
|
+
|
405
|
+
def get_symbol_info_exchange(self, exchange_name: str, symbol: str, market: str = 'spot') -> DataFrame:
|
406
|
+
"""
|
407
|
+
Exchange names to be added as follows:
|
408
|
+
Binance, KuCoin, Gate.io, Bybit, Bingx, Bitget
|
409
|
+
|
410
|
+
Exchange symbols to be added as follows:
|
411
|
+
Spot -> BTC-USDT, ETH-USDT, LTC-USDT
|
412
|
+
Futures -> BTC-USDT-USDT, ETH-USDT-USDT, LTC-USDT-USDT
|
413
|
+
"""
|
414
|
+
if market == 'futures':
|
415
|
+
symbol = symbol + '-USDT'
|
416
|
+
response = self.client.get(
|
417
|
+
urljoin(self.base_url, f"/v1/market/exchange-data/{exchange_name}/{symbol}"), timeout=None
|
418
|
+
)
|
419
|
+
if response.status_code == 200:
|
420
|
+
df = pd.DataFrame(response.json())
|
421
|
+
return df
|
422
|
+
else:
|
423
|
+
raise Exception(f"Failed to get symbol info: {response.json()}")
|
424
|
+
|
425
|
+
def get_cnn_sentiment(self, indicator_name: str, start_date: str = None, end_date: str = None, limit: int = None) -> DataFrame:
|
426
|
+
"""
|
427
|
+
Retrieves Fear and Greed Index data for a specific indicator name.
|
428
|
+
|
429
|
+
Args:
|
430
|
+
indicator_name (str): The indicator name / keyword to retrieve Fear and Greed Index data for.
|
431
|
+
start_date (str, optional): The start date for the data. Defaults to None.
|
432
|
+
end_date (str, optional): The end date for the data. Defaults to None.
|
433
|
+
limit (int, optional): The maximum number of data points to retrieve. Defaults to None.
|
434
|
+
|
435
|
+
Returns:
|
436
|
+
DataFrame: A pandas DataFrame containing the Fear and Greed Index data.
|
437
|
+
|
438
|
+
"""
|
439
|
+
response = self.client.get(
|
440
|
+
urljoin(self.base_url, f"/v1/market/fng-index/{indicator_name}"),
|
441
|
+
params={"start_date": start_date, "end_date": end_date, "limit": limit}, timeout=None
|
442
|
+
)
|
443
|
+
if response.status_code == 200:
|
444
|
+
df = pd.DataFrame(response.json())
|
445
|
+
return df
|
446
|
+
else:
|
447
|
+
raise Exception(f"Failed to get cnn sentiment: {response.json()}")
|
448
|
+
|
449
|
+
def get_cnn_keywords(self) -> DataFrame:
|
450
|
+
response = self.client.get(
|
451
|
+
urljoin(self.base_url, "/v1/market/fng-index/list-indicators"), timeout=None
|
452
|
+
)
|
453
|
+
if response.status_code == 200:
|
454
|
+
df = pd.DataFrame(response.json())
|
455
|
+
df.columns = ['indicator_name']
|
456
|
+
return df
|
457
|
+
else:
|
458
|
+
raise Exception(f"Failed to get cnn keywords: {response.json()}")
|
459
|
+
|
460
|
+
def get_economic_calendar_events(self, start_timestamp: int = None, end_timestamp: int = None, currency: str = 'EUR', country_code: str = 'DE') -> DataFrame:
|
461
|
+
"""
|
462
|
+
Function returns a pandas dataframe with the economic calendar events for the specified currency and country code during given time period.
|
463
|
+
currency: EUR, CNY, NZD, AUD, USD, JPY, UAH, GBP, CHF, CAD
|
464
|
+
country_code: CA, UA, ES, US, FR, JP, IT, NZ, AU, CN, UK, CH, EMU, DE
|
465
|
+
"""
|
466
|
+
start_date = None
|
467
|
+
end_date = None
|
468
|
+
if isinstance(start_timestamp, int):
|
469
|
+
start_date = pd.to_datetime(start_timestamp, unit='s').strftime('%Y-%m-%d')
|
470
|
+
if isinstance(end_timestamp, int):
|
471
|
+
end_date = pd.to_datetime(end_timestamp, unit='s').strftime('%Y-%m-%d')
|
472
|
+
|
473
|
+
params = {
|
474
|
+
"start_date": start_date,
|
475
|
+
"end_date": end_date,
|
476
|
+
"currency": currency,
|
477
|
+
"country_code": country_code
|
478
|
+
}
|
479
|
+
response = self.client.get(
|
480
|
+
urljoin(self.base_url, f"/v1/market/ecocal"), timeout=None, params=params
|
481
|
+
)
|
482
|
+
if response.status_code == 200:
|
483
|
+
df = pd.DataFrame(response.json())
|
484
|
+
return df
|
485
|
+
else:
|
486
|
+
raise Exception(f"Failed to get economic calendar events: {response.json()}")
|
487
|
+
|
488
|
+
def get_bitstamp_symbols(self) -> List[str]:
|
489
|
+
response = self.client.get(
|
490
|
+
urljoin(self.base_url, f"/v1/market/bitstamp/symbols"), timeout=None
|
491
|
+
)
|
492
|
+
if response.status_code == 200:
|
493
|
+
symbols = response.json()
|
494
|
+
# convert all symbols to uppercase
|
495
|
+
symbols = [symbol.upper() for symbol in symbols]
|
496
|
+
return symbols
|
497
|
+
else:
|
498
|
+
raise Exception(f"Failed to get bitstamp symbols: {response.json()}")
|
499
|
+
|
500
|
+
def get_bitstamp_ohlcv_spot(self, symbol: str, interval: str, limit: int, start_timestamp: int = None, end_timestamp: int = None) -> DataFrame:
|
501
|
+
"""
|
502
|
+
get: unix_time + OHLCV data , as pandas dataframe
|
503
|
+
symbol have to be in capital case e.g. (BTCUSDT)
|
504
|
+
interval: 15m, 30m, 1h, 4h, 1d
|
505
|
+
"""
|
506
|
+
params = {"limit": limit}
|
507
|
+
if start_timestamp is not None:
|
508
|
+
params["start"] = start_timestamp
|
509
|
+
if end_timestamp is not None:
|
510
|
+
params["end"] = end_timestamp
|
511
|
+
|
512
|
+
response = self.client.get(
|
513
|
+
urljoin(self.base_url, f"/v1/market/bitstamp/{symbol}/{interval}"),
|
514
|
+
params=params, timeout=None
|
515
|
+
)
|
516
|
+
if response.status_code == 200:
|
517
|
+
df = DataFrame(response.json())
|
518
|
+
df['timestamp'] = pd.to_datetime(df['timestamp'])
|
519
|
+
df['timestamp'] = df['timestamp'].astype("int64") // 10 ** 9 # use int64 instead of int for windows
|
520
|
+
return df
|
521
|
+
else:
|
522
|
+
raise Exception(f"Failed to get bitstamp ohlcv spot: {response.json()}")
|
523
|
+
|
524
|
+
# -------------------- END OF MARKET SERVICE ------------------------ #
|
525
|
+
|
526
|
+
# -------------------- START OF MARKETCAP METRICS SERVICE ------------------------ #
|
527
|
+
# Get historical marketcap rankings for coins
|
528
|
+
def get_historical_marketcap_rankings(self, start_timestamp: int = None, end_timestamp: int = None, interval: str = "1d", market: str = None, exchange_name: str = None) -> dict:
|
529
|
+
"""
|
530
|
+
Get historical marketcap rankings and exchange availability for cryptocurrencies.
|
531
|
+
|
532
|
+
Args:
|
533
|
+
start_timestamp (int, optional): Start timestamp for the data range
|
534
|
+
end_timestamp (int, optional): End timestamp for the data range
|
535
|
+
interval (str, optional): Time interval between data points (e.g. "1d")
|
536
|
+
market (str, optional): Market type (e.g. "futures")
|
537
|
+
exchange_name (str, optional): Exchange name (e.g. "binance", "kucoin", "gate.io", "bybit", "bingx", "bitget")
|
538
|
+
|
539
|
+
Returns:
|
540
|
+
DataFrame: A pandas DataFrame containing the historical marketcap rankings
|
541
|
+
"""
|
542
|
+
response = self.client.get(
|
543
|
+
urljoin(self.base_url, f"/v1/metrics/marketcap/symbols"),
|
544
|
+
timeout=None,
|
545
|
+
params={
|
546
|
+
"start_timestamp": start_timestamp,
|
547
|
+
"end_timestamp": end_timestamp,
|
548
|
+
"interval": interval,
|
549
|
+
"market": market,
|
550
|
+
"exchange": exchange_name
|
551
|
+
}
|
552
|
+
)
|
553
|
+
|
554
|
+
data = response.json()
|
555
|
+
# Process rankings data
|
556
|
+
rankings_df = pd.DataFrame(response.json())
|
557
|
+
rankings_df.rename(columns={rankings_df.columns[0]: 'timestamp'}, inplace=True)
|
558
|
+
rankings_df['timestamp'] = pd.to_datetime(rankings_df['timestamp']).astype("int64") // 10 ** 9
|
559
|
+
return rankings_df
|
560
|
+
|
561
|
+
def get_historical_marketcap_values_for_rankings(self, start_timestamp: int = None, end_timestamp: int = None) -> DataFrame:
|
562
|
+
"""
|
563
|
+
Get historical marketcap values for rankings
|
564
|
+
"""
|
565
|
+
response = self.client.get(
|
566
|
+
urljoin(self.base_url, f"/v1/metrics/marketcap"), timeout=None, params={"start_timestamp": start_timestamp, "end_timestamp": end_timestamp}
|
567
|
+
)
|
568
|
+
if response.status_code == 200:
|
569
|
+
df = pd.DataFrame(response.json())
|
570
|
+
df.rename(columns={df.columns[0]: 'timestamp'}, inplace=True)
|
571
|
+
df['timestamp'] = pd.to_datetime(df['timestamp']).astype("int64") // 10 ** 9
|
572
|
+
return df
|
573
|
+
else:
|
574
|
+
raise Exception(f"Failed to get historical marketcap values for rankings: {response.json()}")
|
575
|
+
|
576
|
+
def get_marketcap_indicator_values(self, symbol: str,market: str, period: int, indicator_name: str, timestamp:int = None):
|
577
|
+
"""
|
578
|
+
Get marketcap indicator values for a specific indicator name and timestamp
|
579
|
+
Indicator names to be added as follows:
|
580
|
+
ker, sma
|
581
|
+
"""
|
582
|
+
response = self.client.get(
|
583
|
+
urljoin(self.base_url, f"/v1/metrics/{indicator_name}/{symbol}"), timeout=None, params={"market": market, "period": period, "timestamp": timestamp}
|
584
|
+
)
|
585
|
+
if response.status_code == 200:
|
586
|
+
return response.json()
|
587
|
+
else:
|
588
|
+
raise Exception(f"Failed to get marketcap indicator values: {response.json()}")
|
589
|
+
|
590
|
+
def get_exchanges_for_mc_symbol(self, symbol: str, market: str, interval: str = '1d', start_timestamp: int = None, end_timestamp: int = None, status: str = 'ACTIVE', quote_currency: str = 'USDT') -> DataFrame:
|
591
|
+
"""
|
592
|
+
status: 'ACTIVE', 'RETIRED'
|
593
|
+
quote_currency: USDT, USDC (can be retrieved from get_unique_quote_currencies())
|
594
|
+
"""
|
595
|
+
|
596
|
+
if start_timestamp is None:
|
597
|
+
start_timestamp = int((datetime.now() - timedelta(days=7, hours=0, minutes=0, seconds=0)).timestamp())
|
598
|
+
if end_timestamp is None:
|
599
|
+
end_timestamp = int((datetime.now() - timedelta(days=0, hours=0, minutes=0, seconds=0)).timestamp())
|
600
|
+
|
601
|
+
params = {
|
602
|
+
"interval": interval,
|
603
|
+
"start_timestamp": start_timestamp,
|
604
|
+
"end_timestamp": end_timestamp,
|
605
|
+
"status": status,
|
606
|
+
"quote_currency": quote_currency
|
607
|
+
}
|
608
|
+
|
609
|
+
response = self.client.get(
|
610
|
+
urljoin(self.base_url, f"/v1/metrics/available_exchanges/{market}/{symbol}"), timeout=None, params=params
|
611
|
+
)
|
612
|
+
if response.status_code == 200:
|
613
|
+
result = response.json()
|
614
|
+
processed_results = []
|
615
|
+
for row in result:
|
616
|
+
data = {'timestamp': row['timestamp']}
|
617
|
+
data.update(row['exchanges'])
|
618
|
+
processed_results.append(data)
|
619
|
+
df = pd.DataFrame(processed_results)
|
620
|
+
cols = ['timestamp'] + sorted([col for col in df.columns if col != 'timestamp'])
|
621
|
+
df = df[cols]
|
622
|
+
df['timestamp'] = pd.to_datetime(df['timestamp']).astype("int64") // 10 ** 9
|
623
|
+
df = df.astype(int)
|
624
|
+
return df
|
625
|
+
else:
|
626
|
+
raise Exception(f"Failed to get exchanges for mc symbol: {response.json()}")
|
627
|
+
|
628
|
+
def get_marketcap_ranking_with_ohlcv(self, market: str, timeframe: str, top_n: int, ohlcv_limit: int, timestamp: int = int((datetime.now() - timedelta(days=1, hours=0, minutes=0, seconds=0)).timestamp())) -> DataFrame:
|
629
|
+
params = {"market": market, "timeframe": timeframe, "top_n": top_n, "ohlcv_limit": ohlcv_limit, "timestamp": timestamp}
|
630
|
+
print(params)
|
631
|
+
response = self.client.get(
|
632
|
+
urljoin(self.base_url, f"/v1/metrics/marketcap/symbols/ohlcv"), timeout=None, params=params
|
633
|
+
)
|
634
|
+
if response.status_code == 200:
|
635
|
+
df = pd.DataFrame(response.json())
|
636
|
+
return df
|
637
|
+
else:
|
638
|
+
raise Exception(f"Failed to get marketcap ranking with ohlcv: {response.json()}")
|
639
|
+
|
640
|
+
def get_stable_or_wrapped_tokens(self, token_type: str = 'stable') -> DataFrame:
|
641
|
+
"""
|
642
|
+
token_type: stable or wrapped
|
643
|
+
"""
|
644
|
+
if token_type not in ['stable', 'wrapped']:
|
645
|
+
raise ValueError("token_type must be either stable or wrapped")
|
646
|
+
response = self.client.get(
|
647
|
+
urljoin(self.base_url, f"/v1/metrics/tokens/{token_type}"), timeout=None
|
648
|
+
)
|
649
|
+
if response.status_code == 200:
|
650
|
+
df = pd.DataFrame(response.json())
|
651
|
+
return df
|
652
|
+
else:
|
653
|
+
raise Exception(f"Failed to get stable or wrapped tokens: {response.json()}")
|
654
|
+
|
655
|
+
def get_exchanges_mapping_for_specific_symbol(self, market: str, symbol: str) -> DataFrame:
|
656
|
+
"""
|
657
|
+
Get the exchanges for a specific symbol and market
|
658
|
+
"""
|
659
|
+
response = self.client.get(
|
660
|
+
urljoin(self.base_url, f"/v1/metrics/markets/{market}/{symbol}"), timeout=None
|
661
|
+
)
|
662
|
+
if response.status_code == 200:
|
663
|
+
df = pd.DataFrame(response.json())
|
664
|
+
return df
|
665
|
+
else:
|
666
|
+
raise Exception(f"Failed to get exchange mappings: {response.json()}")
|
667
|
+
|
668
|
+
def get_exchange_mappings_for_specific_exchange(self,market: str, exchange_name: str) -> DataFrame:
|
669
|
+
"""
|
670
|
+
Get the exchanges for a specific exchange and market
|
671
|
+
exchange_name: binance, kucoin, gate.io, bybit, bingx, bitget (lowercase)
|
672
|
+
market: spot, futures
|
673
|
+
"""
|
674
|
+
params = {
|
675
|
+
"exchange_name": exchange_name.lower()
|
676
|
+
}
|
677
|
+
response = self.client.get(
|
678
|
+
urljoin(self.base_url, f"/v1/metrics/exchange_mappings/{market}"), timeout=None, params=params
|
679
|
+
)
|
680
|
+
if response.status_code == 200:
|
681
|
+
df = pd.DataFrame(response.json())
|
682
|
+
return df
|
683
|
+
else:
|
684
|
+
raise Exception(f"Failed to get exchange mappings: {response.json()}")
|
685
|
+
|
686
|
+
def get_unique_quote_currencies(self, market: str) -> DataFrame:
|
687
|
+
response = self.client.get(
|
688
|
+
urljoin(self.base_url, f"/v1/metrics/quote_currencies/{market}"), timeout=None
|
689
|
+
)
|
690
|
+
if response.status_code == 200:
|
691
|
+
df = pd.DataFrame(response.json())
|
692
|
+
return df
|
693
|
+
else:
|
694
|
+
raise Exception(f"Failed to get unique quote currencies: {response.json()}")
|
695
|
+
|
696
|
+
def get_exchanges_list_for_specific_market(self, market: str) -> List[str]:
|
697
|
+
"""
|
698
|
+
Get the list of exchanges for a specific market
|
699
|
+
"""
|
700
|
+
response = self.client.get(
|
701
|
+
urljoin(self.base_url, f"/v1/metrics/exchange_list/{market}"), timeout=None
|
702
|
+
)
|
703
|
+
if response.status_code == 200:
|
704
|
+
return response.json()
|
705
|
+
else:
|
706
|
+
raise Exception(f"Failed to get exchanges list: {response.json()}")
|
707
|
+
|
708
|
+
# -------------------- END OF MARKETCAP METRICS SERVICE ------------------------ #
|
709
|
+
|
710
|
+
def verify(self, token: Union[str, None] = None) -> bool:
|
711
|
+
if token is None:
|
712
|
+
token = self.jwt
|
713
|
+
response = self.client.get(
|
714
|
+
self.base_url + "/v1/auth/verify",
|
715
|
+
headers={"Authorization": f"Bearer {token}"},
|
716
|
+
)
|
717
|
+
response.raise_for_status()
|
718
|
+
try:
|
719
|
+
res = response.json()
|
720
|
+
return res['result']['data']['json']
|
721
|
+
except:
|
722
|
+
return None
|