Skip to content

Commit 9ee9174

Browse files
committed
BUG: Validate SL/TP vs. adjusted_price when placing orders
Fixes #147
1 parent 921c931 commit 9ee9174

File tree

2 files changed

+20
-8
lines changed

2 files changed

+20
-8
lines changed

backtesting/backtesting.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -699,15 +699,16 @@ def new_order(self,
699699
tp = tp and float(tp)
700700

701701
is_long = size > 0
702+
adjusted_price = self._adjusted_price(size)
702703

703704
if is_long:
704-
if not (sl or -np.inf) <= (limit or stop or self.last_price) <= (tp or np.inf):
705+
if not (sl or -np.inf) < (limit or stop or adjusted_price) < (tp or np.inf):
705706
raise ValueError("Long orders require: SL ({}) < LIMIT ({}) < TP ({})".format(
706-
sl, limit or stop or self.last_price, tp))
707+
sl, limit or stop or adjusted_price, tp))
707708
else:
708-
if not (tp or -np.inf) <= (limit or stop or self.last_price) <= (sl or np.inf):
709+
if not (tp or -np.inf) < (limit or stop or adjusted_price) < (sl or np.inf):
709710
raise ValueError("Short orders require: TP ({}) < LIMIT ({}) < SL ({})".format(
710-
tp, limit or stop or self.last_price, sl))
711+
tp, limit or stop or adjusted_price, sl))
711712

712713
order = Order(self, size, limit, stop, sl, tp, trade)
713714
# Put the new order in the order queue,
@@ -730,11 +731,13 @@ def new_order(self,
730731

731732
@property
732733
def last_price(self) -> float:
733-
"""Return price at the last (current) close.
734-
Used e.g. in `Orders._is_price_ok()` to see if the set price is reasonable.
735-
"""
734+
""" Price at the last (current) close. """
736735
return self._data.Close[-1]
737736

737+
def _adjusted_price(self, size=None, price=None) -> float:
738+
""" Long/short `price`, adjusted for commitions."""
739+
return (price or self.last_price) * (1 + copysign(self._commission, size))
740+
738741
@property
739742
def equity(self) -> float:
740743
return self._cash + sum(trade.pl for trade in self.trades)
@@ -839,7 +842,7 @@ def _process_orders(self):
839842

840843
# Adjust price to include commission (or bid-ask spread).
841844
# In long positions, the adjusted price is a fraction higher, and vice versa.
842-
adjusted_price = price * (1 + copysign(self._commission, order.size))
845+
adjusted_price = self._adjusted_price(order.size, price)
843846

844847
# If order size was specified proportionally,
845848
# precompute true size in units, accounting for margin and spread/commissions

backtesting/test/_test.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,15 @@ def next(self):
392392

393393
self.assertFalse(Backtest(SHORT_DATA, S).run()._trades.empty)
394394

395+
def test_check_adjusted_price_when_placing_order(self):
396+
class S(Strategy):
397+
def init(self): pass
398+
399+
def next(self):
400+
self.buy(tp=self.data.Close * 1.01)
401+
402+
self.assertRaises(ValueError, Backtest(SHORT_DATA, S, commission=.02).run)
403+
395404

396405
class TestStrategy(TestCase):
397406
def _Backtest(self, strategy_coroutine, **kwargs):

0 commit comments

Comments
 (0)