|
15 | 15 | from itertools import compress
|
16 | 16 | from numbers import Number
|
17 | 17 | from inspect import currentframe
|
18 |
| -from typing import Sequence, Optional, Union, Callable |
| 18 | +from typing import Sequence, Union, Callable |
19 | 19 |
|
20 | 20 | import numpy as np
|
21 | 21 | import pandas as pd
|
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 |
|
@@ -270,36 +270,51 @@ def init(self):
|
270 | 270 |
|
271 | 271 | __pdoc__['SignalStrategy.__init__'] = False
|
272 | 272 |
|
273 |
| - def set_signal(self, entry: Sequence[int], exit: Optional[Sequence[bool]] = None, |
| 273 | + def set_signal(self, entry_size: Sequence[float], |
| 274 | + exit_portion: Sequence[float] = None, |
| 275 | + *, |
274 | 276 | plot: bool = True):
|
275 | 277 | """
|
276 |
| - Set entry/exit signal vectors (arrays). An long entry signal is considered |
277 |
| - present wherever `entry` is greater than zero. A short entry signal |
278 |
| - is considered present wherever `entry` is less than zero. If `exit` |
279 |
| - is provided, a nonzero value closes the position, if any; otherwise |
280 |
| - the position is held until a reverse signal in `entry`. |
| 278 | + Set entry/exit signal vectors (arrays). |
| 279 | +
|
| 280 | + A long entry signal is considered present wherever `entry_size` |
| 281 | + is greater than zero, and a short signal wherever `entry_size` |
| 282 | + is less than zero, following `backtesting.backtesting.Order.size` semantics. |
| 283 | +
|
| 284 | + If `exit_portion` is provided, a nonzero value closes portion the position |
| 285 | + (see `backtesting.backtesting.Trade.close()`) in the respective direction |
| 286 | + (positive values close long trades, negative short). |
281 | 287 |
|
282 | 288 | If `plot` is `True`, the signal entry/exit indicators are plotted when
|
283 | 289 | `backtesting.backtesting.Backtest.plot` is called.
|
284 | 290 | """
|
285 |
| - self.__entry_signal = _Indicator(pd.Series(entry, dtype=float).fillna(0), |
286 |
| - name='entry', plot=plot, overlay=False) |
287 |
| - if exit is not None: |
288 |
| - self.__exit_signal = _Indicator(pd.Series(exit, dtype=float).fillna(0), |
289 |
| - name='exit', plot=plot, overlay=False) |
| 291 | + self.__entry_signal = self.I( |
| 292 | + lambda: pd.Series(entry_size, dtype=float).replace(0, np.nan), |
| 293 | + name='entry size', plot=plot, overlay=False, scatter=True, color='black') |
| 294 | + |
| 295 | + if exit_portion is not None: |
| 296 | + self.__exit_signal = self.I( |
| 297 | + lambda: pd.Series(exit_portion, dtype=float).replace(0, np.nan), |
| 298 | + name='exit portion', plot=plot, overlay=False, scatter=True, color='black') |
290 | 299 |
|
291 | 300 | def next(self):
|
292 | 301 | super().next()
|
293 | 302 |
|
294 |
| - if self.position and self.__exit_signal[-1]: |
295 |
| - self.position.close() |
296 |
| - |
297 |
| - signal = self.__entry_signal[-1] |
298 |
| - |
299 |
| - if signal > 0: |
300 |
| - self.buy() |
301 |
| - elif signal < 0: |
302 |
| - self.sell() |
| 303 | + exit_portion = self.__exit_signal[-1] |
| 304 | + if exit_portion > 0: |
| 305 | + for trade in self.trades: |
| 306 | + if trade.is_long: |
| 307 | + trade.close(exit_portion) |
| 308 | + elif exit_portion < 0: |
| 309 | + for trade in self.trades: |
| 310 | + if trade.is_short: |
| 311 | + trade.close(-exit_portion) |
| 312 | + |
| 313 | + entry_size = self.__entry_signal[-1] |
| 314 | + if entry_size > 0: |
| 315 | + self.buy(size=entry_size) |
| 316 | + elif entry_size < 0: |
| 317 | + self.sell(size=-entry_size) |
303 | 318 |
|
304 | 319 |
|
305 | 320 | class TrailingStrategy(Strategy):
|
|
0 commit comments