From 0a7a65d2a8d12f10c7236bdb43eacce699fec441 Mon Sep 17 00:00:00 2001 From: Patrick Collins <54278053+PatrickAlphaVantage@users.noreply.github.com> Date: Thu, 16 Jan 2020 01:36:42 -0500 Subject: [PATCH] Prep for 2.1.3 (#178) * Fixing pypi badge * Fixing typo on README * Use ==/!= to compare str, bytes, and int literals Identity is not the same thing as equality in Python so use ==/!= to compare str, bytes, and int literals. In Python >= 3.8, these instances will raise SyntaxWarnings so it is best to fix them now. https://docs.python.org/3.8/whatsnew/3.8.html#porting-to-python-3-8 $ python ``` >>> pandas = "panda" >>> pandas += "s" >>> pandas == "pandas" True >>> pandas is "pandas" False ``` * added rapidapi key integration Co-authored-by: Igor Tavares Co-authored-by: Christian Clauss Co-authored-by: Patrick Collins --- README.md | 14 +++++++-- alpha_vantage/alphavantage.py | 29 ++++++++++++++----- docs/conf.py | 4 +-- setup.py | 2 +- test_alpha_vantage/test_alphavantage.py | 15 ++++++++-- .../test_integration_alphavantage.py | 26 ++++++++++++++--- 6 files changed, 70 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 288b14e..c38791f 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # alpha_vantage [![Build Status](https://travis-ci.org/RomelTorres/alpha_vantage.png?branch=master)](https://travis-ci.org/RomelTorres/alpha_vantage) -[![PyPI version](https://badge.fury.io/py/alpha_vantage.svg)](https://badge.fury.io/py/alpha_vantage) +[![PyPI version](https://badge.fury.io/py/alpha-vantage.svg)](https://badge.fury.io/py/alpha-vantage) [![Documentation Status](https://readthedocs.org/projects/alpha-vantage/badge/?version=latest)](http://alpha-vantage.readthedocs.io/en/latest/?badge=latest) [![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/RomelTorres/alpha_vantage.svg)](http://isitmaintained.com/project/RomelTorres/alpha_vantage "Average time to resolve an issue") [![Percentage of issues still open](http://isitmaintained.com/badge/open/RomelTorres/alpha_vantage.svg)](http://isitmaintained.com/project/RomelTorres/alpha_vantage "Percentage of issues still open") @@ -13,6 +13,8 @@ Vantage (http://www.alphavantage.co/). It requires a free API key, that can be r ## News +* From version 2.1.3 onwards, [rapidAPI](https://rapidapi.com/alphavantage/api/alpha-vantage-alpha-vantage-default) key integration is now available. +* From version 2.1.0 onwards, error logging of bad API calls has been made more apparent. * From version 1.9.0 onwards, the urllib was substituted by pythons request library that is thread safe. If you have any error, post an issue. * From version 1.8.0 onwards, the column names of the data frames have changed, they are now exactly what alphavantage gives back in their json response. You can see the examples in better detail in the following git repo: https://github.com/RomelTorres/av_example * From version 1.6.0, pandas was taken out as a hard dependency. @@ -24,7 +26,7 @@ pip install alpha_vantage ``` Or install with pandas support, simply install pandas too: ```shell -pip install alpha_vantage, pandas +pip install alpha_vantage pandas ``` If you want to install from source, then use: @@ -41,6 +43,12 @@ ts = TimeSeries(key='YOUR_API_KEY') # Get json object with the intraday data and another with the call's metadata data, meta_data = ts.get_intraday('GOOGL') ``` +You may also get a key from [rapidAPI](https://rapidapi.com/alphavantage/api/alpha-vantage-alpha-vantage-default). Use your rapidAPI key for the key variable, and set ```rapidapi=True``` + +```python +ts = TimeSeries(key='YOUR_API_KEY',rapidapi=True) +``` + Internally there is a retries counter, that can be used to minimize connection errors (in case that the API is not able to respond in time), the default is set to 5 but can be increased or decreased whenever needed. ```python @@ -193,7 +201,7 @@ nosetests The code documentation can be found at https://alpha-vantage.readthedocs.io/en/latest/ ## Contributing -Contributing is always welcome, since sometimes I am busy. Just contact me on how best you can contribute. +Contributing is always welcome. Just contact us on how best you can contribute, add an issue, or make a PR. ## TODOs: * The integration tests are not being run at the moment within travis, gotta fix them to run. diff --git a/alpha_vantage/alphavantage.py b/alpha_vantage/alphavantage.py index 2815dbd..00694be 100644 --- a/alpha_vantage/alphavantage.py +++ b/alpha_vantage/alphavantage.py @@ -22,8 +22,10 @@ class AlphaVantage(object): _ALPHA_VANTAGE_DIGITAL_CURRENCY_LIST = \ "https://www.alphavantage.co/digital_currency_list/" + _RAPIDAPI_URL = "https://alpha-vantage.p.rapidapi.com/query?" + def __init__(self, key=None, output_format='json', - treat_info_as_error=True, indexing_type='date', proxy=None): + treat_info_as_error=True, indexing_type='date', proxy=None, rapidapi=False): """ Initialize the class Keyword Arguments: @@ -38,6 +40,8 @@ def __init__(self, key=None, output_format='json', output_format is 'pandas' proxy: Dictionary mapping protocol or protocol and hostname to the URL of the proxy. + rapidapi: Boolean describing whether or not the API key is + through the RapidAPI platform or not """ if key is None: key = os.getenv('ALPHAVANTAGE_API_KEY') @@ -48,9 +52,16 @@ def __init__(self, key=None, output_format='json', 'ALPHAVANTAGE_API_KEY. Get a free key ' 'from the alphavantage website: ' 'https://www.alphavantage.co/support/#api-key') + self.headers = {} + if rapidapi: + self.headers = { + 'x-rapidapi-host': "alpha-vantage.p.rapidapi.com", + 'x-rapidapi-key': key + } + self.rapidapi = rapidapi self.key = key self.output_format = output_format - if self.output_format is 'pandas' and not _PANDAS_FOUND: + if self.output_format == 'pandas' and not _PANDAS_FOUND: raise ValueError("The pandas library was not found, therefore can " "not be used as an output format, please install " "manually") @@ -109,8 +120,8 @@ def _call_wrapper(self, *args, **kwargs): # key for it and for its meta data. function_name, data_key, meta_data_key = func( self, *args, **kwargs) - url = "{}function={}".format(AlphaVantage._ALPHA_VANTAGE_API_URL, - function_name) + base_url = AlphaVantage._RAPIDAPI_URL if self.rapidapi else AlphaVantage._ALPHA_VANTAGE_API_URL + url = "{}function={}".format(base_url, function_name) for idx, arg_name in enumerate(argspec.args[1:]): try: arg_value = args[idx] @@ -139,10 +150,12 @@ def _call_wrapper(self, *args, **kwargs): raise ValueError("Output format: {} not recognized, only json," "pandas and csv are supported".format( self.output_format.lower())) + apikey_parameter = "" if self.rapidapi else "&apikey={}".format( + self.key) if self._append_type: - url = '{}&apikey={}&datatype={}'.format(url, self.key, oformat) + url = '{}{}&datatype={}'.format(url, apikey_parameter, oformat) else: - url = '{}&apikey={}'.format(url, self.key) + url = '{}{}'.format(url, apikey_parameter) return self._handle_api_call(url), data_key, meta_data_key return _call_wrapper @@ -162,7 +175,7 @@ def _format_wrapper(self, *args, **kwargs): if 'json' in self.output_format.lower() or 'pandas' \ in self.output_format.lower(): data = call_response[data_key] - + if meta_data_key is not None: meta_data = call_response[meta_data_key] else: @@ -268,7 +281,7 @@ def _handle_api_call(self, url): meta_data_key: The key for getting the meta data information out of the json object """ - response = requests.get(url, proxies=self.proxy) + response = requests.get(url, proxies=self.proxy, headers=self.headers) if 'json' in self.output_format.lower() or 'pandas' in \ self.output_format.lower(): json_response = response.json() diff --git a/docs/conf.py b/docs/conf.py index 51fa804..ac50ceb 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -55,9 +55,9 @@ # built documents. # # The short X.Y version. -version = u'2.1.2' +version = u'2.1.3' # The full version, including alpha/beta/rc tags. -release = u'2.1.2' +release = u'2.1.3' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/setup.py b/setup.py index 0587528..1e58a6a 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ setup( name='alpha_vantage', - version='2.1.2', + version='2.1.3', author='Romel J. Torres', author_email='romel.torres@gmail.com', license='MIT', diff --git a/test_alpha_vantage/test_alphavantage.py b/test_alpha_vantage/test_alphavantage.py index 8630164..e72d3da 100644 --- a/test_alpha_vantage/test_alphavantage.py +++ b/test_alpha_vantage/test_alphavantage.py @@ -53,6 +53,19 @@ def test_handle_api_call(self, mock_request): self.assertIsInstance( data, dict, 'Result Data must be a dictionary') + @requests_mock.Mocker() + def test_rapidapi_key(self, mock_request): + """ Test that the rapidAPI key calls the rapidAPI endpoint + """ + ts = TimeSeries(key=TestAlphaVantage._API_KEY_TEST, rapidapi=True) + url = "https://alpha-vantage.p.rapidapi.com/query?function=TIME_SERIES_INTRADAY&symbol=MSFT&interval=1min&outputsize=full&datatype=json" + path_file = self.get_file_from_url("mock_time_series") + with open(path_file) as f: + mock_request.get(url, text=f.read()) + data, _ = ts.get_intraday( + "MSFT", interval='1min', outputsize='full') + self.assertIsInstance( + data, dict, 'Result Data must be a dictionary') @requests_mock.Mocker() def test_time_series_intraday(self, mock_request): @@ -83,7 +96,6 @@ def test_time_series_intraday_pandas(self, mock_request): self.assertIsInstance( data, df, 'Result Data must be a pandas data frame') - @requests_mock.Mocker() def test_time_series_intraday_date_indexing(self, mock_request): """ Test that api call returns a pandas data frame with a date as index @@ -214,4 +226,3 @@ def test_batch_quotes_pandas(self, mock_request): data, _ = ts.get_batch_stock_quotes(symbols=('MSFT', 'FB', 'AAPL')) self.assertIsInstance( data, df, 'Result Data must be a pandas dataframe') - diff --git a/test_alpha_vantage/test_integration_alphavantage.py b/test_alpha_vantage/test_integration_alphavantage.py index 7627630..20519a9 100644 --- a/test_alpha_vantage/test_integration_alphavantage.py +++ b/test_alpha_vantage/test_integration_alphavantage.py @@ -19,6 +19,7 @@ class TestAlphaVantage(unittest.TestCase): """ _API_KEY_TEST = os.environ['API_KEY'] _API_EQ_NAME_TEST = 'MSFT' + _RAPIDAPI_KEY_TEST = os.getenv("RAPIDAPI_KEY") def setUp(self): """ @@ -62,6 +63,19 @@ def test_key_none(self): except ValueError: self.assertTrue(True) + def test_rapidapi_key_with_get_daily(self): + """RapidAPI calls must return the same data as non-rapidapi calls + """ + # Test rapidAPI calls are the same as regular ones + ts_rapidapi = TimeSeries( + key=TestAlphaVantage._RAPIDAPI_KEY_TEST, rapidapi=True) + ts = TimeSeries(key=TestAlphaVantage._API_KEY_TEST) + rapidapi_data, rapidapi_metadata = ts_rapidapi.get_daily( + symbol=TestAlphaVantage._API_EQ_NAME_TEST) + data, metadata = ts.get_daily( + symbol=TestAlphaVantage._API_EQ_NAME_TEST) + self.assertTrue(rapidapi_data == data) + def test_get_daily_is_format(self): """Result must be a dictionary containing the json data """ @@ -136,7 +150,8 @@ def test_get_currency_exchange_intraday_json(self): def test_get_currency_exchange_intraday_pandas(self): """Test that we get a dictionary containing pandas data """ - fe = ForeignExchange(key=TestAlphaVantage._API_KEY_TEST, output_format='pandas') + fe = ForeignExchange( + key=TestAlphaVantage._API_KEY_TEST, output_format='pandas') self._assert_result_is_format(fe.get_currency_exchange_intraday, output_format='pandas', from_symbol='USD', @@ -155,7 +170,8 @@ def test_get_currency_exchange_daily_json(self): def test_get_currency_exchange_daily_pandas(self): """Test that we get a dictionary containing pandas data """ - fe = ForeignExchange(key=TestAlphaVantage._API_KEY_TEST, output_format='pandas') + fe = ForeignExchange( + key=TestAlphaVantage._API_KEY_TEST, output_format='pandas') self._assert_result_is_format(fe.get_currency_exchange_daily, output_format='pandas', from_symbol='USD', @@ -174,7 +190,8 @@ def test_get_currency_exchange_weekly_json(self): def test_get_currency_exchange_weekly_pandas(self): """Test that we get a dictionary containing pandas data """ - fe = ForeignExchange(key=TestAlphaVantage._API_KEY_TEST, output_format='pandas') + fe = ForeignExchange( + key=TestAlphaVantage._API_KEY_TEST, output_format='pandas') self._assert_result_is_format(fe.get_currency_exchange_weekly, output_format='pandas', from_symbol='USD', @@ -192,7 +209,8 @@ def test_get_currency_exchange_monthly_json(self): def test_get_currency_exchange_monthly_pandas(self): """Test that we get a dictionary containing pandas data """ - fe = ForeignExchange(key=TestAlphaVantage._API_KEY_TEST, output_format='pandas') + fe = ForeignExchange( + key=TestAlphaVantage._API_KEY_TEST, output_format='pandas') self._assert_result_is_format(fe.get_currency_exchange_monthly, output_format='pandas', from_symbol='USD',