Timeframe
1m
Direction
Long Only
Stoploss
-100.0%
Trailing Stop
No
ROI
0m: 5.0%
Interface Version
N/A
Startup Candles
N/A
Indicators
1
from freqtrade.strategy import IStrategy, stoploss_from_absolute
from pandas import DataFrame
import talib.abstract as ta
from datetime import datetime
from freqtrade.persistence import Trade
class BollingerReversionStrategy(IStrategy):
minimal_roi = {"0": 0.05}
stoploss = -1
timeframe = '1m'
position_adjustment_enable = True
bollinger_period = 20
bollinger_deviation = 2.0
# Example specific variables
max_entry_position_adjustment = 3
# This number is explained a bit further down
max_dca_multiplier = 5.5
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
bollinger = ta.BBANDS(
dataframe,
timeperiod=self.bollinger_period,
nbdevup=self.bollinger_deviation,
nbdevdn=self.bollinger_deviation,
matype=0
)
dataframe['bb_upper'] = bollinger['upperband']
dataframe['bb_lower'] = bollinger['lowerband']
dataframe['bb_middle'] = bollinger['middleband']
dataframe['close_prev'] = dataframe['close'].shift(1)
return dataframe
def adjust_trade_position(self, trade: Trade, current_time: datetime,
current_rate: float, current_profit: float,
min_stake: float | None, max_stake: float,
current_entry_rate: float, current_exit_rate: float,
current_entry_profit: float, current_exit_profit: float,
**kwargs
) -> float | None | tuple[float | None, str | None]:
# Si hay órdenes abiertas, no hacer nada
if trade.has_open_orders:
return None
# Configuración del DCA por drawdown
dca_step = 0.03 # Cada 3% de caída
dca_multipliers = [0.5, 1.0, 1.5] # Tamaños progresivos
max_dcas = self.max_entry_position_adjustment
# Cuántos DCA ya hicimos
count_entries = trade.nr_of_successful_entries
# ¿Ya alcanzamos el máximo?
if count_entries >= max_dcas:
return None
# Drawdown actual (ejemplo: -0.07 → 7% caída)
dd = abs(current_profit)
# Qué nivel de DCA le toca según la caída
# ejemplo: dd = 0.07, dca_step = 0.03 → level = 2
dca_level = int(dd / dca_step)
# Si el nivel calculado no coincide con la cantidad actual de entradas,
# no es el momento de hacer DCA todavía.
if dca_level <= count_entries:
return None
# Tamaño base del primer trade
base_stake = trade.select_filled_orders(trade.entry_side)[0].stake_amount_filled
# Seleccionar multiplicador correspondiente
# Si dca_level se pasa del listado, usa el último
multiplier = dca_multipliers[min(dca_level - 1, len(dca_multipliers) - 1)]
stake = base_stake * multiplier
return stake, f"DCA_Drawdown_{dd:.2%}"
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
dataframe.loc[
(
(dataframe['close_prev'] < dataframe['bb_lower']) &
(dataframe['close'] > dataframe['bb_lower'])
), 'buy'] = 1
return dataframe
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
return dataframe
# Usa esta función para gestionar las salidas
def custom_exit(self, pair: str, trade, current_time, current_rate, current_profit, **kwargs):
# Obtener dataframe analizado
dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
# Última vela
last_candle = dataframe.iloc[-1].squeeze()
# Señal técnica de salida
sell_signal = (
(last_candle['close_prev'] > last_candle['bb_upper']) and
(last_candle['close'] < last_candle['bb_upper'])
)
# Solo sale si hay señal técnica y profit positivo
if sell_signal and current_profit > 0:
return "sell_signal"
return None