From 51ca00975129991df0adf7f50155dfac92bd8ba6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Delacotte?= Date: Tue, 25 Nov 2025 21:53:18 +0100 Subject: [PATCH] Frictrade --- Frictrade.py | 114 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 84 insertions(+), 30 deletions(-) diff --git a/Frictrade.py b/Frictrade.py index e8f0fbd..1d13b55 100644 --- a/Frictrade.py +++ b/Frictrade.py @@ -56,10 +56,10 @@ class Frictrade(IStrategy): # Custom stoploss use_custom_stoploss = False - trailing_stop = True + trailing_stop = False trailing_stop_positive = 0.15 - trailing_stop_positive_offset = 0.1 - trailing_only_offset_is_reached = False + trailing_stop_positive_offset = 0.5 + trailing_only_offset_is_reached = True # Buy hypers timeframe = '1m' @@ -170,7 +170,6 @@ class Frictrade(IStrategy): self.pairs[pair]['last_count_of_buys'] = trade.nr_of_successful_entries # self.pairs[pair]['count_of_buys'] self.pairs[pair]['last_sell'] = rate self.pairs[pair]['last_candle'] = last_candle - self.pairs[pair]['max_profit'] = 0 self.pairs[pair]['previous_profit'] = 0 self.trades = list() dispo = round(self.wallets.get_available_stake_amount()) @@ -185,6 +184,7 @@ class Frictrade(IStrategy): dispo=dispo, profit=round(profit, 2) ) + self.pairs[pair]['max_profit'] = 0 self.pairs[pair]['force_sell'] = False self.pairs[pair]['has_gain'] = 0 self.pairs[pair]['current_profit'] = 0 @@ -438,6 +438,32 @@ class Frictrade(IStrategy): count_buys = count self.pairs[pair]['total_amount'] = amount + dataframe['absolute_min'] = dataframe['mid'].rolling(1440, min_periods=1).min() + for i in [1, 2, 3, 4]: + dataframe[f"lvl_{i}_pct"] = dataframe['absolute_min'] * (1 + 0.01 * i) + + # dataframe['absolute_max'] = dataframe['mid'].rolling(1440, min_periods=1).max() + # + # absolute_min = dataframe['absolute_min'].min() + # absolute_max = dataframe['absolute_max'].max() + # + # # Écart total + # diff = absolute_max - absolute_min + # + # # Nombre de lignes intermédiaires (1% steps) + # steps = int((absolute_max - absolute_min) / (absolute_min * 0.01)) + # + # # Niveaux de prix à 1%, 2%, ..., steps% + # levels = [absolute_min * (1 + i / 100) for i in range(1, steps + 1)] + # levels = [lvl for lvl in levels if lvl < absolute_max] # évite le dernier niveau exact + # + # # ajout dans le DataFrame + # for i, lvl in enumerate(levels, start=1): + # dataframe[f"lvl_{i}_pct"] = lvl + + # # Indices correspondants + # indices = [(dataframe['mid'] - lvl).abs().idxmin() for lvl in levels] + return dataframe def getOpenTrades(self): @@ -464,25 +490,39 @@ class Frictrade(IStrategy): return dataframe + # def adjust_stake_amount(self, pair: str, last_candle: DataFrame): + # # Calculer le minimum des 14 derniers jours + # nb_pairs = len(self.dp.current_whitelist()) + # + # base_stake_amount = self.config.get('stake_amount') + # + # if True : #self.pairs[pair]['count_of_buys'] == 0: + # factor = 1 #65 / min(65, last_candle['rsi_1d']) + # # if last_candle['min_max_60'] > 0.04: + # # factor = 2 + # + # adjusted_stake_amount = base_stake_amount #max(base_stake_amount / 5, base_stake_amount * factor) + # else: + # adjusted_stake_amount = self.pairs[pair]['first_amount'] + # + # if self.pairs[pair]['count_of_buys'] == 0: + # self.pairs[pair]['first_amount'] = adjusted_stake_amount + # + # return adjusted_stake_amount + def adjust_stake_amount(self, pair: str, last_candle: DataFrame): - # Calculer le minimum des 14 derniers jours - nb_pairs = len(self.dp.current_whitelist()) + # Calcule max/min 180 + low180 = last_candle["min180"] + high180 = last_candle["max180"] - base_stake_amount = self.config.get('stake_amount') + mult = 1 - ((last_candle["mid"] - low180) / (high180 - low180)) - if True : #self.pairs[pair]['count_of_buys'] == 0: - factor = 1 #65 / min(65, last_candle['rsi_1d']) - # if last_candle['min_max_60'] > 0.04: - # factor = 2 - - adjusted_stake_amount = base_stake_amount #max(base_stake_amount / 5, base_stake_amount * factor) - else: - adjusted_stake_amount = self.pairs[pair]['first_amount'] - - if self.pairs[pair]['count_of_buys'] == 0: - self.pairs[pair]['first_amount'] = adjusted_stake_amount - - return adjusted_stake_amount + print(f"low={low180} mid={last_candle['mid']} high={high180} mult={mult}") + # base_size = montant de base que tu veux utiliser (ex: stake_amount ou autre) + base_size = 2 * self.config.get('stake_amount') # exemple fraction du portefeuille; adapte selon ton code + # new stake proportionnel à mult + new_stake = base_size * mult + return new_stake def adjust_trade_position(self, trade: Trade, current_time: datetime, current_rate: float, current_profit: float, min_stake: float, @@ -544,10 +584,9 @@ class Frictrade(IStrategy): # stake_amount = last_amount * current_rate * 0.5 # return stake_amount - - condition = last_candle['hapercent'] > 0 + condition = last_candle['hapercent'] > 0 and last_candle['sma60_deriv1'] > 0 limit_buy = 40 - if decline >= dca_threshold: + if decline >= dca_threshold and condition: try: if self.pairs[pair]['has_gain'] and profit > 0: self.pairs[pair]['force_sell'] = True @@ -594,7 +633,8 @@ class Frictrade(IStrategy): self.printLog(exception) return None - if (increase >= dca_threshold and self.wallets.get_available_stake_amount() > 0): + if current_profit > 0 and (increase >= dca_threshold and self.wallets.get_available_stake_amount() > 0) \ + and (last_candle['max_rsi_24'] < 60): try: self.pairs[pair]['previous_profit'] = profit stake_amount = min(self.wallets.get_available_stake_amount(), self.adjust_stake_amount(pair, last_candle)) @@ -649,8 +689,17 @@ class Frictrade(IStrategy): count_of_buys = trade.nr_of_successful_entries profit = trade.calc_profit(current_rate) #round(current_profit * trade.stake_amount, 1) - self.pairs[pair]['max_profit'] = max(self.pairs[pair]['max_profit'], profit) + + if current_profit > 0: + self.pairs[pair]['max_profit'] = max(self.pairs[pair]['max_profit'], profit) + # else: + # self.pairs[pair]['max_profit'] = 0 + max_profit = self.pairs[pair]['max_profit'] + + # if current_profit > 0: + # print(f"profit={profit} max_profit={max_profit} current_profit={current_profit}") + baisse = 0 if profit > 0: baisse = 1 - (profit / max_profit) @@ -668,11 +717,14 @@ class Frictrade(IStrategy): self.pairs[pair]['max_touch'] = max(last_candle['close'], self.pairs[pair]['max_touch']) # ----- 3) Calcul du profit max atteint ----- - profit_max = (max_price - trade.open_rate) / trade.open_rate + # profit_max = (max_price - trade.open_rate) / trade.open_rate # ----- 5) Calcul du trailing stop dynamique ----- # Exemple : offset=0.321 => stop à +24.8% - trailing_stop = max_price * (1 - self.trailing_stop_positive) + trailing_stop = max_profit * (1.0 - self.trailing_stop_positive) + baisse = 0 + if max_profit: + baisse = (max_profit - profit) / max_profit if minutes % 15 == 0: self.log_trade( @@ -682,7 +734,7 @@ class Frictrade(IStrategy): dispo=dispo, pair=pair, rate=last_candle['close'], - trade_type=f"{round(profit_max * 100, 2)} - {round(trailing_stop,0)}", + trade_type=f"{round(profit, 2)} {round(max_profit, 2)} {round(trailing_stop,2)}", profit=round(profit, 2), buys=count_of_buys, stake=0 @@ -690,12 +742,14 @@ class Frictrade(IStrategy): # ----- 4) OFFSET : faut-il attendre de dépasser trailing_stop_positive_offset ? ----- if self.trailing_only_offset_is_reached: - if profit_max < self.trailing_stop_positive_offset: + # Max profit pas atteint ET perte < 2 * self.trailing_stop_positive + if max_profit < min(2, self.trailing_stop_positive_offset * (count_of_buys - self.pairs[pair]['has_gain']))\ + and (baisse < 0.5 and max_profit > self.trailing_stop_positive_offset): #2 * self.trailing_stop_positive: return None # ne pas activer le trailing encore # Sinon : trailing actif dès le début # ----- 6) Condition de vente ----- - if current_rate <= trailing_stop and self.wallets.get_available_stake_amount() < 300: + if profit > 0 and profit <= trailing_stop: return f"stop_{count_of_buys}" return None