diff --git a/FrictradeLearning.json b/FrictradeLearning.json new file mode 100644 index 0000000..0bb868c --- /dev/null +++ b/FrictradeLearning.json @@ -0,0 +1,27 @@ +{ + "strategy_name": "FrictradeLearning", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.15, + "trailing_stop_positive_offset": 1, + "trailing_only_offset_is_reached": true + }, + "max_open_trades": { + "max_open_trades": 80 + }, + "protection": { + "allow_decrease_rate": 0.8, + "first_adjust_param": 0.004, + "max_steps": 15 + } + }, + "ft_stratparam_v": 1, + "export_time": "2025-12-07 21:51:14.946942+00:00" +} \ No newline at end of file diff --git a/FrictradeLearning.py b/FrictradeLearning.py index 21777ad..951853c 100644 --- a/FrictradeLearning.py +++ b/FrictradeLearning.py @@ -120,7 +120,11 @@ class FrictradeLearning(IStrategy): -18: 0.30, } - allow_decrease_rate = 0.4 + allow_decrease_rate = DecimalParameter(0.1, 0.8, decimals=1, default=0.4, space='protection') + first_adjust_param = DecimalParameter(0.001, 0.01, decimals=3, default=0.003, space='protection') + max_steps = IntParameter(10, 50, default=40, space='protection', optimize=True, load=True) + + hours_force = IntParameter(1, 48, default=24, space='buy', optimize=True, load=True) # ROI table: minimal_roi = { @@ -139,8 +143,6 @@ class FrictradeLearning(IStrategy): # Buy hypers timeframe = '1m' - max_open_trades = 5 - max_amount = 40 parameters = {} # DCA config @@ -252,12 +254,18 @@ class FrictradeLearning(IStrategy): # d = n / (p * (p - 1)) # incrément # return [round(a + i * d, 3) for i in range(p)] def progressive_parts(total, n, first): - # solve for r - # S = first * (r^n - 1)/(r - 1) = total - # numeric solving + + # conditions impossibles → on évite le solveur + if total <= 0 or first <= 0 or n <= 1: + return [0] * n f = lambda r: first * (r ** n - 1) / (r - 1) - total - r = mp.findroot(f, 1.05) # initial guess + + try: + r = mp.findroot(f, 1.2) # 1.2 = plus stable que 1.05 + except Exception: + # fallback en cas d'échec + return [first] * n parts = [round(first * (r ** k), 4) for k in range(n)] return parts @@ -272,9 +280,9 @@ class FrictradeLearning(IStrategy): steps = self.approx_value(last_candle['mid'], self.pairs[pair]['last_ath']) self.pairs[pair]['dca_thresholds'] = progressive_parts( - (last_candle['mid'] - (self.pairs[pair]['last_ath'] * (1 - self.allow_decrease_rate))) / last_candle['mid'], - steps, 0.003) - print(f"val={last_candle['mid']} steps={steps} pct={(last_candle['mid'] - (self.pairs[pair]['last_ath'] * (1 - self.allow_decrease_rate))) / last_candle['mid']}") + (last_candle['mid'] - (self.pairs[pair]['last_ath'] * (1 - self.allow_decrease_rate.value))) / last_candle['mid'], + steps, self.first_adjust_param.value) + print(f"val={last_candle['mid']} steps={steps} pct={(last_candle['mid'] - (self.pairs[pair]['last_ath'] * (1 - self.allow_decrease_rate.value))) / last_candle['mid']}") print(self.pairs[pair]['dca_thresholds']) def confirm_trade_exit(self, pair: str, trade: Trade, order_type: str, amount: float, rate: float, @@ -468,9 +476,9 @@ class FrictradeLearning(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}|{round(last_candle['rsi_1h'], 1) or '-' :>6}|{round(last_candle['rsi_1d'], 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}|" - f"{round(last_candle['ml_prob'], 1) or '-' :>6}|" + f"{round(last_candle['ml_prob'], 1) or '-':>6}|" ) def printLineLog(self): @@ -850,8 +858,8 @@ class FrictradeLearning(IStrategy): # # for val in range(90000, 130000, 10000): # steps = self.approx_value(val, 126000) - # print(f"val={val} steps={steps} pct={(val - (126000 * (1 - self.allow_decrease_rate))) / val}") - # dca = split_ratio_one_third((val - (126000 * (1 - self.allow_decrease_rate))) / 126000, steps) + # print(f"val={val} steps={steps} pct={(val - (126000 * (1 - self.allow_decrease_rate.value))) / val}") + # dca = split_ratio_one_third((val - (126000 * (1 - self.allow_decrease_rate.value))) / 126000, steps) # print(dca) return dataframe @@ -1033,9 +1041,9 @@ class FrictradeLearning(IStrategy): # return adjusted_stake_amount def approx_value(self, x, X_max): - X_min = X_max * (1 - self.allow_decrease_rate) # 126198 * 0.4 = 75718,8 + X_min = X_max * (1 - self.allow_decrease_rate.value) # 126198 * 0.4 = 75718,8 Y_min = 1 - Y_max = 40 + Y_max = self.max_steps.value a = (Y_max - Y_min) / (X_max - X_min) # 39 ÷ (126198 − 126198×0,6) = 0,000772595 b = Y_min - a * X_min # 1 − (0,000772595 × 75718,8) = −38 y = a * x + b # 0,000772595 * 115000 - 38 @@ -1112,7 +1120,9 @@ class FrictradeLearning(IStrategy): if self.pairs[pair]['first_buy']: pct_first = self.getPctFirstBuy(pair, last_candle) - if profit > - self.pairs[pair]['first_amount'] and count_of_buys > 15 and last_candle['sma24_deriv1_1h'] < 0: + if profit > - self.pairs[pair]['first_amount'] \ + and self.wallets.get_available_stake_amount() < self.pairs[pair]['first_amount'] \ + and last_candle['sma24_deriv1_1h'] < 0: stake_amount = trade.stake_amount self.pairs[pair]['previous_profit'] = profit trade_type = "Sell " + (last_candle['enter_tag'] if last_candle['enter_long'] == 1 else '') @@ -1155,21 +1165,21 @@ class FrictradeLearning(IStrategy): # last_fill_price = buy_orders[-1].price # baisse relative - if minutes % 60 == 0: - ath = max(self.pairs[pair]['last_max'], self.get_last_ath_before_candle(last_candle)) - self.pairs[pair]['last_ath'] = ath - else: - ath = self.pairs[pair]['last_ath'] + # if minutes % 60 == 0: + # ath = max(self.pairs[pair]['last_max'], self.get_last_ath_before_candle(last_candle)) + # self.pairs[pair]['last_ath'] = ath + # else: + # ath = self.pairs[pair]['last_ath'] # steps = self.approx_value(last_candle['mid'], ath) - # dca_thresholds = split_ratio_one_third((last_candle['mid'] - (ath * self.allow_decrease_rate)) / last_candle['mid'], steps) #((last_candle['mid'] - (ath * self.allow_decrease_rate)) / steps) / last_candle['mid'] # 0.0025 + 0.0005 * count_of_buys + # dca_thresholds = split_ratio_one_third((last_candle['mid'] - (ath * self.allow_decrease_rate.value)) / last_candle['mid'], steps) #((last_candle['mid'] - (ath * self.allow_decrease_rate.value)) / steps) / last_candle['mid'] # 0.0025 + 0.0005 * count_of_buys if len(self.pairs[pair]['dca_thresholds']) == 0: self.calculateStepsDcaThresholds(last_candle, pair) dca_threshold = self.pairs[pair]['dca_thresholds'][min(count_of_buys - 1, len(self.pairs[pair]['dca_thresholds']) - 1)] - # print(f"{count_of_buys} {ath * (1 - self.allow_decrease_rate)} {round(last_candle['mid'], 2)} {round((last_candle['mid'] - (ath * self.allow_decrease_rate)) / last_candle['mid'], 2)} {steps} {round(dca_threshold, 4)}") + # print(f"{count_of_buys} {ath * (1 - self.allow_decrease_rate.value)} {round(last_candle['mid'], 2)} {round((last_candle['mid'] - (ath * self.allow_decrease_rate.value)) / last_candle['mid'], 2)} {steps} {round(dca_threshold, 4)}") decline = (last_fill_price - current_rate) / last_fill_price increase = - decline @@ -1209,13 +1219,10 @@ class FrictradeLearning(IStrategy): # return None # FIN ########################## ALGO ATH - force = hours > 24 and last_candle['sma60_deriv1_1h'] > 0 + force = hours > self.hours_force.value and last_candle['sma60_deriv1_1h'] > 0 condition = last_candle['percent'] > 0 and last_candle['sma24_deriv1'] > 0 \ and last_candle['close'] < self.pairs[pair]['first_buy'] - # and last_candle['ml_prob'] > 0.65 - limit_buy = 40 - # or (last_candle['close'] <= last_candle['min180'] and hours > 3) if ((force or decline >= dca_threshold) and condition): try: if self.pairs[pair]['has_gain'] and profit > 0: @@ -1224,6 +1231,9 @@ class FrictradeLearning(IStrategy): return None stake_amount = min(self.wallets.get_available_stake_amount(), self.adjust_stake_amount(pair, last_candle)) + + if force: + stake_amount = stake_amount / 4 # print(f"profit={profit} previous={self.pairs[pair]['previous_profit']} count_of_buys={trade.nr_of_successful_entries}") if stake_amount > 0: self.pairs[pair]['previous_profit'] = profit @@ -1402,7 +1412,7 @@ class FrictradeLearning(IStrategy): # ----- 4) OFFSET : faut-il attendre de dépasser trailing_stop_positive_offset ? ----- if current_trailing_only_offset_is_reached: # Max profit pas atteint ET perte < 2 * current_trailing_stop_positive - if max_profit < min(2, max_profit * current_trailing_stop_positive_offset * (count_of_buys - self.pairs[pair]['has_gain'])): #2 * current_trailing_stop_positive: + if profit > max_profit * current_trailing_stop_positive_offset * (count_of_buys - self.pairs[pair]['has_gain']): #2 * current_trailing_stop_positive: print(f"{current_time} trailing non atteint trailing_stop={round(trailing_stop,4)} profit={round(profit, 4)} max={round(max_profit, 4)} " f"{min(2, current_trailing_stop_positive_offset * (count_of_buys - self.pairs[pair]['has_gain']))}") return None # ne pas activer le trailing encore @@ -1514,8 +1524,6 @@ class FrictradeLearning(IStrategy): return dataframe - import pandas as pd - def to_utc_ts(self, x): return pd.to_datetime(x, utc=True)