Skip to content

Commit

Permalink
Merge branch 'release/1.6.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
RomelTorres committed Dec 22, 2017
2 parents 4cbebdb + e7385cb commit 062c622
Show file tree
Hide file tree
Showing 17 changed files with 7,754 additions and 1,078 deletions.
5 changes: 4 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ install:
- pip install simplejson
- pip install pandas==0.19.2
script:
nosetests --nocapture

nosetests --nocapture test_alpha_vantage/test_alphavantage.py
allow_failure:
script:
nosetests --nocapture nosetests -s test_alpha_vantage/test_integration_alphavantage.py
after_sucess:
codecov
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,8 @@ nosetests
The code documentation can be found at https://alpha-vantage.readthedocs.io/en/latest/

## TODOS:
* Separate tests between unittests and integration test.
* Prepare for release 1.6.0
* Update documentation on how to install with pandas support ```pip install alpha_vantage[pandas]``` has to be tested and reproduced properly.

## Star if you like it.
If you like or use this project, consider showing your support by staring it.
151 changes: 96 additions & 55 deletions alpha_vantage/alphavantage.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
try:
# Python 3 import
from urllib.request import urlopen
except ImportError:
# Python 2.* import
from urllib2 import urlopen
import urllib
import sys
from functools import wraps
import inspect
import pandas
# Pandas became an optional dependency, but we still want to track it
try:
import pandas
_PANDAS_FOUND = True
except ImportError:
_PANDAS_FOUND = False
import csv
import re
# Avoid compability issues
if sys.version_info.major == 3 and sys.version_info.minor == 6:
Expand All @@ -16,31 +17,42 @@
from simplejson import loads


class AlphaVantage:
class AlphaVantage(object):
""" Base class where the decorators and base function for the other
classes of this python wrapper will inherit from.
"""
_ALPHA_VANTAGE_API_URL = "http://www.alphavantage.co/query?"
_ALPHA_VANTAGE_MATH_MAP = ['SMA', 'EMA', 'WMA', 'DEMA', 'TEMA', 'TRIMA', 'T3',
'KAMA', 'MAMA']
_ALPHA_VANTAGE_DIGITAL_CURRENCY_LIST = "https://www.alphavantage.co/digital_currency_list/"
_ALPHA_VANTAGE_MATH_MAP = ['SMA', 'EMA', 'WMA', 'DEMA', 'TEMA', 'TRIMA',
'T3', 'KAMA', 'MAMA']
_ALPHA_VANTAGE_DIGITAL_CURRENCY_LIST = \
"https://www.alphavantage.co/digital_currency_list/"

def __init__(self, key=None, retries=5, output_format='json', treat_info_as_error=True):
def __init__(self, key=None, retries=5, output_format='json',
treat_info_as_error=True):
""" Initialize the class
Keyword Arguments:
key: Alpha Vantage api key
retries: Maximum amount of retries in case of faulty connection or
server not able to answer the call.
output_format: Either 'json' or 'pandas'
treat_info_as_error: Treat information from the api as errors
output_format: Either 'json', 'pandas' os 'csv'
"""
if key is None:
raise ValueError(
'Get a free key from the alphavantage website: https://www.alphavantage.co/support/#api-key')
'Get a free key from the alphavantage website:'
' https://www.alphavantage.co/support/#api-key')
self.key = key
self.retries = retries
self.output_format = output_format
if self.output_format is '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")
self.treat_info_as_error = treat_info_as_error
# Not all the calls accept a data type appended at the end, this
# variable will be overriden by those functions not needing it.
self._append_type = True

