diff --git a/FrictradeLearning.json b/FrictradeLearning.json index 40a02be..1562818 100644 --- a/FrictradeLearning.json +++ b/FrictradeLearning.json @@ -9,7 +9,7 @@ }, "trailing": { "trailing_stop": false, - "trailing_stop_positive": 0.2, + "trailing_stop_positive": 0.15, "trailing_stop_positive_offset": 1, "trailing_only_offset_is_reached": true }, @@ -26,10 +26,10 @@ "indic_1h_force_buy": "sma5_deriv1_1h" }, "sell": { - "offset_max": 18, - "offset_min": 17 + "offset_max": 6, + "offset_min": 27 } }, "ft_stratparam_v": 1, - "export_time": "2025-12-14 18:44:03.713386+00:00" + "export_time": "2025-12-15 19:03:21.177158+00:00" } \ No newline at end of file diff --git a/FrictradeLearning.py b/FrictradeLearning.py index 225e3db..f71a1f8 100644 --- a/FrictradeLearning.py +++ b/FrictradeLearning.py @@ -126,8 +126,8 @@ class FrictradeLearning(IStrategy): columns_logged = False pairs = { pair: { - "first_buy": 0, - "last_buy": 0.0, + "first_price": 0, + "last_price": 0.0, "last_min": 999999999999999.5, "last_max": 0, "trade_info": {}, @@ -145,7 +145,6 @@ class FrictradeLearning(IStrategy): 'stop': False, 'max_profit': 0, 'first_amount': 0, - 'first_price': 0, 'total_amount': 0, 'has_gain': 0, 'force_sell': False, @@ -178,19 +177,19 @@ class FrictradeLearning(IStrategy): "note": "pic oct. 2025 (source agrégée, à vérifier selon l'exchange)"} ] - def dynamic_trailing_offset(self, price, ath, nb_entries, max_dca=5): - dd_ath = (ath - price) / ath - dd_ath = max(0.0, min(dd_ath, 0.5)) + def dynamic_trailing_offset(self, pair, price, ath, count_of_buys, max_dca=5): + # dd_ath = (ath - price) / ath + # dd_ath = max(0.0, min(dd_ath, 0.5)) + # + # dca_risk = min(count_of_buys / max_dca, 1.0) + # + # breathing_score = 0.7 * dd_ath + 0.3 * (1 - dca_risk) + # breathing_score = min(max(breathing_score, 0.0), 1.0) + # + # OFFSET_MIN = self.offset_min.value + # OFFSET_MAX = self.offset_min.value + self.offset_max.value - dca_risk = min(nb_entries / max_dca, 1.0) - - breathing_score = 0.7 * dd_ath + 0.3 * (1 - dca_risk) - breathing_score = min(max(breathing_score, 0.0), 1.0) - - OFFSET_MIN = self.offset_min.value - OFFSET_MAX = self.offset_min.value + self.offset_max.value - - return OFFSET_MIN + breathing_score * (OFFSET_MAX - OFFSET_MIN) + return self.pairs[pair]['total_amount'] / 100 # OFFSET_MIN + breathing_score * (OFFSET_MAX - OFFSET_MIN) def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, time_in_force: str, current_time: datetime, entry_tag: Optional[str], **kwargs) -> bool: @@ -210,8 +209,8 @@ class FrictradeLearning(IStrategy): if allow_to_buy: self.trades = list() - self.pairs[pair]['first_buy'] = rate - self.pairs[pair]['last_buy'] = rate + self.pairs[pair]['first_price'] = rate + self.pairs[pair]['last_price'] = rate self.pairs[pair]['max_touch'] = last_candle['close'] self.pairs[pair]['last_candle'] = last_candle self.pairs[pair]['count_of_buys'] = 1 @@ -329,7 +328,7 @@ class FrictradeLearning(IStrategy): self.pairs[pair]['total_amount'] = 0 self.pairs[pair]['count_of_buys'] = 0 self.pairs[pair]['max_touch'] = 0 - self.pairs[pair]['last_buy'] = 0 + self.pairs[pair]['last_price'] = 0 self.pairs[pair]['last_date'] = current_time self.pairs[pair]['current_trade'] = None else: @@ -367,8 +366,8 @@ class FrictradeLearning(IStrategy): # self.pairs[pair]['current_profit'] = profit # # dispo = round(self.wallets.get_available_stake_amount()) - # hours_since_first_buy = (current_time - trade.open_date_utc).seconds / 3600.0 - # days_since_first_buy = (current_time - trade.open_date_utc).days + # hours_since_first_price = (current_time - trade.open_date_utc).seconds / 3600.0 + # days_since_first_price = (current_time - trade.open_date_utc).days # hours = (current_time - trade.date_last_filled_utc).total_seconds() / 3600.0 # minutes = (current_time - trade.date_last_filled_utc).total_seconds() / 60.0 # @@ -406,10 +405,10 @@ class FrictradeLearning(IStrategy): return last_lost def getPctFirstBuy(self, pair, last_candle): - return round((last_candle['close'] - self.pairs[pair]['first_buy']) / self.pairs[pair]['first_buy'], 3) + return round((last_candle['close'] - self.pairs[pair]['first_price']) / self.pairs[pair]['first_price'], 3) def getPctLastBuy(self, pair, last_candle): - return round((last_candle['close'] - self.pairs[pair]['last_buy']) / self.pairs[pair]['last_buy'], 4) + return round((last_candle['close'] - self.pairs[pair]['last_price']) / self.pairs[pair]['last_price'], 4) def expectedProfit(self, pair: str, last_candle: DataFrame): lim = 0.01 @@ -442,7 +441,7 @@ class FrictradeLearning(IStrategy): colonnes_a_exclure = ['last_candle', 'trade_info', 'last_date', 'last_count_of_buys', 'base_stake_amount', 'stop_buy'] df_filtered = df[df['count_of_buys'] > 0].drop(columns=colonnes_a_exclure) - # df_filtered = df_filtered["first_buy", "last_max", "max_touch", "last_sell","last_buy", 'count_of_buys', 'current_profit'] + # df_filtered = df_filtered["first_price", "last_max", "max_touch", "last_sell","last_price", 'count_of_buys', 'current_profit'] self.printLog(df_filtered) @@ -645,14 +644,14 @@ class FrictradeLearning(IStrategy): min_price = min(min_price, buy.price) max_price = max(max_price, buy.price) dataframe['first_price'] = buy.price - self.pairs[pair]['first_buy'] = buy.price + self.pairs[pair]['first_price'] = buy.price self.pairs[pair]['first_amount'] = buy.price * buy.filled # dataframe['close01'] = buy.price * 1.01 # Order(id=2396, trade=1019, order_id=29870026652, side=buy, filled=0.00078, price=63921.01, # status=closed, date=2024-08-26 02:20:11) dataframe['last_price'] = buy.price - self.pairs[pair]['last_buy'] = buy.price + self.pairs[pair]['last_price'] = buy.price count = count + 1 amount += buy.price * buy.filled self.pairs[pair]['count_of_buys'] = count @@ -911,11 +910,17 @@ class FrictradeLearning(IStrategy): # f"steps={steps} " # f"pct={(round(val - (ath * (1 - self.allow_decrease_rate.value))) / val, 4)}") # print(dca_thresholds) + if self.pairs[pair]['last_ath'] == 0: + ath = max(val, self.get_last_ath_before_candle(last_candle['date'])) + self.pairs[pair]['last_ath'] = ath + if len(self.pairs[pair]['dca_thresholds']) == 0: + self.calculateStepsDcaThresholds(last_candle, pair) + print(f"val={val} dca={self.pairs[pair]['dca_thresholds']} ath={self.pairs[pair]['last_ath']} first_price={self.pairs[pair]['first_price']}") - if self.dp: + if self.dp and self.pairs[pair]['first_price'] > 0: if self.dp.runmode.value in ('live', 'dry_run'): - full, mises, steps = self.calculateMises(last_candle, pair) + full, mises, steps = self.calculateMises(pair, self.pairs[pair]['last_ath'], val) # stake = min(self.wallets.get_available_stake_amount(), self.adjust_stake_amount(pair, last_candle)) if val and len(self.pairs[pair]['dca_thresholds']) > 0: print(self.pairs[pair]['dca_thresholds']) @@ -925,14 +930,14 @@ class FrictradeLearning(IStrategy): for dca in self.pairs[pair]['dca_thresholds']: stake = mises[count] pct += dca - offset = self.dynamic_trailing_offset(price=val, ath=ath, nb_entries=count) + offset = self.dynamic_trailing_offset(pair, price=val, ath=ath, count_of_buys=count) - if (count == self.pairs[pair]['count_of_buys']): - print(f"next_buy={val * (1 - pct)} count={count} pct={round(pct, 4)}") + if count == self.pairs[pair]['count_of_buys']: + print(f"next_buy={round(val * (1 - pct),1)} count={count} pct={round(pct, 4)}") dataframe[f"next_buy"] = val * (1 - pct) count += 1 print( - f"stake={stake} count={count} pct={round(pct, 4)} offset={offset} next_buy={val * (1 - pct)}") + f"stake={round(stake, 1)} count={count} pct={round(pct, 4)} offset={round(offset, 1)} next_buy={round(val * (1 - pct), 2)}") return dataframe @@ -1131,17 +1136,18 @@ class FrictradeLearning(IStrategy): count = self.pairs[pair]['count_of_buys'] - self.pairs[pair]['has_gain'] return mises[count] if count < len(mises) else self.pairs[pair]['first_amount'] - full, mises, steps = self.calculateMises(last_candle, pair) + ath = max(self.pairs[pair]['last_max'], self.get_last_ath_before_candle(last_candle['date'])) + full, mises, steps = self.calculateMises(pair, ath, last_candle['mid']) base_stake = mises[self.pairs[pair]['count_of_buys']] if self.pairs[pair]['count_of_buys'] < len( mises) else full / (steps * 2) return base_stake - def calculateMises(self, last_candle, pair): - ath = max(self.pairs[pair]['last_max'], self.get_last_ath_before_candle(last_candle['date'])) + def calculateMises(self, pair, ath, val): + # ath = max(self.pairs[pair]['last_max'], self.get_last_ath_before_candle(last_candle['date'])) self.pairs[pair]['last_ath'] = ath full = self.wallets.get_total_stake_amount() - steps = self.calculateNumberOfSteps(last_candle['mid'], ath, max_steps=self.max_steps.value) + steps = self.calculateNumberOfSteps(val, ath, max_steps=self.max_steps.value) mises = self.progressive_parts(full, steps, full / (steps * 2)) print(f"ath={ath} full={full} steps={steps} mises={mises} ") self.pairs[pair]['mises'] = mises @@ -1167,8 +1173,8 @@ class FrictradeLearning(IStrategy): current_time = current_time.astimezone(timezone.utc) # open_date = trade.open_date.astimezone(timezone.utc) dispo = round(self.wallets.get_available_stake_amount()) - # hours_since_first_buy = (current_time - trade.open_date_utc).seconds / 3600.0 - # days_since_first_buy = (current_time - trade.open_date_utc).days + # hours_since_first_price = (current_time - trade.open_date_utc).seconds / 3600.0 + # days_since_first_price = (current_time - trade.open_date_utc).days hours = (current_time - trade.date_last_filled_utc).total_seconds() / 3600.0 # minutes = (current_time - trade.date_last_filled_utc).total_seconds() / 60.0 @@ -1184,7 +1190,7 @@ class FrictradeLearning(IStrategy): # total_counts = sum( # pair_data['count_of_buys'] for pair_data in self.pairs.values() if not self.getShortName(pair) == 'BTC') # - # if self.pairs[pair]['first_buy']: + # if self.pairs[pair]['first_price']: # pct_first = self.getPctFirstBuy(pair, last_candle) # if profit > - self.pairs[pair]['first_amount'] \ @@ -1208,7 +1214,7 @@ class FrictradeLearning(IStrategy): # stake=round(stake_amount, 2) # ) # - # self.pairs[trade.pair]['last_buy'] = current_rate + # self.pairs[trade.pair]['last_price'] = current_rate # self.pairs[trade.pair]['max_touch'] = last_candle['close'] # self.pairs[trade.pair]['last_candle'] = last_candle # @@ -1223,7 +1229,7 @@ class FrictradeLearning(IStrategy): return None # Dernier prix d'achat réel (pas le prix moyen) - last_fill_price = self.pairs[trade.pair]['last_buy'] # trade.open_rate # remplacé juste après ↓ + last_fill_price = self.pairs[trade.pair]['last_price'] # trade.open_rate # remplacé juste après ↓ # if len(trade.orders) > 0: # # On cherche le dernier BUY exécuté @@ -1287,8 +1293,9 @@ class FrictradeLearning(IStrategy): # FIN ########################## ALGO ATH force = False #hours > self.hours_force.value and last_candle[self.indic_1h_force_buy.value] > 0 - condition = last_candle['percent'] > 0 and last_candle['sma24_deriv1'] > 0 \ - and last_candle['close'] < self.pairs[pair]['first_buy'] + condition = last_candle['percent'] > 0 \ + and ((count_of_buys <= 4 and last_candle['sma24_deriv1'] > 0) or (count_of_buys > 4 and last_candle['sma60_deriv1'] > 0))\ + and last_candle['close'] < self.pairs[pair]['first_price'] if ((force or decline >= dca_threshold) and condition): try: @@ -1321,7 +1328,7 @@ class FrictradeLearning(IStrategy): stake=round(stake_amount, 2) ) - self.pairs[trade.pair]['last_buy'] = current_rate + self.pairs[trade.pair]['last_price'] = current_rate self.pairs[trade.pair]['max_touch'] = last_candle['close'] self.pairs[trade.pair]['last_candle'] = last_candle @@ -1329,7 +1336,7 @@ class FrictradeLearning(IStrategy): # colonnes_a_exclure = ['last_candle', 'stop', # 'trade_info', 'last_date', 'expected_profit', 'last_count_of_buys', 'base_stake_amount', 'stop_buy'] # df_filtered = df[df['count_of_buys'] > 0].drop(columns=colonnes_a_exclure) - # # df_filtered = df_filtered["first_buy", "last_max", "max_touch", "last_sell","last_buy", 'count_of_buys', 'current_profit'] + # # df_filtered = df_filtered["first_price", "last_max", "max_touch", "last_sell","last_price", 'count_of_buys', 'current_profit'] # # self.printLog(df_filtered) @@ -1365,7 +1372,7 @@ class FrictradeLearning(IStrategy): buys=trade.nr_of_successful_entries + 1, stake=round(stake_amount, 2) ) - self.pairs[trade.pair]['last_buy'] = current_rate + self.pairs[trade.pair]['last_price'] = current_rate self.pairs[trade.pair]['max_touch'] = last_candle['close'] self.pairs[trade.pair]['last_candle'] = last_candle return stake_amount @@ -1417,8 +1424,8 @@ class FrictradeLearning(IStrategy): self.pairs[pair]['current_profit'] = profit dispo = round(self.wallets.get_available_stake_amount()) - # hours_since_first_buy = (current_time - trade.open_date_utc).seconds / 3600.0 - # days_since_first_buy = (current_time - trade.open_date_utc).days + # hours_since_first_price = (current_time - trade.open_date_utc).seconds / 3600.0 + # days_since_first_price = (current_time - trade.open_date_utc).days # hours = (current_time - trade.date_last_filled_utc).total_seconds() / 3600.0 minutes = (current_time - trade.date_last_filled_utc).total_seconds() / 60.0 @@ -1432,9 +1439,9 @@ class FrictradeLearning(IStrategy): current_trailing_only_offset_is_reached = self.trailing_only_offset_is_reached current_trailing_stop_positive_offset = self.trailing_stop_positive_offset - current_trailing_stop_positive_offset = self.dynamic_trailing_offset(price=current_rate, + current_trailing_stop_positive_offset = self.dynamic_trailing_offset(pair, price=current_rate, ath=self.pairs[pair]['last_ath'], - nb_entries=count_of_buys) + count_of_buys=count_of_buys) # max_ = last_candle['max180'] # min_ = last_candle['min180']