Skip to content

Commit fdfa1d3

Browse files
authored
Merge pull request #1 from pydata/master
catchup
2 parents a88a6a9 + 3e14574 commit fdfa1d3

27 files changed

+185
-100
lines changed

.travis.yml

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,30 +11,30 @@ env:
1111
matrix:
1212
fast_finish: true
1313
include:
14-
- dist: trusty
14+
- dist: bionic
1515
env:
1616
- PYTHON=2.7 PANDAS=0.19.2
17-
- dist: trusty
17+
- dist: bionic
1818
env:
1919
- PYTHON=2.7 PANDAS=0.22
20-
- dist: trusty
20+
- dist: bionic
2121
env:
2222
- PYTHON=3.5 PANDAS=0.19.2
23-
- dist: trusty
23+
- dist: bionic
2424
env:
2525
- PYTHON=3.5 PANDAS=0.20.3
26-
- dist: trusty
26+
- dist: bionic
2727
env:
2828
- PYTHON=3.6 PANDAS=0.23.0
29-
- dist: trusty
29+
- dist: bionic
3030
env:
31-
- PYTHON=3.6 PANDAS=0.22 DOCBUILD=true
31+
- PYTHON=3.7 PANDAS=0.25 DOCBUILD=true
3232
# In allow failures
33-
- dist: trusty
33+
- dist: bionic
3434
env:
3535
- PYTHON=3.6 PANDAS="MASTER"
3636
allow_failures:
37-
- dist: trusty
37+
- dist: bionic
3838
env:
3939
- PYTHON=3.6 PANDAS="MASTER"
4040

@@ -70,8 +70,8 @@ install:
7070
conda install sphinx ipython matplotlib;
7171
pip install sphinx_rtd_theme doctr;
7272
fi
73-
- pip install beautifulsoup4
74-
- pip install coveralls --quiet
73+
- pip install pip --upgrade
74+
- pip install codecov coveralls beautifulsoup4
7575
- conda list
7676
- python setup.py install
7777

@@ -81,7 +81,7 @@ script:
8181
- flake8 --version
8282
- flake8 pandas_datareader
8383

84-
after_success:
84+
after_script:
8585
- |
8686
if [[ ${DOCBUILD} ]]; then
8787
cd docs
@@ -95,3 +95,4 @@ after_success:
9595
fi
9696
fi
9797
- coveralls
98+
- codecov

README.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ Up to date remote data access for pandas, works for multiple versions of pandas.
1212
.. image:: https://coveralls.io/repos/pydata/pandas-datareader/badge.svg?branch=master
1313
:target: https://coveralls.io/r/pydata/pandas-datareader
1414

15+
.. image:: https://codecov.io/gh/pydata/pandas-datareader/branch/master/graph/badge.svg
16+
:target: https://codecov.io/gh/pydata/pandas-datareader
17+
1518
.. image:: https://readthedocs.org/projects/pandas-datareader/badge/?version=latest
1619
:target: https://pandas-datareader.readthedocs.io/en/latest/
1720

docs/source/remote_data.rst

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,11 @@ IEX
7575

7676
The Investors Exchange (IEX) provides a wide range of data through an
7777
`API <https://iexcloud.io/api/docs/>`__. Historical stock
78-
prices are available for up to 15 years. The usage of these readers requires an API key, which can be stored in the ``IEX_API_TOKEN`` environment variable.
78+
prices are available for up to 15 years. The usage of these readers requires the publishable API key from IEX Cloud Console, which can be stored in the ``IEX_API_KEY`` environment variable.
7979

8080
.. ipython:: python
81-
81+
82+
os.environ["IEX_API_KEY"] = "pk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
8283
import pandas_datareader.data as web
8384
from datetime import datetime
8485
start = datetime(2016, 9, 1)
@@ -114,6 +115,7 @@ Through the
114115
endpoints, it is possible to obtain historical equities data for individual
115116
symbols. The following endpoints are available:
116117

