Improve backtest performance

This commit is contained in:
Matthias 2021-03-13 10:16:32 +01:00
parent ef9977fc1e
commit d1acc8092c
5 changed files with 42 additions and 9 deletions

View File

@ -377,7 +377,7 @@ class Backtesting:
open_trade_count += 1 open_trade_count += 1
# logger.debug(f"{pair} - Emulate creation of new trade: {trade}.") # logger.debug(f"{pair} - Emulate creation of new trade: {trade}.")
open_trades[pair].append(trade) open_trades[pair].append(trade)
LocalTrade.trades.append(trade) LocalTrade.add_bt_trade(trade)
for trade in open_trades[pair]: for trade in open_trades[pair]:
# also check the buying candle for sell conditions. # also check the buying candle for sell conditions.
@ -387,6 +387,8 @@ class Backtesting:
# logger.debug(f"{pair} - Backtesting sell {trade}") # logger.debug(f"{pair} - Backtesting sell {trade}")
open_trade_count -= 1 open_trade_count -= 1
open_trades[pair].remove(trade) open_trades[pair].remove(trade)
LocalTrade.close_bt_trade(trade)
trades.append(trade_entry) trades.append(trade_entry)
if enable_protections: if enable_protections:
self.protections.stop_per_pair(pair, row[DATE_IDX]) self.protections.stop_per_pair(pair, row[DATE_IDX])

View File

@ -208,6 +208,8 @@ class LocalTrade():
use_db: bool = False use_db: bool = False
# Trades container for backtesting # Trades container for backtesting
trades: List['LocalTrade'] = [] trades: List['LocalTrade'] = []
trades_open: List['LocalTrade'] = []
total_profit: float = 0
id: int = 0 id: int = 0
@ -350,6 +352,8 @@ class LocalTrade():
Resets all trades. Only active for backtesting mode. Resets all trades. Only active for backtesting mode.
""" """
LocalTrade.trades = [] LocalTrade.trades = []
LocalTrade.trades_open = []
LocalTrade.total_profit = 0
def adjust_min_max_rates(self, current_price: float) -> None: def adjust_min_max_rates(self, current_price: float) -> None:
""" """
@ -599,7 +603,17 @@ class LocalTrade():
""" """
# Offline mode - without database # Offline mode - without database
sel_trades = [trade for trade in LocalTrade.trades] if is_open is not None:
if is_open:
sel_trades = LocalTrade.trades_open
else:
sel_trades = LocalTrade.trades
else:
# Not used during backtesting, but might be used by a strategy
sel_trades = [trade for trade in LocalTrade.trades + LocalTrade.trades_open
if trade.is_open == is_open]
if pair: if pair:
sel_trades = [trade for trade in sel_trades if trade.pair == pair] sel_trades = [trade for trade in sel_trades if trade.pair == pair]
if open_date: if open_date:
@ -607,10 +621,22 @@ class LocalTrade():
if close_date: if close_date:
sel_trades = [trade for trade in sel_trades if trade.close_date sel_trades = [trade for trade in sel_trades if trade.close_date
and trade.close_date > close_date] and trade.close_date > close_date]
if is_open is not None:
sel_trades = [trade for trade in sel_trades if trade.is_open == is_open]
return sel_trades return sel_trades
@staticmethod
def close_bt_trade(trade):
LocalTrade.trades_open.remove(trade)
LocalTrade.trades.append(trade)
LocalTrade.total_profit += trade.close_profit_abs
@staticmethod
def add_bt_trade(trade):
if trade.is_open:
LocalTrade.trades_open.append(trade)
else:
LocalTrade.trades.append(trade)
@staticmethod @staticmethod
def get_open_trades() -> List[Any]: def get_open_trades() -> List[Any]:
""" """

View File

@ -10,7 +10,7 @@ import arrow
from freqtrade.constants import UNLIMITED_STAKE_AMOUNT from freqtrade.constants import UNLIMITED_STAKE_AMOUNT
from freqtrade.exceptions import DependencyException from freqtrade.exceptions import DependencyException
from freqtrade.exchange import Exchange from freqtrade.exchange import Exchange
from freqtrade.persistence import Trade from freqtrade.persistence import LocalTrade, Trade
from freqtrade.state import RunMode from freqtrade.state import RunMode
@ -66,9 +66,14 @@ class Wallets:
""" """
# Recreate _wallets to reset closed trade balances # Recreate _wallets to reset closed trade balances
_wallets = {} _wallets = {}
closed_trades = Trade.get_trades_proxy(is_open=False)
open_trades = Trade.get_trades_proxy(is_open=True) open_trades = Trade.get_trades_proxy(is_open=True)
tot_profit = sum([trade.close_profit_abs for trade in closed_trades]) # If not backtesting...
# TODO: potentially remove the ._log workaround to determine backtest mode.
if self._log:
closed_trades = Trade.get_trades_proxy(is_open=False)
tot_profit = sum([trade.close_profit_abs for trade in closed_trades])
else:
tot_profit = LocalTrade.total_profit
tot_in_trades = sum([trade.stake_amount for trade in open_trades]) tot_in_trades = sum([trade.stake_amount for trade in open_trades])
current_stake = self.start_cap + tot_profit - tot_in_trades current_stake = self.start_cap + tot_profit - tot_in_trades

View File

@ -191,7 +191,7 @@ def create_mock_trades(fee, use_db: bool = True):
if use_db: if use_db:
Trade.session.add(trade) Trade.session.add(trade)
else: else:
LocalTrade.trades.append(trade) LocalTrade.add_bt_trade(trade)
# Simulate dry_run entries # Simulate dry_run entries
trade = mock_trade_1(fee) trade = mock_trade_1(fee)

View File

@ -1196,6 +1196,6 @@ def test_Trade_object_idem():
# Fails if only a column is added without corresponding parent field # Fails if only a column is added without corresponding parent field
for item in localtrade: for item in localtrade:
if (not item.startswith('__') if (not item.startswith('__')
and item not in ('trades', ) and item not in ('trades', 'trades_open', 'total_profit')
and type(getattr(LocalTrade, item)) not in (property, FunctionType)): and type(getattr(LocalTrade, item)) not in (property, FunctionType)):
assert item in trade assert item in trade