diff --git a/src/pybroker/context.py b/src/pybroker/context.py index 4d99502..e6da781 100644 --- a/src/pybroker/context.py +++ b/src/pybroker/context.py @@ -909,6 +909,10 @@ def cancel_all_pending_orders(self, symbol: Optional[str] = None): """ self._pending_order_scope.remove_all(symbol) + def cancel_stop(self, stop_id: int) -> bool: + """Cancels a :class:`pybroker.portfolio.Stop` with ``stop_id``.""" + return self._portfolio.remove_stop(stop_id) + def cancel_stops( self, val: Union[str, Position, Entry], diff --git a/tests/test_strategy.py b/tests/test_strategy.py index 2508139..270d9e7 100644 --- a/tests/test_strategy.py +++ b/tests/test_strategy.py @@ -1913,6 +1913,32 @@ def exec_fn(ctx): assert np.isnan(sell_order["limit_price"]) assert sell_order["fees"] == 0 + def test_backtest_when_cancel_stop(self, data_source_df): + def exec_fn(ctx): + if ctx.bars == 1: + ctx.buy_shares = 100 + ctx.stop_loss = 10 + elif ctx.bars == 10: + entry = tuple(ctx.long_pos().entries)[0] + stop = next(iter(entry.stops)) + assert ctx.cancel_stop(stop_id=stop.id) + + df = data_source_df[data_source_df["symbol"] == "SPY"] + dates = df["date"].unique() + dates = dates[dates <= np.datetime64(END_DATE)] + strategy = Strategy(data_source_df, START_DATE, END_DATE) + strategy.add_execution(exec_fn, "SPY") + result = strategy.backtest(calc_bootstrap=False) + assert not len(result.trades) + assert len(result.orders) == 1 + buy_order = result.orders.iloc[0] + assert buy_order["type"] == "buy" + assert buy_order["symbol"] == "SPY" + assert buy_order["date"] == dates[1] + assert buy_order["shares"] == 100 + assert np.isnan(buy_order["limit_price"]) + assert buy_order["fees"] == 0 + def test_backtest_when_cancel_stops(self, data_source_df): def exec_fn(ctx): if ctx.bars == 1: