freqai bt fix populate any indicators call - first commit
This commit is contained in:
parent
3012c55ec5
commit
cc796cc594
@ -1184,6 +1184,7 @@ class FreqaiDataKitchen:
|
|||||||
pair: str = "",
|
pair: str = "",
|
||||||
prediction_dataframe: DataFrame = pd.DataFrame(),
|
prediction_dataframe: DataFrame = pd.DataFrame(),
|
||||||
do_corr_pairs: bool = True,
|
do_corr_pairs: bool = True,
|
||||||
|
set_only_targets: bool = False
|
||||||
) -> DataFrame:
|
) -> DataFrame:
|
||||||
"""
|
"""
|
||||||
Use the user defined strategy for populating indicators during retrain
|
Use the user defined strategy for populating indicators during retrain
|
||||||
@ -1222,7 +1223,8 @@ class FreqaiDataKitchen:
|
|||||||
dataframe.copy(),
|
dataframe.copy(),
|
||||||
tf,
|
tf,
|
||||||
informative=base_dataframes[tf],
|
informative=base_dataframes[tf],
|
||||||
set_generalized_indicators=sgi
|
set_generalized_indicators=sgi,
|
||||||
|
set_only_targets=set_only_targets
|
||||||
)
|
)
|
||||||
|
|
||||||
# ensure corr pairs are always last
|
# ensure corr pairs are always last
|
||||||
@ -1235,7 +1237,8 @@ class FreqaiDataKitchen:
|
|||||||
corr_pair,
|
corr_pair,
|
||||||
dataframe.copy(),
|
dataframe.copy(),
|
||||||
tf,
|
tf,
|
||||||
informative=corr_dataframes[corr_pair][tf]
|
informative=corr_dataframes[corr_pair][tf],
|
||||||
|
set_only_targets=set_only_targets
|
||||||
)
|
)
|
||||||
|
|
||||||
self.get_unique_classes_from_labels(dataframe)
|
self.get_unique_classes_from_labels(dataframe)
|
||||||
|
@ -149,12 +149,13 @@ class IFreqaiModel(ABC):
|
|||||||
# the concatenated results for the full backtesting period back to the strategy.
|
# the concatenated results for the full backtesting period back to the strategy.
|
||||||
elif not self.follow_mode:
|
elif not self.follow_mode:
|
||||||
self.dk = FreqaiDataKitchen(self.config, self.live, metadata["pair"])
|
self.dk = FreqaiDataKitchen(self.config, self.live, metadata["pair"])
|
||||||
dataframe = self.dk.use_strategy_to_populate_indicators(
|
|
||||||
strategy, prediction_dataframe=dataframe, pair=metadata["pair"]
|
# dataframe = self.dk.use_strategy_to_populate_indicators(
|
||||||
)
|
# strategy, prediction_dataframe=dataframe, pair=metadata["pair"]
|
||||||
|
# )
|
||||||
if not self.config.get("freqai_backtest_live_models", False):
|
if not self.config.get("freqai_backtest_live_models", False):
|
||||||
logger.info(f"Training {len(self.dk.training_timeranges)} timeranges")
|
logger.info(f"Training {len(self.dk.training_timeranges)} timeranges")
|
||||||
dk = self.start_backtesting(dataframe, metadata, self.dk)
|
dk = self.start_backtesting(dataframe, metadata, self.dk, strategy)
|
||||||
dataframe = dk.remove_features_from_df(dk.return_dataframe)
|
dataframe = dk.remove_features_from_df(dk.return_dataframe)
|
||||||
else:
|
else:
|
||||||
logger.info(
|
logger.info(
|
||||||
@ -255,7 +256,7 @@ class IFreqaiModel(ABC):
|
|||||||
self.dd.save_metric_tracker_to_disk()
|
self.dd.save_metric_tracker_to_disk()
|
||||||
|
|
||||||
def start_backtesting(
|
def start_backtesting(
|
||||||
self, dataframe: DataFrame, metadata: dict, dk: FreqaiDataKitchen
|
self, dataframe: DataFrame, metadata: dict, dk: FreqaiDataKitchen, strategy: IStrategy
|
||||||
) -> FreqaiDataKitchen:
|
) -> FreqaiDataKitchen:
|
||||||
"""
|
"""
|
||||||
The main broad execution for backtesting. For backtesting, each pair enters and then gets
|
The main broad execution for backtesting. For backtesting, each pair enters and then gets
|
||||||
@ -267,12 +268,14 @@ class IFreqaiModel(ABC):
|
|||||||
:param dataframe: DataFrame = strategy passed dataframe
|
:param dataframe: DataFrame = strategy passed dataframe
|
||||||
:param metadata: Dict = pair metadata
|
:param metadata: Dict = pair metadata
|
||||||
:param dk: FreqaiDataKitchen = Data management/analysis tool associated to present pair only
|
:param dk: FreqaiDataKitchen = Data management/analysis tool associated to present pair only
|
||||||
|
:param strategy: Strategy to train on
|
||||||
:return:
|
:return:
|
||||||
FreqaiDataKitchen = Data management/analysis tool associated to present pair only
|
FreqaiDataKitchen = Data management/analysis tool associated to present pair only
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.pair_it += 1
|
self.pair_it += 1
|
||||||
train_it = 0
|
train_it = 0
|
||||||
|
set_only_targets = False
|
||||||
# Loop enforcing the sliding window training/backtesting paradigm
|
# Loop enforcing the sliding window training/backtesting paradigm
|
||||||
# tr_train is the training time range e.g. 1 historical month
|
# tr_train is the training time range e.g. 1 historical month
|
||||||
# tr_backtest is the backtesting time range e.g. the week directly
|
# tr_backtest is the backtesting time range e.g. the week directly
|
||||||
@ -301,14 +304,39 @@ class IFreqaiModel(ABC):
|
|||||||
dk.set_new_model_names(pair, timestamp_model_id)
|
dk.set_new_model_names(pair, timestamp_model_id)
|
||||||
|
|
||||||
if dk.check_if_backtest_prediction_is_valid(len_backtest_df):
|
if dk.check_if_backtest_prediction_is_valid(len_backtest_df):
|
||||||
self.dd.load_metadata(dk)
|
# self.dd.load_metadata(dk)
|
||||||
dk.find_features(dataframe)
|
# dk.find_features(dataframe)
|
||||||
self.check_if_feature_list_matches_strategy(dk)
|
# self.check_if_feature_list_matches_strategy(dk)
|
||||||
append_df = dk.get_backtesting_prediction()
|
append_df = dk.get_backtesting_prediction()
|
||||||
dk.append_predictions(append_df)
|
dk.append_predictions(append_df)
|
||||||
else:
|
else:
|
||||||
dataframe_train = dk.slice_dataframe(tr_train, dataframe)
|
if set_only_targets is False:
|
||||||
dataframe_backtest = dk.slice_dataframe(tr_backtest, dataframe)
|
dataframe = self.dk.use_strategy_to_populate_indicators(
|
||||||
|
strategy, prediction_dataframe=dataframe,
|
||||||
|
pair=metadata["pair"], set_only_targets=set_only_targets
|
||||||
|
)
|
||||||
|
set_only_targets = True
|
||||||
|
|
||||||
|
dataframe_base_train = dataframe.loc[dataframe["date"] < tr_train.stopdt, :]
|
||||||
|
dataframe_base_train = self.dk.use_strategy_to_populate_indicators(
|
||||||
|
strategy, prediction_dataframe=dataframe_base_train,
|
||||||
|
pair=metadata["pair"], set_only_targets=set_only_targets
|
||||||
|
)
|
||||||
|
|
||||||
|
# region need to verify if this block is needed
|
||||||
|
# (or slice dataframe_backtest from dataframe)
|
||||||
|
dataframe_base_backtest = dataframe.loc[dataframe["date"] < tr_backtest.stopdt, :]
|
||||||
|
dataframe_base_backtest = self.dk.use_strategy_to_populate_indicators(
|
||||||
|
strategy, prediction_dataframe=dataframe_base_backtest,
|
||||||
|
pair=metadata["pair"], set_only_targets=set_only_targets
|
||||||
|
)
|
||||||
|
# endregion
|
||||||
|
|
||||||
|
set_only_targets = True
|
||||||
|
|
||||||
|
dataframe_train = dk.slice_dataframe(tr_train, dataframe_base_train)
|
||||||
|
dataframe_backtest = dk.slice_dataframe(tr_backtest, dataframe_base_backtest)
|
||||||
|
|
||||||
if not self.model_exists(dk):
|
if not self.model_exists(dk):
|
||||||
dk.find_features(dataframe_train)
|
dk.find_features(dataframe_train)
|
||||||
dk.find_labels(dataframe_train)
|
dk.find_labels(dataframe_train)
|
||||||
|
@ -596,7 +596,8 @@ class IStrategy(ABC, HyperStrategyMixin):
|
|||||||
|
|
||||||
def populate_any_indicators(self, pair: str, df: DataFrame, tf: str,
|
def populate_any_indicators(self, pair: str, df: DataFrame, tf: str,
|
||||||
informative: DataFrame = None,
|
informative: DataFrame = None,
|
||||||
set_generalized_indicators: bool = False) -> DataFrame:
|
set_generalized_indicators: bool = False,
|
||||||
|
set_only_targets: bool = False) -> DataFrame:
|
||||||
"""
|
"""
|
||||||
Function designed to automatically generate, name and merge features
|
Function designed to automatically generate, name and merge features
|
||||||
from user indicated timeframes in the configuration file. User can add
|
from user indicated timeframes in the configuration file. User can add
|
||||||
@ -607,6 +608,7 @@ class IStrategy(ABC, HyperStrategyMixin):
|
|||||||
:param df: strategy dataframe which will receive merges from informatives
|
:param df: strategy dataframe which will receive merges from informatives
|
||||||
:param tf: timeframe of the dataframe which will modify the feature names
|
:param tf: timeframe of the dataframe which will modify the feature names
|
||||||
:param informative: the dataframe associated with the informative pair
|
:param informative: the dataframe associated with the informative pair
|
||||||
|
:param set_only_targets: if True, only the target features will be set
|
||||||
"""
|
"""
|
||||||
return df
|
return df
|
||||||
|
|
||||||
|
@ -48,7 +48,13 @@ class FreqaiExampleStrategy(IStrategy):
|
|||||||
[0.75, 1, 1.25, 1.5, 1.75], space="sell", default=1.25, optimize=True)
|
[0.75, 1, 1.25, 1.5, 1.75], space="sell", default=1.25, optimize=True)
|
||||||
|
|
||||||
def populate_any_indicators(
|
def populate_any_indicators(
|
||||||
self, pair, df, tf, informative=None, set_generalized_indicators=False
|
self,
|
||||||
|
pair,
|
||||||
|
df,
|
||||||
|
tf,
|
||||||
|
informative=None,
|
||||||
|
set_generalized_indicators=False,
|
||||||
|
set_only_targets=False
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Function designed to automatically generate, name and merge features
|
Function designed to automatically generate, name and merge features
|
||||||
@ -63,66 +69,68 @@ class FreqaiExampleStrategy(IStrategy):
|
|||||||
:param informative: the dataframe associated with the informative pair
|
:param informative: the dataframe associated with the informative pair
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if informative is None:
|
if not set_only_targets:
|
||||||
informative = self.dp.get_pair_dataframe(pair, tf)
|
if informative is None:
|
||||||
|
informative = self.dp.get_pair_dataframe(pair, tf)
|
||||||
|
|
||||||
# first loop is automatically duplicating indicators for time periods
|
# first loop is automatically duplicating indicators for time periods
|
||||||
for t in self.freqai_info["feature_parameters"]["indicator_periods_candles"]:
|
for t in self.freqai_info["feature_parameters"]["indicator_periods_candles"]:
|
||||||
|
|
||||||
t = int(t)
|
t = int(t)
|
||||||
informative[f"%-{pair}rsi-period_{t}"] = ta.RSI(informative, timeperiod=t)
|
informative[f"%-{pair}rsi-period_{t}"] = ta.RSI(informative, timeperiod=t)
|
||||||
informative[f"%-{pair}mfi-period_{t}"] = ta.MFI(informative, timeperiod=t)
|
informative[f"%-{pair}mfi-period_{t}"] = ta.MFI(informative, timeperiod=t)
|
||||||
informative[f"%-{pair}adx-period_{t}"] = ta.ADX(informative, timeperiod=t)
|
informative[f"%-{pair}adx-period_{t}"] = ta.ADX(informative, timeperiod=t)
|
||||||
informative[f"%-{pair}sma-period_{t}"] = ta.SMA(informative, timeperiod=t)
|
informative[f"%-{pair}sma-period_{t}"] = ta.SMA(informative, timeperiod=t)
|
||||||
informative[f"%-{pair}ema-period_{t}"] = ta.EMA(informative, timeperiod=t)
|
informative[f"%-{pair}ema-period_{t}"] = ta.EMA(informative, timeperiod=t)
|
||||||
|
|
||||||
bollinger = qtpylib.bollinger_bands(
|
bollinger = qtpylib.bollinger_bands(
|
||||||
qtpylib.typical_price(informative), window=t, stds=2.2
|
qtpylib.typical_price(informative), window=t, stds=2.2
|
||||||
)
|
)
|
||||||
informative[f"{pair}bb_lowerband-period_{t}"] = bollinger["lower"]
|
informative[f"{pair}bb_lowerband-period_{t}"] = bollinger["lower"]
|
||||||
informative[f"{pair}bb_middleband-period_{t}"] = bollinger["mid"]
|
informative[f"{pair}bb_middleband-period_{t}"] = bollinger["mid"]
|
||||||
informative[f"{pair}bb_upperband-period_{t}"] = bollinger["upper"]
|
informative[f"{pair}bb_upperband-period_{t}"] = bollinger["upper"]
|
||||||
|
|
||||||
informative[f"%-{pair}bb_width-period_{t}"] = (
|
informative[f"%-{pair}bb_width-period_{t}"] = (
|
||||||
informative[f"{pair}bb_upperband-period_{t}"]
|
informative[f"{pair}bb_upperband-period_{t}"]
|
||||||
- informative[f"{pair}bb_lowerband-period_{t}"]
|
- informative[f"{pair}bb_lowerband-period_{t}"]
|
||||||
) / informative[f"{pair}bb_middleband-period_{t}"]
|
) / informative[f"{pair}bb_middleband-period_{t}"]
|
||||||
informative[f"%-{pair}close-bb_lower-period_{t}"] = (
|
informative[f"%-{pair}close-bb_lower-period_{t}"] = (
|
||||||
informative["close"] / informative[f"{pair}bb_lowerband-period_{t}"]
|
informative["close"] / informative[f"{pair}bb_lowerband-period_{t}"]
|
||||||
)
|
)
|
||||||
|
|
||||||
informative[f"%-{pair}roc-period_{t}"] = ta.ROC(informative, timeperiod=t)
|
informative[f"%-{pair}roc-period_{t}"] = ta.ROC(informative, timeperiod=t)
|
||||||
|
|
||||||
informative[f"%-{pair}relative_volume-period_{t}"] = (
|
informative[f"%-{pair}relative_volume-period_{t}"] = (
|
||||||
informative["volume"] / informative["volume"].rolling(t).mean()
|
informative["volume"] / informative["volume"].rolling(t).mean()
|
||||||
)
|
)
|
||||||
|
|
||||||
informative[f"%-{pair}pct-change"] = informative["close"].pct_change()
|
informative[f"%-{pair}pct-change"] = informative["close"].pct_change()
|
||||||
informative[f"%-{pair}raw_volume"] = informative["volume"]
|
informative[f"%-{pair}raw_volume"] = informative["volume"]
|
||||||
informative[f"%-{pair}raw_price"] = informative["close"]
|
informative[f"%-{pair}raw_price"] = informative["close"]
|
||||||
|
|
||||||
indicators = [col for col in informative if col.startswith("%")]
|
indicators = [col for col in informative if col.startswith("%")]
|
||||||
# This loop duplicates and shifts all indicators to add a sense of recency to data
|
# This loop duplicates and shifts all indicators to add a sense of recency to data
|
||||||
for n in range(self.freqai_info["feature_parameters"]["include_shifted_candles"] + 1):
|
for n in range(self.freqai_info["feature_parameters"]["include_shifted_candles"] + 1):
|
||||||
if n == 0:
|
if n == 0:
|
||||||
continue
|
continue
|
||||||
informative_shift = informative[indicators].shift(n)
|
informative_shift = informative[indicators].shift(n)
|
||||||
informative_shift = informative_shift.add_suffix("_shift-" + str(n))
|
informative_shift = informative_shift.add_suffix("_shift-" + str(n))
|
||||||
informative = pd.concat((informative, informative_shift), axis=1)
|
informative = pd.concat((informative, informative_shift), axis=1)
|
||||||
|
|
||||||
df = merge_informative_pair(df, informative, self.config["timeframe"], tf, ffill=True)
|
df = merge_informative_pair(df, informative, self.config["timeframe"], tf, ffill=True)
|
||||||
skip_columns = [
|
skip_columns = [
|
||||||
(s + "_" + tf) for s in ["date", "open", "high", "low", "close", "volume"]
|
(s + "_" + tf) for s in ["date", "open", "high", "low", "close", "volume"]
|
||||||
]
|
]
|
||||||
df = df.drop(columns=skip_columns)
|
df = df.drop(columns=skip_columns)
|
||||||
|
|
||||||
|
# Add generalized indicators here (because in live, it will call this
|
||||||
|
# function to populate indicators during training). Notice how we ensure not to
|
||||||
|
# add them multiple times
|
||||||
|
if set_generalized_indicators:
|
||||||
|
df["%-day_of_week"] = (df["date"].dt.dayofweek + 1) / 7
|
||||||
|
df["%-hour_of_day"] = (df["date"].dt.hour + 1) / 25
|
||||||
|
|
||||||
# Add generalized indicators here (because in live, it will call this
|
|
||||||
# function to populate indicators during training). Notice how we ensure not to
|
|
||||||
# add them multiple times
|
|
||||||
if set_generalized_indicators:
|
if set_generalized_indicators:
|
||||||
df["%-day_of_week"] = (df["date"].dt.dayofweek + 1) / 7
|
|
||||||
df["%-hour_of_day"] = (df["date"].dt.hour + 1) / 25
|
|
||||||
|
|
||||||
# user adds targets here by prepending them with &- (see convention below)
|
# user adds targets here by prepending them with &- (see convention below)
|
||||||
df["&-s_close"] = (
|
df["&-s_close"] = (
|
||||||
df["close"]
|
df["close"]
|
||||||
|
Loading…
Reference in New Issue
Block a user