Skip to content

Commit 7e1ed29

Browse files
committed
REF: Backwards-incompatibly revise lib.SignalStrategy
1 parent 953c9e8 commit 7e1ed29

File tree

1 file changed

+37
-22
lines changed

1 file changed

+37
-22
lines changed

backtesting/lib.py

+37-22
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@
1515
from itertools import compress
1616
from numbers import Number
1717
from inspect import currentframe
18-
from typing import Sequence, Optional, Union, Callable
18+
from typing import Sequence, Union, Callable
1919

2020
import numpy as np
2121
import pandas as pd
2222

2323
from .backtesting import Strategy
2424
from ._plotting import plot_heatmaps as _plot_heatmaps
25-
from ._util import _Array, _Indicator, _as_str
25+
from ._util import _Array, _as_str
2626

2727
__pdoc__ = {}
2828

@@ -270,36 +270,51 @@ def init(self):
270270

271271
__pdoc__['SignalStrategy.__init__'] = False
272272

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+
*,
274276
plot: bool = True):
275277
"""
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).
281287
282288
If `plot` is `True`, the signal entry/exit indicators are plotted when
283289
`backtesting.backtesting.Backtest.plot` is called.
284290
"""
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')
290299

291300
def next(self):
292301
super().next()
293302

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)
303318

304319

305320
class TrailingStrategy(Strategy):

0 commit comments

Comments
 (0)