Skip to content

Commit aaf2d9e

Browse files
authored
Merge pull request #11 from portfoliome/fix-intraday
Fix intraday
2 parents 998d3b7 + 1a1d4fb commit aaf2d9e

File tree

4 files changed

+77
-16
lines changed

4 files changed

+77
-16
lines changed

README.md

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,14 @@
44

55
alphavantage is a Python wrapper for the Alpha Vantage API.
66

7+
The API wrapper can be used to retrieve historical prices such as intraday or daily prices for global equities and ETFs.
8+
9+
## Status
10+
11+
The API aims to support equity time-series data as a first step.
12+
13+
The package is currently in alpha status. It has not been used extensively yet and therefore mainly of the potential quirks of Alpha Vantage's actual API may not be accounted for.
14+
715
## Design Consideration
816

917
This library is intended to provide a simple wrapper with minimal dependencies, and does not intend to introduce pydata stack dependencies (numpy, pandas, etc.) in the future. Differences with existing wrappers for the Alpha Vantage API include:
@@ -19,23 +27,38 @@ The library carries out some conveniences versus using the API without a wrapper
1927

2028
### Conveniences
2129

22-
* converting timestamps to UTC time when applicable
23-
* Simplifies record field names i.e. "4. close" -> "close"
24-
* appends timestamp field to record vs. having timestamp act as dictionary key
25-
* Uses time ascending list/array versus dictionary/hash for price record data structure.
26-
* Mapping ticker symbology from other vendors
27-
30+
* Converts timestamps to UTC time when applicable.
31+
* Simplifies record field names i.e. "4. close" -> "close".
32+
* Appends the timestamp field to record vs. having the timestamp act as dictionary key.
33+
* Uses time ascending list versus a dictionary for price record data structure.
34+
* Returns multiple tickers over a given parameter set using threads.
35+
* Maps ticker symbology from other vendors.
36+
* Excludes intraday data in daily price history requests.
37+
2838
## Examples
2939
```python
30-
from alphavantage.price_history import PriceHistory, IntradayPriceHistory, filter_dividends
40+
from alphavantage.price_history import (
41+
AdjustedPriceHistory, get_results, PriceHistory, IntradayPriceHistory,
42+
filter_dividends
43+
)
44+
45+
# weekly prices
46+
history = PriceHistory(period='W', output_size='compact')
47+
results = history.get('AAPL')
3148

32-
history = PriceHistory(output_size='compact')
49+
# intraday prices, 5 minute interval
50+
history = IntradayPriceHistory(utc=True, interval=5)
3351
results = history.get('AAPL')
3452

35-
history = IntradayPriceHistory(utc=True)
53+
# adjusted daily prices
54+
history = AdjustedPriceHistory(period='D')
3655
results = history.get('AAPL')
3756
dividends = list(filter_dividends(results.records))
3857

58+
# Return multiple tickers
59+
parameters = {'output_size': 'compact', 'period': 'D'}
60+
tickers = ['AAPL', 'MSFT']
61+
results = dict(get_results(PriceHistory, tickers, parameters))
3962
```
4063

4164
## Contributing

alphavantage/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11

2-
version_info = (0, 0, 9)
2+
version_info = (0, 0, 10)
33

44
__version__ = '.'.join(map(str, version_info))

alphavantage/price_history.py

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@
77
"""
88

99
import os
10+
from concurrent.futures import ThreadPoolExecutor, as_completed
1011
from operator import itemgetter
12+
from json import JSONDecodeError
13+
from urllib.error import HTTPError
1114

1215
import requests
1316
from foil.formatters import format_repr_info
@@ -130,10 +133,14 @@ def get(self, ticker):
130133
return self.get_results(ticker, response, retrieved_at)
131134

132135
def get_results(self, ticker, response, retrieved_at):
133-
updated_at, timezone = self.transform_meta_data(response['Meta Data'])
136+
updated_at, timezone, is_intraday = self.transform_meta_data(response['Meta Data'])
134137
records = self.transform_records(response[self.data_key])
135138
records = self.sort_records(self.convert_timezones(records, timezone))
136139

140+
# remove intraday record in daily series
141+
if is_intraday:
142+
records = records[0:-1]
143+
137144
return Results(ticker, records, timezone,
138145
updated_at=updated_at, retrieved_at=retrieved_at)
139146

@@ -182,10 +189,23 @@ def transform_meta_data(self, response_meta):
182189
tz_key = next(
183190
key for key in response_meta if key.endswith('Time Zone')
184191
)
185-
updated_at = self.parse_time(response_meta[refresh_key])
192+
updated_at, is_intraday = self.parse_refresh_time(
193+
response_meta[refresh_key]
194+
)
186195
timezone = response_meta[tz_key]
187196

188-
return updated_at, timezone
197+
return updated_at, timezone, is_intraday
198+
199+
def parse_refresh_time(self, dt):
200+
is_intraday = False
201+
202+
try:
203+
updated_at = self.parse_time(dt)
204+
except ValueError:
205+
updated_at = self.parse_time(dt[0:10])
206+
is_intraday = True
207+
208+
return updated_at, is_intraday
189209

190210

191211
class AdjustedPriceHistory(PriceHistory):
@@ -236,12 +256,12 @@ def convert_timezones(self, records, timezone):
236256
yield record
237257

238258
def transform_meta_data(self, response_meta):
239-
updated_at, timezone = super().transform_meta_data(response_meta)
259+
updated_at, timezone, _ = super().transform_meta_data(response_meta)
240260

241261
if self.utc:
242262
updated_at = convert_to_utc(updated_at, timezone)
243263

244-
return updated_at, timezone
264+
return updated_at, timezone, None
245265

246266

247267
def get_time_series_function(period, adjusted=False):
@@ -280,3 +300,21 @@ def filter_dividends(records):
280300
for record in records:
281301
if record[DIVIDEND] != 0:
282302
yield record[DATE], record[DIVIDEND]
303+
304+
305+
def get_results(cls: PriceHistory, tickers: list, parameters: dict):
306+
"""Return multiple results using threads."""
307+
308+
with ThreadPoolExecutor(max_workers=4) as executor:
309+
future_to_ticker = {
310+
executor.submit(cls(**parameters).get, ticker): ticker
311+
for ticker in tickers
312+
}
313+
314+
for future in as_completed(future_to_ticker):
315+
ticker = future_to_ticker[future]
316+
317+
try:
318+
yield ticker, future.result()
319+
except (HTTPError, JSONDecodeError, KeyError):
320+
pass

tests/test_price_history.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ def test_sort_records(self):
108108
self.assertEqual(expected, result)
109109

110110
def test_transform_meta_data(self):
111-
expected = (self.updated_at, self.timezone)
111+
expected = (self.updated_at, self.timezone, False)
112112
result = self.price_history.transform_meta_data(MOCK_META)
113113

114114
self.assertEqual(expected, result)

0 commit comments

Comments
 (0)