From 9e63bdbac9b866154fbabb8d75c48c63d26c09a2 Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Tue, 20 Jul 2021 04:58:20 +0700 Subject: [PATCH 01/33] feat: add buy signal name --- freqtrade/enums/__init__.py | 2 +- freqtrade/enums/signaltype.py | 7 +++++++ freqtrade/freqtradebot.py | 9 +++++---- freqtrade/optimize/backtesting.py | 4 +++- freqtrade/persistence/models.py | 3 +++ freqtrade/strategy/interface.py | 10 ++++++---- 6 files changed, 25 insertions(+), 10 deletions(-) diff --git a/freqtrade/enums/__init__.py b/freqtrade/enums/__init__.py index 78163d86f..bcf68d622 100644 --- a/freqtrade/enums/__init__.py +++ b/freqtrade/enums/__init__.py @@ -2,5 +2,5 @@ from freqtrade.enums.rpcmessagetype import RPCMessageType from freqtrade.enums.runmode import NON_UTIL_MODES, OPTIMIZE_MODES, TRADING_MODES, RunMode from freqtrade.enums.selltype import SellType -from freqtrade.enums.signaltype import SignalType +from freqtrade.enums.signaltype import SignalType, SignalNameType from freqtrade.enums.state import State diff --git a/freqtrade/enums/signaltype.py b/freqtrade/enums/signaltype.py index d636f378a..5ba2f8b4b 100644 --- a/freqtrade/enums/signaltype.py +++ b/freqtrade/enums/signaltype.py @@ -7,3 +7,10 @@ class SignalType(Enum): """ BUY = "buy" SELL = "sell" + + +class SignalNameType(Enum): + """ + Enum to distinguish between buy and sell signals + """ + BUY_SIGNAL_NAME = "buy_signal_name" diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 7a7371357..037bb954a 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -420,7 +420,7 @@ class FreqtradeBot(LoggingMixin): return False # running get_signal on historical data fetched - (buy, sell) = self.strategy.get_signal(pair, self.strategy.timeframe, analyzed_df) + (buy, sell, buy_signal_name) = self.strategy.get_signal(pair, self.strategy.timeframe, analyzed_df) if buy and not sell: stake_amount = self.wallets.get_trade_stake_amount(pair, self.edge) @@ -435,11 +435,11 @@ class FreqtradeBot(LoggingMixin): if ((bid_check_dom.get('enabled', False)) and (bid_check_dom.get('bids_to_ask_delta', 0) > 0)): if self._check_depth_of_market_buy(pair, bid_check_dom): - return self.execute_buy(pair, stake_amount) + return self.execute_buy(pair, stake_amount, buy_signal_name=buy_signal_name) else: return False - return self.execute_buy(pair, stake_amount) + return self.execute_buy(pair, stake_amount, buy_signal_name=buy_signal_name) else: return False @@ -468,7 +468,7 @@ class FreqtradeBot(LoggingMixin): return False def execute_buy(self, pair: str, stake_amount: float, price: Optional[float] = None, - forcebuy: bool = False) -> bool: + forcebuy: bool = False, buy_signal_name: str = '') -> bool: """ Executes a limit buy for the given pair :param pair: pair for which we want to create a LIMIT_BUY @@ -562,6 +562,7 @@ class FreqtradeBot(LoggingMixin): exchange=self.exchange.id, open_order_id=order_id, strategy=self.strategy.get_strategy_name(), + buy_signal_name=buy_signal_name, timeframe=timeframe_to_minutes(self.config['timeframe']) ) trade.orders.append(order_obj) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 7c6b7cbc3..2b8bd1e2b 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -42,6 +42,7 @@ CLOSE_IDX = 3 SELL_IDX = 4 LOW_IDX = 5 HIGH_IDX = 6 +BUY_SIGNAL_NAME_IDX = 7 class Backtesting: @@ -189,7 +190,7 @@ class Backtesting: """ # Every change to this headers list must evaluate further usages of the resulting tuple # and eventually change the constants for indexes at the top - headers = ['date', 'buy', 'open', 'close', 'sell', 'low', 'high'] + headers = ['date', 'buy', 'open', 'close', 'sell', 'low', 'high', 'buy_signal_name'] data: Dict = {} # Create dict with data for pair, pair_data in processed.items(): @@ -332,6 +333,7 @@ class Backtesting: fee_open=self.fee, fee_close=self.fee, is_open=True, + buy_signal_name=row[BUY_SIGNAL_NAME_IDX], exchange='backtesting', ) return trade diff --git a/freqtrade/persistence/models.py b/freqtrade/persistence/models.py index b4c299120..8d77ac27a 100644 --- a/freqtrade/persistence/models.py +++ b/freqtrade/persistence/models.py @@ -257,6 +257,7 @@ class LocalTrade(): sell_reason: str = '' sell_order_status: str = '' strategy: str = '' + buy_signal_name: str = '' timeframe: Optional[int] = None def __init__(self, **kwargs): @@ -288,6 +289,7 @@ 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_signal_name': self.buy_signal_name, 'timeframe': self.timeframe, 'fee_open': self.fee_open, @@ -703,6 +705,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_signal_name = Column(String(100), nullable=True) timeframe = Column(Integer, nullable=True) def __init__(self, **kwargs): diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 7aa7e57d9..bd77fbb21 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -13,7 +13,7 @@ from pandas import DataFrame from freqtrade.constants import ListPairsWithTimeframes from freqtrade.data.dataprovider import DataProvider -from freqtrade.enums import SellType, SignalType +from freqtrade.enums import SellType, SignalType, SignalNameType from freqtrade.exceptions import OperationalException, StrategyError from freqtrade.exchange import timeframe_to_minutes, timeframe_to_seconds from freqtrade.exchange.exchange import timeframe_to_next_date @@ -506,7 +506,9 @@ class IStrategy(ABC, HyperStrategyMixin): ) return False, False - (buy, sell) = latest[SignalType.BUY.value] == 1, latest[SignalType.SELL.value] == 1 + (buy, sell, buy_signal_name) = latest[SignalType.BUY.value] == 1,\ + latest[SignalType.SELL.value] == 1,\ + latest.get(SignalNameType.BUY_SIGNAL_NAME.value, '') logger.debug('trigger: %s (pair=%s) buy=%s sell=%s', latest['date'], pair, str(buy), str(sell)) timeframe_seconds = timeframe_to_seconds(timeframe) @@ -514,8 +516,8 @@ class IStrategy(ABC, HyperStrategyMixin): current_time=datetime.now(timezone.utc), timeframe_seconds=timeframe_seconds, buy=buy): - return False, sell - return buy, sell + return False, sell, buy_signal_name + return buy, sell, buy_signal_name def ignore_expired_candle(self, latest_date: datetime, current_time: datetime, timeframe_seconds: int, buy: bool): From 104711a9bf80b72e3541af00e2f78b5c22aaaa49 Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Tue, 20 Jul 2021 05:04:25 +0700 Subject: [PATCH 02/33] get_signal signature --- freqtrade/strategy/interface.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index bd77fbb21..cbd19e156 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -478,7 +478,7 @@ class IStrategy(ABC, HyperStrategyMixin): else: raise StrategyError(message) - def get_signal(self, pair: str, timeframe: str, dataframe: DataFrame) -> Tuple[bool, bool]: + def get_signal(self, pair: str, timeframe: str, dataframe: DataFrame) -> Tuple[bool, bool, str]: """ Calculates current signal based based on the buy / sell columns of the dataframe. Used by Bot to get the signal to buy or sell @@ -489,7 +489,7 @@ class IStrategy(ABC, HyperStrategyMixin): """ if not isinstance(dataframe, DataFrame) or dataframe.empty: logger.warning(f'Empty candle (OHLCV) data for pair {pair}') - return False, False + return False, False, '' latest_date = dataframe['date'].max() latest = dataframe.loc[dataframe['date'] == latest_date].iloc[-1] @@ -504,7 +504,7 @@ class IStrategy(ABC, HyperStrategyMixin): 'Outdated history for pair %s. Last tick is %s minutes old', pair, int((arrow.utcnow() - latest_date).total_seconds() // 60) ) - return False, False + return False, False, '' (buy, sell, buy_signal_name) = latest[SignalType.BUY.value] == 1,\ latest[SignalType.SELL.value] == 1,\ From 7d040052189d94884609bd004796f53d81ecc437 Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Tue, 20 Jul 2021 16:14:48 +0700 Subject: [PATCH 03/33] add test and migration --- freqtrade/enums/signaltype.py | 2 +- freqtrade/freqtradebot.py | 2 +- freqtrade/persistence/migrations.py | 7 ++++--- tests/strategy/test_interface.py | 19 ++++++++++++------- tests/test_freqtradebot.py | 2 +- 5 files changed, 19 insertions(+), 13 deletions(-) diff --git a/freqtrade/enums/signaltype.py b/freqtrade/enums/signaltype.py index 5ba2f8b4b..6f29699f1 100644 --- a/freqtrade/enums/signaltype.py +++ b/freqtrade/enums/signaltype.py @@ -11,6 +11,6 @@ class SignalType(Enum): class SignalNameType(Enum): """ - Enum to distinguish between buy and sell signals + Enum for signal name """ BUY_SIGNAL_NAME = "buy_signal_name" diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 037bb954a..852d5bf78 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -690,7 +690,7 @@ class FreqtradeBot(LoggingMixin): analyzed_df, _ = self.dataprovider.get_analyzed_dataframe(trade.pair, self.strategy.timeframe) - (buy, sell) = self.strategy.get_signal(trade.pair, self.strategy.timeframe, analyzed_df) + (buy, sell, _) = self.strategy.get_signal(trade.pair, self.strategy.timeframe, analyzed_df) logger.debug('checking sell') sell_rate = self.exchange.get_sell_rate(trade.pair, True) diff --git a/freqtrade/persistence/migrations.py b/freqtrade/persistence/migrations.py index 00c9b91eb..4fa1cb742 100644 --- a/freqtrade/persistence/migrations.py +++ b/freqtrade/persistence/migrations.py @@ -47,6 +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_signal_name = get_column_def(cols, 'buy_signal_name', 'null') # If ticker-interval existed use that, else null. if has_column(cols, 'ticker_interval'): timeframe = get_column_def(cols, 'timeframe', 'ticker_interval') @@ -80,7 +81,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, + max_rate, min_rate, sell_reason, sell_order_status, strategy, buy_signal_name, timeframe, open_trade_value, close_profit_abs ) select id, lower(exchange), @@ -103,7 +104,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, {timeframe} timeframe, + {strategy} strategy, {buy_signal_name} buy_signal_name, {timeframe} timeframe, {open_trade_value} open_trade_value, {close_profit_abs} close_profit_abs from {table_back_name} """)) @@ -160,7 +161,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, 'open_trade_value'): + if not has_column(cols, 'open_trade_value') or not has_column(cols, 'buy_signal_name'): 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/tests/strategy/test_interface.py b/tests/strategy/test_interface.py index 2beb4465d..c02a43e72 100644 --- a/tests/strategy/test_interface.py +++ b/tests/strategy/test_interface.py @@ -37,15 +37,20 @@ def test_returns_latest_signal(mocker, default_conf, ohlcv_history): mocked_history['buy'] = 0 mocked_history.loc[1, 'sell'] = 1 - assert _STRATEGY.get_signal('ETH/BTC', '5m', mocked_history) == (False, True) + assert _STRATEGY.get_signal('ETH/BTC', '5m', mocked_history) == (False, True, '') mocked_history.loc[1, 'sell'] = 0 mocked_history.loc[1, 'buy'] = 1 - assert _STRATEGY.get_signal('ETH/BTC', '5m', mocked_history) == (True, False) + assert _STRATEGY.get_signal('ETH/BTC', '5m', mocked_history) == (True, False, '') mocked_history.loc[1, 'sell'] = 0 mocked_history.loc[1, 'buy'] = 0 - assert _STRATEGY.get_signal('ETH/BTC', '5m', mocked_history) == (False, False) + assert _STRATEGY.get_signal('ETH/BTC', '5m', mocked_history) == (False, False, '') + mocked_history.loc[1, 'sell'] = 0 + mocked_history.loc[1, 'buy'] = 1 + mocked_history.loc[1, 'buy_signal_name'] = 'buy_signal_01' + + assert _STRATEGY.get_signal('ETH/BTC', '5m', mocked_history) == (True, False, 'buy_signal_01') def test_analyze_pair_empty(default_conf, mocker, caplog, ohlcv_history): @@ -62,15 +67,15 @@ def test_analyze_pair_empty(default_conf, mocker, caplog, ohlcv_history): def test_get_signal_empty(default_conf, mocker, caplog): - assert (False, False) == _STRATEGY.get_signal('foo', default_conf['timeframe'], DataFrame()) + assert (False, False, '') == _STRATEGY.get_signal('foo', default_conf['timeframe'], DataFrame()) assert log_has('Empty candle (OHLCV) data for pair foo', caplog) caplog.clear() - assert (False, False) == _STRATEGY.get_signal('bar', default_conf['timeframe'], None) + assert (False, False, '') == _STRATEGY.get_signal('bar', default_conf['timeframe'], None) assert log_has('Empty candle (OHLCV) data for pair bar', caplog) caplog.clear() - assert (False, False) == _STRATEGY.get_signal('baz', default_conf['timeframe'], DataFrame([])) + assert (False, False, '') == _STRATEGY.get_signal('baz', default_conf['timeframe'], DataFrame([])) assert log_has('Empty candle (OHLCV) data for pair baz', caplog) @@ -106,7 +111,7 @@ def test_get_signal_old_dataframe(default_conf, mocker, caplog, ohlcv_history): caplog.set_level(logging.INFO) mocker.patch.object(_STRATEGY, 'assert_df') - assert (False, False) == _STRATEGY.get_signal('xyz', default_conf['timeframe'], mocked_history) + assert (False, False, '') == _STRATEGY.get_signal('xyz', default_conf['timeframe'], mocked_history) assert log_has('Outdated history for pair xyz. Last tick is 16 minutes old', caplog) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 99e11e893..2309c96f0 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -736,7 +736,7 @@ def test_process_informative_pairs_added(default_conf, ticker, mocker) -> None: refresh_latest_ohlcv=refresh_mock, ) inf_pairs = MagicMock(return_value=[("BTC/ETH", '1m'), ("ETH/USDT", "1h")]) - mocker.patch('freqtrade.strategy.interface.IStrategy.get_signal', return_value=(False, False)) + mocker.patch('freqtrade.strategy.interface.IStrategy.get_signal', return_value=(False, False, '')) mocker.patch('time.sleep', return_value=None) freqtrade = FreqtradeBot(default_conf) From ec526b3f963792e78bafaf772e01dacbdb92ba51 Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Tue, 20 Jul 2021 16:22:04 +0700 Subject: [PATCH 04/33] fix testcase --- tests/conftest.py | 2 +- tests/test_freqtradebot.py | 42 +++++++++++++++++++------------------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index a843d9397..58af2fe90 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -182,7 +182,7 @@ def get_patched_worker(mocker, config) -> Worker: return Worker(args=None, config=config) -def patch_get_signal(freqtrade: FreqtradeBot, value=(True, False)) -> None: +def patch_get_signal(freqtrade: FreqtradeBot, value=(True, False, '')) -> None: """ :param mocker: mocker to patch IStrategy class :param value: which value IStrategy.get_signal() must return diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 2309c96f0..cc89ce00c 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -515,7 +515,7 @@ def test_create_trade_no_signal(default_conf, fee, mocker) -> None: ) default_conf['stake_amount'] = 10 freqtrade = FreqtradeBot(default_conf) - patch_get_signal(freqtrade, value=(False, False)) + patch_get_signal(freqtrade, value=(False, False, '')) Trade.query = MagicMock() Trade.query.filter = MagicMock() @@ -1818,7 +1818,7 @@ def test_handle_trade(default_conf, limit_buy_order, limit_sell_order_open, limi assert trade.is_open is True freqtrade.wallets.update() - patch_get_signal(freqtrade, value=(False, True)) + patch_get_signal(freqtrade, value=(False, True, '')) assert freqtrade.handle_trade(trade) is True assert trade.open_order_id == limit_sell_order['id'] @@ -1843,7 +1843,7 @@ def test_handle_overlapping_signals(default_conf, ticker, limit_buy_order_open, ) freqtrade = FreqtradeBot(default_conf) - patch_get_signal(freqtrade, value=(True, True)) + patch_get_signal(freqtrade, value=(True, True, '')) freqtrade.strategy.min_roi_reached = MagicMock(return_value=False) freqtrade.enter_positions() @@ -1854,7 +1854,7 @@ def test_handle_overlapping_signals(default_conf, ticker, limit_buy_order_open, assert nb_trades == 0 # Buy is triggering, so buying ... - patch_get_signal(freqtrade, value=(True, False)) + patch_get_signal(freqtrade, value=(True, False, '')) freqtrade.enter_positions() trades = Trade.query.all() nb_trades = len(trades) @@ -1862,7 +1862,7 @@ def test_handle_overlapping_signals(default_conf, ticker, limit_buy_order_open, assert trades[0].is_open is True # Buy and Sell are not triggering, so doing nothing ... - patch_get_signal(freqtrade, value=(False, False)) + patch_get_signal(freqtrade, value=(False, False, '')) assert freqtrade.handle_trade(trades[0]) is False trades = Trade.query.all() nb_trades = len(trades) @@ -1870,7 +1870,7 @@ def test_handle_overlapping_signals(default_conf, ticker, limit_buy_order_open, assert trades[0].is_open is True # Buy and Sell are triggering, so doing nothing ... - patch_get_signal(freqtrade, value=(True, True)) + patch_get_signal(freqtrade, value=(True, True, '')) assert freqtrade.handle_trade(trades[0]) is False trades = Trade.query.all() nb_trades = len(trades) @@ -1878,7 +1878,7 @@ def test_handle_overlapping_signals(default_conf, ticker, limit_buy_order_open, assert trades[0].is_open is True # Sell is triggering, guess what : we are Selling! - patch_get_signal(freqtrade, value=(False, True)) + patch_get_signal(freqtrade, value=(False, True, '')) trades = Trade.query.all() assert freqtrade.handle_trade(trades[0]) is True @@ -1896,7 +1896,7 @@ def test_handle_trade_roi(default_conf, ticker, limit_buy_order_open, ) freqtrade = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtrade, value=(True, False)) + patch_get_signal(freqtrade, value=(True, False, '')) freqtrade.strategy.min_roi_reached = MagicMock(return_value=True) freqtrade.enter_positions() @@ -1909,7 +1909,7 @@ def test_handle_trade_roi(default_conf, ticker, limit_buy_order_open, # we might just want to check if we are in a sell condition without # executing # if ROI is reached we must sell - patch_get_signal(freqtrade, value=(False, True)) + patch_get_signal(freqtrade, value=(False, True, '')) assert freqtrade.handle_trade(trade) assert log_has("ETH/BTC - Required profit reached. sell_type=SellType.ROI", caplog) @@ -1935,10 +1935,10 @@ def test_handle_trade_use_sell_signal( trade = Trade.query.first() trade.is_open = True - patch_get_signal(freqtrade, value=(False, False)) + patch_get_signal(freqtrade, value=(False, False, '')) assert not freqtrade.handle_trade(trade) - patch_get_signal(freqtrade, value=(False, True)) + patch_get_signal(freqtrade, value=(False, True, '')) assert freqtrade.handle_trade(trade) assert log_has("ETH/BTC - Sell signal received. sell_type=SellType.SELL_SIGNAL", caplog) @@ -2974,7 +2974,7 @@ def test_sell_profit_only_enable_profit(default_conf, limit_buy_order, limit_buy trade = Trade.query.first() trade.update(limit_buy_order) freqtrade.wallets.update() - patch_get_signal(freqtrade, value=(False, True)) + patch_get_signal(freqtrade, value=(False, True, '')) assert freqtrade.handle_trade(trade) is False freqtrade.strategy.sell_profit_offset = 0.0 @@ -3009,7 +3009,7 @@ def test_sell_profit_only_disable_profit(default_conf, limit_buy_order, limit_bu trade = Trade.query.first() trade.update(limit_buy_order) freqtrade.wallets.update() - patch_get_signal(freqtrade, value=(False, True)) + patch_get_signal(freqtrade, value=(False, True, '')) assert freqtrade.handle_trade(trade) is True assert trade.sell_reason == SellType.SELL_SIGNAL.value @@ -3040,7 +3040,7 @@ def test_sell_profit_only_enable_loss(default_conf, limit_buy_order, limit_buy_o trade = Trade.query.first() trade.update(limit_buy_order) - patch_get_signal(freqtrade, value=(False, True)) + patch_get_signal(freqtrade, value=(False, True, '')) assert freqtrade.handle_trade(trade) is False @@ -3072,7 +3072,7 @@ def test_sell_profit_only_disable_loss(default_conf, limit_buy_order, limit_buy_ trade = Trade.query.first() trade.update(limit_buy_order) freqtrade.wallets.update() - patch_get_signal(freqtrade, value=(False, True)) + patch_get_signal(freqtrade, value=(False, True, '')) assert freqtrade.handle_trade(trade) is True assert trade.sell_reason == SellType.SELL_SIGNAL.value @@ -3101,7 +3101,7 @@ def test_sell_not_enough_balance(default_conf, limit_buy_order, limit_buy_order_ trade = Trade.query.first() amnt = trade.amount trade.update(limit_buy_order) - patch_get_signal(freqtrade, value=(False, True)) + patch_get_signal(freqtrade, value=(False, True, '')) mocker.patch('freqtrade.wallets.Wallets.get_free', MagicMock(return_value=trade.amount * 0.985)) assert freqtrade.handle_trade(trade) is True @@ -3220,11 +3220,11 @@ def test_ignore_roi_if_buy_signal(default_conf, limit_buy_order, limit_buy_order trade = Trade.query.first() trade.update(limit_buy_order) freqtrade.wallets.update() - patch_get_signal(freqtrade, value=(True, True)) + patch_get_signal(freqtrade, value=(True, True, '')) assert freqtrade.handle_trade(trade) is False # Test if buy-signal is absent (should sell due to roi = true) - patch_get_signal(freqtrade, value=(False, True)) + patch_get_signal(freqtrade, value=(False, True, '')) assert freqtrade.handle_trade(trade) is True assert trade.sell_reason == SellType.ROI.value @@ -3485,11 +3485,11 @@ def test_disable_ignore_roi_if_buy_signal(default_conf, limit_buy_order, limit_b trade = Trade.query.first() trade.update(limit_buy_order) # Sell due to min_roi_reached - patch_get_signal(freqtrade, value=(True, True)) + patch_get_signal(freqtrade, value=(True, True, '')) assert freqtrade.handle_trade(trade) is True # Test if buy-signal is absent - patch_get_signal(freqtrade, value=(False, True)) + patch_get_signal(freqtrade, value=(False, True, '')) assert freqtrade.handle_trade(trade) is True assert trade.sell_reason == SellType.SELL_SIGNAL.value @@ -4017,7 +4017,7 @@ def test_order_book_ask_strategy(default_conf, limit_buy_order_open, limit_buy_o freqtrade.wallets.update() assert trade.is_open is True - patch_get_signal(freqtrade, value=(False, True)) + patch_get_signal(freqtrade, value=(False, True, '')) assert freqtrade.handle_trade(trade) is True assert trade.close_rate_requested == order_book_l2.return_value['asks'][0][0] From d31d38a85f3a274c9a8f989c80c3a37b052b596c Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Tue, 20 Jul 2021 16:39:20 +0700 Subject: [PATCH 05/33] add doc --- docs/strategy-advanced.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/docs/strategy-advanced.md b/docs/strategy-advanced.md index 3436604a9..dd38d2cbc 100644 --- a/docs/strategy-advanced.md +++ b/docs/strategy-advanced.md @@ -114,6 +114,32 @@ class AwesomeStrategy(IStrategy): See [Dataframe access](#dataframe-access) for more information about dataframe use in strategy callbacks. +## Buy Signal Name + +When your strategy has multiple buy signal, you can name it. +Then you can access you buy signal on `custom_sell` + +```python +def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + (dataframe['rsi'] < 35) & + (dataframe['volume'] > 0) + ), + ['buy', 'buy_signal_name']] = 1, 'buy_signal_rsi' + + return dataframe + +def custom_sell(self, pair: str, trade: Trade, current_time: datetime, current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + if trade.buy_signal_name == 'buy_signal_rsi' and last_candle['rsi'] > 80: + return 'sell_signal_rsi' + return None + +``` + ## Custom stoploss The stoploss price can only ever move upwards - if the stoploss value returned from `custom_stoploss` would result in a lower stoploss price than was previously set, it will be ignored. The traditional `stoploss` value serves as an absolute lower level and will be instated as the initial stoploss. From ed30c023cdb3d2d1c655f75b242a84f78a712ca7 Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Tue, 20 Jul 2021 19:08:14 +0700 Subject: [PATCH 06/33] fix some testcase --- freqtrade/optimize/backtesting.py | 2 + freqtrade/strategy/interface.py | 1 + tests/optimize/test_backtesting.py | 1 + tests/plugins/test_pairlist.py | 10 +-- tests/rpc/test_rpc.py | 36 ++++---- tests/rpc/test_rpc_apiserver.py | 138 ++++++++++++++--------------- tests/rpc/test_rpc_telegram.py | 68 +++++++------- tests/test_freqtradebot.py | 34 +++---- 8 files changed, 148 insertions(+), 142 deletions(-) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 2b8bd1e2b..35e524f69 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -197,6 +197,7 @@ class Backtesting: if not pair_data.empty: pair_data.loc[:, 'buy'] = 0 # cleanup if buy_signal is exist pair_data.loc[:, 'sell'] = 0 # cleanup if sell_signal is exist + pair_data.loc[:, 'buy_signal_name'] = '' # cleanup if buy_signal_name is exist df_analyzed = self.strategy.advise_sell( self.strategy.advise_buy(pair_data, {'pair': pair}), {'pair': pair})[headers].copy() @@ -205,6 +206,7 @@ class Backtesting: # from the previous candle df_analyzed.loc[:, 'buy'] = df_analyzed.loc[:, 'buy'].shift(1) df_analyzed.loc[:, 'sell'] = df_analyzed.loc[:, 'sell'].shift(1) + df_analyzed.loc[:, 'buy_signal_name'] = df_analyzed.loc[:, 'buy_signal_name'].shift(1) df_analyzed.drop(df_analyzed.head(1).index, inplace=True) diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index cbd19e156..6ff499199 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -404,6 +404,7 @@ class IStrategy(ABC, HyperStrategyMixin): logger.debug("Skipping TA Analysis for already analyzed candle") dataframe['buy'] = 0 dataframe['sell'] = 0 + dataframe['buy_signal_name'] = '' # Other Defs in strategy that want to be called every loop here # twitter_sell = self.watch_twitter_feed(dataframe, metadata) diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py index 30d86f979..102d62273 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -482,6 +482,7 @@ def test_backtest__enter_trade(default_conf, fee, mocker) -> None: 0, # Sell 0.00099, # Low 0.0012, # High + '', # Buy Signal Name ] trade = backtesting._enter_trade(pair, row=row) assert isinstance(trade, LocalTrade) diff --git a/tests/plugins/test_pairlist.py b/tests/plugins/test_pairlist.py index ae8f6e958..3dee47599 100644 --- a/tests/plugins/test_pairlist.py +++ b/tests/plugins/test_pairlist.py @@ -769,20 +769,20 @@ def test_spreadfilter_invalid_data(mocker, default_conf, markets, tickers, caplo get_tickers=tickers ) - ftbot = get_patched_freqtradebot(mocker, default_conf) - ftbot.pairlists.refresh_pairlist() + freqtrade = get_patched_freqtradebot(mocker, default_conf) + freqtrade.pairlists.refresh_pairlist() - assert len(ftbot.pairlists.whitelist) == 5 + assert len(freqtrade.pairlists.whitelist) == 5 tickers.return_value['ETH/BTC']['ask'] = 0.0 del tickers.return_value['TKN/BTC'] del tickers.return_value['LTC/BTC'] mocker.patch.multiple('freqtrade.exchange.Exchange', get_tickers=tickers) - ftbot.pairlists.refresh_pairlist() + freqtrade.pairlists.refresh_pairlist() assert log_has_re(r'Removed .* invalid ticker data.*', caplog) - assert len(ftbot.pairlists.whitelist) == 2 + assert len(freqtrade.pairlists.whitelist) == 2 @pytest.mark.parametrize("pairlistconfig,desc_expected,exception_expected", [ diff --git a/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py index cc29dc157..3d1896a4d 100644 --- a/tests/rpc/test_rpc.py +++ b/tests/rpc/test_rpc.py @@ -35,7 +35,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None: ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) rpc = RPC(freqtradebot) freqtradebot.state = State.RUNNING @@ -69,6 +69,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None: 'min_rate': ANY, 'max_rate': ANY, 'strategy': ANY, + 'buy_signal_name': ANY, 'timeframe': 5, 'open_order_id': ANY, 'close_date': None, @@ -135,6 +136,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None: 'min_rate': ANY, 'max_rate': ANY, 'strategy': ANY, + 'buy_signal_name': ANY, 'timeframe': ANY, 'open_order_id': ANY, 'close_date': None, @@ -190,7 +192,7 @@ def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None: ) del default_conf['fiat_display_currency'] freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) rpc = RPC(freqtradebot) freqtradebot.state = State.RUNNING @@ -237,7 +239,7 @@ def test_rpc_daily_profit(default_conf, update, ticker, fee, ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) stake_currency = default_conf['stake_currency'] fiat_display_currency = default_conf['fiat_display_currency'] @@ -369,7 +371,7 @@ def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee, ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) stake_currency = default_conf['stake_currency'] fiat_display_currency = default_conf['fiat_display_currency'] @@ -457,7 +459,7 @@ def test_rpc_trade_statistics_closed(mocker, default_conf, ticker, fee, ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) stake_currency = default_conf['stake_currency'] fiat_display_currency = default_conf['fiat_display_currency'] @@ -524,7 +526,7 @@ def test_rpc_balance_handle_error(default_conf, mocker): ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) rpc = RPC(freqtradebot) rpc._fiat_converter = CryptoToFiatConverter() with pytest.raises(RPCException, match="Error getting current tickers."): @@ -565,7 +567,7 @@ def test_rpc_balance_handle(default_conf, mocker, tickers): ) default_conf['dry_run'] = False freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) rpc = RPC(freqtradebot) rpc._fiat_converter = CryptoToFiatConverter() @@ -610,7 +612,7 @@ def test_rpc_start(mocker, default_conf) -> None: ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) rpc = RPC(freqtradebot) freqtradebot.state = State.STOPPED @@ -631,7 +633,7 @@ def test_rpc_stop(mocker, default_conf) -> None: ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) rpc = RPC(freqtradebot) freqtradebot.state = State.RUNNING @@ -653,7 +655,7 @@ def test_rpc_stopbuy(mocker, default_conf) -> None: ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) rpc = RPC(freqtradebot) freqtradebot.state = State.RUNNING @@ -685,7 +687,7 @@ def test_rpc_forcesell(default_conf, ticker, fee, mocker) -> None: mocker.patch('freqtrade.wallets.Wallets.get_free', return_value=1000) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) rpc = RPC(freqtradebot) freqtradebot.state = State.STOPPED @@ -803,7 +805,7 @@ def test_performance_handle(default_conf, ticker, limit_buy_order, fee, ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) rpc = RPC(freqtradebot) # Create some test data @@ -836,7 +838,7 @@ def test_rpc_count(mocker, default_conf, ticker, fee) -> None: ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) rpc = RPC(freqtradebot) counts = rpc._rpc_count() @@ -861,7 +863,7 @@ def test_rpcforcebuy(mocker, default_conf, ticker, fee, limit_buy_order_open) -> ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) rpc = RPC(freqtradebot) pair = 'ETH/BTC' trade = rpc._rpc_forcebuy(pair, None) @@ -887,7 +889,7 @@ def test_rpcforcebuy(mocker, default_conf, ticker, fee, limit_buy_order_open) -> # Test not buying freqtradebot = get_patched_freqtradebot(mocker, default_conf) freqtradebot.config['stake_amount'] = 0.0000001 - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) rpc = RPC(freqtradebot) pair = 'TKN/BTC' trade = rpc._rpc_forcebuy(pair, None) @@ -900,7 +902,7 @@ def test_rpcforcebuy_stopped(mocker, default_conf) -> None: mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) rpc = RPC(freqtradebot) pair = 'ETH/BTC' with pytest.raises(RPCException, match=r'trader is not running'): @@ -911,7 +913,7 @@ def test_rpcforcebuy_disabled(mocker, default_conf) -> None: mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) rpc = RPC(freqtradebot) pair = 'ETH/BTC' with pytest.raises(RPCException, match=r'Forcebuy not enabled.'): diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index b8dd112c9..1ab7f178e 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -45,11 +45,11 @@ def botclient(default_conf, mocker): "password": _TEST_PASS, }}) - ftbot = get_patched_freqtradebot(mocker, default_conf) - rpc = RPC(ftbot) + freqtrade = get_patched_freqtradebot(mocker, default_conf) + rpc = RPC(freqtrade) mocker.patch('freqtrade.rpc.api_server.ApiServer.start_api', MagicMock()) apiserver = ApiServer(rpc, default_conf) - yield ftbot, TestClient(apiserver.app) + yield freqtrade, TestClient(apiserver.app) # Cleanup ... ? @@ -83,7 +83,7 @@ def assert_response(response, expected_code=200, needs_cors=True): def test_api_not_found(botclient): - ftbot, client = botclient + freqtrade, client = botclient rc = client_get(client, f"{BASE_URI}/invalid_url") assert_response(rc, 404) @@ -91,7 +91,7 @@ def test_api_not_found(botclient): def test_api_ui_fallback(botclient): - ftbot, client = botclient + freqtrade, client = botclient rc = client_get(client, "/favicon.ico") assert rc.status_code == 200 @@ -126,7 +126,7 @@ def test_api_auth(): def test_api_unauthorized(botclient): - ftbot, client = botclient + freqtrade, client = botclient rc = client.get(f"{BASE_URI}/ping") assert_response(rc, needs_cors=False) assert rc.json() == {'status': 'pong'} @@ -137,20 +137,20 @@ def test_api_unauthorized(botclient): assert rc.json() == {'detail': 'Unauthorized'} # Change only username - ftbot.config['api_server']['username'] = 'Ftrader' + freqtrade.config['api_server']['username'] = 'Ftrader' rc = client_get(client, f"{BASE_URI}/version") assert_response(rc, 401) assert rc.json() == {'detail': 'Unauthorized'} # Change only password - ftbot.config['api_server']['username'] = _TEST_USER - ftbot.config['api_server']['password'] = 'WrongPassword' + freqtrade.config['api_server']['username'] = _TEST_USER + freqtrade.config['api_server']['password'] = 'WrongPassword' rc = client_get(client, f"{BASE_URI}/version") assert_response(rc, 401) assert rc.json() == {'detail': 'Unauthorized'} - ftbot.config['api_server']['username'] = 'Ftrader' - ftbot.config['api_server']['password'] = 'WrongPassword' + freqtrade.config['api_server']['username'] = 'Ftrader' + freqtrade.config['api_server']['password'] = 'WrongPassword' rc = client_get(client, f"{BASE_URI}/version") assert_response(rc, 401) @@ -158,7 +158,7 @@ def test_api_unauthorized(botclient): def test_api_token_login(botclient): - ftbot, client = botclient + freqtrade, client = botclient rc = client.post(f"{BASE_URI}/token/login", data=None, headers={'Authorization': _basic_auth_str('WRONG_USER', 'WRONG_PASS'), @@ -177,7 +177,7 @@ def test_api_token_login(botclient): def test_api_token_refresh(botclient): - ftbot, client = botclient + freqtrade, client = botclient rc = client_post(client, f"{BASE_URI}/token/login") assert_response(rc) rc = client.post(f"{BASE_URI}/token/refresh", @@ -190,12 +190,12 @@ def test_api_token_refresh(botclient): def test_api_stop_workflow(botclient): - ftbot, client = botclient - assert ftbot.state == State.RUNNING + freqtrade, client = botclient + assert freqtrade.state == State.RUNNING rc = client_post(client, f"{BASE_URI}/stop") assert_response(rc) assert rc.json() == {'status': 'stopping trader ...'} - assert ftbot.state == State.STOPPED + assert freqtrade.state == State.STOPPED # Stop bot again rc = client_post(client, f"{BASE_URI}/stop") @@ -206,7 +206,7 @@ def test_api_stop_workflow(botclient): rc = client_post(client, f"{BASE_URI}/start") assert_response(rc) assert rc.json() == {'status': 'starting trader ...'} - assert ftbot.state == State.RUNNING + assert freqtrade.state == State.RUNNING # Call start again rc = client_post(client, f"{BASE_URI}/start") @@ -358,32 +358,32 @@ def test_api_cleanup(default_conf, mocker, caplog): def test_api_reloadconf(botclient): - ftbot, client = botclient + freqtrade, client = botclient rc = client_post(client, f"{BASE_URI}/reload_config") assert_response(rc) assert rc.json() == {'status': 'Reloading config ...'} - assert ftbot.state == State.RELOAD_CONFIG + assert freqtrade.state == State.RELOAD_CONFIG def test_api_stopbuy(botclient): - ftbot, client = botclient - assert ftbot.config['max_open_trades'] != 0 + freqtrade, client = botclient + assert freqtrade.config['max_open_trades'] != 0 rc = client_post(client, f"{BASE_URI}/stopbuy") assert_response(rc) assert rc.json() == {'status': 'No more buy will occur from now. Run /reload_config to reset.'} - assert ftbot.config['max_open_trades'] == 0 + assert freqtrade.config['max_open_trades'] == 0 def test_api_balance(botclient, mocker, rpc_balance): - ftbot, client = botclient + freqtrade, client = botclient - ftbot.config['dry_run'] = False + freqtrade.config['dry_run'] = False mocker.patch('freqtrade.exchange.Exchange.get_balances', return_value=rpc_balance) mocker.patch('freqtrade.exchange.Exchange.get_valid_pair_combination', side_effect=lambda a, b: f"{a}/{b}") - ftbot.wallets.update() + freqtrade.wallets.update() rc = client_get(client, f"{BASE_URI}/balance") assert_response(rc) @@ -400,8 +400,8 @@ def test_api_balance(botclient, mocker, rpc_balance): def test_api_count(botclient, mocker, ticker, fee, markets): - ftbot, client = botclient - patch_get_signal(ftbot, (True, False)) + freqtrade, client = botclient + patch_get_signal(freqtrade, (True, False, '')) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_balances=MagicMock(return_value=ticker), @@ -422,13 +422,13 @@ def test_api_count(botclient, mocker, ticker, fee, markets): assert rc.json()["current"] == 4 assert rc.json()["max"] == 1 - ftbot.config['max_open_trades'] = float('inf') + freqtrade.config['max_open_trades'] = float('inf') rc = client_get(client, f"{BASE_URI}/count") assert rc.json()["max"] == -1 def test_api_locks(botclient): - ftbot, client = botclient + freqtrade, client = botclient rc = client_get(client, f"{BASE_URI}/locks") assert_response(rc) @@ -462,8 +462,8 @@ def test_api_locks(botclient): def test_api_show_config(botclient, mocker): - ftbot, client = botclient - patch_get_signal(ftbot, (True, False)) + freqtrade, client = botclient + patch_get_signal(freqtrade, (True, False, '')) rc = client_get(client, f"{BASE_URI}/show_config") assert_response(rc) @@ -480,8 +480,8 @@ def test_api_show_config(botclient, mocker): def test_api_daily(botclient, mocker, ticker, fee, markets): - ftbot, client = botclient - patch_get_signal(ftbot, (True, False)) + freqtrade, client = botclient + patch_get_signal(freqtrade, (True, False, '')) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_balances=MagicMock(return_value=ticker), @@ -498,8 +498,8 @@ def test_api_daily(botclient, mocker, ticker, fee, markets): def test_api_trades(botclient, mocker, fee, markets): - ftbot, client = botclient - patch_get_signal(ftbot, (True, False)) + freqtrade, client = botclient + patch_get_signal(freqtrade, (True, False, '')) mocker.patch.multiple( 'freqtrade.exchange.Exchange', markets=PropertyMock(return_value=markets) @@ -526,8 +526,8 @@ def test_api_trades(botclient, mocker, fee, markets): def test_api_trade_single(botclient, mocker, fee, ticker, markets): - ftbot, client = botclient - patch_get_signal(ftbot, (True, False)) + freqtrade, client = botclient + patch_get_signal(freqtrade, (True, False, '')) mocker.patch.multiple( 'freqtrade.exchange.Exchange', markets=PropertyMock(return_value=markets), @@ -546,8 +546,8 @@ def test_api_trade_single(botclient, mocker, fee, ticker, markets): def test_api_delete_trade(botclient, mocker, fee, markets): - ftbot, client = botclient - patch_get_signal(ftbot, (True, False)) + freqtrade, client = botclient + patch_get_signal(freqtrade, (True, False, '')) stoploss_mock = MagicMock() cancel_mock = MagicMock() mocker.patch.multiple( @@ -562,7 +562,7 @@ def test_api_delete_trade(botclient, mocker, fee, markets): create_mock_trades(fee) Trade.query.session.flush() - ftbot.strategy.order_types['stoploss_on_exchange'] = True + freqtrade.strategy.order_types['stoploss_on_exchange'] = True trades = Trade.query.all() trades[1].stoploss_order_id = '1234' assert len(trades) > 2 @@ -588,7 +588,7 @@ def test_api_delete_trade(botclient, mocker, fee, markets): def test_api_logs(botclient): - ftbot, client = botclient + freqtrade, client = botclient rc = client_get(client, f"{BASE_URI}/logs") assert_response(rc) assert len(rc.json()) == 2 @@ -620,8 +620,8 @@ def test_api_logs(botclient): def test_api_edge_disabled(botclient, mocker, ticker, fee, markets): - ftbot, client = botclient - patch_get_signal(ftbot, (True, False)) + freqtrade, client = botclient + patch_get_signal(freqtrade, (True, False, '')) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_balances=MagicMock(return_value=ticker), @@ -636,8 +636,8 @@ def test_api_edge_disabled(botclient, mocker, ticker, fee, markets): @pytest.mark.usefixtures("init_persistence") def test_api_profit(botclient, mocker, ticker, fee, markets): - ftbot, client = botclient - patch_get_signal(ftbot, (True, False)) + freqtrade, client = botclient + patch_get_signal(freqtrade, (True, False, '')) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_balances=MagicMock(return_value=ticker), @@ -683,8 +683,8 @@ def test_api_profit(botclient, mocker, ticker, fee, markets): @pytest.mark.usefixtures("init_persistence") def test_api_stats(botclient, mocker, ticker, fee, markets,): - ftbot, client = botclient - patch_get_signal(ftbot, (True, False)) + freqtrade, client = botclient + patch_get_signal(freqtrade, (True, False, '')) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_balances=MagicMock(return_value=ticker), @@ -711,8 +711,8 @@ def test_api_stats(botclient, mocker, ticker, fee, markets,): def test_api_performance(botclient, fee): - ftbot, client = botclient - patch_get_signal(ftbot, (True, False)) + freqtrade, client = botclient + patch_get_signal(freqtrade, (True, False, '')) trade = Trade( pair='LTC/ETH', @@ -757,8 +757,8 @@ def test_api_performance(botclient, fee): def test_api_status(botclient, mocker, ticker, fee, markets): - ftbot, client = botclient - patch_get_signal(ftbot, (True, False)) + freqtrade, client = botclient + patch_get_signal(freqtrade, (True, False, '')) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_balances=MagicMock(return_value=ticker), @@ -845,7 +845,7 @@ def test_api_status(botclient, mocker, ticker, fee, markets): def test_api_version(botclient): - ftbot, client = botclient + freqtrade, client = botclient rc = client_get(client, f"{BASE_URI}/version") assert_response(rc) @@ -853,7 +853,7 @@ def test_api_version(botclient): def test_api_blacklist(botclient, mocker): - ftbot, client = botclient + freqtrade, client = botclient rc = client_get(client, f"{BASE_URI}/blacklist") assert_response(rc) @@ -888,7 +888,7 @@ def test_api_blacklist(botclient, mocker): def test_api_whitelist(botclient): - ftbot, client = botclient + freqtrade, client = botclient rc = client_get(client, f"{BASE_URI}/whitelist") assert_response(rc) @@ -900,7 +900,7 @@ def test_api_whitelist(botclient): def test_api_forcebuy(botclient, mocker, fee): - ftbot, client = botclient + freqtrade, client = botclient rc = client_post(client, f"{BASE_URI}/forcebuy", data='{"pair": "ETH/BTC"}') @@ -908,7 +908,7 @@ def test_api_forcebuy(botclient, mocker, fee): assert rc.json() == {"error": "Error querying /api/v1/forcebuy: Forcebuy not enabled."} # enable forcebuy - ftbot.config['forcebuy_enable'] = True + freqtrade.config['forcebuy_enable'] = True fbuy_mock = MagicMock(return_value=None) mocker.patch("freqtrade.rpc.RPC._rpc_forcebuy", fbuy_mock) @@ -990,7 +990,7 @@ def test_api_forcebuy(botclient, mocker, fee): def test_api_forcesell(botclient, mocker, ticker, fee, markets): - ftbot, client = botclient + freqtrade, client = botclient mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_balances=MagicMock(return_value=ticker), @@ -999,14 +999,14 @@ def test_api_forcesell(botclient, mocker, ticker, fee, markets): markets=PropertyMock(return_value=markets), _is_dry_limit_order_filled=MagicMock(return_value=False), ) - patch_get_signal(ftbot, (True, False)) + patch_get_signal(freqtrade, (True, False, '')) rc = client_post(client, f"{BASE_URI}/forcesell", data='{"tradeid": "1"}') assert_response(rc, 502) assert rc.json() == {"error": "Error querying /api/v1/forcesell: invalid argument"} - ftbot.enter_positions() + freqtrade.enter_positions() rc = client_post(client, f"{BASE_URI}/forcesell", data='{"tradeid": "1"}') @@ -1015,7 +1015,7 @@ def test_api_forcesell(botclient, mocker, ticker, fee, markets): def test_api_pair_candles(botclient, ohlcv_history): - ftbot, client = botclient + freqtrade, client = botclient timeframe = '5m' amount = 3 @@ -1043,7 +1043,7 @@ def test_api_pair_candles(botclient, ohlcv_history): ohlcv_history.loc[1, 'buy'] = 1 ohlcv_history['sell'] = 0 - ftbot.dataprovider._set_cached_df("XRP/BTC", timeframe, ohlcv_history) + freqtrade.dataprovider._set_cached_df("XRP/BTC", timeframe, ohlcv_history) rc = client_get(client, f"{BASE_URI}/pair_candles?limit={amount}&pair=XRP%2FBTC&timeframe={timeframe}") @@ -1081,7 +1081,7 @@ def test_api_pair_candles(botclient, ohlcv_history): def test_api_pair_history(botclient, ohlcv_history): - ftbot, client = botclient + freqtrade, client = botclient timeframe = '5m' # No pair @@ -1134,21 +1134,21 @@ def test_api_pair_history(botclient, ohlcv_history): def test_api_plot_config(botclient): - ftbot, client = botclient + freqtrade, client = botclient rc = client_get(client, f"{BASE_URI}/plot_config") assert_response(rc) assert rc.json() == {} - ftbot.strategy.plot_config = {'main_plot': {'sma': {}}, + freqtrade.strategy.plot_config = {'main_plot': {'sma': {}}, 'subplots': {'RSI': {'rsi': {'color': 'red'}}}} rc = client_get(client, f"{BASE_URI}/plot_config") assert_response(rc) - assert rc.json() == ftbot.strategy.plot_config + assert rc.json() == freqtrade.strategy.plot_config assert isinstance(rc.json()['main_plot'], dict) assert isinstance(rc.json()['subplots'], dict) - ftbot.strategy.plot_config = {'main_plot': {'sma': {}}} + freqtrade.strategy.plot_config = {'main_plot': {'sma': {}}} rc = client_get(client, f"{BASE_URI}/plot_config") assert_response(rc) @@ -1157,7 +1157,7 @@ def test_api_plot_config(botclient): def test_api_strategies(botclient): - ftbot, client = botclient + freqtrade, client = botclient rc = client_get(client, f"{BASE_URI}/strategies") @@ -1170,7 +1170,7 @@ def test_api_strategies(botclient): def test_api_strategy(botclient): - ftbot, client = botclient + freqtrade, client = botclient rc = client_get(client, f"{BASE_URI}/strategy/DefaultStrategy") @@ -1185,7 +1185,7 @@ def test_api_strategy(botclient): def test_list_available_pairs(botclient): - ftbot, client = botclient + freqtrade, client = botclient rc = client_get(client, f"{BASE_URI}/available_pairs") diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py index 782ae69c6..642b55975 100644 --- a/tests/rpc/test_rpc_telegram.py +++ b/tests/rpc/test_rpc_telegram.py @@ -55,7 +55,7 @@ class DummyCls(Telegram): raise Exception('test') -def get_telegram_testobject(mocker, default_conf, mock=True, ftbot=None): +def get_telegram_testobject(mocker, default_conf, mock=True, freqtrade=None): msg_mock = MagicMock() if mock: mocker.patch.multiple( @@ -63,12 +63,12 @@ def get_telegram_testobject(mocker, default_conf, mock=True, ftbot=None): _init=MagicMock(), _send_msg=msg_mock ) - if not ftbot: - ftbot = get_patched_freqtradebot(mocker, default_conf) - rpc = RPC(ftbot) + if not freqtrade: + freqtrade = get_patched_freqtradebot(mocker, default_conf) + rpc = RPC(freqtrade) telegram = Telegram(rpc, default_conf) - return telegram, ftbot, msg_mock + return telegram, freqtrade, msg_mock def test_telegram__init__(default_conf, mocker) -> None: @@ -115,11 +115,11 @@ def test_authorized_only(default_conf, mocker, caplog, update) -> None: patch_exchange(mocker) caplog.set_level(logging.DEBUG) default_conf['telegram']['enabled'] = False - bot = FreqtradeBot(default_conf) - rpc = RPC(bot) + freqtrade = FreqtradeBot(default_conf) + rpc = RPC(freqtrade) dummy = DummyCls(rpc, default_conf) - patch_get_signal(bot, (True, False)) + patch_get_signal(freqtrade, (True, False, '')) dummy.dummy_handler(update=update, context=MagicMock()) assert dummy.state['called'] is True assert log_has('Executing handler: dummy_handler for chat_id: 0', caplog) @@ -135,11 +135,11 @@ def test_authorized_only_unauthorized(default_conf, mocker, caplog) -> None: update.message = Message(randint(1, 100), datetime.utcnow(), chat) default_conf['telegram']['enabled'] = False - bot = FreqtradeBot(default_conf) - rpc = RPC(bot) + freqtrade = FreqtradeBot(default_conf) + rpc = RPC(freqtrade) dummy = DummyCls(rpc, default_conf) - patch_get_signal(bot, (True, False)) + patch_get_signal(freqtrade, (True, False, '')) dummy.dummy_handler(update=update, context=MagicMock()) assert dummy.state['called'] is False assert not log_has('Executing handler: dummy_handler for chat_id: 3735928559', caplog) @@ -152,10 +152,10 @@ def test_authorized_only_exception(default_conf, mocker, caplog, update) -> None default_conf['telegram']['enabled'] = False - bot = FreqtradeBot(default_conf) - rpc = RPC(bot) + freqtrade = FreqtradeBot(default_conf) + rpc = RPC(freqtrade) dummy = DummyCls(rpc, default_conf) - patch_get_signal(bot, (True, False)) + patch_get_signal(freqtrade, (True, False, '')) dummy.dummy_exception(update=update, context=MagicMock()) assert dummy.state['called'] is False @@ -228,7 +228,7 @@ def test_status_handle(default_conf, update, ticker, fee, mocker) -> None: telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) freqtradebot.state = State.STOPPED # Status is also enabled when stopped @@ -285,7 +285,7 @@ def test_status_table_handle(default_conf, update, ticker, fee, mocker) -> None: telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) freqtradebot.state = State.STOPPED # Status table is also enabled when stopped @@ -329,7 +329,7 @@ def test_daily_handle(default_conf, update, ticker, limit_buy_order, fee, telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) # Create some test data freqtradebot.enter_positions() @@ -400,7 +400,7 @@ def test_daily_wrong_input(default_conf, update, ticker, mocker) -> None: ) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) # Try invalid data msg_mock.reset_mock() @@ -432,7 +432,7 @@ def test_profit_handle(default_conf, update, ticker, ticker_sell_up, fee, ) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) telegram._profit(update=update, context=MagicMock()) assert msg_mock.call_count == 1 @@ -486,7 +486,7 @@ def test_telegram_stats(default_conf, update, ticker, ticker_sell_up, fee, get_fee=fee, ) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) telegram._stats(update=update, context=MagicMock()) assert msg_mock.call_count == 1 @@ -512,7 +512,7 @@ def test_telegram_balance_handle(default_conf, update, mocker, rpc_balance, tick side_effect=lambda a, b: f"{a}/{b}") telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) telegram._balance(update=update, context=MagicMock()) result = msg_mock.call_args_list[0][0][0] @@ -532,7 +532,7 @@ def test_balance_handle_empty_response(default_conf, update, mocker) -> None: mocker.patch('freqtrade.exchange.Exchange.get_balances', return_value={}) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) freqtradebot.config['dry_run'] = False telegram._balance(update=update, context=MagicMock()) @@ -545,7 +545,7 @@ def test_balance_handle_empty_response_dry(default_conf, update, mocker) -> None mocker.patch('freqtrade.exchange.Exchange.get_balances', return_value={}) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) telegram._balance(update=update, context=MagicMock()) result = msg_mock.call_args_list[0][0][0] @@ -574,7 +574,7 @@ def test_balance_handle_too_large_response(default_conf, update, mocker) -> None }) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) telegram._balance(update=update, context=MagicMock()) assert msg_mock.call_count > 1 @@ -673,7 +673,7 @@ def test_telegram_forcesell_handle(default_conf, update, ticker, fee, freqtradebot = FreqtradeBot(default_conf) rpc = RPC(freqtradebot) telegram = Telegram(rpc, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) # Create some test data freqtradebot.enter_positions() @@ -732,7 +732,7 @@ def test_telegram_forcesell_down_handle(default_conf, update, ticker, fee, freqtradebot = FreqtradeBot(default_conf) rpc = RPC(freqtradebot) telegram = Telegram(rpc, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) # Create some test data freqtradebot.enter_positions() @@ -793,7 +793,7 @@ def test_forcesell_all_handle(default_conf, update, ticker, fee, mocker) -> None freqtradebot = FreqtradeBot(default_conf) rpc = RPC(freqtradebot) telegram = Telegram(rpc, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) # Create some test data freqtradebot.enter_positions() @@ -834,7 +834,7 @@ def test_forcesell_handle_invalid(default_conf, update, mocker) -> None: return_value=15000.0) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) # Trader is not running freqtradebot.state = State.STOPPED @@ -872,7 +872,7 @@ def test_forcebuy_handle(default_conf, update, mocker) -> None: mocker.patch('freqtrade.rpc.RPC._rpc_forcebuy', fbuy_mock) telegram, freqtradebot, _ = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) # /forcebuy ETH/BTC context = MagicMock() @@ -901,7 +901,7 @@ def test_forcebuy_handle_exception(default_conf, update, mocker) -> None: mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) update.message.text = '/forcebuy ETH/Nonepair' telegram._forcebuy(update=update, context=MagicMock()) @@ -918,7 +918,7 @@ def test_forcebuy_no_pair(default_conf, update, mocker) -> None: telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) context = MagicMock() context.args = [] @@ -946,7 +946,7 @@ def test_performance_handle(default_conf, update, ticker, fee, get_fee=fee, ) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) # Create some test data freqtradebot.enter_positions() @@ -974,7 +974,7 @@ def test_count_handle(default_conf, update, ticker, fee, mocker) -> None: get_fee=fee, ) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) freqtradebot.state = State.STOPPED telegram._count(update=update, context=MagicMock()) @@ -1003,7 +1003,7 @@ def test_telegram_lock_handle(default_conf, update, ticker, fee, mocker) -> None get_fee=fee, ) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) telegram._locks(update=update, context=MagicMock()) assert msg_mock.call_count == 1 assert 'No active locks.' in msg_mock.call_args_list[0][0][0] diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index cc89ce00c..583ec7ddd 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -1972,16 +1972,16 @@ def test_close_trade(default_conf, ticker, limit_buy_order, limit_buy_order_open def test_bot_loop_start_called_once(mocker, default_conf, caplog): - ftbot = get_patched_freqtradebot(mocker, default_conf) + freqtrade = get_patched_freqtradebot(mocker, default_conf) mocker.patch('freqtrade.freqtradebot.FreqtradeBot.create_trade') - patch_get_signal(ftbot) - ftbot.strategy.bot_loop_start = MagicMock(side_effect=ValueError) - ftbot.strategy.analyze = MagicMock() + patch_get_signal(freqtrade) + freqtrade.strategy.bot_loop_start = MagicMock(side_effect=ValueError) + freqtrade.strategy.analyze = MagicMock() - ftbot.process() + freqtrade.process() assert log_has_re(r'Strategy caused the following exception.*', caplog) - assert ftbot.strategy.bot_loop_start.call_count == 1 - assert ftbot.strategy.analyze.call_count == 1 + assert freqtrade.strategy.bot_loop_start.call_count == 1 + assert freqtrade.strategy.analyze.call_count == 1 def test_check_handle_timedout_buy_usercustom(default_conf, ticker, limit_buy_order_old, open_trade, @@ -4044,14 +4044,14 @@ def test_startup_trade_reinit(default_conf, edge_conf, mocker): reinit_mock = MagicMock() mocker.patch('freqtrade.persistence.Trade.stoploss_reinitialization', reinit_mock) - ftbot = get_patched_freqtradebot(mocker, default_conf) - ftbot.startup() + freqtrade = get_patched_freqtradebot(mocker, default_conf) + freqtrade.startup() assert reinit_mock.call_count == 1 reinit_mock.reset_mock() - ftbot = get_patched_freqtradebot(mocker, edge_conf) - ftbot.startup() + freqtrade = get_patched_freqtradebot(mocker, edge_conf) + freqtrade.startup() assert reinit_mock.call_count == 0 @@ -4070,17 +4070,17 @@ def test_sync_wallet_dry_run(mocker, default_conf, ticker, fee, limit_buy_order_ get_fee=fee, ) - bot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(bot) - assert bot.wallets.get_free('BTC') == 0.002 + freqtrade = get_patched_freqtradebot(mocker, default_conf) + patch_get_signal(freqtrade) + assert freqtrade.wallets.get_free('BTC') == 0.002 - n = bot.enter_positions() + n = freqtrade.enter_positions() assert n == 2 trades = Trade.query.all() assert len(trades) == 2 - bot.config['max_open_trades'] = 3 - n = bot.enter_positions() + freqtrade.config['max_open_trades'] = 3 + n = freqtrade.enter_positions() assert n == 0 assert log_has_re(r"Unable to create trade for XRP/BTC: " r"Available balance \(0.0 BTC\) is lower than stake amount \(0.001 BTC\)", From 3d8b2d601d04b301d76cfbc1f745c24753c0d6be Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Tue, 20 Jul 2021 20:23:47 +0700 Subject: [PATCH 07/33] fix test persistance --- tests/test_persistence.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_persistence.py b/tests/test_persistence.py index 89d07ca74..8e486d145 100644 --- a/tests/test_persistence.py +++ b/tests/test_persistence.py @@ -861,6 +861,7 @@ def test_to_json(default_conf, fee): open_date=arrow.utcnow().shift(hours=-2).datetime, open_rate=0.123, exchange='binance', + buy_signal_name='', open_order_id='dry_run_buy_12345' ) result = trade.to_json() @@ -910,6 +911,7 @@ def test_to_json(default_conf, fee): 'min_rate': None, 'max_rate': None, 'strategy': None, + 'buy_signal_name': '', 'timeframe': None, 'exchange': 'binance', } @@ -926,6 +928,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_signal_name='buys_signal_001', exchange='binance', ) result = trade.to_json() @@ -975,6 +978,7 @@ def test_to_json(default_conf, fee): 'sell_reason': None, 'sell_order_status': None, 'strategy': None, + 'buy_signal_name': 'buys_signal_001', 'timeframe': None, 'exchange': 'binance', } From c558fc0b17c10e1ca2eb7bf1020d3712b6a9ab9c Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Tue, 20 Jul 2021 20:40:32 +0700 Subject: [PATCH 08/33] fix feedback --- freqtrade/enums/signaltype.py | 2 +- freqtrade/persistence/migrations.py | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/freqtrade/enums/signaltype.py b/freqtrade/enums/signaltype.py index 6f29699f1..8529c6b79 100644 --- a/freqtrade/enums/signaltype.py +++ b/freqtrade/enums/signaltype.py @@ -11,6 +11,6 @@ class SignalType(Enum): class SignalNameType(Enum): """ - Enum for signal name + Enum for signal columns """ BUY_SIGNAL_NAME = "buy_signal_name" diff --git a/freqtrade/persistence/migrations.py b/freqtrade/persistence/migrations.py index 4fa1cb742..f6a345ed1 100644 --- a/freqtrade/persistence/migrations.py +++ b/freqtrade/persistence/migrations.py @@ -161,7 +161,14 @@ 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, 'open_trade_value') or not has_column(cols, 'buy_signal_name'): + if not has_column(cols, 'open_trade_value'): + 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! + inspector = inspect(engine) + cols = inspector.get_columns('trades') + + if not has_column(cols, 'buy_signal_name'): 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! From cbfedf8b29144325306952a3581bf56f8c912d4b Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Tue, 20 Jul 2021 23:25:00 +0700 Subject: [PATCH 09/33] fix backtest testcase --- freqtrade/data/btanalysis.py | 2 +- freqtrade/optimize/backtesting.py | 2 + tests/optimize/__init__.py | 3 +- tests/optimize/test_backtest_detail.py | 449 +++++++++++++------------ 4 files changed, 238 insertions(+), 218 deletions(-) diff --git a/freqtrade/data/btanalysis.py b/freqtrade/data/btanalysis.py index e7af5eab8..f6122c5aa 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', ] + 'stop_loss_ratio', 'min_rate', 'max_rate', 'is_open', 'buy_signal_name'] def get_latest_optimize_filename(directory: Union[Path, str], variant: str) -> str: diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 7fdc70e70..843d3331e 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -453,6 +453,8 @@ class Backtesting: row_index = indexes[pair] try: row = data[pair][row_index] + print('weeee') + print(row) except IndexError: # missing Data for one pair at the end. # Warnings for this are shown during data loading diff --git a/tests/optimize/__init__.py b/tests/optimize/__init__.py index ca91019e6..f77e6f70f 100644 --- a/tests/optimize/__init__.py +++ b/tests/optimize/__init__.py @@ -18,6 +18,7 @@ class BTrade(NamedTuple): sell_reason: SellType open_tick: int close_tick: int + buy_signal_name: Optional[str] = '' class BTContainer(NamedTuple): @@ -43,7 +44,7 @@ def _get_frame_time_from_offset(offset): def _build_backtest_dataframe(data): - columns = ['date', 'open', 'high', 'low', 'close', 'volume', 'buy', 'sell'] + columns = ['date', 'open', 'high', 'low', 'close', 'volume', 'buy', 'sell', 'buy_signal_name'] frame = DataFrame.from_records(data, columns=columns) frame['date'] = frame['date'].apply(_get_frame_time_from_offset) diff --git a/tests/optimize/test_backtest_detail.py b/tests/optimize/test_backtest_detail.py index 0bf197739..46c8e303d 100644 --- a/tests/optimize/test_backtest_detail.py +++ b/tests/optimize/test_backtest_detail.py @@ -14,13 +14,13 @@ from tests.optimize import (BTContainer, BTrade, _build_backtest_dataframe, # Test 0: Sell with signal sell in candle 3 # Test with Stop-loss at 1% tc0 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) - [2, 4987, 5012, 4986, 4600, 6172, 0, 0], # exit with stoploss hit - [3, 5010, 5000, 4980, 5010, 6172, 0, 1], - [4, 5010, 4987, 4977, 4995, 6172, 0, 0], - [5, 4995, 4995, 4995, 4950, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], # enter trade (signal on last candle) + [2, 4987, 5012, 4986, 4600, 6172, 0, 0, ''], # exit with stoploss hit + [3, 5010, 5000, 4980, 5010, 6172, 0, 1, ''], + [4, 5010, 4987, 4977, 4995, 6172, 0, 0, ''], + [5, 4995, 4995, 4995, 4950, 6172, 0, 0, '']], stop_loss=-0.01, roi={"0": 1}, profit_perc=0.002, use_sell_signal=True, trades=[BTrade(sell_reason=SellType.SELL_SIGNAL, open_tick=1, close_tick=4)] ) @@ -28,13 +28,13 @@ tc0 = BTContainer(data=[ # Test 1: Stop-Loss Triggered 1% loss # Test with Stop-loss at 1% tc1 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) - [2, 4987, 5012, 4600, 4600, 6172, 0, 0], # exit with stoploss hit - [3, 4975, 5000, 4980, 4977, 6172, 0, 0], - [4, 4977, 4987, 4977, 4995, 6172, 0, 0], - [5, 4995, 4995, 4995, 4950, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], # enter trade (signal on last candle) + [2, 4987, 5012, 4600, 4600, 6172, 0, 0, ''], # exit with stoploss hit + [3, 4975, 5000, 4980, 4977, 6172, 0, 0, ''], + [4, 4977, 4987, 4977, 4995, 6172, 0, 0, ''], + [5, 4995, 4995, 4995, 4950, 6172, 0, 0, '']], stop_loss=-0.01, roi={"0": 1}, profit_perc=-0.01, trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=2)] ) @@ -43,13 +43,13 @@ tc1 = BTContainer(data=[ # Test 2: Minus 4% Low, minus 1% close # Test with Stop-Loss at 3% tc2 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) - [2, 4987, 5012, 4962, 4975, 6172, 0, 0], - [3, 4975, 5000, 4800, 4962, 6172, 0, 0], # exit with stoploss hit - [4, 4962, 4987, 4937, 4950, 6172, 0, 0], - [5, 4950, 4975, 4925, 4950, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], # enter trade (signal on last candle) + [2, 4987, 5012, 4962, 4975, 6172, 0, 0, ''], + [3, 4975, 5000, 4800, 4962, 6172, 0, 0, ''], # exit with stoploss hit + [4, 4962, 4987, 4937, 4950, 6172, 0, 0, ''], + [5, 4950, 4975, 4925, 4950, 6172, 0, 0, '']], stop_loss=-0.03, roi={"0": 1}, profit_perc=-0.03, trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=3)] ) @@ -62,14 +62,14 @@ tc2 = BTContainer(data=[ # Trade-A: Stop-Loss Triggered 2% Loss # Trade-B: Stop-Loss Triggered 2% Loss tc3 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) - [2, 4987, 5012, 4800, 4975, 6172, 0, 0], # exit with stoploss hit - [3, 4975, 5000, 4950, 4962, 6172, 1, 0], - [4, 4975, 5000, 4950, 4962, 6172, 0, 0], # enter trade 2 (signal on last candle) - [5, 4962, 4987, 4000, 4000, 6172, 0, 0], # exit with stoploss hit - [6, 4950, 4975, 4975, 4950, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], # enter trade (signal on last candle) + [2, 4987, 5012, 4800, 4975, 6172, 0, 0, ''], # exit with stoploss hit + [3, 4975, 5000, 4950, 4962, 6172, 1, 0, ''], + [4, 4975, 5000, 4950, 4962, 6172, 0, 0, ''], # enter trade 2 (signal on last candle) + [5, 4962, 4987, 4000, 4000, 6172, 0, 0, ''], # exit with stoploss hit + [6, 4950, 4975, 4975, 4950, 6172, 0, 0, '']], stop_loss=-0.02, roi={"0": 1}, profit_perc=-0.04, trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=2), BTrade(sell_reason=SellType.STOP_LOSS, open_tick=4, close_tick=5)] @@ -80,13 +80,13 @@ tc3 = BTContainer(data=[ # Test with Stop-loss at 2% ROI 6% # Stop-Loss Triggered 2% Loss tc4 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) - [2, 4987, 5750, 4850, 5750, 6172, 0, 0], # Exit with stoploss hit - [3, 4975, 5000, 4950, 4962, 6172, 0, 0], - [4, 4962, 4987, 4937, 4950, 6172, 0, 0], - [5, 4950, 4975, 4925, 4950, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], # enter trade (signal on last candle) + [2, 4987, 5750, 4850, 5750, 6172, 0, 0, ''], # Exit with stoploss hit + [3, 4975, 5000, 4950, 4962, 6172, 0, 0, ''], + [4, 4962, 4987, 4937, 4950, 6172, 0, 0, ''], + [5, 4950, 4975, 4925, 4950, 6172, 0, 0, '']], stop_loss=-0.02, roi={"0": 0.06}, profit_perc=-0.02, trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=2)] ) @@ -94,13 +94,13 @@ tc4 = BTContainer(data=[ # Test 5: Drops 0.5% Closes +20%, ROI triggers 3% Gain # stop-loss: 1%, ROI: 3% tc5 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5025, 4980, 4987, 6172, 1, 0], - [1, 5000, 5025, 4980, 4987, 6172, 0, 0], # enter trade (signal on last candle) - [2, 4987, 5025, 4975, 4987, 6172, 0, 0], - [3, 4975, 6000, 4975, 6000, 6172, 0, 0], # ROI - [4, 4962, 4987, 4972, 4950, 6172, 0, 0], - [5, 4950, 4975, 4925, 4950, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5025, 4980, 4987, 6172, 1, 0, ''], + [1, 5000, 5025, 4980, 4987, 6172, 0, 0, ''], # enter trade (signal on last candle) + [2, 4987, 5025, 4975, 4987, 6172, 0, 0, ''], + [3, 4975, 6000, 4975, 6000, 6172, 0, 0, ''], # ROI + [4, 4962, 4987, 4972, 4950, 6172, 0, 0, ''], + [5, 4950, 4975, 4925, 4950, 6172, 0, 0, '']], stop_loss=-0.01, roi={"0": 0.03}, profit_perc=0.03, trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=3)] ) @@ -108,13 +108,13 @@ tc5 = BTContainer(data=[ # Test 6: Drops 3% / Recovers 6% Positive / Closes 1% positve, Stop-Loss triggers 2% Loss # stop-loss: 2% ROI: 5% tc6 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) - [2, 4987, 5300, 4850, 5050, 6172, 0, 0], # Exit with stoploss - [3, 4975, 5000, 4950, 4962, 6172, 0, 0], - [4, 4962, 4987, 4972, 4950, 6172, 0, 0], - [5, 4950, 4975, 4925, 4950, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], # enter trade (signal on last candle) + [2, 4987, 5300, 4850, 5050, 6172, 0, 0, ''], # Exit with stoploss + [3, 4975, 5000, 4950, 4962, 6172, 0, 0, ''], + [4, 4962, 4987, 4972, 4950, 6172, 0, 0, ''], + [5, 4950, 4975, 4925, 4950, 6172, 0, 0, '']], stop_loss=-0.02, roi={"0": 0.05}, profit_perc=-0.02, trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=2)] ) @@ -122,13 +122,13 @@ tc6 = BTContainer(data=[ # Test 7: 6% Positive / 1% Negative / Close 1% Positve, ROI Triggers 3% Gain # stop-loss: 2% ROI: 3% tc7 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0], - [2, 4987, 5300, 4950, 5050, 6172, 0, 0], - [3, 4975, 5000, 4950, 4962, 6172, 0, 0], - [4, 4962, 4987, 4972, 4950, 6172, 0, 0], - [5, 4950, 4975, 4925, 4950, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], + [2, 4987, 5300, 4950, 5050, 6172, 0, 0, ''], + [3, 4975, 5000, 4950, 4962, 6172, 0, 0, ''], + [4, 4962, 4987, 4972, 4950, 6172, 0, 0, ''], + [5, 4950, 4975, 4925, 4950, 6172, 0, 0, '']], stop_loss=-0.02, roi={"0": 0.03}, profit_perc=0.03, trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=2)] ) @@ -137,12 +137,12 @@ tc7 = BTContainer(data=[ # Test 8: trailing_stop should raise so candle 3 causes a stoploss. # stop-loss: 10%, ROI: 10% (should not apply), stoploss adjusted in candle 2 tc8 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5000, 5050, 4950, 5000, 6172, 0, 0], - [2, 5000, 5250, 4750, 4850, 6172, 0, 0], - [3, 4850, 5050, 4650, 4750, 6172, 0, 0], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], + [1, 5000, 5050, 4950, 5000, 6172, 0, 0, ''], + [2, 5000, 5250, 4750, 4850, 6172, 0, 0, ''], + [3, 4850, 5050, 4650, 4750, 6172, 0, 0, ''], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], stop_loss=-0.10, roi={"0": 0.10}, profit_perc=-0.055, trailing_stop=True, trades=[BTrade(sell_reason=SellType.TRAILING_STOP_LOSS, open_tick=1, close_tick=3)] ) @@ -151,12 +151,12 @@ tc8 = BTContainer(data=[ # Test 9: trailing_stop should raise - high and low in same candle. # stop-loss: 10%, ROI: 10% (should not apply), stoploss adjusted in candle 3 tc9 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5000, 5050, 4950, 5000, 6172, 0, 0], - [2, 5000, 5050, 4950, 5000, 6172, 0, 0], - [3, 5000, 5200, 4550, 4850, 6172, 0, 0], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], + [1, 5000, 5050, 4950, 5000, 6172, 0, 0, ''], + [2, 5000, 5050, 4950, 5000, 6172, 0, 0, ''], + [3, 5000, 5200, 4550, 4850, 6172, 0, 0, ''], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], stop_loss=-0.10, roi={"0": 0.10}, profit_perc=-0.064, trailing_stop=True, trades=[BTrade(sell_reason=SellType.TRAILING_STOP_LOSS, open_tick=1, close_tick=3)] ) @@ -165,12 +165,12 @@ tc9 = BTContainer(data=[ # without applying trailing_stop_positive since stoploss_offset is at 10%. # stop-loss: 10%, ROI: 10% (should not apply), stoploss adjusted candle 2 tc10 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5000, 5050, 4950, 5100, 6172, 0, 0], - [2, 5100, 5251, 5100, 5100, 6172, 0, 0], - [3, 4850, 5050, 4650, 4750, 6172, 0, 0], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], + [1, 5000, 5050, 4950, 5100, 6172, 0, 0, ''], + [2, 5100, 5251, 5100, 5100, 6172, 0, 0, ''], + [3, 4850, 5050, 4650, 4750, 6172, 0, 0, ''], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], stop_loss=-0.10, roi={"0": 0.10}, profit_perc=-0.1, trailing_stop=True, trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.10, trailing_stop_positive=0.03, @@ -181,12 +181,12 @@ tc10 = BTContainer(data=[ # applying a positive trailing stop of 3% since stop_positive_offset is reached. # stop-loss: 10%, ROI: 10% (should not apply), stoploss adjusted candle 2 tc11 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5000, 5050, 4950, 5100, 6172, 0, 0], - [2, 5100, 5251, 5100, 5100, 6172, 0, 0], - [3, 5000, 5150, 4650, 4750, 6172, 0, 0], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], + [1, 5000, 5050, 4950, 5100, 6172, 0, 0, ''], + [2, 5100, 5251, 5100, 5100, 6172, 0, 0, ''], + [3, 5000, 5150, 4650, 4750, 6172, 0, 0, ''], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], stop_loss=-0.10, roi={"0": 0.10}, profit_perc=0.019, trailing_stop=True, trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.05, trailing_stop_positive=0.03, @@ -197,12 +197,12 @@ tc11 = BTContainer(data=[ # applying a positive trailing stop of 3% since stop_positive_offset is reached. # stop-loss: 10%, ROI: 10% (should not apply), stoploss adjusted candle 2 tc12 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5000, 5050, 4950, 5100, 6172, 0, 0], - [2, 5100, 5251, 4650, 5100, 6172, 0, 0], - [3, 4850, 5050, 4650, 4750, 6172, 0, 0], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], + [1, 5000, 5050, 4950, 5100, 6172, 0, 0, ''], + [2, 5100, 5251, 4650, 5100, 6172, 0, 0, ''], + [3, 4850, 5050, 4650, 4750, 6172, 0, 0, ''], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], stop_loss=-0.10, roi={"0": 0.10}, profit_perc=0.019, trailing_stop=True, trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.05, trailing_stop_positive=0.03, @@ -212,12 +212,12 @@ tc12 = BTContainer(data=[ # Test 13: Buy and sell ROI on same candle # stop-loss: 10% (should not apply), ROI: 1% tc13 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5000, 5100, 4950, 5100, 6172, 0, 0], - [2, 5100, 5251, 4850, 5100, 6172, 0, 0], - [3, 4850, 5050, 4850, 4750, 6172, 0, 0], - [4, 4750, 4950, 4850, 4750, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], + [1, 5000, 5100, 4950, 5100, 6172, 0, 0, ''], + [2, 5100, 5251, 4850, 5100, 6172, 0, 0, ''], + [3, 4850, 5050, 4850, 4750, 6172, 0, 0, ''], + [4, 4750, 4950, 4850, 4750, 6172, 0, 0, '']], stop_loss=-0.10, roi={"0": 0.01}, profit_perc=0.01, trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=1)] ) @@ -225,12 +225,12 @@ tc13 = BTContainer(data=[ # Test 14 - Buy and Stoploss on same candle # stop-loss: 5%, ROI: 10% (should not apply) tc14 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5000, 5100, 4600, 5100, 6172, 0, 0], - [2, 5100, 5251, 4850, 5100, 6172, 0, 0], - [3, 4850, 5050, 4850, 4750, 6172, 0, 0], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], + [1, 5000, 5100, 4600, 5100, 6172, 0, 0, ''], + [2, 5100, 5251, 4850, 5100, 6172, 0, 0, ''], + [3, 4850, 5050, 4850, 4750, 6172, 0, 0, ''], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], stop_loss=-0.05, roi={"0": 0.10}, profit_perc=-0.05, trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=1)] ) @@ -239,12 +239,12 @@ tc14 = BTContainer(data=[ # Test 15 - Buy and ROI on same candle, followed by buy and Stoploss on next candle # stop-loss: 5%, ROI: 10% (should not apply) tc15 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5000, 5100, 4900, 5100, 6172, 1, 0], - [2, 5100, 5251, 4650, 5100, 6172, 0, 0], - [3, 4850, 5050, 4850, 4750, 6172, 0, 0], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], + [1, 5000, 5100, 4900, 5100, 6172, 1, 0, ''], + [2, 5100, 5251, 4650, 5100, 6172, 0, 0, ''], + [3, 4850, 5050, 4850, 4750, 6172, 0, 0, ''], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], stop_loss=-0.05, roi={"0": 0.01}, profit_perc=-0.04, trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=1), BTrade(sell_reason=SellType.STOP_LOSS, open_tick=2, close_tick=2)] @@ -254,13 +254,13 @@ tc15 = BTContainer(data=[ # Causes negative profit even though sell-reason is ROI. # stop-loss: 10%, ROI: 10% (should not apply), -100% after 65 minutes (limits trade duration) tc16 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0], - [2, 4987, 5300, 4950, 5050, 6172, 0, 0], - [3, 4975, 5000, 4940, 4962, 6172, 0, 0], # ForceSell on ROI (roi=-1) - [4, 4962, 4987, 4972, 4950, 6172, 0, 0], - [5, 4950, 4975, 4925, 4950, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], + [2, 4987, 5300, 4950, 5050, 6172, 0, 0, ''], + [3, 4975, 5000, 4940, 4962, 6172, 0, 0, ''], # ForceSell on ROI (roi=-1) + [4, 4962, 4987, 4972, 4950, 6172, 0, 0, ''], + [5, 4950, 4975, 4925, 4950, 6172, 0, 0, '']], stop_loss=-0.10, roi={"0": 0.10, "65": -1}, profit_perc=-0.012, trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=3)] ) @@ -270,13 +270,13 @@ tc16 = BTContainer(data=[ # stop-loss: 10%, ROI: 10% (should not apply), -100% after 100 minutes (limits trade duration) # Uses open as sell-rate (special case) - since the roi-time is a multiple of the timeframe. tc17 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0], - [2, 4987, 5300, 4950, 5050, 6172, 0, 0], - [3, 4980, 5000, 4940, 4962, 6172, 0, 0], # ForceSell on ROI (roi=-1) - [4, 4962, 4987, 4972, 4950, 6172, 0, 0], - [5, 4950, 4975, 4925, 4950, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], + [2, 4987, 5300, 4950, 5050, 6172, 0, 0, ''], + [3, 4980, 5000, 4940, 4962, 6172, 0, 0, ''], # ForceSell on ROI (roi=-1) + [4, 4962, 4987, 4972, 4950, 6172, 0, 0, ''], + [5, 4950, 4975, 4925, 4950, 6172, 0, 0, '']], stop_loss=-0.10, roi={"0": 0.10, "120": -1}, profit_perc=-0.004, trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=3)] ) @@ -286,13 +286,13 @@ tc17 = BTContainer(data=[ # stop-loss: 10%, ROI: 10% (should not apply), -100% after 100 minutes (limits trade duration) # uses open_rate as sell-price tc18 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0], - [2, 4987, 5300, 4950, 5200, 6172, 0, 0], - [3, 5200, 5220, 4940, 4962, 6172, 0, 0], # Sell on ROI (sells on open) - [4, 4962, 4987, 4972, 4950, 6172, 0, 0], - [5, 4950, 4975, 4925, 4950, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], + [2, 4987, 5300, 4950, 5200, 6172, 0, 0, ''], + [3, 5200, 5220, 4940, 4962, 6172, 0, 0, ''], # Sell on ROI (sells on open) + [4, 4962, 4987, 4972, 4950, 6172, 0, 0, ''], + [5, 4950, 4975, 4925, 4950, 6172, 0, 0, '']], stop_loss=-0.10, roi={"0": 0.10, "120": 0.01}, profit_perc=0.04, trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=3)] ) @@ -301,13 +301,13 @@ tc18 = BTContainer(data=[ # stop-loss: 10%, ROI: 10% (should not apply), -100% after 100 minutes (limits trade duration) # uses calculated ROI (1%) as sell rate, otherwise identical to tc18 tc19 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0], - [2, 4987, 5300, 4950, 5200, 6172, 0, 0], - [3, 5000, 5300, 4940, 4962, 6172, 0, 0], # Sell on ROI - [4, 4962, 4987, 4972, 4950, 6172, 0, 0], - [5, 4550, 4975, 4925, 4950, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], + [2, 4987, 5300, 4950, 5200, 6172, 0, 0, ''], + [3, 5000, 5300, 4940, 4962, 6172, 0, 0, ''], # Sell on ROI + [4, 4962, 4987, 4972, 4950, 6172, 0, 0, ''], + [5, 4550, 4975, 4925, 4950, 6172, 0, 0, '']], stop_loss=-0.10, roi={"0": 0.10, "120": 0.01}, profit_perc=0.01, trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=3)] ) @@ -316,13 +316,13 @@ tc19 = BTContainer(data=[ # stop-loss: 10%, ROI: 10% (should not apply), -100% after 100 minutes (limits trade duration) # uses calculated ROI (1%) as sell rate, otherwise identical to tc18 tc20 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0], - [2, 4987, 5300, 4950, 5200, 6172, 0, 0], - [3, 5200, 5300, 4940, 4962, 6172, 0, 0], # Sell on ROI - [4, 4962, 4987, 4972, 4950, 6172, 0, 0], - [5, 4550, 4975, 4925, 4950, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], + [2, 4987, 5300, 4950, 5200, 6172, 0, 0, ''], + [3, 5200, 5300, 4940, 4962, 6172, 0, 0, ''], # Sell on ROI + [4, 4962, 4987, 4972, 4950, 6172, 0, 0, ''], + [5, 4550, 4975, 4925, 4950, 6172, 0, 0, '']], stop_loss=-0.10, roi={"0": 0.10, "119": 0.01}, profit_perc=0.01, trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=3)] ) @@ -332,12 +332,12 @@ tc20 = BTContainer(data=[ # which cannot happen in reality # stop-loss: 10%, ROI: 4%, Trailing stop adjusted at the sell candle tc21 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5000, 5050, 4950, 5100, 6172, 0, 0], - [2, 5100, 5251, 4650, 5100, 6172, 0, 0], - [3, 4850, 5050, 4650, 4750, 6172, 0, 0], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], + [1, 5000, 5050, 4950, 5100, 6172, 0, 0, ''], + [2, 5100, 5251, 4650, 5100, 6172, 0, 0, ''], + [3, 4850, 5050, 4650, 4750, 6172, 0, 0, ''], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], stop_loss=-0.10, roi={"0": 0.04}, profit_perc=0.04, trailing_stop=True, trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.05, trailing_stop_positive=0.03, @@ -348,12 +348,12 @@ tc21 = BTContainer(data=[ # applying a positive trailing stop of 3% - ROI should apply before trailing stop. # stop-loss: 10%, ROI: 4%, stoploss adjusted candle 2 tc22 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5000, 5050, 4950, 5100, 6172, 0, 0], - [2, 5100, 5251, 5100, 5100, 6172, 0, 0], - [3, 4850, 5050, 4650, 4750, 6172, 0, 0], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], + [1, 5000, 5050, 4950, 5100, 6172, 0, 0, ''], + [2, 5100, 5251, 5100, 5100, 6172, 0, 0, ''], + [3, 4850, 5050, 4650, 4750, 6172, 0, 0, ''], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], stop_loss=-0.10, roi={"0": 0.04}, profit_perc=0.04, trailing_stop=True, trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.05, trailing_stop_positive=0.03, @@ -367,12 +367,12 @@ tc22 = BTContainer(data=[ # Stoploss would trigger in this candle too, but it's no longer relevant. # stop-loss: 10%, ROI: 4%, stoploss adjusted candle 2, ROI adjusted in candle 3 (causing the sell) tc23 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5000, 5050, 4950, 5100, 6172, 0, 0], - [2, 5100, 5251, 5100, 5100, 6172, 0, 0], - [3, 4850, 5251, 4650, 4750, 6172, 0, 0], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], + [1, 5000, 5050, 4950, 5100, 6172, 0, 0, ''], + [2, 5100, 5251, 5100, 5100, 6172, 0, 0, ''], + [3, 4850, 5251, 4650, 4750, 6172, 0, 0, ''], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], stop_loss=-0.10, roi={"0": 0.1, "119": 0.03}, profit_perc=0.03, trailing_stop=True, trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.05, trailing_stop_positive=0.03, @@ -383,13 +383,13 @@ tc23 = BTContainer(data=[ # Stoploss at 1%. # Stoploss wins over Sell-signal (because sell-signal is acted on in the next candle) tc24 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) - [2, 4987, 5012, 4986, 4600, 6172, 0, 0], - [3, 5010, 5000, 4855, 5010, 6172, 0, 1], # Triggers stoploss + sellsignal - [4, 5010, 4987, 4977, 4995, 6172, 0, 0], - [5, 4995, 4995, 4995, 4950, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], # enter trade (signal on last candle) + [2, 4987, 5012, 4986, 4600, 6172, 0, 0, ''], + [3, 5010, 5000, 4855, 5010, 6172, 0, 1, ''], # Triggers stoploss + sellsignal + [4, 5010, 4987, 4977, 4995, 6172, 0, 0, ''], + [5, 4995, 4995, 4995, 4950, 6172, 0, 0, '']], stop_loss=-0.01, roi={"0": 1}, profit_perc=-0.01, use_sell_signal=True, trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=3)] ) @@ -398,13 +398,13 @@ tc24 = BTContainer(data=[ # Stoploss at 1%. # Sell-signal wins over stoploss tc25 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) - [2, 4987, 5012, 4986, 4600, 6172, 0, 0], - [3, 5010, 5000, 4986, 5010, 6172, 0, 1], - [4, 5010, 4987, 4855, 4995, 6172, 0, 0], # Triggers stoploss + sellsignal acted on - [5, 4995, 4995, 4995, 4950, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], # enter trade (signal on last candle) + [2, 4987, 5012, 4986, 4600, 6172, 0, 0, ''], + [3, 5010, 5000, 4986, 5010, 6172, 0, 1, ''], + [4, 5010, 4987, 4855, 4995, 6172, 0, 0, ''], # Triggers stoploss + sellsignal acted on + [5, 4995, 4995, 4995, 4950, 6172, 0, 0, '']], stop_loss=-0.01, roi={"0": 1}, profit_perc=0.002, use_sell_signal=True, trades=[BTrade(sell_reason=SellType.SELL_SIGNAL, open_tick=1, close_tick=4)] ) @@ -413,13 +413,13 @@ tc25 = BTContainer(data=[ # Stoploss at 10% (irrelevant), ROI at 5% (will trigger) # Sell-signal wins over stoploss tc26 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) - [2, 4987, 5012, 4986, 4600, 6172, 0, 0], - [3, 5010, 5251, 4986, 5010, 6172, 0, 1], # Triggers ROI, sell-signal - [4, 5010, 4987, 4855, 4995, 6172, 0, 0], - [5, 4995, 4995, 4995, 4950, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], # enter trade (signal on last candle) + [2, 4987, 5012, 4986, 4600, 6172, 0, 0, ''], + [3, 5010, 5251, 4986, 5010, 6172, 0, 1, ''], # Triggers ROI, sell-signal + [4, 5010, 4987, 4855, 4995, 6172, 0, 0, ''], + [5, 4995, 4995, 4995, 4950, 6172, 0, 0, '']], stop_loss=-0.10, roi={"0": 0.05}, profit_perc=0.05, use_sell_signal=True, trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=3)] ) @@ -429,13 +429,13 @@ tc26 = BTContainer(data=[ # TODO: figure out if sell-signal should win over ROI # Sell-signal wins over stoploss tc27 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) - [2, 4987, 5012, 4986, 4600, 6172, 0, 0], - [3, 5010, 5012, 4986, 5010, 6172, 0, 1], # sell-signal - [4, 5010, 5251, 4855, 4995, 6172, 0, 0], # Triggers ROI, sell-signal acted on - [5, 4995, 4995, 4995, 4950, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], # enter trade (signal on last candle) + [2, 4987, 5012, 4986, 4600, 6172, 0, 0, ''], + [3, 5010, 5012, 4986, 5010, 6172, 0, 1, ''], # sell-signal + [4, 5010, 5251, 4855, 4995, 6172, 0, 0, ''], # Triggers ROI, sell-signal acted on + [5, 4995, 4995, 4995, 4950, 6172, 0, 0, '']], stop_loss=-0.10, roi={"0": 0.05}, profit_perc=0.05, use_sell_signal=True, trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=4)] ) @@ -445,12 +445,12 @@ tc27 = BTContainer(data=[ # therefore "open" will be used # stop-loss: 10%, ROI: 10% (should not apply), stoploss adjusted candle 2 tc28 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5000, 5050, 4950, 5100, 6172, 0, 0], - [2, 5100, 5251, 5100, 5100, 6172, 0, 0], - [3, 4850, 5050, 4650, 4750, 6172, 0, 0], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], + [1, 5000, 5050, 4950, 5100, 6172, 0, 0, ''], + [2, 5100, 5251, 5100, 5100, 6172, 0, 0, ''], + [3, 4850, 5050, 4650, 4750, 6172, 0, 0, ''], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], stop_loss=-0.10, roi={"0": 0.10}, profit_perc=-0.03, trailing_stop=True, trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.05, trailing_stop_positive=0.03, @@ -461,12 +461,12 @@ tc28 = BTContainer(data=[ # high of stoploss candle. # stop-loss: 10%, ROI: 10% (should not apply) tc29 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5000, 5050, 5000, 4900, 6172, 0, 0], # enter trade (signal on last candle) - [2, 4900, 5250, 4500, 5100, 6172, 0, 0], # Triggers trailing-stoploss - [3, 5100, 5100, 4650, 4750, 6172, 0, 0], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], + [1, 5000, 5050, 5000, 4900, 6172, 0, 0, ''], # enter trade (signal on last candle) + [2, 4900, 5250, 4500, 5100, 6172, 0, 0, ''], # Triggers trailing-stoploss + [3, 5100, 5100, 4650, 4750, 6172, 0, 0, ''], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], stop_loss=-0.10, roi={"0": 0.10}, profit_perc=-0.02, trailing_stop=True, trailing_stop_positive=0.03, trades=[BTrade(sell_reason=SellType.TRAILING_STOP_LOSS, open_tick=1, close_tick=2)] @@ -475,12 +475,12 @@ tc29 = BTContainer(data=[ # Test 30: trailing_stop should be triggered immediately on trade open candle. # stop-loss: 10%, ROI: 10% (should not apply) tc30 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5000, 5500, 5000, 4900, 6172, 0, 0], # enter trade (signal on last candle) and stop - [2, 4900, 5250, 4500, 5100, 6172, 0, 0], - [3, 5100, 5100, 4650, 4750, 6172, 0, 0], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], + [1, 5000, 5500, 5000, 4900, 6172, 0, 0, ''], # enter trade (signal on last candle) and stop + [2, 4900, 5250, 4500, 5100, 6172, 0, 0, ''], + [3, 5100, 5100, 4650, 4750, 6172, 0, 0, ''], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], stop_loss=-0.10, roi={"0": 0.10}, profit_perc=-0.01, trailing_stop=True, trailing_stop_positive=0.01, trades=[BTrade(sell_reason=SellType.TRAILING_STOP_LOSS, open_tick=1, close_tick=1)] @@ -489,12 +489,12 @@ tc30 = BTContainer(data=[ # Test 31: trailing_stop should be triggered immediately on trade open candle. # stop-loss: 10%, ROI: 10% (should not apply) tc31 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5000, 5500, 5000, 4900, 6172, 0, 0], # enter trade (signal on last candle) and stop - [2, 4900, 5250, 4500, 5100, 6172, 0, 0], - [3, 5100, 5100, 4650, 4750, 6172, 0, 0], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], + [1, 5000, 5500, 5000, 4900, 6172, 0, 0, ''], # enter trade (signal on last candle) and stop + [2, 4900, 5250, 4500, 5100, 6172, 0, 0, ''], + [3, 5100, 5100, 4650, 4750, 6172, 0, 0, ''], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], stop_loss=-0.10, roi={"0": 0.10}, profit_perc=0.01, trailing_stop=True, trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.02, trailing_stop_positive=0.01, @@ -504,18 +504,33 @@ tc31 = BTContainer(data=[ # Test 32: trailing_stop should be triggered immediately on trade open candle. # stop-loss: 1%, ROI: 10% (should not apply) tc32 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5000, 5500, 5000, 4900, 6172, 0, 0], # enter trade (signal on last candle) and stop - [2, 4900, 5250, 4500, 5100, 6172, 0, 0], - [3, 5100, 5100, 4650, 4750, 6172, 0, 0], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], + [1, 5000, 5500, 5000, 4900, 6172, 0, 0, ''], # enter trade (signal on last candle) and stop + [2, 4900, 5250, 4500, 5100, 6172, 0, 0, ''], + [3, 5100, 5100, 4650, 4750, 6172, 0, 0, ''], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], stop_loss=-0.01, roi={"0": 0.10}, profit_perc=-0.01, trailing_stop=True, trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.02, trailing_stop_positive=0.01, use_custom_stoploss=True, trades=[BTrade(sell_reason=SellType.TRAILING_STOP_LOSS, open_tick=1, close_tick=1)] ) +# Test 33: trailing_stop should be triggered immediately on trade open candle. +# stop-loss: 1%, ROI: 10% (should not apply) +tc33 = BTContainer(data=[ + # D O H L C V B S SN + [0, 5000, 5050, 4950, 5000, 6172, 1, 0, 'buy_signal_01'], + [1, 5000, 5500, 5000, 4900, 6172, 0, 0, ''], # enter trade (signal on last candle) and stop + [2, 4900, 5250, 4500, 5100, 6172, 0, 0, ''], + [3, 5100, 5100, 4650, 4750, 6172, 0, 0, ''], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], + stop_loss=-0.01, roi={"0": 0.10}, profit_perc=-0.01, trailing_stop=True, + trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.02, + trailing_stop_positive=0.01, use_custom_stoploss=True, + trades=[BTrade(sell_reason=SellType.TRAILING_STOP_LOSS, open_tick=1, close_tick=1, buy_signal_name='buy_signal_01')] +) + TESTS = [ tc0, tc1, @@ -550,6 +565,7 @@ TESTS = [ tc30, tc31, tc32, + tc33, ] @@ -598,5 +614,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_signal_name == trade.buy_signal_name assert res.open_date == _get_frame_time_from_offset(trade.open_tick) assert res.close_date == _get_frame_time_from_offset(trade.close_tick) From 5d04d6ffa72812efbc74aada0471c5cc59a388df Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Tue, 20 Jul 2021 23:40:32 +0700 Subject: [PATCH 10/33] fix edge testcase --- freqtrade/optimize/backtesting.py | 2 -- tests/edge/test_edge.py | 47 +++++++++++++++--------------- tests/optimize/test_backtesting.py | 1 + 3 files changed, 24 insertions(+), 26 deletions(-) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 843d3331e..7fdc70e70 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -453,8 +453,6 @@ class Backtesting: row_index = indexes[pair] try: row = data[pair][row_index] - print('weeee') - print(row) except IndexError: # missing Data for one pair at the end. # Warnings for this are shown during data loading diff --git a/tests/edge/test_edge.py b/tests/edge/test_edge.py index 0655b3a0f..254134ce7 100644 --- a/tests/edge/test_edge.py +++ b/tests/edge/test_edge.py @@ -29,7 +29,6 @@ from tests.optimize import (BTContainer, BTrade, _build_backtest_dataframe, tests_start_time = arrow.get(2018, 10, 3) timeframe_in_minute = 60 -_ohlc = {'date': 0, 'buy': 1, 'open': 2, 'high': 3, 'low': 4, 'close': 5, 'sell': 6, 'volume': 7} # Helpers for this test file @@ -77,23 +76,23 @@ def _time_on_candle(number): # End helper functions # Open trade should be removed from the end tc0 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4975, 4987, 6172, 0, 1]], # enter trade (signal on last candle) + # D O H L C V B S SN + [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], + [1, 5000, 5025, 4975, 4987, 6172, 0, 1, '']], # enter trade (signal on last candle) stop_loss=-0.99, roi={"0": float('inf')}, profit_perc=0.00, trades=[] ) # Two complete trades within dataframe(with sell hit for all) tc1 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4975, 4987, 6172, 0, 1], # enter trade (signal on last candle) - [2, 5000, 5025, 4975, 4987, 6172, 0, 0], # exit at open - [3, 5000, 5025, 4975, 4987, 6172, 1, 0], # no action - [4, 5000, 5025, 4975, 4987, 6172, 0, 0], # should enter the trade - [5, 5000, 5025, 4975, 4987, 6172, 0, 1], # no action - [6, 5000, 5025, 4975, 4987, 6172, 0, 0], # should sell + # D O H L C V B S SN + [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], + [1, 5000, 5025, 4975, 4987, 6172, 0, 1, ''], # enter trade (signal on last candle) + [2, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], # exit at open + [3, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], # no action + [4, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], # should enter the trade + [5, 5000, 5025, 4975, 4987, 6172, 0, 1, ''], # no action + [6, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], # should sell ], stop_loss=-0.99, roi={"0": float('inf')}, profit_perc=0.00, trades=[BTrade(sell_reason=SellType.SELL_SIGNAL, open_tick=1, close_tick=2), @@ -102,10 +101,10 @@ tc1 = BTContainer(data=[ # 3) Entered, sl 1%, candle drops 8% => Trade closed, 1% loss tc2 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4600, 4987, 6172, 0, 0], # enter trade, stoploss hit - [2, 5000, 5025, 4975, 4987, 6172, 0, 0], + # D O H L C V B S SN + [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], + [1, 5000, 5025, 4600, 4987, 6172, 0, 0, ''], # enter trade, stoploss hit + [2, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], ], stop_loss=-0.01, roi={"0": float('inf')}, profit_perc=-0.01, trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=1)] @@ -113,10 +112,10 @@ tc2 = BTContainer(data=[ # 4) Entered, sl 3 %, candle drops 4%, recovers to 1 % = > Trade closed, 3 % loss tc3 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4800, 4987, 6172, 0, 0], # enter trade, stoploss hit - [2, 5000, 5025, 4975, 4987, 6172, 0, 0], + # D O H L C V B S SN + [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], + [1, 5000, 5025, 4800, 4987, 6172, 0, 0, ''], # enter trade, stoploss hit + [2, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], ], stop_loss=-0.03, roi={"0": float('inf')}, profit_perc=-0.03, trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=1)] @@ -124,10 +123,10 @@ tc3 = BTContainer(data=[ # 5) Stoploss and sell are hit. should sell on stoploss tc4 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4800, 4987, 6172, 0, 1], # enter trade, stoploss hit, sell signal - [2, 5000, 5025, 4975, 4987, 6172, 0, 0], + # D O H L C V B S SN + [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], + [1, 5000, 5025, 4800, 4987, 6172, 0, 1, ''], # enter trade, stoploss hit, sell signal + [2, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], ], stop_loss=-0.03, roi={"0": float('inf')}, profit_perc=-0.03, trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=1)] diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py index 655464344..64795d064 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -584,6 +584,7 @@ def test_backtest_one(default_conf, fee, mocker, testdatadir) -> None: 'min_rate': [0.1038, 0.10302485], 'max_rate': [0.10501, 0.1038888], 'is_open': [False, False], + 'buy_signal_name': ['', ''], }) pd.testing.assert_frame_equal(results, expected) data_pair = processed[pair] From 66a7070170b919ad0d020e55282ae0cb0873fdef Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Tue, 20 Jul 2021 23:56:03 +0700 Subject: [PATCH 11/33] run linter --- freqtrade/enums/__init__.py | 2 +- freqtrade/freqtradebot.py | 12 ++++++++++-- freqtrade/strategy/interface.py | 2 +- tests/optimize/test_backtest_detail.py | 7 ++++++- tests/rpc/test_rpc_apiserver.py | 6 ++++-- tests/strategy/test_interface.py | 12 ++++++++++-- tests/test_freqtradebot.py | 5 ++++- 7 files changed, 36 insertions(+), 10 deletions(-) diff --git a/freqtrade/enums/__init__.py b/freqtrade/enums/__init__.py index 60f984c33..71e9d7d9e 100644 --- a/freqtrade/enums/__init__.py +++ b/freqtrade/enums/__init__.py @@ -3,5 +3,5 @@ from freqtrade.enums.backteststate import BacktestState from freqtrade.enums.rpcmessagetype import RPCMessageType from freqtrade.enums.runmode import NON_UTIL_MODES, OPTIMIZE_MODES, TRADING_MODES, RunMode from freqtrade.enums.selltype import SellType -from freqtrade.enums.signaltype import SignalType, SignalNameType +from freqtrade.enums.signaltype import SignalNameType, SignalType from freqtrade.enums.state import State diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 8d3b24b10..95c769730 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -420,7 +420,11 @@ class FreqtradeBot(LoggingMixin): return False # running get_signal on historical data fetched - (buy, sell, buy_signal_name) = self.strategy.get_signal(pair, self.strategy.timeframe, analyzed_df) + (buy, sell, buy_signal_name) = self.strategy.get_signal( + pair, + self.strategy.timeframe, + analyzed_df + ) if buy and not sell: stake_amount = self.wallets.get_trade_stake_amount(pair, self.edge) @@ -693,7 +697,11 @@ class FreqtradeBot(LoggingMixin): analyzed_df, _ = self.dataprovider.get_analyzed_dataframe(trade.pair, self.strategy.timeframe) - (buy, sell, _) = self.strategy.get_signal(trade.pair, self.strategy.timeframe, analyzed_df) + (buy, sell, _) = self.strategy.get_signal( + trade.pair, + self.strategy.timeframe, + analyzed_df + ) logger.debug('checking sell') sell_rate = self.exchange.get_rate(trade.pair, refresh=True, side="sell") diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 7158ad8e8..8ee0cea59 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -13,7 +13,7 @@ from pandas import DataFrame from freqtrade.constants import ListPairsWithTimeframes from freqtrade.data.dataprovider import DataProvider -from freqtrade.enums import SellType, SignalType, SignalNameType +from freqtrade.enums import SellType, SignalNameType, SignalType from freqtrade.exceptions import OperationalException, StrategyError from freqtrade.exchange import timeframe_to_minutes, timeframe_to_seconds from freqtrade.exchange.exchange import timeframe_to_next_date diff --git a/tests/optimize/test_backtest_detail.py b/tests/optimize/test_backtest_detail.py index 46c8e303d..d1bf28d58 100644 --- a/tests/optimize/test_backtest_detail.py +++ b/tests/optimize/test_backtest_detail.py @@ -528,7 +528,12 @@ tc33 = BTContainer(data=[ stop_loss=-0.01, roi={"0": 0.10}, profit_perc=-0.01, trailing_stop=True, trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.02, trailing_stop_positive=0.01, use_custom_stoploss=True, - trades=[BTrade(sell_reason=SellType.TRAILING_STOP_LOSS, open_tick=1, close_tick=1, buy_signal_name='buy_signal_01')] + trades=[BTrade( + sell_reason=SellType.TRAILING_STOP_LOSS, + open_tick=1, + close_tick=1, + buy_signal_name='buy_signal_01' + )] ) TESTS = [ diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index afd273998..74d40b611 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -1185,8 +1185,10 @@ def test_api_plot_config(botclient): assert_response(rc) assert rc.json() == {} - freqtrade.strategy.plot_config = {'main_plot': {'sma': {}}, - 'subplots': {'RSI': {'rsi': {'color': 'red'}}}} + freqtrade.strategy.plot_config = { + 'main_plot': {'sma': {}}, + 'subplots': {'RSI': {'rsi': {'color': 'red'}}} + } rc = client_get(client, f"{BASE_URI}/plot_config") assert_response(rc) assert rc.json() == freqtrade.strategy.plot_config diff --git a/tests/strategy/test_interface.py b/tests/strategy/test_interface.py index aec07266d..3678cee4a 100644 --- a/tests/strategy/test_interface.py +++ b/tests/strategy/test_interface.py @@ -76,7 +76,11 @@ def test_get_signal_empty(default_conf, mocker, caplog): assert log_has('Empty candle (OHLCV) data for pair bar', caplog) caplog.clear() - assert (False, False, '') == _STRATEGY.get_signal('baz', default_conf['timeframe'], DataFrame([])) + assert (False, False, '') == _STRATEGY.get_signal( + 'baz', + default_conf['timeframe'], + DataFrame([]) + ) assert log_has('Empty candle (OHLCV) data for pair baz', caplog) @@ -112,7 +116,11 @@ def test_get_signal_old_dataframe(default_conf, mocker, caplog, ohlcv_history): caplog.set_level(logging.INFO) mocker.patch.object(_STRATEGY, 'assert_df') - assert (False, False, '') == _STRATEGY.get_signal('xyz', default_conf['timeframe'], mocked_history) + assert (False, False, '') == _STRATEGY.get_signal( + 'xyz', + default_conf['timeframe'], + mocked_history + ) assert log_has('Outdated history for pair xyz. Last tick is 16 minutes old', caplog) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 48a0f06e8..6372e6d36 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -757,7 +757,10 @@ def test_process_informative_pairs_added(default_conf, ticker, mocker) -> None: refresh_latest_ohlcv=refresh_mock, ) inf_pairs = MagicMock(return_value=[("BTC/ETH", '1m'), ("ETH/USDT", "1h")]) - mocker.patch('freqtrade.strategy.interface.IStrategy.get_signal', return_value=(False, False, '')) + mocker.patch( + 'freqtrade.strategy.interface.IStrategy.get_signal', + return_value=(False, False, '') + ) mocker.patch('time.sleep', return_value=None) freqtrade = FreqtradeBot(default_conf) From db1e676639d08baa5591fdc5621891c60ef7faf5 Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Wed, 21 Jul 2021 00:48:03 +0700 Subject: [PATCH 12/33] retrigger checks From 49886874aafcf1763e563f55cb61fedff0d3ac60 Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Wed, 21 Jul 2021 20:05:35 +0700 Subject: [PATCH 13/33] rename to buy_tag --- docs/strategy-advanced.md | 6 +++--- freqtrade/data/btanalysis.py | 2 +- freqtrade/enums/__init__.py | 2 +- freqtrade/enums/signaltype.py | 4 ++-- freqtrade/freqtradebot.py | 12 ++++++------ freqtrade/optimize/backtesting.py | 12 ++++++------ freqtrade/persistence/migrations.py | 8 ++++---- freqtrade/persistence/models.py | 8 ++++---- freqtrade/strategy/interface.py | 12 ++++++------ tests/optimize/__init__.py | 4 ++-- tests/optimize/test_backtest_detail.py | 4 ++-- tests/optimize/test_backtesting.py | 4 ++-- tests/rpc/test_rpc.py | 4 ++-- tests/strategy/test_interface.py | 2 +- tests/test_persistence.py | 10 +++++----- 15 files changed, 47 insertions(+), 47 deletions(-) diff --git a/docs/strategy-advanced.md b/docs/strategy-advanced.md index 0b52af17b..8ae0c1d16 100644 --- a/docs/strategy-advanced.md +++ b/docs/strategy-advanced.md @@ -114,7 +114,7 @@ class AwesomeStrategy(IStrategy): See [Dataframe access](#dataframe-access) for more information about dataframe use in strategy callbacks. -## Buy Signal Name +## Buy Tag When your strategy has multiple buy signal, you can name it. Then you can access you buy signal on `custom_sell` @@ -126,7 +126,7 @@ def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: (dataframe['rsi'] < 35) & (dataframe['volume'] > 0) ), - ['buy', 'buy_signal_name']] = 1, 'buy_signal_rsi' + ['buy', 'buy_tag']] = 1, 'buy_signal_rsi' return dataframe @@ -134,7 +134,7 @@ 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_signal_name == 'buy_signal_rsi' and last_candle['rsi'] > 80: + if trade.buy_tag == 'buy_signal_rsi' and last_candle['rsi'] > 80: return 'sell_signal_rsi' return None diff --git a/freqtrade/data/btanalysis.py b/freqtrade/data/btanalysis.py index f6122c5aa..d62712cbb 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_signal_name'] + 'stop_loss_ratio', 'min_rate', 'max_rate', 'is_open', 'buy_tag'] def get_latest_optimize_filename(directory: Union[Path, str], variant: str) -> str: diff --git a/freqtrade/enums/__init__.py b/freqtrade/enums/__init__.py index 71e9d7d9e..d803baf31 100644 --- a/freqtrade/enums/__init__.py +++ b/freqtrade/enums/__init__.py @@ -3,5 +3,5 @@ from freqtrade.enums.backteststate import BacktestState from freqtrade.enums.rpcmessagetype import RPCMessageType from freqtrade.enums.runmode import NON_UTIL_MODES, OPTIMIZE_MODES, TRADING_MODES, RunMode from freqtrade.enums.selltype import SellType -from freqtrade.enums.signaltype import SignalNameType, SignalType +from freqtrade.enums.signaltype import SignalTagType, SignalType from freqtrade.enums.state import State diff --git a/freqtrade/enums/signaltype.py b/freqtrade/enums/signaltype.py index 8529c6b79..d2995d57a 100644 --- a/freqtrade/enums/signaltype.py +++ b/freqtrade/enums/signaltype.py @@ -9,8 +9,8 @@ class SignalType(Enum): SELL = "sell" -class SignalNameType(Enum): +class SignalTagType(Enum): """ Enum for signal columns """ - BUY_SIGNAL_NAME = "buy_signal_name" + BUY_TAG = "buy_tag" diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 95c769730..cb9157d33 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -420,7 +420,7 @@ class FreqtradeBot(LoggingMixin): return False # running get_signal on historical data fetched - (buy, sell, buy_signal_name) = self.strategy.get_signal( + (buy, sell, buy_tag) = self.strategy.get_signal( pair, self.strategy.timeframe, analyzed_df @@ -431,13 +431,13 @@ class FreqtradeBot(LoggingMixin): bid_check_dom = self.config.get('bid_strategy', {}).get('check_depth_of_market', {}) if ((bid_check_dom.get('enabled', False)) and - (bid_check_dom.get('bids_to_ask_delta', 0) > 0)): + (bid_check_dom.get('bids_to_ask_delta', 0) > 0)): if self._check_depth_of_market_buy(pair, bid_check_dom): - return self.execute_buy(pair, stake_amount, buy_signal_name=buy_signal_name) + return self.execute_buy(pair, stake_amount, buy_tag=buy_tag) else: return False - return self.execute_buy(pair, stake_amount, buy_signal_name=buy_signal_name) + return self.execute_buy(pair, stake_amount, buy_tag=buy_tag) else: return False @@ -466,7 +466,7 @@ class FreqtradeBot(LoggingMixin): return False def execute_buy(self, pair: str, stake_amount: float, price: Optional[float] = None, - forcebuy: bool = False, buy_signal_name: str = '') -> bool: + forcebuy: bool = False, buy_tag: str = '') -> bool: """ Executes a limit buy for the given pair :param pair: pair for which we want to create a LIMIT_BUY @@ -569,7 +569,7 @@ class FreqtradeBot(LoggingMixin): exchange=self.exchange.id, open_order_id=order_id, strategy=self.strategy.get_strategy_name(), - buy_signal_name=buy_signal_name, + buy_tag=buy_tag, timeframe=timeframe_to_minutes(self.config['timeframe']) ) trade.orders.append(order_obj) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 7fdc70e70..5099c07ef 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -43,7 +43,7 @@ CLOSE_IDX = 3 SELL_IDX = 4 LOW_IDX = 5 HIGH_IDX = 6 -BUY_SIGNAL_NAME_IDX = 7 +buy_tag_IDX = 7 class Backtesting: @@ -210,7 +210,7 @@ class Backtesting: """ # Every change to this headers list must evaluate further usages of the resulting tuple # and eventually change the constants for indexes at the top - headers = ['date', 'buy', 'open', 'close', 'sell', 'low', 'high', 'buy_signal_name'] + headers = ['date', 'buy', 'open', 'close', 'sell', 'low', 'high', 'buy_tag'] data: Dict = {} self.progress.init_step(BacktestState.CONVERT, len(processed)) @@ -221,7 +221,7 @@ class Backtesting: if not pair_data.empty: pair_data.loc[:, 'buy'] = 0 # cleanup if buy_signal is exist pair_data.loc[:, 'sell'] = 0 # cleanup if sell_signal is exist - pair_data.loc[:, 'buy_signal_name'] = '' # cleanup if buy_signal_name is exist + pair_data.loc[:, 'buy_tag'] = '' # cleanup if buy_tag is exist df_analyzed = self.strategy.advise_sell( self.strategy.advise_buy(pair_data, {'pair': pair}), {'pair': pair})[headers].copy() @@ -230,7 +230,7 @@ class Backtesting: # from the previous candle df_analyzed.loc[:, 'buy'] = df_analyzed.loc[:, 'buy'].shift(1) df_analyzed.loc[:, 'sell'] = df_analyzed.loc[:, 'sell'].shift(1) - df_analyzed.loc[:, 'buy_signal_name'] = df_analyzed.loc[:, 'buy_signal_name'].shift(1) + df_analyzed.loc[:, 'buy_tag'] = df_analyzed.loc[:, 'buy_tag'].shift(1) df_analyzed.drop(df_analyzed.head(1).index, inplace=True) @@ -265,7 +265,7 @@ class Backtesting: # Worst case: price reaches stop_positive_offset and dives down. stop_rate = (sell_row[OPEN_IDX] * (1 + abs(self.strategy.trailing_stop_positive_offset) - - abs(self.strategy.trailing_stop_positive))) + abs(self.strategy.trailing_stop_positive))) else: # Worst case: price ticks tiny bit above open and dives down. stop_rate = sell_row[OPEN_IDX] * (1 - abs(trade.stop_loss_pct)) @@ -370,7 +370,7 @@ class Backtesting: fee_open=self.fee, fee_close=self.fee, is_open=True, - buy_signal_name=row[BUY_SIGNAL_NAME_IDX], + buy_tag=row[buy_tag_IDX], exchange='backtesting', ) return trade diff --git a/freqtrade/persistence/migrations.py b/freqtrade/persistence/migrations.py index f6a345ed1..035452437 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_signal_name = get_column_def(cols, 'buy_signal_name', 'null') + buy_tag = get_column_def(cols, 'buy_tag', 'null') # If ticker-interval existed use that, else null. if has_column(cols, 'ticker_interval'): timeframe = get_column_def(cols, 'timeframe', 'ticker_interval') @@ -81,7 +81,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_signal_name, + max_rate, min_rate, sell_reason, sell_order_status, strategy, buy_tag, timeframe, open_trade_value, close_profit_abs ) select id, lower(exchange), @@ -104,7 +104,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_signal_name} buy_signal_name, {timeframe} timeframe, + {strategy} strategy, {buy_tag} buy_tag, {timeframe} timeframe, {open_trade_value} open_trade_value, {close_profit_abs} close_profit_abs from {table_back_name} """)) @@ -168,7 +168,7 @@ def check_migrate(engine, decl_base, previous_tables) -> None: inspector = inspect(engine) cols = inspector.get_columns('trades') - if not has_column(cols, 'buy_signal_name'): + if not has_column(cols, 'buy_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 058e892ec..f42166762 100644 --- a/freqtrade/persistence/models.py +++ b/freqtrade/persistence/models.py @@ -257,7 +257,7 @@ class LocalTrade(): sell_reason: str = '' sell_order_status: str = '' strategy: str = '' - buy_signal_name: str = '' + buy_tag: str = '' timeframe: Optional[int] = None def __init__(self, **kwargs): @@ -289,7 +289,7 @@ 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_signal_name': self.buy_signal_name, + 'buy_tag': self.buy_tag, 'timeframe': self.timeframe, 'fee_open': self.fee_open, @@ -638,7 +638,7 @@ class LocalTrade(): # skip case if trailing-stop changed the stoploss already. if (trade.stop_loss == trade.initial_stop_loss - and trade.initial_stop_loss_pct != desired_stoploss): + and trade.initial_stop_loss_pct != desired_stoploss): # Stoploss value got changed logger.info(f"Stoploss for {trade} needs adjustment...") @@ -705,7 +705,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_signal_name = Column(String(100), nullable=True) + buy_tag = Column(String(100), nullable=True) timeframe = Column(Integer, nullable=True) def __init__(self, **kwargs): diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 8ee0cea59..a5517403d 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -13,7 +13,7 @@ from pandas import DataFrame from freqtrade.constants import ListPairsWithTimeframes from freqtrade.data.dataprovider import DataProvider -from freqtrade.enums import SellType, SignalNameType, SignalType +from freqtrade.enums import SellType, SignalTagType, SignalType from freqtrade.exceptions import OperationalException, StrategyError from freqtrade.exchange import timeframe_to_minutes, timeframe_to_seconds from freqtrade.exchange.exchange import timeframe_to_next_date @@ -422,7 +422,7 @@ class IStrategy(ABC, HyperStrategyMixin): logger.debug("Skipping TA Analysis for already analyzed candle") dataframe['buy'] = 0 dataframe['sell'] = 0 - dataframe['buy_signal_name'] = '' + dataframe['buy_tag'] = '' # Other Defs in strategy that want to be called every loop here # twitter_sell = self.watch_twitter_feed(dataframe, metadata) @@ -525,9 +525,9 @@ class IStrategy(ABC, HyperStrategyMixin): ) return False, False, '' - (buy, sell, buy_signal_name) = latest[SignalType.BUY.value] == 1,\ + (buy, sell, buy_tag) = latest[SignalType.BUY.value] == 1,\ latest[SignalType.SELL.value] == 1,\ - latest.get(SignalNameType.BUY_SIGNAL_NAME.value, '') + latest.get(SignalTagType.BUY_TAG.value, '') logger.debug('trigger: %s (pair=%s) buy=%s sell=%s', latest['date'], pair, str(buy), str(sell)) timeframe_seconds = timeframe_to_seconds(timeframe) @@ -535,8 +535,8 @@ class IStrategy(ABC, HyperStrategyMixin): current_time=datetime.now(timezone.utc), timeframe_seconds=timeframe_seconds, buy=buy): - return False, sell, buy_signal_name - return buy, sell, buy_signal_name + return False, sell, buy_tag + return buy, sell, buy_tag def ignore_expired_candle(self, latest_date: datetime, current_time: datetime, timeframe_seconds: int, buy: bool): diff --git a/tests/optimize/__init__.py b/tests/optimize/__init__.py index f77e6f70f..240f3f871 100644 --- a/tests/optimize/__init__.py +++ b/tests/optimize/__init__.py @@ -18,7 +18,7 @@ class BTrade(NamedTuple): sell_reason: SellType open_tick: int close_tick: int - buy_signal_name: Optional[str] = '' + buy_tag: Optional[str] = '' class BTContainer(NamedTuple): @@ -44,7 +44,7 @@ def _get_frame_time_from_offset(offset): def _build_backtest_dataframe(data): - columns = ['date', 'open', 'high', 'low', 'close', 'volume', 'buy', 'sell', 'buy_signal_name'] + columns = ['date', 'open', 'high', 'low', 'close', 'volume', 'buy', 'sell', 'buy_tag'] frame = DataFrame.from_records(data, columns=columns) frame['date'] = frame['date'].apply(_get_frame_time_from_offset) diff --git a/tests/optimize/test_backtest_detail.py b/tests/optimize/test_backtest_detail.py index d1bf28d58..f7077ef56 100644 --- a/tests/optimize/test_backtest_detail.py +++ b/tests/optimize/test_backtest_detail.py @@ -532,7 +532,7 @@ tc33 = BTContainer(data=[ sell_reason=SellType.TRAILING_STOP_LOSS, open_tick=1, close_tick=1, - buy_signal_name='buy_signal_01' + buy_tag='buy_signal_01' )] ) @@ -619,6 +619,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_signal_name == trade.buy_signal_name + assert res.buy_tag == trade.buy_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 64795d064..873b27b4d 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -584,7 +584,7 @@ def test_backtest_one(default_conf, fee, mocker, testdatadir) -> None: 'min_rate': [0.1038, 0.10302485], 'max_rate': [0.10501, 0.1038888], 'is_open': [False, False], - 'buy_signal_name': ['', ''], + 'buy_tag': ['', ''], }) pd.testing.assert_frame_equal(results, expected) data_pair = processed[pair] @@ -859,7 +859,7 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog, testdatadir): 'locks': [], 'rejected_signals': 20, 'final_balance': 1000, - }) + }) mocker.patch('freqtrade.plugins.pairlistmanager.PairListManager.whitelist', PropertyMock(return_value=['UNITTEST/BTC'])) mocker.patch('freqtrade.optimize.backtesting.Backtesting.backtest', backtestmock) diff --git a/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py index df8679470..889aa05cf 100644 --- a/tests/rpc/test_rpc.py +++ b/tests/rpc/test_rpc.py @@ -69,7 +69,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None: 'min_rate': ANY, 'max_rate': ANY, 'strategy': ANY, - 'buy_signal_name': ANY, + 'buy_tag': ANY, 'timeframe': 5, 'open_order_id': ANY, 'close_date': None, @@ -136,7 +136,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None: 'min_rate': ANY, 'max_rate': ANY, 'strategy': ANY, - 'buy_signal_name': ANY, + 'buy_tag': ANY, 'timeframe': ANY, 'open_order_id': ANY, 'close_date': None, diff --git a/tests/strategy/test_interface.py b/tests/strategy/test_interface.py index 3678cee4a..c812f3007 100644 --- a/tests/strategy/test_interface.py +++ b/tests/strategy/test_interface.py @@ -49,7 +49,7 @@ def test_returns_latest_signal(mocker, default_conf, ohlcv_history): assert _STRATEGY.get_signal('ETH/BTC', '5m', mocked_history) == (False, False, '') mocked_history.loc[1, 'sell'] = 0 mocked_history.loc[1, 'buy'] = 1 - mocked_history.loc[1, 'buy_signal_name'] = 'buy_signal_01' + mocked_history.loc[1, 'buy_tag'] = 'buy_signal_01' assert _STRATEGY.get_signal('ETH/BTC', '5m', mocked_history) == (True, False, 'buy_signal_01') diff --git a/tests/test_persistence.py b/tests/test_persistence.py index 8e486d145..af4979919 100644 --- a/tests/test_persistence.py +++ b/tests/test_persistence.py @@ -861,7 +861,7 @@ def test_to_json(default_conf, fee): open_date=arrow.utcnow().shift(hours=-2).datetime, open_rate=0.123, exchange='binance', - buy_signal_name='', + buy_tag='', open_order_id='dry_run_buy_12345' ) result = trade.to_json() @@ -911,7 +911,7 @@ def test_to_json(default_conf, fee): 'min_rate': None, 'max_rate': None, 'strategy': None, - 'buy_signal_name': '', + 'buy_tag': '', 'timeframe': None, 'exchange': 'binance', } @@ -928,7 +928,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_signal_name='buys_signal_001', + buy_tag='buys_signal_001', exchange='binance', ) result = trade.to_json() @@ -978,7 +978,7 @@ def test_to_json(default_conf, fee): 'sell_reason': None, 'sell_order_status': None, 'strategy': None, - 'buy_signal_name': 'buys_signal_001', + 'buy_tag': 'buys_signal_001', 'timeframe': None, 'exchange': 'binance', } @@ -1323,7 +1323,7 @@ def test_Trade_object_idem(): 'get_open_trades_without_assigned_fees', 'get_open_order_trades', 'get_trades', - ) + ) # Parent (LocalTrade) should have the same attributes for item in trade: From f5a660f845163369659855883a3982bc06fc1816 Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Wed, 21 Jul 2021 20:19:56 +0700 Subject: [PATCH 14/33] caps BUY_TAG_IDX --- freqtrade/optimize/backtesting.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 5099c07ef..fdf341e21 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -43,7 +43,7 @@ CLOSE_IDX = 3 SELL_IDX = 4 LOW_IDX = 5 HIGH_IDX = 6 -buy_tag_IDX = 7 +BUY_TAG_IDX = 7 class Backtesting: @@ -370,7 +370,7 @@ class Backtesting: fee_open=self.fee, fee_close=self.fee, is_open=True, - buy_tag=row[buy_tag_IDX], + buy_tag=row[BUY_TAG_IDX], exchange='backtesting', ) return trade From 235c1afd09dccbc714828e1618f7545ca3dfd776 Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Thu, 22 Jul 2021 01:53:15 +0700 Subject: [PATCH 15/33] add buy_tag on telegram --- freqtrade/freqtradebot.py | 3 ++ freqtrade/rpc/telegram.py | 27 +++++++++++----- freqtrade/strategy/interface.py | 6 ++-- tests/conftest.py | 2 +- tests/rpc/test_rpc.py | 34 ++++++++++---------- tests/rpc/test_rpc_apiserver.py | 24 +++++++-------- tests/rpc/test_rpc_telegram.py | 53 ++++++++++++++++++-------------- tests/strategy/test_interface.py | 14 ++++----- tests/test_freqtradebot.py | 42 ++++++++++++------------- tests/test_persistence.py | 4 +-- 10 files changed, 115 insertions(+), 94 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index cb9157d33..7756aa1d7 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -595,6 +595,7 @@ class FreqtradeBot(LoggingMixin): msg = { 'trade_id': trade.id, 'type': RPCMessageType.BUY, + 'buy_tag': trade.buy_tag, 'exchange': self.exchange.name.capitalize(), 'pair': trade.pair, 'limit': trade.open_rate, @@ -619,6 +620,7 @@ class FreqtradeBot(LoggingMixin): msg = { 'trade_id': trade.id, 'type': RPCMessageType.BUY_CANCEL, + 'buy_tag': trade.buy_tag, 'exchange': self.exchange.name.capitalize(), 'pair': trade.pair, 'limit': trade.open_rate, @@ -639,6 +641,7 @@ class FreqtradeBot(LoggingMixin): msg = { 'trade_id': trade.id, 'type': RPCMessageType.BUY_FILL, + 'buy_tag': trade.buy_tag, 'exchange': self.exchange.name.capitalize(), 'pair': trade.pair, 'open_rate': trade.open_rate, diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 263a3fc6d..a1f6a7e33 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -208,15 +208,25 @@ class Telegram(RPCHandler): else: msg['stake_amount_fiat'] = 0 - message = (f"\N{LARGE BLUE CIRCLE} *{msg['exchange']}:* Buying {msg['pair']}" - f" (#{msg['trade_id']})\n" - f"*Amount:* `{msg['amount']:.8f}`\n" - f"*Open Rate:* `{msg['limit']:.8f}`\n" - f"*Current Rate:* `{msg['current_rate']:.8f}`\n" - f"*Total:* `({round_coin_value(msg['stake_amount'], msg['stake_currency'])}") - + content = [] + content.append( + f"\N{LARGE BLUE CIRCLE} *{msg['exchange']}:* Buying {msg['pair']}" + f" (#{msg['trade_id']})\n" + ) + if msg.get('buy_tag', None): + content.append(f"*Buy Tag:* `{msg['buy_tag']}`\n") + content.append(f"*Amount:* `{msg['amount']:.8f}`\n") + content.append(f"*Open Rate:* `{msg['limit']:.8f}`\n") + content.append(f"*Current Rate:* `{msg['current_rate']:.8f}`\n") + content.append( + f"*Total:* `({round_coin_value(msg['stake_amount'], msg['stake_currency'])}" + ) if msg.get('fiat_currency', None): - message += f", {round_coin_value(msg['stake_amount_fiat'], msg['fiat_currency'])}" + content.append( + f", {round_coin_value(msg['stake_amount_fiat'], msg['fiat_currency'])}" + ) + + message = ''.join(content) message += ")`" return message @@ -354,6 +364,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 "", "*Open Rate:* `{open_rate:.8f}`", "*Close Rate:* `{close_rate}`" if r['close_rate'] else "", "*Current Rate:* `{current_rate:.8f}`", diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index a5517403d..0206e8839 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -508,7 +508,7 @@ class IStrategy(ABC, HyperStrategyMixin): """ if not isinstance(dataframe, DataFrame) or dataframe.empty: logger.warning(f'Empty candle (OHLCV) data for pair {pair}') - return False, False, '' + return False, False, None latest_date = dataframe['date'].max() latest = dataframe.loc[dataframe['date'] == latest_date].iloc[-1] @@ -523,11 +523,11 @@ class IStrategy(ABC, HyperStrategyMixin): 'Outdated history for pair %s. Last tick is %s minutes old', pair, int((arrow.utcnow() - latest_date).total_seconds() // 60) ) - return False, False, '' + return False, False, None (buy, sell, buy_tag) = latest[SignalType.BUY.value] == 1,\ latest[SignalType.SELL.value] == 1,\ - latest.get(SignalTagType.BUY_TAG.value, '') + latest.get(SignalTagType.BUY_TAG.value, None) logger.debug('trigger: %s (pair=%s) buy=%s sell=%s', latest['date'], pair, str(buy), str(sell)) timeframe_seconds = timeframe_to_seconds(timeframe) diff --git a/tests/conftest.py b/tests/conftest.py index 139d989a9..5f3a63c39 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -182,7 +182,7 @@ def get_patched_worker(mocker, config) -> Worker: return Worker(args=None, config=config) -def patch_get_signal(freqtrade: FreqtradeBot, value=(True, False, '')) -> None: +def patch_get_signal(freqtrade: FreqtradeBot, value=(True, False, None)) -> None: """ :param mocker: mocker to patch IStrategy class :param value: which value IStrategy.get_signal() must return diff --git a/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py index 889aa05cf..1e8d38841 100644 --- a/tests/rpc/test_rpc.py +++ b/tests/rpc/test_rpc.py @@ -35,7 +35,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None: ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) rpc = RPC(freqtradebot) freqtradebot.state = State.RUNNING @@ -192,7 +192,7 @@ def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None: ) del default_conf['fiat_display_currency'] freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) rpc = RPC(freqtradebot) freqtradebot.state = State.RUNNING @@ -239,7 +239,7 @@ def test_rpc_daily_profit(default_conf, update, ticker, fee, ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) stake_currency = default_conf['stake_currency'] fiat_display_currency = default_conf['fiat_display_currency'] @@ -371,7 +371,7 @@ def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee, ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) stake_currency = default_conf['stake_currency'] fiat_display_currency = default_conf['fiat_display_currency'] @@ -459,7 +459,7 @@ def test_rpc_trade_statistics_closed(mocker, default_conf, ticker, fee, ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) stake_currency = default_conf['stake_currency'] fiat_display_currency = default_conf['fiat_display_currency'] @@ -526,7 +526,7 @@ def test_rpc_balance_handle_error(default_conf, mocker): ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) rpc = RPC(freqtradebot) rpc._fiat_converter = CryptoToFiatConverter() with pytest.raises(RPCException, match="Error getting current tickers."): @@ -567,7 +567,7 @@ def test_rpc_balance_handle(default_conf, mocker, tickers): ) default_conf['dry_run'] = False freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) rpc = RPC(freqtradebot) rpc._fiat_converter = CryptoToFiatConverter() @@ -612,7 +612,7 @@ def test_rpc_start(mocker, default_conf) -> None: ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) rpc = RPC(freqtradebot) freqtradebot.state = State.STOPPED @@ -633,7 +633,7 @@ def test_rpc_stop(mocker, default_conf) -> None: ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) rpc = RPC(freqtradebot) freqtradebot.state = State.RUNNING @@ -655,7 +655,7 @@ def test_rpc_stopbuy(mocker, default_conf) -> None: ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) rpc = RPC(freqtradebot) freqtradebot.state = State.RUNNING @@ -687,7 +687,7 @@ def test_rpc_forcesell(default_conf, ticker, fee, mocker) -> None: mocker.patch('freqtrade.wallets.Wallets.get_free', return_value=1000) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) rpc = RPC(freqtradebot) freqtradebot.state = State.STOPPED @@ -805,7 +805,7 @@ def test_performance_handle(default_conf, ticker, limit_buy_order, fee, ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) rpc = RPC(freqtradebot) # Create some test data @@ -838,7 +838,7 @@ def test_rpc_count(mocker, default_conf, ticker, fee) -> None: ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) rpc = RPC(freqtradebot) counts = rpc._rpc_count() @@ -863,7 +863,7 @@ def test_rpcforcebuy(mocker, default_conf, ticker, fee, limit_buy_order_open) -> ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) rpc = RPC(freqtradebot) pair = 'ETH/BTC' trade = rpc._rpc_forcebuy(pair, None) @@ -889,7 +889,7 @@ def test_rpcforcebuy(mocker, default_conf, ticker, fee, limit_buy_order_open) -> # Test not buying freqtradebot = get_patched_freqtradebot(mocker, default_conf) freqtradebot.config['stake_amount'] = 0 - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) rpc = RPC(freqtradebot) pair = 'TKN/BTC' trade = rpc._rpc_forcebuy(pair, None) @@ -902,7 +902,7 @@ def test_rpcforcebuy_stopped(mocker, default_conf) -> None: mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) rpc = RPC(freqtradebot) pair = 'ETH/BTC' with pytest.raises(RPCException, match=r'trader is not running'): @@ -913,7 +913,7 @@ def test_rpcforcebuy_disabled(mocker, default_conf) -> None: mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) rpc = RPC(freqtradebot) pair = 'ETH/BTC' with pytest.raises(RPCException, match=r'Forcebuy not enabled.'): diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index 74d40b611..f7d935b64 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -442,7 +442,7 @@ def test_api_balance(botclient, mocker, rpc_balance): def test_api_count(botclient, mocker, ticker, fee, markets): freqtrade, client = botclient - patch_get_signal(freqtrade, (True, False, '')) + patch_get_signal(freqtrade, (True, False, None)) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_balances=MagicMock(return_value=ticker), @@ -504,7 +504,7 @@ def test_api_locks(botclient): def test_api_show_config(botclient, mocker): freqtrade, client = botclient - patch_get_signal(freqtrade, (True, False, '')) + patch_get_signal(freqtrade, (True, False, None)) rc = client_get(client, f"{BASE_URI}/show_config") assert_response(rc) @@ -522,7 +522,7 @@ def test_api_show_config(botclient, mocker): def test_api_daily(botclient, mocker, ticker, fee, markets): freqtrade, client = botclient - patch_get_signal(freqtrade, (True, False, '')) + patch_get_signal(freqtrade, (True, False, None)) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_balances=MagicMock(return_value=ticker), @@ -540,7 +540,7 @@ def test_api_daily(botclient, mocker, ticker, fee, markets): def test_api_trades(botclient, mocker, fee, markets): freqtrade, client = botclient - patch_get_signal(freqtrade, (True, False, '')) + patch_get_signal(freqtrade, (True, False, None)) mocker.patch.multiple( 'freqtrade.exchange.Exchange', markets=PropertyMock(return_value=markets) @@ -568,7 +568,7 @@ def test_api_trades(botclient, mocker, fee, markets): def test_api_trade_single(botclient, mocker, fee, ticker, markets): freqtrade, client = botclient - patch_get_signal(freqtrade, (True, False, '')) + patch_get_signal(freqtrade, (True, False, None)) mocker.patch.multiple( 'freqtrade.exchange.Exchange', markets=PropertyMock(return_value=markets), @@ -588,7 +588,7 @@ def test_api_trade_single(botclient, mocker, fee, ticker, markets): def test_api_delete_trade(botclient, mocker, fee, markets): freqtrade, client = botclient - patch_get_signal(freqtrade, (True, False, '')) + patch_get_signal(freqtrade, (True, False, None)) stoploss_mock = MagicMock() cancel_mock = MagicMock() mocker.patch.multiple( @@ -662,7 +662,7 @@ def test_api_logs(botclient): def test_api_edge_disabled(botclient, mocker, ticker, fee, markets): freqtrade, client = botclient - patch_get_signal(freqtrade, (True, False, '')) + patch_get_signal(freqtrade, (True, False, None)) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_balances=MagicMock(return_value=ticker), @@ -678,7 +678,7 @@ def test_api_edge_disabled(botclient, mocker, ticker, fee, markets): @pytest.mark.usefixtures("init_persistence") def test_api_profit(botclient, mocker, ticker, fee, markets): freqtrade, client = botclient - patch_get_signal(freqtrade, (True, False, '')) + patch_get_signal(freqtrade, (True, False, None)) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_balances=MagicMock(return_value=ticker), @@ -729,7 +729,7 @@ def test_api_profit(botclient, mocker, ticker, fee, markets): @pytest.mark.usefixtures("init_persistence") def test_api_stats(botclient, mocker, ticker, fee, markets,): freqtrade, client = botclient - patch_get_signal(freqtrade, (True, False, '')) + patch_get_signal(freqtrade, (True, False, None)) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_balances=MagicMock(return_value=ticker), @@ -757,7 +757,7 @@ def test_api_stats(botclient, mocker, ticker, fee, markets,): def test_api_performance(botclient, fee): freqtrade, client = botclient - patch_get_signal(freqtrade, (True, False, '')) + patch_get_signal(freqtrade, (True, False, None)) trade = Trade( pair='LTC/ETH', @@ -803,7 +803,7 @@ def test_api_performance(botclient, fee): def test_api_status(botclient, mocker, ticker, fee, markets): freqtrade, client = botclient - patch_get_signal(freqtrade, (True, False, '')) + patch_get_signal(freqtrade, (True, False, None)) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_balances=MagicMock(return_value=ticker), @@ -1044,7 +1044,7 @@ def test_api_forcesell(botclient, mocker, ticker, fee, markets): markets=PropertyMock(return_value=markets), _is_dry_limit_order_filled=MagicMock(return_value=False), ) - patch_get_signal(freqtrade, (True, False, '')) + patch_get_signal(freqtrade, (True, False, None)) rc = client_post(client, f"{BASE_URI}/forcesell", data='{"tradeid": "1"}') diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py index df624e136..ab66d18e8 100644 --- a/tests/rpc/test_rpc_telegram.py +++ b/tests/rpc/test_rpc_telegram.py @@ -119,7 +119,7 @@ def test_authorized_only(default_conf, mocker, caplog, update) -> None: rpc = RPC(freqtrade) dummy = DummyCls(rpc, default_conf) - patch_get_signal(freqtrade, (True, False, '')) + patch_get_signal(freqtrade, (True, False, None)) dummy.dummy_handler(update=update, context=MagicMock()) assert dummy.state['called'] is True assert log_has('Executing handler: dummy_handler for chat_id: 0', caplog) @@ -139,7 +139,7 @@ def test_authorized_only_unauthorized(default_conf, mocker, caplog) -> None: rpc = RPC(freqtrade) dummy = DummyCls(rpc, default_conf) - patch_get_signal(freqtrade, (True, False, '')) + patch_get_signal(freqtrade, (True, False, None)) dummy.dummy_handler(update=update, context=MagicMock()) assert dummy.state['called'] is False assert not log_has('Executing handler: dummy_handler for chat_id: 3735928559', caplog) @@ -155,7 +155,7 @@ def test_authorized_only_exception(default_conf, mocker, caplog, update) -> None freqtrade = FreqtradeBot(default_conf) rpc = RPC(freqtrade) dummy = DummyCls(rpc, default_conf) - patch_get_signal(freqtrade, (True, False, '')) + patch_get_signal(freqtrade, (True, False, None)) dummy.dummy_exception(update=update, context=MagicMock()) assert dummy.state['called'] is False @@ -185,6 +185,7 @@ def test_telegram_status(default_conf, update, mocker) -> None: 'current_rate': 1.098e-05, 'amount': 90.99181074, 'stake_amount': 90.99181074, + 'buy_tag': None, 'close_profit_pct': None, 'profit': -0.0059, 'profit_pct': -0.59, @@ -228,7 +229,7 @@ def test_status_handle(default_conf, update, ticker, fee, mocker) -> None: telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) freqtradebot.state = State.STOPPED # Status is also enabled when stopped @@ -285,7 +286,7 @@ def test_status_table_handle(default_conf, update, ticker, fee, mocker) -> None: telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) freqtradebot.state = State.STOPPED # Status table is also enabled when stopped @@ -329,7 +330,7 @@ def test_daily_handle(default_conf, update, ticker, limit_buy_order, fee, telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) # Create some test data freqtradebot.enter_positions() @@ -400,7 +401,7 @@ def test_daily_wrong_input(default_conf, update, ticker, mocker) -> None: ) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) # Try invalid data msg_mock.reset_mock() @@ -432,7 +433,7 @@ def test_profit_handle(default_conf, update, ticker, ticker_sell_up, fee, ) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) telegram._profit(update=update, context=MagicMock()) assert msg_mock.call_count == 1 @@ -487,7 +488,7 @@ def test_telegram_stats(default_conf, update, ticker, ticker_sell_up, fee, get_fee=fee, ) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) telegram._stats(update=update, context=MagicMock()) assert msg_mock.call_count == 1 @@ -513,7 +514,7 @@ def test_telegram_balance_handle(default_conf, update, mocker, rpc_balance, tick side_effect=lambda a, b: f"{a}/{b}") telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) telegram._balance(update=update, context=MagicMock()) result = msg_mock.call_args_list[0][0][0] @@ -536,7 +537,7 @@ def test_balance_handle_empty_response(default_conf, update, mocker) -> None: mocker.patch('freqtrade.exchange.Exchange.get_balances', return_value={}) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) freqtradebot.config['dry_run'] = False telegram._balance(update=update, context=MagicMock()) @@ -549,7 +550,7 @@ def test_balance_handle_empty_response_dry(default_conf, update, mocker) -> None mocker.patch('freqtrade.exchange.Exchange.get_balances', return_value={}) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) telegram._balance(update=update, context=MagicMock()) result = msg_mock.call_args_list[0][0][0] @@ -578,7 +579,7 @@ def test_balance_handle_too_large_response(default_conf, update, mocker) -> None }) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) telegram._balance(update=update, context=MagicMock()) assert msg_mock.call_count > 1 @@ -677,7 +678,7 @@ def test_telegram_forcesell_handle(default_conf, update, ticker, fee, freqtradebot = FreqtradeBot(default_conf) rpc = RPC(freqtradebot) telegram = Telegram(rpc, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) # Create some test data freqtradebot.enter_positions() @@ -736,7 +737,7 @@ def test_telegram_forcesell_down_handle(default_conf, update, ticker, fee, freqtradebot = FreqtradeBot(default_conf) rpc = RPC(freqtradebot) telegram = Telegram(rpc, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) # Create some test data freqtradebot.enter_positions() @@ -797,7 +798,7 @@ def test_forcesell_all_handle(default_conf, update, ticker, fee, mocker) -> None freqtradebot = FreqtradeBot(default_conf) rpc = RPC(freqtradebot) telegram = Telegram(rpc, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) # Create some test data freqtradebot.enter_positions() @@ -838,7 +839,7 @@ def test_forcesell_handle_invalid(default_conf, update, mocker) -> None: return_value=15000.0) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) # Trader is not running freqtradebot.state = State.STOPPED @@ -876,7 +877,7 @@ def test_forcebuy_handle(default_conf, update, mocker) -> None: mocker.patch('freqtrade.rpc.RPC._rpc_forcebuy', fbuy_mock) telegram, freqtradebot, _ = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) # /forcebuy ETH/BTC context = MagicMock() @@ -905,7 +906,7 @@ def test_forcebuy_handle_exception(default_conf, update, mocker) -> None: mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) update.message.text = '/forcebuy ETH/Nonepair' telegram._forcebuy(update=update, context=MagicMock()) @@ -922,7 +923,7 @@ def test_forcebuy_no_pair(default_conf, update, mocker) -> None: telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) context = MagicMock() context.args = [] @@ -950,7 +951,7 @@ def test_performance_handle(default_conf, update, ticker, fee, get_fee=fee, ) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) # Create some test data freqtradebot.enter_positions() @@ -978,7 +979,7 @@ def test_count_handle(default_conf, update, ticker, fee, mocker) -> None: get_fee=fee, ) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) freqtradebot.state = State.STOPPED telegram._count(update=update, context=MagicMock()) @@ -1007,7 +1008,7 @@ def test_telegram_lock_handle(default_conf, update, ticker, fee, mocker) -> None get_fee=fee, ) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) telegram._locks(update=update, context=MagicMock()) assert msg_mock.call_count == 1 assert 'No active locks.' in msg_mock.call_args_list[0][0][0] @@ -1253,6 +1254,7 @@ def test_send_msg_buy_notification(default_conf, mocker, caplog) -> None: msg = { 'type': RPCMessageType.BUY, 'trade_id': 1, + 'buy_tag': 'buy_signal_01', 'exchange': 'Binance', 'pair': 'ETH/BTC', 'limit': 1.099e-05, @@ -1270,6 +1272,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' \ '*Amount:* `1333.33333333`\n' \ '*Open Rate:* `0.00001099`\n' \ '*Current Rate:* `0.00001099`\n' \ @@ -1297,6 +1300,7 @@ def test_send_msg_buy_cancel_notification(default_conf, mocker) -> None: telegram.send_msg({ 'type': RPCMessageType.BUY_CANCEL, + 'buy_tag': 'buy_signal_01', 'trade_id': 1, 'exchange': 'Binance', 'pair': 'ETH/BTC', @@ -1314,6 +1318,7 @@ def test_send_msg_buy_fill_notification(default_conf, mocker) -> None: telegram.send_msg({ 'type': RPCMessageType.BUY_FILL, + 'buy_tag': 'buy_signal_01', 'trade_id': 1, 'exchange': 'Binance', 'pair': 'ETH/USDT', @@ -1498,6 +1503,7 @@ def test_send_msg_buy_notification_no_fiat(default_conf, mocker) -> None: telegram.send_msg({ 'type': RPCMessageType.BUY, + 'buy_tag': 'buy_signal_01', 'trade_id': 1, 'exchange': 'Binance', 'pair': 'ETH/BTC', @@ -1512,6 +1518,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' '*Amount:* `1333.33333333`\n' '*Open Rate:* `0.00001099`\n' '*Current Rate:* `0.00001099`\n' diff --git a/tests/strategy/test_interface.py b/tests/strategy/test_interface.py index c812f3007..792353f7a 100644 --- a/tests/strategy/test_interface.py +++ b/tests/strategy/test_interface.py @@ -38,15 +38,15 @@ def test_returns_latest_signal(mocker, default_conf, ohlcv_history): mocked_history['buy'] = 0 mocked_history.loc[1, 'sell'] = 1 - assert _STRATEGY.get_signal('ETH/BTC', '5m', mocked_history) == (False, True, '') + assert _STRATEGY.get_signal('ETH/BTC', '5m', mocked_history) == (False, True, None) mocked_history.loc[1, 'sell'] = 0 mocked_history.loc[1, 'buy'] = 1 - assert _STRATEGY.get_signal('ETH/BTC', '5m', mocked_history) == (True, False, '') + assert _STRATEGY.get_signal('ETH/BTC', '5m', mocked_history) == (True, False, None) mocked_history.loc[1, 'sell'] = 0 mocked_history.loc[1, 'buy'] = 0 - assert _STRATEGY.get_signal('ETH/BTC', '5m', mocked_history) == (False, False, '') + assert _STRATEGY.get_signal('ETH/BTC', '5m', mocked_history) == (False, False, None) mocked_history.loc[1, 'sell'] = 0 mocked_history.loc[1, 'buy'] = 1 mocked_history.loc[1, 'buy_tag'] = 'buy_signal_01' @@ -68,15 +68,15 @@ def test_analyze_pair_empty(default_conf, mocker, caplog, ohlcv_history): def test_get_signal_empty(default_conf, mocker, caplog): - assert (False, False, '') == _STRATEGY.get_signal('foo', default_conf['timeframe'], DataFrame()) + assert (False, False, None) == _STRATEGY.get_signal('foo', default_conf['timeframe'], DataFrame()) assert log_has('Empty candle (OHLCV) data for pair foo', caplog) caplog.clear() - assert (False, False, '') == _STRATEGY.get_signal('bar', default_conf['timeframe'], None) + assert (False, False, None) == _STRATEGY.get_signal('bar', default_conf['timeframe'], None) assert log_has('Empty candle (OHLCV) data for pair bar', caplog) caplog.clear() - assert (False, False, '') == _STRATEGY.get_signal( + assert (False, False, None) == _STRATEGY.get_signal( 'baz', default_conf['timeframe'], DataFrame([]) @@ -116,7 +116,7 @@ def test_get_signal_old_dataframe(default_conf, mocker, caplog, ohlcv_history): caplog.set_level(logging.INFO) mocker.patch.object(_STRATEGY, 'assert_df') - assert (False, False, '') == _STRATEGY.get_signal( + assert (False, False, None) == _STRATEGY.get_signal( 'xyz', default_conf['timeframe'], mocked_history diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 6372e6d36..abbef7858 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -536,7 +536,7 @@ def test_create_trade_no_signal(default_conf, fee, mocker) -> None: ) default_conf['stake_amount'] = 10 freqtrade = FreqtradeBot(default_conf) - patch_get_signal(freqtrade, value=(False, False, '')) + patch_get_signal(freqtrade, value=(False, False, None)) Trade.query = MagicMock() Trade.query.filter = MagicMock() @@ -1860,7 +1860,7 @@ def test_handle_trade(default_conf, limit_buy_order, limit_sell_order_open, limi assert trade.is_open is True freqtrade.wallets.update() - patch_get_signal(freqtrade, value=(False, True, '')) + patch_get_signal(freqtrade, value=(False, True, None)) assert freqtrade.handle_trade(trade) is True assert trade.open_order_id == limit_sell_order['id'] @@ -1885,7 +1885,7 @@ def test_handle_overlapping_signals(default_conf, ticker, limit_buy_order_open, ) freqtrade = FreqtradeBot(default_conf) - patch_get_signal(freqtrade, value=(True, True, '')) + patch_get_signal(freqtrade, value=(True, True, None)) freqtrade.strategy.min_roi_reached = MagicMock(return_value=False) freqtrade.enter_positions() @@ -1896,7 +1896,7 @@ def test_handle_overlapping_signals(default_conf, ticker, limit_buy_order_open, assert nb_trades == 0 # Buy is triggering, so buying ... - patch_get_signal(freqtrade, value=(True, False, '')) + patch_get_signal(freqtrade, value=(True, False, None)) freqtrade.enter_positions() trades = Trade.query.all() nb_trades = len(trades) @@ -1904,7 +1904,7 @@ def test_handle_overlapping_signals(default_conf, ticker, limit_buy_order_open, assert trades[0].is_open is True # Buy and Sell are not triggering, so doing nothing ... - patch_get_signal(freqtrade, value=(False, False, '')) + patch_get_signal(freqtrade, value=(False, False, None)) assert freqtrade.handle_trade(trades[0]) is False trades = Trade.query.all() nb_trades = len(trades) @@ -1912,7 +1912,7 @@ def test_handle_overlapping_signals(default_conf, ticker, limit_buy_order_open, assert trades[0].is_open is True # Buy and Sell are triggering, so doing nothing ... - patch_get_signal(freqtrade, value=(True, True, '')) + patch_get_signal(freqtrade, value=(True, True, None)) assert freqtrade.handle_trade(trades[0]) is False trades = Trade.query.all() nb_trades = len(trades) @@ -1920,7 +1920,7 @@ def test_handle_overlapping_signals(default_conf, ticker, limit_buy_order_open, assert trades[0].is_open is True # Sell is triggering, guess what : we are Selling! - patch_get_signal(freqtrade, value=(False, True, '')) + patch_get_signal(freqtrade, value=(False, True, None)) trades = Trade.query.all() assert freqtrade.handle_trade(trades[0]) is True @@ -1938,7 +1938,7 @@ def test_handle_trade_roi(default_conf, ticker, limit_buy_order_open, ) freqtrade = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtrade, value=(True, False, '')) + patch_get_signal(freqtrade, value=(True, False, None)) freqtrade.strategy.min_roi_reached = MagicMock(return_value=True) freqtrade.enter_positions() @@ -1951,7 +1951,7 @@ def test_handle_trade_roi(default_conf, ticker, limit_buy_order_open, # we might just want to check if we are in a sell condition without # executing # if ROI is reached we must sell - patch_get_signal(freqtrade, value=(False, True, '')) + patch_get_signal(freqtrade, value=(False, True, None)) assert freqtrade.handle_trade(trade) assert log_has("ETH/BTC - Required profit reached. sell_type=SellType.ROI", caplog) @@ -1977,10 +1977,10 @@ def test_handle_trade_use_sell_signal( trade = Trade.query.first() trade.is_open = True - patch_get_signal(freqtrade, value=(False, False, '')) + patch_get_signal(freqtrade, value=(False, False, None)) assert not freqtrade.handle_trade(trade) - patch_get_signal(freqtrade, value=(False, True, '')) + patch_get_signal(freqtrade, value=(False, True, None)) assert freqtrade.handle_trade(trade) assert log_has("ETH/BTC - Sell signal received. sell_type=SellType.SELL_SIGNAL", caplog) @@ -3016,7 +3016,7 @@ def test_sell_profit_only_enable_profit(default_conf, limit_buy_order, limit_buy trade = Trade.query.first() trade.update(limit_buy_order) freqtrade.wallets.update() - patch_get_signal(freqtrade, value=(False, True, '')) + patch_get_signal(freqtrade, value=(False, True, None)) assert freqtrade.handle_trade(trade) is False freqtrade.strategy.sell_profit_offset = 0.0 @@ -3051,7 +3051,7 @@ def test_sell_profit_only_disable_profit(default_conf, limit_buy_order, limit_bu trade = Trade.query.first() trade.update(limit_buy_order) freqtrade.wallets.update() - patch_get_signal(freqtrade, value=(False, True, '')) + patch_get_signal(freqtrade, value=(False, True, None)) assert freqtrade.handle_trade(trade) is True assert trade.sell_reason == SellType.SELL_SIGNAL.value @@ -3082,7 +3082,7 @@ def test_sell_profit_only_enable_loss(default_conf, limit_buy_order, limit_buy_o trade = Trade.query.first() trade.update(limit_buy_order) - patch_get_signal(freqtrade, value=(False, True, '')) + patch_get_signal(freqtrade, value=(False, True, None)) assert freqtrade.handle_trade(trade) is False @@ -3114,7 +3114,7 @@ def test_sell_profit_only_disable_loss(default_conf, limit_buy_order, limit_buy_ trade = Trade.query.first() trade.update(limit_buy_order) freqtrade.wallets.update() - patch_get_signal(freqtrade, value=(False, True, '')) + patch_get_signal(freqtrade, value=(False, True, None)) assert freqtrade.handle_trade(trade) is True assert trade.sell_reason == SellType.SELL_SIGNAL.value @@ -3143,7 +3143,7 @@ def test_sell_not_enough_balance(default_conf, limit_buy_order, limit_buy_order_ trade = Trade.query.first() amnt = trade.amount trade.update(limit_buy_order) - patch_get_signal(freqtrade, value=(False, True, '')) + patch_get_signal(freqtrade, value=(False, True, None)) mocker.patch('freqtrade.wallets.Wallets.get_free', MagicMock(return_value=trade.amount * 0.985)) assert freqtrade.handle_trade(trade) is True @@ -3262,11 +3262,11 @@ def test_ignore_roi_if_buy_signal(default_conf, limit_buy_order, limit_buy_order trade = Trade.query.first() trade.update(limit_buy_order) freqtrade.wallets.update() - patch_get_signal(freqtrade, value=(True, True, '')) + patch_get_signal(freqtrade, value=(True, True, None)) assert freqtrade.handle_trade(trade) is False # Test if buy-signal is absent (should sell due to roi = true) - patch_get_signal(freqtrade, value=(False, True, '')) + patch_get_signal(freqtrade, value=(False, True, None)) assert freqtrade.handle_trade(trade) is True assert trade.sell_reason == SellType.ROI.value @@ -3527,11 +3527,11 @@ def test_disable_ignore_roi_if_buy_signal(default_conf, limit_buy_order, limit_b trade = Trade.query.first() trade.update(limit_buy_order) # Sell due to min_roi_reached - patch_get_signal(freqtrade, value=(True, True, '')) + patch_get_signal(freqtrade, value=(True, True, None)) assert freqtrade.handle_trade(trade) is True # Test if buy-signal is absent - patch_get_signal(freqtrade, value=(False, True, '')) + patch_get_signal(freqtrade, value=(False, True, None)) assert freqtrade.handle_trade(trade) is True assert trade.sell_reason == SellType.SELL_SIGNAL.value @@ -4059,7 +4059,7 @@ def test_order_book_ask_strategy(default_conf, limit_buy_order_open, limit_buy_o freqtrade.wallets.update() assert trade.is_open is True - patch_get_signal(freqtrade, value=(False, True, '')) + patch_get_signal(freqtrade, value=(False, True, None)) assert freqtrade.handle_trade(trade) is True assert trade.close_rate_requested == order_book_l2.return_value['asks'][0][0] diff --git a/tests/test_persistence.py b/tests/test_persistence.py index af4979919..8b927be8b 100644 --- a/tests/test_persistence.py +++ b/tests/test_persistence.py @@ -861,7 +861,7 @@ def test_to_json(default_conf, fee): open_date=arrow.utcnow().shift(hours=-2).datetime, open_rate=0.123, exchange='binance', - buy_tag='', + buy_tag=None, open_order_id='dry_run_buy_12345' ) result = trade.to_json() @@ -911,7 +911,7 @@ def test_to_json(default_conf, fee): 'min_rate': None, 'max_rate': None, 'strategy': None, - 'buy_tag': '', + 'buy_tag': None, 'timeframe': None, 'exchange': 'binance', } From 46f2a20a98237aa86df2adf9804901635f66db16 Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Thu, 22 Jul 2021 02:00:51 +0700 Subject: [PATCH 16/33] run flake8 --- tests/strategy/test_interface.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/strategy/test_interface.py b/tests/strategy/test_interface.py index 792353f7a..751f08344 100644 --- a/tests/strategy/test_interface.py +++ b/tests/strategy/test_interface.py @@ -68,7 +68,9 @@ def test_analyze_pair_empty(default_conf, mocker, caplog, ohlcv_history): def test_get_signal_empty(default_conf, mocker, caplog): - assert (False, False, None) == _STRATEGY.get_signal('foo', default_conf['timeframe'], DataFrame()) + assert (False, False, None) == _STRATEGY.get_signal( + 'foo', default_conf['timeframe'], DataFrame() + ) assert log_has('Empty candle (OHLCV) data for pair foo', caplog) caplog.clear() From 25e329623ffad470264b52d8d54adc447149cebb Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Thu, 22 Jul 2021 02:11:54 +0700 Subject: [PATCH 17/33] change signature --- freqtrade/strategy/interface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 0206e8839..49fae0ed6 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -497,7 +497,7 @@ class IStrategy(ABC, HyperStrategyMixin): else: raise StrategyError(message) - def get_signal(self, pair: str, timeframe: str, dataframe: DataFrame) -> Tuple[bool, bool, str]: + def get_signal(self, pair: str, timeframe: str, dataframe: DataFrame) -> Tuple[bool, bool, Optional[str]]: """ Calculates current signal based based on the buy / sell columns of the dataframe. Used by Bot to get the signal to buy or sell From 643b6b950e951ab97ed2ed78a6532b5c8a23cb5e Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Thu, 22 Jul 2021 02:23:34 +0700 Subject: [PATCH 18/33] run flake8 --- freqtrade/strategy/interface.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 49fae0ed6..a6329aaa1 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -497,7 +497,12 @@ class IStrategy(ABC, HyperStrategyMixin): else: raise StrategyError(message) - def get_signal(self, pair: str, timeframe: str, dataframe: DataFrame) -> Tuple[bool, bool, Optional[str]]: + def get_signal( + self, + pair: str, + timeframe: str, + dataframe: DataFrame + ) -> Tuple[bool, bool, Optional[str]]: """ Calculates current signal based based on the buy / sell columns of the dataframe. Used by Bot to get the signal to buy or sell From dd809f756bb378ab787685e50eea62797b63d321 Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Thu, 22 Jul 2021 02:34:20 +0700 Subject: [PATCH 19/33] run mypy --- freqtrade/freqtradebot.py | 2 +- freqtrade/persistence/models.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 7756aa1d7..d0ffddd72 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -466,7 +466,7 @@ class FreqtradeBot(LoggingMixin): return False def execute_buy(self, pair: str, stake_amount: float, price: Optional[float] = None, - forcebuy: bool = False, buy_tag: str = '') -> bool: + forcebuy: bool = False, buy_tag: Optional[str] = None) -> bool: """ Executes a limit buy for the given pair :param pair: pair for which we want to create a LIMIT_BUY diff --git a/freqtrade/persistence/models.py b/freqtrade/persistence/models.py index f42166762..43fbec8c0 100644 --- a/freqtrade/persistence/models.py +++ b/freqtrade/persistence/models.py @@ -257,7 +257,7 @@ class LocalTrade(): sell_reason: str = '' sell_order_status: str = '' strategy: str = '' - buy_tag: str = '' + buy_tag: Optional[str] = None timeframe: Optional[int] = None def __init__(self, **kwargs): From b01daa8bbca7d7937dfdf5c85675eaf9d598d646 Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Thu, 22 Jul 2021 13:09:05 +0700 Subject: [PATCH 20/33] expose buy_tag to api --- freqtrade/rpc/api_server/api_schemas.py | 1 + tests/rpc/test_rpc_apiserver.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/freqtrade/rpc/api_server/api_schemas.py b/freqtrade/rpc/api_server/api_schemas.py index c6b6a6d28..318762136 100644 --- a/freqtrade/rpc/api_server/api_schemas.py +++ b/freqtrade/rpc/api_server/api_schemas.py @@ -151,6 +151,7 @@ class TradeSchema(BaseModel): amount_requested: float stake_amount: float strategy: str + buy_tag: Optional[str] timeframe: int fee_open: Optional[float] fee_open_cost: Optional[float] diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index f7d935b64..fef44cab2 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -875,6 +875,7 @@ def test_api_status(botclient, mocker, ticker, fee, markets): 'sell_reason': None, 'sell_order_status': None, 'strategy': 'DefaultStrategy', + 'buy_tag': None, 'timeframe': 5, 'exchange': 'binance', } @@ -1029,6 +1030,7 @@ def test_api_forcebuy(botclient, mocker, fee): 'sell_reason': None, 'sell_order_status': None, 'strategy': 'DefaultStrategy', + 'buy_tag': None, 'timeframe': 5, 'exchange': 'binance', } From 65b4705b67b932903737dc4a70d67250435ae1a1 Mon Sep 17 00:00:00 2001 From: Kevin Julian Date: Fri, 23 Jul 2021 01:16:10 +0700 Subject: [PATCH 21/33] Update docs/strategy-advanced.md Co-authored-by: Matthias --- docs/strategy-advanced.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/strategy-advanced.md b/docs/strategy-advanced.md index 8ae0c1d16..32113fede 100644 --- a/docs/strategy-advanced.md +++ b/docs/strategy-advanced.md @@ -116,7 +116,7 @@ See [Dataframe access](#dataframe-access) for more information about dataframe u ## Buy Tag -When your strategy has multiple buy signal, you can name it. +When your strategy has multiple buy signals, you can name the signal that triggered. Then you can access you buy signal on `custom_sell` ```python From dd923c34713b9d2a316261e516294c4a4b4f929c Mon Sep 17 00:00:00 2001 From: Kevin Julian Date: Fri, 23 Jul 2021 01:16:24 +0700 Subject: [PATCH 22/33] Update docs/strategy-advanced.md Co-authored-by: Matthias --- docs/strategy-advanced.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/strategy-advanced.md b/docs/strategy-advanced.md index 32113fede..62b1533a6 100644 --- a/docs/strategy-advanced.md +++ b/docs/strategy-advanced.md @@ -126,7 +126,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', 'buy_tag']] = (1, 'buy_signal_rsi') return dataframe From 5fe18be4b57163971329905afb9e8833f77ed154 Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Fri, 23 Jul 2021 01:25:15 +0700 Subject: [PATCH 23/33] add note buy_tag and split 3 assignment for get_signal --- docs/strategy-advanced.md | 4 ++++ freqtrade/strategy/interface.py | 7 ++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/docs/strategy-advanced.md b/docs/strategy-advanced.md index 62b1533a6..fb78a8796 100644 --- a/docs/strategy-advanced.md +++ b/docs/strategy-advanced.md @@ -140,6 +140,10 @@ def custom_sell(self, pair: str, trade: Trade, current_time: datetime, current_r ``` +!!! Note + `buy_tag` is limited to 100 characters, remaining data will be truncated. + + ## Custom stoploss The stoploss price can only ever move upwards - if the stoploss value returned from `custom_stoploss` would result in a lower stoploss price than was previously set, it will be ignored. The traditional `stoploss` value serves as an absolute lower level and will be instated as the initial stoploss. diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index a6329aaa1..35c162c38 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -530,9 +530,10 @@ class IStrategy(ABC, HyperStrategyMixin): ) return False, False, None - (buy, sell, buy_tag) = latest[SignalType.BUY.value] == 1,\ - latest[SignalType.SELL.value] == 1,\ - latest.get(SignalTagType.BUY_TAG.value, None) + buy = latest[SignalType.BUY.value] == 1 + sell = latest[SignalType.SELL.value] == 1 + buy_tag = latest.get(SignalTagType.BUY_TAG.value, None) + logger.debug('trigger: %s (pair=%s) buy=%s sell=%s', latest['date'], pair, str(buy), str(sell)) timeframe_seconds = timeframe_to_seconds(timeframe) From 65fc094c9f4ac42e5092aefab5aa5160b076207e Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Fri, 23 Jul 2021 09:31:51 +0700 Subject: [PATCH 24/33] add to webhook-config --- docs/webhook-config.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/webhook-config.md b/docs/webhook-config.md index 8ce6edc18..288afc384 100644 --- a/docs/webhook-config.md +++ b/docs/webhook-config.md @@ -83,6 +83,7 @@ Possible parameters are: * `fiat_currency` * `order_type` * `current_rate` +* `buy_tag` ### Webhookbuycancel @@ -100,6 +101,7 @@ Possible parameters are: * `fiat_currency` * `order_type` * `current_rate` +* `buy_tag` ### Webhookbuyfill @@ -115,6 +117,7 @@ Possible parameters are: * `stake_amount` * `stake_currency` * `fiat_currency` +* `buy_tag` ### Webhooksell From aea5da0c738bc281bc9513f2c18ac1812ffdddda Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Fri, 23 Jul 2021 11:42:43 +0700 Subject: [PATCH 25/33] changes testcase --- freqtrade/optimize/backtesting.py | 13 +- freqtrade/strategy/interface.py | 2 +- tests/optimize/__init__.py | 6 +- tests/optimize/test_backtest_detail.py | 442 ++++++++++++------------- tests/optimize/test_backtesting.py | 2 +- 5 files changed, 236 insertions(+), 229 deletions(-) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index fdf341e21..0c586883b 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -210,7 +210,7 @@ class Backtesting: """ # Every change to this headers list must evaluate further usages of the resulting tuple # and eventually change the constants for indexes at the top - headers = ['date', 'buy', 'open', 'close', 'sell', 'low', 'high', 'buy_tag'] + headers = ['date', 'buy', 'open', 'close', 'sell', 'low', 'high'] data: Dict = {} self.progress.init_step(BacktestState.CONVERT, len(processed)) @@ -218,10 +218,13 @@ class Backtesting: for pair, pair_data in processed.items(): self.check_abort() self.progress.increment() + has_buy_tag = 'buy_tag' in pair_data + headers = headers + ['buy_tag'] if has_buy_tag else headers if not pair_data.empty: pair_data.loc[:, 'buy'] = 0 # cleanup if buy_signal is exist pair_data.loc[:, 'sell'] = 0 # cleanup if sell_signal is exist - pair_data.loc[:, 'buy_tag'] = '' # cleanup if buy_tag is exist + if has_buy_tag: + pair_data.loc[:, 'buy_tag'] = None # cleanup if buy_tag is exist df_analyzed = self.strategy.advise_sell( self.strategy.advise_buy(pair_data, {'pair': pair}), {'pair': pair})[headers].copy() @@ -230,7 +233,8 @@ class Backtesting: # from the previous candle df_analyzed.loc[:, 'buy'] = df_analyzed.loc[:, 'buy'].shift(1) df_analyzed.loc[:, 'sell'] = df_analyzed.loc[:, 'sell'].shift(1) - df_analyzed.loc[:, 'buy_tag'] = df_analyzed.loc[:, 'buy_tag'].shift(1) + if has_buy_tag: + df_analyzed.loc[:, 'buy_tag'] = df_analyzed.loc[:, 'buy_tag'].shift(1) df_analyzed.drop(df_analyzed.head(1).index, inplace=True) @@ -361,6 +365,7 @@ class Backtesting: if stake_amount and (not min_stake_amount or stake_amount > min_stake_amount): # Enter trade + has_buy_tag = len(row) >= BUY_TAG_IDX + 1 trade = LocalTrade( pair=pair, open_rate=row[OPEN_IDX], @@ -370,7 +375,7 @@ class Backtesting: fee_open=self.fee, fee_close=self.fee, is_open=True, - buy_tag=row[BUY_TAG_IDX], + buy_tag=row[BUY_TAG_IDX] if has_buy_tag else None, exchange='backtesting', ) return trade diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 35c162c38..f10a12fa9 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -422,7 +422,7 @@ class IStrategy(ABC, HyperStrategyMixin): logger.debug("Skipping TA Analysis for already analyzed candle") dataframe['buy'] = 0 dataframe['sell'] = 0 - dataframe['buy_tag'] = '' + dataframe['buy_tag'] = None # Other Defs in strategy that want to be called every loop here # twitter_sell = self.watch_twitter_feed(dataframe, metadata) diff --git a/tests/optimize/__init__.py b/tests/optimize/__init__.py index 240f3f871..80fce9ca5 100644 --- a/tests/optimize/__init__.py +++ b/tests/optimize/__init__.py @@ -18,7 +18,7 @@ class BTrade(NamedTuple): sell_reason: SellType open_tick: int close_tick: int - buy_tag: Optional[str] = '' + buy_tag: Optional[str] = None class BTContainer(NamedTuple): @@ -44,7 +44,9 @@ def _get_frame_time_from_offset(offset): def _build_backtest_dataframe(data): - columns = ['date', 'open', 'high', 'low', 'close', 'volume', 'buy', 'sell', 'buy_tag'] + + columns = ['date', 'open', 'high', 'low', 'close', 'volume', 'buy', 'sell'] + columns = columns + ['buy_tag'] if len(data[0]) == 9 else columns frame = DataFrame.from_records(data, columns=columns) frame['date'] = frame['date'].apply(_get_frame_time_from_offset) diff --git a/tests/optimize/test_backtest_detail.py b/tests/optimize/test_backtest_detail.py index f7077ef56..0d4647308 100644 --- a/tests/optimize/test_backtest_detail.py +++ b/tests/optimize/test_backtest_detail.py @@ -14,13 +14,13 @@ from tests.optimize import (BTContainer, BTrade, _build_backtest_dataframe, # Test 0: Sell with signal sell in candle 3 # Test with Stop-loss at 1% tc0 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], # enter trade (signal on last candle) - [2, 4987, 5012, 4986, 4600, 6172, 0, 0, ''], # exit with stoploss hit - [3, 5010, 5000, 4980, 5010, 6172, 0, 1, ''], - [4, 5010, 4987, 4977, 4995, 6172, 0, 0, ''], - [5, 4995, 4995, 4995, 4950, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) + [2, 4987, 5012, 4986, 4600, 6172, 0, 0], # exit with stoploss hit + [3, 5010, 5000, 4980, 5010, 6172, 0, 1], + [4, 5010, 4987, 4977, 4995, 6172, 0, 0], + [5, 4995, 4995, 4995, 4950, 6172, 0, 0]], stop_loss=-0.01, roi={"0": 1}, profit_perc=0.002, use_sell_signal=True, trades=[BTrade(sell_reason=SellType.SELL_SIGNAL, open_tick=1, close_tick=4)] ) @@ -28,13 +28,13 @@ tc0 = BTContainer(data=[ # Test 1: Stop-Loss Triggered 1% loss # Test with Stop-loss at 1% tc1 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], # enter trade (signal on last candle) - [2, 4987, 5012, 4600, 4600, 6172, 0, 0, ''], # exit with stoploss hit - [3, 4975, 5000, 4980, 4977, 6172, 0, 0, ''], - [4, 4977, 4987, 4977, 4995, 6172, 0, 0, ''], - [5, 4995, 4995, 4995, 4950, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) + [2, 4987, 5012, 4600, 4600, 6172, 0, 0], # exit with stoploss hit + [3, 4975, 5000, 4980, 4977, 6172, 0, 0], + [4, 4977, 4987, 4977, 4995, 6172, 0, 0], + [5, 4995, 4995, 4995, 4950, 6172, 0, 0]], stop_loss=-0.01, roi={"0": 1}, profit_perc=-0.01, trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=2)] ) @@ -43,13 +43,13 @@ tc1 = BTContainer(data=[ # Test 2: Minus 4% Low, minus 1% close # Test with Stop-Loss at 3% tc2 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], # enter trade (signal on last candle) - [2, 4987, 5012, 4962, 4975, 6172, 0, 0, ''], - [3, 4975, 5000, 4800, 4962, 6172, 0, 0, ''], # exit with stoploss hit - [4, 4962, 4987, 4937, 4950, 6172, 0, 0, ''], - [5, 4950, 4975, 4925, 4950, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) + [2, 4987, 5012, 4962, 4975, 6172, 0, 0], + [3, 4975, 5000, 4800, 4962, 6172, 0, 0], # exit with stoploss hit + [4, 4962, 4987, 4937, 4950, 6172, 0, 0], + [5, 4950, 4975, 4925, 4950, 6172, 0, 0]], stop_loss=-0.03, roi={"0": 1}, profit_perc=-0.03, trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=3)] ) @@ -62,14 +62,14 @@ tc2 = BTContainer(data=[ # Trade-A: Stop-Loss Triggered 2% Loss # Trade-B: Stop-Loss Triggered 2% Loss tc3 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], # enter trade (signal on last candle) - [2, 4987, 5012, 4800, 4975, 6172, 0, 0, ''], # exit with stoploss hit - [3, 4975, 5000, 4950, 4962, 6172, 1, 0, ''], - [4, 4975, 5000, 4950, 4962, 6172, 0, 0, ''], # enter trade 2 (signal on last candle) - [5, 4962, 4987, 4000, 4000, 6172, 0, 0, ''], # exit with stoploss hit - [6, 4950, 4975, 4975, 4950, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) + [2, 4987, 5012, 4800, 4975, 6172, 0, 0], # exit with stoploss hit + [3, 4975, 5000, 4950, 4962, 6172, 1, 0], + [4, 4975, 5000, 4950, 4962, 6172, 0, 0], # enter trade 2 (signal on last candle) + [5, 4962, 4987, 4000, 4000, 6172, 0, 0], # exit with stoploss hit + [6, 4950, 4975, 4975, 4950, 6172, 0, 0]], stop_loss=-0.02, roi={"0": 1}, profit_perc=-0.04, trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=2), BTrade(sell_reason=SellType.STOP_LOSS, open_tick=4, close_tick=5)] @@ -80,13 +80,13 @@ tc3 = BTContainer(data=[ # Test with Stop-loss at 2% ROI 6% # Stop-Loss Triggered 2% Loss tc4 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], # enter trade (signal on last candle) - [2, 4987, 5750, 4850, 5750, 6172, 0, 0, ''], # Exit with stoploss hit - [3, 4975, 5000, 4950, 4962, 6172, 0, 0, ''], - [4, 4962, 4987, 4937, 4950, 6172, 0, 0, ''], - [5, 4950, 4975, 4925, 4950, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) + [2, 4987, 5750, 4850, 5750, 6172, 0, 0], # Exit with stoploss hit + [3, 4975, 5000, 4950, 4962, 6172, 0, 0], + [4, 4962, 4987, 4937, 4950, 6172, 0, 0], + [5, 4950, 4975, 4925, 4950, 6172, 0, 0]], stop_loss=-0.02, roi={"0": 0.06}, profit_perc=-0.02, trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=2)] ) @@ -94,13 +94,13 @@ tc4 = BTContainer(data=[ # Test 5: Drops 0.5% Closes +20%, ROI triggers 3% Gain # stop-loss: 1%, ROI: 3% tc5 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5025, 4980, 4987, 6172, 1, 0, ''], - [1, 5000, 5025, 4980, 4987, 6172, 0, 0, ''], # enter trade (signal on last candle) - [2, 4987, 5025, 4975, 4987, 6172, 0, 0, ''], - [3, 4975, 6000, 4975, 6000, 6172, 0, 0, ''], # ROI - [4, 4962, 4987, 4972, 4950, 6172, 0, 0, ''], - [5, 4950, 4975, 4925, 4950, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5025, 4980, 4987, 6172, 1, 0], + [1, 5000, 5025, 4980, 4987, 6172, 0, 0], # enter trade (signal on last candle) + [2, 4987, 5025, 4975, 4987, 6172, 0, 0], + [3, 4975, 6000, 4975, 6000, 6172, 0, 0], # ROI + [4, 4962, 4987, 4972, 4950, 6172, 0, 0], + [5, 4950, 4975, 4925, 4950, 6172, 0, 0]], stop_loss=-0.01, roi={"0": 0.03}, profit_perc=0.03, trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=3)] ) @@ -108,13 +108,13 @@ tc5 = BTContainer(data=[ # Test 6: Drops 3% / Recovers 6% Positive / Closes 1% positve, Stop-Loss triggers 2% Loss # stop-loss: 2% ROI: 5% tc6 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], # enter trade (signal on last candle) - [2, 4987, 5300, 4850, 5050, 6172, 0, 0, ''], # Exit with stoploss - [3, 4975, 5000, 4950, 4962, 6172, 0, 0, ''], - [4, 4962, 4987, 4972, 4950, 6172, 0, 0, ''], - [5, 4950, 4975, 4925, 4950, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) + [2, 4987, 5300, 4850, 5050, 6172, 0, 0], # Exit with stoploss + [3, 4975, 5000, 4950, 4962, 6172, 0, 0], + [4, 4962, 4987, 4972, 4950, 6172, 0, 0], + [5, 4950, 4975, 4925, 4950, 6172, 0, 0]], stop_loss=-0.02, roi={"0": 0.05}, profit_perc=-0.02, trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=2)] ) @@ -122,13 +122,13 @@ tc6 = BTContainer(data=[ # Test 7: 6% Positive / 1% Negative / Close 1% Positve, ROI Triggers 3% Gain # stop-loss: 2% ROI: 3% tc7 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], - [2, 4987, 5300, 4950, 5050, 6172, 0, 0, ''], - [3, 4975, 5000, 4950, 4962, 6172, 0, 0, ''], - [4, 4962, 4987, 4972, 4950, 6172, 0, 0, ''], - [5, 4950, 4975, 4925, 4950, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0], + [2, 4987, 5300, 4950, 5050, 6172, 0, 0], + [3, 4975, 5000, 4950, 4962, 6172, 0, 0], + [4, 4962, 4987, 4972, 4950, 6172, 0, 0], + [5, 4950, 4975, 4925, 4950, 6172, 0, 0]], stop_loss=-0.02, roi={"0": 0.03}, profit_perc=0.03, trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=2)] ) @@ -137,12 +137,12 @@ tc7 = BTContainer(data=[ # Test 8: trailing_stop should raise so candle 3 causes a stoploss. # stop-loss: 10%, ROI: 10% (should not apply), stoploss adjusted in candle 2 tc8 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], - [1, 5000, 5050, 4950, 5000, 6172, 0, 0, ''], - [2, 5000, 5250, 4750, 4850, 6172, 0, 0, ''], - [3, 4850, 5050, 4650, 4750, 6172, 0, 0, ''], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5050, 4950, 5000, 6172, 0, 0], + [2, 5000, 5250, 4750, 4850, 6172, 0, 0], + [3, 4850, 5050, 4650, 4750, 6172, 0, 0], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], stop_loss=-0.10, roi={"0": 0.10}, profit_perc=-0.055, trailing_stop=True, trades=[BTrade(sell_reason=SellType.TRAILING_STOP_LOSS, open_tick=1, close_tick=3)] ) @@ -151,12 +151,12 @@ tc8 = BTContainer(data=[ # Test 9: trailing_stop should raise - high and low in same candle. # stop-loss: 10%, ROI: 10% (should not apply), stoploss adjusted in candle 3 tc9 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], - [1, 5000, 5050, 4950, 5000, 6172, 0, 0, ''], - [2, 5000, 5050, 4950, 5000, 6172, 0, 0, ''], - [3, 5000, 5200, 4550, 4850, 6172, 0, 0, ''], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5050, 4950, 5000, 6172, 0, 0], + [2, 5000, 5050, 4950, 5000, 6172, 0, 0], + [3, 5000, 5200, 4550, 4850, 6172, 0, 0], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], stop_loss=-0.10, roi={"0": 0.10}, profit_perc=-0.064, trailing_stop=True, trades=[BTrade(sell_reason=SellType.TRAILING_STOP_LOSS, open_tick=1, close_tick=3)] ) @@ -165,12 +165,12 @@ tc9 = BTContainer(data=[ # without applying trailing_stop_positive since stoploss_offset is at 10%. # stop-loss: 10%, ROI: 10% (should not apply), stoploss adjusted candle 2 tc10 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], - [1, 5000, 5050, 4950, 5100, 6172, 0, 0, ''], - [2, 5100, 5251, 5100, 5100, 6172, 0, 0, ''], - [3, 4850, 5050, 4650, 4750, 6172, 0, 0, ''], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5050, 4950, 5100, 6172, 0, 0], + [2, 5100, 5251, 5100, 5100, 6172, 0, 0], + [3, 4850, 5050, 4650, 4750, 6172, 0, 0], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], stop_loss=-0.10, roi={"0": 0.10}, profit_perc=-0.1, trailing_stop=True, trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.10, trailing_stop_positive=0.03, @@ -181,12 +181,12 @@ tc10 = BTContainer(data=[ # applying a positive trailing stop of 3% since stop_positive_offset is reached. # stop-loss: 10%, ROI: 10% (should not apply), stoploss adjusted candle 2 tc11 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], - [1, 5000, 5050, 4950, 5100, 6172, 0, 0, ''], - [2, 5100, 5251, 5100, 5100, 6172, 0, 0, ''], - [3, 5000, 5150, 4650, 4750, 6172, 0, 0, ''], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5050, 4950, 5100, 6172, 0, 0], + [2, 5100, 5251, 5100, 5100, 6172, 0, 0], + [3, 5000, 5150, 4650, 4750, 6172, 0, 0], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], stop_loss=-0.10, roi={"0": 0.10}, profit_perc=0.019, trailing_stop=True, trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.05, trailing_stop_positive=0.03, @@ -197,12 +197,12 @@ tc11 = BTContainer(data=[ # applying a positive trailing stop of 3% since stop_positive_offset is reached. # stop-loss: 10%, ROI: 10% (should not apply), stoploss adjusted candle 2 tc12 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], - [1, 5000, 5050, 4950, 5100, 6172, 0, 0, ''], - [2, 5100, 5251, 4650, 5100, 6172, 0, 0, ''], - [3, 4850, 5050, 4650, 4750, 6172, 0, 0, ''], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5050, 4950, 5100, 6172, 0, 0], + [2, 5100, 5251, 4650, 5100, 6172, 0, 0], + [3, 4850, 5050, 4650, 4750, 6172, 0, 0], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], stop_loss=-0.10, roi={"0": 0.10}, profit_perc=0.019, trailing_stop=True, trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.05, trailing_stop_positive=0.03, @@ -212,12 +212,12 @@ tc12 = BTContainer(data=[ # Test 13: Buy and sell ROI on same candle # stop-loss: 10% (should not apply), ROI: 1% tc13 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], - [1, 5000, 5100, 4950, 5100, 6172, 0, 0, ''], - [2, 5100, 5251, 4850, 5100, 6172, 0, 0, ''], - [3, 4850, 5050, 4850, 4750, 6172, 0, 0, ''], - [4, 4750, 4950, 4850, 4750, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5100, 4950, 5100, 6172, 0, 0], + [2, 5100, 5251, 4850, 5100, 6172, 0, 0], + [3, 4850, 5050, 4850, 4750, 6172, 0, 0], + [4, 4750, 4950, 4850, 4750, 6172, 0, 0]], stop_loss=-0.10, roi={"0": 0.01}, profit_perc=0.01, trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=1)] ) @@ -225,12 +225,12 @@ tc13 = BTContainer(data=[ # Test 14 - Buy and Stoploss on same candle # stop-loss: 5%, ROI: 10% (should not apply) tc14 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], - [1, 5000, 5100, 4600, 5100, 6172, 0, 0, ''], - [2, 5100, 5251, 4850, 5100, 6172, 0, 0, ''], - [3, 4850, 5050, 4850, 4750, 6172, 0, 0, ''], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5100, 4600, 5100, 6172, 0, 0], + [2, 5100, 5251, 4850, 5100, 6172, 0, 0], + [3, 4850, 5050, 4850, 4750, 6172, 0, 0], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], stop_loss=-0.05, roi={"0": 0.10}, profit_perc=-0.05, trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=1)] ) @@ -239,12 +239,12 @@ tc14 = BTContainer(data=[ # Test 15 - Buy and ROI on same candle, followed by buy and Stoploss on next candle # stop-loss: 5%, ROI: 10% (should not apply) tc15 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], - [1, 5000, 5100, 4900, 5100, 6172, 1, 0, ''], - [2, 5100, 5251, 4650, 5100, 6172, 0, 0, ''], - [3, 4850, 5050, 4850, 4750, 6172, 0, 0, ''], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5100, 4900, 5100, 6172, 1, 0], + [2, 5100, 5251, 4650, 5100, 6172, 0, 0], + [3, 4850, 5050, 4850, 4750, 6172, 0, 0], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], stop_loss=-0.05, roi={"0": 0.01}, profit_perc=-0.04, trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=1), BTrade(sell_reason=SellType.STOP_LOSS, open_tick=2, close_tick=2)] @@ -254,13 +254,13 @@ tc15 = BTContainer(data=[ # Causes negative profit even though sell-reason is ROI. # stop-loss: 10%, ROI: 10% (should not apply), -100% after 65 minutes (limits trade duration) tc16 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], - [2, 4987, 5300, 4950, 5050, 6172, 0, 0, ''], - [3, 4975, 5000, 4940, 4962, 6172, 0, 0, ''], # ForceSell on ROI (roi=-1) - [4, 4962, 4987, 4972, 4950, 6172, 0, 0, ''], - [5, 4950, 4975, 4925, 4950, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0], + [2, 4987, 5300, 4950, 5050, 6172, 0, 0], + [3, 4975, 5000, 4940, 4962, 6172, 0, 0], # ForceSell on ROI (roi=-1) + [4, 4962, 4987, 4972, 4950, 6172, 0, 0], + [5, 4950, 4975, 4925, 4950, 6172, 0, 0]], stop_loss=-0.10, roi={"0": 0.10, "65": -1}, profit_perc=-0.012, trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=3)] ) @@ -270,13 +270,13 @@ tc16 = BTContainer(data=[ # stop-loss: 10%, ROI: 10% (should not apply), -100% after 100 minutes (limits trade duration) # Uses open as sell-rate (special case) - since the roi-time is a multiple of the timeframe. tc17 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], - [2, 4987, 5300, 4950, 5050, 6172, 0, 0, ''], - [3, 4980, 5000, 4940, 4962, 6172, 0, 0, ''], # ForceSell on ROI (roi=-1) - [4, 4962, 4987, 4972, 4950, 6172, 0, 0, ''], - [5, 4950, 4975, 4925, 4950, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0], + [2, 4987, 5300, 4950, 5050, 6172, 0, 0], + [3, 4980, 5000, 4940, 4962, 6172, 0, 0], # ForceSell on ROI (roi=-1) + [4, 4962, 4987, 4972, 4950, 6172, 0, 0], + [5, 4950, 4975, 4925, 4950, 6172, 0, 0]], stop_loss=-0.10, roi={"0": 0.10, "120": -1}, profit_perc=-0.004, trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=3)] ) @@ -286,13 +286,13 @@ tc17 = BTContainer(data=[ # stop-loss: 10%, ROI: 10% (should not apply), -100% after 100 minutes (limits trade duration) # uses open_rate as sell-price tc18 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], - [2, 4987, 5300, 4950, 5200, 6172, 0, 0, ''], - [3, 5200, 5220, 4940, 4962, 6172, 0, 0, ''], # Sell on ROI (sells on open) - [4, 4962, 4987, 4972, 4950, 6172, 0, 0, ''], - [5, 4950, 4975, 4925, 4950, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0], + [2, 4987, 5300, 4950, 5200, 6172, 0, 0], + [3, 5200, 5220, 4940, 4962, 6172, 0, 0], # Sell on ROI (sells on open) + [4, 4962, 4987, 4972, 4950, 6172, 0, 0], + [5, 4950, 4975, 4925, 4950, 6172, 0, 0]], stop_loss=-0.10, roi={"0": 0.10, "120": 0.01}, profit_perc=0.04, trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=3)] ) @@ -301,13 +301,13 @@ tc18 = BTContainer(data=[ # stop-loss: 10%, ROI: 10% (should not apply), -100% after 100 minutes (limits trade duration) # uses calculated ROI (1%) as sell rate, otherwise identical to tc18 tc19 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], - [2, 4987, 5300, 4950, 5200, 6172, 0, 0, ''], - [3, 5000, 5300, 4940, 4962, 6172, 0, 0, ''], # Sell on ROI - [4, 4962, 4987, 4972, 4950, 6172, 0, 0, ''], - [5, 4550, 4975, 4925, 4950, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0], + [2, 4987, 5300, 4950, 5200, 6172, 0, 0], + [3, 5000, 5300, 4940, 4962, 6172, 0, 0], # Sell on ROI + [4, 4962, 4987, 4972, 4950, 6172, 0, 0], + [5, 4550, 4975, 4925, 4950, 6172, 0, 0]], stop_loss=-0.10, roi={"0": 0.10, "120": 0.01}, profit_perc=0.01, trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=3)] ) @@ -316,13 +316,13 @@ tc19 = BTContainer(data=[ # stop-loss: 10%, ROI: 10% (should not apply), -100% after 100 minutes (limits trade duration) # uses calculated ROI (1%) as sell rate, otherwise identical to tc18 tc20 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], - [2, 4987, 5300, 4950, 5200, 6172, 0, 0, ''], - [3, 5200, 5300, 4940, 4962, 6172, 0, 0, ''], # Sell on ROI - [4, 4962, 4987, 4972, 4950, 6172, 0, 0, ''], - [5, 4550, 4975, 4925, 4950, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0], + [2, 4987, 5300, 4950, 5200, 6172, 0, 0], + [3, 5200, 5300, 4940, 4962, 6172, 0, 0], # Sell on ROI + [4, 4962, 4987, 4972, 4950, 6172, 0, 0], + [5, 4550, 4975, 4925, 4950, 6172, 0, 0]], stop_loss=-0.10, roi={"0": 0.10, "119": 0.01}, profit_perc=0.01, trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=3)] ) @@ -332,12 +332,12 @@ tc20 = BTContainer(data=[ # which cannot happen in reality # stop-loss: 10%, ROI: 4%, Trailing stop adjusted at the sell candle tc21 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], - [1, 5000, 5050, 4950, 5100, 6172, 0, 0, ''], - [2, 5100, 5251, 4650, 5100, 6172, 0, 0, ''], - [3, 4850, 5050, 4650, 4750, 6172, 0, 0, ''], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5050, 4950, 5100, 6172, 0, 0], + [2, 5100, 5251, 4650, 5100, 6172, 0, 0], + [3, 4850, 5050, 4650, 4750, 6172, 0, 0], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], stop_loss=-0.10, roi={"0": 0.04}, profit_perc=0.04, trailing_stop=True, trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.05, trailing_stop_positive=0.03, @@ -348,12 +348,12 @@ tc21 = BTContainer(data=[ # applying a positive trailing stop of 3% - ROI should apply before trailing stop. # stop-loss: 10%, ROI: 4%, stoploss adjusted candle 2 tc22 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], - [1, 5000, 5050, 4950, 5100, 6172, 0, 0, ''], - [2, 5100, 5251, 5100, 5100, 6172, 0, 0, ''], - [3, 4850, 5050, 4650, 4750, 6172, 0, 0, ''], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5050, 4950, 5100, 6172, 0, 0], + [2, 5100, 5251, 5100, 5100, 6172, 0, 0], + [3, 4850, 5050, 4650, 4750, 6172, 0, 0], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], stop_loss=-0.10, roi={"0": 0.04}, profit_perc=0.04, trailing_stop=True, trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.05, trailing_stop_positive=0.03, @@ -367,12 +367,12 @@ tc22 = BTContainer(data=[ # Stoploss would trigger in this candle too, but it's no longer relevant. # stop-loss: 10%, ROI: 4%, stoploss adjusted candle 2, ROI adjusted in candle 3 (causing the sell) tc23 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], - [1, 5000, 5050, 4950, 5100, 6172, 0, 0, ''], - [2, 5100, 5251, 5100, 5100, 6172, 0, 0, ''], - [3, 4850, 5251, 4650, 4750, 6172, 0, 0, ''], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5050, 4950, 5100, 6172, 0, 0], + [2, 5100, 5251, 5100, 5100, 6172, 0, 0], + [3, 4850, 5251, 4650, 4750, 6172, 0, 0], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], stop_loss=-0.10, roi={"0": 0.1, "119": 0.03}, profit_perc=0.03, trailing_stop=True, trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.05, trailing_stop_positive=0.03, @@ -383,13 +383,13 @@ tc23 = BTContainer(data=[ # Stoploss at 1%. # Stoploss wins over Sell-signal (because sell-signal is acted on in the next candle) tc24 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], # enter trade (signal on last candle) - [2, 4987, 5012, 4986, 4600, 6172, 0, 0, ''], - [3, 5010, 5000, 4855, 5010, 6172, 0, 1, ''], # Triggers stoploss + sellsignal - [4, 5010, 4987, 4977, 4995, 6172, 0, 0, ''], - [5, 4995, 4995, 4995, 4950, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) + [2, 4987, 5012, 4986, 4600, 6172, 0, 0], + [3, 5010, 5000, 4855, 5010, 6172, 0, 1], # Triggers stoploss + sellsignal + [4, 5010, 4987, 4977, 4995, 6172, 0, 0], + [5, 4995, 4995, 4995, 4950, 6172, 0, 0]], stop_loss=-0.01, roi={"0": 1}, profit_perc=-0.01, use_sell_signal=True, trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=3)] ) @@ -398,13 +398,13 @@ tc24 = BTContainer(data=[ # Stoploss at 1%. # Sell-signal wins over stoploss tc25 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], # enter trade (signal on last candle) - [2, 4987, 5012, 4986, 4600, 6172, 0, 0, ''], - [3, 5010, 5000, 4986, 5010, 6172, 0, 1, ''], - [4, 5010, 4987, 4855, 4995, 6172, 0, 0, ''], # Triggers stoploss + sellsignal acted on - [5, 4995, 4995, 4995, 4950, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) + [2, 4987, 5012, 4986, 4600, 6172, 0, 0], + [3, 5010, 5000, 4986, 5010, 6172, 0, 1], + [4, 5010, 4987, 4855, 4995, 6172, 0, 0], # Triggers stoploss + sellsignal acted on + [5, 4995, 4995, 4995, 4950, 6172, 0, 0]], stop_loss=-0.01, roi={"0": 1}, profit_perc=0.002, use_sell_signal=True, trades=[BTrade(sell_reason=SellType.SELL_SIGNAL, open_tick=1, close_tick=4)] ) @@ -413,13 +413,13 @@ tc25 = BTContainer(data=[ # Stoploss at 10% (irrelevant), ROI at 5% (will trigger) # Sell-signal wins over stoploss tc26 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], # enter trade (signal on last candle) - [2, 4987, 5012, 4986, 4600, 6172, 0, 0, ''], - [3, 5010, 5251, 4986, 5010, 6172, 0, 1, ''], # Triggers ROI, sell-signal - [4, 5010, 4987, 4855, 4995, 6172, 0, 0, ''], - [5, 4995, 4995, 4995, 4950, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) + [2, 4987, 5012, 4986, 4600, 6172, 0, 0], + [3, 5010, 5251, 4986, 5010, 6172, 0, 1], # Triggers ROI, sell-signal + [4, 5010, 4987, 4855, 4995, 6172, 0, 0], + [5, 4995, 4995, 4995, 4950, 6172, 0, 0]], stop_loss=-0.10, roi={"0": 0.05}, profit_perc=0.05, use_sell_signal=True, trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=3)] ) @@ -429,13 +429,13 @@ tc26 = BTContainer(data=[ # TODO: figure out if sell-signal should win over ROI # Sell-signal wins over stoploss tc27 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], # enter trade (signal on last candle) - [2, 4987, 5012, 4986, 4600, 6172, 0, 0, ''], - [3, 5010, 5012, 4986, 5010, 6172, 0, 1, ''], # sell-signal - [4, 5010, 5251, 4855, 4995, 6172, 0, 0, ''], # Triggers ROI, sell-signal acted on - [5, 4995, 4995, 4995, 4950, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) + [2, 4987, 5012, 4986, 4600, 6172, 0, 0], + [3, 5010, 5012, 4986, 5010, 6172, 0, 1], # sell-signal + [4, 5010, 5251, 4855, 4995, 6172, 0, 0], # Triggers ROI, sell-signal acted on + [5, 4995, 4995, 4995, 4950, 6172, 0, 0]], stop_loss=-0.10, roi={"0": 0.05}, profit_perc=0.05, use_sell_signal=True, trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=4)] ) @@ -445,12 +445,12 @@ tc27 = BTContainer(data=[ # therefore "open" will be used # stop-loss: 10%, ROI: 10% (should not apply), stoploss adjusted candle 2 tc28 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], - [1, 5000, 5050, 4950, 5100, 6172, 0, 0, ''], - [2, 5100, 5251, 5100, 5100, 6172, 0, 0, ''], - [3, 4850, 5050, 4650, 4750, 6172, 0, 0, ''], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5050, 4950, 5100, 6172, 0, 0], + [2, 5100, 5251, 5100, 5100, 6172, 0, 0], + [3, 4850, 5050, 4650, 4750, 6172, 0, 0], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], stop_loss=-0.10, roi={"0": 0.10}, profit_perc=-0.03, trailing_stop=True, trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.05, trailing_stop_positive=0.03, @@ -461,12 +461,12 @@ tc28 = BTContainer(data=[ # high of stoploss candle. # stop-loss: 10%, ROI: 10% (should not apply) tc29 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], - [1, 5000, 5050, 5000, 4900, 6172, 0, 0, ''], # enter trade (signal on last candle) - [2, 4900, 5250, 4500, 5100, 6172, 0, 0, ''], # Triggers trailing-stoploss - [3, 5100, 5100, 4650, 4750, 6172, 0, 0, ''], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5050, 5000, 4900, 6172, 0, 0], # enter trade (signal on last candle) + [2, 4900, 5250, 4500, 5100, 6172, 0, 0], # Triggers trailing-stoploss + [3, 5100, 5100, 4650, 4750, 6172, 0, 0], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], stop_loss=-0.10, roi={"0": 0.10}, profit_perc=-0.02, trailing_stop=True, trailing_stop_positive=0.03, trades=[BTrade(sell_reason=SellType.TRAILING_STOP_LOSS, open_tick=1, close_tick=2)] @@ -475,12 +475,12 @@ tc29 = BTContainer(data=[ # Test 30: trailing_stop should be triggered immediately on trade open candle. # stop-loss: 10%, ROI: 10% (should not apply) tc30 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], - [1, 5000, 5500, 5000, 4900, 6172, 0, 0, ''], # enter trade (signal on last candle) and stop - [2, 4900, 5250, 4500, 5100, 6172, 0, 0, ''], - [3, 5100, 5100, 4650, 4750, 6172, 0, 0, ''], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5500, 5000, 4900, 6172, 0, 0], # enter trade (signal on last candle) and stop + [2, 4900, 5250, 4500, 5100, 6172, 0, 0], + [3, 5100, 5100, 4650, 4750, 6172, 0, 0], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], stop_loss=-0.10, roi={"0": 0.10}, profit_perc=-0.01, trailing_stop=True, trailing_stop_positive=0.01, trades=[BTrade(sell_reason=SellType.TRAILING_STOP_LOSS, open_tick=1, close_tick=1)] @@ -489,12 +489,12 @@ tc30 = BTContainer(data=[ # Test 31: trailing_stop should be triggered immediately on trade open candle. # stop-loss: 10%, ROI: 10% (should not apply) tc31 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], - [1, 5000, 5500, 5000, 4900, 6172, 0, 0, ''], # enter trade (signal on last candle) and stop - [2, 4900, 5250, 4500, 5100, 6172, 0, 0, ''], - [3, 5100, 5100, 4650, 4750, 6172, 0, 0, ''], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5500, 5000, 4900, 6172, 0, 0], # enter trade (signal on last candle) and stop + [2, 4900, 5250, 4500, 5100, 6172, 0, 0], + [3, 5100, 5100, 4650, 4750, 6172, 0, 0], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], stop_loss=-0.10, roi={"0": 0.10}, profit_perc=0.01, trailing_stop=True, trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.02, trailing_stop_positive=0.01, @@ -504,12 +504,12 @@ tc31 = BTContainer(data=[ # Test 32: trailing_stop should be triggered immediately on trade open candle. # stop-loss: 1%, ROI: 10% (should not apply) tc32 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], - [1, 5000, 5500, 5000, 4900, 6172, 0, 0, ''], # enter trade (signal on last candle) and stop - [2, 4900, 5250, 4500, 5100, 6172, 0, 0, ''], - [3, 5100, 5100, 4650, 4750, 6172, 0, 0, ''], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5500, 5000, 4900, 6172, 0, 0], # enter trade (signal on last candle) and stop + [2, 4900, 5250, 4500, 5100, 6172, 0, 0], + [3, 5100, 5100, 4650, 4750, 6172, 0, 0], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], stop_loss=-0.01, roi={"0": 0.10}, profit_perc=-0.01, trailing_stop=True, trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.02, trailing_stop_positive=0.01, use_custom_stoploss=True, @@ -519,12 +519,12 @@ tc32 = BTContainer(data=[ # Test 33: trailing_stop should be triggered immediately on trade open candle. # stop-loss: 1%, ROI: 10% (should not apply) tc33 = BTContainer(data=[ - # D O H L C V B S SN + # D O H L C V B S BT [0, 5000, 5050, 4950, 5000, 6172, 1, 0, 'buy_signal_01'], - [1, 5000, 5500, 5000, 4900, 6172, 0, 0, ''], # enter trade (signal on last candle) and stop - [2, 4900, 5250, 4500, 5100, 6172, 0, 0, ''], - [3, 5100, 5100, 4650, 4750, 6172, 0, 0, ''], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], + [1, 5000, 5500, 5000, 4900, 6172, 0, 0, None], # enter trade (signal on last candle) and stop + [2, 4900, 5250, 4500, 5100, 6172, 0, 0, None], + [3, 5100, 5100, 4650, 4750, 6172, 0, 0, None], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0, None]], stop_loss=-0.01, roi={"0": 0.10}, profit_perc=-0.01, trailing_stop=True, trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.02, trailing_stop_positive=0.01, use_custom_stoploss=True, diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py index 873b27b4d..87f968d6b 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -584,7 +584,7 @@ def test_backtest_one(default_conf, fee, mocker, testdatadir) -> None: 'min_rate': [0.1038, 0.10302485], 'max_rate': [0.10501, 0.1038888], 'is_open': [False, False], - 'buy_tag': ['', ''], + 'buy_tag': [None, None], }) pd.testing.assert_frame_equal(results, expected) data_pair = processed[pair] From 8032257fdf8362e63e5f60b37db62e41ccbc7fc8 Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Fri, 23 Jul 2021 18:30:25 +0700 Subject: [PATCH 26/33] revert test_pairlist --- tests/plugins/test_pairlist.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/plugins/test_pairlist.py b/tests/plugins/test_pairlist.py index 510e9a0ae..b15126a33 100644 --- a/tests/plugins/test_pairlist.py +++ b/tests/plugins/test_pairlist.py @@ -925,20 +925,20 @@ def test_spreadfilter_invalid_data(mocker, default_conf, markets, tickers, caplo get_tickers=tickers ) - freqtrade = get_patched_freqtradebot(mocker, default_conf) - freqtrade.pairlists.refresh_pairlist() + ftbot = get_patched_freqtradebot(mocker, default_conf) + ftbot.pairlists.refresh_pairlist() - assert len(freqtrade.pairlists.whitelist) == 5 + assert len(ftbot.pairlists.whitelist) == 5 tickers.return_value['ETH/BTC']['ask'] = 0.0 del tickers.return_value['TKN/BTC'] del tickers.return_value['LTC/BTC'] mocker.patch.multiple('freqtrade.exchange.Exchange', get_tickers=tickers) - freqtrade.pairlists.refresh_pairlist() + ftbot.pairlists.refresh_pairlist() assert log_has_re(r'Removed .* invalid ticker data.*', caplog) - assert len(freqtrade.pairlists.whitelist) == 2 + assert len(ftbot.pairlists.whitelist) == 2 @pytest.mark.parametrize("pairlistconfig,desc_expected,exception_expected", [ From acfaa39e5446e0f7f8b491f92c74b305b818687b Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Fri, 23 Jul 2021 18:34:18 +0700 Subject: [PATCH 27/33] revert back test_rpc_api_server --- tests/rpc/test_rpc_apiserver.py | 138 ++++++++++++++++---------------- 1 file changed, 69 insertions(+), 69 deletions(-) diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index fef44cab2..803ef7e5d 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -46,13 +46,13 @@ def botclient(default_conf, mocker): "password": _TEST_PASS, }}) - freqtrade = get_patched_freqtradebot(mocker, default_conf) - rpc = RPC(freqtrade) + ftbot = get_patched_freqtradebot(mocker, default_conf) + rpc = RPC(ftbot) mocker.patch('freqtrade.rpc.api_server.ApiServer.start_api', MagicMock()) try: apiserver = ApiServer(default_conf) apiserver.add_rpc_handler(rpc) - yield freqtrade, TestClient(apiserver.app) + yield ftbot, TestClient(apiserver.app) # Cleanup ... ? finally: ApiServer.shutdown() @@ -88,7 +88,7 @@ def assert_response(response, expected_code=200, needs_cors=True): def test_api_not_found(botclient): - freqtrade, client = botclient + ftbot, client = botclient rc = client_get(client, f"{BASE_URI}/invalid_url") assert_response(rc, 404) @@ -96,7 +96,7 @@ def test_api_not_found(botclient): def test_api_ui_fallback(botclient): - freqtrade, client = botclient + ftbot, client = botclient rc = client_get(client, "/favicon.ico") assert rc.status_code == 200 @@ -140,7 +140,7 @@ def test_api_auth(): def test_api_unauthorized(botclient): - freqtrade, client = botclient + ftbot, client = botclient rc = client.get(f"{BASE_URI}/ping") assert_response(rc, needs_cors=False) assert rc.json() == {'status': 'pong'} @@ -151,20 +151,20 @@ def test_api_unauthorized(botclient): assert rc.json() == {'detail': 'Unauthorized'} # Change only username - freqtrade.config['api_server']['username'] = 'Ftrader' + ftbot.config['api_server']['username'] = 'Ftrader' rc = client_get(client, f"{BASE_URI}/version") assert_response(rc, 401) assert rc.json() == {'detail': 'Unauthorized'} # Change only password - freqtrade.config['api_server']['username'] = _TEST_USER - freqtrade.config['api_server']['password'] = 'WrongPassword' + ftbot.config['api_server']['username'] = _TEST_USER + ftbot.config['api_server']['password'] = 'WrongPassword' rc = client_get(client, f"{BASE_URI}/version") assert_response(rc, 401) assert rc.json() == {'detail': 'Unauthorized'} - freqtrade.config['api_server']['username'] = 'Ftrader' - freqtrade.config['api_server']['password'] = 'WrongPassword' + ftbot.config['api_server']['username'] = 'Ftrader' + ftbot.config['api_server']['password'] = 'WrongPassword' rc = client_get(client, f"{BASE_URI}/version") assert_response(rc, 401) @@ -172,7 +172,7 @@ def test_api_unauthorized(botclient): def test_api_token_login(botclient): - freqtrade, client = botclient + ftbot, client = botclient rc = client.post(f"{BASE_URI}/token/login", data=None, headers={'Authorization': _basic_auth_str('WRONG_USER', 'WRONG_PASS'), @@ -191,7 +191,7 @@ def test_api_token_login(botclient): def test_api_token_refresh(botclient): - freqtrade, client = botclient + ftbot, client = botclient rc = client_post(client, f"{BASE_URI}/token/login") assert_response(rc) rc = client.post(f"{BASE_URI}/token/refresh", @@ -204,12 +204,12 @@ def test_api_token_refresh(botclient): def test_api_stop_workflow(botclient): - freqtrade, client = botclient - assert freqtrade.state == State.RUNNING + ftbot, client = botclient + assert ftbot.state == State.RUNNING rc = client_post(client, f"{BASE_URI}/stop") assert_response(rc) assert rc.json() == {'status': 'stopping trader ...'} - assert freqtrade.state == State.STOPPED + assert ftbot.state == State.STOPPED # Stop bot again rc = client_post(client, f"{BASE_URI}/stop") @@ -220,7 +220,7 @@ def test_api_stop_workflow(botclient): rc = client_post(client, f"{BASE_URI}/start") assert_response(rc) assert rc.json() == {'status': 'starting trader ...'} - assert freqtrade.state == State.RUNNING + assert ftbot.state == State.RUNNING # Call start again rc = client_post(client, f"{BASE_URI}/start") @@ -399,32 +399,32 @@ def test_api_cleanup(default_conf, mocker, caplog): def test_api_reloadconf(botclient): - freqtrade, client = botclient + ftbot, client = botclient rc = client_post(client, f"{BASE_URI}/reload_config") assert_response(rc) assert rc.json() == {'status': 'Reloading config ...'} - assert freqtrade.state == State.RELOAD_CONFIG + assert ftbot.state == State.RELOAD_CONFIG def test_api_stopbuy(botclient): - freqtrade, client = botclient - assert freqtrade.config['max_open_trades'] != 0 + ftbot, client = botclient + assert ftbot.config['max_open_trades'] != 0 rc = client_post(client, f"{BASE_URI}/stopbuy") assert_response(rc) assert rc.json() == {'status': 'No more buy will occur from now. Run /reload_config to reset.'} - assert freqtrade.config['max_open_trades'] == 0 + assert ftbot.config['max_open_trades'] == 0 def test_api_balance(botclient, mocker, rpc_balance): - freqtrade, client = botclient + ftbot, client = botclient - freqtrade.config['dry_run'] = False + ftbot.config['dry_run'] = False mocker.patch('freqtrade.exchange.Exchange.get_balances', return_value=rpc_balance) mocker.patch('freqtrade.exchange.Exchange.get_valid_pair_combination', side_effect=lambda a, b: f"{a}/{b}") - freqtrade.wallets.update() + ftbot.wallets.update() rc = client_get(client, f"{BASE_URI}/balance") assert_response(rc) @@ -441,8 +441,8 @@ def test_api_balance(botclient, mocker, rpc_balance): def test_api_count(botclient, mocker, ticker, fee, markets): - freqtrade, client = botclient - patch_get_signal(freqtrade, (True, False, None)) + ftbot, client = botclient + patch_get_signal(ftbot, (True, False, None)) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_balances=MagicMock(return_value=ticker), @@ -463,13 +463,13 @@ def test_api_count(botclient, mocker, ticker, fee, markets): assert rc.json()["current"] == 4 assert rc.json()["max"] == 1 - freqtrade.config['max_open_trades'] = float('inf') + ftbot.config['max_open_trades'] = float('inf') rc = client_get(client, f"{BASE_URI}/count") assert rc.json()["max"] == -1 def test_api_locks(botclient): - freqtrade, client = botclient + ftbot, client = botclient rc = client_get(client, f"{BASE_URI}/locks") assert_response(rc) @@ -503,8 +503,8 @@ def test_api_locks(botclient): def test_api_show_config(botclient, mocker): - freqtrade, client = botclient - patch_get_signal(freqtrade, (True, False, None)) + ftbot, client = botclient + patch_get_signal(ftbot, (True, False, None)) rc = client_get(client, f"{BASE_URI}/show_config") assert_response(rc) @@ -521,8 +521,8 @@ def test_api_show_config(botclient, mocker): def test_api_daily(botclient, mocker, ticker, fee, markets): - freqtrade, client = botclient - patch_get_signal(freqtrade, (True, False, None)) + ftbot, client = botclient + patch_get_signal(ftbot, (True, False, None)) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_balances=MagicMock(return_value=ticker), @@ -539,8 +539,8 @@ def test_api_daily(botclient, mocker, ticker, fee, markets): def test_api_trades(botclient, mocker, fee, markets): - freqtrade, client = botclient - patch_get_signal(freqtrade, (True, False, None)) + ftbot, client = botclient + patch_get_signal(ftbot, (True, False, None)) mocker.patch.multiple( 'freqtrade.exchange.Exchange', markets=PropertyMock(return_value=markets) @@ -567,8 +567,8 @@ def test_api_trades(botclient, mocker, fee, markets): def test_api_trade_single(botclient, mocker, fee, ticker, markets): - freqtrade, client = botclient - patch_get_signal(freqtrade, (True, False, None)) + ftbot, client = botclient + patch_get_signal(ftbot, (True, False, None)) mocker.patch.multiple( 'freqtrade.exchange.Exchange', markets=PropertyMock(return_value=markets), @@ -587,8 +587,8 @@ def test_api_trade_single(botclient, mocker, fee, ticker, markets): def test_api_delete_trade(botclient, mocker, fee, markets): - freqtrade, client = botclient - patch_get_signal(freqtrade, (True, False, None)) + ftbot, client = botclient + patch_get_signal(ftbot, (True, False, None)) stoploss_mock = MagicMock() cancel_mock = MagicMock() mocker.patch.multiple( @@ -603,7 +603,7 @@ def test_api_delete_trade(botclient, mocker, fee, markets): create_mock_trades(fee) Trade.query.session.flush() - freqtrade.strategy.order_types['stoploss_on_exchange'] = True + ftbot.strategy.order_types['stoploss_on_exchange'] = True trades = Trade.query.all() trades[1].stoploss_order_id = '1234' assert len(trades) > 2 @@ -629,7 +629,7 @@ def test_api_delete_trade(botclient, mocker, fee, markets): def test_api_logs(botclient): - freqtrade, client = botclient + ftbot, client = botclient rc = client_get(client, f"{BASE_URI}/logs") assert_response(rc) assert len(rc.json()) == 2 @@ -661,8 +661,8 @@ def test_api_logs(botclient): def test_api_edge_disabled(botclient, mocker, ticker, fee, markets): - freqtrade, client = botclient - patch_get_signal(freqtrade, (True, False, None)) + ftbot, client = botclient + patch_get_signal(ftbot, (True, False, None)) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_balances=MagicMock(return_value=ticker), @@ -677,8 +677,8 @@ def test_api_edge_disabled(botclient, mocker, ticker, fee, markets): @pytest.mark.usefixtures("init_persistence") def test_api_profit(botclient, mocker, ticker, fee, markets): - freqtrade, client = botclient - patch_get_signal(freqtrade, (True, False, None)) + ftbot, client = botclient + patch_get_signal(ftbot, (True, False, None)) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_balances=MagicMock(return_value=ticker), @@ -728,8 +728,8 @@ def test_api_profit(botclient, mocker, ticker, fee, markets): @pytest.mark.usefixtures("init_persistence") def test_api_stats(botclient, mocker, ticker, fee, markets,): - freqtrade, client = botclient - patch_get_signal(freqtrade, (True, False, None)) + ftbot, client = botclient + patch_get_signal(ftbot, (True, False, None)) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_balances=MagicMock(return_value=ticker), @@ -756,8 +756,8 @@ def test_api_stats(botclient, mocker, ticker, fee, markets,): def test_api_performance(botclient, fee): - freqtrade, client = botclient - patch_get_signal(freqtrade, (True, False, None)) + ftbot, client = botclient + patch_get_signal(ftbot, (True, False, None)) trade = Trade( pair='LTC/ETH', @@ -802,8 +802,8 @@ def test_api_performance(botclient, fee): def test_api_status(botclient, mocker, ticker, fee, markets): - freqtrade, client = botclient - patch_get_signal(freqtrade, (True, False, None)) + ftbot, client = botclient + patch_get_signal(ftbot, (True, False, None)) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_balances=MagicMock(return_value=ticker), @@ -891,7 +891,7 @@ def test_api_status(botclient, mocker, ticker, fee, markets): def test_api_version(botclient): - freqtrade, client = botclient + ftbot, client = botclient rc = client_get(client, f"{BASE_URI}/version") assert_response(rc) @@ -899,7 +899,7 @@ def test_api_version(botclient): def test_api_blacklist(botclient, mocker): - freqtrade, client = botclient + ftbot, client = botclient rc = client_get(client, f"{BASE_URI}/blacklist") assert_response(rc) @@ -934,7 +934,7 @@ def test_api_blacklist(botclient, mocker): def test_api_whitelist(botclient): - freqtrade, client = botclient + ftbot, client = botclient rc = client_get(client, f"{BASE_URI}/whitelist") assert_response(rc) @@ -946,7 +946,7 @@ def test_api_whitelist(botclient): def test_api_forcebuy(botclient, mocker, fee): - freqtrade, client = botclient + ftbot, client = botclient rc = client_post(client, f"{BASE_URI}/forcebuy", data='{"pair": "ETH/BTC"}') @@ -954,7 +954,7 @@ def test_api_forcebuy(botclient, mocker, fee): assert rc.json() == {"error": "Error querying /api/v1/forcebuy: Forcebuy not enabled."} # enable forcebuy - freqtrade.config['forcebuy_enable'] = True + ftbot.config['forcebuy_enable'] = True fbuy_mock = MagicMock(return_value=None) mocker.patch("freqtrade.rpc.RPC._rpc_forcebuy", fbuy_mock) @@ -1037,7 +1037,7 @@ def test_api_forcebuy(botclient, mocker, fee): def test_api_forcesell(botclient, mocker, ticker, fee, markets): - freqtrade, client = botclient + ftbot, client = botclient mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_balances=MagicMock(return_value=ticker), @@ -1046,14 +1046,14 @@ def test_api_forcesell(botclient, mocker, ticker, fee, markets): markets=PropertyMock(return_value=markets), _is_dry_limit_order_filled=MagicMock(return_value=False), ) - patch_get_signal(freqtrade, (True, False, None)) + patch_get_signal(ftbot, (True, False, None)) rc = client_post(client, f"{BASE_URI}/forcesell", data='{"tradeid": "1"}') assert_response(rc, 502) assert rc.json() == {"error": "Error querying /api/v1/forcesell: invalid argument"} - freqtrade.enter_positions() + ftbot.enter_positions() rc = client_post(client, f"{BASE_URI}/forcesell", data='{"tradeid": "1"}') @@ -1062,7 +1062,7 @@ def test_api_forcesell(botclient, mocker, ticker, fee, markets): def test_api_pair_candles(botclient, ohlcv_history): - freqtrade, client = botclient + ftbot, client = botclient timeframe = '5m' amount = 3 @@ -1090,7 +1090,7 @@ def test_api_pair_candles(botclient, ohlcv_history): ohlcv_history.loc[1, 'buy'] = 1 ohlcv_history['sell'] = 0 - freqtrade.dataprovider._set_cached_df("XRP/BTC", timeframe, ohlcv_history) + ftbot.dataprovider._set_cached_df("XRP/BTC", timeframe, ohlcv_history) rc = client_get(client, f"{BASE_URI}/pair_candles?limit={amount}&pair=XRP%2FBTC&timeframe={timeframe}") @@ -1128,7 +1128,7 @@ def test_api_pair_candles(botclient, ohlcv_history): def test_api_pair_history(botclient, ohlcv_history): - freqtrade, client = botclient + ftbot, client = botclient timeframe = '5m' # No pair @@ -1181,23 +1181,23 @@ def test_api_pair_history(botclient, ohlcv_history): def test_api_plot_config(botclient): - freqtrade, client = botclient + ftbot, client = botclient rc = client_get(client, f"{BASE_URI}/plot_config") assert_response(rc) assert rc.json() == {} - freqtrade.strategy.plot_config = { + ftbot.strategy.plot_config = { 'main_plot': {'sma': {}}, 'subplots': {'RSI': {'rsi': {'color': 'red'}}} } rc = client_get(client, f"{BASE_URI}/plot_config") assert_response(rc) - assert rc.json() == freqtrade.strategy.plot_config + assert rc.json() == ftbot.strategy.plot_config assert isinstance(rc.json()['main_plot'], dict) assert isinstance(rc.json()['subplots'], dict) - freqtrade.strategy.plot_config = {'main_plot': {'sma': {}}} + ftbot.strategy.plot_config = {'main_plot': {'sma': {}}} rc = client_get(client, f"{BASE_URI}/plot_config") assert_response(rc) @@ -1206,7 +1206,7 @@ def test_api_plot_config(botclient): def test_api_strategies(botclient): - freqtrade, client = botclient + ftbot, client = botclient rc = client_get(client, f"{BASE_URI}/strategies") @@ -1219,7 +1219,7 @@ def test_api_strategies(botclient): def test_api_strategy(botclient): - freqtrade, client = botclient + ftbot, client = botclient rc = client_get(client, f"{BASE_URI}/strategy/DefaultStrategy") @@ -1234,7 +1234,7 @@ def test_api_strategy(botclient): def test_list_available_pairs(botclient): - freqtrade, client = botclient + ftbot, client = botclient rc = client_get(client, f"{BASE_URI}/available_pairs") From ba0fa1120a6537d1d08af191e19b243556064f29 Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Fri, 23 Jul 2021 18:39:18 +0700 Subject: [PATCH 28/33] revert rename naming --- tests/rpc/test_rpc_telegram.py | 26 +++++++++++++------------- tests/test_freqtradebot.py | 34 +++++++++++++++++----------------- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py index ab66d18e8..5aeebb2d7 100644 --- a/tests/rpc/test_rpc_telegram.py +++ b/tests/rpc/test_rpc_telegram.py @@ -63,12 +63,12 @@ def get_telegram_testobject(mocker, default_conf, mock=True, freqtrade=None): _init=MagicMock(), _send_msg=msg_mock ) - if not freqtrade: - freqtrade = get_patched_freqtradebot(mocker, default_conf) - rpc = RPC(freqtrade) + if not ftbot: + ftbot = get_patched_freqtradebot(mocker, default_conf) + rpc = RPC(ftbot) telegram = Telegram(rpc, default_conf) - return telegram, freqtrade, msg_mock + return telegram, ftbot, msg_mock def test_telegram__init__(default_conf, mocker) -> None: @@ -115,11 +115,11 @@ def test_authorized_only(default_conf, mocker, caplog, update) -> None: patch_exchange(mocker) caplog.set_level(logging.DEBUG) default_conf['telegram']['enabled'] = False - freqtrade = FreqtradeBot(default_conf) - rpc = RPC(freqtrade) + bot = FreqtradeBot(default_conf) + rpc = RPC(bot) dummy = DummyCls(rpc, default_conf) - patch_get_signal(freqtrade, (True, False, None)) + patch_get_signal(bot, (True, False, None)) dummy.dummy_handler(update=update, context=MagicMock()) assert dummy.state['called'] is True assert log_has('Executing handler: dummy_handler for chat_id: 0', caplog) @@ -135,11 +135,11 @@ def test_authorized_only_unauthorized(default_conf, mocker, caplog) -> None: update.message = Message(randint(1, 100), datetime.utcnow(), chat) default_conf['telegram']['enabled'] = False - freqtrade = FreqtradeBot(default_conf) - rpc = RPC(freqtrade) + bot = FreqtradeBot(default_conf) + rpc = RPC(bot) dummy = DummyCls(rpc, default_conf) - patch_get_signal(freqtrade, (True, False, None)) + patch_get_signal(bot, (True, False, None)) dummy.dummy_handler(update=update, context=MagicMock()) assert dummy.state['called'] is False assert not log_has('Executing handler: dummy_handler for chat_id: 3735928559', caplog) @@ -152,10 +152,10 @@ def test_authorized_only_exception(default_conf, mocker, caplog, update) -> None default_conf['telegram']['enabled'] = False - freqtrade = FreqtradeBot(default_conf) - rpc = RPC(freqtrade) + bot = FreqtradeBot(default_conf) + rpc = RPC(bot) dummy = DummyCls(rpc, default_conf) - patch_get_signal(freqtrade, (True, False, None)) + patch_get_signal(bot, (True, False, None)) dummy.dummy_exception(update=update, context=MagicMock()) assert dummy.state['called'] is False diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index abbef7858..b3a5bc409 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -2014,16 +2014,16 @@ def test_close_trade(default_conf, ticker, limit_buy_order, limit_buy_order_open def test_bot_loop_start_called_once(mocker, default_conf, caplog): - freqtrade = get_patched_freqtradebot(mocker, default_conf) + ftbot = get_patched_freqtradebot(mocker, default_conf) mocker.patch('freqtrade.freqtradebot.FreqtradeBot.create_trade') - patch_get_signal(freqtrade) - freqtrade.strategy.bot_loop_start = MagicMock(side_effect=ValueError) - freqtrade.strategy.analyze = MagicMock() + patch_get_signal(ftbot) + ftbot.strategy.bot_loop_start = MagicMock(side_effect=ValueError) + ftbot.strategy.analyze = MagicMock() - freqtrade.process() + ftbot.process() assert log_has_re(r'Strategy caused the following exception.*', caplog) - assert freqtrade.strategy.bot_loop_start.call_count == 1 - assert freqtrade.strategy.analyze.call_count == 1 + assert ftbot.strategy.bot_loop_start.call_count == 1 + assert ftbot.strategy.analyze.call_count == 1 def test_check_handle_timedout_buy_usercustom(default_conf, ticker, limit_buy_order_old, open_trade, @@ -4086,14 +4086,14 @@ def test_startup_trade_reinit(default_conf, edge_conf, mocker): reinit_mock = MagicMock() mocker.patch('freqtrade.persistence.Trade.stoploss_reinitialization', reinit_mock) - freqtrade = get_patched_freqtradebot(mocker, default_conf) - freqtrade.startup() + ftbot = get_patched_freqtradebot(mocker, default_conf) + ftbot.startup() assert reinit_mock.call_count == 1 reinit_mock.reset_mock() - freqtrade = get_patched_freqtradebot(mocker, edge_conf) - freqtrade.startup() + ftbot = get_patched_freqtradebot(mocker, edge_conf) + ftbot.startup() assert reinit_mock.call_count == 0 @@ -4112,17 +4112,17 @@ def test_sync_wallet_dry_run(mocker, default_conf, ticker, fee, limit_buy_order_ get_fee=fee, ) - freqtrade = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtrade) - assert freqtrade.wallets.get_free('BTC') == 0.002 + bot = get_patched_freqtradebot(mocker, default_conf) + patch_get_signal(bot) + assert bot.wallets.get_free('BTC') == 0.002 - n = freqtrade.enter_positions() + n = bot.enter_positions() assert n == 2 trades = Trade.query.all() assert len(trades) == 2 - freqtrade.config['max_open_trades'] = 3 - n = freqtrade.enter_positions() + bot.config['max_open_trades'] = 3 + n = bot.enter_positions() assert n == 0 assert log_has_re(r"Unable to create trade for XRP/BTC: " r"Available balance \(0.0 BTC\) is lower than stake amount \(0.001 BTC\)", From b9c2489b73629babd1dcc918fb4ec8c826d686dd Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Fri, 23 Jul 2021 18:41:29 +0700 Subject: [PATCH 29/33] remove SN --- tests/edge/test_edge.py | 46 ++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/tests/edge/test_edge.py b/tests/edge/test_edge.py index 254134ce7..7bdc940df 100644 --- a/tests/edge/test_edge.py +++ b/tests/edge/test_edge.py @@ -76,23 +76,23 @@ def _time_on_candle(number): # End helper functions # Open trade should be removed from the end tc0 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], - [1, 5000, 5025, 4975, 4987, 6172, 0, 1, '']], # enter trade (signal on last candle) + # D O H L C V B S + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 1]], # enter trade (signal on last candle) stop_loss=-0.99, roi={"0": float('inf')}, profit_perc=0.00, trades=[] ) # Two complete trades within dataframe(with sell hit for all) tc1 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], - [1, 5000, 5025, 4975, 4987, 6172, 0, 1, ''], # enter trade (signal on last candle) - [2, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], # exit at open - [3, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], # no action - [4, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], # should enter the trade - [5, 5000, 5025, 4975, 4987, 6172, 0, 1, ''], # no action - [6, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], # should sell + # D O H L C V B S + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 1], # enter trade (signal on last candle) + [2, 5000, 5025, 4975, 4987, 6172, 0, 0], # exit at open + [3, 5000, 5025, 4975, 4987, 6172, 1, 0], # no action + [4, 5000, 5025, 4975, 4987, 6172, 0, 0], # should enter the trade + [5, 5000, 5025, 4975, 4987, 6172, 0, 1], # no action + [6, 5000, 5025, 4975, 4987, 6172, 0, 0], # should sell ], stop_loss=-0.99, roi={"0": float('inf')}, profit_perc=0.00, trades=[BTrade(sell_reason=SellType.SELL_SIGNAL, open_tick=1, close_tick=2), @@ -101,10 +101,10 @@ tc1 = BTContainer(data=[ # 3) Entered, sl 1%, candle drops 8% => Trade closed, 1% loss tc2 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], - [1, 5000, 5025, 4600, 4987, 6172, 0, 0, ''], # enter trade, stoploss hit - [2, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], + # D O H L C V B S + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4600, 4987, 6172, 0, 0], # enter trade, stoploss hit + [2, 5000, 5025, 4975, 4987, 6172, 0, 0], ], stop_loss=-0.01, roi={"0": float('inf')}, profit_perc=-0.01, trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=1)] @@ -112,10 +112,10 @@ tc2 = BTContainer(data=[ # 4) Entered, sl 3 %, candle drops 4%, recovers to 1 % = > Trade closed, 3 % loss tc3 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], - [1, 5000, 5025, 4800, 4987, 6172, 0, 0, ''], # enter trade, stoploss hit - [2, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], + # D O H L C V B S + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4800, 4987, 6172, 0, 0], # enter trade, stoploss hit + [2, 5000, 5025, 4975, 4987, 6172, 0, 0], ], stop_loss=-0.03, roi={"0": float('inf')}, profit_perc=-0.03, trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=1)] @@ -123,10 +123,10 @@ tc3 = BTContainer(data=[ # 5) Stoploss and sell are hit. should sell on stoploss tc4 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], - [1, 5000, 5025, 4800, 4987, 6172, 0, 1, ''], # enter trade, stoploss hit, sell signal - [2, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], + # D O H L C V B S + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4800, 4987, 6172, 0, 1], # enter trade, stoploss hit, sell signal + [2, 5000, 5025, 4975, 4987, 6172, 0, 0], ], stop_loss=-0.03, roi={"0": float('inf')}, profit_perc=-0.03, trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=1)] From 7a0cb95ffb00c1c14a8b6a62b3134017ed919214 Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Fri, 23 Jul 2021 20:43:27 +0700 Subject: [PATCH 30/33] fix testcase --- tests/rpc/test_rpc_telegram.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py index 5aeebb2d7..b678c3363 100644 --- a/tests/rpc/test_rpc_telegram.py +++ b/tests/rpc/test_rpc_telegram.py @@ -55,7 +55,7 @@ class DummyCls(Telegram): raise Exception('test') -def get_telegram_testobject(mocker, default_conf, mock=True, freqtrade=None): +def get_telegram_testobject(mocker, default_conf, mock=True, ftbot=None): msg_mock = MagicMock() if mock: mocker.patch.multiple( From 0fcbe097c064df7943e21bcf7e7d3c0f6f84daf0 Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Fri, 23 Jul 2021 21:06:38 +0700 Subject: [PATCH 31/33] remove blankspace --- tests/optimize/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/optimize/__init__.py b/tests/optimize/__init__.py index 80fce9ca5..f29d8d585 100644 --- a/tests/optimize/__init__.py +++ b/tests/optimize/__init__.py @@ -44,7 +44,6 @@ def _get_frame_time_from_offset(offset): def _build_backtest_dataframe(data): - columns = ['date', 'open', 'high', 'low', 'close', 'volume', 'buy', 'sell'] columns = columns + ['buy_tag'] if len(data[0]) == 9 else columns From b7ba2f115e46dcb7455f33a5154fef19053aae22 Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Fri, 23 Jul 2021 21:20:32 +0700 Subject: [PATCH 32/33] retrigger checks From aa34889c047e13607a1b60ccca01cf63d6f57bab Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 30 Jul 2021 07:14:35 +0200 Subject: [PATCH 33/33] Don't run migrations twice --- freqtrade/persistence/migrations.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/freqtrade/persistence/migrations.py b/freqtrade/persistence/migrations.py index 035452437..a2d88cb31 100644 --- a/freqtrade/persistence/migrations.py +++ b/freqtrade/persistence/migrations.py @@ -161,13 +161,6 @@ 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, 'open_trade_value'): - 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! - inspector = inspect(engine) - cols = inspector.get_columns('trades') - if not has_column(cols, 'buy_tag'): logger.info(f'Running database migration for trades - backup: {table_back_name}') migrate_trades_table(decl_base, inspector, engine, table_back_name, cols)