Enable compounding for backtesting
This commit is contained in:
parent
959ff99046
commit
e4abe902fc
@ -17,7 +17,7 @@ from freqtrade.data import history
|
|||||||
from freqtrade.data.btanalysis import trade_list_to_dataframe
|
from freqtrade.data.btanalysis import trade_list_to_dataframe
|
||||||
from freqtrade.data.converter import trim_dataframe
|
from freqtrade.data.converter import trim_dataframe
|
||||||
from freqtrade.data.dataprovider import DataProvider
|
from freqtrade.data.dataprovider import DataProvider
|
||||||
from freqtrade.exceptions import OperationalException
|
from freqtrade.exceptions import DependencyException, OperationalException
|
||||||
from freqtrade.exchange import timeframe_to_minutes, timeframe_to_seconds
|
from freqtrade.exchange import timeframe_to_minutes, timeframe_to_seconds
|
||||||
from freqtrade.mixins import LoggingMixin
|
from freqtrade.mixins import LoggingMixin
|
||||||
from freqtrade.optimize.optimize_reports import (generate_backtest_stats, show_backtest_results,
|
from freqtrade.optimize.optimize_reports import (generate_backtest_stats, show_backtest_results,
|
||||||
@ -270,6 +270,30 @@ class Backtesting:
|
|||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def _enter_trade(self, pair: str, row, max_open_trades: int,
|
||||||
|
open_trade_count: int) -> Optional[Trade]:
|
||||||
|
self.update_wallets()
|
||||||
|
try:
|
||||||
|
stake_amount = self.wallets.get_trade_stake_amount(
|
||||||
|
pair, max_open_trades - open_trade_count, None)
|
||||||
|
except DependencyException:
|
||||||
|
stake_amount = 0
|
||||||
|
if stake_amount:
|
||||||
|
# Enter trade
|
||||||
|
trade = Trade(
|
||||||
|
pair=pair,
|
||||||
|
open_rate=row[OPEN_IDX],
|
||||||
|
open_date=row[DATE_IDX],
|
||||||
|
stake_amount=stake_amount,
|
||||||
|
amount=round(stake_amount / row[OPEN_IDX], 8),
|
||||||
|
fee_open=self.fee,
|
||||||
|
fee_close=self.fee,
|
||||||
|
is_open=True,
|
||||||
|
exchange='backtesting',
|
||||||
|
)
|
||||||
|
return trade
|
||||||
|
return None
|
||||||
|
|
||||||
def handle_left_open(self, open_trades: Dict[str, List[Trade]],
|
def handle_left_open(self, open_trades: Dict[str, List[Trade]],
|
||||||
data: Dict[str, List[Tuple]]) -> List[Trade]:
|
data: Dict[str, List[Tuple]]) -> List[Trade]:
|
||||||
"""
|
"""
|
||||||
@ -290,7 +314,7 @@ class Backtesting:
|
|||||||
trades.append(trade1)
|
trades.append(trade1)
|
||||||
return trades
|
return trades
|
||||||
|
|
||||||
def backtest(self, processed: Dict, stake_amount: float,
|
def backtest(self, processed: Dict,
|
||||||
start_date: datetime, end_date: datetime,
|
start_date: datetime, end_date: datetime,
|
||||||
max_open_trades: int = 0, position_stacking: bool = False,
|
max_open_trades: int = 0, position_stacking: bool = False,
|
||||||
enable_protections: bool = False) -> DataFrame:
|
enable_protections: bool = False) -> DataFrame:
|
||||||
@ -302,7 +326,6 @@ class Backtesting:
|
|||||||
Avoid extensive logging in this method and functions it calls.
|
Avoid extensive logging in this method and functions it calls.
|
||||||
|
|
||||||
:param processed: a processed dictionary with format {pair, data}
|
: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 start_date: backtesting timerange start datetime
|
||||||
:param end_date: backtesting timerange end datetime
|
:param end_date: backtesting timerange end datetime
|
||||||
:param max_open_trades: maximum number of concurrent trades, <= 0 means unlimited
|
:param max_open_trades: maximum number of concurrent trades, <= 0 means unlimited
|
||||||
@ -310,10 +333,6 @@ class Backtesting:
|
|||||||
:param enable_protections: Should protections be enabled?
|
:param enable_protections: Should protections be enabled?
|
||||||
:return: DataFrame with trades (results of backtesting)
|
:return: DataFrame with trades (results of backtesting)
|
||||||
"""
|
"""
|
||||||
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: List[Trade] = []
|
trades: List[Trade] = []
|
||||||
self.prepare_backtest(enable_protections)
|
self.prepare_backtest(enable_protections)
|
||||||
|
|
||||||
@ -356,30 +375,18 @@ class Backtesting:
|
|||||||
and tmp != end_date
|
and tmp != end_date
|
||||||
and row[BUY_IDX] == 1 and row[SELL_IDX] != 1
|
and row[BUY_IDX] == 1 and row[SELL_IDX] != 1
|
||||||
and not PairLocks.is_pair_locked(pair, row[DATE_IDX])):
|
and not PairLocks.is_pair_locked(pair, row[DATE_IDX])):
|
||||||
self.update_wallets()
|
trade = self._enter_trade(pair, row, max_open_trades, open_trade_count_start)
|
||||||
# Enter trade
|
if trade:
|
||||||
trade = Trade(
|
# TODO: hacky workaround to avoid opening > max_open_trades
|
||||||
pair=pair,
|
# This emulates previous behaviour - not sure if this is correct
|
||||||
open_rate=row[OPEN_IDX],
|
# Prevents buying if the trade-slot was freed in this candle
|
||||||
open_date=row[DATE_IDX],
|
open_trade_count_start += 1
|
||||||
stake_amount=stake_amount,
|
open_trade_count += 1
|
||||||
amount=round(stake_amount / row[OPEN_IDX], 8),
|
# logger.debug(f"{pair} - Backtesting emulates creation of new trade: {trade}.")
|
||||||
fee_open=self.fee,
|
open_trades[pair].append(trade)
|
||||||
fee_close=self.fee,
|
Trade.trades.append(trade)
|
||||||
is_open=True,
|
|
||||||
exchange='backtesting',
|
|
||||||
)
|
|
||||||
# TODO: hacky workaround to avoid opening > max_open_trades
|
|
||||||
# This emulates previous behaviour - not sure if this is correct
|
|
||||||
# Prevents buying if the trade-slot was freed in this candle
|
|
||||||
open_trade_count_start += 1
|
|
||||||
open_trade_count += 1
|
|
||||||
# logger.debug(f"{pair} - Backtesting emulates creation of new trade: {trade}.")
|
|
||||||
open_trades[pair].append(trade)
|
|
||||||
Trade.trades.append(trade)
|
|
||||||
|
|
||||||
for trade in open_trades[pair]:
|
for trade in open_trades[pair]:
|
||||||
# since indexes has been incremented before, we need to go one step back to
|
|
||||||
# also check the buying candle for sell conditions.
|
# also check the buying candle for sell conditions.
|
||||||
trade_entry = self._get_sell_trade_entry(trade, row)
|
trade_entry = self._get_sell_trade_entry(trade, row)
|
||||||
# Sell occured
|
# Sell occured
|
||||||
@ -431,7 +438,6 @@ class Backtesting:
|
|||||||
# Execute backtest and store results
|
# Execute backtest and store results
|
||||||
results = self.backtest(
|
results = self.backtest(
|
||||||
processed=preprocessed,
|
processed=preprocessed,
|
||||||
stake_amount=self.config['stake_amount'],
|
|
||||||
start_date=min_date.datetime,
|
start_date=min_date.datetime,
|
||||||
end_date=max_date.datetime,
|
end_date=max_date.datetime,
|
||||||
max_open_trades=max_open_trades,
|
max_open_trades=max_open_trades,
|
||||||
|
@ -537,7 +537,6 @@ class Hyperopt:
|
|||||||
|
|
||||||
backtesting_results = self.backtesting.backtest(
|
backtesting_results = self.backtesting.backtest(
|
||||||
processed=processed,
|
processed=processed,
|
||||||
stake_amount=self.config['stake_amount'],
|
|
||||||
start_date=min_date.datetime,
|
start_date=min_date.datetime,
|
||||||
end_date=max_date.datetime,
|
end_date=max_date.datetime,
|
||||||
max_open_trades=self.max_open_trades,
|
max_open_trades=self.max_open_trades,
|
||||||
|
@ -503,7 +503,6 @@ def test_backtest_results(default_conf, fee, mocker, caplog, data) -> None:
|
|||||||
min_date, max_date = get_timerange({pair: frame})
|
min_date, max_date = get_timerange({pair: frame})
|
||||||
results = backtesting.backtest(
|
results = backtesting.backtest(
|
||||||
processed=data_processed,
|
processed=data_processed,
|
||||||
stake_amount=default_conf['stake_amount'],
|
|
||||||
start_date=min_date,
|
start_date=min_date,
|
||||||
end_date=max_date,
|
end_date=max_date,
|
||||||
max_open_trades=10,
|
max_open_trades=10,
|
||||||
|
@ -90,7 +90,6 @@ def simple_backtest(config, contour, mocker, testdatadir) -> None:
|
|||||||
assert isinstance(processed, dict)
|
assert isinstance(processed, dict)
|
||||||
results = backtesting.backtest(
|
results = backtesting.backtest(
|
||||||
processed=processed,
|
processed=processed,
|
||||||
stake_amount=config['stake_amount'],
|
|
||||||
start_date=min_date,
|
start_date=min_date,
|
||||||
end_date=max_date,
|
end_date=max_date,
|
||||||
max_open_trades=1,
|
max_open_trades=1,
|
||||||
@ -111,7 +110,6 @@ def _make_backtest_conf(mocker, datadir, conf=None, pair='UNITTEST/BTC'):
|
|||||||
min_date, max_date = get_timerange(processed)
|
min_date, max_date = get_timerange(processed)
|
||||||
return {
|
return {
|
||||||
'processed': processed,
|
'processed': processed,
|
||||||
'stake_amount': conf['stake_amount'],
|
|
||||||
'start_date': min_date,
|
'start_date': min_date,
|
||||||
'end_date': max_date,
|
'end_date': max_date,
|
||||||
'max_open_trades': 10,
|
'max_open_trades': 10,
|
||||||
@ -461,7 +459,6 @@ def test_backtest_one(default_conf, fee, mocker, testdatadir) -> None:
|
|||||||
min_date, max_date = get_timerange(processed)
|
min_date, max_date = get_timerange(processed)
|
||||||
results = backtesting.backtest(
|
results = backtesting.backtest(
|
||||||
processed=processed,
|
processed=processed,
|
||||||
stake_amount=default_conf['stake_amount'],
|
|
||||||
start_date=min_date,
|
start_date=min_date,
|
||||||
end_date=max_date,
|
end_date=max_date,
|
||||||
max_open_trades=10,
|
max_open_trades=10,
|
||||||
@ -523,7 +520,6 @@ def test_backtest_1min_timeframe(default_conf, fee, mocker, testdatadir) -> None
|
|||||||
min_date, max_date = get_timerange(processed)
|
min_date, max_date = get_timerange(processed)
|
||||||
results = backtesting.backtest(
|
results = backtesting.backtest(
|
||||||
processed=processed,
|
processed=processed,
|
||||||
stake_amount=default_conf['stake_amount'],
|
|
||||||
start_date=min_date,
|
start_date=min_date,
|
||||||
end_date=max_date,
|
end_date=max_date,
|
||||||
max_open_trades=1,
|
max_open_trades=1,
|
||||||
@ -678,7 +674,6 @@ def test_backtest_multi_pair(default_conf, fee, mocker, tres, pair, testdatadir)
|
|||||||
min_date, max_date = get_timerange(processed)
|
min_date, max_date = get_timerange(processed)
|
||||||
backtest_conf = {
|
backtest_conf = {
|
||||||
'processed': processed,
|
'processed': processed,
|
||||||
'stake_amount': default_conf['stake_amount'],
|
|
||||||
'start_date': min_date,
|
'start_date': min_date,
|
||||||
'end_date': max_date,
|
'end_date': max_date,
|
||||||
'max_open_trades': 3,
|
'max_open_trades': 3,
|
||||||
@ -694,7 +689,6 @@ def test_backtest_multi_pair(default_conf, fee, mocker, tres, pair, testdatadir)
|
|||||||
|
|
||||||
backtest_conf = {
|
backtest_conf = {
|
||||||
'processed': processed,
|
'processed': processed,
|
||||||
'stake_amount': default_conf['stake_amount'],
|
|
||||||
'start_date': min_date,
|
'start_date': min_date,
|
||||||
'end_date': max_date,
|
'end_date': max_date,
|
||||||
'max_open_trades': 1,
|
'max_open_trades': 1,
|
||||||
|
Loading…
Reference in New Issue
Block a user