Merge pull request #2465 from freqtrade/hyperopt_populate_from_strategy

Hyperopt populate from strategy
This commit is contained in:
Matthias 2019-11-03 10:03:33 +01:00 committed by GitHub
commit 500d16620b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 48 additions and 59 deletions

View File

@ -32,7 +32,7 @@ jobs:
name: backtest name: backtest
- script: - script:
- cp config.json.example config.json - cp config.json.example config.json
- freqtrade --datadir tests/testdata hyperopt -e 5 - freqtrade --datadir tests/testdata --strategy SampleStrategy hyperopt --customhyperopt SampleHyperOpts -e 5
name: hyperopt name: hyperopt
- script: flake8 - script: flake8
name: flake8 name: flake8

View File

@ -23,17 +23,23 @@ Configuring hyperopt is similar to writing your own strategy, and many tasks wil
Depending on the space you want to optimize, only some of the below are required: Depending on the space you want to optimize, only some of the below are required:
* fill `populate_indicators` - probably a copy from your strategy
* fill `buy_strategy_generator` - for buy signal optimization * fill `buy_strategy_generator` - for buy signal optimization
* fill `indicator_space` - for buy signal optimzation * fill `indicator_space` - for buy signal optimzation
* fill `sell_strategy_generator` - for sell signal optimization * fill `sell_strategy_generator` - for sell signal optimization
* fill `sell_indicator_space` - for sell signal optimzation * fill `sell_indicator_space` - for sell signal optimzation
Optional, but recommended: !!! Note
`populate_indicators` needs to create all indicators any of thee spaces may use, otherwise hyperopt will not work.
Optional - can also be loaded from a strategy:
* copy `populate_indicators` from your strategy - otherwise default-strategy will be used
* copy `populate_buy_trend` from your strategy - otherwise default-strategy will be used * copy `populate_buy_trend` from your strategy - otherwise default-strategy will be used
* copy `populate_sell_trend` from your strategy - otherwise default-strategy will be used * copy `populate_sell_trend` from your strategy - otherwise default-strategy will be used
!!! Note
Assuming the optional methods are not in your hyperopt file, please use `--strategy AweSomeStrategy` which contains these methods so hyperopt can use these methods instead.
Rarely you may also need to override: Rarely you may also need to override:
* `roi_space` - for custom ROI optimization (if you need the ranges for the ROI parameters in the optimization hyperspace that differ from default) * `roi_space` - for custom ROI optimization (if you need the ranges for the ROI parameters in the optimization hyperspace that differ from default)
@ -156,7 +162,7 @@ that minimizes the value of the [loss function](#loss-functions).
The above setup expects to find ADX, RSI and Bollinger Bands in the populated indicators. The above setup expects to find ADX, RSI and Bollinger Bands in the populated indicators.
When you want to test an indicator that isn't used by the bot currently, remember to When you want to test an indicator that isn't used by the bot currently, remember to
add it to the `populate_indicators()` method in `hyperopt.py`. add it to the `populate_indicators()` method in your custom hyperopt file.
## Loss-functions ## Loss-functions
@ -270,6 +276,14 @@ For example, to use one month of data, pass the following parameter to the hyper
freqtrade hyperopt --timerange 20180401-20180501 freqtrade hyperopt --timerange 20180401-20180501
``` ```
### Running Hyperopt using methods from a strategy
Hyperopt can reuse `populate_indicators`, `populate_buy_trend`, `populate_sell_trend` from your strategy, assuming these methods are **not** in your custom hyperopt file, and a strategy is provided.
```bash
freqtrade --strategy SampleStrategy hyperopt --customhyperopt SampleHyperopt
```
### Running Hyperopt with Smaller Search Space ### Running Hyperopt with Smaller Search Space
Use the `--spaces` argument to limit the search space used by hyperopt. Use the `--spaces` argument to limit the search space used by hyperopt.
@ -341,8 +355,7 @@ So for example you had `rsi-value: 29.0` so we would look at `rsi`-block, that t
(dataframe['rsi'] < 29.0) (dataframe['rsi'] < 29.0)
``` ```
Translating your whole hyperopt result as the new buy-signal Translating your whole hyperopt result as the new buy-signal would then look like:
would then look like:
```python ```python
def populate_buy_trend(self, dataframe: DataFrame) -> DataFrame: def populate_buy_trend(self, dataframe: DataFrame) -> DataFrame:

View File

@ -5,10 +5,9 @@ This module defines the interface to apply for hyperopts
import logging import logging
import math import math
from abc import ABC, abstractmethod from abc import ABC
from typing import Dict, Any, Callable, List from typing import Dict, Any, Callable, List
from pandas import DataFrame
from skopt.space import Dimension, Integer, Real from skopt.space import Dimension, Integer, Real
from freqtrade import OperationalException from freqtrade import OperationalException
@ -42,15 +41,6 @@ class IHyperOpt(ABC):
# Assign ticker_interval to be used in hyperopt # Assign ticker_interval to be used in hyperopt
IHyperOpt.ticker_interval = str(config['ticker_interval']) IHyperOpt.ticker_interval = str(config['ticker_interval'])
@staticmethod
@abstractmethod
def populate_indicators(dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Populate indicators that will be used in the Buy and Sell strategy.
:param dataframe: Raw data from the exchange and parsed by parse_ticker_dataframe().
:return: A Dataframe with all mandatory indicators for the strategies.
"""
@staticmethod @staticmethod
def buy_strategy_generator(params: Dict[str, Any]) -> Callable: def buy_strategy_generator(params: Dict[str, Any]) -> Callable:
""" """

View File

@ -34,6 +34,9 @@ class HyperOptResolver(IResolver):
self.hyperopt = self._load_hyperopt(hyperopt_name, config, self.hyperopt = self._load_hyperopt(hyperopt_name, config,
extra_dir=config.get('hyperopt_path')) extra_dir=config.get('hyperopt_path'))
if not hasattr(self.hyperopt, 'populate_indicators'):
logger.warning("Hyperopt class does not provide populate_indicators() method. "
"Using populate_indicators from the strategy.")
if not hasattr(self.hyperopt, 'populate_buy_trend'): if not hasattr(self.hyperopt, 'populate_buy_trend'):
logger.warning("Hyperopt class does not provide populate_buy_trend() method. " logger.warning("Hyperopt class does not provide populate_buy_trend() method. "
"Using populate_buy_trend from the strategy.") "Using populate_buy_trend from the strategy.")

View File

@ -149,6 +149,7 @@ def test_hyperoptresolver(mocker, default_conf, caplog) -> None:
patched_configuration_load_config_file(mocker, default_conf) patched_configuration_load_config_file(mocker, default_conf)
hyperopt = DefaultHyperOpt hyperopt = DefaultHyperOpt
delattr(hyperopt, 'populate_indicators')
delattr(hyperopt, 'populate_buy_trend') delattr(hyperopt, 'populate_buy_trend')
delattr(hyperopt, 'populate_sell_trend') delattr(hyperopt, 'populate_sell_trend')
mocker.patch( mocker.patch(
@ -156,8 +157,11 @@ def test_hyperoptresolver(mocker, default_conf, caplog) -> None:
MagicMock(return_value=hyperopt(default_conf)) MagicMock(return_value=hyperopt(default_conf))
) )
x = HyperOptResolver(default_conf, ).hyperopt x = HyperOptResolver(default_conf, ).hyperopt
assert not hasattr(x, 'populate_indicators')
assert not hasattr(x, 'populate_buy_trend') assert not hasattr(x, 'populate_buy_trend')
assert not hasattr(x, 'populate_sell_trend') assert not hasattr(x, 'populate_sell_trend')
assert log_has("Hyperopt class does not provide populate_indicators() method. "
"Using populate_indicators from the strategy.", caplog)
assert log_has("Hyperopt class does not provide populate_sell_trend() method. " assert log_has("Hyperopt class does not provide populate_sell_trend() method. "
"Using populate_sell_trend from the strategy.", caplog) "Using populate_sell_trend from the strategy.", caplog)
assert log_has("Hyperopt class does not provide populate_buy_trend() method. " assert log_has("Hyperopt class does not provide populate_buy_trend() method. "

View File

@ -2,12 +2,11 @@
from functools import reduce from functools import reduce
from typing import Any, Callable, Dict, List from typing import Any, Callable, Dict, List
from datetime import datetime
import numpy as np import numpy as np # noqa
import talib.abstract as ta import talib.abstract as ta
from pandas import DataFrame 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 import freqtrade.vendor.qtpylib.indicators as qtpylib
from freqtrade.optimize.hyperopt_interface import IHyperOpt from freqtrade.optimize.hyperopt_interface import IHyperOpt
@ -34,34 +33,6 @@ class SampleHyperOpts(IHyperOpt):
Sample implementation of these methods can be found in Sample implementation of these methods can be found in
https://github.com/freqtrade/freqtrade/blob/develop/user_data/hyperopts/sample_hyperopt_advanced.py https://github.com/freqtrade/freqtrade/blob/develop/user_data/hyperopts/sample_hyperopt_advanced.py
""" """
@staticmethod
def populate_indicators(dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Add several indicators needed for buy and sell strategies defined below.
"""
# ADX
dataframe['adx'] = ta.ADX(dataframe)
# MACD
macd = ta.MACD(dataframe)
dataframe['macd'] = macd['macd']
dataframe['macdsignal'] = macd['macdsignal']
# MFI
dataframe['mfi'] = ta.MFI(dataframe)
# RSI
dataframe['rsi'] = ta.RSI(dataframe)
# Stochastic Fast
stoch_fast = ta.STOCHF(dataframe)
dataframe['fastd'] = stoch_fast['fastd']
# Minus-DI
dataframe['minus_di'] = ta.MINUS_DI(dataframe)
# Bollinger bands
bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2)
dataframe['bb_lowerband'] = bollinger['lower']
dataframe['bb_upperband'] = bollinger['upper']
# SAR
dataframe['sar'] = ta.SAR(dataframe)
return dataframe
@staticmethod @staticmethod
def buy_strategy_generator(params: Dict[str, Any]) -> Callable: def buy_strategy_generator(params: Dict[str, Any]) -> Callable:

View File

@ -37,6 +37,9 @@ class AdvancedSampleHyperOpts(IHyperOpt):
""" """
@staticmethod @staticmethod
def populate_indicators(dataframe: DataFrame, metadata: dict) -> DataFrame: def populate_indicators(dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
This method can also be loaded from the strategy, if it doesn't exist in the hyperopt class.
"""
dataframe['adx'] = ta.ADX(dataframe) dataframe['adx'] = ta.ADX(dataframe)
macd = ta.MACD(dataframe) macd = ta.MACD(dataframe)
dataframe['macd'] = macd['macd'] dataframe['macd'] = macd['macd']
@ -229,8 +232,10 @@ class AdvancedSampleHyperOpts(IHyperOpt):
def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
""" """
Based on TA indicators. Should be a copy of from strategy Based on TA indicators.
must align to populate_indicators in this file Can be a copy of the corresponding method from the strategy,
or will be loaded from the strategy.
Must align to populate_indicators used (either from this File, or from the strategy)
Only used when --spaces does not include buy Only used when --spaces does not include buy
""" """
dataframe.loc[ dataframe.loc[
@ -246,8 +251,10 @@ class AdvancedSampleHyperOpts(IHyperOpt):
def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
""" """
Based on TA indicators. Should be a copy of from strategy Based on TA indicators.
must align to populate_indicators in this file Can be a copy of the corresponding method from the strategy,
or will be loaded from the strategy.
Must align to populate_indicators used (either from this File, or from the strategy)
Only used when --spaces does not include sell Only used when --spaces does not include sell
""" """
dataframe.loc[ dataframe.loc[

View File

@ -107,16 +107,16 @@ class SampleStrategy(IStrategy):
# RSI # RSI
dataframe['rsi'] = ta.RSI(dataframe) dataframe['rsi'] = ta.RSI(dataframe)
"""
# ADX # ADX
dataframe['adx'] = ta.ADX(dataframe) dataframe['adx'] = ta.ADX(dataframe)
"""
# Awesome oscillator # Awesome oscillator
dataframe['ao'] = qtpylib.awesome_oscillator(dataframe) dataframe['ao'] = qtpylib.awesome_oscillator(dataframe)
# Commodity Channel Index: values Oversold:<-100, Overbought:>100 # Commodity Channel Index: values Oversold:<-100, Overbought:>100
dataframe['cci'] = ta.CCI(dataframe) dataframe['cci'] = ta.CCI(dataframe)
"""
# MACD # MACD
macd = ta.MACD(dataframe) macd = ta.MACD(dataframe)
dataframe['macd'] = macd['macd'] dataframe['macd'] = macd['macd']
@ -126,6 +126,7 @@ class SampleStrategy(IStrategy):
# MFI # MFI
dataframe['mfi'] = ta.MFI(dataframe) dataframe['mfi'] = ta.MFI(dataframe)
"""
# Minus Directional Indicator / Movement # Minus Directional Indicator / Movement
dataframe['minus_dm'] = ta.MINUS_DM(dataframe) dataframe['minus_dm'] = ta.MINUS_DM(dataframe)
dataframe['minus_di'] = ta.MINUS_DI(dataframe) dataframe['minus_di'] = ta.MINUS_DI(dataframe)
@ -149,12 +150,13 @@ class SampleStrategy(IStrategy):
stoch = ta.STOCH(dataframe) stoch = ta.STOCH(dataframe)
dataframe['slowd'] = stoch['slowd'] dataframe['slowd'] = stoch['slowd']
dataframe['slowk'] = stoch['slowk'] dataframe['slowk'] = stoch['slowk']
"""
# Stoch fast # Stoch fast
stoch_fast = ta.STOCHF(dataframe) stoch_fast = ta.STOCHF(dataframe)
dataframe['fastd'] = stoch_fast['fastd'] dataframe['fastd'] = stoch_fast['fastd']
dataframe['fastk'] = stoch_fast['fastk'] dataframe['fastk'] = stoch_fast['fastk']
"""
# Stoch RSI # Stoch RSI
stoch_rsi = ta.STOCHRSI(dataframe) stoch_rsi = ta.STOCHRSI(dataframe)
dataframe['fastd_rsi'] = stoch_rsi['fastd'] dataframe['fastd_rsi'] = stoch_rsi['fastd']
@ -178,12 +180,11 @@ class SampleStrategy(IStrategy):
dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50)
dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100)
# SAR Parabol
dataframe['sar'] = ta.SAR(dataframe)
# SMA - Simple Moving Average # SMA - Simple Moving Average
dataframe['sma'] = ta.SMA(dataframe, timeperiod=40) dataframe['sma'] = ta.SMA(dataframe, timeperiod=40)
""" """
# SAR Parabol
dataframe['sar'] = ta.SAR(dataframe)
# TEMA - Triple Exponential Moving Average # TEMA - Triple Exponential Moving Average
dataframe['tema'] = ta.TEMA(dataframe, timeperiod=9) dataframe['tema'] = ta.TEMA(dataframe, timeperiod=9)