118+
* ``av-intraday`` - Intraday Time Series
117119
* ``av-daily`` - Daily Time Series
118120
* ``av-daily-adjusted`` - Daily Time Series (Adjusted)
119121
* ``av-weekly`` - Weekly Time Series

docs/source/whatsnew/v0.7.0.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ Backwards incompatible API changes
6464
Bug Fixes
6565
~~~~~~~~~
6666

67+
- Fixed import of pandas.compat (:issue:`657`)
6768
- Added support for passing the API KEY to QuandlReader either directly or by
6869
setting the environmental variable QUANDL_API_KEY (:issue:`485`).
6970
- Added support for optionally passing a custom base_url to the EnigmaReader (:issue:`499`).

docs/source/whatsnew/v0.8.0.txt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ Highlights include:
99
aggregated economic data from 90+ official statistical agencies
1010
(:issue:`615`)
1111
- Migrated IEX readers to `IEX Cloud <https://iexcloud.io>`__. All
12-
readers now require an API token (``IEX_API_TOKEN``)
12+
readers now require an API token (``IEX_API_KEY``)
1313
- Removal of Google finance and Morningstar, which were deprecated in 0.7.0.
1414
- Immediate deprecation of Robinhood for quotes and historical data. Robinhood
1515
ended support for these endpoints in 1/2019
@@ -26,7 +26,9 @@ Enhancements
2626
~~~~~~~~~~~~
2727

2828
- Added Tiingo IEX Historical reader.
29+
- Added support for Alpha Vantage intraday time series prices (:issue: `631`)
2930
- Up to 15 years of historical prices from IEX with new platform, IEX Cloud
31+
- Added testing on Python 3.7 (:issue:`667`)
3032

3133
.. _whatsnew_080.api_breaking:
3234

@@ -47,3 +49,5 @@ Bug Fixes
4749

4850
- Fix Yahoo! actions issue where dividends are adjusted twice as a result of a
4951
change to the Yahoo! API. (:issue: `583`)
52+
- Fix AlphaVantage time series data ordering after provider switch to
53+
descending order (maintains ascending order for consistency) (:issue: `662`)

pandas_datareader/av/time_series.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ class AVTimeSeriesReader(AlphaVantage):
3535
"TIME_SERIES_WEEKLY": "Weekly Time Series",
3636
"TIME_SERIES_WEEKLY_ADJUSTED": "Weekly Adjusted Time Series",
3737
"TIME_SERIES_MONTHLY": "Monthly Time Series",
38-
"TIME_SERIES_MONTHLY_ADJUSTED": "Monthly Adjusted Time Series"
38+
"TIME_SERIES_MONTHLY_ADJUSTED": "Monthly Adjusted Time Series",
39+
"TIME_SERIES_INTRADAY": "Time Series (1min)"
3940
}
4041

