Skip to content

Commit 028d1eb

Browse files
committed
Cache calculations for pnl, total position size etc. when we are not changing positions at all. ~13% time saving
1 parent de7914c commit 028d1eb

File tree

1 file changed

+37
-4
lines changed

1 file changed

+37
-4
lines changed

backtesting/backtesting.py

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -362,17 +362,21 @@ def __bool__(self):
362362
@property
363363
def size(self) -> float:
364364
"""Position size in units of asset. Negative if position is short."""
365-
return sum(trade.size for trade in self.__broker.trades)
365+
if self.__broker._trade_sums_dirty:
366+
self.__broker._recalculate_trade_sums()
367+
return self.__broker._open_trade_size_sum
366368

367369
@property
368370
def pl(self) -> float:
369371
"""Profit (positive) or loss (negative) of the current position in cash units."""
370-
return sum(trade.pl for trade in self.__broker.trades)
372+
return self.__broker.unrealized_pl
371373

372374
@property
373375
def pl_pct(self) -> float:
374376
"""Profit (positive) or loss (negative) of the current position in percent."""
375-
total_invested = sum(trade.entry_price * abs(trade.size) for trade in self.__broker.trades)
377+
if self.__broker._trade_sums_dirty:
378+
self.__broker._recalculate_trade_sums()
379+
total_invested = self.__broker._open_trade_entry_abs_value_sum
376380
return (self.pl / total_invested) * 100 if total_invested else 0
377381

378382
@property
@@ -772,6 +776,10 @@ def __init__(self, *, data, cash, spread, commission, margin,
772776
self.trades: List[Trade] = []
773777
self.position = Position(self)
774778
self.closed_trades: List[Trade] = []
779+
self._trade_sums_dirty = True
780+
self._open_trade_size_sum = 0
781+
self._open_trade_entry_value_sum = 0.0
782+
self._open_trade_entry_abs_value_sum = 0.0
775783

776784
def _commission_func(self, order_size, price):
777785
return self._commission_fixed + abs(order_size) * price * self._commission_relative
@@ -829,6 +837,28 @@ def new_order(self,
829837

830838
return order
831839

840+
def _mark_trade_sums_dirty(self) -> None:
841+
self._trade_sums_dirty = True
842+
843+
def _recalculate_trade_sums(self) -> None:
844+
self._open_trade_size_sum = sum(int(trade.size) for trade in self.trades)
845+
self._open_trade_entry_value_sum = sum(
846+
trade.size * trade.entry_price for trade in self.trades
847+
)
848+
self._open_trade_entry_abs_value_sum = sum(
849+
abs(trade.size) * trade.entry_price for trade in self.trades
850+
)
851+
self._trade_sums_dirty = False
852+
853+
@property
854+
def unrealized_pl(self) -> float:
855+
if self._trade_sums_dirty:
856+
self._recalculate_trade_sums()
857+
if not self.trades:
858+
return 0.0
859+
current_price = float(self._data.current_value("Close"))
860+
return current_price * self._open_trade_size_sum - self._open_trade_entry_value_sum
861+
832862
@property
833863
def last_price(self) -> float:
834864
""" Price at the last (current) close. """
@@ -843,7 +873,7 @@ def _adjusted_price(self, size=None, price=None) -> float:
843873

844874
@property
845875
def equity(self) -> float:
846-
return self._cash + sum(trade.pl for trade in self.trades)
876+
return self._cash + self.unrealized_pl
847877

848878
@property
849879
def margin_available(self) -> float:
@@ -1071,6 +1101,7 @@ def _process_orders(self):
10711101
def _reduce_trade(self, trade: Trade, price: float, size: float, time_index: int):
10721102
assert trade.size * size < 0
10731103
assert abs(trade.size) >= abs(size)
1104+
self._mark_trade_sums_dirty()
10741105

10751106
size_left = trade.size + size
10761107
assert size_left * trade.size >= 0
@@ -1091,6 +1122,7 @@ def _reduce_trade(self, trade: Trade, price: float, size: float, time_index: int
10911122
self._close_trade(close_trade, price, time_index)
10921123

10931124
def _close_trade(self, trade: Trade, price: float, time_index: int):
1125+
self._mark_trade_sums_dirty()
10941126
self.trades.remove(trade)
10951127
if trade._sl_order:
10961128
self.orders.remove(trade._sl_order)
@@ -1114,6 +1146,7 @@ def _open_trade(self, price: float, size: int,
11141146
self.trades.append(trade)
11151147
# Apply broker commission at trade open
11161148
self._cash -= self._commission(size, price)
1149+
self._mark_trade_sums_dirty()
11171150
# Create SL/TP (bracket) orders.
11181151
if tp:
11191152
trade.tp = tp

0 commit comments

Comments
 (0)