Skip to content

Commit baeeb5e

Browse files
committed
Trailing pct instead of ATR kernc#223
1 parent 0a76e96 commit baeeb5e

File tree

2 files changed

+53
-0
lines changed

2 files changed

+53
-0
lines changed

backtesting/lib.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,43 @@ def next(self):
452452
self.data.Close[index] + self.__atr[index] * self.__n_atr)
453453

454454

455+
class PercentageTrailingStrategy(Strategy):
456+
"""
457+
A strategy with automatic trailing stop-loss, trailing the current
458+
price at distance of some percentage. Call
459+
`PercentageTrailingStrategy.set_trailing_sl()` to set said percentage
460+
(`5` by default). See [tutorials] for usage examples.
461+
462+
[tutorials]: index.html#tutorials
463+
464+
Remember to call `super().init()` and `super().next()` in your
465+
overridden methods.
466+
"""
467+
_sl_pct = 5.
468+
469+
def init(self):
470+
super().init()
471+
472+
def set_trailing_sl(self, percentage: float = 5):
473+
assert percentage > 0, "percentage must be greater than 0"
474+
"""
475+
Sets the future trailing stop-loss as some (`percentage`)
476+
percentage away from the current price.
477+
"""
478+
self._sl_pct = percentage/100
479+
480+
def next(self):
481+
super().next()
482+
index = len(self.data)-1
483+
for trade in self.trades:
484+
if trade.is_long:
485+
trade.sl = max(trade.sl or -np.inf,
486+
self.data.Close[index]*(1-self._sl_pct))
487+
else:
488+
trade.sl = min(trade.sl or np.inf,
489+
self.data.Close[index]*(1+self._sl_pct))
490+
491+
455492
# Prevent pdoc3 documenting __init__ signature of Strategy subclasses
456493
for cls in list(globals().values()):
457494
if isinstance(cls, type) and issubclass(cls, Strategy):

backtesting/test/_test.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
quantile,
2525
SignalStrategy,
2626
TrailingStrategy,
27+
PercentageTrailingStrategy,
2728
resample_apply,
2829
plot_heatmaps,
2930
random_ohlc_data,
@@ -862,6 +863,21 @@ def next(self):
862863
stats = Backtest(GOOG, S).run()
863864
self.assertEqual(stats['# Trades'], 57)
864865

866+
def test_PercentageTrailingStrategy(self):
867+
class S(PercentageTrailingStrategy):
868+
def init(self):
869+
super().init()
870+
self.set_trailing_sl(5)
871+
self.sma = self.I(lambda: self.data.Close.s.rolling(10).mean())
872+
873+
def next(self):
874+
super().next()
875+
if not self.position and self.data.Close > self.sma:
876+
self.buy()
877+
878+
stats = Backtest(GOOG, S).run()
879+
self.assertEqual(stats['# Trades'], 91)
880+
865881

866882
class TestUtil(TestCase):
867883
def test_as_str(self):

0 commit comments

Comments
 (0)