Compare commits

...

111 Commits

Author SHA1 Message Date
ValueRaider
01ef1bb813 Update to 0.1.78 2022-10-18 12:53:22 +01:00
ValueRaider
1db6be75b8 Merge pull request #1093 from ranaroussi/fix/download-timezones
Add 'ignore_tz' arg to download()
2022-10-18 12:36:00 +01:00
ValueRaider
7902ec8667 Fix empty-df detection and date ordering 2022-10-18 12:31:51 +01:00
ValueRaider
ff42a3ac87 Add 'ignore_tz' arg to download() 2022-10-18 12:22:43 +01:00
ValueRaider
51f2c7301d Bump version again to 0.1.77 to skip bad tag 2022-10-07 22:04:16 +01:00
ValueRaider
632a16670a Bump version to 0.1.76 2022-10-07 21:55:15 +01:00
ValueRaider
fea0dca6f4 Merge pull request #1078 from ranaroussi/fix/info-access-when-unlisted
Fix/info access when unlisted
2022-10-07 21:44:44 +01:00
ValueRaider
c7e95152a0 Tidy code 2022-10-07 17:23:59 +01:00
ValueRaider
a52e972d04 Pretty error check for timezone retrieval 2022-10-07 17:20:58 +01:00
Ran Aroussi
a197d9f78e updated changelog 2022-10-04 12:09:19 +01:00
Ran Aroussi
dbb9bbfbf3 Updated to 0.1.75 2022-10-04 12:01:30 +01:00
ValueRaider
a7b053addd Merge pull request #1067 from ranaroussi/fix/deps
Add new dep 'appdirs' for recent tz-fixes PR
2022-10-01 13:52:10 +01:00
ValueRaider
e8ca256c10 Add new dep 'appdirs' for recent tz-fixes PR 2022-10-01 13:49:02 +01:00
ValueRaider
f651dd1e93 Merge pull request #1048 from yfinance-fork-team/fix/timezone
Fix timezone & datetime handling
2022-09-21 16:53:29 +01:00
ValueRaider
f40cf0aae1 Merge branch 'main' into fix/timezone 2022-09-20 23:28:17 +01:00
ValueRaider
200f57c458 Merge pull request #1033 from yfinance-fork-team/fix/yahoo-appending-latest-price
Bugfix: Relocate out-of-range check to before 15m->30m conversion
2022-09-20 20:24:13 +01:00
ValueRaider
e5d45eaa85 Merge pull request #1032 from yfinance-fork-team/feature/keep-na
Add 'keepna' argument
2022-09-20 14:17:34 +01:00
ValueRaider
42b77a9b54 Merge pull request #1042 from yfinance-fork-team/feature/init-perf-boost
Ticker.init() perf boost
2022-09-20 14:15:50 +01:00
ValueRaider
bca005a2c0 Move user dt parsing to utils.py 2022-09-04 17:45:37 +01:00
ValueRaider
ca891bb187 Restore index to DatetimeIndex ; remove tz arg ; refactor for clean merge 2022-09-04 16:09:55 +01:00
Value Raider
0939ff3c78 Support user providing epoch ints 2022-08-15 16:04:46 +01:00
Value Raider
6f5c5635be Preemptive bugfix 2022-07-28 12:30:13 +01:00
Value Raider
809622e426 Ticker init does unnecessary work - move it out 2022-07-28 12:25:43 +01:00
Value Raider
eec1f3dbad Fix when Yahoo messes up DST 2022-07-27 21:54:12 +01:00
Value Raider
1de789ad72 Fix timestamp->dt 2022-07-15 00:00:08 +01:00
Value Raider
cd68ff68c6 Adjust prices-after-end-dt fix from > to >= 2022-07-14 23:45:18 +01:00
Value Raider
9673970f45 Relocate out-of-range check to before 15m->30m interpolation 2022-07-14 23:04:41 +01:00
Value Raider
6ea69a70ac Add 'keepna' argument 2022-07-14 14:41:24 +01:00
Value Raider
c723a5ab44 Preemptive perf. boost 2022-07-14 01:03:24 +01:00
Value Raider
50741d1409 Add tz to daily/weekly - bugfix 2022-07-12 23:04:04 +01:00
Value Raider
69d0dcd62b Add tz to daily/weekly 2022-07-12 22:54:01 +01:00
Value Raider
5c9348f255 end time should default=00:00 not end-of-day - fix comment 2022-07-12 21:30:29 +01:00
Value Raider
a472546e7b end time should default=00:00 not end-of-day 2022-07-12 21:30:29 +01:00
Value Raider
c914f1f183 Fix user-supplied TZ ; reduce Git diff 2022-07-12 21:30:29 +01:00
Value Raider
92c82342fe Fix PYTZ misuse 2022-07-12 21:30:29 +01:00
Value Raider
7ae08b04f3 Fix setting end dt to midnight 2022-07-12 21:30:29 +01:00
Value Raider
4b50f1e81c Perf boost: store ticker timezone in user file cache 2022-07-12 21:30:29 +01:00
Value Raider
1ed58be749 Perf boost: separate info fetch from financials, to get tz fast 2022-07-12 21:30:28 +01:00
Value Raider
375b4f9376 Fix error when adding Dividends/Splits to df 2022-07-12 21:27:58 +01:00
Value Raider
b6b4426ca9 User-provided 'tz' should 'convert' not 'replace' UTC 2022-07-12 21:27:58 +01:00
Value Raider
149ebe46db Remove debugging print stmt 2022-07-12 21:27:58 +01:00
Value Raider
d80b27cfde Fix Yahoo returning tz in data 2022-07-12 21:27:58 +01:00
Value Raider
36e277317b Fix date->datetime conversion 2022-07-12 21:27:58 +01:00
Value Raider
0e1ea4d2c6 Replace zoneinfo with pytz (installed by pandas) 2022-07-12 21:27:58 +01:00
Value Raider
2d96c383ef Set start/end timezone to exchange if None 2022-07-12 21:27:57 +01:00
Ran Aroussi
ec6279736b Fixed bug introduced in 0.1.73 2022-07-11 23:20:47 +01:00
Ran Aroussi
5d942d9668 Update CHANGELOG.rst 2022-07-11 12:16:00 +01:00
Ran Aroussi
5782cb59fd Update version.py 2022-07-11 12:15:28 +01:00
Ran Aroussi
4c4861a8f1 Merge pull request #1000 from giorgossideris/shares_outstanding_none_values_bug_fix
Handle case that the API returns no data for shares of a specific year (#999)
2022-07-11 12:14:51 +01:00
Ran Aroussi
4d221ca70e Merge pull request #1009 from AndKraev/fix_724
rename incorrect yahoo key treasuryStock
2022-07-11 12:14:31 +01:00
Ran Aroussi
1a8d045baf Merge pull request #1023 from Ckend/main
bugfix: remove useless arg, otherwise affect the proxy arg.
2022-07-11 12:13:44 +01:00
Ran Aroussi
67a55c35ce Merge pull request #1024 from devpg/patch-1
Update multi.py
2022-07-11 12:13:21 +01:00
Ran Aroussi
e547fe4e41 Merge pull request #1026 from ValueRaider/fix/yahoo-appending-latest-price
Bug fix - Yahoo API appends latest price regardless of end date, remove it
2022-07-11 12:13:04 +01:00
Value Raider
9d5366d707 Bug fix - Yahoo API appends latest price regardless of end date, remove it 2022-07-09 18:35:12 +01:00
André Neubauer
4b07d1dceb Update multi.py
Documentation is wrong here. 'show_errors' does print errors if True, and doesn't print errors if False
2022-07-09 12:01:53 +02:00
ckend
9440c1e1c1 bugfix: remove useless arg, otherwise affect the proxy arg. 2022-07-08 17:53:54 +08:00
Ran Aroussi
773d003a67 Update CHANGELOG.rst 2022-06-16 16:32:33 +01:00
Ran Aroussi
a2905a0f8d Update version.py 2022-06-16 16:32:12 +01:00
Ran Aroussi
1810455e15 bugfix 2022-06-16 16:31:56 +01:00
Ran Aroussi
9770f286b4 v# + clog 2022-06-16 16:24:41 +01:00
Ran Aroussi
80eb0ddafb Merge branch 'main' of github.com:ranaroussi/yfinance 2022-06-16 16:17:16 +01:00
Ran Aroussi
a9e9e8dcb3 added Tickers(…).news() 2022-06-16 16:17:12 +01:00
Ran Aroussi
69c0673345 Merge pull request #959 from Kartheyan/patch-1
Fixed max period when interval is 1 minute
2022-06-16 16:16:53 +01:00
Ran Aroussi
8077cabf44 Merge branch 'main' of github.com:ranaroussi/yfinance 2022-06-16 16:04:32 +01:00
Ran Aroussi
e35b98ef1d pep stuff 2022-06-16 16:04:28 +01:00
Ran Aroussi
dc40c6d093 Merge pull request #955 from alker0/issue-677
Support cache in `Tickers()`
2022-06-16 16:04:20 +01:00
Ran Aroussi
8572628ba6 Merge pull request #1017 from ValueRaider/feature/earnings-dates
Feature - fetch full earnings dates history
2022-06-16 16:00:41 +01:00
Ran Aroussi
f878ce5ea5 Merge branch 'main' into feature/earnings-dates 2022-06-16 16:00:32 +01:00
Ran Aroussi
bbf68daf3f Merge pull request #1014 from rickturner2001/feature-earnings-history
Added get_earnings_history to fetch earnings data
2022-06-16 15:51:54 +01:00
Value Raider
afbb5d81a4 Tidy utils change 2022-06-16 12:12:04 +01:00
Value Raider
670ec8e766 Return empty DF if YF missing earnings dates 2022-06-16 12:08:16 +01:00
Value Raider
8fa9438072 Fix EPS % to 0->1 2022-06-16 12:07:15 +01:00
Value Raider
8c56a0c3df Fix timezone handling 2022-06-15 21:17:33 +01:00
Value Raider
6e90fc17cb Fix handling of missing data 2022-06-15 19:57:24 +01:00
Value Raider
0849f68dd8 Clean&format earnings_dates table 2022-06-15 19:36:54 +01:00
Value Raider
77f3810cdd Add feature get_earnings_dates() 2022-06-15 17:42:22 +01:00
rickturner01
f3583b00e3 Added function to retreive earnings calendar 2022-06-11 22:23:15 -04:00
rickturner01
d0606cdb03 Added get_earnings_history to fetch earnings data 2022-06-11 02:50:42 -04:00
andKraev
76a9b09e8e rename incorrect yahoo key treasuryStock 2022-05-27 16:53:17 +03:00
giorgossideris
d757b8f25f Handle case that the API returns no data for shares of a specific year 2022-05-09 15:24:10 +03:00
Kartheyan
ab50edf24c Fixed max period when interval is 1 minute
When attempting to retrieve the maximum period of time (period="max") at 1 minute intervals, the previous default start period of 1900-01-01 (631159200) would result in an error from the API with description "1m data not available for startTime=1497744000 and endTime=*NOW*. Only 7 days worth of 1m granularity data are allowed to be fetched per request." . By setting the start time to subtracting end time minus 604800 (86400*7), it now returns 1 week of 1 minute interval data which is the maximum period for which the API supports at 1 minute intervals.
2022-02-15 15:27:37 +00:00
alker0
9b4bce7d06 Support cache in Tickers() 2022-02-09 14:51:11 +09:00
Ran Aroussi
a396d93601 Update CHANGELOG.rst 2022-01-30 12:13:53 +00:00
Ran Aroussi
79d6741f1e Update version.py 2022-01-30 12:13:15 +00:00
Ran Aroussi
47a119f63b Update setup.py 2022-01-27 16:35:53 +00:00
Ran Aroussi
436c077ee2 Update requirements.txt 2022-01-27 16:35:43 +00:00
Ran Aroussi
72b1e9699e Merge pull request #941 from moshfrid/main
Explicitly set datetime index on actions table
2022-01-27 16:34:50 +00:00
Ran Aroussi
d2c2690cc2 Merge pull request #943 from rafaelsdellama/main
Fix: dev: AttributeError
2022-01-27 16:33:13 +00:00
Rafael Del Lama
65c1753fb2 lock pandas version on setup.py 2022-01-27 13:18:09 -03:00
Rafael Del Lama
93edc3c163 Fix: dev: AttributeError
Lock pandas version to fix AttributeError: 'Index' object has no attribute 'tz_localize' after pandas upgrade version to 1.4.0
2022-01-27 12:03:26 -03:00
moshfrid
b2e2acad06 Explicitly set datetime index on actions table
Explicitly set datetime index on actions df so that if the actions df is empty, it doesn't reset the index when combined with the rest of the dataframe.
2022-01-24 19:14:12 -05:00
ran
24e348e0e4 merged pr #920 2022-01-12 15:43:10 +00:00
Ran Aroussi
0ac69cfd49 Merge pull request #920 from tsherknus/main
Resolves UnboundLocalError in base.py history()
2022-01-12 15:41:34 +00:00
tsherknus
cdced94a00 Resolves UnboundLocalError in base.py history()
In base.py history(), session.get can return None or 404. Clarify exception catching within the history() method of base.py.

Ensure none and 404 values are caught prior to quote processing.

This prevents json parse exception, connection exception, or unbound exception from being thrown during data collection.

Includes updated unit testing.
2022-01-11 18:51:12 -05:00
ran
317239ec6c removed python 3.5 support 2021-12-30 15:36:11 +00:00
ran
6904fa9875 removed python 3.5 support 2021-12-30 15:35:43 +00:00
ran
2259a7a922 updated requests version solves some errors 2021-12-30 15:27:46 +00:00
Ran Aroussi
6ef4be3662 Merge pull request #896 from giorgossideris/shares_outstanding_addition
Make Historical Shares Outstanding number available (#892)
2021-12-30 15:26:48 +00:00
Ran Aroussi
44d2a5e001 Merge pull request #907 from tsherknus/yfinance_001
JSONDecodeError When Data is None
2021-12-30 15:26:38 +00:00
Ran Aroussi
0b88d2f552 Merge pull request #911 from asafravid/patch-12
Complementary key-statistics (currently fetching the important traili…
2021-12-30 15:25:30 +00:00
tsherknus
92a1470b86 Resolves an issue in base.py during historic retrieval of data.
In base.py history(), session.get can return None. Add an additional condition on line 190 of base.py has been added, which will throw a RuntimeError before the json parse exception can be thrown.
2021-12-30 08:40:54 -05:00
Asaf
4439d34d05 Complementary key-statistics (currently fetching the important trailingPegRatio which is the value shown in the website)
Complementary key-statistics (currently fetching the important trailingPegRatio which is the value shown in the website)
2021-12-20 14:21:31 +02:00
giorgossideris
063495a270 Make Historical Shares Outstanding number available (#892)
Before this commit there was no option to get the last 4 years' Shares Outstanding of a company. Now, this numbers can be found at the <code>Ticker.shares</code> attribute. Specifically, they refer to the average number of basic shares for each year.
2021-11-24 23:03:04 +02:00
Ran Aroussi
1297cd10e8 Merge pull request #891 from oliverlewis/oliverlewis/bug_Timezone_value_passed_on_to_the_option_chain_throws_TypeError 2021-11-21 10:41:35 +00:00
Oliver Lewis
9659af19ef typo changed to tz_convert 2021-11-20 23:23:56 -08:00
Oliver Lewis
16d7b54f4a passing utc true as default and adding dt field as series 2021-11-20 22:54:27 -08:00
Ran Aroussi
9040571ddd Update README.md 2021-11-20 20:50:16 +00:00
Ran Aroussi
4abad90a59 Delete docs directory 2021-11-20 20:21:56 +00:00
Ran Aroussi
0e52e92d4c Set theme jekyll-theme-minimal 2021-11-20 20:21:31 +00:00
ran
9eef951acc Added legal disclaimers
Added legal disclaimers to the README file to make sure people are aware that this library is not affiliated, endorsed, or vetted by Yahoo, Inc
2021-11-20 20:13:48 +00:00
Ran Aroussi
bc0cc1b42f Update and rename README.rst to README.md 2021-11-20 20:01:04 +00:00
24 changed files with 896 additions and 729 deletions

View File

@@ -6,7 +6,6 @@ fast_finish: true
matrix:
include:
- python: 2.7
- python: 3.5
- python: 3.6
- python: 3.7
- python: 3.8

View File

@@ -1,6 +1,61 @@
Change Log
===========
0.1.78
------
- Fix download() when different timezones #1085
0.1.77
------
- Fix user experience bug #1078
0.1.75
------
- Fixed datetime-related issues: #1048
- Add 'keepna' argument #1032
- Speedup Ticker() creation #1042
- Improve a bugfix #1033
0.1.74
------
- Fixed bug introduced in 0.1.73 (sorry :/)
0.1.73
------
- Merged several PR that fixed misc issues
0.1.72
------
- Misc bugfixs
0.1.71
------
- Added Tickers(…).news()
- Return empty DF if YF missing earnings dates
- Fix EPS % to 0->1
- Fix timezone handling
- Fix handling of missing data
- Clean&format earnings_dates table
- Add ``.get_earnings_dates()`` to retreive earnings calendar
- Added ``.get_earnings_history()`` to fetch earnings data
0.1.70
------
- Bug fixed - Closes #937
0.1.69
------
- Bug fixed - #920
0.1.68
------
- Upgraded requests dependency
- Removed Python 3.5 support
0.1.67
------
- Added legal disclaimers to make sure people are aware that this library is not affiliated, endorsed, or vetted by Yahoo, Inc.
0.1.66
------
- Merged PR to allow yfinance to be pickled

301
README.md Normal file
View File

@@ -0,0 +1,301 @@
# Download market data from Yahoo! Finance's API
<table border=1 cellpadding=10><tr><td>
#### \*\*\* IMPORTANT LEGAL DISCLAIMER \*\*\*
---
**Yahoo!, Y!Finance, and Yahoo! finance are registered trademarks of
Yahoo, Inc.**
yfinance is **not** affiliated, endorsed, or vetted by Yahoo, Inc. It's
an open-source tool that uses Yahoo's publicly available APIs, and is
intended for research and educational purposes.
**You should refer to Yahoo!'s terms of use**
([here](https://policies.yahoo.com/us/en/yahoo/terms/product-atos/apiforydn/index.htm),
[here](https://legal.yahoo.com/us/en/yahoo/terms/otos/index.html), and
[here](https://policies.yahoo.com/us/en/yahoo/terms/index.htm)) **for
details on your rights to use the actual data downloaded. Remember - the
Yahoo! finance API is intended for personal use only.**
</td></tr></table>
---
<a target="new" href="https://pypi.python.org/pypi/yfinance"><img border=0 src="https://img.shields.io/badge/python-2.7,%203.6+-blue.svg?style=flat" alt="Python version"></a>
<a target="new" href="https://pypi.python.org/pypi/yfinance"><img border=0 src="https://img.shields.io/pypi/v/yfinance.svg?maxAge=60%" alt="PyPi version"></a>
<a target="new" href="https://pypi.python.org/pypi/yfinance"><img border=0 src="https://img.shields.io/pypi/status/yfinance.svg?maxAge=60" alt="PyPi status"></a>
<a target="new" href="https://pypi.python.org/pypi/yfinance"><img border=0 src="https://img.shields.io/pypi/dm/yfinance.svg?maxAge=2592000&label=installs&color=%2327B1FF" alt="PyPi downloads"></a>
<a target="new" href="https://travis-ci.com/github/ranaroussi/yfinance"><img border=0 src="https://img.shields.io/travis/ranaroussi/yfinance/main.svg?maxAge=1" alt="Travis-CI build status"></a>
<a target="new" href="https://www.codefactor.io/repository/github/ranaroussi/yfinance"><img border=0 src="https://www.codefactor.io/repository/github/ranaroussi/yfinance/badge" alt="CodeFactor"></a>
<a target="new" href="https://github.com/ranaroussi/yfinance"><img border=0 src="https://img.shields.io/github/stars/ranaroussi/yfinance.svg?style=social&label=Star&maxAge=60" alt="Star this repo"></a>
<a target="new" href="https://twitter.com/aroussi"><img border=0 src="https://img.shields.io/twitter/follow/aroussi.svg?style=social&label=Follow&maxAge=60" alt="Follow me on twitter"></a>
**yfinance** offers a threaded and Pythonic way to download market data from [Yahoo!Ⓡ finance](https://finance.yahoo.com).
→ Check out this [Blog post](https://aroussi.com/#post/python-yahoo-finance) for a detailed tutorial with code examples.
[Changelog »](https://github.com/ranaroussi/yfinance/blob/main/CHANGELOG.rst)
---
## Quick Start
### The Ticker module
The `Ticker` module, which allows you to access ticker data in a more Pythonic way:
Note: yahoo finance datetimes are received as UTC.
```python
import yfinance as yf
msft = yf.Ticker("MSFT")
# get stock info
msft.info
# get historical market data
hist = msft.history(period="max")
# show actions (dividends, splits)
msft.actions
# show dividends
msft.dividends
# show splits
msft.splits
# show financials
msft.financials
msft.quarterly_financials
# show major holders
msft.major_holders
# show institutional holders
msft.institutional_holders
# show balance sheet
msft.balance_sheet
msft.quarterly_balance_sheet
# show cashflow
msft.cashflow
msft.quarterly_cashflow
# show earnings
msft.earnings
msft.quarterly_earnings
# show sustainability
msft.sustainability
# show analysts recommendations
msft.recommendations
# show next event (earnings, etc)
msft.calendar
# show all earnings dates
msft.earnings_dates
# show ISIN code - *experimental*
# ISIN = International Securities Identification Number
msft.isin
# show options expirations
msft.options
# show news
msft.news
# get option chain for specific expiration
opt = msft.option_chain('YYYY-MM-DD')
# data available via: opt.calls, opt.puts
```
If you want to use a proxy server for downloading data, use:
```python
import yfinance as yf
msft = yf.Ticker("MSFT")
msft.history(..., proxy="PROXY_SERVER")
msft.get_actions(proxy="PROXY_SERVER")
msft.get_dividends(proxy="PROXY_SERVER")
msft.get_splits(proxy="PROXY_SERVER")
msft.get_balance_sheet(proxy="PROXY_SERVER")
msft.get_cashflow(proxy="PROXY_SERVER")
msft.option_chain(..., proxy="PROXY_SERVER")
...
```
To use a custom `requests` session (for example to cache calls to the
API or customize the `User-agent` header), pass a `session=` argument to
the Ticker constructor.
```python
import requests_cache
session = requests_cache.CachedSession('yfinance.cache')
session.headers['User-agent'] = 'my-program/1.0'
ticker = yf.Ticker('msft aapl goog', session=session)
# The scraped response will be stored in the cache
ticker.actions
```
To initialize multiple `Ticker` objects, use
```python
import yfinance as yf
tickers = yf.Tickers('msft aapl goog')
# ^ returns a named tuple of Ticker objects
# access each ticker using (example)
tickers.tickers.MSFT.info
tickers.tickers.AAPL.history(period="1mo")
tickers.tickers.GOOG.actions
```
### Fetching data for multiple tickers
```python
import yfinance as yf
data = yf.download("SPY AAPL", start="2017-01-01", end="2017-04-30")
```
I've also added some options to make life easier :)
```python
data = yf.download( # or pdr.get_data_yahoo(...
# tickers list or string as well
tickers = "SPY AAPL MSFT",
# use "period" instead of start/end
# valid periods: 1d,5d,1mo,3mo,6mo,1y,2y,5y,10y,ytd,max
# (optional, default is '1mo')
period = "ytd",
# fetch data by interval (including intraday if period < 60 days)
# valid intervals: 1m,2m,5m,15m,30m,60m,90m,1h,1d,5d,1wk,1mo,3mo
# (optional, default is '1d')
interval = "1m",
# Whether to ignore timezone when aligning ticker data from
# different timezones. Default is True. False may be useful for
# minute/hourly data.
ignore_tz = False,
# group by ticker (to access via data['SPY'])
# (optional, default is 'column')
group_by = 'ticker',
# adjust all OHLC automatically
# (optional, default is False)
auto_adjust = True,
# download pre/post regular market hours data
# (optional, default is False)
prepost = True,
# use threads for mass downloading? (True/False/Integer)
# (optional, default is True)
threads = True,
# proxy URL scheme use use when downloading?
# (optional, default is None)
proxy = None
)
```
### Managing Multi-Level Columns
The following answer on Stack Overflow is for [How to deal with
multi-level column names downloaded with
yfinance?](https://stackoverflow.com/questions/63107801)
- `yfinance` returns a `pandas.DataFrame` with multi-level column
names, with a level for the ticker and a level for the stock price
data
- The answer discusses:
- How to correctly read the the multi-level columns after
saving the dataframe to a csv with `pandas.DataFrame.to_csv`
- How to download single or multiple tickers into a single
dataframe with single level column names and a ticker column
---
## `pandas_datareader` override
If your code uses `pandas_datareader` and you want to download data
faster, you can "hijack" `pandas_datareader.data.get_data_yahoo()`
method to use **yfinance** while making sure the returned data is in the
same format as **pandas\_datareader**'s `get_data_yahoo()`.
```python
from pandas_datareader import data as pdr
import yfinance as yf
yf.pdr_override() # <== that's all it takes :-)
# download dataframe
data = pdr.get_data_yahoo("SPY", start="2017-01-01", end="2017-04-30")
```
---
## Installation
Install `yfinance` using `pip`:
``` {.sourceCode .bash}
$ pip install yfinance --upgrade --no-cache-dir
```
To install `yfinance` using `conda`, see
[this](https://anaconda.org/ranaroussi/yfinance).
### Requirements
- [Python](https://www.python.org) \>= 2.7, 3.4+
- [Pandas](https://github.com/pydata/pandas) (tested to work with
\>=0.23.1)
- [Numpy](http://www.numpy.org) \>= 1.11.1
- [requests](http://docs.python-requests.org/en/master/) \>= 2.14.2
- [lxml](https://pypi.org/project/lxml/) \>= 4.5.1
- [appdirs](https://pypi.org/project/appdirs) \>=1.4.4
### Optional (if you want to use `pandas_datareader`)
- [pandas\_datareader](https://github.com/pydata/pandas-datareader)
\>= 0.4.0
---
### Legal Stuff
**yfinance** is distributed under the **Apache Software License**. See
the [LICENSE.txt](./LICENSE.txt) file in the release for details.
AGAIN - yfinance is **not** affiliated, endorsed, or vetted by Yahoo, Inc. It's
an open-source tool that uses Yahoo's publicly available APIs, and is
intended for research and educational purposes. You should refer to Yahoo!'s terms of use
([here](https://policies.yahoo.com/us/en/yahoo/terms/product-atos/apiforydn/index.htm),
[here](https://legal.yahoo.com/us/en/yahoo/terms/otos/index.html), and
[here](https://policies.yahoo.com/us/en/yahoo/terms/index.htm)) for
detailes on your rights to use the actual data downloaded.
---
### P.S.
Please drop me an note with any feedback you have.
**Ran Aroussi**

View File

@@ -1,301 +0,0 @@
Yahoo! Finance market data downloader
=====================================
.. image:: https://img.shields.io/badge/python-2.7,%203.4+-blue.svg?style=flat
:target: https://pypi.python.org/pypi/yfinance
:alt: Python version
.. image:: https://img.shields.io/pypi/v/yfinance.svg?maxAge=60
:target: https://pypi.python.org/pypi/yfinance
:alt: PyPi version
.. image:: https://img.shields.io/pypi/status/yfinance.svg?maxAge=60
:target: https://pypi.python.org/pypi/yfinance
:alt: PyPi status
.. image:: https://img.shields.io/pypi/dm/yfinance.svg?maxAge=2592000&label=installs&color=%2327B1FF
:target: https://pypi.python.org/pypi/yfinance
:alt: PyPi downloads
.. image:: https://img.shields.io/travis/ranaroussi/yfinance/main.svg?maxAge=1
:target: https://travis-ci.com/github/ranaroussi/yfinance
:alt: Travis-CI build status
.. image:: https://www.codefactor.io/repository/github/ranaroussi/yfinance/badge
:target: https://www.codefactor.io/repository/github/ranaroussi/yfinance
:alt: CodeFactor
.. image:: https://img.shields.io/github/stars/ranaroussi/yfinance.svg?style=social&label=Star&maxAge=60
:target: https://github.com/ranaroussi/yfinance
:alt: Star this repo
.. image:: https://img.shields.io/twitter/follow/aroussi.svg?style=social&label=Follow&maxAge=60
:target: https://twitter.com/aroussi
:alt: Follow me on twitter
\
Ever since `Yahoo! finance <https://finance.yahoo.com>`_ decommissioned
their historical data API, many programs that relied on it to stop working.
**yfinance** aims to solve this problem by offering a reliable, threaded,
and Pythonic way to download historical market data from Yahoo! finance.
NOTE
~~~~
The library was originally named ``fix-yahoo-finance``, but
I've since renamed it to ``yfinance`` as I no longer consider it a mere "fix".
For reasons of backward-compatibility, ``fix-yahoo-finance`` now import and
uses ``yfinance``, but you should install and use ``yfinance`` directly.
`Changelog » <https://github.com/ranaroussi/yfinance/blob/main/CHANGELOG.rst>`__
-----
==> Check out this `Blog post <https://aroussi.com/#post/python-yahoo-finance>`_ for a detailed tutorial with code examples.
-----
Quick Start
===========
The Ticker module
~~~~~~~~~~~~~~~~~
The ``Ticker`` module, which allows you to access
ticker data in a more Pythonic way:
Note: yahoo finance datetimes are received as UTC.
.. code:: python
import yfinance as yf
msft = yf.Ticker("MSFT")
# get stock info
msft.info
# get historical market data
hist = msft.history(period="max")
# show actions (dividends, splits)
msft.actions
# show dividends
msft.dividends
# show splits
msft.splits
# show financials
msft.financials
msft.quarterly_financials
# show major holders
msft.major_holders
# show institutional holders
msft.institutional_holders
# show balance sheet
msft.balance_sheet
msft.quarterly_balance_sheet
# show cashflow
msft.cashflow
msft.quarterly_cashflow
# show earnings
msft.earnings
msft.quarterly_earnings
# show sustainability
msft.sustainability
# show analysts recommendations
msft.recommendations
# show next event (earnings, etc)
msft.calendar
# show ISIN code - *experimental*
# ISIN = International Securities Identification Number
msft.isin
# show options expirations
msft.options
# show news
msft.news
# get option chain for specific expiration
opt = msft.option_chain('YYYY-MM-DD')
# data available via: opt.calls, opt.puts
If you want to use a proxy server for downloading data, use:
.. code:: python
import yfinance as yf
msft = yf.Ticker("MSFT")
msft.history(..., proxy="PROXY_SERVER")
msft.get_actions(proxy="PROXY_SERVER")
msft.get_dividends(proxy="PROXY_SERVER")
msft.get_splits(proxy="PROXY_SERVER")
msft.get_balance_sheet(proxy="PROXY_SERVER")
msft.get_cashflow(proxy="PROXY_SERVER")
msft.option_chain(..., proxy="PROXY_SERVER")
...
To use a custom ``requests`` session (for example to cache calls to the API
or customize the ``User-agent`` header), pass a ``session=`` argument to the
Ticker constructor.
.. code:: python
import requests_cache
session = requests_cache.CachedSession('yfinance.cache')
session.headers['User-agent'] = 'my-program/1.0'
ticker = yf.Ticker('msft aapl goog', session=session)
# The scraped response will be stored in the cache
ticker.actions
To initialize multiple ``Ticker`` objects, use
.. code:: python
import yfinance as yf
tickers = yf.Tickers('msft aapl goog')
# ^ returns a named tuple of Ticker objects
# access each ticker using (example)
tickers.tickers.MSFT.info
tickers.tickers.AAPL.history(period="1mo")
tickers.tickers.GOOG.actions
Fetching data for multiple tickers
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code:: python
import yfinance as yf
data = yf.download("SPY AAPL", start="2017-01-01", end="2017-04-30")
I've also added some options to make life easier :)
.. code:: python
data = yf.download( # or pdr.get_data_yahoo(...
# tickers list or string as well
tickers = "SPY AAPL MSFT",
# use "period" instead of start/end
# valid periods: 1d,5d,1mo,3mo,6mo,1y,2y,5y,10y,ytd,max
# (optional, default is '1mo')
period = "ytd",
# fetch data by interval (including intraday if period < 60 days)
# valid intervals: 1m,2m,5m,15m,30m,60m,90m,1h,1d,5d,1wk,1mo,3mo
# (optional, default is '1d')
interval = "1m",
# group by ticker (to access via data['SPY'])
# (optional, default is 'column')
group_by = 'ticker',
# adjust all OHLC automatically
# (optional, default is False)
auto_adjust = True,
# download pre/post regular market hours data
# (optional, default is False)
prepost = True,
# use threads for mass downloading? (True/False/Integer)
# (optional, default is True)
threads = True,
# proxy URL scheme use use when downloading?
# (optional, default is None)
proxy = None
)
Managing Multi-Level Columns
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The following answer on Stack Overflow is for `How to deal with multi-level column names downloaded with yfinance? <https://stackoverflow.com/questions/63107801>`_
* ``yfinance`` returns a ``pandas.DataFrame`` with multi-level column names, with a level for the ticker and a level for the stock price data
* The answer discusses:
* How to correctly read the the multi-level columns after saving the dataframe to a csv with ``pandas.DataFrame.to_csv``
* How to download single or multiple tickers into a single dataframe with single level column names and a ticker column
``pandas_datareader`` override
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If your code uses ``pandas_datareader`` and you want to download data faster,
you can "hijack" ``pandas_datareader.data.get_data_yahoo()`` method to use
**yfinance** while making sure the returned data is in the same format as
**pandas_datareader**'s ``get_data_yahoo()``.
.. code:: python
from pandas_datareader import data as pdr
import yfinance as yf
yf.pdr_override() # <== that's all it takes :-)
# download dataframe
data = pdr.get_data_yahoo("SPY", start="2017-01-01", end="2017-04-30")
Installation
------------
Install ``yfinance`` using ``pip``:
.. code:: bash
$ pip install yfinance --upgrade --no-cache-dir
To install ``yfinance`` using ``conda``, see `this <https://anaconda.org/ranaroussi/yfinance>`_.
Requirements
------------
* `Python <https://www.python.org>`_ >= 2.7, 3.4+
* `Pandas <https://github.com/pydata/pandas>`_ (tested to work with >=0.23.1)
* `Numpy <http://www.numpy.org>`_ >= 1.11.1
* `requests <http://docs.python-requests.org/en/master/>`_ >= 2.14.2
* `lxml <https://pypi.org/project/lxml/>`_ >= 4.5.1
Optional (if you want to use ``pandas_datareader``)
---------------------------------------------------
* `pandas_datareader <https://github.com/pydata/pandas-datareader>`_ >= 0.4.0
Legal Stuff
------------
**yfinance** is distributed under the **Apache Software License**. See the `LICENSE.txt <./LICENSE.txt>`_ file in the release for details.
P.S.
------------
Please drop me an note with any feedback you have.
**Ran Aroussi**

View File

@@ -1,3 +0,0 @@
# Ticker
::: yfinance.ticker.Ticker

View File

@@ -1,3 +0,0 @@
# TickerBase Reference
::: yfinance.base.TickerBase

View File

@@ -1,3 +0,0 @@
# Tickers Reference
::: yfinance.tickers.Tickers

View File

@@ -1,18 +0,0 @@
Yahoo! Finance market data downloader
=====================================
Ever since [Yahoo! finance](https://finance.yahoo.com) decommissioned
their historical data API, many programs that relied on it to stop
working.
**yfinance** aims to solve this problem by offering a reliable,
threaded, and Pythonic way to download historical market data from
Yahoo! finance.
NOTE
----
The library was originally named `fix-yahoo-finance`, but I've since
renamed it to `yfinance` as I no longer consider it a mere "fix". For
reasons of backward-compatibility, `fix-yahoo-finance` now import and
uses `yfinance`, but you should install and use `yfinance` directly.

View File

@@ -1,28 +0,0 @@
Installation
============
Install `yfinance` using `pip`:
``` {.sourceCode .bash}
$ pip install yfinance --upgrade --no-cache-dir
```
Install `yfinance` using `conda`:
``` {.sourceCode .bash}
$ conda install -c ranaroussi yfinance
```
### Requirements
- [Python](https://www.python.org) \>= 2.7, 3.4+
- [Pandas](https://github.com/pydata/pandas) (tested to work with
\>=0.23.1)
- [Numpy](http://www.numpy.org) \>= 1.11.1
- [requests](http://docs.python-requests.org/en/master/) \>= 2.14.2
- [lxml](https://pypi.org/project/lxml/) \>= 4.5.1
### Optional (if you want to use `pandas_datareader`)
- [pandas\_datareader](https://github.com/pydata/pandas-datareader)
\>= 0.4.0

View File

@@ -1,3 +0,0 @@
# multi.py Reference
::: yfinance.multi

View File

@@ -1,201 +0,0 @@
Quick Start
===========
The Ticker module
-----------------
The `Ticker` module, which allows you to access ticker data in a more
Pythonic way:
Note: yahoo finance datetimes are received as UTC.
``` {.sourceCode .python}
import yfinance as yf
msft = yf.Ticker("MSFT")
# get stock info
msft.info
# get historical market data
hist = msft.history(period="max")
# show actions (dividends, splits)
msft.actions
# show dividends
msft.dividends
# show splits
msft.splits
# show financials
msft.financials
msft.quarterly_financials
# show major holders
msft.major_holders
# show institutional holders
msft.institutional_holders
# show balance sheet
msft.balance_sheet
msft.quarterly_balance_sheet
# show cashflow
msft.cashflow
msft.quarterly_cashflow
# show earnings
msft.earnings
msft.quarterly_earnings
# show sustainability
msft.sustainability
# show analysts recommendations
msft.recommendations
# show next event (earnings, etc)
msft.calendar
# show ISIN code - *experimental*
# ISIN = International Securities Identification Number
msft.isin
# show options expirations
msft.options
# show news
msft.news
# get option chain for specific expiration
opt = msft.option_chain('YYYY-MM-DD')
# data available via: opt.calls, opt.puts
```
If you want to use a proxy server for downloading data, use:
``` {.sourceCode .python}
import yfinance as yf
msft = yf.Ticker("MSFT")
msft.history(..., proxy="PROXY_SERVER")
msft.get_actions(proxy="PROXY_SERVER")
msft.get_dividends(proxy="PROXY_SERVER")
msft.get_splits(proxy="PROXY_SERVER")
msft.get_balance_sheet(proxy="PROXY_SERVER")
msft.get_cashflow(proxy="PROXY_SERVER")
msft.option_chain(..., proxy="PROXY_SERVER")
...
```
To use a custom `requests` session (for example to cache calls to the
API or customize the `User-agent` header), pass a `session=` argument to
the Ticker constructor.
``` {.sourceCode .python}
import requests_cache
session = requests_cache.CachedSession('yfinance.cache')
session.headers['User-agent'] = 'my-program/1.0'
ticker = yf.Ticker('msft aapl goog', session=session)
# The scraped response will be stored in the cache
ticker.actions
```
To initialize multiple `Ticker` objects, use
``` {.sourceCode .python}
import yfinance as yf
tickers = yf.Tickers('msft aapl goog')
# ^ returns a named tuple of Ticker objects
# access each ticker using (example)
tickers.tickers.MSFT.info
tickers.tickers.AAPL.history(period="1mo")
tickers.tickers.GOOG.actions
```
Fetching data for multiple tickers
----------------------------------
``` {.sourceCode .python}
import yfinance as yf
data = yf.download("SPY AAPL", start="2017-01-01", end="2017-04-30")
```
I've also added some options to make life easier :)
``` {.sourceCode .python}
data = yf.download( # or pdr.get_data_yahoo(...
# tickers list or string as well
tickers = "SPY AAPL MSFT",
# use "period" instead of start/end
# valid periods: 1d,5d,1mo,3mo,6mo,1y,2y,5y,10y,ytd,max
# (optional, default is '1mo')
period = "ytd",
# fetch data by interval (including intraday if period < 60 days)
# valid intervals: 1m,2m,5m,15m,30m,60m,90m,1h,1d,5d,1wk,1mo,3mo
# (optional, default is '1d')
interval = "1m",
# group by ticker (to access via data['SPY'])
# (optional, default is 'column')
group_by = 'ticker',
# adjust all OHLC automatically
# (optional, default is False)
auto_adjust = True,
# download pre/post regular market hours data
# (optional, default is False)
prepost = True,
# use threads for mass downloading? (True/False/Integer)
# (optional, default is True)
threads = True,
# proxy URL scheme use use when downloading?
# (optional, default is None)
proxy = None
)
```
Managing Multi-Level Columns
----------------------------
The following answer on Stack Overflow is for [How to deal with
multi-level column names downloaded with
yfinance?](https://stackoverflow.com/questions/63107801)
- `yfinance` returns a `pandas.DataFrame` with multi-level column
names, with a level for the ticker and a level for the stock price
data
- The answer discusses:
- How to correctly read the the multi-level columns after
saving the dataframe to a csv with `pandas.DataFrame.to_csv`
- How to download single or multiple tickers into a single
dataframe with single level column names and a ticker column
`pandas_datareader` override
----------------------------
If your code uses `pandas_datareader` and you want to download data
faster, you can "hijack" `pandas_datareader.data.get_data_yahoo()`
method to use **yfinance** while making sure the returned data is in the
same format as **pandas\_datareader**'s `get_data_yahoo()`.
``` {.sourceCode .python}
from pandas_datareader import data as pdr
import yfinance as yf
yf.pdr_override() # <== that's all it takes :-)
# download dataframe
data = pdr.get_data_yahoo("SPY", start="2017-01-01", end="2017-04-30")
```

View File

@@ -1,3 +0,0 @@
# utils.py Reference
::: yfinance.utils

View File

@@ -21,6 +21,7 @@ requirements:
- requests >=2.21
- multitasking >=0.0.7
- lxml >=4.5.1
- appdirs >= 1.4.4
- pip
- python
@@ -30,6 +31,7 @@ requirements:
- requests >=2.21
- multitasking >=0.0.7
- lxml >=4.5.1
- appdirs >= 1.4.4
- python
test:

View File

@@ -1,5 +1,6 @@
pandas>=0.24.0
numpy>=1.16.5
requests>=2.21
requests>=2.26
multitasking>=0.0.7
lxml>=4.5.1
appdirs>=1.4.4

View File

@@ -1,10 +1,10 @@
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
#
# Yahoo! Finance market data downloader (+fix for Pandas Datareader)
# yfinance - market data downloader
# https://github.com/ranaroussi/yfinance
"""Yahoo! Finance market data downloader (+fix for Pandas Datareader)"""
"""yfinance - market data downloader"""
from setuptools import setup, find_packages
# from codecs import open
@@ -22,14 +22,15 @@ with open("yfinance/version.py") as f:
here = path.abspath(path.dirname(__file__))
# Get the long description from the README file
with io.open(path.join(here, 'README.rst'), encoding='utf-8') as f:
with io.open(path.join(here, 'README.md'), encoding='utf-8') as f:
long_description = f.read()
setup(
name='yfinance',
version=version,
description='Yahoo! Finance market data downloader',
description='Download market data from Yahoo! Finance API',
long_description=long_description,
long_description_content_type='text/markdown',
url='https://github.com/ranaroussi/yfinance',
author='Ran Aroussi',
author_email='ran@aroussi.com',
@@ -52,7 +53,7 @@ setup(
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
# 'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
@@ -60,12 +61,18 @@ setup(
platforms=['any'],
keywords='pandas, yahoo finance, pandas datareader',
packages=find_packages(exclude=['contrib', 'docs', 'tests', 'examples']),
install_requires=['pandas>=0.24', 'numpy>=1.15',
'requests>=2.20', 'multitasking>=0.0.7',
'lxml>=4.5.1'],
install_requires=['pandas>=0.24.0', 'numpy>=1.15',
'requests>=2.26', 'multitasking>=0.0.7',
'lxml>=4.5.1', 'appdirs>=1.4.4'],
entry_points={
'console_scripts': [
'sample=sample:main',
],
},
)
print("""
NOTE: yfinance is not affiliated, endorsed, or vetted by Yahoo, Inc.
You should refer to Yahoo!'s terms of use for details on your rights
to use the actual data downloaded.""")

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
#
# Yahoo! Finance market data downloader (+fix for Pandas Datareader)
# yfinance - market data downloader
# https://github.com/ranaroussi/yfinance
"""
@@ -25,7 +25,8 @@ class TestTicker(unittest.TestCase):
for ticker in tickers:
# always should have info and history for valid symbols
assert(ticker.info is not None and ticker.info != {})
assert(ticker.history(period="max").empty is False)
history = ticker.history(period="max")
assert(history.empty is False and history is not None)
def test_attributes(self):
for ticker in tickers:
@@ -50,6 +51,9 @@ class TestTicker(unittest.TestCase):
ticker.sustainability
ticker.options
ticker.news
ticker.shares
ticker.earnings_history
ticker.earnings_dates
def test_holders(self):
for ticker in tickers:

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Yahoo! Finance market data downloader (+fix for Pandas Datareader)
# yfinance - market data downloader
# https://github.com/ranaroussi/yfinance
#
# Copyright 2017-2019 Ran Aroussi

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Yahoo! Finance market data downloader (+fix for Pandas Datareader)
# yfinance - market data downloader
# https://github.com/ranaroussi/yfinance
#
# Copyright 2017-2019 Ran Aroussi
@@ -23,6 +23,7 @@ from __future__ import print_function
import time as _time
import datetime as _datetime
import pytz as _tz
import requests as _requests
import pandas as _pd
import numpy as _np
@@ -35,7 +36,7 @@ except ImportError:
from . import utils
# import json as _json
import json as _json
# import re as _re
# import sys as _sys
@@ -43,6 +44,8 @@ from . import shared
_BASE_URL_ = 'https://query2.finance.yahoo.com'
_SCRAPE_URL_ = 'https://finance.yahoo.com/quote'
_ROOT_URL_ = 'https://finance.yahoo.com'
class TickerBase():
def __init__(self, ticker, session=None):
@@ -51,6 +54,7 @@ class TickerBase():
self._history = None
self._base_url = _BASE_URL_
self._scrape_url = _SCRAPE_URL_
self._tz = None
self._fundamentals = False
self._info = None
@@ -62,22 +66,17 @@ class TickerBase():
self._mutualfund_holders = None
self._isin = None
self._news = []
self._shares = None
self._calendar = None
self._expirations = {}
self._earnings_dates = None
self._earnings_history = None
self._earnings = {
"yearly": utils.empty_df(),
"quarterly": utils.empty_df()}
self._financials = {
"yearly": utils.empty_df(),
"quarterly": utils.empty_df()}
self._balancesheet = {
"yearly": utils.empty_df(),
"quarterly": utils.empty_df()}
self._cashflow = {
"yearly": utils.empty_df(),
"quarterly": utils.empty_df()}
self._earnings = None
self._financials = None
self._balancesheet = None
self._cashflow = None
# accept isin as ticker
if utils.is_isin(self.ticker):
@@ -101,8 +100,8 @@ class TickerBase():
def history(self, period="1mo", interval="1d",
start=None, end=None, prepost=False, actions=True,
auto_adjust=True, back_adjust=False,
proxy=None, rounding=False, tz=None, timeout=None, **kwargs):
auto_adjust=True, back_adjust=False, keepna=False,
proxy=None, rounding=False, timeout=None, **kwargs):
"""
:Parameters:
period : str
@@ -124,14 +123,14 @@ class TickerBase():
Adjust all OHLC automatically? Default is True
back_adjust: bool
Back-adjusted data to mimic true historical prices
keepna: bool
Keep NaN rows returned by Yahoo?
Default is False
proxy: str
Optional. Proxy server URL scheme. Default is None
rounding: bool
Round values to 2 decimal places?
Optional. Default is False = precision suggested by Yahoo!
tz: str
Optional timezone locale for dates.
(default data is returned as non-localized dates)
timeout: None or float
If not None stops waiting for a response after given number of
seconds. (Can also be a fraction of a second e.g. 0.01)
@@ -142,21 +141,38 @@ class TickerBase():
error message printing to console.
"""
# Work with errors
debug_mode = True
if "debug" in kwargs and isinstance(kwargs["debug"], bool):
debug_mode = kwargs["debug"]
err_msg = "No data found for this date range, symbol may be delisted"
if start or period is None or period.lower() == "max":
if start is None:
start = -631159200
elif isinstance(start, _datetime.datetime):
start = int(_time.mktime(start.timetuple()))
else:
start = int(_time.mktime(
_time.strptime(str(start), '%Y-%m-%d')))
# Check can get TZ. Fail => probably delisted
try:
tz = self._get_ticker_tz()
except KeyError as e:
if "exchangeTimezoneName" in str(e):
shared._DFS[self.ticker] = utils.empty_df()
shared._ERRORS[self.ticker] = err_msg
if "many" not in kwargs and debug_mode:
print('- %s: %s' % (self.ticker, err_msg))
return utils.empty_df()
else:
raise
if end is None:
end = int(_time.time())
elif isinstance(end, _datetime.datetime):
end = int(_time.mktime(end.timetuple()))
else:
end = int(_time.mktime(_time.strptime(str(end), '%Y-%m-%d')))
end = utils._parse_user_dt(end, tz)
if start is None:
if interval == "1m":
start = end - 604800 # Subtract 7 days
else:
start = -631159200
else:
start = utils._parse_user_dt(start, tz)
params = {"period1": start, "period2": end}
else:
period = period.lower()
@@ -180,25 +196,32 @@ class TickerBase():
url = "{}/v8/finance/chart/{}".format(self._base_url, self.ticker)
session = self.session or _requests
data = session.get(
url=url,
params=params,
proxies=proxy,
headers=utils.user_agent_headers,
timeout=timeout
)
if "Will be right back" in data.text:
raise RuntimeError("*** YAHOO! FINANCE IS CURRENTLY DOWN! ***\n"
"Our engineers are working quickly to resolve "
"the issue. Thank you for your patience.")
data = data.json()
data = None
# Work with errors
debug_mode = True
if "debug" in kwargs and isinstance(kwargs["debug"], bool):
debug_mode = kwargs["debug"]
try:
data = session.get(
url=url,
params=params,
proxies=proxy,
headers=utils.user_agent_headers,
timeout=timeout
)
if "Will be right back" in data.text or data is None:
raise RuntimeError("*** YAHOO! FINANCE IS CURRENTLY DOWN! ***\n"
"Our engineers are working quickly to resolve "
"the issue. Thank you for your patience.")
data = data.json()
except Exception:
pass
if data is None or not type(data) is dict or 'status_code' in data.keys():
shared._DFS[self.ticker] = utils.empty_df()
shared._ERRORS[self.ticker] = err_msg
if "many" not in kwargs and debug_mode:
print('- %s: %s' % (self.ticker, err_msg))
return utils.empty_df()
err_msg = "No data found for this date range, symbol may be delisted"
if "chart" in data and data["chart"]["error"]:
err_msg = data["chart"]["error"]["description"]
shared._DFS[self.ticker] = utils.empty_df()
@@ -217,7 +240,12 @@ class TickerBase():
# parse quotes
try:
quotes = utils.parse_quotes(data["chart"]["result"][0], tz)
quotes = utils.parse_quotes(data["chart"]["result"][0])
# Yahoo bug fix - it often appends latest price even if after end date
if end and not quotes.empty:
endDt = _pd.to_datetime(_datetime.datetime.utcfromtimestamp(end))
if quotes.index[quotes.shape[0]-1] >= endDt:
quotes = quotes.iloc[0:quotes.shape[0]-1]
except Exception:
shared._DFS[self.ticker] = utils.empty_df()
shared._ERRORS[self.ticker] = err_msg
@@ -265,10 +293,13 @@ class TickerBase():
"chart"]["result"][0]["meta"]["priceHint"])
quotes['Volume'] = quotes['Volume'].fillna(0).astype(_np.int64)
quotes.dropna(inplace=True)
if not keepna:
quotes.dropna(inplace=True)
# actions
dividends, splits = utils.parse_actions(data["chart"]["result"][0], tz)
dividends, splits = utils.parse_actions(data["chart"]["result"][0])
tz_exchange = data["chart"]["result"][0]["meta"]["exchangeTimezoneName"]
# combine
df = _pd.concat([quotes, dividends, splits], axis=1, sort=True)
@@ -276,17 +307,16 @@ class TickerBase():
df["Stock Splits"].fillna(0, inplace=True)
# index eod/intraday
df.index = df.index.tz_localize("UTC").tz_convert(
data["chart"]["result"][0]["meta"]["exchangeTimezoneName"])
df.index = df.index.tz_localize("UTC").tz_convert(tz_exchange)
df = utils.fix_Yahoo_dst_issue(df, params["interval"])
if params["interval"][-1] == "m":
df.index.name = "Datetime"
elif params["interval"] == "1h":
pass
else:
df.index = _pd.to_datetime(df.index.date)
if tz is not None:
df.index = df.index.tz_localize(tz)
df.index = _pd.to_datetime(df.index.date).tz_localize(tz_exchange)
df.index.name = "Date"
# duplicates and missing rows cleanup
@@ -302,32 +332,30 @@ class TickerBase():
# ------------------------
def _get_fundamentals(self, kind=None, proxy=None):
def cleanup(data):
df = _pd.DataFrame(data).drop(columns=['maxAge'])
for col in df.columns:
df[col] = _np.where(
df[col].astype(str) == '-', _np.nan, df[col])
def _get_ticker_tz(self):
if not self._tz is None:
return self._tz
df.set_index('endDate', inplace=True)
try:
df.index = _pd.to_datetime(df.index, unit='s')
except ValueError:
df.index = _pd.to_datetime(df.index)
df = df.T
df.columns.name = ''
df.index.name = 'Breakdown'
tkr_tz = utils.cache_lookup_tkr_tz(self.ticker)
if tkr_tz is None:
tkr_tz = self.info["exchangeTimezoneName"]
# info fetch is relatively slow so cache timezone
utils.cache_store_tkr_tz(self.ticker, tkr_tz)
df.index = utils.camel2title(df.index)
return df
self._tz = tkr_tz
return tkr_tz
def _get_info(self, proxy=None):
# setup proxy in requests format
if proxy is not None:
if isinstance(proxy, dict) and "https" in proxy:
proxy = proxy["https"]
proxy = {"https": proxy}
if self._fundamentals:
if (self._info is None) or (self._sustainability is None) or (self._recommendations is None):
## Need to fetch
pass
else:
return
ticker_url = "{}/{}".format(self._scrape_url, self.ticker)
@@ -335,42 +363,6 @@ class TickerBase():
# get info and sustainability
data = utils.get_json(ticker_url, proxy, self.session)
# holders
try:
resp = utils.get_html(ticker_url + '/holders', proxy, self.session)
holders = _pd.read_html(resp)
except Exception:
holders = []
if len(holders) >= 3:
self._major_holders = holders[0]
self._institutional_holders = holders[1]
self._mutualfund_holders = holders[2]
elif len(holders) >= 2:
self._major_holders = holders[0]
self._institutional_holders = holders[1]
elif len(holders) >= 1:
self._major_holders = holders[0]
# self._major_holders = holders[0]
# self._institutional_holders = holders[1]
if self._institutional_holders is not None:
if 'Date Reported' in self._institutional_holders:
self._institutional_holders['Date Reported'] = _pd.to_datetime(
self._institutional_holders['Date Reported'])
if '% Out' in self._institutional_holders:
self._institutional_holders['% Out'] = self._institutional_holders[
'% Out'].str.replace('%', '').astype(float) / 100
if self._mutualfund_holders is not None:
if 'Date Reported' in self._mutualfund_holders:
self._mutualfund_holders['Date Reported'] = _pd.to_datetime(
self._mutualfund_holders['Date Reported'])
if '% Out' in self._mutualfund_holders:
self._mutualfund_holders['% Out'] = self._mutualfund_holders[
'% Out'].str.replace('%', '').astype(float) / 100
# sustainability
d = {}
try:
@@ -401,7 +393,7 @@ class TickerBase():
except Exception:
pass
# For ETFs, provide this valuable data: the top holdings of the ETF
# For ETFs, provide this valuable data: the top holdings of the ETF
try:
if 'topHoldings' in data:
self._info.update(data['topHoldings'])
@@ -462,10 +454,85 @@ class TickerBase():
except Exception:
pass
def _get_fundamentals(self, proxy=None):
def cleanup(data):
df = _pd.DataFrame(data).drop(columns=['maxAge'])
for col in df.columns:
df[col] = _np.where(
df[col].astype(str) == '-', _np.nan, df[col])
df.set_index('endDate', inplace=True)
try:
df.index = _pd.to_datetime(df.index, unit='s')
except ValueError:
df.index = _pd.to_datetime(df.index)
df = df.T
df.columns.name = ''
df.index.name = 'Breakdown'
# rename incorrect yahoo key
df.rename(index={'treasuryStock': 'Gains Losses Not Affecting Retained Earnings'}, inplace=True)
df.index = utils.camel2title(df.index)
return df
# setup proxy in requests format
if proxy is not None:
if isinstance(proxy, dict) and "https" in proxy:
proxy = proxy["https"]
proxy = {"https": proxy}
if self._fundamentals:
return
ticker_url = "{}/{}".format(self._scrape_url, self.ticker)
# holders
try:
resp = utils.get_html(ticker_url + '/holders', proxy, self.session)
holders = _pd.read_html(resp)
except Exception:
holders = []
if len(holders) >= 3:
self._major_holders = holders[0]
self._institutional_holders = holders[1]
self._mutualfund_holders = holders[2]
elif len(holders) >= 2:
self._major_holders = holders[0]
self._institutional_holders = holders[1]
elif len(holders) >= 1:
self._major_holders = holders[0]
# self._major_holders = holders[0]
# self._institutional_holders = holders[1]
if self._institutional_holders is not None:
if 'Date Reported' in self._institutional_holders:
self._institutional_holders['Date Reported'] = _pd.to_datetime(
self._institutional_holders['Date Reported'])
if '% Out' in self._institutional_holders:
self._institutional_holders['% Out'] = self._institutional_holders[
'% Out'].str.replace('%', '').astype(float) / 100
if self._mutualfund_holders is not None:
if 'Date Reported' in self._mutualfund_holders:
self._mutualfund_holders['Date Reported'] = _pd.to_datetime(
self._mutualfund_holders['Date Reported'])
if '% Out' in self._mutualfund_holders:
self._mutualfund_holders['% Out'] = self._mutualfund_holders[
'% Out'].str.replace('%', '').astype(float) / 100
self._get_info(proxy)
# get fundamentals
data = utils.get_json(ticker_url + '/financials', proxy, self.session)
# generic patterns
self._earnings = {"yearly": utils.empty_df(), "quarterly": utils.empty_df()}
self._cashflow = {"yearly": utils.empty_df(), "quarterly": utils.empty_df()}
self._balancesheet = {"yearly": utils.empty_df(), "quarterly": utils.empty_df()}
self._financials = {"yearly": utils.empty_df(), "quarterly": utils.empty_df()}
for key in (
(self._cashflow, 'cashflowStatement', 'cashflowStatements'),
(self._balancesheet, 'balanceSheet', 'balanceSheetStatements'),
@@ -503,6 +570,21 @@ class TickerBase():
except Exception:
pass
# shares outstanding
try:
# keep only years with non None data
available_shares = [shares_data for shares_data in data['annualBasicAverageShares'] if shares_data]
shares = _pd.DataFrame(available_shares)
shares['Year'] = shares['asOfDate'].agg(lambda x: int(x[:4]))
shares.set_index('Year', inplace=True)
shares.drop(columns=['dataId', 'asOfDate',
'periodType', 'currencyCode'], inplace=True)
shares.rename(
columns={'reportedValue': "BasicShares"}, inplace=True)
self._shares = shares
except Exception:
pass
# Analysis
data = utils.get_json(ticker_url + '/analysis', proxy, self.session)
@@ -522,24 +604,68 @@ class TickerBase():
if isinstance(colval, dict):
dict_cols.append(colname)
for k, v in colval.items():
new_colname = colname + ' ' + utils.camel2title([k])[0]
new_colname = colname + ' ' + \
utils.camel2title([k])[0]
analysis.loc[idx, new_colname] = v
self._analysis = analysis[[c for c in analysis.columns if c not in dict_cols]]
self._analysis = analysis[[
c for c in analysis.columns if c not in dict_cols]]
except Exception:
pass
# Complementary key-statistics (currently fetching the important trailingPegRatio which is the value shown in the website)
res = {}
try:
my_headers = {'user-agent': 'curl/7.55.1', 'accept': 'application/json', 'content-type': 'application/json',
'referer': 'https://finance.yahoo.com/', 'cache-control': 'no-cache', 'connection': 'close'}
p = _re.compile(r'root\.App\.main = (.*);')
r = _requests.session().get('https://finance.yahoo.com/quote/{}/key-statistics?p={}'.format(self.ticker,
self.ticker), headers=my_headers)
q_results = {}
my_qs_keys = ['pegRatio'] # QuoteSummaryStore
# , 'quarterlyPegRatio'] # QuoteTimeSeriesStore
my_ts_keys = ['trailingPegRatio']
# Complementary key-statistics
data = _json.loads(p.findall(r.text)[0])
key_stats = data['context']['dispatcher']['stores']['QuoteTimeSeriesStore']
q_results.setdefault(self.ticker, [])
for i in my_ts_keys:
# j=0
try:
# res = {i: key_stats['timeSeries'][i][1]['reportedValue']['raw']}
# We need to loop over multiple items, if they exist: 0,1,2,..
zzz = key_stats['timeSeries'][i]
for j in range(len(zzz)):
if key_stats['timeSeries'][i][j]:
res = {i: key_stats['timeSeries']
[i][j]['reportedValue']['raw']}
q_results[self.ticker].append(res)
# print(res)
# q_results[ticker].append(res)
except:
q_results[ticker].append({i: np.nan})
res = {'Company': ticker}
q_results[ticker].append(res)
except Exception:
pass
if 'trailingPegRatio' in res:
self._info['trailingPegRatio'] = res['trailingPegRatio']
self._fundamentals = True
def get_recommendations(self, proxy=None, as_dict=False, *args, **kwargs):
self._get_fundamentals(proxy=proxy)
self._get_info(proxy)
data = self._recommendations
if as_dict:
return data.to_dict()
return data
def get_calendar(self, proxy=None, as_dict=False, *args, **kwargs):
self._get_fundamentals(proxy=proxy)
self._get_info(proxy)
data = self._calendar
if as_dict:
return data.to_dict()
@@ -569,14 +695,14 @@ class TickerBase():
return data
def get_info(self, proxy=None, as_dict=False, *args, **kwargs):
self._get_fundamentals(proxy=proxy)
self._get_info(proxy)
data = self._info
if as_dict:
return data.to_dict()
return data
def get_sustainability(self, proxy=None, as_dict=False, *args, **kwargs):
self._get_fundamentals(proxy=proxy)
self._get_info(proxy)
data = self._sustainability
if as_dict:
return data.to_dict()
@@ -646,6 +772,13 @@ class TickerBase():
return actions[actions != 0].dropna(how='all').fillna(0)
return []
def get_shares(self, proxy=None, as_dict=False, *args, **kwargs):
self._get_fundamentals(proxy=proxy)
data = self._shares
if as_dict:
return data.to_dict()
return data
def get_isin(self, proxy=None):
# *** experimental ***
if self._isin is not None:
@@ -719,3 +852,121 @@ class TickerBase():
# parse news
self._news = data.get("news", [])
return self._news
def get_earnings_dates(self, proxy=None):
if self._earnings_dates is not None:
return self._earnings_dates
# setup proxy in requests format
if proxy is not None:
if isinstance(proxy, dict) and "https" in proxy:
proxy = proxy["https"]
proxy = {"https": proxy}
page_size = 100 # YF caps at 100, don't go higher
page_offset = 0
dates = None
while True:
url = "{}/calendar/earnings?symbol={}&offset={}&size={}".format(
_ROOT_URL_, self.ticker, page_offset, page_size)
session = self.session or _requests
data = session.get(
url=url,
proxies=proxy,
headers=utils.user_agent_headers
).text
if "Will be right back" in data:
raise RuntimeError("*** YAHOO! FINANCE IS CURRENTLY DOWN! ***\n"
"Our engineers are working quickly to resolve "
"the issue. Thank you for your patience.")
try:
data = _pd.read_html(data)[0]
except ValueError:
if page_offset == 0:
# Should not fail on first page
if "Showing Earnings for:" in data:
# Actually YF was successful, problem is company doesn't have earnings history
dates = utils.empty_earnings_dates_df()
break
if dates is None:
dates = data
else:
dates = _pd.concat([dates, data], axis=0)
page_offset += page_size
if dates is None:
raise Exception("No data found, symbol may be delisted")
dates = dates.reset_index(drop=True)
# Drop redundant columns
dates = dates.drop(["Symbol", "Company"], axis=1)
# Convert types
for cn in ["EPS Estimate", "Reported EPS", "Surprise(%)"]:
dates.loc[dates[cn] == '-', cn] = "NaN"
dates[cn] = dates[cn].astype(float)
# Convert % to range 0->1:
dates["Surprise(%)"] *= 0.01
# Parse earnings date string
cn = "Earnings Date"
# - remove AM/PM and timezone from date string
tzinfo = dates[cn].str.extract('([AP]M[a-zA-Z]*)$')
dates[cn] = dates[cn].replace(' [AP]M[a-zA-Z]*$', '', regex=True)
# - split AM/PM from timezone
tzinfo = tzinfo[0].str.extract('([AP]M)([a-zA-Z]*)', expand=True)
tzinfo.columns = ["AM/PM", "TZ"]
# - combine and parse
dates[cn] = dates[cn] + ' ' + tzinfo["AM/PM"]
dates[cn] = _pd.to_datetime(dates[cn], format="%b %d, %Y, %I %p")
# - instead of attempting decoding of ambiguous timezone abbreviation, just use 'info':
dates[cn] = dates[cn].dt.tz_localize(
tz=self.info["exchangeTimezoneName"])
dates = dates.set_index("Earnings Date")
self._earnings_dates = dates
return dates
def get_earnings_history(self, proxy=None):
if self._earnings_history:
return self._earnings_history
# setup proxy in requests format
if proxy is not None:
if isinstance(proxy, dict) and "https" in proxy:
proxy = proxy["https"]
proxy = {"https": proxy}
url = "{}/calendar/earnings?symbol={}".format(_ROOT_URL_, self.ticker)
session = self.session or _requests
data = session.get(
url=url,
proxies=proxy,
headers=utils.user_agent_headers
).text
if "Will be right back" in data:
raise RuntimeError("*** YAHOO! FINANCE IS CURRENTLY DOWN! ***\n"
"Our engineers are working quickly to resolve "
"the issue. Thank you for your patience.")
try:
# read_html returns a list of pandas Dataframes of all the tables in `data`
data = _pd.read_html(data)[0]
data.replace("-", _np.nan, inplace=True)
data['EPS Estimate'] = _pd.to_numeric(data['EPS Estimate'])
data['Reported EPS'] = _pd.to_numeric(data['Reported EPS'])
self._earnings_history = data
# if no tables are found a ValueError is thrown
except ValueError:
print("Could not find data for {}.".format(self.ticker))
return
return data

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Yahoo! Finance market data downloader (+fix for Pandas Datareader)
# yfinance - market data downloader
# https://github.com/ranaroussi/yfinance
#
# Copyright 2017-2019 Ran Aroussi
@@ -29,8 +29,8 @@ from . import Ticker, utils
from . import shared
def download(tickers, start=None, end=None, actions=False, threads=True,
group_by='column', auto_adjust=False, back_adjust=False,
def download(tickers, start=None, end=None, actions=False, threads=True, ignore_tz=True,
group_by='column', auto_adjust=False, back_adjust=False, keepna=False,
progress=True, period="max", show_errors=True, interval="1d", prepost=False,
proxy=None, rounding=False, timeout=None, **kwargs):
"""Download yahoo tickers
@@ -56,16 +56,22 @@ def download(tickers, start=None, end=None, actions=False, threads=True,
Default is False
auto_adjust: bool
Adjust all OHLC automatically? Default is False
keepna: bool
Keep NaN rows returned by Yahoo?
Default is False
actions: bool
Download dividend + stock splits data. Default is False
threads: bool / int
How many threads to use for mass downloading. Default is True
ignore_tz: bool
When combining from different timezones, ignore that part of datetime.
Default is True
proxy: str
Optional. Proxy server URL scheme. Default is None
rounding: bool
Optional. Round values to 2 decimal places?
show_errors: bool
Optional. Doesn't print errors if True
Optional. Doesn't print errors if False
timeout: None or float
If not None stops waiting for a response after given number of
seconds. (Can also be a fraction of a second e.g. 0.01)
@@ -105,7 +111,7 @@ def download(tickers, start=None, end=None, actions=False, threads=True,
_download_one_threaded(ticker, period=period, interval=interval,
start=start, end=end, prepost=prepost,
actions=actions, auto_adjust=auto_adjust,
back_adjust=back_adjust,
back_adjust=back_adjust, keepna=keepna,
progress=(progress and i > 0), proxy=proxy,
rounding=rounding, timeout=timeout)
while len(shared._DFS) < len(tickers):
@@ -117,7 +123,7 @@ def download(tickers, start=None, end=None, actions=False, threads=True,
data = _download_one(ticker, period=period, interval=interval,
start=start, end=end, prepost=prepost,
actions=actions, auto_adjust=auto_adjust,
back_adjust=back_adjust, proxy=proxy,
back_adjust=back_adjust, keepna=keepna, proxy=proxy,
rounding=rounding, timeout=timeout)
shared._DFS[ticker.upper()] = data
if progress:
@@ -137,12 +143,17 @@ def download(tickers, start=None, end=None, actions=False, threads=True,
ticker = tickers[0]
return shared._DFS[shared._ISINS.get(ticker, ticker)]
if ignore_tz:
for tkr in shared._DFS.keys():
if (shared._DFS[tkr] is not None) and (shared._DFS[tkr].shape[0]>0):
shared._DFS[tkr].index = shared._DFS[tkr].index.tz_localize(None)
try:
data = _pd.concat(shared._DFS.values(), axis=1,
data = _pd.concat(shared._DFS.values(), axis=1, sort=True,
keys=shared._DFS.keys())
except Exception:
_realign_dfs()
data = _pd.concat(shared._DFS.values(), axis=1,
data = _pd.concat(shared._DFS.values(), axis=1, sort=True,
keys=shared._DFS.keys())
# switch names back to isins if applicable
@@ -183,11 +194,11 @@ def _download_one_threaded(ticker, start=None, end=None,
auto_adjust=False, back_adjust=False,
actions=False, progress=True, period="max",
interval="1d", prepost=False, proxy=None,
rounding=False, timeout=None):
keepna=False, rounding=False, timeout=None):
data = _download_one(ticker, start, end, auto_adjust, back_adjust,
actions, period, interval, prepost, proxy, rounding,
timeout)
keepna, timeout)
shared._DFS[ticker.upper()] = data
if progress:
shared._PROGRESS_BAR.animate()
@@ -197,11 +208,11 @@ def _download_one(ticker, start=None, end=None,
auto_adjust=False, back_adjust=False,
actions=False, period="max", interval="1d",
prepost=False, proxy=None, rounding=False,
timeout=None):
keepna=False, timeout=None):
return Ticker(ticker).history(period=period, interval=interval,
start=start, end=end, prepost=prepost,
actions=actions, auto_adjust=auto_adjust,
back_adjust=back_adjust, proxy=proxy,
rounding=rounding, many=True,
rounding=rounding, keepna=keepna, many=True,
timeout=timeout)

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Yahoo! Finance market data downloader (+fix for Pandas Datareader)
# yfinance - market data downloader
# https://github.com/ranaroussi/yfinance
#
# Copyright 2017-2019 Ran Aroussi

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Yahoo! Finance market data downloader (+fix for Pandas Datareader)
# yfinance - market data downloader
# https://github.com/ranaroussi/yfinance
#
# Copyright 2017-2019 Ran Aroussi
@@ -84,9 +84,9 @@ class Ticker(TickerBase):
'currency'])
data['lastTradeDate'] = _pd.to_datetime(
data['lastTradeDate'], unit='s')
data['lastTradeDate'], unit='s', utc=True)
if tz is not None:
data['lastTradeDate'] = data['lastTradeDate'].tz_localize(tz)
data['lastTradeDate'] = data['lastTradeDate'].dt.tz_convert(tz)
return data
def option_chain(self, date=None, proxy=None, tz=None):
@@ -138,6 +138,10 @@ class Ticker(TickerBase):
def actions(self):
return self.get_actions()
@property
def shares(self):
return self.get_shares()
@property
def info(self):
return self.get_info()
@@ -207,3 +211,11 @@ class Ticker(TickerBase):
@property
def analysis(self):
return self.get_analysis()
@property
def earnings_history(self):
return self.get_earnings_history()
@property
def earnings_dates(self):
return self.get_earnings_dates()

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Yahoo! Finance market data downloader (+fix for Pandas Datareader)
# yfinance - market data downloader
# https://github.com/ranaroussi/yfinance
#
# Copyright 2017-2019 Ran Aroussi
@@ -30,14 +30,14 @@ class Tickers():
def __repr__(self):
return 'yfinance.Tickers object <%s>' % ",".join(self.symbols)
def __init__(self, tickers):
def __init__(self, tickers, session=None):
tickers = tickers if isinstance(
tickers, list) else tickers.replace(',', ' ').split()
self.symbols = [ticker.upper() for ticker in tickers]
ticker_objects = {}
for ticker in self.symbols:
ticker_objects[ticker] = Ticker(ticker)
ticker_objects[ticker] = Ticker(ticker, session=session)
self.tickers = ticker_objects
# self.tickers = _namedtuple(
@@ -51,11 +51,11 @@ class Tickers():
timeout=None, **kwargs):
return self.download(
period, interval,
start, end, prepost,
actions, auto_adjust, proxy,
threads, group_by, progress,
timeout, **kwargs)
period, interval,
start, end, prepost,
actions, auto_adjust, proxy,
threads, group_by, progress,
timeout, **kwargs)
def download(self, period="1mo", interval="1d",
start=None, end=None, prepost=False,
@@ -85,3 +85,12 @@ class Tickers():
data.sort_index(level=0, axis=1, inplace=True)
return data
def news(self):
collection = {}
for ticker in self.symbols:
collection[ticker] = []
items = Ticker(ticker).news
for item in items:
collection[ticker].append(item)
return collection

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Yahoo! Finance market data downloader (+fix for Pandas Datareader)
# yfinance - market data downloader
# https://github.com/ranaroussi/yfinance
#
# Copyright 2017-2019 Ran Aroussi
@@ -21,11 +21,15 @@
from __future__ import print_function
import datetime as _datetime
import pytz as _tz
import requests as _requests
import re as _re
import pandas as _pd
import numpy as _np
import sys as _sys
import os as _os
import appdirs as _ad
try:
import ujson as _json
@@ -33,7 +37,8 @@ except ImportError:
import json as _json
user_agent_headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36'}
user_agent_headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36'}
def is_isin(string):
@@ -88,6 +93,13 @@ def empty_df(index=[]):
return empty
def empty_earnings_dates_df():
empty = _pd.DataFrame(
columns=["Symbol", "Company", "Earnings Date",
"EPS Estimate", "Reported EPS", "Surprise(%)"])
return empty
def get_html(url, proxy=None, session=None):
session = session or _requests
html = session.get(url=url, proxies=proxy, headers=user_agent_headers).text
@@ -107,6 +119,13 @@ def get_json(url, proxy=None, session=None):
'(this)')[0].split(';\n}')[0].strip()
data = _json.loads(json_str)[
'context']['dispatcher']['stores']['QuoteSummaryStore']
# add data about Shares Outstanding for companies' tickers if they are available
try:
data['annualBasicAverageShares'] = _json.loads(
json_str)['context']['dispatcher']['stores'][
'QuoteTimeSeriesStore']['timeSeries']['annualBasicAverageShares']
except Exception:
pass
# return data
new_data = _json.dumps(data).replace('{}', 'null')
@@ -120,6 +139,23 @@ def camel2title(o):
return [_re.sub("([a-z])([A-Z])", r"\g<1> \g<2>", i).title() for i in o]
def _parse_user_dt(dt, exchange_tz):
if isinstance(dt, int):
## Should already be epoch, test with conversion:
_datetime.datetime.fromtimestamp(dt)
else:
# Convert str/date -> datetime, set tzinfo=exchange, get timestamp:
if isinstance(dt, str):
dt = _datetime.datetime.strptime(str(dt), '%Y-%m-%d')
if isinstance(dt, _datetime.date) and not isinstance(dt, _datetime.datetime):
dt = _datetime.datetime.combine(dt, _datetime.time(0))
if isinstance(dt, _datetime.datetime) and dt.tzinfo is None:
# Assume user is referring to exchange's timezone
dt = _tz.timezone(exchange_tz).localize(dt)
dt = int(dt.timestamp())
return dt
def auto_adjust(data):
df = data.copy()
ratio = df["Close"] / df["Adj Close"]
@@ -161,7 +197,7 @@ def back_adjust(data):
return df[["Open", "High", "Low", "Close", "Volume"]]
def parse_quotes(data, tz=None):
def parse_quotes(data):
timestamps = data["timestamp"]
ohlc = data["indicators"]["quote"][0]
volumes = ohlc["volume"]
@@ -184,15 +220,14 @@ def parse_quotes(data, tz=None):
quotes.index = _pd.to_datetime(timestamps, unit="s")
quotes.sort_index(inplace=True)
if tz is not None:
quotes.index = quotes.index.tz_localize(tz)
return quotes
def parse_actions(data, tz=None):
dividends = _pd.DataFrame(columns=["Dividends"])
splits = _pd.DataFrame(columns=["Stock Splits"])
def parse_actions(data):
dividends = _pd.DataFrame(
columns=["Dividends"], index=_pd.DatetimeIndex([]))
splits = _pd.DataFrame(
columns=["Stock Splits"], index=_pd.DatetimeIndex([]))
if "events" in data:
if "dividends" in data["events"]:
@@ -201,8 +236,6 @@ def parse_actions(data, tz=None):
dividends.set_index("date", inplace=True)
dividends.index = _pd.to_datetime(dividends.index, unit="s")
dividends.sort_index(inplace=True)
if tz is not None:
dividends.index = dividends.index.tz_localize(tz)
dividends.columns = ["Dividends"]
@@ -212,8 +245,6 @@ def parse_actions(data, tz=None):
splits.set_index("date", inplace=True)
splits.index = _pd.to_datetime(splits.index, unit="s")
splits.sort_index(inplace=True)
if tz is not None:
splits.index = splits.index.tz_localize(tz)
splits["Stock Splits"] = splits["numerator"] / \
splits["denominator"]
splits = splits["Stock Splits"]
@@ -221,6 +252,19 @@ def parse_actions(data, tz=None):
return dividends, splits
def fix_Yahoo_dst_issue(df, interval):
if interval in ["1d","1w","1wk"]:
# These intervals should start at time 00:00. But for some combinations of date and timezone,
# Yahoo has time off by few hours (e.g. Brazil 23:00 around Jan-2022). Suspect DST problem.
# The clue is (a) minutes=0 and (b) hour near 0.
# Obviously Yahoo meant 00:00, so ensure this doesn't affect date conversion:
f_pre_midnight = (df.index.minute == 0) & (df.index.hour.isin([22,23]))
dst_error_hours = _np.array([0]*df.shape[0])
dst_error_hours[f_pre_midnight] = 24-df.index[f_pre_midnight].hour
df.index += _pd.TimedeltaIndex(dst_error_hours, 'h')
return df
class ProgressBar:
def __init__(self, iterations, text='completed'):
self.text = text
@@ -269,3 +313,37 @@ class ProgressBar:
def __str__(self):
return str(self.prog_bar)
# Simple file cache of ticker->timezone:
def get_cache_dirpath():
return _os.path.join(_ad.user_cache_dir(), "py-yfinance")
def cache_lookup_tkr_tz(tkr):
fp = _os.path.join(get_cache_dirpath(), "tkr-tz.csv")
if not _os.path.isfile(fp):
return None
df = _pd.read_csv(fp)
f = df["Ticker"] == tkr
if sum(f) == 0:
return None
return df["Tz"][f].iloc[0]
def cache_store_tkr_tz(tkr,tz):
df = _pd.DataFrame({"Ticker":[tkr], "Tz":[tz]})
dp = get_cache_dirpath()
if not _os.path.isdir(dp):
_os.makedirs(dp)
fp = _os.path.join(dp, "tkr-tz.csv")
if not _os.path.isfile(fp):
df.to_csv(fp, index=False)
return
df_all = _pd.read_csv(fp)
f = df_all["Ticker"]==tkr
if sum(f) > 0:
raise Exception("Tkr {} tz already in cache".format(tkr))
_pd.concat([df_all,df]).to_csv(fp, index=False)

View File

@@ -1 +1 @@
version = "0.1.66"
version = "0.1.78"