hyperopt - freqai - docs and refactoring

This commit is contained in:
Wagner Costa Santos 2022-09-06 15:42:47 -03:00
parent 1820bc6832
commit 8d16dd804d
5 changed files with 29 additions and 111 deletions

View File

@ -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`.
*Important*: The `self.freqai.start()` function cannot be called outside the `populate_indicators()`.
### 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`).
@ -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
The user defines the backtesting timerange with the typical `--timerange` parameter in the

View File

@ -285,6 +285,7 @@ class Configuration:
logger.info('Parameter --stoplosses detected: %s ...', self.args["stoploss_range"])
# Hyperopt section
self._check_hyperopt_analyze_per_epoch_freqai()
self._args_to_config(config, argname='hyperopt',
logstring='Using Hyperopt class name: {}')
@ -537,3 +538,12 @@ class Configuration:
config['pairs'] = load_file(pairs_file)
if 'pairs' in config and isinstance(config['pairs'], list):
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.')

View File

@ -93,16 +93,6 @@ class FreqaiDataDrawer:
"model_filename": "", "trained_timestamp": 0,
"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):
"""
Locate and load a previously saved data drawer full of all pair model metadata in
@ -165,22 +155,14 @@ class FreqaiDataDrawer:
# create a backup
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.
"""
if live:
with self.save_lock:
with open(self.pair_dictionary_path, 'w') as fp:
rapidjson.dump(
self.pair_dict, fp, default=self.np_encoder,
number_mode=rapidjson.NM_NATIVE)
else:
# save_lock it's not working with hyperopt
with self.save_lock:
with open(self.pair_dictionary_path, 'w') as fp:
rapidjson.dump(
self.pair_dict, fp, default=self.np_encoder,
number_mode=rapidjson.NM_NATIVE)
rapidjson.dump(self.pair_dict, fp, default=self.np_encoder,
number_mode=rapidjson.NM_NATIVE)
def save_follower_dict_to_disk(self):
"""
@ -455,7 +437,7 @@ class FreqaiDataDrawer:
self.model_dictionary[coin] = model
self.pair_dict[coin]["model_filename"] = dk.model_filename
self.pair_dict[coin]["data_path"] = str(dk.data_path)
self.save_drawer_to_disk(dk.live)
self.save_drawer_to_disk()
return

View File

@ -92,10 +92,9 @@ class IFreqaiModel(ABC):
def __getstate__(self):
"""
Return state values to be pickled.
It's necessary to allow serialization in hyperopt
Return an empty state to be pickled in hyperopt
"""
return ({"dd": self.dd})
return ({})
def assert_config(self, config: Dict[str, Any]) -> None:

View File

@ -6,9 +6,7 @@ import talib.abstract as ta
from pandas import DataFrame
from technical import qtpylib
from freqtrade.exchange import timeframe_to_prev_date
from freqtrade.persistence import Trade
from freqtrade.strategy import DecimalParameter, IntParameter, IStrategy, merge_informative_pair
from freqtrade.strategy import IStrategy, merge_informative_pair
logger = logging.getLogger(__name__)
@ -47,11 +45,6 @@ class FreqaiExampleStrategy(IStrategy):
startup_candle_count: int = 40
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):
whitelist_pairs = self.dp.current_whitelist()
corr_pairs = self.config["freqai"]["feature_parameters"]["include_corr_pairlist"]
@ -226,83 +219,6 @@ class FreqaiExampleStrategy(IStrategy):
def get_ticker_indicator(self):
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(
self,
pair: str,