Timeframe
1h
Direction
Long Only
Stoploss
-26.5%
Trailing Stop
Yes
ROI
0m: 10.0%, 30m: 75.0%, 60m: 5.0%, 120m: 2.5%
Interface Version
N/A
Startup Candles
18
Indicators
5
freqtrade/freqtrade-strategies
Strategy 003 author@: Gerald Lonlas github@: https://github.com/freqtrade/freqtrade-strategies
"""
Triple Supertrend with ADX/RSI Confirmation (Enhanced):
* Description: This strategy generates three separate Supertrend indicators for both buy and sell conditions.
A long (buy) signal is generated when all three buy Supertrend indicators are in an uptrend,
and additional trend/momentum filters (ADX and RSI) confirm bullish conditions.
Conversely, a short (sell) signal is generated when all three sell Supertrend indicators are in a downtrend,
and the ADX/RSI filters confirm bearish conditions.
Exits are triggered when a secondary Supertrend indicator signals a reversal or if RSI indicates overbought/oversold conditions.
* Author: @juankysoriano (Juan Carlos Soriano) - enhanced by modification
* github: https://github.com/juankysoriano/
*** NOTE: This enhanced Supertrend strategy now includes ADX and RSI filters as additional confirmation.
Use at your own risk.
"""
import logging
import numpy as np
import talib.abstract as ta
from freqtrade.strategy import IntParameter, IStrategy
from numpy.lib import math
from pandas import DataFrame
class TripleSuperTrendADXRSI(IStrategy):
INTERFACE_VERSION: int = 3
# Enable short trades
can_short: bool = True
# Hyperparameters for generating the Supertrend indicators (Buy side)
buy_params = {
"buy_m1": 4,
"buy_m2": 7,
"buy_m3": 1,
"buy_p1": 8,
"buy_p2": 9,
"buy_p3": 8,
}
# Hyperparameters for generating the Supertrend indicators (Sell side)
sell_params = {
"sell_m1": 1,
"sell_m2": 3,
"sell_m3": 6,
"sell_p1": 16,
"sell_p2": 18,
"sell_p3": 18,
}
# ROI table: defines the minimal return on investment for different time periods
minimal_roi = {"0": 0.1, "30": 0.75, "60": 0.05, "120": 0.025}
# Stoploss threshold
stoploss = -0.265
# Trailing stop configuration
trailing_stop = True
trailing_stop_positive = 0.05
trailing_stop_positive_offset = 0.1
trailing_only_offset_is_reached = False
# Strategy timeframe and startup candle count
timeframe = "1h"
startup_candle_count = 18
# ----- SUPER TREND PARAMETERS FOR BUY SIGNALS -----
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)
# ----- SUPER TREND PARAMETERS FOR SELL SIGNALS -----
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)
# ----- NEW PARAMETERS FOR TREND / MOMENTUM CONFIRMATION -----
# ADX filter settings to avoid low-trend environments
adx_period = IntParameter(10, 30, default=14, space="buy")
adx_threshold = IntParameter(20, 40, default=25, space="buy")
# RSI filter settings for entry confirmation and exit signals
rsi_period = IntParameter(10, 30, default=14, space="buy")
# For longs, require RSI to be above this value (bullish momentum confirmation)
rsi_long_threshold = IntParameter(40, 60, default=50, space="buy")
# For shorts, require RSI to be below this value (bearish momentum confirmation)
rsi_short_threshold = IntParameter(40, 60, default=50, space="buy")
# RSI exit thresholds (to catch reversals)
rsi_long_exit = IntParameter(70, 80, default=75, space="sell")
rsi_short_exit = IntParameter(20, 30, default=25, space="sell")
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Adds multiple Supertrend, ADX, and RSI indicators to the dataframe.
Generates Supertrend indicators over different parameter ranges for both buy and sell signals.
Also computes ADX for trend strength and RSI for momentum.
"""
# Generate Supertrend indicators for BUY signals with different multipliers and periods
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"]
# Generate Supertrend indicators for SELL signals with different multipliers and periods
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"]
# Calculate ADX to measure the strength of the trend; helps to filter out weak trends
dataframe['adx'] = ta.ADX(dataframe, timeperiod=self.adx_period.value)
# Calculate RSI to determine momentum and possible overbought/oversold conditions
dataframe['rsi'] = ta.RSI(dataframe, timeperiod=self.rsi_period.value)
return dataframe
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Creates entry signals based on a combination of Supertrend, ADX, and RSI indicators.
A long entry is signaled if:
- All three Supertrend BUY indicators are in an uptrend ('up').
- The trading volume is positive.
- The ADX is above the threshold indicating a strong trend.
- RSI is above the long threshold to confirm bullish momentum.
A short entry is signaled similarly with sell conditions.
"""
# LONG ENTRY CONDITION
dataframe.loc[
(
(dataframe[f"supertrend_1_buy_{self.buy_m1.value}_{self.buy_p1.value}"] == "up")
& (dataframe[f"supertrend_2_buy_{self.buy_m2.value}_{self.buy_p2.value}"] == "up")
& (dataframe[f"supertrend_3_buy_{self.buy_m3.value}_{self.buy_p3.value}"] == "up")
& (dataframe["volume"] > 0)
& (dataframe["adx"] > self.adx_threshold.value)
& (dataframe["rsi"] > self.rsi_long_threshold.value)
),
"enter_long",
] = 1
# SHORT ENTRY CONDITION
dataframe.loc[
(
(dataframe[f"supertrend_1_sell_{self.sell_m1.value}_{self.sell_p1.value}"] == "down")
& (dataframe[f"supertrend_2_sell_{self.sell_m2.value}_{self.sell_p2.value}"] == "down")
& (dataframe[f"supertrend_3_sell_{self.sell_m3.value}_{self.sell_p3.value}"] == "down")
& (dataframe["volume"] > 0)
& (dataframe["adx"] > self.adx_threshold.value)
& (dataframe["rsi"] < self.rsi_short_threshold.value)
),
"enter_short",
] = 1
return dataframe
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Sets exit conditions for trades based on a secondary Supertrend indicator or extreme RSI readings.
For longs: exit if the secondary SELL Supertrend indicator is down OR if RSI is above the long exit threshold.
For shorts: exit if the secondary BUY Supertrend indicator is up OR if RSI is below the short exit threshold.
"""
# LONG EXIT CONDITION
dataframe.loc[
(
(dataframe[f"supertrend_2_sell_{self.sell_m2.value}_{self.sell_p2.value}"] == "down")
| (dataframe["rsi"] > self.rsi_long_exit.value)
),
"exit_long",
] = 1
# SHORT EXIT CONDITION
dataframe.loc[
(
(dataframe[f"supertrend_2_buy_{self.buy_m2.value}_{self.buy_p2.value}"] == "up")
| (dataframe["rsi"] < self.rsi_short_exit.value)
),
"exit_short",
] = 1
return dataframe
def supertrend(self, dataframe: DataFrame, multiplier, period):
"""
Calculates the Supertrend indicator based on the Average True Range (ATR).
The indicator computes basic upper and lower bands and then adjusts them to create final bands.
It then determines the trend direction ('up' or 'down') based on the closing price relative to these bands.
"""
df = dataframe.copy()
# Calculate True Range and its moving average (ATR)
df["TR"] = ta.TRANGE(df)
df["ATR"] = ta.SMA(df["TR"], period)
# Define column names for the Supertrend value and its direction
st = "ST_" + str(period) + "_" + str(multiplier)
stx = "STX_" + str(period) + "_" + str(multiplier)
# Compute the 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"]
# Initialize final bands
df["final_ub"] = 0.00
df["final_lb"] = 0.00
close_col = df.columns.get_loc("close")
basic_ub_col = df.columns.get_loc("basic_ub")
basic_lb_col = df.columns.get_loc("basic_lb")
final_ub_col = df.columns.get_loc("final_ub")
final_lb_col = df.columns.get_loc("final_lb")
# Adjust the final bands based on previous values and the closing price
for i in range(period, len(df)):
df.iat[i, final_ub_col] = (
df.iat[i, basic_ub_col]
if df.iat[i, basic_ub_col] < df.iat[i - 1, final_ub_col]
or df.iat[i - 1, close_col] > df.iat[i - 1, final_ub_col]
else df.iat[i - 1, final_ub_col]
)
df.iat[i, final_lb_col] = (
df.iat[i, basic_lb_col]
if df.iat[i, basic_lb_col] > df.iat[i - 1, final_lb_col]
or df.iat[i - 1, close_col] < df.iat[i - 1, final_lb_col]
else df.iat[i - 1, final_lb_col]
)
# Initialize the Supertrend column
df[st] = 0.00
st_col = df.columns.get_loc(st)
# Determine the Supertrend value based on the relationship between closing price and the final bands
for i in range(period, len(df)):
df.iat[i, st_col] = (
df.iat[i, final_ub_col]
if df.iat[i - 1, st_col] == df.iat[i - 1, final_ub_col]
and df.iat[i, close_col] <= df.iat[i, final_ub_col]
else df.iat[i, final_lb_col]
if df.iat[i - 1, st_col] == df.iat[i - 1, final_ub_col]
and df.iat[i, close_col] > df.iat[i, final_ub_col]
else df.iat[i, final_lb_col]
if df.iat[i - 1, st_col] == df.iat[i - 1, final_lb_col]
and df.iat[i, close_col] >= df.iat[i, final_lb_col]
else df.iat[i, final_ub_col]
if df.iat[i - 1, st_col] == df.iat[i - 1, final_lb_col]
and df.iat[i, close_col] < df.iat[i, final_lb_col]
else 0.00
)
# Mark the trend direction as 'up' or 'down'
df[stx] = np.where(
(df[st] > 0.00), np.where((df["close"] < df[st]), "down", "up"), None
)
# Cleanup: remove temporary band columns and fill any missing values
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]})