From c07aa686973147611d02063a612d18e11d6f940d Mon Sep 17 00:00:00 2001 From: Kagari Date: Sun, 4 Jan 2026 05:10:33 +0800 Subject: [PATCH 1/2] Refactor Supertrend method for efficiency Refactor Supertrend calculation to improve clarity and performance by using numpy arrays for calculations. --- user_data/strategies/Supertrend.py | 115 ++++++++++++++++------------- 1 file changed, 65 insertions(+), 50 deletions(-) diff --git a/user_data/strategies/Supertrend.py b/user_data/strategies/Supertrend.py index 1ab90b0..60e68cd 100644 --- a/user_data/strategies/Supertrend.py +++ b/user_data/strategies/Supertrend.py @@ -80,30 +80,47 @@ class Supertrend(IStrategy): sell_p3 = IntParameter(7, 21, default=14) def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + new_cols = [] + 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'] - + st = self.supertrend(dataframe, multiplier, period)[['STX']].rename( + columns={'STX': f'supertrend_1_buy_{multiplier}_{period}'}) + new_cols.append(st) + 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'] - + st = self.supertrend(dataframe, multiplier, period)[['STX']].rename( + columns={'STX': f'supertrend_2_buy_{multiplier}_{period}'}) + new_cols.append(st) + 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'] - + st = self.supertrend(dataframe, multiplier, period)[['STX']].rename( + columns={'STX': f'supertrend_3_buy_{multiplier}_{period}'}) + new_cols.append(st) + 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'] - + st = self.supertrend(dataframe, multiplier, period)[['STX']].rename( + columns={'STX': f'supertrend_1_sell_{multiplier}_{period}'}) + new_cols.append(st) + 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'] - + st = self.supertrend(dataframe, multiplier, period)[['STX']].rename( + columns={'STX': f'supertrend_2_sell_{multiplier}_{period}'}) + new_cols.append(st) + 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'] - + st = self.supertrend(dataframe, multiplier, period)[['STX']].rename( + columns={'STX': f'supertrend_3_sell_{multiplier}_{period}'}) + new_cols.append(st) + + if new_cols: + dataframe = pd.concat([dataframe] + new_cols, axis=1) + return dataframe def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: @@ -136,42 +153,40 @@ class Supertrend(IStrategy): Supertrend Indicator; adapted for freqtrade from: https://github.com/freqtrade/freqtrade-strategies/issues/30 """ - def supertrend(self, dataframe: DataFrame, multiplier, period): + def supertrend(self, dataframe: pd.DataFrame, multiplier, period): df = dataframe.copy() - - 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 - df['basic_ub'] = (df['high'] + df['low']) / 2 + multiplier * df['ATR'] - df['basic_lb'] = (df['high'] + df['low']) / 2 - multiplier * df['ATR'] - - # Compute final upper and lower bands - df['final_ub'] = 0.00 - df['final_lb'] = 0.00 - for i in range(period, len(df)): - df['final_ub'].iat[i] = df['basic_ub'].iat[i] if df['basic_ub'].iat[i] < df['final_ub'].iat[i - 1] or df['close'].iat[i - 1] > df['final_ub'].iat[i - 1] else df['final_ub'].iat[i - 1] - df['final_lb'].iat[i] = df['basic_lb'].iat[i] if df['basic_lb'].iat[i] > df['final_lb'].iat[i - 1] or df['close'].iat[i - 1] < df['final_lb'].iat[i - 1] else df['final_lb'].iat[i - 1] - - # Set the Supertrend value - df[st] = 0.00 - for i in range(period, len(df)): - df[st].iat[i] = df['final_ub'].iat[i] if df[st].iat[i - 1] == df['final_ub'].iat[i - 1] and df['close'].iat[i] <= df['final_ub'].iat[i] else \ - df['final_lb'].iat[i] if df[st].iat[i - 1] == df['final_ub'].iat[i - 1] and df['close'].iat[i] > df['final_ub'].iat[i] else \ - df['final_lb'].iat[i] if df[st].iat[i - 1] == df['final_lb'].iat[i - 1] and df['close'].iat[i] >= df['final_lb'].iat[i] else \ - df['final_ub'].iat[i] if df[st].iat[i - 1] == df['final_lb'].iat[i - 1] and df['close'].iat[i] < df['final_lb'].iat[i] else 0.00 - # Mark the trend direction up/down - df[stx] = np.where((df[st] > 0.00), np.where((df['close'] < df[st]), 'down', 'up'), None) - - # Remove basic and final bands from the columns - df.drop(['basic_ub', 'basic_lb', 'final_ub', 'final_lb'], inplace=True, axis=1) - - df.fillna(0, inplace=True) - - return DataFrame(index=df.index, data={ - 'ST' : df[st], - 'STX' : df[stx] - }) + high = df['high'].values + low = df['low'].values + close = df['close'].values + length = len(df) + + # 1. TR and ATR + tr = ta.TRANGE(df['high'], df['low'], df['close']) + atr = pd.Series(tr).rolling(period).mean().to_numpy() + + # 2. basic upper / lower bands + basic_ub = (high + low) / 2 + multiplier * atr + basic_lb = (high + low) / 2 - multiplier * atr + + # 3. final upper / lower bands + final_ub = np.zeros(length) + final_lb = np.zeros(length) + final_ub[:period] = basic_ub[:period] + final_lb[:period] = basic_lb[:period] + + for i in range(period, length): + 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] + + # 4. ST calculation + st = np.zeros(length) + for i in range(period, length): + if st[i-1] == final_ub[i-1]: + st[i] = final_ub[i] if close[i] <= final_ub[i] else final_lb[i] + elif st[i-1] == final_lb[i-1]: + st[i] = final_lb[i] if close[i] >= final_lb[i] else final_ub[i] + + # 5. STX direction + stx = np.where(st > 0, np.where(close < st, 'down', 'up'), None) + + return pd.DataFrame({'ST': st, 'STX': stx}, index=df.index) From fc4894e5a5ee2afc9805647bfdee773172da9653 Mon Sep 17 00:00:00 2001 From: Kagari Date: Mon, 12 Jan 2026 23:07:28 +0800 Subject: [PATCH 2/2] Import pandas and handle NaN values in Supertrend Added pandas import and implemented fillna to handle NaN values in the Supertrend strategy. --- user_data/strategies/Supertrend.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/user_data/strategies/Supertrend.py b/user_data/strategies/Supertrend.py index 60e68cd..78de972 100644 --- a/user_data/strategies/Supertrend.py +++ b/user_data/strategies/Supertrend.py @@ -18,6 +18,7 @@ from freqtrade.strategy import IStrategy, IntParameter from pandas import DataFrame import talib.abstract as ta import numpy as np +import pandas as pd class Supertrend(IStrategy): # Buy params, Sell params, ROI, Stoploss and Trailing Stop are values generated by 'freqtrade hyperopt --strategy Supertrend --hyperopt-loss ShortTradeDurHyperOptLoss --timerange=20210101- --timeframe=1h --spaces all' @@ -171,8 +172,6 @@ class Supertrend(IStrategy): # 3. final upper / lower bands final_ub = np.zeros(length) final_lb = np.zeros(length) - final_ub[:period] = basic_ub[:period] - final_lb[:period] = basic_lb[:period] for i in range(period, length): 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] @@ -189,4 +188,9 @@ class Supertrend(IStrategy): # 5. STX direction stx = np.where(st > 0, np.where(close < st, 'down', 'up'), None) - return pd.DataFrame({'ST': st, 'STX': stx}, index=df.index) + # 6. fillna + result = pd.DataFrame({'ST': st, 'STX': stx}, index=df.index) + result.fillna(0, inplace=True) + + return result +