cryptodatapy 0.2.4__py3-none-any.whl → 0.2.6__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/tickers.csv +0 -1
- cryptodatapy/extract/data_vendors/CoinMetrics.ipynb +747 -0
- cryptodatapy/extract/data_vendors/coinmetrics_api.py +279 -209
- cryptodatapy/extract/data_vendors/cryptocompare_api.py +3 -5
- cryptodatapy/extract/data_vendors/datavendor.py +32 -12
- cryptodatapy/extract/data_vendors/glassnode_api.py +3 -2
- cryptodatapy/extract/data_vendors/tiingo_api.py +3 -2
- cryptodatapy/extract/datarequest.py +55 -9
- cryptodatapy/extract/libraries/ccxt_api.py +51 -133
- cryptodatapy/transform/cc_onchain_data.csv +118423 -0
- cryptodatapy/transform/clean.py +17 -15
- cryptodatapy/transform/clean_onchain_data.ipynb +4750 -0
- cryptodatapy/transform/clean_perp_futures_ohlcv.ipynb +1874 -276
- cryptodatapy/transform/convertparams.py +28 -19
- cryptodatapy/transform/credit_data.ipynb +291 -0
- cryptodatapy/transform/eqty_data.ipynb +809 -0
- cryptodatapy/transform/filter.py +13 -11
- cryptodatapy/transform/global_credit_data_daily.parquet +0 -0
- cryptodatapy/transform/od.py +1 -0
- cryptodatapy/transform/rates_data.ipynb +465 -0
- cryptodatapy/transform/us_rates_daily.csv +227752 -0
- cryptodatapy/util/datacredentials.py +28 -7
- {cryptodatapy-0.2.4.dist-info → cryptodatapy-0.2.6.dist-info}/METADATA +2 -2
- {cryptodatapy-0.2.4.dist-info → cryptodatapy-0.2.6.dist-info}/RECORD +26 -27
- cryptodatapy/.DS_Store +0 -0
- cryptodatapy/.idea/.gitignore +0 -3
- cryptodatapy/.idea/cryptodatapy.iml +0 -12
- cryptodatapy/.idea/csv-plugin.xml +0 -16
- cryptodatapy/.idea/inspectionProfiles/Project_Default.xml +0 -6
- cryptodatapy/.idea/inspectionProfiles/profiles_settings.xml +0 -6
- cryptodatapy/.idea/misc.xml +0 -4
- cryptodatapy/.idea/modules.xml +0 -8
- cryptodatapy/.idea/vcs.xml +0 -6
- {cryptodatapy-0.2.4.dist-info → cryptodatapy-0.2.6.dist-info}/LICENSE +0 -0
- {cryptodatapy-0.2.4.dist-info → cryptodatapy-0.2.6.dist-info}/WHEEL +0 -0
@@ -84,8 +84,9 @@ class CryptoCompare(DataVendor):
|
|
84
84
|
if categories is None:
|
85
85
|
self.categories = ['crypto']
|
86
86
|
if api_key is None:
|
87
|
-
raise TypeError("Set your
|
88
|
-
"
|
87
|
+
raise TypeError("Set your CryptoCompare api key in environment variables as 'CRYPTOCOMPARE_API_KEY' or "
|
88
|
+
"add it as an argument when instantiating the class. To get an api key, visit: "
|
89
|
+
"https://min-api.cryptocompare.com/")
|
89
90
|
if exchanges is None:
|
90
91
|
self.exchanges = self.get_exchanges_info(as_list=True)
|
91
92
|
if indexes is None:
|
@@ -467,9 +468,6 @@ class CryptoCompare(DataVendor):
|
|
467
468
|
urls_params = self.set_urls_params(data_req, data_type, ticker)
|
468
469
|
url, params = urls_params['url'], urls_params['params']
|
469
470
|
|
470
|
-
# # data req
|
471
|
-
# data_resp = DataRequest().get_req(url=url, params=params)
|
472
|
-
|
473
471
|
# data req
|
474
472
|
data_resp = DataRequest().get_req(url=url, params=params)
|
475
473
|
|
@@ -92,7 +92,7 @@ class DataVendor(ABC):
|
|
92
92
|
|
93
93
|
@exchanges.setter
|
94
94
|
def exchanges(
|
95
|
-
self, exchanges: Optional[Union[str, List[str], Dict[str, List[str]]]]
|
95
|
+
self, exchanges: Optional[Union[str, List[str], Dict[str, List[str]], pd.DataFrame]]
|
96
96
|
):
|
97
97
|
"""
|
98
98
|
Sets a list of available exchanges for the data vendor.
|
@@ -101,6 +101,7 @@ class DataVendor(ABC):
|
|
101
101
|
exchanges is None
|
102
102
|
or isinstance(exchanges, list)
|
103
103
|
or isinstance(exchanges, dict)
|
104
|
+
or isinstance(exchanges, pd.DataFrame)
|
104
105
|
):
|
105
106
|
self._exchanges = exchanges
|
106
107
|
elif isinstance(exchanges, str):
|
@@ -126,11 +127,16 @@ class DataVendor(ABC):
|
|
126
127
|
return self._indexes
|
127
128
|
|
128
129
|
@indexes.setter
|
129
|
-
def indexes(self, indexes: Optional[Union[str, List[str], Dict[str, List[str]]]]):
|
130
|
+
def indexes(self, indexes: Optional[Union[str, List[str], Dict[str, List[str]], pd.DataFrame]]):
|
130
131
|
"""
|
131
132
|
Sets a list of available indexes for the data vendor.
|
132
133
|
"""
|
133
|
-
if
|
134
|
+
if (
|
135
|
+
indexes is None
|
136
|
+
or isinstance(indexes, list)
|
137
|
+
or isinstance(indexes, dict)
|
138
|
+
or isinstance(indexes, pd.DataFrame)
|
139
|
+
):
|
134
140
|
self._indexes = indexes
|
135
141
|
elif isinstance(indexes, str):
|
136
142
|
self._indexes = [indexes]
|
@@ -155,11 +161,16 @@ class DataVendor(ABC):
|
|
155
161
|
return self._assets
|
156
162
|
|
157
163
|
@assets.setter
|
158
|
-
def assets(self, assets: Optional[Union[str, List[str], Dict[str, List[str]]]]):
|
164
|
+
def assets(self, assets: Optional[Union[str, List[str], Dict[str, List[str]], pd.DataFrame]]):
|
159
165
|
"""
|
160
166
|
Sets a list of available assets for the data vendor.
|
161
167
|
"""
|
162
|
-
if
|
168
|
+
if (
|
169
|
+
assets is None
|
170
|
+
or isinstance(assets, list)
|
171
|
+
or isinstance(assets, dict)
|
172
|
+
or isinstance(assets, pd.DataFrame)
|
173
|
+
):
|
163
174
|
self._assets = assets
|
164
175
|
elif isinstance(assets, str):
|
165
176
|
self._assets = [assets]
|
@@ -184,11 +195,16 @@ class DataVendor(ABC):
|
|
184
195
|
return self._markets
|
185
196
|
|
186
197
|
@markets.setter
|
187
|
-
def markets(self, markets: Optional[Union[str, List[str], Dict[str, List[str]]]]):
|
198
|
+
def markets(self, markets: Optional[Union[str, List[str], Dict[str, List[str]], pd.DataFrame]]):
|
188
199
|
"""
|
189
200
|
Sets a list of available markets for the data vendor.
|
190
201
|
"""
|
191
|
-
if
|
202
|
+
if (
|
203
|
+
markets is None
|
204
|
+
or isinstance(markets, list)
|
205
|
+
or isinstance(markets, dict)
|
206
|
+
or isinstance(markets, pd.DataFrame)
|
207
|
+
):
|
192
208
|
self._markets = markets
|
193
209
|
elif isinstance(markets, str):
|
194
210
|
self._markets = [markets]
|
@@ -251,11 +267,16 @@ class DataVendor(ABC):
|
|
251
267
|
return self._fields
|
252
268
|
|
253
269
|
@fields.setter
|
254
|
-
def fields(self, fields: Optional[Union[str, List[str], Dict[str, List[str]]]]):
|
270
|
+
def fields(self, fields: Optional[Union[str, List[str], Dict[str, List[str]], pd.DataFrame]]):
|
255
271
|
"""
|
256
272
|
Sets a list of available fields for the data vendor.
|
257
273
|
"""
|
258
|
-
if
|
274
|
+
if (
|
275
|
+
fields is None
|
276
|
+
or isinstance(fields, list)
|
277
|
+
or isinstance(fields, dict)
|
278
|
+
or isinstance(fields, pd.DataFrame)
|
279
|
+
):
|
259
280
|
self._fields = fields
|
260
281
|
elif isinstance(fields, str):
|
261
282
|
self._fields = [fields]
|
@@ -280,9 +301,7 @@ class DataVendor(ABC):
|
|
280
301
|
return self._frequencies
|
281
302
|
|
282
303
|
@frequencies.setter
|
283
|
-
def frequencies(
|
284
|
-
self, frequencies: Optional[Union[str, List[str], Dict[str, List[str]]]]
|
285
|
-
):
|
304
|
+
def frequencies(self, frequencies: Optional[Union[str, List[str], Dict[str, List[str]], pd.DataFrame]]):
|
286
305
|
"""
|
287
306
|
Sets a list of available data frequencies for the data vendor.
|
288
307
|
"""
|
@@ -290,6 +309,7 @@ class DataVendor(ABC):
|
|
290
309
|
frequencies is None
|
291
310
|
or isinstance(frequencies, list)
|
292
311
|
or isinstance(frequencies, dict)
|
312
|
+
or isinstance(frequencies, pd.DataFrame)
|
293
313
|
):
|
294
314
|
self._frequencies = frequencies
|
295
315
|
elif isinstance(frequencies, str):
|
@@ -79,8 +79,9 @@ class Glassnode(DataVendor):
|
|
79
79
|
if categories is None:
|
80
80
|
self.categories = ['crypto']
|
81
81
|
if api_key is None:
|
82
|
-
raise TypeError("Set your
|
83
|
-
"
|
82
|
+
raise TypeError("Set your Glassnode api key in environment variables as 'GLASSNODE_API_KEY' or "
|
83
|
+
"add it as an argument when instantiating the class. To get an api key, visit: "
|
84
|
+
"https://docs.glassnode.com/basic-api/api-key")
|
84
85
|
if assets is None:
|
85
86
|
self.assets = self.get_assets_info(as_list=True)
|
86
87
|
if fields is None:
|
@@ -109,8 +109,9 @@ class Tiingo(DataVendor):
|
|
109
109
|
if categories is None:
|
110
110
|
self.categories = ["crypto", "fx", "eqty"]
|
111
111
|
if api_key is None:
|
112
|
-
raise TypeError("Set your
|
113
|
-
"
|
112
|
+
raise TypeError("Set your Tiingo api key in environment variables as 'TIINGO_API_KEY' or "
|
113
|
+
"add it as an argument when instantiating the class. To get an api key, visit: "
|
114
|
+
"https://www.tiingo.com/")
|
114
115
|
if exchanges is None:
|
115
116
|
self.exchanges = self.get_exchanges_info()
|
116
117
|
if assets is None:
|
@@ -197,7 +197,9 @@ class DataRequest:
|
|
197
197
|
"y": "yearly",
|
198
198
|
}
|
199
199
|
|
200
|
-
if frequency
|
200
|
+
if frequency is None:
|
201
|
+
self._frequency = frequency
|
202
|
+
elif frequency not in list(freq_dict.keys()):
|
201
203
|
raise ValueError(
|
202
204
|
f"{frequency} is an invalid data frequency. Valid frequencies are: {freq_dict}"
|
203
205
|
)
|
@@ -555,15 +557,59 @@ class DataRequest:
|
|
555
557
|
# get request
|
556
558
|
try:
|
557
559
|
resp = requests.get(url, params=params, headers=headers)
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
560
|
+
# check for status code
|
561
|
+
resp.raise_for_status()
|
562
|
+
|
563
|
+
return resp.json()
|
564
|
+
|
565
|
+
# handle HTTP errors
|
566
|
+
except requests.exceptions.HTTPError as http_err:
|
567
|
+
status_code = resp.status_code
|
568
|
+
|
569
|
+
# Tailored handling for different status codes
|
570
|
+
if status_code == 400:
|
571
|
+
logging.warning(f"Bad Request (400): {resp.text}")
|
572
|
+
elif status_code == 401:
|
573
|
+
logging.warning("Unauthorized (401): Check the authentication credentials.")
|
574
|
+
elif status_code == 403:
|
575
|
+
logging.warning("Forbidden (403): You do not have permission to access this resource.")
|
576
|
+
elif status_code == 404:
|
577
|
+
logging.warning("Not Found (404): The requested resource could not be found.")
|
578
|
+
elif status_code == 500:
|
579
|
+
logging.error("Internal Server Error (500): The server encountered an error.")
|
580
|
+
elif status_code == 503:
|
581
|
+
logging.error("Service Unavailable (503): The server is temporarily unavailable.")
|
582
|
+
else:
|
583
|
+
logging.error(f"HTTP error occurred: {http_err} (Status Code: {status_code})")
|
584
|
+
logging.error(f"Response Content: {resp.text}")
|
585
|
+
|
586
|
+
# Increment attempts and log warning
|
587
|
+
attempts += 1
|
588
|
+
logging.warning(f"Attempt #{attempts}: Failed to get data due to: {http_err}")
|
589
|
+
sleep(self.pause) # Pause before retrying
|
590
|
+
if attempts == self.trials:
|
591
|
+
logging.error("Max attempts reached. Unable to fetch data.")
|
592
|
+
break
|
593
|
+
|
594
|
+
# handle non-HTTP exceptions (e.g., network issues)
|
595
|
+
except requests.exceptions.RequestException as req_err:
|
562
596
|
attempts += 1
|
563
|
-
logging.warning(f"
|
597
|
+
logging.warning(f"Request error on attempt #{attempts}: {req_err}. "
|
598
|
+
f"Retrying after {self.pause} seconds...")
|
564
599
|
sleep(self.pause)
|
565
|
-
if attempts ==
|
600
|
+
if attempts == self.trials:
|
601
|
+
logging.error("Max attempts reached. Unable to fetch data due to request errors.")
|
566
602
|
break
|
567
603
|
|
568
|
-
|
569
|
-
|
604
|
+
# handle other exceptions
|
605
|
+
except Exception as e:
|
606
|
+
attempts += 1
|
607
|
+
logging.warning(f"An unexpected error occurred: {e}. "
|
608
|
+
f"Retrying after {self.pause} seconds...")
|
609
|
+
sleep(self.pause)
|
610
|
+
if attempts == self.trials:
|
611
|
+
logging.error("Max attempts reached. Unable to fetch data due to request errors.")
|
612
|
+
break
|
613
|
+
|
614
|
+
# return None if the API call fails
|
615
|
+
return None
|
@@ -3,7 +3,6 @@ from time import sleep
|
|
3
3
|
from typing import Any, Dict, List, Optional, Union
|
4
4
|
|
5
5
|
import ccxt
|
6
|
-
import numpy as np
|
7
6
|
import pandas as pd
|
8
7
|
|
9
8
|
from cryptodatapy.extract.datarequest import DataRequest
|
@@ -20,7 +19,6 @@ class CCXT(Library):
|
|
20
19
|
"""
|
21
20
|
Retrieves data from CCXT API.
|
22
21
|
"""
|
23
|
-
|
24
22
|
def __init__(
|
25
23
|
self,
|
26
24
|
categories: Union[str, List[str]] = "crypto",
|
@@ -86,97 +84,16 @@ class CCXT(Library):
|
|
86
84
|
rate_limit,
|
87
85
|
)
|
88
86
|
|
89
|
-
def get_exchanges_info(
|
90
|
-
self,
|
91
|
-
exch: Optional[str] = None,
|
92
|
-
as_list: bool = True
|
93
|
-
) -> Union[List[str], pd.DataFrame]:
|
87
|
+
def get_exchanges_info(self) -> List[str]:
|
94
88
|
"""
|
95
89
|
Get exchanges info.
|
96
90
|
|
97
|
-
Parameters
|
98
|
-
----------
|
99
|
-
exch: str, default None
|
100
|
-
Name of exchange.
|
101
|
-
as_list: bool, default False
|
102
|
-
Returns exchanges info as list.
|
103
|
-
|
104
91
|
Returns
|
105
92
|
-------
|
106
93
|
exch: list or pd.DataFrame
|
107
94
|
List or dataframe with info on supported exchanges.
|
108
95
|
"""
|
109
|
-
|
110
|
-
if as_list:
|
111
|
-
self.exchanges = ccxt.exchanges
|
112
|
-
|
113
|
-
# df
|
114
|
-
else:
|
115
|
-
if exch is not None:
|
116
|
-
self.exchanges = [exch]
|
117
|
-
else:
|
118
|
-
self.exchanges = ccxt.exchanges
|
119
|
-
print(
|
120
|
-
"Getting metadata for all supported exchanges can take a few minutes."
|
121
|
-
" For quick info on a specific exchange, provide the name of the exchange in the exch parameter."
|
122
|
-
)
|
123
|
-
|
124
|
-
# exch df
|
125
|
-
exch_df = pd.DataFrame(
|
126
|
-
index=self.exchanges,
|
127
|
-
columns=[
|
128
|
-
"id",
|
129
|
-
"name",
|
130
|
-
"countries",
|
131
|
-
"urls",
|
132
|
-
"version",
|
133
|
-
"api",
|
134
|
-
"has",
|
135
|
-
"timeframes",
|
136
|
-
"timeout",
|
137
|
-
"rateLimit",
|
138
|
-
"userAgent",
|
139
|
-
"verbose",
|
140
|
-
"markets",
|
141
|
-
"symbols",
|
142
|
-
"currencies",
|
143
|
-
"markets_by_id",
|
144
|
-
"currencies_by_id",
|
145
|
-
"api_key",
|
146
|
-
"secret",
|
147
|
-
"uid",
|
148
|
-
"options",
|
149
|
-
],
|
150
|
-
)
|
151
|
-
|
152
|
-
# Extract exchange info
|
153
|
-
for index, row in exch_df.iterrows():
|
154
|
-
try:
|
155
|
-
exchange = getattr(ccxt, index)()
|
156
|
-
exchange.load_markets()
|
157
|
-
except AttributeError as e:
|
158
|
-
print(f"AttributeError: {e} for exchange {index}")
|
159
|
-
exch_df.loc[index, :] = np.nan
|
160
|
-
except ccxt.BaseError as e: # Catch specific ccxt exceptions
|
161
|
-
print(f"CCXT Error: {e} for exchange {index}")
|
162
|
-
exch_df.loc[index, :] = np.nan
|
163
|
-
except Exception as e: # Fallback for any other exceptions
|
164
|
-
print(f"Unexpected error: {e} for exchange {index}")
|
165
|
-
exch_df.loc[index, :] = np.nan
|
166
|
-
else:
|
167
|
-
for col in exch_df.columns:
|
168
|
-
try:
|
169
|
-
exch_df.loc[index, col] = str(getattr(exchange, col))
|
170
|
-
except AttributeError as e:
|
171
|
-
print(f"AttributeError: {e} for attribute {col} in exchange {index}")
|
172
|
-
exch_df.loc[index, col] = np.nan
|
173
|
-
except Exception as e: # Fallback for any other exceptions
|
174
|
-
print(f"Unexpected error: {e} for attribute {col} in exchange {index}")
|
175
|
-
exch_df.loc[index, col] = np.nan
|
176
|
-
|
177
|
-
# set index name
|
178
|
-
exch_df.index.name = "exchange"
|
179
|
-
self.exchanges = exch_df
|
96
|
+
self.exchanges = ccxt.exchanges
|
180
97
|
|
181
98
|
return self.exchanges
|
182
99
|
|
@@ -337,7 +254,7 @@ class CCXT(Library):
|
|
337
254
|
Get CCXT metadata.
|
338
255
|
"""
|
339
256
|
if self.exchanges is None:
|
340
|
-
self.exchanges = self.get_exchanges_info(
|
257
|
+
self.exchanges = self.get_exchanges_info()
|
341
258
|
if self.market_types is None:
|
342
259
|
self.market_types = ["spot", "future", "perpetual_future", "option"]
|
343
260
|
if self.assets is None:
|
@@ -356,6 +273,7 @@ class CCXT(Library):
|
|
356
273
|
data_type: str,
|
357
274
|
ticker: str,
|
358
275
|
start_date: str = None,
|
276
|
+
end_date: str = None,
|
359
277
|
) -> pd.DataFrame:
|
360
278
|
"""
|
361
279
|
Sends data request to Python client.
|
@@ -370,9 +288,8 @@ class CCXT(Library):
|
|
370
288
|
Ticker symbol to request data for.
|
371
289
|
start_date: str
|
372
290
|
Start date in 'YYYY-MM-DD' format.
|
373
|
-
|
374
|
-
|
375
|
-
----------------
|
291
|
+
end_date: str
|
292
|
+
End date in 'YYYY-MM-DD' format.
|
376
293
|
|
377
294
|
|
378
295
|
Returns
|
@@ -384,6 +301,8 @@ class CCXT(Library):
|
|
384
301
|
cx_data_req = ConvertParams(data_req).to_ccxt()
|
385
302
|
if start_date is None:
|
386
303
|
start_date = cx_data_req['start_date']
|
304
|
+
if end_date is None:
|
305
|
+
end_date = cx_data_req['end_date']
|
387
306
|
|
388
307
|
# data types
|
389
308
|
data_types = {'ohlcv': 'fetchOHLCV', 'funding_rates': 'fetchFundingRateHistory'}
|
@@ -399,24 +318,25 @@ class CCXT(Library):
|
|
399
318
|
cx_data_req["freq"],
|
400
319
|
since=start_date,
|
401
320
|
limit=self.max_obs_per_call,
|
321
|
+
params={'until': end_date}
|
402
322
|
)
|
403
323
|
elif data_type == 'funding_rates':
|
404
324
|
data_resp = getattr(exch, data_types[data_type])(
|
405
325
|
ticker,
|
406
326
|
since=start_date,
|
407
327
|
limit=1000,
|
328
|
+
params={'until': end_date}
|
408
329
|
)
|
409
330
|
|
410
|
-
|
331
|
+
return data_resp
|
411
332
|
|
412
333
|
except Exception as e:
|
413
334
|
logging.warning(f"Failed to get {data_type} data for {ticker}.")
|
414
335
|
logging.warning(e)
|
415
336
|
|
416
|
-
|
417
|
-
return data_resp
|
337
|
+
return None
|
418
338
|
|
419
|
-
def
|
339
|
+
def fetch_all_ohlcv_hist(self, data_req: DataRequest, ticker: str) -> pd.DataFrame:
|
420
340
|
"""
|
421
341
|
Submits get requests to API until entire OHLCV history has been collected. Only necessary when
|
422
342
|
number of observations is larger than the maximum number of observations per call.
|
@@ -436,24 +356,24 @@ class CCXT(Library):
|
|
436
356
|
# convert data request parameters to CCXT format and set start date
|
437
357
|
cx_data_req = ConvertParams(data_req).to_ccxt()
|
438
358
|
start_date = cx_data_req['start_date']
|
359
|
+
end_date = cx_data_req['end_date']
|
439
360
|
|
440
361
|
# create empty df
|
441
362
|
df = pd.DataFrame()
|
363
|
+
|
442
364
|
# while loop condition
|
443
365
|
missing_vals, attempts = True, 0
|
444
366
|
|
445
367
|
# run a while loop until all data collected
|
446
368
|
while missing_vals and attempts < cx_data_req['trials']:
|
447
369
|
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
start_date=start_date)
|
370
|
+
data_resp = self.req_data(data_req=data_req,
|
371
|
+
data_type='ohlcv',
|
372
|
+
ticker=ticker,
|
373
|
+
start_date=start_date,
|
374
|
+
end_date=end_date)
|
454
375
|
|
455
|
-
|
456
|
-
logging.warning(e)
|
376
|
+
if data_resp is None:
|
457
377
|
attempts += 1
|
458
378
|
sleep(self.get_rate_limit_info(exch=cx_data_req['exch'])[cx_data_req['exch']] / 1000)
|
459
379
|
logging.warning(
|
@@ -463,6 +383,7 @@ class CCXT(Library):
|
|
463
383
|
logging.warning(
|
464
384
|
f"Failed to get OHLCV data from {cx_data_req['exch']} for {ticker} after many attempts."
|
465
385
|
)
|
386
|
+
return None
|
466
387
|
|
467
388
|
else:
|
468
389
|
# name cols and create df
|
@@ -488,7 +409,7 @@ class CCXT(Library):
|
|
488
409
|
|
489
410
|
return df
|
490
411
|
|
491
|
-
def
|
412
|
+
def fetch_all_funding_hist(self, data_req: DataRequest, ticker: str) -> pd.DataFrame:
|
492
413
|
"""
|
493
414
|
Submits get requests to API until entire funding rate history has been collected. Only necessary when
|
494
415
|
number of observations is larger than the maximum number of observations per call.
|
@@ -508,6 +429,7 @@ class CCXT(Library):
|
|
508
429
|
# convert data request parameters to CCXT format and set start date
|
509
430
|
cx_data_req = ConvertParams(data_req).to_ccxt()
|
510
431
|
start_date = cx_data_req['start_date']
|
432
|
+
end_date = cx_data_req['end_date']
|
511
433
|
|
512
434
|
# create empty df
|
513
435
|
df = pd.DataFrame()
|
@@ -517,15 +439,14 @@ class CCXT(Library):
|
|
517
439
|
# run a while loop until all data collected
|
518
440
|
while missing_vals and attempts < cx_data_req['trials']:
|
519
441
|
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
442
|
+
# data req
|
443
|
+
data_resp = self.req_data(data_req=data_req,
|
444
|
+
data_type='funding_rates',
|
445
|
+
ticker=ticker,
|
446
|
+
start_date=start_date,
|
447
|
+
end_date=end_date)
|
526
448
|
|
527
|
-
|
528
|
-
logging.warning(e)
|
449
|
+
if data_resp is None:
|
529
450
|
attempts += 1
|
530
451
|
sleep(self.get_rate_limit_info(exch=cx_data_req['exch'])[cx_data_req['exch']] / 1000)
|
531
452
|
logging.warning(
|
@@ -533,8 +454,9 @@ class CCXT(Library):
|
|
533
454
|
)
|
534
455
|
if attempts == cx_data_req["trials"]:
|
535
456
|
logging.warning(
|
536
|
-
f"Failed to get
|
457
|
+
f"Failed to get funding_rates from {cx_data_req['exch']} for {ticker} after many attempts."
|
537
458
|
)
|
459
|
+
return None
|
538
460
|
|
539
461
|
else:
|
540
462
|
# add to df
|
@@ -580,7 +502,7 @@ class CCXT(Library):
|
|
580
502
|
|
581
503
|
return WrangleData(data_req, data_resp).ccxt()
|
582
504
|
|
583
|
-
def
|
505
|
+
def fetch_tidy_ohlcv(self, data_req: DataRequest, ticker: str) -> pd.DataFrame:
|
584
506
|
"""
|
585
507
|
Gets entire OHLCV history and wrangles the data response into tidy data format.
|
586
508
|
|
@@ -597,13 +519,15 @@ class CCXT(Library):
|
|
597
519
|
Dataframe with entire data history retrieved and wrangled into tidy data format.
|
598
520
|
"""
|
599
521
|
# get entire data history
|
600
|
-
df = self.
|
522
|
+
df = self.fetch_all_ohlcv_hist(data_req, ticker)
|
523
|
+
|
601
524
|
# wrangle df
|
602
|
-
df
|
525
|
+
if df is not None:
|
526
|
+
df = self.wrangle_data_resp(data_req, df)
|
603
527
|
|
604
528
|
return df
|
605
529
|
|
606
|
-
def
|
530
|
+
def fetch_tidy_funding_rates(self, data_req: DataRequest, ticker: str) -> pd.DataFrame:
|
607
531
|
"""
|
608
532
|
Gets entire funding rates history and wrangles the data response into tidy data format.
|
609
533
|
|
@@ -620,9 +544,11 @@ class CCXT(Library):
|
|
620
544
|
Dataframe with entire data history retrieved and wrangled into tidy data format.
|
621
545
|
"""
|
622
546
|
# get entire data history
|
623
|
-
df = self.
|
547
|
+
df = self.fetch_all_funding_hist(data_req, ticker)
|
548
|
+
|
624
549
|
# wrangle df
|
625
|
-
df
|
550
|
+
if df is not None:
|
551
|
+
df = self.wrangle_data_resp(data_req, df)
|
626
552
|
|
627
553
|
return df
|
628
554
|
|
@@ -680,7 +606,7 @@ class CCXT(Library):
|
|
680
606
|
f" Market type must be perpetual futures."
|
681
607
|
)
|
682
608
|
|
683
|
-
def
|
609
|
+
def fetch_ohlcv(self, data_req: DataRequest) -> pd.DataFrame:
|
684
610
|
"""
|
685
611
|
Loops list of tickers, retrieves OHLCV data for each ticker in tidy format and stores it in a
|
686
612
|
multiindex dataframe.
|
@@ -707,13 +633,9 @@ class CCXT(Library):
|
|
707
633
|
# loop through tickers
|
708
634
|
for mkt, ticker in zip(cx_data_req['mkts'], data_req.tickers):
|
709
635
|
|
710
|
-
|
711
|
-
df0 = self.get_tidy_ohlcv(data_req, mkt)
|
712
|
-
|
713
|
-
except AssertionError:
|
714
|
-
logging.info(f"Failed to get OHLCV data for {ticker} after many attempts.")
|
636
|
+
df0 = self.fetch_tidy_ohlcv(data_req, mkt)
|
715
637
|
|
716
|
-
|
638
|
+
if df0 is not None:
|
717
639
|
# add ticker to index
|
718
640
|
df0['ticker'] = ticker.upper()
|
719
641
|
df0.set_index(['ticker'], append=True, inplace=True)
|
@@ -722,7 +644,7 @@ class CCXT(Library):
|
|
722
644
|
|
723
645
|
return df
|
724
646
|
|
725
|
-
def
|
647
|
+
def fetch_funding_rates(self, data_req: DataRequest) -> pd.DataFrame:
|
726
648
|
"""
|
727
649
|
Loops list of tickers, retrieves funding rates data for each ticker in tidy format and stores it in a
|
728
650
|
multiindex dataframe.
|
@@ -749,13 +671,9 @@ class CCXT(Library):
|
|
749
671
|
# loop through tickers
|
750
672
|
for mkt, ticker in zip(cx_data_req['mkts'], data_req.tickers):
|
751
673
|
|
752
|
-
|
753
|
-
df0 = self.get_tidy_funding_rates(data_req, mkt)
|
754
|
-
|
755
|
-
except AssertionError:
|
756
|
-
logging.info(f"Failed to get funding rates for {ticker} after many attempts.")
|
674
|
+
df0 = self.fetch_tidy_funding_rates(data_req, mkt)
|
757
675
|
|
758
|
-
|
676
|
+
if df0 is not None:
|
759
677
|
# add ticker to index
|
760
678
|
df0['ticker'] = ticker.upper()
|
761
679
|
df0.set_index(['ticker'], append=True, inplace=True)
|
@@ -783,12 +701,12 @@ class CCXT(Library):
|
|
783
701
|
# get OHLCV data
|
784
702
|
ohlcv_list = ["open", "high", "low", "close", "volume"]
|
785
703
|
if any([field in ohlcv_list for field in data_req.fields]):
|
786
|
-
df0 = self.
|
704
|
+
df0 = self.fetch_ohlcv(data_req)
|
787
705
|
df = pd.concat([df, df0])
|
788
706
|
|
789
707
|
# get funding rate data
|
790
708
|
if any([field == "funding_rate" for field in data_req.fields]):
|
791
|
-
df1 = self.
|
709
|
+
df1 = self.fetch_funding_rates(data_req)
|
792
710
|
df = pd.concat([df, df1], axis=1)
|
793
711
|
|
794
712
|
# check if df empty
|