Remove pandas_ta.tar.gz from the repository

This commit is contained in:
Miha Kralj
2026-02-28 17:12:08 -08:00
parent 768123d056
commit 42390aa22e
229 changed files with 15 additions and 35501 deletions
-2
View File
@@ -1,2 +0,0 @@
The filename, directory name, or volume label syntax is incorrect.
Error occurred while processing: https://files.pythonhosted.org/packages/95/6d/60b88a0334a8c6a5be114ed2c46c8f3e164127d0eccd9ff99b50773f2b20/pandas_ta-0.4.71b0.tar.gz.
Vendored
BIN
View File
Binary file not shown.
+15 -1
View File
@@ -49,6 +49,9 @@ BenchmarkDotNet.Artifacts/
# Temporary files and agent workspace
temp/
temp_pandas_ta/
temp_decompile/
Invoke-WebRequest/
.temp/
*.tmp
*.temp
@@ -78,4 +81,15 @@ plans/
# SonarLint IDE plugin
.sonarlint/
.obsidian/
.obsidian/
# macOS
.DS_Store
**/.DS_Store
# Python cache
__pycache__/
.pytest_cache/
# PowerShell accidents
$null
@@ -1,68 +0,0 @@
on:
push:
branches:
- main
- development
pull_request:
branches:
- main
- development
workflow_dispatch:
jobs:
test:
if: github.actor != 'dependabot[bot]' && github.ref != 'refs/heads/master'
runs-on: ${{ matrix.os }}
strategy:
# If failure occurs on one OS, it is still relevant to see which others may work
fail-fast: false
matrix:
python-version: ["3.12"]
os: [ubuntu-latest]
steps:
- name: Github Actor Info
run: |
echo "Workflow triggered by github actor ${{ github.actor }}"
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Checkout
uses: actions/checkout@v3
- name: Activate Cache for pip
uses: actions/cache@v3
id: cache-python-env
with:
path: ${{ env.pythonLocation }}
key: "v3-${{ env.pythonLocation }}-${{ hashFiles('setup.py') }}"
- name: Update pip and friends
run: |
python -m pip install --upgrade pip setuptools wheel
if: steps.cache-python-env.outputs.cache-hit != 'true'
- name: Install TA-lib
# Inspiration for this step is taken from here: https://github.com/mrjbq7/ta-lib/blob/master/.github/workflows/tests.yml
shell: bash
run: |
wget https://raw.githubusercontent.com/mrjbq7/ta-lib/master/tools/build_talib_from_source.bash
chmod +x build_talib_from_source.bash
./build_talib_from_source.bash $DEPS_PATH
python -m pip install TA-lib
env:
DEPS_PATH: ${{ github.workspace }}/dependencies
TA_INCLUDE_PATH: ${{ github.workspace }}/dependencies/include
TA_LIBRARY_PATH: ${{ github.workspace }}/dependencies/lib
- name: Install pandas-ta
run: |
python -m pip install ".[test]"
- name: Run tests
shell: bash
run: |
make tests
@@ -1,147 +0,0 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
.DS_Store
# Distribution / packaging
.pypirc
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# pycharm
.idea/*
# pytest
.pytest_cache/
# pytest debug logs generated via --debug
pytestdebug.log
# dotenv
.env
# virtualenv
.venv
venv/
ENV/
env/**
.env*/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mypy
.mypy_cache/
# vscode settings
.vscode/**
# zed settings
.zed
.zed/**
# misc
dev.sh
Dockerfile
flake.nix
flake.lock
Makefile
scripts/
setup.py*
ta-lib/
venv.sh
website.tar.gz
# Data & NB Exclusions
csv/
# data/
jnb/
notes/
# mkdocs documentation
all_tests.sh
mkdocs.yml
docs/
site/
examples/
*.bak
@@ -1,73 +0,0 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Guidelines
Not everyone is familiar with open-source communities or intuitively understands what is acceptable behavior. To assist, the following guidelines and examples provide a quick overview to help you avoid common pitfalls:
1. Do **not** feel entitled to free software, support, or advice, especially if you are **not** a contributor, member, or business customer. Do **not** expect others to give you status reports as if they work for you or owe you something, even if you've made a donation. Please refrain from using GitHub Issues or other development tools for general discussions, technical support, or personal opinions.
2. Slow down and **read** the documentation and use the troubleshooting checklists provided to identify the root cause of a problem **before** submitting _invalid_ bug reports, inciting public disputes, or insulting community members in public spaces. Such actions are disruptive and prevent maintainers and contributors from working on features and improvements that benefit users.
3. Ignorant, reckless, or harsh communication is unacceptable, whether public or private.
Many issues that new users become upset about in old issue comments have been resolved. If not, rest assured that we are working diligently to improve the software.
## Enforcement
We will enforce our community standards as necessary appropriate to the circumstances to protect everyone's well-being if there are instances of abusive, harassing, or otherwise unacceptable behavior.
Initial warnings may be issued in the form of a snarky comment, especially if you seem to be exhibiting behaviour from Guideline #3 above. For serious cases, we will refer you to this Code of Conduct to prevent any misunderstandings. We also reserve the right to remove rants, personal attacks, spam, and unsolicited advertising from our community forums.
In addition, we may use technical measures to temporarily or permanently restrict your access to our infrastructure.
-21
View File
@@ -1,21 +0,0 @@
The MIT License (MIT)
Copyright (c) 2019+ Kevin Johnson
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@@ -1,52 +0,0 @@
Metadata-Version: 2.4
Name: pandas-ta
Version: 0.4.71b0
Summary: A Comprehensive Python 3 Technical Analysis Library with Pandas Dataframe Extension for Quantitative Researchers, Traders, and Investors.
Project-URL: Homepage, https://www.pandas-ta.dev
Project-URL: Documentation, https://www.pandas-ta.dev/api
Project-URL: Repository, https://github.com/twopirllc/pandas-ta
Author-email: Pandas TA Support <support@pandas-ta.dev>
License-File: LICENSE
Keywords: ai,dataframe extension,finance,indicators,library,machine learning,pandas,ta,technical analysis,trading
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Financial and Insurance Industry
Classifier: Intended Audience :: Science/Research
Classifier: Natural Language :: English
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Office/Business :: Financial
Classifier: Topic :: Office/Business :: Financial :: Investment
Classifier: Topic :: Scientific/Engineering
Classifier: Topic :: Scientific/Engineering :: Information Analysis
Requires-Python: >=3.12
Requires-Dist: numba==0.61.2
Requires-Dist: numpy>=2.2.6
Requires-Dist: pandas>=2.3.2
Requires-Dist: tqdm>=4.67.1
Description-Content-Type: text/markdown
<p align="center">
<a href="https://www.pandas-ta.dev"><img src="https://www.pandas-ta.dev/assets/images/pta-logo.webp" alt="Pandas TA"></a>
</p>
# Pandas TA
A popular and comprehensive Technical Analysis Library in Python 3 that leverages [_numba_](http://numba.pydata.org/) and [_numpy_](https://numpy.org/) for accuracy and performance, and [_pandas_](https://pandas.pydata.org/) for simplicity and bulk processing. The library contains more than 150 indicators and utilities as well as 60 Candlestick Patterns when [TA Lib](https://ta-lib.org) is installed.
<br>
![Price Chart](https://www.pandas-ta.dev/assets/images/SPY_Chart.png)
![Volume Chart](https://www.pandas-ta.dev/assets/images/SPY_VOL.png)
<br>
## Details
* [Getting Started](https://www.pandas-ta.dev/getting-started/installation/)
* [Documentation](https://www.pandas-ta.dev/api/)
* [Support](https://www.pandas-ta.dev/support/)
* [License](https://www.pandas-ta.dev/legal/license/)
<br>
Copyright © 2025 - Pandas TA
@@ -1,24 +0,0 @@
<p align="center">
<a href="https://www.pandas-ta.dev"><img src="https://www.pandas-ta.dev/assets/images/pta-logo.webp" alt="Pandas TA"></a>
</p>
# Pandas TA
A popular and comprehensive Technical Analysis Library in Python 3 that leverages [_numba_](http://numba.pydata.org/) and [_numpy_](https://numpy.org/) for accuracy and performance, and [_pandas_](https://pandas.pydata.org/) for simplicity and bulk processing. The library contains more than 150 indicators and utilities as well as 60 Candlestick Patterns when [TA Lib](https://ta-lib.org) is installed.
<br>
![Price Chart](https://www.pandas-ta.dev/assets/images/SPY_Chart.png)
![Volume Chart](https://www.pandas-ta.dev/assets/images/SPY_VOL.png)
<br>
## Details
* [Getting Started](https://www.pandas-ta.dev/getting-started/installation/)
* [Documentation](https://www.pandas-ta.dev/api/)
* [Support](https://www.pandas-ta.dev/support/)
* [License](https://www.pandas-ta.dev/legal/license/)
<br>
Copyright © 2025 - Pandas TA
@@ -1,31 +0,0 @@
,date,high,low,close
0,2020-6-19,11382,11150,11286
1,2020-6-20,11351,11155,11325
2,2020-6-21,11355,11225,11238
3,2020-6-22,11675,11232,11593
4,2020-6-23,11649,11488,11509
5,2020-6-24,11553,11110,11233
6,2020-6-25,11232,10842,11147
7,2020-6-26,11185,10901,11045
8,2020-6-27,11051,10791,10913
9,2020-6-28,11163,10756,10970
10,2020-6-29,11033,10814,10972
11,2020-6-30,11004,10882,10903
12,2020-7-1,11111,10887,11051
13,2020-7-2,11077,10772,10888
14,2020-7-3,10920,10821,10837
15,2020-7-4,10960,10814,10884
16,2020-7-5,10900,10700,10831
17,2020-7-6,11102,10796,11065
18,2020-7-7,11097,10952,10988
19,2020-7-8,11208,10968,11138
20,2020-7-9,11149,10906,10961
21,2020-7-10,11062,10880,11043
22,2020-7-11,11044,10910,10957
23,2020-7-12,11061,10915,11036
24,2020-7-13,11063,10908,11000
25,2020-7-14,11040,10925,11010
26,2020-7-15,11017,10955,10975
27,2020-7-16,11003,10835,10934
28,2020-7-17,10980,10875,10937
29,2020-7-18,10990,10894,10905
1 date high low close
2 0 2020-6-19 11382 11150 11286
3 1 2020-6-20 11351 11155 11325
4 2 2020-6-21 11355 11225 11238
5 3 2020-6-22 11675 11232 11593
6 4 2020-6-23 11649 11488 11509
7 5 2020-6-24 11553 11110 11233
8 6 2020-6-25 11232 10842 11147
9 7 2020-6-26 11185 10901 11045
10 8 2020-6-27 11051 10791 10913
11 9 2020-6-28 11163 10756 10970
12 10 2020-6-29 11033 10814 10972
13 11 2020-6-30 11004 10882 10903
14 12 2020-7-1 11111 10887 11051
15 13 2020-7-2 11077 10772 10888
16 14 2020-7-3 10920 10821 10837
17 15 2020-7-4 10960 10814 10884
18 16 2020-7-5 10900 10700 10831
19 17 2020-7-6 11102 10796 11065
20 18 2020-7-7 11097 10952 10988
21 19 2020-7-8 11208 10968 11138
22 20 2020-7-9 11149 10906 10961
23 21 2020-7-10 11062 10880 11043
24 22 2020-7-11 11044 10910 10957
25 23 2020-7-12 11061 10915 11036
26 24 2020-7-13 11063 10908 11000
27 25 2020-7-14 11040 10925 11010
28 26 2020-7-15 11017 10955 10975
29 27 2020-7-16 11003 10835 10934
30 28 2020-7-17 10980 10875 10937
31 29 2020-7-18 10990 10894 10905
@@ -1,248 +0,0 @@
Date,Open,High,Low,Close,VWAP,Volume
2020-10-26,343.7,344.9,336.7,339.8,341.19,8695206
2020-10-27,338.3,339.75,331.05,334.05,333.74,9910942
2020-10-28,335.75,339.85,334.4,335.65,337.29,8984606
2020-10-29,333.65,340.0,331.15,337.45,335.96,7239961
2020-10-30,339.25,345.0,338.45,340.7,341.18,11496123
2020-11-02,341.75,341.75,333.7,334.8,336.22,4831133
2020-11-03,335.5,339.45,332.65,335.65,336.36,4757798
2020-11-04,338.0,351.55,337.5,342.95,345.59,15381900
2020-11-05,347.95,349.0,344.05,345.4,346.41,5602436
2020-11-06,346.15,349.0,343.4,345.75,344.92,5457793
2020-11-09,350.0,352.8,347.0,351.95,350.46,7107285
2020-11-10,350.75,350.75,341.15,342.3,343.63,9049620
2020-11-11,344.5,347.75,341.5,346.75,344.53,7637879
2020-11-12,348.0,350.7,344.3,345.55,346.81,5114424
2020-11-13,344.5,346.25,342.7,344.15,344.04,4727888
2020-11-14,347.15,347.55,343.1,345.45,345.82,749242
2020-11-17,347.0,350.5,343.7,348.7,347.99,6653834
2020-11-18,349.75,352.35,341.8,345.3,345.38,7407808
2020-11-19,347.7,349.5,341.6,342.6,345.72,11056851
2020-11-20,343.8,348.5,342.05,346.3,345.96,6181393
2020-11-23,348.0,357.4,346.0,355.85,352.34,10333347
2020-11-24,359.0,361.4,355.0,355.5,357.97,10092441
2020-11-25,357.9,358.8,349.55,350.0,352.95,6812338
2020-11-26,351.0,356.4,347.2,354.85,352.19,7366370
2020-11-27,354.85,357.5,347.85,350.5,351.8,12402084
2020-12-01,352.7,353.9,346.25,352.85,350.99,10693567
2020-12-02,354.45,360.3,354.2,358.9,357.61,9434077
2020-12-03,364.0,364.0,358.0,360.3,360.33,10697045
2020-12-04,361.1,362.4,359.0,360.8,360.69,7427393
2020-12-07,362.0,362.75,357.5,358.45,359.01,8392383
2020-12-08,360.3,365.0,358.4,363.7,361.67,11813301
2020-12-09,367.75,367.75,357.5,359.5,362.22,18137688
2020-12-10,356.2,358.0,352.75,355.9,355.84,6897397
2020-12-11,357.4,360.0,352.65,353.5,355.85,5863616
2020-12-14,353.5,354.55,347.1,349.35,349.28,11501839
2020-12-15,349.3,353.9,347.5,352.7,351.95,7231781
2020-12-16,353.9,358.95,353.2,358.4,356.44,6666272
2020-12-17,358.4,359.25,355.5,356.9,357.27,4165371
2020-12-18,358.95,365.8,357.5,363.55,362.94,17398081
2020-12-21,362.5,367.45,348.35,353.95,360.92,9665063
2020-12-22,354.0,365.7,349.45,364.2,357.14,8186039
2020-12-23,374.0,387.6,371.7,385.55,379.89,44874433
2020-12-24,386.45,386.7,378.65,382.2,382.71,12240172
2020-12-28,383.45,386.4,382.0,382.9,384.06,4725879
2020-12-29,384.0,390.5,383.1,385.0,387.04,11459126
2020-12-30,385.0,386.6,382.8,384.4,384.43,7188435
2020-12-31,381.2,387.6,381.2,386.25,385.2,6394605
2021-01-01,385.05,390.75,385.05,388.1,388.0,5042336
2021-01-04,390.0,397.95,387.8,396.4,393.91,9755721
2021-01-05,394.1,409.8,393.5,406.3,403.28,25156747
2021-01-06,405.0,417.4,403.3,406.4,410.04,22486300
2021-01-07,412.25,413.0,403.85,406.75,406.88,12344755
2021-01-08,407.5,432.65,407.25,430.2,423.56,40993230
2021-01-11,436.0,451.0,435.0,446.8,444.84,47334018
2021-01-12,447.95,460.75,442.4,457.7,451.3,22134465
2021-01-13,461.0,467.45,452.8,459.0,460.7,29190193
2021-01-14,452.0,466.0,430.0,454.35,450.54,71551807
2021-01-15,454.75,459.4,436.6,438.55,447.14,25307779
2021-01-18,441.0,445.0,429.1,431.55,438.58,21510587
2021-01-19,433.0,439.55,427.7,430.25,432.24,15677790
2021-01-20,434.0,447.85,433.0,444.95,443.71,24419679
2021-01-21,451.6,453.45,442.3,445.8,448.46,15911377
2021-01-22,447.45,452.0,440.45,444.75,445.22,11398812
2021-01-25,445.15,451.0,435.15,437.25,441.91,7380169
2021-01-27,438.0,454.75,437.25,446.45,449.19,29590210
2021-01-28,443.25,448.6,427.3,431.9,434.54,14812435
2021-01-29,437.4,437.7,415.55,417.9,425.79,15632421
2021-02-01,416.1,423.8,408.2,421.5,415.3,15081852
2021-02-02,428.0,435.55,423.65,428.35,427.93,13310498
2021-02-03,430.2,439.95,430.2,433.5,436.37,9897666
2021-02-04,433.5,436.9,428.25,429.9,430.85,7193299
2021-02-05,432.0,433.4,420.55,425.55,424.89,8679026
2021-02-08,430.35,437.7,426.3,435.3,431.95,12516340
2021-02-09,440.25,451.75,435.2,439.35,445.17,29474830
2021-02-10,442.0,444.15,435.0,439.0,438.83,10607735
2021-02-11,439.0,440.5,433.4,437.0,436.02,9237026
2021-02-12,440.0,449.2,439.6,442.0,444.59,19280834
2021-02-15,444.6,444.9,436.8,439.7,440.59,7844877
2021-02-16,446.0,447.8,433.8,437.55,440.41,12453798
2021-02-17,435.9,439.0,428.5,430.2,432.63,7390192
2021-02-18,430.3,436.65,429.35,432.95,432.81,8385736
2021-02-19,433.15,435.75,426.1,429.95,431.28,8478666
2021-02-22,430.7,432.85,416.0,418.7,422.91,10666087
2021-02-23,419.7,426.0,414.3,415.5,419.17,8992353
2021-02-24,417.7,425.8,414.75,423.1,419.2,5060297
2021-02-25,423.1,429.0,420.45,421.3,424.69,9110392
2021-02-26,417.95,419.85,408.0,410.3,413.09,11731941
2021-03-01,411.0,418.7,409.2,414.4,414.87,6421696
2021-03-02,419.7,433.1,418.25,430.4,426.86,14875234
2021-03-03,433.0,436.65,428.25,435.5,433.26,8782452
2021-03-04,428.9,444.3,427.5,438.8,439.03,12963797
2021-03-05,440.0,440.0,417.0,420.85,426.19,43931172
2021-03-08,425.1,425.15,415.55,416.9,418.53,12354838
2021-03-09,419.8,422.8,410.05,419.2,416.27,11905629
2021-03-10,426.1,430.7,423.0,426.7,427.09,17291368
2021-03-12,431.5,432.8,422.1,425.2,427.39,8960126
2021-03-15,424.8,428.6,417.8,426.35,424.52,7517124
2021-03-16,427.25,434.95,425.65,429.3,430.57,10673647
2021-03-17,429.0,435.5,417.7,419.65,427.84,12134812
2021-03-18,423.25,424.15,404.3,410.15,414.52,10082931
2021-03-19,408.95,413.65,399.45,410.5,408.79,16875402
2021-03-22,411.05,416.25,410.2,414.45,413.97,6622984
2021-03-23,417.7,423.4,412.7,415.5,416.98,11575194
2021-03-24,411.35,415.45,409.2,411.0,411.84,5140347
2021-03-25,410.7,411.7,397.75,399.65,402.63,12737129
2021-03-26,404.8,407.7,400.65,403.9,403.72,9033369
2021-03-30,405.0,420.45,405.0,418.1,413.6,13171390
2021-03-31,419.8,419.8,413.35,414.15,415.37,7445134
2021-04-01,418.85,422.85,414.35,416.4,417.72,7596943
2021-04-05,416.45,427.9,416.2,425.45,423.14,21216395
2021-04-06,427.95,428.9,422.35,427.15,425.99,8320520
2021-04-07,425.0,439.0,423.4,438.0,431.67,13867650
2021-04-08,441.95,445.95,440.0,442.1,442.94,12916614
2021-04-09,442.9,451.35,440.0,450.1,446.5,11699385
2021-04-12,450.0,450.1,429.4,432.6,435.75,15086267
2021-04-13,432.0,437.55,412.6,418.95,420.89,20420320
2021-04-15,418.9,434.45,415.0,430.7,428.6,26758891
2021-04-16,441.7,473.65,436.0,469.2,464.06,109361636
2021-04-19,463.0,477.95,461.1,472.75,470.56,43809855
2021-04-20,476.0,477.55,467.8,470.1,471.11,17730691
2021-04-22,471.6,494.5,471.6,486.65,486.53,42620929
2021-04-23,483.55,487.35,474.35,475.7,481.46,13596003
2021-04-26,479.4,483.85,477.0,480.3,479.77,9314644
2021-04-27,481.4,487.0,481.0,485.05,484.07,7209467
2021-04-28,485.85,493.2,481.25,489.3,487.55,9615534
2021-04-29,492.7,492.8,485.7,489.85,489.45,7641133
2021-04-30,491.5,511.8,489.3,492.75,501.42,29115571
2021-05-03,487.95,496.0,483.25,487.35,488.82,8599731
2021-05-04,487.3,488.3,477.8,481.95,482.16,8102429
2021-05-05,484.8,496.0,483.3,490.6,490.81,11856591
2021-05-06,493.7,514.8,487.25,512.3,506.32,24413544
2021-05-07,514.0,516.55,507.5,515.25,512.83,14666581
2021-05-10,517.6,528.5,513.25,525.95,521.17,12544472
2021-05-11,517.95,523.3,514.05,518.4,517.71,7649499
2021-05-12,519.95,522.55,504.85,507.6,512.01,7871107
2021-05-14,507.5,508.4,492.75,498.45,497.83,6799879
2021-05-17,498.45,503.7,495.0,499.8,500.11,5752650
2021-05-18,502.0,514.8,500.9,508.05,509.98,9380746
2021-05-19,508.3,517.8,505.15,511.65,512.88,7490396
2021-05-20,516.0,517.0,503.6,508.25,510.08,6292304
2021-05-21,510.0,515.9,509.15,512.7,512.84,5197503
2021-05-24,511.9,518.5,509.65,514.9,515.62,7436812
2021-05-25,517.4,524.85,515.7,517.55,520.01,7954564
2021-05-26,514.9,530.45,514.75,527.25,525.5,8823190
2021-05-27,527.25,545.0,525.05,540.9,539.19,29411126
2021-05-28,539.0,540.3,532.65,538.7,536.6,5878626
2021-05-31,538.7,540.55,531.05,539.05,537.4,5386205
2021-06-01,541.9,547.0,539.65,542.8,543.11,5965044
2021-06-02,541.95,545.0,533.0,543.0,539.1,4713568
2021-06-03,545.7,549.9,538.3,539.05,541.49,5637991
2021-06-04,543.5,545.5,540.45,541.2,542.62,4771716
2021-06-07,544.7,551.2,542.7,548.25,548.14,4348433
2021-06-08,550.0,555.5,549.15,550.6,552.19,5429734
2021-06-09,553.0,553.25,542.2,544.2,547.74,6206899
2021-06-10,545.55,555.5,545.55,554.25,552.94,5720744
2021-06-11,555.0,558.95,551.5,554.3,555.3,5612359
2021-06-14,553.9,562.8,553.0,561.6,559.27,5952159
2021-06-15,561.5,564.0,557.0,557.9,560.54,3837245
2021-06-16,559.9,562.55,553.5,555.25,557.94,5497269
2021-06-17,552.5,561.5,549.55,558.7,555.66,4399348
2021-06-18,560.0,561.95,546.05,549.8,551.95,10007116
2021-06-21,544.75,544.75,535.2,542.15,540.03,5390821
2021-06-22,545.25,559.95,542.3,556.55,553.47,5752921
2021-06-23,560.0,560.85,538.4,540.15,545.31,11242702
2021-06-24,541.9,550.55,539.2,549.05,546.31,8668680
2021-06-25,549.85,551.0,540.5,547.5,546.25,5039822
2021-06-28,549.75,550.8,544.25,547.4,547.78,3259830
2021-06-29,547.8,553.75,541.0,542.6,546.44,7927880
2021-06-30,544.85,548.0,542.0,545.65,545.7,4293322
2021-07-01,545.0,545.9,538.0,539.35,540.69,4329374
2021-07-02,540.1,542.3,533.6,538.6,537.3,4157307
2021-07-05,542.9,542.9,535.55,536.35,537.49,3439169
2021-07-06,536.55,538.3,531.85,532.6,533.81,4193441
2021-07-07,534.05,536.4,525.1,532.15,531.49,5377687
2021-07-08,533.9,536.95,528.0,531.0,532.33,4565510
2021-07-09,527.4,530.95,523.25,525.8,526.28,5724608
2021-07-12,530.75,532.15,523.2,525.85,528.04,3865556
2021-07-13,528.0,529.5,522.6,524.85,525.64,4172358
2021-07-14,526.25,563.3,525.6,561.7,551.0,29348083
2021-07-15,561.0,579.65,559.0,575.9,572.71,31066201
2021-07-16,580.0,589.25,568.3,577.75,577.56,36907827
2021-07-19,572.55,584.0,570.0,573.8,577.28,10857851
2021-07-20,575.7,578.15,561.55,568.1,567.43,10523527
2021-07-22,577.1,585.0,571.0,584.2,579.61,12043385
2021-07-23,586.5,601.8,583.7,599.15,594.86,14057455
2021-07-26,600.1,600.5,588.25,590.45,594.0,6099903
2021-07-27,588.9,594.65,584.15,591.45,588.79,3983137
2021-07-28,590.95,593.35,583.0,591.95,587.86,4573930
2021-07-29,593.6,598.0,587.9,590.45,593.23,5834470
2021-07-30,588.85,593.0,585.0,587.15,589.07,3178206
2021-08-02,588.2,595.4,582.25,592.25,588.64,4623898
2021-08-03,604.0,604.0,594.15,599.4,597.75,5165607
2021-08-04,603.5,604.0,594.3,596.8,597.91,4509356
2021-08-05,597.5,614.5,597.5,600.9,606.72,11294364
2021-08-06,603.75,606.4,596.3,598.0,601.42,5270200
2021-08-09,600.0,606.0,595.5,596.8,599.75,4646751
2021-08-10,598.4,608.95,593.1,603.85,603.0,7027782
2021-08-11,601.9,606.5,598.45,601.25,601.72,4282513
2021-08-12,602.0,608.9,600.65,605.85,605.08,3952508
2021-08-13,606.0,620.45,606.0,615.5,614.1,8364684
2021-08-16,616.1,620.9,610.5,614.05,615.39,3572426
2021-08-17,613.75,637.05,611.2,634.9,626.66,11822772
2021-08-18,635.75,639.2,624.6,629.4,631.97,6923577
2021-08-20,622.5,631.95,618.0,620.05,623.36,6170693
2021-08-23,626.0,636.95,623.5,628.85,630.46,7036375
2021-08-24,636.0,636.0,624.1,633.55,630.64,4483718
2021-08-25,635.0,642.8,629.55,631.65,636.31,6407718
2021-08-26,632.2,638.5,626.0,628.95,631.86,4208351
2021-08-27,629.5,636.0,625.3,634.95,632.11,4371554
2021-08-30,635.95,639.5,630.55,632.45,633.87,4371855
2021-08-31,634.6,642.55,628.9,640.95,637.51,7795908
2021-09-01,643.8,647.35,635.25,642.05,641.38,4725222
2021-09-02,645.0,657.0,640.75,651.45,648.56,4538778
2021-09-03,653.0,656.7,649.5,655.1,653.68,3774970
2021-09-06,656.65,688.7,656.65,686.45,675.53,12338416
2021-09-07,690.0,690.0,672.75,674.25,679.16,6406109
2021-09-08,675.4,675.4,660.0,662.2,664.96,6716231
2021-09-09,660.9,664.55,654.0,662.35,660.1,5805730
2021-09-13,662.35,671.85,657.85,670.75,667.33,5588789
2021-09-14,671.0,675.7,670.0,673.45,673.41,3661338
2021-09-15,674.0,680.55,670.0,674.05,675.03,4145416
2021-09-16,679.75,680.75,665.0,667.85,670.77,4657846
2021-09-17,671.4,674.4,662.75,665.25,668.16,5407781
2021-09-20,657.75,670.5,653.4,662.5,664.4,4546430
2021-09-21,660.5,669.0,657.1,667.6,663.15,3683551
2021-09-22,669.0,674.5,663.0,668.15,668.59,4791652
2021-09-23,672.9,677.0,671.4,674.3,674.63,3231293
2021-09-24,680.5,699.15,675.0,676.5,687.0,12126588
2021-09-27,678.05,678.35,651.0,653.9,658.22,8399082
2021-09-28,653.9,653.9,635.25,639.6,642.5,8112462
2021-09-29,629.95,646.5,627.35,640.75,636.66,8839566
2021-09-30,638.75,646.75,632.55,634.1,638.65,6013959
2021-10-01,634.8,639.75,630.2,636.25,635.17,4208854
2021-10-04,639.9,643.6,635.95,641.05,638.84,5247122
2021-10-05,636.45,649.5,634.05,646.85,641.56,5456074
2021-10-06,650.4,650.45,634.4,635.75,640.73,4836599
2021-10-07,639.0,648.55,636.9,642.95,643.86,3724343
2021-10-08,646.15,667.55,645.0,661.15,659.07,11045983
2021-10-11,648.95,666.3,635.0,652.8,651.74,10211466
2021-10-12,652.0,662.4,642.55,659.1,653.44,9340216
2021-10-13,665.0,674.7,656.0,672.6,664.89,9462941
2021-10-14,697.0,739.85,695.0,708.25,717.07,59188803
2021-10-18,719.0,720.8,702.4,709.75,711.39,10599446
2021-10-19,717.1,729.95,708.0,711.55,721.38,10932574
2021-10-20,713.6,722.75,695.75,701.95,705.96,8054391
2021-10-21,708.95,709.0,681.7,696.3,694.67,7958456
2021-10-22,701.75,705.5,678.25,682.4,689.25,5379151
1 Date Open High Low Close VWAP Volume
2 2020-10-26 343.7 344.9 336.7 339.8 341.19 8695206
3 2020-10-27 338.3 339.75 331.05 334.05 333.74 9910942
4 2020-10-28 335.75 339.85 334.4 335.65 337.29 8984606
5 2020-10-29 333.65 340.0 331.15 337.45 335.96 7239961
6 2020-10-30 339.25 345.0 338.45 340.7 341.18 11496123
7 2020-11-02 341.75 341.75 333.7 334.8 336.22 4831133
8 2020-11-03 335.5 339.45 332.65 335.65 336.36 4757798
9 2020-11-04 338.0 351.55 337.5 342.95 345.59 15381900
10 2020-11-05 347.95 349.0 344.05 345.4 346.41 5602436
11 2020-11-06 346.15 349.0 343.4 345.75 344.92 5457793
12 2020-11-09 350.0 352.8 347.0 351.95 350.46 7107285
13 2020-11-10 350.75 350.75 341.15 342.3 343.63 9049620
14 2020-11-11 344.5 347.75 341.5 346.75 344.53 7637879
15 2020-11-12 348.0 350.7 344.3 345.55 346.81 5114424
16 2020-11-13 344.5 346.25 342.7 344.15 344.04 4727888
17 2020-11-14 347.15 347.55 343.1 345.45 345.82 749242
18 2020-11-17 347.0 350.5 343.7 348.7 347.99 6653834
19 2020-11-18 349.75 352.35 341.8 345.3 345.38 7407808
20 2020-11-19 347.7 349.5 341.6 342.6 345.72 11056851
21 2020-11-20 343.8 348.5 342.05 346.3 345.96 6181393
22 2020-11-23 348.0 357.4 346.0 355.85 352.34 10333347
23 2020-11-24 359.0 361.4 355.0 355.5 357.97 10092441
24 2020-11-25 357.9 358.8 349.55 350.0 352.95 6812338
25 2020-11-26 351.0 356.4 347.2 354.85 352.19 7366370
26 2020-11-27 354.85 357.5 347.85 350.5 351.8 12402084
27 2020-12-01 352.7 353.9 346.25 352.85 350.99 10693567
28 2020-12-02 354.45 360.3 354.2 358.9 357.61 9434077
29 2020-12-03 364.0 364.0 358.0 360.3 360.33 10697045
30 2020-12-04 361.1 362.4 359.0 360.8 360.69 7427393
31 2020-12-07 362.0 362.75 357.5 358.45 359.01 8392383
32 2020-12-08 360.3 365.0 358.4 363.7 361.67 11813301
33 2020-12-09 367.75 367.75 357.5 359.5 362.22 18137688
34 2020-12-10 356.2 358.0 352.75 355.9 355.84 6897397
35 2020-12-11 357.4 360.0 352.65 353.5 355.85 5863616
36 2020-12-14 353.5 354.55 347.1 349.35 349.28 11501839
37 2020-12-15 349.3 353.9 347.5 352.7 351.95 7231781
38 2020-12-16 353.9 358.95 353.2 358.4 356.44 6666272
39 2020-12-17 358.4 359.25 355.5 356.9 357.27 4165371
40 2020-12-18 358.95 365.8 357.5 363.55 362.94 17398081
41 2020-12-21 362.5 367.45 348.35 353.95 360.92 9665063
42 2020-12-22 354.0 365.7 349.45 364.2 357.14 8186039
43 2020-12-23 374.0 387.6 371.7 385.55 379.89 44874433
44 2020-12-24 386.45 386.7 378.65 382.2 382.71 12240172
45 2020-12-28 383.45 386.4 382.0 382.9 384.06 4725879
46 2020-12-29 384.0 390.5 383.1 385.0 387.04 11459126
47 2020-12-30 385.0 386.6 382.8 384.4 384.43 7188435
48 2020-12-31 381.2 387.6 381.2 386.25 385.2 6394605
49 2021-01-01 385.05 390.75 385.05 388.1 388.0 5042336
50 2021-01-04 390.0 397.95 387.8 396.4 393.91 9755721
51 2021-01-05 394.1 409.8 393.5 406.3 403.28 25156747
52 2021-01-06 405.0 417.4 403.3 406.4 410.04 22486300
53 2021-01-07 412.25 413.0 403.85 406.75 406.88 12344755
54 2021-01-08 407.5 432.65 407.25 430.2 423.56 40993230
55 2021-01-11 436.0 451.0 435.0 446.8 444.84 47334018
56 2021-01-12 447.95 460.75 442.4 457.7 451.3 22134465
57 2021-01-13 461.0 467.45 452.8 459.0 460.7 29190193
58 2021-01-14 452.0 466.0 430.0 454.35 450.54 71551807
59 2021-01-15 454.75 459.4 436.6 438.55 447.14 25307779
60 2021-01-18 441.0 445.0 429.1 431.55 438.58 21510587
61 2021-01-19 433.0 439.55 427.7 430.25 432.24 15677790
62 2021-01-20 434.0 447.85 433.0 444.95 443.71 24419679
63 2021-01-21 451.6 453.45 442.3 445.8 448.46 15911377
64 2021-01-22 447.45 452.0 440.45 444.75 445.22 11398812
65 2021-01-25 445.15 451.0 435.15 437.25 441.91 7380169
66 2021-01-27 438.0 454.75 437.25 446.45 449.19 29590210
67 2021-01-28 443.25 448.6 427.3 431.9 434.54 14812435
68 2021-01-29 437.4 437.7 415.55 417.9 425.79 15632421
69 2021-02-01 416.1 423.8 408.2 421.5 415.3 15081852
70 2021-02-02 428.0 435.55 423.65 428.35 427.93 13310498
71 2021-02-03 430.2 439.95 430.2 433.5 436.37 9897666
72 2021-02-04 433.5 436.9 428.25 429.9 430.85 7193299
73 2021-02-05 432.0 433.4 420.55 425.55 424.89 8679026
74 2021-02-08 430.35 437.7 426.3 435.3 431.95 12516340
75 2021-02-09 440.25 451.75 435.2 439.35 445.17 29474830
76 2021-02-10 442.0 444.15 435.0 439.0 438.83 10607735
77 2021-02-11 439.0 440.5 433.4 437.0 436.02 9237026
78 2021-02-12 440.0 449.2 439.6 442.0 444.59 19280834
79 2021-02-15 444.6 444.9 436.8 439.7 440.59 7844877
80 2021-02-16 446.0 447.8 433.8 437.55 440.41 12453798
81 2021-02-17 435.9 439.0 428.5 430.2 432.63 7390192
82 2021-02-18 430.3 436.65 429.35 432.95 432.81 8385736
83 2021-02-19 433.15 435.75 426.1 429.95 431.28 8478666
84 2021-02-22 430.7 432.85 416.0 418.7 422.91 10666087
85 2021-02-23 419.7 426.0 414.3 415.5 419.17 8992353
86 2021-02-24 417.7 425.8 414.75 423.1 419.2 5060297
87 2021-02-25 423.1 429.0 420.45 421.3 424.69 9110392
88 2021-02-26 417.95 419.85 408.0 410.3 413.09 11731941
89 2021-03-01 411.0 418.7 409.2 414.4 414.87 6421696
90 2021-03-02 419.7 433.1 418.25 430.4 426.86 14875234
91 2021-03-03 433.0 436.65 428.25 435.5 433.26 8782452
92 2021-03-04 428.9 444.3 427.5 438.8 439.03 12963797
93 2021-03-05 440.0 440.0 417.0 420.85 426.19 43931172
94 2021-03-08 425.1 425.15 415.55 416.9 418.53 12354838
95 2021-03-09 419.8 422.8 410.05 419.2 416.27 11905629
96 2021-03-10 426.1 430.7 423.0 426.7 427.09 17291368
97 2021-03-12 431.5 432.8 422.1 425.2 427.39 8960126
98 2021-03-15 424.8 428.6 417.8 426.35 424.52 7517124
99 2021-03-16 427.25 434.95 425.65 429.3 430.57 10673647
100 2021-03-17 429.0 435.5 417.7 419.65 427.84 12134812
101 2021-03-18 423.25 424.15 404.3 410.15 414.52 10082931
102 2021-03-19 408.95 413.65 399.45 410.5 408.79 16875402
103 2021-03-22 411.05 416.25 410.2 414.45 413.97 6622984
104 2021-03-23 417.7 423.4 412.7 415.5 416.98 11575194
105 2021-03-24 411.35 415.45 409.2 411.0 411.84 5140347
106 2021-03-25 410.7 411.7 397.75 399.65 402.63 12737129
107 2021-03-26 404.8 407.7 400.65 403.9 403.72 9033369
108 2021-03-30 405.0 420.45 405.0 418.1 413.6 13171390
109 2021-03-31 419.8 419.8 413.35 414.15 415.37 7445134
110 2021-04-01 418.85 422.85 414.35 416.4 417.72 7596943
111 2021-04-05 416.45 427.9 416.2 425.45 423.14 21216395
112 2021-04-06 427.95 428.9 422.35 427.15 425.99 8320520
113 2021-04-07 425.0 439.0 423.4 438.0 431.67 13867650
114 2021-04-08 441.95 445.95 440.0 442.1 442.94 12916614
115 2021-04-09 442.9 451.35 440.0 450.1 446.5 11699385
116 2021-04-12 450.0 450.1 429.4 432.6 435.75 15086267
117 2021-04-13 432.0 437.55 412.6 418.95 420.89 20420320
118 2021-04-15 418.9 434.45 415.0 430.7 428.6 26758891
119 2021-04-16 441.7 473.65 436.0 469.2 464.06 109361636
120 2021-04-19 463.0 477.95 461.1 472.75 470.56 43809855
121 2021-04-20 476.0 477.55 467.8 470.1 471.11 17730691
122 2021-04-22 471.6 494.5 471.6 486.65 486.53 42620929
123 2021-04-23 483.55 487.35 474.35 475.7 481.46 13596003
124 2021-04-26 479.4 483.85 477.0 480.3 479.77 9314644
125 2021-04-27 481.4 487.0 481.0 485.05 484.07 7209467
126 2021-04-28 485.85 493.2 481.25 489.3 487.55 9615534
127 2021-04-29 492.7 492.8 485.7 489.85 489.45 7641133
128 2021-04-30 491.5 511.8 489.3 492.75 501.42 29115571
129 2021-05-03 487.95 496.0 483.25 487.35 488.82 8599731
130 2021-05-04 487.3 488.3 477.8 481.95 482.16 8102429
131 2021-05-05 484.8 496.0 483.3 490.6 490.81 11856591
132 2021-05-06 493.7 514.8 487.25 512.3 506.32 24413544
133 2021-05-07 514.0 516.55 507.5 515.25 512.83 14666581
134 2021-05-10 517.6 528.5 513.25 525.95 521.17 12544472
135 2021-05-11 517.95 523.3 514.05 518.4 517.71 7649499
136 2021-05-12 519.95 522.55 504.85 507.6 512.01 7871107
137 2021-05-14 507.5 508.4 492.75 498.45 497.83 6799879
138 2021-05-17 498.45 503.7 495.0 499.8 500.11 5752650
139 2021-05-18 502.0 514.8 500.9 508.05 509.98 9380746
140 2021-05-19 508.3 517.8 505.15 511.65 512.88 7490396
141 2021-05-20 516.0 517.0 503.6 508.25 510.08 6292304
142 2021-05-21 510.0 515.9 509.15 512.7 512.84 5197503
143 2021-05-24 511.9 518.5 509.65 514.9 515.62 7436812
144 2021-05-25 517.4 524.85 515.7 517.55 520.01 7954564
145 2021-05-26 514.9 530.45 514.75 527.25 525.5 8823190
146 2021-05-27 527.25 545.0 525.05 540.9 539.19 29411126
147 2021-05-28 539.0 540.3 532.65 538.7 536.6 5878626
148 2021-05-31 538.7 540.55 531.05 539.05 537.4 5386205
149 2021-06-01 541.9 547.0 539.65 542.8 543.11 5965044
150 2021-06-02 541.95 545.0 533.0 543.0 539.1 4713568
151 2021-06-03 545.7 549.9 538.3 539.05 541.49 5637991
152 2021-06-04 543.5 545.5 540.45 541.2 542.62 4771716
153 2021-06-07 544.7 551.2 542.7 548.25 548.14 4348433
154 2021-06-08 550.0 555.5 549.15 550.6 552.19 5429734
155 2021-06-09 553.0 553.25 542.2 544.2 547.74 6206899
156 2021-06-10 545.55 555.5 545.55 554.25 552.94 5720744
157 2021-06-11 555.0 558.95 551.5 554.3 555.3 5612359
158 2021-06-14 553.9 562.8 553.0 561.6 559.27 5952159
159 2021-06-15 561.5 564.0 557.0 557.9 560.54 3837245
160 2021-06-16 559.9 562.55 553.5 555.25 557.94 5497269
161 2021-06-17 552.5 561.5 549.55 558.7 555.66 4399348
162 2021-06-18 560.0 561.95 546.05 549.8 551.95 10007116
163 2021-06-21 544.75 544.75 535.2 542.15 540.03 5390821
164 2021-06-22 545.25 559.95 542.3 556.55 553.47 5752921
165 2021-06-23 560.0 560.85 538.4 540.15 545.31 11242702
166 2021-06-24 541.9 550.55 539.2 549.05 546.31 8668680
167 2021-06-25 549.85 551.0 540.5 547.5 546.25 5039822
168 2021-06-28 549.75 550.8 544.25 547.4 547.78 3259830
169 2021-06-29 547.8 553.75 541.0 542.6 546.44 7927880
170 2021-06-30 544.85 548.0 542.0 545.65 545.7 4293322
171 2021-07-01 545.0 545.9 538.0 539.35 540.69 4329374
172 2021-07-02 540.1 542.3 533.6 538.6 537.3 4157307
173 2021-07-05 542.9 542.9 535.55 536.35 537.49 3439169
174 2021-07-06 536.55 538.3 531.85 532.6 533.81 4193441
175 2021-07-07 534.05 536.4 525.1 532.15 531.49 5377687
176 2021-07-08 533.9 536.95 528.0 531.0 532.33 4565510
177 2021-07-09 527.4 530.95 523.25 525.8 526.28 5724608
178 2021-07-12 530.75 532.15 523.2 525.85 528.04 3865556
179 2021-07-13 528.0 529.5 522.6 524.85 525.64 4172358
180 2021-07-14 526.25 563.3 525.6 561.7 551.0 29348083
181 2021-07-15 561.0 579.65 559.0 575.9 572.71 31066201
182 2021-07-16 580.0 589.25 568.3 577.75 577.56 36907827
183 2021-07-19 572.55 584.0 570.0 573.8 577.28 10857851
184 2021-07-20 575.7 578.15 561.55 568.1 567.43 10523527
185 2021-07-22 577.1 585.0 571.0 584.2 579.61 12043385
186 2021-07-23 586.5 601.8 583.7 599.15 594.86 14057455
187 2021-07-26 600.1 600.5 588.25 590.45 594.0 6099903
188 2021-07-27 588.9 594.65 584.15 591.45 588.79 3983137
189 2021-07-28 590.95 593.35 583.0 591.95 587.86 4573930
190 2021-07-29 593.6 598.0 587.9 590.45 593.23 5834470
191 2021-07-30 588.85 593.0 585.0 587.15 589.07 3178206
192 2021-08-02 588.2 595.4 582.25 592.25 588.64 4623898
193 2021-08-03 604.0 604.0 594.15 599.4 597.75 5165607
194 2021-08-04 603.5 604.0 594.3 596.8 597.91 4509356
195 2021-08-05 597.5 614.5 597.5 600.9 606.72 11294364
196 2021-08-06 603.75 606.4 596.3 598.0 601.42 5270200
197 2021-08-09 600.0 606.0 595.5 596.8 599.75 4646751
198 2021-08-10 598.4 608.95 593.1 603.85 603.0 7027782
199 2021-08-11 601.9 606.5 598.45 601.25 601.72 4282513
200 2021-08-12 602.0 608.9 600.65 605.85 605.08 3952508
201 2021-08-13 606.0 620.45 606.0 615.5 614.1 8364684
202 2021-08-16 616.1 620.9 610.5 614.05 615.39 3572426
203 2021-08-17 613.75 637.05 611.2 634.9 626.66 11822772
204 2021-08-18 635.75 639.2 624.6 629.4 631.97 6923577
205 2021-08-20 622.5 631.95 618.0 620.05 623.36 6170693
206 2021-08-23 626.0 636.95 623.5 628.85 630.46 7036375
207 2021-08-24 636.0 636.0 624.1 633.55 630.64 4483718
208 2021-08-25 635.0 642.8 629.55 631.65 636.31 6407718
209 2021-08-26 632.2 638.5 626.0 628.95 631.86 4208351
210 2021-08-27 629.5 636.0 625.3 634.95 632.11 4371554
211 2021-08-30 635.95 639.5 630.55 632.45 633.87 4371855
212 2021-08-31 634.6 642.55 628.9 640.95 637.51 7795908
213 2021-09-01 643.8 647.35 635.25 642.05 641.38 4725222
214 2021-09-02 645.0 657.0 640.75 651.45 648.56 4538778
215 2021-09-03 653.0 656.7 649.5 655.1 653.68 3774970
216 2021-09-06 656.65 688.7 656.65 686.45 675.53 12338416
217 2021-09-07 690.0 690.0 672.75 674.25 679.16 6406109
218 2021-09-08 675.4 675.4 660.0 662.2 664.96 6716231
219 2021-09-09 660.9 664.55 654.0 662.35 660.1 5805730
220 2021-09-13 662.35 671.85 657.85 670.75 667.33 5588789
221 2021-09-14 671.0 675.7 670.0 673.45 673.41 3661338
222 2021-09-15 674.0 680.55 670.0 674.05 675.03 4145416
223 2021-09-16 679.75 680.75 665.0 667.85 670.77 4657846
224 2021-09-17 671.4 674.4 662.75 665.25 668.16 5407781
225 2021-09-20 657.75 670.5 653.4 662.5 664.4 4546430
226 2021-09-21 660.5 669.0 657.1 667.6 663.15 3683551
227 2021-09-22 669.0 674.5 663.0 668.15 668.59 4791652
228 2021-09-23 672.9 677.0 671.4 674.3 674.63 3231293
229 2021-09-24 680.5 699.15 675.0 676.5 687.0 12126588
230 2021-09-27 678.05 678.35 651.0 653.9 658.22 8399082
231 2021-09-28 653.9 653.9 635.25 639.6 642.5 8112462
232 2021-09-29 629.95 646.5 627.35 640.75 636.66 8839566
233 2021-09-30 638.75 646.75 632.55 634.1 638.65 6013959
234 2021-10-01 634.8 639.75 630.2 636.25 635.17 4208854
235 2021-10-04 639.9 643.6 635.95 641.05 638.84 5247122
236 2021-10-05 636.45 649.5 634.05 646.85 641.56 5456074
237 2021-10-06 650.4 650.45 634.4 635.75 640.73 4836599
238 2021-10-07 639.0 648.55 636.9 642.95 643.86 3724343
239 2021-10-08 646.15 667.55 645.0 661.15 659.07 11045983
240 2021-10-11 648.95 666.3 635.0 652.8 651.74 10211466
241 2021-10-12 652.0 662.4 642.55 659.1 653.44 9340216
242 2021-10-13 665.0 674.7 656.0 672.6 664.89 9462941
243 2021-10-14 697.0 739.85 695.0 708.25 717.07 59188803
244 2021-10-18 719.0 720.8 702.4 709.75 711.39 10599446
245 2021-10-19 717.1 729.95 708.0 711.55 721.38 10932574
246 2021-10-20 713.6 722.75 695.75 701.95 705.96 8054391
247 2021-10-21 708.95 709.0 681.7 696.3 694.67 7958456
248 2021-10-22 701.75 705.5 678.25 682.4 689.25 5379151
File diff suppressed because it is too large Load Diff
@@ -1,101 +0,0 @@
,date,open,high,low,close,volume
0,2018-07-05,272.17,273.18,270.96,273.11,56925919.0
1,2018-07-06,273.14,275.84,272.715,275.42,66493696.0
2,2018-07-09,276.55,277.96,276.5,277.9,50550399.0
3,2018-07-10,278.41,279.01,278.08,278.9,51966829.0
4,2018-07-11,277.15,278.04,276.52,276.86,77054739.0
5,2018-07-12,278.28,279.43,277.6,279.37,60124687.0
6,2018-07-13,279.17,279.93,278.66,279.59,48234964.0
7,2018-07-16,279.64,279.803,278.84,279.34,48201038.0
8,2018-07-17,278.47,280.91,278.41,280.47,52315500.0
9,2018-07-18,280.56,281.18,280.06,281.06,44593465.0
10,2018-07-19,280.31,280.74,279.46,280.0,61412117.0
11,2018-07-20,279.77,280.48,279.5,279.68,82372729.0
12,2018-07-23,279.45,280.43,279.06,280.2,47047565.0
13,2018-07-24,281.79,282.56,280.63,281.61,68026935.0
14,2018-07-25,281.33,284.37,281.28,284.01,78882927.0
15,2018-07-26,283.2,284.11,283.09,283.34,57919495.0
16,2018-07-27,283.71,283.82,280.38,281.42,76783177.0
17,2018-07-30,281.51,281.69,279.36,279.95,63742508.0
18,2018-07-31,280.81,282.02,280.38,281.33,68570493.0
19,2018-08-01,281.56,282.13,280.1315,280.86,53853326.0
20,2018-08-02,279.39,282.58,279.16,282.39,63426363.0
21,2018-08-03,282.53,283.6577,282.33,283.6,53935386.0
22,2018-08-06,283.64,284.99,283.2015,284.64,39400887.0
23,2018-08-07,285.39,286.01,285.24,285.58,43196646.0
24,2018-08-08,285.39,285.91,284.94,285.46,42114551.0
25,2018-08-09,285.53,285.97,284.915,285.07,35716976.0
26,2018-08-10,283.45,284.055,282.36,283.16,77076044.0
27,2018-08-13,283.47,284.16,281.77,282.1,65732909.0
28,2018-08-14,282.92,284.17,282.4833,283.9,43842031.0
29,2018-08-15,282.38,282.54,280.16,281.78,102925355.0
30,2018-08-16,283.4,285.04,283.36,284.06,69967919.0
31,2018-08-17,283.83,285.5601,283.37,285.06,65618481.0
32,2018-08-20,285.57,285.97,285.06,285.67,39807490.0
33,2018-08-21,286.25,287.31,285.7135,286.34,67271983.0
34,2018-08-22,285.88,286.76,285.575,286.17,44993333.0
35,2018-08-23,285.97,286.94,285.43,285.79,49204851.0
36,2018-08-24,286.44,287.67,286.38,287.51,57487399.0
37,2018-08-27,288.86,289.9,288.68,289.78,57072377.0
38,2018-08-28,290.3,290.4175,289.4,289.92,46943472.0
39,2018-08-29,290.16,291.74,289.8854,291.48,61485514.0
40,2018-08-30,290.94,291.36,289.63,290.3,61229501.0
41,2018-08-31,289.84,290.81,289.29,290.31,66140838.0
42,2018-09-04,289.84,290.21,288.68,289.81,57594367.0
43,2018-09-05,289.41,289.64,287.89,289.03,72452437.0
44,2018-09-06,289.15,289.49,287.0,288.16,65909863.0
45,2018-09-07,286.98,288.7,286.71,287.6,73524824.0
46,2018-09-10,288.74,289.04,287.88,288.1,50191113.0
47,2018-09-11,287.37,289.55,286.975,289.05,50530492.0
48,2018-09-12,289.06,289.8,288.23,289.12,59810758.0
49,2018-09-13,290.32,291.0384,289.995,290.83,51034222.0
50,2018-09-14,291.06,291.27,290.0,290.88,55079875.0
51,2018-09-17,290.82,290.86,289.03,289.34,68244043.0
52,2018-09-18,289.58,291.58,289.55,290.91,61930407.0
53,2018-09-19,290.97,291.69,290.825,291.22,49080562.0
54,2018-09-20,292.64,293.94,291.2363,293.58,100360646.0
55,2018-09-21,293.09,293.22,291.81,291.99,105479656.0
56,2018-09-24,291.34,291.5,290.37,291.02,53409645.0
57,2018-09-25,291.53,291.65,290.4833,290.75,44370037.0
58,2018-09-26,290.91,292.24,289.41,289.88,79739674.0
59,2018-09-27,290.41,291.91,290.1,290.69,59249455.0
60,2018-09-28,289.99,291.28,289.95,290.72,70091385.0
61,2018-10-01,292.11,292.93,290.98,291.73,62078937.0
62,2018-10-02,291.56,292.355,291.14,291.56,47258227.0
63,2018-10-03,292.74,293.21,291.32,291.72,64694594.0
64,2018-10-04,291.18,291.24,287.66,289.44,111545910.0
65,2018-10-05,289.69,290.27,286.22,287.82,105951698.0
66,2018-10-08,287.05,288.22,285.5,287.82,87742172.0
67,2018-10-09,287.39,288.86,286.77,287.4,74338982.0
68,2018-10-10,286.83,286.91,277.88,278.3,214731000.0
69,2018-10-11,277.08,278.9,270.36,272.17,274840491.0
70,2018-10-12,276.77,277.09,272.37,275.95,183186492.0
71,2018-10-15,275.55,277.04,274.3,274.4,102263717.0
72,2018-10-16,276.6,280.82,276.07,280.4,118255834.0
73,2018-10-17,280.44,281.15,277.56,280.45,110625987.0
74,2018-10-18,279.4,280.07,274.97,276.4,134557525.0
75,2018-10-19,277.13,279.3,275.47,276.25,139901634.0
76,2018-10-22,277.0,277.36,274.41,275.01,82415812.0
77,2018-10-23,270.95,274.87,268.61,273.61,146352719.0
78,2018-10-24,273.33,273.76,264.7,265.32,177806697.0
79,2018-10-25,267.38,271.81,266.23,270.08,138061545.0
80,2018-10-26,265.92,271.0,262.29,265.33,201574596.0
81,2018-10-29,268.8,270.25,259.85,263.86,160749101.0
82,2018-10-30,263.67,268.12,263.12,267.77,157115995.0
83,2018-10-31,270.65,273.23,270.12,270.63,128296325.0
84,2018-11-01,271.6,273.73,270.38,273.51,99495037.0
85,2018-11-02,274.75,275.23,269.59,271.89,122634107.0
86,2018-11-05,272.44,274.01,271.35,273.39,65622482.0
87,2018-11-06,273.32,275.3,273.25,275.12,60085894.0
88,2018-11-07,277.56,281.1,277.08,281.01,102752099.0
89,2018-11-08,280.11,281.22,279.22,280.5,65584886.0
90,2018-11-09,279.03,279.24,276.18,277.76,98812561.0
91,2018-11-12,277.19,277.46,271.99,272.57,99673574.0
92,2018-11-13,273.09,275.325,271.25,272.06,98176610.0
93,2018-11-14,274.16,274.61,268.45,270.2,125335931.0
94,2018-11-15,268.78,273.54,267.0102,273.02,135101437.0
95,2018-11-16,271.79,274.75,271.21,273.73,126668040.0
96,2018-11-19,273.05,273.38,268.07,269.1,103061706.0
97,2018-11-20,265.36,267.0,263.15,264.12,136021311.0
98,2018-11-21,265.86,267.15,265.01,265.02,75563743.0
99,2018-11-23,263.18,264.8234,263.07,263.25,42807878.0
1 date open high low close volume
2 0 2018-07-05 272.17 273.18 270.96 273.11 56925919.0
3 1 2018-07-06 273.14 275.84 272.715 275.42 66493696.0
4 2 2018-07-09 276.55 277.96 276.5 277.9 50550399.0
5 3 2018-07-10 278.41 279.01 278.08 278.9 51966829.0
6 4 2018-07-11 277.15 278.04 276.52 276.86 77054739.0
7 5 2018-07-12 278.28 279.43 277.6 279.37 60124687.0
8 6 2018-07-13 279.17 279.93 278.66 279.59 48234964.0
9 7 2018-07-16 279.64 279.803 278.84 279.34 48201038.0
10 8 2018-07-17 278.47 280.91 278.41 280.47 52315500.0
11 9 2018-07-18 280.56 281.18 280.06 281.06 44593465.0
12 10 2018-07-19 280.31 280.74 279.46 280.0 61412117.0
13 11 2018-07-20 279.77 280.48 279.5 279.68 82372729.0
14 12 2018-07-23 279.45 280.43 279.06 280.2 47047565.0
15 13 2018-07-24 281.79 282.56 280.63 281.61 68026935.0
16 14 2018-07-25 281.33 284.37 281.28 284.01 78882927.0
17 15 2018-07-26 283.2 284.11 283.09 283.34 57919495.0
18 16 2018-07-27 283.71 283.82 280.38 281.42 76783177.0
19 17 2018-07-30 281.51 281.69 279.36 279.95 63742508.0
20 18 2018-07-31 280.81 282.02 280.38 281.33 68570493.0
21 19 2018-08-01 281.56 282.13 280.1315 280.86 53853326.0
22 20 2018-08-02 279.39 282.58 279.16 282.39 63426363.0
23 21 2018-08-03 282.53 283.6577 282.33 283.6 53935386.0
24 22 2018-08-06 283.64 284.99 283.2015 284.64 39400887.0
25 23 2018-08-07 285.39 286.01 285.24 285.58 43196646.0
26 24 2018-08-08 285.39 285.91 284.94 285.46 42114551.0
27 25 2018-08-09 285.53 285.97 284.915 285.07 35716976.0
28 26 2018-08-10 283.45 284.055 282.36 283.16 77076044.0
29 27 2018-08-13 283.47 284.16 281.77 282.1 65732909.0
30 28 2018-08-14 282.92 284.17 282.4833 283.9 43842031.0
31 29 2018-08-15 282.38 282.54 280.16 281.78 102925355.0
32 30 2018-08-16 283.4 285.04 283.36 284.06 69967919.0
33 31 2018-08-17 283.83 285.5601 283.37 285.06 65618481.0
34 32 2018-08-20 285.57 285.97 285.06 285.67 39807490.0
35 33 2018-08-21 286.25 287.31 285.7135 286.34 67271983.0
36 34 2018-08-22 285.88 286.76 285.575 286.17 44993333.0
37 35 2018-08-23 285.97 286.94 285.43 285.79 49204851.0
38 36 2018-08-24 286.44 287.67 286.38 287.51 57487399.0
39 37 2018-08-27 288.86 289.9 288.68 289.78 57072377.0
40 38 2018-08-28 290.3 290.4175 289.4 289.92 46943472.0
41 39 2018-08-29 290.16 291.74 289.8854 291.48 61485514.0
42 40 2018-08-30 290.94 291.36 289.63 290.3 61229501.0
43 41 2018-08-31 289.84 290.81 289.29 290.31 66140838.0
44 42 2018-09-04 289.84 290.21 288.68 289.81 57594367.0
45 43 2018-09-05 289.41 289.64 287.89 289.03 72452437.0
46 44 2018-09-06 289.15 289.49 287.0 288.16 65909863.0
47 45 2018-09-07 286.98 288.7 286.71 287.6 73524824.0
48 46 2018-09-10 288.74 289.04 287.88 288.1 50191113.0
49 47 2018-09-11 287.37 289.55 286.975 289.05 50530492.0
50 48 2018-09-12 289.06 289.8 288.23 289.12 59810758.0
51 49 2018-09-13 290.32 291.0384 289.995 290.83 51034222.0
52 50 2018-09-14 291.06 291.27 290.0 290.88 55079875.0
53 51 2018-09-17 290.82 290.86 289.03 289.34 68244043.0
54 52 2018-09-18 289.58 291.58 289.55 290.91 61930407.0
55 53 2018-09-19 290.97 291.69 290.825 291.22 49080562.0
56 54 2018-09-20 292.64 293.94 291.2363 293.58 100360646.0
57 55 2018-09-21 293.09 293.22 291.81 291.99 105479656.0
58 56 2018-09-24 291.34 291.5 290.37 291.02 53409645.0
59 57 2018-09-25 291.53 291.65 290.4833 290.75 44370037.0
60 58 2018-09-26 290.91 292.24 289.41 289.88 79739674.0
61 59 2018-09-27 290.41 291.91 290.1 290.69 59249455.0
62 60 2018-09-28 289.99 291.28 289.95 290.72 70091385.0
63 61 2018-10-01 292.11 292.93 290.98 291.73 62078937.0
64 62 2018-10-02 291.56 292.355 291.14 291.56 47258227.0
65 63 2018-10-03 292.74 293.21 291.32 291.72 64694594.0
66 64 2018-10-04 291.18 291.24 287.66 289.44 111545910.0
67 65 2018-10-05 289.69 290.27 286.22 287.82 105951698.0
68 66 2018-10-08 287.05 288.22 285.5 287.82 87742172.0
69 67 2018-10-09 287.39 288.86 286.77 287.4 74338982.0
70 68 2018-10-10 286.83 286.91 277.88 278.3 214731000.0
71 69 2018-10-11 277.08 278.9 270.36 272.17 274840491.0
72 70 2018-10-12 276.77 277.09 272.37 275.95 183186492.0
73 71 2018-10-15 275.55 277.04 274.3 274.4 102263717.0
74 72 2018-10-16 276.6 280.82 276.07 280.4 118255834.0
75 73 2018-10-17 280.44 281.15 277.56 280.45 110625987.0
76 74 2018-10-18 279.4 280.07 274.97 276.4 134557525.0
77 75 2018-10-19 277.13 279.3 275.47 276.25 139901634.0
78 76 2018-10-22 277.0 277.36 274.41 275.01 82415812.0
79 77 2018-10-23 270.95 274.87 268.61 273.61 146352719.0
80 78 2018-10-24 273.33 273.76 264.7 265.32 177806697.0
81 79 2018-10-25 267.38 271.81 266.23 270.08 138061545.0
82 80 2018-10-26 265.92 271.0 262.29 265.33 201574596.0
83 81 2018-10-29 268.8 270.25 259.85 263.86 160749101.0
84 82 2018-10-30 263.67 268.12 263.12 267.77 157115995.0
85 83 2018-10-31 270.65 273.23 270.12 270.63 128296325.0
86 84 2018-11-01 271.6 273.73 270.38 273.51 99495037.0
87 85 2018-11-02 274.75 275.23 269.59 271.89 122634107.0
88 86 2018-11-05 272.44 274.01 271.35 273.39 65622482.0
89 87 2018-11-06 273.32 275.3 273.25 275.12 60085894.0
90 88 2018-11-07 277.56 281.1 277.08 281.01 102752099.0
91 89 2018-11-08 280.11 281.22 279.22 280.5 65584886.0
92 90 2018-11-09 279.03 279.24 276.18 277.76 98812561.0
93 91 2018-11-12 277.19 277.46 271.99 272.57 99673574.0
94 92 2018-11-13 273.09 275.325 271.25 272.06 98176610.0
95 93 2018-11-14 274.16 274.61 268.45 270.2 125335931.0
96 94 2018-11-15 268.78 273.54 267.0102 273.02 135101437.0
97 95 2018-11-16 271.79 274.75 271.21 273.73 126668040.0
98 96 2018-11-19 273.05 273.38 268.07 269.1 103061706.0
99 97 2018-11-20 265.36 267.0 263.15 264.12 136021311.0
100 98 2018-11-21 265.86 267.15 265.01 265.02 75563743.0
101 99 2018-11-23 263.18 264.8234 263.07 263.25 42807878.0
Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 262 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

@@ -1 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 96 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-miterlimit:1;"><g id="pandas-ta"><g transform="matrix(0.732166,0,0,1,-4.21259,-3.55271e-15)"><g transform="matrix(21.0163,0,0,21.0163,134.752,20.2035)"></g><text x="37.089px" y="20.204px" style="font-family:'Lato-Bold', 'Lato', sans-serif;font-weight:700;font-size:21.016px;fill:#fff;stroke:#000;stroke-width:0.54px;">P<tspan x="49.699px " y="20.204px ">a</tspan>ndas T<tspan x="119.83px " y="20.204px ">A</tspan></text></g></g><g id="logo"><rect id="bg" x="0.96" y="1.92" width="20.16" height="20.16" style="fill:#fff;"/><path d="M21.12,1.92l0,20.16l-20.16,0l-0,-20.16l20.16,0Zm-0.504,0.504l-19.152,-0l-0,19.152l19.152,0l0,-19.152Z"/><g id="bars"><rect id="_1" serif:id="1" x="2.88" y="12.48" width="3.436" height="2.88" style="fill:#ef5552;"/><rect id="_2" serif:id="2" x="7.175" y="15.36" width="3.436" height="4.8" style="fill:#ef5552;"/><rect id="_3" serif:id="3" x="11.469" y="11.52" width="3.436" height="2.88" style="fill:#4cae4f;"/><rect id="_4" serif:id="4" x="15.764" y="3.84" width="3.436" height="6.72" style="fill:#4cae4f;"/></g><path d="M2.88,14.016c0,-0 3.474,6.213 8.589,3.744c6.51,-3.142 7.731,-9.12 7.731,-9.12" style="fill:none;stroke:#0d28f2;stroke-width:0.45px;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:1.5;"/></g></svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

@@ -1,72 +0,0 @@
[project]
name = "pandas-ta"
version = "0.4.71b"
description = "A Comprehensive Python 3 Technical Analysis Library with Pandas Dataframe Extension for Quantitative Researchers, Traders, and Investors."
authors = [{ name = "Pandas TA Support", email = "support@pandas-ta.dev" }]
requires-python = ">=3.12"
dependencies = [
"numba==0.61.2",
"numpy>=2.2.6",
"pandas>=2.3.2",
"tqdm>=4.67.1",
]
readme = { file = "README.md", content-type = "text/markdown" }
license-files = ["LICENSE"]
keywords = [
"library",
"technical analysis",
"ta",
"indicators",
"pandas",
"dataframe extension",
"finance",
"trading",
"machine learning",
"ai",
]
classifiers = [
"Development Status :: 4 - Beta",
"Programming Language :: Python :: 3.12",
"Operating System :: OS Independent",
"Natural Language :: English",
"Intended Audience :: Developers",
"Intended Audience :: Financial and Insurance Industry",
"Intended Audience :: Science/Research",
"Topic :: Office/Business :: Financial",
"Topic :: Office/Business :: Financial :: Investment",
"Topic :: Scientific/Engineering",
"Topic :: Scientific/Engineering :: Information Analysis",
]
[project.urls]
Homepage = "https://www.pandas-ta.dev"
Documentation = "https://www.pandas-ta.dev/api"
Repository = "https://github.com/twopirllc/pandas-ta"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[dependency-groups]
dev = [
"bokeh>=3.7.3",
"cryptography>=45.0.6",
"ipython>=9.4.0",
"jupyterlab>=4.4.6",
"matplotlib>=3.10.5",
"pytest>=8.4.1",
"requests>=2.32.5",
"ta-lib==0.6.4",
"yfinance>=0.2.65",
]
docs = [
"mkdocs>=1.6.1",
"mkdocs-include-markdown-plugin>=7.1.6",
"mkdocs-macros-plugin>=1.3.9",
"mkdocs-material>=9.6.18",
"mkdocs-minify-plugin>=0.8.0",
"mkdocs-plotly-plugin>=0.1.3",
"mkdocs-redirects>=1.2.2",
"mkdocstrings-python>=1.17.0",
"ruff>=0.12.10",
]
@@ -1,383 +0,0 @@
# This file was autogenerated by uv via the following command:
# uv export --dev --no-hashes -vn -o requirements.dev.txt
-e .
anyio==4.10.0
# via
# httpx
# jupyter-server
appnope==0.1.4 ; sys_platform == 'darwin'
# via ipykernel
argon2-cffi==25.1.0
# via jupyter-server
argon2-cffi-bindings==21.2.0 ; python_full_version >= '3.14'
# via argon2-cffi
argon2-cffi-bindings==25.1.0 ; python_full_version < '3.14'
# via argon2-cffi
arrow==1.3.0
# via isoduration
asttokens==3.0.0
# via stack-data
async-lru==2.0.5
# via jupyterlab
attrs==25.3.0
# via
# jsonschema
# referencing
babel==2.17.0
# via jupyterlab-server
beautifulsoup4==4.13.5
# via
# nbconvert
# yfinance
bleach==6.2.0
# via nbconvert
bokeh==3.7.3
certifi==2025.8.3
# via
# curl-cffi
# httpcore
# httpx
# requests
cffi==1.17.1
# via
# argon2-cffi-bindings
# cryptography
# curl-cffi
# pyzmq
charset-normalizer==3.4.3
# via requests
colorama==0.4.6 ; sys_platform == 'win32'
# via
# ipython
# pytest
# tqdm
comm==0.2.3
# via ipykernel
contourpy==1.3.3
# via
# bokeh
# matplotlib
cryptography==45.0.6
curl-cffi==0.13.0
# via yfinance
cycler==0.12.1
# via matplotlib
debugpy==1.8.16
# via ipykernel
decorator==5.2.1
# via ipython
defusedxml==0.7.1
# via nbconvert
executing==2.2.0
# via stack-data
fastjsonschema==2.21.2
# via nbformat
fonttools==4.59.1
# via matplotlib
fqdn==1.5.1
# via jsonschema
frozendict==2.4.6
# via yfinance
h11==0.16.0
# via httpcore
httpcore==1.0.9
# via httpx
httpx==0.28.1
# via jupyterlab
idna==3.10
# via
# anyio
# httpx
# jsonschema
# requests
iniconfig==2.1.0
# via pytest
ipykernel==6.30.1
# via jupyterlab
ipython==9.4.0
# via ipykernel
ipython-pygments-lexers==1.1.1
# via ipython
isoduration==20.11.0
# via jsonschema
jedi==0.19.2
# via ipython
jinja2==3.1.6
# via
# bokeh
# jupyter-server
# jupyterlab
# jupyterlab-server
# nbconvert
json5==0.12.1
# via jupyterlab-server
jsonpointer==3.0.0
# via jsonschema
jsonschema==4.25.1
# via
# jupyter-events
# jupyterlab-server
# nbformat
jsonschema-specifications==2025.4.1
# via jsonschema
jupyter-client==8.6.3
# via
# ipykernel
# jupyter-server
# nbclient
jupyter-core==5.8.1
# via
# ipykernel
# jupyter-client
# jupyter-server
# jupyterlab
# nbclient
# nbconvert
# nbformat
jupyter-events==0.12.0
# via jupyter-server
jupyter-lsp==2.2.6
# via jupyterlab
jupyter-server==2.17.0
# via
# jupyter-lsp
# jupyterlab
# jupyterlab-server
# notebook-shim
jupyter-server-terminals==0.5.3
# via jupyter-server
jupyterlab==4.4.6
jupyterlab-pygments==0.3.0
# via nbconvert
jupyterlab-server==2.27.3
# via jupyterlab
kiwisolver==1.4.9
# via matplotlib
lark==1.2.2
# via rfc3987-syntax
llvmlite==0.44.0
# via numba
markupsafe==3.0.2
# via
# jinja2
# nbconvert
matplotlib==3.10.5
matplotlib-inline==0.1.7
# via
# ipykernel
# ipython
mistune==3.1.3
# via nbconvert
multitasking==0.0.12
# via yfinance
narwhals==2.2.0
# via bokeh
nbclient==0.10.2
# via nbconvert
nbconvert==7.16.6
# via jupyter-server
nbformat==5.10.4
# via
# jupyter-server
# nbclient
# nbconvert
nest-asyncio==1.6.0
# via ipykernel
notebook-shim==0.2.4
# via jupyterlab
numba==0.61.2
# via pandas-ta
numpy==2.2.6
# via
# bokeh
# contourpy
# matplotlib
# numba
# pandas
# pandas-ta
# ta-lib
# yfinance
packaging==25.0
# via
# bokeh
# ipykernel
# jupyter-events
# jupyter-server
# jupyterlab
# jupyterlab-server
# matplotlib
# nbconvert
# pytest
pandas==2.3.2
# via
# bokeh
# pandas-ta
# yfinance
pandocfilters==1.5.1
# via nbconvert
parso==0.8.5
# via jedi
peewee==3.18.2
# via yfinance
pexpect==4.9.0 ; sys_platform != 'emscripten' and sys_platform != 'win32'
# via ipython
pillow==11.3.0
# via
# bokeh
# matplotlib
platformdirs==4.3.8
# via
# jupyter-core
# yfinance
pluggy==1.6.0
# via pytest
prometheus-client==0.22.1
# via jupyter-server
prompt-toolkit==3.0.51
# via ipython
protobuf==6.32.0
# via yfinance
psutil==7.0.0
# via ipykernel
ptyprocess==0.7.0 ; os_name != 'nt' or (sys_platform != 'emscripten' and sys_platform != 'win32')
# via
# pexpect
# terminado
pure-eval==0.2.3
# via stack-data
pycparser==2.22
# via cffi
pygments==2.19.2
# via
# ipython
# ipython-pygments-lexers
# nbconvert
# pytest
pyparsing==3.2.3
# via matplotlib
pytest==8.4.1
python-dateutil==2.9.0.post0
# via
# arrow
# jupyter-client
# matplotlib
# pandas
python-json-logger==3.3.0
# via jupyter-events
pytz==2025.2
# via
# pandas
# yfinance
pywin32==311 ; platform_python_implementation != 'PyPy' and sys_platform == 'win32'
# via jupyter-core
pywinpty==3.0.0 ; os_name == 'nt'
# via
# jupyter-server
# jupyter-server-terminals
# terminado
pyyaml==6.0.2
# via
# bokeh
# jupyter-events
pyzmq==27.0.2
# via
# ipykernel
# jupyter-client
# jupyter-server
referencing==0.36.2
# via
# jsonschema
# jsonschema-specifications
# jupyter-events
requests==2.32.5
# via
# jupyterlab-server
# yfinance
rfc3339-validator==0.1.4
# via
# jsonschema
# jupyter-events
rfc3986-validator==0.1.1
# via
# jsonschema
# jupyter-events
rfc3987-syntax==1.1.0
# via jsonschema
rpds-py==0.27.0
# via
# jsonschema
# referencing
send2trash==1.8.3
# via jupyter-server
setuptools==80.9.0
# via
# jupyterlab
# ta-lib
six==1.17.0
# via
# python-dateutil
# rfc3339-validator
sniffio==1.3.1
# via anyio
soupsieve==2.7
# via beautifulsoup4
stack-data==0.6.3
# via ipython
ta-lib==0.6.4
terminado==0.18.1
# via
# jupyter-server
# jupyter-server-terminals
tinycss2==1.4.0
# via bleach
tornado==6.5.2
# via
# bokeh
# ipykernel
# jupyter-client
# jupyter-server
# jupyterlab
# terminado
tqdm==4.67.1
# via pandas-ta
traitlets==5.14.3
# via
# ipykernel
# ipython
# jupyter-client
# jupyter-core
# jupyter-events
# jupyter-server
# jupyterlab
# matplotlib-inline
# nbclient
# nbconvert
# nbformat
types-python-dateutil==2.9.0.20250822
# via arrow
typing-extensions==4.15.0
# via
# anyio
# beautifulsoup4
# referencing
tzdata==2025.2
# via pandas
uri-template==1.3.0
# via jsonschema
urllib3==2.5.0
# via requests
wcwidth==0.2.13
# via prompt-toolkit
webcolors==24.11.1
# via jsonschema
webencodings==0.5.1
# via
# bleach
# tinycss2
websocket-client==1.8.0
# via jupyter-server
websockets==15.0.1
# via yfinance
xyzservices==2025.4.0
# via bokeh
yfinance==0.2.65
@@ -1,383 +0,0 @@
# This file was autogenerated by uv via the following command:
# uv export --no-hashes -vn -o requirements.txt
-e .
anyio==4.10.0
# via
# httpx
# jupyter-server
appnope==0.1.4 ; sys_platform == 'darwin'
# via ipykernel
argon2-cffi==25.1.0
# via jupyter-server
argon2-cffi-bindings==21.2.0 ; python_full_version >= '3.14'
# via argon2-cffi
argon2-cffi-bindings==25.1.0 ; python_full_version < '3.14'
# via argon2-cffi
arrow==1.3.0
# via isoduration
asttokens==3.0.0
# via stack-data
async-lru==2.0.5
# via jupyterlab
attrs==25.3.0
# via
# jsonschema
# referencing
babel==2.17.0
# via jupyterlab-server
beautifulsoup4==4.13.5
# via
# nbconvert
# yfinance
bleach==6.2.0
# via nbconvert
bokeh==3.7.3
certifi==2025.8.3
# via
# curl-cffi
# httpcore
# httpx
# requests
cffi==1.17.1
# via
# argon2-cffi-bindings
# cryptography
# curl-cffi
# pyzmq
charset-normalizer==3.4.3
# via requests
colorama==0.4.6 ; sys_platform == 'win32'
# via
# ipython
# pytest
# tqdm
comm==0.2.3
# via ipykernel
contourpy==1.3.3
# via
# bokeh
# matplotlib
cryptography==45.0.6
curl-cffi==0.13.0
# via yfinance
cycler==0.12.1
# via matplotlib
debugpy==1.8.16
# via ipykernel
decorator==5.2.1
# via ipython
defusedxml==0.7.1
# via nbconvert
executing==2.2.0
# via stack-data
fastjsonschema==2.21.2
# via nbformat
fonttools==4.59.1
# via matplotlib
fqdn==1.5.1
# via jsonschema
frozendict==2.4.6
# via yfinance
h11==0.16.0
# via httpcore
httpcore==1.0.9
# via httpx
httpx==0.28.1
# via jupyterlab
idna==3.10
# via
# anyio
# httpx
# jsonschema
# requests
iniconfig==2.1.0
# via pytest
ipykernel==6.30.1
# via jupyterlab
ipython==9.4.0
# via ipykernel
ipython-pygments-lexers==1.1.1
# via ipython
isoduration==20.11.0
# via jsonschema
jedi==0.19.2
# via ipython
jinja2==3.1.6
# via
# bokeh
# jupyter-server
# jupyterlab
# jupyterlab-server
# nbconvert
json5==0.12.1
# via jupyterlab-server
jsonpointer==3.0.0
# via jsonschema
jsonschema==4.25.1
# via
# jupyter-events
# jupyterlab-server
# nbformat
jsonschema-specifications==2025.4.1
# via jsonschema
jupyter-client==8.6.3
# via
# ipykernel
# jupyter-server
# nbclient
jupyter-core==5.8.1
# via
# ipykernel
# jupyter-client
# jupyter-server
# jupyterlab
# nbclient
# nbconvert
# nbformat
jupyter-events==0.12.0
# via jupyter-server
jupyter-lsp==2.2.6
# via jupyterlab
jupyter-server==2.17.0
# via
# jupyter-lsp
# jupyterlab
# jupyterlab-server
# notebook-shim
jupyter-server-terminals==0.5.3
# via jupyter-server
jupyterlab==4.4.6
jupyterlab-pygments==0.3.0
# via nbconvert
jupyterlab-server==2.27.3
# via jupyterlab
kiwisolver==1.4.9
# via matplotlib
lark==1.2.2
# via rfc3987-syntax
llvmlite==0.44.0
# via numba
markupsafe==3.0.2
# via
# jinja2
# nbconvert
matplotlib==3.10.5
matplotlib-inline==0.1.7
# via
# ipykernel
# ipython
mistune==3.1.3
# via nbconvert
multitasking==0.0.12
# via yfinance
narwhals==2.2.0
# via bokeh
nbclient==0.10.2
# via nbconvert
nbconvert==7.16.6
# via jupyter-server
nbformat==5.10.4
# via
# jupyter-server
# nbclient
# nbconvert
nest-asyncio==1.6.0
# via ipykernel
notebook-shim==0.2.4
# via jupyterlab
numba==0.61.2
# via pandas-ta
numpy==2.2.6
# via
# bokeh
# contourpy
# matplotlib
# numba
# pandas
# pandas-ta
# ta-lib
# yfinance
packaging==25.0
# via
# bokeh
# ipykernel
# jupyter-events
# jupyter-server
# jupyterlab
# jupyterlab-server
# matplotlib
# nbconvert
# pytest
pandas==2.3.2
# via
# bokeh
# pandas-ta
# yfinance
pandocfilters==1.5.1
# via nbconvert
parso==0.8.5
# via jedi
peewee==3.18.2
# via yfinance
pexpect==4.9.0 ; sys_platform != 'emscripten' and sys_platform != 'win32'
# via ipython
pillow==11.3.0
# via
# bokeh
# matplotlib
platformdirs==4.3.8
# via
# jupyter-core
# yfinance
pluggy==1.6.0
# via pytest
prometheus-client==0.22.1
# via jupyter-server
prompt-toolkit==3.0.51
# via ipython
protobuf==6.32.0
# via yfinance
psutil==7.0.0
# via ipykernel
ptyprocess==0.7.0 ; os_name != 'nt' or (sys_platform != 'emscripten' and sys_platform != 'win32')
# via
# pexpect
# terminado
pure-eval==0.2.3
# via stack-data
pycparser==2.22
# via cffi
pygments==2.19.2
# via
# ipython
# ipython-pygments-lexers
# nbconvert
# pytest
pyparsing==3.2.3
# via matplotlib
pytest==8.4.1
python-dateutil==2.9.0.post0
# via
# arrow
# jupyter-client
# matplotlib
# pandas
python-json-logger==3.3.0
# via jupyter-events
pytz==2025.2
# via
# pandas
# yfinance
pywin32==311 ; platform_python_implementation != 'PyPy' and sys_platform == 'win32'
# via jupyter-core
pywinpty==3.0.0 ; os_name == 'nt'
# via
# jupyter-server
# jupyter-server-terminals
# terminado
pyyaml==6.0.2
# via
# bokeh
# jupyter-events
pyzmq==27.0.2
# via
# ipykernel
# jupyter-client
# jupyter-server
referencing==0.36.2
# via
# jsonschema
# jsonschema-specifications
# jupyter-events
requests==2.32.5
# via
# jupyterlab-server
# yfinance
rfc3339-validator==0.1.4
# via
# jsonschema
# jupyter-events
rfc3986-validator==0.1.1
# via
# jsonschema
# jupyter-events
rfc3987-syntax==1.1.0
# via jsonschema
rpds-py==0.27.0
# via
# jsonschema
# referencing
send2trash==1.8.3
# via jupyter-server
setuptools==80.9.0
# via
# jupyterlab
# ta-lib
six==1.17.0
# via
# python-dateutil
# rfc3339-validator
sniffio==1.3.1
# via anyio
soupsieve==2.7
# via beautifulsoup4
stack-data==0.6.3
# via ipython
ta-lib==0.6.4
terminado==0.18.1
# via
# jupyter-server
# jupyter-server-terminals
tinycss2==1.4.0
# via bleach
tornado==6.5.2
# via
# bokeh
# ipykernel
# jupyter-client
# jupyter-server
# jupyterlab
# terminado
tqdm==4.67.1
# via pandas-ta
traitlets==5.14.3
# via
# ipykernel
# ipython
# jupyter-client
# jupyter-core
# jupyter-events
# jupyter-server
# jupyterlab
# matplotlib-inline
# nbclient
# nbconvert
# nbformat
types-python-dateutil==2.9.0.20250822
# via arrow
typing-extensions==4.15.0
# via
# anyio
# beautifulsoup4
# referencing
tzdata==2025.2
# via pandas
uri-template==1.3.0
# via jsonschema
urllib3==2.5.0
# via requests
wcwidth==0.2.13
# via prompt-toolkit
webcolors==24.11.1
# via jsonschema
webencodings==0.5.1
# via
# bleach
# tinycss2
websocket-client==1.8.0
# via jupyter-server
websockets==15.0.1
# via yfinance
xyzservices==2025.4.0
# via bokeh
yfinance==0.2.65
@@ -1,65 +0,0 @@
# -*- coding: utf-8 -*-
from importlib.metadata import version
version = version("pandas-ta")
from pandas_ta.maps import EXCHANGE_TZ, RATE, Category, Imports
from pandas_ta.utils import *
from pandas_ta.utils import __all__ as utils_all
# Flat Structure. Supports ta.ema() or ta.overlap.ema()
from pandas_ta.candle import *
from pandas_ta.cycle import *
from pandas_ta.momentum import *
from pandas_ta.overlap import *
from pandas_ta.performance import *
from pandas_ta.statistics import *
from pandas_ta.trend import *
from pandas_ta.volatility import *
from pandas_ta.volume import *
from pandas_ta.candle import __all__ as candle_all
from pandas_ta.cycle import __all__ as cycle_all
from pandas_ta.momentum import __all__ as momentum_all
from pandas_ta.overlap import __all__ as overlap_all
from pandas_ta.performance import __all__ as performance_all
from pandas_ta.statistics import __all__ as statistics_all
from pandas_ta.trend import __all__ as trend_all
from pandas_ta.volatility import __all__ as volatility_all
from pandas_ta.volume import __all__ as volume_all
# Common Averages useful for Indicators
# with a mamode argument, like ta.adx()
from pandas_ta.ma import ma
# Custom External Directory Commands. See help(import_dir)
from pandas_ta.custom import create_dir, import_dir
# Enable "ta" DataFrame Extension
from pandas_ta.core import AnalysisIndicators
__all__ = [
# "name",
"EXCHANGE_TZ",
"RATE",
"Category",
"Imports",
"version",
"ma",
"create_dir",
"import_dir",
"AnalysisIndicators",
"AllStudy",
"CommonStudy",
]
__all__ += [
utils_all
+ candle_all
+ cycle_all
+ momentum_all
+ overlap_all
+ performance_all
+ statistics_all
+ trend_all
+ volatility_all
+ volume_all
]
@@ -1,7 +0,0 @@
#-*- coding: utf-8 -*-
from pandas_ta import version
SUPPORT="http://www.pandas-ta.dev/support"
if __name__ == "__main__":
print(f"Pandas TA: {version}\nSupport: {SUPPORT}")
@@ -1,71 +0,0 @@
# -*- coding: utf-8 -*-
from pathlib import Path
from typing import (
Any,
Dict,
Iterable,
List,
Optional,
Sequence,
TextIO,
Tuple,
TypeVar,
Union
)
from numpy import ndarray, recarray, void
from numpy import bool_ as np_bool_
from numpy import floating as np_floating
from numpy import generic as np_generic
from numpy import integer as np_integer
from numpy import number as np_number
from pandas import DataFrame, Series
# Generic types
T = TypeVar("T")
# Scalars
Scalar = Union[str, float, int, complex, bool, object, np_generic]
Number = Union[int, float, complex, np_number, np_bool_]
Int = Union[int, np_integer]
Float = Union[float, np_floating]
IntFloat = Union[Int, Float]
# Basic sequences
MaybeTuple = Union[T, Tuple[T, ...]]
MaybeList = Union[T | List[T]]
TupleList = Union[List[T], Tuple[T, ...]]
MaybeTupleList = Union[T, List[T], Tuple[T, ...]]
MaybeIterable = Union[T, Iterable[T]]
MaybeSequence = Union[T, Sequence[T]]
ListStr = List[str]
DictLike = Union[None, dict]
DictLikeSequence = MaybeSequence[DictLike]
Args = Tuple[Any, ...]
ArgsLike = Union[None, Args]
Kwargs = Dict[str, Any]
KwargsLike = Union[None, Kwargs]
KwargsLikeSequence = MaybeSequence[KwargsLike]
FileName = Union[str, Path]
DTypeLike = Any
PandasDTypeLike = Any
Shape = Tuple[int, ...]
RelaxedShape = Union[int, Shape]
Array = ndarray
Array1d = ndarray
Array2d = ndarray
Array3d = ndarray
Record = void
RecordArray = ndarray
RecArray = recarray
MaybeArray = Union[T, Array]
SeriesFrame = Union[Series, DataFrame]
MaybeSeries = Union[T, Series]
MaybeSeriesFrame = Union[T, Series, DataFrame]
AnyArray = Union[Array, Series, DataFrame]
AnyArray1d = Union[Array1d, Series]
AnyArray2d = Union[Array2d, DataFrame]
@@ -1,16 +0,0 @@
# -*- coding: utf-8 -*-
from .cdl_doji import cdl_doji
from .cdl_inside import cdl_inside
from .cdl_pattern import cdl_pattern, cdl, ALL_PATTERNS as CDL_PATTERN_NAMES
from .cdl_z import cdl_z
from .ha import ha
__all__ = [
"cdl_doji",
"cdl_inside",
"cdl_pattern",
"cdl",
"CDL_PATTERN_NAMES",
"cdl_z",
"ha",
]
@@ -1,90 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import Series
from pandas_ta.overlap import sma
from pandas_ta._typing import DictLike, Int, IntFloat
from pandas_ta.utils import high_low_range, v_percent
from pandas_ta.utils import real_body, v_offset, v_pos_default
from pandas_ta.utils import v_bool, v_scalar, v_series
def cdl_doji(
open_: Series, high: Series, low: Series, close: Series,
length: Int = None, factor: IntFloat = None,
scalar: IntFloat = None, asint: bool = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Doji
Attempts to identify a "Doji" candle which is shorter than 10% of
the average of the 10 previous bars High-Low range.
Sources:
* [TA Lib](https://github.com/TA-Lib/ta-lib/blob/main/src/ta_func/ta_CDLDOJI.c)
Parameters:
open_ (Series): ```open``` Series
high (Series): ```high``` Series
low (Series): ```low``` Series
close (Series): ```close``` Series
length (int): The period. Default: ```10```
factor (float): Doji value. Default: ```100```
scalar (float): Scalar. Default: ```100```
asint (bool): Returns as ```Int```. Default: ```True```
offset (int): Post shift. Default: ```0```
Other Parameters:
naive (bool): Prefills potential Doji; bodies that are less
than a percentage, ```factor```, of it's High-Low range.
Default: ```False```
fillna (value): Replaces ```na```'s with ```value```.
Returns:
(Series): 1 column
Warning:
TA-Lib Correlation: ```np.float64(0.9434563530497265)```
Tip:
Corrective contributions welcome!
"""
# Validate
length = v_pos_default(length, 10)
open_ = v_series(open_, length)
high = v_series(high, length)
low = v_series(low, length)
close = v_series(close, length)
if open_ is None or high is None or low is None or close is None:
return
factor = v_scalar(factor, 10) if v_percent(factor) else 10
scalar = v_scalar(scalar, 100)
asint = v_bool(asint, True)
offset = v_offset(offset)
naive = kwargs.pop("naive", False)
# Calculate
body = real_body(open_, close).abs()
hl_range = high_low_range(high, low).abs()
hl_range_avg = sma(hl_range, length)
doji = body < 0.01 * factor * hl_range_avg
if naive:
doji.iat[:length] = body < 0.01 * factor * hl_range
if asint:
doji = scalar * doji.astype(int)
# Offset
if offset != 0:
doji = doji.shift(offset)
# Fill
if "fillna" in kwargs:
doji.fillna(kwargs["fillna"], inplace=True)
# Name and Category
doji.name = f"CDL_DOJI_{length}_{0.01 * factor}"
doji.category = "candle"
return doji
@@ -1,84 +0,0 @@
# -*- coding: utf-8 -*-
from numba import njit
from numpy import roll, where
from pandas import Series
from pandas_ta._typing import DictLike, Int, IntFloat
from pandas_ta.utils import (
v_bool,
v_offset,
v_offset,
v_scalar,
v_series
)
@njit(cache=True)
def np_cdl_inside(high, low):
hdiff = where(high - roll(high, 1) < 0, 1, 0)
ldiff = where(low - roll(low, 1) > 0, 1, 0)
return hdiff & ldiff
def cdl_inside(
open_: Series, high: Series, low: Series, close: Series,
asbool: bool = None, scalar: IntFloat = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Inside Bar
Attempts to identify an "Inside" candle which is smaller than it's
previous candle.
Sources:
* [TA Lib](https://github.com/TA-Lib/ta-lib/blob/main/src/ta_func/ta_CDL3INSIDE.c)
* [tradingview](https://www.tradingview.com/script/IyIGN1WO-Inside-Bar/)
Parameters:
open_ (Series): ```open``` Series
high (Series): ```high``` Series
low (Series): ```low``` Series
close (Series): ```close``` Series
asbool (bool): Return booleans. Default: ```False```
scalar (float): Scalar. Default: ```100```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): Replaces ```na```'s with ```value```.
Returns:
(Series): 1 column
"""
# Validate
open_ = v_series(open_)
high = v_series(high)
low = v_series(low)
close = v_series(close)
if open_ is None or high is None or low is None or close is None:
return
asbool = v_bool(asbool, False)
scalar = v_scalar(scalar, 100)
offset = v_offset(offset)
# Calculate
np_high, np_low = high.to_numpy(), low.to_numpy()
np_inside = np_cdl_inside(np_high, np_low)
inside = Series(np_inside, index=close.index, dtype=bool)
if not asbool:
inside = scalar * inside.astype(int)
# Offset
if offset != 0:
inside = inside.shift(offset)
# Fill
if "fillna" in kwargs:
inside.fillna(kwargs["fillna"], inplace=True)
# Name and Category
inside.name = f"CDL_INSIDE"
inside.category = "candle"
return inside
@@ -1,126 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import Series, DataFrame
from pandas_ta._typing import DictLike, Int, IntFloat, List
from pandas_ta.maps import Imports
from pandas_ta.utils import v_offset, v_scalar, v_series
from pandas_ta.candle import cdl_doji, cdl_inside
ALL_PATTERNS = [
"2crows", "3blackcrows", "3inside", "3linestrike", "3outside",
"3starsinsouth", "3whitesoldiers", "abandonedbaby", "advanceblock",
"belthold", "breakaway", "closingmarubozu", "concealbabyswall",
"counterattack", "darkcloudcover", "doji", "dojistar", "dragonflydoji",
"engulfing", "eveningdojistar", "eveningstar", "gapsidesidewhite",
"gravestonedoji", "hammer", "hangingman", "harami", "haramicross",
"highwave", "hikkake", "hikkakemod", "homingpigeon", "identical3crows",
"inneck", "inside", "invertedhammer", "kicking", "kickingbylength",
"ladderbottom", "longleggeddoji", "longline", "marubozu", "matchinglow",
"mathold", "morningdojistar", "morningstar", "onneck", "piercing",
"rickshawman", "risefall3methods", "separatinglines", "shootingstar",
"shortline", "spinningtop", "stalledpattern", "sticksandwich", "takuri",
"tasukigap", "thrusting", "tristar", "unique3river", "upsidegap2crows",
"xsidegap3methods"
]
def cdl_pattern(
open_: Series, high: Series, low: Series, close: Series,
name: str | List[str] = "all",
scalar: IntFloat = None,
offset: Int = None, **kwargs: DictLike
) -> DataFrame:
"""Candle Pattern
This function wraps TA Lib candle patterns.
Sources:
* [TA Lib](https://ta-lib.org)
Parameters:
open_ (Series): ```open``` Series
high (Series): ```high``` Series
low (Series): ```low``` Series
close (Series): ```close``` Series
name (str | List[str]): Pattern name or a list of pattern names.
Default: ```"all"```
scalar (float): Scalar. Default: ```100```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): Replaces ```na```'s with ```value```.
Returns:
(DataFrame): Pattern Column(s)
Warning: TA Lib
TA Lib must be installed
"""
# Validate Arguments
open_ = v_series(open_, 1)
high = v_series(high, 1)
low = v_series(low, 1)
close = v_series(close, 1)
if open_ is None or high is None or low is None or close is None:
return
offset = v_offset(offset)
scalar = v_scalar(scalar, 100)
pta_patterns = {"doji": cdl_doji, "inside": cdl_inside}
if name == "all":
name = ALL_PATTERNS
if isinstance(name, str):
name = [name]
if Imports["talib"]:
import talib.abstract as tala
result = {}
for n in name:
if n not in ALL_PATTERNS:
print(f"[X] There is no candle pattern named {n} available!")
continue
if n in pta_patterns:
pattern_result = pta_patterns[n](
open_, high, low, close, offset=offset, scalar=scalar, **kwargs
)
if not isinstance(pattern_result, Series):
continue
result[pattern_result.name] = pattern_result
else:
if not Imports["talib"]:
print(f"[i] Requires TA-Lib to use {n}. (pip install TA-Lib)")
continue
pf = tala.Function(f"CDL{n.upper()}")
pattern_result = Series(
0.01 * scalar * pf(open_, high, low, close, **kwargs)
)
pattern_result.index = close.index
# Offset
if offset != 0:
pattern_result = pattern_result.shift(offset)
# Fill
if "fillna" in kwargs:
pattern_result.fillna(kwargs["fillna"], inplace=True)
result[f"CDL_{n.upper()}"] = pattern_result
if len(result) == 0:
return
# Name and Category
df = DataFrame(result)
df.name = "CDL_PATTERN"
df.category = "candle"
return df
cdl = cdl_pattern # Alias
@@ -1,93 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import DataFrame, Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.statistics import zscore
from pandas_ta.utils import v_bool, v_offset, v_pos_default, v_series
def cdl_z(
open_: Series, high: Series, low: Series, close: Series,
length: Int = None, full: bool = None, ddof: Int = None,
offset: Int = None, **kwargs: DictLike
) -> DataFrame:
"""Z Candles
Creates candlesticks using a rolling Z Score.
Sources:
* Kevin Johnson
Parameters:
open_ (Series): ```open``` Series
high (Series): ```high``` Series
low (Series): ```low``` Series
close (Series): ```close``` Series
length (int): The period. Default: ```10```
full (bool): Apply ```length``` to whole DataFrame.
Default: ```False```
ddof (int): By default, uses Pandas ```ddof=1```.
For Numpy calculation, use ```0```. Default: ```1```
offset (int): Post shift. Default: ```0```
Other Parameters:
naive (bool): If ```True```, prefills potential Doji less
than the length if it less than a percentage of it's
High-Low range. Default: ```False```
fillna (value): Replaces ```na```'s with ```value```.
Returns:
(DataFrame): 4 columns
Note:
* Numpy ```std()``` [ddof](https://numpy.org/doc/stable/reference/generated/numpy.std.html) explanation.
"""
# Validate
length = v_pos_default(length, 30)
open_ = v_series(open_, length)
high = v_series(high, length)
low = v_series(low, length)
close = v_series(close, length)
if open_ is None or high is None or low is None or close is None:
return
full = v_bool(full, False)
ddof = int(ddof) if isinstance(ddof, int) and 0 <= ddof < length else 1
offset = v_offset(offset)
# Calculate
if full:
length = close.size
z_open = zscore(open_, length=length, ddof=ddof)
z_high = zscore(high, length=length, ddof=ddof)
z_low = zscore(low, length=length, ddof=ddof)
z_close = zscore(close, length=length, ddof=ddof)
_full = "a" if full else ""
_props = _full if full else f"_{length}_{ddof}"
data = {
f"open_Z{_props}": z_open,
f"high_Z{_props}": z_high,
f"low_Z{_props}": z_low,
f"close_Z{_props}": z_close,
}
df = DataFrame(data, index=close.index)
if full:
df.fillna(method="backfill", axis=0, inplace=True)
# Offset
if offset != 0:
df = df.shift(offset)
# Fill
if "fillna" in kwargs:
df.fillna(kwargs["fillna"], inplace=True)
# Name and Category
df.name = f"CDL_Z{_props}"
df.category = "candle"
return df
@@ -1,86 +0,0 @@
# -*- coding: utf-8 -*-
from numba import njit
from numpy import empty_like, maximum, minimum
from pandas import DataFrame, Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.utils import v_offset, v_series
@njit(cache=True)
def np_ha(np_open, np_high, np_low, np_close):
ha_close = 0.25 * (np_open + np_high + np_low + np_close)
ha_open = empty_like(ha_close)
ha_open[0] = 0.5 * (np_open[0] + np_close[0])
m = np_close.size
for i in range(1, m):
ha_open[i] = 0.5 * (ha_open[i - 1] + ha_close[i - 1])
ha_high = maximum(maximum(ha_open, ha_close), np_high)
ha_low = minimum(minimum(ha_open, ha_close), np_low)
return ha_open, ha_high, ha_low, ha_close
def ha(
open_: Series, high: Series, low: Series, close: Series,
offset: Int = None, **kwargs: DictLike
) -> DataFrame:
"""Heikin Ashi Candles
Creates Japanese _ohlc_ candlesticks that attempts to filter out market
noise. Developed by Munehisa Homma in the 1700s, Heikin Ashi Candles share
some characteristics with standard candlestick charts but creates a
smoother candlestick appearance.
Sources:
* [Investopedia](https://www.investopedia.com/terms/h/heikinashi.asp)
Parameters:
open_ (Series): ```open``` Series
high (Series): ```high``` Series
low (Series): ```low``` Series
close (Series): ```close``` Series
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): Replaces ```na```'s with ```value```.
Returns:
(DataFrame): 4 columns
"""
# Validate
open_ = v_series(open_, 1)
high = v_series(high, 1)
low = v_series(low, 1)
close = v_series(close, 1)
offset = v_offset(offset)
if open_ is None or high is None or low is None or close is None:
return
# Calculate
np_open, np_high = open_.to_numpy(), high.to_numpy()
np_low, np_close = low.to_numpy(), close.to_numpy()
ha_open, ha_high, ha_low, ha_close = np_ha(np_open, np_high, np_low, np_close)
df = DataFrame({
"HA_open": ha_open,
"HA_high": ha_high,
"HA_low": ha_low,
"HA_close": ha_close,
}, index=close.index)
# Offset
if offset != 0:
df = df.shift(offset)
# Fill
if "fillna" in kwargs:
df.fillna(kwargs["fillna"], inplace=True)
# Name and Category
df.name = "Heikin-Ashi"
df.category = "candle"
return df
File diff suppressed because it is too large Load Diff
@@ -1,177 +0,0 @@
# -*- coding: utf-8 -*-
import importlib
import os
import sys
import types
from glob import glob
from os.path import abspath, basename, exists, join, splitext
import pandas_ta
from pandas_ta._typing import DictLike
def bind(name: str, fn: types.FunctionType, method: types.MethodType = None):
"""Bind
Helper function to bind the function and class method defined in a custom
indicator module to the active pandas_ta instance.
Parameters:
name (str): The name of the indicator within pandas_ta
fn (types.FunctionType): The indicator function
method (types.MethodType): The class method corresponding to the passed function
"""
setattr(pandas_ta, name, fn)
setattr(pandas_ta.AnalysisIndicators, name, method)
def create_dir(path: str, categories: bool = True, verbose: bool = True):
"""Create Dir
Sets up a suitable folder structure for working with custom indicators.
Use it **once** to setup and initialize the custom folder.
Parameters:
path (str): Indicator directory full path
categories (bool): Create category sub-folders
verbose (bool): Verbose output
"""
# ensure that the passed directory exists / is readable
if not exists(path):
os.makedirs(path)
if verbose:
print(f"[i] Created main directory '{path}'.")
# list the contents of the directory
# dirs = glob(abspath(join(path, '*')))
# optionally add any missing category subdirectories
if categories:
for _ in [*pandas_ta.Category]:
d = abspath(join(path, _))
if not exists(d):
os.makedirs(d)
if verbose:
dirname = basename(d)
print(f"[i] Created an empty sub-directory '{dirname}'.")
def get_module_functions(module: types.ModuleType) -> DictLike:
"""Get Module Functions
Returns a dictionary with the mapping: "name" to a _function_.
Parameters:
module (types.ModuleType): python module
Returns:
(DictLike): Returns a dictionary with the mapping: "name" to a _function_
Example:
Example return
```py
{
"func1_name": func1,
"func2_name": func2, # ...
}
```
"""
module_functions = {}
for name, item in vars(module).items():
if isinstance(item, types.FunctionType):
module_functions[name] = item
return module_functions
def import_dir(path: str, verbose: bool = True):
"""Import Dir
Import a directory of custom (proprietary) indicators into Pandas TA.
Parameters:
path (str): Full path to indicator directory.
verbose (bool): Output process to STDOUT.
"""
# ensure that the passed directory exists / is readable
if not exists(path):
print(f"[X] Unable to read the directory '{path}'.")
return
# list the contents of the directory
dirs = glob(abspath(join(path, "*")))
# traverse full directory, importing all modules found there
for d in dirs:
dirname = basename(d)
# only look in directories which are valid pandas_ta categories
if dirname not in [*pandas_ta.Category]:
if verbose and dirname not in ["__pycache__", "__init__.py"]:
print(
f"[i] Skipping the sub-directory '{dirname}' since it's not a valid pandas_ta category."
)
continue
# for each module found in that category (directory)...
for module in glob(abspath(join(path, dirname, "*.py"))):
module_name = splitext(basename(module))[0]
if module_name not in ["__init__"]:
# ensure that the supplied path is included in our python path
if d not in sys.path:
sys.path.append(d)
# (re)load the indicator module
module_functions = load_indicator_module(module_name)
# figure out which of the modules functions to bind to pandas_ta
_callable = module_functions.get(module_name, None)
_method_callable = module_functions.get(f"{module_name}_method", None)
if _callable == None:
print(
f"[X] Unable to find a function named '{module_name}' in the module '{module_name}.py'."
)
continue
if _method_callable == None:
missing_method = f"{module_name}_method"
print(
f"[X] Unable to find a method function named '{missing_method}' in the module '{module_name}.py'."
)
continue
# add it to the correct category if it's not there yet
if module_name not in pandas_ta.Category[dirname]:
pandas_ta.Category[dirname].append(module_name)
bind(module_name, _callable, _method_callable)
if verbose:
print(
f"[i] Successfully imported the custom indicator '{module}' into category '{dirname}'."
)
def load_indicator_module(name: str) -> dict:
"""
Helper function to (re)load an indicator module.
Returns:
dict: module functions mapping
```{
"func1_name": func1,
"func2_name": func2, # ...
}```
"""
try:
module = importlib.import_module(name)
except Exception as ex:
print(f"[X] An error occurred when attempting to load module {name}: {ex}")
sys.exit(1)
# reload to refresh previously loaded module
module = importlib.reload(module)
return get_module_functions(module)
@@ -1,8 +0,0 @@
# -*- coding: utf-8 -*-
from .ebsw import ebsw
from .reflex import reflex
__all__ = [
"ebsw",
"reflex",
]
@@ -1,142 +0,0 @@
# -*- coding: utf-8 -*-
from numpy import cos, exp, mean, nan, pi, roll, sin, sqrt, zeros
from pandas import Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.utils import v_bool, v_offset, v_pos_default, v_series
def ebsw(
close: Series, length: Int = None, bars: Int = None,
initial_version: bool = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Even Better SineWave
This indicator attempts to quantify market cycles using a low pass filter.
Sources:
* [rengel8](https://github.com/rengel8)
* J.F.Ehlers 'Cycle Analytics for Traders', 2014
* [Pandas TA Issue #350](https://github.com/twopirllc/pandas-ta/issues/350)
* [Proreal Code](https://www.prorealcode.com/prorealtime-indicators/even-better-sinewave/)
Parameters:
close (Series): ```close``` Series
length (int): Max cycle/trend period. Values between ```40-48``` work
as expected with minimum value: ```39```. Default: ```40```
bars (int): Period of low pass filtering. Default: ```10```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): Replaces ```na```'s with ```value```.
Returns:
(Series): 1 column
Note:
The _default_ is more cycle oriented and seems to be less
whipsaw-prune. The older version might offer earlier signals at medium
and stronger reversals. Compared to TradingView, returns very close
results but appears to be one bar earlier.
"""
# Validate
length = v_pos_default(length, 40)
close = v_series(close, length)
if close is None:
return
initial_version = v_bool(initial_version, False)
bars = v_pos_default(bars, 10)
offset = v_offset(offset)
# Calculate
# allow initial version to be used (more responsive/caution!)
m = close.size
if isinstance(initial_version, bool) and initial_version:
# not the default version that is active
alpha1 = hp = 0 # alpha and HighPass
a1 = b1 = c1 = c2 = c3 = 0
filter_ = power_ = wave = 0
lastClose = lastHP = 0
filtHist = [0, 0] # Filter history
result = [nan for _ in range(0, length - 1)] + [0]
for i in range(length, m):
# HighPass filter cyclic components whose periods are shorter than
# Duration input
alpha1 = (1 - sin(360 / length)) / cos(360 / length)
hp = 0.5 * (1 + alpha1) * (close.iloc[i] - lastClose) + alpha1 * lastHP
# Smooth with a Super Smoother Filter from equation 3-3
a1 = exp(-sqrt(2) * pi / bars)
b1 = 2 * a1 * cos(sqrt(2) * 180 / bars)
c2 = b1
c3 = -1 * a1 * a1
c1 = 1 - c2 - c3
filter_ = 0.5 * c1 * (hp + lastHP) + c2 * \
filtHist[1] + c3 * filtHist[0]
# filter_ = float("{:.8f}".format(float(filter_))) # to fix for
# small scientific notations, the big ones fail
# 3 Bar average of wave amplitude and power
wave = (filter_ + filtHist[1] + filtHist[0]) / 3
power_ = (filter_ * filter_ + filtHist[1] * filtHist[1] \
+ filtHist[0] * filtHist[0]) / 3
# Normalize the Average Wave to Square Root of the Average Power
wave = wave / sqrt(power_)
# update storage, result
filtHist.append(filter_) # append new filter_ value
# remove first element of list (left) -> updating/trim
filtHist.pop(0)
lastHP = hp
lastClose = close.iloc[i]
result.append(wave)
else: # Default
lastHP = lastClose = 0
filtHist = zeros(3)
result = [nan] * (length - 1) + [0]
angle = 2 * pi / length
alpha1 = (1 - sin(angle)) / cos(angle)
ang = 2 ** .5 * pi / bars
a1 = exp(-ang)
c2 = 2 * a1 * cos(ang)
c3 = -a1 ** 2
c1 = 1 - c2 - c3
for i in range(length, m):
hp = 0.5 * (1 + alpha1) * (close.iloc[i] - lastClose) + alpha1 * lastHP
# Rotate filters to overwrite oldest value
filtHist = roll(filtHist, -1)
filtHist[-1] = 0.5 * c1 * \
(hp + lastHP) + c2 * filtHist[1] + c3 * filtHist[0]
# Wave calculation
wave = mean(filtHist)
rms = sqrt(mean(filtHist ** 2))
wave = wave / rms
# Update past values
lastHP = hp
lastClose = close.iloc[i]
result.append(wave)
ebsw = Series(result, index=close.index)
# Offset
if offset != 0:
ebsw = ebsw.shift(offset)
# Fill
if "fillna" in kwargs:
ebsw.fillna(kwargs["fillna"], inplace=True)
# Name and Category
ebsw.name = f"EBSW_{length}_{bars}"
ebsw.category = "cycle"
return ebsw
@@ -1,115 +0,0 @@
# -*- coding: utf-8 -*-
from numba import njit
from numpy import cos, exp, nan, sqrt, zeros_like
from pandas import Series
from pandas_ta._typing import DictLike, Int, IntFloat
from pandas_ta.utils import v_offset, v_pos_default, v_series
@njit(cache=True)
def np_reflex(x, n, k, alpha, pi, sqrt2):
m, ratio = x.size, 2 * sqrt2 / k
a = exp(-pi * ratio)
b = 2 * a * cos(180 * ratio)
c = a * a - b + 1
_f = zeros_like(x)
_ms = zeros_like(x)
result = zeros_like(x)
for i in range(2, m):
_f[i] = 0.5 * c * (x[i] + x[i - 1]) + b * _f[i - 1] - a * a * _f[i - 2]
for i in range(n, m):
slope = (_f[i - n] - _f[i]) / n
_sum = 0
for j in range(1, n):
_sum += _f[i] - _f[i - j] + j * slope
_sum /= n
_ms[i] = alpha * _sum * _sum + (1 - alpha) * _ms[i - 1]
if _ms[i] != 0.0:
result[i] = _sum / sqrt(_ms[i])
return result
def reflex(
close: Series, length: Int = None,
smooth: Int = None, alpha: IntFloat = None,
pi: IntFloat = None, sqrt2: IntFloat = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Reflex
This cycle indicator, by John F. Ehlers, attempts to reduce lag.
Sources:
* [rengel8](https://github.com/rengel8) (2021-08-11) based on the
implementation from "ProRealCode"
* [traders.com](http://traders.com/Documentation/FEEDbk_docs/2020/02/TradersTips.html)
* [prorealcode](https://www.prorealcode.com/prorealtime-indicators/reflex-and-trendflex-indicators-john-f-ehlers/)
Parameters:
close (Series): ```close``` Series
length (int): The period. Default: ```20```
smooth (int): SuperSmoother period. Default: ```20```
alpha (float): Alpha weight of Difference Sums. Default: ```0.04```
pi (float): Ehlers's truncated value: ```3.14159```.
Default: ```3.14159```
sqrt2 (float): Ehlers's truncated value: ```1.414```.
Default: ```1.414```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): Replaces ```na```'s with ```value```.
Returns:
(Series): 1 column
Tip:
This implementation has a separate control parameter for the
internal applied SuperSmoother.
Note:
John F. Ehlers introduced two indicators within the article
"Reflex: A New Zero-Lag Indicator” in February 2020, TASC magazine.
One of which is Reflex, a lag reduced cycle indicator. Both indicators
(Reflex/Trendflex) are oscillators that complement each other with the
focus for cycle and trend.
"""
# Validate
length = v_pos_default(length, 20)
smooth = v_pos_default(smooth, 20)
_length = max(length, smooth) + 1
close = v_series(close, _length)
if close is None:
return
alpha = v_pos_default(alpha, 0.04)
pi = v_pos_default(pi, 3.14159)
sqrt2 = v_pos_default(sqrt2, 1.414)
offset = v_offset(offset)
# Calculate
np_close = close.to_numpy()
result = np_reflex(np_close, length, smooth, alpha, pi, sqrt2)
result[:length] = nan
result = Series(result, index=close.index)
# Offset
if offset != 0:
result = result.shift(offset)
# Fill
if "fillna" in kwargs:
result.fillna(kwargs["fillna"], inplace=True)
# Name and Category
result.name = f"REFLEX_{length}_{smooth}_{alpha}"
result.category = "cycle"
return result
@@ -1,75 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import Series
from pandas_ta._typing import DictLike
from pandas_ta.overlap.dema import dema
from pandas_ta.overlap.ema import ema
from pandas_ta.overlap.fwma import fwma
from pandas_ta.overlap.hma import hma
from pandas_ta.overlap.linreg import linreg
from pandas_ta.overlap.midpoint import midpoint
from pandas_ta.overlap.pwma import pwma
from pandas_ta.overlap.rma import rma
from pandas_ta.overlap.sinwma import sinwma
from pandas_ta.overlap.sma import sma
from pandas_ta.overlap.ssf import ssf
from pandas_ta.overlap.swma import swma
from pandas_ta.overlap.t3 import t3
from pandas_ta.overlap.tema import tema
from pandas_ta.overlap.trima import trima
from pandas_ta.overlap.vidya import vidya
from pandas_ta.overlap.wma import wma
def ma(name: str = None, source: Series = None, **kwargs: DictLike) -> Series:
"""MA Selection Utility
Available MAs: dema, ema, fwma, hma, linreg, midpoint, pwma, rma,
sinwma, sma, ssf, swma, t3, tema, trima, vidya, wma.
Parameters:
name (str): One of the Available MAs. Default: "ema"
source (Series): Input Series ```source```.
Other Parameters:
kwargs (**kwargs): Additional args for the MA.
Returns:
(Series): Selected MA
Esourceample:
```py linenums="0"
ema8 = ta.ma("ema", df.close, length=8)
sma50 = ta.ma("sma", df.close, length=50)
pwma10 = ta.ma("pwma", df.close, length=10, asc=False)
```
"""
_mas = [
"dema", "ema", "fwma", "hma", "linreg", "midpoint", "pwma", "rma",
"sinwma", "sma", "ssf", "swma", "t3", "tema", "trima", "vidya", "wma"
]
if name is None and source is None:
return _mas
elif isinstance(name, str) and name.lower() in _mas:
name = name.lower()
else: # "ema"
name = _mas[1]
if name == "dema": return dema(source, **kwargs)
elif name == "fwma": return fwma(source, **kwargs)
elif name == "hma": return hma(source, **kwargs)
elif name == "linreg": return linreg(source, **kwargs)
elif name == "midpoint": return midpoint(source, **kwargs)
elif name == "pwma": return pwma(source, **kwargs)
elif name == "rma": return rma(source, **kwargs)
elif name == "sinwma": return sinwma(source, **kwargs)
elif name == "sma": return sma(source, **kwargs)
elif name == "ssf": return ssf(source, **kwargs)
elif name == "swma": return swma(source, **kwargs)
elif name == "t3": return t3(source, **kwargs)
elif name == "tema": return tema(source, **kwargs)
elif name == "trima": return trima(source, **kwargs)
elif name == "vidya": return vidya(source, **kwargs)
elif name == "wma": return wma(source, **kwargs)
else: return ema(source, **kwargs)
@@ -1,90 +0,0 @@
# -*- coding: utf-8 -*-
from importlib.util import find_spec
from pandas_ta._typing import Dict, IntFloat, ListStr
Imports: Dict[str, bool] = {
"talib": find_spec("talib") is not None,
"vectorbt": find_spec("vectorbt") is not None,
"yfinance": find_spec("yfinance") is not None,
}
# Not ideal and not dynamic but it works.
# TODO: find a dynamic solution later.
Category: Dict[str, ListStr] = {
"candle": [
"cdl_pattern", "cdl_z", "ha"
],
"cycle": ["ebsw", "reflex"],
"momentum": [
"ao", "apo", "bias", "bop", "brar", "cci", "cfo", "cg", "cmo",
"coppock", "crsi", "cti", "er", "eri", "exhc", "fisher",
"inertia", "kdj", "kst", "macd", "mom", "pgo", "ppo", "psl", "qqe",
"roc", "rsi", "rsx", "rvgi", "slope", "smc", "smi", "squeeze",
"squeeze_pro", "stc", "stoch", "stochf", "stochrsi", "tmo", "trix",
"tsi", "uo", "willr"
],
"overlap": [
"alligator", "alma", "dema", "ema", "fwma", "hilo", "hl2", "hlc3",
"hma", "hwma", "ichimoku", "jma", "kama", "linreg", "mama",
"mcgd", "midpoint", "midprice", "ohlc4", "pivots", "pwma", "rma",
"sinwma", "sma", "smma", "ssf", "ssf3", "supertrend", "swma", "t3",
"tema", "trima", "vidya", "wcp", "wma", "zlma"
],
"performance": ["log_return", "percent_return"],
"statistics": [
"entropy", "kurtosis", "mad", "median", "quantile", "skew", "stdev",
"tos_stdevall", "variance", "zscore"
],
"trend": [
"adx", "alphatrend", "amat", "aroon", "chop", "cksp", "decay",
"decreasing", "dpo", "ht_trendline", "increasing",
"long_run", "psar", "qstick", "rwi", "short_run", "trendflex",
"vhf", "vortex", "zigzag"
],
"volatility": [
"aberration", "accbands", "atr", "atrts", "bbands", "chandelier_exit",
"donchian", "hwc", "kc", "massi", "natr", "pdist", "rvi", "thermo",
"true_range", "ui"
],
# Note: "vp" or "Volume Profile" is excluded since it does not
# return a Time Series
"volume": [
"ad", "adosc", "aobv", "cmf", "efi", "eom", "kvo", "mfi", "nvi",
"obv", "pvi", "pvo", "pvol", "pvr", "pvt", "tsv", "vhm", "vwap",
"vwma"
],
}
CANDLE_AGG: Dict[str, str] = {
"open": "first",
"high": "max",
"low": "min",
"close": "last",
"volume": "sum"
}
# https://www.worldtimezone.com/markets24.php
EXCHANGE_TZ: Dict[str, IntFloat] = {
"NZSX": 12, "ASX": 11,
"TSE": 9, "HKE": 8, "SSE": 8, "SGX": 8,
"NSE": 5.5, "DIFX": 4, "RTS": 3,
"JSE": 2, "FWB": 1, "LSE": 1,
"BMF": -2, "NYSE": -4, "TSX": -4,
"GENR": 0 # Generated Data
}
RATE: Dict[str, IntFloat] = {
"DAYS_PER_MONTH": 21,
"MINUTES_PER_HOUR": 60,
"MONTHS_PER_YEAR": 12,
"QUARTERS_PER_YEAR": 4,
"TRADING_DAYS_PER_YEAR": 252, # Keep even
"TRADING_HOURS_PER_DAY": 6.5,
"WEEKS_PER_YEAR": 52,
"YEARLY": 1,
}
@@ -1,93 +0,0 @@
# -*- coding: utf-8 -*-
from .ao import ao
from .apo import apo
from .bias import bias
from .bop import bop
from .brar import brar
from .cci import cci
from .cfo import cfo
from .cg import cg
from .cmo import cmo
from .coppock import coppock
from .crsi import crsi
from .cti import cti
from .dm import dm
from .er import er
from .eri import eri
from .exhc import exhc
from .fisher import fisher
from .inertia import inertia
from .kdj import kdj
from .kst import kst
from .macd import macd
from .mom import mom
from .pgo import pgo
from .ppo import ppo
from .psl import psl
from .qqe import qqe
from .roc import roc
from .rsi import rsi
from .rsx import rsx
from .rvgi import rvgi
from .slope import slope
from .smc import smc
from .smi import smi
from .squeeze import squeeze
from .squeeze_pro import squeeze_pro
from .stc import stc
from .stoch import stoch
from .stochf import stochf
from .stochrsi import stochrsi
from .tmo import tmo
from .trix import trix
from .tsi import tsi
from .uo import uo
from .willr import willr
__all__ = [
"ao",
"apo",
"bias",
"bop",
"brar",
"cci",
"cfo",
"cg",
"cmo",
"coppock",
"crsi",
"cti",
"dm",
"er",
"eri",
"exhc",
"fisher",
"inertia",
"kdj",
"kst",
"macd",
"mom",
"pgo",
"ppo",
"psl",
"qqe",
"roc",
"rsi",
"rsx",
"rvgi",
"slope",
"smc",
"smi",
"squeeze",
"squeeze_pro",
"stc",
"stoch",
"stochf",
"stochrsi",
"tmo",
"trix",
"tsi",
"uo",
"willr",
]
@@ -1,66 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.overlap import sma
from pandas_ta.utils import v_offset, v_pos_default, v_series
def ao(
high: Series, low: Series, fast: Int = None, slow: Int = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Awesome Oscillator
This indicator attempts to identify momentum with the intention to
affirm trends or anticipate possible reversals.
Sources:
* [ifcm](https://www.ifcm.co.uk/ntx-indicators/awesome-oscillator)
* [tradingview](https://www.tradingview.com/wiki/Awesome_Oscillator_(AO))
Parameters:
high (Series): ```high``` Series
low (Series): ```low``` Series
fast (int): Fast period. Default: ```5```
slow (int): Slow period. Default: ```34```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(Series): 1 column
"""
# Validate
fast = v_pos_default(fast, 5)
slow = v_pos_default(slow, 34)
if slow < fast:
fast, slow = slow, fast
_length = max(fast, slow)
high = v_series(high, _length)
low = v_series(low, _length)
if high is None or low is None:
return
offset = v_offset(offset)
# Calculate
median_price = 0.5 * (high + low)
fast_sma = sma(median_price, fast)
slow_sma = sma(median_price, slow)
ao = fast_sma - slow_sma
# Offset
if offset != 0:
ao = ao.shift(offset)
# Fill
if "fillna" in kwargs:
ao.fillna(kwargs["fillna"], inplace=True)
# Name and Category
ao.name = f"AO_{fast}_{slow}"
ao.category = "momentum"
return ao
@@ -1,75 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.ma import ma
from pandas_ta.maps import Imports
from pandas_ta.utils import tal_ma, v_mamode, v_offset
from pandas_ta.utils import v_pos_default, v_series, v_talib
def apo(
close: Series, fast: Int = None, slow: Int = None,
mamode: str = None, talib: bool = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Absolute Price Oscillator
This indicator attempts to quantify momentum.
Sources:
* [tradingtechnologies](https://www.tradingtechnologies.com/xtrader-help/x-study/technical-indicator-definitions/absolute-price-oscillator-apo/)
Parameters:
close (Series): ```close``` Series
fast (int): Fast period. Default: ```12```
slow (int): Slow period. Default: ```26```
mamode (str): See ```help(ta.ma)```. Default: ```"sma"```
talib (bool): If installed, use TA Lib. Default: ```True```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(Series): 1 column
Note:
* Simply the difference of two different EMAs.
* APO and MACD lines are equivalent.
"""
# Validate
fast = v_pos_default(fast, 12)
slow = v_pos_default(slow, 26)
if slow < fast:
fast, slow = slow, fast
close = v_series(close, max(fast, slow))
if close is None:
return
mamode = v_mamode(mamode, "sma")
mode_tal = v_talib(talib)
offset = v_offset(offset)
# Calculate
if Imports["talib"] and mode_tal:
from talib import APO
apo = APO(close, fast, slow, tal_ma(mamode))
else:
fastma = ma(mamode, close, length=fast, talib=mode_tal)
slowma = ma(mamode, close, length=slow, talib=mode_tal)
apo = fastma - slowma
# Offset
if offset != 0:
apo = apo.shift(offset)
# Fill
if "fillna" in kwargs:
apo.fillna(kwargs["fillna"], inplace=True)
# Name and Category
apo.name = f"APO_{fast}_{slow}"
apo.category = "momentum"
return apo
@@ -1,59 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.ma import ma
from pandas_ta.utils import v_mamode, v_offset, v_pos_default, v_series
def bias(
close: Series, length: Int = None, mamode: str = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Bias
This indicator computes the Rate of Change between the source and a
moving average.
Sources:
* Few internet resources on definitive definition.
Parameters:
close (Series): ```close``` Series
length (int): The period. Default: ```26```
mamode (str): See ```help(ta.ma)```. Default: ```"sma"```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(Series): 1 column
"""
# Validate
length = v_pos_default(length, 26)
close = v_series(close, length)
if close is None:
return
mamode = v_mamode(mamode, "sma")
offset = v_offset(offset)
# Calculate
bma = ma(mamode, close, length=length, **kwargs)
bias = (close / bma) - 1
# Offset
if offset != 0:
bias = bias.shift(offset)
# Fill
if "fillna" in kwargs:
bias.fillna(kwargs["fillna"], inplace=True)
# Name and Category
bias.name = f"BIAS_{bma.name}"
bias.category = "momentum"
return bias
@@ -1,73 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import Series
from pandas_ta._typing import DictLike, Int, IntFloat
from pandas_ta.maps import Imports
from pandas_ta.utils import (
non_zero_range,
v_offset,
v_scalar,
v_series,
v_talib
)
def bop(
open_: Series, high: Series, low: Series, close: Series,
scalar: IntFloat = None, talib: bool = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Balance of Power
This indicator attempts to quantify the market strength of buyers
versus sellers.
Sources:
* [worden](http://www.worden.com/TeleChartHelp/Content/Indicators/Balance_of_Power.htm)
Parameters:
open_ (Series): ```open``` Series
high (Series): ```high``` Series
low (Series): ```low``` Series
close (Series): ```close``` Series
scalar (float): Scalar. Default: ```1```
talib (bool): If installed, use TA Lib. Default: ```True```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(Series): 1 column
"""
# Validate
open_ = v_series(open_)
high = v_series(high)
low = v_series(low)
close = v_series(close)
scalar = v_scalar(scalar, 1)
mode_tal = v_talib(talib)
offset = v_offset(offset)
# Calculate
if Imports["talib"] and mode_tal and close.size:
from talib import BOP
bop = BOP(open_, high, low, close)
else:
high_low_range = non_zero_range(high, low)
close_open_range = non_zero_range(close, open_)
bop = scalar * close_open_range / high_low_range
# Offset
if offset != 0:
bop = bop.shift(offset)
# Fill
if "fillna" in kwargs:
bop.fillna(kwargs["fillna"], inplace=True)
# Name and Category
bop.name = f"BOP"
bop.category = "momentum"
return bop
@@ -1,93 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import DataFrame, Series
from pandas_ta._typing import DictLike, Int, IntFloat
from pandas_ta.utils import (
non_zero_range,
v_drift,
v_offset,
v_pos_default,
v_scalar,
v_series
)
def brar(
open_: Series, high: Series, low: Series, close: Series,
length: Int = None, scalar: IntFloat = None, drift: Int = None,
offset: Int = None, **kwargs: DictLike
) -> DataFrame:
"""BRAR
BR and AR
Sources:
* No internet resources on definitive definition.
Parameters:
open_ (Series): ```open``` Series
high (Series): ```high``` Series
low (Series): ```low``` Series
close (Series): ```close``` Series
length (int): The period. Default: ```26```
scalar (float): Scalar. Default: ```100```
drift (int): Difference amount. Default: ```1```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(DataFrame): 2 columns
"""
# Validate
length = v_pos_default(length, 26)
open_ = v_series(open_, length)
high = v_series(high, length)
low = v_series(low, length)
close = v_series(close, length)
if open_ is None or high is None or low is None or close is None:
return
scalar = v_scalar(scalar, 100)
drift = v_drift(drift)
offset = v_offset(offset)
# Calculate
high_open_range = non_zero_range(high, open_)
open_low_range = non_zero_range(open_, low)
hcy = non_zero_range(high, close.shift(drift))
cyl = non_zero_range(close.shift(drift), low)
hcy[hcy < 0] = 0 # Zero negative values
cyl[cyl < 0] = 0 # ""
ar = scalar * high_open_range.rolling(length).sum() \
/ open_low_range.rolling(length).sum()
br = scalar * hcy.rolling(length).sum() \
/ cyl.rolling(length).sum()
# Offset
if offset != 0:
ar = ar.shift(offset)
br = ar.shift(offset)
# Fill
if "fillna" in kwargs:
ar.fillna(kwargs["fillna"], inplace=True)
br.fillna(kwargs["fillna"], inplace=True)
# Name and Category
_props = f"_{length}"
ar.name = f"AR{_props}"
br.name = f"BR{_props}"
ar.category = br.category = "momentum"
data = {ar.name: ar, br.name: br}
df = DataFrame(data, index=close.index)
df.name = f"BRAR{_props}"
df.category = "momentum"
return df
@@ -1,75 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import Series
from pandas_ta._typing import DictLike, Int, IntFloat
from pandas_ta.maps import Imports
from pandas_ta.overlap import hlc3, sma
from pandas_ta.statistics import mad
from pandas_ta.utils import v_offset, v_pos_default, v_series, v_talib
def cci(
high: Series, low: Series, close: Series, length: Int = None,
c: IntFloat = None, talib: bool = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Commodity Channel Index
This indicator attempts to identify "overbought" and "oversold" levels
relative to a mean.
Sources:
* [tradingview](https://www.tradingview.com/wiki/Commodity_Channel_Index_(CCI))
Parameters:
high (Series): ```high``` Series
low (Series): ```low``` Series
close (Series): ```close``` Series
length (int): The period. Default: ```14```
c (float): Scaling Constant. Default: ```0.015```
talib (bool): If installed, use TA Lib. Default: ```True```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(Series): 1 column
"""
# Validate
length = v_pos_default(length, 14)
high = v_series(high, length)
low = v_series(low, length)
close = v_series(close, length)
if high is None or low is None or close is None:
return
c = v_pos_default(c, 0.015)
mode_tal = v_talib(talib)
offset = v_offset(offset)
# Calculate
if Imports["talib"] and mode_tal:
from talib import CCI
cci = CCI(high, low, close, length)
else:
typical_price = hlc3(high=high, low=low, close=close, talib=mode_tal)
mean_typical_price = sma(typical_price, length=length, talib=mode_tal)
mad_typical_price = mad(typical_price, length=length)
cci = typical_price - mean_typical_price / (c * mad_typical_price)
# Offset
if offset != 0:
cci = cci.shift(offset)
# Fill
if "fillna" in kwargs:
cci.fillna(kwargs["fillna"], inplace=True)
# Name and Category
cci.name = f"CCI_{length}_{c}"
cci.category = "momentum"
return cci
@@ -1,69 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import Series
from pandas_ta._typing import DictLike, Int, IntFloat
from pandas_ta.overlap import linreg
from pandas_ta.utils import (
v_drift,
v_offset,
v_pos_default,
v_scalar,
v_series
)
def cfo(
close: Series, length: Int = None,
scalar: IntFloat = None, drift: Int = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Chande Forcast Oscillator
This indicator attempts to calculate the percentage difference between
the actual price and the Time Series Forecast (the endpoint of a
linear regression line).
Sources:
* [fmlabs](https://www.fmlabs.com/reference/default.htm?url=ForecastOscillator.htm)
Parameters:
close (Series): ```close``` Series
length (int): The period. Default: ```9```
scalar (float): Scalar. Default: ```100```
drift (int): Difference amount. Default: ```1```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(Series): 1 column
"""
# Validate
length = v_pos_default(length, 9)
close = v_series(close, length)
if close is None:
return
scalar = v_scalar(scalar, 100)
drift = v_drift(drift)
offset = v_offset(offset)
# Calculate
# Finding linear regression of Series
cfo = scalar * (close - linreg(close, length=length, tsf=True)) / close
# Offset
if offset != 0:
cfo = cfo.shift(offset)
# Fill
if "fillna" in kwargs:
cfo.fillna(kwargs["fillna"], inplace=True)
# Name and Category
cfo.name = f"CFO_{length}"
cfo.category = "momentum"
return cfo
@@ -1,57 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.utils import v_offset, v_pos_default, v_series, weights
def cg(
close: Series, length: Int = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Center of Gravity
This indicator, by John Ehlers, attempts to identify turning points with
minimal to zero lag and smoothing.
Sources:
* [MESA Software](http://www.mesasoftware.com/papers/TheCGOscillator.pdf)
Parameters:
close (Series): ```close``` Series
length (int): The period. Default: ```10```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(Series): 1 column
"""
# Validate
length = v_pos_default(length, 10)
close = v_series(close, length)
if close is None:
return
offset = v_offset(offset)
# Calculate
coefficients = range(1, length + 1)
numerator = close.rolling(length).apply(weights(coefficients), raw=True)
cg = -numerator / close.rolling(length).sum()
# Offset
if offset != 0:
cg = cg.shift(offset)
# Fill
if "fillna" in kwargs:
cg.fillna(kwargs["fillna"], inplace=True)
# Name and Category
cg.name = f"CG_{length}"
cg.category = "momentum"
return cg
@@ -1,90 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import Series
from pandas_ta._typing import DictLike, Int, IntFloat
from pandas_ta.maps import Imports
from pandas_ta.overlap import rma
from pandas_ta.utils import (
v_drift,
v_offset,
v_pos_default,
v_scalar,
v_series,
v_talib
)
def cmo(
close: Series, length: Int = None, scalar: IntFloat = None,
talib: bool = None, drift: Int = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Chande Momentum Oscillator
This indicator attempts to capture momentum.
Sources:
* [tradingtechnologies](https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/chande-momentum-oscillator-cmo/)
* [tradingview](https://www.tradingview.com/script/hdrf0fXV-Variable-Index-Dynamic-Average-VIDYA/)
Parameters:
close (Series): ```close``` Series
scalar (float): Scalar. Default: ```100```
talib (bool): If installed, use TA Lib. Uses EMA if ```False```.
Default: ```True```
drift (int): Difference amount. Default: ```1```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(Series): 1 column
Note:
* Overbought around 50
* Oversold around -50.
"""
# Validate
length = v_pos_default(length, 14)
close = v_series(close, length + 1)
if close is None:
return
scalar = v_scalar(scalar, 100)
mode_tal = v_talib(talib)
drift = v_drift(drift)
offset = v_offset(offset)
# Calculate
if Imports["talib"] and mode_tal:
from talib import CMO
cmo = CMO(close, length)
else:
mom = close.diff(drift)
positive = mom.copy().clip(lower=0)
negative = mom.copy().clip(upper=0).abs()
if mode_tal:
pos_ = rma(positive, length)
neg_ = rma(negative, length)
else:
pos_ = positive.rolling(length).sum()
neg_ = negative.rolling(length).sum()
cmo = scalar * (pos_ - neg_) / (pos_ + neg_)
# Offset
if offset != 0:
cmo = cmo.shift(offset)
# Fill
if "fillna" in kwargs:
cmo.fillna(kwargs["fillna"], inplace=True)
# Name and Category
cmo.name = f"CMO_{length}"
cmo.category = "momentum"
return cmo
@@ -1,70 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.overlap import wma
from pandas_ta.utils import v_offset, v_pos_default, v_series
from .roc import roc
def coppock(
close: Series, length: Int = None,
fast: Int = None, slow: Int = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Coppock Curve
This indicator, by Edwin Coppock 1962, was originally called the
"Trendex Model", attempts to identify major upturns and downturns.
Sources:
* [wikipedia](https://en.wikipedia.org/wiki/Coppock_curve)
Parameters:
close (Series): ```close``` Series
length (int): WMA period. Default: ```10```
fast (int): Fast ROC period. Default: ```11```
slow (int): Slow ROC period. Default: ```14```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(Series): 1 column
Note:
Although designed for monthly use, a daily calculation over the same
period length can be made, converting the periods to 294-day and
231-day rate of changes, and a 210-day WMA.
"""
# Validate
length = v_pos_default(length, 10)
fast = v_pos_default(fast, 11)
slow = v_pos_default(slow, 14)
_length = length + fast + slow
close = v_series(close, _length)
if close is None:
return
offset = v_offset(offset)
# Calculate
total_roc = roc(close, fast) + roc(close, slow)
coppock = wma(total_roc, length)
# Offset
if offset != 0:
coppock = coppock.shift(offset)
# Fill
if "fillna" in kwargs:
coppock.fillna(kwargs["fillna"], inplace=True)
# Name and Category
coppock.name = f"COPC_{fast}_{slow}_{length}"
coppock.category = "momentum"
return coppock
@@ -1,106 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import Series
from pandas_ta._typing import DictLike, Int, IntFloat
from pandas_ta.maps import Imports
from pandas_ta.momentum.rsi import rsi
from pandas_ta.utils import (
consecutive_streak,
percent_rank,
v_drift,
v_offset,
v_pos_default,
v_scalar,
v_series,
v_talib,
)
def crsi(
close: Series, rsi_length: Int = None, streak_length: Int = None,
rank_length: Int = None, scalar: IntFloat = None,
talib: bool = None, drift: Int = None,
offset: Int = None, **kwargs: DictLike,
) -> Series:
"""Connors Relative Strength Index
This indicator attempts to identify momentum and potential reversals at
"overbought" or "oversold" conditions.
Sources:
* [alvarezquanttrading](https://alvarezquanttrading.com/blog/connorsrsi-analysis/)
* [tradingview](https://www.tradingview.com/support/solutions/43000502017-connors-rsi-crsi/)
* An Introduction to ConnorsRSI. Connors Research Trading Strategy Series.
Connors, L., Alvarez, C., & Radtke, M. (2012). ISBN 978-0-9853072-9-5.
Parameters:
close (Series): ```close``` Series
rsi_length (int): The RSI period. Default: ```3```
streak_length (int): Streak RSI period. Default: ```2```
rank_length (int): Percent Rank length. Default: ```100```
scalar (float): Scalar. Default: ```100```
talib (bool): If installed, use TA Lib. Default: ```True```
drift (int): Difference amount. Default: ```1```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(Series): 1 column
"""
# Validate
rsi_length = v_pos_default(rsi_length, 3)
streak_length = v_pos_default(streak_length, 2)
rank_length = v_pos_default(rank_length, 100)
_length = max(rsi_length, streak_length, rank_length)
close = v_series(close, _length)
if "length" in kwargs:
kwargs.pop("length")
if close is None:
return None
scalar = v_scalar(scalar, 100)
mode_tal = v_talib(talib)
drift = v_drift(drift)
offset = v_offset(offset)
# Calculate
np_close = close.to_numpy()
streak = Series(consecutive_streak(np_close), index=close.index)
if Imports["talib"] and mode_tal:
from talib import RSI
_rsi = RSI(close, rsi_length)
_streak_rsi = RSI(streak, streak_length)
else:
# Both TA-lib and Pandas-TA use the Wilder's RSI
# and its smoothing function
_rsi = rsi(
close, length=rsi_length, scalar=scalar, talib=talib,
drift=drift, offset=offset, **kwargs
)
_streak_rsi = rsi(
streak, length=streak_length, scalar=scalar, talib=talib,
drift=drift, offset=offset, **kwargs
)
_crsi = (_rsi + _streak_rsi + percent_rank(close, rank_length)) / 3.0
crsi = Series(_crsi, index=close.index)
# Offset
if offset != 0:
crsi = crsi.shift(offset)
# Fill
if "fillna" in kwargs:
crsi.fillna(kwargs["fillna"], inplace=True)
# Name and Category
crsi.name = f"CRSI_{rsi_length}_{streak_length}_{rank_length}"
crsi.category = "momentum"
return crsi
@@ -1,56 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.overlap import linreg
from pandas_ta.utils import v_offset, v_pos_default, v_series
def cti(
close: Series, length: Int = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Correlation Trend Indicator
This oscillator, by John Ehlers' in 2020, attempts to identify the
magnitude and direction of a trend using linear regession.
Note:
This is a wrapper for ```ta.linreg(close, r=True)```.
Parameters:
close (Series): ```close``` Series
length (int): The period. Default: ```12```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(Series): 1 column
"""
# Validate
length = v_pos_default(length, 12)
close = v_series(close, length)
if close is None:
return
offset = v_offset(offset)
# Calculate
cti = linreg(close, length=length, r=True)
# Offset
if offset != 0:
cti = cti.shift(offset)
# Fill
if "fillna" in kwargs:
cti.fillna(method=kwargs["fillna"], inplace=True)
# Name and Category
cti.name = f"CTI_{length}"
cti.category = "momentum"
return cti
@@ -1,94 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import DataFrame, Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.ma import ma
from pandas_ta.maps import Imports
from pandas_ta.utils import (
v_drift,
v_mamode,
v_offset,
v_pos_default,
v_series,
v_talib,
zero
)
def dm(
high: Series, low: Series, length: Int = None,
mamode: str = None, talib: bool = None, drift: Int = None,
offset: Int = None, **kwargs: DictLike
) -> DataFrame:
"""Directional Movement
This indicator, by J. Welles Wilder in 1978, attempts to
determine direction.
Sources:
* [sierrachart](https://www.sierrachart.com/index.php?page=doc/StudiesReference.php&ID=24&Name=Directional_Movement_Index)
* [tradingview](https://www.tradingview.com/pine-script-reference/#fun_dmi)
Parameters:
high (Series): ```high``` Series
low (Series): ```low``` Series
mamode (str): See ```help(ta.ma)```. Default: ```"rma"```
talib (bool): If installed, use TA Lib. Default: ```True```
drift (int): Difference amount. Default: ```1```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(DataFrame): 2 columns
"""
# Validate
length = v_pos_default(length, 14)
high = v_series(high, length)
low = v_series(low, length)
if high is None or low is None:
return
mamode = v_mamode(mamode, "rma")
mode_tal = v_talib(talib)
drift = v_drift(drift)
offset = v_offset(offset)
if Imports["talib"] and mode_tal and high.size and low.size:
from talib import MINUS_DM, PLUS_DM
pos = PLUS_DM(high, low, length)
neg = MINUS_DM(high, low, length)
else:
up = high - high.shift(drift)
dn = low.shift(drift) - low
pos_ = ((up > dn) & (up > 0)) * up
neg_ = ((dn > up) & (dn > 0)) * dn
pos_ = pos_.apply(zero)
neg_ = neg_.apply(zero)
# Not the same values as TA Lib's -+DM (Good First Issue)
pos = ma(mamode, pos_, length=length, talib=mode_tal)
neg = ma(mamode, neg_, length=length, talib=mode_tal)
# Offset
if offset != 0:
pos = pos.shift(offset)
neg = neg.shift(offset)
# Fill
if "fillna" in kwargs:
pos.fillna(kwargs["fillna"], inplace=True)
neg.fillna(kwargs["fillna"], inplace=True)
# Name and Category
_props = f"_{length}"
data = {f"DMP{_props}": pos, f"DMN{_props}": neg}
df = DataFrame(data, index=high.index)
df.name = f"DM{_props}"
df.category = "momentum"
return df
@@ -1,93 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import DataFrame, Series, concat
from pandas_ta._typing import DictLike, Int
from pandas_ta.utils import (
signals,
v_drift,
v_offset,
v_pos_default,
v_series
)
def er(
close: Series, length: Int = None, drift: Int = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Efficiency Ratio
This indicator, by Perry J. Kaufman, attempts to identify market noise
or volatility.
Sources:
* "New Trading Systems and Methods", Perry J. Kaufman
* [tc2000](https://help.tc2000.com/m/69404/l/749623-kaufman-efficiency-ratio)
Parameters:
close (Series): ```close``` Series
length (int): The period. Default: ```1```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(Series): 1 column
Note:
It is calculated by dividing the net change in price movement over
```n``` periods by the sum of the absolute net changes over the
same ```n``` periods.
"""
# Validate
length = v_pos_default(length, 10)
close = v_series(close, length + 1)
if close is None:
return
drift = v_drift(drift)
offset = v_offset(offset)
# Calculate
abs_diff = close.diff(length).abs()
abs_volatility = close.diff(drift).abs()
abs_volatility_rsum = abs_volatility.rolling(window=length).sum()
er = abs_diff / abs_volatility_rsum
# Offset
if offset != 0:
er = er.shift(offset)
# Fill
if "fillna" in kwargs:
er.fillna(kwargs["fillna"], inplace=True)
# Name and Category
er.name = f"ER_{length}"
er.category = "momentum"
signal_indicators = kwargs.pop("signal_indicators", False)
if not signal_indicators:
return er
else:
signalsdf = concat(
[
DataFrame({er.name: er}),
signals(
indicator=er,
xa=kwargs.pop("xa", 80),
xb=kwargs.pop("xb", 20),
xseries=kwargs.pop("xseries", None),
xseries_a=kwargs.pop("xseries_a", None),
xseries_b=kwargs.pop("xseries_b", None),
cross_values=kwargs.pop("cross_values", False),
cross_series=kwargs.pop("cross_series", True),
offset=offset,
),
],
axis=1,
)
return signalsdf
@@ -1,75 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import DataFrame, Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.overlap import ema
from pandas_ta.utils import v_offset, v_pos_default, v_series
def eri(
high: Series, low: Series, close: Series, length: Int = None,
offset: Int = None, **kwargs: DictLike
) -> DataFrame:
"""Elder Ray Index
This indicator, by Dr Alexander Elder, attempts to identify market
strength.
Sources:
* [admiralmarkets](https://admiralmarkets.com/education/articles/forex-indicators/bears-and-bulls-power-indicator)
Parameters:
high (Series): ```high``` Series
low (Series): ```low``` Series
close (Series): ```close``` Series
length (int): The period. Default: ```14```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(DataFrame): 2 columns
Note:
* Possible entry signals when used in combination with a trend,
* Bear Power attempts to quantify lower value appeal.
* Bull Power attempts the to quantify higher value appeal.
"""
# Validate
length = v_pos_default(length, 13)
high = v_series(high, length)
low = v_series(low, length)
close = v_series(close, length)
if high is None or low is None or close is None:
return
offset = v_offset(offset)
# Calculate
ema_ = ema(close, length)
bull = high - ema_
bear = low - ema_
# Offset
if offset != 0:
bull = bull.shift(offset)
bear = bear.shift(offset)
# Fill
if "fillna" in kwargs:
bull.fillna(kwargs["fillna"], inplace=True)
bear.fillna(kwargs["fillna"], inplace=True)
# Name and Category
bull.name = f"BULLP_{length}"
bear.name = f"BEARP_{length}"
bull.category = bear.category = "momentum"
data = {bull.name: bull, bear.name: bear}
df = DataFrame(data, index=close.index)
df.name = f"ERI_{length}"
df.category = bull.category
return df
@@ -1,110 +0,0 @@
# -*- coding: utf-8 -*-
from numba import njit
from numpy import clip, cumsum, int64, nan, where
from pandas import DataFrame, Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.utils import (
nb_ffill,
nb_idiff,
v_bool,
v_int,
v_offset,
v_pos_default,
v_series
)
@njit(cache=True)
def nb_exhc(x, n, cap, lb, ub, show_all):
x_diff = nb_idiff(x, n)
neg_diff, pos_diff = x_diff < 0, x_diff > 0
dn_csum = cumsum(neg_diff)
up_csum = cumsum(pos_diff)
dn = dn_csum - nb_ffill(where(~neg_diff, dn_csum, nan))
up = up_csum - nb_ffill(where(~pos_diff, up_csum, nan))
if cap > 0:
dn = clip(dn, 0, cap)
up = clip(up, 0, cap)
if show_all:
dn = where(dn == 0, 0, dn)
up = where(up == 0, 0, up)
else:
between_lu = (dn >= lb) & (dn <= ub)
dn = where(between_lu, dn, 0)
up = where(between_lu, up, 0)
return dn, up
def exhc(
close: Series, length: Int = None, cap: Int = None,
asint: bool = None, show_all: bool = None, nozeros: bool = None,
offset: Int = None, **kwargs: DictLike
) -> DataFrame:
"""Exhaustion Count
This indicator attempts to identify rising/falling exhaustion.
Sources:
* [demark](https://demark.com)
* [practicaltechnicalanalysis](http://practicaltechnicalanalysis.blogspot.com/2013/01/tom-demark-sequential.html)
Parameters:
close (Series): Series of close's
length (int): The period. Default: ```4```
cap (int): Count cap. For no cap, set to ```0```. Default: ```13```
show_all (bool): Counts 1 - 13. For 6 - 9, set to ```False```.
Default: ```True```
asint (bool): Returns as ```Int```. Default: ```False```
nozeros (bool): Replace zeros with ```np.nan```. Default: ```False```
offset (int): Post shift. Default: ```0```
Returns:
(DataFrame): 2 columns
Note:
Similar to TD Sequential
"""
# Validate
length = v_pos_default(length, 4)
close = v_series(close, length + 1)
if close is None:
return
cap = v_int(cap, 13, -1)
show_all = v_bool(show_all, True)
asint = v_bool(asint, False)
nozeros = v_bool(nozeros, False)
offset = v_offset(offset)
# Calculate
np_close = close.to_numpy()
dn, up = nb_exhc(np_close, length, cap, 6, 9, show_all)
if asint:
dn = dn.astype(int64)
up = up.astype(int64)
# Name and Category
data = {
"EXHC_DNa" if show_all else "EXHC_DN": dn,
"EXHC_UPa" if show_all else "EXHC_UP": up
}
df = DataFrame(data, index=close.index)
df.name = "EXHCa" if show_all else "EXHC"
df.category = "momentum"
if nozeros:
df.replace({0: nan}, inplace=True)
# Offset
if offset != 0:
df = df.shift(offset)
return df
@@ -1,95 +0,0 @@
# -*- coding: utf-8 -*-
from numpy import isnan, log, nan
from pandas import DataFrame, Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.overlap import hl2
from pandas_ta.utils import high_low_range, v_offset, v_pos_default, v_series
def fisher(
high: Series, low: Series, length: Int = None, signal: Int = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Fisher Transform
This indicator attempts to identify significant reversals through
normalization.
Parameters:
high (Series): ```high``` Series
low (Series): ```low``` Series
length (int): The period. Default: ```9```
signal (int): Signal period. Default: ```1```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(DataFrame): 2 columns
Tip: Reversal Signal
When the two lines cross.
"""
# Validate
length = v_pos_default(length, 9)
signal = v_pos_default(signal, 1)
_length = max(length, signal)
high = v_series(high, _length)
low = v_series(low, _length)
if high is None or low is None:
return
offset = v_offset(offset)
# Calculate
hl2_ = hl2(high, low)
highest_hl2 = hl2_.rolling(length).max()
lowest_hl2 = hl2_.rolling(length).min()
hlr = high_low_range(highest_hl2, lowest_hl2)
hlr[hlr < 0.001] = 0.001
position = ((hl2_ - lowest_hl2) / hlr) - 0.5
v = 0
m = high.size
result = [nan for _ in range(0, length - 1)] + [0]
for i in range(length, m):
v = 0.66 * position.iat[i] + 0.67 * v
if v < -0.99:
v = -0.999
if v > 0.99:
v = 0.999
result.append(0.5 * (log((1 + v) / (1 - v)) + result[i - 1]))
fisher = Series(result, index=high.index)
if all(isnan(fisher)):
return # Emergency Break
signalma = fisher.shift(signal)
# Offset
if offset != 0:
fisher = fisher.shift(offset)
signalma = signalma.shift(offset)
# Fill
if "fillna" in kwargs:
fisher.fillna(kwargs["fillna"], inplace=True)
signalma.fillna(kwargs["fillna"], inplace=True)
# Name and Category
_props = f"_{length}_{signal}"
fisher.name = f"FISHERT{_props}"
signalma.name = f"FISHERTs{_props}"
fisher.category = signalma.category = "momentum"
data = {fisher.name: fisher, signalma.name: signalma}
df = DataFrame(data, index=high.index)
df.name = f"FISHERT{_props}"
df.category = fisher.category
return df
@@ -1,118 +0,0 @@
# -*- coding: utf-8 -*-
from numpy import isnan
from pandas import Series
from pandas_ta._typing import DictLike, Int, IntFloat
from pandas_ta.overlap import linreg
from pandas_ta.utils import (
v_bool,
v_drift,
v_mamode,
v_offset,
v_pos_default,
v_scalar,
v_series
)
from pandas_ta.volatility import rvi
def inertia(
close: Series, high: Series = None, low: Series = None,
length: Int = None, rvi_length: Int = None, scalar: IntFloat = None,
refined: bool = None, thirds: bool = None,
drift: Int = None, mamode: str = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Inertia
This indicator, by Donald Dorsey, is the _rvi_ smoothed by the Least Squares
MA.
Sources:
* Donald Dorsey, some article in September, 1995.
* [sierrachart](https://www.sierrachart.com/index.php?page=doc/StudiesReference.php&ID=285&Name=Inertia)
* [tradingview](https://www.tradingview.com/script/mLZJqxKn-Relative-Volatility-Index/)
Parameters:
high (Series): ```high``` Series
low (Series): ```low``` Series
close (Series): ```close``` Series
length (int): The period. Default: ```20```
rvi_length (int): RVI period. Default: ```14```
refined (bool): Use 'refined' calculation. Default: ```False```
thirds (bool): Use 'thirds' calculation. Default: ```False```
mamode (str): See ```help(ta.ma)```. Default: ```"ema"```
drift (int): Difference amount. Default: ```1```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(Series): 1 column
Note:
* Negative Inertia when less than 50.
* Positive Inertia when greater than 50.
"""
# Validate
length = v_pos_default(length, 20)
rvi_length = v_pos_default(rvi_length, 14)
_length = 2 * max(length, rvi_length) - min(length, rvi_length) // 2 - 1
close = v_series(close, _length)
if close is None:
return
refined = v_bool(refined, False)
thirds = v_bool(thirds, False)
if refined or thirds:
high = v_series(high, _length)
low = v_series(low, _length)
if high is None or low is None:
return
scalar = v_scalar(scalar, 100)
mamode = v_mamode(mamode, "ema")
drift = v_drift(drift)
offset = v_offset(offset)
# Calculate
if refined:
_mode = "r"
rvi_ = rvi(
close, high=high, low=low, length=rvi_length,
scalar=scalar, refined=refined, mamode=mamode
)
elif thirds:
_mode = "t"
rvi_ = rvi(
close, high=high, low=low, length=rvi_length,
scalar=scalar, thirds=thirds, mamode=mamode
)
else:
_mode = ""
rvi_ = rvi(close, length=rvi_length, scalar=scalar, mamode=mamode)
if all(isnan(rvi_)):
return # Emergency Break
inertia = linreg(rvi_, length=length)
if all(isnan(inertia)):
return # Emergency Break
# Offset
if offset != 0:
inertia = inertia.shift(offset)
# Fill
if "fillna" in kwargs:
inertia.fillna(kwargs["fillna"], inplace=True)
# Name and Category
_props = f"_{length}_{rvi_length}"
inertia.name = f"INERTIA{_mode}{_props}"
inertia.category = "momentum"
return inertia
@@ -1,94 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import DataFrame, Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.utils import (
non_zero_range,
pd_rma,
v_offset,
v_pos_default,
v_series
)
def kdj(
high: Series, low: Series, close: Series,
length: Int = None, signal: Int = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""KDJ
This indicator, derived from the Slow Stochastic, includes an
extra signal named the J line. The J line represents the divergence
of the %D value from the %K.
Sources:
* [anychart](https://docs.anychart.com/Stock_Charts/Technical_Indicators/Mathematical_Description#kdj)
* [prorealcode](https://www.prorealcode.com/prorealtime-indicators/kdj/)
Parameters:
high (Series): ```high``` Series
low (Series): ```low``` Series
close (Series): ```close``` Series
length (int): The period. Default: ```9```
signal (int): Signal period. Default: ```3```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(DataFrame): 3 columns
Note:
The J can go beyond ```[0, 100]``` for %K and %D lines when charted.
"""
# Validate
length = v_pos_default(length, 9)
signal = v_pos_default(signal, 3)
_length = length + signal + 1
high = v_series(high, _length)
low = v_series(low, _length)
close = v_series(close, _length)
if high is None or low is None or close is None:
return
offset = v_offset(offset)
# Calculate
highest_high = high.rolling(length).max()
lowest_low = low.rolling(length).min()
fastk = 100 * (close - lowest_low) / \
non_zero_range(highest_high, lowest_low)
k = pd_rma(fastk, n=signal)
d = pd_rma(k, n=signal)
j = 3 * k - 2 * d
# Offset
if offset != 0:
k = k.shift(offset)
d = d.shift(offset)
j = j.shift(offset)
# Fill
if "fillna" in kwargs:
k.fillna(kwargs["fillna"], inplace=True)
d.fillna(kwargs["fillna"], inplace=True)
j.fillna(kwargs["fillna"], inplace=True)
# Name and Category
_props = f"_{length}_{signal}"
k.name = f"K{_props}"
d.name = f"D{_props}"
j.name = f"J{_props}"
k.category = d.category = j.category = "momentum"
data = {k.name: k, d.name: d, j.name: j}
df = DataFrame(data, index=close.index)
df.name = f"KDJ{_props}"
df.category = "momentum"
return df
@@ -1,97 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import DataFrame, Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.utils import v_drift, v_offset, v_pos_default, v_series
from .roc import roc
def kst(
close: Series, signal: Int = None,
roc1: Int = None, roc2: Int = None, roc3: Int = None, roc4: Int = None,
sma1: Int = None, sma2: Int = None, sma3: Int = None, sma4: Int = None,
drift: Int = None,
offset: Int = None, **kwargs: DictLike
) -> DataFrame:
"""'Know Sure Thing'
This indicator, by Martin Pring, attempts to capture trends using a
smoothed indicator of four different smoothed ROCs.
Sources:
* [incrediblecharts](https://www.incrediblecharts.com/indicators/kst.php)
* [tradingview](https://www.tradingview.com/wiki/Know_Sure_Thing_(KST))
Parameters:
close (Series): ```close``` Series
roc1 (int): ROC 1 period. Default: ```10```
roc2 (int): ROC 2 period. Default: ```15```
roc3 (int): ROC 3 period. Default: ```20```
roc4 (int): ROC 4 period. Default: ```30```
sma1 (int): SMA 1 period. Default: ```10```
sma2 (int): SMA 2 period. Default: ```10```
sma3 (int): SMA 3 period. Default: ```10```
sma4 (int): SMA 4 period. Default: ```15```
signal (int): Signal period. Default: ```9```
drift (int): Difference amount. Default: ```1```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(DataFrame): 2 columns
"""
# Validate
roc1 = int(roc1) if roc1 and roc1 > 0 else 10
roc2 = int(roc2) if roc2 and roc2 > 0 else 15
roc3 = int(roc3) if roc3 and roc3 > 0 else 20
roc4 = int(roc4) if roc4 and roc4 > 0 else 30
sma1 = int(sma1) if sma1 and sma1 > 0 else 10
sma2 = int(sma2) if sma2 and sma2 > 0 else 10
sma3 = int(sma3) if sma3 and sma3 > 0 else 10
sma4 = int(sma4) if sma4 and sma4 > 0 else 15
signal = v_pos_default(signal, 9)
_rmax = max(roc1, roc2, roc3, roc4)
_smax = max(sma1, sma2, sma3, sma4)
_length = _rmax + _smax
close = v_series(close, _length)
if close is None:
return
drift = v_drift(drift)
offset = v_offset(offset)
# Calculate
rocma1 = roc(close, roc1).rolling(sma1).mean()
rocma2 = roc(close, roc2).rolling(sma2).mean()
rocma3 = roc(close, roc3).rolling(sma3).mean()
rocma4 = roc(close, roc4).rolling(sma4).mean()
kst = 100 * (rocma1 + 2 * rocma2 + 3 * rocma3 + 4 * rocma4)
kst_signal = kst.rolling(signal).mean()
# Offset
if offset != 0:
kst = kst.shift(offset)
kst_signal = kst_signal.shift(offset)
# Fill
if "fillna" in kwargs:
kst.fillna(kwargs["fillna"], inplace=True)
kst_signal.fillna(kwargs["fillna"], inplace=True)
# Name and Category
kst.name = f"KST_{roc1}_{roc2}_{roc3}_{roc4}_{sma1}_{sma2}_{sma3}_{sma4}"
kst_signal.name = f"KSTs_{signal}"
kst.category = kst_signal.category = "momentum"
data = {kst.name: kst, kst_signal.name: kst_signal}
df = DataFrame(data, index=close.index)
df.name = f"KST_{roc1}_{roc2}_{roc3}_{roc4}_{sma1}_{sma2}_{sma3}_{sma4}_{signal}"
df.category = "momentum"
return df
@@ -1,141 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import concat, DataFrame, Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.maps import Imports
from pandas_ta.overlap import ema
from pandas_ta.utils import (
signals,
v_offset,
v_pos_default,
v_series,
v_talib
)
def macd(
close: Series, fast: Int = None, slow: Int = None,
signal: Int = None, talib: bool = None,
offset: Int = None, **kwargs: DictLike
) -> DataFrame:
"""Moving Average Convergence Divergence
This indicator attempts to identify trends.
Sources:
* [tradingview](https://www.tradingview.com/wiki/MACD_(Moving_Average_Convergence/Divergence))
* [tradingview (AS Mode)](https://tr.tradingview.com/script/YFlKXHnP/)
Parameters:
close (Series): ```close``` Series
fast (int): Fast MA period. Default: ```12```
slow (int): Slow MA period. Default: ```26```
signal (int): Signal period. Default: ```9```
talib (bool): If installed, use TA Lib. Default: ```True```
offset (int): Post shift. Default: ```0```
Other Parameters:
asmode (value): Enable AS version of MACD. Default: ```False```
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(DataFrame): 3 columns
"""
# Validate
fast = v_pos_default(fast, 12)
slow = v_pos_default(slow, 26)
signal = v_pos_default(signal, 9)
if slow < fast:
fast, slow = slow, fast
_length = slow + signal - 1
close = v_series(close, _length)
if close is None:
return
mode_tal = v_talib(talib)
offset = v_offset(offset)
as_mode = kwargs.setdefault("asmode", False)
# Calculate
if Imports["talib"] and mode_tal:
from talib import MACD
macd, signalma, histogram = MACD(close, fast, slow, signal)
else:
fastma = ema(close, length=fast, talib=mode_tal)
slowma = ema(close, length=slow, talib=mode_tal)
macd = fastma - slowma
macd_fvi = macd.loc[macd.first_valid_index():, ]
signalma = ema(close=macd_fvi, length=signal, talib=mode_tal)
histogram = macd - signalma
if as_mode:
macd = macd - signalma
macd_fvi = macd.loc[macd.first_valid_index():, ]
signalma = ema(close=macd_fvi, length=signal, talib=mode_tal)
histogram = macd - signalma
# Offset
if offset != 0:
macd = macd.shift(offset)
histogram = histogram.shift(offset)
signalma = signalma.shift(offset)
# Fill
if "fillna" in kwargs:
macd.fillna(kwargs["fillna"], inplace=True)
histogram.fillna(kwargs["fillna"], inplace=True)
signalma.fillna(kwargs["fillna"], inplace=True)
# Name and Category
_asmode = "AS" if as_mode else ""
_props = f"_{fast}_{slow}_{signal}"
macd.name = f"MACD{_asmode}{_props}"
histogram.name = f"MACD{_asmode}h{_props}"
signalma.name = f"MACD{_asmode}s{_props}"
macd.category = histogram.category = signalma.category = "momentum"
data = {
macd.name: macd,
histogram.name: histogram,
signalma.name: signalma
}
df = DataFrame(data, index=close.index)
df.name = f"MACD{_asmode}{_props}"
df.category = macd.category
signal_indicators = kwargs.pop("signal_indicators", False)
if not signal_indicators:
return df
else:
signalsdf = concat(
[
df,
signals(
indicator=histogram,
xa=kwargs.pop("xa", 0),
xb=kwargs.pop("xb", None),
xseries=kwargs.pop("xseries", None),
xseries_a=kwargs.pop("xseries_a", None),
xseries_b=kwargs.pop("xseries_b", None),
cross_values=kwargs.pop("cross_values", True),
cross_series=kwargs.pop("cross_series", True),
offset=offset,
),
signals(
indicator=macd,
xa=kwargs.pop("xa", 0),
xb=kwargs.pop("xb", None),
xseries=kwargs.pop("xseries", None),
xseries_a=kwargs.pop("xseries_a", None),
xseries_b=kwargs.pop("xseries_b", None),
cross_values=kwargs.pop("cross_values", False),
cross_series=kwargs.pop("cross_series", True),
offset=offset,
),
],
axis=1,
)
return signalsdf
@@ -1,76 +0,0 @@
# -*- coding: utf-8 -*-
from numba import njit
from pandas import Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.maps import Imports
from pandas_ta.utils import (
nb_idiff,
v_offset,
v_pos_default,
v_series,
v_talib
)
@njit(cache=True)
def nb_mom(x, n):
return nb_idiff(x, n)
def mom(
close: Series, length: Int = None, talib: bool = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Momentum
This indicator attempts to quantify speed by using the differences over
a bar length.
Sources:
* [onlinetradingconcepts](http://www.onlinetradingconcepts.com/TechnicalAnalysis/Momentum.html)
Parameters:
close (Series): ```close``` Series
length (int): The period. Default: ```1```
talib (bool): If installed, use TA Lib. Default: ```True```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(Series): 1 column
"""
# Validate
length = v_pos_default(length, 10)
close = v_series(close, length + 1)
if close is None:
return
mode_tal = v_talib(talib)
offset = v_offset(offset)
# Calculate
if Imports["talib"] and mode_tal:
from talib import MOM
mom = MOM(close, length)
else:
np_close = close.to_numpy()
_mom = nb_mom(np_close, length)
mom = Series(_mom, index=close.index)
# Offset
if offset != 0:
mom = mom.shift(offset)
# Fill
if "fillna" in kwargs:
mom.fillna(kwargs["fillna"], inplace=True)
# Name and Category
mom.name = f"MOM_{length}"
mom.category = "momentum"
return mom
@@ -1,68 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.overlap import ema, sma
from pandas_ta.utils import v_offset, v_pos_default, v_series
from pandas_ta.volatility import atr
def pgo(
high: Series, low: Series, close: Series, length: Int = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Pretty Good Oscillator
This indicator, by Mark Johnson, attempts to identify breakouts for longer
time periods based on the distance of the current bar to its N-day
SMA, expressed in terms of an ATR over a similar length.
Sources:
* [tradingtechnologies](https://library.tradingtechnologies.com/trade/chrt-ti-pretty-good-oscillator.html)
Parameters:
high (Series): ```high``` Series
low (Series): ```low``` Series
close (Series): ```close``` Series
length (int): The period. Default: ```14```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(Series): 1 column
Note: Entry
* Long when greater than 3.
* Short when less than -3.
"""
# Validate
length = v_pos_default(length, 14)
_length = 2 * length
high = v_series(high, _length)
low = v_series(low, _length)
close = v_series(close, _length)
if high is None or low is None or close is None:
return
offset = v_offset(offset)
# Calculate
pgo = (close - sma(close, length)) \
/ ema(atr(high, low, close, length), length)
# Offset
if offset != 0:
pgo = pgo.shift(offset)
# Fill
if "fillna" in kwargs:
pgo.fillna(kwargs["fillna"], inplace=True)
# Name and Category
pgo.name = f"PGO_{length}"
pgo.category = "momentum"
return pgo
@@ -1,107 +0,0 @@
# -*- coding: utf-8 -*-
from numpy import isnan
from pandas import DataFrame, Series
from pandas_ta._typing import DictLike, Int, IntFloat
from pandas_ta.ma import ma
from pandas_ta.maps import Imports
from pandas_ta.utils import (
tal_ma,
v_mamode,
v_offset,
v_pos_default,
v_scalar,
v_series,
v_talib
)
def ppo(
close: Series, fast: Int = None, slow: Int = None, signal: Int = None,
scalar: IntFloat = None, mamode: str = None, talib: bool = None,
offset: Int = None, **kwargs: DictLike
) -> DataFrame:
"""Percentage Price Oscillator
Similar to MACD.
Sources:
* [investopedia](https://www.investopedia.com/terms/p/ppo.asp)
Parameters:
close (Series): ```close``` Series
fast (int): Fast MA period. Default: ```12```
slow (int): Slow MA period. Default: ```26```
signal (int): Signal period. Default: ```9```
scalar (float): Scalar. Default: ```100```
mamode (str): See ```help(ta.ma)```. Default: ```"sma"```
talib (bool): If installed, use TA Lib. Default: ```True```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(DataFrame): 3 columns
"""
# Validate
fast = v_pos_default(fast, 12)
slow = v_pos_default(slow, 26)
signal = v_pos_default(signal, 9)
if slow < fast:
fast, slow = slow, fast
_length = max(fast, slow, signal)
close = v_series(close, _length)
if close is None:
return
scalar = v_scalar(scalar, 100)
mamode = v_mamode(mamode, "sma")
mode_tal = v_talib(talib)
offset = v_offset(offset)
# Calculate
if Imports["talib"] and mode_tal:
from talib import PPO
ppo = PPO(close, fast, slow, tal_ma(mamode))
else:
fastma = ma(mamode, close, length=fast, talib=mode_tal)
slowma = ma(mamode, close, length=slow, talib=mode_tal)
ppo = scalar * (fastma - slowma) / slowma
if all(isnan(ppo)):
return # Emergency Break
signalma = ma("ema", ppo, length=signal, talib=mode_tal)
histogram = ppo - signalma
# Offset
if offset != 0:
ppo = ppo.shift(offset)
histogram = histogram.shift(offset)
signalma = signalma.shift(offset)
# Fill
if "fillna" in kwargs:
ppo.fillna(kwargs["fillna"], inplace=True)
histogram.fillna(kwargs["fillna"], inplace=True)
signalma.fillna(kwargs["fillna"], inplace=True)
# Name and Category
_props = f"_{fast}_{slow}_{signal}"
ppo.name = f"PPO{_props}"
histogram.name = f"PPOh{_props}"
signalma.name = f"PPOs{_props}"
ppo.category = histogram.category = signalma.category = "momentum"
data = {
ppo.name: ppo,
histogram.name: histogram,
signalma.name: signalma
}
df = DataFrame(data, index=close.index)
df.name = f"PPO{_props}"
df.category = ppo.category
return df
@@ -1,79 +0,0 @@
# -*- coding: utf-8 -*-
from numpy import sign
from pandas import Series
from pandas_ta._typing import DictLike, Int, IntFloat
from pandas_ta.utils import (
v_drift,
v_offset,
v_pos_default,
v_scalar,
v_series
)
def psl(
close: Series, open_: Series = None,
length: Int = None, scalar: IntFloat = None, drift: Int = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Psychological Line
This indicator compares the number of the rising bars to the total number
of bars. In other words, it is the percentage of bars that are above the
previous bar over a given length.
Sources:
* [quantshare](https://www.quantshare.com/item-851-psychological-line)
Parameters:
close (Series): ```close``` Series
open_ (Series): ```open``` Series
length (int): The period. Default: ```12```
scalar (float): Scalar. Default: ```100```
drift (int): Difference amount. Default: ```1```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(Series): 1 column
"""
# Validate
length = v_pos_default(length, 12)
close = v_series(close, length)
if close is None:
return
scalar = v_scalar(scalar, 100)
drift = v_drift(drift)
offset = v_offset(offset)
# Calculate
if open_ is not None:
open_ = v_series(open_)
diff = sign(close - open_)
else:
diff = sign(close.diff(drift))
diff.fillna(0, inplace=True)
diff[diff <= 0] = 0 # Set negative values to zero
psl = scalar * diff.rolling(length).sum() / length
# Offset
if offset != 0:
psl = psl.shift(offset)
# Fill
if "fillna" in kwargs:
psl.fillna(kwargs["fillna"], inplace=True)
# Name and Category
_props = f"_{length}"
psl.name = f"PSL{_props}"
psl.category = "momentum"
return psl
@@ -1,174 +0,0 @@
# -*- coding: utf-8 -*-
from numpy import isnan, maximum, minimum, nan
from pandas import DataFrame, Series
from pandas_ta._typing import DictLike, Int, IntFloat
from pandas_ta.ma import ma
from pandas_ta.utils import (
v_drift,
v_mamode,
v_offset,
v_pos_default,
v_scalar,
v_series
)
from .rsi import rsi
def qqe(
close: Series, length: Int = None,
smooth: Int = None, factor: IntFloat = None,
mamode: str = None, drift: Int = None,
offset: Int = None, **kwargs: DictLike
) -> DataFrame:
"""Quantitative Qualitative Estimation
This indicator is similar to SuperTrend but uses a Smoothed ```rsi```
with upper and lower bands.
Sources:
* [prorealcode](https://www.prorealcode.com/prorealtime-indicators/qqe-quantitative-qualitative-estimation/)
* [tradingpedia](https://www.tradingpedia.com/forex-trading-indicators/quantitative-qualitative-estimation)
* [tradingview](https://www.tradingview.com/script/IYfA9R2k-QQE-MT4/)
Parameters:
close (Series): ```close``` Series
length (int): RSI period. Default: ```14```
smooth (int): RSI smoothing period. Default: ```5```
factor (float): QQE Factor. Default: ```4.236```
mamode (str): See ```help(ta.ma)```. Default: ```"ema"```
drift (int): Difference amount. Default: ```1```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(DataFrame): 4 columns
Tip: Trend
* Long: When the Smoothed RSI crosses the previous upperband.
* Short: When the Smoothed RSI crosses the previous lowerband.
Note: See also
* QQE.mq5 by EarnForex Copyright © 2010
* Tim Hyder (2008) version
* Roman Ignatov (2006) version
"""
# Validate
length = v_pos_default(length, 14)
smooth = v_pos_default(smooth, 5)
wilders_length = 2 * length - 1
_length = wilders_length + smooth
close = v_series(close, _length)
if close is None:
return
factor = v_scalar(factor, 4.236)
mamode = v_mamode(mamode, "ema")
drift = v_drift(drift)
offset = v_offset(offset)
# Calculate
rsi_ = rsi(close, length)
_mode = mamode.lower()[0] if mamode != "ema" else ""
rsi_ma = ma(mamode, rsi_, length=smooth)
# RSI MA True Range
rsi_ma_tr = rsi_ma.diff(drift).abs()
if all(isnan(rsi_ma_tr)):
return
# Double Smooth the RSI MA True Range using Wilder's Length with a default
# width of 4.236.
smoothed_rsi_tr_ma = ma("ema", rsi_ma_tr, length=wilders_length)
if all(isnan(smoothed_rsi_tr_ma)):
return # Emergency Break
dar = factor * ma("ema", smoothed_rsi_tr_ma, length=wilders_length)
if all(isnan(dar)):
return # Emergency Break
# Create the Upper and Lower Bands around RSI MA.
upperband = rsi_ma + dar
lowerband = rsi_ma - dar
m = close.size
long = Series(0, index=close.index)
short = Series(0, index=close.index)
trend = Series(1, index=close.index)
qqe = Series(rsi_ma.iat[0], index=close.index)
qqe_long = Series(nan, index=close.index)
qqe_short = Series(nan, index=close.index)
for i in range(1, m):
c_rsi, p_rsi = rsi_ma.iat[i], rsi_ma.iat[i - 1]
c_long, p_long = long.iat[i - 1], long.iat[i - 2]
c_short, p_short = short.iat[i - 1], short.iat[i - 2]
# Long Line
if p_rsi > c_long and c_rsi > c_long:
long.iat[i] = maximum(c_long, lowerband.iat[i])
else:
long.iat[i] = lowerband.iat[i]
# Short Line
if p_rsi < c_short and c_rsi < c_short:
short.iat[i] = minimum(c_short, upperband.iat[i])
else:
short.iat[i] = upperband.iat[i]
# Trend & QQE Calculation
# Long: Current RSI_MA value Crosses the Prior Short Line Value
# Short: Current RSI_MA Crosses the Prior Long Line Value
if (c_rsi > c_short and p_rsi < p_short) or \
(c_rsi <= c_short and p_rsi >= p_short):
trend.iat[i] = 1
qqe.iat[i] = qqe_long.iat[i] = long.iat[i]
elif (c_rsi > c_long and p_rsi < p_long) or \
(c_rsi <= c_long and p_rsi >= p_long):
trend.iat[i] = -1
qqe.iat[i] = qqe_short.iat[i] = short.iat[i]
else:
trend.iat[i] = trend.iat[i - 1]
if trend.iat[i] == 1:
qqe.iat[i] = qqe_long.iat[i] = long.iat[i]
else:
qqe.iat[i] = qqe_short.iat[i] = short.iat[i]
# Offset
if offset != 0:
rsi_ma = rsi_ma.shift(offset)
qqe = qqe.shift(offset)
long = long.shift(offset)
short = short.shift(offset)
# Fill
if "fillna" in kwargs:
rsi_ma.fillna(kwargs["fillna"], inplace=True)
qqe.fillna(kwargs["fillna"], inplace=True)
qqe_long.fillna(kwargs["fillna"], inplace=True)
qqe_short.fillna(kwargs["fillna"], inplace=True)
# Name and Category
_props = f"{_mode}_{length}_{smooth}_{factor}"
qqe.name = f"QQE{_props}"
rsi_ma.name = f"QQE{_props}_RSI{_mode.upper()}MA"
qqe_long.name = f"QQEl{_props}"
qqe_short.name = f"QQEs{_props}"
qqe.category = rsi_ma.category = "momentum"
qqe_long.category = qqe_short.category = qqe.category
data = {
qqe.name: qqe,
rsi_ma.name: rsi_ma,
# long.name: long,
# short.name: short
qqe_long.name: qqe_long,
qqe_short.name: qqe_short
}
df = DataFrame(data, index=close.index)
df.name = f"QQE{_props}"
df.category = qqe.category
return df
@@ -1,84 +0,0 @@
# -*- coding: utf-8 -*-
from numba import njit
from pandas import Series
from pandas_ta._typing import DictLike, Int, IntFloat
from pandas_ta.maps import Imports
from pandas_ta.utils import (
nb_idiff,
nb_shift,
v_offset,
v_pos_default,
v_scalar,
v_series,
v_talib
)
# from .mom import mom
@njit(cache=True)
def nb_roc(x, n, k):
return k * nb_idiff(x, n) / nb_shift(x, n)
def roc(
close: Series, length: Int = None,
scalar: IntFloat = None, talib: bool = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Rate of Change
This indicator, also (confusingly) known as Momentum, is a pure
oscillator that quantifies the percent change.
Sources:
* [tradingview](https://www.tradingview.com/wiki/Rate_of_Change_(ROC))
Parameters:
close (Series): ```close``` Series
length (int): The period. Default: ```10```
scalar (float): Scalar. Default: ```100```
talib (bool): If installed, use TA Lib. Default: ```True```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(Series): 1 column
"""
# Validate
length = v_pos_default(length, 10)
close = v_series(close, length + 1)
if close is None:
return
scalar = v_scalar(scalar, 100)
mode_tal = v_talib(talib)
offset = v_offset(offset)
# Calculate
if Imports["talib"] and mode_tal:
from talib import ROC
roc = ROC(close, length)
else:
# roc = scalar * mom(close=close, length=length, talib=mode_tal) \
# / close.shift(length)
np_close = close.to_numpy()
_roc = nb_roc(np_close, length, scalar)
roc = Series(_roc, index=close.index)
# Offset
if offset != 0:
roc = roc.shift(offset)
# Fill
if "fillna" in kwargs:
roc.fillna(kwargs["fillna"], inplace=True)
# Name and Category
roc.name = f"ROC_{length}"
roc.category = "momentum"
return roc
@@ -1,115 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import DataFrame, Series, concat
from pandas_ta._typing import DictLike, Int, IntFloat
from pandas_ta.maps import Imports
from pandas_ta.ma import ma
from pandas_ta.utils import (
signals,
v_drift,
v_mamode,
v_offset,
v_pos_default,
v_scalar,
v_series,
v_talib
)
def rsi(
close: Series, length: Int = None, scalar: IntFloat = None,
mamode: str = None, talib: bool = None,
drift: Int = None, offset: Int = None,
**kwargs: DictLike
) -> Series:
"""Relative Strength Index
This oscillator used to attempts to quantify "velocity" and "magnitude".
Sources:
* [tradingview](https://www.tradingview.com/wiki/Relative_Strength_Index_(RSI))
Parameters:
close (Series): ```close``` Series
length (int): The period. Default: ```14```
scalar (float): Scalar. Default: ```100```
mamode (str): See ```help(ta.ma)```. Default: ```"rma"```
talib (bool): If installed, use TA Lib. Default: ```True```
drift (int): Difference amount. Default: ```1```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(Series): 1 column
Warning:
TA-Lib Correlation: ```np.float64(0.9289853267851295)```
Tip:
Corrective contributions welcome!
"""
# Validate
length = v_pos_default(length, 14)
close = v_series(close, length + 1)
if close is None:
return
scalar = v_scalar(scalar, 100)
mamode = v_mamode(mamode, "rma")
mode_tal = v_talib(talib)
drift = v_drift(drift)
offset = v_offset(offset)
# Calculate
if Imports["talib"] and mode_tal:
from talib import RSI
rsi = RSI(close, length)
else:
negative = close.diff(drift)
positive = negative.copy()
positive[positive < 0] = 0 # Make negatives 0 for the positive series
negative[negative > 0] = 0 # Make positives 0 for the negative series
positive_avg = ma(mamode, positive, length=length, talib=mode_tal)
negative_avg = ma(mamode, negative, length=length, talib=mode_tal)
rsi = scalar * positive_avg / (positive_avg + negative_avg.abs())
# Offset
if offset != 0:
rsi = rsi.shift(offset)
# Fill
if "fillna" in kwargs:
rsi.fillna(kwargs["fillna"], inplace=True)
# Name and Category
rsi.name = f"RSI_{length}"
rsi.category = "momentum"
signal_indicators = kwargs.pop("signal_indicators", False)
if not signal_indicators:
return rsi
else:
signalsdf = concat(
[
DataFrame({rsi.name: rsi}),
signals(
indicator=rsi,
xa=kwargs.pop("xa", 80),
xb=kwargs.pop("xb", 20),
xseries=kwargs.pop("xseries", None),
xseries_a=kwargs.pop("xseries_a", None),
xseries_b=kwargs.pop("xseries_b", None),
cross_values=kwargs.pop("cross_values", False),
cross_series=kwargs.pop("cross_series", True),
offset=offset,
),
],
axis=1,
)
return signalsdf
@@ -1,148 +0,0 @@
# -*- coding: utf-8 -*-
from numpy import nan
from pandas import DataFrame, Series, concat
from pandas_ta._typing import DictLike, Int
from pandas_ta.utils import (
signals,
v_drift,
v_offset,
v_pos_default,
v_series
)
def rsx(
close: Series, length: Int = None, drift: Int = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Relative Strength Xtra
This indicator, by Jurik Research, is an enhanced version of the RSI which
attemps to reduce noise and provide a clearer, though slightly
delayed, signal.
Sources:
* [jurikres](http://www.jurikres.com/catalog1/ms_rsx.htm)
* [prorealcode](https://www.prorealcode.com/prorealtime-indicators/jurik-rsx/)
Parameters:
close (Series): ```close``` Series
length (int): The period. Default: ```14```
drift (int): Difference amount. Default: ```1```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(Series): 1 column
"""
# Validate
length = v_pos_default(length, 14)
close = v_series(close, length)
if close is None:
return
drift = v_drift(drift)
offset = v_offset(offset)
# Calculate
m = close.size
vC, v1C = 0, 0
v4, v8, v10, v14, v18, v20 = 0, 0, 0, 0, 0, 0
f0, f8, f10, f18, f20, f28, f30, f38 = 0, 0, 0, 0, 0, 0, 0, 0
f40, f48, f50, f58, f60, f68, f70, f78 = 0, 0, 0, 0, 0, 0, 0, 0
f80, f88, f90 = 0, 0, 0
result = [nan for _ in range(0, length - 1)] + [50]
for i in range(length, m):
if f90 == 0:
f90 = 1.0
f0 = 0.0
if length - 1.0 >= 5:
f88 = length - 1.0
else:
f88 = 5.0
f8 = 100.0 * close.iat[i]
f18 = 3.0 / (length + 2.0)
f20 = 1.0 - f18
else:
if f88 <= f90:
f90 = f88 + 1
else:
f90 = f90 + 1
f10 = f8
f8 = 100 * close.iat[i]
v8 = f8 - f10
f28 = f20 * f28 + f18 * v8
f30 = f18 * f28 + f20 * f30
vC = 1.5 * f28 - 0.5 * f30
f38 = f20 * f38 + f18 * vC
f40 = f18 * f38 + f20 * f40
v10 = 1.5 * f38 - 0.5 * f40
f48 = f20 * f48 + f18 * v10
f50 = f18 * f48 + f20 * f50
v14 = 1.5 * f48 - 0.5 * f50
f58 = f20 * f58 + f18 * abs(v8)
f60 = f18 * f58 + f20 * f60
v18 = 1.5 * f58 - 0.5 * f60
f68 = f20 * f68 + f18 * v18
f70 = f18 * f68 + f20 * f70
v1C = 1.5 * f68 - 0.5 * f70
f78 = f20 * f78 + f18 * v1C
f80 = f18 * f78 + f20 * f80
v20 = 1.5 * f78 - 0.5 * f80
if f88 >= f90 and f8 != f10:
f0 = 1.0
if f88 == f90 and f0 == 0.0:
f90 = 0.0
if f88 < f90 and v20 > 0.0000000001:
v4 = (v14 / v20 + 1.0) * 50.0
if v4 > 100.0:
v4 = 100.0
if v4 < 0.0:
v4 = 0.0
else:
v4 = 50.0
result.append(v4)
rsx = Series(result, index=close.index)
# Offset
if offset != 0:
rsx = rsx.shift(offset)
# Fill
if "fillna" in kwargs:
rsx.fillna(kwargs["fillna"], inplace=True)
# Name and Category
rsx.name = f"RSX_{length}"
rsx.category = "momentum"
signal_indicators = kwargs.pop("signal_indicators", False)
if not signal_indicators:
return rsx
else:
signalsdf = concat(
[
DataFrame({rsx.name: rsx}),
signals(
indicator=rsx,
xa=kwargs.pop("xa", 80),
xb=kwargs.pop("xb", 20),
xseries=kwargs.pop("xseries", None),
xseries_a=kwargs.pop("xseries_a", None),
xseries_b=kwargs.pop("xseries_b", None),
cross_values=kwargs.pop("cross_values", False),
cross_series=kwargs.pop("cross_series", True),
offset=offset,
),
],
axis=1
)
return signalsdf
@@ -1,87 +0,0 @@
# -*- coding: utf-8 -*-
from numpy import isnan
from pandas import DataFrame, Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.overlap import swma
from pandas_ta.utils import non_zero_range, v_offset, v_pos_default, v_series
def rvgi(
open_: Series, high: Series, low: Series, close: Series,
length: Int = None, swma_length: Int = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Relative Vigor Index
This indicator attempts to quantify the strength of a trend relative to
its trading range.
Sources:
* [investopedia](https://www.investopedia.com/terms/r/relative_vigor_index.asp)
Parameters:
open_ (Series): ```open``` Series
high (Series): ```high``` Series
low (Series): ```low``` Series
close (Series): ```close``` Series
length (int): The period. Default: ```14```
swma_length (int): SWMA period. Default: ```4```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(Series): 1 column
"""
# Validate
length = v_pos_default(length, 14)
swma_length = v_pos_default(swma_length, 4)
_length = length + swma_length - 1
open_ = v_series(open_, _length)
high = v_series(high, _length)
low = v_series(low, _length)
close = v_series(close, _length)
if open_ is None or high is None or low is None or close is None:
return
offset = v_offset(offset)
# Calculate
high_low_range = non_zero_range(high, low)
close_open_range = non_zero_range(close, open_)
numerator = swma(close_open_range, length=swma_length) \
.rolling(length).sum()
denominator = swma(high_low_range, length=swma_length) \
.rolling(length).sum()
rvgi = numerator / denominator
signal = swma(rvgi, length=swma_length)
if all(isnan(signal.to_numpy())):
return # Emergency Break
# Offset
if offset != 0:
rvgi = rvgi.shift(offset)
signal = signal.shift(offset)
# Fill
if "fillna" in kwargs:
rvgi.fillna(kwargs["fillna"], inplace=True)
signal.fillna(kwargs["fillna"], inplace=True)
# Name and Category
rvgi.name = f"RVGI_{length}_{swma_length}"
signal.name = f"RVGIs_{length}_{swma_length}"
rvgi.category = signal.category = "momentum"
data = {rvgi.name: rvgi, signal.name: signal}
df = DataFrame(data, index=close.index)
df.name = f"RVGI_{length}_{swma_length}"
df.category = rvgi.category
return df
@@ -1,71 +0,0 @@
# -*- coding: utf-8 -*-
from numpy import arctan, rad2deg
from pandas import Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.utils import (
nb_idiff,
v_bool,
v_offset,
v_pos_default,
v_series
)
def slope(
close: Series, length: Int = None,
as_angle: bool = None, to_degrees: bool = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Slope
Calculates a rolling slope.
Parameters:
close (Series): ```close``` Series
length (int): The period. Default: ```1```
as_angle (bool): Converts slope to an angle in radians
per ```np.arctan()```. Default: ```False```
to_degrees (value): If ```as_angle=True```, converts radians to
degrees. Default: ```False```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(Series): 1 column
"""
# Validate
length = v_pos_default(length, 1)
close = v_series(close, length + 1)
if close is None:
return
as_angle = v_bool(as_angle, False)
to_degrees = v_bool(to_degrees, False)
offset = v_offset(offset)
# Calculate
np_close = close.to_numpy()
_slope = nb_idiff(np_close, length) / length
if as_angle:
_slope = arctan(_slope)
if to_degrees:
_slope = rad2deg(_slope)
slope = Series(_slope, index=close.index)
# Offset
if offset != 0:
slope = slope.shift(offset)
# Fill
if "fillna" in kwargs:
slope.fillna(kwargs["fillna"], inplace=True)
# Name and Category
slope.name = f"SLOPE_{length}" if not as_angle else f"ANGLE{'d' if to_degrees else 'r'}_{length}"
slope.category = "momentum"
return slope
@@ -1,118 +0,0 @@
# -*- coding: utf-8 -*-
from sys import float_info as sflt
from numpy import maximum, minimum
from pandas import DataFrame, Series
from pandas_ta._typing import DictLike, Int, IntFloat
from pandas_ta.ma import ma
from pandas_ta.utils import (
v_bool,
v_mamode,
v_offset,
v_pos_default,
v_scalar,
v_series,
v_talib
)
def smc(
open_: Series, high: Series, low: Series, close: Series,
abr_length: Int = None, close_length: Int = None, vol_length: Int = None,
percent: Int = None, vol_ratio: IntFloat = None, asint: bool = None,
mamode: str = None, talib: bool = None,
offset: Int = None, **kwargs: DictLike
) -> DictLike:
"""Smart Money Concept
This indicator combines several techniques in an attempt to identify
significant movements that might indicate "smart money" actions.
It uses candlestick patterns, moving averages, and imbalance calculations.
Sources:
* [tradingview](https://www.tradingview.com/script/CnB3fSph-Smart-Money-Concepts-LuxAlgo/)
Parameters:
abr_length (int): ABR length. Default: ```14```
close_length (int): The ```close``` MA period. Default: ```50```
vol_length (int): Volatility period. Default: ```20```
percent (int): Percent of wick that exceeds the body. Default: ```5```
vol_ratio (float): Volatility ratio (high) limit. Default: ```1.5```
asint (bool): Returns as ```Int```. Default: ```True```
mamode (str): See ```help(ta.ma)```. Default: ```"sma"```
talib (bool): If installed, use TA Lib. Default: ```True```
offset (int): Post shift. Default: ```0```
Returns:
(DataFrame): 7 columns
"""
# Validate
abr_length = v_pos_default(abr_length, 14)
close_length = v_pos_default(close_length, 50)
vol_length = v_pos_default(vol_length, 20)
if close_length < abr_length:
abr_length, close_length = close_length, abr_length
_length = max(abr_length, close_length, vol_length) + 1
open_ = v_series(open_, _length)
high = v_series(high, _length)
low = v_series(low, _length)
close = v_series(close, _length)
if open_ is None or high is None or low is None or close is None:
return
percent = v_pos_default(percent, 5)
body_percent = 0.01 * percent
vol_ratio = v_scalar(vol_ratio, 1.5)
asint = v_bool(asint)
mamode = v_mamode(mamode, "sma")
mode_tal = v_talib(talib)
offset = v_offset(offset)
# Calculate
body_high, body_low = maximum(open_, close), minimum(open_, close)
body = body_high - body_low + sflt.epsilon
close_ma = ma(mamode, body, length=close_length, talib=mode_tal)
# Calculate imbalance sizes and percentages based on Average Bar Range (abr)
abr = high.rolling(window=abr_length).max() - low.rolling(window=abr_length).min()
top_imbalance = low.shift(2) - high
btm_imbalance = low - high.shift(2)
top_imbalance_pct = 100 * top_imbalance / abr
btm_imbalance_pct = 100 * btm_imbalance / abr
hld = high - low + sflt.epsilon
high_volatility = hld > vol_ratio * ma(mamode, hld, length=vol_length, talib=mode_tal)
btm_imbalance_flag = (btm_imbalance > 0) & (btm_imbalance_pct > 1)
top_imbalance_flag = (top_imbalance > 0) & (top_imbalance_pct > 1)
if asint:
high_volatility = high_volatility.astype(int)
btm_imbalance_flag = btm_imbalance_flag.astype(int)
top_imbalance_flag = top_imbalance_flag.astype(int)
_props = f"_{abr_length}_{close_length}_{vol_length}_{percent}"
data = {
f"SMChv{_props}": high_volatility,
f"SMCbf{_props}": btm_imbalance_flag,
f"SMCbi{_props}": btm_imbalance,
f"SMCbp{_props}": btm_imbalance_pct,
f"SMCtf{_props}": top_imbalance_flag,
f"SMCti{_props}": top_imbalance,
f"SMCtp{_props}": top_imbalance_pct,
}
df = DataFrame(data, index=close.index)
# Offset
if offset != 0:
df = df.shift(offset)
# Fill
df.ffill(inplace=True)
df.bfill(inplace=True)
# Name and Category
df.name = f"SMC{_props}"
df.category = "momentum"
return df
@@ -1,92 +0,0 @@
# -*- coding: utf-8 -*-
from numpy import isnan
from pandas import DataFrame, Series
from pandas_ta._typing import DictLike, Int, IntFloat
from pandas_ta.utils import v_offset, v_pos_default, v_scalar, v_series
from .tsi import tsi
def smi(
close: Series, fast: Int = None, slow: Int = None,
signal: Int = None, scalar: IntFloat = None,
offset: Int = None, **kwargs: DictLike
) -> DataFrame:
"""SMI Ergodic Indicator
This indicator, by William Blau, is the same as the TSI except the SMI
includes a signal line. A trend is considered bullish when crossing above
zero and bearish when crossing below zero. This implementation includes
both the SMI Ergodic Indicator and SMI Ergodic Oscillator.
Sources:
* [motivewave](https://www.motivewave.com/studies/smi_ergodic_indicator.htm)
* [tradingview A](https://www.tradingview.com/script/Xh5Q0une-SMI-Ergodic-Oscillator/)
* [tradingview B](https://www.tradingview.com/script/cwrgy4fw-SMIIO/)
Parameters:
close (Series): ```close``` Series
fast (int): The short period. Default: ```5```
slow (int): The long period. Default: ```20```
signal (int): Signal period. Default: ```5```
scalar (float): Scalar. Default: ```1```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(DataFrame): 3 columns
"""
# Validate
fast = v_pos_default(fast, 5)
slow = v_pos_default(slow, 20)
signal = v_pos_default(signal, 5)
if slow < fast:
fast, slow = slow, fast
_length = slow + signal + 1
close = v_series(close, _length)
if close is None:
return
scalar = v_scalar(scalar, 1)
offset = v_offset(offset)
# Calculate
tsi_df = tsi(close, fast=fast, slow=slow, signal=signal, scalar=scalar)
if tsi_df is None:
return # Emergency Break
smi = tsi_df.iloc[:, 0]
signalma = tsi_df.iloc[:, 1]
if all(isnan(signalma)):
return # Emergency Break
osc = smi - signalma
# Offset
if offset != 0:
smi = smi.shift(offset)
signalma = signalma.shift(offset)
osc = osc.shift(offset)
# Fill
if "fillna" in kwargs:
smi.fillna(kwargs["fillna"], inplace=True)
signalma.fillna(kwargs["fillna"], inplace=True)
osc.fillna(kwargs["fillna"], inplace=True)
# Name and Category
# _scalar = f"_{scalar}" if scalar != 1 else ""
_props = f"_{fast}_{slow}_{signal}_{scalar}"
smi.name = f"SMI{_props}"
signalma.name = f"SMIs{_props}"
osc.name = f"SMIo{_props}"
smi.category = signalma.category = osc.category = "momentum"
data = {smi.name: smi, signalma.name: signalma, osc.name: osc}
df = DataFrame(data, index=close.index)
df.name = f"SMI{_props}"
df.category = smi.category
return df
@@ -1,205 +0,0 @@
# -*- coding: utf-8 -*-
from numpy import nan
from pandas import DataFrame, Series
from pandas_ta._typing import DictLike, Int, IntFloat
from pandas_ta.overlap import ema, linreg, sma
from pandas_ta.trend import decreasing, increasing
from pandas_ta.utils import (
simplify_columns,
unsigned_differences,
v_bool,
v_mamode,
v_offset,
v_pos_default,
v_series
)
from pandas_ta.volatility import bbands, kc
from .mom import mom
def squeeze(
high: Series, low: Series, close: Series,
bb_length: Int = None, bb_std: IntFloat = None,
kc_length: Int = None, kc_scalar: IntFloat = None,
mom_length: Int = None, mom_smooth: Int = None,
use_tr: bool = None, mamode: str = None,
prenan: bool = None,
offset: Int = None, **kwargs: DictLike
) -> DataFrame:
"""Squeeze
This indicator, based on John Carter's "TTM Squeeze" indicator, attempts
identify momentum using volatility.
Sources:
* "Mastering the Trade" (chapter 11), John Carter
* [thinkorswim](https://tlc.thinkorswim.com/center/reference/Tech-Indicators/studies-library/T-U/TTM-Squeeze)
* [tradestation](https://tradestation.tradingappstore.com/products/TTMSqueeze)
* [tradingview](https://www.tradingview.com/scripts/lazybear/)
Parameters:
high (Series): ```high``` Series
low (Series): ```low``` Series
close (Series): ```close``` Series
bb_length (int): BB period. Default: ```20```
bb_std (float): BB Std. Dev. Default: ```2```
kc_length (int): KC period. Default: ```20```
kc_scalar (float): KC scalar. Default: ```1.5```
mom_length (int): Momentum Period. Default: ```12```
mom_smooth (int): Momentum Smoothing period. Default: ```6```
mamode (str): One of: "ema" or "sma". Default: ```"sma"```
prenan (bool): Apply prenans. Default: ```False```
offset (int): Post shift. Default: ```0```
Other Parameters:
tr (value): Use True Range for Keltner Channels. Default: ```True```
asint (bool): Returns as ```Int```. Default: ```True```
lazybear (value): LazyBear's TradingView. Default: ```False```
detailed (value): Extra detailed. Default: ```False```
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(DataFrame):
* Default: 4 columns
* Detailed: 10 columns
Note: Volatility
* Increasing: ```kc``` and ```bbands``` difference increases
* Decreasing: ```kc``` and ```bbands``` difference decreases
"""
# Validate
bb_length = v_pos_default(bb_length, 20)
kc_length = v_pos_default(kc_length, 20)
mom_length = v_pos_default(mom_length, 12)
mom_smooth = v_pos_default(mom_smooth, 6)
_length = max(bb_length, kc_length, mom_length, mom_smooth) + 1
high = v_series(high, _length)
low = v_series(low, _length)
close = v_series(close, _length)
if high is None or low is None or close is None:
return
bb_std = v_pos_default(bb_std, 2.0)
kc_scalar = v_pos_default(kc_scalar, 1.5)
mamode = v_mamode(mamode, "sma")
prenan = v_bool(prenan, False)
offset = v_offset(offset)
use_tr = kwargs.pop("tr", True)
asint = kwargs.pop("asint", True)
detailed = kwargs.pop("detailed", False)
lazybear = kwargs.pop("lazybear", False)
# Calculate
bbd = bbands(close, length=bb_length, std=bb_std, mamode=mamode)
kch = kc(
high, low, close, length=kc_length, scalar=kc_scalar,
mamode=mamode, tr=use_tr
)
# Simplify KC and BBAND column names for dynamic access
bbd.columns = simplify_columns(bbd)
kch.columns = simplify_columns(kch)
if lazybear:
highest_high = high.rolling(kc_length).max()
lowest_low = low.rolling(kc_length).min()
avg_ = 0.5 * (0.5 * (highest_high + lowest_low) + kch.b)
squeeze = linreg(close - avg_, length=kc_length)
else:
momo = mom(close, length=mom_length)
if mamode.lower() == "ema":
squeeze = ema(momo, length=mom_smooth)
else: # "sma"
squeeze = sma(momo, length=mom_smooth)
# Classify Squeezes
squeeze_on = (bbd.l > kch.l) & (bbd.u < kch.u)
squeeze_off = (bbd.l < kch.l) & (bbd.u > kch.u)
no_squeeze = ~squeeze_on & ~squeeze_off
# Offset
if offset != 0:
squeeze = squeeze.shift(offset)
squeeze_on = squeeze_on.shift(offset)
squeeze_off = squeeze_off.shift(offset)
no_squeeze = no_squeeze.shift(offset)
# Fill
if "fillna" in kwargs:
squeeze.fillna(kwargs["fillna"], inplace=True)
squeeze_on.fillna(kwargs["fillna"], inplace=True)
squeeze_off.fillna(kwargs["fillna"], inplace=True)
no_squeeze.fillna(kwargs["fillna"], inplace=True)
# Name and Category
_props = "" if use_tr else "hlr"
_props += f"_{bb_length}_{bb_std}_{kc_length}_{kc_scalar}"
_props += "_LB" if lazybear else ""
squeeze.name = f"SQZ{_props}"
if asint:
squeeze_on = squeeze_on.astype(int)
squeeze_off = squeeze_off.astype(int)
no_squeeze = no_squeeze.astype(int)
if prenan:
nanlength = max(bb_length, kc_length) - 2
squeeze_on[:nanlength] = nan
squeeze_off[:nanlength] = nan
no_squeeze[:nanlength] = nan
data = {
squeeze.name: squeeze,
f"SQZ_ON": squeeze_on,
f"SQZ_OFF": squeeze_off,
f"SQZ_NO": no_squeeze
}
df = DataFrame(data, index=close.index)
df.name = squeeze.name
df.category = squeeze.category = "momentum"
# More Detail
if detailed:
pos_squeeze = squeeze[squeeze >= 0]
neg_squeeze = squeeze[squeeze < 0]
pos_inc, pos_dec = unsigned_differences(pos_squeeze, asint=True)
neg_inc, neg_dec = unsigned_differences(neg_squeeze, asint=True)
pos_inc *= squeeze
pos_dec *= squeeze
neg_dec *= squeeze
neg_inc *= squeeze
pos_inc.replace(0, nan, inplace=True)
pos_dec.replace(0, nan, inplace=True)
neg_dec.replace(0, nan, inplace=True)
neg_inc.replace(0, nan, inplace=True)
sqz_inc = squeeze * increasing(squeeze)
sqz_dec = squeeze * decreasing(squeeze)
sqz_inc.replace(0, nan, inplace=True)
sqz_dec.replace(0, nan, inplace=True)
# Handle fills
if "fillna" in kwargs:
sqz_inc.fillna(kwargs["fillna"], inplace=True)
sqz_dec.fillna(kwargs["fillna"], inplace=True)
pos_inc.fillna(kwargs["fillna"], inplace=True)
pos_dec.fillna(kwargs["fillna"], inplace=True)
neg_dec.fillna(kwargs["fillna"], inplace=True)
neg_inc.fillna(kwargs["fillna"], inplace=True)
df[f"SQZ_INC"] = sqz_inc
df[f"SQZ_DEC"] = sqz_dec
df[f"SQZ_PINC"] = pos_inc
df[f"SQZ_PDEC"] = pos_dec
df[f"SQZ_NDEC"] = neg_dec
df[f"SQZ_NINC"] = neg_inc
return df
@@ -1,222 +0,0 @@
# -*- coding: utf-8 -*-
from numpy import nan
from pandas import DataFrame, Series
from pandas_ta._typing import DictLike, Int, IntFloat
from pandas_ta.ma import ma
from pandas_ta.momentum import mom
from pandas_ta.trend import decreasing, increasing
from pandas_ta.utils import (
simplify_columns,
unsigned_differences,
v_bool,
v_mamode,
v_offset,
v_pos_default,
v_scalar,
v_series
)
from pandas_ta.volatility import bbands, kc
def squeeze_pro(
high: Series, low: Series, close: Series,
bb_length: Int = None, bb_std: IntFloat = None,
kc_length: Int = None, kc_scalar_narrow: IntFloat = None,
kc_scalar_normal: IntFloat = None, kc_scalar_wide: IntFloat = None,
mom_length: Int = None, mom_smooth: Int = None,
use_tr: bool = None, mamode: str = None,
prenan: bool = None,
offset: Int = None, **kwargs: DictLike
) -> DataFrame:
"""Squeeze Pro
This indicator, based on John Carter's "TTM Squeeze" indicator, attempts
identify momentum using volatility with additional details.
Sources:
* [usethinkscript](https://usethinkscript.com/threads/john-carters-squeeze-pro-indicator-for-thinkorswim-free.4021/)
* [tradingview](https://www.tradingview.com/script/TAAt6eRX-Squeeze-PRO-Indicator-Makit0/)
Parameters:
high (Series): ```high``` Series
low (Series): ```low``` Series
close (Series): ```close``` Series
bb_length (int): BB period. Default: ```20```
bb_std (float): BB Std. Dev. Default: ```2```
kc_length (int): KC period. Default: ```20```
kc_scalar_normal (float): Keltner Channel scalar for normal channel.
Default: ```1.5```
kc_scalar_narrow (float): Narrow channel KC scalar. Default: ```1```
kc_scalar_wide (float): Wide channel KC scalar. Default: ```2```
mom_length (int): Momentum Period. Default: ```12```
mom_smooth (int): Momentum Smoothing period. Default: ```6```
mamode (str): One of: "ema" or "sma". Default: ```"sma"```
prenan (bool): Apply prenans. Default: ```False```
offset (int): Post shift. Default: ```0```
Other Parameters:
tr (value): Use True Range for Keltner Channels.
Default: ```True```
asint (bool): Returns as ```Int```. Default: ```True```
mamode (value): Which MA to use. Default: ```"sma"```
detailed (value): Extra detailed. Default: ```False```
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(DataFrame): 6 columns (_default_) or 12 columns if ```detailed=True```
Warning:
May be depreciated in the future and combined with ```squeeze```.
"""
# Validate
bb_length = v_pos_default(bb_length, 20)
kc_length = v_pos_default(kc_length, 20)
mom_length = v_pos_default(mom_length, 12)
mom_smooth = v_pos_default(mom_smooth, 6)
_length = max(bb_length, kc_length, mom_length, mom_smooth) + 1
high = v_series(high, _length)
low = v_series(low, _length)
close = v_series(close, _length)
if high is None or low is None or close is None:
return
kc_scalar_narrow = v_scalar(kc_scalar_narrow, 1)
kc_scalar_normal = v_scalar(kc_scalar_normal, 1.5)
kc_scalar_wide = v_scalar(kc_scalar_wide, 2)
prenan = v_bool(prenan, False)
valid_kc_scaler = kc_scalar_wide > kc_scalar_normal \
and kc_scalar_normal > kc_scalar_narrow
if not valid_kc_scaler:
return
bb_std = v_pos_default(bb_std, 2.0)
mamode = v_mamode(mamode, "sma")
offset = v_offset(offset)
use_tr = kwargs.pop("tr", True)
asint = kwargs.pop("asint", True)
detailed = kwargs.pop("detailed", False)
# Calculate
bbd = bbands(close, length=bb_length, std=bb_std, mamode=mamode)
kch_wide = kc(
high, low, close, length=kc_length, scalar=kc_scalar_wide,
mamode=mamode, tr=use_tr
)
kch_normal = kc(
high, low, close, length=kc_length, scalar=kc_scalar_normal,
mamode=mamode, tr=use_tr
)
kch_narrow = kc(
high, low, close, length=kc_length, scalar=kc_scalar_narrow,
mamode=mamode, tr=use_tr
)
# Simplify KC and BBAND column names for dynamic access
bbd.columns = simplify_columns(bbd)
kch_wide.columns = simplify_columns(kch_wide)
kch_normal.columns = simplify_columns(kch_normal)
kch_narrow.columns = simplify_columns(kch_narrow)
momo = mom(close, length=mom_length)
squeeze = ma(mamode, momo, length=mom_smooth)
# Classify Squeezes
squeeze_on_wide = (bbd.l > kch_wide.l) & (bbd.u < kch_wide.u)
squeeze_on_normal = (bbd.l > kch_normal.l) & (bbd.u < kch_normal.u)
squeeze_on_narrow = (bbd.l > kch_narrow.l) & (bbd.u < kch_narrow.u)
squeeze_off_wide = (bbd.l < kch_wide.l) & (bbd.u > kch_wide.u)
no_squeeze = ~squeeze_on_wide & ~squeeze_off_wide
# Offset
if offset != 0:
squeeze = squeeze.shift(offset)
squeeze_on_wide = squeeze_on_wide.shift(offset)
squeeze_on_normal = squeeze_on_normal.shift(offset)
squeeze_on_narrow = squeeze_on_narrow.shift(offset)
squeeze_off_wide = squeeze_off_wide.shift(offset)
no_squeeze = no_squeeze.shift(offset)
# Fill
if "fillna" in kwargs:
squeeze.fillna(kwargs["fillna"], inplace=True)
squeeze_on_wide.fillna(kwargs["fillna"], inplace=True)
squeeze_on_normal.fillna(kwargs["fillna"], inplace=True)
squeeze_on_narrow.fillna(kwargs["fillna"], inplace=True)
squeeze_off_wide.fillna(kwargs["fillna"], inplace=True)
no_squeeze.fillna(kwargs["fillna"], inplace=True)
# Name and Category
_props = "" if use_tr else "hlr"
_props += f"_{bb_length}_{bb_std}_{kc_length}_{kc_scalar_wide}_{kc_scalar_normal}_{kc_scalar_narrow}"
squeeze.name = f"SQZPRO{_props}"
if asint:
squeeze_on_wide = squeeze_on_wide.astype(int)
squeeze_on_narrow = squeeze_on_narrow.astype(int)
squeeze_on_normal = squeeze_on_normal.astype(int)
squeeze_off_wide = squeeze_off_wide.astype(int)
no_squeeze = no_squeeze.astype(int)
if prenan:
nanlength = max(bb_length, kc_length) - 2
squeeze_on_wide[:nanlength] = nan
squeeze_on_narrow[:nanlength] = nan
squeeze_on_normal[:nanlength] = nan
squeeze_off_wide[:nanlength] = nan
no_squeeze[:nanlength] = nan
data = {
squeeze.name: squeeze,
f"SQZPRO_ON_WIDE": squeeze_on_wide,
f"SQZPRO_ON_NORMAL": squeeze_on_normal,
f"SQZPRO_ON_NARROW": squeeze_on_narrow,
f"SQZPRO_OFF": squeeze_off_wide,
f"SQZPRO_NO": no_squeeze
}
df = DataFrame(data, index=close.index)
df.name = squeeze.name
df.category = squeeze.category = "momentum"
# More Detail
if detailed:
pos_squeeze = squeeze[squeeze >= 0]
neg_squeeze = squeeze[squeeze < 0]
pos_inc, pos_dec = unsigned_differences(pos_squeeze, asint=True)
neg_inc, neg_dec = unsigned_differences(neg_squeeze, asint=True)
pos_inc *= squeeze
pos_dec *= squeeze
neg_dec *= squeeze
neg_inc *= squeeze
pos_inc.replace(0, nan, inplace=True)
pos_dec.replace(0, nan, inplace=True)
neg_dec.replace(0, nan, inplace=True)
neg_inc.replace(0, nan, inplace=True)
sqz_inc = squeeze * increasing(squeeze)
sqz_dec = squeeze * decreasing(squeeze)
sqz_inc.replace(0, nan, inplace=True)
sqz_dec.replace(0, nan, inplace=True)
# Fill
if "fillna" in kwargs:
sqz_inc.fillna(kwargs["fillna"], inplace=True)
sqz_dec.fillna(kwargs["fillna"], inplace=True)
pos_inc.fillna(kwargs["fillna"], inplace=True)
pos_dec.fillna(kwargs["fillna"], inplace=True)
neg_dec.fillna(kwargs["fillna"], inplace=True)
neg_inc.fillna(kwargs["fillna"], inplace=True)
df[f"SQZPRO_INC"] = sqz_inc
df[f"SQZPRO_DEC"] = sqz_dec
df[f"SQZPRO_PINC"] = pos_inc
df[f"SQZPRO_PDEC"] = pos_dec
df[f"SQZPRO_NDEC"] = neg_dec
df[f"SQZPRO_NINC"] = neg_inc
return df
@@ -1,175 +0,0 @@
# -*- coding: utf-8 -*-
from numpy import nan
from pandas import DataFrame, Series
from pandas_ta._typing import DictLike, Int, IntFloat
from pandas_ta.overlap import ema
from pandas_ta.utils import (
non_zero_range,
v_offset,
v_pos_default,
v_series
)
def schaff_tc(close: Series, seed: Series, tc_length: int, factor: IntFloat):
lowest_xmacd = seed.rolling(tc_length).min()
xmacd_range = non_zero_range(seed.rolling(tc_length).max(), lowest_xmacd)
m = len(seed)
# Initialize lists
stoch1, pf = [0] * m, [0] * m
stoch2, pff = [0] * m, [0] * m
for i in range(1, m):
# %Fast K of MACD
if lowest_xmacd.iloc[i] > 0:
stoch1[i] = 100 * ((seed.iloc[i] - lowest_xmacd.iloc[i]) / xmacd_range.iloc[i])
else:
stoch1[i] = stoch1[i - 1]
# Smoothed Calculation for % Fast D of MACD
pf[i] = round(pf[i - 1] + (factor * (stoch1[i] - pf[i - 1])), 8)
# find min and max so far
if i < tc_length:
# If there are not enough elements for a full tclength window,
# use what is available
lowest_pf = min(pf[:i+1])
highest_pf = max(pf[:i+1])
else:
lowest_pf = min(pf[i - tc_length + 1:i + 1])
highest_pf = max(pf[i - tc_length + 1:i + 1])
# Ensure non-zero range
pf_range = highest_pf - lowest_pf if highest_pf - lowest_pf > 0 else 1
# % of Fast K of PF
if pf_range > 0:
stoch2[i] = 100 * ((pf[i] - lowest_pf) / pf_range)
else:
stoch2[i] = stoch2[i - 1]
pff[i] = round(pff[i - 1] + (factor * (stoch2[i] - pff[i - 1])), 8)
pf_series = Series(pf, index=close.index)
pff_series = Series(pff, index=close.index)
return pff_series, pf_series
def stc(
close: Series, tc_length: Int = None,
fast: Int = None, slow: Int = None, factor: IntFloat = None,
offset: Int = None, **kwargs: DictLike
) -> DataFrame:
"""Schaff Trend Cycle
This indicator is an evolved MACD with additional smoothing.
Sources:
* [rengel8](https://github.com/rengel8)
* [prorealcode](https://www.prorealcode.com/prorealtime-indicators/schaff-trend-cycle2/)
Parameters:
close (Series): ```close``` Series
tc_length (int): TC period. (Adjust to the half of cycle)
Default: ```10```
fast (int): Fast MA period. Default: ```12```
slow (int): Slow MA period. Default: ```26```
factor (float): Smoothing factor for last stoch. calculation.
Default: ```0.5```
offset (int): How many bars to shift the results. Default: ```0``
Other Parameters:
ma1 (Series): User chosen MA. Default: ```False```
ma2 (Series): User chosen MA. Default: ```False```
osc (Series): User chosen oscillator. Default: ```False```
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(DataFrame): 3 columns
Note:
Can also seed STC with two MAs, ```ma1``` and ```ma2```, or an oscillator ```osc```.
* ```ma1``` and ```ma2``` are **both** required if this option is used.
"""
# Validate
fast = v_pos_default(fast, 12)
slow = v_pos_default(slow, 26)
tc_length = v_pos_default(tc_length, 10)
if slow < fast:
fast, slow = slow, fast
_length = max(tc_length, fast, slow)
close = v_series(close, _length)
if close is None:
return
factor = v_pos_default(factor, 0.5)
offset = v_offset(offset)
# Calculate
# kwargs allows for three more series (ma1, ma2 and osc) which can be passed
# here ma1 and ma2 input negate internal ema calculations, osc substitutes
# both ma's.
ma1 = kwargs.pop("ma1", False)
ma2 = kwargs.pop("ma2", False)
osc = kwargs.pop("osc", False)
if isinstance(ma1, Series) and isinstance(ma2, Series) and not osc:
ma1 = v_series(ma1, _length)
ma2 = v_series(ma2, _length)
if ma1 is None or ma2 is None:
return
seed = ma1 - ma2
elif isinstance(osc, Series):
osc = v_series(osc, _length)
if osc is None:
return
seed = osc
else:
fastma = ema(close, length=fast)
slowma = ema(close, length=slow)
seed = fastma - slowma
pff, pf = schaff_tc(close, seed, tc_length, factor)
pf[:_length - 1] = nan
stc = Series(pff, index=close.index)
macd = Series(seed, index=close.index)
stoch = Series(pf, index=close.index)
stc.iloc[:_length - 1] = nan
# Offset
if offset != 0:
stc = stc.shift(offset)
macd = macd.shift(offset)
stoch = stoch.shift(offset)
# Fill
if "fillna" in kwargs:
stc.fillna(kwargs["fillna"], inplace=True)
macd.fillna(kwargs["fillna"], inplace=True)
stoch.fillna(kwargs["fillna"], inplace=True)
# Name and Category
_props = f"_{tc_length}_{fast}_{slow}_{factor}"
stc.name = f"STC{_props}"
macd.name = f"STCmacd{_props}"
stoch.name = f"STCstoch{_props}"
stc.category = macd.category = stoch.category = "momentum"
data = {
stc.name: stc,
macd.name: macd,
stoch.name: stoch
}
df = DataFrame(data, index=close.index)
df.name = f"STC{_props}"
df.category = stc.category
return df
@@ -1,121 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import DataFrame, Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.ma import ma
from pandas_ta.maps import Imports
from pandas_ta.utils import (
non_zero_range,
tal_ma,
v_mamode,
v_offset,
v_pos_default,
v_series,
v_talib
)
def stoch(
high: Series, low: Series, close: Series,
k: Int = None, d: Int = None, smooth_k: Int = None,
mamode: str = None, talib: bool = None,
offset: Int = None, **kwargs: DictLike
) -> DataFrame:
"""Stochastic
This indicator, by George Lane in the 1950's, attempts to identify and
quantify momentum; it assumes that momentum precedes value change.
Sources:
* [sierrachart](https://www.sierrachart.com/index.php?page=doc/StudiesReference.php&ID=332&Name=KD_-_Slow)
* [tradingview](https://www.tradingview.com/wiki/Stochastic_(STOCH))
Parameters:
high (Series): ```high``` Series
low (Series): ```low``` Series
close (Series): ```close``` Series
k (int): The Fast %K period. Default: ```14```
d (int): The Slow %D period. Default: ```3```
smooth_k (int): The Slow %K period. Default: ```3```
mamode (str): See ```help(ta.ma)```. Default: ```"sma"```
talib (bool): If installed, use TA Lib. Default: ```True```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(DataFrame): 3 columns
"""
# Validate
k = v_pos_default(k, 14)
d = v_pos_default(d, 3)
smooth_k = v_pos_default(smooth_k, 3)
_length = k + d + smooth_k
high = v_series(high, _length)
low = v_series(low, _length)
close = v_series(close, _length)
if high is None or low is None or close is None:
return
mode_tal = v_talib(talib)
mamode = v_mamode(mamode, "sma")
offset = v_offset(offset)
# Calculate
if Imports["talib"] and mode_tal and smooth_k > 2:
from talib import STOCH
stoch_ = STOCH(
high, low, close, k, d, tal_ma(mamode), d, tal_ma(mamode)
)
stoch_k, stoch_d = stoch_[0], stoch_[1]
else:
ll = low.rolling(k).min()
hh = high.rolling(k).max()
stoch = 100 * (close - ll) / non_zero_range(hh, ll)
if stoch is None: return
stoch_fvi = stoch.loc[stoch.first_valid_index():, ]
if smooth_k == 1:
stoch_k = stoch
else:
stoch_k = ma(mamode, stoch_fvi, length=smooth_k)
stochk_fvi = stoch_k.loc[stoch_k.first_valid_index():, ]
stoch_d = ma(mamode, stochk_fvi, length=d)
stoch_h = stoch_k - stoch_d # Histogram
# Offset
if offset != 0:
stoch_k = stoch_k.shift(offset)
stoch_d = stoch_d.shift(offset)
stoch_h = stoch_h.shift(offset)
# Fill
if "fillna" in kwargs:
stoch_k.fillna(kwargs["fillna"], inplace=True)
stoch_d.fillna(kwargs["fillna"], inplace=True)
stoch_h.fillna(kwargs["fillna"], inplace=True)
# Name and Category
_name = "STOCH"
_props = f"_{k}_{d}_{smooth_k}"
stoch_k.name = f"{_name}k{_props}"
stoch_d.name = f"{_name}d{_props}"
stoch_h.name = f"{_name}h{_props}"
stoch_k.category = stoch_d.category = stoch_h.category = "momentum"
data = {
stoch_k.name: stoch_k,
stoch_d.name: stoch_d,
stoch_h.name: stoch_h
}
df = DataFrame(data, index=close.index)
df.name = f"{_name}{_props}"
df.category = stoch_k.category
return df
@@ -1,100 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import DataFrame, Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.ma import ma
from pandas_ta.maps import Imports
from pandas_ta.utils import (
non_zero_range,
tal_ma,
v_mamode,
v_offset,
v_pos_default,
v_series,
v_talib
)
def stochf(
high: Series, low: Series, close: Series,
k: Int = None, d: Int = None,
mamode: str = None, talib: bool = None,
offset: Int = None, **kwargs: DictLike
) -> DataFrame:
"""Fast Stochastic
This indicator, by George Lane in the 1950's, attempts to identify and
quantify momentum like STOCH, but is more volatile.
Sources:
* [corporatefinanceinstitute](https://corporatefinanceinstitute.com/resources/knowledge/trading-investing/fast-stochastic-indicator/)
* [sierrachart](https://www.sierrachart.com/index.php?page=doc/StudiesReference.php&ID=333&Name=KD_-_Fast)
Parameters:
high (Series): ```high``` Series
low (Series): ```low``` Series
close (Series): ```close``` Series
k (int): The Fast %K period. Default: ```14```
d (int): The Slow %D period. Default: ```3```
mamode (str): See ```help(ta.ma)```. Default: ```"sma"```
talib (bool): If installed, use TA Lib. Default: ```True```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(DataFrame): 2 columns
"""
# Validate
k = v_pos_default(k, 14)
d = v_pos_default(d, 3)
_length = k + d - 1
high = v_series(high, _length)
low = v_series(low, _length)
close = v_series(close, _length)
if high is None or low is None or close is None:
return
mamode = v_mamode(mamode, "sma")
mode_tal = v_talib(talib)
offset = v_offset(offset)
# Calculate
if Imports["talib"] and mode_tal:
from talib import STOCHF
stochf_ = STOCHF(high, low, close, k, d, tal_ma(mamode))
stochf_k, stochf_d = stochf_[0], stochf_[1]
else:
lowest_low = low.rolling(k).min()
highest_high = high.rolling(k).max()
stochf_k = 100 * (close - lowest_low) \
/ non_zero_range(highest_high, lowest_low)
stochfk_fvi = stochf_k.loc[stochf_k.first_valid_index():, ]
stochf_d = ma(mamode, stochfk_fvi, length=d, talib=mode_tal)
# Offset
if offset != 0:
stochf_k = stochf_k.shift(offset)
stochf_d = stochf_d.shift(offset)
# Fill
if "fillna" in kwargs:
stochf_k.fillna(kwargs["fillna"], inplace=True)
stochf_d.fillna(kwargs["fillna"], inplace=True)
# Name and Category
_name = "STOCHF"
_props = f"_{k}_{d}"
stochf_k.name = f"{_name}k{_props}"
stochf_d.name = f"{_name}d{_props}"
stochf_k.category = stochf_d.category = "momentum"
data = {stochf_k.name: stochf_k, stochf_d.name: stochf_d}
df = DataFrame(data, index=close.index)
df.name = f"{_name}{_props}"
df.category = stochf_k.category
return df
@@ -1,104 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import DataFrame, Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.ma import ma
from pandas_ta.maps import Imports
from pandas_ta.momentum import rsi
from pandas_ta.utils import (
non_zero_range,
v_mamode,
v_offset,
v_pos_default,
v_series,
v_talib
)
def stochrsi(
close: Series, length: Int = None, rsi_length: Int = None,
k: Int = None, d: Int = None, mamode: str = None,
talib: bool = None, offset: Int = None, **kwargs: DictLike
) -> DataFrame:
"""Stochastic RSI
This indicator attempts to quantify RSI relative to its High-Low range.
Sources:
* "Stochastic RSI and Dynamic Momentum Index", Tushar Chande and
Stanley Kroll, Stock & Commodities V.11:5 (189-199)
* [tradingview](https://www.tradingview.com/wiki/Stochastic_(STOCH))
Parameters:
close (Series): ```close``` Series
length (int): The period. Default: ```14```
rsi_length (int): RSI period. Default: ```14```
k (int): The Fast %K period. Default: ```3```
d (int): The Slow %K period. Default: ```3```
mamode (str): See ```help(ta.ma)```. Default: ```"sma"```
talib (bool): If installed, use TA Lib. Default: ```True```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(DataFrame): 2 columns
Note:
May be more sensitive to RSI and thus identify potential "overbought"
or "oversold" signals.
"""
# Validate
length = v_pos_default(length, 14)
rsi_length = v_pos_default(rsi_length, 14)
k = v_pos_default(k, 3)
d = v_pos_default(d, 3)
_length = length + rsi_length + 2
close = v_series(close, _length)
if close is None:
return
mamode = v_mamode(mamode, "sma")
mode_tal = v_talib(talib)
offset = v_offset(offset)
# Calculate
# if Imports["talib"] and mode_tal:
# from talib import RSI
# rsi_ = RSI(close, length)
# else:
rsi_ = rsi(close, length=rsi_length)
lowest_rsi = rsi_.rolling(length).min()
highest_rsi = rsi_.rolling(length).max()
stoch = 100 * (rsi_ - lowest_rsi) / non_zero_range(highest_rsi, lowest_rsi)
stochrsi_k = ma(mamode, stoch, length=k)
stochrsi_d = ma(mamode, stochrsi_k, length=d)
# Offset
if offset != 0:
stochrsi_k = stochrsi_k.shift(offset)
stochrsi_d = stochrsi_d.shift(offset)
# Fill
if "fillna" in kwargs:
stochrsi_k.fillna(kwargs["fillna"], inplace=True)
stochrsi_d.fillna(kwargs["fillna"], inplace=True)
# Name and Category
_name = "STOCHRSI"
_props = f"_{length}_{rsi_length}_{k}_{d}"
stochrsi_k.name = f"{_name}k{_props}"
stochrsi_d.name = f"{_name}d{_props}"
stochrsi_k.category = stochrsi_d.category = "momentum"
data = {stochrsi_k.name: stochrsi_k, stochrsi_d.name: stochrsi_d}
df = DataFrame(data, index=close.index)
df.name = f"{_name}{_props}"
df.category = stochrsi_k.category
return df
@@ -1,129 +0,0 @@
# -*- coding: utf-8 -*-
from numpy import isnan, zeros
from pandas import DataFrame, Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.ma import ma
from pandas_ta.utils import (
sum_signed_rolling_deltas,
v_bool,
v_mamode,
v_offset,
v_pos_default,
v_series
)
def tmo(
open_: Series, close: Series, tmo_length: Int = None,
calc_length: Int = None, smooth_length: Int = None,
momentum: bool = None, normalize: bool = None, exclusive: bool = None,
mamode: str = None, offset: Int = None, **kwargs: DictLike,
) -> DataFrame:
"""True Momentum Oscillator
This indicator attempts to quantify momentum.
Sources:
* [tradingview A](https://www.tradingview.com/script/VRwDppqd-True-Momentum-Oscillator/)
* [tradingview B](https://www.tradingview.com/script/65vpO7T5-True-Momentum-Oscillator-Universal-Edition/)
Parameters:
open_ (Series): ```open``` Series
close (Series): ```close``` Series
tmo_length (int): TMO period. Default: ```14```
calc_length (int): Initial MA period. Default: ```5```
smooth_length (int): Main and smooth signal MA period. Default: ```3```
mamode (str): See ```help(ta.ma)```. Default: ```"ema"```
momentum (bool): Compute main and smooth momentum. Default: ```False```
normalize (bool): Normalize. Default: ```False```
exclusive (bool): Exclusive period over ```n``` bars, or inclusively
over ```n-1``` bars. Default: ```True```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): DataFrame.fillna(value)
Returns:
(DataFrame): 4 columns
"""
# Validate
tmo_length = v_pos_default(tmo_length, 14)
calc_length = v_pos_default(calc_length, 5)
smooth_length = v_pos_default(smooth_length, 3)
_length = max(tmo_length, calc_length, smooth_length)
open_ = v_series(open_, _length)
close = v_series(close, _length)
offset = v_offset(offset)
if "length" in kwargs:
kwargs.pop("length")
if open_ is None or close is None:
return None
mamode = v_mamode(mamode, "ema")
compute_momentum = v_bool(momentum, False)
normalize_signal = v_bool(normalize, False)
exclusive = v_bool(exclusive, True)
signed_diff_sum = sum_signed_rolling_deltas(
open_, close, tmo_length, exclusive=exclusive
)
if all(isnan(signed_diff_sum)):
return None # Emergency Break
initial_ma = ma(mamode, signed_diff_sum, length=calc_length)
if all(isnan(initial_ma)):
return None # Emergency Break
main = ma(mamode, initial_ma, length=smooth_length)
if all(isnan(main)):
return None # Emergency Break
smooth = ma(mamode, main, length=smooth_length)
if all(isnan(smooth)):
return None # Emergency Break
if compute_momentum:
mom_main = main - main.shift(tmo_length)
mom_smooth = smooth - smooth.shift(tmo_length)
else:
zero_array = zeros(main.size)
mom_main = Series(zero_array, index=main.index)
mom_smooth = Series(zero_array, index=smooth.index)
# Offset
if offset != 0:
main = main.shift(offset)
smooth = smooth.shift(offset)
mom_main = mom_main.shift(offset)
mom_smooth = mom_smooth.shift(offset)
# Fill
if "fillna" in kwargs:
main.fillna(kwargs["fillna"], inplace=True)
smooth.fillna(kwargs["fillna"], inplace=True)
mom_main.fillna(kwargs["fillna"], inplace=True)
mom_smooth.fillna(kwargs["fillna"], inplace=True)
# Name and Category
_props = f"_{tmo_length}_{calc_length}_{smooth_length}"
main.name = f"TMO{_props}"
smooth.name = f"TMOs{_props}"
mom_main.name = f"TMOM{_props}"
mom_smooth.name = f"TMOMs{_props}"
main.category = smooth.category = "momentum"
mom_main.category = mom_smooth.category = main.category
data = {
main.name: main,
smooth.name: smooth,
mom_main.name: mom_main,
mom_smooth.name: mom_smooth,
}
df = DataFrame(data, index=close.index)
df.name = f"TMO{_props}"
df.category = main.category
return df
@@ -1,93 +0,0 @@
# -*- coding: utf-8 -*-
from numpy import isnan
from pandas import DataFrame, Series
from pandas_ta._typing import DictLike, Int, IntFloat
from pandas_ta.overlap.ema import ema
from pandas_ta.utils import (
v_drift,
v_offset,
v_pos_default,
v_scalar,
v_series
)
def trix(
close: Series, length: Int = None, signal: Int = None,
scalar: IntFloat = None, drift: Int = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Trix
This indicator attempts to identify divergences as an oscillator.
Sources:
* [tradingview](https://www.tradingview.com/wiki/TRIX)
Parameters:
close (Series): ```close``` Series
length (int): The period. Default: ```18```
signal (int): Signal period. Default: ```9```
scalar (float): Scalar. Default: ```100```
drift (int): Difference amount. Default: ```1```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(Series): 1 column
"""
# Validate
length = v_pos_default(length, 30)
signal = v_pos_default(signal, 9)
if length < signal:
length, signal = signal, length
_length = 3 * length - 1
close = v_series(close, _length)
if close is None:
return
scalar = v_scalar(scalar, 100)
drift = v_drift(drift)
offset = v_offset(offset)
# Calculate
ema1 = ema(close=close, length=length, **kwargs)
if all(isnan(ema1)):
return # Emergency Break
ema2 = ema(close=ema1, length=length, **kwargs)
if all(isnan(ema2)):
return # Emergency Break
ema3 = ema(close=ema2, length=length, **kwargs)
if all(isnan(ema3)):
return # Emergency Break
trix = scalar * ema3.pct_change(drift)
trix_signal = trix.rolling(signal).mean()
# Offset
if offset != 0:
trix = trix.shift(offset)
trix_signal = trix_signal.shift(offset)
# Fill
if "fillna" in kwargs:
trix.fillna(kwargs["fillna"], inplace=True)
trix_signal.fillna(kwargs["fillna"], inplace=True)
# Name and Category
trix.name = f"TRIX_{length}_{signal}"
trix_signal.name = f"TRIXs_{length}_{signal}"
trix.category = trix_signal.category = "momentum"
data = {trix.name: trix, trix_signal.name: trix_signal}
df = DataFrame(data, index=close.index)
df.name = f"TRIX_{length}_{signal}"
df.category = "momentum"
return df
@@ -1,106 +0,0 @@
# -*- coding: utf-8 -*-
from numpy import isnan
from pandas import DataFrame, Series
from pandas_ta._typing import DictLike, Int, IntFloat
from pandas_ta.ma import ma
from pandas_ta.overlap import ema
from pandas_ta.utils import (
v_drift,
v_mamode,
v_offset,
v_pos_default,
v_scalar,
v_series
)
def tsi(
close: Series, fast: Int = None, slow: Int = None,
signal: Int = None, scalar: IntFloat = None,
mamode: str = None, drift: Int = None,
offset: Int = None, **kwargs: DictLike
) -> DataFrame:
"""True Strength Index
This indicator attempts to identify short-term swings in trend direction
as well as identifying possible "overbought" and "oversold" signals.
Sources:
* [investopedia](https://www.investopedia.com/terms/t/tsi.asp)
Parameters:
close (Series): ```close``` Series
fast (int): Fast MA period. Default: ```13```
slow (int): Slow MA period. Default: ```25```
signal (int): Signal period. Default: ```13```
scalar (float): Scalar. Default: ```100```
mamode (str): Signal MA. See ```help(ta.ma)```. Default: ```"ema"```
drift (int): Difference amount. Default: ```1```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(DataFrame): 2 columns
"""
# Validate
fast = v_pos_default(fast, 13)
slow = v_pos_default(slow, 25)
signal = v_pos_default(signal, 13)
if slow < fast:
fast, slow = slow, fast
_length = slow + signal + 1
close = v_series(close, _length)
if "length" in kwargs:
kwargs.pop("length")
if close is None:
return
scalar = v_scalar(scalar, 100)
mamode = v_mamode(mamode, "ema")
drift = v_drift(drift)
offset = v_offset(offset)
# Calculate
diff = close.diff(drift)
slow_ema = ema(close=diff, length=slow, **kwargs)
if all(isnan(slow_ema)):
return # Emergency Break
fast_slow_ema = ema(close=slow_ema, length=fast, **kwargs)
abs_diff = diff.abs()
abs_slow_ema = ema(close=abs_diff, length=slow, **kwargs)
if all(isnan(abs_slow_ema)):
return # Emergency Break
abs_fast_slow_ema = ema(close=abs_slow_ema, length=fast, **kwargs)
tsi = scalar * fast_slow_ema / abs_fast_slow_ema
if all(isnan(tsi)):
return # Emergency Break
tsi_signal = ma(mamode, tsi, length=signal)
# Offset
if offset != 0:
tsi = tsi.shift(offset)
tsi_signal = tsi_signal.shift(offset)
# Fill
if "fillna" in kwargs:
tsi.fillna(kwargs["fillna"], inplace=True)
tsi_signal.fillna(kwargs["fillna"], inplace=True)
# Name and Category
tsi.name = f"TSI_{fast}_{slow}_{signal}"
tsi_signal.name = f"TSIs_{fast}_{slow}_{signal}"
tsi.category = tsi_signal.category = "momentum"
data = {tsi.name: tsi, tsi_signal.name: tsi_signal}
df = DataFrame(data, index=close.index)
df.name = f"TSI_{fast}_{slow}_{signal}"
df.category = "momentum"
return df
@@ -1,105 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import DataFrame, Series
from pandas_ta._typing import DictLike, Int, IntFloat
from pandas_ta.maps import Imports
from pandas_ta.utils import (
v_drift,
v_offset,
v_pos_default,
v_series,
v_talib
)
def uo(
high: Series, low: Series, close: Series,
fast: Int = None, medium: Int = None, slow: Int = None,
fast_w: IntFloat = None, medium_w: IntFloat = None, slow_w: IntFloat = None,
talib: bool = None, drift: Int = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Ultimate Oscillator
This indicator, by Larry Williams, attempts to identify momentum.
Sources:
* [tradingview](https://www.tradingview.com/wiki/Ultimate_Oscillator_(UO))
Parameters:
high (Series): ```high``` Series
low (Series): ```low``` Series
close (Series): ```close``` Series
fast (int): The Fast %K period. Default: ```7```
medium (int): The Slow %K period. Default: ```14```
slow (int): The Slow %D period. Default: ```28```
fast_w (float): The Fast %K period. Default: ```4.0```
medium_w (float): The Slow %K period. Default: ```2.0```
slow_w (float): The Slow %D period. Default: ```1.0```
talib (bool): If installed, use TA Lib. Default: ```True```
drift (int): Difference amount. Default: ```1```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(Series): 1 column
"""
# Validate
fast = v_pos_default(fast, 7)
medium = v_pos_default(medium, 14)
slow = v_pos_default(slow, 28)
_length = max(fast, medium, slow) + 1
high = v_series(high, _length)
low = v_series(low, _length)
close = v_series(close, _length)
if high is None or low is None or close is None:
return
fast_w = v_pos_default(fast_w, 4.0)
medium_w = v_pos_default(medium_w, 2.0)
slow_w = v_pos_default(slow_w, 1.0)
mode_tal = v_talib(talib)
drift = v_drift(drift)
offset = v_offset(offset)
# Calculate
if Imports["talib"] and mode_tal:
from talib import ULTOSC
uo = ULTOSC(high, low, close, fast, medium, slow)
else:
close_drift = close.shift(drift)
tdf = DataFrame({
"high": high, "low": low, f"close_{drift}": close_drift
})
max_h_or_pc = tdf.loc[:, ["high", f"close_{drift}"]].max(axis=1)
min_l_or_pc = tdf.loc[:, ["low", f"close_{drift}"]].min(axis=1)
del tdf
bp = close - min_l_or_pc
tr = max_h_or_pc - min_l_or_pc
fast_avg = bp.rolling(fast).sum() / tr.rolling(fast).sum()
medium_avg = bp.rolling(medium).sum() / tr.rolling(medium).sum()
slow_avg = bp.rolling(slow).sum() / tr.rolling(slow).sum()
total_weight = fast_w + medium_w + slow_w
weights = (fast_w * fast_avg) + (medium_w * medium_avg) \
+ (slow_w * slow_avg)
uo = 100 * weights / total_weight
# Offset
if offset != 0:
uo = uo.shift(offset)
# Fill
if "fillna" in kwargs:
uo.fillna(kwargs["fillna"], inplace=True)
# Name and Category
uo.name = f"UO_{fast}_{medium}_{slow}"
uo.category = "momentum"
return uo
@@ -1,75 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.maps import Imports
from pandas_ta.utils import v_offset, v_pos_default, v_series, v_talib
def willr(
high: Series, low: Series, close: Series,
length: Int = None, talib: bool = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""William's Percent R
This indicator attempts to identify "overbought" and "oversold"
conditions similar to the RSI.
Sources:
* [tradingview](https://www.tradingview.com/wiki/Williams_%25R_(%25R))
Parameters:
high (Series): ```high``` Series
low (Series): ```low``` Series
close (Series): ```close``` Series
length (int): The period. Default: ```14```
talib (bool): If installed, use TA Lib. Default: ```True```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(Series): 1 column
"""
# Validate
length = v_pos_default(length, 14)
if "min_periods" in kwargs and kwargs["min_periods"] is not None:
min_periods = int(kwargs["min_periods"])
else:
min_periods = length
_length = max(length, min_periods)
high = v_series(high, _length)
low = v_series(low, _length)
close = v_series(close, _length)
if high is None or low is None or close is None:
return
mode_tal = v_talib(talib)
offset = v_offset(offset)
# Calculate
if Imports["talib"] and mode_tal:
from talib import WILLR
willr = WILLR(high, low, close, length)
else:
lowest_low = low.rolling(length, min_periods=min_periods).min()
highest_high = high.rolling(length, min_periods=min_periods).max()
willr = 100 * ((close - lowest_low) / (highest_high - lowest_low) - 1)
# Offset
if offset != 0:
willr = willr.shift(offset)
# Fill
if "fillna" in kwargs:
willr.fillna(kwargs["fillna"], inplace=True)
# Name and Category
willr.name = f"WILLR_{length}"
willr.category = "momentum"
return willr
@@ -1,77 +0,0 @@
# -*- coding: utf-8 -*-
from .alligator import alligator
from .alma import alma
from .dema import dema
from .ema import ema
from .fwma import fwma
from .hilo import hilo
from .hl2 import hl2
from .hlc3 import hlc3
from .hma import hma
from .hwma import hwma
from .ichimoku import ichimoku
from .jma import jma
from .kama import kama
from .linreg import linreg
from .mama import mama
from .mcgd import mcgd
from .midpoint import midpoint
from .midprice import midprice
from .ohlc4 import ohlc4
from .pivots import pivots
from .pwma import pwma
from .rma import rma
from .sinwma import sinwma
from .sma import sma
from .smma import smma
from .ssf import ssf
from .ssf3 import ssf3
from .supertrend import supertrend
from .swma import swma
from .t3 import t3
from .tema import tema
from .trima import trima
from .vidya import vidya
from .wcp import wcp
from .wma import wma
from .zlma import zlma
__all__ = [
"alligator",
"alma",
"dema",
"ema",
"fwma",
"hilo",
"hl2",
"hlc3",
"hma",
"hwma",
"ichimoku",
"jma",
"kama",
"linreg",
"mama",
"mcgd",
"midpoint",
"midprice",
"ohlc4",
"pivots",
"pwma",
"rma",
"sinwma",
"sma",
"smma",
"ssf",
"ssf3",
"supertrend",
"swma",
"t3",
"tema",
"trima",
"vidya",
"wcp",
"wma",
"zlma",
]
@@ -1,86 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import DataFrame, Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.utils import v_offset, v_pos_default, v_series, v_talib
from .smma import smma
def alligator(
close: Series, jaw: Int = None, teeth: Int = None, lips: Int = None,
talib: bool = None, offset: Int = None, **kwargs: DictLike
) -> DataFrame:
"""Bill Williams Alligator
This indicator, by Bill Williams, attempts to identify trends.
Sources:
* [sierrachart](https://www.sierrachart.com/index.php?page=doc/StudiesReference.php&ID=175&Name=Bill_Williams_Alligator)
* [tradingview](https://www.tradingview.com/scripts/alligator/)
Parameters:
close (Series): ```close``` Series
jaw (int): Jaw period. Default: ```13```
teeth (int): Teeth period. Default: ```8```
lips (int): Lips period. Default: ```5```
talib (bool): If installed, use TA Lib. Default: ```True```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(DataFrame): 3 columns
Tip:
To avoid data leaks, offsets are to be done manually.
Note:
Williams believed the fx market trends between 15% and 30% of the
time. Otherwise it is range bound. Inspired by fractal geometry,
where the outputs are meant to resemble an alligator opening and
closing its mouth. It It consists of 3 lines: Jaw, Teeth, and
Lips which each have differing lengths.
"""
# Validate
jaw = v_pos_default(jaw, 13)
teeth = v_pos_default(teeth, 8)
lips = v_pos_default(lips, 5)
close = v_series(close, max(jaw, teeth, lips))
if close is None:
return
mode_tal = v_talib(talib)
offset = v_offset(offset)
# Calculate
gator_jaw = smma(close, length=jaw, talib=mode_tal)
gator_teeth = smma(close, length=teeth, talib=mode_tal)
gator_lips = smma(close, length=lips, talib=mode_tal)
# Offset
if offset != 0:
gator_jaw = gator_jaw.shift(offset)
gator_teeth = gator_teeth.shift(offset)
gator_lips = gator_lips.shift(offset)
# Fill
if "fillna" in kwargs:
gator_jaw.fillna(kwargs["fillna"], inplace=True)
gator_teeth.fillna(kwargs["fillna"], inplace=True)
gator_lips.fillna(kwargs["fillna"], inplace=True)
# Name and Category
_props = f"_{jaw}_{teeth}_{lips}"
data = {
f"AGj{_props}": gator_jaw,
f"AGt{_props}": gator_teeth,
f"AGl{_props}": gator_lips
}
df = DataFrame(data, index=close.index)
df.name = f"AG{_props}"
df.category = "overlap"
return df
@@ -1,81 +0,0 @@
# -*- coding: utf-8 -*-
from numpy import append, arange, array, exp, floor, nan, tensordot
from numpy.version import version as np_version
from pandas_ta._typing import DictLike, Int, IntFloat
from pandas import Series
from pandas_ta.utils import strided_window, v_offset, v_pos_default, v_series
def alma(
close: Series, length: Int = None,
sigma: IntFloat = None, dist_offset: IntFloat = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Arnaud Legoux Moving Average
This indicator attempts to reduce lag with Gaussian smoothing.
Sources:
* [prorealcode](https://www.prorealcode.com/prorealtime-indicators/alma-arnaud-legoux-moving-average/)
* [sierrachart](https://www.sierrachart.com/index.php?page=doc/StudiesReference.php&ID=475&Name=Moving_Average_-_Arnaud_Legoux)
Parameters:
close (Series): ```close``` Series
length (int): The period. Default: ```9```
sigma (float): Smoothing value. Default ```6.0```
dist_offset (float): Distribution offset, range ```[0, 1]```.
Default ```0.85```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(Series): 1 column
"""
# Validate
length = v_pos_default(length, 9)
close = v_series(close, length)
if close is None:
return
sigma = v_pos_default(sigma, 6.0)
if isinstance(dist_offset, float) and 0 <= dist_offset <= 1:
offset_ = float(dist_offset)
else:
offset_ = 0.85
offset = v_offset(offset)
# Calculate
np_close = close.to_numpy()
x = arange(length)
k = floor(offset_ * (length - 1))
weights = exp(-0.5 * ((sigma / length) * (x - k)) ** 2)
weights /= weights.sum()
if np_version >= "1.20.0":
from numpy.lib.stride_tricks import sliding_window_view
window = sliding_window_view(np_close, length)
else:
window = strided_window(np_close, length)
result = append(array([nan] * (length - 1)),
tensordot(window, weights, axes=1))
alma = Series(result, index=close.index)
# Offset
if offset != 0:
alma = alma.shift(offset)
# Fill
if "fillna" in kwargs:
alma.fillna(kwargs["fillna"], inplace=True)
# Name and Category
alma.name = f"ALMA_{length}_{sigma}_{offset_}"
alma.category = "overlap"
return alma
@@ -1,75 +0,0 @@
# -*- coding: utf-8 -*-
from numpy import isnan
from pandas import Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.maps import Imports
from pandas_ta.utils import v_offset, v_pos_default, v_series, v_talib
from .ema import ema
def dema(
close: Series, length: Int = None, talib: bool = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Double Exponential Moving Average
This indicator attempts to create a smoother average with less lag than
the EMA.
Sources:
* [tradingtechnologies](https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/double-exponential-moving-average-dema/)
Parameters:
close (Series): ```close``` Series
length (int): The period. Default: ```10```
talib (bool): If installed, use TA Lib. Default: ```True```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(Series): 1 column
Warning:
TA-Lib Correlation: ```np.float64(0.9999894518202522)```
Tip:
Corrective contributions welcome!
"""
# Validate
length = v_pos_default(length, 10)
close = v_series(close, length)
if close is None:
return
mode_tal = v_talib(talib)
offset = v_offset(offset)
# Calculate
if Imports["talib"] and mode_tal:
from talib import DEMA
dema = DEMA(close, length)
else:
ema1 = ema(close=close, length=length, talib=mode_tal)
ema2 = ema(close=ema1, length=length, talib=mode_tal)
dema = 2 * ema1 - ema2
if all(isnan(dema.to_numpy())):
return # Emergency Break
# Offset
if offset != 0:
dema = dema.shift(offset)
# Fill
if "fillna" in kwargs:
dema.fillna(kwargs["fillna"], inplace=True)
# Name and Category
dema.name = f"DEMA_{length}"
dema.category = "overlap"
return dema
@@ -1,80 +0,0 @@
# -*- coding: utf-8 -*-
from numpy import nan
from pandas import Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.maps import Imports
from pandas_ta.utils import (
v_bool,
v_offset,
v_pos_default,
v_series,
v_talib
)
def ema(
close: Series, length: Int = None,
talib: bool = None, presma: bool = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Exponential Moving Average
This Moving Average is more responsive than the Simple Moving
Average (SMA).
Sources:
* [investopedia](https://www.investopedia.com/ask/answers/122314/what-exponential-moving-average-ema-formula-and-how-ema-calculated.asp)
* [stockcharts](https://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:moving_averages)
Parameters:
close (Series): ```close``` Series
length (int): The period. Default: ```10```
talib (bool): If installed, use TA Lib. Default: ```True```
presma (bool): Initialize with SMA like TA Lib. Default: ```True```
offset (int): Post shift. Default: ```0```
Other Parameters:
adjust (bool): Default: ```False```
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(Series): 1 column
"""
# Validate
length = v_pos_default(length, 10)
close = v_series(close, length)
if close is None:
return
mode_tal = v_talib(talib)
presma = v_bool(presma, True)
offset = v_offset(offset)
adjust = kwargs.setdefault("adjust", False)
# Calculate
if Imports["talib"] and mode_tal and length > 1:
from talib import EMA
ema = EMA(close, length)
else:
if presma: # TA Lib implementation
close = close.copy()
sma_nth = close.iloc[0:length].mean()
close.iloc[:length - 1] = nan
close.iloc[length - 1] = sma_nth
ema = close.ewm(span=length, adjust=adjust).mean()
# Offset
if offset != 0:
ema = ema.shift(offset)
# Fill
if "fillna" in kwargs:
ema.fillna(kwargs["fillna"], inplace=True)
# Name and Category
ema.name = f"EMA_{length}"
ema.category = "overlap"
return ema
@@ -1,66 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.utils import (
fibonacci,
v_ascending,
v_offset,
v_pos_default,
v_series,
weights
)
def fwma(
close: Series, length: Int = None, asc: bool = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Fibonacci's Weighted Moving Average
This indicator, by Kevin Johnson, is similar to a Weighted Moving Average
(WMA) where the weights are based on the Fibonacci Sequence.
Sources:
* Kevin Johnson
Parameters:
close (Series): ```close``` Series
length (int): The period. Default: ```10```
asc (bool): Recent values weigh more. Default: ```True```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(Series): 1 column
"""
# Validate
length = v_pos_default(length, 10)
close = v_series(close, length)
if close is None:
return
asc = v_ascending(asc)
offset = v_offset(offset)
# Calculate
fibs = fibonacci(n=length, weighted=True)
fwma = close.rolling(length, min_periods=length) \
.apply(weights(fibs), raw=True)
# Offset
if offset != 0:
fwma = fwma.shift(offset)
# Fill
if "fillna" in kwargs:
fwma.fillna(kwargs["fillna"], inplace=True)
# Name and Category
fwma.name = f"FWMA_{length}"
fwma.category = "overlap"
return fwma
@@ -1,100 +0,0 @@
# -*- coding: utf-8 -*-
from numpy import nan
from pandas import DataFrame, Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.ma import ma
from pandas_ta.utils import v_mamode, v_offset, v_pos_default, v_series
def hilo(
high: Series, low: Series, close: Series,
high_length: Int = None, low_length: Int = None, mamode: str = None,
offset: Int = None, **kwargs: DictLike
) -> DataFrame:
"""Gann HiLo Activator
This indicator, by Robert Krausz, uses two different Moving Averages to
identify trends.
Sources:
* Gann HiLo Activator, , Stocks & Commodities Magazine, 1998
* [sierrachart](https://www.sierrachart.com/index.php?page=doc/StudiesReference.php&ID=447&Name=Gann_HiLo_Activator)
* [tradingview](https://www.tradingview.com/script/XNQSLIYb-Gann-High-Low/)
Parameters:
high (Series): ```high``` Series
low (Series): ```low``` Series
close (Series): ```close``` Series
high_length (int): High period. Default: ```13```
low_length (int): Low period. Default: ```21```
mamode (str): See ```help(ta.ma)```. Default: ```"sma"```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(DataFrame): 3 columns
Note:
Increasing ```high_length``` and decreasing ```low_length``` is
better for short trades and vice versa for long trades.
"""
# Validate
high_length = v_pos_default(high_length, 13)
low_length = v_pos_default(low_length, 21)
_length = max(high_length, low_length) + 1
high = v_series(high, _length)
low = v_series(low, _length)
close = v_series(close, _length)
if high is None or low is None or close is None:
return
mamode = v_mamode(mamode, "sma")
offset = v_offset(offset)
# Calculate
m = close.size
hilo = Series(nan, index=close.index)
long = Series(nan, index=close.index)
short = Series(nan, index=close.index)
high_ma = ma(mamode, high, length=high_length)
low_ma = ma(mamode, low, length=low_length)
for i in range(1, m):
if close.iat[i] > high_ma.iat[i - 1]:
hilo.iat[i] = long.iat[i] = low_ma.iat[i]
elif close.iat[i] < low_ma.iat[i - 1]:
hilo.iat[i] = short.iat[i] = high_ma.iat[i]
else:
hilo.iat[i] = hilo.iat[i - 1]
long.iat[i] = short.iat[i] = hilo.iat[i - 1]
# Offset
if offset != 0:
hilo = hilo.shift(offset)
long = long.shift(offset)
short = short.shift(offset)
# Fill
if "fillna" in kwargs:
hilo.fillna(kwargs["fillna"], inplace=True)
long.fillna(kwargs["fillna"], inplace=True)
short.fillna(kwargs["fillna"], inplace=True)
# Name and Category
_props = f"_{high_length}_{low_length}"
data = {
f"HILO{_props}": hilo,
f"HILOl{_props}": long,
f"HILOs{_props}": short
}
df = DataFrame(data, index=close.index)
df.name = f"HILO{_props}"
df.category = "overlap"
return df
@@ -1,52 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.utils import v_offset, v_series
def hl2(
high: Series, low: Series,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""HL2
HL2 is the midpoint/average of high and low.
Parameters:
high (Series): ```high``` Series
low (Series): ```low``` Series
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```.
Only works when offset.
Returns:
(Series): 1 column
"""
# Validate
high = v_series(high)
low = v_series(low)
offset = v_offset(offset)
if high is None or low is None:
return
# Calculate
avg = 0.5 * (high.to_numpy() + low.to_numpy())
hl2 = Series(avg, index=high.index)
# Offset
if offset != 0:
hl2 = hl2.shift(offset)
# Fill
if "fillna" in kwargs:
hl2.fillna(kwargs["fillna"], inplace=True)
# Name and Category
hl2.name = "HL2"
hl2.category = "overlap"
return hl2
@@ -1,60 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.maps import Imports
from pandas_ta.utils import v_offset, v_series, v_talib
def hlc3(
high: Series, low: Series, close: Series, talib: bool = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""HLC3
HLC3 is the average of high, low and close.
Parameters:
high (Series): ```high``` Series
low (Series): ```low``` Series
close (Series): ```close``` Series
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```.
Only works when offset.
Returns:
(Series): 1 column
"""
# Validate
high = v_series(high)
low = v_series(low)
close = v_series(close)
mode_tal = v_talib(talib)
offset = v_offset(offset)
if high is None or low is None or close is None:
return
# Calculate
if Imports["talib"] and mode_tal and close.size:
from talib import TYPPRICE
hlc3 = TYPPRICE(high, low, close)
else:
avg = (high.to_numpy() + low.to_numpy() + close.to_numpy()) / 3.0
hlc3 = Series(avg, index=close.index)
# Offset
if offset != 0:
hlc3 = hlc3.shift(offset)
# Fill
if "fillna" in kwargs:
hlc3.fillna(kwargs["fillna"], inplace=True)
# Name and Category
hlc3.name = "HLC3"
hlc3.category = "overlap"
return hlc3
@@ -1,72 +0,0 @@
# -*- coding: utf-8 -*-
from sys import modules as module_
from numpy import sqrt
from pandas import Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.utils import v_mamode, v_offset, v_pos_default, v_series
from .ema import ema
from .sma import sma
from .wma import wma
def hma(
close: Series, length: Int = None, mamode: str = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Hull Moving Average
This indicator, by Alan Hull, attempts to reduce lag compared to
classical moving averages.
Sources:
* [Alan Hull](https://alanhull.com/hull-moving-average)
Parameters:
close (Series): ```close``` Series
length (int): The period. Default: ```10```
mamode (str): One of: 'ema', 'sma', or 'wma'. Default: ```"wma"```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(Series): 1 column
"""
# Validate
length = v_pos_default(length, 10)
close = v_series(close, length + 2)
if close is None:
return
mamode = v_mamode(mamode, "wma")
offset = v_offset(offset)
if mamode not in ["ema", "sma", "wma"]:
return
_ma = getattr(module_[__name__], mamode)
# Calculate
half_length = int(length / 2)
sqrt_length = int(sqrt(length))
maf = _ma(close, length=half_length)
mas = _ma(close, length=length)
hma = _ma(close=2 * maf - mas, length=sqrt_length)
# Offset
if offset != 0:
hma = hma.shift(offset)
# Fill
if "fillna" in kwargs:
hma.fillna(kwargs["fillna"], inplace=True)
# Name and Category
hma.name = f"HMA{"" if mamode == "wma" else mamode[0]}_{length}"
hma.category = "overlap"
return hma

Some files were not shown because too many files have changed in this diff Show More