cryptodatapy 0.2.15__py3-none-any.whl → 0.2.17__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.
- cryptodatapy/conf/fields.csv +1 -1
- cryptodatapy/extract/data_vendors/coinmetrics_api.py +32 -32
- cryptodatapy/extract/data_vendors/cryptocompare_api.py +159 -204
- cryptodatapy/extract/data_vendors/datavendor.py +21 -0
- cryptodatapy/extract/data_vendors/tiingo_api.py +5 -1
- cryptodatapy/extract/libraries/ccxt_api.py +215 -209
- cryptodatapy/transform/convertparams.py +35 -57
- cryptodatapy/util/datacredentials.py +18 -3
- {cryptodatapy-0.2.15.dist-info → cryptodatapy-0.2.17.dist-info}/METADATA +1 -1
- {cryptodatapy-0.2.15.dist-info → cryptodatapy-0.2.17.dist-info}/RECORD +12 -12
- {cryptodatapy-0.2.15.dist-info → cryptodatapy-0.2.17.dist-info}/LICENSE +0 -0
- {cryptodatapy-0.2.15.dist-info → cryptodatapy-0.2.17.dist-info}/WHEEL +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
import logging
|
2
2
|
from time import sleep
|
3
|
-
from typing import Dict, Optional, Union, Any
|
3
|
+
from typing import Dict, Optional, Union, Any, List
|
4
4
|
|
5
5
|
import pandas as pd
|
6
6
|
|
@@ -13,13 +13,6 @@ from cryptodatapy.util.datacredentials import DataCredentials
|
|
13
13
|
# data credentials
|
14
14
|
data_cred = DataCredentials()
|
15
15
|
|
16
|
-
# url endpoints
|
17
|
-
urls = {'exchanges_info': 'exchanges/general', 'indexes_info': 'index/list', 'assets_info': 'all/coinlist',
|
18
|
-
'markets_info': 'v2/cccagg/pairs', 'on-chain_tickers_info': 'blockchain/list',
|
19
|
-
'on-chain_info': 'blockchain/latest?fsym=BTC', 'social_info': 'social/coin/histo/day',
|
20
|
-
'news': 'v2/news/?lang=EN', 'news_sources': 'news/feeds', 'rate_limit_info': 'rate/limit',
|
21
|
-
'top_mkt_cap_info': 'top/mktcapfull?', 'indexes': 'index/'}
|
22
|
-
|
23
16
|
|
24
17
|
class CryptoCompare(DataVendor):
|
25
18
|
"""
|
@@ -28,15 +21,16 @@ class CryptoCompare(DataVendor):
|
|
28
21
|
|
29
22
|
def __init__(
|
30
23
|
self,
|
31
|
-
categories=
|
24
|
+
categories: List[str] = ['crypto'],
|
32
25
|
exchanges: Optional[list[str]] = None,
|
33
26
|
indexes: Optional[list[str]] = None,
|
34
27
|
assets: Optional[list[str]] = None,
|
35
28
|
markets: Optional[list[str]] = None,
|
36
|
-
market_types=
|
29
|
+
market_types: List[str] = ['spot'],
|
37
30
|
fields: Optional[list[str]] = None,
|
38
|
-
frequencies=
|
31
|
+
frequencies: List[str] = ['1min', '1h', 'd'],
|
39
32
|
base_url: str = data_cred.cryptocompare_base_url,
|
33
|
+
api_endpoints: Dict[str, str] = data_cred.cryptomcompare_endpoints,
|
40
34
|
api_key: str = data_cred.cryptocompare_api_key,
|
41
35
|
max_obs_per_call: int = 2000,
|
42
36
|
rate_limit: Optional[pd.DataFrame] = None
|
@@ -65,6 +59,9 @@ class CryptoCompare(DataVendor):
|
|
65
59
|
'8h', 'd', 'w', 'm']
|
66
60
|
base_url: str
|
67
61
|
Base url used for GET requests. If not provided, default is set to base_url stored in DataCredentials.
|
62
|
+
api_endpoints: dict, optional, default None
|
63
|
+
Dictionary with available API endpoints. If not provided, default is set to api_endpoints stored in
|
64
|
+
DataCredentials.
|
68
65
|
api_key: str
|
69
66
|
Api key, e.g. 'dcf13983adf7dfa79a0dfa35adf'. If not provided, default is set to
|
70
67
|
api_key stored in DataCredentials.
|
@@ -74,31 +71,17 @@ class CryptoCompare(DataVendor):
|
|
74
71
|
rate_limit: pd.DataFrame, optional, Default None
|
75
72
|
Number of API calls made and left, by time frequency.
|
76
73
|
"""
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
self.frequencies = ['1min', '1h', 'd']
|
82
|
-
if market_types is None:
|
83
|
-
self.market_types = ['spot']
|
84
|
-
if categories is None:
|
85
|
-
self.categories = ['crypto']
|
74
|
+
super().__init__(
|
75
|
+
categories, exchanges, indexes, assets, markets, market_types,
|
76
|
+
fields, frequencies, base_url, api_endpoints, api_key, max_obs_per_call, rate_limit
|
77
|
+
)
|
86
78
|
if api_key is None:
|
87
79
|
raise TypeError("Set your CryptoCompare api key in environment variables as 'CRYPTOCOMPARE_API_KEY' or "
|
88
80
|
"add it as an argument when instantiating the class. To get an api key, visit: "
|
89
81
|
"https://min-api.cryptocompare.com/")
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
self.indexes = self.get_indexes_info(as_list=True)
|
94
|
-
if assets is None:
|
95
|
-
self.assets = self.get_assets_info(as_list=True)
|
96
|
-
if markets is None:
|
97
|
-
self.markets = self.get_markets_info(as_list=True)
|
98
|
-
if fields is None:
|
99
|
-
self.fields = self.get_fields_info(data_type=None)
|
100
|
-
if rate_limit is None:
|
101
|
-
self.rate_limit = self.get_rate_limit_info()
|
82
|
+
self.data_req = None
|
83
|
+
self.data_resp = None
|
84
|
+
self.data = pd.DataFrame()
|
102
85
|
|
103
86
|
def req_meta(self, info_type: str) -> Dict[str, Any]:
|
104
87
|
"""
|
@@ -116,7 +99,9 @@ class CryptoCompare(DataVendor):
|
|
116
99
|
meta: dictionary
|
117
100
|
Metadata in JSON format.
|
118
101
|
"""
|
119
|
-
|
102
|
+
self.data_resp = DataRequest().get_req(url=self.base_url + self.api_endpoints[info_type],
|
103
|
+
params={'api_key': self.api_key})
|
104
|
+
return self.data_resp
|
120
105
|
|
121
106
|
def get_exchanges_info(self, as_list: bool = False) -> Union[list[str], pd.DataFrame]:
|
122
107
|
"""
|
@@ -133,11 +118,11 @@ class CryptoCompare(DataVendor):
|
|
133
118
|
List or dataframe with info on available indexes.
|
134
119
|
"""
|
135
120
|
# data req
|
136
|
-
|
121
|
+
self.req_meta(info_type='exchanges_info')
|
137
122
|
# wrangle data resp
|
138
|
-
|
123
|
+
self.exchanges = WrangleInfo(self.data_resp).cc_exch_info(as_list=as_list)
|
139
124
|
|
140
|
-
return
|
125
|
+
return self.exchanges
|
141
126
|
|
142
127
|
def get_indexes_info(self, as_list: bool = False) -> Union[list[str], pd.DataFrame]:
|
143
128
|
"""
|
@@ -154,11 +139,11 @@ class CryptoCompare(DataVendor):
|
|
154
139
|
List or dataframe with info on available indexes.
|
155
140
|
"""
|
156
141
|
# data req
|
157
|
-
|
142
|
+
self.req_meta(info_type='indexes_info')
|
158
143
|
# wrangle data resp
|
159
|
-
indexes = WrangleInfo(data_resp).cc_indexes_info(as_list=as_list)
|
144
|
+
self.indexes = WrangleInfo(self.data_resp).cc_indexes_info(as_list=as_list)
|
160
145
|
|
161
|
-
return indexes
|
146
|
+
return self.indexes
|
162
147
|
|
163
148
|
def get_assets_info(self, as_list: bool = False) -> Union[list[str], pd.DataFrame]:
|
164
149
|
"""
|
@@ -175,11 +160,11 @@ class CryptoCompare(DataVendor):
|
|
175
160
|
List or dataframe with info on available assets.
|
176
161
|
"""
|
177
162
|
# data req
|
178
|
-
|
163
|
+
self.req_meta(info_type='assets_info')
|
179
164
|
# wrangle data resp
|
180
|
-
assets = WrangleInfo(data_resp).cc_assets_info(as_list=as_list)
|
165
|
+
self.assets = WrangleInfo(self.data_resp).cc_assets_info(as_list=as_list)
|
181
166
|
|
182
|
-
return assets
|
167
|
+
return self.assets
|
183
168
|
|
184
169
|
def get_markets_info(self, as_list: bool = False) -> Union[list[str], dict[str]]:
|
185
170
|
"""
|
@@ -196,11 +181,11 @@ class CryptoCompare(DataVendor):
|
|
196
181
|
Dictionary or list with info on available markets.
|
197
182
|
"""
|
198
183
|
# data req
|
199
|
-
|
184
|
+
self.req_meta(info_type='markets_info')
|
200
185
|
# wrangle data resp
|
201
|
-
|
186
|
+
self.markets = WrangleInfo(self.data_resp).cc_mkts_info(as_list=as_list)
|
202
187
|
|
203
|
-
return
|
188
|
+
return self.markets
|
204
189
|
|
205
190
|
def get_onchain_tickers_info(self, as_list: bool = False) -> Union[list[str], pd.DataFrame]:
|
206
191
|
"""
|
@@ -217,18 +202,12 @@ class CryptoCompare(DataVendor):
|
|
217
202
|
list or dataframe with info on tickers with available on-chain data.
|
218
203
|
"""
|
219
204
|
# data req
|
220
|
-
|
205
|
+
self.req_meta(info_type='on-chain_tickers_info')
|
221
206
|
# wrangle data resp
|
222
|
-
tickers = WrangleInfo(data_resp).cc_onchain_tickers_info(as_list=as_list)
|
207
|
+
tickers = WrangleInfo(self.data_resp).cc_onchain_tickers_info(as_list=as_list)
|
223
208
|
|
224
209
|
return tickers
|
225
210
|
|
226
|
-
def req_onchain(self) -> Dict[str, Any]:
|
227
|
-
"""
|
228
|
-
Get request for on-chain info.
|
229
|
-
"""
|
230
|
-
return DataRequest().get_req(url=self.base_url + urls['on-chain_info'], params={'api_key': self.api_key})
|
231
|
-
|
232
211
|
def get_onchain_info(self) -> list[str]:
|
233
212
|
"""
|
234
213
|
Get on-chain fields info.
|
@@ -239,9 +218,9 @@ class CryptoCompare(DataVendor):
|
|
239
218
|
List of available on-chain fields.
|
240
219
|
"""
|
241
220
|
# data req
|
242
|
-
|
221
|
+
self.req_meta(info_type='on-chain_info')
|
243
222
|
# wrangle data resp
|
244
|
-
onchain_fields = WrangleInfo(data_resp).cc_onchain_info()
|
223
|
+
onchain_fields = WrangleInfo(self.data_resp).cc_onchain_info()
|
245
224
|
|
246
225
|
return onchain_fields
|
247
226
|
|
@@ -255,9 +234,9 @@ class CryptoCompare(DataVendor):
|
|
255
234
|
List of available social fields.
|
256
235
|
"""
|
257
236
|
# data req
|
258
|
-
|
237
|
+
self.req_meta(info_type='social_info')
|
259
238
|
# wrangle data resp
|
260
|
-
social_fields = WrangleInfo(data_resp).cc_social_info()
|
239
|
+
social_fields = WrangleInfo(self.data_resp).cc_social_info()
|
261
240
|
|
262
241
|
return social_fields
|
263
242
|
|
@@ -277,24 +256,26 @@ class CryptoCompare(DataVendor):
|
|
277
256
|
"""
|
278
257
|
ohlcv_list = ['open', 'high', 'low', 'close', 'volumefrom']
|
279
258
|
|
280
|
-
# fields
|
259
|
+
# fields
|
281
260
|
if data_type == 'market':
|
282
|
-
|
261
|
+
self.fields = ohlcv_list
|
283
262
|
elif data_type == 'on-chain':
|
284
|
-
|
263
|
+
self.fields = self.get_onchain_info()
|
285
264
|
elif data_type == 'off-chain':
|
286
|
-
|
265
|
+
self.fields = self.get_social_info()
|
287
266
|
else:
|
288
|
-
|
267
|
+
self.fields = ohlcv_list + self.get_onchain_info() + self.get_social_info()
|
289
268
|
|
290
|
-
return
|
269
|
+
return self.fields
|
291
270
|
|
292
271
|
def req_rate_limit(self) -> Dict[str, Any]:
|
293
272
|
"""
|
294
273
|
Get request for rate limit info.
|
295
274
|
"""
|
296
|
-
|
297
|
-
|
275
|
+
self.data_resp = DataRequest().get_req(url=self.base_url.replace('data', 'stats') +
|
276
|
+
self.api_endpoints['rate_limit_info'],
|
277
|
+
params={'api_key': self.api_key})
|
278
|
+
return self.data_resp
|
298
279
|
|
299
280
|
def get_rate_limit_info(self) -> pd.DataFrame:
|
300
281
|
"""
|
@@ -306,11 +287,11 @@ class CryptoCompare(DataVendor):
|
|
306
287
|
DataFrame with API calls left and made by period (hour, day, month).
|
307
288
|
"""
|
308
289
|
# data req
|
309
|
-
|
290
|
+
self.req_rate_limit()
|
310
291
|
# wrangle data resp
|
311
|
-
rate_limit = WrangleInfo(data_resp).cc_rate_limit_info()
|
292
|
+
self.rate_limit = WrangleInfo(self.data_resp).cc_rate_limit_info()
|
312
293
|
|
313
|
-
return rate_limit
|
294
|
+
return self.rate_limit
|
314
295
|
|
315
296
|
def get_news(self) -> pd.DataFrame:
|
316
297
|
"""
|
@@ -322,9 +303,9 @@ class CryptoCompare(DataVendor):
|
|
322
303
|
News articles from various sources with title, source, body, ...
|
323
304
|
"""
|
324
305
|
# data req
|
325
|
-
|
306
|
+
self.req_meta(info_type='news')
|
326
307
|
# wrangle data resp
|
327
|
-
news = WrangleInfo(data_resp).cc_news()
|
308
|
+
news = WrangleInfo(self.data_resp).cc_news()
|
328
309
|
|
329
310
|
return news
|
330
311
|
|
@@ -338,9 +319,9 @@ class CryptoCompare(DataVendor):
|
|
338
319
|
News source info.
|
339
320
|
"""
|
340
321
|
# data req
|
341
|
-
|
322
|
+
self.req_meta(info_type='news_sources')
|
342
323
|
# wrangle data resp
|
343
|
-
news_sources = WrangleInfo(data_resp).cc_news_sources()
|
324
|
+
news_sources = WrangleInfo(self.data_resp).cc_news_sources()
|
344
325
|
|
345
326
|
return news_sources
|
346
327
|
|
@@ -348,12 +329,15 @@ class CryptoCompare(DataVendor):
|
|
348
329
|
"""
|
349
330
|
Get request for top mkt cap coins info.
|
350
331
|
"""
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
332
|
+
self.data_resp = DataRequest().get_req(
|
333
|
+
url=self.base_url + self.api_endpoints['top_mkt_cap_info'],
|
334
|
+
params={
|
335
|
+
'limit': n,
|
336
|
+
'tsym': 'USD',
|
337
|
+
'api_key': self.api_key
|
338
|
+
}
|
339
|
+
)
|
340
|
+
return self.data_resp
|
357
341
|
|
358
342
|
def get_top_mkt_cap_info(self, n: int = 100) -> list[str]:
|
359
343
|
"""
|
@@ -374,13 +358,13 @@ class CryptoCompare(DataVendor):
|
|
374
358
|
raise ValueError("Maximum number of assets is 100. Change n parameter and try again.")
|
375
359
|
|
376
360
|
# date req
|
377
|
-
|
361
|
+
self.req_top_mkt_cap(n=n)
|
378
362
|
# wrangle data resp
|
379
|
-
tickers = WrangleInfo(data_resp).cc_top_mkt_cap_info()
|
363
|
+
tickers = WrangleInfo(self.data_resp).cc_top_mkt_cap_info()
|
380
364
|
|
381
365
|
return tickers
|
382
366
|
|
383
|
-
def set_urls_params(self, data_req: DataRequest, data_type: str, ticker: str) -> Dict[str, Union[str,
|
367
|
+
def set_urls_params(self, data_req: DataRequest, data_type: str, ticker: str) -> Dict[str, Union[str, Any]]:
|
384
368
|
"""
|
385
369
|
Sets url and params for get request.
|
386
370
|
|
@@ -400,25 +384,26 @@ class CryptoCompare(DataVendor):
|
|
400
384
|
|
401
385
|
"""
|
402
386
|
# convert data req params
|
403
|
-
|
387
|
+
self.data_req = ConvertParams(data_req).to_cryptocompare()
|
404
388
|
|
405
389
|
# set params
|
406
390
|
if data_type == 'indexes':
|
407
|
-
url = self.base_url +
|
391
|
+
url = (self.base_url + self.api_endpoints['indexes'] + self.data_req.source_freq[:5] + "/" +
|
392
|
+
self.data_req.source_freq[5:])
|
408
393
|
params = {
|
409
394
|
'indexName': ticker,
|
410
|
-
'toTs':
|
395
|
+
'toTs': self.data_req.source_end_date,
|
411
396
|
'limit': self.max_obs_per_call,
|
412
397
|
'api_key': self.api_key
|
413
398
|
}
|
414
399
|
elif data_type == 'ohlcv':
|
415
|
-
url = self.base_url + f"v2/{
|
400
|
+
url = self.base_url + f"v2/{self.data_req.source_freq}"
|
416
401
|
params = {
|
417
402
|
'fsym': ticker,
|
418
|
-
'tsym':
|
403
|
+
'tsym': self.data_req.quote_ccy,
|
419
404
|
'limit': self.max_obs_per_call,
|
420
|
-
'e':
|
421
|
-
'toTs':
|
405
|
+
'e': self.data_req.exch,
|
406
|
+
'toTs': self.data_req.source_end_date,
|
422
407
|
'api_key': self.api_key
|
423
408
|
}
|
424
409
|
|
@@ -427,16 +412,16 @@ class CryptoCompare(DataVendor):
|
|
427
412
|
params = {
|
428
413
|
'fsym': ticker,
|
429
414
|
'limit': self.max_obs_per_call,
|
430
|
-
'toTs':
|
415
|
+
'toTs': self.data_req.source_end_date,
|
431
416
|
'api_key': self.api_key
|
432
417
|
}
|
433
418
|
|
434
419
|
elif data_type == 'social':
|
435
|
-
url = self.base_url + "social/coin/" +
|
420
|
+
url = self.base_url + "social/coin/" + self.data_req.source_freq[:5] + '/' + self.data_req.source_freq[5:]
|
436
421
|
params = {
|
437
422
|
'coinId': int(self.get_assets_info().loc[ticker.upper(), 'Id']),
|
438
423
|
'limit': self.max_obs_per_call,
|
439
|
-
'toTs':
|
424
|
+
'toTs': self.data_req.source_end_date,
|
440
425
|
'api_key': self.api_key
|
441
426
|
}
|
442
427
|
|
@@ -444,7 +429,7 @@ class CryptoCompare(DataVendor):
|
|
444
429
|
url = self.base_url
|
445
430
|
params = {}
|
446
431
|
|
447
|
-
return
|
432
|
+
return url, params
|
448
433
|
|
449
434
|
def req_data(self, data_req: DataRequest, data_type: str, ticker: str) -> Dict[str, Any]:
|
450
435
|
"""
|
@@ -465,13 +450,12 @@ class CryptoCompare(DataVendor):
|
|
465
450
|
Data response in JSON format.
|
466
451
|
"""
|
467
452
|
# set params
|
468
|
-
|
469
|
-
url, params = urls_params['url'], urls_params['params']
|
453
|
+
url, params = self.set_urls_params(data_req, data_type, ticker)
|
470
454
|
|
471
455
|
# data req
|
472
|
-
|
456
|
+
self.data_req = data_req.get_req(url=url, params=params)
|
473
457
|
|
474
|
-
return
|
458
|
+
return self.data_req
|
475
459
|
|
476
460
|
def get_all_data_hist(self, data_req: DataRequest, data_type: str, ticker: str) -> pd.DataFrame:
|
477
461
|
"""
|
@@ -494,11 +478,10 @@ class CryptoCompare(DataVendor):
|
|
494
478
|
|
495
479
|
"""
|
496
480
|
# convert data req params
|
497
|
-
|
481
|
+
self.data_req = ConvertParams(data_req).to_cryptocompare()
|
498
482
|
|
499
483
|
# set params
|
500
|
-
|
501
|
-
url, params = urls_params['url'], urls_params['params']
|
484
|
+
url, params = self.set_urls_params(data_req, data_type, ticker)
|
502
485
|
|
503
486
|
# create empty df
|
504
487
|
df = pd.DataFrame()
|
@@ -509,26 +492,25 @@ class CryptoCompare(DataVendor):
|
|
509
492
|
while missing_vals:
|
510
493
|
|
511
494
|
# data req
|
512
|
-
data_resp = DataRequest().get_req(url=url, params=params)
|
495
|
+
self.data_resp = DataRequest().get_req(url=url, params=params)
|
513
496
|
|
514
497
|
# add data resp to df
|
515
|
-
if data_resp:
|
498
|
+
if self.data_resp:
|
516
499
|
if data_type == 'indexes' or data_type == 'social':
|
517
|
-
df1 = pd.DataFrame(data_resp['Data'])
|
500
|
+
df1 = pd.DataFrame(self.data_resp['Data'])
|
518
501
|
else:
|
519
|
-
df1 = pd.DataFrame(data_resp['Data']['Data'])
|
502
|
+
df1 = pd.DataFrame(self.data_resp['Data']['Data'])
|
520
503
|
df = pd.concat([df, df1]) # add data to empty df
|
521
504
|
|
522
505
|
# check if all data has been extracted
|
523
|
-
if len(df1) < (self.max_obs_per_call - 1) or df1.time[0] <=
|
506
|
+
if len(df1) < (self.max_obs_per_call - 1) or df1.time[0] <= self.data_req.source_start_date or \
|
524
507
|
all(df1.drop(columns=['time']).iloc[0] == 0) or \
|
525
508
|
all(df1.drop(columns=['time']).iloc[0].astype(str) == 'nan'):
|
526
509
|
missing_vals = False
|
527
510
|
# reset end date and pause before calling API again
|
528
511
|
else:
|
529
|
-
# change end date
|
530
512
|
params['toTs'] = df1.time[0]
|
531
|
-
sleep(
|
513
|
+
sleep(self.data_req.pause)
|
532
514
|
|
533
515
|
return df
|
534
516
|
|
@@ -549,7 +531,6 @@ class CryptoCompare(DataVendor):
|
|
549
531
|
df: pd.DataFrame
|
550
532
|
Wrangled dataframe with DatetimeIndex and values in tidy format.
|
551
533
|
"""
|
552
|
-
|
553
534
|
return WrangleData(data_req, data_resp).cryptocompare()
|
554
535
|
|
555
536
|
def get_tidy_data(self, data_req: DataRequest, data_type: str, ticker: str) -> pd.DataFrame:
|
@@ -595,18 +576,18 @@ class CryptoCompare(DataVendor):
|
|
595
576
|
Dataframe with DatetimeIndex (level 0), ticker (level 1) and values for fields (cols), in tidy data format.
|
596
577
|
"""
|
597
578
|
# convert data request parameters to CryptoCompare format
|
598
|
-
|
579
|
+
self.data_req = ConvertParams(data_req).to_cryptocompare()
|
599
580
|
|
600
581
|
# empty df to add data
|
601
582
|
df = pd.DataFrame()
|
602
583
|
|
603
584
|
# loop through tickers
|
604
|
-
for ticker in
|
585
|
+
for ticker in self.data_req.source_tickers:
|
605
586
|
|
606
587
|
try:
|
607
588
|
df0 = self.get_tidy_data(data_req, data_type, ticker)
|
608
|
-
except Exception:
|
609
|
-
logging.info(f"Failed to get {data_type} data for {ticker} after many attempts.")
|
589
|
+
except Exception as e:
|
590
|
+
logging.info(f"Failed to get {data_type} data for {ticker} after many attempts: {e}.")
|
610
591
|
else:
|
611
592
|
# add ticker to index
|
612
593
|
df0['ticker'] = ticker
|
@@ -630,23 +611,16 @@ class CryptoCompare(DataVendor):
|
|
630
611
|
df: pd.DataFrame - MultiIndex
|
631
612
|
DataFrame with DatetimeIndex (level 0), ticker (level 1), and index values (cols), in tidy format.
|
632
613
|
"""
|
633
|
-
# convert
|
634
|
-
|
635
|
-
|
636
|
-
# fields list
|
637
|
-
market_fields = self.get_fields_info(data_type='market')
|
638
|
-
|
639
|
-
# check if tickers and fields are correct
|
640
|
-
if any([ticker in self.indexes for ticker in cc_data_req['tickers']]) and \
|
641
|
-
any([field in market_fields for field in cc_data_req['fields']]):
|
642
|
-
try:
|
643
|
-
df = self.get_all_tickers(data_req, data_type='indexes')
|
614
|
+
# convert params
|
615
|
+
self.data_req = ConvertParams(data_req).to_cryptocompare()
|
644
616
|
|
645
|
-
|
646
|
-
|
617
|
+
# get indexes
|
618
|
+
try:
|
619
|
+
df = self.get_all_tickers(data_req, data_type='indexes')
|
620
|
+
return df
|
647
621
|
|
648
|
-
|
649
|
-
|
622
|
+
except Exception as e:
|
623
|
+
logging.warning(e)
|
650
624
|
|
651
625
|
def get_ohlcv(self, data_req: DataRequest) -> pd.DataFrame:
|
652
626
|
"""
|
@@ -662,23 +636,16 @@ class CryptoCompare(DataVendor):
|
|
662
636
|
df: pd.DataFrame - MultiIndex
|
663
637
|
DataFrame with DatetimeIndex (level 0), ticker (level 1), and OHLCV values (cols), in tidy format.
|
664
638
|
"""
|
665
|
-
# convert
|
666
|
-
|
667
|
-
|
668
|
-
# fields list
|
669
|
-
market_fields = self.get_fields_info(data_type='market')
|
639
|
+
# convert params
|
640
|
+
self.data_req = ConvertParams(data_req).to_cryptocompare()
|
670
641
|
|
671
|
-
#
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
df = self.get_all_tickers(data_req, data_type='ohlcv')
|
676
|
-
|
677
|
-
except Exception as e:
|
678
|
-
logging.warning(e)
|
642
|
+
# get ohlcv
|
643
|
+
try:
|
644
|
+
df = self.get_all_tickers(data_req, data_type='ohlcv')
|
645
|
+
return df
|
679
646
|
|
680
|
-
|
681
|
-
|
647
|
+
except Exception as e:
|
648
|
+
logging.warning(e)
|
682
649
|
|
683
650
|
def get_onchain(self, data_req: DataRequest) -> pd.DataFrame:
|
684
651
|
"""
|
@@ -695,28 +662,16 @@ class CryptoCompare(DataVendor):
|
|
695
662
|
DataFrame with DatetimeIndex (level 0), ticker (level 1), and values for on-chain fields (cols),
|
696
663
|
in tidy format.
|
697
664
|
"""
|
698
|
-
# convert
|
699
|
-
|
700
|
-
|
701
|
-
# check if frequency daily
|
702
|
-
if cc_data_req['freq'] != 'histoday':
|
703
|
-
raise ValueError(f"On-chain data is only available on a daily frequency."
|
704
|
-
f" Change data request frequency to 'd' and try again.")
|
665
|
+
# convert params
|
666
|
+
self.data_req = ConvertParams(data_req).to_cryptocompare()
|
705
667
|
|
706
|
-
#
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
if any([ticker in self.assets for ticker in cc_data_req['tickers']]) and \
|
711
|
-
any([field in onchain_fields for field in cc_data_req['fields']]):
|
712
|
-
try:
|
713
|
-
df = self.get_all_tickers(data_req, data_type='on-chain')
|
714
|
-
|
715
|
-
except Exception as e:
|
716
|
-
logging.warning(e)
|
668
|
+
# get on-chain
|
669
|
+
try:
|
670
|
+
df = self.get_all_tickers(data_req, data_type='on-chain')
|
671
|
+
return df
|
717
672
|
|
718
|
-
|
719
|
-
|
673
|
+
except Exception as e:
|
674
|
+
logging.warning(e)
|
720
675
|
|
721
676
|
def get_social(self, data_req: DataRequest) -> pd.DataFrame:
|
722
677
|
"""
|
@@ -733,50 +688,53 @@ class CryptoCompare(DataVendor):
|
|
733
688
|
DataFrame with DatetimeIndex (level 0), ticker (level 1), and values for social media fields (cols),
|
734
689
|
in tidy format.
|
735
690
|
"""
|
736
|
-
# convert
|
737
|
-
|
738
|
-
|
739
|
-
# check frequency
|
740
|
-
if cc_data_req['freq'] == 'histominute':
|
741
|
-
raise ValueError(f"Social media data is only available on a daily and hourly frequency."
|
742
|
-
f" Change data request frequency to 'd' or '1h' and try again.")
|
743
|
-
|
744
|
-
# fields list
|
745
|
-
social_fields = self.get_fields_info(data_type='off-chain')
|
746
|
-
|
747
|
-
# check if tickers and fields are correct
|
748
|
-
if any([ticker in self.assets for ticker in cc_data_req['tickers']]) and \
|
749
|
-
any([field in social_fields for field in cc_data_req['fields']]):
|
750
|
-
try:
|
751
|
-
df = self.get_all_tickers(data_req, data_type='social')
|
752
|
-
|
753
|
-
except Exception as e:
|
754
|
-
logging.warning(e)
|
691
|
+
# convert params
|
692
|
+
self.data_req = ConvertParams(data_req).to_cryptocompare()
|
755
693
|
|
756
|
-
|
757
|
-
|
694
|
+
# get social
|
695
|
+
try:
|
696
|
+
df = self.get_all_tickers(data_req, data_type='social')
|
697
|
+
return df
|
698
|
+
except Exception as e:
|
699
|
+
logging.warning(e)
|
758
700
|
|
759
701
|
def check_params(self, data_req: DataRequest) -> None:
|
760
702
|
"""
|
761
703
|
Checks the parameters of the data request before requesting data to reduce API calls
|
762
704
|
and improve efficiency.
|
763
705
|
"""
|
764
|
-
# convert
|
765
|
-
|
706
|
+
# convert params
|
707
|
+
self.data_req = ConvertParams(data_req).to_cryptocompare()
|
766
708
|
|
767
|
-
# tickers
|
768
|
-
|
769
|
-
|
770
|
-
tickers_list =
|
709
|
+
# tickers list
|
710
|
+
self.get_assets_info(as_list=True)
|
711
|
+
self.get_indexes_info(as_list=True)
|
712
|
+
tickers_list = self.assets + self.indexes
|
771
713
|
|
772
|
-
#
|
773
|
-
if not all([ticker in tickers_list for ticker in
|
714
|
+
# tickers
|
715
|
+
if not all([ticker in tickers_list for ticker in self.data_req.source_tickers]):
|
774
716
|
raise ValueError("Some assets are not available. "
|
775
|
-
"Check assets and indexes
|
717
|
+
"Check available assets and indexes with get_assets_info() or get_indexes_info().")
|
776
718
|
|
777
|
-
|
719
|
+
# fields
|
720
|
+
if not all([field in self.get_fields_info() for field in self.data_req.source_fields]):
|
778
721
|
raise ValueError("Some fields are not available. "
|
779
|
-
"Check fields
|
722
|
+
"Check available fields with get_fields_info().")
|
723
|
+
|
724
|
+
# check freq
|
725
|
+
if self.data_req.source_freq not in ['histoday', 'histohour', 'histominute']:
|
726
|
+
raise ValueError(f"Frequency {self.data_req.source_freq} not available. "
|
727
|
+
f"Check available frequencies with get_frequencies().")
|
728
|
+
|
729
|
+
# on-chain freq
|
730
|
+
if self.data_req.source_fields in self.get_onchain_info() and self.data_req.source_freq != 'histoday':
|
731
|
+
raise ValueError(f"On-chain data is only available on a daily frequency."
|
732
|
+
f" Change data request frequency to 'd' and try again.")
|
733
|
+
|
734
|
+
# social freq
|
735
|
+
if self.data_req.source_fields in self.get_social_info() and self.data_req.source_freq == 'histominute':
|
736
|
+
raise ValueError(f"Social media data is only available on a daily and hourly frequency."
|
737
|
+
f" Change data request frequency to 'd' or '1h' and try again.")
|
780
738
|
|
781
739
|
def get_data(self, data_req: DataRequest) -> pd.DataFrame:
|
782
740
|
"""
|
@@ -798,16 +756,13 @@ class CryptoCompare(DataVendor):
|
|
798
756
|
# check data req params
|
799
757
|
self.check_params(data_req)
|
800
758
|
|
801
|
-
# df to store data
|
802
|
-
df = pd.DataFrame()
|
803
|
-
|
804
759
|
# get indexes
|
805
760
|
try:
|
806
761
|
df0 = self.get_indexes(data_req)
|
807
762
|
except Exception as e:
|
808
763
|
logging.warning(e)
|
809
764
|
else:
|
810
|
-
|
765
|
+
self.data = pd.concat([self.data, df0])
|
811
766
|
|
812
767
|
# get ohlcv
|
813
768
|
try:
|
@@ -815,7 +770,7 @@ class CryptoCompare(DataVendor):
|
|
815
770
|
except Exception as e:
|
816
771
|
logging.warning(e)
|
817
772
|
else:
|
818
|
-
|
773
|
+
self.data = pd.concat([self.data, df1])
|
819
774
|
|
820
775
|
# get onchain
|
821
776
|
try:
|
@@ -823,7 +778,7 @@ class CryptoCompare(DataVendor):
|
|
823
778
|
except Exception as e:
|
824
779
|
logging.warning(e)
|
825
780
|
else:
|
826
|
-
|
781
|
+
self.data = pd.concat([self.data, df2], axis=1)
|
827
782
|
|
828
783
|
# get social
|
829
784
|
try:
|
@@ -831,16 +786,16 @@ class CryptoCompare(DataVendor):
|
|
831
786
|
except Exception as e:
|
832
787
|
logging.warning(e)
|
833
788
|
else:
|
834
|
-
|
789
|
+
self.data = pd.concat([self.data, df3], axis=1)
|
835
790
|
|
836
791
|
# check if df empty
|
837
|
-
if
|
792
|
+
if self.data.empty:
|
838
793
|
raise Exception('No data returned. Check data request parameters and try again.')
|
839
794
|
|
840
795
|
# filter df for desired fields and sort index by date
|
841
|
-
fields = [field for field in data_req.fields if field in
|
842
|
-
|
796
|
+
fields = [field for field in data_req.fields if field in self.data.columns]
|
797
|
+
self.data = self.data.loc[:, fields].sort_index()
|
843
798
|
|
844
799
|
logging.info("Data retrieved from CryptoCompare.")
|
845
800
|
|
846
|
-
return
|
801
|
+
return self.data
|