-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Description
Expected behavior
The SLs should be displayed in the trade list (within the result stats) whenever they are set (according to the strategy).

Actual behavior
When I get the list of the trades, some of them are unprovided with the SL. This seems to happen for the trades being closed for reaching the SL.
Additional info, steps to reproduce, full crash traceback, screenshots
I analyzed the source code and the SL values are correctly passed within the strategy.buy()
method. I followed the code and the values are actually erased when order._replace(stop_price=None)
is called (due to the the stop loss being reached).
backtesting.py/backtesting/backtesting.py
Lines 883 to 892 in f8f4969
# Check if stop condition was hit | |
stop_price = order.stop | |
if stop_price: | |
is_stop_hit = ((high >= stop_price) if order.is_long else (low <= stop_price)) | |
if not is_stop_hit: | |
continue | |
# > When the stop price is reached, a stop order becomes a market/limit order. | |
# https://www.sec.gov/fast-answers/answersstopordhtm.html | |
order._replace(stop_price=None) |
The reason is the stop_price
parameter , aka order.stop
, having the same id
of the order.parent_trade.sl
. The "coupling" arises within the __set_contingent
method, line 738-739, where we create the stop order by passing the price
variable (and its reference), which is the SL of the order. In brief, the contingent stop order has the attribute stop
with the same reference of the attribute sl
of the parent order. When the former gets None'd, the latter follows it.
backtesting.py/backtesting/backtesting.py
Lines 730 to 740 in f8f4969
def __set_contingent(self, type, price): | |
assert type in ('sl', 'tp') | |
assert price is None or 0 < price < np.inf, f'Make sure 0 < price < inf! price: {price}' | |
attr = f'_{self.__class__.__qualname__}__{type}_order' | |
order: Order = getattr(self, attr) | |
if order: | |
order.cancel() | |
if price: | |
kwargs = {'stop': price} if type == 'sl' else {'limit': price} | |
order = self.__broker.new_order(-self.size, trade=self, tag=self.tag, **kwargs) | |
setattr(self, attr, order) |
As proof, see below where we can see that the two variables point to the same address.
Software versions
- backtesting 0.6.4
- pandas 2.2.3
- numpy 1.26.0
- bokeh 3.7.3