Merge pull request #2801 from freqtrade/backtest_arguments_2

Backtest arguments instead of dictionary
This commit is contained in:
Matthias 2020-01-25 13:11:23 +01:00 committed by GitHub
commit a97bb10877
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 70 additions and 82 deletions

View File

@ -279,30 +279,28 @@ class Backtesting:
return bt_res
return None
def backtest(self, args: Dict) -> DataFrame:
def backtest(self, processed: Dict, stake_amount: float,
start_date, end_date,
max_open_trades: int = 0, position_stacking: bool = False) -> DataFrame:
"""
Implements backtesting functionality
Implement backtesting functionality
NOTE: This method is used by Hyperopt at each iteration. Please keep it optimized.
Of course try to not have ugly code. By some accessor are sometime slower than functions.
Avoid, logging on this method
Avoid extensive logging in this method and functions it calls.
:param args: a dict containing:
stake_amount: btc amount to use for each trade
processed: a processed dictionary with format {pair, data}
max_open_trades: maximum number of concurrent trades (default: 0, disabled)
position_stacking: do we allow position stacking? (default: False)
:return: DataFrame
:param processed: a processed dictionary with format {pair, data}
:param stake_amount: amount to use for each trade
:param start_date: backtesting timerange start datetime
:param end_date: backtesting timerange end datetime
:param max_open_trades: maximum number of concurrent trades, <= 0 means unlimited
:param position_stacking: do we allow position stacking?
:return: DataFrame with trades (results of backtesting)
"""
# Arguments are long and noisy, so this is commented out.
# Uncomment if you need to debug the backtest() method.
# logger.debug(f"Start backtest, args: {args}")
processed = args['processed']
stake_amount = args['stake_amount']
max_open_trades = args.get('max_open_trades', 0)
position_stacking = args.get('position_stacking', False)
start_date = args['start_date']
end_date = args['end_date']
logger.debug(f"Run backtest, stake_amount: {stake_amount}, "
f"start_date: {start_date}, end_date: {end_date}, "
f"max_open_trades: {max_open_trades}, position_stacking: {position_stacking}"
)
trades = []
trade_count_lock: Dict = {}
@ -369,18 +367,21 @@ class Backtesting:
def start(self) -> None:
"""
Run a backtesting end-to-end
Run backtesting end-to-end
:return: None
"""
data: Dict[str, Any] = {}
logger.info('Using stake_currency: %s ...', self.config['stake_currency'])
logger.info('Using stake_amount: %s ...', self.config['stake_amount'])
# Use max_open_trades in backtesting, except --disable-max-market-positions is set
if self.config.get('use_max_market_positions', True):
max_open_trades = self.config['max_open_trades']
else:
logger.info('Ignoring max_open_trades (--disable-max-market-positions was used) ...')
max_open_trades = 0
position_stacking = self.config.get('position_stacking', False)
data, timerange = self.load_bt_data()
@ -403,14 +404,12 @@ class Backtesting:
)
# Execute backtest and print results
all_results[self.strategy.get_strategy_name()] = self.backtest(
{
'stake_amount': self.config.get('stake_amount'),
'processed': preprocessed,
'max_open_trades': max_open_trades,
'position_stacking': self.config.get('position_stacking', False),
'start_date': min_date,
'end_date': max_date,
}
processed=preprocessed,
stake_amount=self.config['stake_amount'],
start_date=min_date,
end_date=max_date,
max_open_trades=max_open_trades,
position_stacking=position_stacking,
)
for strategy, results in all_results.items():

View File

@ -372,14 +372,12 @@ class Hyperopt:
min_date, max_date = get_timerange(processed)
backtesting_results = self.backtesting.backtest(
{
'stake_amount': self.config['stake_amount'],
'processed': processed,
'max_open_trades': self.max_open_trades,
'position_stacking': self.position_stacking,
'start_date': min_date,
'end_date': max_date,
}
processed=processed,
stake_amount=self.config['stake_amount'],
start_date=min_date,
end_date=max_date,
max_open_trades=self.max_open_trades,
position_stacking=self.position_stacking,
)
return self._get_results_dict(backtesting_results, min_date, max_date,
params_dict, params_details)

View File

@ -382,13 +382,11 @@ def test_backtest_results(default_conf, fee, mocker, caplog, data) -> None:
data_processed = {pair: frame.copy()}
min_date, max_date = get_timerange({pair: frame})
results = backtesting.backtest(
{
'stake_amount': default_conf['stake_amount'],
'processed': data_processed,
'max_open_trades': 10,
'start_date': min_date,
'end_date': max_date,
}
processed=data_processed,
stake_amount=default_conf['stake_amount'],
start_date=min_date,
end_date=max_date,
max_open_trades=10,
)
assert len(results) == len(data.trades)

View File

