import backtrader as bt import math class CrossoverStrategy(bt.Strategy): params = ( ('risk_percent', 0.005), ('sma_fast', 20), ('sma_slow', 50), ('macd1', 12), ('macd2', 26), ('macdsig', 9), ('rsi_period', 14), ('atr_period', 14), ('atr_multiplier', 1.5), # Ajustement pour un stop-loss plus efficace ('trend_filter_sma', 200), ('trailing_stop_multiplier', 1.8), # Ajustement pour un trailing stop plus réactif ('range_atr_threshold', 0.002), ('rsi_reentry_threshold', 50), ('cooldown_period', 3), ('tp_multiplier', 2.0), # Ajustement du Take-Profit ('partial_tp_ratio', 1.5), # Sécurisation partielle des gains ('max_losses_before_reduction', 3), # Réduction du risque après X pertes consécutives ('risk_reduction_factor', 0.5), # Réduction de 50% après pertes successives ) def __init__(self): self.sma_fast = bt.indicators.SimpleMovingAverage(self.data.close, period=self.params.sma_fast) self.sma_slow = bt.indicators.SimpleMovingAverage(self.data.close, period=self.params.sma_slow) self.sma_trend = bt.indicators.SimpleMovingAverage(self.data.close, period=self.params.trend_filter_sma) self.macd = bt.indicators.MACD(self.data.close, period_me1=self.params.macd1, period_me2=self.params.macd2, period_signal=self.params.macdsig) self.rsi = bt.indicators.RelativeStrengthIndex(self.data.close, period=self.params.rsi_period) self.atr = bt.indicators.AverageTrueRange(period=self.params.atr_period) self.order = None self.trailing_stop = None self.last_trade_time = None self.last_trend = None self.entry_time = None self.atr_take_profit = None self.loss_streak = 0 self.dynamic_risk_percent = self.params.risk_percent def next(self): if self.order: return if self.last_trade_time and (self.data.datetime[0] - self.last_trade_time) < self.params.cooldown_period: return cash = self.broker.get_cash() value = self.broker.get_value() risk_value = value * self.dynamic_risk_percent max_size = cash / self.data.close[0] trend_up = self.sma_fast[0] > self.sma_slow[0] and self.sma_fast[0] > self.sma_trend[0] trend_down = self.sma_fast[0] < self.sma_slow[0] and self.sma_fast[0] < self.sma_trend[0] market_range = abs(self.sma_fast[0] - self.sma_slow[0]) < self.params.range_atr_threshold * self.atr[0] if not self.position: if trend_up and self.macd.macd[0] > -0.2 and self.rsi[0] > 40 and not market_range: self._enter_long_position() elif trend_down and self.macd.macd[0] < 0.2 and self.rsi[0] < 60 and not market_range: self._enter_short_position() else: self._manage_open_position() def _enter_long_position(self): cost = self.data.close[0] atr_stop_loss = cost - self.params.atr_multiplier * self.atr[0] self.atr_take_profit = cost + self.params.tp_multiplier * (cost - atr_stop_loss) risk_per_share = cost - atr_stop_loss size = self._calculate_position_size(risk_per_share) if self.order or size == 0: return self.order = self.buy(size=size, exectype=bt.Order.Market) self.trailing_stop = cost - self.params.trailing_stop_multiplier * self.atr[0] self.last_trade_time = self.data.datetime[0] self.entry_time = self.data.datetime[0] def _enter_short_position(self): cost = self.data.close[0] atr_stop_loss = cost + self.params.atr_multiplier * self.atr[0] self.atr_take_profit = cost - self.params.tp_multiplier * (atr_stop_loss - cost) risk_per_share = atr_stop_loss - cost size = self._calculate_position_size(risk_per_share) if self.order or size == 0: return self.order = self.sell(size=size, exectype=bt.Order.Market) self.trailing_stop = cost + self.params.trailing_stop_multiplier * self.atr[0] self.last_trade_time = self.data.datetime[0] self.entry_time = self.data.datetime[0] def _calculate_position_size(self, risk_per_share): cash = self.broker.get_cash() risk_value = self.broker.get_value() * self.dynamic_risk_percent return min(int(risk_value / risk_per_share), int(cash / self.data.close[0])) def _manage_open_position(self): current_price = self.data.close[0] if self.position.size > 0: self.trailing_stop = max(self.trailing_stop, current_price - self.params.trailing_stop_multiplier * self.atr[0]) if current_price >= self.atr_take_profit * self.params.partial_tp_ratio: self.sell(size=self.position.size // 2) if current_price <= self.trailing_stop or current_price >= self.atr_take_profit: self.order = self.close() elif self.position.size < 0: self.trailing_stop = min(self.trailing_stop, current_price + self.params.trailing_stop_multiplier * self.atr[0]) if current_price <= self.atr_take_profit * self.params.partial_tp_ratio: self.buy(size=self.position.size // 2) if current_price >= self.trailing_stop or current_price <= self.atr_take_profit: self.order = self.close() def notify_order(self, order): if order.status in [bt.Order.Completed, bt.Order.Canceled, bt.Order.Margin]: if order.status == bt.Order.Completed and order.isbuy(): self.loss_streak = 0 elif order.status == bt.Order.Completed and order.issell(): self.loss_streak += 1 if self.loss_streak >= self.params.max_losses_before_reduction: self.dynamic_risk_percent *= self.params.risk_reduction_factor self.order = None