From a7b09858f4cc2fb6bf53d52fb8e4f0652676405f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Delacotte?= Date: Wed, 26 Nov 2025 18:26:54 +0100 Subject: [PATCH] Frictrade --- Frictrade.py | 83 ++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 70 insertions(+), 13 deletions(-) diff --git a/Frictrade.py b/Frictrade.py index 1d13b55..bae54d7 100644 --- a/Frictrade.py +++ b/Frictrade.py @@ -343,7 +343,9 @@ class Frictrade(IStrategy): f"| {date:<16} |{action:<10} | {pair[0:3]:<3} | {trade_type or '-':<18} |{rate or '-':>9}| {dispo or '-':>6} " f"|{color}{profit or '-':>10}{RESET}| {pct_max or '-':>6} | {round(self.pairs[pair]['max_touch'], 2) or '-':>11} | {last_lost or '-':>12} " f"| {last_max or '-':>7} | {last_min or '-':>7} |{total_counts or '-':>5}|{stake or '-':>7}" - f"{round(last_candle['max_rsi_24'], 1) or '-' :>6}|" + f"{round(last_candle['max_rsi_24'], 1) or '-' :>6}|{round(last_candle['rsi_1h'], 1) or '-' :>6}|{round(last_candle['rsi_1d'], 1) or '-' :>6}|" + f"{round(last_candle['rtp_1h'] * 100, 0) or '-' :>6}|{round(last_candle['rtp_1d'] * 100, 0) or '-' :>6}|" + ) def printLineLog(self): @@ -404,11 +406,22 @@ class Frictrade(IStrategy): dataframe['min180'] = talib.MIN(dataframe['mid'], timeperiod=180) dataframe['max180'] = talib.MAX(dataframe['mid'], timeperiod=180) - # # ################### INFORMATIVE 1h - # informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe='1h') + # ################### INFORMATIVE 1h + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe='1h') + informative['mid'] = informative['open'] + (informative['close'] - informative['open']) / 2 # informative = self.populate1hIndicators(df=informative, metadata=metadata) - # # informative = self.calculateRegression(informative, 'mid', lookback=15) - # dataframe = merge_informative_pair(dataframe, informative, '1m', '1h', ffill=True) + informative['rsi'] = talib.RSI(informative['mid'], timeperiod=14) + informative = self.rsi_trend_probability(informative) + # informative = self.calculateRegression(informative, 'mid', lookback=15) + dataframe = merge_informative_pair(dataframe, informative, '1m', '1h', ffill=True) + + # ################### INFORMATIVE 1d + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe='1d') + informative['mid'] = informative['open'] + (informative['close'] - informative['open']) / 2 + informative['rsi'] = talib.RSI(informative['mid'], timeperiod=5) + informative = self.rsi_trend_probability(informative) + # informative = self.calculateRegression(informative, 'mid', lookback=15) + dataframe = merge_informative_pair(dataframe, informative, '1m', '1d', ffill=True) dataframe['last_price'] = dataframe['close'] dataframe['first_price'] = dataframe['close'] @@ -439,10 +452,16 @@ class Frictrade(IStrategy): 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['absolute_max'] = dataframe['mid'].rolling(1440, min_periods=1).max() + # steps = (dataframe['absolute_max'] - dataframe['absolute_min']) / (dataframe['absolute_min'] * 0.01) + # levels = [dataframe['absolute_min'] * (1 + i / 100) for i in range(1, steps + 1)] + # + # print(levels) + + # print(f"min={dataframe['absolute_min'].min()} max={dataframe['absolute_max'].max()}") + for i in [0, 1, 2, 3]: 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() @@ -475,6 +494,7 @@ class Frictrade(IStrategy): dataframe.loc[ ( (dataframe['sma5_inv'] == 1) + # & (dataframe['max_rsi_24'] < 80) # & ( # (dataframe['percent3'] <= -0.003) # | (dataframe['percent12'] <= -0.003) @@ -719,9 +739,26 @@ class Frictrade(IStrategy): # ----- 3) Calcul du profit max atteint ----- # profit_max = (max_price - trade.open_rate) / trade.open_rate + current_trailing_stop_positive = self.trailing_stop_positive + current_trailing_only_offset_is_reached = self.trailing_only_offset_is_reached + current_trailing_stop_positive_offset = self.trailing_stop_positive_offset + + max_ = last_candle['max180'] + min_ = last_candle['min180'] + mid = last_candle['mid'] + # éviter division par zéro + position = (mid - min_) / (max_ - min_) + zone = int(position * 3) # 0 à 2 + + if zone == 0: + current_trailing_stop_positive = True + current_trailing_stop_positive_offset = self.trailing_stop_positive_offset * 2 + # if zone == 1: + + # ----- 5) Calcul du trailing stop dynamique ----- # Exemple : offset=0.321 => stop à +24.8% - trailing_stop = max_profit * (1.0 - self.trailing_stop_positive) + trailing_stop = max_profit * (1.0 - current_trailing_stop_positive) baisse = 0 if max_profit: baisse = (max_profit - profit) / max_profit @@ -741,10 +778,10 @@ class Frictrade(IStrategy): ) # ----- 4) OFFSET : faut-il attendre de dépasser trailing_stop_positive_offset ? ----- - if self.trailing_only_offset_is_reached: - # 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: + if current_trailing_only_offset_is_reached: + # Max profit pas atteint ET perte < 2 * current_trailing_stop_positive + if max_profit < min(2, current_trailing_stop_positive_offset * (count_of_buys - self.pairs[pair]['has_gain']))\ + and (baisse < 0.5 and max_profit > current_trailing_stop_positive_offset): #2 * current_trailing_stop_positive: return None # ne pas activer le trailing encore # Sinon : trailing actif dès le début @@ -757,7 +794,7 @@ class Frictrade(IStrategy): # get access to all pairs available in whitelist. pairs = self.dp.current_whitelist() informative_pairs = [(pair, '1h') for pair in pairs] - # informative_pairs += [(pair, '1d') for pair in pairs] + informative_pairs += [(pair, '1d') for pair in pairs] return informative_pairs @@ -825,3 +862,23 @@ class Frictrade(IStrategy): # # return df + + def rsi_trend_probability(self, dataframe): + dataframe = dataframe.copy() + + dataframe['rsi14'] = talib.RSI(dataframe['mid'], 14) + dataframe['rsi60'] = talib.RSI(dataframe['mid'], 60) + + dataframe['cross_soft'] = np.tanh((dataframe['rsi14'] - dataframe['rsi60']) / 7) + + dataframe['gap'] = (dataframe['rsi14'] - dataframe['rsi60']) / 100 + dataframe['trend'] = (dataframe['rsi60'] - 50) / 50 + + dataframe['rtp'] = ( + 0.6 * dataframe['cross_soft'] + + 0.25 * dataframe['gap'] + + 0.15 * dataframe['trend'] + ).clip(-1, 1) + + return dataframe +