For code/output blocks: Use ``` (aka backtick or grave accent) in a single line before and after the block. See: http://commonmark.org/help/

How to Exit Current Position and Enter Opposite Position at Same Time



  • Hi Backtrader Community,

    I am trying to modify "Quickstart" strategy as a common Renko strategy.

    Strategy:

    • Buy: If two consecutive candles are bullish
    • Sell: If two consecutive candles are bearish
    • Exit: If two consecutive candles move to the opposite direction

    As a start, I changed the logic as follows:

    class TestStrategy(bt.Strategy):
    
        def log(self, txt, dt=None):
            ''' Logging function fot this strategy'''
            dt = dt or self.datas[0].datetime.date(0)
            print('%s, %s' % (dt.isoformat(), txt))
    
        def __init__(self):
            # Keep a reference to the "close" line in the data[0] dataseries
            self.dataclose = self.datas[0].close
            # Keep a reference to the "open" line in the data[0] dataseries
            self.dataopen = self.datas[0].open
    
            # To keep track of pending orders
            self.order = None
    
        def notify_order(self, order):
            if order.status in [order.Submitted, order.Accepted]:
                # Buy/Sell order submitted/accepted to/by broker - 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, %.2f' % order.executed.price)
                elif order.issell():
                    self.log('SELL EXECUTED, %.2f' % order.executed.price)
    
                self.bar_executed = len(self)
    
            elif order.status in [order.Canceled, order.Margin, order.Rejected]:
                self.log('Order Canceled/Margin/Rejected')
    
            # Write down: no pending order
            self.order = None
    
        def next(self):
            # Simply log the closing price of the series from the reference
            self.log('Open, %.2f' % self.dataopen[0])
            self.log('Close, %.2f' % self.dataclose[0])
    
            # Check if an order is pending ... if yes, we cannot send a 2nd one
            if self.order:
                return
    
            # Check if we are in the market
            if not self.position:
    
                if self.dataclose[0] > self.dataopen[0]:
                    # current bar is bullish
    
                    if self.dataclose[-1] > self.dataopen[-1]:
                        # previous bar is bullish
    
                        # BUY with all possible default parameters
                        self.log('BUY CREATE, %.2f' % self.dataclose[0])
                        # Keep track of the created order to avoid a 2nd order
                        self.order = self.buy()
    
                if self.dataclose[0] < self.dataopen[0]:
                    # current bar is bearish
    
                    if self.dataclose[-1] < self.dataopen[-1]:
                        # previous bar is bearish
    
    
                    # SELL with all possible default parameters
                        self.log('SELL CREATE, %.2f' % self.dataclose[0])
    
                        # Keep track of the created order to avoid a 2nd order
                        self.order = self.sell()
    
            else:
    
                # Already in the market ... we might exit
                if self.dataclose[0] > self.dataopen[0]:
                    # current bar is bullish
    
                    if self.dataclose[-1] > self.dataopen[-1]:
                        # previous bar is bullish
    
                        # Exit
                        self.log('Exit: BUY CREATE, %.2f' % self.dataclose[0])
                        # Keep track of the created order to avoid a 2nd order
                        self.order = self.buy()
    
                # Already in the market ... we might exit
                if self.dataclose[0] < self.dataopen[0]:
                    # current bar is bearish
    
                    if self.dataclose[-1] < self.dataopen[-1]:
                        # previous bar is bearish
    
                    # Exit
                        self.log('Exit: SELL CREATE, %.2f' % self.dataclose[0])
    
                        # Keep track of the created order to avoid a 2nd order
                        self.order = self.sell()
    

    This, of course, is unsuccessful because it fails to avoid a 2nd order and it fails to exit the current position and entering the opposite position at same time.

    How could I code this strategy? Thanks for your insights!



  • I think you can use order_target_xxx for backtests. Lets operate with 100 stocks. If you have buy signal, you do

    self.order_target_size(size=100)
    

    bt buys 100 stocks. If then you have sell signal, you do

    self.order_target_size(size=-100)
    

    And bt will reverse position selling 200 stocks.

    Docs - Target Orders


  • administrators

    The approach by @ab_trader is probably the cleanest.

    See also how you can reverse position with a sizer and buy, sell commands (where the size is not specified and left to the Sizer to calculate)



  • Somehow I am having hard time understanding how order_target_xxx works and I ended up using buy and sell commands with size.

    # Check if we are in the market
        if not self.position: #Enter Signal
            # Two Bullish Candles
            if self.dataclose[0] > self.dataopen[0] and self.dataclose[-1] > self.dataopen[-1]:
                # BUY 
                self.order = self.buy(size=100)
            
            # Two Bearish Candles
            elif self.dataclose[0] < self.dataopen[0] and self.dataclose[-1] < self.dataopen[-1]:
                # SELL 
                self.order = self.sell(size=100)
    
            else:
                pass
    
        else: #Exit Signal
                # Already in the market & Two Bullish Candles
            if self.position.size < 0 and self.dataclose[0] > self.dataopen[0] and self.dataclose[-1] > self.dataopen[-1]:
                self.order = self.buy(size=100)
    
                # Already in the market & Two Bearish Candles
            if self.position.size > 0 and self.dataclose[0] < self.dataopen[0] and self.dataclose[-1] < self.dataopen[-1]:
                self.order = self.sell(size=100)
    

    This does avoid 2nd order but I would like to know how order_target_xxx is different (or better) and how it will change the above code.

    Also, this made me question how size works. Feel stupid to ask but I do not get why buy size=100 and then sell size=100 end up selling 200. If I continue to reverse the position, size will continue to increase as well? I guess I'm not understanding the concept intuitively. I am grateful if you could help me understand.


  • administrators

    Instead of doing a buy / sell and later sell / buy, you replace each pair of calls with a single order_target_size.


Log in to reply
 

Looks like your connection to Backtrader Community was lost, please wait while we try to reconnect.