Multiple stop loss issued
-
I have this code for a RSI strategy that I've tried to implement Stop Loss.
import backtrader as bt from datetime import datetime class firstStrategy(bt.Strategy): params = ( ('stopLoss', 0.25), ('upperband', 75), ('lowerband', 25), ('period', 9) ) def __init__(self): self.rsi = bt.indicators.RelativeStrengthIndex(self.data.close, period=9, safediv=True) self.order = None self.stopOrder = None def notify_order(self, order): if order.status in [order.Submitted, order.Accepted]: return if order.status in [order.Completed]: if order.isbuy(): if self.params.stopLoss: self.stopOrder = self.close(price=order.executed.price, exectype=bt.Order.StopTrail, trailpercent=self.params.stopLoss) self.order = None def next(self): if not self.position: if self.rsi <= 25: self.buy() elif self.rsi >= 75: self.close() # def next(self): # if not self.position: # if self.rsi <= 25: # self.buy() # elif self.rsi >= 75: # self.close() startcash = 100 cerebro = bt.Cerebro() cerebro.addstrategy(firstStrategy) data = bt.feeds.GenericCSVData( dataname='NEOUSDT-15m-data.csv', fromdate=datetime(2020, 2, 1, 0, 0), todate=datetime(2020, 6, 1, 0, 0), timeframe=bt.TimeFrame.Minutes, compression=15, dtformat='%Y-%m-%d %H:%M:%S' ) cerebro.adddata(data) cerebro.broker.setcash(startcash) cerebro.addsizer(bt.sizers.PercentSizer, percents=90) # Set the order size cerebro.addanalyzer(bt.analyzers.DrawDown, _name='Drawdown') cerebro.broker.setcommission(commission=0.0001, margin=False) cerebro.run() portvalue = round(cerebro.broker.getvalue(), 2) pnl = round(portvalue - startcash, 2) print('Final Portfolio Value: ${}'.format(portvalue)) print('P/L: ${}'.format(pnl)) cerebro.plot(style='candlestick')
The results were quite ridiculous, and the graph showed that multiple Stop Loss order were issued, even tho the order was supossed to be closed, as you can see:
So what can I do to stop it? -
I've changed the code, and now I'm trying to use a bracket order. However, the trailpercent arg doesn't do a thing. It can be 0.1, it can be 1, or even 100, and the results are same. This is the new code:
import backtrader as bt from datetime import datetime class firstStrategy(bt.Strategy): params = ( ('stopLoss', 0.25), ('upperband', 75), ('lowerband', 25), ('period', 9) ) def __init__(self): self.rsi = bt.indicators.RelativeStrengthIndex(self.data.close, period=self.p.period, upperband=self.p.upperband, lowerband=self.p.lowerband, safediv=True) def next(self): if not self.position and self.rsi <= self.p.lowerband: # self.buy() self.buy_bracket(exectype=bt.Order.Market, price=self.data.close, limitexec=None, stopexec=bt.Order.StopTrail, trailpercent=0.1, stopprice=self.data.close) elif self.rsi >= self.p.upperband: self.close() startcash = 100 cerebro = bt.Cerebro() cerebro.addstrategy(firstStrategy) data = bt.feeds.GenericCSVData( dataname='NEOUSDT-15m-data.csv', fromdate=datetime(2020, 2, 1, 0, 0), todate=datetime(2020, 6, 1, 0, 0), timeframe=bt.TimeFrame.Minutes, compression=15, dtformat='%Y-%m-%d %H:%M:%S' ) cerebro.adddata(data) cerebro.broker.setcash(startcash) cerebro.addsizer(bt.sizers.PercentSizer, percents=90) # Set the order size cerebro.addanalyzer(bt.analyzers.DrawDown, _name='Drawdown') cerebro.broker.setcommission(commission=0.0001, margin=False) cerebro.run() portvalue = round(cerebro.broker.getvalue(), 2) pnl = round(portvalue - startcash, 2) print('Final Portfolio Value: ${}'.format(portvalue)) print('P/L: ${}'.format(pnl)) cerebro.plot(style='candlestick')
If this is intended behaviour, can I use another kind of order?
-
BUMP....
-
Looks like your first script with
StopTrail
orders worked as expected. You used 25% stop from the price of order execution. Your first orders were executed at prices around 14.5 - 15.5 and you stops were set to approx 11.5 - 12.0. You can see them executed at that spike of the cash, 6 red triangles in a row. -
@Eduardo-Menges-Mattje said in Multiple stop loss issued:
However, the trailpercent arg doesn't do a thing. It can be 0.1, it can be 1, or even 100
Just to clarify (Docs - Orders - StopTrail)
Percentage based distance: trailpercent=0.02 (i.e.: 2%)
You start with 0.1 which is 10% and your price range is much less than this value. You need to use much less % of stop in order to hit it before your
self.close()
closes position in regular way.You may want to add more logging into your script, this will help you to understand what is going on.
-
@ab_trader said in Multiple stop loss issued:
Just to clarify (Docs - Orders - StopTrail)
Percentage based distance: trailpercent=0.02 (i.e.: 2%)
You start with 0.1 which is 10% and your price range is much less than this value. You need to use much less % of stop in order to hit it before your
self.close()
closes position in regular way.
Even with trailpercent=0.01, the result is the same as 0.1 or any other value. As I said, it is as if trailpercent is completely disregarded.You may want to add more logging into your script, this will help you to understand what is going on.
Ok. This is the code with the logging:import backtrader as bt from datetime import datetime class firstStrategy(bt.Strategy): params = dict( stopLoss=0.01, upperband=75, lowerband=25, period=9 ) def log(self, txt, dt=None): dt = dt or self.data.datetime[0] if isinstance(dt, float): dt = bt.num2date(dt) print('%s, %s' % (dt.isoformat(), txt)) def notify_order(self, order): if order.status in [order.Completed]: if order.isbuy(): self.log( 'BUY EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' % (order.executed.price, order.executed.value, order.executed.comm)) else: self.log('SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' % (order.executed.price, order.executed.value, order.executed.comm)) self.order = None def __init__(self): self.rsi = bt.indicators.RelativeStrengthIndex(period=self.p.period, upperband=self.p.upperband, lowerband=self.p.lowerband, safediv=True) def next(self): if not self.position and self.rsi <= self.p.lowerband: # self.buy() self.buy_bracket(exectype=bt.Order.Market, price=self.data.close, limitexec=None, stopexec=bt.Order.StopTrail, trailpercent=self.p.stopLoss, stopprice=self.data.close) self.log('BUY CREATE, exectype Market, price %.2f' % self.data.close[0]) elif self.position and self.rsi >= self.p.upperband: self.close() self.log('BUY CREATE, exectype Close, price %.2f' % self.data.close[0]) startcash = 100 cerebro = bt.Cerebro() cerebro.addstrategy(firstStrategy) data = bt.feeds.GenericCSVData( dataname='NEOUSDT-15m-data.csv', fromdate=datetime(2020, 2, 1, 0, 0), todate=datetime(2020, 6, 1, 0, 0), timeframe=bt.TimeFrame.Minutes, compression=15, dtformat='%Y-%m-%d %H:%M:%S' ) cerebro.adddata(data) cerebro.broker.setcash(startcash) cerebro.addsizer(bt.sizers.PercentSizer, percents=90) # Set the order size cerebro.addanalyzer(bt.analyzers.DrawDown, _name='Drawdown') cerebro.broker.setcommission(commission=0.0001, margin=False) cerebro.run() portvalue = round(cerebro.broker.getvalue(), 2) pnl = round(portvalue - startcash, 2) print('Final Portfolio Value: ${}'.format(portvalue)) print('P/L: ${}'.format(pnl)) cerebro.plot(style='candlestick')
This is the log with trailpercent=0.01:
https://controlc.com/90e3b126
This is the log with trailpercent=0.10:
https://controlc.com/80136684 -
I'll take a look. Also please log ohlc prices and rsi values.
-
@ab_trader said in Multiple stop loss issued:
I'll take a look. Also please log ohlc prices and rsi values.
This is the csv file: https://drive.google.com/file/d/1zx6Ywdts38sHVJqr9ZYfYV5Izxh_MBVk/view?usp=sharing
-
BUMP....
-
So it turns out that this is intended behaviour.
StopTrail compares the latest price, not the executed price. -
Good you figured this out. Maybe this is true in your case since you use
Market
order as a first order.For this type of order the execution price is unknown at the moment when order is issued. You passed close prices and maybestoptrail
started to work from that value.I didn't have a chance to go thru all the info you dropped here. Especially having no RSI values and having outputs started from the middle of the whole price array. But I am still thinking that my guess above is true. Even 1% stop trail is large for your set of prices.
-
@ab_trader said in Multiple stop loss issued:
Good you figured this out. Maybe this is true in your case since you use
Market
order as a first order.For this type of order the execution price is unknown at the moment when order is issued. You passed close prices and maybestoptrail
started to work from that value.I didn't have a chance to go thru all the info you dropped here. Especially having no RSI values and having outputs started from the middle of the whole price array. But I am still thinking that my guess above is true. Even 1% stop trail is large for your set of prices.
Is there a way for StopLoss to trigger only when
executed price - current price <= percentage
? -
bt
's stop order triggers when the current price is lower than the stop price. so stop price need to be defined asexecuted price * ( 1 - stop percent)
.i've played with your data and script, and it seems to me that the issue is in the
stoptrail
side of the bracket order. I've changed it to the regular stop order, it worked as expected but with the StopTrail the results were weird.In your case if you want to do the following:
- issue market
buy
order based onrsi
value - than issue
stoptrail
order with the position open - and issue closing order based on
rsi
condition with the cancellation of the existingstoptrail
order
than you may want to take a look on the OCO orders.
- issue market