From b37c31cc21c077bd6b2b4b9e9c993e076eb0548a Mon Sep 17 00:00:00 2001 From: robcaulk Date: Thu, 2 Jun 2022 14:37:40 +0200 Subject: [PATCH] fix ta-lib issue with simultaneous method access --- freqtrade/templates/FreqaiExampleStrategy.py | 239 ++++++++++--------- 1 file changed, 132 insertions(+), 107 deletions(-) diff --git a/freqtrade/templates/FreqaiExampleStrategy.py b/freqtrade/templates/FreqaiExampleStrategy.py index d9dc38f0d..4fc2b6e48 100644 --- a/freqtrade/templates/FreqaiExampleStrategy.py +++ b/freqtrade/templates/FreqaiExampleStrategy.py @@ -49,9 +49,10 @@ class FreqaiExampleStrategy(IStrategy): startup_candle_count: int = 300 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) + 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() @@ -85,89 +86,103 @@ class FreqaiExampleStrategy(IStrategy): :coin: the name of the coin which will modify the feature names. """ - if informative is None: - informative = self.dp.get_pair_dataframe(pair, tf) + with self.model.bridge.lock: + 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"]: + # first loop is automatically duplicating indicators for time periods + for t in self.freqai_info["feature_parameters"]["indicator_periods"]: - t = int(t) - informative['%-' + coin + "rsi-period_" + str(t)] = ta.RSI(informative, timeperiod=t) - informative['%-' + coin + "mfi-period_" + str(t)] = ta.MFI(informative, timeperiod=t) - informative['%-' + coin + "adx-period_" + str(t)] = ta.ADX(informative, window=t) - informative[coin + "20sma-period_" + str(t)] = ta.SMA(informative, timeperiod=t) - informative[coin + "21ema-period_" + str(t)] = ta.EMA(informative, timeperiod=t) - informative['%-' + coin + "close_over_20sma-period_" + - str(t)] = (informative["close"] / - informative[coin + "20sma-period_" + str(t)]) + t = int(t) + informative["%-" + coin + "rsi-period_" + str(t)] = ta.RSI( + informative, timeperiod=t + ) + informative["%-" + coin + "mfi-period_" + str(t)] = ta.MFI( + informative, timeperiod=t + ) + informative["%-" + coin + "adx-period_" + str(t)] = ta.ADX(informative, window=t) + informative[coin + "20sma-period_" + str(t)] = ta.SMA(informative, timeperiod=t) + informative[coin + "21ema-period_" + str(t)] = ta.EMA(informative, timeperiod=t) + informative["%-" + coin + "close_over_20sma-period_" + str(t)] = ( + informative["close"] / informative[coin + "20sma-period_" + str(t)] + ) - informative['%-' + coin + "mfi-period_" + str(t)] = ta.MFI(informative, timeperiod=t) + informative["%-" + coin + "mfi-period_" + str(t)] = ta.MFI( + informative, timeperiod=t + ) - informative[coin + "ema21-period_" + str(t)] = ta.EMA(informative, timeperiod=t) - informative[coin + "sma20-period_" + str(t)] = ta.SMA(informative, timeperiod=t) + informative[coin + "ema21-period_" + str(t)] = ta.EMA(informative, timeperiod=t) + informative[coin + "sma20-period_" + str(t)] = ta.SMA(informative, timeperiod=t) - bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=t, - stds=2.2) - informative[coin + "bb_lowerband-period_" + str(t)] = bollinger["lower"] - informative[coin + "bb_middleband-period_" + str(t)] = bollinger["mid"] - informative[coin + "bb_upperband-period_" + str(t)] = bollinger["upper"] - informative['%-' + coin + "bb_width-period_" + str(t)] = ( - informative[coin + "bb_upperband-period_" + str(t)] - - informative[coin + "bb_lowerband-period_" + str(t)] - ) / informative[coin + "bb_middleband-period_" + str(t)] - informative['%-' + coin + "close-bb_lower-period_" + str(t)] = ( - informative["close"] / informative[coin + "bb_lowerband-period_" + str(t)] - ) + bollinger = qtpylib.bollinger_bands( + qtpylib.typical_price(informative), window=t, stds=2.2 + ) + informative[coin + "bb_lowerband-period_" + str(t)] = bollinger["lower"] + informative[coin + "bb_middleband-period_" + str(t)] = bollinger["mid"] + informative[coin + "bb_upperband-period_" + str(t)] = bollinger["upper"] + informative["%-" + coin + "bb_width-period_" + str(t)] = ( + informative[coin + "bb_upperband-period_" + str(t)] + - informative[coin + "bb_lowerband-period_" + str(t)] + ) / informative[coin + "bb_middleband-period_" + str(t)] + informative["%-" + coin + "close-bb_lower-period_" + str(t)] = ( + informative["close"] / informative[coin + "bb_lowerband-period_" + str(t)] + ) - informative['%-' + coin + "roc-period_" + str(t)] = ta.ROC(informative, timeperiod=t) - informative['%-' + coin + "adx-period_" + str(t)] = ta.ADX(informative, window=t) + informative["%-" + coin + "roc-period_" + str(t)] = ta.ROC( + informative, timeperiod=t + ) + informative["%-" + coin + "adx-period_" + str(t)] = ta.ADX(informative, window=t) - macd = ta.MACD(informative, timeperiod=t) - informative['%-' + coin + "macd-period_" + str(t)] = macd["macd"] + macd = ta.MACD(informative, timeperiod=t) + informative["%-" + coin + "macd-period_" + str(t)] = macd["macd"] - informative['%-' + coin + "relative_volume-period_" + str(t)] = ( - informative["volume"] / informative["volume"].rolling(t).mean() - ) + informative["%-" + coin + "relative_volume-period_" + str(t)] = ( + informative["volume"] / informative["volume"].rolling(t).mean() + ) - informative['%-' + coin + "pct-change"] = informative["close"].pct_change() - informative['%-' + coin + "raw_volume"] = informative["volume"] - informative['%-' + coin + 'raw_price'] = informative['close'] + informative["%-" + coin + "pct-change"] = informative["close"].pct_change() + informative["%-" + coin + "raw_volume"] = informative["volume"] + informative["%-" + 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"]["shift"] + 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) + 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"]["shift"] + 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) + 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 pair == metadata['pair'] and tf == self.timeframe: - 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 pair == metadata["pair"] and tf == self.timeframe: + df["%-day_of_week"] = (df["date"].dt.dayofweek + 1) / 7 + df["%-hour_of_day"] = (df["date"].dt.hour + 1) / 25 return df def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: self.freqai_info = self.config["freqai"] - self.pair = metadata['pair'] + self.pair = metadata["pair"] # the following loops are necessary for building the features # indicated by the user in the configuration file. # All indicators must be populated by populate_any_indicators() for live functionality # to work correctly. for tf in self.freqai_info["timeframes"]: - dataframe = self.populate_any_indicators(metadata, self.pair, dataframe.copy(), tf, - coin=self.pair.split("/")[0] + "-") + dataframe = self.populate_any_indicators( + metadata, self.pair, dataframe.copy(), tf, coin=self.pair.split("/")[0] + "-" + ) for pair in self.freqai_info["corr_pairlist"]: - if metadata['pair'] in pair: + if metadata["pair"] in pair: continue # do not include whitelisted pair twice if it is in corr_pairlist dataframe = self.populate_any_indicators( metadata, pair, dataframe.copy(), tf, coin=pair.split("/")[0] + "-" @@ -189,38 +204,28 @@ class FreqaiExampleStrategy(IStrategy): def populate_entry_trend(self, df: DataFrame, metadata: dict) -> DataFrame: - enter_long_conditions = [ - df['do_predict'] == 1, - df['prediction'] > df["target_roi"] - ] + enter_long_conditions = [df["do_predict"] == 1, df["prediction"] > df["target_roi"]] if enter_long_conditions: - df.loc[reduce(lambda x, y: x & y, - enter_long_conditions), ["enter_long", "enter_tag"]] = (1, 'long') + df.loc[ + reduce(lambda x, y: x & y, enter_long_conditions), ["enter_long", "enter_tag"] + ] = (1, "long") - enter_short_conditions = [ - df['do_predict'] == 1, - df['prediction'] < df["sell_roi"] - ] + enter_short_conditions = [df["do_predict"] == 1, df["prediction"] < df["sell_roi"]] if enter_short_conditions: - df.loc[reduce(lambda x, y: x & y, - enter_short_conditions), ["enter_short", "enter_tag"]] = (1, 'short') + df.loc[ + reduce(lambda x, y: x & y, enter_short_conditions), ["enter_short", "enter_tag"] + ] = (1, "short") return df def populate_exit_trend(self, df: DataFrame, metadata: dict) -> DataFrame: - exit_long_conditions = [ - df['do_predict'] == 1, - df['prediction'] < df['sell_roi'] * 0.25 - ] + exit_long_conditions = [df["do_predict"] == 1, df["prediction"] < df["sell_roi"] * 0.25] if exit_long_conditions: df.loc[reduce(lambda x, y: x & y, exit_long_conditions), "exit_long"] = 1 - exit_short_conditions = [ - df['do_predict'] == 1, - df['prediction'] > df['target_roi'] * 0.25 - ] + exit_short_conditions = [df["do_predict"] == 1, df["prediction"] > df["target_roi"] * 0.25] if exit_short_conditions: df.loc[reduce(lambda x, y: x & y, exit_short_conditions), "exit_short"] = 1 @@ -229,19 +234,20 @@ 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): + 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)] + 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) + follow_mode = self.config.get("freqai", {}).get("follow_mode", False) if not follow_mode: pair_dict = self.model.bridge.data_drawer.pair_dict @@ -250,53 +256,63 @@ class FreqaiExampleStrategy(IStrategy): entry_tag = trade.enter_tag - if 'prediction' + entry_tag not in pair_dict[pair]: + if "prediction" + entry_tag not in pair_dict[pair]: with self.model.bridge.lock: - pair_dict[pair]['prediction' + entry_tag] = abs(trade_candle['prediction']) + pair_dict[pair]["prediction" + entry_tag] = abs(trade_candle["prediction"]) if not follow_mode: self.model.bridge.data_drawer.save_drawer_to_disk() else: self.model.bridge.data_drawer.save_follower_dict_to_dist() else: - if pair_dict[pair]['prediction' + entry_tag] > 0: - roi_price = abs(trade_candle['prediction']) + if pair_dict[pair]["prediction" + entry_tag] > 0: + roi_price = abs(trade_candle["prediction"]) else: with self.model.bridge.lock: - pair_dict[pair]['prediction' + entry_tag] = abs(trade_candle['prediction']) + pair_dict[pair]["prediction" + entry_tag] = abs(trade_candle["prediction"]) if not follow_mode: self.model.bridge.data_drawer.save_drawer_to_disk() else: self.model.bridge.data_drawer.save_follower_dict_to_dist() - roi_price = abs(trade_candle['prediction']) + roi_price = abs(trade_candle["prediction"]) roi_time = self.max_roi_time_long.value - roi_decay = roi_price * (1 - ((current_time - trade.open_date_utc).seconds) / - (roi_time * 60)) + 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' + return "roi_custom_win" if current_profit < -roi_decay: - return 'roi_custom_loss' + 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: + 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) + follow_mode = self.config.get("freqai", {}).get("follow_mode", False) if not follow_mode: pair_dict = self.model.bridge.data_drawer.pair_dict else: pair_dict = self.model.bridge.data_drawer.follower_dict with self.model.bridge.lock: - pair_dict[pair]['prediction' + entry_tag] = 0 + pair_dict[pair]["prediction" + entry_tag] = 0 if not follow_mode: self.model.bridge.data_drawer.save_drawer_to_disk() else: @@ -304,18 +320,27 @@ class FreqaiExampleStrategy(IStrategy): return True - 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: + 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)): + if side == "long": + if rate > (last_candle["close"] * (1 + 0.0025)): return False else: - if rate < (last_candle['close'] * (1 - 0.0025)): + if rate < (last_candle["close"] * (1 - 0.0025)): return False return True