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 use filters properly?



  • This is acutally about two questions one is major one about filters and other is minor about buy and sell. I will start with the minor one

    I am backtesting a simple strat to buy at open and sell at the close on the same day. I already got an answer here but I am curious why this didn't work in backtrader .

        def next(self):
            self.buy(price=self.data.open[0])
            self.close(price=self.data.close[0]) 
          # self.sell gives same result
    

    some basic info

    # Data
                      open       close
    Date                              
    2019-05-20  183.520004  183.089996
    2019-05-21  185.220001  186.600006
    2019-05-22  184.660004  182.779999
    2019-05-23  179.800003  179.660004
    2019-05-24  180.199997  178.970001
    
    # Log details
    2019-05-21, SELL EXECUTED, Price: 185.22, Cost: 185.49, Comm 0.00
    2019-05-22, BUY EXECUTED, Price: 184.66, Cost: 184.66, Comm 0.00
    2019-05-22, SELL EXECUTED, Price: 184.66, Cost: 185.07, Comm 0.00
    2019-05-23, BUY EXECUTED, Price: 179.80, Cost: 179.80, Comm 0.00
    2019-05-23, SELL EXECUTED, Price: 179.80, Cost: 182.44, Comm 0.00
    2019-05-24, BUY EXECUTED, Price: 180.20, Cost: 180.20, Comm 0.00
    2019-05-24, SELL EXECUTED, Price: 180.20, Cost: 181.32, Comm 0.00
    
    

    Now the major question is how to properly use filters. I am using DaySplitter_Close filter to understand it.

    class TestStrategy(bt.Strategy):
        def next(self):
            print("Ticks: "+str(len(self))+" Price: "+str(self.data.close[0]))
    
    cerebro = bt.Cerebro()
    cerebro.addstrategy(TestStrategy)
    df=bt.feeds.PandasData(dataname=data)
    df.addfilter(bt.filters.DaySplitter_Close)
    cerebro.adddata(df)
    cerebro.run()
    
    

    I understood that it basically splits the data into two streams one with OHLX were X is the average of OHL and a second stream with only close values. When I run my strategy I get this result.

    Ticks: 1901 Price: 184.30667114257812
    Ticks: 1902 Price: 182.77999877929688
    Ticks: 1903 Price: 179.38333129882812
    Ticks: 1904 Price: 179.66000366210938
    Ticks: 1905 Price: 180.31999715169272
    Ticks: 1906 Price: 178.97000122070312
    

    I understood that both streams are streaming simultaneously. How can I access the streams seperately. Like this

    print("stream_1",str(self.data1.close[0]))
    print("stream_2",str(self.data2.close[0]))
    

  • administrators

    It splits a bar in two parts and NOT in two streams. A filter modifies the data it is applied to.



  • @backtrader14 said in How to use filters properly?:
    So in DaySplitter_Close for a single day it generates two bars and it alternates between OHLX and CCCC right?

    Can you tell me why this doesn't work?

    def next(self):
        self.buy(price=self.data.open[0])
        self.close(price=self.data.close[0])


  • @backtrader14 said in How to use filters properly?:

    Can you tell me why this doesn't work?

    next() is called right after the price bar[0] closed. How orders can be executed at prices which are already gone?

    self.buy() and self.close() as they are shown in your post are Market orders. Market orders are executed based on the first coming price, which is the upcoming open.



  • @backtrader14 said in How to use filters properly?:
    lets take this example

                     open      close
    2019-05-24  180.199997  178.970001
    

    This is what I think how it works

    def next(self):
        self.buy(price=self.data.open[0])
        self.sell(price=self.data.close[0])
    

    I thought this as a loop kind of execution. when next() is called initially; In here self.buy(price=self.data.open[0]) it buys at 180.19 then in the next line self.sell(price=self.data.close[0]) (changed to sell) its sells the stock @178.97. Both buying and selling are happening in the same day ie 2019-05-24 . Then bar moves to the next position ie 2019-05-25 and again next is initialized and repeats the process. Is this logic correct?



  • Both of your orders are market orders, so they both are executed with the first upcoming open of 180.20 on 5/24. It doesn't matter what is shown as a price parameter for market order.



  • Try this - buys at open, set stop loss = 0.6$, sell at close if have an open position at close.

    from __future__ import (absolute_import, division, print_function,
                            unicode_literals)
    
    import backtrader as bt
    
    class TestStrategy(bt.Strategy):
    
        def __init__(self):
            self.sl = None
            self.o = self.datas[0].open
    
        def log(self, txt, dt=None):
            dt = dt or self.datas[0].datetime.date(0)
            print('%s, %s' % (dt.isoformat(), txt))
    
        def start(self):
            self.counter = 1
    
        def notify_order(self, order):
            if order.status in [order.Submitted, order.Accepted]:
                return
    
            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)
    
            elif order.status in [order.Canceled, order.Margin, order.Rejected]:
                self.log('Order Canceled/Margin/Rejected')
    
        def next_open(self):
            self.counter += 1
    
            if self.counter == 1:
                self.buy(coc=False)
                self.sl = self.sell(price=self.o[0]-0.6, exectype=bt.Order.Stop)
    
        def next(self):
            self.counter += 1
    
            if self.counter == 4:
                pos = self.getposition()
                if pos:
                    self.cancel(self.sl)
                    self.sell()
    
            if self.counter == 4: self.counter = 0
    
    
    if __name__ == '__main__':
    
        cerebro = bt.Cerebro(cheat_on_open=True)
        cerebro.addstrategy(TestStrategy)
        data = <your data feed>
        data.addfilter(bt.filters.DaySplitter_Close)
        cerebro.adddata(data)
        cerebro.broker.set_coc(True)
        cerebro.run()
    


  • @ab_trader said in How to use filters properly?:
    I think stop loss is not working properly

    # this is my data
    
                      open       close
    Date                              
    2019-05-10  197.419998  197.179993
    2019-05-13  187.710007  185.720001
    2019-05-14  186.410004  188.660004
    2019-05-15  186.270004  190.919998
    2019-05-16  189.910004  190.080002
    2019-05-17  186.929993  189.000000
    2019-05-20  183.520004  183.089996
    2019-05-21  185.220001  186.600006
    2019-05-22  184.660004  182.779999
    2019-05-23  179.800003  179.660004
    2019-05-24  180.199997  178.970001
    2019-05-28  178.919998  178.229996
    
    # this is the log output
    2019-05-20, SELL EXECUTED, 189.00
    2019-05-20, BUY EXECUTED, 183.52
    2019-05-20, SELL EXECUTED, 182.92
    2019-05-21, BUY EXECUTED, 185.22
    2019-05-22, Order Canceled/Margin/Rejected
    2019-05-22, SELL EXECUTED, 186.60
    2019-05-22, BUY EXECUTED, 184.66
    2019-05-22, SELL EXECUTED, 184.06
    2019-05-23, BUY EXECUTED, 179.80
    2019-05-23, SELL EXECUTED, 179.20
    2019-05-24, BUY EXECUTED, 180.20
    2019-05-24, SELL EXECUTED, 179.60
    2019-05-28, BUY EXECUTED, 178.92
    2019-05-28, SELL EXECUTED, 178.32
    
    

    If we look at the trading @ 2019-05-28 it has sold the stock at a higher price 178.32 than our real price 178.22.

    Can you tell the rationale behing the counter and why 4? Whats this do self.datas[0].open ?



  • @backtrader14 said in How to use filters properly?:

    If we look at the trading @ 2019-05-28 it has sold the stock at a higher price 178.32 than our real price 178.22.

    This is why they call it stop loss - you can't get more losses than 0.6$ independently on how low your close can be.

    Can you tell the rationale behing the counter and why 4?

    DaySplitter_Close filter splits price bar into 2 sub-bars OHLX and CCCC, for each of these sub-bars next_open() and next() called. So finally we have 4 calls, 1st call is next_open() for bar OHLX (this is your daily bar opening with cheat-on-open) and 4th call is for next() for bar CCCC (this is your daily bar closing with cheat-on-close).

    Whats this do self.datas[0].open ?

    This is open price of the data feed.



  • Thank you. it cleared most of my doubts.


 

});