Compare commits
205 Commits
dna_rules
...
inverse-fu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7953fbc90b | ||
|
|
e3bbd8a4fc | ||
|
|
53992318ff | ||
|
|
25fb188331 | ||
|
|
b2ec3d42ce | ||
|
|
816bdf0aea | ||
|
|
71adce0cca | ||
|
|
34001e860d | ||
|
|
ff4f2f8ac2 | ||
|
|
5b7996adda | ||
|
|
21925e5f23 | ||
|
|
60aaeedda0 | ||
|
|
3d10b5d5a2 | ||
|
|
60238aea43 | ||
|
|
e97b88df45 | ||
|
|
043666b96f | ||
|
|
cff6b63ae5 | ||
|
|
5fd0bf5383 | ||
|
|
8b359f06df | ||
|
|
b0ca75394d | ||
|
|
b7c07d2913 | ||
|
|
9c252a50d4 | ||
|
|
f93aebbc81 | ||
|
|
797a914e4e | ||
|
|
d995163498 | ||
|
|
5c2f0b6c38 | ||
|
|
328c16ca69 | ||
|
|
ccd231b51c | ||
|
|
a4daf13513 | ||
|
|
243d4bb9dc | ||
|
|
b4cc5be234 | ||
|
|
5f025cc72a | ||
|
|
8a052cb55b | ||
|
|
d7216eac15 | ||
|
|
d76027fba6 | ||
|
|
dffdf15cfa | ||
|
|
61e679acfd | ||
|
|
58df1a1499 | ||
|
|
51dcea96eb | ||
|
|
0bc6f94b58 | ||
|
|
4a01f536df | ||
|
|
14e0a73ce4 | ||
|
|
4820ed2ddd | ||
|
|
8af63a0f3a | ||
|
|
95023593bc | ||
|
|
b30a5e2de0 | ||
|
|
a2b39c303d | ||
|
|
1df4dbc223 | ||
|
|
6454cb2255 | ||
|
|
76d1ee029b | ||
|
|
a3bf9631b6 | ||
|
|
4b0acbdcba | ||
|
|
b5547b4768 | ||
|
|
2795ebaeb5 | ||
|
|
6f2e17d05a | ||
|
|
157b161b83 | ||
|
|
3e5b8ae508 | ||
|
|
a5692e2ab5 | ||
|
|
b4624fcf96 | ||
|
|
51d35e43c3 | ||
|
|
31b4f1dc3c | ||
|
|
8e79331ffb | ||
|
|
85ef8cf9d5 | ||
|
|
0b19b16f9e | ||
|
|
cc4c3207eb | ||
|
|
7b0951f083 | ||
|
|
77b71d8164 | ||
|
|
94b74b834d | ||
|
|
e004ff7f4a | ||
|
|
0858bbac3b | ||
|
|
f4069a14c0 | ||
|
|
4b9edda1ec | ||
|
|
bc303a04e9 | ||
|
|
831130c605 | ||
|
|
9eee3e70d7 | ||
|
|
4f3bcabfe6 | ||
|
|
9c080b4bab | ||
|
|
0392cac894 | ||
|
|
a42fc9d9ab | ||
|
|
21b76aa8c8 | ||
|
|
580b91db21 | ||
|
|
4a5162aaaf | ||
|
|
508bd0acde | ||
|
|
5d93e8b0d6 | ||
|
|
46f7ef1973 | ||
|
|
18d609b08d | ||
|
|
1cfcd8bbb0 | ||
|
|
8a17c2fb15 | ||
|
|
51e48396b0 | ||
|
|
8dcc0f17a1 | ||
|
|
bc86d82bb8 | ||
|
|
f1b46a603d | ||
|
|
42bf7baf8d | ||
|
|
04fdfb390c | ||
|
|
8efc66c13f | ||
|
|
689da58d4b | ||
|
|
c926772354 | ||
|
|
7971bafbd3 | ||
|
|
963f6df397 | ||
|
|
7a0b60fc44 | ||
|
|
650652ea66 | ||
|
|
e00b5c0b1a | ||
|
|
97e750c9d5 | ||
|
|
bebdab0348 | ||
|
|
ea73c4afe9 | ||
|
|
1712c6162e | ||
|
|
8b4f426b36 | ||
|
|
1a07afa365 | ||
|
|
5d6f0d170d | ||
|
|
d270217719 | ||
|
|
2e078016b2 | ||
|
|
eb53f182ba | ||
|
|
1f4424c6d9 | ||
|
|
109680eaf2 | ||
|
|
0f96bcc507 | ||
|
|
336875b147 | ||
|
|
3ad0a2bee5 | ||
|
|
07db5c7bd5 | ||
|
|
ceb0331907 | ||
|
|
e39bccbe7d | ||
|
|
6ae50534a3 | ||
|
|
95849bd482 | ||
|
|
13a7fea49d | ||
|
|
8bde520ffc | ||
|
|
d8dc65282f | ||
|
|
4681221bac | ||
|
|
22732f1db8 | ||
|
|
df82b85a9b | ||
|
|
52640815ed | ||
|
|
00b3fccb9e | ||
|
|
5d74df9eed | ||
|
|
f9917d6a86 | ||
|
|
60f893d39d | ||
|
|
284d54e823 | ||
|
|
a654af4899 | ||
|
|
97b5d5275d | ||
|
|
2a0b67d3cf | ||
|
|
e5cc608b55 | ||
|
|
e15fee22ee | ||
|
|
3219e11c76 | ||
|
|
bd4d50daca | ||
|
|
bc2efcbab3 | ||
|
|
27ce01ce2c | ||
|
|
76a1b9bf8b | ||
|
|
76cc671d13 | ||
|
|
4826d3f96a | ||
|
|
dd68a02bc7 | ||
|
|
b006ef5f4e | ||
|
|
3fbffae598 | ||
|
|
d89e46d136 | ||
|
|
981ccf1c71 | ||
|
|
746a220ef6 | ||
|
|
d93c4e2916 | ||
|
|
edc2817b3d | ||
|
|
a12033c7a0 | ||
|
|
fd955a432c | ||
|
|
077201ddcc | ||
|
|
5034b6b9fa | ||
|
|
340ca60ebe | ||
|
|
c5e8d5d6e6 | ||
|
|
08d0b99a4d | ||
|
|
df11855fe4 | ||
|
|
7afe5eb3c4 | ||
|
|
cf4b681bb3 | ||
|
|
83008ab01e | ||
|
|
dcce864e6d | ||
|
|
422d813dd8 | ||
|
|
fd2812b1b5 | ||
|
|
d6b1eb174b | ||
|
|
1af5e7c1b1 | ||
|
|
b801cc09e1 | ||
|
|
9f7b89f770 | ||
|
|
d60e00a396 | ||
|
|
6304fdab05 | ||
|
|
dc2bc2d917 | ||
|
|
83f537e099 | ||
|
|
4019565091 | ||
|
|
c095b2bb68 | ||
|
|
9a38ffa7e5 | ||
|
|
6a229cc3cb | ||
|
|
1fd01b7390 | ||
|
|
f6b34fd3f5 | ||
|
|
7466f80730 | ||
|
|
edeaf96e0b | ||
|
|
49e23a036c | ||
|
|
81f51107b9 | ||
|
|
cdb1912e24 | ||
|
|
699a68045b | ||
|
|
18fee9f5d0 | ||
|
|
b8473ae6cb | ||
|
|
2e91359a81 | ||
|
|
e6e375961a | ||
|
|
96406afb8d | ||
|
|
63015ef81e | ||
|
|
89e9e0e097 | ||
|
|
8777ed36f6 | ||
|
|
49a1ac4671 | ||
|
|
220c545b41 | ||
|
|
1241c04e8e | ||
|
|
7798ba1db2 | ||
|
|
5b65ea3506 | ||
|
|
17083487e7 | ||
|
|
d6c2a96270 | ||
|
|
ef31851558 | ||
|
|
e919a3a68b |
25
.dockerignore
Normal file
25
.dockerignore
Normal file
@@ -0,0 +1,25 @@
|
||||
__pycache__
|
||||
*.pyc
|
||||
*.pyo
|
||||
*.pyd
|
||||
.Python
|
||||
env
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
.tox
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*,cover
|
||||
*.log
|
||||
.git
|
||||
*.md
|
||||
!README*.md
|
||||
README-secret.md
|
||||
.travis.yml
|
||||
Dockerfile
|
||||
docker-compose.yml
|
||||
.idea
|
||||
venv
|
||||
155
.gitignore
vendored
155
.gitignore
vendored
@@ -1,20 +1,149 @@
|
||||
/_pycache_
|
||||
/.pytest_cache
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-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/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# IDE
|
||||
/.vscode
|
||||
/jesse.egg-info
|
||||
/.idea
|
||||
|
||||
.DS_Store
|
||||
/storage/*.key
|
||||
/storage/*.sqlite
|
||||
/storage/*.gz
|
||||
.DS_Store
|
||||
/.vagrant
|
||||
*.pyc
|
||||
/__pycache__
|
||||
/venv
|
||||
__pycache__
|
||||
.vscode
|
||||
/dist
|
||||
/build
|
||||
/*.egg-info
|
||||
/*.egg
|
||||
testing-*.py
|
||||
|
||||
28
Dockerfile
Normal file
28
Dockerfile
Normal file
@@ -0,0 +1,28 @@
|
||||
FROM python:3.9.0
|
||||
ENV PYTHONUNBUFFERED 1
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get -y install build-essential libssl-dev \
|
||||
&& apt-get clean \
|
||||
&& pip install --upgrade pip
|
||||
|
||||
RUN pip3 install Cython numpy
|
||||
|
||||
# Prepare environment
|
||||
RUN mkdir /jesse-docker
|
||||
WORKDIR /jesse-docker
|
||||
|
||||
# Install TA-lib
|
||||
COPY docker_build_helpers/* /tmp/
|
||||
RUN cd /tmp && /tmp/install_ta-lib.sh && rm -r /tmp/*ta-lib*
|
||||
ENV LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
|
||||
|
||||
# Install dependencies
|
||||
COPY requirements.txt /jesse-docker
|
||||
RUN pip3 install -r requirements.txt
|
||||
|
||||
# Build
|
||||
COPY . /jesse-docker
|
||||
RUN pip3 install -e .
|
||||
|
||||
WORKDIR /home
|
||||
28
DockerfileTest
Normal file
28
DockerfileTest
Normal file
@@ -0,0 +1,28 @@
|
||||
FROM python:3.9.0
|
||||
ENV PYTHONUNBUFFERED 1
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get -y install build-essential libssl-dev \
|
||||
&& apt-get clean \
|
||||
&& pip install --upgrade pip
|
||||
|
||||
RUN pip3 install Cython numpy codecov pytest-cov
|
||||
|
||||
# Prepare environment
|
||||
RUN mkdir /jesse-docker
|
||||
WORKDIR /jesse-docker
|
||||
|
||||
# Install TA-lib
|
||||
COPY docker_build_helpers/* /tmp/
|
||||
RUN cd /tmp && /tmp/install_ta-lib.sh && rm -r /tmp/*ta-lib*
|
||||
ENV LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
|
||||
|
||||
# Install dependencies
|
||||
COPY requirements.txt /jesse-docker
|
||||
RUN pip3 install -r requirements.txt
|
||||
|
||||
# Build
|
||||
COPY . /jesse-docker
|
||||
RUN pip3 install -e .
|
||||
|
||||
ENTRYPOINT pytest --cov=./ # && codecov
|
||||
17
docker_build_helpers/install_ta-lib.sh
Executable file
17
docker_build_helpers/install_ta-lib.sh
Executable file
@@ -0,0 +1,17 @@
|
||||
if [ -z "$1" ]; then
|
||||
INSTALL_LOC=/usr/local
|
||||
else
|
||||
INSTALL_LOC=${1}
|
||||
fi
|
||||
echo "Installing to ${INSTALL_LOC}"
|
||||
if [ ! -f "${INSTALL_LOC}/lib/libta_lib.a" ]; then
|
||||
tar zxvf ta-lib-0.4.0-src.tar.gz
|
||||
cd ta-lib \
|
||||
&& sed -i.bak "s|0.00000001|0.000000000000000001 |g" src/ta_func/ta_utility.h \
|
||||
&& ./configure --prefix=${INSTALL_LOC}/ \
|
||||
&& make \
|
||||
&& which sudo && sudo make install || make install \
|
||||
&& echo "export LD_LIBRARY_PATH=/usr/local/lib" >> /root/.bashrc
|
||||
else
|
||||
echo "TA-lib already installed, skipping installation"
|
||||
fi
|
||||
BIN
docker_build_helpers/ta-lib-0.4.0-src.tar.gz
Normal file
BIN
docker_build_helpers/ta-lib-0.4.0-src.tar.gz
Normal file
Binary file not shown.
@@ -1,5 +1,7 @@
|
||||
import os
|
||||
import sys
|
||||
# Hide the "FutureWarning: pandas.util.testing is deprecated." caused by empyrical
|
||||
import warnings
|
||||
from pydoc import locate
|
||||
|
||||
import click
|
||||
@@ -7,9 +9,6 @@ import pkg_resources
|
||||
|
||||
import jesse.helpers as jh
|
||||
|
||||
# Hide the "FutureWarning: pandas.util.testing is deprecated." caused by empyrical
|
||||
import warnings
|
||||
|
||||
warnings.simplefilter(action='ignore', category=FutureWarning)
|
||||
|
||||
# Python version validation.
|
||||
@@ -28,7 +27,7 @@ ls = os.listdir('.')
|
||||
is_jesse_project = 'strategies' in ls and 'config.py' in ls and 'storage' in ls and 'routes.py' in ls
|
||||
|
||||
|
||||
def validate_cwd():
|
||||
def validate_cwd() -> None:
|
||||
"""
|
||||
make sure we're in a Jesse project
|
||||
"""
|
||||
@@ -42,7 +41,7 @@ def validate_cwd():
|
||||
os._exit(1)
|
||||
|
||||
|
||||
def inject_local_config():
|
||||
def inject_local_config() -> None:
|
||||
"""
|
||||
injects config from local config file
|
||||
"""
|
||||
@@ -51,7 +50,7 @@ def inject_local_config():
|
||||
set_config(local_config)
|
||||
|
||||
|
||||
def inject_local_routes():
|
||||
def inject_local_routes() -> None:
|
||||
"""
|
||||
injects routes from local routes folder
|
||||
"""
|
||||
@@ -68,7 +67,7 @@ if is_jesse_project:
|
||||
inject_local_routes()
|
||||
|
||||
|
||||
def register_custom_exception_handler():
|
||||
def register_custom_exception_handler() -> None:
|
||||
"""
|
||||
|
||||
:return:
|
||||
@@ -85,9 +84,11 @@ def register_custom_exception_handler():
|
||||
os.makedirs('storage/logs', exist_ok=True)
|
||||
|
||||
if jh.is_livetrading():
|
||||
logging.basicConfig(filename='storage/logs/live-trade.txt', level=logging.INFO, filemode='w', format=log_format)
|
||||
logging.basicConfig(filename='storage/logs/live-trade.txt', level=logging.INFO,
|
||||
filemode='w', format=log_format)
|
||||
elif jh.is_paper_trading():
|
||||
logging.basicConfig(filename='storage/logs/paper-trade.txt', level=logging.INFO, filemode='w',
|
||||
logging.basicConfig(filename='storage/logs/paper-trade.txt', level=logging.INFO,
|
||||
filemode='w',
|
||||
format=log_format)
|
||||
elif jh.is_collecting_data():
|
||||
logging.basicConfig(filename='storage/logs/collect.txt', level=logging.INFO, filemode='w',
|
||||
@@ -99,21 +100,14 @@ def register_custom_exception_handler():
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
# main thread
|
||||
def handle_exception(exc_type, exc_value, exc_traceback):
|
||||
"""
|
||||
|
||||
:param exc_type:
|
||||
:param exc_value:
|
||||
:param exc_traceback:
|
||||
:return:
|
||||
"""
|
||||
def handle_exception(exc_type, exc_value, exc_traceback) -> None:
|
||||
if issubclass(exc_type, KeyboardInterrupt):
|
||||
sys.excepthook(exc_type, exc_value, exc_traceback)
|
||||
return
|
||||
|
||||
# handle Breaking exceptions
|
||||
if exc_type in [
|
||||
exceptions.ConfigException, exceptions.RouteNotFound, exceptions.InvalidRoutes,
|
||||
exceptions.InvalidConfig, exceptions.RouteNotFound, exceptions.InvalidRoutes,
|
||||
exceptions.CandleNotFoundInDatabase
|
||||
]:
|
||||
click.clear()
|
||||
@@ -177,18 +171,13 @@ def register_custom_exception_handler():
|
||||
|
||||
# other threads
|
||||
if jh.python_version() >= 3.8:
|
||||
def handle_thread_exception(args):
|
||||
"""
|
||||
|
||||
:param args:
|
||||
:return:
|
||||
"""
|
||||
def handle_thread_exception(args) -> None:
|
||||
if args.exc_type == SystemExit:
|
||||
return
|
||||
|
||||
# handle Breaking exceptions
|
||||
if args.exc_type in [
|
||||
exceptions.ConfigException, exceptions.RouteNotFound, exceptions.InvalidRoutes,
|
||||
exceptions.InvalidConfig, exceptions.RouteNotFound, exceptions.InvalidRoutes,
|
||||
exceptions.CandleNotFoundInDatabase
|
||||
]:
|
||||
click.clear()
|
||||
@@ -209,7 +198,8 @@ def register_custom_exception_handler():
|
||||
)
|
||||
|
||||
if jh.is_live() or jh.is_collecting_data():
|
||||
logging.error("Uncaught Exception:", exc_info=(args.exc_type, args.exc_value, args.exc_traceback))
|
||||
logging.error("Uncaught Exception:",
|
||||
exc_info=(args.exc_type, args.exc_value, args.exc_traceback))
|
||||
else:
|
||||
print('=' * 30 + ' EXCEPTION TRACEBACK:')
|
||||
traceback.print_tb(args.exc_traceback, file=sys.stdout)
|
||||
@@ -254,7 +244,7 @@ def register_custom_exception_handler():
|
||||
# create a Click group
|
||||
@click.group()
|
||||
@click.version_option(pkg_resources.get_distribution("jesse").version)
|
||||
def cli():
|
||||
def cli() -> None:
|
||||
pass
|
||||
|
||||
|
||||
@@ -262,7 +252,9 @@ def cli():
|
||||
@click.argument('exchange', required=True, type=str)
|
||||
@click.argument('symbol', required=True, type=str)
|
||||
@click.argument('start_date', required=True, type=str)
|
||||
def import_candles(exchange, symbol, start_date):
|
||||
@click.option('--skip-confirmation', is_flag=True,
|
||||
help="Will prevent confirmation for skipping duplicates")
|
||||
def import_candles(exchange: str, symbol: str, start_date: str, skip_confirmation: bool) -> None:
|
||||
"""
|
||||
imports historical candles from exchange
|
||||
"""
|
||||
@@ -276,7 +268,7 @@ def import_candles(exchange, symbol, start_date):
|
||||
|
||||
from jesse.modes import import_candles_mode
|
||||
|
||||
import_candles_mode.run(exchange, symbol, start_date)
|
||||
import_candles_mode.run(exchange, symbol, start_date, skip_confirmation)
|
||||
|
||||
db.close_connection()
|
||||
|
||||
@@ -290,12 +282,14 @@ def import_candles(exchange, symbol, start_date):
|
||||
help='Outputs a CSV file of all executed trades on completion.')
|
||||
@click.option('--json/--no-json', default=False,
|
||||
help='Outputs a JSON file of all executed trades on completion.')
|
||||
@click.option('--fee/--no-fee', default=True, help='You can use "--no-fee" as a quick way to set trading fee to zero.')
|
||||
@click.option('--fee/--no-fee', default=True,
|
||||
help='You can use "--no-fee" as a quick way to set trading fee to zero.')
|
||||
@click.option('--chart/--no-chart', default=False,
|
||||
help='Generates charts of daily portfolio balance and assets price change. Useful for a visual comparision of your portfolio against the market.')
|
||||
@click.option('--tradingview/--no-tradingview', default=False,
|
||||
help="Generates an output that can be copy-and-pasted into tradingview.com's pine-editor too see the trades in their charts.")
|
||||
def backtest(start_date, finish_date, debug, csv, json, fee, chart, tradingview):
|
||||
def backtest(start_date: str, finish_date: str, debug: bool, csv: bool, json: bool, fee: bool, chart: bool,
|
||||
tradingview: bool) -> None:
|
||||
"""
|
||||
backtest mode. Enter in "YYYY-MM-DD" "YYYY-MM-DD"
|
||||
"""
|
||||
@@ -319,7 +313,8 @@ def backtest(start_date, finish_date, debug, csv, json, fee, chart, tradingview)
|
||||
config['env']['exchanges'][e]['fee'] = 0
|
||||
get_exchange(e).fee = 0
|
||||
|
||||
backtest_mode.run(start_date, finish_date, chart=chart, tradingview=tradingview, csv=csv, json=json)
|
||||
backtest_mode.run(start_date, finish_date, chart=chart, tradingview=tradingview, csv=csv,
|
||||
json=json)
|
||||
|
||||
db.close_connection()
|
||||
|
||||
@@ -335,7 +330,10 @@ def backtest(start_date, finish_date, debug, csv, json, fee, chart, tradingview)
|
||||
'--debug/--no-debug', default=False,
|
||||
help='Displays detailed logs about the genetics algorithm. Use it if you are interested int he genetics algorithm.'
|
||||
)
|
||||
def optimize(start_date, finish_date, optimal_total, cpu, debug):
|
||||
@click.option('--csv/--no-csv', default=False, help='Outputs a CSV file of all DNAs on completion.')
|
||||
@click.option('--json/--no-json', default=False, help='Outputs a JSON file of all DNAs on completion.')
|
||||
def optimize(start_date: str, finish_date: str, optimal_total: int, cpu: int, debug: bool, csv: bool,
|
||||
json: bool) -> None:
|
||||
"""
|
||||
tunes the hyper-parameters of your strategy
|
||||
"""
|
||||
@@ -350,12 +348,12 @@ def optimize(start_date, finish_date, optimal_total, cpu, debug):
|
||||
|
||||
from jesse.modes.optimize_mode import optimize_mode
|
||||
|
||||
optimize_mode(start_date, finish_date, optimal_total, cpu)
|
||||
optimize_mode(start_date, finish_date, optimal_total, cpu, csv, json)
|
||||
|
||||
|
||||
@cli.command()
|
||||
@click.argument('name', required=True, type=str)
|
||||
def make_strategy(name):
|
||||
def make_strategy(name: str) -> None:
|
||||
"""
|
||||
generates a new strategy folder from jesse/strategies/ExampleStrategy
|
||||
"""
|
||||
@@ -373,7 +371,7 @@ def make_strategy(name):
|
||||
|
||||
@cli.command()
|
||||
@click.argument('name', required=True, type=str)
|
||||
def make_project(name):
|
||||
def make_project(name: str) -> None:
|
||||
"""
|
||||
generates a new strategy folder from jesse/strategies/ExampleStrategy
|
||||
"""
|
||||
@@ -389,8 +387,9 @@ def make_project(name):
|
||||
|
||||
|
||||
@cli.command()
|
||||
@click.option('--dna/--no-dna', default=False, help='Translates DNA into parameters. Used in optimize mode only')
|
||||
def routes(dna):
|
||||
@click.option('--dna/--no-dna', default=False,
|
||||
help='Translates DNA into parameters. Used in optimize mode only')
|
||||
def routes(dna: bool) -> None:
|
||||
"""
|
||||
lists all routes
|
||||
"""
|
||||
@@ -408,7 +407,7 @@ def routes(dna):
|
||||
|
||||
if 'plugins' in ls:
|
||||
@cli.command()
|
||||
def collect():
|
||||
def collect() -> None:
|
||||
"""
|
||||
fetches streamed market data such as tickers, trades, and orderbook from
|
||||
the WS connection and stores them into the database for later research.
|
||||
@@ -431,7 +430,7 @@ if 'plugins' in ls:
|
||||
@click.option('--debug/--no-debug', default=False)
|
||||
@click.option('--dev/--no-dev', default=False)
|
||||
@click.option('--fee/--no-fee', default=True)
|
||||
def live(testdrive, debug, dev, fee):
|
||||
def live(testdrive: bool, debug: bool, dev: bool, fee: bool) -> None:
|
||||
"""
|
||||
trades in real-time on exchange with REAL money
|
||||
"""
|
||||
@@ -468,7 +467,7 @@ if 'plugins' in ls:
|
||||
@click.option('--debug/--no-debug', default=False)
|
||||
@click.option('--dev/--no-dev', default=False)
|
||||
@click.option('--fee/--no-fee', default=True)
|
||||
def paper(debug, dev, fee):
|
||||
def paper(debug: bool, dev: bool, fee: bool) -> None:
|
||||
"""
|
||||
trades in real-time on exchange with PAPER money
|
||||
"""
|
||||
|
||||
130
jesse/config.py
130
jesse/config.py
@@ -30,69 +30,139 @@ config = {
|
||||
'Sandbox': {
|
||||
'fee': 0,
|
||||
'type': 'spot',
|
||||
# used only in margin trading
|
||||
# used only in futures trading
|
||||
'settlement_currency': 'USDT',
|
||||
# accepted values are: 'cross' and 'isolated'
|
||||
'futures_leverage_mode': 'cross',
|
||||
# 1x, 2x, 10x, 50x, etc. Enter as integers
|
||||
'futures_leverage': 1,
|
||||
'assets': [
|
||||
{'asset': 'USDT', 'balance': 10000},
|
||||
{'asset': 'USDT', 'balance': 10_000},
|
||||
{'asset': 'BTC', 'balance': 0},
|
||||
],
|
||||
},
|
||||
|
||||
# https://www.bitfinex.com
|
||||
'Bitfinex': {
|
||||
'type': 'margin',
|
||||
# used only in margin trading
|
||||
'settlement_currency': 'USD',
|
||||
'fee': 0.002,
|
||||
|
||||
# backtest mode only: accepted are 'spot' and 'futures'
|
||||
'type': 'futures',
|
||||
|
||||
# futures mode only
|
||||
'settlement_currency': 'USD',
|
||||
# accepted values are: 'cross' and 'isolated'
|
||||
'futures_leverage_mode': 'cross',
|
||||
# 1x, 2x, 10x, 50x, etc. Enter as integers
|
||||
'futures_leverage': 1,
|
||||
|
||||
# used for spot exchange only
|
||||
'assets': [
|
||||
{'asset': 'USDT', 'balance': 10000},
|
||||
{'asset': 'USD', 'balance': 10000},
|
||||
{'asset': 'USDT', 'balance': 10_000},
|
||||
{'asset': 'USD', 'balance': 10_000},
|
||||
{'asset': 'BTC', 'balance': 0},
|
||||
],
|
||||
},
|
||||
|
||||
# https://www.binance.com
|
||||
'Binance': {
|
||||
'type': 'spot',
|
||||
# used only in margin trading
|
||||
'settlement_currency': 'USDT',
|
||||
'fee': 0.001,
|
||||
|
||||
# backtest mode only: accepted are 'spot' and 'futures'
|
||||
'type': 'futures',
|
||||
|
||||
# futures mode only
|
||||
'settlement_currency': 'USDT',
|
||||
# accepted values are: 'cross' and 'isolated'
|
||||
'futures_leverage_mode': 'cross',
|
||||
# 1x, 2x, 10x, 50x, etc. Enter as integers
|
||||
'futures_leverage': 1,
|
||||
|
||||
# used for spot exchange only
|
||||
'assets': [
|
||||
{'asset': 'USDT', 'balance': 10000},
|
||||
{'asset': 'USDT', 'balance': 10_000},
|
||||
{'asset': 'BTC', 'balance': 0},
|
||||
],
|
||||
},
|
||||
|
||||
# https://www.binance.com
|
||||
# https://www.binance.com/en/futures/BTC_USDT
|
||||
'Binance Futures': {
|
||||
'type': 'margin',
|
||||
# used only in margin trading
|
||||
'fee': 0.0004,
|
||||
|
||||
# backtest mode only: accepted are 'spot' and 'futures'
|
||||
'type': 'futures',
|
||||
|
||||
# futures mode only
|
||||
'settlement_currency': 'USDT',
|
||||
'fee': 0.0002,
|
||||
# accepted values are: 'cross' and 'isolated'
|
||||
'futures_leverage_mode': 'cross',
|
||||
# 1x, 2x, 10x, 50x, etc. Enter as integers
|
||||
'futures_leverage': 1,
|
||||
|
||||
# used for spot exchange only
|
||||
'assets': [
|
||||
{'asset': 'USDT', 'balance': 10000},
|
||||
{'asset': 'USDT', 'balance': 10_000},
|
||||
],
|
||||
},
|
||||
|
||||
# https://www.binance.com/en/delivery/btcusd_perpetual
|
||||
'Binance Inverse Futures': {
|
||||
'fee': 0.0004,
|
||||
|
||||
# backtest mode only: accepted are 'spot' and 'futures'
|
||||
'type': 'inverse futures',
|
||||
|
||||
# accepted values are: 'cross' and 'isolated'
|
||||
'futures_leverage_mode': 'cross',
|
||||
# 1x, 2x, 10x, 50x, etc. Enter as integers
|
||||
'futures_leverage': 1,
|
||||
# Price per contract, also called the contract-multiplier
|
||||
'contract_size': 100,
|
||||
|
||||
'assets': [
|
||||
{'asset': 'BTC', 'balance': 1},
|
||||
{'asset': 'ETH', 'balance': 10},
|
||||
],
|
||||
},
|
||||
|
||||
# https://testnet.binancefuture.com
|
||||
'Testnet Binance Futures': {
|
||||
'type': 'margin',
|
||||
# used only in margin trading
|
||||
'fee': 0.0004,
|
||||
|
||||
# backtest mode only: accepted are 'spot' and 'futures'
|
||||
'type': 'futures',
|
||||
|
||||
# futures mode only
|
||||
'settlement_currency': 'USDT',
|
||||
'fee': 0.0002,
|
||||
# accepted values are: 'cross' and 'isolated'
|
||||
'futures_leverage_mode': 'cross',
|
||||
# 1x, 2x, 10x, 50x, etc. Enter as integers
|
||||
'futures_leverage': 1,
|
||||
|
||||
# used for spot mode
|
||||
'assets': [
|
||||
{'asset': 'USDT', 'balance': 10000},
|
||||
{'asset': 'USDT', 'balance': 10_000},
|
||||
],
|
||||
},
|
||||
|
||||
# https://pro.coinbase.com
|
||||
'Coinbase': {
|
||||
'type': 'spot',
|
||||
# used only in margin trading
|
||||
'settlement_currency': 'USDT',
|
||||
'fee': 0.005,
|
||||
|
||||
# backtest mode only: accepted are 'spot' and 'futures'
|
||||
'type': 'futures',
|
||||
|
||||
# futures mode only
|
||||
'settlement_currency': 'USD',
|
||||
# accepted values are: 'cross' and 'isolated'
|
||||
'futures_leverage_mode': 'cross',
|
||||
# 1x, 2x, 10x, 50x, etc. Enter as integers
|
||||
'futures_leverage': 1,
|
||||
|
||||
# used for spot exchange only
|
||||
'assets': [
|
||||
{'asset': 'USDT', 'balance': 10000},
|
||||
{'asset': 'USDT', 'balance': 10_000},
|
||||
{'asset': 'USD', 'balance': 10_000},
|
||||
{'asset': 'BTC', 'balance': 0},
|
||||
],
|
||||
},
|
||||
@@ -131,7 +201,7 @@ config = {
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
'data': {
|
||||
# The minimum number of warmup candles that is loaded before each session.
|
||||
'warmup_candles_num': 210,
|
||||
'warmup_candles_num': 240,
|
||||
}
|
||||
},
|
||||
|
||||
@@ -171,22 +241,24 @@ config = {
|
||||
backup_config = config.copy()
|
||||
|
||||
|
||||
def set_config(c):
|
||||
def set_config(c) -> None:
|
||||
global config
|
||||
config['env'] = c
|
||||
# add sandbox because it isn't in the local config file
|
||||
config['env']['exchanges']['Sandbox'] = {
|
||||
'type': 'spot',
|
||||
# used only in margin trading
|
||||
# used only in futures trading
|
||||
'settlement_currency': 'USDT',
|
||||
'fee': 0,
|
||||
'futures_leverage_mode': 'cross',
|
||||
'futures_leverage': 1,
|
||||
'assets': [
|
||||
{'asset': 'USDT', 'balance': 10000},
|
||||
{'asset': 'USDT', 'balance': 10_000},
|
||||
{'asset': 'BTC', 'balance': 0},
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def reset_config():
|
||||
def reset_config() -> None:
|
||||
global config
|
||||
config = backup_config.copy()
|
||||
|
||||
@@ -21,13 +21,17 @@ class timeframes:
|
||||
MINUTE_5 = '5m'
|
||||
MINUTE_15 = '15m'
|
||||
MINUTE_30 = '30m'
|
||||
MINUTE_45 = '45m'
|
||||
HOUR_1 = '1h'
|
||||
HOUR_2 = '2h'
|
||||
HOUR_3 = '3h'
|
||||
HOUR_4 = '4h'
|
||||
HOUR_6 = '6h'
|
||||
HOUR_8 = '8h'
|
||||
HOUR_12 = '12h'
|
||||
DAY_1 = '1D'
|
||||
DAY_3 = '3D'
|
||||
WEEK_1 = '1W'
|
||||
|
||||
|
||||
class colors:
|
||||
|
||||
@@ -58,12 +58,17 @@ class InvalidShape(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class ConfigException(Exception):
|
||||
class InvalidConfig(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class InvalidTimeframe(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class NegativeBalance(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class InsufficientMargin(Exception):
|
||||
pass
|
||||
|
||||
@@ -8,64 +8,24 @@ class Exchange(ABC):
|
||||
|
||||
@abstractmethod
|
||||
def market_order(self, symbol, qty, current_price, side, role, flags):
|
||||
"""
|
||||
|
||||
:param symbol:
|
||||
:param qty:
|
||||
:param current_price:
|
||||
:param side:
|
||||
:param role:
|
||||
:param flags:
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def limit_order(self, symbol, qty, price, side, role, flags):
|
||||
"""
|
||||
|
||||
:param symbol:
|
||||
:param qty:
|
||||
:param price:
|
||||
:param side:
|
||||
:param role:
|
||||
:param flags:
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def stop_order(self, symbol, qty, price, side, role, flags):
|
||||
"""
|
||||
|
||||
:param symbol:
|
||||
:param qty:
|
||||
:param price:
|
||||
:param side:
|
||||
:param role:
|
||||
:param flags:
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def cancel_all_orders(self, symbol):
|
||||
"""
|
||||
|
||||
:param symbol:
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def cancel_order(self, symbol, order_id):
|
||||
"""
|
||||
|
||||
:param symbol:
|
||||
:param order_id:
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_exec_inst(self, flags):
|
||||
"""
|
||||
|
||||
:param flags:
|
||||
"""
|
||||
pass
|
||||
|
||||
206
jesse/helpers.py
206
jesse/helpers.py
@@ -5,6 +5,7 @@ import random
|
||||
import string
|
||||
import sys
|
||||
import uuid
|
||||
from typing import List, Tuple, Union, Any
|
||||
|
||||
import arrow
|
||||
import click
|
||||
@@ -13,21 +14,24 @@ import numpy as np
|
||||
CACHED_CONFIG = dict()
|
||||
|
||||
|
||||
def app_currency():
|
||||
def app_currency() -> str:
|
||||
from jesse.routes import router
|
||||
return quote_asset(router.routes[0].symbol)
|
||||
underlying = quote_asset(router.routes[0].symbol)
|
||||
if underlying.upper() == 'PERP':
|
||||
underlying = base_asset(router.routes[0].symbol)
|
||||
return underlying
|
||||
|
||||
|
||||
def app_mode():
|
||||
def app_mode() -> str:
|
||||
from jesse.config import config
|
||||
return config['app']['trading_mode']
|
||||
|
||||
|
||||
def arrow_to_timestamp(arrow_time):
|
||||
def arrow_to_timestamp(arrow_time: arrow.arrow.Arrow) -> int:
|
||||
return arrow_time.int_timestamp * 1000
|
||||
|
||||
|
||||
def base_asset(symbol: str):
|
||||
def base_asset(symbol: str) -> str:
|
||||
return symbol.split('-')[0]
|
||||
|
||||
|
||||
@@ -49,11 +53,16 @@ def binary_search(arr: list, item) -> int:
|
||||
return -1
|
||||
|
||||
|
||||
def clean_orderbook_list(arr):
|
||||
def class_iter(Class):
|
||||
return (value for variable, value in vars(Class).items() if
|
||||
not callable(getattr(Class, variable)) and not variable.startswith("__"))
|
||||
|
||||
|
||||
def clean_orderbook_list(arr) -> List[List[float]]:
|
||||
return [[float(i[0]), float(i[1])] for i in arr]
|
||||
|
||||
|
||||
def color(msg_text: str, msg_color: str):
|
||||
def color(msg_text: str, msg_color: str) -> str:
|
||||
if not msg_text:
|
||||
return ''
|
||||
|
||||
@@ -77,7 +86,7 @@ def color(msg_text: str, msg_color: str):
|
||||
raise ValueError('unsupported color')
|
||||
|
||||
|
||||
def convert_number(old_max, old_min, new_max, new_min, old_value):
|
||||
def convert_number(old_max: float, old_min: float, new_max: float, new_min: float, old_value: float) -> float:
|
||||
"""
|
||||
convert a number from one range (ex 40-119) to another
|
||||
range (ex 0-30) while keeping the ratio.
|
||||
@@ -92,15 +101,22 @@ def convert_number(old_max, old_min, new_max, new_min, old_value):
|
||||
|
||||
return new_value
|
||||
|
||||
def dashless_symbol(symbol):
|
||||
|
||||
def dashless_symbol(symbol: str) -> str:
|
||||
return symbol.replace("-", "")
|
||||
|
||||
def date_diff_in_days(date1, date2):
|
||||
|
||||
def dashy_symbol(symbol: str) -> str:
|
||||
return symbol[0:3] + '-' + symbol[3:]
|
||||
|
||||
|
||||
def date_diff_in_days(date1: arrow.arrow.Arrow, date2: arrow.arrow.Arrow) -> int:
|
||||
if type(date1) is not arrow.arrow.Arrow or type(
|
||||
date2) is not arrow.arrow.Arrow:
|
||||
raise TypeError('dates must be Arrow instances')
|
||||
|
||||
dif = date2 - date1
|
||||
|
||||
return abs(dif.days)
|
||||
|
||||
|
||||
@@ -130,11 +146,10 @@ def dna_to_hp(strategy_hp, dna: str):
|
||||
raise TypeError('Only int and float types are implemented')
|
||||
|
||||
hp[h['name']] = decoded_gene
|
||||
|
||||
return hp
|
||||
|
||||
|
||||
def dump_exception():
|
||||
def dump_exception() -> None:
|
||||
"""
|
||||
a useful debugging helper
|
||||
"""
|
||||
@@ -143,7 +158,8 @@ def dump_exception():
|
||||
terminate_app()
|
||||
|
||||
|
||||
def estimate_average_price(order_qty, order_price, current_qty, current_entry_price):
|
||||
def estimate_average_price(order_qty: float, order_price: float, current_qty: float,
|
||||
current_entry_price: float) -> float:
|
||||
"""Estimates the new entry price for the position.
|
||||
This is used after having a new order and updating the currently holding position.
|
||||
|
||||
@@ -160,7 +176,7 @@ def estimate_average_price(order_qty, order_price, current_qty, current_entry_pr
|
||||
current_entry_price) / (abs(order_qty) + abs(current_qty))
|
||||
|
||||
|
||||
def estimate_PNL(qty, entry_price, exit_price, trade_type, trading_fee=0):
|
||||
def estimate_PNL(qty: float, entry_price: float, exit_price: float, trade_type: str, trading_fee: float = 0) -> float:
|
||||
qty = abs(qty)
|
||||
profit = qty * (exit_price - entry_price)
|
||||
|
||||
@@ -172,7 +188,7 @@ def estimate_PNL(qty, entry_price, exit_price, trade_type, trading_fee=0):
|
||||
return profit - fee
|
||||
|
||||
|
||||
def estimate_PNL_percentage(qty, entry_price, exit_price, trade_type):
|
||||
def estimate_PNL_percentage(qty: float, entry_price: float, exit_price: float, trade_type: str) -> float:
|
||||
qty = abs(qty)
|
||||
profit = qty * (exit_price - entry_price)
|
||||
|
||||
@@ -186,20 +202,24 @@ def file_exists(path: str) -> bool:
|
||||
return os.path.isfile(path)
|
||||
|
||||
|
||||
def floor_with_precision(num, precision=0):
|
||||
def floor_with_precision(num: float, precision: int = 0) -> float:
|
||||
temp = 10 ** precision
|
||||
return math.floor(num * temp) / temp
|
||||
|
||||
|
||||
def format_currency(num):
|
||||
def format_currency(num: float) -> str:
|
||||
return f'{num:,}'
|
||||
|
||||
|
||||
def generate_unique_id():
|
||||
def generate_unique_id() -> str:
|
||||
return str(uuid.uuid4())
|
||||
|
||||
|
||||
def get_candle_source(candles: np.ndarray, source_type="close") -> np.ndarray:
|
||||
def get_arrow(timestamp: int) -> arrow.arrow.Arrow:
|
||||
return timestamp_to_arrow(timestamp)
|
||||
|
||||
|
||||
def get_candle_source(candles: np.ndarray, source_type: str = "close") -> np.ndarray:
|
||||
"""
|
||||
Returns the candles corresponding the selected type.
|
||||
|
||||
@@ -228,7 +248,7 @@ def get_candle_source(candles: np.ndarray, source_type="close") -> np.ndarray:
|
||||
raise ValueError('type string not recognised')
|
||||
|
||||
|
||||
def get_config(keys: str, default=None):
|
||||
def get_config(keys: str, default: Any = None) -> Any:
|
||||
"""
|
||||
Gets keys as a single string separated with "." and returns value.
|
||||
Also accepts a default value so that the app would work even if
|
||||
@@ -242,16 +262,19 @@ def get_config(keys: str, default=None):
|
||||
if not str:
|
||||
raise ValueError('keys string cannot be empty')
|
||||
|
||||
if not keys in CACHED_CONFIG:
|
||||
from functools import reduce
|
||||
from jesse.config import config
|
||||
CACHED_CONFIG[keys] = reduce(lambda d, k: d.get(k, default) if isinstance(d, dict) else default,
|
||||
keys.split("."), config)
|
||||
if is_unit_testing() or not keys in CACHED_CONFIG:
|
||||
if os.environ.get(keys.upper().replace(".", "_")) is not None:
|
||||
CACHED_CONFIG[keys] = os.environ.get(keys.upper().replace(".", "_"))
|
||||
else:
|
||||
from functools import reduce
|
||||
from jesse.config import config
|
||||
CACHED_CONFIG[keys] = reduce(lambda d, k: d.get(k, default) if isinstance(d, dict) else default,
|
||||
keys.split("."), config)
|
||||
|
||||
return CACHED_CONFIG[keys]
|
||||
|
||||
|
||||
def get_strategy_class(strategy_name):
|
||||
def get_strategy_class(strategy_name: str):
|
||||
from pydoc import locate
|
||||
|
||||
if is_unit_testing():
|
||||
@@ -264,7 +287,7 @@ def insecure_hash(msg: str) -> str:
|
||||
return hashlib.md5(msg.encode()).hexdigest()
|
||||
|
||||
|
||||
def insert_list(index: int, item, arr: list):
|
||||
def insert_list(index: int, item, arr: list) -> list:
|
||||
"""
|
||||
helper to insert an item in a Python List without removing the item
|
||||
"""
|
||||
@@ -274,71 +297,85 @@ def insert_list(index: int, item, arr: list):
|
||||
return arr[:index] + [item] + arr[index:]
|
||||
|
||||
|
||||
def is_backtesting():
|
||||
def is_backtesting() -> bool:
|
||||
from jesse.config import config
|
||||
return config['app']['trading_mode'] == 'backtest'
|
||||
|
||||
|
||||
def is_collecting_data():
|
||||
def is_collecting_data() -> bool:
|
||||
from jesse.config import config
|
||||
return config['app']['trading_mode'] == 'collect'
|
||||
|
||||
|
||||
def is_debuggable(debug_item):
|
||||
def is_debuggable(debug_item) -> bool:
|
||||
from jesse.config import config
|
||||
return is_debugging() and config['env']['logging'][debug_item]
|
||||
|
||||
|
||||
def is_debugging():
|
||||
def is_debugging() -> bool:
|
||||
from jesse.config import config
|
||||
return config['app']['debug_mode']
|
||||
|
||||
|
||||
def is_importing_candles():
|
||||
def is_importing_candles() -> bool:
|
||||
from jesse.config import config
|
||||
return config['app']['trading_mode'] == 'import-candles'
|
||||
|
||||
|
||||
def is_live():
|
||||
def is_live() -> bool:
|
||||
return is_livetrading() or is_paper_trading()
|
||||
|
||||
|
||||
def is_livetrading():
|
||||
def is_livetrading() -> bool:
|
||||
from jesse.config import config
|
||||
return config['app']['trading_mode'] == 'livetrade'
|
||||
|
||||
|
||||
def is_optimizing():
|
||||
def is_optimizing() -> bool:
|
||||
from jesse.config import config
|
||||
return config['app']['trading_mode'] == 'optimize'
|
||||
|
||||
|
||||
def is_paper_trading():
|
||||
def is_paper_trading() -> bool:
|
||||
from jesse.config import config
|
||||
return config['app']['trading_mode'] == 'papertrade'
|
||||
|
||||
|
||||
def is_test_driving():
|
||||
def is_test_driving() -> bool:
|
||||
from jesse.config import config
|
||||
return config['app']['is_test_driving']
|
||||
|
||||
|
||||
def is_unit_testing():
|
||||
def is_unit_testing() -> bool:
|
||||
return "pytest" in sys.modules
|
||||
|
||||
|
||||
def key(exchange, symbol, timeframe=None):
|
||||
def is_valid_uuid(uuid_to_test, version: int = 4) -> bool:
|
||||
try:
|
||||
uuid_obj = uuid.UUID(uuid_to_test, version=version)
|
||||
except ValueError:
|
||||
return False
|
||||
return str(uuid_obj) == uuid_to_test
|
||||
|
||||
|
||||
def key(exchange: str, symbol: str, timeframe: str = None):
|
||||
if timeframe is None:
|
||||
return '{}-{}'.format(exchange, symbol)
|
||||
|
||||
return '{}-{}-{}'.format(exchange, symbol, timeframe)
|
||||
|
||||
|
||||
def max_timeframe(timeframes_list):
|
||||
def max_timeframe(timeframes_list: list) -> str:
|
||||
from jesse.enums import timeframes
|
||||
|
||||
if timeframes.WEEK_1 in timeframes_list:
|
||||
return timeframes.WEEK_1
|
||||
if timeframes.DAY_3 in timeframes_list:
|
||||
return timeframes.DAY_3
|
||||
if timeframes.DAY_1 in timeframes_list:
|
||||
return timeframes.DAY_1
|
||||
if timeframes.HOUR_12 in timeframes_list:
|
||||
return timeframes.HOUR_12
|
||||
if timeframes.HOUR_8 in timeframes_list:
|
||||
return timeframes.HOUR_8
|
||||
if timeframes.HOUR_6 in timeframes_list:
|
||||
@@ -351,6 +388,8 @@ def max_timeframe(timeframes_list):
|
||||
return timeframes.HOUR_2
|
||||
if timeframes.HOUR_1 in timeframes_list:
|
||||
return timeframes.HOUR_1
|
||||
if timeframes.MINUTE_45 in timeframes_list:
|
||||
return timeframes.MINUTE_45
|
||||
if timeframes.MINUTE_30 in timeframes_list:
|
||||
return timeframes.MINUTE_30
|
||||
if timeframes.MINUTE_15 in timeframes_list:
|
||||
@@ -363,7 +402,7 @@ def max_timeframe(timeframes_list):
|
||||
return timeframes.MINUTE_1
|
||||
|
||||
|
||||
def normalize(x, x_min, x_max):
|
||||
def normalize(x: float, x_min: float, x_max: float) -> float:
|
||||
"""
|
||||
Rescaling data to have values between 0 and 1
|
||||
"""
|
||||
@@ -371,7 +410,11 @@ def normalize(x, x_min, x_max):
|
||||
return x_new
|
||||
|
||||
|
||||
def now_to_timestamp():
|
||||
def now() -> int:
|
||||
return now_to_timestamp()
|
||||
|
||||
|
||||
def now_to_timestamp() -> int:
|
||||
if not (is_live() or is_collecting_data() or is_importing_candles()):
|
||||
from jesse.store import store
|
||||
return store.app.time
|
||||
@@ -379,11 +422,18 @@ def now_to_timestamp():
|
||||
return arrow.utcnow().int_timestamp * 1000
|
||||
|
||||
|
||||
def now():
|
||||
return now_to_timestamp()
|
||||
def np_ffill(arr: np.ndarray, axis: int = 0) -> np.ndarray:
|
||||
idx_shape = tuple([slice(None)] + [np.newaxis] * (len(arr.shape) - axis - 1))
|
||||
idx = np.where(~np.isnan(arr), np.arange(arr.shape[axis])[idx_shape], 0)
|
||||
np.maximum.accumulate(idx, axis=axis, out=idx)
|
||||
slc = [np.arange(k)[tuple([slice(None) if dim == i else np.newaxis
|
||||
for dim in range(len(arr.shape))])]
|
||||
for i, k in enumerate(arr.shape)]
|
||||
slc[axis] = idx
|
||||
return arr[tuple(slc)]
|
||||
|
||||
|
||||
def np_shift(arr: np.ndarray, num: int, fill_value=0):
|
||||
def np_shift(arr: np.ndarray, num: int, fill_value=0) -> np.ndarray:
|
||||
result = np.empty_like(arr)
|
||||
|
||||
if num > 0:
|
||||
@@ -398,7 +448,7 @@ def np_shift(arr: np.ndarray, num: int, fill_value=0):
|
||||
return result
|
||||
|
||||
|
||||
def opposite_side(s):
|
||||
def opposite_side(s: str) -> str:
|
||||
from jesse.enums import sides
|
||||
|
||||
if s == sides.BUY:
|
||||
@@ -408,7 +458,7 @@ def opposite_side(s):
|
||||
raise ValueError('unsupported side')
|
||||
|
||||
|
||||
def opposite_type(t):
|
||||
def opposite_type(t: str) -> str:
|
||||
from jesse.enums import trade_types
|
||||
|
||||
if t == trade_types.LONG:
|
||||
@@ -418,7 +468,7 @@ def opposite_type(t):
|
||||
raise ValueError('unsupported type')
|
||||
|
||||
|
||||
def orderbook_insertion_index_search(arr, target, ascending=True):
|
||||
def orderbook_insertion_index_search(arr, target: int, ascending: bool = True) -> Tuple[bool, int]:
|
||||
target = target[0]
|
||||
lower = 0
|
||||
upper = len(arr)
|
||||
@@ -453,14 +503,7 @@ def orderbook_insertion_index_search(arr, target, ascending=True):
|
||||
upper = x
|
||||
|
||||
|
||||
def orderbook_trim_price(p: float, ascending: bool, unit: float):
|
||||
"""
|
||||
|
||||
:param p:
|
||||
:param ascending:
|
||||
:param unit:
|
||||
:return:
|
||||
"""
|
||||
def orderbook_trim_price(p: float, ascending: bool, unit: float) -> float:
|
||||
if ascending:
|
||||
trimmed = np.ceil(p / unit) * unit
|
||||
if math.log10(unit) < 0:
|
||||
@@ -473,7 +516,7 @@ def orderbook_trim_price(p: float, ascending: bool, unit: float):
|
||||
return p if trimmed == p - unit else trimmed
|
||||
|
||||
|
||||
def prepare_qty(qty, side):
|
||||
def prepare_qty(qty: float, side: str) -> float:
|
||||
if side.lower() in ('sell', 'short'):
|
||||
return -abs(qty)
|
||||
|
||||
@@ -487,7 +530,7 @@ def python_version() -> float:
|
||||
return float('{}.{}'.format(sys.version_info[0], sys.version_info[1]))
|
||||
|
||||
|
||||
def quote_asset(symbol: str):
|
||||
def quote_asset(symbol: str) -> str:
|
||||
try:
|
||||
return symbol.split('-')[1]
|
||||
except IndexError:
|
||||
@@ -495,11 +538,11 @@ def quote_asset(symbol: str):
|
||||
raise InvalidRoutes("The symbol format is incorrect. Correct example: 'BTC-USDT'. Yours is '{}'".format(symbol))
|
||||
|
||||
|
||||
def random_str(num_characters=8):
|
||||
def random_str(num_characters: int = 8) -> str:
|
||||
return ''.join(random.choice(string.ascii_letters) for i in range(num_characters))
|
||||
|
||||
|
||||
def readable_duration(seconds, granularity=2):
|
||||
def readable_duration(seconds: int, granularity: int = 2) -> str:
|
||||
intervals = (
|
||||
('weeks', 604800), # 60 * 60 * 24 * 7
|
||||
('days', 86400), # 60 * 60 * 24
|
||||
@@ -509,6 +552,7 @@ def readable_duration(seconds, granularity=2):
|
||||
)
|
||||
|
||||
result = []
|
||||
seconds = int(seconds)
|
||||
|
||||
for name, count in intervals:
|
||||
value = seconds // count
|
||||
@@ -524,12 +568,12 @@ def relative_to_absolute(path: str) -> str:
|
||||
return os.path.abspath(path)
|
||||
|
||||
|
||||
def round_price_for_live_mode(price, roundable_price):
|
||||
def round_price_for_live_mode(price: float, roundable_price: float) -> Union[float, np.array]:
|
||||
"""
|
||||
Rounds price(s) based on exchange requirements
|
||||
|
||||
:param price: float
|
||||
:param roundable_price: float | nd.array
|
||||
:param roundable_price: float
|
||||
:return: float | nd.array
|
||||
"""
|
||||
n = int(math.log10(price))
|
||||
@@ -544,7 +588,7 @@ def round_price_for_live_mode(price, roundable_price):
|
||||
return np.round(roundable_price, price_round_precision)
|
||||
|
||||
|
||||
def round_qty_for_live_mode(price, roundable_qty):
|
||||
def round_qty_for_live_mode(price: float, roundable_qty: float) -> Union[float, np.array]:
|
||||
"""
|
||||
Rounds qty(s) based on exchange requirements
|
||||
|
||||
@@ -560,8 +604,13 @@ def round_qty_for_live_mode(price, roundable_qty):
|
||||
qty_round_precision = n + 1
|
||||
if qty_round_precision > 3:
|
||||
qty_round_precision = 3
|
||||
rounded = np.round(roundable_qty, qty_round_precision)
|
||||
|
||||
return np.round(roundable_qty, qty_round_precision)
|
||||
for index, q in enumerate(rounded):
|
||||
if q == 0.0:
|
||||
rounded[index] = 0.001
|
||||
|
||||
return rounded
|
||||
|
||||
|
||||
def secure_hash(msg: str) -> str:
|
||||
@@ -572,7 +621,7 @@ def should_execute_silently() -> bool:
|
||||
return is_optimizing() or is_unit_testing()
|
||||
|
||||
|
||||
def side_to_type(s):
|
||||
def side_to_type(s: str) -> str:
|
||||
from jesse.enums import trade_types, sides
|
||||
|
||||
if s == sides.BUY:
|
||||
@@ -582,14 +631,14 @@ def side_to_type(s):
|
||||
raise ValueError
|
||||
|
||||
|
||||
def string_after_character(string: str, character: str):
|
||||
def string_after_character(string: str, character: str) -> str:
|
||||
try:
|
||||
return string.split(character, 1)[1]
|
||||
except IndexError:
|
||||
return None
|
||||
|
||||
|
||||
def style(msg_text: str, msg_style: str):
|
||||
def style(msg_text: str, msg_style: str) -> str:
|
||||
if msg_style is None:
|
||||
return msg_text
|
||||
|
||||
@@ -602,7 +651,7 @@ def style(msg_text: str, msg_style: str):
|
||||
raise ValueError('unsupported style')
|
||||
|
||||
|
||||
def terminate_app():
|
||||
def terminate_app() -> None:
|
||||
# close the database
|
||||
from jesse.services.db import close_connection
|
||||
close_connection()
|
||||
@@ -610,9 +659,10 @@ def terminate_app():
|
||||
os._exit(1)
|
||||
|
||||
|
||||
def timeframe_to_one_minutes(timeframe):
|
||||
def timeframe_to_one_minutes(timeframe: str) -> int:
|
||||
from jesse.enums import timeframes
|
||||
from jesse.exceptions import InvalidTimeframe
|
||||
all_timeframes = [timeframe for timeframe in class_iter(timeframes)]
|
||||
|
||||
dic = {
|
||||
timeframes.MINUTE_1: 1,
|
||||
@@ -620,36 +670,36 @@ def timeframe_to_one_minutes(timeframe):
|
||||
timeframes.MINUTE_5: 5,
|
||||
timeframes.MINUTE_15: 15,
|
||||
timeframes.MINUTE_30: 30,
|
||||
timeframes.MINUTE_45: 45,
|
||||
timeframes.HOUR_1: 60,
|
||||
timeframes.HOUR_2: 60 * 2,
|
||||
timeframes.HOUR_3: 60 * 3,
|
||||
timeframes.HOUR_4: 60 * 4,
|
||||
timeframes.HOUR_6: 60 * 6,
|
||||
timeframes.HOUR_8: 60 * 8,
|
||||
timeframes.HOUR_12: 60 * 12,
|
||||
timeframes.DAY_1: 60 * 24,
|
||||
timeframes.DAY_3: 60 * 24 * 3,
|
||||
timeframes.WEEK_1: 60 * 24 * 7,
|
||||
}
|
||||
|
||||
try:
|
||||
return dic[timeframe]
|
||||
except KeyError:
|
||||
raise InvalidTimeframe(
|
||||
'Timeframe "{}" is invalid. Supported timeframes are 1m, 3m, 5m, 15m, 30m, 1h, 2h, 3h, 4h, 6h, 8h, 1D'.format(
|
||||
timeframe))
|
||||
'Timeframe "{}" is invalid. Supported timeframes are {}.'.format(
|
||||
timeframe, ', '.join(all_timeframes)))
|
||||
|
||||
|
||||
def timestamp_to_arrow(timestamp):
|
||||
def timestamp_to_arrow(timestamp: int) -> arrow.arrow.Arrow:
|
||||
return arrow.get(timestamp / 1000)
|
||||
|
||||
|
||||
def get_arrow(timestamp):
|
||||
return timestamp_to_arrow(timestamp)
|
||||
|
||||
|
||||
def timestamp_to_date(timestamp: int) -> str:
|
||||
return str(arrow.get(timestamp / 1000))[:10]
|
||||
|
||||
|
||||
def timestamp_to_time(timestamp):
|
||||
def timestamp_to_time(timestamp: int) -> str:
|
||||
return str(arrow.get(timestamp / 1000))
|
||||
|
||||
|
||||
@@ -662,7 +712,7 @@ def today_to_timestamp() -> int:
|
||||
return arrow.utcnow().floor('day').int_timestamp * 1000
|
||||
|
||||
|
||||
def type_to_side(t):
|
||||
def type_to_side(t: str) -> str:
|
||||
from jesse.enums import trade_types, sides
|
||||
|
||||
if t == trade_types.LONG:
|
||||
|
||||
@@ -16,32 +16,38 @@ from .bollinger_bands_width import bollinger_bands_width
|
||||
from .bop import bop
|
||||
from .cc import cc
|
||||
from .cci import cci
|
||||
from .cfo import cfo
|
||||
from .cg import cg
|
||||
from .chande import chande
|
||||
from .cmo import cmo
|
||||
from .correlation_cycle import correlation_cycle
|
||||
from .correl import correl
|
||||
from .correlation_cycle import correlation_cycle
|
||||
from .cvi import cvi
|
||||
from .damiani_volatmeter import damiani_volatmeter
|
||||
from .dec_osc import dec_osc
|
||||
from .decycler import decycler
|
||||
from .dema import dema
|
||||
from .devstop import devstop
|
||||
from .di import di
|
||||
from .dm import dm
|
||||
from .dx import dx
|
||||
from .donchian import donchian
|
||||
from .dpo import dpo
|
||||
from .dti import dti
|
||||
from .dx import dx
|
||||
from .efi import efi
|
||||
from .ema import ema
|
||||
from .emd import emd
|
||||
from .emv import emv
|
||||
from .er import er
|
||||
from .fisher import fisher
|
||||
from .fosc import fosc
|
||||
from .frama import frama
|
||||
from .fwma import fwma
|
||||
from .gatorosc import gatorosc
|
||||
from .gauss import gauss
|
||||
from .hma import hma
|
||||
from .high_pass import high_pass
|
||||
from .high_pass_2_pole import high_pass_2_pole
|
||||
from .hma import hma
|
||||
from .ht_dcperiod import ht_dcperiod
|
||||
from .ht_dcphase import ht_dcphase
|
||||
from .ht_phasor import ht_phasor
|
||||
@@ -52,6 +58,7 @@ from .ichimoku_cloud import ichimoku_cloud
|
||||
from .ichimoku_cloud_seq import ichimoku_cloud_seq
|
||||
from .itrend import itrend
|
||||
from .kama import kama
|
||||
from .kaufmanstop import kaufmanstop
|
||||
from .keltner import keltner
|
||||
from .kst import kst
|
||||
from .kvo import kvo
|
||||
@@ -63,9 +70,9 @@ from .lrsi import lrsi
|
||||
from .macd import macd
|
||||
from .macdext import macdext
|
||||
from .mama import mama
|
||||
from .marketfi import marketfi
|
||||
from .mass import mass
|
||||
from .mcginley_dynamic import mcginley_dynamic
|
||||
from .marketfi import marketfi
|
||||
from .medprice import medprice
|
||||
from .mfi import mfi
|
||||
from .midpoint import midpoint
|
||||
@@ -83,20 +90,27 @@ from .pvi import pvi
|
||||
from .qstick import qstick
|
||||
from .reflex import reflex
|
||||
from .roc import roc
|
||||
from .roofing import roofing
|
||||
from .rocp import rocp
|
||||
from .rocr import rocr
|
||||
from .rocr100 import rocr100
|
||||
from .roofing import roofing
|
||||
from .rsi import rsi
|
||||
from .rsmk import rsmk
|
||||
from .rsx import rsx
|
||||
from .rvi import rvi
|
||||
from .safezonestop import safezonestop
|
||||
from .sar import sar
|
||||
from .sarext import sarext
|
||||
from .sinwma import sinwma
|
||||
from .sma import sma
|
||||
from .smma import smma
|
||||
from .srsi import srsi
|
||||
from .stc import stc
|
||||
from .stddev import stddev
|
||||
from .stochastic import stoch
|
||||
from .stochf import stochf
|
||||
from .supersmoother import supersmoother
|
||||
from .supersmoother_3_pole import supersmoother_3_pole
|
||||
from .supertrend import supertrend
|
||||
from .t3 import t3
|
||||
from .tema import tema
|
||||
@@ -111,12 +125,13 @@ from .ultosc import ultosc
|
||||
from .var import var
|
||||
from .vi import vi
|
||||
from .vidya import vidya
|
||||
from .vosc import vosc
|
||||
from .voss import voss
|
||||
from .vpci import vpci
|
||||
from .vpt import vpt
|
||||
from .vwma import vwma
|
||||
from .vwmacd import vwmacd
|
||||
from .vosc import vosc
|
||||
from .voss import voss
|
||||
from .vwap import vwap
|
||||
from .wad import wad
|
||||
from .wclprice import wclprice
|
||||
from .wilders import wilders
|
||||
|
||||
@@ -3,10 +3,12 @@ from collections import namedtuple
|
||||
import numpy as np
|
||||
import talib
|
||||
|
||||
from jesse.helpers import get_config
|
||||
|
||||
AC = namedtuple('AC', ['osc', 'change'])
|
||||
|
||||
|
||||
def acosc(candles: np.ndarray, sequential=False) -> AC:
|
||||
def acosc(candles: np.ndarray, sequential: bool = False) -> AC:
|
||||
"""
|
||||
Acceleration / Deceleration Oscillator (AC)
|
||||
|
||||
@@ -15,8 +17,9 @@ def acosc(candles: np.ndarray, sequential=False) -> AC:
|
||||
|
||||
:return: AC(osc, change)
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
med = talib.MEDPRICE(candles[:, 3], candles[:, 4])
|
||||
ao = talib.SMA(med, 5) - talib.SMA(med, 34)
|
||||
|
||||
@@ -3,8 +3,10 @@ from typing import Union
|
||||
import numpy as np
|
||||
import talib
|
||||
|
||||
from jesse.helpers import get_config
|
||||
|
||||
def ad(candles: np.ndarray, sequential=False) -> Union[float, np.ndarray]:
|
||||
|
||||
def ad(candles: np.ndarray, sequential: bool = False) -> Union[float, np.ndarray]:
|
||||
"""
|
||||
AD - Chaikin A/D Line
|
||||
|
||||
@@ -13,8 +15,9 @@ def ad(candles: np.ndarray, sequential=False) -> Union[float, np.ndarray]:
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
res = talib.AD(candles[:, 3], candles[:, 4], candles[:, 2], candles[:, 5])
|
||||
|
||||
|
||||
@@ -3,23 +3,27 @@ from typing import Union
|
||||
import numpy as np
|
||||
import talib
|
||||
|
||||
from jesse.helpers import get_config
|
||||
|
||||
def adosc(candles: np.ndarray, fastperiod=3, slowperiod=10, sequential=False) -> Union[float, np.ndarray]:
|
||||
|
||||
def adosc(candles: np.ndarray, fast_period: int = 3, slow_period: int = 10, sequential: bool = False) -> Union[
|
||||
float, np.ndarray]:
|
||||
"""
|
||||
ADOSC - Chaikin A/D Oscillator
|
||||
|
||||
:param candles: np.ndarray
|
||||
:param fastperiod: int - default: 3
|
||||
:param slowperiod: int - default: 10
|
||||
:param fast_period: int - default: 3
|
||||
:param slow_period: int - default: 10
|
||||
:param sequential: bool - default=False
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
res = talib.ADOSC(candles[:, 3], candles[:, 4], candles[:, 2], candles[:, 5], fastperiod=fastperiod,
|
||||
slowperiod=slowperiod)
|
||||
res = talib.ADOSC(candles[:, 3], candles[:, 4], candles[:, 2], candles[:, 5], fastperiod=fast_period,
|
||||
slowperiod=slow_period)
|
||||
|
||||
if sequential:
|
||||
return res
|
||||
|
||||
@@ -3,8 +3,10 @@ from typing import Union
|
||||
import numpy as np
|
||||
import talib
|
||||
|
||||
from jesse.helpers import get_config
|
||||
|
||||
def adx(candles: np.ndarray, period=14, sequential=False) -> Union[float, np.ndarray]:
|
||||
|
||||
def adx(candles: np.ndarray, period: int = 14, sequential: bool = False) -> Union[float, np.ndarray]:
|
||||
"""
|
||||
ADX - Average Directional Movement Index
|
||||
|
||||
@@ -14,8 +16,9 @@ def adx(candles: np.ndarray, period=14, sequential=False) -> Union[float, np.nda
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
res = talib.ADX(candles[:, 3], candles[:, 4], candles[:, 2], timeperiod=period)
|
||||
|
||||
|
||||
@@ -3,8 +3,10 @@ from typing import Union
|
||||
import numpy as np
|
||||
import talib
|
||||
|
||||
from jesse.helpers import get_config
|
||||
|
||||
def adxr(candles: np.ndarray, period=14, sequential=False) -> Union[float, np.ndarray]:
|
||||
|
||||
def adxr(candles: np.ndarray, period: int = 14, sequential: bool = False) -> Union[float, np.ndarray]:
|
||||
"""
|
||||
ADXR - Average Directional Movement Index Rating
|
||||
|
||||
@@ -14,8 +16,9 @@ def adxr(candles: np.ndarray, period=14, sequential=False) -> Union[float, np.nd
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
res = talib.ADXR(candles[:, 3], candles[:, 4], candles[:, 2], timeperiod=period)
|
||||
|
||||
|
||||
@@ -3,11 +3,12 @@ from collections import namedtuple
|
||||
import numpy as np
|
||||
|
||||
from jesse.helpers import get_candle_source, np_shift
|
||||
from jesse.helpers import get_config
|
||||
|
||||
AG = namedtuple('AG', ['jaw', 'teeth', 'lips'])
|
||||
|
||||
|
||||
def alligator(candles: np.ndarray, source_type="close", sequential=False) -> AG:
|
||||
def alligator(candles: np.ndarray, source_type: str = "close", sequential: bool = False) -> AG:
|
||||
"""
|
||||
Alligator
|
||||
|
||||
@@ -17,8 +18,9 @@ def alligator(candles: np.ndarray, source_type="close", sequential=False) -> AG:
|
||||
|
||||
:return: AG(jaw, teeth, lips)
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
source = get_candle_source(candles, source_type=source_type)
|
||||
|
||||
@@ -32,7 +34,7 @@ def alligator(candles: np.ndarray, source_type="close", sequential=False) -> AG:
|
||||
return AG(jaw[-1], teeth[-1], lips[-1])
|
||||
|
||||
|
||||
def numpy_ewma(data, window):
|
||||
def numpy_ewma(data: np.ndarray, window: int):
|
||||
"""
|
||||
|
||||
:param data:
|
||||
|
||||
@@ -3,10 +3,12 @@ from collections import namedtuple
|
||||
import numpy as np
|
||||
import talib
|
||||
|
||||
from jesse.helpers import get_config
|
||||
|
||||
AO = namedtuple('AO', ['osc', 'change'])
|
||||
|
||||
|
||||
def ao(candles: np.ndarray, sequential=False) -> AO:
|
||||
def ao(candles: np.ndarray, sequential: bool = False) -> AO:
|
||||
"""
|
||||
Awesome Oscillator
|
||||
|
||||
@@ -15,8 +17,9 @@ def ao(candles: np.ndarray, sequential=False) -> AO:
|
||||
|
||||
:return: AO(osc, change)
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
med = talib.MEDPRICE(candles[:, 3], candles[:, 4])
|
||||
res = talib.SMA(med, 5) - talib.SMA(med, 34)
|
||||
|
||||
@@ -4,27 +4,29 @@ import numpy as np
|
||||
import talib
|
||||
|
||||
from jesse.helpers import get_candle_source
|
||||
from jesse.helpers import get_config
|
||||
|
||||
|
||||
def apo(candles: np.ndarray, fastperiod=12, slowperiod=26, matype=0, source_type="close", sequential=False) -> Union[
|
||||
float, np.ndarray]:
|
||||
def apo(candles: np.ndarray, fast_period: int = 12, slow_period: int = 26, matype: int = 0, source_type: str = "close",
|
||||
sequential: bool = False) -> Union[float, np.ndarray]:
|
||||
"""
|
||||
APO - Absolute Price Oscillator
|
||||
|
||||
:param candles: np.ndarray
|
||||
:param fastperiod: int - default: 12
|
||||
:param slowperiod: int - default: 26
|
||||
:param fast_period: int - default: 12
|
||||
:param slow_period: int - default: 26
|
||||
:param matype: int - default: 0
|
||||
:param source_type: str - default: "close"
|
||||
:param sequential: bool - default=False
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
source = get_candle_source(candles, source_type=source_type)
|
||||
|
||||
res = talib.APO(source, fastperiod=fastperiod, slowperiod=slowperiod, matype=matype)
|
||||
res = talib.APO(source, fastperiod=fast_period, slowperiod=slow_period, matype=matype)
|
||||
|
||||
return res if sequential else res[-1]
|
||||
|
||||
@@ -3,10 +3,12 @@ from collections import namedtuple
|
||||
import numpy as np
|
||||
import talib
|
||||
|
||||
from jesse.helpers import get_config
|
||||
|
||||
AROON = namedtuple('AROON', ['down', 'up'])
|
||||
|
||||
|
||||
def aroon(candles: np.ndarray, period=14, sequential=False) -> AROON:
|
||||
def aroon(candles: np.ndarray, period: int = 14, sequential: bool = False) -> AROON:
|
||||
"""
|
||||
AROON - Aroon
|
||||
|
||||
@@ -16,8 +18,9 @@ def aroon(candles: np.ndarray, period=14, sequential=False) -> AROON:
|
||||
|
||||
:return: AROON(down, up)
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
aroondown, aroonup = talib.AROON(candles[:, 3], candles[:, 4], timeperiod=period)
|
||||
|
||||
|
||||
@@ -3,8 +3,10 @@ from typing import Union
|
||||
import numpy as np
|
||||
import talib
|
||||
|
||||
from jesse.helpers import get_config
|
||||
|
||||
def aroonosc(candles: np.ndarray, period=14, sequential=False) -> Union[float, np.ndarray]:
|
||||
|
||||
def aroonosc(candles: np.ndarray, period: int = 14, sequential: bool = False) -> Union[float, np.ndarray]:
|
||||
"""
|
||||
AROONOSC - Aroon Oscillator
|
||||
|
||||
@@ -14,8 +16,9 @@ def aroonosc(candles: np.ndarray, period=14, sequential=False) -> Union[float, n
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
res = talib.AROONOSC(candles[:, 3], candles[:, 4], timeperiod=period)
|
||||
|
||||
|
||||
@@ -3,8 +3,10 @@ from typing import Union
|
||||
import numpy as np
|
||||
import talib
|
||||
|
||||
from jesse.helpers import get_config
|
||||
|
||||
def atr(candles: np.ndarray, period=14, sequential=False) -> Union[float, np.ndarray]:
|
||||
|
||||
def atr(candles: np.ndarray, period: int = 14, sequential: bool = False) -> Union[float, np.ndarray]:
|
||||
"""
|
||||
ATR - Average True Range
|
||||
|
||||
@@ -14,8 +16,9 @@ def atr(candles: np.ndarray, period=14, sequential=False) -> Union[float, np.nda
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
res = talib.ATR(candles[:, 3], candles[:, 4], candles[:, 2], timeperiod=period)
|
||||
|
||||
|
||||
@@ -3,8 +3,10 @@ from typing import Union
|
||||
import numpy as np
|
||||
import talib
|
||||
|
||||
from jesse.helpers import get_config
|
||||
|
||||
def avgprice(candles: np.ndarray, sequential=False) -> Union[float, np.ndarray]:
|
||||
|
||||
def avgprice(candles: np.ndarray, sequential: bool = False) -> Union[float, np.ndarray]:
|
||||
"""
|
||||
AVGPRICE - Average Price
|
||||
|
||||
@@ -13,8 +15,9 @@ def avgprice(candles: np.ndarray, sequential=False) -> Union[float, np.ndarray]:
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
res = talib.AVGPRICE(candles[:, 1], candles[:, 3], candles[:, 4], candles[:, 2])
|
||||
|
||||
|
||||
@@ -3,8 +3,10 @@ from typing import Union
|
||||
import numpy as np
|
||||
import talib
|
||||
|
||||
from jesse.helpers import get_config
|
||||
|
||||
def beta(candles: np.ndarray, period=5, sequential=False) -> Union[float, np.ndarray]:
|
||||
|
||||
def beta(candles: np.ndarray, period: int = 5, sequential: bool = False) -> Union[float, np.ndarray]:
|
||||
"""
|
||||
BETA - Beta
|
||||
|
||||
@@ -14,8 +16,9 @@ def beta(candles: np.ndarray, period=5, sequential=False) -> Union[float, np.nda
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
res = talib.BETA(candles[:, 3], candles[:, 4], timeperiod=period)
|
||||
|
||||
|
||||
@@ -4,12 +4,14 @@ import numpy as np
|
||||
import talib
|
||||
|
||||
from jesse.helpers import get_candle_source
|
||||
from jesse.helpers import get_config
|
||||
|
||||
BollingerBands = namedtuple('BollingerBands', ['upperband', 'middleband', 'lowerband'])
|
||||
|
||||
|
||||
def bollinger_bands(candles: np.ndarray, period=20, devup=2, devdn=2, matype=0, source_type="close",
|
||||
sequential=False) -> BollingerBands:
|
||||
def bollinger_bands(candles: np.ndarray, period: int = 20, devup: float = 2, devdn: float = 2, matype: int = 0,
|
||||
source_type: str = "close",
|
||||
sequential: bool = False) -> BollingerBands:
|
||||
"""
|
||||
BBANDS - Bollinger Bands
|
||||
|
||||
@@ -23,8 +25,9 @@ def bollinger_bands(candles: np.ndarray, period=20, devup=2, devdn=2, matype=0,
|
||||
|
||||
:return: BollingerBands(upperband, middleband, lowerband)
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
source = get_candle_source(candles, source_type=source_type)
|
||||
upperbands, middlebands, lowerbands = talib.BBANDS(source, timeperiod=period, nbdevup=devup, nbdevdn=devdn,
|
||||
|
||||
@@ -4,10 +4,12 @@ import numpy as np
|
||||
import talib
|
||||
|
||||
from jesse.helpers import get_candle_source
|
||||
from jesse.helpers import get_config
|
||||
|
||||
|
||||
def bollinger_bands_width(candles: np.ndarray, period=20, devup=2, devdn=2, matype=0, source_type="close",
|
||||
sequential=False) -> Union[float, np.ndarray]:
|
||||
def bollinger_bands_width(candles: np.ndarray, period: int = 20, devup: float = 2, devdn: float = 2, matype: int = 0,
|
||||
source_type: str = "close",
|
||||
sequential: bool = False) -> Union[float, np.ndarray]:
|
||||
"""
|
||||
BBW - Bollinger Bands Width - Bollinger Bands Bandwidth
|
||||
|
||||
@@ -21,8 +23,9 @@ def bollinger_bands_width(candles: np.ndarray, period=20, devup=2, devdn=2, maty
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
source = get_candle_source(candles, source_type=source_type)
|
||||
upperbands, middlebands, lowerbands = talib.BBANDS(source, timeperiod=period, nbdevup=devup, nbdevdn=devdn,
|
||||
|
||||
@@ -3,8 +3,10 @@ from typing import Union
|
||||
import numpy as np
|
||||
import talib
|
||||
|
||||
from jesse.helpers import get_config
|
||||
|
||||
def bop(candles: np.ndarray, sequential=False) -> Union[float, np.ndarray]:
|
||||
|
||||
def bop(candles: np.ndarray, sequential: bool = False) -> Union[float, np.ndarray]:
|
||||
"""
|
||||
BOP - Balance Of Power
|
||||
|
||||
@@ -13,8 +15,9 @@ def bop(candles: np.ndarray, sequential=False) -> Union[float, np.ndarray]:
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
res = talib.BOP(candles[:, 1], candles[:, 3], candles[:, 4], candles[:, 2])
|
||||
|
||||
|
||||
@@ -4,9 +4,12 @@ import numpy as np
|
||||
import talib
|
||||
|
||||
from jesse.helpers import get_candle_source
|
||||
from jesse.helpers import get_config
|
||||
|
||||
|
||||
def cc(candles: np.ndarray, wma_period=10, roc_short_period=11, roc_long_period=14, source_type="close", sequential=False) -> Union[float, np.ndarray]:
|
||||
def cc(candles: np.ndarray, wma_period: int = 10, roc_short_period: int = 11, roc_long_period: int = 14,
|
||||
source_type: str = "close",
|
||||
sequential: bool = False) -> Union[float, np.ndarray]:
|
||||
"""
|
||||
CC - Coppock Curve
|
||||
|
||||
@@ -19,10 +22,12 @@ def cc(candles: np.ndarray, wma_period=10, roc_short_period=11, roc_long_period=
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
source = get_candle_source(candles, source_type=source_type)
|
||||
res = talib.WMA(talib.ROC(source, timeperiod=roc_long_period) + talib.ROC(source, timeperiod=roc_short_period), timeperiod=wma_period)
|
||||
res = talib.WMA(talib.ROC(source, timeperiod=roc_long_period) + talib.ROC(source, timeperiod=roc_short_period),
|
||||
timeperiod=wma_period)
|
||||
|
||||
return res if sequential else res[-1]
|
||||
|
||||
@@ -3,8 +3,10 @@ from typing import Union
|
||||
import numpy as np
|
||||
import talib
|
||||
|
||||
from jesse.helpers import get_config
|
||||
|
||||
def cci(candles: np.ndarray, period=14, sequential=False) -> Union[float, np.ndarray]:
|
||||
|
||||
def cci(candles: np.ndarray, period: int = 14, sequential: bool = False) -> Union[float, np.ndarray]:
|
||||
"""
|
||||
CCI - Commodity Channel Index
|
||||
|
||||
@@ -14,8 +16,9 @@ def cci(candles: np.ndarray, period=14, sequential=False) -> Union[float, np.nda
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
res = talib.CCI(candles[:, 3], candles[:, 4], candles[:, 2], timeperiod=period)
|
||||
|
||||
|
||||
35
jesse/indicators/cfo.py
Normal file
35
jesse/indicators/cfo.py
Normal file
@@ -0,0 +1,35 @@
|
||||
from typing import Union
|
||||
|
||||
import numpy as np
|
||||
import talib
|
||||
|
||||
from jesse.helpers import get_candle_source
|
||||
from jesse.helpers import get_config
|
||||
|
||||
|
||||
def cfo(candles: np.ndarray, period: int = 14, scalar: float = 100, source_type: str = "close",
|
||||
sequential: bool = False) -> Union[
|
||||
float, np.ndarray]:
|
||||
"""
|
||||
CFO - Chande Forcast Oscillator
|
||||
|
||||
:param candles: np.ndarray
|
||||
:param period: int - default=14
|
||||
:param source_type: str - default: "close"
|
||||
:param sequential: bool - default=False
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
source = get_candle_source(candles, source_type=source_type)
|
||||
|
||||
cfo = scalar * (source - talib.LINEARREG(source, timeperiod=period))
|
||||
cfo /= source
|
||||
|
||||
if sequential:
|
||||
return cfo
|
||||
else:
|
||||
return None if np.isnan(cfo[-1]) else cfo[-1]
|
||||
46
jesse/indicators/cg.py
Normal file
46
jesse/indicators/cg.py
Normal file
@@ -0,0 +1,46 @@
|
||||
from typing import Union
|
||||
|
||||
import numpy as np
|
||||
from numba import njit
|
||||
|
||||
from jesse.helpers import get_candle_source
|
||||
from jesse.helpers import get_config
|
||||
|
||||
|
||||
def cg(candles: np.ndarray, period: int = 10, source_type: str = "close", sequential: bool = False) -> Union[
|
||||
float, np.ndarray]:
|
||||
"""
|
||||
Center of Gravity (CG)
|
||||
|
||||
:param candles: np.ndarray
|
||||
:param period: int - default: 10
|
||||
:param source_type: str - default: "close"
|
||||
:param sequential: bool - default=False
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
source = get_candle_source(candles, source_type=source_type)
|
||||
res = go_fast(source, period)
|
||||
|
||||
return np.concatenate((np.full((candles.shape[0] - res.shape[0]), np.nan), res), axis=0) if sequential else res[-1]
|
||||
|
||||
|
||||
@njit
|
||||
def go_fast(source, period): # Function is compiled to machine code when called the first time
|
||||
res = np.full_like(source, fill_value=np.nan)
|
||||
for i in range(0, len(source)):
|
||||
if i > period:
|
||||
num = 0
|
||||
denom = 0
|
||||
for count in range(0, period - 1):
|
||||
close = source[i - count]
|
||||
if not np.isnan(close):
|
||||
num = num + (1 + count) * close
|
||||
denom = denom + close
|
||||
result = -num / denom if denom != 0 else 0
|
||||
res[i] = result
|
||||
return res
|
||||
@@ -4,8 +4,11 @@ import numpy as np
|
||||
import talib
|
||||
from scipy.ndimage.filters import maximum_filter1d, minimum_filter1d
|
||||
|
||||
from jesse.helpers import get_config
|
||||
|
||||
def chande(candles: np.ndarray, period=22, mult=3.0, direction="long", sequential=False) -> Union[float, np.ndarray]:
|
||||
|
||||
def chande(candles: np.ndarray, period: int = 22, mult: float = 3.0, direction: str = "long",
|
||||
sequential: bool = False) -> Union[float, np.ndarray]:
|
||||
"""
|
||||
Chandelier Exits
|
||||
|
||||
@@ -17,8 +20,9 @@ def chande(candles: np.ndarray, period=22, mult=3.0, direction="long", sequentia
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
candles_close = candles[:, 2]
|
||||
candles_high = candles[:, 3]
|
||||
@@ -38,7 +42,7 @@ def chande(candles: np.ndarray, period=22, mult=3.0, direction="long", sequentia
|
||||
return result if sequential else result[-1]
|
||||
|
||||
|
||||
def filter1d_same(a, W, type, fillna=np.nan):
|
||||
def filter1d_same(a: np.ndarray, W: int, type: str, fillna=np.nan):
|
||||
out_dtype = np.full(0, fillna).dtype
|
||||
hW = (W - 1) // 2 # Half window size
|
||||
if type == 'max':
|
||||
|
||||
@@ -4,9 +4,11 @@ import numpy as np
|
||||
import talib
|
||||
|
||||
from jesse.helpers import get_candle_source
|
||||
from jesse.helpers import get_config
|
||||
|
||||
|
||||
def cmo(candles: np.ndarray, period=14, source_type="close", sequential=False) -> Union[float, np.ndarray]:
|
||||
def cmo(candles: np.ndarray, period: int = 14, source_type: str = "close", sequential: bool = False) -> Union[
|
||||
float, np.ndarray]:
|
||||
"""
|
||||
CMO - Chande Momentum Oscillator
|
||||
|
||||
@@ -17,8 +19,9 @@ def cmo(candles: np.ndarray, period=14, source_type="close", sequential=False) -
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
source = get_candle_source(candles, source_type=source_type)
|
||||
res = talib.CMO(source, timeperiod=period)
|
||||
|
||||
@@ -3,8 +3,10 @@ from typing import Union
|
||||
import numpy as np
|
||||
import talib
|
||||
|
||||
from jesse.helpers import get_config
|
||||
|
||||
def correl(candles: np.ndarray, period=5, sequential=False) -> Union[float, np.ndarray]:
|
||||
|
||||
def correl(candles: np.ndarray, period: int = 5, sequential: bool = False) -> Union[float, np.ndarray]:
|
||||
"""
|
||||
CORREL - Pearson's Correlation Coefficient (r)
|
||||
|
||||
@@ -14,8 +16,9 @@ def correl(candles: np.ndarray, period=5, sequential=False) -> Union[float, np.n
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
res = talib.CORREL(candles[:, 3], candles[:, 4], timeperiod=period)
|
||||
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import math
|
||||
from collections import namedtuple
|
||||
|
||||
import numpy as np
|
||||
from numba import njit
|
||||
|
||||
from jesse.helpers import get_candle_source, np_shift
|
||||
from jesse.helpers import get_config
|
||||
|
||||
CC = namedtuple('CC', ['real', 'imag', 'angle', 'state'])
|
||||
|
||||
|
||||
def correlation_cycle(candles: np.ndarray, period=20, threshold=9, source_type="close", sequential=False) -> CC:
|
||||
def correlation_cycle(candles: np.ndarray, period: int = 20, threshold: int = 9, source_type: str = "close",
|
||||
sequential: bool = False) -> CC:
|
||||
"""
|
||||
"Correlation Cycle, Correlation Angle, Market State - John Ehlers
|
||||
|
||||
@@ -20,13 +22,30 @@ def correlation_cycle(candles: np.ndarray, period=20, threshold=9, source_type="
|
||||
|
||||
:return: CC(real, imag)
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
source = get_candle_source(candles, source_type=source_type)
|
||||
|
||||
realPart, imagPart, angle = go_fast(source, period, threshold)
|
||||
|
||||
priorAngle = np_shift(angle, 1, fill_value=np.nan)
|
||||
angle = np.where(np.logical_and(priorAngle > angle, priorAngle - angle < 270.0), priorAngle, angle)
|
||||
|
||||
# Market State Function
|
||||
state = np.where(np.abs(angle - priorAngle) < threshold, np.where(angle >= 0.0, 1, np.where(angle < 0.0, -1, 0)), 0)
|
||||
|
||||
if sequential:
|
||||
return CC(realPart, imagPart, angle, state)
|
||||
else:
|
||||
return CC(realPart[-1], imagPart[-1], angle[-1], state[-1])
|
||||
|
||||
|
||||
@njit
|
||||
def go_fast(source, period, threshold): # Function is compiled to machine code when called the first time
|
||||
# Correlation Cycle Function
|
||||
PIx2 = 4.0 * math.asin(1.0)
|
||||
PIx2 = 4.0 * np.arcsin(1.0)
|
||||
period = max(2, period)
|
||||
|
||||
realPart = np.full_like(source, np.nan)
|
||||
@@ -75,16 +94,9 @@ def correlation_cycle(candles: np.ndarray, period=20, threshold=9, source_type="
|
||||
imagPart[i] = (period * Ixy - Ix * Iy) / np.sqrt(temp_1 * temp_2)
|
||||
|
||||
# Correlation Angle Phasor
|
||||
HALF_OF_PI = math.asin(1.0)
|
||||
HALF_OF_PI = np.arcsin(1.0)
|
||||
angle = np.where(imagPart == 0, 0.0, np.degrees(np.arctan(realPart / imagPart) + HALF_OF_PI))
|
||||
angle = np.where(imagPart > 0.0, angle - 180.0, angle)
|
||||
priorAngle = np_shift(angle, 1, fill_value=np.nan)
|
||||
angle = np.where(np.logical_and(priorAngle > angle, priorAngle - angle < 270.0), priorAngle, angle)
|
||||
|
||||
# Market State Function
|
||||
state = np.where(np.abs(angle - priorAngle) < threshold, np.where(angle >= 0.0, 1, np.where(angle < 0.0, -1, 0)), 0)
|
||||
|
||||
if sequential:
|
||||
return CC(realPart, imagPart, angle, state)
|
||||
else:
|
||||
return CC(realPart[-1], imagPart[-1], angle[-1], state[-1])
|
||||
return realPart, imagPart, angle
|
||||
@@ -3,8 +3,10 @@ from typing import Union
|
||||
import numpy as np
|
||||
import tulipy as ti
|
||||
|
||||
from jesse.helpers import get_config
|
||||
|
||||
def cvi(candles: np.ndarray, period=5, sequential=False) -> Union[float, np.ndarray]:
|
||||
|
||||
def cvi(candles: np.ndarray, period: int = 5, sequential: bool = False) -> Union[float, np.ndarray]:
|
||||
"""
|
||||
CVI - Chaikins Volatility
|
||||
|
||||
@@ -14,8 +16,9 @@ def cvi(candles: np.ndarray, period=5, sequential=False) -> Union[float, np.ndar
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
res = ti.cvi(np.ascontiguousarray(candles[:, 3]), np.ascontiguousarray(candles[:, 4]), period=period)
|
||||
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
import talib
|
||||
from collections import namedtuple
|
||||
|
||||
import numpy as np
|
||||
import talib
|
||||
from numba import njit
|
||||
|
||||
from jesse.helpers import get_candle_source
|
||||
from jesse.helpers import get_config
|
||||
|
||||
DamianiVolatmeter = namedtuple('DamianiVolatmeter', ['vol', 'anti' ])
|
||||
DamianiVolatmeter = namedtuple('DamianiVolatmeter', ['vol', 'anti'])
|
||||
|
||||
|
||||
def damiani_volatmeter(candles: np.ndarray, vis_atr=13, vis_std=20, sed_atr=40, sed_std=100, threshold=1.4, source_type="close", sequential=False) -> DamianiVolatmeter:
|
||||
def damiani_volatmeter(candles: np.ndarray, vis_atr: int = 13, vis_std: int = 20, sed_atr: int = 40, sed_std: int = 100,
|
||||
threshold: float = 1.4, source_type: str = "close",
|
||||
sequential: bool = False) -> DamianiVolatmeter:
|
||||
"""
|
||||
Damiani Volatmeter
|
||||
|
||||
@@ -24,26 +28,33 @@ def damiani_volatmeter(candles: np.ndarray, vis_atr=13, vis_std=20, sed_atr=40,
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
source = get_candle_source(candles, source_type=source_type)
|
||||
|
||||
lag_s = 0.5
|
||||
|
||||
vol = np.full_like(source, 0)
|
||||
t = np.full_like(source, 0)
|
||||
|
||||
atrvis = talib.ATR(candles[:, 3], candles[:, 4], candles[:, 2], timeperiod=vis_atr)
|
||||
atrsed = talib.ATR(candles[:, 3], candles[:, 4], candles[:, 2], timeperiod=sed_atr)
|
||||
|
||||
for i in range(source.shape[0]):
|
||||
if not (i < sed_std):
|
||||
vol[i]=atrvis[i] / atrsed[i] + lag_s * (vol[i - 1] - vol[i - 3])
|
||||
anti_thres = np.std(source[i - vis_std:i]) / np.std(source[i - sed_std:i])
|
||||
t[i] = threshold - anti_thres
|
||||
vol, t = damiani_volatmeter_fast(source, sed_std, atrvis, atrsed, vis_std, threshold)
|
||||
|
||||
if sequential:
|
||||
return DamianiVolatmeter(vol, t)
|
||||
else:
|
||||
return DamianiVolatmeter(vol[-1], t[-1])
|
||||
|
||||
|
||||
@njit
|
||||
def damiani_volatmeter_fast(source, sed_std, atrvis, atrsed, vis_std,
|
||||
threshold): # Function is compiled to machine code when called the first time
|
||||
lag_s = 0.5
|
||||
|
||||
vol = np.full_like(source, 0)
|
||||
t = np.full_like(source, 0)
|
||||
for i in range(source.shape[0]):
|
||||
if not (i < sed_std):
|
||||
vol[i] = atrvis[i] / atrsed[i] + lag_s * (vol[i - 1] - vol[i - 3])
|
||||
anti_thres = np.std(source[i - vis_std:i]) / np.std(source[i - sed_std:i])
|
||||
t[i] = threshold - anti_thres
|
||||
return vol, t
|
||||
|
||||
@@ -3,9 +3,12 @@ from typing import Union
|
||||
import numpy as np
|
||||
|
||||
from jesse.helpers import get_candle_source
|
||||
from jesse.helpers import get_config
|
||||
from .high_pass_2_pole import high_pass_2_pole_fast
|
||||
|
||||
|
||||
def dec_osc(candles: np.ndarray, hp_period=125, k=1, source_type="close", sequential=False) -> Union[float, np.ndarray]:
|
||||
def dec_osc(candles: np.ndarray, hp_period: int = 125, k: float = 1, source_type: str = "close",
|
||||
sequential: bool = False) -> Union[float, np.ndarray]:
|
||||
"""
|
||||
Ehlers Decycler Oscillator
|
||||
|
||||
@@ -16,30 +19,17 @@ def dec_osc(candles: np.ndarray, hp_period=125, k=1, source_type="close", sequen
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
source = get_candle_source(candles, source_type=source_type)
|
||||
alphaArg1 = 2 * np.pi * 0.707 / hp_period
|
||||
alpha1 = (np.cos(alphaArg1) + np.sin(alphaArg1) - 1) / np.cos(alphaArg1)
|
||||
coeff1 = np.array([(1 - alpha1 / 2) ** 2, 2 * (1 - alpha1), -(1 - alpha1) ** 2])
|
||||
hp1 = np.copy(source)
|
||||
|
||||
alphaArg2 = 2 * np.pi * 0.707 / (0.5 * hp_period)
|
||||
alpha2 = (np.cos(alphaArg2) + np.sin(alphaArg2) - 1) / np.cos(alphaArg2)
|
||||
coeff2 = np.array([(1 - alpha2 / 2) ** 2, 2 * (1 - alpha2), -(1 - alpha2) ** 2])
|
||||
hp2 = np.copy(source)
|
||||
hp = high_pass_2_pole_fast(source, hp_period)
|
||||
dec = source - hp
|
||||
decosc = high_pass_2_pole_fast(dec, 0.5 * hp_period)
|
||||
|
||||
for i in range(source.shape[0]):
|
||||
val1 = np.array([source[i] - 2 * source[i - 1] + source[i - 2], hp1[i - 1], hp1[i - 2]])
|
||||
hp1[i] = np.matmul(coeff1, val1)
|
||||
|
||||
val2 = np.array(
|
||||
[(source[i] - hp1[i]) - 2 * (source[i - 1] - hp1[i - 1]) + (source[i - 2] - hp1[i - 2]), hp2[i - 1],
|
||||
hp2[i - 2]])
|
||||
hp2[i] = np.matmul(coeff2, val2)
|
||||
|
||||
res = 100 * k * hp2 / source
|
||||
res = 100 * k * decosc / source
|
||||
|
||||
if sequential:
|
||||
return res
|
||||
|
||||
@@ -3,9 +3,12 @@ from typing import Union
|
||||
import numpy as np
|
||||
|
||||
from jesse.helpers import get_candle_source
|
||||
from jesse.helpers import get_config
|
||||
from .high_pass_2_pole import high_pass_2_pole_fast
|
||||
|
||||
|
||||
def decycler(candles: np.ndarray, hp_period=125, source_type="close", sequential=False) -> Union[float, np.ndarray]:
|
||||
def decycler(candles: np.ndarray, hp_period: int = 125, source_type: str = "close", sequential: bool = False) -> Union[
|
||||
float, np.ndarray]:
|
||||
"""
|
||||
Ehlers Simple Decycler
|
||||
|
||||
@@ -15,20 +18,13 @@ def decycler(candles: np.ndarray, hp_period=125, source_type="close", sequential
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
source = get_candle_source(candles, source_type=source_type)
|
||||
alphaArg1 = 2 * np.pi * 0.707 / hp_period
|
||||
alpha1 = (np.cos(alphaArg1) + np.sin(alphaArg1) - 1) / np.cos(alphaArg1)
|
||||
coeff1 = np.array([(1 - alpha1 / 2) ** 2, 2 * (1 - alpha1), -(1 - alpha1) ** 2])
|
||||
hp1 = np.copy(source)
|
||||
|
||||
for i in range(source.shape[0]):
|
||||
val1 = np.array([source[i] - 2 * source[i - 1] + source[i - 2], hp1[i - 1], hp1[i - 2]])
|
||||
hp1[i] = np.matmul(coeff1, val1)
|
||||
|
||||
res = source - hp1
|
||||
hp = high_pass_2_pole_fast(source, hp_period)
|
||||
res = source - hp
|
||||
|
||||
if sequential:
|
||||
return res
|
||||
|
||||
@@ -4,9 +4,11 @@ import numpy as np
|
||||
import talib
|
||||
|
||||
from jesse.helpers import get_candle_source
|
||||
from jesse.helpers import get_config
|
||||
|
||||
|
||||
def dema(candles: np.ndarray, period=30, source_type="close", sequential=False) -> Union[float, np.ndarray]:
|
||||
def dema(candles: np.ndarray, period: int = 30, source_type: str = "close", sequential: bool = False) -> Union[
|
||||
float, np.ndarray]:
|
||||
"""
|
||||
DEMA - Double Exponential Moving Average
|
||||
|
||||
@@ -17,8 +19,9 @@ def dema(candles: np.ndarray, period=30, source_type="close", sequential=False)
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
source = get_candle_source(candles, source_type=source_type)
|
||||
res = talib.DEMA(source, timeperiod=period)
|
||||
|
||||
39
jesse/indicators/devstop.py
Normal file
39
jesse/indicators/devstop.py
Normal file
@@ -0,0 +1,39 @@
|
||||
from typing import Union
|
||||
|
||||
import numpy as np
|
||||
import talib
|
||||
|
||||
from jesse.helpers import get_config
|
||||
|
||||
def devstop(candles: np.ndarray, period:int=20, mult: float = 0, direction: str = "long", sequential: bool = False) -> Union[
|
||||
float, np.ndarray]:
|
||||
"""
|
||||
Kase Dev Stops
|
||||
|
||||
:param candles: np.ndarray
|
||||
:param period: int - default=20
|
||||
:param mult: float - default=0
|
||||
:param direction: str - default=long
|
||||
:param sequential: bool - default=False
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
high = candles[:, 3]
|
||||
low = candles[:, 4]
|
||||
|
||||
AVTR = talib.SMA(talib.MAX(high, 2) - talib.MIN(low, 2), period)
|
||||
SD = talib.STDDEV(talib.MAX(high, 2) - talib.MIN(low, 2), period)
|
||||
|
||||
if direction == "long":
|
||||
res = talib.MAX(high - AVTR - mult * SD, period)
|
||||
else:
|
||||
res = talib.MIN(low + AVTR + mult * SD, period)
|
||||
|
||||
if sequential:
|
||||
return res
|
||||
else:
|
||||
return None if np.isnan(res[-1]) else res[-1]
|
||||
@@ -3,10 +3,12 @@ from collections import namedtuple
|
||||
import numpy as np
|
||||
import talib
|
||||
|
||||
from jesse.helpers import get_config
|
||||
|
||||
DI = namedtuple('DI', ['plus', 'minus'])
|
||||
|
||||
|
||||
def di(candles: np.ndarray, period=14, sequential=False) -> DI:
|
||||
def di(candles: np.ndarray, period: int = 14, sequential: bool = False) -> DI:
|
||||
"""
|
||||
DI - Directional Indicator
|
||||
|
||||
@@ -16,8 +18,9 @@ def di(candles: np.ndarray, period=14, sequential=False) -> DI:
|
||||
|
||||
:return: DI(plus, minus)
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
MINUS_DI = talib.MINUS_DI(candles[:, 3], candles[:, 4], candles[:, 2], timeperiod=period)
|
||||
PLUS_DI = talib.PLUS_DI(candles[:, 3], candles[:, 4], candles[:, 2], timeperiod=period)
|
||||
|
||||
@@ -3,10 +3,12 @@ from collections import namedtuple
|
||||
import numpy as np
|
||||
import talib
|
||||
|
||||
from jesse.helpers import get_config
|
||||
|
||||
DM = namedtuple('DM', ['plus', 'minus'])
|
||||
|
||||
|
||||
def dm(candles: np.ndarray, period=14, sequential=False) -> DM:
|
||||
def dm(candles: np.ndarray, period: int = 14, sequential: bool = False) -> DM:
|
||||
"""
|
||||
DM - Directional Movement
|
||||
|
||||
@@ -16,11 +18,12 @@ def dm(candles: np.ndarray, period=14, sequential=False) -> DM:
|
||||
|
||||
:return: DM(plus, minus)
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
MINUS_DI = talib.MINUS_DM(candles[:, 3], candles[:, 4], timeperiod=period)
|
||||
PLUS_DI = talib.PLUS_DM(candles[:, 3], candles[:, 4], timeperiod=period)
|
||||
MINUS_DI = talib.MINUS_DM(candles[:, 3], candles[:, 4], timeperiod=period)
|
||||
PLUS_DI = talib.PLUS_DM(candles[:, 3], candles[:, 4], timeperiod=period)
|
||||
|
||||
if sequential:
|
||||
return DM(PLUS_DI, MINUS_DI)
|
||||
|
||||
@@ -3,10 +3,12 @@ from collections import namedtuple
|
||||
import numpy as np
|
||||
import talib
|
||||
|
||||
from jesse.helpers import get_config
|
||||
|
||||
DonchianChannel = namedtuple('DonchianChannel', ['upperband', 'middleband', 'lowerband'])
|
||||
|
||||
|
||||
def donchian(candles: np.ndarray, period=20, sequential=False) -> DonchianChannel:
|
||||
def donchian(candles: np.ndarray, period: int = 20, sequential: bool = False) -> DonchianChannel:
|
||||
"""
|
||||
Donchian Channels
|
||||
|
||||
@@ -16,8 +18,9 @@ def donchian(candles: np.ndarray, period=20, sequential=False) -> DonchianChanne
|
||||
|
||||
:return: DonchianChannel(upperband, middleband, lowerband)
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
UC = talib.MAX(candles[:, 3], timeperiod=period)
|
||||
LC = talib.MIN(candles[:, 4], timeperiod=period)
|
||||
|
||||
@@ -4,9 +4,11 @@ import numpy as np
|
||||
import tulipy as ti
|
||||
|
||||
from jesse.helpers import get_candle_source
|
||||
from jesse.helpers import get_config
|
||||
|
||||
|
||||
def dpo(candles: np.ndarray, period=5, source_type="close", sequential=False) -> Union[float, np.ndarray]:
|
||||
def dpo(candles: np.ndarray, period: int = 5, source_type: str = "close", sequential: bool = False) -> Union[
|
||||
float, np.ndarray]:
|
||||
"""
|
||||
DPO - Detrended Price Oscillator
|
||||
|
||||
@@ -17,8 +19,9 @@ def dpo(candles: np.ndarray, period=5, source_type="close", sequential=False) ->
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
source = get_candle_source(candles, source_type=source_type)
|
||||
res = ti.dpo(np.ascontiguousarray(source), period=period)
|
||||
|
||||
@@ -4,9 +4,11 @@ import numpy as np
|
||||
import talib
|
||||
|
||||
import jesse.helpers as jh
|
||||
from jesse.helpers import get_config
|
||||
|
||||
|
||||
def dti(candles: np.ndarray, r=14, s=10, u=5, sequential=False) -> Union[float, np.ndarray]:
|
||||
def dti(candles: np.ndarray, r: int = 14, s: int = 10, u: int = 5, sequential: bool = False) -> Union[
|
||||
float, np.ndarray]:
|
||||
"""
|
||||
DTI by William Blau
|
||||
|
||||
@@ -18,8 +20,9 @@ def dti(candles: np.ndarray, r=14, s=10, u=5, sequential=False) -> Union[float,
|
||||
|
||||
:return: float
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
high = candles[:, 3]
|
||||
low = candles[:, 4]
|
||||
|
||||
@@ -3,9 +3,10 @@ from typing import Union
|
||||
import numpy as np
|
||||
import talib
|
||||
|
||||
from jesse.helpers import get_config
|
||||
|
||||
|
||||
def dx(candles: np.ndarray, period=14, sequential=False) -> Union[float, np.ndarray]:
|
||||
def dx(candles: np.ndarray, period: int = 14, sequential: bool = False) -> Union[float, np.ndarray]:
|
||||
"""
|
||||
DX - Directional Movement Index
|
||||
|
||||
@@ -15,8 +16,9 @@ def dx(candles: np.ndarray, period=14, sequential=False) -> Union[float, np.ndar
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
res = talib.DX(candles[:, 3], candles[:, 4], candles[:, 2], timeperiod=period)
|
||||
|
||||
|
||||
@@ -2,11 +2,14 @@ from typing import Union
|
||||
|
||||
import numpy as np
|
||||
import talib
|
||||
from numba import njit
|
||||
|
||||
from jesse.helpers import get_candle_source
|
||||
from jesse.helpers import get_config
|
||||
|
||||
|
||||
def efi(candles: np.ndarray, period=13, source_type="close", sequential=False) -> Union[float, np.ndarray]:
|
||||
def efi(candles: np.ndarray, period: int = 13, source_type: str = "close", sequential: bool = False) -> Union[
|
||||
float, np.ndarray]:
|
||||
"""
|
||||
EFI - Elders Force Index
|
||||
|
||||
@@ -17,16 +20,23 @@ def efi(candles: np.ndarray, period=13, source_type="close", sequential=False) -
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
source = get_candle_source(candles, source_type=source_type)
|
||||
|
||||
dif = np.zeros(len(source) - 1)
|
||||
for i in range(1, len(source)):
|
||||
dif[i - 1] = (source[i] - source[i - 1]) * candles[:, 5][i]
|
||||
dif = efi_fast(source, candles[:, 5])
|
||||
|
||||
res = talib.EMA(dif, timeperiod=period)
|
||||
res_with_nan = np.concatenate((np.full((candles.shape[0] - res.shape[0]), np.nan), res))
|
||||
|
||||
return res_with_nan if sequential else res_with_nan[-1]
|
||||
|
||||
|
||||
@njit
|
||||
def efi_fast(source, volume):
|
||||
dif = np.zeros(len(source) - 1)
|
||||
for i in range(1, len(source)):
|
||||
dif[i - 1] = (source[i] - source[i - 1]) * volume[i]
|
||||
return dif
|
||||
|
||||
@@ -4,9 +4,11 @@ import numpy as np
|
||||
import talib
|
||||
|
||||
from jesse.helpers import get_candle_source
|
||||
from jesse.helpers import get_config
|
||||
|
||||
|
||||
def ema(candles: np.ndarray, period=5, source_type="close", sequential=False) -> Union[float, np.ndarray]:
|
||||
def ema(candles: np.ndarray, period: int = 5, source_type: str = "close", sequential: bool = False) -> Union[
|
||||
float, np.ndarray]:
|
||||
"""
|
||||
EMA - Exponential Moving Average
|
||||
|
||||
@@ -17,8 +19,9 @@ def ema(candles: np.ndarray, period=5, source_type="close", sequential=False) ->
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
source = get_candle_source(candles, source_type=source_type)
|
||||
res = talib.EMA(source, timeperiod=period)
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import math
|
||||
from collections import namedtuple
|
||||
|
||||
import numpy as np
|
||||
import talib
|
||||
from numba import njit
|
||||
|
||||
from jesse.helpers import get_config
|
||||
|
||||
EMD = namedtuple('EMD', ['upperband', 'middleband', 'lowerband'])
|
||||
|
||||
|
||||
def emd(candles: np.ndarray, period=20, delta=0.5, fraction=0.1, sequential=False) -> EMD:
|
||||
def emd(candles: np.ndarray, period: int = 20, delta=0.5, fraction=0.1, sequential: bool = False) -> EMD:
|
||||
"""
|
||||
Empirical Mode Decomposition by John F. Ehlers and Ric Way
|
||||
|
||||
@@ -19,14 +21,32 @@ def emd(candles: np.ndarray, period=20, delta=0.5, fraction=0.1, sequential=Fals
|
||||
|
||||
:return: EMD(upperband, middleband, lowerband)
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
price = (candles[:, 3] + candles[:, 4]) / 2
|
||||
|
||||
bp = bp_fast(price, period, delta)
|
||||
|
||||
mean = talib.SMA(bp, timeperiod=2 * period)
|
||||
peak, valley = peak_valley_fast(bp, price)
|
||||
|
||||
avg_peak = fraction * talib.SMA(peak, timeperiod=50)
|
||||
avg_valley = fraction * talib.SMA(valley, timeperiod=50)
|
||||
|
||||
if sequential:
|
||||
return EMD(avg_peak, mean, avg_valley)
|
||||
else:
|
||||
return EMD(avg_peak[-1], mean[-1], avg_valley[-1])
|
||||
|
||||
|
||||
@njit
|
||||
def bp_fast(price, period, delta):
|
||||
# bandpass filter
|
||||
beta = math.cos(2 * math.pi / period)
|
||||
gamma = 1 / math.cos(4 * math.pi * delta / period)
|
||||
alpha = gamma - math.sqrt(gamma * gamma - 1)
|
||||
beta = np.cos(2 * np.pi / period)
|
||||
gamma = 1 / np.cos(4 * np.pi * delta / period)
|
||||
alpha = gamma - np.sqrt(gamma * gamma - 1)
|
||||
bp = np.zeros_like(price)
|
||||
|
||||
for i in range(price.shape[0]):
|
||||
@@ -34,9 +54,11 @@ def emd(candles: np.ndarray, period=20, delta=0.5, fraction=0.1, sequential=Fals
|
||||
bp[i] = 0.5 * (1 - alpha) * (price[i] - price[i - 2]) + beta * (1 + alpha) * bp[i - 1] - alpha * bp[i - 2]
|
||||
else:
|
||||
bp[i] = 0.5 * (1 - alpha) * (price[i] - price[i - 2])
|
||||
return bp
|
||||
|
||||
mean = talib.SMA(bp, timeperiod=2 * period)
|
||||
|
||||
@njit
|
||||
def peak_valley_fast(bp, price):
|
||||
peak = np.copy(bp)
|
||||
valley = np.copy(bp)
|
||||
|
||||
@@ -49,10 +71,4 @@ def emd(candles: np.ndarray, period=20, delta=0.5, fraction=0.1, sequential=Fals
|
||||
if bp[i - 1] < bp[i] and bp[i - 1] < bp[i - 2]:
|
||||
valley[i] = bp[i - 1]
|
||||
|
||||
avg_peak = fraction * talib.SMA(peak, timeperiod=50)
|
||||
avg_valley = fraction * talib.SMA(valley, timeperiod=50)
|
||||
|
||||
if sequential:
|
||||
return EMD(avg_peak, mean, avg_valley)
|
||||
else:
|
||||
return EMD(avg_peak[-1], mean[-1], avg_valley[-1])
|
||||
return peak, valley
|
||||
|
||||
@@ -3,9 +3,10 @@ from typing import Union
|
||||
import numpy as np
|
||||
import tulipy as ti
|
||||
|
||||
from jesse.helpers import get_config
|
||||
|
||||
|
||||
def emv(candles: np.ndarray, sequential=False) -> Union[float, np.ndarray]:
|
||||
def emv(candles: np.ndarray, sequential: bool = False) -> Union[float, np.ndarray]:
|
||||
"""
|
||||
EMV - Ease of Movement
|
||||
|
||||
@@ -14,9 +15,11 @@ def emv(candles: np.ndarray, sequential=False) -> Union[float, np.ndarray]:
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
res = ti.emv(np.ascontiguousarray(candles[:, 3]), np.ascontiguousarray(candles[:, 4]), np.ascontiguousarray(candles[:, 5]))
|
||||
res = ti.emv(np.ascontiguousarray(candles[:, 3]), np.ascontiguousarray(candles[:, 4]),
|
||||
np.ascontiguousarray(candles[:, 5]))
|
||||
|
||||
return np.concatenate((np.full((candles.shape[0] - res.shape[0]), np.nan), res), axis=0) if sequential else res[-1]
|
||||
|
||||
35
jesse/indicators/er.py
Normal file
35
jesse/indicators/er.py
Normal file
@@ -0,0 +1,35 @@
|
||||
from typing import Union
|
||||
|
||||
import numpy as np
|
||||
from numpy.lib.stride_tricks import sliding_window_view
|
||||
|
||||
from jesse.helpers import get_candle_source
|
||||
from jesse.helpers import get_config
|
||||
|
||||
|
||||
def er(candles: np.ndarray, period: int = 5, source_type: str = "close", sequential: bool = False) -> Union[
|
||||
float, np.ndarray]:
|
||||
"""
|
||||
ER - The Kaufman Efficiency indicator
|
||||
|
||||
:param candles: np.ndarray
|
||||
:param period: int - default: 5
|
||||
:param source_type: str - default: "close"
|
||||
:param sequential: bool - default=False
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
source = get_candle_source(candles, source_type=source_type)
|
||||
|
||||
change = np.abs(np.diff(source, period))
|
||||
abs_dif = np.abs(np.diff(source))
|
||||
swv = sliding_window_view(abs_dif, window_shape=period)
|
||||
volatility = swv.sum()
|
||||
|
||||
res = change / volatility
|
||||
|
||||
return np.concatenate((np.full((candles.shape[0] - res.shape[0]), np.nan), res), axis=0) if sequential else res[-1]
|
||||
@@ -3,10 +3,12 @@ from collections import namedtuple
|
||||
import numpy as np
|
||||
import tulipy as ti
|
||||
|
||||
from jesse.helpers import get_config
|
||||
|
||||
FisherTransform = namedtuple('FisherTransform', ['fisher', 'signal'])
|
||||
|
||||
|
||||
def fisher(candles: np.ndarray, period=9, sequential=False) -> FisherTransform:
|
||||
def fisher(candles: np.ndarray, period: int = 9, sequential: bool = False) -> FisherTransform:
|
||||
"""
|
||||
The Fisher Transform helps identify price reversals.
|
||||
|
||||
@@ -16,8 +18,9 @@ def fisher(candles: np.ndarray, period=9, sequential=False) -> FisherTransform:
|
||||
|
||||
:return: FisherTransform(fisher, signal)
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
fisher, fisher_signal = ti.fisher(np.ascontiguousarray(candles[:, 3]), np.ascontiguousarray(candles[:, 4]),
|
||||
period=period)
|
||||
|
||||
@@ -4,9 +4,11 @@ import numpy as np
|
||||
import tulipy as ti
|
||||
|
||||
from jesse.helpers import get_candle_source
|
||||
from jesse.helpers import get_config
|
||||
|
||||
|
||||
def fosc(candles: np.ndarray, period=5, source_type="close", sequential=False) -> Union[float, np.ndarray]:
|
||||
def fosc(candles: np.ndarray, period: int = 5, source_type: str = "close", sequential: bool = False) -> Union[
|
||||
float, np.ndarray]:
|
||||
"""
|
||||
FOSC - Forecast Oscillator
|
||||
|
||||
@@ -17,8 +19,9 @@ def fosc(candles: np.ndarray, period=5, source_type="close", sequential=False) -
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
source = get_candle_source(candles, source_type=source_type)
|
||||
res = ti.fosc(np.ascontiguousarray(source), period=period)
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import math
|
||||
from typing import Union
|
||||
|
||||
import numpy as np
|
||||
from numba import njit
|
||||
|
||||
from jesse.helpers import get_config
|
||||
|
||||
|
||||
def frama(candles: np.ndarray, window=10, FC=1, SC=300, sequential=False) -> Union[float, np.ndarray]:
|
||||
def frama(candles: np.ndarray, window: int = 10, FC: int = 1, SC: int = 300, sequential: bool = False) -> Union[
|
||||
float, np.ndarray]:
|
||||
"""
|
||||
Fractal Adaptive Moving Average (FRAMA)
|
||||
|
||||
@@ -16,9 +19,9 @@ def frama(candles: np.ndarray, window=10, FC=1, SC=300, sequential=False) -> Uni
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
|
||||
if len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
n = window
|
||||
|
||||
@@ -27,7 +30,17 @@ def frama(candles: np.ndarray, window=10, FC=1, SC=300, sequential=False) -> Uni
|
||||
print("FRAMA n must be even. Adding one")
|
||||
n += 1
|
||||
|
||||
w = math.log(2.0 / (SC + 1))
|
||||
frama = frame_fast(candles, n, SC, FC)
|
||||
|
||||
if sequential:
|
||||
return frama
|
||||
else:
|
||||
return frama[-1]
|
||||
|
||||
|
||||
@njit
|
||||
def frame_fast(candles, n, SC, FC):
|
||||
w = np.log(2.0 / (SC + 1))
|
||||
|
||||
D = np.zeros(len(candles))
|
||||
D[:n] = np.NaN
|
||||
@@ -38,24 +51,22 @@ def frama(candles: np.ndarray, window=10, FC=1, SC=300, sequential=False) -> Uni
|
||||
for i in range(n, len(candles)):
|
||||
per = candles[i - n:i]
|
||||
|
||||
# take 2 batches of the input
|
||||
split = np.split(per, 2)
|
||||
v1 = split[0]
|
||||
v2 = split[1]
|
||||
v1 = per[len(per)//2:]
|
||||
v2 = per[:len(per)//2]
|
||||
|
||||
N1 = (np.max(v1[:, 3]) - np.min(v1[:, 4])) / (n / 2)
|
||||
N2 = (np.max(v2[:, 3]) - np.min(v2[:, 4])) / (n / 2)
|
||||
N3 = (np.max(per[:, 3]) - np.min(per[:, 4])) / n
|
||||
N1 = (max(v1[:, 3]) - min(v1[:, 4])) / (n / 2)
|
||||
N2 = (max(v2[:, 3]) - min(v2[:, 4])) / (n / 2)
|
||||
N3 = (max(per[:, 3]) - min(per[:, 4])) / n
|
||||
|
||||
if N1 > 0 and N2 > 0 and N3 > 0:
|
||||
D[i] = (math.log(N1 + N2) - math.log(N3)) / math.log(2)
|
||||
D[i] = (np.log(N1 + N2) - np.log(N3)) / np.log(2)
|
||||
else:
|
||||
D[i] = D[i - 1]
|
||||
|
||||
oldalpha = math.exp(w * (D[i] - 1))
|
||||
oldalpha = np.exp(w * (D[i] - 1))
|
||||
# keep btwn 1 & 0.01
|
||||
oldalpha = np.max([oldalpha, 0.1])
|
||||
oldalpha = np.min([oldalpha, 1])
|
||||
oldalpha = max([oldalpha, 0.1])
|
||||
oldalpha = min([oldalpha, 1])
|
||||
|
||||
oldN = (2 - oldalpha) / oldalpha
|
||||
N = ((SC - FC) * ((oldN - 1) / (SC - 1))) + FC
|
||||
@@ -73,8 +84,4 @@ def frama(candles: np.ndarray, window=10, FC=1, SC=300, sequential=False) -> Uni
|
||||
|
||||
for i in range(n, len(frama)):
|
||||
frama[i] = (alphas[i] * candles[:, 2][i]) + (1 - alphas[i]) * frama[i - 1]
|
||||
|
||||
if sequential:
|
||||
return frama
|
||||
else:
|
||||
return frama[-1]
|
||||
return frama
|
||||
|
||||
52
jesse/indicators/fwma.py
Normal file
52
jesse/indicators/fwma.py
Normal file
@@ -0,0 +1,52 @@
|
||||
from math import fabs
|
||||
from typing import Union
|
||||
|
||||
import numpy as np
|
||||
from numpy.lib.stride_tricks import sliding_window_view
|
||||
|
||||
from jesse.helpers import get_candle_source
|
||||
from jesse.helpers import get_config
|
||||
|
||||
|
||||
def fwma(candles: np.ndarray, period: int = 5, source_type: str = "close", sequential: bool = False) -> Union[
|
||||
float, np.ndarray]:
|
||||
"""
|
||||
Fibonacci's Weighted Moving Average (FWMA)
|
||||
|
||||
:param candles: np.ndarray
|
||||
:param period: int - default: 5
|
||||
:param source_type: str - default: "close"
|
||||
:param sequential: bool - default=False
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
source = get_candle_source(candles, source_type=source_type)
|
||||
fibs = fibonacci(n=period)
|
||||
swv = sliding_window_view(source, window_shape=period)
|
||||
res = np.average(swv, weights=fibs, axis=-1)
|
||||
|
||||
return np.concatenate((np.full((candles.shape[0] - res.shape[0]), np.nan), res), axis=0) if sequential else res[-1]
|
||||
|
||||
|
||||
def fibonacci(n: int = 2) -> np.ndarray:
|
||||
"""Fibonacci Sequence as a numpy array"""
|
||||
n = int(fabs(n)) if n >= 0 else 2
|
||||
|
||||
n -= 1
|
||||
a, b = 1, 1
|
||||
|
||||
result = np.array([a])
|
||||
|
||||
for i in range(0, n):
|
||||
a, b = b, a + b
|
||||
result = np.append(result, a)
|
||||
|
||||
fib_sum = np.sum(result)
|
||||
if fib_sum > 0:
|
||||
return result / fib_sum
|
||||
else:
|
||||
return result
|
||||
@@ -4,11 +4,12 @@ import numpy as np
|
||||
import talib
|
||||
|
||||
from jesse.helpers import get_candle_source, np_shift
|
||||
from jesse.helpers import get_config
|
||||
|
||||
GATOR = namedtuple('GATOR', ['upper', 'lower', 'upper_change', 'lower_change'])
|
||||
|
||||
|
||||
def gatorosc(candles: np.ndarray, source_type="close", sequential=False) -> GATOR:
|
||||
def gatorosc(candles: np.ndarray, source_type: str = "close", sequential: bool = False) -> GATOR:
|
||||
"""
|
||||
Gator Oscillator by Bill M. Williams
|
||||
|
||||
@@ -19,8 +20,9 @@ def gatorosc(candles: np.ndarray, source_type="close", sequential=False) -> GATO
|
||||
:return: GATOR(upper, lower, upper_change, lower_change)
|
||||
"""
|
||||
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
source = get_candle_source(candles, source_type=source_type)
|
||||
|
||||
@@ -39,6 +41,7 @@ def gatorosc(candles: np.ndarray, source_type="close", sequential=False) -> GATO
|
||||
else:
|
||||
return GATOR(upper[-1], lower[-1], upper_change[-1], lower_change[-1])
|
||||
|
||||
|
||||
def numpy_ewma(data, window):
|
||||
"""
|
||||
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import math
|
||||
from typing import Union
|
||||
|
||||
import numpy as np
|
||||
from numba import njit
|
||||
|
||||
from jesse.helpers import get_candle_source
|
||||
from jesse.helpers import get_config
|
||||
|
||||
|
||||
def gauss(candles: np.ndarray, period=14, poles=4, source_type="close", sequential=False) -> Union[float, np.ndarray]:
|
||||
def gauss(candles: np.ndarray, period: int = 14, poles: int = 4, source_type: str = "close",
|
||||
sequential: bool = False) -> Union[float, np.ndarray]:
|
||||
"""
|
||||
Gaussian Filter
|
||||
|
||||
@@ -19,16 +21,32 @@ def gauss(candles: np.ndarray, period=14, poles=4, source_type="close", sequenti
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
source = get_candle_source(candles, source_type=source_type)
|
||||
fil, to_fill = gauss_fast(source, period, poles)
|
||||
|
||||
if to_fill != 0:
|
||||
res = np.insert(fil[poles:], 0, np.repeat(np.nan, to_fill))
|
||||
else:
|
||||
res = fil[poles:]
|
||||
|
||||
if sequential:
|
||||
return res
|
||||
else:
|
||||
return None if np.isnan(res[-1]) else res[-1]
|
||||
|
||||
|
||||
@njit
|
||||
def gauss_fast(source, period, poles):
|
||||
N = len(source)
|
||||
source = source[~np.isnan(source)]
|
||||
to_fill = N - len(source)
|
||||
PI = math.pi
|
||||
beta = (1 - math.cos(2 * PI / period)) / (math.pow(2, 1 / poles) - 1)
|
||||
alpha = -beta + math.sqrt(math.pow(beta, 2) + 2 * beta)
|
||||
PI = np.pi
|
||||
beta = (1 - np.cos(2 * PI / period)) / (np.power(2, 1 / poles) - 1)
|
||||
alpha = -beta + np.sqrt(np.power(beta, 2) + 2 * beta)
|
||||
|
||||
fil = np.zeros(poles + len(source))
|
||||
if poles == 1:
|
||||
@@ -52,12 +70,4 @@ def gauss(candles: np.ndarray, period=14, poles=4, source_type="close", sequenti
|
||||
|
||||
fil[poles + i] = np.dot(coeff, val)
|
||||
|
||||
if to_fill != 0:
|
||||
res = np.insert(fil[poles:], 0, np.repeat(np.nan, to_fill))
|
||||
else:
|
||||
res = fil[poles:]
|
||||
|
||||
if sequential:
|
||||
return res
|
||||
else:
|
||||
return None if np.isnan(res[-1]) else res[-1]
|
||||
return fil, to_fill
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import math
|
||||
from typing import Union
|
||||
|
||||
import numpy as np
|
||||
from numba import njit
|
||||
|
||||
from jesse.helpers import get_candle_source
|
||||
from jesse.helpers import get_config
|
||||
|
||||
|
||||
|
||||
def high_pass(candles: np.ndarray, period=48, source_type="close", sequential=False) -> Union[float, np.ndarray]:
|
||||
def high_pass(candles: np.ndarray, period: int = 48, source_type: str = "close", sequential: bool = False) -> Union[
|
||||
float, np.ndarray]:
|
||||
"""
|
||||
High Pass Filter indicator by John F. Ehlers
|
||||
(1 pole) high-pass filter indicator by John F. Ehlers
|
||||
|
||||
:param candles: np.ndarray
|
||||
:param period: int - default=48
|
||||
@@ -18,20 +20,26 @@ def high_pass(candles: np.ndarray, period=48, source_type="close", sequential=Fa
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
source = get_candle_source(candles, source_type=source_type)
|
||||
|
||||
hpf = np.full_like(source, 0)
|
||||
|
||||
for i in range(source.shape[0]):
|
||||
if not (i < 2):
|
||||
alpha_arg = 2 * math.pi / (period * 1.414)
|
||||
alpha1 = (math.cos(alpha_arg) + math.sin(alpha_arg) - 1) / math.cos(alpha_arg)
|
||||
hpf[i] = math.pow(1.0 - alpha1 / 2.0, 2) * (source[i] - 2 * source[i - 1] + source[i - 2]) + 2 * (1 - alpha1) * hpf[i - 1] - math.pow(1 - alpha1, 2) * hpf[i - 2]
|
||||
hpf = high_pass_fast(source, period)
|
||||
|
||||
if sequential:
|
||||
return hpf
|
||||
else:
|
||||
return None if np.isnan(hpf[-1]) else hpf[-1]
|
||||
|
||||
|
||||
@njit
|
||||
def high_pass_fast(source, period): # Function is compiled to machine code when called the first time
|
||||
k = 1
|
||||
alpha = 1 + (np.sin(2 * np.pi * k / period) - 1) / np.cos(2 * np.pi * k / period)
|
||||
newseries = np.copy(source)
|
||||
for i in range(1, source.shape[0]):
|
||||
newseries[i] = (1 - alpha / 2) * source[i] - (1 - alpha / 2) * source[i - 1] \
|
||||
+ (1 - alpha) * newseries[i - 1]
|
||||
return newseries
|
||||
|
||||
46
jesse/indicators/high_pass_2_pole.py
Normal file
46
jesse/indicators/high_pass_2_pole.py
Normal file
@@ -0,0 +1,46 @@
|
||||
from typing import Union
|
||||
|
||||
import numpy as np
|
||||
from numba import njit
|
||||
|
||||
from jesse.helpers import get_candle_source
|
||||
from jesse.helpers import get_config
|
||||
|
||||
|
||||
def high_pass_2_pole(candles: np.ndarray, period: int = 48, source_type: str = "close", sequential: bool = False) -> Union[
|
||||
float, np.ndarray]:
|
||||
"""
|
||||
(2 pole) high-pass filter indicator by John F. Ehlers
|
||||
|
||||
:param candles: np.ndarray
|
||||
:param period: int - default=48
|
||||
:param source_type: str - default: "close"
|
||||
:param sequential: bool - default=False
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
source = get_candle_source(candles, source_type=source_type)
|
||||
|
||||
hpf = high_pass_2_pole_fast(source, period)
|
||||
|
||||
if sequential:
|
||||
return hpf
|
||||
else:
|
||||
return None if np.isnan(hpf[-1]) else hpf[-1]
|
||||
|
||||
|
||||
@njit
|
||||
def high_pass_2_pole_fast(source, period, K=0.707): # Function is compiled to machine code when called the first time
|
||||
alpha = 1 + (np.sin(2 * np.pi * K / period) - 1) / np.cos(2 * np.pi * K / period)
|
||||
newseries = np.copy(source)
|
||||
for i in range(2, source.shape[0]):
|
||||
newseries[i] = (1 - alpha / 2) ** 2 * source[i] \
|
||||
- 2 * (1 - alpha / 2) ** 2 * source[i - 1] \
|
||||
+ (1 - alpha / 2) ** 2 * source[i - 2] \
|
||||
+ 2 * (1 - alpha) * newseries[i - 1] - (1 - alpha) ** 2 * newseries[i - 2]
|
||||
return newseries
|
||||
@@ -4,9 +4,11 @@ import numpy as np
|
||||
import tulipy as ti
|
||||
|
||||
from jesse.helpers import get_candle_source
|
||||
from jesse.helpers import get_config
|
||||
|
||||
|
||||
def hma(candles: np.ndarray, period=5, source_type="close", sequential=False) -> Union[float, np.ndarray]:
|
||||
def hma(candles: np.ndarray, period: int = 5, source_type: str = "close", sequential: bool = False) -> Union[
|
||||
float, np.ndarray]:
|
||||
"""
|
||||
Hull Moving Average
|
||||
|
||||
@@ -17,8 +19,9 @@ def hma(candles: np.ndarray, period=5, source_type="close", sequential=False) ->
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
source = get_candle_source(candles, source_type=source_type)
|
||||
res = ti.hma(np.ascontiguousarray(source), period=period)
|
||||
|
||||
@@ -4,9 +4,10 @@ import numpy as np
|
||||
import talib
|
||||
|
||||
from jesse.helpers import get_candle_source
|
||||
from jesse.helpers import get_config
|
||||
|
||||
|
||||
def ht_dcperiod(candles: np.ndarray, source_type="close", sequential=False) -> Union[float, np.ndarray]:
|
||||
def ht_dcperiod(candles: np.ndarray, source_type: str = "close", sequential: bool = False) -> Union[float, np.ndarray]:
|
||||
"""
|
||||
HT_DCPERIOD - Hilbert Transform - Dominant Cycle Period
|
||||
|
||||
@@ -16,8 +17,9 @@ def ht_dcperiod(candles: np.ndarray, source_type="close", sequential=False) -> U
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
source = get_candle_source(candles, source_type=source_type)
|
||||
res = talib.HT_DCPERIOD(source)
|
||||
|
||||
@@ -4,9 +4,10 @@ import numpy as np
|
||||
import talib
|
||||
|
||||
from jesse.helpers import get_candle_source
|
||||
from jesse.helpers import get_config
|
||||
|
||||
|
||||
def ht_dcphase(candles: np.ndarray, source_type="close", sequential=False) -> Union[float, np.ndarray]:
|
||||
def ht_dcphase(candles: np.ndarray, source_type: str = "close", sequential: bool = False) -> Union[float, np.ndarray]:
|
||||
"""
|
||||
HT_DCPHASE - Hilbert Transform - Dominant Cycle Phase
|
||||
|
||||
@@ -16,10 +17,11 @@ def ht_dcphase(candles: np.ndarray, source_type="close", sequential=False) -> Un
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
source = get_candle_source(candles, source_type=source_type)
|
||||
res = talib.HT_DCPHASE (source)
|
||||
res = talib.HT_DCPHASE(source)
|
||||
|
||||
return res if sequential else res[-1]
|
||||
|
||||
@@ -4,10 +4,12 @@ import numpy as np
|
||||
import talib
|
||||
|
||||
from jesse.helpers import get_candle_source
|
||||
from jesse.helpers import get_config
|
||||
|
||||
IQ = namedtuple('IQ', ['inphase', 'quadrature'])
|
||||
|
||||
def ht_phasor(candles: np.ndarray, source_type="close", sequential=False) -> IQ:
|
||||
|
||||
def ht_phasor(candles: np.ndarray, source_type: str = "close", sequential: bool = False) -> IQ:
|
||||
"""
|
||||
HT_PHASOR - Hilbert Transform - Phasor Components
|
||||
|
||||
@@ -17,8 +19,9 @@ def ht_phasor(candles: np.ndarray, source_type="close", sequential=False) -> IQ:
|
||||
|
||||
:return: IQ(inphase, quadrature)
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
source = get_candle_source(candles, source_type=source_type)
|
||||
inphase, quadrature = talib.HT_PHASOR(source)
|
||||
|
||||
@@ -4,10 +4,12 @@ import numpy as np
|
||||
import talib
|
||||
|
||||
from jesse.helpers import get_candle_source
|
||||
from jesse.helpers import get_config
|
||||
|
||||
SINEWAVE = namedtuple('SINEWAVE', ['sine', 'lead'])
|
||||
|
||||
def ht_sine(candles: np.ndarray, source_type="close", sequential=False) -> SINEWAVE:
|
||||
|
||||
def ht_sine(candles: np.ndarray, source_type: str = "close", sequential: bool = False) -> SINEWAVE:
|
||||
"""
|
||||
HT_SINE - Hilbert Transform - SineWave
|
||||
|
||||
@@ -17,8 +19,9 @@ def ht_sine(candles: np.ndarray, source_type="close", sequential=False) -> SINEW
|
||||
|
||||
:return: SINEWAVE(sine, lead)
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
source = get_candle_source(candles, source_type=source_type)
|
||||
sine, leadsine = talib.HT_SINE(source)
|
||||
|
||||
@@ -4,9 +4,10 @@ import numpy as np
|
||||
import talib
|
||||
|
||||
from jesse.helpers import get_candle_source
|
||||
from jesse.helpers import get_config
|
||||
|
||||
|
||||
def ht_trendline(candles: np.ndarray, source_type="close", sequential=False) -> Union[float, np.ndarray]:
|
||||
def ht_trendline(candles: np.ndarray, source_type: str = "close", sequential: bool = False) -> Union[float, np.ndarray]:
|
||||
"""
|
||||
HT_TRENDLINE - Hilbert Transform - Instantaneous Trendline
|
||||
|
||||
@@ -16,8 +17,9 @@ def ht_trendline(candles: np.ndarray, source_type="close", sequential=False) ->
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
source = get_candle_source(candles, source_type=source_type)
|
||||
res = talib.HT_TRENDLINE(source)
|
||||
|
||||
@@ -4,9 +4,10 @@ import numpy as np
|
||||
import talib
|
||||
|
||||
from jesse.helpers import get_candle_source
|
||||
from jesse.helpers import get_config
|
||||
|
||||
|
||||
def ht_trendmode(candles: np.ndarray, source_type="close", sequential=False) -> Union[float, np.ndarray]:
|
||||
def ht_trendmode(candles: np.ndarray, source_type: str = "close", sequential: bool = False) -> Union[float, np.ndarray]:
|
||||
"""
|
||||
HT_TRENDMODE - Hilbert Transform - Trend vs Cycle Mode
|
||||
|
||||
@@ -16,8 +17,9 @@ def ht_trendmode(candles: np.ndarray, source_type="close", sequential=False) ->
|
||||
|
||||
:return: int | np.ndarray
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
source = get_candle_source(candles, source_type=source_type)
|
||||
res = talib.HT_TRENDMODE(source)
|
||||
|
||||
@@ -5,8 +5,8 @@ import numpy as np
|
||||
IchimokuCloud = namedtuple('IchimokuCloud', ['conversion_line', 'base_line', 'span_a', 'span_b'])
|
||||
|
||||
|
||||
def ichimoku_cloud(candles: np.ndarray, conversion_line_period=9, base_line_period=26, lagging_line_period=52,
|
||||
displacement=26) -> IchimokuCloud:
|
||||
def ichimoku_cloud(candles: np.ndarray, conversion_line_period: int = 9, base_line_period: int = 26,
|
||||
lagging_line_period: int = 52, displacement: int = 26) -> IchimokuCloud:
|
||||
"""
|
||||
Ichimoku Cloud
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ from collections import namedtuple
|
||||
import numpy as np
|
||||
import talib
|
||||
|
||||
from jesse.helpers import get_config
|
||||
from jesse.helpers import np_shift
|
||||
|
||||
IchimokuCloud = namedtuple('IchimokuCloud',
|
||||
@@ -10,8 +11,9 @@ IchimokuCloud = namedtuple('IchimokuCloud',
|
||||
'future_span_b'])
|
||||
|
||||
|
||||
def ichimoku_cloud_seq(candles: np.ndarray, conversion_line_period=9, base_line_period=26, lagging_line_period=52,
|
||||
displacement=26, sequential=False) -> IchimokuCloud:
|
||||
def ichimoku_cloud_seq(candles: np.ndarray, conversion_line_period: int = 9, base_line_period: int = 26,
|
||||
lagging_line_period: int = 52, displacement: int = 26,
|
||||
sequential: bool = False) -> IchimokuCloud:
|
||||
"""
|
||||
Ichimoku Cloud
|
||||
|
||||
@@ -28,8 +30,9 @@ def ichimoku_cloud_seq(candles: np.ndarray, conversion_line_period=9, base_line_
|
||||
if len(candles) < lagging_line_period + displacement:
|
||||
raise ValueError("Too few candles available for lagging_line_period + displacement.")
|
||||
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
small_ph = talib.MAX(candles[:, 3], conversion_line_period)
|
||||
small_pl = talib.MIN(candles[:, 4], conversion_line_period)
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
from collections import namedtuple
|
||||
|
||||
import numpy as np
|
||||
from numba import njit
|
||||
|
||||
from jesse.helpers import get_candle_source
|
||||
from jesse.helpers import get_config
|
||||
|
||||
ITREND = namedtuple('ITREND', ['signal', 'it', 'trigger'])
|
||||
|
||||
|
||||
def itrend(candles: np.ndarray, alpha=0.07, source_type="hl2", sequential=False) -> ITREND:
|
||||
def itrend(candles: np.ndarray, alpha: float = 0.07, source_type: str = "hl2", sequential: bool = False) -> ITREND:
|
||||
"""
|
||||
Instantaneous Trendline
|
||||
|
||||
@@ -18,28 +20,34 @@ def itrend(candles: np.ndarray, alpha=0.07, source_type="hl2", sequential=False)
|
||||
|
||||
:return: ITREND(signal, it, trigger)
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
source = get_candle_source(candles, source_type=source_type)
|
||||
|
||||
coeff = np.array(
|
||||
[(alpha - alpha ** 2 / 4), alpha ** 2 / 2, - (alpha - alpha ** 2 * 3 / 4), 2 * (1 - alpha), - (1 - alpha) ** 2])
|
||||
signal, it, trigger = itrend_fast(source, alpha)
|
||||
|
||||
if sequential:
|
||||
return ITREND(signal, it, trigger)
|
||||
else:
|
||||
return ITREND(signal[-1], it[-1], trigger[-1])
|
||||
|
||||
|
||||
@njit
|
||||
def itrend_fast(source, alpha):
|
||||
it = np.copy(source)
|
||||
for i in range(2, 7):
|
||||
it[i] = (source[i] + 2 * source[i - 1] + source[i - 2]) / 4
|
||||
for i in range(7, source.shape[0]):
|
||||
val = np.array([source[i], source[i - 1], source[i - 2], it[i - 1], it[i - 2]])
|
||||
it[i] = np.matmul(coeff, val)
|
||||
it[i] = (alpha - alpha ** 2 / 4) * source[i] \
|
||||
+ alpha ** 2 / 2 * source[i - 1] \
|
||||
- (alpha - alpha ** 2 * 3 / 4) * source[i - 2] \
|
||||
+ 2 * (1 - alpha) * it[i - 1] - (1 - alpha) ** 2 * it[i - 2]
|
||||
|
||||
# compute lead 2 trigger & signal
|
||||
lag2 = np.roll(it, 20)
|
||||
lag2[:20] = it[:20]
|
||||
trigger = 2 * it - lag2
|
||||
signal = (trigger > it) * 1 - (trigger < it) * 1
|
||||
|
||||
if sequential:
|
||||
return ITREND(signal, it, trigger)
|
||||
else:
|
||||
return ITREND(signal[-1], it[-1], trigger[-1])
|
||||
return signal, it, trigger
|
||||
|
||||
@@ -4,9 +4,11 @@ import numpy as np
|
||||
import talib
|
||||
|
||||
from jesse.helpers import get_candle_source
|
||||
from jesse.helpers import get_config
|
||||
|
||||
|
||||
def kama(candles: np.ndarray, period=30, source_type="close", sequential=False) -> Union[float, np.ndarray]:
|
||||
def kama(candles: np.ndarray, period: int = 30, source_type: str = "close", sequential: bool = False) -> Union[
|
||||
float, np.ndarray]:
|
||||
"""
|
||||
KAMA - Kaufman Adaptive Moving Average
|
||||
|
||||
@@ -17,8 +19,9 @@ def kama(candles: np.ndarray, period=30, source_type="close", sequential=False)
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
source = get_candle_source(candles, source_type=source_type)
|
||||
res = talib.KAMA(source, timeperiod=period)
|
||||
|
||||
39
jesse/indicators/kaufmanstop.py
Normal file
39
jesse/indicators/kaufmanstop.py
Normal file
@@ -0,0 +1,39 @@
|
||||
from typing import Union
|
||||
|
||||
import numpy as np
|
||||
import talib
|
||||
|
||||
from jesse.helpers import get_config
|
||||
|
||||
|
||||
def kaufmanstop(candles: np.ndarray, period: int = 22, mult: float = 2, direction: str = "long", sequential: bool = False) -> Union[
|
||||
float, np.ndarray]:
|
||||
"""
|
||||
Perry Kaufman's Stops
|
||||
|
||||
:param candles: np.ndarray
|
||||
:param period: int - default=22
|
||||
:param mult: float - default=2
|
||||
:param direction: str - default=long
|
||||
:param sequential: bool - default=False
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
high = candles[:, 3]
|
||||
low = candles[:, 4]
|
||||
|
||||
hl_diff = talib.SMA(high - low, period)
|
||||
|
||||
if direction == "long":
|
||||
res = hl_diff * mult - low
|
||||
else:
|
||||
res = hl_diff * mult + high
|
||||
|
||||
if sequential:
|
||||
return res
|
||||
else:
|
||||
return None if np.isnan(res[-1]) else res[-1]
|
||||
@@ -4,11 +4,13 @@ import numpy as np
|
||||
import talib
|
||||
|
||||
from jesse.helpers import get_candle_source
|
||||
from jesse.helpers import get_config
|
||||
|
||||
KeltnerChannel = namedtuple('KeltnerChannel', ['upperband', 'middleband', 'lowerband'])
|
||||
|
||||
|
||||
def keltner(candles: np.ndarray, period=20, multiplier=2, matype=1, source_type="close", sequential=False) -> KeltnerChannel:
|
||||
def keltner(candles: np.ndarray, period: int = 20, multiplier: float = 2, matype: int = 1, source_type: str = "close",
|
||||
sequential: bool = False) -> KeltnerChannel:
|
||||
"""
|
||||
Keltner Channels
|
||||
|
||||
@@ -22,8 +24,9 @@ def keltner(candles: np.ndarray, period=20, multiplier=2, matype=1, source_type=
|
||||
:return: KeltnerChannel(upperband, middleband, lowerband)
|
||||
"""
|
||||
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
source = get_candle_source(candles, source_type=source_type)
|
||||
e = talib.MA(source, timeperiod=period, matype=matype)
|
||||
|
||||
@@ -4,11 +4,14 @@ import numpy as np
|
||||
import talib
|
||||
|
||||
from jesse.helpers import get_candle_source
|
||||
from jesse.helpers import get_config
|
||||
|
||||
KST = namedtuple('KST', ['line', 'signal'])
|
||||
|
||||
|
||||
def kst(candles: np.ndarray, sma_period1=10, sma_period2=10, sma_period3=10, sma_period4=15, roc_period1=10, roc_period2=15, roc_period3=20, roc_period4=30, signal_period=9, source_type="close", sequential=False) -> KST:
|
||||
def kst(candles: np.ndarray, sma_period1: int = 10, sma_period2: int = 10, sma_period3: int = 10, sma_period4: int = 15,
|
||||
roc_period1: int = 10, roc_period2: int = 15, roc_period3: int = 20, roc_period4: int = 30,
|
||||
signal_period: int = 9, source_type: str = "close", sequential: bool = False) -> KST:
|
||||
"""
|
||||
Know Sure Thing (KST)
|
||||
|
||||
@@ -27,13 +30,12 @@ def kst(candles: np.ndarray, sma_period1=10, sma_period2=10, sma_period3=10, sma
|
||||
|
||||
:return: KST(line, signal)
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
source = get_candle_source(candles, source_type=source_type)
|
||||
|
||||
|
||||
aroc1 = talib.SMA(talib.ROC(source, timeperiod=roc_period1), sma_period1)
|
||||
aroc2 = talib.SMA(talib.ROC(source, timeperiod=roc_period2), sma_period2)
|
||||
aroc3 = talib.SMA(talib.ROC(source, timeperiod=roc_period3), sma_period3)
|
||||
|
||||
@@ -3,8 +3,11 @@ from typing import Union
|
||||
import numpy as np
|
||||
import tulipy as ti
|
||||
|
||||
from jesse.helpers import get_config
|
||||
|
||||
def kvo(candles: np.ndarray, short_period=2, long_period=5, sequential=False) -> Union[float, np.ndarray]:
|
||||
|
||||
def kvo(candles: np.ndarray, short_period: int = 2, long_period: int = 5, sequential: bool = False) -> Union[
|
||||
float, np.ndarray]:
|
||||
"""
|
||||
KVO - Klinger Volume Oscillator
|
||||
|
||||
@@ -15,9 +18,12 @@ def kvo(candles: np.ndarray, short_period=2, long_period=5, sequential=False) -
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
res = ti.kvo(np.ascontiguousarray(candles[:, 3]), np.ascontiguousarray(candles[:, 4]), np.ascontiguousarray(candles[:, 2]), np.ascontiguousarray(candles[:, 5]), short_period=short_period, long_period=long_period)
|
||||
res = ti.kvo(np.ascontiguousarray(candles[:, 3]), np.ascontiguousarray(candles[:, 4]),
|
||||
np.ascontiguousarray(candles[:, 2]), np.ascontiguousarray(candles[:, 5]), short_period=short_period,
|
||||
long_period=long_period)
|
||||
|
||||
return np.concatenate((np.full((candles.shape[0] - res.shape[0]), np.nan), res), axis=0) if sequential else res[-1]
|
||||
|
||||
@@ -4,9 +4,11 @@ import numpy as np
|
||||
import talib
|
||||
|
||||
from jesse.helpers import get_candle_source
|
||||
from jesse.helpers import get_config
|
||||
|
||||
|
||||
def linearreg(candles: np.ndarray, period=14, source_type="close", sequential=False) -> Union[float, np.ndarray]:
|
||||
def linearreg(candles: np.ndarray, period: int = 14, source_type: str = "close", sequential: bool = False) -> Union[
|
||||
float, np.ndarray]:
|
||||
"""
|
||||
LINEARREG - Linear Regression
|
||||
|
||||
@@ -17,8 +19,9 @@ def linearreg(candles: np.ndarray, period=14, source_type="close", sequential=Fa
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
source = get_candle_source(candles, source_type=source_type)
|
||||
res = talib.LINEARREG(source, timeperiod=period)
|
||||
|
||||
@@ -4,9 +4,11 @@ import numpy as np
|
||||
import talib
|
||||
|
||||
from jesse.helpers import get_candle_source
|
||||
from jesse.helpers import get_config
|
||||
|
||||
|
||||
def linearreg_angle(candles: np.ndarray, period=14, source_type="close", sequential=False) -> Union[float, np.ndarray]:
|
||||
def linearreg_angle(candles: np.ndarray, period: int = 14, source_type: str = "close", sequential: bool = False) -> \
|
||||
Union[float, np.ndarray]:
|
||||
"""
|
||||
LINEARREG_ANGLE - Linear Regression Angle
|
||||
|
||||
@@ -17,8 +19,9 @@ def linearreg_angle(candles: np.ndarray, period=14, source_type="close", sequent
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
source = get_candle_source(candles, source_type=source_type)
|
||||
res = talib.LINEARREG_ANGLE(source, timeperiod=period)
|
||||
|
||||
@@ -4,9 +4,11 @@ import numpy as np
|
||||
import talib
|
||||
|
||||
from jesse.helpers import get_candle_source
|
||||
from jesse.helpers import get_config
|
||||
|
||||
|
||||
def linearreg_intercept(candles: np.ndarray, period=14, source_type="close", sequential=False) -> Union[float, np.ndarray]:
|
||||
def linearreg_intercept(candles: np.ndarray, period: int = 14, source_type: str = "close", sequential: bool = False) -> \
|
||||
Union[float, np.ndarray]:
|
||||
"""
|
||||
LINEARREG_INTERCEPT - Linear Regression Intercept
|
||||
|
||||
@@ -17,8 +19,9 @@ def linearreg_intercept(candles: np.ndarray, period=14, source_type="close", seq
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
source = get_candle_source(candles, source_type=source_type)
|
||||
res = talib.LINEARREG_INTERCEPT(source, timeperiod=period)
|
||||
|
||||
@@ -4,9 +4,11 @@ import numpy as np
|
||||
import talib
|
||||
|
||||
from jesse.helpers import get_candle_source
|
||||
from jesse.helpers import get_config
|
||||
|
||||
|
||||
def linearreg_slope(candles: np.ndarray, period=14, source_type="close", sequential=False) -> Union[float, np.ndarray]:
|
||||
def linearreg_slope(candles: np.ndarray, period: int = 14, source_type: str = "close", sequential: bool = False) -> \
|
||||
Union[float, np.ndarray]:
|
||||
"""
|
||||
LINEARREG_SLOPE - Linear Regression Slope
|
||||
|
||||
@@ -17,8 +19,9 @@ def linearreg_slope(candles: np.ndarray, period=14, source_type="close", sequent
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
source = get_candle_source(candles, source_type=source_type)
|
||||
res = talib.LINEARREG_SLOPE(source, timeperiod=period)
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
from typing import Union
|
||||
|
||||
import numpy as np
|
||||
from numba import njit
|
||||
|
||||
from jesse.helpers import get_config
|
||||
|
||||
|
||||
def lrsi(candles: np.ndarray, alpha=0.2, sequential=False) -> Union[float, np.ndarray]:
|
||||
def lrsi(candles: np.ndarray, alpha: float = 0.2, sequential: bool = False) -> Union[float, np.ndarray]:
|
||||
"""
|
||||
RSI Laguerre Filter
|
||||
|
||||
@@ -13,11 +16,21 @@ def lrsi(candles: np.ndarray, alpha=0.2, sequential=False) -> Union[float, np.nd
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
rsi = lrsi_fast(alpha, candles)
|
||||
|
||||
if sequential:
|
||||
return rsi
|
||||
else:
|
||||
return None if np.isnan(rsi[-1]) else rsi[-1]
|
||||
|
||||
|
||||
@njit
|
||||
def lrsi_fast(alpha, candles):
|
||||
price = (candles[:, 3] + candles[:, 4]) / 2
|
||||
|
||||
l0 = np.copy(price)
|
||||
l1 = np.copy(price)
|
||||
l2 = np.copy(price)
|
||||
@@ -55,7 +68,4 @@ def lrsi(candles: np.ndarray, alpha=0.2, sequential=False) -> Union[float, np.nd
|
||||
else:
|
||||
rsi[i] = cu / (cu + cd)
|
||||
|
||||
if sequential:
|
||||
return rsi
|
||||
else:
|
||||
return None if np.isnan(rsi[-1]) else rsi[-1]
|
||||
return rsi
|
||||
|
||||
@@ -4,17 +4,19 @@ import numpy as np
|
||||
import talib
|
||||
|
||||
from jesse.helpers import get_candle_source
|
||||
from jesse.helpers import get_config
|
||||
|
||||
MACD = namedtuple('MACD', ['macd', 'signal', 'hist'])
|
||||
|
||||
|
||||
def macd(candles: np.ndarray, fastperiod=12, slowperiod=26, signalperiod=9, source_type="close",
|
||||
sequential=False) -> MACD:
|
||||
def macd(candles: np.ndarray, fast_period: int = 12, slow_period: int = 26, signal_period: int = 9,
|
||||
source_type: str = "close",
|
||||
sequential: bool = False) -> MACD:
|
||||
"""
|
||||
MACD - Moving Average Convergence/Divergence
|
||||
|
||||
:param candles: np.ndarray
|
||||
:param fastperiod: int - default: 12
|
||||
:param fast_period: int - default: 12
|
||||
:param slow_period: int - default: 26
|
||||
:param signal_period: int - default: 9
|
||||
:param source_type: str - default: "close"
|
||||
@@ -22,12 +24,13 @@ def macd(candles: np.ndarray, fastperiod=12, slowperiod=26, signalperiod=9, sour
|
||||
|
||||
:return: MACD(macd, signal, hist)
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
source = get_candle_source(candles, source_type=source_type)
|
||||
macd, macdsignal, macdhist = talib.MACD(source, fastperiod=fastperiod, slowperiod=slowperiod,
|
||||
signalperiod=signalperiod)
|
||||
macd, macdsignal, macdhist = talib.MACD(source, fastperiod=fast_period, slowperiod=slow_period,
|
||||
signalperiod=signal_period)
|
||||
|
||||
if sequential:
|
||||
return MACD(macd, macdsignal, macdhist)
|
||||
|
||||
@@ -4,17 +4,19 @@ import numpy as np
|
||||
import talib
|
||||
|
||||
from jesse.helpers import get_candle_source
|
||||
from jesse.helpers import get_config
|
||||
|
||||
MACDEXT = namedtuple('MACDEXT', ['macd', 'signal', 'hist'])
|
||||
|
||||
|
||||
def macdext(candles: np.ndarray, fastperiod=12, fastmatype=0, slowperiod=26, slowmatype=0, signalperiod=9,
|
||||
signalmatype=0, source_type="close", sequential=False) -> MACDEXT:
|
||||
def macdext(candles: np.ndarray, fast_period: int = 12, fast_matype: int = 0, slow_period: int = 26,
|
||||
slow_matype: int = 0, signal_period: int = 9, signal_matype: int = 0, source_type: str = "close",
|
||||
sequential: bool = False) -> MACDEXT:
|
||||
"""
|
||||
MACDEXT - MACD with controllable MA type
|
||||
|
||||
:param candles: np.ndarray
|
||||
:param fastperiod: int - default: 12
|
||||
:param fast_period: int - default: 12
|
||||
:param fastmatype: int - default: 0
|
||||
:param slow_period: int - default: 26
|
||||
:param slowmatype: int - default: 0
|
||||
@@ -25,13 +27,14 @@ def macdext(candles: np.ndarray, fastperiod=12, fastmatype=0, slowperiod=26, slo
|
||||
|
||||
:return: MACDEXT(macd, signal, hist)
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
source = get_candle_source(candles, source_type=source_type)
|
||||
macd, macdsignal, macdhist = talib.MACDEXT(source, fastperiod=fastperiod, fastmatype=fastmatype,
|
||||
slowperiod=slowperiod, slowmatype=slowmatype,
|
||||
signalperiod=signalperiod, signalmatype=signalmatype)
|
||||
macd, macdsignal, macdhist = talib.MACDEXT(source, fastperiod=fast_period, fastmatype=fast_matype,
|
||||
slowperiod=slow_period, slowmatype=slow_matype,
|
||||
signalperiod=signal_period, signalmatype=signal_matype)
|
||||
|
||||
if sequential:
|
||||
return MACDEXT(macd, macdsignal, macdhist)
|
||||
|
||||
@@ -4,11 +4,13 @@ import numpy as np
|
||||
import talib
|
||||
|
||||
from jesse.helpers import get_candle_source
|
||||
from jesse.helpers import get_config
|
||||
|
||||
MAMA = namedtuple('MAMA', ['mama', 'fama'])
|
||||
|
||||
|
||||
def mama(candles: np.ndarray, fastlimit=0.5, slowlimit=0.05, source_type="close", sequential=False) -> MAMA:
|
||||
def mama(candles: np.ndarray, fastlimit: float = 0.5, slowlimit: float = 0.05, source_type: str = "close",
|
||||
sequential: bool = False) -> MAMA:
|
||||
"""
|
||||
MAMA - MESA Adaptive Moving Average
|
||||
|
||||
@@ -20,8 +22,9 @@ def mama(candles: np.ndarray, fastlimit=0.5, slowlimit=0.05, source_type="close"
|
||||
|
||||
:return: MAMA(mama, fama)
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
source = get_candle_source(candles, source_type=source_type)
|
||||
mama, fama = talib.MAMA(source, fastlimit=fastlimit, slowlimit=slowlimit)
|
||||
|
||||
@@ -3,9 +3,10 @@ from typing import Union
|
||||
import numpy as np
|
||||
import tulipy as ti
|
||||
|
||||
from jesse.helpers import get_config
|
||||
|
||||
|
||||
def marketfi(candles: np.ndarray, sequential=False) -> Union[float, np.ndarray]:
|
||||
def marketfi(candles: np.ndarray, sequential: bool = False) -> Union[float, np.ndarray]:
|
||||
"""
|
||||
MARKETFI - Market Facilitation Index
|
||||
|
||||
@@ -14,9 +15,11 @@ def marketfi(candles: np.ndarray, sequential=False) -> Union[float, np.ndarray]:
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
res = ti.marketfi(np.ascontiguousarray(candles[:, 3]), np.ascontiguousarray(candles[:, 4]), np.ascontiguousarray(candles[:, 5]))
|
||||
res = ti.marketfi(np.ascontiguousarray(candles[:, 3]), np.ascontiguousarray(candles[:, 4]),
|
||||
np.ascontiguousarray(candles[:, 5]))
|
||||
|
||||
return np.concatenate((np.full((candles.shape[0] - res.shape[0]), np.nan), res), axis=0) if sequential else res[-1]
|
||||
|
||||
@@ -3,9 +3,10 @@ from typing import Union
|
||||
import numpy as np
|
||||
import tulipy as ti
|
||||
|
||||
from jesse.helpers import get_config
|
||||
|
||||
|
||||
def mass(candles: np.ndarray, period=5, sequential=False) -> Union[float, np.ndarray]:
|
||||
def mass(candles: np.ndarray, period: int = 5, sequential: bool = False) -> Union[float, np.ndarray]:
|
||||
"""
|
||||
MASS - Mass Index
|
||||
|
||||
@@ -15,8 +16,9 @@ def mass(candles: np.ndarray, period=5, sequential=False) -> Union[float, np.nda
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
res = ti.mass(np.ascontiguousarray(candles[:, 3]), np.ascontiguousarray(candles[:, 4]), period=period)
|
||||
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
from typing import Union
|
||||
|
||||
import numpy as np
|
||||
from numba import njit
|
||||
|
||||
from jesse.helpers import get_candle_source
|
||||
from jesse.helpers import get_config
|
||||
|
||||
|
||||
def mcginley_dynamic(candles: np.ndarray, period=10, k=0.6, source_type="close", sequential=False) -> Union[
|
||||
def mcginley_dynamic(candles: np.ndarray, period: int = 10, k: float = 0.6, source_type: str = "close",
|
||||
sequential: bool = False) -> Union[
|
||||
float, np.ndarray]:
|
||||
"""
|
||||
McGinley Dynamic
|
||||
@@ -17,19 +20,27 @@ def mcginley_dynamic(candles: np.ndarray, period=10, k=0.6, source_type="close",
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
source = get_candle_source(candles, source_type=source_type)
|
||||
|
||||
mg = np.full_like(source, np.nan)
|
||||
for i in range(len(source)):
|
||||
if i == 0:
|
||||
mg[i] = source[i]
|
||||
else:
|
||||
mg[i] = mg[i - 1] + ((source[i] - mg[i - 1]) / np.max([(k * period * ((source[i] / mg[i - 1]) ** 4)), 1]))
|
||||
mg = md_fast(source, k, period)
|
||||
|
||||
if sequential:
|
||||
return mg
|
||||
else:
|
||||
return None if np.isnan(mg[-1]) else mg[-1]
|
||||
|
||||
|
||||
@njit
|
||||
def md_fast(source, k, period):
|
||||
mg = np.full_like(source, np.nan)
|
||||
for i in range(len(source)):
|
||||
if i == 0:
|
||||
mg[i] = source[i]
|
||||
else:
|
||||
mg[i] = mg[i - 1] + ((source[i] - mg[i - 1]) / max([(k * period * ((source[i] / mg[i - 1]) ** 4)), 1]))
|
||||
|
||||
return mg
|
||||
|
||||
@@ -3,8 +3,10 @@ from typing import Union
|
||||
import numpy as np
|
||||
import talib
|
||||
|
||||
from jesse.helpers import get_config
|
||||
|
||||
def medprice(candles: np.ndarray, sequential=False) -> Union[float, np.ndarray]:
|
||||
|
||||
def medprice(candles: np.ndarray, sequential: bool = False) -> Union[float, np.ndarray]:
|
||||
"""
|
||||
MEDPRICE - Median Price
|
||||
|
||||
@@ -13,8 +15,9 @@ def medprice(candles: np.ndarray, sequential=False) -> Union[float, np.ndarray]:
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
res = talib.MEDPRICE(candles[:, 3], candles[:, 4])
|
||||
|
||||
|
||||
@@ -3,8 +3,10 @@ from typing import Union
|
||||
import numpy as np
|
||||
import talib
|
||||
|
||||
from jesse.helpers import get_config
|
||||
|
||||
def mfi(candles: np.ndarray, period=14, sequential=False) -> Union[float, np.ndarray]:
|
||||
|
||||
def mfi(candles: np.ndarray, period: int = 14, sequential: bool = False) -> Union[float, np.ndarray]:
|
||||
"""
|
||||
MFI - Money Flow Index
|
||||
|
||||
@@ -14,8 +16,9 @@ def mfi(candles: np.ndarray, period=14, sequential=False) -> Union[float, np.nda
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
res = talib.MFI(candles[:, 3], candles[:, 4], candles[:, 2], candles[:, 5], timeperiod=period)
|
||||
|
||||
|
||||
@@ -4,9 +4,11 @@ import numpy as np
|
||||
import talib
|
||||
|
||||
from jesse.helpers import get_candle_source
|
||||
from jesse.helpers import get_config
|
||||
|
||||
|
||||
def midpoint(candles: np.ndarray, period=14, source_type="close", sequential=False) -> Union[float, np.ndarray]:
|
||||
def midpoint(candles: np.ndarray, period: int = 14, source_type: str = "close", sequential: bool = False) -> Union[
|
||||
float, np.ndarray]:
|
||||
"""
|
||||
MIDPOINT - MidPoint over period
|
||||
|
||||
@@ -17,8 +19,9 @@ def midpoint(candles: np.ndarray, period=14, source_type="close", sequential=Fal
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
source = get_candle_source(candles, source_type=source_type)
|
||||
res = talib.MIDPOINT(source, timeperiod=period)
|
||||
|
||||
@@ -3,8 +3,10 @@ from typing import Union
|
||||
import numpy as np
|
||||
import talib
|
||||
|
||||
from jesse.helpers import get_config
|
||||
|
||||
def midprice(candles: np.ndarray, period=14, sequential=False) -> Union[float, np.ndarray]:
|
||||
|
||||
def midprice(candles: np.ndarray, period: int = 14, sequential: bool = False) -> Union[float, np.ndarray]:
|
||||
"""
|
||||
MIDPRICE - Midpoint Price over period
|
||||
|
||||
@@ -14,8 +16,9 @@ def midprice(candles: np.ndarray, period=14, sequential=False) -> Union[float, n
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
res = talib.MIDPRICE(candles[:, 3], candles[:, 4], timeperiod=period)
|
||||
|
||||
|
||||
@@ -3,10 +3,12 @@ from collections import namedtuple
|
||||
import numpy as np
|
||||
from scipy.signal import argrelextrema
|
||||
|
||||
from jesse.helpers import get_config, np_ffill
|
||||
|
||||
EXTREMA = namedtuple('EXTREMA', ['min', 'max', 'last_min', 'last_max'])
|
||||
|
||||
|
||||
def minmax(candles: np.ndarray, order=3, sequential=False) -> EXTREMA:
|
||||
def minmax(candles: np.ndarray, order: int = 3, sequential: bool = False) -> EXTREMA:
|
||||
"""
|
||||
minmax - Get extrema
|
||||
|
||||
@@ -16,8 +18,9 @@ def minmax(candles: np.ndarray, order=3, sequential=False) -> EXTREMA:
|
||||
|
||||
:return: EXTREMA(min, max, last_min, last_max)
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
low = candles[:, 4]
|
||||
high = candles[:, 3]
|
||||
@@ -41,19 +44,3 @@ def minmax(candles: np.ndarray, order=3, sequential=False) -> EXTREMA:
|
||||
else:
|
||||
return EXTREMA(min[-1], max[-1], last_min[-1], last_max[-1])
|
||||
|
||||
|
||||
def np_ffill(arr, axis=0):
|
||||
"""
|
||||
|
||||
:param arr:
|
||||
:param axis:
|
||||
:return:
|
||||
"""
|
||||
idx_shape = tuple([slice(None)] + [np.newaxis] * (len(arr.shape) - axis - 1))
|
||||
idx = np.where(~np.isnan(arr), np.arange(arr.shape[axis])[idx_shape], 0)
|
||||
np.maximum.accumulate(idx, axis=axis, out=idx)
|
||||
slc = [np.arange(k)[tuple([slice(None) if dim == i else np.newaxis
|
||||
for dim in range(len(arr.shape))])]
|
||||
for i, k in enumerate(arr.shape)]
|
||||
slc[axis] = idx
|
||||
return arr[tuple(slc)]
|
||||
|
||||
@@ -4,9 +4,11 @@ import numpy as np
|
||||
import talib
|
||||
|
||||
from jesse.helpers import get_candle_source
|
||||
from jesse.helpers import get_config
|
||||
|
||||
|
||||
def mom(candles: np.ndarray, period=10, source_type="close", sequential=False) -> Union[float, np.ndarray]:
|
||||
def mom(candles: np.ndarray, period: int = 10, source_type: str = "close", sequential: bool = False) -> Union[
|
||||
float, np.ndarray]:
|
||||
"""
|
||||
MOM - Momentum
|
||||
|
||||
@@ -17,8 +19,9 @@ def mom(candles: np.ndarray, period=10, source_type="close", sequential=False) -
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
source = get_candle_source(candles, source_type=source_type)
|
||||
res = talib.MOM(source, timeperiod=period)
|
||||
|
||||
@@ -4,10 +4,12 @@ import numpy as np
|
||||
import tulipy as ti
|
||||
|
||||
from jesse.helpers import get_candle_source
|
||||
from jesse.helpers import get_config
|
||||
|
||||
MSW = namedtuple('MSW', ['sine', 'lead'])
|
||||
|
||||
def msw(candles: np.ndarray, period=5, source_type="close", sequential=False) -> MSW:
|
||||
|
||||
def msw(candles: np.ndarray, period: int = 5, source_type: str = "close", sequential: bool = False) -> MSW:
|
||||
"""
|
||||
MSW - Mesa Sine Wave
|
||||
|
||||
@@ -18,8 +20,9 @@ def msw(candles: np.ndarray, period=5, source_type="close", sequential=False) ->
|
||||
|
||||
:return: MSW(sine, lead)
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
source = get_candle_source(candles, source_type=source_type)
|
||||
msw_sine, msw_lead = ti.msw(np.ascontiguousarray(source), period=period)
|
||||
@@ -30,4 +33,4 @@ def msw(candles: np.ndarray, period=5, source_type="close", sequential=False) ->
|
||||
if sequential:
|
||||
return MSW(s, l)
|
||||
else:
|
||||
return MSW(s[-1], l[-1])
|
||||
return MSW(s[-1], l[-1])
|
||||
|
||||
@@ -3,8 +3,10 @@ from typing import Union
|
||||
import numpy as np
|
||||
import talib
|
||||
|
||||
from jesse.helpers import get_config
|
||||
|
||||
def natr(candles: np.ndarray, period=14, sequential=False) -> Union[float, np.ndarray]:
|
||||
|
||||
def natr(candles: np.ndarray, period: int = 14, sequential: bool = False) -> Union[float, np.ndarray]:
|
||||
"""
|
||||
NATR - Normalized Average True Range
|
||||
|
||||
@@ -14,8 +16,9 @@ def natr(candles: np.ndarray, period=14, sequential=False) -> Union[float, np.nd
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
res = talib.NATR(candles[:, 3], candles[:, 4], candles[:, 2], timeperiod=period)
|
||||
|
||||
|
||||
@@ -4,9 +4,10 @@ import numpy as np
|
||||
import tulipy as ti
|
||||
|
||||
from jesse.helpers import get_candle_source
|
||||
from jesse.helpers import get_config
|
||||
|
||||
|
||||
def nvi(candles: np.ndarray, source_type="close", sequential=False) -> Union[float, np.ndarray]:
|
||||
def nvi(candles: np.ndarray, source_type: str = "close", sequential: bool = False) -> Union[float, np.ndarray]:
|
||||
"""
|
||||
NVI - Negative Volume Index
|
||||
|
||||
@@ -16,8 +17,9 @@ def nvi(candles: np.ndarray, source_type="close", sequential=False) -> Union[flo
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
source = get_candle_source(candles, source_type=source_type)
|
||||
res = ti.nvi(np.ascontiguousarray(source), np.ascontiguousarray(candles[:, 5]))
|
||||
|
||||
@@ -3,8 +3,10 @@ from typing import Union
|
||||
import numpy as np
|
||||
import talib
|
||||
|
||||
from jesse.helpers import get_config
|
||||
|
||||
def obv(candles: np.ndarray, sequential=False) -> Union[float, np.ndarray]:
|
||||
|
||||
def obv(candles: np.ndarray, sequential: bool = False) -> Union[float, np.ndarray]:
|
||||
"""
|
||||
OBV - On Balance Volume
|
||||
|
||||
@@ -13,8 +15,9 @@ def obv(candles: np.ndarray, sequential=False) -> Union[float, np.ndarray]:
|
||||
|
||||
:return: float | np.ndarray
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
res = talib.OBV(candles[:, 2], candles[:, 5])
|
||||
|
||||
|
||||
@@ -3,8 +3,11 @@ from typing import Union
|
||||
import numpy as np
|
||||
import talib
|
||||
|
||||
from jesse.helpers import get_config
|
||||
|
||||
def pattern_recognition(candles: np.ndarray, pattern_type, penetration=0, sequential=False) -> Union[int, np.ndarray]:
|
||||
|
||||
def pattern_recognition(candles: np.ndarray, pattern_type: str, penetration: int = 0, sequential: bool = False) -> \
|
||||
Union[int, np.ndarray]:
|
||||
"""
|
||||
Pattern Recognition
|
||||
|
||||
@@ -15,8 +18,9 @@ def pattern_recognition(candles: np.ndarray, pattern_type, penetration=0, sequen
|
||||
|
||||
:return: int | np.ndarray
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
if pattern_type == "CDL2CROWS":
|
||||
res = talib.CDL2CROWS(candles[:, 1], candles[:, 3], candles[:, 4], candles[:, 2])
|
||||
|
||||
@@ -2,10 +2,12 @@ from collections import namedtuple
|
||||
|
||||
import numpy as np
|
||||
|
||||
from jesse.helpers import get_config
|
||||
|
||||
PIVOT = namedtuple('PIVOT', ['r4', 'r3', 'r2', 'r1', 'pp', 's1', 's2', 's3', 's4'])
|
||||
|
||||
|
||||
def pivot(candles: np.ndarray, mode=0, sequential=False) -> PIVOT:
|
||||
def pivot(candles: np.ndarray, mode: int = 0, sequential: bool = False) -> PIVOT:
|
||||
"""
|
||||
Pivot Points
|
||||
|
||||
@@ -15,8 +17,9 @@ def pivot(candles: np.ndarray, mode=0, sequential=False) -> PIVOT:
|
||||
|
||||
:return: PIVOT(r4, r3, r2, r1, pp, s1, s2, s3, s4)
|
||||
"""
|
||||
if not sequential and len(candles) > 240:
|
||||
candles = candles[-240:]
|
||||
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
|
||||
if not sequential and len(candles) > warmup_candles_num:
|
||||
candles = candles[-warmup_candles_num:]
|
||||
|
||||
high = candles[:, 3]
|
||||
low = candles[:, 4]
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user