From b44bd0171cf69c6dc527f8e008fe2f50539fcda5 Mon Sep 17 00:00:00 2001 From: Johan van der Vlugt <83158576+JohanVlugt@users.noreply.github.com> Date: Fri, 19 Aug 2022 19:10:37 +0200 Subject: [PATCH 1/6] Example Classifier strat --- .../templates/FreqaiHybridExampleStrategy.py | 377 ++++++++++++++++++ 1 file changed, 377 insertions(+) create mode 100644 freqtrade/templates/FreqaiHybridExampleStrategy.py diff --git a/freqtrade/templates/FreqaiHybridExampleStrategy.py b/freqtrade/templates/FreqaiHybridExampleStrategy.py new file mode 100644 index 000000000..acceccee7 --- /dev/null +++ b/freqtrade/templates/FreqaiHybridExampleStrategy.py @@ -0,0 +1,377 @@ +import logging +from datetime import datetime, timedelta +from functools import reduce +from typing import Optional + +import numpy as np +import pandas as pd +import talib.abstract as ta +from freqtrade.exchange import timeframe_to_prev_date +from freqtrade.persistence import Trade +from freqtrade.strategy import (DecimalParameter, IntParameter, IStrategy, + merge_informative_pair) +from numpy.lib import math +from pandas import DataFrame +from technical import qtpylib + +logger = logging.getLogger(__name__) + + +class FreqaiExampleHybridStrategy(IStrategy): + """ + Example classifier hybrid strategy showing how the user connects their own + IFreqaiModel to the strategy. Namely, the user uses: + self.freqai.start(dataframe, metadata) + + to make predictions on their data. populate_any_indicators() automatically + generates the variety of features indicated by the user in the + canonical freqtrade configuration file under config['freqai']. + + The underlying original supertrend strat is authored by @juankysoriano (Juan Carlos Soriano) + * github: https://github.com/juankysoriano/ + + This strategy is not designed to be used live + """ + + minimal_roi = {"0": 0.1, "30": 0.75, "60": 0.05, "120": 0.025, "240": -1} + + plot_config = { + "main_plot": {}, + "subplots": { + "prediction": {"prediction": {"color": "blue"}}, + "target_roi": { + "target_roi": {"color": "brown"}, + }, + "do_predict": { + "do_predict": {"color": "brown"}, + }, + }, + } + + process_only_new_candles = True + stoploss = -0.1 + use_exit_signal = True + startup_candle_count: int = 300 + can_short = True + + 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) + + buy_params = { + "buy_m1": 4, + "buy_m2": 7, + "buy_m3": 1, + "buy_p1": 8, + "buy_p2": 9, + "buy_p3": 8, + } + + # Sell hyperspace params: + sell_params = { + "sell_m1": 1, + "sell_m2": 3, + "sell_m3": 6, + "sell_p1": 16, + "sell_p2": 18, + "sell_p3": 18, + } + + buy_m1 = IntParameter(1, 7, default=1) + buy_m2 = IntParameter(1, 7, default=3) + buy_m3 = IntParameter(1, 7, default=4) + buy_p1 = IntParameter(7, 21, default=14) + buy_p2 = IntParameter(7, 21, default=10) + buy_p3 = IntParameter(7, 21, default=10) + + sell_m1 = IntParameter(1, 7, default=1) + sell_m2 = IntParameter(1, 7, default=3) + sell_m3 = IntParameter(1, 7, default=4) + sell_p1 = IntParameter(7, 21, default=14) + sell_p2 = IntParameter(7, 21, default=10) + sell_p3 = IntParameter(7, 21, default=10) + + def informative_pairs(self): + whitelist_pairs = self.dp.current_whitelist() + corr_pairs = self.config["freqai"]["feature_parameters"]["include_corr_pairlist"] + informative_pairs = [] + for tf in self.config["freqai"]["feature_parameters"]["include_timeframes"]: + for pair in whitelist_pairs: + informative_pairs.append((pair, tf)) + for pair in corr_pairs: + if pair in whitelist_pairs: + continue # avoid duplication + informative_pairs.append((pair, tf)) + return informative_pairs + + def populate_any_indicators( + self, pair, df, tf, informative=None, set_generalized_indicators=False + ): + """ + Function designed to automatically generate, name and merge features + from user indicated timeframes in the configuration file. User controls the indicators + passed to the training/prediction by prepending indicators with `'%-' + coin ` + (see convention below). I.e. user should not prepend any supporting metrics + (e.g. bb_lowerband below) with % unless they explicitly want to pass that metric to the + model. + :param pair: pair to be used as informative + :param df: strategy dataframe which will receive merges from informatives + :param tf: timeframe of the dataframe which will modify the feature names + :param informative: the dataframe associated with the informative pair + """ + + coin = pair.split('/')[0] + + if informative is None: + informative = self.dp.get_pair_dataframe(pair, tf) + + # first loop is automatically duplicating indicators for time periods + for t in self.freqai_info["feature_parameters"]["indicator_periods_candles"]: + + t = int(t) + informative[f"%-{coin}rsi-period_{t}"] = ta.RSI(informative, timeperiod=t) + informative[f"%-{coin}mfi-period_{t}"] = ta.MFI(informative, timeperiod=t) + informative[f"%-{coin}adx-period_{t}"] = ta.ADX(informative, window=t) + informative[f"%-{coin}sma-period_{t}"] = ta.SMA(informative, timeperiod=t) + informative[f"%-{coin}ema-period_{t}"] = ta.EMA(informative, timeperiod=t) + + informative[f"%-{coin}mfi-period_{t}"] = ta.MFI(informative, timeperiod=t) + + bollinger = qtpylib.bollinger_bands( + qtpylib.typical_price(informative), window=t, stds=2.2 + ) + informative[f"{coin}bb_lowerband-period_{t}"] = bollinger["lower"] + informative[f"{coin}bb_middleband-period_{t}"] = bollinger["mid"] + informative[f"{coin}bb_upperband-period_{t}"] = bollinger["upper"] + + informative[f"%-{coin}bb_width-period_{t}"] = ( + informative[f"{coin}bb_upperband-period_{t}"] + - informative[f"{coin}bb_lowerband-period_{t}"] + ) / informative[f"{coin}bb_middleband-period_{t}"] + informative[f"%-{coin}close-bb_lower-period_{t}"] = ( + informative["close"] / informative[f"{coin}bb_lowerband-period_{t}"] + ) + + informative[f"%-{coin}roc-period_{t}"] = ta.ROC(informative, timeperiod=t) + + informative[f"%-{coin}relative_volume-period_{t}"] = ( + informative["volume"] / informative["volume"].rolling(t).mean() + ) + + informative[f"%-{coin}pct-change"] = informative["close"].pct_change() + informative[f"%-{coin}raw_volume"] = informative["volume"] + informative[f"%-{coin}raw_price"] = informative["close"] + + indicators = [col for col in informative if col.startswith("%")] + # 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): + if n == 0: + continue + informative_shift = informative[indicators].shift(n) + informative_shift = informative_shift.add_suffix("_shift-" + str(n)) + informative = pd.concat((informative, informative_shift), axis=1) + + df = merge_informative_pair(df, informative, self.config["timeframe"], tf, ffill=True) + skip_columns = [ + (s + "_" + tf) for s in ["date", "open", "high", "low", "close", "volume"] + ] + 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 + + # Classifiers are typically set up with strings as targets: + df['&s-up_or_down'] = np.where( df["close"].shift(-50) > + df["close"], 'up', 'down') + + # REGRESSOR Model: Can use single or multi traget + # user adds targets here by prepending them with &- (see convention below) + #df["&-s_close"] = ( + # df["close"] + # .shift(-self.freqai_info["feature_parameters"]["label_period_candles"]) + # .rolling(self.freqai_info["feature_parameters"]["label_period_candles"]) + # .mean() + # / df["close"] + # - 1 + #) + # If user wishes to use multiple targets, they can add more by + # appending more columns with '&'. User should keep in mind that multi targets + # requires a multioutput prediction model such as + # templates/CatboostPredictionMultiModel.py, + + # df["&-s_range"] = ( + # df["close"] + # .shift(-self.freqai_info["feature_parameters"]["label_period_candles"]) + # .rolling(self.freqai_info["feature_parameters"]["label_period_candles"]) + # .max() + # - + # df["close"] + # .shift(-self.freqai_info["feature_parameters"]["label_period_candles"]) + # .rolling(self.freqai_info["feature_parameters"]["label_period_candles"]) + # .min() + # ) + + return df + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # All indicators must be populated by populate_any_indicators() for live functionality + # to work correctly. + + # the model will return all labels created by user in `populate_any_indicators` + # (& appended targets), an indication of whether or not the prediction should be accepted, + # the target mean/std values for each of the labels created by user in + # `populate_any_indicators()` for each training period. + + for multiplier in self.buy_m1.range: + for period in self.buy_p1.range: + dataframe[f"supertrend_1_buy_{multiplier}_{period}"] = self.supertrend( + dataframe, multiplier, period + )["STX"] + + for multiplier in self.buy_m2.range: + for period in self.buy_p2.range: + dataframe[f"supertrend_2_buy_{multiplier}_{period}"] = self.supertrend( + dataframe, multiplier, period + )["STX"] + + for multiplier in self.buy_m3.range: + for period in self.buy_p3.range: + dataframe[f"supertrend_3_buy_{multiplier}_{period}"] = self.supertrend( + dataframe, multiplier, period + )["STX"] + + for multiplier in self.sell_m1.range: + for period in self.sell_p1.range: + dataframe[f"supertrend_1_sell_{multiplier}_{period}"] = self.supertrend( + dataframe, multiplier, period + )["STX"] + + for multiplier in self.sell_m2.range: + for period in self.sell_p2.range: + dataframe[f"supertrend_2_sell_{multiplier}_{period}"] = self.supertrend( + dataframe, multiplier, period + )["STX"] + + for multiplier in self.sell_m3.range: + for period in self.sell_p3.range: + dataframe[f"supertrend_3_sell_{multiplier}_{period}"] = self.supertrend( + dataframe, multiplier, period + )["STX"] + + dataframe = self.freqai.start(dataframe, metadata, self) + + return dataframe + + def populate_entry_trend(self, df: DataFrame, metadata: dict) -> DataFrame: + + df.loc[ + (df[f"supertrend_1_buy_{self.buy_m1.value}_{self.buy_p1.value}"] == "up") & + (df[f"supertrend_2_buy_{self.buy_m2.value}_{self.buy_p2.value}"] == "up") & + (df[f"supertrend_3_buy_{self.buy_m3.value}_{self.buy_p3.value}"] == "up") & + (df["do_predict"] == 1) & + (df['&s-up_or_down'] == 'up'), + "enter_long", + ] = 1 + + df.loc[ + (df[f"supertrend_1_sell_{self.sell_m1.value}_{self.sell_p1.value}"] == "down") & + (df[f"supertrend_2_sell_{self.sell_m2.value}_{self.sell_p2.value}"] == "down") & + (df[f"supertrend_3_sell_{self.sell_m3.value}_{self.sell_p3.value}"] == "down") & + (df["do_predict"] == 1) & + (df['&s-up_or_down'] == 'down'), + "enter_short", + ] = 1 + + return df + + def populate_exit_trend(self, df: DataFrame, metadata: dict) -> DataFrame: + + df.loc[ + (df[f"supertrend_2_sell_{self.sell_m2.value}_{self.sell_p2.value}"] == "down"), + "exit_long", + ] = 1 + + df.loc[ + (df[f"supertrend_2_buy_{self.buy_m2.value}_{self.buy_p2.value}"] == "up"), + "exit_short", + ] = 1 + + return df + + def get_ticker_indicator(self): + return int(self.config["timeframe"][:-1]) + + def confirm_trade_entry(self, pair: str, order_type: str, amount: float, + rate: float, time_in_force: str, current_time, entry_tag, side: str, + **kwargs, ) -> bool: + + df, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = df.iloc[-1].squeeze() + + if side == "long": + if rate > (last_candle["close"] * (1 + 0.0025)): + return False + else: + if rate < (last_candle["close"] * (1 - 0.0025)): + return False + + return True + + def leverage(self, pair: str, current_time: datetime, current_rate: float, + proposed_leverage: float, max_leverage: float, entry_tag: Optional[str], side: str, + **kwargs) -> float: + + return 1 + + """ + Supertrend Indicator; adapted for freqtrade, optimized by the math genius. + from: Perkmeister#2394 + """ + + def supertrend(self, dataframe: DataFrame, multiplier, period): + df = dataframe.copy() + last_row = dataframe.tail(1).index.item() + + df['TR'] = ta.TRANGE(df) + df['ATR'] = ta.SMA(df['TR'], period) + + st = 'ST_' + str(period) + '_' + str(multiplier) + stx = 'STX_' + str(period) + '_' + str(multiplier) + + # Compute basic upper and lower bands + BASIC_UB = ((df['high'] + df['low']) / 2 + multiplier * df['ATR']).values + BASIC_LB = ((df['high'] + df['low']) / 2 - multiplier * df['ATR']).values + FINAL_UB = np.zeros(last_row + 1) + FINAL_LB = np.zeros(last_row + 1) + ST = np.zeros(last_row + 1) + CLOSE = df['close'].values + + # Compute final upper and lower bands + for i in range(period, last_row + 1): + FINAL_UB[i] = BASIC_UB[i] if BASIC_UB[i] < FINAL_UB[i - 1] or CLOSE[i - 1] > FINAL_UB[i - 1] else FINAL_UB[i - 1] + FINAL_LB[i] = BASIC_LB[i] if BASIC_LB[i] > FINAL_LB[i - 1] or CLOSE[i - 1] < FINAL_LB[i - 1] else FINAL_LB[i - 1] + + # Set the Supertrend value + for i in range(period, last_row + 1): + ST[i] = FINAL_UB[i] if ST[i - 1] == FINAL_UB[i - 1] and CLOSE[i] <= FINAL_UB[i] else \ + FINAL_LB[i] if ST[i - 1] == FINAL_UB[i - 1] and CLOSE[i] > FINAL_UB[i] else \ + FINAL_LB[i] if ST[i - 1] == FINAL_LB[i - 1] and CLOSE[i] >= FINAL_LB[i] else \ + FINAL_UB[i] if ST[i - 1] == FINAL_LB[i - 1] and CLOSE[i] < FINAL_LB[i] else 0.00 + df_ST = pd.DataFrame(ST, columns=[st]) + df = pd.concat([df, df_ST],axis=1) + + # Mark the trend direction up/down + df[stx] = np.where((df[st] > 0.00), np.where((df['close'] < df[st]), 'down', 'up'), np.NaN) + + df.fillna(0, inplace=True) + + return DataFrame(index=df.index, data={ + 'ST' : df[st], + 'STX' : df[stx] + }) From 90c03178b1a8a9ee458b6d46c10525bdd7ff4966 Mon Sep 17 00:00:00 2001 From: robcaulk Date: Sat, 20 Aug 2022 17:02:18 +0200 Subject: [PATCH 2/6] provide user directions, clean up strategy, remove unnecessary code. --- config_examples/config_freqai.example.json | 1 - .../templates/FreqaiHybridExampleStrategy.py | 204 +++++++----------- 2 files changed, 82 insertions(+), 123 deletions(-) diff --git a/config_examples/config_freqai.example.json b/config_examples/config_freqai.example.json index aeb1cb13d..330bcd9f5 100644 --- a/config_examples/config_freqai.example.json +++ b/config_examples/config_freqai.example.json @@ -75,7 +75,6 @@ "weight_factor": 0.9, "principal_component_analysis": false, "use_SVM_to_remove_outliers": true, - "stratify_training_data": 0, "indicator_max_period_candles": 20, "indicator_periods_candles": [10, 20] }, diff --git a/freqtrade/templates/FreqaiHybridExampleStrategy.py b/freqtrade/templates/FreqaiHybridExampleStrategy.py index acceccee7..99abf27d4 100644 --- a/freqtrade/templates/FreqaiHybridExampleStrategy.py +++ b/freqtrade/templates/FreqaiHybridExampleStrategy.py @@ -1,64 +1,72 @@ import logging -from datetime import datetime, timedelta -from functools import reduce from typing import Optional import numpy as np import pandas as pd import talib.abstract as ta -from freqtrade.exchange import timeframe_to_prev_date -from freqtrade.persistence import Trade from freqtrade.strategy import (DecimalParameter, IntParameter, IStrategy, merge_informative_pair) -from numpy.lib import math from pandas import DataFrame -from technical import qtpylib logger = logging.getLogger(__name__) class FreqaiExampleHybridStrategy(IStrategy): """ - Example classifier hybrid strategy showing how the user connects their own - IFreqaiModel to the strategy. Namely, the user uses: - self.freqai.start(dataframe, metadata) + Example of a hybrid FreqAI strat, designed to illustrate how a user may employ + FreqAI to bolster a typical Freqtrade strategy. + + Launching this strategy would be: + + freqtrade trade --strategy FreqaiExampleHyridStrategy --strategy-path freqtrade/templates + --freqaimodel CatboostClassifier --config config_examples/config_freqai.example.json + + or the user simply adds this to their config: + + "freqai": { + "enabled": true, + "purge_old_models": true, + "train_period_days": 15, + "identifier": "uniqe-id", + "feature_parameters": { + "include_timeframes": [ + "3m", + "15m", + "1h" + ], + "include_corr_pairlist": [ + "BTC/USDT", + "ETH/USDT" + ], + "label_period_candles": 20, + "include_shifted_candles": 2, + "DI_threshold": 0.9, + "weight_factor": 0.9, + "principal_component_analysis": false, + "use_SVM_to_remove_outliers": true, + "indicator_max_period_candles": 20, + "indicator_periods_candles": [10, 20] + }, + "data_split_parameters": { + "test_size": 0.33, + "random_state": 1 + }, + "model_training_parameters": { + "n_estimators": 800 + } + }, - to make predictions on their data. populate_any_indicators() automatically - generates the variety of features indicated by the user in the - canonical freqtrade configuration file under config['freqai']. - - The underlying original supertrend strat is authored by @juankysoriano (Juan Carlos Soriano) - * github: https://github.com/juankysoriano/ - This strategy is not designed to be used live """ minimal_roi = {"0": 0.1, "30": 0.75, "60": 0.05, "120": 0.025, "240": -1} - plot_config = { - "main_plot": {}, - "subplots": { - "prediction": {"prediction": {"color": "blue"}}, - "target_roi": { - "target_roi": {"color": "brown"}, - }, - "do_predict": { - "do_predict": {"color": "brown"}, - }, - }, - } - process_only_new_candles = True stoploss = -0.1 use_exit_signal = True startup_candle_count: int = 300 can_short = True - 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) - buy_params = { "buy_m1": 4, "buy_m2": 7, @@ -77,7 +85,7 @@ class FreqaiExampleHybridStrategy(IStrategy): "sell_p2": 18, "sell_p3": 18, } - + buy_m1 = IntParameter(1, 7, default=1) buy_m2 = IntParameter(1, 7, default=3) buy_m3 = IntParameter(1, 7, default=4) @@ -92,6 +100,7 @@ class FreqaiExampleHybridStrategy(IStrategy): sell_p2 = IntParameter(7, 21, default=10) sell_p3 = IntParameter(7, 21, default=10) + # FreqAI required function, leave as is or add you additional informatives to existing structure. def informative_pairs(self): whitelist_pairs = self.dp.current_whitelist() corr_pairs = self.config["freqai"]["feature_parameters"]["include_corr_pairlist"] @@ -105,16 +114,15 @@ class FreqaiExampleHybridStrategy(IStrategy): informative_pairs.append((pair, tf)) return informative_pairs + # FreqAI required function, user can add or remove indicators, but general structure + # must stay the same. def populate_any_indicators( self, pair, df, tf, informative=None, set_generalized_indicators=False ): """ - Function designed to automatically generate, name and merge features - from user indicated timeframes in the configuration file. User controls the indicators - passed to the training/prediction by prepending indicators with `'%-' + coin ` - (see convention below). I.e. user should not prepend any supporting metrics - (e.g. bb_lowerband below) with % unless they explicitly want to pass that metric to the - model. + User feeds these indicators to FreqAI to train a classifier to decide + if the market will go up or down. + :param pair: pair to be used as informative :param df: strategy dataframe which will receive merges from informatives :param tf: timeframe of the dataframe which will modify the feature names @@ -135,34 +143,14 @@ class FreqaiExampleHybridStrategy(IStrategy): informative[f"%-{coin}adx-period_{t}"] = ta.ADX(informative, window=t) informative[f"%-{coin}sma-period_{t}"] = ta.SMA(informative, timeperiod=t) informative[f"%-{coin}ema-period_{t}"] = ta.EMA(informative, timeperiod=t) - informative[f"%-{coin}mfi-period_{t}"] = ta.MFI(informative, timeperiod=t) - - bollinger = qtpylib.bollinger_bands( - qtpylib.typical_price(informative), window=t, stds=2.2 - ) - informative[f"{coin}bb_lowerband-period_{t}"] = bollinger["lower"] - informative[f"{coin}bb_middleband-period_{t}"] = bollinger["mid"] - informative[f"{coin}bb_upperband-period_{t}"] = bollinger["upper"] - - informative[f"%-{coin}bb_width-period_{t}"] = ( - informative[f"{coin}bb_upperband-period_{t}"] - - informative[f"{coin}bb_lowerband-period_{t}"] - ) / informative[f"{coin}bb_middleband-period_{t}"] - informative[f"%-{coin}close-bb_lower-period_{t}"] = ( - informative["close"] / informative[f"{coin}bb_lowerband-period_{t}"] - ) - informative[f"%-{coin}roc-period_{t}"] = ta.ROC(informative, timeperiod=t) - informative[f"%-{coin}relative_volume-period_{t}"] = ( informative["volume"] / informative["volume"].rolling(t).mean() ) - informative[f"%-{coin}pct-change"] = informative["close"].pct_change() - informative[f"%-{coin}raw_volume"] = informative["volume"] - informative[f"%-{coin}raw_price"] = informative["close"] - + # FreqAI needs the following lines in order to detect features and automatically + # expand upon them. indicators = [col for col in informative if col.startswith("%")] # 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): @@ -178,56 +166,22 @@ class FreqaiExampleHybridStrategy(IStrategy): ] 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 + # User can set the "target" here (in present case it is the + # "up" or "down") if set_generalized_indicators: - df["%-day_of_week"] = (df["date"].dt.dayofweek + 1) / 7 - df["%-hour_of_day"] = (df["date"].dt.hour + 1) / 25 - - # Classifiers are typically set up with strings as targets: - df['&s-up_or_down'] = np.where( df["close"].shift(-50) > - df["close"], 'up', 'down') - - # REGRESSOR Model: Can use single or multi traget - # user adds targets here by prepending them with &- (see convention below) - #df["&-s_close"] = ( - # df["close"] - # .shift(-self.freqai_info["feature_parameters"]["label_period_candles"]) - # .rolling(self.freqai_info["feature_parameters"]["label_period_candles"]) - # .mean() - # / df["close"] - # - 1 - #) - # If user wishes to use multiple targets, they can add more by - # appending more columns with '&'. User should keep in mind that multi targets - # requires a multioutput prediction model such as - # templates/CatboostPredictionMultiModel.py, - - # df["&-s_range"] = ( - # df["close"] - # .shift(-self.freqai_info["feature_parameters"]["label_period_candles"]) - # .rolling(self.freqai_info["feature_parameters"]["label_period_candles"]) - # .max() - # - - # df["close"] - # .shift(-self.freqai_info["feature_parameters"]["label_period_candles"]) - # .rolling(self.freqai_info["feature_parameters"]["label_period_candles"]) - # .min() - # ) + # User "looks into the future" here to figure out if the future + # will be "up" or "down". This same column name is available to + # the user + df['&s-up_or_down'] = np.where(df["close"].shift(-50) > + df["close"], 'up', 'down') return df def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: - # All indicators must be populated by populate_any_indicators() for live functionality - # to work correctly. + # User creates their own custom strat here. Present example is a supertrend + # based strategy. - # the model will return all labels created by user in `populate_any_indicators` - # (& appended targets), an indication of whether or not the prediction should be accepted, - # the target mean/std values for each of the labels created by user in - # `populate_any_indicators()` for each training period. - for multiplier in self.buy_m1.range: for period in self.buy_p1.range: dataframe[f"supertrend_1_buy_{multiplier}_{period}"] = self.supertrend( @@ -270,20 +224,23 @@ class FreqaiExampleHybridStrategy(IStrategy): def populate_entry_trend(self, df: DataFrame, metadata: dict) -> DataFrame: + # User now can use their custom strat creation in addition to their + # future prediction "up" or "down". + df.loc[ (df[f"supertrend_1_buy_{self.buy_m1.value}_{self.buy_p1.value}"] == "up") & (df[f"supertrend_2_buy_{self.buy_m2.value}_{self.buy_p2.value}"] == "up") & (df[f"supertrend_3_buy_{self.buy_m3.value}_{self.buy_p3.value}"] == "up") & (df["do_predict"] == 1) & - (df['&s-up_or_down'] == 'up'), + (df['&s-up_or_down'] == 'up'), "enter_long", ] = 1 df.loc[ - (df[f"supertrend_1_sell_{self.sell_m1.value}_{self.sell_p1.value}"] == "down") & - (df[f"supertrend_2_sell_{self.sell_m2.value}_{self.sell_p2.value}"] == "down") & - (df[f"supertrend_3_sell_{self.sell_m3.value}_{self.sell_p3.value}"] == "down") & - (df["do_predict"] == 1) & + (df[f"supertrend_1_sell_{self.sell_m1.value}_{self.sell_p1.value}"] == "down") & + (df[f"supertrend_2_sell_{self.sell_m2.value}_{self.sell_p2.value}"] == "down") & + (df[f"supertrend_3_sell_{self.sell_m3.value}_{self.sell_p3.value}"] == "down") & + (df["do_predict"] == 1) & (df['&s-up_or_down'] == 'down'), "enter_short", ] = 1 @@ -291,7 +248,7 @@ class FreqaiExampleHybridStrategy(IStrategy): return df def populate_exit_trend(self, df: DataFrame, metadata: dict) -> DataFrame: - + df.loc[ (df[f"supertrend_2_sell_{self.sell_m2.value}_{self.sell_p2.value}"] == "down"), "exit_long", @@ -308,8 +265,8 @@ class FreqaiExampleHybridStrategy(IStrategy): return int(self.config["timeframe"][:-1]) def confirm_trade_entry(self, pair: str, order_type: str, amount: float, - rate: float, time_in_force: str, current_time, entry_tag, side: str, - **kwargs, ) -> bool: + rate: float, time_in_force: str, current_time, entry_tag, side: str, + **kwargs, ) -> bool: df, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) last_candle = df.iloc[-1].squeeze() @@ -322,7 +279,7 @@ class FreqaiExampleHybridStrategy(IStrategy): return False return True - + def leverage(self, pair: str, current_time: datetime, current_rate: float, proposed_leverage: float, max_leverage: float, entry_tag: Optional[str], side: str, **kwargs) -> float: @@ -335,6 +292,7 @@ class FreqaiExampleHybridStrategy(IStrategy): """ def supertrend(self, dataframe: DataFrame, multiplier, period): + df = dataframe.copy() last_row = dataframe.tail(1).index.item() @@ -354,17 +312,19 @@ class FreqaiExampleHybridStrategy(IStrategy): # Compute final upper and lower bands for i in range(period, last_row + 1): - FINAL_UB[i] = BASIC_UB[i] if BASIC_UB[i] < FINAL_UB[i - 1] or CLOSE[i - 1] > FINAL_UB[i - 1] else FINAL_UB[i - 1] - FINAL_LB[i] = BASIC_LB[i] if BASIC_LB[i] > FINAL_LB[i - 1] or CLOSE[i - 1] < FINAL_LB[i - 1] else FINAL_LB[i - 1] + FINAL_UB[i] = BASIC_UB[i] if BASIC_UB[i] < FINAL_UB[i - + 1] or CLOSE[i - 1] > FINAL_UB[i - 1] else FINAL_UB[i - 1] + FINAL_LB[i] = BASIC_LB[i] if BASIC_LB[i] > FINAL_LB[i - + 1] or CLOSE[i - 1] < FINAL_LB[i - 1] else FINAL_LB[i - 1] # Set the Supertrend value for i in range(period, last_row + 1): ST[i] = FINAL_UB[i] if ST[i - 1] == FINAL_UB[i - 1] and CLOSE[i] <= FINAL_UB[i] else \ - FINAL_LB[i] if ST[i - 1] == FINAL_UB[i - 1] and CLOSE[i] > FINAL_UB[i] else \ + FINAL_LB[i] if ST[i - 1] == FINAL_UB[i - 1] and CLOSE[i] > FINAL_UB[i] else \ FINAL_LB[i] if ST[i - 1] == FINAL_LB[i - 1] and CLOSE[i] >= FINAL_LB[i] else \ - FINAL_UB[i] if ST[i - 1] == FINAL_LB[i - 1] and CLOSE[i] < FINAL_LB[i] else 0.00 + FINAL_UB[i] if ST[i - 1] == FINAL_LB[i - 1] and CLOSE[i] < FINAL_LB[i] else 0.00 df_ST = pd.DataFrame(ST, columns=[st]) - df = pd.concat([df, df_ST],axis=1) + df = pd.concat([df, df_ST], axis=1) # Mark the trend direction up/down df[stx] = np.where((df[st] > 0.00), np.where((df['close'] < df[st]), 'down', 'up'), np.NaN) @@ -372,6 +332,6 @@ class FreqaiExampleHybridStrategy(IStrategy): df.fillna(0, inplace=True) return DataFrame(index=df.index, data={ - 'ST' : df[st], - 'STX' : df[stx] + 'ST': df[st], + 'STX': df[stx] }) From 64b083443752e9aa9af5fd81a2c77cf42db4c690 Mon Sep 17 00:00:00 2001 From: robcaulk Date: Sat, 20 Aug 2022 17:04:38 +0200 Subject: [PATCH 3/6] add credit in docstring --- freqtrade/templates/FreqaiHybridExampleStrategy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/templates/FreqaiHybridExampleStrategy.py b/freqtrade/templates/FreqaiHybridExampleStrategy.py index 99abf27d4..145590add 100644 --- a/freqtrade/templates/FreqaiHybridExampleStrategy.py +++ b/freqtrade/templates/FreqaiHybridExampleStrategy.py @@ -56,7 +56,7 @@ class FreqaiExampleHybridStrategy(IStrategy): } }, - This strategy is not designed to be used live + Thanks to @smarm and @jooopieeert for developing and sharing the strategy. """ minimal_roi = {"0": 0.1, "30": 0.75, "60": 0.05, "120": 0.025, "240": -1} From 6189aa817ce3dcb6b5185d62350e641d32c41a3e Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 20 Aug 2022 19:50:18 +0200 Subject: [PATCH 4/6] Fix HybridExample formatting --- .../templates/FreqaiHybridExampleStrategy.py | 25 ++++++++----------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/freqtrade/templates/FreqaiHybridExampleStrategy.py b/freqtrade/templates/FreqaiHybridExampleStrategy.py index 145590add..bb9c37b03 100644 --- a/freqtrade/templates/FreqaiHybridExampleStrategy.py +++ b/freqtrade/templates/FreqaiHybridExampleStrategy.py @@ -1,13 +1,13 @@ import logging -from typing import Optional import numpy as np import pandas as pd import talib.abstract as ta -from freqtrade.strategy import (DecimalParameter, IntParameter, IStrategy, - merge_informative_pair) from pandas import DataFrame +from freqtrade.strategy import IntParameter, IStrategy, merge_informative_pair + + logger = logging.getLogger(__name__) @@ -100,7 +100,7 @@ class FreqaiExampleHybridStrategy(IStrategy): sell_p2 = IntParameter(7, 21, default=10) sell_p3 = IntParameter(7, 21, default=10) - # FreqAI required function, leave as is or add you additional informatives to existing structure. + # FreqAI required function, leave as is or add additional informatives to existing structure. def informative_pairs(self): whitelist_pairs = self.dp.current_whitelist() corr_pairs = self.config["freqai"]["feature_parameters"]["include_corr_pairlist"] @@ -120,7 +120,7 @@ class FreqaiExampleHybridStrategy(IStrategy): self, pair, df, tf, informative=None, set_generalized_indicators=False ): """ - User feeds these indicators to FreqAI to train a classifier to decide + User feeds these indicators to FreqAI to train a classifier to decide if the market will go up or down. :param pair: pair to be used as informative @@ -177,6 +177,7 @@ class FreqaiExampleHybridStrategy(IStrategy): return df + # flake8: noqa: C901 def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: # User creates their own custom strat here. Present example is a supertrend @@ -280,12 +281,6 @@ class FreqaiExampleHybridStrategy(IStrategy): return True - def leverage(self, pair: str, current_time: datetime, current_rate: float, - proposed_leverage: float, max_leverage: float, entry_tag: Optional[str], side: str, - **kwargs) -> float: - - return 1 - """ Supertrend Indicator; adapted for freqtrade, optimized by the math genius. from: Perkmeister#2394 @@ -312,10 +307,10 @@ class FreqaiExampleHybridStrategy(IStrategy): # Compute final upper and lower bands for i in range(period, last_row + 1): - FINAL_UB[i] = BASIC_UB[i] if BASIC_UB[i] < FINAL_UB[i - - 1] or CLOSE[i - 1] > FINAL_UB[i - 1] else FINAL_UB[i - 1] - FINAL_LB[i] = BASIC_LB[i] if BASIC_LB[i] > FINAL_LB[i - - 1] or CLOSE[i - 1] < FINAL_LB[i - 1] else FINAL_LB[i - 1] + FINAL_UB[i] = (BASIC_UB[i] if BASIC_UB[i] < FINAL_UB[i - 1] + or CLOSE[i - 1] > FINAL_UB[i - 1] else FINAL_UB[i - 1]) + FINAL_LB[i] = (BASIC_LB[i] if BASIC_LB[i] > FINAL_LB[i - 1] + or CLOSE[i - 1] < FINAL_LB[i - 1] else FINAL_LB[i - 1]) # Set the Supertrend value for i in range(period, last_row + 1): From 005594c29cad73c7814059312af5def40ee8bfa6 Mon Sep 17 00:00:00 2001 From: smarmau Date: Sun, 28 Aug 2022 11:29:48 +0200 Subject: [PATCH 5/6] simplify hybrid template --- .../templates/FreqaiHybridExampleStrategy.py | 256 +++++++----------- 1 file changed, 93 insertions(+), 163 deletions(-) diff --git a/freqtrade/templates/FreqaiHybridExampleStrategy.py b/freqtrade/templates/FreqaiHybridExampleStrategy.py index bb9c37b03..dfd67ae90 100644 --- a/freqtrade/templates/FreqaiHybridExampleStrategy.py +++ b/freqtrade/templates/FreqaiHybridExampleStrategy.py @@ -4,6 +4,7 @@ import numpy as np import pandas as pd import talib.abstract as ta from pandas import DataFrame +from technical import qtpylib from freqtrade.strategy import IntParameter, IStrategy, merge_informative_pair @@ -48,7 +49,7 @@ class FreqaiExampleHybridStrategy(IStrategy): "indicator_periods_candles": [10, 20] }, "data_split_parameters": { - "test_size": 0.33, + "test_size": 0, "random_state": 1 }, "model_training_parameters": { @@ -56,49 +57,44 @@ class FreqaiExampleHybridStrategy(IStrategy): } }, - Thanks to @smarm and @jooopieeert for developing and sharing the strategy. + Thanks to @smarmau and @johanvulgt for developing and sharing the strategy. """ - minimal_roi = {"0": 0.1, "30": 0.75, "60": 0.05, "120": 0.025, "240": -1} + minimal_roi = { + "60": 0.01, + "30": 0.02, + "0": 0.04 + } + + plot_config = { + 'main_plot': { + 'tema': {}, + }, + 'subplots': { + "MACD": { + 'macd': {'color': 'blue'}, + 'macdsignal': {'color': 'orange'}, + }, + "RSI": { + 'rsi': {'color': 'red'}, + }, + "Up_or_down": { + '&s-up_or_down': {'color': 'green'}, + } + } + } process_only_new_candles = True - stoploss = -0.1 + stoploss = -0.05 use_exit_signal = True startup_candle_count: int = 300 can_short = True - buy_params = { - "buy_m1": 4, - "buy_m2": 7, - "buy_m3": 1, - "buy_p1": 8, - "buy_p2": 9, - "buy_p3": 8, - } - - # Sell hyperspace params: - sell_params = { - "sell_m1": 1, - "sell_m2": 3, - "sell_m3": 6, - "sell_p1": 16, - "sell_p2": 18, - "sell_p3": 18, - } - - buy_m1 = IntParameter(1, 7, default=1) - buy_m2 = IntParameter(1, 7, default=3) - buy_m3 = IntParameter(1, 7, default=4) - buy_p1 = IntParameter(7, 21, default=14) - buy_p2 = IntParameter(7, 21, default=10) - buy_p3 = IntParameter(7, 21, default=10) - - sell_m1 = IntParameter(1, 7, default=1) - sell_m2 = IntParameter(1, 7, default=3) - sell_m3 = IntParameter(1, 7, default=4) - sell_p1 = IntParameter(7, 21, default=14) - sell_p2 = IntParameter(7, 21, default=10) - sell_p3 = IntParameter(7, 21, default=10) + # Hyperoptable parameters + buy_rsi = IntParameter(low=1, high=50, default=30, space='buy', optimize=True, load=True) + sell_rsi = IntParameter(low=50, high=100, default=70, space='sell', optimize=True, load=True) + short_rsi = IntParameter(low=51, high=100, default=70, space='sell', optimize=True, load=True) + exit_short_rsi = IntParameter(low=1, high=50, default=30, space='buy', optimize=True, load=True) # FreqAI required function, leave as is or add additional informatives to existing structure. def informative_pairs(self): @@ -143,7 +139,6 @@ class FreqaiExampleHybridStrategy(IStrategy): informative[f"%-{coin}adx-period_{t}"] = ta.ADX(informative, window=t) informative[f"%-{coin}sma-period_{t}"] = ta.SMA(informative, timeperiod=t) informative[f"%-{coin}ema-period_{t}"] = ta.EMA(informative, timeperiod=t) - informative[f"%-{coin}mfi-period_{t}"] = ta.MFI(informative, timeperiod=t) informative[f"%-{coin}roc-period_{t}"] = ta.ROC(informative, timeperiod=t) informative[f"%-{coin}relative_volume-period_{t}"] = ( informative["volume"] / informative["volume"].rolling(t).mean() @@ -183,150 +178,85 @@ class FreqaiExampleHybridStrategy(IStrategy): # User creates their own custom strat here. Present example is a supertrend # based strategy. - for multiplier in self.buy_m1.range: - for period in self.buy_p1.range: - dataframe[f"supertrend_1_buy_{multiplier}_{period}"] = self.supertrend( - dataframe, multiplier, period - )["STX"] - - for multiplier in self.buy_m2.range: - for period in self.buy_p2.range: - dataframe[f"supertrend_2_buy_{multiplier}_{period}"] = self.supertrend( - dataframe, multiplier, period - )["STX"] - - for multiplier in self.buy_m3.range: - for period in self.buy_p3.range: - dataframe[f"supertrend_3_buy_{multiplier}_{period}"] = self.supertrend( - dataframe, multiplier, period - )["STX"] - - for multiplier in self.sell_m1.range: - for period in self.sell_p1.range: - dataframe[f"supertrend_1_sell_{multiplier}_{period}"] = self.supertrend( - dataframe, multiplier, period - )["STX"] - - for multiplier in self.sell_m2.range: - for period in self.sell_p2.range: - dataframe[f"supertrend_2_sell_{multiplier}_{period}"] = self.supertrend( - dataframe, multiplier, period - )["STX"] - - for multiplier in self.sell_m3.range: - for period in self.sell_p3.range: - dataframe[f"supertrend_3_sell_{multiplier}_{period}"] = self.supertrend( - dataframe, multiplier, period - )["STX"] - dataframe = self.freqai.start(dataframe, metadata, self) + # TA indicators to combine with the Freqai targets + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + # TEMA - Triple Exponential Moving Average + dataframe['tema'] = ta.TEMA(dataframe, timeperiod=9) + return dataframe def populate_entry_trend(self, df: DataFrame, metadata: dict) -> DataFrame: - # User now can use their custom strat creation in addition to their - # future prediction "up" or "down". + df.loc[ + ( + # Signal: RSI crosses above 30 + (qtpylib.crossed_above(df['rsi'], self.buy_rsi.value)) & + (df['tema'] <= df['bb_middleband']) & # Guard: tema below BB middle + (df['tema'] > df['tema'].shift(1)) & # Guard: tema is raising + (df['volume'] > 0) & # Make sure Volume is not 0 + (df['do_predict'] == 1) & # Make sure Freqai is confident in the prediction + # Only enter trade if Freqai thinks the trend is in this direction + (df['&s-up_or_down'] == 'up') + ), + 'enter_long'] = 1 df.loc[ - (df[f"supertrend_1_buy_{self.buy_m1.value}_{self.buy_p1.value}"] == "up") & - (df[f"supertrend_2_buy_{self.buy_m2.value}_{self.buy_p2.value}"] == "up") & - (df[f"supertrend_3_buy_{self.buy_m3.value}_{self.buy_p3.value}"] == "up") & - (df["do_predict"] == 1) & - (df['&s-up_or_down'] == 'up'), - "enter_long", - ] = 1 - - df.loc[ - (df[f"supertrend_1_sell_{self.sell_m1.value}_{self.sell_p1.value}"] == "down") & - (df[f"supertrend_2_sell_{self.sell_m2.value}_{self.sell_p2.value}"] == "down") & - (df[f"supertrend_3_sell_{self.sell_m3.value}_{self.sell_p3.value}"] == "down") & - (df["do_predict"] == 1) & - (df['&s-up_or_down'] == 'down'), - "enter_short", - ] = 1 + ( + # Signal: RSI crosses above 70 + (qtpylib.crossed_above(df['rsi'], self.short_rsi.value)) & + (df['tema'] > df['bb_middleband']) & # Guard: tema above BB middle + (df['tema'] < df['tema'].shift(1)) & # Guard: tema is falling + (df['volume'] > 0) & # Make sure Volume is not 0 + (df['do_predict'] == 1) & # Make sure Freqai is confident in the prediction + # Only enter trade if Freqai thinks the trend is in this direction + (df['&s-up_or_down'] == 'down') + ), + 'enter_short'] = 1 return df def populate_exit_trend(self, df: DataFrame, metadata: dict) -> DataFrame: df.loc[ - (df[f"supertrend_2_sell_{self.sell_m2.value}_{self.sell_p2.value}"] == "down"), - "exit_long", - ] = 1 + ( + # Signal: RSI crosses above 70 + (qtpylib.crossed_above(df['rsi'], self.sell_rsi.value)) & + (df['tema'] > df['bb_middleband']) & # Guard: tema above BB middle + (df['tema'] < df['tema'].shift(1)) & # Guard: tema is falling + (df['volume'] > 0) # Make sure Volume is not 0 + ), + + 'exit_long'] = 1 df.loc[ - (df[f"supertrend_2_buy_{self.buy_m2.value}_{self.buy_p2.value}"] == "up"), - "exit_short", - ] = 1 + ( + # Signal: RSI crosses above 30 + (qtpylib.crossed_above(df['rsi'], self.exit_short_rsi.value)) & + # Guard: tema below BB middle + (df['tema'] <= df['bb_middleband']) & + (df['tema'] > df['tema'].shift(1)) & # Guard: tema is raising + (df['volume'] > 0) # Make sure Volume is not 0 + ), + 'exit_short'] = 1 return df def get_ticker_indicator(self): return int(self.config["timeframe"][:-1]) - - def confirm_trade_entry(self, pair: str, order_type: str, amount: float, - rate: float, time_in_force: str, current_time, entry_tag, side: str, - **kwargs, ) -> bool: - - df, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) - last_candle = df.iloc[-1].squeeze() - - if side == "long": - if rate > (last_candle["close"] * (1 + 0.0025)): - return False - else: - if rate < (last_candle["close"] * (1 - 0.0025)): - return False - - return True - - """ - Supertrend Indicator; adapted for freqtrade, optimized by the math genius. - from: Perkmeister#2394 - """ - - def supertrend(self, dataframe: DataFrame, multiplier, period): - - df = dataframe.copy() - last_row = dataframe.tail(1).index.item() - - df['TR'] = ta.TRANGE(df) - df['ATR'] = ta.SMA(df['TR'], period) - - st = 'ST_' + str(period) + '_' + str(multiplier) - stx = 'STX_' + str(period) + '_' + str(multiplier) - - # Compute basic upper and lower bands - BASIC_UB = ((df['high'] + df['low']) / 2 + multiplier * df['ATR']).values - BASIC_LB = ((df['high'] + df['low']) / 2 - multiplier * df['ATR']).values - FINAL_UB = np.zeros(last_row + 1) - FINAL_LB = np.zeros(last_row + 1) - ST = np.zeros(last_row + 1) - CLOSE = df['close'].values - - # Compute final upper and lower bands - for i in range(period, last_row + 1): - FINAL_UB[i] = (BASIC_UB[i] if BASIC_UB[i] < FINAL_UB[i - 1] - or CLOSE[i - 1] > FINAL_UB[i - 1] else FINAL_UB[i - 1]) - FINAL_LB[i] = (BASIC_LB[i] if BASIC_LB[i] > FINAL_LB[i - 1] - or CLOSE[i - 1] < FINAL_LB[i - 1] else FINAL_LB[i - 1]) - - # Set the Supertrend value - for i in range(period, last_row + 1): - ST[i] = FINAL_UB[i] if ST[i - 1] == FINAL_UB[i - 1] and CLOSE[i] <= FINAL_UB[i] else \ - FINAL_LB[i] if ST[i - 1] == FINAL_UB[i - 1] and CLOSE[i] > FINAL_UB[i] else \ - FINAL_LB[i] if ST[i - 1] == FINAL_LB[i - 1] and CLOSE[i] >= FINAL_LB[i] else \ - FINAL_UB[i] if ST[i - 1] == FINAL_LB[i - 1] and CLOSE[i] < FINAL_LB[i] else 0.00 - df_ST = pd.DataFrame(ST, columns=[st]) - df = pd.concat([df, df_ST], axis=1) - - # Mark the trend direction up/down - df[stx] = np.where((df[st] > 0.00), np.where((df['close'] < df[st]), 'down', 'up'), np.NaN) - - df.fillna(0, inplace=True) - - return DataFrame(index=df.index, data={ - 'ST': df[st], - 'STX': df[stx] - }) From ff3a4995c1c34cc737313908cde420aa0ef50754 Mon Sep 17 00:00:00 2001 From: smarmau Date: Sun, 28 Aug 2022 11:45:20 +0200 Subject: [PATCH 6/6] remove unnecessary code --- freqtrade/templates/FreqaiHybridExampleStrategy.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/freqtrade/templates/FreqaiHybridExampleStrategy.py b/freqtrade/templates/FreqaiHybridExampleStrategy.py index dfd67ae90..0a91455f5 100644 --- a/freqtrade/templates/FreqaiHybridExampleStrategy.py +++ b/freqtrade/templates/FreqaiHybridExampleStrategy.py @@ -257,6 +257,3 @@ class FreqaiExampleHybridStrategy(IStrategy): 'exit_short'] = 1 return df - - def get_ticker_indicator(self): - return int(self.config["timeframe"][:-1])