|
22 | 22 |
|
23 | 23 | from .backtesting import Strategy
|
24 | 24 | from ._plotting import plot_heatmaps as _plot_heatmaps
|
25 |
| -from ._util import _Array, _Indicator, _as_str |
| 25 | +from ._util import _Array, _as_str |
26 | 26 |
|
27 | 27 | __pdoc__ = {}
|
28 | 28 |
|
@@ -280,36 +280,51 @@ def init(self):
|
280 | 280 |
|
281 | 281 | __pdoc__['SignalStrategy.__init__'] = False
|
282 | 282 |
|
283 |
| - def set_signal(self, entry: Sequence[int], exit: Optional[Sequence[bool]] = None, |
| 283 | + def set_signal(self, entry_size: Sequence[float], |
| 284 | + exit_portion: Sequence[float] = None, |
| 285 | + *, |
284 | 286 | plot: bool = True):
|
285 | 287 | """
|
286 |
| - Set entry/exit signal vectors (arrays). An long entry signal is considered |
287 |
| - present wherever `entry` is greater than zero. A short entry signal |
288 |
| - is considered present wherever `entry` is less than zero. If `exit` |
289 |
| - is provided, a nonzero value closes the position, if any; otherwise |
290 |
| - the position is held until a reverse signal in `entry`. |
| 288 | + Set entry/exit signal vectors (arrays). |
| 289 | +
|
| 290 | + A long entry signal is considered present wherever `entry_size` |
| 291 | + is greater than zero, and a short signal wherever `entry_size` |
| 292 | + is less than zero, following `backtesting.backtesting.Order.size` semantics. |
| 293 | +
|
| 294 | + If `exit_portion` is provided, a nonzero value closes portion the position |
| 295 | + (see `backtesting.backtesting.Trade.close()`) in the respective direction |
| 296 | + (positive values close long trades, negative short). |
291 | 297 |
|
292 | 298 | If `plot` is `True`, the signal entry/exit indicators are plotted when
|
293 | 299 | `backtesting.backtesting.Backtest.plot` is called.
|
294 | 300 | """
|
295 |
| - self.__entry_signal = _Indicator(pd.Series(entry, dtype=float).fillna(0), |
296 |
| - name='entry', plot=plot, overlay=False) |
297 |
| - if exit is not None: |
298 |
| - self.__exit_signal = _Indicator(pd.Series(exit, dtype=float).fillna(0), |
299 |
| - name='exit', plot=plot, overlay=False) |
| 301 | + self.__entry_signal = self.I( |
| 302 | + lambda: pd.Series(entry_size, dtype=float).replace(0, np.nan), |
| 303 | + name='entry size', plot=plot, overlay=False, scatter=True, color='black') |
| 304 | + |
| 305 | + if exit_portion is not None: |
| 306 | + self.__exit_signal = self.I( |
| 307 | + lambda: pd.Series(exit_portion, dtype=float).replace(0, np.nan), |
| 308 | + name='exit portion', plot=plot, overlay=False, scatter=True, color='black') |
300 | 309 |
|
301 | 310 | def next(self):
|
302 | 311 | super().next()
|
303 | 312 |
|
304 |
| - if self.position and self.__exit_signal[-1]: |
305 |
| - self.position.close() |
306 |
| - |
307 |
| - signal = self.__entry_signal[-1] |
308 |
| - |
309 |
| - if signal > 0: |
310 |
| - self.buy() |
311 |
| - elif signal < 0: |
312 |
| - self.sell() |
| 313 | + exit_portion = self.__exit_signal[-1] |
| 314 | + if exit_portion > 0: |
| 315 | + for trade in self.trades: |
| 316 | + if trade.is_long: |
| 317 | + trade.close(exit_portion) |
| 318 | + elif exit_portion < 0: |
| 319 | + for trade in self.trades: |
| 320 | + if trade.is_short: |
| 321 | + trade.close(-exit_portion) |
| 322 | + |
| 323 | + entry_size = self.__entry_signal[-1] |
| 324 | + if entry_size > 0: |
| 325 | + self.buy(size=entry_size) |
| 326 | + elif entry_size < 0: |
| 327 | + self.sell(size=-entry_size) |
313 | 328 |
|
314 | 329 |
|
315 | 330 | class TrailingStrategy(Strategy):
|
|
0 commit comments