Timeframe
5m
Direction
Long Only
Stoploss
N/A
Trailing Stop
Yes
ROI
0m: 5.0%, 30m: 3.0%, 60m: 1.0%
Interface Version
3
Startup Candles
N/A
Indicators
1
freqtrade/freqtrade-strategies
Strategy 003 author@: Gerald Lonlas github@: https://github.com/freqtrade/freqtrade-strategies
from freqtrade.strategy import IStrategy
from freqtrade.strategy import BooleanParameter, DecimalParameter
from pandas import DataFrame
import numpy as np
import talib.abstract as ta # Import for RSI
# --------------------------------
# Strategy: engulfing
# Author: Gemini
# Version: 3.2 (Fixed Hyperopt Toggles)
# --------------------------------
class engulfing(IStrategy):
INTERFACE_VERSION = 3
# -----------------------------------
# HYPEROPT PARAMETERS
# NOTE: Only parameters with optimize=True are tuned by Hyperopt.
# -----------------------------------
# Buy Filters (FIXED TO OPTIMIZED VALUES)
use_volume_filter = BooleanParameter(default=True, space='buy', optimize=False)
use_trend_filter = BooleanParameter(default=False, space='buy', optimize=False)
use_body_size_filter = BooleanParameter(default=True, space='buy', optimize=False)
use_risk_filter = BooleanParameter(default=False, space='buy', optimize=False)
# 🛑 ENABLE OPTIMIZATION FOR THE NEW RSI FILTER 🛑
use_rsi_filter = BooleanParameter(default=True, space='buy', optimize=True)
# Filter Values (FIXED TO OPTIMIZED VALUES)
volume_multiplier = DecimalParameter(1.0, 3.0, decimals=2, default=2.83, space='buy', optimize=False)
risk_threshold = DecimalParameter(0.1, 0.8, decimals=2, default=0.33, space='buy', optimize=False)
# 🛑 ENABLE OPTIMIZATION FOR THE NEW RSI THRESHOLD 🛑
rsi_buy_threshold = DecimalParameter(30.0, 70.0, decimals=1, default=60.0, space='buy', optimize=True)
# Stoploss Parameter (FIXED TO OPTIMIZED VALUE)
stoploss_param = DecimalParameter(
-0.20, -0.05, decimals=3, default=-0.088, space='stoploss', optimize=False
)
# Trailing Stop Parameters (FIXED TO OPTIMIZED VALUES)
tsp_param = DecimalParameter(0.005, 0.03, decimals=3, default=0.335, space='trailing_stop', optimize=False)
tsp_offset_param = DecimalParameter(0.01, 0.05, decimals=3, default=0.373, space='trailing_stop', optimize=False)
# -----------------------------------
# STOPLOSS PROPERTY
@property
def stoploss(self):
return self.stoploss_param.value
@stoploss.setter
def stoploss(self, value: float):
self.stoploss_param.value = value
# TRAILING_STOP_POSITIVE PROPERTY
@property
def trailing_stop_positive(self):
return self.tsp_param.value
@trailing_stop_positive.setter
def trailing_stop_positive(self, value: float):
self.tsp_param.value = value
# TRAILING_STOP_POSITIVE_OFFSET PROPERTY
@property
def trailing_stop_positive_offset(self):
return self.tsp_offset_param.value
@trailing_stop_positive_offset.setter
def trailing_stop_positive_offset(self, value: float):
self.tsp_offset_param.value = value
# Strategy Parameters (Fixed)
minimal_roi = { "0": 0.05, "30": 0.03, "60": 0.01 }
trailing_stop = True
trailing_only_offset_is_reached = False
timeframe = '5m'
startup_candle_count: int = 20
process_only_new_candles = False
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Adds required indicators for signals and filters.
"""
# --- 1. Base Signal: Bullish Engulfing ---
dataframe['bullish_engulfing'] = (
(dataframe['close'] > dataframe['open']) &
(dataframe['close'].shift(1) < dataframe['open'].shift(1)) &
(dataframe['open'] <= dataframe['close'].shift(1)) &
(dataframe['close'] >= dataframe['open'].shift(1))
)
# --- 2. Volume Filter Calculation ---
dataframe['volume_mean_20'] = dataframe['volume'].rolling(window=20).mean()
dataframe['volume_above_average'] = (
dataframe['volume'] > (dataframe['volume_mean_20'] * self.volume_multiplier.value)
)
# --- 3. Body Size Filter Calculation ---
dataframe['body_size'] = (dataframe['close'] - dataframe['open']).abs()
dataframe['body_size_mean_10'] = dataframe['body_size'].rolling(window=10).mean()
dataframe['strong_body'] = (
dataframe['body_size'] > dataframe['body_size_mean_10']
)
# --- 4. Trend Filter Calculation ---
dataframe['short_term_downtrend'] = (
(dataframe['low'] < dataframe['low'].shift(1)) &
(dataframe['low'].shift(1) < dataframe['low'].shift(2))
)
# --- 5. Risk Filter Calculation ---
dataframe['low_risk_entry'] = (
dataframe['close'] < dataframe['low'] + (dataframe['high'] - dataframe['low']) * self.risk_threshold.value
)
# --- 6. RSI Confirmation Filter ---
dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14)
dataframe['rsi_confirm'] = (dataframe['rsi'] < self.rsi_buy_threshold.value)
return dataframe
def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Combines base signal with all filters.
"""
conditions = [
# 1. Base Signal (ALWAYS REQUIRED)
dataframe['bullish_engulfing'],
(dataframe['volume'] > 0), # Freqtrade safety
]
# 2. Filter: Volume Confirmation
if self.use_volume_filter.value:
conditions.append(dataframe['volume_above_average'])
# 3. Filter: Trend Confirmation
if self.use_trend_filter.value:
conditions.append(dataframe['short_term_downtrend'])
# 4. Filter: Signal Strength
if self.use_body_size_filter.value:
conditions.append(dataframe['strong_body'])
# 5. Filter: Risk Check
if self.use_risk_filter.value:
conditions.append(dataframe['low_risk_entry'])
# 6. Filter: RSI Check
if self.use_rsi_filter.value:
conditions.append(dataframe['rsi_confirm'])
# Combine all active conditions
if conditions:
dataframe.loc[
np.all(conditions, axis=0),
'buy'] = 1
return dataframe
def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Uses Bearish Engulfing for exit.
"""
dataframe['bearish_engulfing'] = (
(dataframe['close'] < dataframe['open']) &
(dataframe['close'].shift(1) > dataframe['open'].shift(1)) &
(dataframe['open'] >= dataframe['close'].shift(1)) &
(dataframe['close'] <= dataframe['open'].shift(1))
)
dataframe.loc[(dataframe['bearish_engulfing']), 'sell'] = 1
return dataframe