hyperopt - freqai - docs and refactoring
This commit is contained in:
parent
1820bc6832
commit
8d16dd804d
@ -279,6 +279,8 @@ The FreqAI strategy requires the user to include the following lines of code in
|
|||||||
|
|
||||||
Notice how the `populate_any_indicators()` is where the user adds their own features ([more information](#feature-engineering)) and labels ([more information](#setting-classifier-targets)). See a full example at `templates/FreqaiExampleStrategy.py`.
|
Notice how the `populate_any_indicators()` is where the user adds their own features ([more information](#feature-engineering)) and labels ([more information](#setting-classifier-targets)). See a full example at `templates/FreqaiExampleStrategy.py`.
|
||||||
|
|
||||||
|
*Important*: The `self.freqai.start()` function cannot be called outside the `populate_indicators()`.
|
||||||
|
|
||||||
### Setting the `startup_candle_count`
|
### Setting the `startup_candle_count`
|
||||||
Users need to take care to set the `startup_candle_count` in their strategy the same way they would for any normal Freqtrade strategy (see details [here](strategy-customization.md#strategy-startup-period)). This value is used by Freqtrade to ensure that a sufficient amount of data is provided when calling on the `dataprovider` to avoid any NaNs at the beginning of the first training. Users can easily set this value by identifying the longest period (in candle units) that they pass to their indicator creation functions (e.g. talib functions). In the present example, the user would pass 20 to as this value (since it is the maximum value in their `indicators_periods_candles`).
|
Users need to take care to set the `startup_candle_count` in their strategy the same way they would for any normal Freqtrade strategy (see details [here](strategy-customization.md#strategy-startup-period)). This value is used by Freqtrade to ensure that a sufficient amount of data is provided when calling on the `dataprovider` to avoid any NaNs at the beginning of the first training. Users can easily set this value by identifying the longest period (in candle units) that they pass to their indicator creation functions (e.g. talib functions). In the present example, the user would pass 20 to as this value (since it is the maximum value in their `indicators_periods_candles`).
|
||||||
|
|
||||||
@ -532,6 +534,15 @@ for each pair, for each backtesting window within the expanded `--timerange`.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### Hyperopt
|
||||||
|
|
||||||
|
The [Hyperopt](hyperopt.md) module can be executed with some restrictions:
|
||||||
|
|
||||||
|
- The `--analyze-per-epoch` hyperopt parameter is not compatible with FreqAI.
|
||||||
|
- It's not possible to hyperopt indicators in `populate_any_indicators()` function. This means that the user cannot optimize model parameters using hyperopt. Apart from this exception, it is possible to optimize all other [spaces](hyperopt.md###runninghyperoptwithsmallersearchspace).
|
||||||
|
- The [Backtesting](#backtesting) instructions also apply apply to Hyperopt.
|
||||||
|
|
||||||
|
|
||||||
### Deciding the size of the sliding training window and backtesting duration
|
### Deciding the size of the sliding training window and backtesting duration
|
||||||
|
|
||||||
The user defines the backtesting timerange with the typical `--timerange` parameter in the
|
The user defines the backtesting timerange with the typical `--timerange` parameter in the
|
||||||
|
@ -285,6 +285,7 @@ class Configuration:
|
|||||||
logger.info('Parameter --stoplosses detected: %s ...', self.args["stoploss_range"])
|
logger.info('Parameter --stoplosses detected: %s ...', self.args["stoploss_range"])
|
||||||
|
|
||||||
# Hyperopt section
|
# Hyperopt section
|
||||||
|
self._check_hyperopt_analyze_per_epoch_freqai()
|
||||||
self._args_to_config(config, argname='hyperopt',
|
self._args_to_config(config, argname='hyperopt',
|
||||||
logstring='Using Hyperopt class name: {}')
|
logstring='Using Hyperopt class name: {}')
|
||||||
|
|
||||||
@ -537,3 +538,12 @@ class Configuration:
|
|||||||
config['pairs'] = load_file(pairs_file)
|
config['pairs'] = load_file(pairs_file)
|
||||||
if 'pairs' in config and isinstance(config['pairs'], list):
|
if 'pairs' in config and isinstance(config['pairs'], list):
|
||||||
config['pairs'].sort()
|
config['pairs'].sort()
|
||||||
|
|
||||||
|
def _check_hyperopt_analyze_per_epoch_freqai(self) -> None:
|
||||||
|
"""
|
||||||
|
Helper for block hyperopt with analyze-per-epoch param.
|
||||||
|
"""
|
||||||
|
if ("analyze_per_epoch" in self.args and
|
||||||
|
self.args["analyze_per_epoch"] and "freqaimodel" in self.args):
|
||||||
|
raise OperationalException('analyze-per-epoch parameter is \
|
||||||
|
not allowed with a Freqai strategy.')
|
||||||
|
@ -93,16 +93,6 @@ class FreqaiDataDrawer:
|
|||||||
"model_filename": "", "trained_timestamp": 0,
|
"model_filename": "", "trained_timestamp": 0,
|
||||||
"priority": 1, "first": True, "data_path": "", "extras": {}}
|
"priority": 1, "first": True, "data_path": "", "extras": {}}
|
||||||
|
|
||||||
def __getstate__(self):
|
|
||||||
"""
|
|
||||||
Return state values to be pickled.
|
|
||||||
It's necessary to allow serialization in hyperopt
|
|
||||||
"""
|
|
||||||
return ({
|
|
||||||
"pair_dict": self.pair_dict,
|
|
||||||
"pair_dictionary_path": self.pair_dictionary_path
|
|
||||||
})
|
|
||||||
|
|
||||||
def load_drawer_from_disk(self):
|
def load_drawer_from_disk(self):
|
||||||
"""
|
"""
|
||||||
Locate and load a previously saved data drawer full of all pair model metadata in
|
Locate and load a previously saved data drawer full of all pair model metadata in
|
||||||
@ -165,21 +155,13 @@ class FreqaiDataDrawer:
|
|||||||
# create a backup
|
# create a backup
|
||||||
shutil.copy(self.historic_predictions_path, self.historic_predictions_bkp_path)
|
shutil.copy(self.historic_predictions_path, self.historic_predictions_bkp_path)
|
||||||
|
|
||||||
def save_drawer_to_disk(self, live=False):
|
def save_drawer_to_disk(self):
|
||||||
"""
|
"""
|
||||||
Save data drawer full of all pair model metadata in present model folder.
|
Save data drawer full of all pair model metadata in present model folder.
|
||||||
"""
|
"""
|
||||||
if live:
|
|
||||||
with self.save_lock:
|
with self.save_lock:
|
||||||
with open(self.pair_dictionary_path, 'w') as fp:
|
with open(self.pair_dictionary_path, 'w') as fp:
|
||||||
rapidjson.dump(
|
rapidjson.dump(self.pair_dict, fp, default=self.np_encoder,
|
||||||
self.pair_dict, fp, default=self.np_encoder,
|
|
||||||
number_mode=rapidjson.NM_NATIVE)
|
|
||||||
else:
|
|
||||||
# save_lock it's not working with hyperopt
|
|
||||||
with open(self.pair_dictionary_path, 'w') as fp:
|
|
||||||
rapidjson.dump(
|
|
||||||
self.pair_dict, fp, default=self.np_encoder,
|
|
||||||
number_mode=rapidjson.NM_NATIVE)
|
number_mode=rapidjson.NM_NATIVE)
|
||||||
|
|
||||||
def save_follower_dict_to_disk(self):
|
def save_follower_dict_to_disk(self):
|
||||||
@ -455,7 +437,7 @@ class FreqaiDataDrawer:
|
|||||||
self.model_dictionary[coin] = model
|
self.model_dictionary[coin] = model
|
||||||
self.pair_dict[coin]["model_filename"] = dk.model_filename
|
self.pair_dict[coin]["model_filename"] = dk.model_filename
|
||||||
self.pair_dict[coin]["data_path"] = str(dk.data_path)
|
self.pair_dict[coin]["data_path"] = str(dk.data_path)
|
||||||
self.save_drawer_to_disk(dk.live)
|
self.save_drawer_to_disk()
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -92,10 +92,9 @@ class IFreqaiModel(ABC):
|
|||||||
|
|
||||||
def __getstate__(self):
|
def __getstate__(self):
|
||||||
"""
|
"""
|
||||||
Return state values to be pickled.
|
Return an empty state to be pickled in hyperopt
|
||||||
It's necessary to allow serialization in hyperopt
|
|
||||||
"""
|
"""
|
||||||
return ({"dd": self.dd})
|
return ({})
|
||||||
|
|
||||||
def assert_config(self, config: Dict[str, Any]) -> None:
|
def assert_config(self, config: Dict[str, Any]) -> None:
|
||||||
|
|
||||||
|
@ -6,9 +6,7 @@ import talib.abstract as ta
|
|||||||
from pandas import DataFrame
|
from pandas import DataFrame
|
||||||
from technical import qtpylib
|
from technical import qtpylib
|
||||||
|
|
||||||
from freqtrade.exchange import timeframe_to_prev_date
|
from freqtrade.strategy import IStrategy, merge_informative_pair
|
||||||
from freqtrade.persistence import Trade
|
|
||||||
from freqtrade.strategy import DecimalParameter, IntParameter, IStrategy, merge_informative_pair
|
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@ -47,11 +45,6 @@ class FreqaiExampleStrategy(IStrategy):
|
|||||||
startup_candle_count: int = 40
|
startup_candle_count: int = 40
|
||||||
can_short = False
|
can_short = False
|
||||||
|
|
||||||
linear_roi_offset = DecimalParameter(
|
|
||||||
0.00, 0.02, default=0.005, space="sell", optimize=False, load=True
|
|
||||||
)
|
|
||||||
max_roi_time_long = IntParameter(0, 800, default=400, space="sell", optimize=False, load=True)
|
|
||||||
|
|
||||||
def informative_pairs(self):
|
def informative_pairs(self):
|
||||||
whitelist_pairs = self.dp.current_whitelist()
|
whitelist_pairs = self.dp.current_whitelist()
|
||||||
corr_pairs = self.config["freqai"]["feature_parameters"]["include_corr_pairlist"]
|
corr_pairs = self.config["freqai"]["feature_parameters"]["include_corr_pairlist"]
|
||||||
@ -226,83 +219,6 @@ class FreqaiExampleStrategy(IStrategy):
|
|||||||
def get_ticker_indicator(self):
|
def get_ticker_indicator(self):
|
||||||
return int(self.config["timeframe"][:-1])
|
return int(self.config["timeframe"][:-1])
|
||||||
|
|
||||||
def custom_exit(
|
|
||||||
self, pair: str, trade: Trade, current_time, current_rate, current_profit, **kwargs
|
|
||||||
):
|
|
||||||
|
|
||||||
dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe)
|
|
||||||
|
|
||||||
trade_date = timeframe_to_prev_date(self.config["timeframe"], trade.open_date_utc)
|
|
||||||
trade_candle = dataframe.loc[(dataframe["date"] == trade_date)]
|
|
||||||
|
|
||||||
if trade_candle.empty:
|
|
||||||
return None
|
|
||||||
trade_candle = trade_candle.squeeze()
|
|
||||||
|
|
||||||
follow_mode = self.config.get("freqai", {}).get("follow_mode", False)
|
|
||||||
|
|
||||||
if not follow_mode:
|
|
||||||
pair_dict = self.freqai.dd.pair_dict
|
|
||||||
else:
|
|
||||||
pair_dict = self.freqai.dd.follower_dict
|
|
||||||
|
|
||||||
entry_tag = trade.enter_tag
|
|
||||||
|
|
||||||
if (
|
|
||||||
"prediction" + entry_tag not in pair_dict[pair]
|
|
||||||
or pair_dict[pair]['extras']["prediction" + entry_tag] == 0
|
|
||||||
):
|
|
||||||
pair_dict[pair]['extras']["prediction" + entry_tag] = abs(trade_candle["&-s_close"])
|
|
||||||
if not follow_mode:
|
|
||||||
self.freqai.dd.save_drawer_to_disk()
|
|
||||||
else:
|
|
||||||
self.freqai.dd.save_follower_dict_to_disk()
|
|
||||||
|
|
||||||
roi_price = pair_dict[pair]['extras']["prediction" + entry_tag]
|
|
||||||
roi_time = self.max_roi_time_long.value
|
|
||||||
|
|
||||||
roi_decay = roi_price * (
|
|
||||||
1 - ((current_time - trade.open_date_utc).seconds) / (roi_time * 60)
|
|
||||||
)
|
|
||||||
if roi_decay < 0:
|
|
||||||
roi_decay = self.linear_roi_offset.value
|
|
||||||
else:
|
|
||||||
roi_decay += self.linear_roi_offset.value
|
|
||||||
|
|
||||||
if current_profit > roi_decay:
|
|
||||||
return "roi_custom_win"
|
|
||||||
|
|
||||||
if current_profit < -roi_decay:
|
|
||||||
return "roi_custom_loss"
|
|
||||||
|
|
||||||
def confirm_trade_exit(
|
|
||||||
self,
|
|
||||||
pair: str,
|
|
||||||
trade: Trade,
|
|
||||||
order_type: str,
|
|
||||||
amount: float,
|
|
||||||
rate: float,
|
|
||||||
time_in_force: str,
|
|
||||||
exit_reason: str,
|
|
||||||
current_time,
|
|
||||||
**kwargs,
|
|
||||||
) -> bool:
|
|
||||||
|
|
||||||
entry_tag = trade.enter_tag
|
|
||||||
follow_mode = self.config.get("freqai", {}).get("follow_mode", False)
|
|
||||||
if not follow_mode:
|
|
||||||
pair_dict = self.freqai.dd.pair_dict
|
|
||||||
else:
|
|
||||||
pair_dict = self.freqai.dd.follower_dict
|
|
||||||
|
|
||||||
pair_dict[pair]['extras']["prediction" + entry_tag] = 0
|
|
||||||
if not follow_mode:
|
|
||||||
self.freqai.dd.save_drawer_to_disk()
|
|
||||||
else:
|
|
||||||
self.freqai.dd.save_follower_dict_to_disk()
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def confirm_trade_entry(
|
def confirm_trade_entry(
|
||||||
self,
|
self,
|
||||||
pair: str,
|
pair: str,
|
||||||
|
Loading…
Reference in New Issue
Block a user