Navigation

    Backtrader Community

    • Register
    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    For code/output blocks: Use ``` (aka backtick or grave accent) in a single line before and after the block. See: http://commonmark.org/help/

    TrailStop is not working for me

    General Code/Help
    3
    6
    41
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • D
      dragosgr last edited by

      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

      A 1 Reply Last reply Reply Quote 0
      • A
        ab_trader @dragosgr last edited by

        @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 the SELL order and for that data feed. Therefore you issue trailing order for each data feed on each bar. And bt executes them. Add data name to the order logs, you will see it.

        D 1 Reply Last reply Reply Quote 0
        • D
          dragosgr @ab_trader last edited by

          @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?

          8ff3184b-aa7c-4e81-a319-2306980265df-image.png

          A 1 Reply Last reply Reply Quote 0
          • run-out
            run-out last edited by

            @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'

            1. First bar, no shares, and say no doch. Nothing happens.
            2. Second bar, no shares and say, yes doch, let's trade! self.order is sell order for -0.15
            3. self.order completed and we are now short 10 FB shares. Reset self.order to None
            4. Next bar, pos is not zero so self.order is None so buy order with trailpercent.
            5. Next bar, buy order not filled yet. pos is not zero so, self.order is None so buy order with trailpercent.
            6. Next bar, buy order(s) not filled yet. pos is not zero so, self.order is None so buy order with trailpercent.
            7. Next bar, buy order(s) not filled yet. pos is not zero so, self.order is None so buy order with trailpercent.
            8. Next bar, buy order(s) not filled yet. pos is not zero so, self.order is None so buy order with trailpercent.
            9. 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.

            1 Reply Last reply Reply Quote 3
            • A
              ab_trader @dragosgr last edited by

              @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 the SELL order. Looks like number of trailing orders were issued and than executed when prices met the stop condition.

              1 Reply Last reply Reply Quote 3
              • D
                dragosgr last edited by

                Thank you both!
                I made it work!

                1 Reply Last reply Reply Quote 1
                • 1 / 1
                • First post
                  Last post
                Copyright © 2016, 2017, 2018 NodeBB Forums | Contributors
                $(document).ready(function () { app.coldLoad(); }); }