Compare commits
142 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
317239ec6c | ||
|
|
6904fa9875 | ||
|
|
2259a7a922 | ||
|
|
6ef4be3662 | ||
|
|
44d2a5e001 | ||
|
|
0b88d2f552 | ||
|
|
92a1470b86 | ||
|
|
4439d34d05 | ||
|
|
063495a270 | ||
|
|
1297cd10e8 | ||
|
|
9659af19ef | ||
|
|
16d7b54f4a | ||
|
|
9040571ddd | ||
|
|
4abad90a59 | ||
|
|
0e52e92d4c | ||
|
|
9eef951acc | ||
|
|
bc0cc1b42f | ||
|
|
608849efc9 | ||
|
|
1c390a51c2 | ||
|
|
cade9b0715 | ||
|
|
a1852184c4 | ||
|
|
45be120cef | ||
|
|
c92b7ee3bf | ||
|
|
275c0fe002 | ||
|
|
4f27b17073 | ||
|
|
a41b1820ac | ||
|
|
b1fc53134b | ||
|
|
f83a63bd4e | ||
|
|
a76e12aca2 | ||
|
|
43d037dd25 | ||
|
|
bb202aa7a0 | ||
|
|
15310a814a | ||
|
|
454b0dd885 | ||
|
|
6654a41a8d | ||
|
|
25efeacab0 | ||
|
|
0edce6b15c | ||
|
|
4b9851562c | ||
|
|
016a724cb0 | ||
|
|
4607ca69be | ||
|
|
969cd4bdf8 | ||
|
|
4a7dfb070b | ||
|
|
8568e4894b | ||
|
|
b42d176851 | ||
|
|
d0224ba940 | ||
|
|
e7bba3b7ff | ||
|
|
7b37374bd0 | ||
|
|
1f5a093c5a | ||
|
|
44f7bf6916 | ||
|
|
b0a14b5498 | ||
|
|
41be0a00a5 | ||
|
|
fc0f97926e | ||
|
|
7a3267ea3d | ||
|
|
920680bbe5 | ||
|
|
5733a1d9dd | ||
|
|
9a54340de3 | ||
|
|
6761b57f8b | ||
|
|
5ddb7f0406 | ||
|
|
2bf3889106 | ||
|
|
97e15d0f88 | ||
|
|
8a5c4acabd | ||
|
|
1cc8d969f9 | ||
|
|
7a7995eb39 | ||
|
|
6df6a0402d | ||
|
|
73c8cea20e | ||
|
|
7c4dbae2a7 | ||
|
|
7bd488ad74 | ||
|
|
99e0b21031 | ||
|
|
56d62676e4 | ||
|
|
8b1f586a34 | ||
|
|
80b35b93a3 | ||
|
|
de5be0a9f4 | ||
|
|
11b8da4dad | ||
|
|
107cf9efac | ||
|
|
f39cd175bf | ||
|
|
f5df1d5544 | ||
|
|
a10714d1ac | ||
|
|
2d480e0c67 | ||
|
|
e80bea1cfc | ||
|
|
2aa163828a | ||
|
|
3709d88343 | ||
|
|
dcf3e25ed8 | ||
|
|
c8bbc725e3 | ||
|
|
027beac6d2 | ||
|
|
5dda3c4f3f | ||
|
|
3288aabe7a | ||
|
|
bfe2befb2b | ||
|
|
a4f8a9d3dd | ||
|
|
76b346765f | ||
|
|
f5ce434865 | ||
|
|
9a9e21ab03 | ||
|
|
419b897ed6 | ||
|
|
4e3943d2f2 | ||
|
|
ee92950242 | ||
|
|
3f28b11937 | ||
|
|
b8bade44b8 | ||
|
|
a08823febe | ||
|
|
eb42fbfbcd | ||
|
|
ad11f61ef5 | ||
|
|
ca1377305f | ||
|
|
824b3d47d1 | ||
|
|
d4dafa4850 | ||
|
|
fac8304454 | ||
|
|
1d68e56fba | ||
|
|
ab8eef710c | ||
|
|
959afa3213 | ||
|
|
3248fed613 | ||
|
|
e77a1cf293 | ||
|
|
51ead25faa | ||
|
|
65b0e343d7 | ||
|
|
a82ff40423 | ||
|
|
81d3ea5e95 | ||
|
|
b12b1eceff | ||
|
|
9a2a21a7ef | ||
|
|
8b6960f966 | ||
|
|
1d247c7a4a | ||
|
|
d912d1380a | ||
|
|
8bdc51f9fa | ||
|
|
cf8ed4d3f3 | ||
|
|
c0849956ae | ||
|
|
a3201984e9 | ||
|
|
20a869a204 | ||
|
|
16536bf18e | ||
|
|
b9a0e64284 | ||
|
|
649f15bdca | ||
|
|
a72557bddc | ||
|
|
58b3eaf23f | ||
|
|
ad5ca232af | ||
|
|
b58b809412 | ||
|
|
54191ca10c | ||
|
|
80525860e2 | ||
|
|
3dde9d6e8c | ||
|
|
79b33bbb4d | ||
|
|
e28283a964 | ||
|
|
46b0ab7787 | ||
|
|
3898215ed6 | ||
|
|
e79256891f | ||
|
|
d65063d316 | ||
|
|
15c8b9aea7 | ||
|
|
a4fa6b5abe | ||
|
|
a62f9cc1ea | ||
|
|
ae5aad9c67 | ||
|
|
40f3a1ee7f |
17
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
17
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
*** READ BEFORE POSTING ***
|
||||
|
||||
Before posting an issue - please upgrade to the latest version and confirm the issue/bug is still there.
|
||||
|
||||
Upgrade using:
|
||||
`$ pip install yfinance --upgrade --no-cache-dir`
|
||||
|
||||
Bug still there? Delete this content and submit your bug report here...
|
||||
7
.github/dependabot.yml
vendored
Normal file
7
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: pip
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: daily
|
||||
open-pull-requests-limit: 10
|
||||
18
.github/workflows/ci.yml
vendored
Normal file
18
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
name: ci
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.x
|
||||
- run: pip install -r requirements.txt
|
||||
- run: pip install mkdocstrings==0.14.0
|
||||
- run: pip install mkdocs-material
|
||||
- run: mkdocs gh-deploy --force
|
||||
31
.github/workflows/python-publish.yml
vendored
Normal file
31
.github/workflows/python-publish.yml
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
# This workflow will upload a Python Package using Twine when a release is created
|
||||
# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries
|
||||
|
||||
name: Upload Python Package
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [created]
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install setuptools wheel twine
|
||||
- name: Build and publish
|
||||
env:
|
||||
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
|
||||
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
|
||||
run: |
|
||||
python setup.py sdist bdist_wheel
|
||||
twine upload dist/*
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -3,3 +3,9 @@ yfinance/__pycache__/*
|
||||
dist
|
||||
yfinance.egg-info
|
||||
*.pyc
|
||||
.coverage
|
||||
.vscode/
|
||||
build/
|
||||
*.html
|
||||
*.css
|
||||
*.png
|
||||
|
||||
10
.travis.yml
10
.travis.yml
@@ -6,26 +6,30 @@ fast_finish: true
|
||||
matrix:
|
||||
include:
|
||||
- python: 2.7
|
||||
- python: 3.5
|
||||
- python: 3.6
|
||||
- python: 3.7
|
||||
- python: 3.8
|
||||
- python: 3.9
|
||||
dist: xenial
|
||||
sudo: true
|
||||
|
||||
install:
|
||||
- pip install Cython
|
||||
- pip install pytest>=4.6
|
||||
- pip install pytest-cov
|
||||
- pip install coveralls
|
||||
- pip install -r requirements.txt
|
||||
- pip install .
|
||||
|
||||
script:
|
||||
- nosetests
|
||||
- pytest --cov=yfinance/
|
||||
|
||||
after_success:
|
||||
- coveralls
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- main
|
||||
|
||||
notifications:
|
||||
slack: tradologics:HcnS6XusfcuS02waQPCG18oc
|
||||
|
||||
@@ -1,10 +1,83 @@
|
||||
Change Log
|
||||
===========
|
||||
|
||||
0.1.55 - to be released
|
||||
0.1.68
|
||||
------
|
||||
- fixed institutional investors and mutual fund holders issue (#459)
|
||||
- fix for UTC timestamps in options chains (#429)
|
||||
- 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
|
||||
|
||||
0.1.65
|
||||
------
|
||||
- Merged PRs to fix some bugs
|
||||
- Added lookup by ISIN ``utils.get_all_by_isin(...)``, ``utils.get_ticker_by_isin(...)``, ``utils.get_info_by_isin(...)``, ``utils.get_news_by_isin(...)``
|
||||
- ``yf.Ticker``, ``yf.Tickers``, and ``yf.download`` will auto-detect ISINs and convert them to tickers
|
||||
- Propagating timeout parameter through code, setting request.get(timeout)
|
||||
- Adds ``Ticker.analysis`` and ``Ticker.get_analysis(...)``
|
||||
|
||||
0.1.64
|
||||
------
|
||||
- Merged PRs to fix some bugs
|
||||
- Added ``Ticker.stats()`` method
|
||||
- Added ``Ticker.news`` property
|
||||
- Providing topHoldings for ETFs
|
||||
- Replaceed drop duplicate prices with indexes
|
||||
- Added pre-market price to ``Ticker.info``
|
||||
|
||||
|
||||
0.1.63
|
||||
------
|
||||
- Duplicates and missing rows cleanup
|
||||
|
||||
0.1.62
|
||||
------
|
||||
- Added UserAgent to all requests (via ```utils.user_agent_headers```)
|
||||
|
||||
0.1.61
|
||||
------
|
||||
- Switched to using ```query2.finance.yahoo.com```, which used HTTP/1.1
|
||||
|
||||
0.1.60
|
||||
------
|
||||
- Gracefully fail on misc operations (options, auto/back adjustments, etc)
|
||||
- Added financial data to ```info()```
|
||||
- Using session headers
|
||||
- Get price even if open price not available
|
||||
- Argument added for silencing error printing
|
||||
- Merged PRs to fix some bugs
|
||||
|
||||
0.1.59
|
||||
------
|
||||
- Added custom requests session instance support in holders
|
||||
|
||||
0.1.58
|
||||
------
|
||||
- Allow specifying a custom requests session instance
|
||||
|
||||
0.1.57
|
||||
------
|
||||
- Added Conversion rate hint using 'financialCurrency' property in earnings
|
||||
- Add important try+catch statements
|
||||
- Fixed issue with 1 hour interval
|
||||
- Merged PRs to fix some bugs
|
||||
- Fixed issue with special characters in tickers
|
||||
|
||||
0.1.56
|
||||
------
|
||||
- Updated numpy version
|
||||
- Merged PRs to fix some bugs
|
||||
|
||||
0.1.55
|
||||
------
|
||||
- Fixed institutional investors and mutual fund holders issue (#459)
|
||||
- Fix for UTC timestamps in options chains (#429)
|
||||
|
||||
0.1.54
|
||||
------
|
||||
@@ -12,7 +85,7 @@ Change Log
|
||||
|
||||
0.1.53
|
||||
------
|
||||
- Added `Ticker.isin` + `Ticker.get_isin(...)`. This is still experimental. Do not rely on it for production.
|
||||
- Added ``Ticker.isin`` + ``Ticker.get_isin(...)``. This is still experimental. Do not rely on it for production.
|
||||
- Bug fixed: holders were always returning results for MSFT
|
||||
|
||||
0.1.52
|
||||
@@ -21,24 +94,24 @@ Change Log
|
||||
|
||||
0.1.51
|
||||
------
|
||||
- Added holdings data (`Ticker.major_holders` and `Ticker.institutional_holders`)
|
||||
- Added logo url to `Ticker.info`
|
||||
- Added holdings data (``Ticker.major_holders`` and ``Ticker.institutional_holders``)
|
||||
- Added logo url to ``Ticker.info``
|
||||
- Handling different date formats in fundamentals
|
||||
- Faster JSON parsing using regex
|
||||
- Trying to re-download JSON twice before giving up
|
||||
- Using ujson instead of json if installed
|
||||
- Fixed (more) `ticker.info` issues
|
||||
- Fixed (more) ``ticker.info`` issues
|
||||
- Misc bugfixes
|
||||
|
||||
0.1.50
|
||||
------
|
||||
- Fixed `ticker.info` issues
|
||||
- Fixed ``ticker.info`` issues
|
||||
- Handle sustainability index error
|
||||
- Added test script based on @GregoryMorse's pull request
|
||||
|
||||
0.1.49
|
||||
------
|
||||
- Fixed `elementwise comparison`` warning
|
||||
- Fixed ``elementwise comparison`` warning
|
||||
|
||||
0.1.48
|
||||
------
|
||||
|
||||
292
README.md
Normal file
292
README.md
Normal file
@@ -0,0 +1,292 @@
|
||||
# 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 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",
|
||||
|
||||
# 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
|
||||
|
||||
### 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**
|
||||
277
README.rst
277
README.rst
@@ -1,277 +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/master.svg?maxAge=1
|
||||
:target: https://travis-ci.com/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** aimes 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-competability, ``fix-yahoo-finance`` now import and
|
||||
uses ``yfinance``, but you should install and use ``yfinance`` directly.
|
||||
|
||||
`Changelog » <./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
|
||||
|
||||
# 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 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
|
||||
)
|
||||
|
||||
|
||||
``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
|
||||
|
||||
|
||||
Install ``yfinance`` using ``conda``:
|
||||
|
||||
.. code:: 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
|
||||
|
||||
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**
|
||||
22
meta.yaml
22
meta.yaml
@@ -1,5 +1,5 @@
|
||||
{% set name = "yfinance" %}
|
||||
{% set version = "0.1.55" %}
|
||||
{% set version = "0.1.58" %}
|
||||
|
||||
package:
|
||||
name: "{{ name|lower }}"
|
||||
@@ -7,7 +7,7 @@ package:
|
||||
|
||||
source:
|
||||
url: "https://pypi.io/packages/source/{{ name[0] }}/{{ name }}/{{ name }}-{{ version }}.tar.gz"
|
||||
sha256: "89e984306bee13ed9a1a5d543f4f01f85716120666e404596a4e932ac7448288"
|
||||
sha256: "207da19e87792bf742d2744eee2fe18169853c1b82cfe14a9a7cfb8d05f09137"
|
||||
|
||||
build:
|
||||
noarch: python
|
||||
@@ -16,20 +16,21 @@ build:
|
||||
|
||||
requirements:
|
||||
host:
|
||||
- pandas >=0.24.0
|
||||
- numpy >=1.16.5
|
||||
- requests >=2.21
|
||||
- multitasking >=0.0.7
|
||||
- numpy >=1.15
|
||||
- pandas >=0.24
|
||||
- lxml >=4.5.1
|
||||
- pip
|
||||
- python
|
||||
- requests >=2.20
|
||||
- lxml>=4.5.1
|
||||
|
||||
run:
|
||||
- pandas >=0.24.0
|
||||
- numpy >=1.16.5
|
||||
- requests >=2.21
|
||||
- multitasking >=0.0.7
|
||||
- numpy >=1.15
|
||||
- pandas >=0.24
|
||||
- lxml >=4.5.1
|
||||
- python
|
||||
- requests >=2.20
|
||||
- lxml>=4.5.1
|
||||
|
||||
test:
|
||||
imports:
|
||||
@@ -50,4 +51,3 @@ about:
|
||||
extra:
|
||||
recipe-maintainers:
|
||||
- ranaroussi
|
||||
- bradmetz
|
||||
|
||||
19
mkdocs.yml
Normal file
19
mkdocs.yml
Normal file
@@ -0,0 +1,19 @@
|
||||
# site_name: My Docs
|
||||
|
||||
# # mkdocs.yml
|
||||
# theme:
|
||||
# name: "material"
|
||||
|
||||
# plugins:
|
||||
# - search
|
||||
# - mkdocstrings
|
||||
|
||||
# nav:
|
||||
# - Introduction: 'index.md'
|
||||
# - Installation: 'installation.md'
|
||||
# - Quick Start: 'quickstart.md'
|
||||
# # - Ticker: 'Ticker.md'
|
||||
# - TickerBase: 'TickerBase.md'
|
||||
# # - Tickers: 'Tickers.md'
|
||||
# - utils: 'utils.md'
|
||||
# - multi: 'multi.md'
|
||||
@@ -1,5 +1,5 @@
|
||||
pandas>=0.24
|
||||
numpy>=1.15
|
||||
requests>=2.21
|
||||
pandas>=0.24.0
|
||||
numpy>=1.16.5
|
||||
requests>=2.26
|
||||
multitasking>=0.0.7
|
||||
lxml>=4.5.1
|
||||
|
||||
64
runtest.py
64
runtest.py
@@ -1,64 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: UTF-8 -*-
|
||||
#
|
||||
# Yahoo! Finance market data downloader (+fix for Pandas Datareader)
|
||||
# https://github.com/ranaroussi/yfinance
|
||||
|
||||
"""
|
||||
Sanity check for most common library uses all working
|
||||
|
||||
- Stock: Microsoft
|
||||
- ETF: Russell 2000 Growth
|
||||
- Mutual fund: Vanguard 500 Index fund
|
||||
- Index: S&P500
|
||||
- Currency BTC-USD
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
import yfinance as yf
|
||||
|
||||
|
||||
def test_yfinance():
|
||||
for symbol in ['MSFT', 'IWO', 'VFINX', '^GSPC', 'BTC-USD']:
|
||||
print(">>", symbol, end=' ... ')
|
||||
ticker = yf.Ticker(symbol)
|
||||
|
||||
# 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)
|
||||
|
||||
# following should always gracefully handled, no crashes
|
||||
ticker.cashflow
|
||||
ticker.balance_sheet
|
||||
ticker.financials
|
||||
ticker.sustainability
|
||||
ticker.major_holders
|
||||
ticker.institutional_holders
|
||||
ticker.mutualfund_holders
|
||||
|
||||
print("OK")
|
||||
|
||||
# Ford has no institutional investors table or mutual fund holders
|
||||
ticker = yf.Ticker('F')
|
||||
print(">> F", end=" ... ")
|
||||
assert(ticker.info is not None and ticker.info != {})
|
||||
assert(ticker.major_holders is not None)
|
||||
assert(ticker.institutional_holders is None)
|
||||
print("OK")
|
||||
# NKLA has no institutional investors table or mutual fund holders
|
||||
ticker = yf.Ticker('NKLA')
|
||||
print(">> NKLA", end=" ... ")
|
||||
assert(ticker.info is not None and ticker.info != {})
|
||||
assert(ticker.major_holders is not None)
|
||||
assert(ticker.institutional_holders is None)
|
||||
print("OK")
|
||||
# NKLA has no institutional investors table or mutual fund holders
|
||||
ticker = yf.Ticker('NESN.SW')
|
||||
print(">> NESN.SW", end=" ... ")
|
||||
assert(ticker.info is not None and ticker.info != {})
|
||||
assert(ticker.major_holders is not None)
|
||||
assert(ticker.institutional_holders is None)
|
||||
print("OK")
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_yfinance()
|
||||
31
setup.py
31
setup.py
@@ -1,27 +1,36 @@
|
||||
#!/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
|
||||
import io
|
||||
from os import path
|
||||
|
||||
# --- get version ---
|
||||
version = "unknown"
|
||||
with open("yfinance/version.py") as f:
|
||||
line = f.read().strip()
|
||||
version = line.replace("version = ", "").replace('"', '')
|
||||
# --- /get version ---
|
||||
|
||||
|
||||
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="0.1.55",
|
||||
description='Yahoo! Finance market data downloader',
|
||||
version=version,
|
||||
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',
|
||||
@@ -44,14 +53,16 @@ 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',
|
||||
],
|
||||
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',
|
||||
'requests>=2.26', 'multitasking>=0.0.7',
|
||||
'lxml>=4.5.1'],
|
||||
entry_points={
|
||||
'console_scripts': [
|
||||
@@ -59,3 +70,9 @@ setup(
|
||||
],
|
||||
},
|
||||
)
|
||||
|
||||
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.""")
|
||||
|
||||
63
test_yfinance.py
Normal file
63
test_yfinance.py
Normal file
@@ -0,0 +1,63 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: UTF-8 -*-
|
||||
#
|
||||
# yfinance - market data downloader
|
||||
# https://github.com/ranaroussi/yfinance
|
||||
|
||||
"""
|
||||
Sanity check for most common library uses all working
|
||||
- Stock: Microsoft
|
||||
- ETF: Russell 2000 Growth
|
||||
- Mutual fund: Vanguard 500 Index fund
|
||||
- Index: S&P500
|
||||
- Currency BTC-USD
|
||||
"""
|
||||
|
||||
import yfinance as yf
|
||||
import unittest
|
||||
|
||||
symbols = ['MSFT', 'IWO', 'VFINX', '^GSPC', 'BTC-USD']
|
||||
tickers = [yf.Ticker(symbol) for symbol in symbols]
|
||||
|
||||
|
||||
class TestTicker(unittest.TestCase):
|
||||
def test_info_history(self):
|
||||
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)
|
||||
|
||||
def test_attributes(self):
|
||||
for ticker in tickers:
|
||||
ticker.isin
|
||||
ticker.major_holders
|
||||
ticker.institutional_holders
|
||||
ticker.mutualfund_holders
|
||||
ticker.dividends
|
||||
ticker.splits
|
||||
ticker.actions
|
||||
ticker.info
|
||||
ticker.calendar
|
||||
ticker.recommendations
|
||||
ticker.earnings
|
||||
ticker.quarterly_earnings
|
||||
ticker.financials
|
||||
ticker.quarterly_financials
|
||||
ticker.balance_sheet
|
||||
ticker.quarterly_balance_sheet
|
||||
ticker.cashflow
|
||||
ticker.quarterly_cashflow
|
||||
ticker.sustainability
|
||||
ticker.options
|
||||
ticker.news
|
||||
ticker.shares
|
||||
|
||||
def test_holders(self):
|
||||
for ticker in tickers:
|
||||
assert(ticker.info is not None and ticker.info != {})
|
||||
assert(ticker.major_holders is not None)
|
||||
assert(ticker.institutional_holders is not None)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -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
|
||||
@@ -19,13 +19,14 @@
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
__version__ = "0.1.54"
|
||||
__author__ = "Ran Aroussi"
|
||||
|
||||
from . import version
|
||||
from .ticker import Ticker
|
||||
from .tickers import Tickers
|
||||
from .multi import download
|
||||
|
||||
__version__ = version.version
|
||||
__author__ = "Ran Aroussi"
|
||||
|
||||
|
||||
def pdr_override():
|
||||
"""
|
||||
|
||||
400
yfinance/base.py
400
yfinance/base.py
@@ -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
|
||||
@@ -26,6 +26,7 @@ import datetime as _datetime
|
||||
import requests as _requests
|
||||
import pandas as _pd
|
||||
import numpy as _np
|
||||
import re as _re
|
||||
|
||||
try:
|
||||
from urllib.parse import quote as urlencode
|
||||
@@ -34,28 +35,34 @@ except ImportError:
|
||||
|
||||
from . import utils
|
||||
|
||||
# import json as _json
|
||||
import json as _json
|
||||
# import re as _re
|
||||
# import sys as _sys
|
||||
|
||||
from . import shared
|
||||
|
||||
_BASE_URL_ = 'https://query2.finance.yahoo.com'
|
||||
_SCRAPE_URL_ = 'https://finance.yahoo.com/quote'
|
||||
|
||||
class TickerBase():
|
||||
def __init__(self, ticker):
|
||||
def __init__(self, ticker, session=None):
|
||||
self.ticker = ticker.upper()
|
||||
self.session = session
|
||||
self._history = None
|
||||
self._base_url = 'https://query1.finance.yahoo.com'
|
||||
self._scrape_url = 'https://finance.yahoo.com/quote'
|
||||
self._base_url = _BASE_URL_
|
||||
self._scrape_url = _SCRAPE_URL_
|
||||
|
||||
self._fundamentals = False
|
||||
self._info = None
|
||||
self._analysis = None
|
||||
self._sustainability = None
|
||||
self._recommendations = None
|
||||
self._major_holders = None
|
||||
self._institutional_holders = None
|
||||
self._mutualfund_holders = None
|
||||
self._isin = None
|
||||
self._news = []
|
||||
self._shares = None
|
||||
|
||||
self._calendar = None
|
||||
self._expirations = {}
|
||||
@@ -73,10 +80,30 @@ class TickerBase():
|
||||
"yearly": utils.empty_df(),
|
||||
"quarterly": utils.empty_df()}
|
||||
|
||||
# accept isin as ticker
|
||||
if utils.is_isin(self.ticker):
|
||||
self.ticker = utils.get_ticker_by_isin(self.ticker, None, session)
|
||||
|
||||
def stats(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:
|
||||
return
|
||||
|
||||
ticker_url = "{}/{}".format(self._scrape_url, self.ticker)
|
||||
|
||||
# get info and sustainability
|
||||
data = utils.get_json(ticker_url, proxy, self.session)
|
||||
return data
|
||||
|
||||
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, **kwargs):
|
||||
proxy=None, rounding=False, tz=None, timeout=None, **kwargs):
|
||||
"""
|
||||
:Parameters:
|
||||
period : str
|
||||
@@ -106,6 +133,10 @@ class TickerBase():
|
||||
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)
|
||||
Default is None.
|
||||
**kwargs: dict
|
||||
debug: bool
|
||||
Optional. If passed as False, will suppress
|
||||
@@ -114,7 +145,7 @@ class TickerBase():
|
||||
|
||||
if start or period is None or period.lower() == "max":
|
||||
if start is None:
|
||||
start = -2208988800
|
||||
start = -631159200
|
||||
elif isinstance(start, _datetime.datetime):
|
||||
start = int(_time.mktime(start.timetuple()))
|
||||
else:
|
||||
@@ -148,12 +179,25 @@ class TickerBase():
|
||||
|
||||
# Getting data from json
|
||||
url = "{}/v8/finance/chart/{}".format(self._base_url, self.ticker)
|
||||
data = _requests.get(url=url, params=params, proxies=proxy)
|
||||
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()
|
||||
|
||||
session = self.session or _requests
|
||||
|
||||
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
|
||||
|
||||
# Work with errors
|
||||
debug_mode = True
|
||||
@@ -207,10 +251,20 @@ class TickerBase():
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if auto_adjust:
|
||||
quotes = utils.auto_adjust(quotes)
|
||||
elif back_adjust:
|
||||
quotes = utils.back_adjust(quotes)
|
||||
try:
|
||||
if auto_adjust:
|
||||
quotes = utils.auto_adjust(quotes)
|
||||
elif back_adjust:
|
||||
quotes = utils.back_adjust(quotes)
|
||||
except Exception as e:
|
||||
if auto_adjust:
|
||||
err_msg = "auto_adjust failed with %s" % e
|
||||
else:
|
||||
err_msg = "back_adjust failed with %s" % 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))
|
||||
|
||||
if rounding:
|
||||
quotes = _np.round(quotes, data[
|
||||
@@ -233,12 +287,18 @@ class TickerBase():
|
||||
|
||||
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.name = "Date"
|
||||
|
||||
# duplicates and missing rows cleanup
|
||||
df.dropna(how='all', inplace=True)
|
||||
df = df[~df.index.duplicated(keep='first')]
|
||||
|
||||
self._history = df.copy()
|
||||
|
||||
if not actions:
|
||||
@@ -276,68 +336,104 @@ class TickerBase():
|
||||
if self._fundamentals:
|
||||
return
|
||||
|
||||
ticker_url = "{}/{}".format(self._scrape_url, self.ticker)
|
||||
|
||||
# get info and sustainability
|
||||
url = '%s/%s' % (self._scrape_url, self.ticker)
|
||||
data = utils.get_json(url, proxy)
|
||||
data = utils.get_json(ticker_url, proxy, self.session)
|
||||
|
||||
# holders
|
||||
url = "{}/{}/holders".format(self._scrape_url, self.ticker)
|
||||
holders = _pd.read_html(url)
|
||||
|
||||
if len(holders)>=3:
|
||||
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:
|
||||
elif len(holders) >= 2:
|
||||
self._major_holders = holders[0]
|
||||
self._institutional_holders = holders[1]
|
||||
else:
|
||||
elif len(holders) >= 1:
|
||||
self._major_holders = holders[0]
|
||||
|
||||
#self._major_holders = holders[0]
|
||||
#self._institutional_holders = holders[1]
|
||||
|
||||
|
||||
# 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'])
|
||||
self._institutional_holders['Date Reported'])
|
||||
if '% Out' in self._institutional_holders:
|
||||
self._institutional_holders['% Out'] = self._institutional_holders[
|
||||
'% Out'].str.replace('%', '').astype(float)/100
|
||||
'% 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'])
|
||||
self._mutualfund_holders['Date Reported'])
|
||||
if '% Out' in self._mutualfund_holders:
|
||||
self._mutualfund_holders['% Out'] = self._mutualfund_holders[
|
||||
'% Out'].str.replace('%', '').astype(float)/100
|
||||
'% Out'].str.replace('%', '').astype(float) / 100
|
||||
|
||||
# sustainability
|
||||
d = {}
|
||||
if isinstance(data.get('esgScores'), dict):
|
||||
for item in data['esgScores']:
|
||||
if not isinstance(data['esgScores'][item], (dict, list)):
|
||||
d[item] = data['esgScores'][item]
|
||||
try:
|
||||
if isinstance(data.get('esgScores'), dict):
|
||||
for item in data['esgScores']:
|
||||
if not isinstance(data['esgScores'][item], (dict, list)):
|
||||
d[item] = data['esgScores'][item]
|
||||
|
||||
s = _pd.DataFrame(index=[0], data=d)[-1:].T
|
||||
s.columns = ['Value']
|
||||
s.index.name = '%.f-%.f' % (
|
||||
s[s.index == 'ratingYear']['Value'].values[0],
|
||||
s[s.index == 'ratingMonth']['Value'].values[0])
|
||||
s = _pd.DataFrame(index=[0], data=d)[-1:].T
|
||||
s.columns = ['Value']
|
||||
s.index.name = '%.f-%.f' % (
|
||||
s[s.index == 'ratingYear']['Value'].values[0],
|
||||
s[s.index == 'ratingMonth']['Value'].values[0])
|
||||
|
||||
self._sustainability = s[~s.index.isin(
|
||||
['maxAge', 'ratingYear', 'ratingMonth'])]
|
||||
self._sustainability = s[~s.index.isin(
|
||||
['maxAge', 'ratingYear', 'ratingMonth'])]
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# info (be nice to python 2)
|
||||
self._info = {}
|
||||
items = ['summaryProfile', 'summaryDetail', 'quoteType',
|
||||
'defaultKeyStatistics', 'assetProfile', 'summaryDetail']
|
||||
for item in items:
|
||||
if isinstance(data.get(item), dict):
|
||||
self._info.update(data[item])
|
||||
try:
|
||||
items = ['summaryProfile', 'financialData', 'quoteType',
|
||||
'defaultKeyStatistics', 'assetProfile', 'summaryDetail']
|
||||
for item in items:
|
||||
if isinstance(data.get(item), dict):
|
||||
self._info.update(data[item])
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# For ETFs, provide this valuable data: the top holdings of the ETF
|
||||
try:
|
||||
if 'topHoldings' in data:
|
||||
self._info.update(data['topHoldings'])
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
if not isinstance(data.get('summaryDetail'), dict):
|
||||
# For some reason summaryDetail did not give any results. The price dict usually has most of the same info
|
||||
self._info.update(data.get('price', {}))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
# self._info['regularMarketPrice'] = self._info['regularMarketOpen']
|
||||
self._info['regularMarketPrice'] = data.get('price', {}).get(
|
||||
'regularMarketPrice', self._info.get('regularMarketOpen', None))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
self._info['preMarketPrice'] = data.get('price', {}).get(
|
||||
'preMarketPrice', self._info.get('preMarketPrice', None))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
self._info['regularMarketPrice'] = self._info['regularMarketOpen']
|
||||
self._info['logo_url'] = ""
|
||||
try:
|
||||
domain = self._info['website'].split(
|
||||
@@ -373,7 +469,7 @@ class TickerBase():
|
||||
pass
|
||||
|
||||
# get fundamentals
|
||||
data = utils.get_json(url+'/financials', proxy)
|
||||
data = utils.get_json(ticker_url + '/financials', proxy, self.session)
|
||||
|
||||
# generic patterns
|
||||
for key in (
|
||||
@@ -381,53 +477,138 @@ class TickerBase():
|
||||
(self._balancesheet, 'balanceSheet', 'balanceSheetStatements'),
|
||||
(self._financials, 'incomeStatement', 'incomeStatementHistory')
|
||||
):
|
||||
|
||||
item = key[1] + 'History'
|
||||
if isinstance(data.get(item), dict):
|
||||
key[0]['yearly'] = cleanup(data[item][key[2]])
|
||||
try:
|
||||
key[0]['yearly'] = cleanup(data[item][key[2]])
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
item = key[1]+'HistoryQuarterly'
|
||||
item = key[1] + 'HistoryQuarterly'
|
||||
if isinstance(data.get(item), dict):
|
||||
key[0]['quarterly'] = cleanup(data[item][key[2]])
|
||||
try:
|
||||
key[0]['quarterly'] = cleanup(data[item][key[2]])
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# earnings
|
||||
if isinstance(data.get('earnings'), dict):
|
||||
earnings = data['earnings']['financialsChart']
|
||||
df = _pd.DataFrame(earnings['yearly']).set_index('date')
|
||||
df.columns = utils.camel2title(df.columns)
|
||||
df.index.name = 'Year'
|
||||
self._earnings['yearly'] = df
|
||||
try:
|
||||
earnings = data['earnings']['financialsChart']
|
||||
earnings['financialCurrency'] = 'USD' if 'financialCurrency' not in data['earnings'] else data['earnings']['financialCurrency']
|
||||
self._earnings['financialCurrency'] = earnings['financialCurrency']
|
||||
df = _pd.DataFrame(earnings['yearly']).set_index('date')
|
||||
df.columns = utils.camel2title(df.columns)
|
||||
df.index.name = 'Year'
|
||||
self._earnings['yearly'] = df
|
||||
|
||||
df = _pd.DataFrame(earnings['quarterly']).set_index('date')
|
||||
df.columns = utils.camel2title(df.columns)
|
||||
df.index.name = 'Quarter'
|
||||
self._earnings['quarterly'] = df
|
||||
df = _pd.DataFrame(earnings['quarterly']).set_index('date')
|
||||
df.columns = utils.camel2title(df.columns)
|
||||
df.index.name = 'Quarter'
|
||||
self._earnings['quarterly'] = df
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# shares outstanding
|
||||
try:
|
||||
shares = _pd.DataFrame(data['annualBasicAverageShares'])
|
||||
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)
|
||||
|
||||
if isinstance(data.get('earningsTrend'), dict):
|
||||
try:
|
||||
analysis = _pd.DataFrame(data['earningsTrend']['trend'])
|
||||
analysis['endDate'] = _pd.to_datetime(analysis['endDate'])
|
||||
analysis.set_index('period', inplace=True)
|
||||
analysis.index = analysis.index.str.upper()
|
||||
analysis.index.name = 'Period'
|
||||
analysis.columns = utils.camel2title(analysis.columns)
|
||||
|
||||
dict_cols = []
|
||||
|
||||
for idx, row in analysis.iterrows():
|
||||
for colname, colval in row.items():
|
||||
if isinstance(colval, dict):
|
||||
dict_cols.append(colname)
|
||||
for k, v in colval.items():
|
||||
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]]
|
||||
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
|
||||
my_ts_keys = ['trailingPegRatio'] # , 'quarterlyPegRatio'] # QuoteTimeSeriesStore
|
||||
|
||||
# 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)
|
||||
self._get_fundamentals(proxy=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)
|
||||
self._get_fundamentals(proxy=proxy)
|
||||
data = self._calendar
|
||||
if as_dict:
|
||||
return data.to_dict()
|
||||
return data
|
||||
|
||||
def get_major_holders(self, proxy=None, as_dict=False, *args, **kwargs):
|
||||
self._get_fundamentals(proxy)
|
||||
self._get_fundamentals(proxy=proxy)
|
||||
data = self._major_holders
|
||||
if as_dict:
|
||||
return data.to_dict()
|
||||
return data
|
||||
|
||||
def get_institutional_holders(self, proxy=None, as_dict=False, *args, **kwargs):
|
||||
self._get_fundamentals(proxy)
|
||||
self._get_fundamentals(proxy=proxy)
|
||||
data = self._institutional_holders
|
||||
if data is not None:
|
||||
if as_dict:
|
||||
@@ -435,7 +616,7 @@ class TickerBase():
|
||||
return data
|
||||
|
||||
def get_mutualfund_holders(self, proxy=None, as_dict=False, *args, **kwargs):
|
||||
self._get_fundamentals(proxy)
|
||||
self._get_fundamentals(proxy=proxy)
|
||||
data = self._mutualfund_holders
|
||||
if data is not None:
|
||||
if as_dict:
|
||||
@@ -443,35 +624,44 @@ class TickerBase():
|
||||
return data
|
||||
|
||||
def get_info(self, proxy=None, as_dict=False, *args, **kwargs):
|
||||
self._get_fundamentals(proxy)
|
||||
self._get_fundamentals(proxy=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)
|
||||
self._get_fundamentals(proxy=proxy)
|
||||
data = self._sustainability
|
||||
if as_dict:
|
||||
return data.to_dict()
|
||||
return data
|
||||
|
||||
def get_earnings(self, proxy=None, as_dict=False, freq="yearly"):
|
||||
self._get_fundamentals(proxy)
|
||||
self._get_fundamentals(proxy=proxy)
|
||||
data = self._earnings[freq]
|
||||
if as_dict:
|
||||
dict_data = data.to_dict()
|
||||
dict_data['financialCurrency'] = 'USD' if 'financialCurrency' not in self._earnings else self._earnings['financialCurrency']
|
||||
return dict_data
|
||||
return data
|
||||
|
||||
def get_analysis(self, proxy=None, as_dict=False, *args, **kwargs):
|
||||
self._get_fundamentals(proxy=proxy)
|
||||
data = self._analysis
|
||||
if as_dict:
|
||||
return data.to_dict()
|
||||
return data
|
||||
|
||||
def get_financials(self, proxy=None, as_dict=False, freq="yearly"):
|
||||
self._get_fundamentals(proxy)
|
||||
self._get_fundamentals(proxy=proxy)
|
||||
data = self._financials[freq]
|
||||
if as_dict:
|
||||
return data.to_dict()
|
||||
return data
|
||||
|
||||
def get_balancesheet(self, proxy=None, as_dict=False, freq="yearly"):
|
||||
self._get_fundamentals(proxy)
|
||||
self._get_fundamentals(proxy=proxy)
|
||||
data = self._balancesheet[freq]
|
||||
if as_dict:
|
||||
return data.to_dict()
|
||||
@@ -481,7 +671,7 @@ class TickerBase():
|
||||
return self.get_balancesheet(proxy, as_dict, freq)
|
||||
|
||||
def get_cashflow(self, proxy=None, as_dict=False, freq="yearly"):
|
||||
self._get_fundamentals(proxy)
|
||||
self._get_fundamentals(proxy=proxy)
|
||||
data = self._cashflow[freq]
|
||||
if as_dict:
|
||||
return data.to_dict()
|
||||
@@ -490,20 +680,33 @@ class TickerBase():
|
||||
def get_dividends(self, proxy=None):
|
||||
if self._history is None:
|
||||
self.history(period="max", proxy=proxy)
|
||||
dividends = self._history["Dividends"]
|
||||
return dividends[dividends != 0]
|
||||
if self._history is not None and "Dividends" in self._history:
|
||||
dividends = self._history["Dividends"]
|
||||
return dividends[dividends != 0]
|
||||
return []
|
||||
|
||||
def get_splits(self, proxy=None):
|
||||
if self._history is None:
|
||||
self.history(period="max", proxy=proxy)
|
||||
splits = self._history["Stock Splits"]
|
||||
return splits[splits != 0]
|
||||
if self._history is not None and "Stock Splits" in self._history:
|
||||
splits = self._history["Stock Splits"]
|
||||
return splits[splits != 0]
|
||||
return []
|
||||
|
||||
def get_actions(self, proxy=None):
|
||||
if self._history is None:
|
||||
self.history(period="max", proxy=proxy)
|
||||
actions = self._history[["Dividends", "Stock Splits"]]
|
||||
return actions[actions != 0].dropna(how='all').fillna(0)
|
||||
if self._history is not None and "Dividends" in self._history and "Stock Splits" in self._history:
|
||||
actions = self._history[["Dividends", "Stock Splits"]]
|
||||
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 ***
|
||||
@@ -530,7 +733,12 @@ class TickerBase():
|
||||
url = 'https://markets.businessinsider.com/ajax/' \
|
||||
'SearchController_Suggest?max_results=25&query=%s' \
|
||||
% urlencode(q)
|
||||
data = _requests.get(url=url, proxies=proxy).text
|
||||
session = self.session or _requests
|
||||
data = session.get(
|
||||
url=url,
|
||||
proxies=proxy,
|
||||
headers=utils.user_agent_headers
|
||||
).text
|
||||
|
||||
search_str = '"{}|'.format(ticker)
|
||||
if search_str not in data:
|
||||
@@ -545,3 +753,31 @@ class TickerBase():
|
||||
|
||||
self._isin = data.split(search_str)[1].split('"')[0].split('|')[0]
|
||||
return self._isin
|
||||
|
||||
def get_news(self, proxy=None):
|
||||
if self._news:
|
||||
return self._news
|
||||
|
||||
# setup proxy in requests format
|
||||
if proxy is not None:
|
||||
if isinstance(proxy, dict) and "https" in proxy:
|
||||
proxy = proxy["https"]
|
||||
proxy = {"https": proxy}
|
||||
|
||||
# Getting data from json
|
||||
url = "{}/v1/finance/search?q={}".format(self._base_url, self.ticker)
|
||||
session = self.session or _requests
|
||||
data = session.get(
|
||||
url=url,
|
||||
proxies=proxy,
|
||||
headers=utils.user_agent_headers
|
||||
)
|
||||
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()
|
||||
|
||||
# parse news
|
||||
self._news = data.get("news", [])
|
||||
return self._news
|
||||
|
||||
@@ -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
|
||||
@@ -31,8 +31,8 @@ from . import shared
|
||||
|
||||
def download(tickers, start=None, end=None, actions=False, threads=True,
|
||||
group_by='column', auto_adjust=False, back_adjust=False,
|
||||
progress=True, period="max", interval="1d", prepost=False,
|
||||
proxy=None, rounding=False, **kwargs):
|
||||
progress=True, period="max", show_errors=True, interval="1d", prepost=False,
|
||||
proxy=None, rounding=False, timeout=None, **kwargs):
|
||||
"""Download yahoo tickers
|
||||
:Parameters:
|
||||
tickers : str, list
|
||||
@@ -64,12 +64,29 @@ def download(tickers, start=None, end=None, actions=False, threads=True,
|
||||
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
|
||||
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)
|
||||
"""
|
||||
|
||||
# create ticker list
|
||||
tickers = tickers if isinstance(
|
||||
tickers, (list, set, tuple)) else tickers.replace(',', ' ').split()
|
||||
|
||||
# accept isin as ticker
|
||||
shared._ISINS = {}
|
||||
_tickers_ = []
|
||||
for ticker in tickers:
|
||||
if utils.is_isin(ticker):
|
||||
isin = ticker
|
||||
ticker = utils.get_ticker_by_isin(ticker, proxy)
|
||||
shared._ISINS[ticker] = isin
|
||||
_tickers_.append(ticker)
|
||||
|
||||
tickers = _tickers_
|
||||
|
||||
tickers = list(set([ticker.upper() for ticker in tickers]))
|
||||
|
||||
if progress:
|
||||
@@ -90,7 +107,7 @@ def download(tickers, start=None, end=None, actions=False, threads=True,
|
||||
actions=actions, auto_adjust=auto_adjust,
|
||||
back_adjust=back_adjust,
|
||||
progress=(progress and i > 0), proxy=proxy,
|
||||
rounding=rounding)
|
||||
rounding=rounding, timeout=timeout)
|
||||
while len(shared._DFS) < len(tickers):
|
||||
_time.sleep(0.01)
|
||||
|
||||
@@ -100,7 +117,8 @@ 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, rounding=rounding)
|
||||
back_adjust=back_adjust, proxy=proxy,
|
||||
rounding=rounding, timeout=timeout)
|
||||
shared._DFS[ticker.upper()] = data
|
||||
if progress:
|
||||
shared._PROGRESS_BAR.animate()
|
||||
@@ -108,7 +126,7 @@ def download(tickers, start=None, end=None, actions=False, threads=True,
|
||||
if progress:
|
||||
shared._PROGRESS_BAR.completed()
|
||||
|
||||
if shared._ERRORS:
|
||||
if shared._ERRORS and show_errors:
|
||||
print('\n%.f Failed download%s:' % (
|
||||
len(shared._ERRORS), 's' if len(shared._ERRORS) > 1 else ''))
|
||||
# print(shared._ERRORS)
|
||||
@@ -116,7 +134,8 @@ def download(tickers, start=None, end=None, actions=False, threads=True,
|
||||
v for v in list(shared._ERRORS.items())]))
|
||||
|
||||
if len(tickers) == 1:
|
||||
return shared._DFS[tickers[0]]
|
||||
ticker = tickers[0]
|
||||
return shared._DFS[shared._ISINS.get(ticker, ticker)]
|
||||
|
||||
try:
|
||||
data = _pd.concat(shared._DFS.values(), axis=1,
|
||||
@@ -126,6 +145,9 @@ def download(tickers, start=None, end=None, actions=False, threads=True,
|
||||
data = _pd.concat(shared._DFS.values(), axis=1,
|
||||
keys=shared._DFS.keys())
|
||||
|
||||
# switch names back to isins if applicable
|
||||
data.rename(columns=shared._ISINS, inplace=True)
|
||||
|
||||
if group_by == 'column':
|
||||
data.columns = data.columns.swaplevel(0, 1)
|
||||
data.sort_index(level=0, axis=1, inplace=True)
|
||||
@@ -161,10 +183,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):
|
||||
rounding=False, timeout=None):
|
||||
|
||||
data = _download_one(ticker, start, end, auto_adjust, back_adjust,
|
||||
actions, period, interval, prepost, proxy, rounding)
|
||||
actions, period, interval, prepost, proxy, rounding,
|
||||
timeout)
|
||||
shared._DFS[ticker.upper()] = data
|
||||
if progress:
|
||||
shared._PROGRESS_BAR.animate()
|
||||
@@ -173,10 +196,12 @@ def _download_one_threaded(ticker, start=None, end=None,
|
||||
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):
|
||||
prepost=False, proxy=None, rounding=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, many=True,
|
||||
timeout=timeout)
|
||||
|
||||
@@ -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
|
||||
@@ -22,3 +22,4 @@
|
||||
_DFS = {}
|
||||
_PROGRESS_BAR = None
|
||||
_ERRORS = {}
|
||||
_ISINS = {}
|
||||
|
||||
@@ -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
|
||||
@@ -31,6 +31,7 @@ import pandas as _pd
|
||||
# import re as _re
|
||||
from collections import namedtuple as _namedtuple
|
||||
|
||||
from . import utils
|
||||
from .base import TickerBase
|
||||
|
||||
|
||||
@@ -53,13 +54,17 @@ class Ticker(TickerBase):
|
||||
proxy = proxy["https"]
|
||||
proxy = {"https": proxy}
|
||||
|
||||
r = _requests.get(url=url, proxies=proxy).json()
|
||||
if r['optionChain']['result']:
|
||||
r = _requests.get(
|
||||
url=url,
|
||||
proxies=proxy,
|
||||
headers=utils.user_agent_headers
|
||||
).json()
|
||||
if len(r.get('optionChain', {}).get('result', [])) > 0:
|
||||
for exp in r['optionChain']['result'][0]['expirationDates']:
|
||||
self._expirations[_datetime.datetime.utcfromtimestamp(
|
||||
exp).strftime('%Y-%m-%d')] = exp
|
||||
return r['optionChain']['result'][0]['options'][0]
|
||||
return {}
|
||||
opt = r['optionChain']['result'][0].get('options', [])
|
||||
return opt[0] if len(opt) > 0 else []
|
||||
|
||||
def _options2df(self, opt, tz=None):
|
||||
data = _pd.DataFrame(opt).reindex(columns=[
|
||||
@@ -79,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):
|
||||
@@ -133,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()
|
||||
@@ -194,3 +203,11 @@ class Ticker(TickerBase):
|
||||
if not self._expirations:
|
||||
self._download_options()
|
||||
return tuple(self._expirations.keys())
|
||||
|
||||
@property
|
||||
def news(self):
|
||||
return self.get_news()
|
||||
|
||||
@property
|
||||
def analysis(self):
|
||||
return self.get_analysis()
|
||||
|
||||
@@ -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
|
||||
@@ -22,19 +22,7 @@
|
||||
from __future__ import print_function
|
||||
|
||||
from . import Ticker, multi
|
||||
from collections import namedtuple as _namedtuple
|
||||
|
||||
|
||||
def genTickers(tickers):
|
||||
tickers = tickers if isinstance(
|
||||
tickers, list) else tickers.replace(',', ' ').split()
|
||||
tickers = [ticker.upper() for ticker in tickers]
|
||||
ticker_objects = {}
|
||||
|
||||
for ticker in tickers:
|
||||
ticker_objects[ticker] = Ticker(ticker)
|
||||
return _namedtuple("Tickers", ticker_objects.keys()
|
||||
)(*ticker_objects.values())
|
||||
# from collections import namedtuple as _namedtuple
|
||||
|
||||
|
||||
class Tickers():
|
||||
@@ -51,28 +39,29 @@ class Tickers():
|
||||
for ticker in self.symbols:
|
||||
ticker_objects[ticker] = Ticker(ticker)
|
||||
|
||||
self.tickers = _namedtuple(
|
||||
"Tickers", ticker_objects.keys(), rename=True
|
||||
)(*ticker_objects.values())
|
||||
self.tickers = ticker_objects
|
||||
# self.tickers = _namedtuple(
|
||||
# "Tickers", ticker_objects.keys(), rename=True
|
||||
# )(*ticker_objects.values())
|
||||
|
||||
def history(self, period="1mo", interval="1d",
|
||||
start=None, end=None, prepost=False,
|
||||
actions=True, auto_adjust=True, proxy=None,
|
||||
threads=True, group_by='column', progress=True,
|
||||
**kwargs):
|
||||
timeout=None, **kwargs):
|
||||
|
||||
return self.download(
|
||||
period, interval,
|
||||
start, end, prepost,
|
||||
actions, auto_adjust, proxy,
|
||||
threads, group_by, progress,
|
||||
**kwargs)
|
||||
timeout, **kwargs)
|
||||
|
||||
def download(self, period="1mo", interval="1d",
|
||||
start=None, end=None, prepost=False,
|
||||
actions=True, auto_adjust=True, proxy=None,
|
||||
threads=True, group_by='column', progress=True,
|
||||
**kwargs):
|
||||
timeout=None, **kwargs):
|
||||
|
||||
data = multi.download(self.symbols,
|
||||
start=start, end=end,
|
||||
@@ -85,10 +74,11 @@ class Tickers():
|
||||
group_by='ticker',
|
||||
threads=threads,
|
||||
progress=progress,
|
||||
timeout=timeout,
|
||||
**kwargs)
|
||||
|
||||
for symbol in self.symbols:
|
||||
getattr(self.tickers, symbol)._history = data[symbol]
|
||||
self.tickers.get(symbol, {})._history = data[symbol]
|
||||
|
||||
if group_by == 'column':
|
||||
data.columns = data.columns.swaplevel(0, 1)
|
||||
|
||||
@@ -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
|
||||
@@ -26,7 +26,6 @@ import re as _re
|
||||
import pandas as _pd
|
||||
import numpy as _np
|
||||
import sys as _sys
|
||||
import re as _re
|
||||
|
||||
try:
|
||||
import ujson as _json
|
||||
@@ -34,6 +33,53 @@ 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'}
|
||||
|
||||
|
||||
def is_isin(string):
|
||||
return bool(_re.match("^([A-Z]{2})([A-Z0-9]{9})([0-9]{1})$", string))
|
||||
|
||||
|
||||
def get_all_by_isin(isin, proxy=None, session=None):
|
||||
if not(is_isin(isin)):
|
||||
raise ValueError("Invalid ISIN number")
|
||||
|
||||
from .base import _BASE_URL_
|
||||
session = session or _requests
|
||||
url = "{}/v1/finance/search?q={}".format(_BASE_URL_, isin)
|
||||
data = session.get(url=url, proxies=proxy, headers=user_agent_headers)
|
||||
try:
|
||||
data = data.json()
|
||||
ticker = data.get('quotes', [{}])[0]
|
||||
return {
|
||||
'ticker': {
|
||||
'symbol': ticker['symbol'],
|
||||
'shortname': ticker['shortname'],
|
||||
'longname': ticker['longname'],
|
||||
'type': ticker['quoteType'],
|
||||
'exchange': ticker['exchDisp'],
|
||||
},
|
||||
'news': data.get('news', [])
|
||||
}
|
||||
except Exception:
|
||||
return {}
|
||||
|
||||
|
||||
def get_ticker_by_isin(isin, proxy=None, session=None):
|
||||
data = get_all_by_isin(isin, proxy, session)
|
||||
return data.get('ticker', {}).get('symbol', '')
|
||||
|
||||
|
||||
def get_info_by_isin(isin, proxy=None, session=None):
|
||||
data = get_all_by_isin(isin, proxy, session)
|
||||
return data.get('ticker', {})
|
||||
|
||||
|
||||
def get_news_by_isin(isin, proxy=None, session=None):
|
||||
data = get_all_by_isin(isin, proxy, session)
|
||||
return data.get('news', {})
|
||||
|
||||
|
||||
def empty_df(index=[]):
|
||||
empty = _pd.DataFrame(index=index, data={
|
||||
'Open': _np.nan, 'High': _np.nan, 'Low': _np.nan,
|
||||
@@ -42,11 +88,18 @@ def empty_df(index=[]):
|
||||
return empty
|
||||
|
||||
|
||||
def get_json(url, proxy=None):
|
||||
html = _requests.get(url=url, proxies=proxy).text
|
||||
def get_html(url, proxy=None, session=None):
|
||||
session = session or _requests
|
||||
html = session.get(url=url, proxies=proxy, headers=user_agent_headers).text
|
||||
return html
|
||||
|
||||
|
||||
def get_json(url, proxy=None, session=None):
|
||||
session = session or _requests
|
||||
html = session.get(url=url, proxies=proxy, headers=user_agent_headers).text
|
||||
|
||||
if "QuoteSummaryStore" not in html:
|
||||
html = _requests.get(url=url, proxies=proxy).text
|
||||
html = session.get(url=url, proxies=proxy).text
|
||||
if "QuoteSummaryStore" not in html:
|
||||
return {}
|
||||
|
||||
@@ -54,6 +107,12 @@ def get_json(url, proxy=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')
|
||||
|
||||
1
yfinance/version.py
Normal file
1
yfinance/version.py
Normal file
@@ -0,0 +1 @@
|
||||
version = "0.1.68"
|
||||
Reference in New Issue
Block a user