4142
def __init__(self, symbols=None, function="TIME_SERIES_DAILY",
@@ -47,11 +48,11 @@ def __init__(self, symbols=None, function="TIME_SERIES_DAILY",
4748
pause=pause, session=session,
4849
api_key=api_key)
4950

50-
self.func = function
51+
self._func = function
5152

5253
@property
5354
def function(self):
54-
return self.func
55+
return self._func
5556

5657
@property
5758
def output_size(self):
@@ -67,15 +68,20 @@ def data_key(self):
6768

6869
@property
6970
def params(self):
70-
return {
71+
p = {
7172
"symbol": self.symbols,
7273
"function": self.function,
7374
"apikey": self.api_key,
7475
"outputsize": self.output_size
7576
}
77+
if self.function == "TIME_SERIES_INTRADAY":
78+
p.update({"interval": "1min"})
79+
return p
7680

7781
def _read_lines(self, out):
7882
data = super(AVTimeSeriesReader, self)._read_lines(out)
83+
# reverse since alphavantage returns descending by date
84+
data = data[::-1]
7985
start_str = self.start.strftime('%Y-%m-%d')
8086
end_str = self.end.strftime('%Y-%m-%d')
8187
data = data.loc[start_str:end_str]

pandas_datareader/bankofcanada.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from __future__ import unicode_literals
22

3-
import pandas.compat as compat
3+
from pandas_datareader.compat import string_types
44

55
from pandas_datareader.base import _BaseReader
66

@@ -17,7 +17,7 @@ class BankOfCanadaReader(_BaseReader):
1717
@property
1818
def url(self):
1919
"""API URL"""
20-
if not isinstance(self.symbols, compat.string_types):
20+
if not isinstance(self.symbols, string_types):
2121
raise ValueError('data name must be string')
2222

2323
return '{0}/{1}/csv'.format(self._URL, self.symbols)

pandas_datareader/base.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44

55
import requests
66

7-
import pandas.compat as compat
87
from pandas import DataFrame
98
from pandas import read_csv, concat
109
from pandas.io.common import urlencode
11-
from pandas.compat import StringIO, bytes_to_str
10+
from pandas_datareader.compat import bytes_to_str, string_types, binary_type, \
11+
StringIO
1212

1313
from pandas_datareader._utils import (RemoteDataError, SymbolWarning,
1414
_sanitize_dates, _init_session)
@@ -99,7 +99,7 @@ def _read_url_as_StringIO(self, url, params=None):
9999
service = self.__class__.__name__
100100
raise IOError("{} request returned no data; check URL for invalid "
101101
"inputs: {}".format(service, self.url))
102-
if isinstance(text, compat.binary_type):
102+
if isinstance(text, binary_type):
103103
out.write(bytes_to_str(text))
104104
else:
105105
out.write(text)
@@ -205,7 +205,7 @@ def _get_params(self, *args, **kwargs):
205205
def read(self):
206206
"""Read data"""
207207
# If a single symbol, (e.g., 'GOOG')
208-
if isinstance(self.symbols, (compat.string_types, int)):
208+
if isinstance(self.symbols, (string_types, int)):
209209
df = self._read_one_data(self.url,
210210
params=self._get_params(self.symbols))
211211
# Or multiple symbols, (e.g., ['GOOG', 'AAPL', 'MSFT'])

pandas_datareader/compat/__init__.py

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
# flake8: noqa
2-
import sys
3-
from distutils.version import LooseVersion
4-
from io import BytesIO
5-
62
import pandas as pd
7-
import pandas.compat as compat
83
import pandas.io.common as com
4+
import sys
5+
from distutils.version import LooseVersion
96

107
PY3 = sys.version_info >= (3, 0)
118

@@ -45,11 +42,42 @@ def get_filepath_or_buffer(filepath_or_buffer, encoding=None,
4542
else:
4643
from pandas.core.common import is_list_like
4744

48-
49-
if compat.PY3:
45+
if PY3:
5046
from urllib.error import HTTPError
47+
from functools import reduce
48+
49+
string_types = str,
50+
binary_type = bytes
51+
from io import StringIO
52+
53+
def str_to_bytes(s, encoding=None):
54+
return s.encode(encoding or 'ascii')
55+
56+
57+
def bytes_to_str(b, encoding=None):
58+
return b.decode(encoding or 'utf-8')
5159
else:
5260
from urllib2 import HTTPError
61+
from cStringIO import StringIO
62+
reduce = reduce
63+
binary_type = str
64+
string_types = basestring,
65+
66+
67+
def bytes_to_str(b, encoding=None):
68+
return b
69+
70+
71+
def str_to_bytes(s, encoding=None):
72+
return s
73+
74+
75+
def lmap(*args, **kwargs):
76+
return list(map(*args, **kwargs))
77+
78+
79+
def lrange(*args, **kwargs):
80+
return list(range(*args, **kwargs))
5381

5482

5583
def concat(*args, **kwargs):

pandas_datareader/data.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ def DataReader(name, data_source=None, start=None, end=None,
298298
"tiingo", "yahoo-actions", "yahoo-dividends",
299299
"av-forex", "av-daily", "av-daily-adjusted",
300300
"av-weekly", "av-weekly-adjusted", "av-monthly",
301-
"av-monthly-adjusted", "econdb"]
301+
"av-monthly-adjusted", "av-intraday", "econdb"]
302302

303303
if data_source not in expected_source:
304304
msg = "data_source=%r is not implemented" % data_source
@@ -444,6 +444,13 @@ def DataReader(name, data_source=None, start=None, end=None,
444444
retry_count=retry_count, pause=pause,
445445
session=session, api_key=access_key).read()
446446

447+
elif data_source == "av-intraday":
448+
return AVTimeSeriesReader(symbols=name,
449+
function="TIME_SERIES_INTRADAY",
450+
start=start, end=end,
451+
retry_count=retry_count, pause=pause,
452+
session=session, api_key=access_key).read()
453+
447454
elif data_source == "econdb":
448455
return EcondbReader(symbols=name, start=start, end=end,
449456
retry_count=retry_count, pause=pause,

pandas_datareader/enigma.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
import os
22
import time
33

4-
from pandas.compat import StringIO
5-
import pandas.compat as compat
64
import pandas as pd
75

8-
from pandas_datareader.base import _BaseReader
6+
from pandas_datareader.base import _BaseReader, string_types
7+
from pandas_datareader.compat import StringIO
98

109

1110
class EnigmaReader(_BaseReader):
@@ -65,7 +64,7 @@ def __init__(self,
6564
self._api_key = api_key
6665

6766
self._dataset_id = dataset_id
68-
if not isinstance(self._dataset_id, compat.string_types):
67+
if not isinstance(self._dataset_id, string_types):
6968
raise ValueError(
7069
"The Enigma dataset_id must be a string (ex: "
7170
"'bedaf052-5fcd-4758-8d27-048ce8746c6a')")

pandas_datareader/eurostat.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
from __future__ import unicode_literals
22

33
import pandas as pd
4-
import pandas.compat as compat
54

65
from pandas_datareader.io.sdmx import read_sdmx, _read_sdmx_dsd
6+
from pandas_datareader.compat import string_types
77
from pandas_datareader.base import _BaseReader
88

99

@@ -15,7 +15,7 @@ class EurostatReader(_BaseReader):
1515
@property
1616
def url(self):
1717
"""API URL"""
18-
if not isinstance(self.symbols, compat.string_types):
18+
if not isinstance(self.symbols, string_types):
1919
raise ValueError('data name must be string')
2020

2121
q = '{0}/data/{1}/?startperiod={2}&endperiod={3}'
@@ -25,7 +25,7 @@ def url(self):
2525
@property
2626
def dsd_url(self):
2727
"""API DSD URL"""
28-
if not isinstance(self.symbols, compat.string_types):
28+
if not isinstance(self.symbols, string_types):
2929
raise ValueError('data name must be string')
3030

3131
return '{0}/datastructure/ESTAT/DSD_{1}'.format(

pandas_datareader/famafrench.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import datetime as dt
22
import re
33
import tempfile
4+
45
from zipfile import ZipFile
56

67
from pandas import read_csv, to_datetime
7-
from pandas.compat import lmap, StringIO
8+
from pandas_datareader.compat import lmap, StringIO
89

910
from pandas_datareader.base import _BaseReader
1011

pandas_datareader/iex/daily.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,16 @@ def _range_string_from_date(self):
101101
elif 1 <= years < 2:
102102
return "2y"
103103
elif 0 <= years < 1:
104+
delta_days = (datetime.datetime.now() - self.start).days
105+
if 0 <= delta_days < 6:
106+
return "5d"
107+
elif 6 <= delta_days < 28:
108+
return "1m"
109+
elif 28 <= delta_days < 84:
110+
return "3m"
111+
elif 84 <= delta_days < 168:
112+
return "6m"
113+
104114
return "1y"
105115
else:
106116
raise ValueError(

0 commit comments

Comments
 (0)