Buying and Selling with Signals



  • Im trying to code the strategy that works in this way:

    1. If today is a down candle (open price higher than the closing price), I will go long position. If today is an up candle (closing price is higher than the open price), I will go short position.
    2. If yesterday I was in a long position, today is an up candle, I will close my long position and start a short position.
    3. I am using a stop loss and take profit price for each of my position. As such, I am using the bracket order. However, as the exectype is Limit Order and I would like it to be implemented as a Market Order, I change the price into self.data.open[0] with reference to yesterday's signal.
    from __future__ import (absolute_import, division, print_function,
                            unicode_literals)
    import os.path
    import argparse
    import datetime
    import collections
    
    import backtrader as bt
    import backtrader.feeds as btfeeds
    import backtrader.plot as plt
    import backtrader.indicators as btinds
    
    class Plotter(plt.Plot):
    
        def __init__(self):
            super().__init__(volup='#60cc73')  # custom color for volume up bars 
    
        def show(self):
            mng = self.mpyplot.get_current_fig_manager()
            mng.window.state('zoomed')
            self.mpyplot.show()  
    
    class St(bt.Strategy):
        
        def log(self, txt, dt=None):
            ''' Logging function fot this strategy'''
            dt = dt or self.data.datetime.date(0)
            print('%s, %s' % (dt.isoformat(), txt))
            
        def __init__(self):
            self.signal = 0
            
        def notify_order(self, order):
            if order.status in [order.Submitted, order.Accepted]:
                return
            
            if order.status in [order.Completed]:
                if order.isbuy():
                    self.signal +=1
                    self.log(
                        'BUY EXECUTED, Price: %.5f, Cost: %.5f, Position : %d' %
                        (order.executed.price,
                         order.executed.value,self.signal))
                    self.buyprice = order.executed.price
                    
                else:  # Sell
                    self.signal -= 1    
                    self.log('SELL EXECUTED, Price: %.5f, Cost: %.5f,Position : %d' %
                                 (order.executed.price,
                                  order.executed.value,self.signal))
                
                    self.bar_executed = len(self)
                
            elif order.status in [order.Canceled, order.Margin, order.Rejected]:
                self.log('Order Canceled/Margin/Rejected')
            
            self.order = None
            
        def notify_trade(self, trade):
            if not trade.isclosed:
                return
    
            self.log('OPERATION PROFIT, GROSS %.5f, NET %.5f' %
                     (trade.pnl, trade.pnlcomm))
            
        def next(self):
            txt = ','.join(
                ['%04d' % len(self.data),
                 self.data.datetime.date(0).isoformat(),
                 'Open : %.5f' % self.data.open[0],
                 'High : %.5f' % self.data.high[0],
                 'Low : %.5f' % self.data.low[0],
                 'Close : %.5f' % self.data.close[0],
                 'Position : %d' % self.signal])
            print(txt)
            
            if self.signal == 0:
                if self.data.open[-1] > self.data.close[-1]:
                    self.log('BUY CREATE, %.5f, Limitprice : %.5f, Stopprice : %.5f' % (self.data.open[0],1.01*self.data.open[0],0.99*self.data.open[0]))
                    self.order = self.buy_bracket(limitprice = 1.01*self.data.open[0],price=self.data.open[0],stopprice = 0.99*self.data.open[0])
                elif self.data.open[-1] < self.data.close[-1]:
                    self.log('SELL CREATE, %.5f, Limitprice : %.5f, Stopprice : %.5f' % (self.data.open[0],0.99*self.data.open[0],1.01*self.data.open[0]))
                    self.order = self.sell_bracket(limitprice =0.99*self.data.open[0],
                                                  price = self.data.open[0],
                                                  stopprice= 1.01*self.data.open[0])
            elif self.signal == 1:
                if self.data.open[-1] > self.data.close[-1]:
                    return
                elif self.data.open[-1] < self.data.close[-1]:
                    self.log('SELL CREATE, %.5f, Limitprice : %.5f, Stopprice : %.5f' % (self.data.open[0],0.99*self.data.open[0],1.01*self.data.open[0]))
                    self.order = self.sell_bracket(limitprice =0.99*self.data.open[0],
                                                  price = self.data.open[0],
                                                  stopprice= 1.01*self.data.open[0],size=2)
            elif self.signal == -1:
                if self.data.open[-1] < self.data.close[-1]:
                    return
                elif self.data.open[-1] > self.data.close[-1]:
                    self.log('BUY CREATE, %.5f, Limitprice : %.5f, Stopprice : %.5f' % (self.data.open[0],1.01*self.data.open[0],0.99*self.data.open[0]))
                    self.order = self.buy_bracket(limitprice = 1.01*self.data.open[0],price=self.data.open[0],stopprice = 0.99*self.data.open[0])
    
    class Plotter(plt.Plot):
    
        def __init__(self):
            super().__init__(volup='#60cc73')  # custom color for volume up bars 
    
        def show(self):
            mng = self.mpyplot.get_current_fig_manager()
            mng.window.state('zoomed')
            self.mpyplot.show()  
                      
    def runstrat():
        args = parse_args()
    
        cerebro = bt.Cerebro()
        modpath = 'd:\\I - TradersGPS\\'
        datapath = os.path.join(modpath, 'GBPUSD_D1_UTC+2_00.csv') 
        data = btfeeds.GenericCSVData(dataname=datapath,
                                       timeframe=bt.TimeFrame.Days,
                                      fromdate = datetime.datetime(2017,4,10),
                                      todate=datetime.datetime(2017,4,20),                                  
                                      nullvalue=0.0,
                                      dtformat=('%Y.%m.%d'),
                                      tmformat=('%H:%M'),
                                      datetime=0,
                                      time=1,
                                      open=2,
                                      high=3,
                                      low=4,
                                      close=5,
                                      volume=6,
                                      openinterest=-1)
    
        cerebro.adddata(data)
    
        cerebro.addstrategy(St)
        cerebro.broker.setcash(100000.0)
        print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())
        cerebro.run(stdstats=False)
        print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
        #plotter = Plotter()
        #cerebro.plot(plotter=plotter,subplot=False)
        
    
            
    def parse_args():
        parser = argparse.ArgumentParser(
            formatter_class=argparse.ArgumentDefaultsHelpFormatter,
            description='Sample for pivot point and cross plotting')
    
        parser.add_argument('--data0',
                            default='d:\\I - TradersGPS\\GBPUSD_D1_UTC+2_00.csv',
                            required=False, help='Data0 to read in')
    
        parser.add_argument('--data1', 
                            default='d:\\I - TradersGPS\\GBPUSD_H1_UTC+2_00.csv',
                            required=False, help='Data1 to read in')
    
        parser.add_argument('--fromdate', required=False, default='2001-01-01',
                            help='Date[time] in YYYY-MM-DD[THH:MM:SS] format')
        
        parser.add_argument('--todate', required=False, default='2007-01-01',
                            help='Date[time] in YYYY-MM-DD[THH:MM:SS] format')
    
        parser.add_argument('--plot', required=False, action='store_true',
                            help=('Plot the result'))
    
        return parser.parse_args()
    
    
    if __name__ == '__main__':
        runstrat()
        
    

    This is a snippet of my data. It is a daily data with date. time, open, high, low,close,volume.

    2017.04.10,00:00,1.23802,1.24286,1.23657,1.24117,196634
    2017.04.11,00:00,1.24116,1.24944,1.24035,1.24893,235307
    2017.04.12,00:00,1.24891,1.25483,1.24801,1.25387,261157
    2017.04.13,00:00,1.2537,1.25742,1.25,1.25008,248594
    2017.04.14,00:00,1.25007,1.25346,1.2497,1.25172,161537
    2017.04.17,00:00,1.25271,1.25959,1.2524,1.25631,164495
    2017.04.18,00:00,1.25638,1.29053,1.25151,1.284,336253
    2017.04.19,00:00,1.28398,1.28603,1.27701,1.27772,278525
    2017.04.20,00:00,1.27773,1.28444,1.27724,1.28298,114281

    This is the output from my code:

    Starting Portfolio Value: 100000.00
    0001,2017-04-10,Open : 1.23802,High : 1.24286,Low : 1.23657,Close : 1.24117,Position : 0
    2017-04-10, BUY CREATE, 1.23802, Limitprice : 1.25040, Stopprice : 1.22564
    0002,2017-04-11,Open : 1.24116,High : 1.24944,Low : 1.24035,Close : 1.24893,Position : 0
    2017-04-11, SELL CREATE, 1.24116, Limitprice : 1.22875, Stopprice : 1.25357
    2017-04-12, SELL EXECUTED, Price: 1.24891, Cost: -1.24891,Position : -1
    0003,2017-04-12,Open : 1.24891,High : 1.25483,Low : 1.24801,Close : 1.25387,Position : -1
    2017-04-13, BUY EXECUTED, Price: 1.25370, Cost: -1.24891, Position : 0
    2017-04-13, Order Canceled/Margin/Rejected
    2017-04-13, OPERATION PROFIT, GROSS -0.00479, NET -0.00479
    0004,2017-04-13,Open : 1.25370,High : 1.25742,Low : 1.25000,Close : 1.25008,Position : 0
    2017-04-13, SELL CREATE, 1.25370, Limitprice : 1.24116, Stopprice : 1.26624
    0005,2017-04-14,Open : 1.25007,High : 1.25346,Low : 1.24970,Close : 1.25172,Position : 0
    2017-04-14, BUY CREATE, 1.25007, Limitprice : 1.26257, Stopprice : 1.23757
    2017-04-17, SELL EXECUTED, Price: 1.25370, Cost: -1.25370,Position : -1
    0006,2017-04-17,Open : 1.25271,High : 1.25959,Low : 1.25240,Close : 1.25631,Position : -1
    2017-04-18, BUY EXECUTED, Price: 1.26624, Cost: -1.25370, Position : 0
    2017-04-18, Order Canceled/Margin/Rejected
    2017-04-18, OPERATION PROFIT, GROSS -0.01254, NET -0.01254
    0007,2017-04-18,Open : 1.25638,High : 1.29053,Low : 1.25151,Close : 1.28400,Position : 0
    2017-04-18, SELL CREATE, 1.25638, Limitprice : 1.24382, Stopprice : 1.26894
    2017-04-19, SELL EXECUTED, Price: 1.28398, Cost: -1.28398,Position : -1
    0008,2017-04-19,Open : 1.28398,High : 1.28603,Low : 1.27701,Close : 1.27772,Position : -1
    Final Portfolio Value: 99999.99
    

    I would like to know how to set the limit price and the stop price to be a percentage of the execution price. For now, the execution price is the open price of the next day but the limit and stop price are based on the open price of that day.

    I would also like to know why are there buy orders that are not executed (e.g. : 2017-04-10, BUY CREATE, 1.23802)

    Thanks a lot :D



  • I tried using the manual bracket order to code the same strategies.

    from __future__ import (absolute_import, division, print_function,
                            unicode_literals)
    import os.path
    import argparse
    import datetime
    import collections
    
    import backtrader as bt
    import backtrader.feeds as btfeeds
    import backtrader.plot as plt
    import backtrader.indicators as btinds
    
    MAINSIGNALS = collections.OrderedDict(
            (('longonly',bt.SIGNAL_LONG),
             ('shortonly',bt.SIGNAL_SHORT),
            ))
    
    class Plotter(plt.Plot):
    
        def __init__(self):
            super().__init__(volup='#60cc73')  # custom color for volume up bars 
    
        def show(self):
            mng = self.mpyplot.get_current_fig_manager()
            mng.window.state('zoomed')
            self.mpyplot.show()  
    
    class St(bt.Strategy):
        
        def log(self, txt, dt=None):
            ''' Logging function fot this strategy'''
            dt = dt or self.data.datetime.date(0)
            print('%s, %s' % (dt.isoformat(), txt))
            
        def __init__(self):
            self.signal = 0
            
        def notify_order(self, order):
            if order.status in [order.Submitted, order.Accepted]:
                return
            
            if order.status in [order.Completed]:
                if order.isbuy():
                    self.signal +=1
                    self.log(
                        'BUY EXECUTED, Price: %.5f, Cost: %.5f, Position : %d' %
                        (order.executed.price,
                         order.executed.value,self.signal))
                    self.buyprice = order.executed.price
                    
                else:  # Sell
                    self.signal -= 1    
                    self.log('SELL EXECUTED, Price: %.5f, Cost: %.5f,Position : %d' %
                                 (order.executed.price,
                                  order.executed.value,self.signal))
                
                    self.bar_executed = len(self)
                
            elif order.status in [order.Canceled, order.Margin, order.Rejected]:
                self.log('Order Canceled/Margin/Rejected')
            
            self.order = None
            
        def notify_trade(self, trade):
            if not trade.isclosed:
                return
    
            self.log('OPERATION PROFIT, GROSS %.5f, NET %.5f' %
                     (trade.pnl, trade.pnlcomm))
            
        def next(self):
            txt = ','.join(
                ['%04d' % len(self.data),
                 self.data.datetime.date(0).isoformat(),
                 'Open : %.5f' % self.data.open[0],
                 'High : %.5f' % self.data.high[0],
                 'Low : %.5f' % self.data.low[0],
                 'Close : %.5f' % self.data.close[0],
                 'Position : %d' % self.signal])
            print(txt)
            
    
            if self.signal == 0:
                if self.data.open[0] > self.data.close[0]:
    
                    o1 = self.buy(exectype=bt.Order.Market,
                                  transmit=False)
    
                    print('{}: Oref {} / Buy at {}'.format(
                        self.datetime.date(), o1.ref,self.data.open[1]))
    
                    o2 = self.sell(exectype=bt.Order.Stop,
                                   price=0.99*self.data.open[1],
                                   parent=o1,
                                   transmit=False)
    
                    print('{}: Oref {} / Sell Stop at {}'.format(
                        self.datetime.date(), o2.ref,0.99*self.data.open[1]))
    
                    o3 = self.sell(exectype=bt.Order.Limit,
                                   price=1.01*self.data.open[1],
                                   parent=o1,
                                   transmit=True)
    
                    print('{}: Oref {} / Sell Limit at {}'.format(
                        self.datetime.date(), o3.ref,1.01*self.data.open[1]))
    
                    self.orefs = [o1.ref, o2.ref, o3.ref]
                    
                elif self.data.open[0] < self.data.close[0]:
                    
                    o1 = self.sell(exectype=bt.Order.Market,
                                  transmit=False)
    
                    print('{}: Oref {} / Sell at {}'.format(
                        self.datetime.date(), o1.ref,self.data.open[1]))
    
                    o2 = self.buy(exectype=bt.Order.Stop,
                                   price=1.01*self.data.open[1],
                                   parent=o1,
                                   transmit=False)
    
                    print('{}: Oref {} / Buy Stop at {}'.format(
                        self.datetime.date(), o2.ref,1.01*self.data.open[1]))
    
                    o3 = self.buy(exectype=bt.Order.Limit,
                                   price=0.99*self.data.open[1],
                                   parent=o1,
                                   transmit=True)
    
                    print('{}: Oref {} / Buy Limit at {}'.format(
                        self.datetime.date(), o3.ref,0.99*self.data.open[1]))
    
                    self.orefs = [o1.ref, o2.ref, o3.ref]
                    
            elif self.signal == 1:
                if self.data.open[0] > self.data.close[0]:
                    return
                
                elif self.data.open[0] < self.data.close[0]:
                    o1 = self.sell(exectype=bt.Order.Market,
                                  transmit=False,size=2)
    
                    print('{}: Oref {} / Sell at {}'.format(
                        self.datetime.date(), o1.ref,self.data.open[1]))
    
                    o2 = self.buy(exectype=bt.Order.Stop,
                                   price=1.01*self.data.open[1],
                                   parent=o1,
                                   transmit=False)
    
                    print('{}: Oref {} / Buy Stop at {}'.format(
                        self.datetime.date(), o2.ref,1.01*self.data.open[1]))
    
                    o3 = self.buy(exectype=bt.Order.Limit,
                                   price=0.99*self.data.open[1],
                                   parent=o1,
                                   transmit=True)
    
                    print('{}: Oref {} / Buy Limit at {}'.format(
                        self.datetime.date(), o3.ref,0.99*self.data.open[1]))
    
                    self.orefs = [o1.ref, o2.ref, o3.ref]
                    
            elif self.signal == -1:
                if self.data.open[0] < self.data.close[0]:
                    return
                
                elif self.data.open[0] > self.data.close[0] :
                    
                    o1 = self.sell(exectype=bt.Order.Market,size=2,
                                  transmit=False)
    
                    print('{}: Oref {} / Sell at {}'.format(
                        self.datetime.date(), o1.ref,self.data.open[1]))
    
                    o2 = self.buy(exectype=bt.Order.Stop,
                                   price=1.01*self.data.open[1],
                                   parent=o1,
                                   transmit=False)
    
                    print('{}: Oref {} / Buy Stop at {}'.format(
                        self.datetime.date(), o2.ref,1.01*self.data.open[1]))
    
                    o3 = self.buy(exectype=bt.Order.Limit,
                                   price=0.99*self.data.open[1],
                                   parent=o1,
                                   transmit=True)
    
                    print('{}: Oref {} / Buy Limit at {}'.format(
                        self.datetime.date(), o3.ref,0.99*self.data.open[1]))
    
                    self.orefs = [o1.ref, o2.ref, o3.ref]
                
                    
    
    
    class Plotter(plt.Plot):
    
        def __init__(self):
            super().__init__(volup='#60cc73')  # custom color for volume up bars 
    
        def show(self):
            mng = self.mpyplot.get_current_fig_manager()
            mng.window.state('zoomed')
            self.mpyplot.show()  
                      
    def runstrat():
        args = parse_args()
    
        cerebro = bt.Cerebro()
        modpath = 'd:\\I - TradersGPS\\'
        datapath = os.path.join(modpath, 'GBPUSD_D1_UTC+2_00.csv') 
        data = btfeeds.GenericCSVData(dataname=datapath,
                                       timeframe=bt.TimeFrame.Days,
                                      fromdate = datetime.datetime(2017,4,10),
                                      todate=datetime.datetime(2017,4,20),                                  
                                      nullvalue=0.0,
                                      dtformat=('%Y.%m.%d'),
                                      tmformat=('%H:%M'),
                                      datetime=0,
                                      time=1,
                                      open=2,
                                      high=3,
                                      low=4,
                                      close=5,
                                      volume=6,
                                      openinterest=-1)
    
        cerebro.adddata(data)
    
        cerebro.addstrategy(St)
        cerebro.broker.setcash(100000.0)
        print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())
        cerebro.run(stdstats=False)
        print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
        #plotter = Plotter()
        #cerebro.plot(plotter=plotter,subplot=False)
        
    
            
    def parse_args():
        parser = argparse.ArgumentParser(
            formatter_class=argparse.ArgumentDefaultsHelpFormatter,
            description='Sample for pivot point and cross plotting')
    
        parser.add_argument('--data0',
                            default='d:\\I - TradersGPS\\GBPUSD_D1_UTC+2_00.csv',
                            required=False, help='Data0 to read in')
    
        parser.add_argument('--data1', 
                            default='d:\\I - TradersGPS\\GBPUSD_H1_UTC+2_00.csv',
                            required=False, help='Data1 to read in')
    
        parser.add_argument('--fromdate', required=False, default='2001-01-01',
                            help='Date[time] in YYYY-MM-DD[THH:MM:SS] format')
        
        parser.add_argument('--todate', required=False, default='2007-01-01',
                            help='Date[time] in YYYY-MM-DD[THH:MM:SS] format')
    
        parser.add_argument('--plot', required=False, action='store_true',
                            help=('Plot the result'))
    
        return parser.parse_args()
    
    
    if __name__ == '__main__':
        runstrat()
        
    

    I got the warning that the array index out of range. Is it because of the [1] ? Thankss :)

      File "<ipython-input-11-2905f8945d39>", line 176, in next
        self.datetime.date(), o1.ref,self.data.open[1]))
    
      File "C:\Users\KT\Miniconda3\lib\site-packages\backtrader\linebuffer.py", line 163, in __getitem__
        return self.array[self.idx + ago]
    IndexError: array index out of range
    

  • administrators

    @KT said in Buying and Selling with Signals:

    I would like to know how to set the limit price and the stop price to be a percentage of the execution price

    You cannot with a bracket order. You can do it manually or you can set the bracketing orders to be StopTrail orders, where you can set the trailing in percentage mode, but it will not be a percentage of the execution price, because that price is not known in advance.

    @KT said in Buying and Selling with Signals:

    Is it because of the [1]

    [1] is the future and therefore an error can be expected.

    @KT said in Buying and Selling with Signals:

    I tried using the manual bracket

    buy_bracket generates 3 orders. Creating the 3 orders won't also allow you a priori to set things in terms of the execution price because that price is only known after execution and not during order creation.



  • Thanks @backtrader for the help :)

    May I know how to implement it manually?


  • administrators

    Once you receive notification of order execution, you use order.executed.price as the reference to send the desired orders into the market at a percentage of the execution price.



  • Hi @backtrader Can I see the sample implementation using the code above? I am quite confused on how to implement it. Thanks :)


  • administrators

    There is no such thing as a sample implementation.

    • You send an order to the market: self.buy(price=myprice, exectype=bt.Order.MyPreferredExecutionType)
    • The order is completed and notified to notify_order(order)
    • order.executed.price contains the actual execution price
    • A new order is sent to act as a stop-loss order: self.sell(price=% of order.executed.price, exectype=probably Stop)

Log in to reply
 

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