RSI Oversold Bounce strategy. Buy: RSI crosses above oversold level (coming from below). Sell: RSI crosses above overbought level. Filter: ADX > threshold to confirm trend strength.
Timeframe
1h
Direction
Long Only
Stoploss
-8.0%
Trailing Stop
Yes
ROI
0m: 6.0%, 60m: 4.0%, 120m: 2.0%, 240m: 0.0%
Interface Version
3
Startup Candles
N/A
Indicators
2
freqtrade/freqtrade-strategies
Strategy 003 author@: Gerald Lonlas github@: https://github.com/freqtrade/freqtrade-strategies
"""
RSI Oversold Bounce Strategy
Buy when RSI drops below oversold threshold (default 30) then starts rising.
Sell when RSI rises above overbought threshold (default 70).
Includes ADX filter to avoid flat/ranging markets with weak signals.
Timeframe: 1h
Pairs: BTC/USDT, ETH/USDT, SOL/USDT, BNB/USDT
"""
import numpy as np
import pandas as pd
from datetime import datetime, timedelta, timezone
from pandas import DataFrame
from typing import Optional, Union
from freqtrade.strategy import (
IStrategy,
Trade,
Order,
PairLocks,
BooleanParameter,
CategoricalParameter,
DecimalParameter,
IntParameter,
RealParameter,
)
import talib.abstract as ta
from technical import qtpylib
class RsiOversoldStrategy(IStrategy):
"""
RSI Oversold Bounce strategy.
Buy: RSI crosses above oversold level (coming from below).
Sell: RSI crosses above overbought level.
Filter: ADX > threshold to confirm trend strength.
"""
INTERFACE_VERSION = 3
can_short: bool = False
minimal_roi = {
"0": 0.06,
"60": 0.04,
"120": 0.02,
"240": 0.0,
}
stoploss = -0.08 # 8% stop loss
# Trailing stop
trailing_stop = True
trailing_stop_positive = 0.015
trailing_stop_positive_offset = 0.035
trailing_only_offset_is_reached = True
timeframe = "1h"
process_only_new_candles = True
use_exit_signal = True
exit_profit_only = False
ignore_roi_if_entry_signal = False
# Hyperoptable parameters
buy_rsi = IntParameter(15, 40, default=30, space="buy", optimize=True)
sell_rsi = IntParameter(60, 85, default=70, space="sell", optimize=True)
buy_adx = IntParameter(15, 40, default=25, space="buy", optimize=True)
buy_rsi_period = IntParameter(7, 21, default=14, space="buy", optimize=True)
startup_candle_count: int = 50
order_types = {
"entry": "limit",
"exit": "limit",
"stoploss": "market",
"stoploss_on_exchange": False,
}
order_time_in_force = {"entry": "GTC", "exit": "GTC"}
plot_config = {
"main_plot": {},
"subplots": {
"RSI": {
"rsi": {"color": "red"},
},
"ADX": {
"adx": {"color": "purple"},
},
},
}
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""Calculate RSI and ADX indicators."""
# RSI with multiple periods for hyperopt
for period in range(7, 22):
dataframe[f"rsi_{period}"] = ta.RSI(dataframe, timeperiod=period)
# ADX for trend strength filter
dataframe["adx"] = ta.ADX(dataframe)
return dataframe
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""Buy when RSI crosses above oversold threshold with ADX confirmation."""
rsi_col = f"rsi_{self.buy_rsi_period.value}"
dataframe.loc[
(
(qtpylib.crossed_above(dataframe[rsi_col], self.buy_rsi.value))
& (dataframe["adx"] > self.buy_adx.value)
& (dataframe["volume"] > 0)
),
"enter_long",
] = 1
return dataframe
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""Sell when RSI crosses above overbought threshold."""
rsi_col = f"rsi_{self.buy_rsi_period.value}"
dataframe.loc[
(
(qtpylib.crossed_above(dataframe[rsi_col], self.sell_rsi.value))
& (dataframe["volume"] > 0)
),
"exit_long",
] = 1
return dataframe