Compare commits

...

142 Commits

Author SHA1 Message Date
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
Ran Aroussi
608849efc9 Update version.py 2021-11-13 22:53:47 +00:00
Ran Aroussi
1c390a51c2 Update CHANGELOG.rst 2021-11-13 22:53:36 +00:00
Ran Aroussi
cade9b0715 Merge pull request #885 from ShayAgros/main
Allow yfinance to be serialized by Pickle
2021-11-13 22:50:50 +00:00
Shay Agroskin
a1852184c4 Allow yfinance to be serialized by Pickle
Holding a reference to an object (Requests in this case) as part of the
object's attributes prevents it from being serialized by Pickle.

This commit removes such module reference as part of yfinance
attributes.

Signed-off-by: Shay Agroskin <shayagr@amazon.com>
2021-11-13 21:13:41 +02:00
Ran Aroussi
45be120cef Update README.rst 2021-11-13 17:35:55 +00:00
Ran Aroussi
c92b7ee3bf Update README.rst 2021-11-13 17:29:14 +00:00
ran
275c0fe002 more isin stuff (see changelog) 2021-11-13 17:25:23 +00:00
ran
4f27b17073 added lookup by isin
get_all_by_isin()
get_ticker_by_isin()
get_info_by_isin()
get_news_by_isin()
2021-11-13 16:51:11 +00:00
Ran Aroussi
a41b1820ac Merge pull request #883 from MatthiasSchinzel/timeout
Adding possibilty to modify timeout parameter of requests.get function.
2021-11-13 16:49:08 +00:00
Ran Aroussi
b1fc53134b Merge pull request #880 from AjitisAjit/main
Adds to analysis property that pulls content from analysis tab
2021-11-13 16:48:56 +00:00
Ran Aroussi
f83a63bd4e Merge pull request #857 from asafravid/main
try + except still required - topHoldings, summaryDetail
2021-11-13 16:48:33 +00:00
Matthias Schinzel
a76e12aca2 Fixing typo 2021-11-12 22:48:19 +01:00
Matthias Schinzel
43d037dd25 Propagating timeout parameter through code, setting request.get(timeout) 2021-11-12 22:39:24 +01:00
Ajit
bb202aa7a0 Adds Analysis 2021-11-10 20:04:58 +08:00
Ajit
15310a814a Fixes styling 2021-11-10 14:44:47 +08:00
Asaf
454b0dd885 try + except still required - topHoldings, summaryDetail
Reason is that a failure would be harmful without a graceful try+except
2021-10-21 18:47:53 +03:00
ran
6654a41a8d v# + clog 2021-10-19 21:29:06 +01:00
ran
25efeacab0 Merge branch 'main' of github.com:ranaroussi/yfinance 2021-10-19 21:25:40 +01:00
ran
0edce6b15c max = 1950 2021-10-19 21:25:37 +01:00
Ran Aroussi
4b9851562c Merge pull request #794 from jasonstrimpel/replace-drop-duplicate-prices-with-indexes
Replace drop duplicate prices with indexes
2021-10-19 21:24:43 +01:00
Ran Aroussi
016a724cb0 Merge pull request #786 from neon-paradise/feature/pre-market-price
add pre-market price to info
2021-10-19 21:21:31 +01:00
Ran Aroussi
4607ca69be Merge pull request #828 from asafravid/patch-8
When topHoldings is available (for ETFs) - provide it in the info. Closes #827
2021-10-19 21:21:01 +01:00
Ran Aroussi
969cd4bdf8 Merge branch 'main' into patch-8 2021-10-19 21:20:39 +01:00
Ran Aroussi
4a7dfb070b Merge pull request #855 from a0rtega/symbol_news
Add .news ticker property
2021-10-19 21:17:17 +01:00
ran
8568e4894b added stats() 2021-10-19 21:16:20 +01:00
Alberto Ortega
b42d176851 Add .news ticker property 2021-10-16 16:47:43 +02:00
Victor Silva
d0224ba940 Merge pull request #830 from asafravid/patch-9
For ETFs, provide this valuable data: the top holdings of the ETF. Closes #827
2021-10-13 12:16:23 -03:00
Victor Silva
e7bba3b7ff Merge pull request #815 from KilianB/patch-1
Do not use relative links in README
2021-10-13 12:06:45 -03:00
Asaf
7b37374bd0 or ETFs, provide this valuable data: the top holdings of the ETF
or ETFs, provide this valuable data: the top holdings of the ETF
2021-09-02 21:02:06 +03:00
Asaf
1f5a093c5a When topHoldings is available (for ETFs) - provide it in the info
For ETFs, provide this valuable data: the top holdings of the ETF 
       `if 'topHoldings' in data: self._info.update(data['topHoldings'])`
