diff --git a/freqtrade/misc.py b/freqtrade/misc.py index 409b10231..bd8ff2496 100644 --- a/freqtrade/misc.py +++ b/freqtrade/misc.py @@ -147,10 +147,10 @@ def build_subcommands(parser: argparse.ArgumentParser) -> None: metavar='INT', ) backtest.add_argument( - '--limit-max-trades', + '--realistic-simulation', help='uses max_open_trades from config to simulate real world limitations', action='store_true', - dest='limit_max_trades', + dest='realistic_simulation', ) @@ -167,7 +167,7 @@ def start_backtesting(args) -> None: 'BACKTEST_LIVE': 'true' if args.live else '', 'BACKTEST_CONFIG': args.config, 'BACKTEST_TICKER_INTERVAL': str(args.ticker_interval), - 'BACKTEST_LIMIT_MAX_TRADES': 'true' if args.limit_max_trades else '', + 'BACKTEST_REALISTIC_SIMULATION': 'true' if args.realistic_simulation else '', }) path = os.path.join(os.path.dirname(__file__), 'tests', 'test_backtesting.py') pytest.main(['-s', path]) diff --git a/freqtrade/tests/test_backtesting.py b/freqtrade/tests/test_backtesting.py index 2e2c195c4..36c701426 100644 --- a/freqtrade/tests/test_backtesting.py +++ b/freqtrade/tests/test_backtesting.py @@ -83,13 +83,14 @@ def generate_text_table(data: Dict[str, Dict], results: DataFrame, stake_currenc return tabulate(tabular_data, headers=headers) -def backtest(config: Dict, processed, mocker, max_open_trades=0): +def backtest(config: Dict, processed, mocker, max_open_trades=0, realistic=True): """ Implements backtesting functionality :param config: config to use :param processed: a processed dictionary with format {pair, data} :param mocker: mocker instance :param max_open_trades: maximum number of concurrent trades (default: 0, disabled) + :param realistic: do we try to simulate realistic trades? (default: True) :return: DataFrame """ trades = [] @@ -100,7 +101,11 @@ def backtest(config: Dict, processed, mocker, max_open_trades=0): pair_data['buy'], pair_data['sell'] = 0, 0 ticker = populate_sell_trend(populate_buy_trend(pair_data)) # for each buy point + lock_pair_until = None for row in ticker[ticker.buy == 1].itertuples(index=True): + if realistic: + if lock_pair_until is not None and row.Index <= lock_pair_until: + continue if max_open_trades > 0: # Check if max_open_trades has already been reached for the given date if not trade_count_lock.get(row.date, 0) < max_open_trades: @@ -125,6 +130,7 @@ def backtest(config: Dict, processed, mocker, max_open_trades=0): if min_roi_reached(trade, row2.close, row2.date) or row2.sell == 1: current_profit = trade.calc_profit(row2.close) + lock_pair_until = row2.Index trades.append((pair, current_profit, row2.Index - row.Index)) break @@ -133,7 +139,7 @@ def backtest(config: Dict, processed, mocker, max_open_trades=0): def get_max_open_trades(config): - if not os.environ.get('BACKTEST_LIMIT_MAX_TRADES'): + if not os.environ.get('BACKTEST_REALISTIC_SIMULATION'): return 0 print('Using max_open_trades: {} ...'.format(config['max_open_trades'])) return config['max_open_trades'] @@ -176,6 +182,7 @@ def test_backtest(backtest_conf, mocker): )) # Execute backtest and print results - results = backtest(config, preprocess(data), mocker, get_max_open_trades(config)) + realistic = os.environ.get('BACKTEST_REALISTIC_SIMULATION') + results = backtest(config, preprocess(data), mocker, get_max_open_trades(config), realistic) print('====================== BACKTESTING REPORT ======================================\n\n') print(generate_text_table(data, results, config['stake_currency'])) diff --git a/freqtrade/tests/test_misc.py b/freqtrade/tests/test_misc.py index 8d373c1d7..a6f61d58b 100644 --- a/freqtrade/tests/test_misc.py +++ b/freqtrade/tests/test_misc.py @@ -109,7 +109,7 @@ def test_start_backtesting(mocker): live=True, loglevel=20, ticker_interval=1, - limit_max_trades=True, + realistic_simulation=True, ) start_backtesting(args) assert env_mock == { @@ -117,7 +117,7 @@ def test_start_backtesting(mocker): 'BACKTEST_LIVE': 'true', 'BACKTEST_CONFIG': 'config.json', 'BACKTEST_TICKER_INTERVAL': '1', - 'BACKTEST_LIMIT_MAX_TRADES': 'true', + 'BACKTEST_REALISTIC_SIMULATION': 'true', } assert pytest_mock.call_count == 1