Add Sphinx Documentation
- Initial draft of Sphinx documentation using rst
- update docstrings
- add deploy_doc.yml for automated deployment through GitHub Actions
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
name: Build and Deploy Sphinx Docs
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- dev-documented
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Check out the repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.x'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install -r requirements.txt
|
||||
pip install Sphinx==8.0.2 pydata-sphinx-theme==0.15.4 Jinja2==3.1.4 sphinx-copybutton==0.5.2
|
||||
|
||||
- name: Build Sphinx documentation
|
||||
run: |
|
||||
sphinx-build -b html doc/source doc/_build/html -v
|
||||
|
||||
- name: List generated HTML files
|
||||
run: |
|
||||
ls -l -R doc/_build/html
|
||||
|
||||
- name: Deploy to GitHub Pages
|
||||
uses: peaceiris/actions-gh-pages@v4
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_branch: documentation
|
||||
publish_dir: doc/_build/html
|
||||
destination_dir: docs
|
||||
enable_jekyll: false
|
||||
@@ -18,3 +18,10 @@ test.ipynb
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
|
||||
# Documentation
|
||||
/doc/build/
|
||||
/doc/_build/
|
||||
/doc/source/reference/api
|
||||
!yfinance.css
|
||||
!/doc/source/development/assets/branches.png
|
||||
@@ -35,19 +35,16 @@ Yahoo! finance API is intended for personal use only.**
|
||||
|
||||
**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.
|
||||
## Main Features
|
||||
- `Ticker` module: Class for accessing single ticker data.
|
||||
- `Tickers` module: Class for handling multiple tickers.
|
||||
- `download` Efficiently download market data for multiple tickers.
|
||||
- `Sector` and `Industry` modules : Classes for accessing sector and industry information.
|
||||
- Market Screening: `EquityQuery` and `Screener` to build query and screen the market.
|
||||
- Caching and Smart Scraping
|
||||
|
||||
[Changelog »](https://github.com/ranaroussi/yfinance/blob/main/CHANGELOG.rst)
|
||||
|
||||
---
|
||||
|
||||
- [Installation](#installation)
|
||||
- [Quick start](#quick-start)
|
||||
- [Advanced](#logging)
|
||||
- [Wiki](https://github.com/ranaroussi/yfinance/wiki)
|
||||
- [Contribute](#developers-want-to-contribute)
|
||||
|
||||
---
|
||||
## Documentation
|
||||
The official documentation is available on [ranaroussi.github.io/yfinance](https://ranaroussi.github.io/yfinance/index.html)
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -67,292 +64,10 @@ $ pip install "yfinance[optional]"
|
||||
|
||||
[Required dependencies](./requirements.txt) , [all dependencies](./setup.py#L62).
|
||||
|
||||
---
|
||||
The list of changes can be found in the [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:
|
||||
|
||||
```python
|
||||
import yfinance as yf
|
||||
|
||||
msft = yf.Ticker("MSFT")
|
||||
|
||||
# get all stock info
|
||||
msft.info
|
||||
|
||||
# get historical market data
|
||||
hist = msft.history(period="1mo")
|
||||
|
||||
# show meta information about the history (requires history() to be called first)
|
||||
msft.history_metadata
|
||||
|
||||
# show actions (dividends, splits, capital gains)
|
||||
msft.actions
|
||||
msft.dividends
|
||||
msft.splits
|
||||
msft.capital_gains # only for mutual funds & etfs
|
||||
|
||||
# show share count
|
||||
msft.get_shares_full(start="2022-01-01", end=None)
|
||||
|
||||
# show financials:
|
||||
msft.calendar
|
||||
msft.sec_filings
|
||||
# - income statement
|
||||
msft.income_stmt
|
||||
msft.quarterly_income_stmt
|
||||
# - balance sheet
|
||||
msft.balance_sheet
|
||||
msft.quarterly_balance_sheet
|
||||
# - cash flow statement
|
||||
msft.cashflow
|
||||
msft.quarterly_cashflow
|
||||
# see `Ticker.get_income_stmt()` for more options
|
||||
|
||||
# show holders
|
||||
msft.major_holders
|
||||
msft.institutional_holders
|
||||
msft.mutualfund_holders
|
||||
msft.insider_transactions
|
||||
msft.insider_purchases
|
||||
msft.insider_roster_holders
|
||||
|
||||
msft.sustainability
|
||||
|
||||
# show recommendations
|
||||
msft.recommendations
|
||||
msft.recommendations_summary
|
||||
msft.upgrades_downgrades
|
||||
|
||||
# show analysts data
|
||||
msft.analyst_price_targets
|
||||
msft.earnings_estimate
|
||||
msft.revenue_estimate
|
||||
msft.earnings_history
|
||||
msft.eps_trend
|
||||
msft.eps_revisions
|
||||
msft.growth_estimates
|
||||
|
||||
# Show future and historic earnings dates, returns at most next 4 quarters and last 8 quarters by default.
|
||||
# Note: If more are needed use msft.get_earnings_dates(limit=XX) with increased limit argument.
|
||||
msft.earnings_dates
|
||||
|
||||
# show ISIN code - *experimental*
|
||||
# ISIN = International Securities Identification Number
|
||||
msft.isin
|
||||
|
||||
# show options expirations
|
||||
msft.options
|
||||
|
||||
# show news
|
||||
msft.news
|
||||
|
||||
# get option chain for specific expiration
|
||||
opt = msft.option_chain('YYYY-MM-DD')
|
||||
# data available via: opt.calls, opt.puts
|
||||
```
|
||||
|
||||
For tickers that are ETFs/Mutual Funds, `Ticker.funds_data` provides access to fund related data.
|
||||
|
||||
Funds' Top Holdings and other data with category average is returned as `pd.DataFrame`.
|
||||
|
||||
```python
|
||||
import yfinance as yf
|
||||
spy = yf.Ticker('SPY')
|
||||
data = spy.funds_data
|
||||
|
||||
# show fund description
|
||||
data.description
|
||||
|
||||
# show operational information
|
||||
data.fund_overview
|
||||
data.fund_operations
|
||||
|
||||
# show holdings related information
|
||||
data.asset_classes
|
||||
data.top_holdings
|
||||
data.equity_holdings
|
||||
data.bond_holdings
|
||||
data.bond_ratings
|
||||
data.sector_weightings
|
||||
```
|
||||
|
||||
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_capital_gains(proxy="PROXY_SERVER")
|
||||
msft.get_balance_sheet(proxy="PROXY_SERVER")
|
||||
msft.get_cashflow(proxy="PROXY_SERVER")
|
||||
msft.option_chain(..., proxy="PROXY_SERVER")
|
||||
...
|
||||
```
|
||||
|
||||
### Multiple tickers
|
||||
|
||||
To initialize multiple `Ticker` objects, use
|
||||
|
||||
```python
|
||||
import yfinance as yf
|
||||
|
||||
tickers = yf.Tickers('msft aapl goog')
|
||||
|
||||
# access each ticker using (example)
|
||||
tickers.tickers['MSFT'].info
|
||||
tickers.tickers['AAPL'].history(period="1mo")
|
||||
tickers.tickers['GOOG'].actions
|
||||
```
|
||||
|
||||
To download price history into one table:
|
||||
|
||||
```python
|
||||
import yfinance as yf
|
||||
data = yf.download("SPY AAPL", period="1mo")
|
||||
```
|
||||
|
||||
#### `yf.download()` and `Ticker.history()` have many options for configuring fetching and processing. [Review the Wiki](https://github.com/ranaroussi/yfinance/wiki) for more options and detail.
|
||||
|
||||
### Sector and Industry
|
||||
|
||||
The `Sector` and `Industry` modules allow you to access the US market information.
|
||||
|
||||
To initialize, use the relevant sector or industry key as below. (Complete mapping of the keys is available in `const.py`.)
|
||||
|
||||
```python
|
||||
import yfinance as yf
|
||||
|
||||
tech = yf.Sector('technology')
|
||||
software = yf.Industry('software-infrastructure')
|
||||
|
||||
# Common information
|
||||
tech.key
|
||||
tech.name
|
||||
tech.symbol
|
||||
tech.ticker
|
||||
tech.overview
|
||||
tech.top_companies
|
||||
tech.research_reports
|
||||
|
||||
# Sector information
|
||||
tech.top_etfs
|
||||
tech.top_mutual_funds
|
||||
tech.industries
|
||||
|
||||
# Industry information
|
||||
software.sector_key
|
||||
software.sector_name
|
||||
software.top_performing_companies
|
||||
software.top_growth_companies
|
||||
```
|
||||
|
||||
The modules can be chained with Ticker as below.
|
||||
```python
|
||||
import yfinance as yf
|
||||
|
||||
# Ticker to Sector and Industry
|
||||
msft = yf.Ticker('MSFT')
|
||||
tech = yf.Sector(msft.info.get('sectorKey'))
|
||||
software = yf.Industry(msft.info.get('industryKey'))
|
||||
|
||||
# Sector and Industry to Ticker
|
||||
tech_ticker = tech.ticker
|
||||
tech_ticker.info
|
||||
software_ticker = software.ticker
|
||||
software_ticker.history()
|
||||
```
|
||||
|
||||
### Market Screener
|
||||
The `Screener` module allows you to screen the market based on specified queries.
|
||||
|
||||
#### Query Construction
|
||||
To create a query, you can use the `EquityQuery` class to construct your filters step by step. The queries support operators: `GT` (greater than), `LT` (less than), `BTWN` (between), `EQ` (equals), and logical operators `AND` and `OR` for combining multiple conditions.
|
||||
|
||||
#### Screener
|
||||
The `Screener` class is used to execute the queries and return the filtered results. You can set a custom body for the screener or use predefined configurations.
|
||||
|
||||
<!-- TODO: link to Github Pages for more including list of predefined bodies, supported fields, operands, and sample code -->
|
||||
|
||||
### Logging
|
||||
|
||||
`yfinance` now uses the `logging` module to handle messages, default behaviour is only print errors. If debugging, use `yf.enable_debug_mode()` to switch logging to debug with custom formatting.
|
||||
|
||||
### Smarter scraping
|
||||
|
||||
Install the `nospam` packages for smarter scraping using `pip` (see [Installation](#installation)). These packages help cache calls such that Yahoo is not spammed with requests.
|
||||
|
||||
To use a custom `requests` session, pass a `session=` argument to
|
||||
the Ticker constructor. This allows for caching calls to the API as well as a custom way to modify requests via the `User-agent` header.
|
||||
|
||||
```python
|
||||
import requests_cache
|
||||
session = requests_cache.CachedSession('yfinance.cache')
|
||||
session.headers['User-agent'] = 'my-program/1.0'
|
||||
ticker = yf.Ticker('msft', session=session)
|
||||
# The scraped response will be stored in the cache
|
||||
ticker.actions
|
||||
```
|
||||
|
||||
Combine `requests_cache` with rate-limiting to avoid triggering Yahoo's rate-limiter/blocker that can corrupt data.
|
||||
```python
|
||||
from requests import Session
|
||||
from requests_cache import CacheMixin, SQLiteCache
|
||||
from requests_ratelimiter import LimiterMixin, MemoryQueueBucket
|
||||
from pyrate_limiter import Duration, RequestRate, Limiter
|
||||
class CachedLimiterSession(CacheMixin, LimiterMixin, Session):
|
||||
pass
|
||||
|
||||
session = CachedLimiterSession(
|
||||
limiter=Limiter(RequestRate(2, Duration.SECOND*5)), # max 2 requests per 5 seconds
|
||||
bucket_class=MemoryQueueBucket,
|
||||
backend=SQLiteCache("yfinance.cache"),
|
||||
)
|
||||
```
|
||||
|
||||
### 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
|
||||
|
||||
### Persistent cache store
|
||||
|
||||
To reduce Yahoo, yfinance store some data locally: timezones to localize dates, and cookie. Cache location is:
|
||||
|
||||
- Windows = C:/Users/\<USER\>/AppData/Local/py-yfinance
|
||||
- Linux = /home/\<USER\>/.cache/py-yfinance
|
||||
- MacOS = /Users/\<USER\>/Library/Caches/py-yfinance
|
||||
|
||||
You can direct cache to use a different location with `set_tz_cache_location()`:
|
||||
|
||||
```python
|
||||
import yfinance as yf
|
||||
yf.set_tz_cache_location("custom/cache/location")
|
||||
...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Developers: want to contribute?
|
||||
|
||||
`yfinance` relies on community to investigate bugs and contribute code. Developer guide: https://github.com/ranaroussi/yfinance/discussions/1084
|
||||
|
||||
---
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
# Minimal makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line, and also
|
||||
# from the environment for the first two.
|
||||
SPHINXOPTS ?=
|
||||
SPHINXBUILD ?= sphinx-build
|
||||
SOURCEDIR = source
|
||||
BUILDDIR = build
|
||||
|
||||
# Put it first so that "make" without argument is like "make help".
|
||||
help:
|
||||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
||||
.PHONY: help Makefile
|
||||
|
||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||
%: Makefile
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
@@ -0,0 +1,35 @@
|
||||
@ECHO OFF
|
||||
|
||||
pushd %~dp0
|
||||
|
||||
REM Command file for Sphinx documentation
|
||||
|
||||
if "%SPHINXBUILD%" == "" (
|
||||
set SPHINXBUILD=sphinx-build
|
||||
)
|
||||
set SOURCEDIR=source
|
||||
set BUILDDIR=build
|
||||
|
||||
%SPHINXBUILD% >NUL 2>NUL
|
||||
if errorlevel 9009 (
|
||||
echo.
|
||||
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
||||
echo.installed, then set the SPHINXBUILD environment variable to point
|
||||
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
||||
echo.may add the Sphinx directory to PATH.
|
||||
echo.
|
||||
echo.If you don't have Sphinx installed, grab it from
|
||||
echo.https://www.sphinx-doc.org/
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
if "%1" == "" goto help
|
||||
|
||||
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||
goto end
|
||||
|
||||
:help
|
||||
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||
|
||||
:end
|
||||
popd
|
||||
@@ -0,0 +1,4 @@
|
||||
/* Hide the "Section Navigation" title */
|
||||
p.bd-links__title {
|
||||
display: none;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
{{ fullname | escape | underline}}
|
||||
|
||||
.. currentmodule:: {{ module }}
|
||||
|
||||
.. autoclass:: {{ objname }}
|
||||
|
||||
{% block attributes %}
|
||||
{% if attributes %}
|
||||
.. rubric:: Attributes
|
||||
|
||||
.. autosummary::
|
||||
:toctree: attributes
|
||||
{% for item in attributes %}
|
||||
~{{ name }}.{{ item }}
|
||||
{%- endfor %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block methods %}
|
||||
{% if methods %}
|
||||
.. rubric:: Methods
|
||||
|
||||
.. autosummary::
|
||||
:toctree: methods
|
||||
{% for item in methods %}
|
||||
~{{ name }}.{{ item }}
|
||||
{%- endfor %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,45 @@
|
||||
import os
|
||||
import sys
|
||||
sys.path.insert(0, os.path.abspath('../..'))
|
||||
|
||||
# Configuration file for the Sphinx documentation builder.
|
||||
#
|
||||
# For the full list of built-in configuration values, see the documentation:
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
||||
|
||||
# -- Project information -----------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
|
||||
|
||||
project = 'yfinance - market data downloader'
|
||||
copyright = '2017-2019 Ran Aroussi'
|
||||
author = 'Ran Aroussi'
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
|
||||
|
||||
extensions = ['sphinx.ext.autodoc',
|
||||
'sphinx.ext.napoleon',
|
||||
"sphinx.ext.githubpages",
|
||||
"sphinx.ext.autosectionlabel",
|
||||
"sphinx.ext.autosummary",
|
||||
"sphinx_copybutton"]
|
||||
|
||||
templates_path = ['_templates']
|
||||
exclude_patterns = []
|
||||
autoclass_content = 'both'
|
||||
autosummary_generate = True
|
||||
autodoc_default_options = {
|
||||
'exclude-members': '__init__'
|
||||
}
|
||||
|
||||
# -- Options for HTML output -------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
|
||||
|
||||
html_title = 'yfinance'
|
||||
html_theme = 'pydata_sphinx_theme'
|
||||
html_theme_options = {
|
||||
"github_url": "https://github.com/ranaroussi/yfinance",
|
||||
"navbar_align": "left"
|
||||
}
|
||||
html_static_path = ['_static']
|
||||
html_css_files = ['yfinance.css']
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 41 KiB |
@@ -0,0 +1,109 @@
|
||||
********************************
|
||||
Contributiong to yfinance
|
||||
********************************
|
||||
|
||||
`yfinance` relies on the community to investigate bugs and contribute code. Here’s how you can help:
|
||||
|
||||
Contributing
|
||||
------------
|
||||
|
||||
1. Fork the repository on GitHub.
|
||||
2. Clone your forked repository:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
git clone https://github.com/your-username/yfinance.git
|
||||
|
||||
3. Create a new branch for your feature or bug fix:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
git checkout -b feature-branch-name
|
||||
|
||||
4. Make your changes, commit them, and push your branch to GitHub. To keep the commit history and `network graph <https://github.com/ranaroussi/yfinance/network>`_ compact:
|
||||
|
||||
Use short summaries for commits
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
git commit -m "short summary" -m "full commit message"
|
||||
|
||||
**Squash** tiny or negligible commits with meaningful ones.
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
git rebase -i HEAD~2
|
||||
git push --force-with-lease origin <branch-name>
|
||||
|
||||
5. Open a pull request on the `yfinance` GitHub page.
|
||||
|
||||
For more information, see the `Developer Guide <https://github.com/ranaroussi/yfinance/discussions/1084>`_.
|
||||
|
||||
Branches
|
||||
---------
|
||||
|
||||
To support rapid development without breaking stable versions, this project uses a two-layer branch model:
|
||||
|
||||
.. image:: assets/branches.png
|
||||
:alt: Branching Model
|
||||
|
||||
`Inspiration <https://miro.medium.com/max/700/1*2YagIpX6LuauC3ASpwHekg.png>`_
|
||||
|
||||
- **dev**: New features and some bug fixes are merged here. This branch allows collective testing, conflict resolution, and further stabilization before merging into the stable branch.
|
||||
- **main**: Stable branch where PIP releases are created.
|
||||
|
||||
By default, branches target **main**, but most contributions should target **dev**.
|
||||
|
||||
**Exceptions**:
|
||||
Direct merges to **main** are allowed if:
|
||||
|
||||
- `yfinance` is massively broken
|
||||
- Part of `yfinance` is broken, and the fix is simple and isolated
|
||||
|
||||
Unit Tests
|
||||
----------
|
||||
|
||||
Tests are written using Python’s `unittest` module. Here are some ways to run tests:
|
||||
|
||||
- **Run all price tests**:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
python -m unittest tests.test_prices
|
||||
|
||||
- **Run a subset of price tests**:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
python -m unittest tests.test_prices.TestPriceRepair
|
||||
|
||||
- **Run a specific test**:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
python -m unittest tests.test_prices.TestPriceRepair.test_ticker_missing
|
||||
|
||||
- **Run all tests**:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
python -m unittest discover -s tests
|
||||
|
||||
Rebasing
|
||||
--------------
|
||||
|
||||
If asked to move your branch from **main** to **dev**:
|
||||
|
||||
1. Ensure all relevant branches are pulled.
|
||||
2. Run:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
git checkout <your-branch>
|
||||
git rebase --onto dev main <branch-name>
|
||||
git push --force-with-lease origin <branch-name>
|
||||
|
||||
Running the GitHub Version of yfinance
|
||||
--------------------------------------
|
||||
|
||||
To download and run a GitHub version of `yfinance`, refer to `GitHub discussion <https://github.com/ranaroussi/yfinance/discussions/1080>`_
|
||||
@@ -0,0 +1,46 @@
|
||||
*************************************
|
||||
Contribution to the documentation
|
||||
*************************************
|
||||
|
||||
.. contents:: Documentation:
|
||||
:local:
|
||||
|
||||
About documentation
|
||||
------------------------
|
||||
* yfinance documentation is written in reStructuredText (rst) and built using Sphinx.
|
||||
* The documentation file is in `doc/source/..`.
|
||||
* Most of the notes under API References read from class and methods docstrings. These documentations, found in `doc/source/reference/api` is autogenerated by Sphinx and not included in git.
|
||||
|
||||
Building documentation locally
|
||||
-------------------------------
|
||||
To build the documentation locally, follow these steps:
|
||||
|
||||
1. **Install Required Dependencies**:
|
||||
|
||||
* Make sure `Sphinx` and any other dependencies are installed. If a `requirements.txt` file is available, you can install dependencies by running:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
pip install -r requirements.txt
|
||||
|
||||
|
||||
2. **Build with Sphinx**:
|
||||
|
||||
* After dependencies are installed, use the sphinx-build command to generate HTML documentation.
|
||||
* Go to `doc/` directory Run:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
make clean && make html
|
||||
|
||||
3. **View Documentation Locally**:
|
||||
|
||||
* Open `doc/build/html/index.html` in the browser to view the generated documentation.
|
||||
|
||||
Building documentation on main
|
||||
-------------------------------
|
||||
The documentation updates are built on merge to `main` branch. This is done via GitHub Actions workflow based on `/yfinance/.github/workflows/deploy_doc.yml`.
|
||||
|
||||
1. Reivew the changes locally and push to `dev`.
|
||||
|
||||
2. When `dev` gets merged to `main`, GitHub Actions workflow is automated to build documentation.
|
||||
@@ -0,0 +1,9 @@
|
||||
Development
|
||||
===============================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
contributing
|
||||
documentation
|
||||
reporting_bug
|
||||
@@ -0,0 +1,5 @@
|
||||
********************************
|
||||
Reporting a Bug
|
||||
********************************
|
||||
|
||||
Open a new issue on our `GitHub <https://github.com/ranaroussi/yfinance/issues>`_.
|
||||
@@ -0,0 +1,9 @@
|
||||
Getting Started
|
||||
===============
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
installation
|
||||
quick_start
|
||||
legal
|
||||
@@ -0,0 +1,17 @@
|
||||
********************
|
||||
Installation Guide
|
||||
********************
|
||||
|
||||
Install `yfinance` using `pip`:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ pip install yfinance --upgrade --no-cache-dir
|
||||
|
||||
To install with optional dependencies, replace `optional` with: `nospam` for `caching-requests <https://github.com/ranaroussi/yfinance?tab=readme-ov-file#smarter-scraping>`_, `repair` for `price repair <https://github.com/ranaroussi/yfinance/wiki/Price-repair>`_, or `nospam`, `repair` for both:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ pip install "yfinance[optional]"
|
||||
|
||||
For required dependencies, check out the `requirements file <./requirements.txt>`_, and for all dependencies, see the `setup.py file <./setup.py#L62>`_.
|
||||
@@ -0,0 +1,12 @@
|
||||
********************
|
||||
Legal Information
|
||||
********************
|
||||
|
||||
yfinance is distributed under the Apache Software License. See the `LICENSE.txt <../../../../LICENSE.txt>`_ file 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.
|
||||
|
||||
Refer to Yahoo!'s terms of use:
|
||||
|
||||
- `API Terms <https://policies.yahoo.com/us/en/yahoo/terms/product-atos/apiforydn/index.htm>`_
|
||||
- `Yahoo Terms <https://legal.yahoo.com/us/en/yahoo/terms/otos/index.html>`_
|
||||
@@ -0,0 +1,30 @@
|
||||
********************
|
||||
Quick Start
|
||||
********************
|
||||
|
||||
The Ticker module allows you to access ticker data in a more Pythonic way:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import yfinance as yf
|
||||
|
||||
msft = yf.Ticker("MSFT")
|
||||
|
||||
# get all stock info
|
||||
msft.info
|
||||
|
||||
# get historical market data
|
||||
hist = msft.history(period="1mo")
|
||||
|
||||
# show actions (dividends, splits, capital gains)
|
||||
msft.actions
|
||||
msft.dividends
|
||||
msft.splits
|
||||
|
||||
To work with multiple tickers, use:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
tickers = yf.Tickers('msft aapl goog')
|
||||
tickers.tickers['MSFT'].info
|
||||
tickers.tickers['AAPL'].history(period="1mo")
|
||||
@@ -0,0 +1,30 @@
|
||||
yfinance documentation
|
||||
==============================================
|
||||
|
||||
Download Market Data from Yahoo! Finance's API
|
||||
------------------------------------------------
|
||||
|
||||
.. admonition:: 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.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 3
|
||||
:hidden:
|
||||
:titlesonly:
|
||||
|
||||
getting_started/index
|
||||
user_guide/index
|
||||
reference/index
|
||||
development/index
|
||||
@@ -0,0 +1,2 @@
|
||||
import yfinance as yf
|
||||
data = yf.download("SPY AAPL", period="1mo")
|
||||
@@ -0,0 +1,18 @@
|
||||
import yfinance as yf
|
||||
spy = yf.Ticker('SPY')
|
||||
data = spy.funds_data
|
||||
|
||||
# show fund description
|
||||
data.description
|
||||
|
||||
# show operational information
|
||||
data.fund_overview
|
||||
data.fund_operations
|
||||
|
||||
# show holdings related information
|
||||
data.asset_classes
|
||||
data.top_holdings
|
||||
data.equity_holdings
|
||||
data.bond_holdings
|
||||
data.bond_ratings
|
||||
data.sector_weightings
|
||||
@@ -0,0 +1,13 @@
|
||||
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_capital_gains(proxy="PROXY_SERVER")
|
||||
msft.get_balance_sheet(proxy="PROXY_SERVER")
|
||||
msft.get_cashflow(proxy="PROXY_SERVER")
|
||||
msft.option_chain(..., proxy="PROXY_SERVER")
|
||||
...
|
||||
@@ -0,0 +1,25 @@
|
||||
import yfinance as yf
|
||||
|
||||
tech = yf.Sector('technology')
|
||||
software = yf.Industry('software-infrastructure')
|
||||
|
||||
# Common information
|
||||
tech.key
|
||||
tech.name
|
||||
tech.symbol
|
||||
tech.ticker
|
||||
tech.overview
|
||||
tech.top_companies
|
||||
tech.research_reports
|
||||
|
||||
# Sector information
|
||||
tech.top_etfs
|
||||
tech.top_mutual_funds
|
||||
tech.industries
|
||||
|
||||
# Industry information
|
||||
software.sector_key
|
||||
software.sector_name
|
||||
software.top_performing_companies
|
||||
software.top_growth_companies
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import yfinance as yf
|
||||
# Ticker to Sector and Industry
|
||||
msft = yf.Ticker('MSFT')
|
||||
tech = yf.Sector(msft.info.get('sectorKey'))
|
||||
software = yf.Industry(msft.info.get('industryKey'))
|
||||
|
||||
# Sector and Industry to Ticker
|
||||
tech_ticker = tech.ticker
|
||||
tech_ticker.info
|
||||
software_ticker = software.ticker
|
||||
software_ticker.history()
|
||||
@@ -0,0 +1,77 @@
|
||||
import yfinance as yf
|
||||
|
||||
msft = yf.Ticker("MSFT")
|
||||
|
||||
# get all stock info
|
||||
msft.info
|
||||
|
||||
# get historical market data
|
||||
hist = msft.history(period="1mo")
|
||||
|
||||
# show meta information about the history (requires history() to be called first)
|
||||
msft.history_metadata
|
||||
|
||||
# show actions (dividends, splits, capital gains)
|
||||
msft.actions
|
||||
msft.dividends
|
||||
msft.splits
|
||||
msft.capital_gains # only for mutual funds & etfs
|
||||
|
||||
# show share count
|
||||
msft.get_shares_full(start="2022-01-01", end=None)
|
||||
|
||||
# show financials:
|
||||
msft.calendar
|
||||
msft.sec_filings
|
||||
# - income statement
|
||||
msft.income_stmt
|
||||
msft.quarterly_income_stmt
|
||||
# - balance sheet
|
||||
msft.balance_sheet
|
||||
msft.quarterly_balance_sheet
|
||||
# - cash flow statement
|
||||
msft.cashflow
|
||||
msft.quarterly_cashflow
|
||||
# see `Ticker.get_income_stmt()` for more options
|
||||
|
||||
# show holders
|
||||
msft.major_holders
|
||||
msft.institutional_holders
|
||||
msft.mutualfund_holders
|
||||
msft.insider_transactions
|
||||
msft.insider_purchases
|
||||
msft.insider_roster_holders
|
||||
|
||||
msft.sustainability
|
||||
|
||||
# show recommendations
|
||||
msft.recommendations
|
||||
msft.recommendations_summary
|
||||
msft.upgrades_downgrades
|
||||
|
||||
# show analysts data
|
||||
msft.analyst_price_targets
|
||||
msft.earnings_estimate
|
||||
msft.revenue_estimate
|
||||
msft.earnings_history
|
||||
msft.eps_trend
|
||||
msft.eps_revisions
|
||||
msft.growth_estimates
|
||||
|
||||
# Show future and historic earnings dates, returns at most next 4 quarters and last 8 quarters by default.
|
||||
# Note: If more are needed use msft.get_earnings_dates(limit=XX) with increased limit argument.
|
||||
msft.earnings_dates
|
||||
|
||||
# show ISIN code - *experimental*
|
||||
# ISIN = International Securities Identification Number
|
||||
msft.isin
|
||||
|
||||
# show options expirations
|
||||
msft.options
|
||||
|
||||
# show news
|
||||
msft.news
|
||||
|
||||
# get option chain for specific expiration
|
||||
opt = msft.option_chain('YYYY-MM-DD')
|
||||
# data available via: opt.calls, opt.puts
|
||||
@@ -0,0 +1,8 @@
|
||||
import yfinance as yf
|
||||
|
||||
tickers = yf.Tickers('msft aapl goog')
|
||||
|
||||
# access each ticker using (example)
|
||||
tickers.tickers['MSFT'].info
|
||||
tickers.tickers['AAPL'].history(period="1mo")
|
||||
tickers.tickers['GOOG'].actions
|
||||
@@ -0,0 +1,33 @@
|
||||
=======================
|
||||
API Reference
|
||||
=======================
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
The `yfinance` package provides easy access to Yahoo! Finance's API to retrieve market data. It includes classes and functions for downloading historical market data, accessing ticker information, managing cache, and more.
|
||||
|
||||
|
||||
Public API
|
||||
==========
|
||||
|
||||
The following are the publicly available classes, and functions exposed by the `yfinance` package:
|
||||
|
||||
- :attr:`Ticker <yfinance.Ticker>`: Class for accessing single ticker data.
|
||||
- :attr:`Tickers <yfinance.Tickers>`: Class for handling multiple tickers.
|
||||
- :attr:`Sector <yfinance.Sector>`: Domain class for accessing sector information.
|
||||
- :attr:`Industry <yfinance.Industry>`: Domain class for accessing industry information.
|
||||
- :attr:`download <yfinance.download>`: Function to download market data for multiple tickers.
|
||||
- :attr:`EquityQuery <yfinance.EquityQuery>`: Class to build equity market query.
|
||||
- :attr:`Screener <yfinance.Screener>`: Class to screen the market using defined query.
|
||||
- :attr:`enable_debug_mode <yfinance.enable_debug_mode>`: Function to enable debug mode for logging.
|
||||
- :attr:`set_tz_cache_location <yfinance.set_tz_cache_location>`: Function to set the timezone cache location.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:hidden:
|
||||
|
||||
|
||||
yfinance.ticker_tickers
|
||||
yfinance.sector_industry
|
||||
yfinance.functions
|
||||
@@ -0,0 +1,51 @@
|
||||
=========================
|
||||
Functions and Utilities
|
||||
=========================
|
||||
|
||||
.. currentmodule:: yfinance
|
||||
|
||||
Download Market Data
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
The `download` function allows you to retrieve market data for multiple tickers at once.
|
||||
|
||||
.. autosummary::
|
||||
:toctree: api/
|
||||
|
||||
download
|
||||
|
||||
Query Market Data
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
The `Sector` and `Industry` modules allow you to access the sector and industry information.
|
||||
|
||||
.. autosummary::
|
||||
:toctree: api/
|
||||
|
||||
EquityQuery
|
||||
Screener
|
||||
|
||||
.. seealso::
|
||||
:attr:`EquityQuery.valid_operand_fields <yfinance.EquityQuery.valid_operand_fields>`
|
||||
supported operand values for query
|
||||
:attr:`EquityQuery.valid_eq_operand_map <yfinance.EquityQuery.valid_eq_operand_map>`
|
||||
supported `EQ query operand parameters`
|
||||
:attr:`Screener.predefined_bodies <yfinance.Screener.predefined_bodies>`
|
||||
supported predefined screens
|
||||
|
||||
|
||||
Enable Debug Mode
|
||||
~~~~~~~~~~~~~~~~~
|
||||
Enables logging of debug information for the `yfinance` package.
|
||||
|
||||
.. autosummary::
|
||||
:toctree: api/
|
||||
|
||||
enable_debug_mode
|
||||
|
||||
Set Timezone Cache Location
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Sets the cache location for timezone data.
|
||||
|
||||
.. autosummary::
|
||||
:toctree: api/
|
||||
|
||||
set_tz_cache_location
|
||||
@@ -0,0 +1,32 @@
|
||||
=======================
|
||||
Sector and Industry
|
||||
=======================
|
||||
|
||||
.. currentmodule:: yfinance
|
||||
|
||||
Class
|
||||
------------
|
||||
The `Sector` and `Industry` modules provide access to the Sector and Industry information.
|
||||
|
||||
.. autosummary::
|
||||
:toctree: api/
|
||||
:recursive:
|
||||
|
||||
Sector
|
||||
Industry
|
||||
|
||||
.. seealso::
|
||||
:attr:`Sector.industries <yfinance.Sector.industries>`
|
||||
Map of sector and industry
|
||||
|
||||
Sample Code
|
||||
------------
|
||||
To initialize, use the relevant sector or industry key as below.
|
||||
|
||||
.. literalinclude:: examples/sector_industry.py
|
||||
:language: python
|
||||
|
||||
The modules can be chained with Ticker as below.
|
||||
|
||||
.. literalinclude:: examples/sector_industry_ticker.py
|
||||
:language: python
|
||||
@@ -0,0 +1,46 @@
|
||||
=====================
|
||||
Ticker and Tickers
|
||||
=====================
|
||||
|
||||
.. currentmodule:: yfinance
|
||||
|
||||
|
||||
Class
|
||||
------------
|
||||
The `Ticker` module, allows you to access ticker data in a Pythonic way.
|
||||
|
||||
.. autosummary::
|
||||
:toctree: api/
|
||||
|
||||
Ticker
|
||||
Tickers
|
||||
|
||||
|
||||
Sample Code
|
||||
------------
|
||||
The `Ticker` module, allows you to access ticker data in a Pythonic way.
|
||||
|
||||
.. literalinclude:: examples/ticker.py
|
||||
:language: python
|
||||
|
||||
To initialize multiple `Ticker` objects, use
|
||||
|
||||
.. literalinclude:: examples/tickers.py
|
||||
:language: python
|
||||
|
||||
For tickers that are ETFs/Mutual Funds, `Ticker.funds_data` provides access to fund related data.
|
||||
|
||||
Funds' Top Holdings and other data with category average is returned as `pd.DataFrame`.
|
||||
|
||||
.. literalinclude:: examples/funds_data.py
|
||||
:language: python
|
||||
|
||||
If you want to use a proxy server for downloading data, use:
|
||||
|
||||
.. literalinclude:: examples/proxy.py
|
||||
:language: python
|
||||
|
||||
To initialize multiple `Ticker` objects, use `Tickers` module
|
||||
|
||||
.. literalinclude:: examples/tickers.py
|
||||
:language: python
|
||||
@@ -0,0 +1,11 @@
|
||||
User Guide
|
||||
==========
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
logging
|
||||
proxy
|
||||
smart_scraping
|
||||
persistent_cache
|
||||
multi_level_columns
|
||||
@@ -0,0 +1,11 @@
|
||||
Logging in yfinance
|
||||
===================
|
||||
|
||||
`yfinance` uses the `logging` module to handle messages. By default, only errors are logged.
|
||||
|
||||
If debugging, you can switch to debug mode with custom formatting using:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import yfinance as yf
|
||||
yf.enable_debug_mode()
|
||||
@@ -0,0 +1,13 @@
|
||||
******************************
|
||||
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 singledataframe with single level column names and a ticker column
|
||||
@@ -0,0 +1,16 @@
|
||||
******************************
|
||||
Persistent Cache Store
|
||||
******************************
|
||||
|
||||
To reduce Yahoo, yfinance store some data locally: timezones to localize dates, and cookie. Cache location is:
|
||||
|
||||
- Windows = C:/Users/\<USER\>/AppData/Local/py-yfinance
|
||||
- Linux = /home/\<USER\>/.cache/py-yfinance
|
||||
- MacOS = /Users/\<USER\>/Library/Caches/py-yfinance
|
||||
|
||||
You can direct cache to use a different location with `set_tz_cache_location()`:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import yfinance as yf
|
||||
yf.set_tz_cache_location("custom/cache/location")
|
||||
@@ -0,0 +1,11 @@
|
||||
*********************
|
||||
Using a Proxy Server
|
||||
*********************
|
||||
|
||||
You can download data via a proxy:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
msft = yf.Ticker("MSFT")
|
||||
msft.history(..., proxy="PROXY_SERVER")
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
******************************
|
||||
Smarter Scraping with Caching
|
||||
******************************
|
||||
|
||||
|
||||
Install the `nospam` package to cache API calls and reduce spam to Yahoo:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pip install yfinance[nospam]
|
||||
|
||||
To use a custom `requests` session, pass a `session=` argument to
|
||||
the Ticker constructor. This allows for caching calls to the API as well as a custom way to modify requests via the `User-agent` header.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import requests_cache
|
||||
session = requests_cache.CachedSession('yfinance.cache')
|
||||
session.headers['User-agent'] = 'my-program/1.0'
|
||||
ticker = yf.Ticker('MSFT', session=session)
|
||||
|
||||
# The scraped response will be stored in the cache
|
||||
ticker.actions
|
||||
|
||||
|
||||
Combine `requests_cache` with rate-limiting to avoid triggering Yahoo's rate-limiter/blocker that can corrupt data.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from requests import Session
|
||||
from requests_cache import CacheMixin, SQLiteCache
|
||||
from requests_ratelimiter import LimiterMixin, MemoryQueueBucket
|
||||
from pyrate_limiter import Duration, RequestRate, Limiter
|
||||
class CachedLimiterSession(CacheMixin, LimiterMixin, Session):
|
||||
pass
|
||||
|
||||
session = CachedLimiterSession(
|
||||
limiter=Limiter(RequestRate(2, Duration.SECOND*5)), # max 2 requests per 5 seconds
|
||||
bucket_class=MemoryQueueBucket,
|
||||
backend=SQLiteCache("yfinance.cache"),
|
||||
)
|
||||
+1
-1
@@ -8,4 +8,4 @@ pytz>=2022.5
|
||||
frozendict>=2.3.4
|
||||
beautifulsoup4>=4.11.1
|
||||
html5lib>=1.1
|
||||
peewee>=3.16.2
|
||||
peewee>=3.16.2
|
||||
+9
-6
@@ -560,12 +560,15 @@ class TickerBase:
|
||||
def get_earnings_dates(self, limit=12, proxy=None) -> Optional[pd.DataFrame]:
|
||||
"""
|
||||
Get earning dates (future and historic)
|
||||
:param limit: max amount of upcoming and recent earnings dates to return.
|
||||
Default value 12 should return next 4 quarters and last 8 quarters.
|
||||
Increase if more history is needed.
|
||||
|
||||
:param proxy: requests proxy to use.
|
||||
:return: pandas dataframe
|
||||
|
||||
Args:
|
||||
limit (int): max amount of upcoming and recent earnings dates to return.
|
||||
Default value 12 should return next 4 quarters and last 8 quarters.
|
||||
Increase if more history is needed.
|
||||
proxy: requests proxy to use.
|
||||
|
||||
Returns:
|
||||
pd.DataFrame
|
||||
"""
|
||||
if self._earnings_dates and limit in self._earnings_dates:
|
||||
return self._earnings_dates[limit]
|
||||
|
||||
+102
-113
@@ -427,119 +427,108 @@ EQUITY_SCREENER_EQ_MAP = {
|
||||
}
|
||||
}
|
||||
EQUITY_SCREENER_FIELDS = {
|
||||
# EQ Fields
|
||||
"region",
|
||||
"sector",
|
||||
"peer_group",
|
||||
"exchanges",
|
||||
|
||||
# price
|
||||
"eodprice",
|
||||
"intradaypricechange",
|
||||
"lastclosemarketcap.lasttwelvemonths",
|
||||
"percentchange",
|
||||
"lastclose52weekhigh.lasttwelvemonths",
|
||||
"fiftytwowkpercentchange",
|
||||
"intradayprice",
|
||||
"lastclose52weeklow.lasttwelvemonths",
|
||||
"intradaymarketcap",
|
||||
|
||||
# trading
|
||||
"beta",
|
||||
"avgdailyvol3m",
|
||||
"pctheldinsider",
|
||||
"pctheldinst",
|
||||
"dayvolume",
|
||||
"eodvolume",
|
||||
|
||||
# short interest
|
||||
"short_percentage_of_shares_outstanding.value",
|
||||
"short_interest.value",
|
||||
"short_percentage_of_float.value",
|
||||
"days_to_cover_short.value",
|
||||
"short_interest_percentage_change.value",
|
||||
|
||||
# valuation
|
||||
"bookvalueshare.lasttwelvemonths",
|
||||
"lastclosemarketcaptotalrevenue.lasttwelvemonths",
|
||||
"lastclosetevtotalrevenue.lasttwelvemonths",
|
||||
"pricebookratio.quarterly",
|
||||
"peratio.lasttwelvemonths",
|
||||
"lastclosepricetangiblebookvalue.lasttwelvemonths",
|
||||
"lastclosepriceearnings.lasttwelvemonths",
|
||||
"pegratio_5y",
|
||||
|
||||
# profitability
|
||||
"consecutive_years_of_dividend_growth_count",
|
||||
"returnonassets.lasttwelvemonths",
|
||||
"returnonequity.lasttwelvemonths",
|
||||
"forward_dividend_per_share",
|
||||
"forward_dividend_yield",
|
||||
"returnontotalcapital.lasttwelvemonths",
|
||||
|
||||
# leverage
|
||||
"lastclosetevebit.lasttwelvemonths",
|
||||
"netdebtebitda.lasttwelvemonths",
|
||||
"totaldebtequity.lasttwelvemonths",
|
||||
"ltdebtequity.lasttwelvemonths",
|
||||
"ebitinterestexpense.lasttwelvemonths",
|
||||
"ebitdainterestexpense.lasttwelvemonths",
|
||||
"lastclosetevebitda.lasttwelvemonths",
|
||||
"totaldebtebitda.lasttwelvemonths",
|
||||
|
||||
# liquidity
|
||||
"quickratio.lasttwelvemonths",
|
||||
"altmanzscoreusingtheaveragestockinformationforaperiod.lasttwelvemonths",
|
||||
"currentratio.lasttwelvemonths",
|
||||
"operatingcashflowtocurrentliabilities.lasttwelvemonths",
|
||||
|
||||
# income statement
|
||||
"totalrevenues.lasttwelvemonths",
|
||||
"netincomemargin.lasttwelvemonths",
|
||||
"grossprofit.lasttwelvemonths",
|
||||
"ebitda1yrgrowth.lasttwelvemonths",
|
||||
"dilutedepscontinuingoperations.lasttwelvemonths",
|
||||
"quarterlyrevenuegrowth.quarterly",
|
||||
"epsgrowth.lasttwelvemonths",
|
||||
"netincomeis.lasttwelvemonths",
|
||||
"ebitda.lasttwelvemonths",
|
||||
"dilutedeps1yrgrowth.lasttwelvemonths",
|
||||
"totalrevenues1yrgrowth.lasttwelvemonths",
|
||||
"operatingincome.lasttwelvemonths",
|
||||
"netincome1yrgrowth.lasttwelvemonths",
|
||||
"grossprofitmargin.lasttwelvemonths",
|
||||
"ebitdamargin.lasttwelvemonths",
|
||||
"ebit.lasttwelvemonths",
|
||||
"basicepscontinuingoperations.lasttwelvemonths",
|
||||
"netepsbasic.lasttwelvemonths"
|
||||
"netepsdiluted.lasttwelvemonths",
|
||||
|
||||
# balance sheet
|
||||
"totalassets.lasttwelvemonths",
|
||||
"totalcommonsharesoutstanding.lasttwelvemonths",
|
||||
"totaldebt.lasttwelvemonths",
|
||||
"totalequity.lasttwelvemonths",
|
||||
"totalcurrentassets.lasttwelvemonths",
|
||||
"totalcashandshortterminvestments.lasttwelvemonths",
|
||||
"totalcommonequity.lasttwelvemonths",
|
||||
"totalcurrentliabilities.lasttwelvemonths",
|
||||
"totalsharesoutstanding",
|
||||
|
||||
# cash flow
|
||||
"forward_dividend_yield",
|
||||
"leveredfreecashflow.lasttwelvemonths",
|
||||
"capitalexpenditure.lasttwelvemonths",
|
||||
"cashfromoperations.lasttwelvemonths",
|
||||
"leveredfreecashflow1yrgrowth.lasttwelvemonths",
|
||||
"unleveredfreecashflow.lasttwelvemonths",
|
||||
"cashfromoperations1yrgrowth.lasttwelvemonths",
|
||||
|
||||
# ESG
|
||||
"esg_score",
|
||||
"environmental_score",
|
||||
"governance_score",
|
||||
"social_score",
|
||||
"highest_controversy"
|
||||
"eq_fields": {
|
||||
"region",
|
||||
"sector",
|
||||
"peer_group",
|
||||
"exchanges"},
|
||||
"price":{
|
||||
"eodprice",
|
||||
"intradaypricechange",
|
||||
"lastclosemarketcap.lasttwelvemonths",
|
||||
"percentchange",
|
||||
"lastclose52weekhigh.lasttwelvemonths",
|
||||
"fiftytwowkpercentchange",
|
||||
"intradayprice",
|
||||
"lastclose52weeklow.lasttwelvemonths",
|
||||
"intradaymarketcap"},
|
||||
"trading":{
|
||||
"beta",
|
||||
"avgdailyvol3m",
|
||||
"pctheldinsider",
|
||||
"pctheldinst",
|
||||
"dayvolume",
|
||||
"eodvolume"},
|
||||
"short_interest":{
|
||||
"short_percentage_of_shares_outstanding.value",
|
||||
"short_interest.value",
|
||||
"short_percentage_of_float.value",
|
||||
"days_to_cover_short.value",
|
||||
"short_interest_percentage_change.value"},
|
||||
"valuation":{
|
||||
"bookvalueshare.lasttwelvemonths",
|
||||
"lastclosemarketcaptotalrevenue.lasttwelvemonths",
|
||||
"lastclosetevtotalrevenue.lasttwelvemonths",
|
||||
"pricebookratio.quarterly",
|
||||
"peratio.lasttwelvemonths",
|
||||
"lastclosepricetangiblebookvalue.lasttwelvemonths",
|
||||
"lastclosepriceearnings.lasttwelvemonths",
|
||||
"pegratio_5y"},
|
||||
"profitability":{
|
||||
"consecutive_years_of_dividend_growth_count",
|
||||
"returnonassets.lasttwelvemonths",
|
||||
"returnonequity.lasttwelvemonths",
|
||||
"forward_dividend_per_share",
|
||||
"forward_dividend_yield",
|
||||
"returnontotalcapital.lasttwelvemonths"},
|
||||
"leverage":{
|
||||
"lastclosetevebit.lasttwelvemonths",
|
||||
"netdebtebitda.lasttwelvemonths",
|
||||
"totaldebtequity.lasttwelvemonths",
|
||||
"ltdebtequity.lasttwelvemonths",
|
||||
"ebitinterestexpense.lasttwelvemonths",
|
||||
"ebitdainterestexpense.lasttwelvemonths",
|
||||
"lastclosetevebitda.lasttwelvemonths",
|
||||
"totaldebtebitda.lasttwelvemonths"},
|
||||
"liquidity":{
|
||||
"quickratio.lasttwelvemonths",
|
||||
"altmanzscoreusingtheaveragestockinformationforaperiod.lasttwelvemonths",
|
||||
"currentratio.lasttwelvemonths",
|
||||
"operatingcashflowtocurrentliabilities.lasttwelvemonths"},
|
||||
"income_statement":{
|
||||
"totalrevenues.lasttwelvemonths",
|
||||
"netincomemargin.lasttwelvemonths",
|
||||
"grossprofit.lasttwelvemonths",
|
||||
"ebitda1yrgrowth.lasttwelvemonths",
|
||||
"dilutedepscontinuingoperations.lasttwelvemonths",
|
||||
"quarterlyrevenuegrowth.quarterly",
|
||||
"epsgrowth.lasttwelvemonths",
|
||||
"netincomeis.lasttwelvemonths",
|
||||
"ebitda.lasttwelvemonths",
|
||||
"dilutedeps1yrgrowth.lasttwelvemonths",
|
||||
"totalrevenues1yrgrowth.lasttwelvemonths",
|
||||
"operatingincome.lasttwelvemonths",
|
||||
"netincome1yrgrowth.lasttwelvemonths",
|
||||
"grossprofitmargin.lasttwelvemonths",
|
||||
"ebitdamargin.lasttwelvemonths",
|
||||
"ebit.lasttwelvemonths",
|
||||
"basicepscontinuingoperations.lasttwelvemonths",
|
||||
"netepsbasic.lasttwelvemonths"
|
||||
"netepsdiluted.lasttwelvemonths"},
|
||||
"balance_sheet":{
|
||||
"totalassets.lasttwelvemonths",
|
||||
"totalcommonsharesoutstanding.lasttwelvemonths",
|
||||
"totaldebt.lasttwelvemonths",
|
||||
"totalequity.lasttwelvemonths",
|
||||
"totalcurrentassets.lasttwelvemonths",
|
||||
"totalcashandshortterminvestments.lasttwelvemonths",
|
||||
"totalcommonequity.lasttwelvemonths",
|
||||
"totalcurrentliabilities.lasttwelvemonths",
|
||||
"totalsharesoutstanding"},
|
||||
"cash_flow":{
|
||||
"forward_dividend_yield",
|
||||
"leveredfreecashflow.lasttwelvemonths",
|
||||
"capitalexpenditure.lasttwelvemonths",
|
||||
"cashfromoperations.lasttwelvemonths",
|
||||
"leveredfreecashflow1yrgrowth.lasttwelvemonths",
|
||||
"unleveredfreecashflow.lasttwelvemonths",
|
||||
"cashfromoperations1yrgrowth.lasttwelvemonths"},
|
||||
"esg":{
|
||||
"esg_score",
|
||||
"environmental_score",
|
||||
"governance_score",
|
||||
"social_score",
|
||||
"highest_controversy"}
|
||||
}
|
||||
|
||||
PREDEFINED_SCREENER_BODY_MAP = {
|
||||
|
||||
+112
-12
@@ -1,14 +1,27 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from ..ticker import Ticker
|
||||
from ..const import _QUERY1_URL_
|
||||
from ..data import YfData
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
import pandas as _pd
|
||||
|
||||
_QUERY_URL_ = f'{_QUERY1_URL_}/v1/finance'
|
||||
|
||||
class Domain:
|
||||
class Domain(ABC):
|
||||
"""
|
||||
Abstract base class representing a domain entity in financial data, with key attributes
|
||||
and methods for fetching and parsing data. Derived classes must implement the `_fetch_and_parse()` method.
|
||||
"""
|
||||
|
||||
def __init__(self, key: str, session=None, proxy=None):
|
||||
"""
|
||||
Initializes the Domain object with a key, session, and proxy.
|
||||
|
||||
Args:
|
||||
key (str): Unique key identifying the domain entity.
|
||||
session (Optional[requests.Session]): Session object for HTTP requests. Defaults to None.
|
||||
proxy (Optional[Dict]): Proxy settings. Defaults to None.
|
||||
"""
|
||||
self._key: str = key
|
||||
self.proxy = proxy
|
||||
self.session = session
|
||||
@@ -19,47 +32,105 @@ class Domain:
|
||||
self._overview: Optional[Dict] = None
|
||||
self._top_companies: Optional[_pd.DataFrame] = None
|
||||
self._research_reports: Optional[List[Dict[str, str]]] = None
|
||||
|
||||
|
||||
@property
|
||||
def key(self) -> str:
|
||||
"""
|
||||
Retrieves the key of the domain entity.
|
||||
|
||||
Returns:
|
||||
str: The unique key of the domain entity.
|
||||
"""
|
||||
return self._key
|
||||
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
"""
|
||||
Retrieves the name of the domain entity.
|
||||
|
||||
Returns:
|
||||
str: The name of the domain entity.
|
||||
"""
|
||||
self._ensure_fetched(self._name)
|
||||
return self._name
|
||||
|
||||
|
||||
@property
|
||||
def symbol(self) -> str:
|
||||
"""
|
||||
Retrieves the symbol of the domain entity.
|
||||
|
||||
Returns:
|
||||
str: The symbol representing the domain entity.
|
||||
"""
|
||||
self._ensure_fetched(self._symbol)
|
||||
return self._symbol
|
||||
|
||||
|
||||
@property
|
||||
def ticker(self) -> Ticker:
|
||||
"""
|
||||
Retrieves a Ticker object based on the domain entity's symbol.
|
||||
|
||||
Returns:
|
||||
Ticker: A Ticker object associated with the domain entity.
|
||||
"""
|
||||
self._ensure_fetched(self._symbol)
|
||||
return Ticker(self._symbol)
|
||||
|
||||
|
||||
@property
|
||||
def overview(self) -> Dict:
|
||||
"""
|
||||
Retrieves the overview information of the domain entity.
|
||||
|
||||
Returns:
|
||||
Dict: A dictionary containing an overview of the domain entity.
|
||||
"""
|
||||
self._ensure_fetched(self._overview)
|
||||
return self._overview
|
||||
|
||||
|
||||
@property
|
||||
def top_companies(self) -> Optional[_pd.DataFrame]:
|
||||
"""
|
||||
Retrieves the top companies within the domain entity.
|
||||
|
||||
Returns:
|
||||
pandas.DataFrame: A DataFrame containing the top companies in the domain.
|
||||
"""
|
||||
self._ensure_fetched(self._top_companies)
|
||||
return self._top_companies
|
||||
|
||||
|
||||
@property
|
||||
def research_reports(self) -> List[Dict[str, str]]:
|
||||
"""
|
||||
Retrieves research reports related to the domain entity.
|
||||
|
||||
Returns:
|
||||
List[Dict[str, str]]: A list of research reports, where each report is a dictionary with metadata.
|
||||
"""
|
||||
self._ensure_fetched(self._research_reports)
|
||||
return self._research_reports
|
||||
|
||||
|
||||
def _fetch(self, query_url, proxy) -> Dict:
|
||||
"""
|
||||
Fetches data from the given query URL.
|
||||
|
||||
Args:
|
||||
query_url (str): The URL used for the data query.
|
||||
proxy (Dict): Proxy settings for the request.
|
||||
|
||||
Returns:
|
||||
Dict: The JSON response data from the request.
|
||||
"""
|
||||
params_dict = {"formatted": "true", "withReturns": "true", "lang": "en-US", "region": "US"}
|
||||
result = self._data.get_raw_json(query_url, user_agent_headers=self._data.user_agent_headers, params=params_dict, proxy=proxy)
|
||||
return result
|
||||
|
||||
|
||||
def _parse_and_assign_common(self, data) -> None:
|
||||
"""
|
||||
Parses and assigns common data fields such as name, symbol, overview, and top companies.
|
||||
|
||||
Args:
|
||||
data (Dict): The raw data received from the API.
|
||||
"""
|
||||
self._name = data.get('name')
|
||||
self._symbol = data.get('symbol')
|
||||
self._overview = self._parse_overview(data.get('overview', {}))
|
||||
@@ -67,6 +138,15 @@ class Domain:
|
||||
self._research_reports = data.get('researchReports')
|
||||
|
||||
def _parse_overview(self, overview) -> Dict:
|
||||
"""
|
||||
Parses the overview data for the domain entity.
|
||||
|
||||
Args:
|
||||
overview (Dict): The raw overview data.
|
||||
|
||||
Returns:
|
||||
Dict: A dictionary containing parsed overview information.
|
||||
"""
|
||||
return {
|
||||
"companies_count": overview.get('companiesCount', None),
|
||||
"market_cap": overview.get('marketCap', {}).get('raw', None),
|
||||
@@ -78,6 +158,15 @@ class Domain:
|
||||
}
|
||||
|
||||
def _parse_top_companies(self, top_companies) -> Optional[_pd.DataFrame]:
|
||||
"""
|
||||
Parses the top companies data and converts it into a pandas DataFrame.
|
||||
|
||||
Args:
|
||||
top_companies (Dict): The raw top companies data.
|
||||
|
||||
Returns:
|
||||
Optional[pandas.DataFrame]: A DataFrame containing top company data, or None if no data is available.
|
||||
"""
|
||||
top_companies_column = ['symbol', 'name', 'rating', 'market weight']
|
||||
top_companies_values = [(c.get('symbol'),
|
||||
c.get('name'),
|
||||
@@ -87,11 +176,22 @@ class Domain:
|
||||
if not top_companies_values:
|
||||
return None
|
||||
|
||||
return _pd.DataFrame(top_companies_values, columns = top_companies_column).set_index('symbol')
|
||||
return _pd.DataFrame(top_companies_values, columns=top_companies_column).set_index('symbol')
|
||||
|
||||
@abstractmethod
|
||||
def _fetch_and_parse(self) -> None:
|
||||
"""
|
||||
Abstract method for fetching and parsing domain-specific data.
|
||||
Must be implemented by derived classes.
|
||||
"""
|
||||
raise NotImplementedError("_fetch_and_parse() needs to be implemented by children classes")
|
||||
|
||||
def _ensure_fetched(self, attribute) -> None:
|
||||
"""
|
||||
Ensures that the given attribute is fetched by calling `_fetch_and_parse()` if the attribute is None.
|
||||
|
||||
Args:
|
||||
attribute: The attribute to check and potentially fetch.
|
||||
"""
|
||||
if attribute is None:
|
||||
self._fetch_and_parse()
|
||||
@@ -7,7 +7,17 @@ from .domain import Domain, _QUERY_URL_
|
||||
from .. import utils
|
||||
|
||||
class Industry(Domain):
|
||||
"""
|
||||
Represents an industry within a sector.
|
||||
"""
|
||||
|
||||
def __init__(self, key, session=None, proxy=None):
|
||||
"""
|
||||
Args:
|
||||
key (str): The key identifier for the industry.
|
||||
session (optional): The session to use for requests.
|
||||
proxy (optional): The proxy to use for requests.
|
||||
"""
|
||||
super(Industry, self).__init__(key, session, proxy)
|
||||
self._query_url = f'{_QUERY_URL_}/industries/{self._key}'
|
||||
|
||||
@@ -17,29 +27,68 @@ class Industry(Domain):
|
||||
self._top_growth_companies = None
|
||||
|
||||
def __repr__(self):
|
||||
"""
|
||||
Returns a string representation of the Industry instance.
|
||||
|
||||
Returns:
|
||||
str: String representation of the Industry instance.
|
||||
"""
|
||||
return f'yfinance.Industry object <{self._key}>'
|
||||
|
||||
@property
|
||||
def sector_key(self) -> str:
|
||||
"""
|
||||
Returns the sector key of the industry.
|
||||
|
||||
Returns:
|
||||
str: The sector key.
|
||||
"""
|
||||
self._ensure_fetched(self._sector_key)
|
||||
return self._sector_key
|
||||
|
||||
@property
|
||||
def sector_name(self) -> str:
|
||||
"""
|
||||
Returns the sector name of the industry.
|
||||
|
||||
Returns:
|
||||
str: The sector name.
|
||||
"""
|
||||
self._ensure_fetched(self._sector_name)
|
||||
return self._sector_name
|
||||
|
||||
@property
|
||||
def top_performing_companies(self) -> Optional[_pd.DataFrame]:
|
||||
"""
|
||||
Returns the top performing companies in the industry.
|
||||
|
||||
Returns:
|
||||
Optional[pd.DataFrame]: DataFrame containing top performing companies.
|
||||
"""
|
||||
self._ensure_fetched(self._top_performing_companies)
|
||||
return self._top_performing_companies
|
||||
|
||||
@property
|
||||
def top_growth_companies(self) -> Optional[_pd.DataFrame]:
|
||||
"""
|
||||
Returns the top growth companies in the industry.
|
||||
|
||||
Returns:
|
||||
Optional[pd.DataFrame]: DataFrame containing top growth companies.
|
||||
"""
|
||||
self._ensure_fetched(self._top_growth_companies)
|
||||
return self._top_growth_companies
|
||||
|
||||
def _parse_top_performing_companies(self, top_performing_companies: Dict) -> Optional[_pd.DataFrame]:
|
||||
"""
|
||||
Parses the top performing companies data.
|
||||
|
||||
Args:
|
||||
top_performing_companies (Dict): Dictionary containing top performing companies data.
|
||||
|
||||
Returns:
|
||||
Optional[pd.DataFrame]: DataFrame containing parsed top performing companies data.
|
||||
"""
|
||||
compnaies_column = ['symbol','name','ytd return',' last price','target price']
|
||||
compnaies_values = [(c.get('symbol', None),
|
||||
c.get('name', None),
|
||||
@@ -53,6 +102,15 @@ class Industry(Domain):
|
||||
return _pd.DataFrame(compnaies_values, columns = compnaies_column).set_index('symbol')
|
||||
|
||||
def _parse_top_growth_companies(self, top_growth_companies: Dict) -> Optional[_pd.DataFrame]:
|
||||
"""
|
||||
Parses the top growth companies data.
|
||||
|
||||
Args:
|
||||
top_growth_companies (Dict): Dictionary containing top growth companies data.
|
||||
|
||||
Returns:
|
||||
Optional[pd.DataFrame]: DataFrame containing parsed top growth companies data.
|
||||
"""
|
||||
compnaies_column = ['symbol','name','ytd return',' growth estimate']
|
||||
compnaies_values = [(c.get('symbol', None),
|
||||
c.get('name', None),
|
||||
@@ -65,6 +123,9 @@ class Industry(Domain):
|
||||
return _pd.DataFrame(compnaies_values, columns = compnaies_column).set_index('symbol')
|
||||
|
||||
def _fetch_and_parse(self) -> None:
|
||||
"""
|
||||
Fetches and parses the industry data.
|
||||
"""
|
||||
result = None
|
||||
|
||||
try:
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
from __future__ import print_function
|
||||
from typing import Dict, Optional
|
||||
from ..utils import dynamic_docstring, generate_list_table_from_dict
|
||||
from ..const import SECTOR_INDUSTY_MAPPING
|
||||
|
||||
import pandas as _pd
|
||||
|
||||
@@ -7,48 +9,127 @@ from .domain import Domain, _QUERY_URL_
|
||||
from .. import utils
|
||||
|
||||
class Sector(Domain):
|
||||
"""
|
||||
Represents a financial market sector and allows retrieval of sector-related data
|
||||
such as top ETFs, top mutual funds, and industry data.
|
||||
"""
|
||||
|
||||
def __init__(self, key, session=None, proxy=None):
|
||||
"""
|
||||
Args:
|
||||
key (str): The key representing the sector.
|
||||
session (requests.Session, optional): A session for making requests. Defaults to None.
|
||||
proxy (dict, optional): A dictionary containing proxy settings for the request. Defaults to None.
|
||||
|
||||
.. seealso::
|
||||
|
||||
:attr:`Sector.industries <yfinance.Sector.industries>`
|
||||
Map of sector and industry
|
||||
"""
|
||||
super(Sector, self).__init__(key, session, proxy)
|
||||
self._query_url: str = f'{_QUERY_URL_}/sectors/{self._key}'
|
||||
|
||||
self._top_etfs: Optional[Dict] = None
|
||||
self._top_mutual_funds: Optional[Dict] = None
|
||||
self._industries: Optional[_pd.DataFrame] = None
|
||||
|
||||
def __repr__(self):
|
||||
"""
|
||||
Returns the string representation of the Sector object.
|
||||
|
||||
Returns:
|
||||
str: A string representation of the object.
|
||||
"""
|
||||
return f'yfinance.Sector object <{self._key}>'
|
||||
|
||||
@property
|
||||
def top_etfs(self) -> Dict[str, str]:
|
||||
"""
|
||||
Gets the top ETFs for the sector.
|
||||
|
||||
Returns:
|
||||
Dict[str, str]: A dictionary of ETF symbols and names.
|
||||
"""
|
||||
self._ensure_fetched(self._top_etfs)
|
||||
return self._top_etfs
|
||||
|
||||
@property
|
||||
def top_mutual_funds(self) -> Dict[str, str]:
|
||||
"""
|
||||
Gets the top mutual funds for the sector.
|
||||
|
||||
Returns:
|
||||
Dict[str, str]: A dictionary of mutual fund symbols and names.
|
||||
"""
|
||||
self._ensure_fetched(self._top_mutual_funds)
|
||||
return self._top_mutual_funds
|
||||
|
||||
@dynamic_docstring({"sector_industry": generate_list_table_from_dict(SECTOR_INDUSTY_MAPPING,bullets=True)})
|
||||
@property
|
||||
def industries(self) -> _pd.DataFrame:
|
||||
"""
|
||||
Gets the industries within the sector.
|
||||
|
||||
Returns:
|
||||
pandas.DataFrame: A DataFrame with industries' key, name, symbol, and market weight.
|
||||
|
||||
{sector_industry}
|
||||
"""
|
||||
self._ensure_fetched(self._industries)
|
||||
return self._industries
|
||||
|
||||
def _parse_top_etfs(self, top_etfs: Dict) -> Dict[str, str]:
|
||||
"""
|
||||
Parses top ETF data from the API response.
|
||||
|
||||
Args:
|
||||
top_etfs (Dict): The raw ETF data from the API response.
|
||||
|
||||
Returns:
|
||||
Dict[str, str]: A dictionary of ETF symbols and names.
|
||||
"""
|
||||
return {e.get('symbol'): e.get('name') for e in top_etfs}
|
||||
|
||||
def _parse_top_mutual_funds(self, top_mutual_funds: Dict) -> Dict[str, str]:
|
||||
"""
|
||||
Parses top mutual funds data from the API response.
|
||||
|
||||
Args:
|
||||
top_mutual_funds (Dict): The raw mutual fund data from the API response.
|
||||
|
||||
Returns:
|
||||
Dict[str, str]: A dictionary of mutual fund symbols and names.
|
||||
"""
|
||||
return {e.get('symbol'): e.get('name') for e in top_mutual_funds}
|
||||
|
||||
def _parse_industries(self, industries: Dict) -> _pd.DataFrame:
|
||||
"""
|
||||
Parses industry data from the API response into a DataFrame.
|
||||
|
||||
Args:
|
||||
industries (Dict): The raw industry data from the API response.
|
||||
|
||||
Returns:
|
||||
pandas.DataFrame: A DataFrame containing industry key, name, symbol, and market weight.
|
||||
"""
|
||||
industries_column = ['key','name','symbol','market weight']
|
||||
industries_values = [(i.get('key'),
|
||||
i.get('name'),
|
||||
i.get('symbol'),
|
||||
i.get('marketWeight',{}).get('raw', None)
|
||||
) for i in industries if i.get('name') != 'All Industries']
|
||||
return _pd.DataFrame(industries_values, columns = industries_column).set_index('key')
|
||||
return _pd.DataFrame(industries_values, columns=industries_column).set_index('key')
|
||||
|
||||
def _fetch_and_parse(self) -> None:
|
||||
"""
|
||||
Fetches and parses sector data from the API.
|
||||
|
||||
Fetches data for the sector and parses the top ETFs, top mutual funds,
|
||||
and industries within the sector. Stores the parsed data in the corresponding
|
||||
attributes `_top_etfs`, `_top_mutual_funds`, and `_industries`.
|
||||
|
||||
Raises:
|
||||
Exception: If fetching or parsing the sector data fails.
|
||||
"""
|
||||
result = None
|
||||
|
||||
try:
|
||||
|
||||
+36
-50
@@ -24,6 +24,7 @@ from __future__ import print_function
|
||||
import logging
|
||||
import time as _time
|
||||
import traceback
|
||||
from typing import Union
|
||||
|
||||
import multitasking as _multitasking
|
||||
import pandas as _pd
|
||||
@@ -38,56 +39,41 @@ def download(tickers, start=None, end=None, actions=False, threads=True,
|
||||
ignore_tz=None, group_by='column', auto_adjust=False, back_adjust=False,
|
||||
repair=False, keepna=False, progress=True, period="max", interval="1d",
|
||||
prepost=False, proxy=None, rounding=False, timeout=10, session=None,
|
||||
multi_level_index=True):
|
||||
"""Download yahoo tickers
|
||||
:Parameters:
|
||||
tickers : str, list
|
||||
List of tickers to download
|
||||
period : str
|
||||
Valid periods: 1d,5d,1mo,3mo,6mo,1y,2y,5y,10y,ytd,max
|
||||
Either Use period parameter or use start and end
|
||||
interval : str
|
||||
Valid intervals: 1m,2m,5m,15m,30m,60m,90m,1h,1d,5d,1wk,1mo,3mo
|
||||
Intraday data cannot extend last 60 days
|
||||
start: str
|
||||
Download start date string (YYYY-MM-DD) or _datetime, inclusive.
|
||||
Default is 99 years ago
|
||||
E.g. for start="2020-01-01", the first data point will be on "2020-01-01"
|
||||
end: str
|
||||
Download end date string (YYYY-MM-DD) or _datetime, exclusive.
|
||||
Default is now
|
||||
E.g. for end="2023-01-01", the last data point will be on "2022-12-31"
|
||||
group_by : str
|
||||
Group by 'ticker' or 'column' (default)
|
||||
prepost : bool
|
||||
Include Pre and Post market data in results?
|
||||
Default is False
|
||||
auto_adjust: bool
|
||||
Adjust all OHLC automatically? Default is False
|
||||
repair: bool
|
||||
Detect currency unit 100x mixups and attempt repair
|
||||
Default is False
|
||||
keepna: bool
|
||||
Keep NaN rows returned by Yahoo?
|
||||
Default is False
|
||||
actions: bool
|
||||
Download dividend + stock splits data. Default is False
|
||||
threads: bool / int
|
||||
How many threads to use for mass downloading. Default is True
|
||||
ignore_tz: bool
|
||||
When combining from different timezones, ignore that part of datetime.
|
||||
Default depends on interval. Intraday = False. Day+ = True.
|
||||
proxy: str
|
||||
Optional. Proxy server URL scheme. Default is None
|
||||
rounding: bool
|
||||
Optional. Round values to 2 decimal places?
|
||||
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)
|
||||
session: None or Session
|
||||
Optional. Pass your own session object to be used for all requests
|
||||
multi_level_index: bool
|
||||
Optional. Always return a MultiIndex DataFrame? Default is False
|
||||
multi_level_index=True) -> Union[_pd.DataFrame, None]:
|
||||
"""
|
||||
Download yahoo tickers
|
||||
|
||||
Args:
|
||||
tickers (str or list): List of tickers to download.
|
||||
period (str): Time period to download.
|
||||
Valid periods are: '1d', '5d', '1mo', '3mo', '6mo', '1y', '2y', '5y', '10y', 'ytd', 'max'.
|
||||
Either use `period` or specify `start` and `end`.
|
||||
interval (str): Data interval.
|
||||
Valid intervals are: '1m', '2m', '5m', '15m', '30m', '60m', '90m', '1h', '1d', '5d', '1wk', '1mo', '3mo'.
|
||||
Intraday data is limited to the last 60 days.
|
||||
start (str): Start date (YYYY-MM-DD) or _datetime, inclusive.
|
||||
Default is 99 years ago.
|
||||
Example: For `start="2020-01-01"`, the first data point will be "2020-01-01".
|
||||
end (str): End date (YYYY-MM-DD) or _datetime, exclusive.
|
||||
Default is the current date.
|
||||
Example: For `end="2023-01-01"`, the last data point will be "2022-12-31".
|
||||
group_by (str): Group data by 'ticker' or 'column'. Default is 'column'.
|
||||
prepost (bool): Include pre and post market data in results? Default is False.
|
||||
auto_adjust (bool): Automatically adjust all OHLC data? Default is False.
|
||||
repair (bool): Detect and repair currency unit mixups (e.g., 100x errors)? Default is False.
|
||||
keepna (bool): Keep rows with NaN values returned by Yahoo? Default is False.
|
||||
actions (bool): Download dividend and stock split data? Default is False.
|
||||
threads (bool or int): Number of threads for mass downloading. Default is True (automatically determines the number of threads).
|
||||
ignore_tz (bool): Ignore timezones when combining data across timezones?
|
||||
Default depends on the interval. For intraday intervals, the default is False. For daily and above, the default is True.
|
||||
proxy (str, optional): URL of the proxy server. Default is None.
|
||||
rounding (bool, optional): Round values to two decimal places? Default is False.
|
||||
timeout (None or float, optional): Maximum time to wait for a response, in seconds. Can be a fraction of a second (e.g., 0.01). Default is None.
|
||||
session (None or Session, optional): Pass a custom session object for all requests. Default is None.
|
||||
multi_level_index (bool): Optional. Always return a MultiIndex DataFrame? Default is False
|
||||
|
||||
Returns:
|
||||
pd.DataFrame or None
|
||||
"""
|
||||
logger = utils.get_yf_logger()
|
||||
|
||||
|
||||
+114
-8
@@ -9,15 +9,21 @@ from typing import Dict, Optional
|
||||
|
||||
_QUOTE_SUMMARY_URL_ = f"{_BASE_URL_}/v10/finance/quoteSummary/"
|
||||
|
||||
'''
|
||||
Supports ETF and Mutual Funds Data
|
||||
Queried Modules: quoteType, summaryProfile, fundProfile, topHoldings
|
||||
|
||||
Notes:
|
||||
- fundPerformance module is not implemented as better data is queryable using history
|
||||
'''
|
||||
class FundsData:
|
||||
"""
|
||||
ETF and Mutual Funds Data
|
||||
Queried Modules: quoteType, summaryProfile, fundProfile, topHoldings
|
||||
|
||||
Notes:
|
||||
- fundPerformance module is not implemented as better data is queryable using history
|
||||
"""
|
||||
def __init__(self, data: YfData, symbol: str, proxy=None):
|
||||
"""
|
||||
Args:
|
||||
data (YfData): The YfData object for fetching data.
|
||||
symbol (str): The symbol of the fund.
|
||||
proxy (optional): Proxy settings for fetching data.
|
||||
"""
|
||||
self._data = data
|
||||
self._symbol = symbol
|
||||
self.proxy = proxy
|
||||
@@ -41,71 +47,143 @@ class FundsData:
|
||||
self._sector_weightings = None
|
||||
|
||||
def quote_type(self) -> str:
|
||||
"""
|
||||
Returns the quote type of the fund.
|
||||
|
||||
Returns:
|
||||
str: The quote type.
|
||||
"""
|
||||
if self._quote_type is None:
|
||||
self._fetch_and_parse()
|
||||
return self._quote_type
|
||||
|
||||
@property
|
||||
def description(self) -> str:
|
||||
"""
|
||||
Returns the description of the fund.
|
||||
|
||||
Returns:
|
||||
str: The description.
|
||||
"""
|
||||
if self._description is None:
|
||||
self._fetch_and_parse()
|
||||
return self._description
|
||||
|
||||
@property
|
||||
def fund_overview(self) -> Dict[str, Optional[str]]:
|
||||
"""
|
||||
Returns the fund overview.
|
||||
|
||||
Returns:
|
||||
Dict[str, Optional[str]]: The fund overview.
|
||||
"""
|
||||
if self._fund_overview is None:
|
||||
self._fetch_and_parse()
|
||||
return self._fund_overview
|
||||
|
||||
@property
|
||||
def fund_operations(self) -> pd.DataFrame:
|
||||
"""
|
||||
Returns the fund operations.
|
||||
|
||||
Returns:
|
||||
pd.DataFrame: The fund operations.
|
||||
"""
|
||||
if self._fund_operations is None:
|
||||
self._fetch_and_parse()
|
||||
return self._fund_operations
|
||||
|
||||
@property
|
||||
def asset_classes(self) -> Dict[str, float]:
|
||||
"""
|
||||
Returns the asset classes of the fund.
|
||||
|
||||
Returns:
|
||||
Dict[str, float]: The asset classes.
|
||||
"""
|
||||
if self._asset_classes is None:
|
||||
self._fetch_and_parse()
|
||||
return self._asset_classes
|
||||
|
||||
@property
|
||||
def top_holdings(self) -> pd.DataFrame:
|
||||
"""
|
||||
Returns the top holdings of the fund.
|
||||
|
||||
Returns:
|
||||
pd.DataFrame: The top holdings.
|
||||
"""
|
||||
if self._top_holdings is None:
|
||||
self._fetch_and_parse()
|
||||
return self._top_holdings
|
||||
|
||||
@property
|
||||
def equity_holdings(self) -> pd.DataFrame:
|
||||
"""
|
||||
Returns the equity holdings of the fund.
|
||||
|
||||
Returns:
|
||||
pd.DataFrame: The equity holdings.
|
||||
"""
|
||||
if self._equity_holdings is None:
|
||||
self._fetch_and_parse()
|
||||
return self._equity_holdings
|
||||
|
||||
@property
|
||||
def bond_holdings(self) -> pd.DataFrame:
|
||||
"""
|
||||
Returns the bond holdings of the fund.
|
||||
|
||||
Returns:
|
||||
pd.DataFrame: The bond holdings.
|
||||
"""
|
||||
if self._bond_holdings is None:
|
||||
self._fetch_and_parse()
|
||||
return self._bond_holdings
|
||||
|
||||
@property
|
||||
def bond_ratings(self) -> Dict[str, float]:
|
||||
"""
|
||||
Returns the bond ratings of the fund.
|
||||
|
||||
Returns:
|
||||
Dict[str, float]: The bond ratings.
|
||||
"""
|
||||
if self._bond_ratings is None:
|
||||
self._fetch_and_parse()
|
||||
return self._bond_ratings
|
||||
|
||||
@property
|
||||
def sector_weightings(self) -> Dict[str,float]:
|
||||
"""
|
||||
Returns the sector weightings of the fund.
|
||||
|
||||
Returns:
|
||||
Dict[str, float]: The sector weightings.
|
||||
"""
|
||||
if self._sector_weightings is None:
|
||||
self._fetch_and_parse()
|
||||
return self._sector_weightings
|
||||
|
||||
def _fetch(self, proxy):
|
||||
"""
|
||||
Fetches the raw JSON data from the API.
|
||||
|
||||
Args:
|
||||
proxy: Proxy settings for fetching data.
|
||||
|
||||
Returns:
|
||||
dict: The raw JSON data.
|
||||
"""
|
||||
modules = ','.join(["quoteType", "summaryProfile", "topHoldings", "fundProfile"])
|
||||
params_dict = {"modules": modules, "corsDomain": "finance.yahoo.com", "symbol": self._symbol, "formatted": "false"}
|
||||
result = self._data.get_raw_json(_QUOTE_SUMMARY_URL_+self._symbol, user_agent_headers=self._data.user_agent_headers, params=params_dict, proxy=proxy)
|
||||
return result
|
||||
|
||||
def _fetch_and_parse(self) -> None:
|
||||
"""
|
||||
Fetches and parses the data from the API.
|
||||
"""
|
||||
result = self._fetch(self.proxy)
|
||||
try:
|
||||
data = result["quoteSummary"]["result"][0]
|
||||
@@ -128,15 +206,37 @@ class FundsData:
|
||||
|
||||
@staticmethod
|
||||
def _parse_raw_values(data, default=None):
|
||||
"""
|
||||
Parses raw values from the data.
|
||||
|
||||
Args:
|
||||
data: The data to parse.
|
||||
default: The default value if data is not a dictionary.
|
||||
|
||||
Returns:
|
||||
The parsed value or the default value.
|
||||
"""
|
||||
if not isinstance(data, dict):
|
||||
return data
|
||||
|
||||
return data.get("raw", default)
|
||||
|
||||
def _parse_description(self, data) -> None:
|
||||
"""
|
||||
Parses the description from the data.
|
||||
|
||||
Args:
|
||||
data: The data to parse.
|
||||
"""
|
||||
self._description = data.get("longBusinessSummary", "")
|
||||
|
||||
def _parse_top_holdings(self, data) -> None: # done
|
||||
def _parse_top_holdings(self, data) -> None:
|
||||
"""
|
||||
Parses the top holdings from the data.
|
||||
|
||||
Args:
|
||||
data: The data to parse.
|
||||
"""
|
||||
# asset classes
|
||||
self._asset_classes = {
|
||||
"cashPosition": self._parse_raw_values(data.get("cashPosition", None)),
|
||||
@@ -207,6 +307,12 @@ class FundsData:
|
||||
self._sector_weightings = dict((key, d[key]) for d in data.get("sectorWeightings", []) for key in d)
|
||||
|
||||
def _parse_fund_profile(self, data):
|
||||
"""
|
||||
Parses the fund profile from the data.
|
||||
|
||||
Args:
|
||||
data: The data to parse.
|
||||
"""
|
||||
self._fund_overview = {
|
||||
"categoryName": data.get("categoryName", None),
|
||||
"family": data.get("family", None),
|
||||
|
||||
@@ -4,11 +4,28 @@ from yfinance import utils
|
||||
from yfinance.data import YfData
|
||||
from yfinance.const import _BASE_URL_, PREDEFINED_SCREENER_BODY_MAP
|
||||
from .screener_query import Query
|
||||
from ..utils import dynamic_docstring, generate_list_table_from_dict_of_dict
|
||||
|
||||
_SCREENER_URL_ = f"{_BASE_URL_}/v1/finance/screener"
|
||||
|
||||
class Screener:
|
||||
"""
|
||||
The `Screener` class is used to execute the queries and return the filtered results.
|
||||
|
||||
The Screener class provides methods to set and manipulate the body of a screener request,
|
||||
fetch and parse the screener results, and access predefined screener bodies.
|
||||
"""
|
||||
def __init__(self, session=None, proxy=None):
|
||||
"""
|
||||
Args:
|
||||
session (requests.Session, optional): A requests session object to be used for making HTTP requests. Defaults to None.
|
||||
proxy (str, optional): A proxy URL to be used for making HTTP requests. Defaults to None.
|
||||
|
||||
.. seealso::
|
||||
|
||||
:attr:`Screener.predefined_bodies <yfinance.Screener.predefined_bodies>`
|
||||
supported predefined screens
|
||||
"""
|
||||
self.proxy = proxy
|
||||
self.session = session
|
||||
|
||||
@@ -25,17 +42,41 @@ class Screener:
|
||||
|
||||
@property
|
||||
def response(self) -> Dict:
|
||||
"""
|
||||
Fetch screen result
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
result = screener.response
|
||||
symbols = [quote['symbol'] for quote in result['quotes']]
|
||||
"""
|
||||
if self._body_updated or self._response is None:
|
||||
self._fetch_and_parse()
|
||||
|
||||
self._body_updated = False
|
||||
return self._response
|
||||
|
||||
@dynamic_docstring({"predefined_screeners": generate_list_table_from_dict_of_dict(PREDEFINED_SCREENER_BODY_MAP,bullets=False)})
|
||||
@property
|
||||
def predefined_bodies(self) -> Dict:
|
||||
"""
|
||||
Predefined Screeners
|
||||
{predefined_screeners}
|
||||
"""
|
||||
return self._predefined_bodies
|
||||
|
||||
def set_default_body(self, query: Query, offset: int = 0, size: int = 100, sortField: str = "ticker", sortType: str = "desc", quoteType: str = "equity", userId: str = "", userIdType: str = "guid") -> None:
|
||||
"""
|
||||
Set the default body using a custom query
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
screener.set_default_body(qf)
|
||||
"""
|
||||
self._body_updated = True
|
||||
|
||||
self._body = {
|
||||
@@ -50,6 +91,21 @@ class Screener:
|
||||
}
|
||||
|
||||
def set_predefined_body(self, k: str) -> None:
|
||||
"""
|
||||
Set a predefined body
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
screener.set_predefined_body('day_gainers')
|
||||
|
||||
|
||||
.. seealso::
|
||||
|
||||
:attr:`Screener.predefined_bodies <yfinance.Screener.predefined_bodies>`
|
||||
supported predefined screens
|
||||
"""
|
||||
body = PREDEFINED_SCREENER_BODY_MAP.get(k, None)
|
||||
if not body:
|
||||
raise ValueError(f'Invalid key {k} provided for predefined screener')
|
||||
@@ -58,6 +114,24 @@ class Screener:
|
||||
self._body = body
|
||||
|
||||
def set_body(self, body: Dict) -> None:
|
||||
"""
|
||||
Set the fully custom body
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
screener.set_body({
|
||||
"offset": 0,
|
||||
"size": 100,
|
||||
"sortField": "ticker",
|
||||
"sortType": "desc",
|
||||
"quoteType": "equity",
|
||||
"query": qf.to_dict(),
|
||||
"userId": "",
|
||||
"userIdType": "guid"
|
||||
})
|
||||
"""
|
||||
missing_keys = [key for key in self._accepted_body_keys if key not in body]
|
||||
if missing_keys:
|
||||
raise ValueError(f"Missing required keys in body: {missing_keys}")
|
||||
@@ -71,6 +145,15 @@ class Screener:
|
||||
|
||||
|
||||
def patch_body(self, values: Dict) -> None:
|
||||
"""
|
||||
Patch parts of the body
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
screener.patch_body({"offset": 100})
|
||||
"""
|
||||
extra_keys = [key for key in values if key not in self._accepted_body_keys]
|
||||
if extra_keys:
|
||||
raise ValueError(f"Body contains extra keys: {extra_keys}")
|
||||
|
||||
@@ -1,19 +1,67 @@
|
||||
from abc import ABC, abstractmethod
|
||||
import numbers
|
||||
from typing import List, Union, Dict, Set
|
||||
from typing import List, Union, Dict
|
||||
|
||||
from yfinance.const import EQUITY_SCREENER_EQ_MAP, EQUITY_SCREENER_FIELDS
|
||||
from yfinance.exceptions import YFNotImplementedError
|
||||
from ..utils import dynamic_docstring, generate_list_table_from_dict
|
||||
|
||||
class Query:
|
||||
class Query(ABC):
|
||||
def __init__(self, operator: str, operand: Union[numbers.Real, str, List['Query']]):
|
||||
self.operator = operator
|
||||
self.operands = operand
|
||||
|
||||
|
||||
@abstractmethod
|
||||
def to_dict(self) -> Dict:
|
||||
raise YFNotImplementedError('to_dict() needs to be implemented by children classes')
|
||||
|
||||
class EquityQuery(Query):
|
||||
"""
|
||||
The `EquityQuery` class constructs filters for stocks based on specific criteria such as region, sector, exchange, and peer group.
|
||||
|
||||
The queries support operators: `GT` (greater than), `LT` (less than), `BTWN` (between), `EQ` (equals), and logical operators `AND` and `OR` for combining multiple conditions.
|
||||
|
||||
Example:
|
||||
Screen for stocks where the end-of-day price is greater than 3.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
gt = yf.EquityQuery('gt', ['eodprice', 3])
|
||||
|
||||
Screen for stocks where the average daily volume over the last 3 months is less than a very large number.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
lt = yf.EquityQuery('lt', ['avgdailyvol3m', 99999999999])
|
||||
|
||||
Screen for stocks where the intraday market cap is between 0 and 100 million.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
btwn = yf.EquityQuery('btwn', ['intradaymarketcap', 0, 100000000])
|
||||
|
||||
Screen for stocks in the Technology sector.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
eq = yf.EquityQuery('eq', ['sector', 'Technology'])
|
||||
|
||||
Combine queries using AND/OR.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
qt = yf.EquityQuery('and', [gt, lt])
|
||||
qf = yf.EquityQuery('or', [qt, btwn, eq])
|
||||
"""
|
||||
def __init__(self, operator: str, operand: Union[numbers.Real, str, List['EquityQuery']]):
|
||||
"""
|
||||
.. seealso::
|
||||
|
||||
:attr:`EquityQuery.valid_operand_fields <yfinance.EquityQuery.valid_operand_fields>`
|
||||
supported operand values for query
|
||||
:attr:`EquityQuery.valid_eq_operand_map <yfinance.EquityQuery.valid_eq_operand_map>`
|
||||
supported `EQ query operand parameters`
|
||||
"""
|
||||
operator = operator.upper()
|
||||
|
||||
if not isinstance(operand, list):
|
||||
@@ -34,16 +82,26 @@ class EquityQuery(Query):
|
||||
|
||||
self.operator = operator
|
||||
self.operands = operand
|
||||
self._valid_eq_map = EQUITY_SCREENER_EQ_MAP
|
||||
self._valid_fields = EQUITY_SCREENER_FIELDS
|
||||
|
||||
self._valid_eq_operand_map = EQUITY_SCREENER_EQ_MAP
|
||||
self._valid_operand_fields = EQUITY_SCREENER_FIELDS
|
||||
|
||||
@dynamic_docstring({"valid_eq_operand_map_table": generate_list_table_from_dict(EQUITY_SCREENER_EQ_MAP)})
|
||||
@property
|
||||
def valid_eq_map(self) -> Dict:
|
||||
return self._valid_eq_map
|
||||
def valid_eq_operand_map(self) -> Dict:
|
||||
"""
|
||||
Valid Operand Map for Operator "EQ"
|
||||
{valid_eq_operand_map_table}
|
||||
"""
|
||||
return self._valid_eq_operand_map
|
||||
|
||||
@dynamic_docstring({"valid_operand_fields_table": generate_list_table_from_dict(EQUITY_SCREENER_FIELDS)})
|
||||
@property
|
||||
def valid_fields(self) -> Set:
|
||||
return self._valid_fields
|
||||
def valid_operand_fields(self) -> Dict:
|
||||
"""
|
||||
Valid Operand Fields
|
||||
{valid_operand_fields_table}
|
||||
"""
|
||||
return self._valid_operand_fields
|
||||
|
||||
def _validate_or_and_operand(self, operand: List['EquityQuery']) -> None:
|
||||
if len(operand) <= 1:
|
||||
@@ -54,7 +112,8 @@ class EquityQuery(Query):
|
||||
def _validate_eq_operand(self, operand: List[Union[str, numbers.Real]]) -> None:
|
||||
if len(operand) != 2:
|
||||
raise ValueError('Operand must be length 2 for EQ')
|
||||
if operand[0] not in EQUITY_SCREENER_FIELDS:
|
||||
|
||||
if not any(operand[0] in fields_by_type for fields_by_type in EQUITY_SCREENER_FIELDS.values()):
|
||||
raise ValueError('Invalid field for Screener')
|
||||
if operand[0] not in EQUITY_SCREENER_EQ_MAP:
|
||||
raise ValueError('Invalid EQ key')
|
||||
@@ -64,7 +123,7 @@ class EquityQuery(Query):
|
||||
def _validate_btwn_operand(self, operand: List[Union[str, numbers.Real]]) -> None:
|
||||
if len(operand) != 3:
|
||||
raise ValueError('Operand must be length 3 for BTWN')
|
||||
if operand[0] not in EQUITY_SCREENER_FIELDS:
|
||||
if not any(operand[0] in fields_by_type for fields_by_type in EQUITY_SCREENER_FIELDS.values()):
|
||||
raise ValueError('Invalid field for Screener')
|
||||
if isinstance(operand[1], numbers.Real) is False:
|
||||
raise TypeError('Invalid comparison type for BTWN')
|
||||
@@ -74,7 +133,7 @@ class EquityQuery(Query):
|
||||
def _validate_gt_lt(self, operand: List[Union[str, numbers.Real]]) -> None:
|
||||
if len(operand) != 2:
|
||||
raise ValueError('Operand must be length 2 for GT/LT')
|
||||
if operand[0] not in EQUITY_SCREENER_FIELDS:
|
||||
if not any(operand[0] in fields_by_type for fields_by_type in EQUITY_SCREENER_FIELDS.values()):
|
||||
raise ValueError('Invalid field for Screener')
|
||||
if isinstance(operand[1], numbers.Real) is False:
|
||||
raise TypeError('Invalid comparison type for GT/LT')
|
||||
|
||||
@@ -932,3 +932,64 @@ class ProgressBar:
|
||||
def __str__(self):
|
||||
return str(self.prog_bar)
|
||||
|
||||
def dynamic_docstring(placeholders: dict):
|
||||
"""
|
||||
A decorator to dynamically update the docstring of a function or method.
|
||||
|
||||
Args:
|
||||
placeholders (dict): A dictionary where keys are placeholder names and values are the strings to insert.
|
||||
"""
|
||||
def decorator(func):
|
||||
if func.__doc__:
|
||||
docstring = func.__doc__
|
||||
# Replace each placeholder with its corresponding value
|
||||
for key, value in placeholders.items():
|
||||
docstring = docstring.replace(f"{{{key}}}", value)
|
||||
func.__doc__ = docstring
|
||||
return func
|
||||
return decorator
|
||||
|
||||
def _generate_table_configurations() -> str:
|
||||
import textwrap
|
||||
table = textwrap.dedent("""
|
||||
.. list-table:: Permitted Keys/Values
|
||||
:widths: 25 75
|
||||
:header-rows: 1
|
||||
|
||||
* - Key
|
||||
- Values
|
||||
""")
|
||||
|
||||
return table
|
||||
|
||||
def generate_list_table_from_dict(data: dict, bullets: bool=True) -> str:
|
||||
"""
|
||||
Generate a list-table for the docstring showing permitted keys/values.
|
||||
"""
|
||||
table = _generate_table_configurations()
|
||||
for key, values in data.items():
|
||||
value_str = ', '.join(sorted(values))
|
||||
table += f" * - {key}\n"
|
||||
if bullets:
|
||||
table += " -\n"
|
||||
for value in sorted(values):
|
||||
table += f" - {value}\n"
|
||||
else:
|
||||
table += f" - {value_str}\n"
|
||||
return table
|
||||
|
||||
def generate_list_table_from_dict_of_dict(data: dict, bullets: bool=True) -> str:
|
||||
"""
|
||||
Generate a list-table for the docstring showing permitted keys/values.
|
||||
"""
|
||||
table = _generate_table_configurations()
|
||||
for key, values in data.items():
|
||||
value_str = values
|
||||
table += f" * - {key}\n"
|
||||
if bullets:
|
||||
table += " -\n"
|
||||
for value in sorted(values):
|
||||
table += f" - {value}\n"
|
||||
else:
|
||||
table += f" - {value_str}\n"
|
||||
return table
|
||||
Reference in New Issue
Block a user