def _retry(func):
""" Decorator for retrying api calls (in case of errors from the api
Expand Down Expand Up @@ -120,7 +132,20 @@ def _call_wrapper(self, *args, **kwargs):
# None (in other words, this will call the api with its
# internal defined parameter)
url = '{}&{}={}'.format(url, arg_name, arg_value)
url = '{}&apikey={}'.format(url, self.key)
# Allow the output format to be json or csv (supported by
# alphavantage api). Pandas is simply json converted.
if 'json' or 'csv' in self.output_format.lower():
oformat = self.output_format.lower()
elif 'pandas' in self.output_format.lower():
oformat = 'json'
else:
raise ValueError("Output format: {} not recognized, only json,"
"pandas and csv are supported".format(
self.output_format.lower()))
if self._append_type:
url = '{}&apikey={}&datatype={}'.format(url, self.key, oformat)
else:
url = '{}&apikey={}'.format(url, self.key)
return self._handle_api_call(url), data_key, meta_data_key
return _call_wrapper

Expand All @@ -135,31 +160,37 @@ def _output_format(cls, func, override=None):
"""
@wraps(func)
def _format_wrapper(self, *args, **kwargs):
json_response, data_key, meta_data_key = func(
call_response, data_key, meta_data_key = func(
self, *args, **kwargs)
data = json_response[data_key]
if meta_data_key is not None:
meta_data = json_response[meta_data_key]
else:
meta_data = None
# Allow to override the output parameter in the call
if override is None:
output_format = self.output_format.lower()
elif 'json' or 'pandas' in override.lower():
output_format = override.lower()
# Choose output format
if output_format == 'json':
return data, meta_data
elif output_format == 'pandas':
data_pandas = pandas.DataFrame.from_dict(data,
orient='index',
dtype=float)
data_pandas.index.name = 'Date'
# Rename columns to have a nicer name
col_names = [re.sub(r'\d+.', '', name).strip(' ')
for name in list(data_pandas)]
data_pandas.columns = col_names
return data_pandas, meta_data
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:
meta_data = None
# Allow to override the output parameter in the call
if override is None:
output_format = self.output_format.lower()
elif 'json' or 'pandas' in override.lower():
output_format = override.lower()
# Choose output format
if output_format == 'json':
return data, meta_data
elif output_format == 'pandas':
data_pandas = pandas.DataFrame.from_dict(data,
orient='index',
dtype=float)
data_pandas.index.name = 'date'
# Rename columns to have a nicer name
col_names = [re.sub(r'\d+.', '', name).strip(' ')
for name in list(data_pandas)]
data_pandas.columns = col_names
# Set Date as an actual column so a new numerical index will be created
data_pandas.reset_index(level=0, inplace=True)
return data_pandas, meta_data
elif 'csv' in self.output_format.lower():
return call_response, None
else:
raise ValueError('Format: {} is not supported'.format(
self.output_format))
Expand All @@ -172,8 +203,8 @@ def map_to_matype(self, matype):
is given.
Keyword Arguments:
matype: The math type of the alpha vantage api. It accepts integers
or a string representing the math type.
matype: The math type of the alpha vantage api. It accepts
integers or a string representing the math type.
* 0 = Simple Moving Average (SMA),
* 1 = Exponential Moving Average (EMA),
Expand Down Expand Up @@ -202,19 +233,29 @@ def _handle_api_call(self, url):
Keyword Arguments:
url: The url of the service
data_key: The key for getting the data from the jso object
meta_data_key: The key for getting the meta data information out of
the json object
meta_data_key: The key for getting the meta data information out
of the json object
"""
response = urlopen(url)
# In order to keep supporting python 2.7, we have to do this.
if sys.version_info.major == 3:
response = urllib.request.urlopen(url)
else:
response = urllib.urlopen(url)
url_response = response.read()
json_response = loads(url_response)

if not json_response:
raise ValueError(
'Error getting data from the api, no return was given.')
elif "Error Message" in json_response:
raise ValueError(json_response["Error Message"])
elif "Information" in json_response and self.treat_info_as_error:
raise ValueError(json_response["Information"])

return json_response
if 'json' in self.output_format.lower() or 'pandas' in \
self.output_format.lower():
json_response = loads(url_response)
if not json_response:
raise ValueError(
'Error getting data from the api, no return was given.')
elif "Error Message" in json_response:
raise ValueError(json_response["Error Message"])
elif "Information" in json_response and self.treat_info_as_error:
raise ValueError(json_response["Information"])
return json_response
else:
csv_response = csv.reader(url_response)
if not csv_response:
raise ValueError(
'Error getting data from the api, no return was given.')
return csv_response
10 changes: 10 additions & 0 deletions alpha_vantage/foreignexchange.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@ class ForeignExchange(av):
"""Realtime currency exchange rates for physical and digital currencies.
"""

def __init__(self, *args, **kwargs):
"""
Inherit AlphaVantage base class with its default arguments
"""
super(ForeignExchange, self).__init__(*args, **kwargs)
self._append_type = False
if self.output_format.lower() == 'csv':
raise ValueError("Output format {} is not comatible with the {}".format(
self.output_format.lower(), self.__name__))

@av._output_format
@av._call_api_on_func
def get_currency_exchange_rate(self, from_currency, to_currency):
Expand Down
15 changes: 14 additions & 1 deletion alpha_vantage/sectorperformance.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,27 @@
#!/usr/bin/env python
from .alphavantage import AlphaVantage as av
from functools import wraps
import pandas
try:
import pandas
except ImportError:
pass
import re


class SectorPerformances(av):
"""This class implements all the sector performance api calls
"""

def __init__(self, *args, **kwargs):
"""
Inherit AlphaVantage base class with its default arguments
"""
super(SectorPerformances, self).__init__(*args, **kwargs)
self._append_type = False
if self.output_format.lower() == 'csv':
raise ValueError("Output format {} is not comatible with the {}".format(
self.output_format.lower(), self.__name__))

def percentage_to_float(self, val):
""" Transform a string of ther form f.f% into f.f/100
Expand Down
Loading

0 comments on commit 062c622

Please sign in to comment.