Skip to content

Commit de7914c

Browse files
committed
Added a function to specifically get the most recent value of ohlc, rather than generating the full slice first. ~12% speed improvement
1 parent 27d8557 commit de7914c

File tree

2 files changed

+45
-12
lines changed

2 files changed

+45
-12
lines changed

backtesting/_util.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,11 @@ def __get_array(self, key) -> _Array:
216216
arr = self.__cache[key] = cast(_Array, self.__arrays[key][:self.__len])
217217
return arr
218218

219+
def current_value(self, key: str):
220+
if self.__len <= 0:
221+
raise IndexError("No data available")
222+
return self.__arrays[key][self.__len - 1]
223+
219224
@property
220225
def Open(self) -> _Array:
221226
return self.__get_array('Open')

backtesting/backtesting.py

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -870,7 +870,9 @@ def next(self):
870870

871871
def _process_orders(self):
872872
data = self._data
873-
open, high, low = data.Open[-1], data.High[-1], data.Low[-1]
873+
open_price = data.current_value("Open")
874+
high_price = data.current_value("High")
875+
low_price = data.current_value("Low")
874876
reprocess_orders = False
875877

876878
# Process orders
@@ -883,7 +885,9 @@ def _process_orders(self):
883885
# Check if stop condition was hit
884886
stop_price = order.stop
885887
if stop_price:
886-
is_stop_hit = ((high >= stop_price) if order.is_long else (low <= stop_price))
888+
is_stop_hit = (
889+
high_price >= stop_price if order.is_long else low_price <= stop_price
890+
)
887891
if not is_stop_hit:
888892
continue
889893

@@ -894,7 +898,9 @@ def _process_orders(self):
894898
# Determine purchase price.
895899
# Check if limit order can be filled.
896900
if order.limit:
897-
is_limit_hit = low <= order.limit if order.is_long else high >= order.limit
901+
is_limit_hit = (
902+
low_price <= order.limit if order.is_long else high_price >= order.limit
903+
)
898904
# When stop and limit are hit within the same bar, we pessimistically
899905
# assume limit was hit before the stop (i.e. "before it counts")
900906
is_limit_hit_before_stop = (is_limit_hit and
@@ -905,14 +911,20 @@ def _process_orders(self):
905911
continue
906912

907913
# stop_price, if set, was hit within this bar
908-
price = (min(stop_price or open, order.limit)
909-
if order.is_long else
910-
max(stop_price or open, order.limit))
914+
price = (
915+
min(stop_price or open_price, order.limit)
916+
if order.is_long
917+
else max(stop_price or open_price, order.limit)
918+
)
911919
else:
912920
# Market-if-touched / market order
913921
# Contingent orders always on next open
914922
prev_close = data.Close[-2]
915-
price = prev_close if self._trade_on_close and not order.is_contingent else open
923+
price = (
924+
prev_close
925+
if self._trade_on_close and not order.is_contingent
926+
else open_price
927+
)
916928
if stop_price:
917929
price = max(price, stop_price) if order.is_long else min(price, stop_price)
918930

@@ -1018,12 +1030,28 @@ def _process_orders(self):
10181030
reprocess_orders = True
10191031
# Order.stop and TP hit within the same bar, but SL wasn't. This case
10201032
# is not ambiguous, because stop and TP go in the same price direction.
1021-
elif stop_price and not order.limit and order.tp and (
1022-
(order.is_long and order.tp <= high and (order.sl or -np.inf) < low) or
1023-
(order.is_short and order.tp >= low and (order.sl or np.inf) > high)):
1033+
elif (
1034+
stop_price
1035+
and not order.limit
1036+
and order.tp
1037+
and (
1038+
(
1039+
order.is_long
1040+
and order.tp <= high_price
1041+
and (order.sl or -np.inf) < low_price
1042+
)
1043+
or (
1044+
order.is_short
1045+
and order.tp >= low_price
1046+
and (order.sl or np.inf) > high_price
1047+
)
1048+
)
1049+
):
10241050
reprocess_orders = True
1025-
elif (low <= (order.sl or -np.inf) <= high or
1026-
low <= (order.tp or -np.inf) <= high):
1051+
elif (
1052+
low_price <= (order.sl or -np.inf) <= high_price
1053+
or low_price <= (order.tp or -np.inf) <= high_price
1054+
):
10271055
warnings.warn(
10281056
f"({data.index[-1]}) A contingent SL/TP order would execute in the "
10291057
"same bar its parent stop/limit order was turned into a trade. "

0 commit comments

Comments
 (0)