cryptodatapy 0.2.7__tar.gz → 0.2.9__tar.gz
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-0.2.7 → cryptodatapy-0.2.9}/PKG-INFO +1 -1
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/pyproject.toml +4 -1
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/src/cryptodatapy/extract/datarequest.py +27 -2
- cryptodatapy-0.2.9/src/cryptodatapy/extract/exchanges/__init__.py +2 -0
- cryptodatapy-0.2.9/src/cryptodatapy/extract/exchanges/dydx.py +137 -0
- cryptodatapy-0.2.9/src/cryptodatapy/extract/exchanges/exchange.py +439 -0
- cryptodatapy-0.2.9/src/cryptodatapy/extract/libraries/ccxt_api.py +1490 -0
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/src/cryptodatapy/extract/libraries/library.py +1 -3
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/src/cryptodatapy/extract/libraries/pandasdr_api.py +151 -137
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/src/cryptodatapy/extract/web/web.py +62 -0
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/src/cryptodatapy/transform/convertparams.py +73 -164
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/src/cryptodatapy/transform/wrangle.py +43 -23
- cryptodatapy-0.2.7/src/cryptodatapy/conf/fx_tickers.csv +0 -31
- cryptodatapy-0.2.7/src/cryptodatapy/extract/data_vendors/CoinMetrics.ipynb +0 -747
- cryptodatapy-0.2.7/src/cryptodatapy/extract/libraries/Untitled.ipynb +0 -33
- cryptodatapy-0.2.7/src/cryptodatapy/extract/libraries/ccxt.ipynb +0 -747
- cryptodatapy-0.2.7/src/cryptodatapy/extract/libraries/ccxt_api.py +0 -995
- cryptodatapy-0.2.7/src/cryptodatapy/extract/libraries/yfinance_api.py +0 -511
- cryptodatapy-0.2.7/src/cryptodatapy/transform/cc_onchain_data.csv +0 -118423
- cryptodatapy-0.2.7/src/cryptodatapy/transform/clean_onchain_data.ipynb +0 -4750
- cryptodatapy-0.2.7/src/cryptodatapy/transform/clean_perp_futures_ohlcv.ipynb +0 -2819
- cryptodatapy-0.2.7/src/cryptodatapy/transform/cmdty_data.ipynb +0 -402
- cryptodatapy-0.2.7/src/cryptodatapy/transform/credit_data.ipynb +0 -291
- cryptodatapy-0.2.7/src/cryptodatapy/transform/eqty_data.ipynb +0 -836
- cryptodatapy-0.2.7/src/cryptodatapy/transform/global_credit_data_daily.parquet +0 -0
- cryptodatapy-0.2.7/src/cryptodatapy/transform/rates_data.ipynb +0 -465
- cryptodatapy-0.2.7/src/cryptodatapy/transform/us_rates_daily.csv +0 -227752
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/LICENSE +0 -0
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/README.md +0 -0
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/src/cryptodatapy/__init__.py +0 -0
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/src/cryptodatapy/conf/__init__.py +0 -0
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/src/cryptodatapy/conf/fields.csv +0 -0
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/src/cryptodatapy/conf/tickers.csv +0 -0
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/src/cryptodatapy/datasets/__init__.py +0 -0
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/src/cryptodatapy/datasets/br_econ_calendar.csv +0 -0
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/src/cryptodatapy/datasets/ca_econ_calendar.csv +0 -0
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/src/cryptodatapy/datasets/cn_econ_calendar.csv +0 -0
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/src/cryptodatapy/datasets/de_econ_calendar.csv +0 -0
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/src/cryptodatapy/datasets/ez_econ_calendar.csv +0 -0
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/src/cryptodatapy/datasets/fr_econ_calendar.csv +0 -0
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/src/cryptodatapy/datasets/gb_econ_calendar.csv +0 -0
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/src/cryptodatapy/datasets/get_econ_calendars.py +0 -0
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/src/cryptodatapy/datasets/id_econ_calendar.csv +0 -0
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/src/cryptodatapy/datasets/in_econ_calendar.csv +0 -0
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/src/cryptodatapy/datasets/it_econ_calendar.csv +0 -0
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/src/cryptodatapy/datasets/jp_econ_calendar.csv +0 -0
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/src/cryptodatapy/datasets/kr_econ_calendar.csv +0 -0
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/src/cryptodatapy/datasets/mx_econ_calendar.csv +0 -0
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/src/cryptodatapy/datasets/ru_econ_calendar.csv +0 -0
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/src/cryptodatapy/datasets/tr_econ_calendar.csv +0 -0
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/src/cryptodatapy/datasets/us_econ_calendar.csv +0 -0
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/src/cryptodatapy/extract/__init__.py +0 -0
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/src/cryptodatapy/extract/data_vendors/.ipynb_checkpoints/CCXT-checkpoint.ipynb +0 -0
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/src/cryptodatapy/extract/data_vendors/.ipynb_checkpoints/DBNomics-checkpoint.ipynb +0 -0
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/src/cryptodatapy/extract/data_vendors/.ipynb_checkpoints/InvestPy-checkpoint.ipynb +0 -0
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/src/cryptodatapy/extract/data_vendors/.ipynb_checkpoints/NasdaqDataLink-checkpoint.ipynb +0 -0
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/src/cryptodatapy/extract/data_vendors/.ipynb_checkpoints/PandasDataReader-checkpoint.ipynb +0 -0
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/src/cryptodatapy/extract/data_vendors/__init__.py +0 -0
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/src/cryptodatapy/extract/data_vendors/coinmetrics_api.py +0 -0
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/src/cryptodatapy/extract/data_vendors/cryptocompare_api.py +0 -0
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/src/cryptodatapy/extract/data_vendors/datavendor.py +0 -0
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/src/cryptodatapy/extract/data_vendors/glassnode_api.py +0 -0
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/src/cryptodatapy/extract/data_vendors/tiingo_api.py +0 -0
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/src/cryptodatapy/extract/getdata.py +0 -0
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/src/cryptodatapy/extract/libraries/__init__.py +0 -0
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/src/cryptodatapy/extract/libraries/dbnomics_api.py +0 -0
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/src/cryptodatapy/extract/libraries/investpy_api.py +0 -0
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/src/cryptodatapy/extract/web/__init__.py +0 -0
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/src/cryptodatapy/extract/web/aqr.py +0 -0
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/src/cryptodatapy/transform/__init__.py +0 -0
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/src/cryptodatapy/transform/clean.py +0 -0
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/src/cryptodatapy/transform/filter.py +0 -0
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/src/cryptodatapy/transform/impute.py +0 -0
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/src/cryptodatapy/transform/od.py +0 -0
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/src/cryptodatapy/util/__init__.py +0 -0
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/src/cryptodatapy/util/datacatalog.py +0 -0
- {cryptodatapy-0.2.7 → cryptodatapy-0.2.9}/src/cryptodatapy/util/datacredentials.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
[tool.poetry]
|
2
2
|
name = "cryptodatapy"
|
3
|
-
version = "0.2.
|
3
|
+
version = "0.2.9"
|
4
4
|
description = "Cryptoasset data library"
|
5
5
|
authors = ["Systamental"]
|
6
6
|
license = "Apache License 2.0"
|
@@ -43,6 +43,9 @@ sphinx-rtd-theme = ">=1.0.0"
|
|
43
43
|
[tool.poetry.group.dev.dependencies]
|
44
44
|
pytest-asyncio = ">=0.24.0"
|
45
45
|
|
46
|
+
[tool.pytest.ini_options]
|
47
|
+
asyncio_default_fixture_loop_scope = "function"
|
48
|
+
|
46
49
|
[build-system]
|
47
50
|
requires = ["poetry-core>=1.0.0"]
|
48
51
|
build-backend = "poetry.core.masonry.api"
|
@@ -12,7 +12,6 @@ class DataRequest:
|
|
12
12
|
"""
|
13
13
|
Data request class which contains parameters for data retrieval.
|
14
14
|
"""
|
15
|
-
|
16
15
|
def __init__(
|
17
16
|
self,
|
18
17
|
source: str = "ccxt",
|
@@ -21,6 +20,7 @@ class DataRequest:
|
|
21
20
|
markets: Optional[Union[str, List[str]]] = None,
|
22
21
|
freq: str = "d",
|
23
22
|
exch: Optional[str] = None,
|
23
|
+
countries: Optional[Union[str, List[str]]] = None,
|
24
24
|
mkt_type: Optional[str] = "spot",
|
25
25
|
start_date: Optional[Union[str, datetime, pd.Timestamp]] = None,
|
26
26
|
end_date: Optional[Union[str, datetime, pd.Timestamp]] = None,
|
@@ -55,6 +55,8 @@ class DataRequest:
|
|
55
55
|
Frequency of data observations. Defaults to daily 'd' which includes weekends for cryptoassets.
|
56
56
|
exch: str, optional, default None
|
57
57
|
Name of asset exchange, e.g. 'Binance', 'FTX', 'IEX', 'Nasdaq', etc.
|
58
|
+
countries: list or str, optional, default None
|
59
|
+
Country codes for which to pull data, e.g. 'US', 'GB', 'CN', 'JP', etc.
|
58
60
|
mkt_type: str, optional, default 'spot'
|
59
61
|
Market type, e.g. 'spot ', 'future', 'perpetual_future', 'option'.
|
60
62
|
start_date: str, datetime or pd.Timestamp, optional, default None
|
@@ -99,6 +101,7 @@ class DataRequest:
|
|
99
101
|
self.markets = markets # markets
|
100
102
|
self.freq = freq # frequency
|
101
103
|
self.exch = exch # exchange
|
104
|
+
self.countries = countries # country codes
|
102
105
|
self.mkt_type = mkt_type # market type
|
103
106
|
self.start_date = start_date # start date
|
104
107
|
self.end_date = end_date # end date
|
@@ -282,6 +285,27 @@ class DataRequest:
|
|
282
285
|
else:
|
283
286
|
raise TypeError("Exchange must be a string.")
|
284
287
|
|
288
|
+
@property
|
289
|
+
def countries(self):
|
290
|
+
"""
|
291
|
+
Returns country codes for data request.
|
292
|
+
"""
|
293
|
+
return self._countries
|
294
|
+
|
295
|
+
@countries.setter
|
296
|
+
def countries(self, countries):
|
297
|
+
"""
|
298
|
+
Sets country codes for data request.
|
299
|
+
"""
|
300
|
+
if countries is None:
|
301
|
+
self._countries = countries
|
302
|
+
elif isinstance(countries, str):
|
303
|
+
self._countries = [countries]
|
304
|
+
elif isinstance(countries, list):
|
305
|
+
self._countries = countries
|
306
|
+
else:
|
307
|
+
raise TypeError("Country codes must be a string or list of strings.")
|
308
|
+
|
285
309
|
@property
|
286
310
|
def mkt_type(self):
|
287
311
|
"""
|
@@ -665,7 +689,8 @@ class DataRequest:
|
|
665
689
|
Data response in JSON format.
|
666
690
|
"""
|
667
691
|
# set number of attempts
|
668
|
-
attempts = 0
|
692
|
+
attempts, resp = 0, None
|
693
|
+
|
669
694
|
# run a while loop in case the attempt fails
|
670
695
|
while attempts < self.trials:
|
671
696
|
|
@@ -0,0 +1,137 @@
|
|
1
|
+
import logging
|
2
|
+
from typing import Any, Dict, List, Optional, Union
|
3
|
+
|
4
|
+
import pandas as pd
|
5
|
+
import asyncio
|
6
|
+
|
7
|
+
from cryptodatapy.extract.datarequest import DataRequest
|
8
|
+
from cryptodatapy.extract.exchanges.exchange import Exchange
|
9
|
+
from cryptodatapy.transform.convertparams import ConvertParams
|
10
|
+
from cryptodatapy.transform.wrangle import WrangleData
|
11
|
+
from cryptodatapy.util.datacredentials import DataCredentials
|
12
|
+
|
13
|
+
# data credentials
|
14
|
+
data_cred = DataCredentials()
|
15
|
+
|
16
|
+
|
17
|
+
class Dydx(Exchange):
|
18
|
+
"""
|
19
|
+
Retrieves data from dydx exchange.
|
20
|
+
"""
|
21
|
+
def __init__(
|
22
|
+
self,
|
23
|
+
name: str = "dydx",
|
24
|
+
exch_type: str = "dex",
|
25
|
+
is_active: bool = True,
|
26
|
+
categories: Union[str, List[str]] = "crypto",
|
27
|
+
assets: Optional[Dict[str, List[str]]] = None,
|
28
|
+
markets: Optional[Dict[str, List[str]]] = None,
|
29
|
+
market_types: List[str] = ["spot", "future", "perpetual_future", "option"],
|
30
|
+
fields: Optional[List[str]] = ["open", "high", "low", "close", "volume", "funding_rate", 'oi'],
|
31
|
+
frequencies: Optional[Dict[str, Union[str, int]]] = None,
|
32
|
+
fees: Optional[Dict[str, float]] = {'spot': {'maker': 0.0, 'taker': 0.0},
|
33
|
+
'perpetual_future': {'maker': 0.0, 'taker': 0.0}
|
34
|
+
},
|
35
|
+
base_url: Optional[str] = None,
|
36
|
+
api_key: Optional[str] = None,
|
37
|
+
max_obs_per_call: Optional[int] = None,
|
38
|
+
rate_limit: Optional[Any] = None
|
39
|
+
):
|
40
|
+
"""
|
41
|
+
Initializes the Dydx class.
|
42
|
+
|
43
|
+
Parameters:
|
44
|
+
-----------
|
45
|
+
name: str
|
46
|
+
The name of the exchange.
|
47
|
+
exch_type: str
|
48
|
+
The type of the exchange.
|
49
|
+
is_active: bool
|
50
|
+
Whether the exchange is active.
|
51
|
+
categories: Union[str, List[str]]
|
52
|
+
The categories of the exchange.
|
53
|
+
assets: Optional[Dict[str, List[str]]]
|
54
|
+
The assets traded on the exchange.
|
55
|
+
markets: Optional[Dict[str, List[str]]]
|
56
|
+
The markets traded on the exchange.
|
57
|
+
market_types: List[str]
|
58
|
+
The types of markets traded on the exchange.
|
59
|
+
fields: Optional[List[str]]
|
60
|
+
The fields to retrieve from the exchange.
|
61
|
+
frequencies: Optional[Dict[str, Union[str, int]]]
|
62
|
+
The frequencies of the data to retrieve.
|
63
|
+
fees: Optional[Dict[str, float]]
|
64
|
+
The fees for the exchange.
|
65
|
+
base_url: Optional[str]
|
66
|
+
The base url of the exchange.
|
67
|
+
api_key: Optional[str]
|
68
|
+
The api key for the exchange.
|
69
|
+
max_obs_per_call: Optional[int]
|
70
|
+
The maximum number of observations per call.
|
71
|
+
rate_limit: Optional[Any]
|
72
|
+
The rate limit for the exchange.
|
73
|
+
"""
|
74
|
+
super().__init__(
|
75
|
+
name=name,
|
76
|
+
exch_type=exch_type,
|
77
|
+
is_active=is_active,
|
78
|
+
categories=categories,
|
79
|
+
assets=assets,
|
80
|
+
markets=markets,
|
81
|
+
market_types=market_types,
|
82
|
+
fields=fields,
|
83
|
+
frequencies=frequencies,
|
84
|
+
fees=fees,
|
85
|
+
base_url=base_url,
|
86
|
+
api_key=api_key,
|
87
|
+
max_obs_per_call=max_obs_per_call,
|
88
|
+
rate_limit=rate_limit
|
89
|
+
)
|
90
|
+
self.data_req = None
|
91
|
+
self.data = pd.DataFrame()
|
92
|
+
|
93
|
+
def get_assets_info(self):
|
94
|
+
pass
|
95
|
+
|
96
|
+
def get_markets_info(self):
|
97
|
+
pass
|
98
|
+
|
99
|
+
def get_fields_info(self, data_type: Optional[str]):
|
100
|
+
pass
|
101
|
+
|
102
|
+
def get_frequencies_info(self):
|
103
|
+
pass
|
104
|
+
|
105
|
+
def get_rate_limit_info(self):
|
106
|
+
pass
|
107
|
+
|
108
|
+
def get_metadata(self):
|
109
|
+
pass
|
110
|
+
|
111
|
+
def _fetch_ohlcv(self):
|
112
|
+
pass
|
113
|
+
|
114
|
+
def _fetch_funding_rates(self):
|
115
|
+
pass
|
116
|
+
|
117
|
+
def _fetch_open_interest(self):
|
118
|
+
pass
|
119
|
+
|
120
|
+
def _convert_params(self):
|
121
|
+
pass
|
122
|
+
|
123
|
+
@staticmethod
|
124
|
+
def _wrangle_data_resp(data_req: DataRequest, data_resp: Union[Dict[str, Any], pd.DataFrame]) -> pd.DataFrame:
|
125
|
+
pass
|
126
|
+
|
127
|
+
def _fetch_tidy_ohlcv(self):
|
128
|
+
pass
|
129
|
+
|
130
|
+
def _fetch_tidy_funding_rates(self):
|
131
|
+
pass
|
132
|
+
|
133
|
+
def _fetch_tidy_open_interest(self):
|
134
|
+
pass
|
135
|
+
|
136
|
+
def get_data(self, data_req) -> pd.DataFrame:
|
137
|
+
pass
|
@@ -0,0 +1,439 @@
|
|
1
|
+
from abc import ABC, abstractmethod
|
2
|
+
from typing import Any, Dict, List, Optional, Union
|
3
|
+
|
4
|
+
import pandas as pd
|
5
|
+
|
6
|
+
from cryptodatapy.extract.datarequest import DataRequest
|
7
|
+
|
8
|
+
|
9
|
+
class Exchange(ABC):
|
10
|
+
"""
|
11
|
+
Abstract base class for crypto exchanges (CEX or DEX).
|
12
|
+
|
13
|
+
This class provides a blueprint for interacting with crypto exchanges,
|
14
|
+
including authentication, data retrieval, and trading functionality.
|
15
|
+
"""
|
16
|
+
def __init__(
|
17
|
+
self,
|
18
|
+
name,
|
19
|
+
exch_type,
|
20
|
+
is_active,
|
21
|
+
categories,
|
22
|
+
assets,
|
23
|
+
markets,
|
24
|
+
market_types,
|
25
|
+
fields,
|
26
|
+
frequencies,
|
27
|
+
fees,
|
28
|
+
base_url,
|
29
|
+
api_key,
|
30
|
+
max_obs_per_call,
|
31
|
+
rate_limit
|
32
|
+
):
|
33
|
+
self.name = name
|
34
|
+
self.exch_type = exch_type
|
35
|
+
self.is_active = is_active
|
36
|
+
self.categories = categories
|
37
|
+
self.assets = assets
|
38
|
+
self.markets = markets
|
39
|
+
self.market_types = market_types
|
40
|
+
self.fields = fields
|
41
|
+
self.frequencies = frequencies
|
42
|
+
self.fees = fees
|
43
|
+
self.base_url = base_url
|
44
|
+
self.api_key = api_key
|
45
|
+
self.max_obs_per_call = max_obs_per_call
|
46
|
+
self.rate_limit = rate_limit
|
47
|
+
|
48
|
+
@property
|
49
|
+
def name(self):
|
50
|
+
"""
|
51
|
+
Returns the type of exchange.
|
52
|
+
"""
|
53
|
+
return self._name
|
54
|
+
|
55
|
+
@name.setter
|
56
|
+
def name(self, name: str):
|
57
|
+
"""
|
58
|
+
Sets the name of the exchange.
|
59
|
+
"""
|
60
|
+
if name is None:
|
61
|
+
self._name = name
|
62
|
+
elif not isinstance(name, str):
|
63
|
+
raise TypeError("Exchange name must be a string.")
|
64
|
+
else:
|
65
|
+
self._name = name
|
66
|
+
|
67
|
+
@property
|
68
|
+
def exch_type(self):
|
69
|
+
"""
|
70
|
+
Returns the type of exchange.
|
71
|
+
"""
|
72
|
+
return self._exch_type
|
73
|
+
|
74
|
+
@exch_type.setter
|
75
|
+
def exch_type(self, exch_type: str):
|
76
|
+
"""
|
77
|
+
Sets the type of exchange.
|
78
|
+
"""
|
79
|
+
valid_exch_types = ['cex', 'dex']
|
80
|
+
|
81
|
+
if exch_type is None:
|
82
|
+
self._exch_type = exch_type
|
83
|
+
elif not isinstance(exch_type, str):
|
84
|
+
raise TypeError("Exchange type must be a string.")
|
85
|
+
elif exch_type not in valid_exch_types:
|
86
|
+
raise ValueError(f"{exch_type} is invalid. Valid exchange types are: {valid_exch_types}")
|
87
|
+
else:
|
88
|
+
self._exch_type = exch_type
|
89
|
+
|
90
|
+
@property
|
91
|
+
def is_active(self):
|
92
|
+
"""
|
93
|
+
Returns whether the exchange is active.
|
94
|
+
"""
|
95
|
+
return self._is_active
|
96
|
+
|
97
|
+
@is_active.setter
|
98
|
+
def is_active(self, is_active: bool):
|
99
|
+
"""
|
100
|
+
Sets whether the exchange is active.
|
101
|
+
"""
|
102
|
+
if is_active is None:
|
103
|
+
self._is_active = is_active
|
104
|
+
elif not isinstance(is_active, bool):
|
105
|
+
raise TypeError("is_active must be a boolean.")
|
106
|
+
else:
|
107
|
+
self._is_active = is_active
|
108
|
+
|
109
|
+
@property
|
110
|
+
def categories(self):
|
111
|
+
"""
|
112
|
+
Returns a list of available categories for the data vendor.
|
113
|
+
"""
|
114
|
+
return self._categories
|
115
|
+
|
116
|
+
@categories.setter
|
117
|
+
def categories(self, categories: Union[str, List[str]]):
|
118
|
+
"""
|
119
|
+
Sets a list of available categories for the data vendor.
|
120
|
+
"""
|
121
|
+
valid_categories = [
|
122
|
+
"crypto",
|
123
|
+
"fx",
|
124
|
+
"eqty",
|
125
|
+
"cmdty",
|
126
|
+
"index",
|
127
|
+
"rates",
|
128
|
+
"bonds",
|
129
|
+
"credit",
|
130
|
+
"macro",
|
131
|
+
"alt",
|
132
|
+
]
|
133
|
+
cat = []
|
134
|
+
|
135
|
+
if categories is None:
|
136
|
+
self._categories = categories
|
137
|
+
else:
|
138
|
+
if not isinstance(categories, str) and not isinstance(categories, list):
|
139
|
+
raise TypeError("Categories must be a string or list of strings.")
|
140
|
+
if isinstance(categories, str):
|
141
|
+
categories = [categories]
|
142
|
+
for category in categories:
|
143
|
+
if category in valid_categories:
|
144
|
+
cat.append(category)
|
145
|
+
else:
|
146
|
+
raise ValueError(
|
147
|
+
f"{category} is invalid. Valid categories are: {valid_categories}"
|
148
|
+
)
|
149
|
+
self._categories = cat
|
150
|
+
|
151
|
+
@property
|
152
|
+
def assets(self):
|
153
|
+
"""
|
154
|
+
Returns a list of available assets for the data vendor.
|
155
|
+
"""
|
156
|
+
return self._assets
|
157
|
+
|
158
|
+
@assets.setter
|
159
|
+
def assets(self, assets: Optional[Union[str, List[str], Dict[str, List[str]]]]):
|
160
|
+
"""
|
161
|
+
Sets a list of available assets for the data vendor.
|
162
|
+
"""
|
163
|
+
if (
|
164
|
+
assets is None
|
165
|
+
or isinstance(assets, list)
|
166
|
+
or isinstance(assets, dict)
|
167
|
+
or isinstance(assets, pd.DataFrame)
|
168
|
+
):
|
169
|
+
self._assets = assets
|
170
|
+
elif isinstance(assets, str):
|
171
|
+
self._assets = [assets]
|
172
|
+
else:
|
173
|
+
raise TypeError(
|
174
|
+
"Assets must be a string (ticker), list of strings (tickers),"
|
175
|
+
" a dict with {cat: List[str]} key-value pairs or dataframe."
|
176
|
+
)
|
177
|
+
|
178
|
+
@abstractmethod
|
179
|
+
def get_assets_info(self):
|
180
|
+
"""
|
181
|
+
Gets info for available assets from the data vendor.
|
182
|
+
"""
|
183
|
+
# to be implemented by subclasses
|
184
|
+
|
185
|
+
@property
|
186
|
+
def markets(self):
|
187
|
+
"""
|
188
|
+
Returns a list of available markets for the data vendor.
|
189
|
+
"""
|
190
|
+
return self._markets
|
191
|
+
|
192
|
+
@markets.setter
|
193
|
+
def markets(self, markets: Optional[Union[str, List[str], Dict[str, List[str]]]]):
|
194
|
+
"""
|
195
|
+
Sets a list of available markets for the data vendor.
|
196
|
+
"""
|
197
|
+
if (
|
198
|
+
markets is None
|
199
|
+
or isinstance(markets, list)
|
200
|
+
or isinstance(markets, dict)
|
201
|
+
or isinstance(markets, pd.DataFrame)
|
202
|
+
):
|
203
|
+
self._markets = markets
|
204
|
+
elif isinstance(markets, str):
|
205
|
+
self._markets = [markets]
|
206
|
+
else:
|
207
|
+
raise TypeError(
|
208
|
+
"Markets must be a string (ticker), list of strings (tickers),"
|
209
|
+
" a dict with {cat: List[str]} key-value pairs or dataframe."
|
210
|
+
)
|
211
|
+
|
212
|
+
@abstractmethod
|
213
|
+
def get_markets_info(self):
|
214
|
+
"""
|
215
|
+
Gets info for available markets from the data vendor.
|
216
|
+
"""
|
217
|
+
# to be implemented by subclasses
|
218
|
+
|
219
|
+
@property
|
220
|
+
def market_types(self):
|
221
|
+
"""
|
222
|
+
Returns a list of available market types for the data vendor.
|
223
|
+
"""
|
224
|
+
return self._market_types
|
225
|
+
|
226
|
+
@market_types.setter
|
227
|
+
def market_types(self, market_types: Optional[Union[str, List[str]]]):
|
228
|
+
"""
|
229
|
+
Sets a list of available market types for the data vendor.
|
230
|
+
"""
|
231
|
+
valid_mkt_types, mkt_types_list = [
|
232
|
+
None,
|
233
|
+
"spot",
|
234
|
+
"etf",
|
235
|
+
"perpetual_future",
|
236
|
+
"future",
|
237
|
+
"swap",
|
238
|
+
"option",
|
239
|
+
], []
|
240
|
+
|
241
|
+
if market_types is None:
|
242
|
+
self._market_types = market_types
|
243
|
+
elif isinstance(market_types, str) and market_types in valid_mkt_types:
|
244
|
+
self._market_types = [market_types]
|
245
|
+
elif isinstance(market_types, list):
|
246
|
+
for mkt in market_types:
|
247
|
+
if mkt in valid_mkt_types:
|
248
|
+
mkt_types_list.append(mkt)
|
249
|
+
else:
|
250
|
+
raise ValueError(
|
251
|
+
f"{mkt} is invalid. Valid market types are: {valid_mkt_types}"
|
252
|
+
)
|
253
|
+
self._market_types = mkt_types_list
|
254
|
+
else:
|
255
|
+
raise TypeError("Market types must be a string or list of strings.")
|
256
|
+
|
257
|
+
@property
|
258
|
+
def fields(self):
|
259
|
+
"""
|
260
|
+
Returns a list of available fields for the data vendor.
|
261
|
+
"""
|
262
|
+
return self._fields
|
263
|
+
|
264
|
+
@fields.setter
|
265
|
+
def fields(self, fields: Optional[Union[str, List[str], Dict[str, List[str]]]]):
|
266
|
+
"""
|
267
|
+
Sets a list of available fields for the data vendor.
|
268
|
+
"""
|
269
|
+
if fields is None or isinstance(fields, list) or isinstance(fields, dict):
|
270
|
+
self._fields = fields
|
271
|
+
elif isinstance(fields, str):
|
272
|
+
self._fields = [fields]
|
273
|
+
else:
|
274
|
+
raise TypeError(
|
275
|
+
"Fields must be a string (field), list of strings (fields) or"
|
276
|
+
" a dict with {cat: List[str]} key-value pairs."
|
277
|
+
)
|
278
|
+
|
279
|
+
@abstractmethod
|
280
|
+
def get_fields_info(self, data_type: Optional[str]):
|
281
|
+
"""
|
282
|
+
Gets info for available fields from the data vendor.
|
283
|
+
"""
|
284
|
+
# to be implemented by subclasses
|
285
|
+
|
286
|
+
@property
|
287
|
+
def frequencies(self):
|
288
|
+
"""
|
289
|
+
Returns a list of available data frequencies for the data vendor.
|
290
|
+
"""
|
291
|
+
return self._frequencies
|
292
|
+
|
293
|
+
@frequencies.setter
|
294
|
+
def frequencies(
|
295
|
+
self, frequencies: Optional[Union[str, List[str], Dict[str, List[str]]]]
|
296
|
+
):
|
297
|
+
"""
|
298
|
+
Sets a list of available data frequencies for the data vendor.
|
299
|
+
"""
|
300
|
+
if (
|
301
|
+
frequencies is None
|
302
|
+
or isinstance(frequencies, list)
|
303
|
+
or isinstance(frequencies, dict)
|
304
|
+
):
|
305
|
+
self._frequencies = frequencies
|
306
|
+
elif isinstance(frequencies, str):
|
307
|
+
self._frequencies = [frequencies]
|
308
|
+
else:
|
309
|
+
raise TypeError(
|
310
|
+
"Frequencies must be a string (frequency), list of strings (frequencies) or"
|
311
|
+
" a dict with {cat: List[str]} key-value pairs."
|
312
|
+
)
|
313
|
+
|
314
|
+
@abstractmethod
|
315
|
+
def get_frequencies_info(self, data_type: Optional[str]):
|
316
|
+
"""
|
317
|
+
Gets info for available frequencies from the exchange.
|
318
|
+
"""
|
319
|
+
# to be implemented by subclasses
|
320
|
+
|
321
|
+
@property
|
322
|
+
def fees(self):
|
323
|
+
"""
|
324
|
+
Returns a list of fees for the data vendor.
|
325
|
+
"""
|
326
|
+
return self._fees
|
327
|
+
|
328
|
+
@fees.setter
|
329
|
+
def fees(self, fees: Optional[Union[float, Dict[str, float]]]):
|
330
|
+
"""
|
331
|
+
Sets a list of fees for the data vendor.
|
332
|
+
"""
|
333
|
+
if fees is None or isinstance(fees, float) or isinstance(fees, dict):
|
334
|
+
self._fees = fees
|
335
|
+
else:
|
336
|
+
raise TypeError("Fees must be a float or dict with {cat: float} key-value pairs.")
|
337
|
+
|
338
|
+
@property
|
339
|
+
def base_url(self):
|
340
|
+
"""
|
341
|
+
Returns the base url for the data vendor.
|
342
|
+
"""
|
343
|
+
return self._base_url
|
344
|
+
|
345
|
+
@base_url.setter
|
346
|
+
def base_url(self, url: Optional[str]):
|
347
|
+
"""
|
348
|
+
Sets the base url for the data vendor.
|
349
|
+
"""
|
350
|
+
if url is None or isinstance(url, str):
|
351
|
+
self._base_url = url
|
352
|
+
else:
|
353
|
+
raise TypeError(
|
354
|
+
"Base url must be a string containing the data vendor's base URL to which endpoint paths"
|
355
|
+
" are appended."
|
356
|
+
)
|
357
|
+
|
358
|
+
@property
|
359
|
+
def api_key(self):
|
360
|
+
"""
|
361
|
+
Returns the api key for the data vendor.
|
362
|
+
"""
|
363
|
+
return self._api_key
|
364
|
+
|
365
|
+
@api_key.setter
|
366
|
+
def api_key(self, api_key: Optional[str]):
|
367
|
+
"""
|
368
|
+
Sets the api key for the data vendor.
|
369
|
+
"""
|
370
|
+
if api_key is None or isinstance(api_key, str) or isinstance(api_key, dict):
|
371
|
+
self._api_key = api_key
|
372
|
+
else:
|
373
|
+
raise TypeError(
|
374
|
+
"Api key must be a string or dict with data source-api key key-value pairs."
|
375
|
+
)
|
376
|
+
|
377
|
+
@property
|
378
|
+
def max_obs_per_call(self):
|
379
|
+
"""
|
380
|
+
Returns the maximum observations per API call for the data vendor.
|
381
|
+
"""
|
382
|
+
return self._max_obs_per_call
|
383
|
+
|
384
|
+
@max_obs_per_call.setter
|
385
|
+
def max_obs_per_call(self, limit: Optional[Union[int, str]]):
|
386
|
+
"""
|
387
|
+
Sets the maximum number of observations per API call for the data vendor.
|
388
|
+
"""
|
389
|
+
if limit is None:
|
390
|
+
self._max_obs_per_call = limit
|
391
|
+
elif isinstance(limit, int) or isinstance(limit, str):
|
392
|
+
self._max_obs_per_call = int(limit)
|
393
|
+
else:
|
394
|
+
raise TypeError(
|
395
|
+
"Maximum number of observations per API call must be an integer or string."
|
396
|
+
)
|
397
|
+
|
398
|
+
@property
|
399
|
+
def rate_limit(self):
|
400
|
+
"""
|
401
|
+
Returns the number of API calls made and remaining.
|
402
|
+
"""
|
403
|
+
return self._rate_limit
|
404
|
+
|
405
|
+
@rate_limit.setter
|
406
|
+
def rate_limit(self, limit: Optional[Any]):
|
407
|
+
"""
|
408
|
+
Sets the number of API calls made and remaining.
|
409
|
+
"""
|
410
|
+
self._rate_limit = limit
|
411
|
+
|
412
|
+
@abstractmethod
|
413
|
+
def get_rate_limit_info(self):
|
414
|
+
"""
|
415
|
+
Gets the number of API calls made and remaining.
|
416
|
+
"""
|
417
|
+
# to be implemented by subclasses
|
418
|
+
|
419
|
+
@abstractmethod
|
420
|
+
def get_metadata(self) -> None:
|
421
|
+
"""
|
422
|
+
Get exchange metadata.
|
423
|
+
"""
|
424
|
+
# to be implemented by subclasses
|
425
|
+
|
426
|
+
@abstractmethod
|
427
|
+
def get_data(self, data_req) -> pd.DataFrame:
|
428
|
+
"""
|
429
|
+
Submits get data request to API.
|
430
|
+
"""
|
431
|
+
# to be implemented by subclasses
|
432
|
+
|
433
|
+
@staticmethod
|
434
|
+
@abstractmethod
|
435
|
+
def _wrangle_data_resp(data_req: DataRequest, data_resp: Union[Dict[str, Any], pd.DataFrame]) -> pd.DataFrame:
|
436
|
+
"""
|
437
|
+
Wrangles data response from data vendor API into tidy format.
|
438
|
+
"""
|
439
|
+
# to be implemented by subclasses
|