cryptodatapy 0.2.5__py3-none-any.whl → 0.2.7__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/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 +197 -36
- cryptodatapy/extract/libraries/Untitled.ipynb +33 -0
- cryptodatapy/extract/libraries/ccxt.ipynb +628 -754
- cryptodatapy/extract/libraries/ccxt_api.py +630 -346
- cryptodatapy/extract/libraries/pandasdr_api.py +13 -12
- cryptodatapy/extract/libraries/yfinance_api.py +511 -0
- 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 +1712 -1097
- cryptodatapy/transform/cmdty_data.ipynb +402 -0
- cryptodatapy/transform/convertparams.py +139 -181
- cryptodatapy/transform/credit_data.ipynb +291 -0
- cryptodatapy/transform/eqty_data.ipynb +836 -0
- cryptodatapy/transform/filter.py +13 -10
- 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/transform/wrangle.py +109 -20
- cryptodatapy/util/datacredentials.py +28 -7
- {cryptodatapy-0.2.5.dist-info → cryptodatapy-0.2.7.dist-info}/METADATA +10 -7
- {cryptodatapy-0.2.5.dist-info → cryptodatapy-0.2.7.dist-info}/RECORD +33 -31
- {cryptodatapy-0.2.5.dist-info → cryptodatapy-0.2.7.dist-info}/WHEEL +1 -1
- 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.5.dist-info → cryptodatapy-0.2.7.dist-info}/LICENSE +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:
|
@@ -17,8 +17,9 @@ class DataRequest:
|
|
17
17
|
self,
|
18
18
|
source: str = "ccxt",
|
19
19
|
tickers: Union[str, List[str]] = "btc",
|
20
|
-
freq: str = "d",
|
21
20
|
quote_ccy: Optional[str] = None,
|
21
|
+
markets: Optional[Union[str, List[str]]] = None,
|
22
|
+
freq: str = "d",
|
22
23
|
exch: Optional[str] = None,
|
23
24
|
mkt_type: Optional[str] = "spot",
|
24
25
|
start_date: Optional[Union[str, datetime, pd.Timestamp]] = None,
|
@@ -30,8 +31,11 @@ class DataRequest:
|
|
30
31
|
trials: Optional[int] = 3,
|
31
32
|
pause: Optional[float] = 0.1,
|
32
33
|
source_tickers: Optional[Union[str, List[str]]] = None,
|
34
|
+
source_markets: Optional[Union[str, List[str]]] = None,
|
33
35
|
source_freq: Optional[str] = None,
|
34
|
-
|
36
|
+
source_start_date: Optional[Union[str, int, datetime, pd.Timestamp]] = None,
|
37
|
+
source_end_date: Optional[Union[str, int, datetime, pd.Timestamp]] = None,
|
38
|
+
source_fields: Optional[Union[str, List[str]]] = None
|
35
39
|
):
|
36
40
|
"""
|
37
41
|
Constructor
|
@@ -41,12 +45,14 @@ class DataRequest:
|
|
41
45
|
source: str, default 'ccxt'
|
42
46
|
Name of data source.
|
43
47
|
tickers: list or str, default 'btc'
|
44
|
-
Ticker symbols for assets
|
45
|
-
e.g. 'BTC', '
|
48
|
+
Ticker symbols for base assets.
|
49
|
+
e.g. 'BTC', 'EUR', 'SPY', 'US_Manuf_PMI', 'EZ_Rates_10Y', etc.
|
50
|
+
quote_ccy: str, optional, default None
|
51
|
+
Ticker symbol for quote asset, e.g. 'USDT' for BTCUSDT (bitcoin in Tether USD), 'GBP' for EURGBP, etc.
|
52
|
+
markets: list or str, optional, default None
|
53
|
+
Markets/traded pairs of base assets vs quote assets, e.g. 'BTC/USDT', 'EUR/USD', 'SPY/USD', etc.
|
46
54
|
freq: str, default 'd'
|
47
55
|
Frequency of data observations. Defaults to daily 'd' which includes weekends for cryptoassets.
|
48
|
-
quote_ccy: str, optional, default None
|
49
|
-
Quote currency for base asset, e.g. 'GBP' for EURGBP, 'USD' for BTCUSD (bitcoin in dollars), etc.
|
50
56
|
exch: str, optional, default None
|
51
57
|
Name of asset exchange, e.g. 'Binance', 'FTX', 'IEX', 'Nasdaq', etc.
|
52
58
|
mkt_type: str, optional, default 'spot'
|
@@ -72,18 +78,26 @@ class DataRequest:
|
|
72
78
|
source_tickers: list or str, optional, default None
|
73
79
|
List or string of ticker symbols for assets or time series in the format used by the
|
74
80
|
data source. If None, tickers will be converted from CryptoDataPy to data source format.
|
81
|
+
source_markets: list or str, optional, default None
|
82
|
+
List or string of markets/traded pairs of base assets vs quote assets in the format used by the
|
83
|
+
data source. If None, markets will be converted from CryptoDataPy to data source format.
|
75
84
|
source_freq: str, optional, default None
|
76
85
|
Frequency of observations for assets or time series in format used by data source. If None,
|
77
86
|
frequency will be converted from CryptoDataPy to data source format.
|
87
|
+
source_start_date: str, int, datetime or pd.Timestamp, optional, default None
|
88
|
+
Start date for data request in format used by data source.
|
89
|
+
source_end_date: str, int, datetime or pd.Timestamp, optional, default None
|
90
|
+
End date for data request in format used by data source.
|
78
91
|
source_fields: list or str, optional, default None
|
79
92
|
List or string of fields for assets or time series in format used by data source. If None,
|
80
93
|
fields will be converted from CryptoDataPy to data source format.
|
81
94
|
"""
|
82
95
|
# params
|
83
|
-
self.source = source #
|
96
|
+
self.source = source # name of data source
|
84
97
|
self.tickers = tickers # tickers
|
85
|
-
self.freq = freq # frequency
|
86
98
|
self.quote_ccy = quote_ccy # quote ccy
|
99
|
+
self.markets = markets # markets
|
100
|
+
self.freq = freq # frequency
|
87
101
|
self.exch = exch # exchange
|
88
102
|
self.mkt_type = mkt_type # market type
|
89
103
|
self.start_date = start_date # start date
|
@@ -95,7 +109,10 @@ class DataRequest:
|
|
95
109
|
self.trials = trials # number of times to try query request
|
96
110
|
self.pause = pause # number of seconds to pause between query request trials
|
97
111
|
self.source_tickers = source_tickers # tickers used by data source
|
112
|
+
self.source_markets = source_markets
|
98
113
|
self.source_freq = source_freq # frequency used by data source
|
114
|
+
self.source_start_date = source_start_date # start date used by data source
|
115
|
+
self.source_end_date = source_end_date # end date used by data source
|
99
116
|
self.source_fields = source_fields # fields used by data source
|
100
117
|
|
101
118
|
@property
|
@@ -151,6 +168,46 @@ class DataRequest:
|
|
151
168
|
else:
|
152
169
|
raise TypeError("Tickers must be a string or list of strings (tickers).")
|
153
170
|
|
171
|
+
@property
|
172
|
+
def quote_ccy(self):
|
173
|
+
"""
|
174
|
+
Returns quote currency for data request.
|
175
|
+
"""
|
176
|
+
return self._quote_ccy
|
177
|
+
|
178
|
+
@quote_ccy.setter
|
179
|
+
def quote_ccy(self, quote):
|
180
|
+
"""
|
181
|
+
Sets quote currency for data request.
|
182
|
+
"""
|
183
|
+
if quote is None:
|
184
|
+
self._quote_ccy = quote
|
185
|
+
elif isinstance(quote, str):
|
186
|
+
self._quote_ccy = quote
|
187
|
+
else:
|
188
|
+
raise TypeError("Quote currency must be a string.")
|
189
|
+
|
190
|
+
@property
|
191
|
+
def markets(self):
|
192
|
+
"""
|
193
|
+
Returns markets for data request.
|
194
|
+
"""
|
195
|
+
return self._markets
|
196
|
+
|
197
|
+
@markets.setter
|
198
|
+
def markets(self, markets):
|
199
|
+
"""
|
200
|
+
Sets markets for data request.
|
201
|
+
"""
|
202
|
+
if markets is None:
|
203
|
+
self._markets = markets
|
204
|
+
elif isinstance(markets, str):
|
205
|
+
self._markets = [markets]
|
206
|
+
elif isinstance(markets, list):
|
207
|
+
self._markets = markets
|
208
|
+
else:
|
209
|
+
raise TypeError("Markets must be a string or list of strings (markets).")
|
210
|
+
|
154
211
|
@property
|
155
212
|
def freq(self):
|
156
213
|
"""
|
@@ -197,32 +254,15 @@ class DataRequest:
|
|
197
254
|
"y": "yearly",
|
198
255
|
}
|
199
256
|
|
200
|
-
if frequency
|
257
|
+
if frequency is None:
|
258
|
+
self._frequency = frequency
|
259
|
+
elif frequency not in list(freq_dict.keys()):
|
201
260
|
raise ValueError(
|
202
261
|
f"{frequency} is an invalid data frequency. Valid frequencies are: {freq_dict}"
|
203
262
|
)
|
204
263
|
else:
|
205
264
|
self._frequency = frequency
|
206
265
|
|
207
|
-
@property
|
208
|
-
def quote_ccy(self):
|
209
|
-
"""
|
210
|
-
Returns quote currency for data request.
|
211
|
-
"""
|
212
|
-
return self._quote_ccy
|
213
|
-
|
214
|
-
@quote_ccy.setter
|
215
|
-
def quote_ccy(self, quote):
|
216
|
-
"""
|
217
|
-
Sets quote currency for data request.
|
218
|
-
"""
|
219
|
-
if quote is None:
|
220
|
-
self._quote_ccy = quote
|
221
|
-
elif isinstance(quote, str):
|
222
|
-
self._quote_ccy = quote
|
223
|
-
else:
|
224
|
-
raise TypeError("Quote currency must be a string.")
|
225
|
-
|
226
266
|
@property
|
227
267
|
def exch(self):
|
228
268
|
"""
|
@@ -484,6 +524,29 @@ class DataRequest:
|
|
484
524
|
"Source tickers must be a string or list of strings (tickers) in data source's format."
|
485
525
|
)
|
486
526
|
|
527
|
+
@property
|
528
|
+
def source_markets(self):
|
529
|
+
"""
|
530
|
+
Returns markets for data request in data source format.
|
531
|
+
"""
|
532
|
+
return self._source_markets
|
533
|
+
|
534
|
+
@source_markets.setter
|
535
|
+
def source_markets(self, markets):
|
536
|
+
"""
|
537
|
+
Sets markets for data request in data source format.
|
538
|
+
"""
|
539
|
+
if markets is None:
|
540
|
+
self._source_markets = markets
|
541
|
+
elif isinstance(markets, str):
|
542
|
+
self._source_markets = [markets]
|
543
|
+
elif isinstance(markets, list):
|
544
|
+
self._source_markets = markets
|
545
|
+
else:
|
546
|
+
raise TypeError(
|
547
|
+
"Source markets must be a string or list of strings (markets) in data source's format."
|
548
|
+
)
|
549
|
+
|
487
550
|
@property
|
488
551
|
def source_freq(self):
|
489
552
|
"""
|
@@ -505,6 +568,60 @@ class DataRequest:
|
|
505
568
|
"Source data frequency must be a string in data source's format."
|
506
569
|
)
|
507
570
|
|
571
|
+
@property
|
572
|
+
def source_start_date(self):
|
573
|
+
"""
|
574
|
+
Returns start date for data request in data source format.
|
575
|
+
"""
|
576
|
+
return self._source_start_date
|
577
|
+
|
578
|
+
@source_start_date.setter
|
579
|
+
def source_start_date(self, start_date):
|
580
|
+
"""
|
581
|
+
Sets start date for data request in data source format.
|
582
|
+
"""
|
583
|
+
if start_date is None:
|
584
|
+
self._source_start_date = start_date
|
585
|
+
elif isinstance(start_date, str):
|
586
|
+
self._source_start_date = start_date
|
587
|
+
elif isinstance(start_date, int):
|
588
|
+
self._source_start_date = start_date
|
589
|
+
elif isinstance(start_date, datetime):
|
590
|
+
self._source_start_date = start_date
|
591
|
+
elif isinstance(start_date, pd.Timestamp):
|
592
|
+
self._source_start_date = start_date
|
593
|
+
else:
|
594
|
+
raise ValueError(
|
595
|
+
'Start date must be in "YYYY-MM-DD" string, integer, datetime or pd.Timestamp format.'
|
596
|
+
)
|
597
|
+
|
598
|
+
@property
|
599
|
+
def source_end_date(self):
|
600
|
+
"""
|
601
|
+
Returns end date for data request in data source format.
|
602
|
+
"""
|
603
|
+
return self._source_end_date
|
604
|
+
|
605
|
+
@source_end_date.setter
|
606
|
+
def source_end_date(self, end_date):
|
607
|
+
"""
|
608
|
+
Sets end date for data request in data source format.
|
609
|
+
"""
|
610
|
+
if end_date is None:
|
611
|
+
self._source_end_date = end_date
|
612
|
+
elif isinstance(end_date, str):
|
613
|
+
self._source_end_date = end_date
|
614
|
+
elif isinstance(end_date, int):
|
615
|
+
self._source_end_date = end_date
|
616
|
+
elif isinstance(end_date, datetime):
|
617
|
+
self._source_end_date = end_date
|
618
|
+
elif isinstance(end_date, pd.Timestamp):
|
619
|
+
self._source_end_date = end_date
|
620
|
+
else:
|
621
|
+
raise ValueError(
|
622
|
+
'End date must be in "YYYY-MM-DD" string, integer, datetime or pd.Timestamp format.'
|
623
|
+
)
|
624
|
+
|
508
625
|
@property
|
509
626
|
def source_fields(self):
|
510
627
|
"""
|
@@ -555,15 +672,59 @@ class DataRequest:
|
|
555
672
|
# get request
|
556
673
|
try:
|
557
674
|
resp = requests.get(url, params=params, headers=headers)
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
675
|
+
# check for status code
|
676
|
+
resp.raise_for_status()
|
677
|
+
|
678
|
+
return resp.json()
|
679
|
+
|
680
|
+
# handle HTTP errors
|
681
|
+
except requests.exceptions.HTTPError as http_err:
|
682
|
+
status_code = resp.status_code
|
683
|
+
|
684
|
+
# Tailored handling for different status codes
|
685
|
+
if status_code == 400:
|
686
|
+
logging.warning(f"Bad Request (400): {resp.text}")
|
687
|
+
elif status_code == 401:
|
688
|
+
logging.warning("Unauthorized (401): Check the authentication credentials.")
|
689
|
+
elif status_code == 403:
|
690
|
+
logging.warning("Forbidden (403): You do not have permission to access this resource.")
|
691
|
+
elif status_code == 404:
|
692
|
+
logging.warning("Not Found (404): The requested resource could not be found.")
|
693
|
+
elif status_code == 500:
|
694
|
+
logging.error("Internal Server Error (500): The server encountered an error.")
|
695
|
+
elif status_code == 503:
|
696
|
+
logging.error("Service Unavailable (503): The server is temporarily unavailable.")
|
697
|
+
else:
|
698
|
+
logging.error(f"HTTP error occurred: {http_err} (Status Code: {status_code})")
|
699
|
+
logging.error(f"Response Content: {resp.text}")
|
700
|
+
|
701
|
+
# Increment attempts and log warning
|
702
|
+
attempts += 1
|
703
|
+
logging.warning(f"Attempt #{attempts}: Failed to get data due to: {http_err}")
|
704
|
+
sleep(self.pause) # Pause before retrying
|
705
|
+
if attempts == self.trials:
|
706
|
+
logging.error("Max attempts reached. Unable to fetch data.")
|
707
|
+
break
|
708
|
+
|
709
|
+
# handle non-HTTP exceptions (e.g., network issues)
|
710
|
+
except requests.exceptions.RequestException as req_err:
|
562
711
|
attempts += 1
|
563
|
-
logging.warning(f"
|
712
|
+
logging.warning(f"Request error on attempt #{attempts}: {req_err}. "
|
713
|
+
f"Retrying after {self.pause} seconds...")
|
564
714
|
sleep(self.pause)
|
565
|
-
if attempts ==
|
715
|
+
if attempts == self.trials:
|
716
|
+
logging.error("Max attempts reached. Unable to fetch data due to request errors.")
|
566
717
|
break
|
567
718
|
|
568
|
-
|
569
|
-
|
719
|
+
# handle other exceptions
|
720
|
+
except Exception as e:
|
721
|
+
attempts += 1
|
722
|
+
logging.warning(f"An unexpected error occurred: {e}. "
|
723
|
+
f"Retrying after {self.pause} seconds...")
|
724
|
+
sleep(self.pause)
|
725
|
+
if attempts == self.trials:
|
726
|
+
logging.error("Max attempts reached. Unable to fetch data due to request errors.")
|
727
|
+
break
|
728
|
+
|
729
|
+
# return None if the API call fails
|
730
|
+
return None
|
@@ -0,0 +1,33 @@
|
|
1
|
+
{
|
2
|
+
"cells": [
|
3
|
+
{
|
4
|
+
"cell_type": "code",
|
5
|
+
"execution_count": null,
|
6
|
+
"id": "89bd834b-aec0-45fe-82a0-c5d873c9518a",
|
7
|
+
"metadata": {},
|
8
|
+
"outputs": [],
|
9
|
+
"source": []
|
10
|
+
}
|
11
|
+
],
|
12
|
+
"metadata": {
|
13
|
+
"kernelspec": {
|
14
|
+
"display_name": "cryptodatapy",
|
15
|
+
"language": "python",
|
16
|
+
"name": "cryptodatapy"
|
17
|
+
},
|
18
|
+
"language_info": {
|
19
|
+
"codemirror_mode": {
|
20
|
+
"name": "ipython",
|
21
|
+
"version": 3
|
22
|
+
},
|
23
|
+
"file_extension": ".py",
|
24
|
+
"mimetype": "text/x-python",
|
25
|
+
"name": "python",
|
26
|
+
"nbconvert_exporter": "python",
|
27
|
+
"pygments_lexer": "ipython3",
|
28
|
+
"version": "3.9.12"
|
29
|
+
}
|
30
|
+
},
|
31
|
+
"nbformat": 4,
|
32
|
+
"nbformat_minor": 5
|
33
|
+
}
|