2021-08-31 23:56:29 +03:00
Kilian Brachtendorf
44f7bf6916 Do not use relative links in README
Relative links will not work when the README is displayed on an external page e.g. https://pypi.org/project/yfinance/ which is the first google entry for this project.
2021-08-16 13:19:47 +02:00
Jason Strimpel
b0a14b5498 Replace drop duplicate prices with indexes
Reindex the resutling price DataFrame by removing duplicate DatetimeIndex values instead of price data. Remove the call to groupby which is made redundant by the reindexing. This method appears to be the fastest based on a single data point found here.

This fixes an issue where valid trading days are removed from the returned data. Currently duplicates are dropped broadly under the (presumed) assumption that the chance of a stock's open, high, low, close and adjusted close all to be the same within a data range is an edge case that would never happen. With mutual funds and other securities that price daily (and correspondingly don't have a OHLC) this is much more likely.

Resolves: #793
2021-07-23 09:29:03 +08:00
pchernov
41be0a00a5 add pre-market price to info 2021-07-19 16:28:19 +03:00
Ran Aroussi
fc0f97926e Create bug_report.md 2021-07-10 21:55:48 +01:00
Ran Aroussi
7a3267ea3d Create bug_report.md 2021-07-10 21:54:59 +01:00
Ran Aroussi
920680bbe5 Update bug_report.md 2021-07-10 21:54:35 +01:00
Ran Aroussi
5733a1d9dd Update issue templates 2021-07-10 21:52:47 +01:00
Ran Aroussi
9a54340de3 Update README.rst 2021-07-10 21:33:21 +01:00
ran
6761b57f8b v# + changelog 2021-07-10 21:25:37 +01:00
ran
5ddb7f0406 duplicates and missing rows cleanup 2021-07-10 21:25:30 +01:00
ran
2bf3889106 added utils.user_agent_headers 2021-07-09 19:15:57 +01:00
ran
97e15d0f88 v# + changelog 2021-07-09 19:10:59 +01:00
ran
8a5c4acabd Added UserAgent to all requests 2021-07-09 19:10:53 +01:00
ran
1cc8d969f9 v# + changelog 2021-07-08 09:25:29 +01:00
ran
7a7995eb39 Switched to using query2.finance.yahoo.com, which used HTTP/1.1 2021-07-08 09:25:18 +01:00
ran
6df6a0402d ci stuff (2) 2021-07-03 14:42:50 +01:00
ran
73c8cea20e CI stuff 2021-07-03 14:41:02 +01:00
ran
7c4dbae2a7 ver + changelog 2021-07-03 14:25:50 +01:00
ran
7bd488ad74 graceful optionChain fail 2021-07-03 14:25:17 +01:00
ran
99e0b21031 introduced get_html for info() 2021-07-03 14:25:03 +01:00
ran
56d62676e4 pep stuff 2021-07-03 14:24:45 +01:00
ran
8b1f586a34 all tests are in test.py 2021-07-03 14:24:19 +01:00
ran
80b35b93a3 auto/back adjust graceful fail 2021-07-03 13:33:17 +01:00
ran
de5be0a9f4 added financialData 2021-07-03 13:23:04 +01:00
Ran Aroussi
11b8da4dad Merge pull request #721 from arnaudgelas/tests
Create a folder named 'tests' which contain all tests
2021-07-03 13:22:29 +01:00
Ran Aroussi
107cf9efac Merge pull request #730 from major/patch-1
✏ Trivial spelling fix
2021-07-03 13:19:10 +01:00
Ran Aroussi
f39cd175bf Merge pull request #734 from hughdavenport/hughdavenport-patch-1
Get price even if open price not available
2021-07-03 13:18:50 +01:00
Ran Aroussi
f5df1d5544 Merge pull request #743 from asafravid/master
self._history() graceful handling in case it fails
2021-07-03 13:17:41 +01:00
Ran Aroussi
a10714d1ac Merge branch 'main' into master 2021-07-03 13:17:35 +01:00
Ran Aroussi
2d480e0c67 Merge pull request #749 from jonathanng/main
added header
2021-07-03 13:15:34 +01:00
Jonathan Ng
e80bea1cfc added header 2021-07-02 09:26:20 -04:00
Asaf
2aa163828a Add headers to session
Add headers to session
2021-07-02 08:10:06 +03:00
Arnaud Gelas
3709d88343 Create a folder named 'tests' which contain all tests 2021-07-01 20:41:28 +02:00
Asaf
dcf3e25ed8 Remove unecessary else after return
Remove unecessary else after return
2021-06-28 12:30:55 +03:00
Asaf
c8bbc725e3 Add checks after self._history() in get_() functions
- get_dividends(), get_splits() and get_actions():
  - Since self.history(() may fail, if self._history is not None needs to be re-checked (As well as the action within self._history
2021-06-28 12:24:41 +03:00
Asaf
027beac6d2 Delete base.py 2021-06-28 12:23:53 +03:00
Asaf
5dda3c4f3f Add None checks after self.history() which may fail
- get_dividends(), get_splits() and get_actions():
  - Since self.history(() may fail, if self._history is not None needs to be re-checked (As well as the action within self._history
2021-06-28 12:22:52 +03:00
Hugh Davenport
3288aabe7a If summaryDetail fails, pull in the price dict
For some tickers, such as `ETHI.AX`, the `summaryDetail` result is `None`. In this case, use the `price` dict if possible.
2021-06-16 13:07:09 +12:00
Hugh Davenport
bfe2befb2b Get price even if open price not available
If for some reason the regularMarketOpen value is not retrieved in the summaryDetail, then still attempt to retrieve the regularMarketPrice
2021-06-16 12:42:24 +12:00
Major Hayden
a4f8a9d3dd ✏ Trivial spelling fix 2021-06-08 07:47:25 -05:00
silvavn
76b346765f added requirements to ci 2021-06-05 16:24:49 -06:00
silvavn
f5ce434865 quickfix 2021-06-05 16:20:06 -06:00
Victor Silva
9a9e21ab03 Merge pull request #728 from ranaroussi/documentation
Documentation
2021-06-05 16:15:57 -06:00
silvavn
419b897ed6 Deploy website 2021-06-05 16:14:09 -06:00
silvavn
4e3943d2f2 Mkdocs documentation 2021-05-29 17:22:07 -06:00
Victor Silva
ee92950242 Merge pull request #698 from ranaroussi/dependabot/add-v2-config-file
Upgrade to GitHub-native Dependabot
2021-05-29 16:16:22 -06:00
Victor Silva
3f28b11937 Merge pull request #717 from pranjal-joshi/main
[New Feature] Argument added for silencing error printing
2021-05-25 14:40:26 -06:00
Pranjal Joshi
b8bade44b8 Argument added for silencing error printing
`show_errors` argument added to `download` function with default value `True`.
`if show_errors == False` then errors will not be printed.
This is a fix for **Symbol Delisted** error printed if the data for that symbol is unavailable on Yahoo! finance.
2021-05-21 13:38:27 +05:30
dependabot-preview[bot]
a08823febe Upgrade to GitHub-native Dependabot 2021-04-29 20:25:09 +00:00
ran
eb42fbfbcd version bump + changelog update 2021-03-25 00:42:57 +00:00
ran
ad11f61ef5 added session to holders 2021-03-25 00:42:41 +00:00
ran
ca1377305f changelog update + version bump 2021-03-25 00:05:04 +00:00
ran
824b3d47d1 Allow specifying a custom requests session instance 2021-03-25 00:04:41 +00:00
ran
d4dafa4850 updated changelog 2021-03-24 23:36:48 +00:00
ran
fac8304454 fixed issue with special characters in tickers 2021-03-24 23:36:32 +00:00
ran
1d68e56fba Fixed issue with 1 hour interval 2021-03-24 23:26:16 +00:00
ran
ab8eef710c conda meta file 2021-03-24 23:26:16 +00:00
ran
959afa3213 moved version to external file 2021-03-24 23:26:16 +00:00
Ran Aroussi
3248fed613 Merge pull request #410 from trenton3983/master
Add link to SO question on how to deal with multi-level dataframe column name
2021-03-24 23:25:37 +00:00
Ran Aroussi
e77a1cf293 Merge pull request #635 from jlsajfj/master
Fix for issue of 1 hour interval incorrect date
2021-03-24 23:16:33 +00:00
Ran Aroussi
51ead25faa Merge pull request #643 from asafravid/sync-with-ranaroussi/yfinance-v0.1.56
2 important updates
2021-03-24 23:09:36 +00:00
Asaf
65b0e343d7 Merge branch 'main' into sync-with-ranaroussi/yfinance-v0.1.56 2021-03-25 00:15:09 +02:00
Asaf
a82ff40423 2 updates
1. Add trys and catch where misssing
2. Add "financialCurrency" property to earnings so as to allow required conversion rate for USD comparisons with USD-based fields
2021-03-24 23:51:29 +02:00
ran
81d3ea5e95 PR merged 2021-03-24 11:34:23 +00:00
Ran Aroussi
b12b1eceff Update README.rst 2021-03-24 11:24:16 +00:00
Ran Aroussi
9a2a21a7ef Update README.rst 2021-03-24 11:22:27 +00:00
Ran Aroussi
8b6960f966 Update setup.py 2021-03-24 11:21:03 +00:00
Ran Aroussi
1d247c7a4a Update .travis.yml 2021-03-24 11:20:39 +00:00
jlsajfj
d912d1380a Fix for issue of 1 hour interval incorrect date
Fix was found [here](https://stackoverflow.com/a/65260400)
2021-03-18 10:25:43 -04:00
Victor Silva
8bdc51f9fa Merge pull request #506 from HMellor/master
LGTM. Testing is now done with PyTest.
2021-03-14 09:39:57 -06:00
Victor Silva
cf8ed4d3f3 Merge pull request #217 from ceppelli/master
Merging as this was a missing piece of code.
2021-03-14 09:25:31 -06:00
Victor Silva
c0849956ae Merge pull request #169 from mapogu/ticket_xyz_financial_data
Merged as this was a missing piece of information.
2021-03-14 09:23:04 -06:00
Victor Silva
a3201984e9 Merge pull request #157 from zhenwenc/fix-missing-proxy-param
Merged as this was definitely a missing piece of code.
2021-03-14 09:18:33 -06:00
Asaf
20a869a204 Revert to 'USD' when there is no 'financialCurrency' does not exist
Revert to 'USD' when there is no 'financialCurrency' does not exist
2021-03-10 02:53:29 +02:00
Asaf
16536bf18e Add 'financialCurrency' to earnings
Add 'financialCurrency' to earnings
It is a MUST for comparing earnings and/or revenues, to the marketCap and enterpriseValue
2021-03-09 21:55:28 +02:00
Ran Aroussi
b9a0e64284 Create a release → publish to PyPi
Publishes a new version on PyPi whenever a new release version is created on Github
2021-03-03 23:34:48 +00:00
Victor Silva
649f15bdca Merge pull request #507 from danieljllo/master
Fix README.rst 'compatibility' typo
2021-03-02 12:44:36 -07:00
beglitis
a72557bddc Merge pull request #480 from PaulRenvoise/fix/fundamentals
Fix the fetching of fundamentals
2021-01-18 14:16:47 +00:00
beglitis
58b3eaf23f Merge pull request #529 from ussfletcher/patch-1
Update base.py
2021-01-11 09:52:24 +00:00
ussfletcher
ad5ca232af Update base.py
Proxy was not successfully passed through in calls to _get_fundamentals. This is due to _get_fundamentals having two keyword arguments "def _get_fundamentals(self, kind=None, proxy=None):" thus proxy was being stored to "kind" by default.

Fixed by adding keyword back into calls to _get_fundamentals
2020-12-09 17:47:23 -08:00
danieljllo
b58b809412 Fix README.rst 'compatibility' typo 2020-11-21 09:39:00 -06:00
HMellor
54191ca10c Swap badge from my username to yours 2020-11-21 11:21:24 +00:00
HMellor
80525860e2 Add minimum pytest version install 2020-11-21 10:59:59 +00:00
HMellor
3dde9d6e8c Add automatic testing and coverage 2020-11-21 10:38:06 +00:00
Asaf
79b33bbb4d Fix Holders error for no tables found
This Pull request must be pulled in it is a major fix
2020-11-11 23:30:03 +02:00
Paul Renvoisé
e28283a964 fix(get_fundamentals) prevent the override of ticker url by holders url 2020-10-19 14:21:10 +02:00
Trenton J. McKinney
46b0ab7787 Update README.rst 2020-08-05 14:00:04 -07:00
Trenton J. McKinney
3898215ed6 Update README.rst 2020-08-05 13:57:51 -07:00
Trenton J. McKinney
e79256891f Update README.rst 2020-08-05 13:57:00 -07:00
Trenton J. McKinney
d65063d316 Update README.rst 2020-08-05 13:55:24 -07:00
Trenton J. McKinney
15c8b9aea7 Update README.rst 2020-08-05 13:52:18 -07:00
Trenton J. McKinney
a4fa6b5abe Update README.rst
I've added this update as per https://twitter.com/Aroussi/status/1291009917818736641 with Ran Aroussi. This update adds a link to a stack overflow answer, which discusses dealing with multi-level column names.
2020-08-05 13:46:24 -07:00
ceppelli
a62f9cc1ea [FIX] - proxy parameter is not propagated correcty in the _get_fundamentals method 2020-03-10 16:21:49 +01:00
mapogu
ae5aad9c67 Add financialData to info 2020-01-14 13:25:36 +01:00
Frederick Cai
40f3a1ee7f added missing proxy parameter 2020-01-01 18:21:08 +13:00
23 changed files with 1041 additions and 505 deletions

17
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View 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
View 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
View 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
View 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
View File

@@ -3,3 +3,9 @@ yfinance/__pycache__/*
dist
yfinance.egg-info
*.pyc
.coverage
.vscode/
build/
*.html
*.css
*.png

View File

@@ -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

View File

@@ -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
View 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**

View File

@@ -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**

View File

@@ -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
View 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'

View File

@@ -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

View File

@@ -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()

View File

@@ -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
View 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()

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
@@ -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():
"""

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
@@ -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

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
@@ -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)

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
@@ -22,3 +22,4 @@
_DFS = {}
_PROGRESS_BAR = None
_ERRORS = {}
_ISINS = {}

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
@@ -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()

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
@@ -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)

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
@@ -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
View File

@@ -0,0 +1 @@
version = "0.1.68"