Keep trade state in LocalTrade
This commit is contained in:
parent
49426a924d
commit
0e8cf366f5
@ -924,7 +924,7 @@ class Backtesting:
|
|||||||
Handling of left open trades at the end of backtesting
|
Handling of left open trades at the end of backtesting
|
||||||
"""
|
"""
|
||||||
for pair in open_trades.keys():
|
for pair in open_trades.keys():
|
||||||
for trade in open_trades[pair]:
|
for trade in list(open_trades[pair]):
|
||||||
if trade.open_order_id and trade.nr_of_successful_entries == 0:
|
if trade.open_order_id and trade.nr_of_successful_entries == 0:
|
||||||
# Ignore trade if entry-order did not fill yet
|
# Ignore trade if entry-order did not fill yet
|
||||||
continue
|
continue
|
||||||
@ -1098,15 +1098,12 @@ class Backtesting:
|
|||||||
indexes: Dict = defaultdict(int)
|
indexes: Dict = defaultdict(int)
|
||||||
current_time = start_date + timedelta(minutes=self.timeframe_min)
|
current_time = start_date + timedelta(minutes=self.timeframe_min)
|
||||||
|
|
||||||
open_trades: Dict[str, List[LocalTrade]] = defaultdict(list)
|
|
||||||
open_trade_count = 0
|
|
||||||
|
|
||||||
self.progress.init_step(BacktestState.BACKTEST, int(
|
self.progress.init_step(BacktestState.BACKTEST, int(
|
||||||
(end_date - start_date) / timedelta(minutes=self.timeframe_min)))
|
(end_date - start_date) / timedelta(minutes=self.timeframe_min)))
|
||||||
|
|
||||||
# Loop timerange and get candle for each pair at that point in time
|
# Loop timerange and get candle for each pair at that point in time
|
||||||
while current_time <= end_date:
|
while current_time <= end_date:
|
||||||
open_trade_count_start = open_trade_count
|
open_trade_count_start = LocalTrade.bt_open_open_trade_count
|
||||||
self.check_abort()
|
self.check_abort()
|
||||||
for i, pair in enumerate(data):
|
for i, pair in enumerate(data):
|
||||||
row_index = indexes[pair]
|
row_index = indexes[pair]
|
||||||
@ -1118,13 +1115,11 @@ class Backtesting:
|
|||||||
indexes[pair] = row_index
|
indexes[pair] = row_index
|
||||||
self.dataprovider._set_dataframe_max_index(row_index)
|
self.dataprovider._set_dataframe_max_index(row_index)
|
||||||
|
|
||||||
for t in list(open_trades[pair]):
|
for t in list(LocalTrade.bt_trades_open_pp[pair]):
|
||||||
# 1. Manage currently open orders of active trades
|
# 1. Manage currently open orders of active trades
|
||||||
if self.manage_open_orders(t, current_time, row):
|
if self.manage_open_orders(t, current_time, row):
|
||||||
# Close trade
|
# Close trade
|
||||||
open_trade_count -= 1
|
|
||||||
open_trade_count_start -= 1
|
open_trade_count_start -= 1
|
||||||
open_trades[pair].remove(t)
|
|
||||||
LocalTrade.remove_bt_trade(t)
|
LocalTrade.remove_bt_trade(t)
|
||||||
self.wallets.update()
|
self.wallets.update()
|
||||||
|
|
||||||
@ -1134,7 +1129,7 @@ class Backtesting:
|
|||||||
# don't open on the last row
|
# don't open on the last row
|
||||||
trade_dir = self.check_for_trade_entry(row)
|
trade_dir = self.check_for_trade_entry(row)
|
||||||
if (
|
if (
|
||||||
(position_stacking or len(open_trades[pair]) == 0)
|
(position_stacking or len(LocalTrade.bt_trades_open_pp[pair]) == 0)
|
||||||
and self.trade_slot_available(max_open_trades, open_trade_count_start)
|
and self.trade_slot_available(max_open_trades, open_trade_count_start)
|
||||||
and current_time != end_date
|
and current_time != end_date
|
||||||
and trade_dir is not None
|
and trade_dir is not None
|
||||||
@ -1146,13 +1141,11 @@ class Backtesting:
|
|||||||
# This emulates previous behavior - not sure if this is correct
|
# This emulates previous behavior - not sure if this is correct
|
||||||
# Prevents entering if the trade-slot was freed in this candle
|
# Prevents entering if the trade-slot was freed in this candle
|
||||||
open_trade_count_start += 1
|
open_trade_count_start += 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)
|
|
||||||
LocalTrade.add_bt_trade(trade)
|
LocalTrade.add_bt_trade(trade)
|
||||||
self.wallets.update()
|
self.wallets.update()
|
||||||
|
|
||||||
for trade in list(open_trades[pair]):
|
for trade in list(LocalTrade.bt_trades_open_pp[pair]):
|
||||||
# 3. Process entry orders.
|
# 3. Process entry orders.
|
||||||
order = trade.select_order(trade.entry_side, is_open=True)
|
order = trade.select_order(trade.entry_side, is_open=True)
|
||||||
if order and self._get_order_filled(order.price, row):
|
if order and self._get_order_filled(order.price, row):
|
||||||
@ -1178,8 +1171,6 @@ class Backtesting:
|
|||||||
trade.close(order.price, show_msg=False)
|
trade.close(order.price, show_msg=False)
|
||||||
|
|
||||||
# logger.debug(f"{pair} - Backtesting exit {trade}")
|
# logger.debug(f"{pair} - Backtesting exit {trade}")
|
||||||
open_trade_count -= 1
|
|
||||||
open_trades[pair].remove(trade)
|
|
||||||
LocalTrade.close_bt_trade(trade)
|
LocalTrade.close_bt_trade(trade)
|
||||||
self.wallets.update()
|
self.wallets.update()
|
||||||
self.run_protections(
|
self.run_protections(
|
||||||
@ -1189,7 +1180,7 @@ class Backtesting:
|
|||||||
self.progress.increment()
|
self.progress.increment()
|
||||||
current_time += timedelta(minutes=self.timeframe_min)
|
current_time += timedelta(minutes=self.timeframe_min)
|
||||||
|
|
||||||
self.handle_left_open(open_trades, data=data)
|
self.handle_left_open(LocalTrade.bt_trades_open_pp, data=data)
|
||||||
self.wallets.update()
|
self.wallets.update()
|
||||||
|
|
||||||
results = trade_list_to_dataframe(LocalTrade.trades)
|
results = trade_list_to_dataframe(LocalTrade.trades)
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
This module contains the class to persist trades into SQLite
|
This module contains the class to persist trades into SQLite
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
from collections import defaultdict
|
||||||
from datetime import datetime, timedelta, timezone
|
from datetime import datetime, timedelta, timezone
|
||||||
from math import isclose
|
from math import isclose
|
||||||
from typing import Any, Dict, List, Optional
|
from typing import Any, Dict, List, Optional
|
||||||
@ -255,6 +256,9 @@ class LocalTrade():
|
|||||||
# Trades container for backtesting
|
# Trades container for backtesting
|
||||||
trades: List['LocalTrade'] = []
|
trades: List['LocalTrade'] = []
|
||||||
trades_open: List['LocalTrade'] = []
|
trades_open: List['LocalTrade'] = []
|
||||||
|
# Copy of trades_open - but indexed by pair
|
||||||
|
bt_trades_open_pp: Dict[str, List['LocalTrade']] = defaultdict(list)
|
||||||
|
bt_open_open_trade_count: int = 0
|
||||||
total_profit: float = 0
|
total_profit: float = 0
|
||||||
realized_profit: float = 0
|
realized_profit: float = 0
|
||||||
|
|
||||||
@ -538,6 +542,8 @@ class LocalTrade():
|
|||||||
"""
|
"""
|
||||||
LocalTrade.trades = []
|
LocalTrade.trades = []
|
||||||
LocalTrade.trades_open = []
|
LocalTrade.trades_open = []
|
||||||
|
LocalTrade.bt_trades_open_pp = defaultdict(list)
|
||||||
|
LocalTrade.bt_open_open_trade_count = 0
|
||||||
LocalTrade.total_profit = 0
|
LocalTrade.total_profit = 0
|
||||||
|
|
||||||
def adjust_min_max_rates(self, current_price: float, current_price_low: float) -> None:
|
def adjust_min_max_rates(self, current_price: float, current_price_low: float) -> None:
|
||||||
@ -1067,6 +1073,8 @@ class LocalTrade():
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def close_bt_trade(trade):
|
def close_bt_trade(trade):
|
||||||
LocalTrade.trades_open.remove(trade)
|
LocalTrade.trades_open.remove(trade)
|
||||||
|
LocalTrade.bt_trades_open_pp[trade.pair].remove(trade)
|
||||||
|
LocalTrade.bt_open_open_trade_count -= 1
|
||||||
LocalTrade.trades.append(trade)
|
LocalTrade.trades.append(trade)
|
||||||
LocalTrade.total_profit += trade.close_profit_abs
|
LocalTrade.total_profit += trade.close_profit_abs
|
||||||
|
|
||||||
@ -1074,12 +1082,16 @@ class LocalTrade():
|
|||||||
def add_bt_trade(trade):
|
def add_bt_trade(trade):
|
||||||
if trade.is_open:
|
if trade.is_open:
|
||||||
LocalTrade.trades_open.append(trade)
|
LocalTrade.trades_open.append(trade)
|
||||||
|
LocalTrade.bt_trades_open_pp[trade.pair].append(trade)
|
||||||
|
LocalTrade.bt_open_open_trade_count += 1
|
||||||
else:
|
else:
|
||||||
LocalTrade.trades.append(trade)
|
LocalTrade.trades.append(trade)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def remove_bt_trade(trade):
|
def remove_bt_trade(trade):
|
||||||
LocalTrade.trades_open.remove(trade)
|
LocalTrade.trades_open.remove(trade)
|
||||||
|
LocalTrade.bt_trades_open_pp[trade.pair].remove(trade)
|
||||||
|
LocalTrade.bt_open_open_trade_count -= 1
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_open_trades() -> List[Any]:
|
def get_open_trades() -> List[Any]:
|
||||||
@ -1096,7 +1108,7 @@ class LocalTrade():
|
|||||||
if Trade.use_db:
|
if Trade.use_db:
|
||||||
return Trade.query.filter(Trade.is_open.is_(True)).count()
|
return Trade.query.filter(Trade.is_open.is_(True)).count()
|
||||||
else:
|
else:
|
||||||
return len(LocalTrade.trades_open)
|
return LocalTrade.bt_open_open_trade_count
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def stoploss_reinitialization(desired_stoploss):
|
def stoploss_reinitialization(desired_stoploss):
|
||||||
|
@ -2406,6 +2406,8 @@ def test_Trade_object_idem():
|
|||||||
'get_trading_volume',
|
'get_trading_volume',
|
||||||
|
|
||||||
)
|
)
|
||||||
|
EXCLUDES2 = ('trades', 'trades_open', 'bt_trades_open_pp', 'bt_open_open_trade_count',
|
||||||
|
'total_profit')
|
||||||
|
|
||||||
# Parent (LocalTrade) should have the same attributes
|
# Parent (LocalTrade) should have the same attributes
|
||||||
for item in trade:
|
for item in trade:
|
||||||
@ -2416,7 +2418,7 @@ 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', 'trades_open', 'total_profit')
|
and item not in EXCLUDES2
|
||||||
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
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user