TrailStop is not working for me
-
Hello,
I'm trying to play around with TrailStop and when the trade is closed it does 6 trades instead of one.
Why is this happening?
Can you please help me out?```import backtrader.plot import matplotlib %matplotlib inline import matplotlib.pyplot as plt import quantstats from __future__ import (absolute_import, division, print_function, unicode_literals) import argparse import datetime import backtrader as bt import backtrader.feeds as btfeeds import pandas import datetime class MAcrossover(bt.Strategy): # Moving average parameters params = (('pfast',20),('pslow',50),) def log(self, txt, dt=None,dt2=None): dt = dt or self.datas[0].datetime.date(0) dt2 = dt2 or self.datas[0].datetime.time(0) print('%s %s, %s' % (dt.isoformat(),dt2.isoformat(), txt)) def __init__(self): self.inds = dict() self.SL = dict() for i, d in enumerate(self.datas): self.dataclose = d.close # To keep track of pending orders and buy price/commission self.order = None self.buyprice = None self.buycomm = None self.inds[d] = dict() dn = d._name self.SL[dn] = dict() #Initiate DonchianChannels self.inds[d]['donchianSlow'] = DonchianChannels(d,period=720) self.inds[d]['donchianFast'] = DonchianChannels(d,period=360) def notify_order(self, order): if order.status in [order.Submitted, order.Accepted]: # An active Buy/Sell order has been submitted/accepted - Nothing to do return # Check if an order has been completed # Attention: broker could reject order if not enough cash 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)) self.buyprice = order.executed.price self.buycomm = order.executed.comm elif order.issell(): self.log('SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' % (order.executed.price, order.executed.value, order.executed.comm)) self.bar_executed = len(self) elif order.status in [order.Canceled, order.Margin, order.Rejected]: if order.status == order.Canceled: self.log('Order Canceled') elif order.status == order.Margin: self.log('Order Margin') elif order.status == order.Rejected: self.log('Order Rejected') # Reset orders self.order = None def notify_trade(self, trade): if not trade.isclosed: return self.log('OPERATION PROFIT, GROSS %.2f, NET %.2f' % (trade.pnl, trade.pnlcomm)) def next(self): for i, d in enumerate(self.datas): dt, dn = self.datetime.date(), d._name pos = self.getposition(d).size posprice = self.getposition(d).price if pos==0: if d.close[0] < self.inds[d]['donchianSlow'].dcl[0]: self.log(f'SELL CREATE {self.dataclose[0]:2f}') self.order = self.order_target_percent(data=d,target=-0.15) elif self.order is None: self.buy(data = d,size = pos, exectype=bt.Order.StopTrail, trailpercent=0.05) if __name__ == '__main__': # Create a cerebro entity cerebro = bt.Cerebro() # Benchmark is Buy and Hold cerebro.addanalyzer(bt.analyzers.TimeReturn) # Add a strategy cerebro.addstrategy(MAcrossover) for altcoin in altcoins: data = bt.feeds.PandasData(dataname=data_new[data_new["Altcoin"]==altcoin][['Open','High','Low','Close','Volume','OpenInterest']][20000:]) cerebro.adddata(data, name = altcoin) # Set our desired cash start cerebro.broker.setcash(100000.0) # Set the commission - 0.1% ... divide by 100 to remove the % cerebro.broker.setcommission(commission=0.0015) cerebro.addsizer(bt.sizers.PercentSizer, percents=99) # Print out the starting conditions print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue()) cerebro.addanalyzer(bt.analyzers.PyFolio, _name='PyFolio') # Run over everything results = cerebro.run() # Print out the final result print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue()) # cerebro.plot()
And this is what it happens:
Starting Portfolio Value: 100000.00
2020-03-08 15:00:00, SELL CREATE 8400.000000
2020-03-08 16:00:00, SELL EXECUTED, Price: 8400.00, Cost: -8400.00, Comm 12.60
2020-03-08 23:00:00, SELL CREATE 8032.000000
2020-03-09 00:00:00, SELL EXECUTED, Price: 199.38, Cost: -14953.12, Comm 22.43
2020-03-09 23:00:00, BUY EXECUTED, Price: 203.31, Cost: -14953.12, Comm 22.87
2020-03-09 23:00:00, BUY EXECUTED, Price: 203.31, Cost: 15247.97, Comm 22.87
2020-03-09 23:00:00, BUY EXECUTED, Price: 203.31, Cost: 15247.97, Comm 22.87
2020-03-09 23:00:00, BUY EXECUTED, Price: 203.31, Cost: 15247.97, Comm 22.87
2020-03-09 23:00:00, BUY EXECUTED, Price: 203.31, Cost: 15247.97, Comm 22.87
2020-03-09 23:00:00, BUY EXECUTED, Price: 203.31, Cost: 15247.97, Comm 22.87
2020-03-09 23:00:00, BUY EXECUTED, Price: 203.31, Cost: 15247.97, Comm 22.87
2020-03-09 23:00:00, BUY EXECUTED, Price: 203.31, Cost: 15247.97, Comm 22.87
2020-03-09 23:00:00, Order Margin
2020-03-09 23:00:00, Order Margin
2020-03-09 23:00:00, Order Margin
2020-03-09 23:00:00, Order Margin -
@dragosgr It actually tries to do more than 6 trades, 6 of them were executed, but there was not enough cash for other. You can see
Order Margin
notifications.I believe the issue is in the conditional statement under which you issue the trailing order.
self.order is None
is true for all bars and all data feeds, except the bar you issue theSELL
order and for that data feed. Therefore you issue trailing order for each data feed on each bar. Andbt
executes them. Add data name to the order logs, you will see it. -
@ab_trader Thanks for the quick reply.
My understanding is that the trailing order should only happen when pos is different than 0. Which it happens. I have the transactions below and it shows that it only trades ETHUSDT (on which the positions should have been closed).
It looks that it goes several time through the same asset and the position is not updated after the trail order happens. It is a bit annoying because if I don't use trailing stops everything works as expected.
Is this a correct understanding?
What shall I do in this case if I want to have a trailing stop in place? -
@dragosgr said in TrailStop is not working for me:
if pos == 0: if d.close[0] < self.inds[d]['donchianSlow'].dcl[0]: self.log(f'SELL CREATE {self.dataclose[0]:2f}') self.order = self.order_target_percent(data=d, target=-0.15) elif self.order is None: self.buy(data=d, size=pos, exectype=bt.Order.StopTrail, trailpercent=0.05)
Let's work through this logic for a moment. Let's 'FB'
- First bar, no shares, and say no doch. Nothing happens.
- Second bar, no shares and say, yes doch, let's trade! self.order is sell order for -0.15
- self.order completed and we are now short 10 FB shares. Reset self.order to None
- Next bar, pos is not zero so self.order is None so buy order with trailpercent.
- Next bar, buy order not filled yet. pos is not zero so, self.order is None so buy order with trailpercent.
- Next bar, buy order(s) not filled yet. pos is not zero so, self.order is None so buy order with trailpercent.
- Next bar, buy order(s) not filled yet. pos is not zero so, self.order is None so buy order with trailpercent.
- Next bar, buy order(s) not filled yet. pos is not zero so, self.order is None so buy order with trailpercent.
- Next bar, buy order(s) not filled yet. pos is not zero so, self.order is None so buy order with trailpercent.
...
When dealing with multiple securities, it is usually best to handle the order with a dictionary with the
data
as key. -
@dragosgr said in TrailStop is not working for me:
My understanding is that the trailing order should only happen when pos is different than 0. Which it happens. I have the transactions below and it shows that it only trades ETHUSDT (on which the positions should have been closed).
It looks that it goes several time through the same asset and the position is not updated after the trail order happens. It is a bit annoying because if I don't use trailing stops everything works as expected.
Is this a correct understanding?My bad, I assumed wrongly. Would be useful if you add logging statement under that
elif self.order is None
condition. Same way you did for theSELL
order. Looks like number of trailing orders were issued and than executed when prices met the stop condition. -
Thank you both!
I made it work!