@ -103,14 +103,12 @@ def simple_backtest(config, contour, num_results, mocker, testdatadir) -> None:
min_date, max_date = get_timerange(processed)
assert isinstance(processed, dict)
results = backtesting.backtest(
{
'stake_amount': config['stake_amount'],
'processed': processed,
'max_open_trades': 1,
'position_stacking': False,
'start_date': min_date,
'end_date': max_date,
}
processed=processed,
stake_amount=config['stake_amount'],
start_date=min_date,
end_date=max_date,
max_open_trades=1,
position_stacking=False,
)
# results :: <class 'pandas.core.frame.DataFrame'>
assert len(results) == num_results
@ -132,7 +130,7 @@ def _load_pair_as_ticks(pair, tickfreq):
# FIX: fixturize this?
def _make_backtest_conf(mocker, datadir, conf=None, pair='UNITTEST/BTC', record=None):
def _make_backtest_conf(mocker, datadir, conf=None, pair='UNITTEST/BTC'):
data = history.load_data(datadir=datadir, timeframe='1m', pairs=[pair])
data = trim_dictlist(data, -201)
patch_exchange(mocker)
@ -140,13 +138,12 @@ def _make_backtest_conf(mocker, datadir, conf=None, pair='UNITTEST/BTC', record=
processed = backtesting.strategy.tickerdata_to_dataframe(data)
min_date, max_date = get_timerange(processed)
return {
'stake_amount': conf['stake_amount'],
'processed': processed,
'max_open_trades': 10,
'position_stacking': False,
'record': record,
'stake_amount': conf['stake_amount'],
'start_date': min_date,
'end_date': max_date,
'max_open_trades': 10,
'position_stacking': False,
}
@ -422,14 +419,12 @@ def test_backtest(default_conf, fee, mocker, testdatadir) -> None:
data_processed = backtesting.strategy.tickerdata_to_dataframe(data)
min_date, max_date = get_timerange(data_processed)
results = backtesting.backtest(
{
'stake_amount': default_conf['stake_amount'],
'processed': data_processed,
'max_open_trades': 10,
'position_stacking': False,
'start_date': min_date,
'end_date': max_date,
}
processed=data_processed,
stake_amount=default_conf['stake_amount'],
start_date=min_date,
end_date=max_date,
max_open_trades=10,
position_stacking=False,
)
assert not results.empty
assert len(results) == 2
@ -478,14 +473,12 @@ def test_backtest_1min_ticker_interval(default_conf, fee, mocker, testdatadir) -
processed = backtesting.strategy.tickerdata_to_dataframe(data)
min_date, max_date = get_timerange(processed)
results = backtesting.backtest(
{
'stake_amount': default_conf['stake_amount'],
'processed': processed,
'max_open_trades': 1,
'position_stacking': False,
'start_date': min_date,
'end_date': max_date,
}
processed=processed,
stake_amount=default_conf['stake_amount'],
start_date=min_date,
end_date=max_date,
max_open_trades=1,
position_stacking=False,
)
assert not results.empty
assert len(results) == 1
@ -525,7 +518,7 @@ def test_backtest_clash_buy_sell(mocker, default_conf, testdatadir):
backtesting = Backtesting(default_conf)
backtesting.strategy.advise_buy = fun # Override
backtesting.strategy.advise_sell = fun # Override
results = backtesting.backtest(backtest_conf)
results = backtesting.backtest(**backtest_conf)
assert results.empty
@ -540,7 +533,7 @@ def test_backtest_only_sell(mocker, default_conf, testdatadir):
backtesting = Backtesting(default_conf)
backtesting.strategy.advise_buy = fun # Override
backtesting.strategy.advise_sell = fun # Override
results = backtesting.backtest(backtest_conf)
results = backtesting.backtest(**backtest_conf)
assert results.empty
@ -553,7 +546,7 @@ def test_backtest_alternate_buy_sell(default_conf, fee, mocker, testdatadir):
backtesting = Backtesting(default_conf)
backtesting.strategy.advise_buy = _trend_alternate # Override
backtesting.strategy.advise_sell = _trend_alternate # Override
results = backtesting.backtest(backtest_conf)
results = backtesting.backtest(**backtest_conf)
backtesting._store_backtest_result("test_.json", results)
# 200 candles in backtest data
# won't buy on first (shifted by 1)
@ -598,15 +591,15 @@ def test_backtest_multi_pair(default_conf, fee, mocker, tres, pair, testdatadir)
data_processed = backtesting.strategy.tickerdata_to_dataframe(data)
min_date, max_date = get_timerange(data_processed)
backtest_conf = {
'stake_amount': default_conf['stake_amount'],
'processed': data_processed,
'max_open_trades': 3,
'position_stacking': False,
'stake_amount': default_conf['stake_amount'],
'start_date': min_date,
'end_date': max_date,
'max_open_trades': 3,
'position_stacking': False,
}
results = backtesting.backtest(backtest_conf)
results = backtesting.backtest(**backtest_conf)
# Make sure we have parallel trades
assert len(evaluate_result_multi(results, '5m', 2)) > 0
@ -614,14 +607,14 @@ def test_backtest_multi_pair(default_conf, fee, mocker, tres, pair, testdatadir)
assert len(evaluate_result_multi(results, '5m', 3)) == 0
backtest_conf = {
'stake_amount': default_conf['stake_amount'],
'processed': data_processed,
'max_open_trades': 1,
'position_stacking': False,
'stake_amount': default_conf['stake_amount'],
'start_date': min_date,
'end_date': max_date,
'max_open_trades': 1,
'position_stacking': False,
}
results = backtesting.backtest(backtest_conf)
results = backtesting.backtest(**backtest_conf)
assert len(evaluate_result_multi(results, '5m', 1)) == 0