diff --git a/docs/deprecated.md b/docs/deprecated.md index d86a7ac7a..be1d51837 100644 --- a/docs/deprecated.md +++ b/docs/deprecated.md @@ -43,3 +43,24 @@ As this does however increase risk and provides no benefit, it's been removed fo Using separate hyperopt files was deprecated in 2021.4 and was removed in 2021.9. Please switch to the new [Parametrized Strategies](hyperopt.md) to benefit from the new hyperopt interface. + +## Margin / short changes + +// TODO-lev: update version here + +## Strategy changes + +As strategies now have to support multiple different signal types, some things had to change. + +Columns: + +* `buy` -> `enter_long` +* `sell` -> `exit_long` +* `buy_tag` -> `enter_tag` + +New columns are `enter_short` and `exit_short`, which will initiate short trades (requires additional configuration!) + +### webhooks - `buy_tag` has been renamed to `enter_tag` + +This should apply only to your strategy and potentially to webhooks. +We will keep a compatibility layer for 1-2 versions (so both `buy_tag` and `enter_tag` will still work), but support for this in webhooks will disappear after that. diff --git a/docs/strategy-advanced.md b/docs/strategy-advanced.md index 573d184ff..560b4dcb6 100644 --- a/docs/strategy-advanced.md +++ b/docs/strategy-advanced.md @@ -77,7 +77,7 @@ class AwesomeStrategy(IStrategy): *** -## Buy Tag +## Enter Tag When your strategy has multiple buy signals, you can name the signal that triggered. Then you can access you buy signal on `custom_sell` @@ -89,7 +89,7 @@ def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: (dataframe['rsi'] < 35) & (dataframe['volume'] > 0) ), - ['buy', 'buy_tag']] = (1, 'buy_signal_rsi') + ['buy', 'enter_tag']] = (1, 'buy_signal_rsi') return dataframe @@ -97,14 +97,14 @@ def custom_sell(self, pair: str, trade: Trade, current_time: datetime, current_r current_profit: float, **kwargs): dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) last_candle = dataframe.iloc[-1].squeeze() - if trade.buy_tag == 'buy_signal_rsi' and last_candle['rsi'] > 80: + if trade.enter_tag == 'buy_signal_rsi' and last_candle['rsi'] > 80: return 'sell_signal_rsi' return None ``` !!! Note - `buy_tag` is limited to 100 characters, remaining data will be truncated. + `enter_tag` is limited to 100 characters, remaining data will be truncated. ## Exit tag diff --git a/docs/strategy-customization.md b/docs/strategy-customization.md index 178ed108b..e90d87c4a 100644 --- a/docs/strategy-customization.md +++ b/docs/strategy-customization.md @@ -498,7 +498,7 @@ for more information. & (dataframe['volume'] > 0) ), - ['buy', 'buy_tag']] = (1, 'buy_signal_rsi') + ['buy', 'enter_tag']] = (1, 'buy_signal_rsi') return dataframe ``` diff --git a/docs/webhook-config.md b/docs/webhook-config.md index ec944cb50..43aa0502c 100644 --- a/docs/webhook-config.md +++ b/docs/webhook-config.md @@ -83,7 +83,7 @@ Possible parameters are: * `fiat_currency` * `order_type` * `current_rate` -* `buy_tag` +* `enter_tag` ### Webhookbuycancel @@ -101,7 +101,7 @@ Possible parameters are: * `fiat_currency` * `order_type` * `current_rate` -* `buy_tag` +* `enter_tag` ### Webhookbuyfill @@ -117,7 +117,7 @@ Possible parameters are: * `stake_amount` * `stake_currency` * `fiat_currency` -* `buy_tag` +* `enter_tag` ### Webhooksell diff --git a/freqtrade/data/btanalysis.py b/freqtrade/data/btanalysis.py index 2631e4a46..136fc673a 100644 --- a/freqtrade/data/btanalysis.py +++ b/freqtrade/data/btanalysis.py @@ -30,7 +30,7 @@ BT_DATA_COLUMNS = ['pair', 'stake_amount', 'amount', 'open_date', 'close_date', 'fee_open', 'fee_close', 'trade_duration', 'profit_ratio', 'profit_abs', 'sell_reason', 'initial_stop_loss_abs', 'initial_stop_loss_ratio', 'stop_loss_abs', - 'stop_loss_ratio', 'min_rate', 'max_rate', 'is_open', 'buy_tag', + 'stop_loss_ratio', 'min_rate', 'max_rate', 'is_open', 'enter_tag', 'is_short' ] # TODO-lev: usage of the above might need compatibility code (buy_tag, is_short?, ...?) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 4a1f5085f..ba981f2f0 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -736,8 +736,7 @@ class FreqtradeBot(LoggingMixin): exchange=self.exchange.id, open_order_id=order_id, strategy=self.strategy.get_strategy_name(), - # TODO-lev: compatibility layer for buy_tag (!) - buy_tag=enter_tag, + enter_tag=enter_tag, timeframe=timeframe_to_minutes(self.config['timeframe']), leverage=leverage, is_short=is_short, @@ -769,7 +768,8 @@ class FreqtradeBot(LoggingMixin): msg = { 'trade_id': trade.id, 'type': RPCMessageType.SHORT if trade.is_short else RPCMessageType.BUY, - 'buy_tag': trade.buy_tag, + 'buy_tag': trade.enter_tag, + 'enter_tag': trade.enter_tag, 'exchange': self.exchange.name.capitalize(), 'pair': trade.pair, 'limit': trade.open_rate, @@ -794,7 +794,8 @@ class FreqtradeBot(LoggingMixin): msg = { 'trade_id': trade.id, 'type': msg_type, - 'buy_tag': trade.buy_tag, + 'buy_tag': trade.enter_tag, + 'enter_tag': trade.enter_tag, 'exchange': self.exchange.name.capitalize(), 'pair': trade.pair, 'limit': trade.open_rate, @@ -816,7 +817,8 @@ class FreqtradeBot(LoggingMixin): msg = { 'trade_id': trade.id, 'type': msg_type, - 'buy_tag': trade.buy_tag, + 'buy_tag': trade.enter_tag, + 'enter_tag': trade.enter_tag, 'exchange': self.exchange.name.capitalize(), 'pair': trade.pair, 'open_rate': trade.open_rate, @@ -1399,7 +1401,8 @@ class FreqtradeBot(LoggingMixin): 'current_rate': current_rate, 'profit_amount': profit_trade, 'profit_ratio': profit_ratio, - 'buy_tag': trade.buy_tag, + 'buy_tag': trade.enter_tag, + 'enter_tag': trade.enter_tag, 'sell_reason': trade.sell_reason, 'open_date': trade.open_date, 'close_date': trade.close_date or datetime.utcnow(), @@ -1443,7 +1446,8 @@ class FreqtradeBot(LoggingMixin): 'current_rate': current_rate, 'profit_amount': profit_trade, 'profit_ratio': profit_ratio, - 'buy_tag': trade.buy_tag, + 'buy_tag': trade.enter_tag, + 'enter_tag': trade.enter_tag, 'sell_reason': trade.sell_reason, 'open_date': trade.open_date, 'close_date': trade.close_date or datetime.now(timezone.utc), diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 65414022e..8ada54288 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -494,7 +494,7 @@ class Backtesting: fee_open=self.fee, fee_close=self.fee, is_open=True, - buy_tag=row[ENTER_TAG_IDX] if has_enter_tag else None, + enter_tag=row[ENTER_TAG_IDX] if has_enter_tag else None, exchange=self._exchange_name, is_short=(direction == 'short'), leverage=leverage, diff --git a/freqtrade/optimize/optimize_reports.py b/freqtrade/optimize/optimize_reports.py index 30feeb5ac..a8523aeb1 100644 --- a/freqtrade/optimize/optimize_reports.py +++ b/freqtrade/optimize/optimize_reports.py @@ -422,8 +422,8 @@ def generate_strategy_stats(btdata: Dict[str, DataFrame], starting_balance=start_balance, results=results, skip_nan=False) - buy_tag_results = generate_tag_metrics("buy_tag", starting_balance=start_balance, - results=results, skip_nan=False) + enter_tag_results = generate_tag_metrics("enter_tag", starting_balance=start_balance, + results=results, skip_nan=False) sell_reason_stats = generate_sell_reason_stats(max_open_trades=max_open_trades, results=results) @@ -448,7 +448,7 @@ def generate_strategy_stats(btdata: Dict[str, DataFrame], 'best_pair': best_pair, 'worst_pair': worst_pair, 'results_per_pair': pair_results, - 'results_per_buy_tag': buy_tag_results, + 'results_per_enter_tag': enter_tag_results, 'sell_reason_summary': sell_reason_stats, 'left_open_trades': left_open_results, # 'days_breakdown_stats': days_breakdown_stats, @@ -634,7 +634,7 @@ def text_table_tags(tag_type: str, tag_results: List[Dict[str, Any]], stake_curr :param stake_currency: stake-currency - used to correctly name headers :return: pretty printed table with tabulate as string """ - if(tag_type == "buy_tag"): + if(tag_type == "enter_tag"): headers = _get_line_header("TAG", stake_currency) else: headers = _get_line_header_sell("TAG", stake_currency) @@ -818,10 +818,12 @@ def show_backtest_result(strategy: str, results: Dict[str, Any], stake_currency: print(' BACKTESTING REPORT '.center(len(table.splitlines()[0]), '=')) print(table) - if results.get('results_per_buy_tag') is not None: + if (results.get('results_per_enter_tag') is not None + or results.get('results_per_buy_tag') is not None): + # results_per_buy_tag is deprecated and should be removed 2 versions after short golive. table = text_table_tags( - "buy_tag", - results['results_per_buy_tag'], + "enter_tag", + results.get('results_per_enter_tag', results.get('results_per_buy_tag')), stake_currency=stake_currency) if isinstance(table, str) and len(table) > 0: diff --git a/freqtrade/persistence/migrations.py b/freqtrade/persistence/migrations.py index 2b1d10bc1..99b8f0925 100644 --- a/freqtrade/persistence/migrations.py +++ b/freqtrade/persistence/migrations.py @@ -47,7 +47,7 @@ def migrate_trades_table(decl_base, inspector, engine, table_back_name: str, col min_rate = get_column_def(cols, 'min_rate', 'null') sell_reason = get_column_def(cols, 'sell_reason', 'null') strategy = get_column_def(cols, 'strategy', 'null') - buy_tag = get_column_def(cols, 'buy_tag', 'null') + enter_tag = get_column_def(cols, 'buy_tag', get_column_def(cols, 'enter_tag', 'null')) trading_mode = get_column_def(cols, 'trading_mode', 'null') @@ -98,7 +98,7 @@ def migrate_trades_table(decl_base, inspector, engine, table_back_name: str, col stake_amount, amount, amount_requested, open_date, close_date, open_order_id, stop_loss, stop_loss_pct, initial_stop_loss, initial_stop_loss_pct, stoploss_order_id, stoploss_last_update, - max_rate, min_rate, sell_reason, sell_order_status, strategy, buy_tag, + max_rate, min_rate, sell_reason, sell_order_status, strategy, enter_tag, timeframe, open_trade_value, close_profit_abs, trading_mode, leverage, isolated_liq, is_short, interest_rate, funding_fees @@ -116,7 +116,7 @@ def migrate_trades_table(decl_base, inspector, engine, table_back_name: str, col {stoploss_order_id} stoploss_order_id, {stoploss_last_update} stoploss_last_update, {max_rate} max_rate, {min_rate} min_rate, {sell_reason} sell_reason, {sell_order_status} sell_order_status, - {strategy} strategy, {buy_tag} buy_tag, {timeframe} timeframe, + {strategy} strategy, {enter_tag} enter_tag, {timeframe} timeframe, {open_trade_value} open_trade_value, {close_profit_abs} close_profit_abs, {trading_mode} trading_mode, {leverage} leverage, {isolated_liq} isolated_liq, {is_short} is_short, {interest_rate} interest_rate, @@ -180,7 +180,7 @@ def check_migrate(engine, decl_base, previous_tables) -> None: table_back_name = get_backup_name(tabs, 'trades_bak') # Check for latest column - if not has_column(cols, 'funding_fees'): + if not has_column(cols, 'enter_tag'): logger.info(f'Running database migration for trades - backup: {table_back_name}') migrate_trades_table(decl_base, inspector, engine, table_back_name, cols) # Reread columns - the above recreated the table! diff --git a/freqtrade/persistence/models.py b/freqtrade/persistence/models.py index f9df45111..3314f8204 100644 --- a/freqtrade/persistence/models.py +++ b/freqtrade/persistence/models.py @@ -264,7 +264,7 @@ class LocalTrade(): sell_reason: str = '' sell_order_status: str = '' strategy: str = '' - buy_tag: Optional[str] = None + enter_tag: Optional[str] = None timeframe: Optional[int] = None trading_mode: TradingMode = TradingMode.SPOT @@ -280,6 +280,14 @@ class LocalTrade(): # Futures properties funding_fees: Optional[float] = None + @property + def buy_tag(self) -> Optional[str]: + """ + Compatibility between buy_tag (old) and enter_tag (new) + Consider buy_tag deprecated + """ + return self.enter_tag + @property def has_no_leverage(self) -> bool: """Returns true if this is a non-leverage, non-short trade""" @@ -389,7 +397,8 @@ class LocalTrade(): 'amount_requested': round(self.amount_requested, 8) if self.amount_requested else None, 'stake_amount': round(self.stake_amount, 8), 'strategy': self.strategy, - 'buy_tag': self.buy_tag, + 'buy_tag': self.enter_tag, + 'enter_tag': self.enter_tag, 'timeframe': self.timeframe, 'fee_open': self.fee_open, @@ -928,7 +937,7 @@ class Trade(_DECL_BASE, LocalTrade): sell_reason = Column(String(100), nullable=True) sell_order_status = Column(String(100), nullable=True) strategy = Column(String(100), nullable=True) - buy_tag = Column(String(100), nullable=True) + enter_tag = Column(String(100), nullable=True) timeframe = Column(Integer, nullable=True) trading_mode = Column(Enum(TradingMode), nullable=True) @@ -1099,7 +1108,7 @@ class Trade(_DECL_BASE, LocalTrade): ] @staticmethod - def get_buy_tag_performance(pair: Optional[str]) -> List[Dict[str, Any]]: + def get_enter_tag_performance(pair: Optional[str]) -> List[Dict[str, Any]]: """ Returns List of dicts containing all Trades, based on buy tag performance Can either be average for all pairs or a specific pair provided @@ -1110,25 +1119,25 @@ class Trade(_DECL_BASE, LocalTrade): if(pair is not None): filters.append(Trade.pair == pair) - buy_tag_perf = Trade.query.with_entities( - Trade.buy_tag, + enter_tag_perf = Trade.query.with_entities( + Trade.enter_tag, 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(*filters)\ - .group_by(Trade.buy_tag) \ + .group_by(Trade.enter_tag) \ .order_by(desc('profit_sum_abs')) \ .all() return [ { - 'buy_tag': buy_tag if buy_tag is not None else "Other", + 'enter_tag': enter_tag if enter_tag is not None else "Other", 'profit_ratio': profit, 'profit_pct': round(profit * 100, 2), 'profit_abs': profit_abs, 'count': count } - for buy_tag, profit, profit_abs, count in buy_tag_perf + for enter_tag, profit, profit_abs, count in enter_tag_perf ] @staticmethod @@ -1178,7 +1187,7 @@ class Trade(_DECL_BASE, LocalTrade): mix_tag_perf = Trade.query.with_entities( Trade.id, - Trade.buy_tag, + Trade.enter_tag, Trade.sell_reason, func.sum(Trade.close_profit).label('profit_sum'), func.sum(Trade.close_profit_abs).label('profit_sum_abs'), @@ -1189,12 +1198,12 @@ class Trade(_DECL_BASE, LocalTrade): .all() return_list: List[Dict] = [] - for id, buy_tag, sell_reason, profit, profit_abs, count in mix_tag_perf: - buy_tag = buy_tag if buy_tag is not None else "Other" + for id, enter_tag, sell_reason, profit, profit_abs, count in mix_tag_perf: + enter_tag = enter_tag if enter_tag is not None else "Other" sell_reason = sell_reason if sell_reason is not None else "Other" - if(sell_reason is not None and buy_tag is not None): - mix_tag = buy_tag + " " + sell_reason + if(sell_reason is not None and enter_tag is not None): + mix_tag = enter_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, diff --git a/freqtrade/rpc/api_server/api_schemas.py b/freqtrade/rpc/api_server/api_schemas.py index 2b4164d6b..2df18a812 100644 --- a/freqtrade/rpc/api_server/api_schemas.py +++ b/freqtrade/rpc/api_server/api_schemas.py @@ -185,7 +185,8 @@ class TradeSchema(BaseModel): amount_requested: float stake_amount: float strategy: str - buy_tag: Optional[str] + buy_tag: Optional[str] # Deprecated + enter_tag: Optional[str] timeframe: int fee_open: Optional[float] fee_open_cost: Optional[float] diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 9a47cd112..7242bec2a 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -782,27 +782,23 @@ class RPC: return pair_rates - def _rpc_buy_tag_performance(self, pair: Optional[str]) -> List[Dict[str, Any]]: + def _rpc_enter_tag_performance(self, pair: Optional[str]) -> List[Dict[str, Any]]: """ Handler for buy tag performance. Shows a performance statistic from finished trades """ - buy_tags = Trade.get_buy_tag_performance(pair) - - return buy_tags + return Trade.get_enter_tag_performance(pair) def _rpc_sell_reason_performance(self, pair: Optional[str]) -> List[Dict[str, Any]]: """ Handler for sell reason performance. Shows a performance statistic from finished trades """ - sell_reasons = Trade.get_sell_reason_performance(pair) - - return sell_reasons + return Trade.get_sell_reason_performance(pair) def _rpc_mix_tag_performance(self, pair: Optional[str]) -> List[Dict[str, Any]]: """ - Handler for mix tag (buy_tag + sell_reason) performance. + Handler for mix tag (enter_tag + sell_reason) 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 6c6f745e7..8cfaccdf6 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -154,7 +154,7 @@ class Telegram(RPCHandler): CommandHandler('trades', self._trades), CommandHandler('delete', self._delete_trade), CommandHandler('performance', self._performance), - CommandHandler('buys', self._buy_tag_performance), + CommandHandler(['buys', 'entries'], self._enter_tag_performance), CommandHandler('sells', self._sell_reason_performance), CommandHandler('mix_tags', self._mix_tag_performance), CommandHandler('stats', self._stats), @@ -182,7 +182,8 @@ class Telegram(RPCHandler): CallbackQueryHandler(self._profit, pattern='update_profit'), CallbackQueryHandler(self._balance, pattern='update_balance'), CallbackQueryHandler(self._performance, pattern='update_performance'), - CallbackQueryHandler(self._buy_tag_performance, pattern='update_buy_tag_performance'), + CallbackQueryHandler(self._enter_tag_performance, + pattern='update_enter_tag_performance'), CallbackQueryHandler(self._sell_reason_performance, pattern='update_sell_reason_performance'), CallbackQueryHandler(self._mix_tag_performance, pattern='update_mix_tag_performance'), @@ -226,7 +227,7 @@ class Telegram(RPCHandler): f"{emoji} *{msg['exchange']}:* {'Bought' if is_fill else 'Buying'} {msg['pair']}" f" (#{msg['trade_id']})\n" ) - message += f"*Buy Tag:* `{msg['buy_tag']}`\n" if msg.get('buy_tag', None) else "" + message += f"*Enter Tag:* `{msg['enter_tag']}`\n" if msg.get('enter_tag', None) else "" message += f"*Amount:* `{msg['amount']:.8f}`\n" if msg['type'] == RPCMessageType.BUY_FILL: @@ -251,7 +252,7 @@ class Telegram(RPCHandler): microsecond=0) - msg['open_date'].replace(microsecond=0) msg['duration_min'] = msg['duration'].total_seconds() / 60 - msg['buy_tag'] = msg['buy_tag'] if "buy_tag" in msg.keys() else None + msg['enter_tag'] = msg['enter_tag'] if "enter_tag" in msg.keys() else None msg['emoji'] = self._get_sell_emoji(msg) # Check if all sell properties are available. @@ -271,7 +272,7 @@ class Telegram(RPCHandler): f"{'Sold' if is_fill else 'Selling'} {msg['pair']} (#{msg['trade_id']})\n" f"*{'Profit' if is_fill else 'Unrealized Profit'}:* " f"`{msg['profit_ratio']:.2%}{msg['profit_extra']}`\n" - f"*Buy Tag:* `{msg['buy_tag']}`\n" + f"*Enter Tag:* `{msg['enter_tag']}`\n" f"*Sell Reason:* `{msg['sell_reason']}`\n" f"*Duration:* `{msg['duration']} ({msg['duration_min']:.1f} min)`\n" f"*Amount:* `{msg['amount']:.8f}`\n" @@ -397,7 +398,7 @@ class Telegram(RPCHandler): "*Trade ID:* `{trade_id}` `(since {open_date_hum})`", "*Current Pair:* {pair}", "*Amount:* `{amount} ({stake_amount} {base_currency})`", - "*Buy Tag:* `{buy_tag}`" if r['buy_tag'] else "", + "*Enter Tag:* `{enter_tag}`" if r['enter_tag'] else "", "*Open Rate:* `{open_rate:.8f}`", "*Close Rate:* `{close_rate}`" if r['close_rate'] else "", "*Current Rate:* `{current_rate:.8f}`", @@ -972,7 +973,7 @@ class Telegram(RPCHandler): self._send_msg(str(e)) @authorized_only - def _buy_tag_performance(self, update: Update, context: CallbackContext) -> None: + def _enter_tag_performance(self, update: Update, context: CallbackContext) -> None: """ Handler for /buys PAIR . Shows a performance statistic from finished trades @@ -985,11 +986,11 @@ class Telegram(RPCHandler): if context.args and isinstance(context.args[0], str): pair = context.args[0] - trades = self._rpc._rpc_buy_tag_performance(pair) + trades = self._rpc._rpc_enter_tag_performance(pair) output = "Buy Tag Performance:\n" for i, trade in enumerate(trades): stat_line = ( - f"{i+1}.\t {trade['buy_tag']}\t" + f"{i+1}.\t {trade['enter_tag']}\t" f"{round_coin_value(trade['profit_abs'], self._config['stake_currency'])} " f"({trade['profit_ratio']:.2%}) " f"({trade['count']})\n") @@ -1001,7 +1002,7 @@ class Telegram(RPCHandler): output += stat_line self._send_msg(output, parse_mode=ParseMode.HTML, - reload_able=True, callback_path="update_buy_tag_performance", + reload_able=True, callback_path="update_enter_tag_performance", query=update.callback_query) except RPCException as e: self._send_msg(str(e)) @@ -1277,7 +1278,8 @@ class Telegram(RPCHandler): " *table :* `will display trades in a table`\n" " `pending buy orders are marked with an asterisk (*)`\n" " `pending sell orders are marked with a double asterisk (**)`\n" - "*/buys :* `Shows the buy_tag performance`\n" + # TODO-lev: Update commands and help (?) + "*/buys :* `Shows the enter_tag performance`\n" "*/sells :* `Shows the sell reason performance`\n" "*/mix_tags :* `Shows combined buy tag + sell reason performance`\n" "*/trades [limit]:* `Lists last closed trades (limited to 10 by default)`\n" diff --git a/tests/conftest.py b/tests/conftest.py index e184903d1..6a85f5de2 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -215,8 +215,6 @@ def patch_get_signal( ) -> None: """ :param mocker: mocker to patch IStrategy class - :param value: which value IStrategy.get_signal() must return - (buy, sell, buy_tag) :return: None """ # returns (Signal-direction, signaname) diff --git a/tests/conftest_trades.py b/tests/conftest_trades.py index 0ad01e72f..a245033b9 100644 --- a/tests/conftest_trades.py +++ b/tests/conftest_trades.py @@ -102,7 +102,7 @@ def mock_trade_2(fee, is_short: bool): open_order_id=f'dry_run_sell_{direc(is_short)}_12345', strategy='StrategyTestV3', timeframe=5, - buy_tag='TEST1', + enter_tag='TEST1', sell_reason='sell_signal', open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=20), close_date=datetime.now(tz=timezone.utc) - timedelta(minutes=2), @@ -258,7 +258,7 @@ def mock_trade_5(fee, is_short: bool): open_rate=0.123, exchange='binance', strategy='SampleStrategy', - buy_tag='TEST1', + enter_tag='TEST1', stoploss_order_id=f'prod_stoploss_{direc(is_short)}_3455', timeframe=5, is_short=is_short @@ -314,7 +314,7 @@ def mock_trade_6(fee, is_short: bool): open_rate=0.15, exchange='binance', strategy='SampleStrategy', - buy_tag='TEST2', + enter_tag='TEST2', open_order_id=f"prod_sell_{direc(is_short)}_6", timeframe=5, is_short=is_short diff --git a/tests/optimize/test_backtest_detail.py b/tests/optimize/test_backtest_detail.py index 599450b57..6db88d123 100644 --- a/tests/optimize/test_backtest_detail.py +++ b/tests/optimize/test_backtest_detail.py @@ -621,6 +621,6 @@ def test_backtest_results(default_conf, fee, mocker, caplog, data) -> None: for c, trade in enumerate(data.trades): res = results.iloc[c] assert res.sell_reason == trade.sell_reason.value - assert res.buy_tag == trade.enter_tag + assert res.enter_tag == trade.enter_tag assert res.open_date == _get_frame_time_from_offset(trade.open_tick) assert res.close_date == _get_frame_time_from_offset(trade.close_tick) diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py index 21d11d7f7..ba74ed674 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -698,7 +698,7 @@ def test_backtest_one(default_conf, fee, mocker, testdatadir) -> None: 'min_rate': [0.10370188, 0.10300000000000001], 'max_rate': [0.10501, 0.1038888], 'is_open': [False, False], - 'buy_tag': [None, None], + 'enter_tag': [None, None], "is_short": [False, False], }) pd.testing.assert_frame_equal(results, expected) diff --git a/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py index 9821c9468..5996fc1f7 100644 --- a/tests/rpc/test_rpc.py +++ b/tests/rpc/test_rpc.py @@ -70,6 +70,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None: 'max_rate': ANY, 'strategy': ANY, 'buy_tag': ANY, + 'enter_tag': ANY, 'timeframe': 5, 'open_order_id': ANY, 'close_date': None, @@ -143,6 +144,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None: 'max_rate': ANY, 'strategy': ANY, 'buy_tag': ANY, + 'enter_tag': ANY, 'timeframe': ANY, 'open_order_id': ANY, 'close_date': None, @@ -842,8 +844,8 @@ def test_performance_handle(default_conf, ticker, limit_buy_order, fee, assert prec_satoshi(res[0]['profit_pct'], 6.2) -def test_buy_tag_performance_handle(default_conf, ticker, limit_buy_order, fee, - limit_sell_order, mocker) -> None: +def test_enter_tag_performance_handle(default_conf, ticker, limit_buy_order, fee, + limit_sell_order, mocker) -> None: mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) mocker.patch.multiple( 'freqtrade.exchange.Exchange', @@ -869,23 +871,23 @@ def test_buy_tag_performance_handle(default_conf, ticker, limit_buy_order, fee, trade.close_date = datetime.utcnow() trade.is_open = False - res = rpc._rpc_buy_tag_performance(None) + res = rpc._rpc_enter_tag_performance(None) assert len(res) == 1 - assert res[0]['buy_tag'] == 'Other' + assert res[0]['enter_tag'] == 'Other' assert res[0]['count'] == 1 assert prec_satoshi(res[0]['profit_pct'], 6.2) - trade.buy_tag = "TEST_TAG" - res = rpc._rpc_buy_tag_performance(None) + trade.enter_tag = "TEST_TAG" + res = rpc._rpc_enter_tag_performance(None) assert len(res) == 1 - assert res[0]['buy_tag'] == 'TEST_TAG' + assert res[0]['enter_tag'] == 'TEST_TAG' assert res[0]['count'] == 1 assert prec_satoshi(res[0]['profit_pct'], 6.2) -def test_buy_tag_performance_handle_2(mocker, default_conf, markets, fee): +def test_enter_tag_performance_handle_2(mocker, default_conf, markets, fee): mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) mocker.patch.multiple( 'freqtrade.exchange.Exchange', @@ -896,21 +898,21 @@ def test_buy_tag_performance_handle_2(mocker, default_conf, markets, fee): create_mock_trades(fee) rpc = RPC(freqtradebot) - res = rpc._rpc_buy_tag_performance(None) + res = rpc._rpc_enter_tag_performance(None) assert len(res) == 2 - assert res[0]['buy_tag'] == 'TEST1' + assert res[0]['enter_tag'] == 'TEST1' assert res[0]['count'] == 1 assert prec_satoshi(res[0]['profit_pct'], 0.5) - assert res[1]['buy_tag'] == 'Other' + assert res[1]['enter_tag'] == 'Other' assert res[1]['count'] == 1 assert prec_satoshi(res[1]['profit_pct'], 1.0) # Test for a specific pair - res = rpc._rpc_buy_tag_performance('ETC/BTC') + res = rpc._rpc_enter_tag_performance('ETC/BTC') assert len(res) == 1 assert res[0]['count'] == 1 - assert res[0]['buy_tag'] == 'TEST1' + assert res[0]['enter_tag'] == 'TEST1' assert prec_satoshi(res[0]['profit_pct'], 0.5) @@ -1020,7 +1022,7 @@ def test_mix_tag_performance_handle(default_conf, ticker, limit_buy_order, fee, assert res[0]['count'] == 1 assert prec_satoshi(res[0]['profit_pct'], 6.2) - trade.buy_tag = "TESTBUY" + trade.enter_tag = "TESTBUY" trade.sell_reason = "TESTSELL" res = rpc._rpc_mix_tag_performance(None) diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index 177750ff6..e023f76a2 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -959,6 +959,7 @@ def test_api_status(botclient, mocker, ticker, fee, markets, is_short, 'sell_order_status': None, 'strategy': CURRENT_TEST_STRATEGY, 'buy_tag': None, + 'enter_tag': None, 'timeframe': 5, 'exchange': 'binance', } @@ -1117,6 +1118,7 @@ def test_api_forcebuy(botclient, mocker, fee): 'sell_order_status': None, 'strategy': CURRENT_TEST_STRATEGY, 'buy_tag': None, + 'enter_tag': None, 'timeframe': 5, 'exchange': 'binance', } diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py index 36ad304d1..77f6e3152 100644 --- a/tests/rpc/test_rpc_telegram.py +++ b/tests/rpc/test_rpc_telegram.py @@ -93,7 +93,7 @@ def test_telegram_init(default_conf, mocker, caplog) -> None: message_str = ("rpc.telegram is listening for following commands: [['status'], ['profit'], " "['balance'], ['start'], ['stop'], ['forcesell'], ['forcebuy'], ['trades'], " - "['delete'], ['performance'], ['buys'], ['sells'], ['mix_tags'], " + "['delete'], ['performance'], ['buys', 'entries'], ['sells'], ['mix_tags'], " "['stats'], ['daily'], ['weekly'], ['monthly'], " "['count'], ['locks'], ['unlock', 'delete_locks'], " "['reload_config', 'reload_conf'], ['show_config', 'show_conf'], " @@ -189,6 +189,7 @@ def test_telegram_status(default_conf, update, mocker) -> None: 'amount': 90.99181074, 'stake_amount': 90.99181074, 'buy_tag': None, + 'enter_tag': None, 'close_profit_ratio': None, 'profit': -0.0059, 'profit_ratio': -0.0059, @@ -954,6 +955,7 @@ def test_telegram_forcesell_handle(default_conf, update, ticker, fee, 'stake_currency': 'BTC', 'fiat_currency': 'USD', 'buy_tag': ANY, + 'enter_tag': ANY, 'sell_reason': SellType.FORCE_SELL.value, 'open_date': ANY, 'close_date': ANY, @@ -1018,6 +1020,7 @@ def test_telegram_forcesell_down_handle(default_conf, update, ticker, fee, 'stake_currency': 'BTC', 'fiat_currency': 'USD', 'buy_tag': ANY, + 'enter_tag': ANY, 'sell_reason': SellType.FORCE_SELL.value, 'open_date': ANY, 'close_date': ANY, @@ -1072,6 +1075,7 @@ def test_forcesell_all_handle(default_conf, update, ticker, fee, mocker) -> None 'stake_currency': 'BTC', 'fiat_currency': 'USD', 'buy_tag': ANY, + 'enter_tag': ANY, 'sell_reason': SellType.FORCE_SELL.value, 'open_date': ANY, 'close_date': ANY, @@ -1235,14 +1239,14 @@ def test_buy_tag_performance_handle(default_conf, update, ticker, fee, # Simulate fulfilled LIMIT_BUY order for trade trade.update(limit_buy_order) - trade.buy_tag = "TESTBUY" + trade.enter_tag = "TESTBUY" # Simulate fulfilled LIMIT_SELL order for trade trade.update(limit_sell_order) trade.close_date = datetime.utcnow() trade.is_open = False - telegram._buy_tag_performance(update=update, context=MagicMock()) + telegram._enter_tag_performance(update=update, context=MagicMock()) assert msg_mock.call_count == 1 assert 'Buy Tag Performance' in msg_mock.call_args_list[0][0][0] assert 'TESTBUY\t0.00006217 BTC (6.20%) (1)' in msg_mock.call_args_list[0][0][0] @@ -1297,7 +1301,7 @@ def test_mix_tag_performance_handle(default_conf, update, ticker, fee, # Simulate fulfilled LIMIT_BUY order for trade trade.update(limit_buy_order) - trade.buy_tag = "TESTBUY" + trade.enter_tag = "TESTBUY" trade.sell_reason = "TESTSELL" # Simulate fulfilled LIMIT_SELL order for trade @@ -1598,7 +1602,7 @@ def test_send_msg_buy_notification(default_conf, mocker, caplog) -> None: msg = { 'type': RPCMessageType.BUY, 'trade_id': 1, - 'buy_tag': 'buy_signal_01', + 'enter_tag': 'buy_signal_01', 'exchange': 'Binance', 'pair': 'ETH/BTC', 'limit': 1.099e-05, @@ -1616,7 +1620,7 @@ def test_send_msg_buy_notification(default_conf, mocker, caplog) -> None: telegram.send_msg(msg) assert msg_mock.call_args[0][0] \ == '\N{LARGE BLUE CIRCLE} *Binance:* Buying ETH/BTC (#1)\n' \ - '*Buy Tag:* `buy_signal_01`\n' \ + '*Enter Tag:* `buy_signal_01`\n' \ '*Amount:* `1333.33333333`\n' \ '*Open Rate:* `0.00001099`\n' \ '*Current Rate:* `0.00001099`\n' \ @@ -1644,7 +1648,7 @@ def test_send_msg_buy_cancel_notification(default_conf, mocker) -> None: telegram.send_msg({ 'type': RPCMessageType.BUY_CANCEL, - 'buy_tag': 'buy_signal_01', + 'enter_tag': 'buy_signal_01', 'trade_id': 1, 'exchange': 'Binance', 'pair': 'ETH/BTC', @@ -1691,7 +1695,7 @@ def test_send_msg_buy_fill_notification(default_conf, mocker) -> None: telegram.send_msg({ 'type': RPCMessageType.BUY_FILL, 'trade_id': 1, - 'buy_tag': 'buy_signal_01', + 'enter_tag': 'buy_signal_01', 'exchange': 'Binance', 'pair': 'ETH/BTC', 'stake_amount': 0.001, @@ -1705,7 +1709,7 @@ def test_send_msg_buy_fill_notification(default_conf, mocker) -> None: assert msg_mock.call_args[0][0] \ == '\N{CHECK MARK} *Binance:* Bought ETH/BTC (#1)\n' \ - '*Buy Tag:* `buy_signal_01`\n' \ + '*Enter Tag:* `buy_signal_01`\n' \ '*Amount:* `1333.33333333`\n' \ '*Open Rate:* `0.00001099`\n' \ '*Total:* `(0.00100000 BTC, 12.345 USD)`' @@ -1732,7 +1736,7 @@ def test_send_msg_sell_notification(default_conf, mocker) -> None: 'profit_ratio': -0.57405275, 'stake_currency': 'ETH', 'fiat_currency': 'USD', - 'buy_tag': 'buy_signal1', + 'enter_tag': 'buy_signal1', 'sell_reason': SellType.STOP_LOSS.value, 'open_date': arrow.utcnow().shift(hours=-1), 'close_date': arrow.utcnow(), @@ -1740,7 +1744,7 @@ def test_send_msg_sell_notification(default_conf, mocker) -> None: assert msg_mock.call_args[0][0] \ == ('\N{WARNING SIGN} *Binance:* Selling KEY/ETH (#1)\n' '*Unrealized Profit:* `-57.41% (loss: -0.05746268 ETH / -24.812 USD)`\n' - '*Buy Tag:* `buy_signal1`\n' + '*Enter Tag:* `buy_signal1`\n' '*Sell Reason:* `stop_loss`\n' '*Duration:* `1:00:00 (60.0 min)`\n' '*Amount:* `1333.33333333`\n' @@ -1764,7 +1768,7 @@ def test_send_msg_sell_notification(default_conf, mocker) -> None: 'profit_amount': -0.05746268, 'profit_ratio': -0.57405275, 'stake_currency': 'ETH', - 'buy_tag': 'buy_signal1', + 'enter_tag': 'buy_signal1', 'sell_reason': SellType.STOP_LOSS.value, 'open_date': arrow.utcnow().shift(days=-1, hours=-2, minutes=-30), 'close_date': arrow.utcnow(), @@ -1772,7 +1776,7 @@ def test_send_msg_sell_notification(default_conf, mocker) -> None: assert msg_mock.call_args[0][0] \ == ('\N{WARNING SIGN} *Binance:* Selling KEY/ETH (#1)\n' '*Unrealized Profit:* `-57.41%`\n' - '*Buy Tag:* `buy_signal1`\n' + '*Enter Tag:* `buy_signal1`\n' '*Sell Reason:* `stop_loss`\n' '*Duration:* `1 day, 2:30:00 (1590.0 min)`\n' '*Amount:* `1333.33333333`\n' @@ -1835,7 +1839,7 @@ def test_send_msg_sell_fill_notification(default_conf, mocker) -> None: 'profit_amount': -0.05746268, 'profit_ratio': -0.57405275, 'stake_currency': 'ETH', - 'buy_tag': 'buy_signal1', + 'enter_tag': 'buy_signal1', 'sell_reason': SellType.STOP_LOSS.value, 'open_date': arrow.utcnow().shift(days=-1, hours=-2, minutes=-30), 'close_date': arrow.utcnow(), @@ -1843,7 +1847,7 @@ def test_send_msg_sell_fill_notification(default_conf, mocker) -> None: assert msg_mock.call_args[0][0] \ == ('\N{WARNING SIGN} *Binance:* Sold KEY/ETH (#1)\n' '*Profit:* `-57.41%`\n' - '*Buy Tag:* `buy_signal1`\n' + '*Enter Tag:* `buy_signal1`\n' '*Sell Reason:* `stop_loss`\n' '*Duration:* `1 day, 2:30:00 (1590.0 min)`\n' '*Amount:* `1333.33333333`\n' @@ -1894,7 +1898,7 @@ def test_send_msg_buy_notification_no_fiat(default_conf, mocker) -> None: telegram.send_msg({ 'type': RPCMessageType.BUY, - 'buy_tag': 'buy_signal_01', + 'enter_tag': 'buy_signal_01', 'trade_id': 1, 'exchange': 'Binance', 'pair': 'ETH/BTC', @@ -1909,7 +1913,7 @@ def test_send_msg_buy_notification_no_fiat(default_conf, mocker) -> None: 'open_date': arrow.utcnow().shift(hours=-1) }) assert msg_mock.call_args[0][0] == ('\N{LARGE BLUE CIRCLE} *Binance:* Buying ETH/BTC (#1)\n' - '*Buy Tag:* `buy_signal_01`\n' + '*Enter Tag:* `buy_signal_01`\n' '*Amount:* `1333.33333333`\n' '*Open Rate:* `0.00001099`\n' '*Current Rate:* `0.00001099`\n' @@ -1935,14 +1939,14 @@ def test_send_msg_sell_notification_no_fiat(default_conf, mocker) -> None: 'profit_ratio': -0.57405275, 'stake_currency': 'ETH', 'fiat_currency': 'USD', - 'buy_tag': 'buy_signal1', + 'enter_tag': 'buy_signal1', 'sell_reason': SellType.STOP_LOSS.value, 'open_date': arrow.utcnow().shift(hours=-2, minutes=-35, seconds=-3), 'close_date': arrow.utcnow(), }) assert msg_mock.call_args[0][0] == ('\N{WARNING SIGN} *Binance:* Selling KEY/ETH (#1)\n' '*Unrealized Profit:* `-57.41%`\n' - '*Buy Tag:* `buy_signal1`\n' + '*Enter Tag:* `buy_signal1`\n' '*Sell Reason:* `stop_loss`\n' '*Duration:* `2:35:03 (155.1 min)`\n' '*Amount:* `1333.33333333`\n' diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index bd5fcf313..98242f38d 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -2869,6 +2869,7 @@ def test_execute_trade_exit_up(default_conf_usdt, ticker_usdt, fee, ticker_usdt_ 'amount': amt, 'order_type': 'limit', 'buy_tag': None, + 'enter_tag': None, 'open_rate': open_rate, 'current_rate': 2.01 if is_short else 2.3, 'profit_amount': 0.29554455 if is_short else 5.685, @@ -2925,6 +2926,7 @@ def test_execute_trade_exit_down(default_conf_usdt, ticker_usdt, fee, ticker_usd 'amount': 29.70297029 if is_short else 30.0, 'order_type': 'limit', 'buy_tag': None, + 'enter_tag': None, 'open_rate': 2.02 if is_short else 2.0, 'current_rate': 2.2 if is_short else 2.0, 'profit_amount': -5.65990099 if is_short else -0.00075, @@ -3002,6 +3004,7 @@ def test_execute_trade_exit_custom_exit_price( 'amount': amount, 'order_type': 'limit', 'buy_tag': None, + 'enter_tag': None, 'open_rate': open_rate, 'current_rate': current_rate, 'profit_amount': profit_amount, @@ -3066,6 +3069,7 @@ def test_execute_trade_exit_down_stoploss_on_exchange_dry_run( 'amount': 29.70297029 if is_short else 30.0, 'order_type': 'limit', 'buy_tag': None, + 'enter_tag': None, 'open_rate': 2.02 if is_short else 2.0, 'current_rate': 2.2 if is_short else 2.0, 'profit_amount': -0.3 if is_short else -0.8985, @@ -3319,6 +3323,7 @@ def test_execute_trade_exit_market_order( 'amount': round(amount, 9), 'order_type': 'market', 'buy_tag': None, + 'enter_tag': None, 'open_rate': open_rate, 'current_rate': current_rate, 'profit_amount': profit_amount, diff --git a/tests/test_persistence.py b/tests/test_persistence.py index 2f5f61a15..f1401eef1 100644 --- a/tests/test_persistence.py +++ b/tests/test_persistence.py @@ -1551,7 +1551,7 @@ def test_to_json(default_conf, fee): open_date=arrow.utcnow().shift(hours=-2).datetime, open_rate=0.123, exchange='binance', - buy_tag=None, + enter_tag=None, open_order_id='dry_run_buy_12345' ) result = trade.to_json() @@ -1602,6 +1602,7 @@ def test_to_json(default_conf, fee): 'max_rate': None, 'strategy': None, 'buy_tag': None, + 'enter_tag': None, 'timeframe': None, 'exchange': 'binance', 'leverage': None, @@ -1624,7 +1625,7 @@ def test_to_json(default_conf, fee): close_date=arrow.utcnow().shift(hours=-1).datetime, open_rate=0.123, close_rate=0.125, - buy_tag='buys_signal_001', + enter_tag='buys_signal_001', exchange='binance', ) result = trade.to_json() @@ -1675,6 +1676,7 @@ def test_to_json(default_conf, fee): 'sell_order_status': None, 'strategy': None, 'buy_tag': 'buys_signal_001', + 'enter_tag': 'buys_signal_001', 'timeframe': None, 'exchange': 'binance', 'leverage': None, @@ -2116,7 +2118,7 @@ def test_Trade_object_idem(): 'get_open_order_trades', 'get_trades', 'get_sell_reason_performance', - 'get_buy_tag_performance', + 'get_enter_tag_performance', 'get_mix_tag_performance', )