mirror of
https://github.com/freqtrade/freqtrade.git
synced 2024-11-10 10:21:59 +00:00
Merge pull request #2538 from freqtrade/strategy_template
new-strategy / new-hyperopt - from templates
This commit is contained in:
commit
1b645d64c8
|
@ -1,6 +1,7 @@
|
|||
[run]
|
||||
omit =
|
||||
scripts/*
|
||||
freqtrade/templates/*
|
||||
freqtrade/vendor/*
|
||||
freqtrade/__main__.py
|
||||
tests/*
|
||||
|
|
8
.github/workflows/ci.yml
vendored
8
.github/workflows/ci.yml
vendored
|
@ -81,11 +81,13 @@ jobs:
|
|||
- name: Backtesting
|
||||
run: |
|
||||
cp config.json.example config.json
|
||||
freqtrade backtesting --datadir tests/testdata --strategy DefaultStrategy
|
||||
freqtrade create-userdir --userdir user_data
|
||||
freqtrade backtesting --datadir tests/testdata --strategy SampleStrategy
|
||||
|
||||
- name: Hyperopt
|
||||
run: |
|
||||
cp config.json.example config.json
|
||||
freqtrade create-userdir --userdir user_data
|
||||
freqtrade hyperopt --datadir tests/testdata -e 5 --strategy SampleStrategy --hyperopt SampleHyperOpt
|
||||
|
||||
- name: Flake8
|
||||
|
@ -142,11 +144,13 @@ jobs:
|
|||
- name: Backtesting
|
||||
run: |
|
||||
cp config.json.example config.json
|
||||
freqtrade backtesting --datadir tests/testdata --strategy DefaultStrategy
|
||||
freqtrade create-userdir --userdir user_data
|
||||
freqtrade backtesting --datadir tests/testdata --strategy SampleStrategy
|
||||
|
||||
- name: Hyperopt
|
||||
run: |
|
||||
cp config.json.example config.json
|
||||
freqtrade create-userdir --userdir user_data
|
||||
freqtrade hyperopt --datadir tests/testdata -e 5 --strategy SampleStrategy --hyperopt SampleHyperOpt
|
||||
|
||||
- name: Flake8
|
||||
|
|
|
@ -28,10 +28,12 @@ jobs:
|
|||
name: pytest
|
||||
- script:
|
||||
- cp config.json.example config.json
|
||||
- freqtrade backtesting --datadir tests/testdata --strategy DefaultStrategy
|
||||
- freqtrade create-userdir --userdir user_data
|
||||
- freqtrade backtesting --datadir tests/testdata --strategy SampleStrategy
|
||||
name: backtest
|
||||
- script:
|
||||
- cp config.json.example config.json
|
||||
- freqtrade create-userdir --userdir user_data
|
||||
- freqtrade hyperopt --datadir tests/testdata -e 5 --strategy SampleStrategy --hyperopt SampleHyperOpt
|
||||
name: hyperopt
|
||||
- script: flake8
|
||||
|
|
|
@ -200,8 +200,8 @@ If the day shows the same day, then the last candle can be assumed as incomplete
|
|||
To keep the jupyter notebooks aligned with the documentation, the following should be ran after updating a example notebook.
|
||||
|
||||
``` bash
|
||||
jupyter nbconvert --ClearOutputPreprocessor.enabled=True --inplace user_data/notebooks/strategy_analysis_example.ipynb
|
||||
jupyter nbconvert --ClearOutputPreprocessor.enabled=True --to markdown user_data/notebooks/strategy_analysis_example.ipynb --stdout > docs/strategy_analysis_example.md
|
||||
jupyter nbconvert --ClearOutputPreprocessor.enabled=True --inplace freqtrade/templates/strategy_analysis_example.ipynb
|
||||
jupyter nbconvert --ClearOutputPreprocessor.enabled=True --to markdown freqtrade/templates/strategy_analysis_example.ipynb --stdout > docs/strategy_analysis_example.md
|
||||
```
|
||||
|
||||
## Continuous integration
|
||||
|
|
|
@ -15,10 +15,13 @@ To learn how to get data for the pairs and exchange you're interrested in, head
|
|||
## Prepare Hyperopting
|
||||
|
||||
Before we start digging into Hyperopt, we recommend you to take a look at
|
||||
the sample hyperopt file located in [user_data/hyperopts/](https://github.com/freqtrade/freqtrade/blob/develop/user_data/hyperopts/sample_hyperopt.py).
|
||||
the sample hyperopt file located in [user_data/hyperopts/](https://github.com/freqtrade/freqtrade/blob/develop/freqtrade/templates/sample_hyperopt.py).
|
||||
|
||||
Configuring hyperopt is similar to writing your own strategy, and many tasks will be similar and a lot of code can be copied across from the strategy.
|
||||
|
||||
The simplest way to get started is to use `freqtrade new-hyperopt --hyperopt AwesomeHyperopt`.
|
||||
This will create a new hyperopt file from a template, which will be located under `user_data/hyperopts/AwesomeHyperopt.py`.
|
||||
|
||||
### Checklist on all tasks / possibilities in hyperopt
|
||||
|
||||
Depending on the space you want to optimize, only some of the below are required:
|
||||
|
@ -423,7 +426,7 @@ These ranges should be sufficient in most cases. The minutes in the steps (ROI d
|
|||
|
||||
If you have the `generate_roi_table()` and `roi_space()` methods in your custom hyperopt file, remove them in order to utilize these adaptive ROI tables and the ROI hyperoptimization space generated by Freqtrade by default.
|
||||
|
||||
Override the `roi_space()` method if you need components of the ROI tables to vary in other ranges. Override the `generate_roi_table()` and `roi_space()` methods and implement your own custom approach for generation of the ROI tables during hyperoptimization if you need a different structure of the ROI tables or other amount of rows (steps). A sample for these methods can be found in [user_data/hyperopts/sample_hyperopt_advanced.py](https://github.com/freqtrade/freqtrade/blob/develop/user_data/hyperopts/sample_hyperopt_advanced.py).
|
||||
Override the `roi_space()` method if you need components of the ROI tables to vary in other ranges. Override the `generate_roi_table()` and `roi_space()` methods and implement your own custom approach for generation of the ROI tables during hyperoptimization if you need a different structure of the ROI tables or other amount of rows (steps). A sample for these methods can be found in [user_data/hyperopts/sample_hyperopt_advanced.py](https://github.com/freqtrade/freqtrade/blob/develop/freqtrade/templates/sample_hyperopt_advanced.py).
|
||||
|
||||
### Understand Hyperopt Stoploss results
|
||||
|
||||
|
@ -458,7 +461,7 @@ If you are optimizing stoploss values, Freqtrade creates the 'stoploss' optimiza
|
|||
|
||||
If you have the `stoploss_space()` method in your custom hyperopt file, remove it in order to utilize Stoploss hyperoptimization space generated by Freqtrade by default.
|
||||
|
||||
Override the `stoploss_space()` method and define the desired range in it if you need stoploss values to vary in other range during hyperoptimization. A sample for this method can be found in [user_data/hyperopts/sample_hyperopt_advanced.py](https://github.com/freqtrade/freqtrade/blob/develop/user_data/hyperopts/sample_hyperopt_advanced.py).
|
||||
Override the `stoploss_space()` method and define the desired range in it if you need stoploss values to vary in other range during hyperoptimization. A sample for this method can be found in [user_data/hyperopts/sample_hyperopt_advanced.py](https://github.com/freqtrade/freqtrade/blob/develop/freqtrade/templates/sample_hyperopt_advanced.py).
|
||||
|
||||
### Validate backtesting results
|
||||
|
||||
|
|
|
@ -162,7 +162,7 @@ Clone the git repository:
|
|||
|
||||
```bash
|
||||
git clone https://github.com/freqtrade/freqtrade.git
|
||||
|
||||
cd freqtrade
|
||||
```
|
||||
|
||||
Optionally checkout the master branch to get the latest stable release:
|
||||
|
@ -171,22 +171,24 @@ Optionally checkout the master branch to get the latest stable release:
|
|||
git checkout master
|
||||
```
|
||||
|
||||
#### 4. Initialize the configuration
|
||||
|
||||
```bash
|
||||
cd freqtrade
|
||||
cp config.json.example config.json
|
||||
```
|
||||
|
||||
> *To edit the config please refer to [Bot Configuration](configuration.md).*
|
||||
|
||||
#### 5. Install python dependencies
|
||||
#### 4. Install python dependencies
|
||||
|
||||
``` bash
|
||||
python3 -m pip install --upgrade pip
|
||||
python3 -m pip install -e .
|
||||
```
|
||||
|
||||
#### 5. Initialize the configuration
|
||||
|
||||
```bash
|
||||
# Initialize the user_directory
|
||||
freqtrade create-userdir --userdir user_data/
|
||||
|
||||
cp config.json.example config.json
|
||||
```
|
||||
|
||||
> *To edit the config please refer to [Bot Configuration](configuration.md).*
|
||||
|
||||
#### 6. Run the Bot
|
||||
|
||||
If this is the first time you run the bot, ensure you are running it in Dry-run `"dry_run": true,` otherwise it will start to buy and sell coins.
|
||||
|
@ -227,7 +229,7 @@ If that is not available on your system, feel free to try the instructions below
|
|||
Make sure to use 64bit Windows and 64bit Python to avoid problems with backtesting or hyperopt due to the memory constraints 32bit applications have under Windows.
|
||||
|
||||
!!! Hint
|
||||
Using the [Anaconda Distribution](https://www.anaconda.com/distribution/) under Windows can greatly help with installation problems. Check out the [Conda section](#using-conda) in this document.
|
||||
Using the [Anaconda Distribution](https://www.anaconda.com/distribution/) under Windows can greatly help with installation problems. Check out the [Conda section](#using-conda) in this document for more information.
|
||||
|
||||
#### Clone the git repository
|
||||
|
||||
|
|
|
@ -7,24 +7,28 @@ indicators.
|
|||
|
||||
This is very simple. Copy paste your strategy file into the directory `user_data/strategies`.
|
||||
|
||||
Let assume you have a class called `AwesomeStrategy` in the file `awesome-strategy.py`:
|
||||
Let assume you have a class called `AwesomeStrategy` in the file `AwesomeStrategy.py`:
|
||||
|
||||
1. Move your file into `user_data/strategies` (you should have `user_data/strategies/awesome-strategy.py`
|
||||
1. Move your file into `user_data/strategies` (you should have `user_data/strategies/AwesomeStrategy.py`
|
||||
2. Start the bot with the param `--strategy AwesomeStrategy` (the parameter is the class name)
|
||||
|
||||
```bash
|
||||
freqtrade trade --strategy AwesomeStrategy
|
||||
```
|
||||
|
||||
## Change your strategy
|
||||
## Develop your own strategy
|
||||
|
||||
The bot includes a default strategy file. However, we recommend you to
|
||||
use your own file to not have to lose your parameters every time the default
|
||||
strategy file will be updated on Github. Put your custom strategy file
|
||||
into the directory `user_data/strategies`.
|
||||
The bot includes a default strategy file.
|
||||
Also, several other strategies are available in the [strategy repository](https://github.com/freqtrade/freqtrade-strategies).
|
||||
|
||||
Best copy the test-strategy and modify this copy to avoid having bot-updates override your changes.
|
||||
`cp user_data/strategies/sample_strategy.py user_data/strategies/awesome-strategy.py`
|
||||
You will however most likely have your own idea for a strategy.
|
||||
This document intends to help you develop one for yourself.
|
||||
|
||||
To get started, use `freqtrade new-strategy --strategy AwesomeStrategy`.
|
||||
This will create a new strategy file from a template, which will be located under `user_data/strategies/AwesomeStrategy.py`.
|
||||
|
||||
!!! Note
|
||||
This is just a template file, which will most likely not be profitable out of the box.
|
||||
|
||||
### Anatomy of a strategy
|
||||
|
||||
|
@ -48,7 +52,7 @@ Future versions will require this to be set.
|
|||
freqtrade trade --strategy AwesomeStrategy
|
||||
```
|
||||
|
||||
**For the following section we will use the [user_data/strategies/sample_strategy.py](https://github.com/freqtrade/freqtrade/blob/develop/user_data/strategies/sample_strategy.py)
|
||||
**For the following section we will use the [user_data/strategies/sample_strategy.py](https://github.com/freqtrade/freqtrade/blob/develop/freqtrade/templates/sample_strategy.py)
|
||||
file as reference.**
|
||||
|
||||
!!! Note "Strategies and Backtesting"
|
||||
|
@ -114,7 +118,7 @@ def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame
|
|||
```
|
||||
|
||||
!!! Note "Want more indicator examples?"
|
||||
Look into the [user_data/strategies/sample_strategy.py](https://github.com/freqtrade/freqtrade/blob/develop/user_data/strategies/sample_strategy.py).
|
||||
Look into the [user_data/strategies/sample_strategy.py](https://github.com/freqtrade/freqtrade/blob/develop/freqtrade/templates/sample_strategy.py).
|
||||
Then uncomment indicators you need.
|
||||
|
||||
### Strategy startup period
|
||||
|
@ -478,7 +482,7 @@ Printing more than a few rows is also possible (simply use `print(dataframe)` i
|
|||
### Where can i find a strategy template?
|
||||
|
||||
The strategy template is located in the file
|
||||
[user_data/strategies/sample_strategy.py](https://github.com/freqtrade/freqtrade/blob/develop/user_data/strategies/sample_strategy.py).
|
||||
[user_data/strategies/sample_strategy.py](https://github.com/freqtrade/freqtrade/blob/develop/freqtrade/templates/sample_strategy.py).
|
||||
|
||||
### Specify custom strategy location
|
||||
|
||||
|
|
110
docs/utils.md
110
docs/utils.md
|
@ -2,6 +2,116 @@
|
|||
|
||||
Besides the Live-Trade and Dry-Run run modes, the `backtesting`, `edge` and `hyperopt` optimization subcommands, and the `download-data` subcommand which prepares historical data, the bot contains a number of utility subcommands. They are described in this section.
|
||||
|
||||
## Create userdir
|
||||
|
||||
Creates the directory structure to hold your files for freqtrade.
|
||||
Will also create strategy and hyperopt examples for you to get started.
|
||||
Can be used multiple times - using `--reset` will reset the sample strategy and hyperopt files to their default state.
|
||||
|
||||
```
|
||||
usage: freqtrade create-userdir [-h] [--userdir PATH] [--reset]
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
--userdir PATH, --user-data-dir PATH
|
||||
Path to userdata directory.
|
||||
--reset Reset sample files to their original state.
|
||||
```
|
||||
|
||||
!!! Warning
|
||||
Using `--reset` may result in loss of data, since this will overwrite all sample files without asking again.
|
||||
|
||||
```
|
||||
├── backtest_results
|
||||
├── data
|
||||
├── hyperopt_results
|
||||
├── hyperopts
|
||||
│ ├── sample_hyperopt_advanced.py
|
||||
│ ├── sample_hyperopt_loss.py
|
||||
│ └── sample_hyperopt.py
|
||||
├── notebooks
|
||||
│ └── strategy_analysis_example.ipynb
|
||||
├── plot
|
||||
└── strategies
|
||||
└── sample_strategy.py
|
||||
```
|
||||
|
||||
## Create new strategy
|
||||
|
||||
Creates a new strategy from a template similar to SampleStrategy.
|
||||
The file will be named inline with your class name, and will not overwrite existing files.
|
||||
|
||||
Results will be located in `user_data/strategies/<strategyclassname>.py`.
|
||||
|
||||
### Sample usage of new-strategy
|
||||
|
||||
```bash
|
||||
freqtrade new-strategy --strategy AwesomeStrategy
|
||||
```
|
||||
|
||||
With custom user directory
|
||||
|
||||
```bash
|
||||
freqtrade new-strategy --userdir ~/.freqtrade/ --strategy AwesomeStrategy
|
||||
```
|
||||
|
||||
### new-strategy complete options
|
||||
|
||||
``` output
|
||||
usage: freqtrade new-strategy [-h] [--userdir PATH] [-s NAME]
|
||||
[--template {full,minimal}]
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
--userdir PATH, --user-data-dir PATH
|
||||
Path to userdata directory.
|
||||
-s NAME, --strategy NAME
|
||||
Specify strategy class name which will be used by the
|
||||
bot.
|
||||
--template {full,minimal}
|
||||
Use a template which is either `minimal` or `full`
|
||||
(containing multiple sample indicators). Default:
|
||||
`full`.
|
||||
|
||||
```
|
||||
|
||||
## Create new hyperopt
|
||||
|
||||
Creates a new hyperopt from a template similar to SampleHyperopt.
|
||||
The file will be named inline with your class name, and will not overwrite existing files.
|
||||
|
||||
Results will be located in `user_data/hyperopts/<classname>.py`.
|
||||
|
||||
### Sample usage of new-hyperopt
|
||||
|
||||
```bash
|
||||
freqtrade new-hyperopt --hyperopt AwesomeHyperopt
|
||||
```
|
||||
|
||||
With custom user directory
|
||||
|
||||
```bash
|
||||
freqtrade new-hyperopt --userdir ~/.freqtrade/ --hyperopt AwesomeHyperopt
|
||||
```
|
||||
|
||||
### new-hyperopt complete options
|
||||
|
||||
``` output
|
||||
usage: freqtrade new-hyperopt [-h] [--userdir PATH] [--hyperopt NAME]
|
||||
[--template {full,minimal}]
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
--userdir PATH, --user-data-dir PATH
|
||||
Path to userdata directory.
|
||||
--hyperopt NAME Specify hyperopt class name which will be used by the
|
||||
bot.
|
||||
--template {full,minimal}
|
||||
Use a template which is either `minimal` or `full`
|
||||
(containing multiple sample indicators). Default:
|
||||
`full`.
|
||||
```
|
||||
|
||||
## List Exchanges
|
||||
|
||||
Use the `list-exchanges` subcommand to see the exchanges available for the bot.
|
||||
|
|
|
@ -37,7 +37,11 @@ ARGS_LIST_TIMEFRAMES = ["exchange", "print_one_column"]
|
|||
ARGS_LIST_PAIRS = ["exchange", "print_list", "list_pairs_print_json", "print_one_column",
|
||||
"print_csv", "base_currencies", "quote_currencies", "list_pairs_all"]
|
||||
|
||||
ARGS_CREATE_USERDIR = ["user_data_dir"]
|
||||
ARGS_CREATE_USERDIR = ["user_data_dir", "reset"]
|
||||
|
||||
ARGS_BUILD_STRATEGY = ["user_data_dir", "strategy", "template"]
|
||||
|
||||
ARGS_BUILD_HYPEROPT = ["user_data_dir", "hyperopt", "template"]
|
||||
|
||||
ARGS_DOWNLOAD_DATA = ["pairs", "pairs_file", "days", "download_trades", "exchange",
|
||||
"timeframes", "erase"]
|
||||
|
@ -52,7 +56,7 @@ ARGS_PLOT_PROFIT = ["pairs", "timerange", "export", "exportfilename", "db_url",
|
|||
NO_CONF_REQURIED = ["download-data", "list-timeframes", "list-markets", "list-pairs",
|
||||
"plot-dataframe", "plot-profit"]
|
||||
|
||||
NO_CONF_ALLOWED = ["create-userdir", "list-exchanges"]
|
||||
NO_CONF_ALLOWED = ["create-userdir", "list-exchanges", "new-hyperopt", "new-strategy"]
|
||||
|
||||
|
||||
class Arguments:
|
||||
|
@ -117,6 +121,7 @@ class Arguments:
|
|||
from freqtrade.optimize import start_backtesting, start_hyperopt, start_edge
|
||||
from freqtrade.utils import (start_create_userdir, start_download_data,
|
||||
start_list_exchanges, start_list_markets,
|
||||
start_new_hyperopt, start_new_strategy,
|
||||
start_list_timeframes, start_trading)
|
||||
from freqtrade.plot.plot_utils import start_plot_dataframe, start_plot_profit
|
||||
|
||||
|
@ -158,6 +163,18 @@ class Arguments:
|
|||
create_userdir_cmd.set_defaults(func=start_create_userdir)
|
||||
self._build_args(optionlist=ARGS_CREATE_USERDIR, parser=create_userdir_cmd)
|
||||
|
||||
# add new-strategy subcommand
|
||||
build_strategy_cmd = subparsers.add_parser('new-strategy',
|
||||
help="Create new strategy")
|
||||
build_strategy_cmd.set_defaults(func=start_new_strategy)
|
||||
self._build_args(optionlist=ARGS_BUILD_STRATEGY, parser=build_strategy_cmd)
|
||||
|
||||
# add new-hyperopt subcommand
|
||||
build_hyperopt_cmd = subparsers.add_parser('new-hyperopt',
|
||||
help="Create new hyperopt")
|
||||
build_hyperopt_cmd.set_defaults(func=start_new_hyperopt)
|
||||
self._build_args(optionlist=ARGS_BUILD_HYPEROPT, parser=build_hyperopt_cmd)
|
||||
|
||||
# Add list-exchanges subcommand
|
||||
list_exchanges_cmd = subparsers.add_parser(
|
||||
'list-exchanges',
|
||||
|
|
|
@ -62,6 +62,11 @@ AVAILABLE_CLI_OPTIONS = {
|
|||
help='Path to userdata directory.',
|
||||
metavar='PATH',
|
||||
),
|
||||
"reset": Arg(
|
||||
'--reset',
|
||||
help='Reset sample files to their original state.',
|
||||
action='store_true',
|
||||
),
|
||||
# Main options
|
||||
"strategy": Arg(
|
||||
'-s', '--strategy',
|
||||
|
@ -334,6 +339,14 @@ AVAILABLE_CLI_OPTIONS = {
|
|||
help='Clean all existing data for the selected exchange/pairs/timeframes.',
|
||||
action='store_true',
|
||||
),
|
||||
# Templating options
|
||||
"template": Arg(
|
||||
'--template',
|
||||
help='Use a template which is either `minimal` or '
|
||||
'`full` (containing multiple sample indicators). Default: `%(default)s`.',
|
||||
choices=['full', 'minimal'],
|
||||
default='full',
|
||||
),
|
||||
# Plot dataframe
|
||||
"indicators1": Arg(
|
||||
'--indicators1',
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import logging
|
||||
from typing import Any, Dict, Optional
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
from freqtrade import OperationalException
|
||||
from freqtrade.constants import USER_DATA_FILES
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
@ -31,7 +33,8 @@ def create_userdata_dir(directory: str, create_dir=False) -> Path:
|
|||
:param create_dir: Create directory if it does not exist.
|
||||
:return: Path object containing the directory
|
||||
"""
|
||||
sub_dirs = ["backtest_results", "data", "hyperopts", "hyperopt_results", "plot", "strategies", ]
|
||||
sub_dirs = ["backtest_results", "data", "hyperopts", "hyperopt_results", "notebooks",
|
||||
"plot", "strategies", ]
|
||||
folder = Path(directory)
|
||||
if not folder.is_dir():
|
||||
if create_dir:
|
||||
|
@ -48,3 +51,26 @@ def create_userdata_dir(directory: str, create_dir=False) -> Path:
|
|||
if not subfolder.is_dir():
|
||||
subfolder.mkdir(parents=False)
|
||||
return folder
|
||||
|
||||
|
||||
def copy_sample_files(directory: Path, overwrite: bool = False) -> None:
|
||||
"""
|
||||
Copy files from templates to User data directory.
|
||||
:param directory: Directory to copy data to
|
||||
:param overwrite: Overwrite existing sample files
|
||||
"""
|
||||
if not directory.is_dir():
|
||||
raise OperationalException(f"Directory `{directory}` does not exist.")
|
||||
sourcedir = Path(__file__).parents[1] / "templates"
|
||||
for source, target in USER_DATA_FILES.items():
|
||||
targetdir = directory / target
|
||||
if not targetdir.is_dir():
|
||||
raise OperationalException(f"Directory `{targetdir}` does not exist.")
|
||||
targetfile = targetdir / source
|
||||
if targetfile.exists():
|
||||
if not overwrite:
|
||||
logger.warning(f"File `{targetfile}` exists already, not deploying sample file.")
|
||||
continue
|
||||
else:
|
||||
logger.warning(f"File `{targetfile}` exists already, overwriting.")
|
||||
shutil.copy(str(sourcedir / source), str(targetfile))
|
||||
|
|
|
@ -22,6 +22,18 @@ AVAILABLE_PAIRLISTS = ['StaticPairList', 'VolumePairList', 'PrecisionFilter', 'P
|
|||
DRY_RUN_WALLET = 999.9
|
||||
MATH_CLOSE_PREC = 1e-14 # Precision used for float comparisons
|
||||
|
||||
USERPATH_HYPEROPTS = 'hyperopts'
|
||||
USERPATH_STRATEGY = 'strategies'
|
||||
|
||||
# Soure files with destination directories within user-directory
|
||||
USER_DATA_FILES = {
|
||||
'sample_strategy.py': USERPATH_STRATEGY,
|
||||
'sample_hyperopt_advanced.py': USERPATH_HYPEROPTS,
|
||||
'sample_hyperopt_loss.py': USERPATH_HYPEROPTS,
|
||||
'sample_hyperopt.py': USERPATH_HYPEROPTS,
|
||||
'strategy_analysis_example.ipynb': 'notebooks',
|
||||
}
|
||||
|
||||
TIMEFRAMES = [
|
||||
'1m', '3m', '5m', '15m', '30m',
|
||||
'1h', '2h', '4h', '6h', '8h', '12h',
|
||||
|
|
|
@ -127,3 +127,16 @@ def round_dict(d, n):
|
|||
|
||||
def plural(num, singular: str, plural: str = None) -> str:
|
||||
return singular if (num == 1 or num == -1) else plural or singular + 's'
|
||||
|
||||
|
||||
def render_template(templatefile: str, arguments: dict = {}):
|
||||
|
||||
from jinja2 import Environment, PackageLoader, select_autoescape
|
||||
|
||||
env = Environment(
|
||||
loader=PackageLoader('freqtrade', 'templates'),
|
||||
autoescape=select_autoescape(['html', 'xml'])
|
||||
)
|
||||
template = env.get_template(templatefile)
|
||||
|
||||
return template.render(**arguments)
|
||||
|
|
|
@ -8,7 +8,7 @@ from pathlib import Path
|
|||
from typing import Optional, Dict
|
||||
|
||||
from freqtrade import OperationalException
|
||||
from freqtrade.constants import DEFAULT_HYPEROPT_LOSS
|
||||
from freqtrade.constants import DEFAULT_HYPEROPT_LOSS, USERPATH_HYPEROPTS
|
||||
from freqtrade.optimize.hyperopt_interface import IHyperOpt
|
||||
from freqtrade.optimize.hyperopt_loss_interface import IHyperOptLoss
|
||||
from freqtrade.resolvers import IResolver
|
||||
|
@ -58,7 +58,7 @@ class HyperOptResolver(IResolver):
|
|||
current_path = Path(__file__).parent.parent.joinpath('optimize').resolve()
|
||||
|
||||
abs_paths = self.build_search_paths(config, current_path=current_path,
|
||||
user_subdir='hyperopts', extra_dir=extra_dir)
|
||||
user_subdir=USERPATH_HYPEROPTS, extra_dir=extra_dir)
|
||||
|
||||
hyperopt = self._load_object(paths=abs_paths, object_type=IHyperOpt,
|
||||
object_name=hyperopt_name, kwargs={'config': config})
|
||||
|
@ -110,7 +110,7 @@ class HyperOptLossResolver(IResolver):
|
|||
current_path = Path(__file__).parent.parent.joinpath('optimize').resolve()
|
||||
|
||||
abs_paths = self.build_search_paths(config, current_path=current_path,
|
||||
user_subdir='hyperopts', extra_dir=extra_dir)
|
||||
user_subdir=USERPATH_HYPEROPTS, extra_dir=extra_dir)
|
||||
|
||||
hyperoptloss = self._load_object(paths=abs_paths, object_type=IHyperOptLoss,
|
||||
object_name=hyper_loss_name)
|
||||
|
|
|
@ -129,7 +129,8 @@ class StrategyResolver(IResolver):
|
|||
current_path = Path(__file__).parent.parent.joinpath('strategy').resolve()
|
||||
|
||||
abs_paths = self.build_search_paths(config, current_path=current_path,
|
||||
user_subdir='strategies', extra_dir=extra_dir)
|
||||
user_subdir=constants.USERPATH_STRATEGY,
|
||||
extra_dir=extra_dir)
|
||||
|
||||
if ":" in strategy_name:
|
||||
logger.info("loading base64 encoded strategy")
|
||||
|
|
127
freqtrade/templates/base_hyperopt.py.j2
Normal file
127
freqtrade/templates/base_hyperopt.py.j2
Normal file
|
@ -0,0 +1,127 @@
|
|||
# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement
|
||||
|
||||
# --- Do not remove these libs ---
|
||||
from functools import reduce
|
||||
from typing import Any, Callable, Dict, List
|
||||
|
||||
import numpy as np # noqa
|
||||
import pandas as pd # noqa
|
||||
from pandas import DataFrame
|
||||
from skopt.space import Categorical, Dimension, Integer, Real # noqa
|
||||
|
||||
from freqtrade.optimize.hyperopt_interface import IHyperOpt
|
||||
|
||||
# --------------------------------
|
||||
# Add your lib to import here
|
||||
import talib.abstract as ta # noqa
|
||||
import freqtrade.vendor.qtpylib.indicators as qtpylib
|
||||
|
||||
|
||||
class {{ hyperopt }}(IHyperOpt):
|
||||
"""
|
||||
This is a Hyperopt template to get you started.
|
||||
|
||||
More information in https://github.com/freqtrade/freqtrade/blob/develop/docs/hyperopt.md
|
||||
|
||||
You should:
|
||||
- Add any lib you need to build your hyperopt.
|
||||
|
||||
You must keep:
|
||||
- The prototypes for the methods: populate_indicators, indicator_space, buy_strategy_generator.
|
||||
|
||||
The roi_space, generate_roi_table, stoploss_space methods are no longer required to be
|
||||
copied in every custom hyperopt. However, you may override them if you need the
|
||||
'roi' and the 'stoploss' spaces that differ from the defaults offered by Freqtrade.
|
||||
Sample implementation of these methods can be found in
|
||||
https://github.com/freqtrade/freqtrade/blob/develop/user_data/hyperopts/sample_hyperopt_advanced.py
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def buy_strategy_generator(params: Dict[str, Any]) -> Callable:
|
||||
"""
|
||||
Define the buy strategy parameters to be used by Hyperopt.
|
||||
"""
|
||||
def populate_buy_trend(dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||
"""
|
||||
Buy strategy Hyperopt will build and use.
|
||||
"""
|
||||
conditions = []
|
||||
|
||||
# GUARDS AND TRENDS
|
||||
{{ buy_guards | indent(12) }}
|
||||
|
||||
# TRIGGERS
|
||||
if 'trigger' in params:
|
||||
if params['trigger'] == 'bb_lower':
|
||||
conditions.append(dataframe['close'] < dataframe['bb_lowerband'])
|
||||
if params['trigger'] == 'macd_cross_signal':
|
||||
conditions.append(qtpylib.crossed_above(
|
||||
dataframe['macd'], dataframe['macdsignal']
|
||||
))
|
||||
if params['trigger'] == 'sar_reversal':
|
||||
conditions.append(qtpylib.crossed_above(
|
||||
dataframe['close'], dataframe['sar']
|
||||
))
|
||||
|
||||
if conditions:
|
||||
dataframe.loc[
|
||||
reduce(lambda x, y: x & y, conditions),
|
||||
'buy'] = 1
|
||||
|
||||
return dataframe
|
||||
|
||||
return populate_buy_trend
|
||||
|
||||
@staticmethod
|
||||
def indicator_space() -> List[Dimension]:
|
||||
"""
|
||||
Define your Hyperopt space for searching buy strategy parameters.
|
||||
"""
|
||||
return [
|
||||
{{ buy_space | indent(12) }}
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def sell_strategy_generator(params: Dict[str, Any]) -> Callable:
|
||||
"""
|
||||
Define the sell strategy parameters to be used by Hyperopt.
|
||||
"""
|
||||
def populate_sell_trend(dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||
"""
|
||||
Sell strategy Hyperopt will build and use.
|
||||
"""
|
||||
conditions = []
|
||||
|
||||
# GUARDS AND TRENDS
|
||||
{{ sell_guards | indent(12) }}
|
||||
|
||||
# TRIGGERS
|
||||
if 'sell-trigger' in params:
|
||||
if params['sell-trigger'] == 'sell-bb_upper':
|
||||
conditions.append(dataframe['close'] > dataframe['bb_upperband'])
|
||||
if params['sell-trigger'] == 'sell-macd_cross_signal':
|
||||
conditions.append(qtpylib.crossed_above(
|
||||
dataframe['macdsignal'], dataframe['macd']
|
||||
))
|
||||
if params['sell-trigger'] == 'sell-sar_reversal':
|
||||
conditions.append(qtpylib.crossed_above(
|
||||
dataframe['sar'], dataframe['close']
|
||||
))
|
||||
|
||||
if conditions:
|
||||
dataframe.loc[
|
||||
reduce(lambda x, y: x & y, conditions),
|
||||
'sell'] = 1
|
||||
|
||||
return dataframe
|
||||
|
||||
return populate_sell_trend
|
||||
|
||||
@staticmethod
|
||||
def sell_indicator_space() -> List[Dimension]:
|
||||
"""
|
||||
Define your Hyperopt space for searching sell strategy parameters.
|
||||
"""
|
||||
return [
|
||||
{{ sell_space | indent(12) }}
|
||||
]
|
138
freqtrade/templates/base_strategy.py.j2
Normal file
138
freqtrade/templates/base_strategy.py.j2
Normal file
|
@ -0,0 +1,138 @@
|
|||
# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement
|
||||
|
||||
# --- Do not remove these libs ---
|
||||
import numpy as np # noqa
|
||||
import pandas as pd # noqa
|
||||
from pandas import DataFrame
|
||||
|
||||
from freqtrade.strategy.interface import IStrategy
|
||||
|
||||
# --------------------------------
|
||||
# Add your lib to import here
|
||||
import talib.abstract as ta
|
||||
import freqtrade.vendor.qtpylib.indicators as qtpylib
|
||||
|
||||
|
||||
class {{ strategy }}(IStrategy):
|
||||
"""
|
||||
This is a strategy template to get you started.
|
||||
More information in https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-optimization.md
|
||||
|
||||
You can:
|
||||
:return: a Dataframe with all mandatory indicators for the strategies
|
||||
- Rename the class name (Do not forget to update class_name)
|
||||
- Add any methods you want to build your strategy
|
||||
- Add any lib you need to build your strategy
|
||||
|
||||
You must keep:
|
||||
- the lib in the section "Do not remove these libs"
|
||||
- the prototype for the methods: minimal_roi, stoploss, populate_indicators, populate_buy_trend,
|
||||
populate_sell_trend, hyperopt_space, buy_strategy_generator
|
||||
"""
|
||||
# Strategy interface version - allow new iterations of the strategy interface.
|
||||
# Check the documentation or the Sample strategy to get the latest version.
|
||||
INTERFACE_VERSION = 2
|
||||
|
||||
# Minimal ROI designed for the strategy.
|
||||
# This attribute will be overridden if the config file contains "minimal_roi".
|
||||
minimal_roi = {
|
||||
"60": 0.01,
|
||||
"30": 0.02,
|
||||
"0": 0.04
|
||||
}
|
||||
|
||||
# Optimal stoploss designed for the strategy.
|
||||
# This attribute will be overridden if the config file contains "stoploss".
|
||||
stoploss = -0.10
|
||||
|
||||
# Trailing stoploss
|
||||
trailing_stop = False
|
||||
# trailing_stop_positive = 0.01
|
||||
# trailing_stop_positive_offset = 0.0 # Disabled / not configured
|
||||
|
||||
# Optimal ticker interval for the strategy.
|
||||
ticker_interval = '5m'
|
||||
|
||||
# Run "populate_indicators()" only for new candle.
|
||||
process_only_new_candles = False
|
||||
|
||||
# These values can be overridden in the "ask_strategy" section in the config.
|
||||
use_sell_signal = True
|
||||
sell_profit_only = False
|
||||
ignore_roi_if_buy_signal = False
|
||||
|
||||
# Number of candles the strategy requires before producing valid signals
|
||||
startup_candle_count: int = 20
|
||||
|
||||
# Optional order type mapping.
|
||||
order_types = {
|
||||
'buy': 'limit',
|
||||
'sell': 'limit',
|
||||
'stoploss': 'market',
|
||||
'stoploss_on_exchange': False
|
||||
}
|
||||
|
||||
# Optional order time in force.
|
||||
order_time_in_force = {
|
||||
'buy': 'gtc',
|
||||
'sell': 'gtc'
|
||||
}
|
||||
|
||||
def informative_pairs(self):
|
||||
"""
|
||||
Define additional, informative pair/interval combinations to be cached from the exchange.
|
||||
These pair/interval combinations are non-tradeable, unless they are part
|
||||
of the whitelist as well.
|
||||
For more information, please consult the documentation
|
||||
:return: List of tuples in the format (pair, interval)
|
||||
Sample: return [("ETH/USDT", "5m"),
|
||||
("BTC/USDT", "15m"),
|
||||
]
|
||||
"""
|
||||
return []
|
||||
|
||||
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||
"""
|
||||
Adds several different TA indicators to the given DataFrame
|
||||
|
||||
Performance Note: For the best performance be frugal on the number of indicators
|
||||
you are using. Let uncomment only the indicator you are using in your strategies
|
||||
or your hyperopt configuration, otherwise you will waste your memory and CPU usage.
|
||||
:param dataframe: Raw data from the exchange and parsed by parse_ticker_dataframe()
|
||||
:param metadata: Additional information, like the currently traded pair
|
||||
:return: a Dataframe with all mandatory indicators for the strategies
|
||||
"""
|
||||
{{ indicators | indent(8) }}
|
||||
|
||||
return dataframe
|
||||
|
||||
def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||
"""
|
||||
Based on TA indicators, populates the buy signal for the given dataframe
|
||||
:param dataframe: DataFrame populated with indicators
|
||||
:param metadata: Additional information, like the currently traded pair
|
||||
:return: DataFrame with buy column
|
||||
"""
|
||||
dataframe.loc[
|
||||
(
|
||||
{{ buy_trend | indent(16) }}
|
||||
(dataframe['volume'] > 0) # Make sure Volume is not 0
|
||||
),
|
||||
'buy'] = 1
|
||||
|
||||
return dataframe
|
||||
|
||||
def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||
"""
|
||||
Based on TA indicators, populates the sell signal for the given dataframe
|
||||
:param dataframe: DataFrame populated with indicators
|
||||
:param metadata: Additional information, like the currently traded pair
|
||||
:return: DataFrame with buy column
|
||||
"""
|
||||
dataframe.loc[
|
||||
(
|
||||
{{ sell_trend | indent(16) }}
|
||||
(dataframe['volume'] > 0) # Make sure Volume is not 0
|
||||
),
|
||||
'sell'] = 1
|
||||
return dataframe
|
|
@ -1,16 +1,21 @@
|
|||
# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement
|
||||
|
||||
# --- Do not remove these libs ---
|
||||
from functools import reduce
|
||||
from typing import Any, Callable, Dict, List
|
||||
|
||||
import numpy as np # noqa
|
||||
import talib.abstract as ta
|
||||
import pandas as pd # noqa
|
||||
from pandas import DataFrame
|
||||
from skopt.space import Categorical, Dimension, Integer, Real # noqa
|
||||
|
||||
import freqtrade.vendor.qtpylib.indicators as qtpylib
|
||||
from freqtrade.optimize.hyperopt_interface import IHyperOpt
|
||||
|
||||
# --------------------------------
|
||||
# Add your lib to import here
|
||||
import talib.abstract as ta # noqa
|
||||
import freqtrade.vendor.qtpylib.indicators as qtpylib
|
||||
|
||||
|
||||
class SampleHyperOpt(IHyperOpt):
|
||||
"""
|
|
@ -1,18 +1,21 @@
|
|||
# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement
|
||||
|
||||
# --- Do not remove these libs ---
|
||||
from functools import reduce
|
||||
from math import exp
|
||||
from typing import Any, Callable, Dict, List
|
||||
from datetime import datetime
|
||||
|
||||
import numpy as np# noqa F401
|
||||
import talib.abstract as ta
|
||||
import numpy as np # noqa
|
||||
import pandas as pd # noqa
|
||||
from pandas import DataFrame
|
||||
from skopt.space import Categorical, Dimension, Integer, Real
|
||||
from skopt.space import Categorical, Dimension, Integer, Real # noqa
|
||||
|
||||
import freqtrade.vendor.qtpylib.indicators as qtpylib
|
||||
from freqtrade.optimize.hyperopt_interface import IHyperOpt
|
||||
|
||||
# --------------------------------
|
||||
# Add your lib to import here
|
||||
import talib.abstract as ta # noqa
|
||||
import freqtrade.vendor.qtpylib.indicators as qtpylib
|
||||
|
||||
|
||||
class AdvancedSampleHyperOpt(IHyperOpt):
|
||||
"""
|
|
@ -1,13 +1,16 @@
|
|||
# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement
|
||||
|
||||
# --- Do not remove these libs ---
|
||||
from freqtrade.strategy.interface import IStrategy
|
||||
import numpy as np # noqa
|
||||
import pandas as pd # noqa
|
||||
from pandas import DataFrame
|
||||
# --------------------------------
|
||||
|
||||
from freqtrade.strategy.interface import IStrategy
|
||||
|
||||
# --------------------------------
|
||||
# Add your lib to import here
|
||||
import talib.abstract as ta
|
||||
import freqtrade.vendor.qtpylib.indicators as qtpylib
|
||||
import numpy # noqa
|
||||
|
||||
|
||||
# This class is a sample. Feel free to customize it.
|
||||
|
@ -110,19 +113,18 @@ class SampleStrategy(IStrategy):
|
|||
# ADX
|
||||
dataframe['adx'] = ta.ADX(dataframe)
|
||||
|
||||
"""
|
||||
# Aroon, Aroon Oscillator
|
||||
aroon = ta.AROON(dataframe)
|
||||
dataframe['aroonup'] = aroon['aroonup']
|
||||
dataframe['aroondown'] = aroon['aroondown']
|
||||
dataframe['aroonosc'] = ta.AROONOSC(dataframe)
|
||||
# # Aroon, Aroon Oscillator
|
||||
# aroon = ta.AROON(dataframe)
|
||||
# dataframe['aroonup'] = aroon['aroonup']
|
||||
# dataframe['aroondown'] = aroon['aroondown']
|
||||
# dataframe['aroonosc'] = ta.AROONOSC(dataframe)
|
||||
|
||||
# Awesome oscillator
|
||||
dataframe['ao'] = qtpylib.awesome_oscillator(dataframe)
|
||||
# # Awesome oscillator
|
||||
# dataframe['ao'] = qtpylib.awesome_oscillator(dataframe)
|
||||
|
||||
# # Commodity Channel Index: values Oversold:<-100, Overbought:>100
|
||||
# dataframe['cci'] = ta.CCI(dataframe)
|
||||
|
||||
# Commodity Channel Index: values Oversold:<-100, Overbought:>100
|
||||
dataframe['cci'] = ta.CCI(dataframe)
|
||||
"""
|
||||
# MACD
|
||||
macd = ta.MACD(dataframe)
|
||||
dataframe['macd'] = macd['macd']
|
||||
|
@ -132,42 +134,39 @@ class SampleStrategy(IStrategy):
|
|||
# MFI
|
||||
dataframe['mfi'] = ta.MFI(dataframe)
|
||||
|
||||
"""
|
||||
# Minus Directional Indicator / Movement
|
||||
dataframe['minus_dm'] = ta.MINUS_DM(dataframe)
|
||||
dataframe['minus_di'] = ta.MINUS_DI(dataframe)
|
||||
# # Minus Directional Indicator / Movement
|
||||
# dataframe['minus_dm'] = ta.MINUS_DM(dataframe)
|
||||
# dataframe['minus_di'] = ta.MINUS_DI(dataframe)
|
||||
|
||||
# Plus Directional Indicator / Movement
|
||||
dataframe['plus_dm'] = ta.PLUS_DM(dataframe)
|
||||
dataframe['plus_di'] = ta.PLUS_DI(dataframe)
|
||||
dataframe['minus_di'] = ta.MINUS_DI(dataframe)
|
||||
# # Plus Directional Indicator / Movement
|
||||
# dataframe['plus_dm'] = ta.PLUS_DM(dataframe)
|
||||
# dataframe['plus_di'] = ta.PLUS_DI(dataframe)
|
||||
# dataframe['minus_di'] = ta.MINUS_DI(dataframe)
|
||||
|
||||
# ROC
|
||||
dataframe['roc'] = ta.ROC(dataframe)
|
||||
# # ROC
|
||||
# dataframe['roc'] = ta.ROC(dataframe)
|
||||
|
||||
# Inverse Fisher transform on RSI, values [-1.0, 1.0] (https://goo.gl/2JGGoy)
|
||||
rsi = 0.1 * (dataframe['rsi'] - 50)
|
||||
dataframe['fisher_rsi'] = (numpy.exp(2 * rsi) - 1) / (numpy.exp(2 * rsi) + 1)
|
||||
# # Inverse Fisher transform on RSI, values [-1.0, 1.0] (https://goo.gl/2JGGoy)
|
||||
# rsi = 0.1 * (dataframe['rsi'] - 50)
|
||||
# dataframe['fisher_rsi'] = (np.exp(2 * rsi) - 1) / (np.exp(2 * rsi) + 1)
|
||||
|
||||
# Inverse Fisher transform on RSI normalized, value [0.0, 100.0] (https://goo.gl/2JGGoy)
|
||||
dataframe['fisher_rsi_norma'] = 50 * (dataframe['fisher_rsi'] + 1)
|
||||
# # Inverse Fisher transform on RSI normalized, value [0.0, 100.0] (https://goo.gl/2JGGoy)
|
||||
# dataframe['fisher_rsi_norma'] = 50 * (dataframe['fisher_rsi'] + 1)
|
||||
|
||||
# # Stoch
|
||||
# stoch = ta.STOCH(dataframe)
|
||||
# dataframe['slowd'] = stoch['slowd']
|
||||
# dataframe['slowk'] = stoch['slowk']
|
||||
|
||||
# Stoch
|
||||
stoch = ta.STOCH(dataframe)
|
||||
dataframe['slowd'] = stoch['slowd']
|
||||
dataframe['slowk'] = stoch['slowk']
|
||||
"""
|
||||
# Stoch fast
|
||||
stoch_fast = ta.STOCHF(dataframe)
|
||||
dataframe['fastd'] = stoch_fast['fastd']
|
||||
dataframe['fastk'] = stoch_fast['fastk']
|
||||
|
||||
"""
|
||||
# Stoch RSI
|
||||
stoch_rsi = ta.STOCHRSI(dataframe)
|
||||
dataframe['fastd_rsi'] = stoch_rsi['fastd']
|
||||
dataframe['fastk_rsi'] = stoch_rsi['fastk']
|
||||
"""
|
||||
# # Stoch RSI
|
||||
# stoch_rsi = ta.STOCHRSI(dataframe)
|
||||
# dataframe['fastd_rsi'] = stoch_rsi['fastd']
|
||||
# dataframe['fastk_rsi'] = stoch_rsi['fastk']
|
||||
|
||||
# Overlap Studies
|
||||
# ------------------------------------
|
||||
|
@ -178,17 +177,16 @@ class SampleStrategy(IStrategy):
|
|||
dataframe['bb_middleband'] = bollinger['mid']
|
||||
dataframe['bb_upperband'] = bollinger['upper']
|
||||
|
||||
"""
|
||||
# EMA - Exponential Moving Average
|
||||
dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3)
|
||||
dataframe['ema5'] = ta.EMA(dataframe, timeperiod=5)
|
||||
dataframe['ema10'] = ta.EMA(dataframe, timeperiod=10)
|
||||
dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50)
|
||||
dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100)
|
||||
# # EMA - Exponential Moving Average
|
||||
# dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3)
|
||||
# dataframe['ema5'] = ta.EMA(dataframe, timeperiod=5)
|
||||
# dataframe['ema10'] = ta.EMA(dataframe, timeperiod=10)
|
||||
# dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50)
|
||||
# dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100)
|
||||
|
||||
# # SMA - Simple Moving Average
|
||||
# dataframe['sma'] = ta.SMA(dataframe, timeperiod=40)
|
||||
|
||||
# SMA - Simple Moving Average
|
||||
dataframe['sma'] = ta.SMA(dataframe, timeperiod=40)
|
||||
"""
|
||||
# SAR Parabol
|
||||
dataframe['sar'] = ta.SAR(dataframe)
|
||||
|
||||
|
@ -204,65 +202,57 @@ class SampleStrategy(IStrategy):
|
|||
|
||||
# Pattern Recognition - Bullish candlestick patterns
|
||||
# ------------------------------------
|
||||
"""
|
||||
# Hammer: values [0, 100]
|
||||
dataframe['CDLHAMMER'] = ta.CDLHAMMER(dataframe)
|
||||
# Inverted Hammer: values [0, 100]
|
||||
dataframe['CDLINVERTEDHAMMER'] = ta.CDLINVERTEDHAMMER(dataframe)
|
||||
# Dragonfly Doji: values [0, 100]
|
||||
dataframe['CDLDRAGONFLYDOJI'] = ta.CDLDRAGONFLYDOJI(dataframe)
|
||||
# Piercing Line: values [0, 100]
|
||||
dataframe['CDLPIERCING'] = ta.CDLPIERCING(dataframe) # values [0, 100]
|
||||
# Morningstar: values [0, 100]
|
||||
dataframe['CDLMORNINGSTAR'] = ta.CDLMORNINGSTAR(dataframe) # values [0, 100]
|
||||
# Three White Soldiers: values [0, 100]
|
||||
dataframe['CDL3WHITESOLDIERS'] = ta.CDL3WHITESOLDIERS(dataframe) # values [0, 100]
|
||||
"""
|
||||
# # Hammer: values [0, 100]
|
||||
# dataframe['CDLHAMMER'] = ta.CDLHAMMER(dataframe)
|
||||
# # Inverted Hammer: values [0, 100]
|
||||
# dataframe['CDLINVERTEDHAMMER'] = ta.CDLINVERTEDHAMMER(dataframe)
|
||||
# # Dragonfly Doji: values [0, 100]
|
||||
# dataframe['CDLDRAGONFLYDOJI'] = ta.CDLDRAGONFLYDOJI(dataframe)
|
||||
# # Piercing Line: values [0, 100]
|
||||
# dataframe['CDLPIERCING'] = ta.CDLPIERCING(dataframe) # values [0, 100]
|
||||
# # Morningstar: values [0, 100]
|
||||
# dataframe['CDLMORNINGSTAR'] = ta.CDLMORNINGSTAR(dataframe) # values [0, 100]
|
||||
# # Three White Soldiers: values [0, 100]
|
||||
# dataframe['CDL3WHITESOLDIERS'] = ta.CDL3WHITESOLDIERS(dataframe) # values [0, 100]
|
||||
|
||||
# Pattern Recognition - Bearish candlestick patterns
|
||||
# ------------------------------------
|
||||
"""
|
||||
# Hanging Man: values [0, 100]
|
||||
dataframe['CDLHANGINGMAN'] = ta.CDLHANGINGMAN(dataframe)
|
||||
# Shooting Star: values [0, 100]
|
||||
dataframe['CDLSHOOTINGSTAR'] = ta.CDLSHOOTINGSTAR(dataframe)
|
||||
# Gravestone Doji: values [0, 100]
|
||||
dataframe['CDLGRAVESTONEDOJI'] = ta.CDLGRAVESTONEDOJI(dataframe)
|
||||
# Dark Cloud Cover: values [0, 100]
|
||||
dataframe['CDLDARKCLOUDCOVER'] = ta.CDLDARKCLOUDCOVER(dataframe)
|
||||
# Evening Doji Star: values [0, 100]
|
||||
dataframe['CDLEVENINGDOJISTAR'] = ta.CDLEVENINGDOJISTAR(dataframe)
|
||||
# Evening Star: values [0, 100]
|
||||
dataframe['CDLEVENINGSTAR'] = ta.CDLEVENINGSTAR(dataframe)
|
||||
"""
|
||||
# # Hanging Man: values [0, 100]
|
||||
# dataframe['CDLHANGINGMAN'] = ta.CDLHANGINGMAN(dataframe)
|
||||
# # Shooting Star: values [0, 100]
|
||||
# dataframe['CDLSHOOTINGSTAR'] = ta.CDLSHOOTINGSTAR(dataframe)
|
||||
# # Gravestone Doji: values [0, 100]
|
||||
# dataframe['CDLGRAVESTONEDOJI'] = ta.CDLGRAVESTONEDOJI(dataframe)
|
||||
# # Dark Cloud Cover: values [0, 100]
|
||||
# dataframe['CDLDARKCLOUDCOVER'] = ta.CDLDARKCLOUDCOVER(dataframe)
|
||||
# # Evening Doji Star: values [0, 100]
|
||||
# dataframe['CDLEVENINGDOJISTAR'] = ta.CDLEVENINGDOJISTAR(dataframe)
|
||||
# # Evening Star: values [0, 100]
|
||||
# dataframe['CDLEVENINGSTAR'] = ta.CDLEVENINGSTAR(dataframe)
|
||||
|
||||
# Pattern Recognition - Bullish/Bearish candlestick patterns
|
||||
# ------------------------------------
|
||||
"""
|
||||
# Three Line Strike: values [0, -100, 100]
|
||||
dataframe['CDL3LINESTRIKE'] = ta.CDL3LINESTRIKE(dataframe)
|
||||
# Spinning Top: values [0, -100, 100]
|
||||
dataframe['CDLSPINNINGTOP'] = ta.CDLSPINNINGTOP(dataframe) # values [0, -100, 100]
|
||||
# Engulfing: values [0, -100, 100]
|
||||
dataframe['CDLENGULFING'] = ta.CDLENGULFING(dataframe) # values [0, -100, 100]
|
||||
# Harami: values [0, -100, 100]
|
||||
dataframe['CDLHARAMI'] = ta.CDLHARAMI(dataframe) # values [0, -100, 100]
|
||||
# Three Outside Up/Down: values [0, -100, 100]
|
||||
dataframe['CDL3OUTSIDE'] = ta.CDL3OUTSIDE(dataframe) # values [0, -100, 100]
|
||||
# Three Inside Up/Down: values [0, -100, 100]
|
||||
dataframe['CDL3INSIDE'] = ta.CDL3INSIDE(dataframe) # values [0, -100, 100]
|
||||
"""
|
||||
# # Three Line Strike: values [0, -100, 100]
|
||||
# dataframe['CDL3LINESTRIKE'] = ta.CDL3LINESTRIKE(dataframe)
|
||||
# # Spinning Top: values [0, -100, 100]
|
||||
# dataframe['CDLSPINNINGTOP'] = ta.CDLSPINNINGTOP(dataframe) # values [0, -100, 100]
|
||||
# # Engulfing: values [0, -100, 100]
|
||||
# dataframe['CDLENGULFING'] = ta.CDLENGULFING(dataframe) # values [0, -100, 100]
|
||||
# # Harami: values [0, -100, 100]
|
||||
# dataframe['CDLHARAMI'] = ta.CDLHARAMI(dataframe) # values [0, -100, 100]
|
||||
# # Three Outside Up/Down: values [0, -100, 100]
|
||||
# dataframe['CDL3OUTSIDE'] = ta.CDL3OUTSIDE(dataframe) # values [0, -100, 100]
|
||||
# # Three Inside Up/Down: values [0, -100, 100]
|
||||
# dataframe['CDL3INSIDE'] = ta.CDL3INSIDE(dataframe) # values [0, -100, 100]
|
||||
|
||||
# Chart type
|
||||
# ------------------------------------
|
||||
"""
|
||||
# Heikinashi stategy
|
||||
heikinashi = qtpylib.heikinashi(dataframe)
|
||||
dataframe['ha_open'] = heikinashi['open']
|
||||
dataframe['ha_close'] = heikinashi['close']
|
||||
dataframe['ha_high'] = heikinashi['high']
|
||||
dataframe['ha_low'] = heikinashi['low']
|
||||
"""
|
||||
# # Chart type
|
||||
# # ------------------------------------
|
||||
# # Heikinashi stategy
|
||||
# heikinashi = qtpylib.heikinashi(dataframe)
|
||||
# dataframe['ha_open'] = heikinashi['open']
|
||||
# dataframe['ha_close'] = heikinashi['close']
|
||||
# dataframe['ha_high'] = heikinashi['high']
|
||||
# dataframe['ha_low'] = heikinashi['low']
|
||||
|
||||
# Retrieve best bid and best ask from the orderbook
|
||||
# ------------------------------------
|
3
freqtrade/templates/subtemplates/buy_trend_full.j2
Normal file
3
freqtrade/templates/subtemplates/buy_trend_full.j2
Normal file
|
@ -0,0 +1,3 @@
|
|||
(qtpylib.crossed_above(dataframe['rsi'], 30)) & # Signal: RSI crosses above 30
|
||||
(dataframe['tema'] <= dataframe['bb_middleband']) & # Guard: tema below BB middle
|
||||
(dataframe['tema'] > dataframe['tema'].shift(1)) & # Guard: tema is raising
|
1
freqtrade/templates/subtemplates/buy_trend_minimal.j2
Normal file
1
freqtrade/templates/subtemplates/buy_trend_minimal.j2
Normal file
|
@ -0,0 +1 @@
|
|||
(qtpylib.crossed_above(dataframe['rsi'], 30)) & # Signal: RSI crosses above 30
|
|
@ -0,0 +1,8 @@
|
|||
if params.get('mfi-enabled'):
|
||||
conditions.append(dataframe['mfi'] < params['mfi-value'])
|
||||
if params.get('fastd-enabled'):
|
||||
conditions.append(dataframe['fastd'] < params['fastd-value'])
|
||||
if params.get('adx-enabled'):
|
||||
conditions.append(dataframe['adx'] > params['adx-value'])
|
||||
if params.get('rsi-enabled'):
|
||||
conditions.append(dataframe['rsi'] < params['rsi-value'])
|
|
@ -0,0 +1,2 @@
|
|||
if params.get('rsi-enabled'):
|
||||
conditions.append(dataframe['rsi'] < params['rsi-value'])
|
|
@ -0,0 +1,9 @@
|
|||
Integer(10, 25, name='mfi-value'),
|
||||
Integer(15, 45, name='fastd-value'),
|
||||
Integer(20, 50, name='adx-value'),
|
||||
Integer(20, 40, name='rsi-value'),
|
||||
Categorical([True, False], name='mfi-enabled'),
|
||||
Categorical([True, False], name='fastd-enabled'),
|
||||
Categorical([True, False], name='adx-enabled'),
|
||||
Categorical([True, False], name='rsi-enabled'),
|
||||
Categorical(['bb_lower', 'macd_cross_signal', 'sar_reversal'], name='trigger')
|
|
@ -0,0 +1,3 @@
|
|||
Integer(20, 40, name='rsi-value'),
|
||||
Categorical([True, False], name='rsi-enabled'),
|
||||
Categorical(['bb_lower', 'macd_cross_signal', 'sar_reversal'], name='trigger')
|
|
@ -0,0 +1,8 @@
|
|||
if params.get('sell-mfi-enabled'):
|
||||
conditions.append(dataframe['mfi'] > params['sell-mfi-value'])
|
||||
if params.get('sell-fastd-enabled'):
|
||||
conditions.append(dataframe['fastd'] > params['sell-fastd-value'])
|
||||
if params.get('sell-adx-enabled'):
|
||||
conditions.append(dataframe['adx'] < params['sell-adx-value'])
|
||||
if params.get('sell-rsi-enabled'):
|
||||
conditions.append(dataframe['rsi'] > params['sell-rsi-value'])
|
|
@ -0,0 +1,2 @@
|
|||
if params.get('sell-rsi-enabled'):
|
||||
conditions.append(dataframe['rsi'] > params['sell-rsi-value'])
|
11
freqtrade/templates/subtemplates/hyperopt_sell_space_full.j2
Normal file
11
freqtrade/templates/subtemplates/hyperopt_sell_space_full.j2
Normal file
|
@ -0,0 +1,11 @@
|
|||
Integer(75, 100, name='sell-mfi-value'),
|
||||
Integer(50, 100, name='sell-fastd-value'),
|
||||
Integer(50, 100, name='sell-adx-value'),
|
||||
Integer(60, 100, name='sell-rsi-value'),
|
||||
Categorical([True, False], name='sell-mfi-enabled'),
|
||||
Categorical([True, False], name='sell-fastd-enabled'),
|
||||
Categorical([True, False], name='sell-adx-enabled'),
|
||||
Categorical([True, False], name='sell-rsi-enabled'),
|
||||
Categorical(['sell-bb_upper',
|
||||
'sell-macd_cross_signal',
|
||||
'sell-sar_reversal'], name='sell-trigger')
|
|
@ -0,0 +1,5 @@
|
|||
Integer(60, 100, name='sell-rsi-value'),
|
||||
Categorical([True, False], name='sell-rsi-enabled'),
|
||||
Categorical(['sell-bb_upper',
|
||||
'sell-macd_cross_signal',
|
||||
'sell-sar_reversal'], name='sell-trigger')
|
161
freqtrade/templates/subtemplates/indicators_full.j2
Normal file
161
freqtrade/templates/subtemplates/indicators_full.j2
Normal file
|
@ -0,0 +1,161 @@
|
|||
|
||||
# Momentum Indicators
|
||||
# ------------------------------------
|
||||
|
||||
# RSI
|
||||
dataframe['rsi'] = ta.RSI(dataframe)
|
||||
|
||||
# ADX
|
||||
dataframe['adx'] = ta.ADX(dataframe)
|
||||
|
||||
# # Aroon, Aroon Oscillator
|
||||
# aroon = ta.AROON(dataframe)
|
||||
# dataframe['aroonup'] = aroon['aroonup']
|
||||
# dataframe['aroondown'] = aroon['aroondown']
|
||||
# dataframe['aroonosc'] = ta.AROONOSC(dataframe)
|
||||
|
||||
# # Awesome oscillator
|
||||
# dataframe['ao'] = qtpylib.awesome_oscillator(dataframe)
|
||||
|
||||
# # Commodity Channel Index: values Oversold:<-100, Overbought:>100
|
||||
# dataframe['cci'] = ta.CCI(dataframe)
|
||||
|
||||
# MACD
|
||||
macd = ta.MACD(dataframe)
|
||||
dataframe['macd'] = macd['macd']
|
||||
dataframe['macdsignal'] = macd['macdsignal']
|
||||
dataframe['macdhist'] = macd['macdhist']
|
||||
|
||||
# MFI
|
||||
dataframe['mfi'] = ta.MFI(dataframe)
|
||||
|
||||
# # Minus Directional Indicator / Movement
|
||||
# dataframe['minus_dm'] = ta.MINUS_DM(dataframe)
|
||||
# dataframe['minus_di'] = ta.MINUS_DI(dataframe)
|
||||
|
||||
# # Plus Directional Indicator / Movement
|
||||
# dataframe['plus_dm'] = ta.PLUS_DM(dataframe)
|
||||
# dataframe['plus_di'] = ta.PLUS_DI(dataframe)
|
||||
# dataframe['minus_di'] = ta.MINUS_DI(dataframe)
|
||||
|
||||
# # ROC
|
||||
# dataframe['roc'] = ta.ROC(dataframe)
|
||||
|
||||
# # Inverse Fisher transform on RSI, values [-1.0, 1.0] (https://goo.gl/2JGGoy)
|
||||
# rsi = 0.1 * (dataframe['rsi'] - 50)
|
||||
# dataframe['fisher_rsi'] = (np.exp(2 * rsi) - 1) / (np.exp(2 * rsi) + 1)
|
||||
|
||||
# # Inverse Fisher transform on RSI normalized, value [0.0, 100.0] (https://goo.gl/2JGGoy)
|
||||
# dataframe['fisher_rsi_norma'] = 50 * (dataframe['fisher_rsi'] + 1)
|
||||
|
||||
# # Stoch
|
||||
# stoch = ta.STOCH(dataframe)
|
||||
# dataframe['slowd'] = stoch['slowd']
|
||||
# dataframe['slowk'] = stoch['slowk']
|
||||
|
||||
# Stoch fast
|
||||
stoch_fast = ta.STOCHF(dataframe)
|
||||
dataframe['fastd'] = stoch_fast['fastd']
|
||||
dataframe['fastk'] = stoch_fast['fastk']
|
||||
|
||||
# # Stoch RSI
|
||||
# stoch_rsi = ta.STOCHRSI(dataframe)
|
||||
# dataframe['fastd_rsi'] = stoch_rsi['fastd']
|
||||
# dataframe['fastk_rsi'] = stoch_rsi['fastk']
|
||||
|
||||
# Overlap Studies
|
||||
# ------------------------------------
|
||||
|
||||
# Bollinger bands
|
||||
bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2)
|
||||
dataframe['bb_lowerband'] = bollinger['lower']
|
||||
dataframe['bb_middleband'] = bollinger['mid']
|
||||
dataframe['bb_upperband'] = bollinger['upper']
|
||||
|
||||
# # EMA - Exponential Moving Average
|
||||
# dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3)
|
||||
# dataframe['ema5'] = ta.EMA(dataframe, timeperiod=5)
|
||||
# dataframe['ema10'] = ta.EMA(dataframe, timeperiod=10)
|
||||
# dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50)
|
||||
# dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100)
|
||||
|
||||
# # SMA - Simple Moving Average
|
||||
# dataframe['sma'] = ta.SMA(dataframe, timeperiod=40)
|
||||
|
||||
# SAR Parabol
|
||||
dataframe['sar'] = ta.SAR(dataframe)
|
||||
|
||||
# TEMA - Triple Exponential Moving Average
|
||||
dataframe['tema'] = ta.TEMA(dataframe, timeperiod=9)
|
||||
|
||||
# Cycle Indicator
|
||||
# ------------------------------------
|
||||
# Hilbert Transform Indicator - SineWave
|
||||
hilbert = ta.HT_SINE(dataframe)
|
||||
dataframe['htsine'] = hilbert['sine']
|
||||
dataframe['htleadsine'] = hilbert['leadsine']
|
||||
|
||||
# Pattern Recognition - Bullish candlestick patterns
|
||||
# ------------------------------------
|
||||
# # Hammer: values [0, 100]
|
||||
# dataframe['CDLHAMMER'] = ta.CDLHAMMER(dataframe)
|
||||
# # Inverted Hammer: values [0, 100]
|
||||
# dataframe['CDLINVERTEDHAMMER'] = ta.CDLINVERTEDHAMMER(dataframe)
|
||||
# # Dragonfly Doji: values [0, 100]
|
||||
# dataframe['CDLDRAGONFLYDOJI'] = ta.CDLDRAGONFLYDOJI(dataframe)
|
||||
# # Piercing Line: values [0, 100]
|
||||
# dataframe['CDLPIERCING'] = ta.CDLPIERCING(dataframe) # values [0, 100]
|
||||
# # Morningstar: values [0, 100]
|
||||
# dataframe['CDLMORNINGSTAR'] = ta.CDLMORNINGSTAR(dataframe) # values [0, 100]
|
||||
# # Three White Soldiers: values [0, 100]
|
||||
# dataframe['CDL3WHITESOLDIERS'] = ta.CDL3WHITESOLDIERS(dataframe) # values [0, 100]
|
||||
|
||||
# Pattern Recognition - Bearish candlestick patterns
|
||||
# ------------------------------------
|
||||
# # Hanging Man: values [0, 100]
|
||||
# dataframe['CDLHANGINGMAN'] = ta.CDLHANGINGMAN(dataframe)
|
||||
# # Shooting Star: values [0, 100]
|
||||
# dataframe['CDLSHOOTINGSTAR'] = ta.CDLSHOOTINGSTAR(dataframe)
|
||||
# # Gravestone Doji: values [0, 100]
|
||||
# dataframe['CDLGRAVESTONEDOJI'] = ta.CDLGRAVESTONEDOJI(dataframe)
|
||||
# # Dark Cloud Cover: values [0, 100]
|
||||
# dataframe['CDLDARKCLOUDCOVER'] = ta.CDLDARKCLOUDCOVER(dataframe)
|
||||
# # Evening Doji Star: values [0, 100]
|
||||
# dataframe['CDLEVENINGDOJISTAR'] = ta.CDLEVENINGDOJISTAR(dataframe)
|
||||
# # Evening Star: values [0, 100]
|
||||
# dataframe['CDLEVENINGSTAR'] = ta.CDLEVENINGSTAR(dataframe)
|
||||
|
||||
# Pattern Recognition - Bullish/Bearish candlestick patterns
|
||||
# ------------------------------------
|
||||
# # Three Line Strike: values [0, -100, 100]
|
||||
# dataframe['CDL3LINESTRIKE'] = ta.CDL3LINESTRIKE(dataframe)
|
||||
# # Spinning Top: values [0, -100, 100]
|
||||
# dataframe['CDLSPINNINGTOP'] = ta.CDLSPINNINGTOP(dataframe) # values [0, -100, 100]
|
||||
# # Engulfing: values [0, -100, 100]
|
||||
# dataframe['CDLENGULFING'] = ta.CDLENGULFING(dataframe) # values [0, -100, 100]
|
||||
# # Harami: values [0, -100, 100]
|
||||
# dataframe['CDLHARAMI'] = ta.CDLHARAMI(dataframe) # values [0, -100, 100]
|
||||
# # Three Outside Up/Down: values [0, -100, 100]
|
||||
# dataframe['CDL3OUTSIDE'] = ta.CDL3OUTSIDE(dataframe) # values [0, -100, 100]
|
||||
# # Three Inside Up/Down: values [0, -100, 100]
|
||||
# dataframe['CDL3INSIDE'] = ta.CDL3INSIDE(dataframe) # values [0, -100, 100]
|
||||
|
||||
# # Chart type
|
||||
# # ------------------------------------
|
||||
# # Heikinashi stategy
|
||||
# heikinashi = qtpylib.heikinashi(dataframe)
|
||||
# dataframe['ha_open'] = heikinashi['open']
|
||||
# dataframe['ha_close'] = heikinashi['close']
|
||||
# dataframe['ha_high'] = heikinashi['high']
|
||||
# dataframe['ha_low'] = heikinashi['low']
|
||||
|
||||
# Retrieve best bid and best ask from the orderbook
|
||||
# ------------------------------------
|
||||
"""
|
||||
# first check if dataprovider is available
|
||||
if self.dp:
|
||||
if self.dp.runmode in ('live', 'dry_run'):
|
||||
ob = self.dp.orderbook(metadata['pair'], 1)
|
||||
dataframe['best_bid'] = ob['bids'][0][0]
|
||||
dataframe['best_ask'] = ob['asks'][0][0]
|
||||
"""
|
17
freqtrade/templates/subtemplates/indicators_minimal.j2
Normal file
17
freqtrade/templates/subtemplates/indicators_minimal.j2
Normal file
|
@ -0,0 +1,17 @@
|
|||
|
||||
# Momentum Indicators
|
||||
# ------------------------------------
|
||||
|
||||
# RSI
|
||||
dataframe['rsi'] = ta.RSI(dataframe)
|
||||
|
||||
# Retrieve best bid and best ask from the orderbook
|
||||
# ------------------------------------
|
||||
"""
|
||||
# first check if dataprovider is available
|
||||
if self.dp:
|
||||
if self.dp.runmode in ('live', 'dry_run'):
|
||||
ob = self.dp.orderbook(metadata['pair'], 1)
|
||||
dataframe['best_bid'] = ob['bids'][0][0]
|
||||
dataframe['best_ask'] = ob['asks'][0][0]
|
||||
"""
|
3
freqtrade/templates/subtemplates/sell_trend_full.j2
Normal file
3
freqtrade/templates/subtemplates/sell_trend_full.j2
Normal file
|
@ -0,0 +1,3 @@
|
|||
(qtpylib.crossed_above(dataframe['rsi'], 70)) & # Signal: RSI crosses above 70
|
||||
(dataframe['tema'] > dataframe['bb_middleband']) & # Guard: tema above BB middle
|
||||
(dataframe['tema'] < dataframe['tema'].shift(1)) & # Guard: tema is falling
|
1
freqtrade/templates/subtemplates/sell_trend_minimal.j2
Normal file
1
freqtrade/templates/subtemplates/sell_trend_minimal.j2
Normal file
|
@ -0,0 +1 @@
|
|||
(qtpylib.crossed_above(dataframe['rsi'], 70)) & # Signal: RSI crosses above 70
|
|
@ -1,3 +1,4 @@
|
|||
import csv
|
||||
import logging
|
||||
import sys
|
||||
from collections import OrderedDict
|
||||
|
@ -5,19 +6,21 @@ from pathlib import Path
|
|||
from typing import Any, Dict, List
|
||||
|
||||
import arrow
|
||||
import csv
|
||||
import rapidjson
|
||||
from tabulate import tabulate
|
||||
|
||||
from freqtrade import OperationalException
|
||||
from freqtrade.configuration import Configuration, TimeRange, remove_credentials
|
||||
from freqtrade.configuration.directory_operations import create_userdata_dir
|
||||
from freqtrade.configuration import (Configuration, TimeRange,
|
||||
remove_credentials)
|
||||
from freqtrade.configuration.directory_operations import (copy_sample_files,
|
||||
create_userdata_dir)
|
||||
from freqtrade.constants import USERPATH_HYPEROPTS, USERPATH_STRATEGY
|
||||
from freqtrade.data.history import (convert_trades_to_ohlcv,
|
||||
refresh_backtest_ohlcv_data,
|
||||
refresh_backtest_trades_data)
|
||||
from freqtrade.exchange import (available_exchanges, ccxt_exchanges, market_is_active,
|
||||
symbol_is_pair)
|
||||
from freqtrade.misc import plural
|
||||
from freqtrade.exchange import (available_exchanges, ccxt_exchanges,
|
||||
market_is_active, symbol_is_pair)
|
||||
from freqtrade.misc import plural, render_template
|
||||
from freqtrade.resolvers import ExchangeResolver
|
||||
from freqtrade.state import RunMode
|
||||
|
||||
|
@ -81,12 +84,95 @@ def start_create_userdir(args: Dict[str, Any]) -> None:
|
|||
:return: None
|
||||
"""
|
||||
if "user_data_dir" in args and args["user_data_dir"]:
|
||||
create_userdata_dir(args["user_data_dir"], create_dir=True)
|
||||
userdir = create_userdata_dir(args["user_data_dir"], create_dir=True)
|
||||
copy_sample_files(userdir, overwrite=args["reset"])
|
||||
else:
|
||||
logger.warning("`create-userdir` requires --userdir to be set.")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def deploy_new_strategy(strategy_name, strategy_path: Path, subtemplate: str):
|
||||
"""
|
||||
Deploy new strategy from template to strategy_path
|
||||
"""
|
||||
indicators = render_template(templatefile=f"subtemplates/indicators_{subtemplate}.j2",)
|
||||
buy_trend = render_template(templatefile=f"subtemplates/buy_trend_{subtemplate}.j2",)
|
||||
sell_trend = render_template(templatefile=f"subtemplates/sell_trend_{subtemplate}.j2",)
|
||||
|
||||
strategy_text = render_template(templatefile='base_strategy.py.j2',
|
||||
arguments={"strategy": strategy_name,
|
||||
"indicators": indicators,
|
||||
"buy_trend": buy_trend,
|
||||
"sell_trend": sell_trend,
|
||||
})
|
||||
|
||||
logger.info(f"Writing strategy to `{strategy_path}`.")
|
||||
strategy_path.write_text(strategy_text)
|
||||
|
||||
|
||||
def start_new_strategy(args: Dict[str, Any]) -> None:
|
||||
|
||||
config = setup_utils_configuration(args, RunMode.UTIL_NO_EXCHANGE)
|
||||
|
||||
if "strategy" in args and args["strategy"]:
|
||||
if args["strategy"] == "DefaultStrategy":
|
||||
raise OperationalException("DefaultStrategy is not allowed as name.")
|
||||
|
||||
new_path = config['user_data_dir'] / USERPATH_STRATEGY / (args["strategy"] + ".py")
|
||||
|
||||
if new_path.exists():
|
||||
raise OperationalException(f"`{new_path}` already exists. "
|
||||
"Please choose another Strategy Name.")
|
||||
|
||||
deploy_new_strategy(args['strategy'], new_path, args['template'])
|
||||
|
||||
else:
|
||||
raise OperationalException("`new-strategy` requires --strategy to be set.")
|
||||
|
||||
|
||||
def deploy_new_hyperopt(hyperopt_name, hyperopt_path: Path, subtemplate: str):
|
||||
"""
|
||||
Deploys a new hyperopt template to hyperopt_path
|
||||
"""
|
||||
buy_guards = render_template(
|
||||
templatefile=f"subtemplates/hyperopt_buy_guards_{subtemplate}.j2",)
|
||||
sell_guards = render_template(
|
||||
templatefile=f"subtemplates/hyperopt_sell_guards_{subtemplate}.j2",)
|
||||
buy_space = render_template(
|
||||
templatefile=f"subtemplates/hyperopt_buy_space_{subtemplate}.j2",)
|
||||
sell_space = render_template(
|
||||
templatefile=f"subtemplates/hyperopt_sell_space_{subtemplate}.j2",)
|
||||
|
||||
strategy_text = render_template(templatefile='base_hyperopt.py.j2',
|
||||
arguments={"hyperopt": hyperopt_name,
|
||||
"buy_guards": buy_guards,
|
||||
"sell_guards": sell_guards,
|
||||
"buy_space": buy_space,
|
||||
"sell_space": sell_space,
|
||||
})
|
||||
|
||||
logger.info(f"Writing hyperopt to `{hyperopt_path}`.")
|
||||
hyperopt_path.write_text(strategy_text)
|
||||
|
||||
|
||||
def start_new_hyperopt(args: Dict[str, Any]) -> None:
|
||||
|
||||
config = setup_utils_configuration(args, RunMode.UTIL_NO_EXCHANGE)
|
||||
|
||||
if "hyperopt" in args and args["hyperopt"]:
|
||||
if args["hyperopt"] == "DefaultHyperopt":
|
||||
raise OperationalException("DefaultHyperopt is not allowed as name.")
|
||||
|
||||
new_path = config['user_data_dir'] / USERPATH_HYPEROPTS / (args["hyperopt"] + ".py")
|
||||
|
||||
if new_path.exists():
|
||||
raise OperationalException(f"`{new_path}` already exists. "
|
||||
"Please choose another Strategy Name.")
|
||||
deploy_new_hyperopt(args['hyperopt'], new_path, args['template'])
|
||||
else:
|
||||
raise OperationalException("`new-hyperopt` requires --hyperopt to be set.")
|
||||
|
||||
|
||||
def start_download_data(args: Dict[str, Any]) -> None:
|
||||
"""
|
||||
Download data (former download_backtest_data.py script)
|
||||
|
|
|
@ -12,6 +12,7 @@ jsonschema==3.1.1
|
|||
TA-Lib==0.4.17
|
||||
tabulate==0.8.6
|
||||
coinmarketcap==5.0.3
|
||||
jinja2==2.10.3
|
||||
|
||||
# find first, C search in arrays
|
||||
py_find_1st==1.1.4
|
||||
|
|
1
setup.py
1
setup.py
|
@ -78,6 +78,7 @@ setup(name='freqtrade',
|
|||
'python-rapidjson',
|
||||
'sdnotify',
|
||||
'colorama',
|
||||
'jinja2',
|
||||
# from requirements.txt
|
||||
'numpy',
|
||||
'pandas',
|
||||
|
|
|
@ -869,6 +869,7 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog, testdatadir):
|
|||
'backtesting',
|
||||
'--config', 'config.json',
|
||||
'--datadir', str(testdatadir),
|
||||
'--strategy-path', str(Path(__file__).parents[2] / 'freqtrade/templates'),
|
||||
'--ticker-interval', '1m',
|
||||
'--timerange', '1510694220-1510700340',
|
||||
'--enable-position-stacking',
|
||||
|
|
|
@ -36,13 +36,15 @@ def test_search_strategy():
|
|||
|
||||
|
||||
def test_load_strategy(default_conf, result):
|
||||
default_conf.update({'strategy': 'SampleStrategy'})
|
||||
default_conf.update({'strategy': 'SampleStrategy',
|
||||
'strategy_path': str(Path(__file__).parents[2] / 'freqtrade/templates')
|
||||
})
|
||||
resolver = StrategyResolver(default_conf)
|
||||
assert 'rsi' in resolver.strategy.advise_indicators(result, {'pair': 'ETH/BTC'})
|
||||
|
||||
|
||||
def test_load_strategy_base64(result, caplog, default_conf):
|
||||
with open("user_data/strategies/sample_strategy.py", "rb") as file:
|
||||
with (Path(__file__).parents[2] / 'freqtrade/templates/sample_strategy.py').open("rb") as file:
|
||||
encoded_string = urlsafe_b64encode(file.read()).decode("utf-8")
|
||||
default_conf.update({'strategy': 'SampleStrategy:{}'.format(encoded_string)})
|
||||
|
||||
|
@ -54,10 +56,10 @@ def test_load_strategy_base64(result, caplog, default_conf):
|
|||
|
||||
|
||||
def test_load_strategy_invalid_directory(result, caplog, default_conf):
|
||||
default_conf['strategy'] = 'SampleStrategy'
|
||||
default_conf['strategy'] = 'DefaultStrategy'
|
||||
resolver = StrategyResolver(default_conf)
|
||||
extra_dir = Path.cwd() / 'some/path'
|
||||
resolver._load_strategy('SampleStrategy', config=default_conf, extra_dir=extra_dir)
|
||||
resolver._load_strategy('DefaultStrategy', config=default_conf, extra_dir=extra_dir)
|
||||
|
||||
assert log_has_re(r'Path .*' + r'some.*path.*' + r'.* does not exist', caplog)
|
||||
|
||||
|
|
|
@ -17,8 +17,6 @@ from freqtrade.configuration.config_validation import validate_config_schema
|
|||
from freqtrade.configuration.deprecated_settings import (
|
||||
check_conflicting_settings, process_deprecated_setting,
|
||||
process_temporary_deprecated_settings)
|
||||
from freqtrade.configuration.directory_operations import (create_datadir,
|
||||
create_userdata_dir)
|
||||
from freqtrade.configuration.load_config import load_config_file
|
||||
from freqtrade.constants import DEFAULT_DB_DRYRUN_URL, DEFAULT_DB_PROD_URL
|
||||
from freqtrade.loggers import _set_loggers
|
||||
|
@ -670,45 +668,6 @@ def test_validate_default_conf(default_conf) -> None:
|
|||
validate(default_conf, constants.CONF_SCHEMA, Draft4Validator)
|
||||
|
||||
|
||||
def test_create_datadir(mocker, default_conf, caplog) -> None:
|
||||
mocker.patch.object(Path, "is_dir", MagicMock(return_value=False))
|
||||
md = mocker.patch.object(Path, 'mkdir', MagicMock())
|
||||
|
||||
create_datadir(default_conf, '/foo/bar')
|
||||
assert md.call_args[1]['parents'] is True
|
||||
assert log_has('Created data directory: /foo/bar', caplog)
|
||||
|
||||
|
||||
def test_create_userdata_dir(mocker, default_conf, caplog) -> None:
|
||||
mocker.patch.object(Path, "is_dir", MagicMock(return_value=False))
|
||||
md = mocker.patch.object(Path, 'mkdir', MagicMock())
|
||||
|
||||
x = create_userdata_dir('/tmp/bar', create_dir=True)
|
||||
assert md.call_count == 7
|
||||
assert md.call_args[1]['parents'] is False
|
||||
assert log_has(f'Created user-data directory: {Path("/tmp/bar")}', caplog)
|
||||
assert isinstance(x, Path)
|
||||
assert str(x) == str(Path("/tmp/bar"))
|
||||
|
||||
|
||||
def test_create_userdata_dir_exists(mocker, default_conf, caplog) -> None:
|
||||
mocker.patch.object(Path, "is_dir", MagicMock(return_value=True))
|
||||
md = mocker.patch.object(Path, 'mkdir', MagicMock())
|
||||
|
||||
create_userdata_dir('/tmp/bar')
|
||||
assert md.call_count == 0
|
||||
|
||||
|
||||
def test_create_userdata_dir_exists_exception(mocker, default_conf, caplog) -> None:
|
||||
mocker.patch.object(Path, "is_dir", MagicMock(return_value=False))
|
||||
md = mocker.patch.object(Path, 'mkdir', MagicMock())
|
||||
|
||||
with pytest.raises(OperationalException,
|
||||
match=r'Directory `.{1,2}tmp.{1,2}bar` does not exist.*'):
|
||||
create_userdata_dir('/tmp/bar', create_dir=False)
|
||||
assert md.call_count == 0
|
||||
|
||||
|
||||
def test_validate_tsl(default_conf):
|
||||
default_conf['stoploss'] = 0.0
|
||||
with pytest.raises(OperationalException, match='The config stoploss needs to be different '
|
||||
|
|
91
tests/test_directory_operations.py
Normal file
91
tests/test_directory_operations.py
Normal file
|
@ -0,0 +1,91 @@
|
|||
# pragma pylint: disable=missing-docstring, protected-access, invalid-name
|
||||
from pathlib import Path
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
|
||||
from freqtrade import OperationalException
|
||||
from freqtrade.configuration.directory_operations import (copy_sample_files,
|
||||
create_datadir,
|
||||
create_userdata_dir)
|
||||
from tests.conftest import log_has, log_has_re
|
||||
|
||||
|
||||
def test_create_datadir(mocker, default_conf, caplog) -> None:
|
||||
mocker.patch.object(Path, "is_dir", MagicMock(return_value=False))
|
||||
md = mocker.patch.object(Path, 'mkdir', MagicMock())
|
||||
|
||||
create_datadir(default_conf, '/foo/bar')
|
||||
assert md.call_args[1]['parents'] is True
|
||||
assert log_has('Created data directory: /foo/bar', caplog)
|
||||
|
||||
|
||||
def test_create_userdata_dir(mocker, default_conf, caplog) -> None:
|
||||
mocker.patch.object(Path, "is_dir", MagicMock(return_value=False))
|
||||
md = mocker.patch.object(Path, 'mkdir', MagicMock())
|
||||
|
||||
x = create_userdata_dir('/tmp/bar', create_dir=True)
|
||||
assert md.call_count == 8
|
||||
assert md.call_args[1]['parents'] is False
|
||||
assert log_has(f'Created user-data directory: {Path("/tmp/bar")}', caplog)
|
||||
assert isinstance(x, Path)
|
||||
assert str(x) == str(Path("/tmp/bar"))
|
||||
|
||||
|
||||
def test_create_userdata_dir_exists(mocker, default_conf, caplog) -> None:
|
||||
mocker.patch.object(Path, "is_dir", MagicMock(return_value=True))
|
||||
md = mocker.patch.object(Path, 'mkdir', MagicMock())
|
||||
|
||||
create_userdata_dir('/tmp/bar')
|
||||
assert md.call_count == 0
|
||||
|
||||
|
||||
def test_create_userdata_dir_exists_exception(mocker, default_conf, caplog) -> None:
|
||||
mocker.patch.object(Path, "is_dir", MagicMock(return_value=False))
|
||||
md = mocker.patch.object(Path, 'mkdir', MagicMock())
|
||||
|
||||
with pytest.raises(OperationalException,
|
||||
match=r'Directory `.{1,2}tmp.{1,2}bar` does not exist.*'):
|
||||
create_userdata_dir('/tmp/bar', create_dir=False)
|
||||
assert md.call_count == 0
|
||||
|
||||
|
||||
def test_copy_sample_files(mocker, default_conf, caplog) -> None:
|
||||
mocker.patch.object(Path, "is_dir", MagicMock(return_value=True))
|
||||
mocker.patch.object(Path, "exists", MagicMock(return_value=False))
|
||||
copymock = mocker.patch('shutil.copy', MagicMock())
|
||||
|
||||
copy_sample_files(Path('/tmp/bar'))
|
||||
assert copymock.call_count == 5
|
||||
assert copymock.call_args_list[0][0][1] == str(
|
||||
Path('/tmp/bar') / 'strategies/sample_strategy.py')
|
||||
assert copymock.call_args_list[1][0][1] == str(
|
||||
Path('/tmp/bar') / 'hyperopts/sample_hyperopt_advanced.py')
|
||||
assert copymock.call_args_list[2][0][1] == str(
|
||||
Path('/tmp/bar') / 'hyperopts/sample_hyperopt_loss.py')
|
||||
assert copymock.call_args_list[3][0][1] == str(
|
||||
Path('/tmp/bar') / 'hyperopts/sample_hyperopt.py')
|
||||
assert copymock.call_args_list[4][0][1] == str(
|
||||
Path('/tmp/bar') / 'notebooks/strategy_analysis_example.ipynb')
|
||||
|
||||
|
||||
def test_copy_sample_files_errors(mocker, default_conf, caplog) -> None:
|
||||
mocker.patch.object(Path, "is_dir", MagicMock(return_value=False))
|
||||
mocker.patch.object(Path, "exists", MagicMock(return_value=False))
|
||||
mocker.patch('shutil.copy', MagicMock())
|
||||
with pytest.raises(OperationalException,
|
||||
match=r"Directory `.{1,2}tmp.{1,2}bar` does not exist\."):
|
||||
copy_sample_files(Path('/tmp/bar'))
|
||||
|
||||
mocker.patch.object(Path, "is_dir", MagicMock(side_effect=[True, False]))
|
||||
|
||||
with pytest.raises(OperationalException,
|
||||
match=r"Directory `.{1,2}tmp.{1,2}bar.{1,2}strategies` does not exist\."):
|
||||
copy_sample_files(Path('/tmp/bar'))
|
||||
mocker.patch.object(Path, "is_dir", MagicMock(return_value=True))
|
||||
mocker.patch.object(Path, "exists", MagicMock(return_value=True))
|
||||
copy_sample_files(Path('/tmp/bar'))
|
||||
assert log_has_re(r"File `.*` exists already, not deploying sample file\.", caplog)
|
||||
caplog.clear()
|
||||
copy_sample_files(Path('/tmp/bar'), overwrite=True)
|
||||
assert log_has_re(r"File `.*` exists already, overwriting\.", caplog)
|
|
@ -9,8 +9,9 @@ from freqtrade.state import RunMode
|
|||
from freqtrade.utils import (setup_utils_configuration, start_create_userdir,
|
||||
start_download_data, start_list_exchanges,
|
||||
start_list_markets, start_list_timeframes,
|
||||
start_new_hyperopt, start_new_strategy,
|
||||
start_trading)
|
||||
from tests.conftest import get_args, log_has, patch_exchange
|
||||
from tests.conftest import get_args, log_has, log_has_re, patch_exchange
|
||||
|
||||
|
||||
def test_setup_utils_configuration():
|
||||
|
@ -442,6 +443,7 @@ def test_create_datadir_failed(caplog):
|
|||
|
||||
def test_create_datadir(caplog, mocker):
|
||||
cud = mocker.patch("freqtrade.utils.create_userdata_dir", MagicMock())
|
||||
csf = mocker.patch("freqtrade.utils.copy_sample_files", MagicMock())
|
||||
args = [
|
||||
"create-userdir",
|
||||
"--userdir",
|
||||
|
@ -450,9 +452,82 @@ def test_create_datadir(caplog, mocker):
|
|||
start_create_userdir(get_args(args))
|
||||
|
||||
assert cud.call_count == 1
|
||||
assert csf.call_count == 1
|
||||
assert len(caplog.record_tuples) == 0
|
||||
|
||||
|
||||
def test_start_new_strategy(mocker, caplog):
|
||||
wt_mock = mocker.patch.object(Path, "write_text", MagicMock())
|
||||
mocker.patch.object(Path, "exists", MagicMock(return_value=False))
|
||||
|
||||
args = [
|
||||
"new-strategy",
|
||||
"--strategy",
|
||||
"CoolNewStrategy"
|
||||
]
|
||||
start_new_strategy(get_args(args))
|
||||
|
||||
assert wt_mock.call_count == 1
|
||||
assert "CoolNewStrategy" in wt_mock.call_args_list[0][0][0]
|
||||
assert log_has_re("Writing strategy to .*", caplog)
|
||||
|
||||
|
||||
def test_start_new_strategy_DefaultStrat(mocker, caplog):
|
||||
args = [
|
||||
"new-strategy",
|
||||
"--strategy",
|
||||
"DefaultStrategy"
|
||||
]
|
||||
with pytest.raises(OperationalException,
|
||||
match=r"DefaultStrategy is not allowed as name\."):
|
||||
start_new_strategy(get_args(args))
|
||||
|
||||
|
||||
def test_start_new_strategy_no_arg(mocker, caplog):
|
||||
args = [
|
||||
"new-strategy",
|
||||
]
|
||||
with pytest.raises(OperationalException,
|
||||
match="`new-strategy` requires --strategy to be set."):
|
||||
start_new_strategy(get_args(args))
|
||||
|
||||
|
||||
def test_start_new_hyperopt(mocker, caplog):
|
||||
wt_mock = mocker.patch.object(Path, "write_text", MagicMock())
|
||||
mocker.patch.object(Path, "exists", MagicMock(return_value=False))
|
||||
|
||||
args = [
|
||||
"new-hyperopt",
|
||||
"--hyperopt",
|
||||
"CoolNewhyperopt"
|
||||
]
|
||||
start_new_hyperopt(get_args(args))
|
||||
|
||||
assert wt_mock.call_count == 1
|
||||
assert "CoolNewhyperopt" in wt_mock.call_args_list[0][0][0]
|
||||
assert log_has_re("Writing hyperopt to .*", caplog)
|
||||
|
||||
|
||||
def test_start_new_hyperopt_DefaultHyperopt(mocker, caplog):
|
||||
args = [
|
||||
"new-hyperopt",
|
||||
"--hyperopt",
|
||||
"DefaultHyperopt"
|
||||
]
|
||||
with pytest.raises(OperationalException,
|
||||
match=r"DefaultHyperopt is not allowed as name\."):
|
||||
start_new_hyperopt(get_args(args))
|
||||
|
||||
|
||||
def test_start_new_hyperopt_no_arg(mocker, caplog):
|
||||
args = [
|
||||
"new-hyperopt",
|
||||
]
|
||||
with pytest.raises(OperationalException,
|
||||
match="`new-hyperopt` requires --hyperopt to be set."):
|
||||
start_new_hyperopt(get_args(args))
|
||||
|
||||
|
||||
def test_download_data_keyboardInterrupt(mocker, caplog, markets):
|
||||
dl_mock = mocker.patch('freqtrade.utils.refresh_backtest_ohlcv_data',
|
||||
MagicMock(side_effect=KeyboardInterrupt))
|
||||
|
|
Loading…
Reference in New Issue
Block a user