diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 73d9bb382..5ecf5b2a3 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -1150,6 +1150,7 @@ class FreqtradeBot(LoggingMixin): trade.close_rate_requested = limit trade.sell_reason = sell_reason.sell_reason if(exit_tag is not None): + trade.sell_reason = exit_tag trade.exit_tag = exit_tag # In case of market sell orders the order can be closed immediately if order.get('status', 'unknown') in ('closed', 'expired'): @@ -1191,8 +1192,8 @@ class FreqtradeBot(LoggingMixin): 'current_rate': current_rate, 'profit_amount': profit_trade, 'profit_ratio': profit_ratio, + 'buy_tag': trade.buy_tag, 'sell_reason': trade.sell_reason, - 'exit_tag': trade.exit_tag, 'open_date': trade.open_date, 'close_date': trade.close_date or datetime.utcnow(), 'stake_currency': self.config['stake_currency'], @@ -1235,8 +1236,8 @@ class FreqtradeBot(LoggingMixin): 'current_rate': current_rate, 'profit_amount': profit_trade, 'profit_ratio': profit_ratio, + 'buy_tag': trade.buy_tag, 'sell_reason': trade.sell_reason, - 'exit_tag': trade.exit_tag, 'open_date': trade.open_date, 'close_date': trade.close_date or datetime.now(timezone.utc), 'stake_currency': self.config['stake_currency'], diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 6c2a20cb1..827be4d76 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -360,11 +360,10 @@ class Backtesting: if sell.sell_flag: trade.close_date = sell_candle_time - if(sell_row[EXIT_TAG_IDX] is not None): - trade.exit_tag = sell_row[EXIT_TAG_IDX] - else: - trade.exit_tag = None trade.sell_reason = sell.sell_reason + if(sell_row[EXIT_TAG_IDX] is not None): + trade.sell_reason = sell_row[EXIT_TAG_IDX] + trade.exit_tag = sell_row[EXIT_TAG_IDX] trade_dur = int((trade.close_date_utc - trade.open_date_utc).total_seconds() // 60) closerate = self._get_close_rate(sell_row, trade, sell, trade_dur) diff --git a/freqtrade/optimize/optimize_reports.py b/freqtrade/optimize/optimize_reports.py index 30005f524..67dacd7c6 100644 --- a/freqtrade/optimize/optimize_reports.py +++ b/freqtrade/optimize/optimize_reports.py @@ -387,8 +387,6 @@ def generate_strategy_stats(btdata: Dict[str, DataFrame], buy_tag_results = generate_tag_metrics("buy_tag", starting_balance=starting_balance, results=results, skip_nan=False) - exit_tag_results = generate_tag_metrics("exit_tag", starting_balance=starting_balance, - results=results, skip_nan=False) sell_reason_stats = generate_sell_reason_stats(max_open_trades=max_open_trades, results=results) @@ -414,7 +412,6 @@ def generate_strategy_stats(btdata: Dict[str, DataFrame], 'worst_pair': worst_pair, 'results_per_pair': pair_results, 'results_per_buy_tag': buy_tag_results, - 'results_per_exit_tag': exit_tag_results, 'sell_reason_summary': sell_reason_stats, 'left_open_trades': left_open_results, 'total_trades': len(results), @@ -744,15 +741,6 @@ def show_backtest_result(strategy: str, results: Dict[str, Any], stake_currency: print(' BUY TAG STATS '.center(len(table.splitlines()[0]), '=')) print(table) - table = text_table_tags( - "exit_tag", - results['results_per_exit_tag'], - stake_currency=stake_currency) - - if isinstance(table, str) and len(table) > 0: - print(' SELL TAG STATS '.center(len(table.splitlines()[0]), '=')) - print(table) - table = text_table_sell_reason(sell_reason_stats=results['sell_reason_summary'], stake_currency=stake_currency) if isinstance(table, str) and len(table) > 0: diff --git a/freqtrade/persistence/models.py b/freqtrade/persistence/models.py index 945201982..e03830d7f 100644 --- a/freqtrade/persistence/models.py +++ b/freqtrade/persistence/models.py @@ -325,7 +325,6 @@ class LocalTrade(): 'profit_pct': round(self.close_profit * 100, 2) if self.close_profit else None, 'profit_abs': self.close_profit_abs, - # +str(self.sell_reason) ## CHANGE TO BUY TAG IF NEEDED 'sell_reason': (f' ({self.sell_reason})' if self.sell_reason else ''), 'exit_tag': (f' ({self.exit_tag})' if self.exit_tag else ''), 'sell_order_status': self.sell_order_status, @@ -904,15 +903,15 @@ class Trade(_DECL_BASE, LocalTrade): ] @staticmethod - def get_exit_tag_performance(pair: str) -> List[Dict[str, Any]]: + def get_sell_reason_performance(pair: str) -> List[Dict[str, Any]]: """ - Returns List of dicts containing all Trades, based on exit tag performance + Returns List of dicts containing all Trades, based on sell reason performance Can either be average for all pairs or a specific pair provided NOTE: Not supported in Backtesting. """ if(pair is not None): tag_perf = Trade.query.with_entities( - Trade.exit_tag, + Trade.sell_reason, func.sum(Trade.close_profit).label('profit_sum'), func.sum(Trade.close_profit_abs).label('profit_sum_abs'), func.count(Trade.pair).label('count') @@ -922,29 +921,29 @@ class Trade(_DECL_BASE, LocalTrade): .all() else: tag_perf = Trade.query.with_entities( - Trade.exit_tag, + Trade.sell_reason, func.sum(Trade.close_profit).label('profit_sum'), func.sum(Trade.close_profit_abs).label('profit_sum_abs'), func.count(Trade.pair).label('count') ).filter(Trade.is_open.is_(False))\ - .group_by(Trade.exit_tag) \ + .group_by(Trade.sell_reason) \ .order_by(desc('profit_sum_abs')) \ .all() return [ { - 'exit_tag': exit_tag if exit_tag is not None else "Other", + 'sell_reason': sell_reason if sell_reason is not None else "Other", 'profit': profit, 'profit_abs': profit_abs, 'count': count } - for exit_tag, profit, profit_abs, count in tag_perf + for sell_reason, profit, profit_abs, count in tag_perf ] @staticmethod def get_mix_tag_performance(pair: str) -> List[Dict[str, Any]]: """ - Returns List of dicts containing all Trades, based on buy_tag + exit_tag performance + Returns List of dicts containing all Trades, based on buy_tag + sell_reason performance Can either be average for all pairs or a specific pair provided NOTE: Not supported in Backtesting. """ @@ -952,7 +951,7 @@ class Trade(_DECL_BASE, LocalTrade): tag_perf = Trade.query.with_entities( Trade.id, Trade.buy_tag, - Trade.exit_tag, + Trade.sell_reason, func.sum(Trade.close_profit).label('profit_sum'), func.sum(Trade.close_profit_abs).label('profit_sum_abs'), func.count(Trade.pair).label('count') @@ -965,7 +964,7 @@ class Trade(_DECL_BASE, LocalTrade): tag_perf = Trade.query.with_entities( Trade.id, Trade.buy_tag, - Trade.exit_tag, + Trade.sell_reason, func.sum(Trade.close_profit).label('profit_sum'), func.sum(Trade.close_profit_abs).label('profit_sum_abs'), func.count(Trade.pair).label('count') @@ -975,12 +974,12 @@ class Trade(_DECL_BASE, LocalTrade): .all() return_list = [] - for id, buy_tag, exit_tag, profit, profit_abs, count in tag_perf: + for id, buy_tag, sell_reason, profit, profit_abs, count in tag_perf: buy_tag = buy_tag if buy_tag is not None else "Other" - exit_tag = exit_tag if exit_tag is not None else "Other" + sell_reason = sell_reason if sell_reason is not None else "Other" - if(exit_tag is not None and buy_tag is not None): - mix_tag = buy_tag + " " + exit_tag + if(sell_reason is not None and buy_tag is not None): + mix_tag = buy_tag + " " + sell_reason i = 0 if not any(item["mix_tag"] == mix_tag for item in return_list): return_list.append({'mix_tag': mix_tag, @@ -990,8 +989,6 @@ class Trade(_DECL_BASE, LocalTrade): else: while i < len(return_list): if return_list[i]["mix_tag"] == mix_tag: - print("item below") - print(return_list[i]) return_list[i] = { 'mix_tag': mix_tag, 'profit': profit + return_list[i]["profit"], diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 508ce6894..2a664e7bc 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -161,6 +161,8 @@ class RPC: current_rate = NAN else: current_rate = trade.close_rate + + buy_tag = trade.buy_tag current_profit = trade.calc_profit_ratio(current_rate) current_profit_abs = trade.calc_profit(current_rate) current_profit_fiat: Optional[float] = None @@ -191,6 +193,7 @@ class RPC: profit_pct=round(current_profit * 100, 2), profit_abs=current_profit_abs, profit_fiat=current_profit_fiat, + buy_tag=buy_tag, stoploss_current_dist=stoploss_current_dist, stoploss_current_dist_ratio=round(stoploss_current_dist_ratio, 8), @@ -696,19 +699,19 @@ class RPC: [x.update({'profit': round(x['profit'] * 100, 2)}) for x in buy_tags] return buy_tags - def _rpc_exit_tag_performance(self, pair: str) -> List[Dict[str, Any]]: + def _rpc_sell_reason_performance(self, pair: str) -> List[Dict[str, Any]]: """ - Handler for sell tag performance. + Handler for sell reason performance. Shows a performance statistic from finished trades """ - exit_tags = Trade.get_exit_tag_performance(pair) + sell_reasons = Trade.get_sell_reason_performance(pair) # Round and convert to % - [x.update({'profit': round(x['profit'] * 100, 2)}) for x in exit_tags] - return exit_tags + [x.update({'profit': round(x['profit'] * 100, 2)}) for x in sell_reasons] + return sell_reasons def _rpc_mix_tag_performance(self, pair: str) -> List[Dict[str, Any]]: """ - Handler for mix tag performance. + Handler for mix tag (buy_tag + exit_tag) performance. Shows a performance statistic from finished trades """ mix_tags = Trade.get_mix_tag_performance(pair) diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 0a84b588a..2352d366a 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -156,7 +156,7 @@ class Telegram(RPCHandler): CommandHandler('delete', self._delete_trade), CommandHandler('performance', self._performance), CommandHandler('buys', self._buy_tag_performance), - CommandHandler('sells', self._exit_tag_performance), + CommandHandler('sells', self._sell_reason_performance), CommandHandler('mix_tags', self._mix_tag_performance), CommandHandler('stats', self._stats), CommandHandler('daily', self._daily), @@ -244,8 +244,8 @@ class Telegram(RPCHandler): msg['duration'] = msg['close_date'].replace( microsecond=0) - msg['open_date'].replace(microsecond=0) msg['duration_min'] = msg['duration'].total_seconds() / 60 - msg['tags'] = self._get_tags_string(msg) + msg['buy_tag'] = msg['buy_tag'] if "buy_tag" in msg.keys() else None msg['emoji'] = self._get_sell_emoji(msg) # Check if all sell properties are available. @@ -261,7 +261,7 @@ class Telegram(RPCHandler): message = ("{emoji} *{exchange}:* Selling {pair} (#{trade_id})\n" "*Profit:* `{profit_percent:.2f}%{profit_extra}`\n" - "{tags}" + "*Buy Tag:* `{buy_tag}`\n" "*Sell Reason:* `{sell_reason}`\n" "*Duration:* `{duration} ({duration_min:.1f} min)`\n" "*Amount:* `{amount:.8f}`\n" @@ -357,18 +357,6 @@ class Telegram(RPCHandler): else: return "\N{CROSS MARK}" - def _get_tags_string(self, msg): - """ - Get string lines for buy/sell tags to display when a sell is made - """ - tag_lines = "" - - if ("buy_tag" in msg.keys() and msg['buy_tag'] is not None): - tag_lines += ("*Buy Tag:* `{buy_tag}`\n").format(msg['buy_tag']) - if ("exit_tag" in msg.keys() and msg['exit_tag'] is not None): - tag_lines += ("*Sell Tag:* `{exit_tag}`\n").format(msg['exit_tag']) - return tag_lines - @authorized_only def _status(self, update: Update, context: CallbackContext) -> None: """ @@ -401,7 +389,6 @@ class Telegram(RPCHandler): "*Current Pair:* {pair}", "*Amount:* `{amount} ({stake_amount} {base_currency})`", "*Buy Tag:* `{buy_tag}`" if r['buy_tag'] else "", - "*Sell Tag:* `{exit_tag}`" if r['exit_tag'] else "", "*Open Rate:* `{open_rate:.8f}`", "*Close Rate:* `{close_rate}`" if r['close_rate'] else "", "*Current Rate:* `{current_rate:.8f}`", @@ -925,7 +912,7 @@ class Telegram(RPCHandler): self._send_msg(str(e)) @authorized_only - def _exit_tag_performance(self, update: Update, context: CallbackContext) -> None: + def _sell_reason_performance(self, update: Update, context: CallbackContext) -> None: """ Handler for /sells. Shows a performance statistic from finished trades @@ -938,11 +925,11 @@ class Telegram(RPCHandler): if context.args: pair = context.args[0] - trades = self._rpc._rpc_exit_tag_performance(pair) - output = "Sell Tag Performance:\n" + trades = self._rpc._rpc_sell_reason_performance(pair) + output = "Sell Reason Performance:\n" for i, trade in enumerate(trades): stat_line = ( - f"{i+1}.\t {trade['exit_tag']}\t" + f"{i+1}.\t {trade['sell_reason']}\t" f"{round_coin_value(trade['profit_abs'], self._config['stake_currency'])} " f"({trade['profit']:.2f}%) " f"({trade['count']})\n") @@ -954,7 +941,7 @@ class Telegram(RPCHandler): output += stat_line self._send_msg(output, parse_mode=ParseMode.HTML, - reload_able=True, callback_path="update_exit_tag_performance", + reload_able=True, callback_path="update_sell_reason_performance", query=update.callback_query) except RPCException as e: self._send_msg(str(e))