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

Volume Filler – Execution Bits added repeatedly?



  • When adding a Volume Filler to a strategy, the strategy started executing a very small number of Trades, despite executing thousands of Orders. Without the Volume Filler, the strategy executes the number of Trades normally.

    I created a test strategy to try to understand this behavior, using a data feed file from the github repository (file: 2006-01-02-volume-min-001.txt in datas directory).

    The Volume Filler used to test always returns a constant maximum volume.

    # A simple filler: max returned size is constant
    class TestFiller(with_metaclass(MetaParams, object)):
        def __call__(self, order, price, ago):
            maxsize = FILLER_VOLUME
            return min(maxsize, abs(order.executed.remsize))
    

    And the strategy always tries to buy a constant maximum volume, but sends sell orders before reaching the maximum volume.

    class TestStrategy(backtrader.Strategy):
            
        def next(self):
            
            # Computes volume of orders already opened
            buy_orders_volume = 0
            sell_orders_volume = 0
            for order in self.broker.get_orders_open():
                remsize = order.executed.remsize
                if order.isbuy():
                    buy_orders_volume += remsize
                else:
                    sell_orders_volume += abs(remsize)
    
            # Target volumes based on constant value and current position        
            target_sell_volume = self.getposition().size;
            target_buy_volume = STRATEGY_VOLUME - self.getposition().size
            
            # Computes how much volume is needed to achieve target
            required_sell_volume = target_sell_volume - sell_orders_volume
            required_buy_volume = target_buy_volume - buy_orders_volume
            
            # Send orders immediately
            if required_sell_volume > 0:
                self.close(size=required_sell_volume)
            
            if required_buy_volume > 0:
                self.buy(size=required_buy_volume)
    

    I added a notify_order() method to show the Execution Bits of the partial Buy Orders and also the associated Trade.

    def notify_order(self, order):
            
            # Log only partial buy orders
            if order.isbuy():
                if order.status in [backtrader.Order.Partial]:
                    # Trade associated with the order
                    # (code snippet from method _addnotification() of backtrader.Strategy class) 
                    datatrades = self._trades[order.data][order.tradeid]
                    trade = datatrades[-1]
                    print("TRADE - ref: {}, size: {} ".format(trade.ref, trade.size))
                    
                    # Execution bits
                    for exbit in order.executed.exbits:
                        print('EXBIT - dt: {}, size:{}, price:{}, closed:{}, opened:{}'.format(
                            order.data.num2date(exbit.dt), 
                            exbit.size, 
                            exbit.price, 
                            exbit.closed, 
                            exbit.opened))
    
                    print('---')
    

    Complete code:

    import backtrader 
    import datetime 
    from backtrader.utils.py3 import with_metaclass
    from backtrader.metabase import MetaParams
    
    # Test params
    STRATEGY_VOLUME = 100
    FILLER_VOLUME = 5
    USE_FILLER = True 
    CASH = 1000000
    FROM_DATE =  datetime.datetime(2006, 1, 5) 
    TO_DATE = datetime.datetime(2006, 1, 6) 
    
    # A simple filler: max returned size is constant
    class TestFiller(with_metaclass(MetaParams, object)):
        def __call__(self, order, price, ago):
            maxsize = FILLER_VOLUME
            return min(maxsize, abs(order.executed.remsize))
    
    
    class TestStrategy(backtrader.Strategy):
            
        def next(self):
            
            # Computes volume of orders already opened
            buy_orders_volume = 0
            sell_orders_volume = 0
            for order in self.broker.get_orders_open():
                remsize = order.executed.remsize
                if order.isbuy():
                    buy_orders_volume += remsize
                else:
                    sell_orders_volume += abs(remsize)
    
            # Target volumes based on constant value and current position        
            target_sell_volume = self.getposition().size;
            target_buy_volume = STRATEGY_VOLUME - self.getposition().size
            
            # Computes how much volume is needed to achieve target
            required_sell_volume = target_sell_volume - sell_orders_volume
            required_buy_volume = target_buy_volume - buy_orders_volume
            
            # Send orders immediately
            if required_sell_volume > 0:
                self.close(size=required_sell_volume)
            
            if required_buy_volume > 0:
                self.buy(size=required_buy_volume)
            
    
        def notify_order(self, order):
            
            # Log only partial buy orders
            if order.isbuy():
                if order.status in [backtrader.Order.Partial]:
                    # Trade associated with the order
                    # (code snippet from method _addnotification() of backtrader.Strategy class) 
                    datatrades = self._trades[order.data][order.tradeid]
                    trade = datatrades[-1]
                    print("TRADE - ref: {}, size: {} ".format(trade.ref, trade.size))
                    
                    # Execution bits
                    for exbit in order.executed.exbits:
                        print('EXBIT - dt: {}, size:{}, price:{}, closed:{}, opened:{}'.format(
                            order.data.num2date(exbit.dt), 
                            exbit.size, 
                            exbit.price, 
                            exbit.closed, 
                            exbit.opened))
    
                    print('---')
    
    
    def run():
        cerebro = backtrader.Cerebro()
        cerebro.broker.setcash(CASH)
    
        if USE_FILLER:
            cerebro.broker.set_filler(TestFiller())
       
        data = backtrader.feeds.BacktraderCSVData(dataname='../../datas/2006-01-02-volume-min-001.txt', 
                                                fromdate = FROM_DATE, 
                                                todate = TO_DATE,
                                                timeframe = backtrader.TimeFrame.Minutes,
                                           )
        cerebro.adddata(data)
        #cerebro.replaydata(data, timeframe=bt.TimeFrame.Days)
    
        cerebro.addstrategy(TestStrategy)
        cerebro.addanalyzer(backtrader.analyzers.TradeAnalyzer, _name="myTradeAnalysis")
        st = cerebro.run()[0]
        trade_analyzer = st.analyzers.myTradeAnalysis.get_analysis()
        
        print("Total trades: " + str(trade_analyzer.total.total))
        cerebro.plot()
    
    
    if __name__ == '__main__':
        run()
    
    

    Log output (truncated):

    TRADE - ref: 1, size: 5 
    EXBIT - dt: 2006-01-05 09:02:00, size:5, price:3666.0, closed:0, opened:5
    ---
    TRADE - ref: 1, size: 10 
    EXBIT - dt: 2006-01-05 09:02:00, size:5, price:3666.0, closed:0, opened:5
    EXBIT - dt: 2006-01-05 09:03:00, size:5, price:3661.0, closed:0, opened:5
    ---
    TRADE - ref: 1, size: 25 
    EXBIT - dt: 2006-01-05 09:02:00, size:5, price:3666.0, closed:0, opened:5
    EXBIT - dt: 2006-01-05 09:03:00, size:5, price:3661.0, closed:0, opened:5
    EXBIT - dt: 2006-01-05 09:04:00, size:5, price:3662.0, closed:0, opened:5
    ---
    TRADE - ref: 1, size: 45 
    EXBIT - dt: 2006-01-05 09:02:00, size:5, price:3666.0, closed:0, opened:5
    EXBIT - dt: 2006-01-05 09:03:00, size:5, price:3661.0, closed:0, opened:5
    EXBIT - dt: 2006-01-05 09:04:00, size:5, price:3662.0, closed:0, opened:5
    EXBIT - dt: 2006-01-05 09:05:00, size:5, price:3660.0, closed:0, opened:5
    ---
    TRADE - ref: 1, size: 60 
    EXBIT - dt: 2006-01-05 09:02:00, size:5, price:3666.0, closed:0, opened:5
    EXBIT - dt: 2006-01-05 09:03:00, size:5, price:3661.0, closed:0, opened:5
    EXBIT - dt: 2006-01-05 09:04:00, size:5, price:3662.0, closed:0, opened:5
    EXBIT - dt: 2006-01-05 09:05:00, size:5, price:3660.0, closed:0, opened:5
    EXBIT - dt: 2006-01-05 09:06:00, size:5, price:3661.0, closed:0, opened:5
    ---
    TRADE - ref: 1, size: 80 
    EXBIT - dt: 2006-01-05 09:02:00, size:5, price:3666.0, closed:0, opened:5
    EXBIT - dt: 2006-01-05 09:03:00, size:5, price:3661.0, closed:0, opened:5
    EXBIT - dt: 2006-01-05 09:04:00, size:5, price:3662.0, closed:0, opened:5
    EXBIT - dt: 2006-01-05 09:05:00, size:5, price:3660.0, closed:0, opened:5
    EXBIT - dt: 2006-01-05 09:06:00, size:5, price:3661.0, closed:0, opened:5
    EXBIT - dt: 2006-01-05 09:07:00, size:5, price:3661.0, closed:0, opened:5
    ---
    TRADE - ref: 1, size: 80 
    EXBIT - dt: 2006-01-05 09:07:00, size:5, price:3661.0, closed:0, opened:5
    ---
    TRADE - ref: 1, size: 115 
    EXBIT - dt: 2006-01-05 09:02:00, size:5, price:3666.0, closed:0, opened:5
    EXBIT - dt: 2006-01-05 09:03:00, size:5, price:3661.0, closed:0, opened:5
    EXBIT - dt: 2006-01-05 09:04:00, size:5, price:3662.0, closed:0, opened:5
    EXBIT - dt: 2006-01-05 09:05:00, size:5, price:3660.0, closed:0, opened:5
    EXBIT - dt: 2006-01-05 09:06:00, size:5, price:3661.0, closed:0, opened:5
    EXBIT - dt: 2006-01-05 09:07:00, size:5, price:3661.0, closed:0, opened:5
    EXBIT - dt: 2006-01-05 09:08:00, size:5, price:3662.0, closed:0, opened:5
    ---
    TRADE - ref: 1, size: 115 
    EXBIT - dt: 2006-01-05 09:08:00, size:5, price:3662.0, closed:0, opened:5
    ---
    TRADE - ref: 1, size: 155 
    EXBIT - dt: 2006-01-05 09:02:00, size:5, price:3666.0, closed:0, opened:5
    EXBIT - dt: 2006-01-05 09:03:00, size:5, price:3661.0, closed:0, opened:5
    EXBIT - dt: 2006-01-05 09:04:00, size:5, price:3662.0, closed:0, opened:5
    EXBIT - dt: 2006-01-05 09:05:00, size:5, price:3660.0, closed:0, opened:5
    EXBIT - dt: 2006-01-05 09:06:00, size:5, price:3661.0, closed:0, opened:5
    EXBIT - dt: 2006-01-05 09:07:00, size:5, price:3661.0, closed:0, opened:5
    EXBIT - dt: 2006-01-05 09:08:00, size:5, price:3662.0, closed:0, opened:5
    EXBIT - dt: 2006-01-05 09:09:00, size:5, price:3662.0, closed:0, opened:5
    ---
    TRADE - ref: 1, size: 155 
    EXBIT - dt: 2006-01-05 09:09:00, size:5, price:3662.0, closed:0, opened:5
    ---
    TRADE - ref: 1, size: 200 
    EXBIT - dt: 2006-01-05 09:02:00, size:5, price:3666.0, closed:0, opened:5
    EXBIT - dt: 2006-01-05 09:03:00, size:5, price:3661.0, closed:0, opened:5
    EXBIT - dt: 2006-01-05 09:04:00, size:5, price:3662.0, closed:0, opened:5
    EXBIT - dt: 2006-01-05 09:05:00, size:5, price:3660.0, closed:0, opened:5
    EXBIT - dt: 2006-01-05 09:06:00, size:5, price:3661.0, closed:0, opened:5
    EXBIT - dt: 2006-01-05 09:07:00, size:5, price:3661.0, closed:0, opened:5
    EXBIT - dt: 2006-01-05 09:08:00, size:5, price:3662.0, closed:0, opened:5
    EXBIT - dt: 2006-01-05 09:09:00, size:5, price:3662.0, closed:0, opened:5
    EXBIT - dt: 2006-01-05 09:10:00, size:5, price:3659.0, closed:0, opened:5
    ---
    TRADE - ref: 1, size: 200 
    EXBIT - dt: 2006-01-05 09:10:00, size:5, price:3659.0, closed:0, opened:5
    ---
    TRADE - ref: 1, size: 235 
    EXBIT - dt: 2006-01-05 09:02:00, size:5, price:3666.0, closed:0, opened:5
    EXBIT - dt: 2006-01-05 09:03:00, size:5, price:3661.0, closed:0, opened:5
    EXBIT - dt: 2006-01-05 09:04:00, size:5, price:3662.0, closed:0, opened:5
    EXBIT - dt: 2006-01-05 09:05:00, size:5, price:3660.0, closed:0, opened:5
    EXBIT - dt: 2006-01-05 09:06:00, size:5, price:3661.0, closed:0, opened:5
    EXBIT - dt: 2006-01-05 09:07:00, size:5, price:3661.0, closed:0, opened:5
    EXBIT - dt: 2006-01-05 09:08:00, size:5, price:3662.0, closed:0, opened:5
    EXBIT - dt: 2006-01-05 09:09:00, size:5, price:3662.0, closed:0, opened:5
    EXBIT - dt: 2006-01-05 09:10:00, size:5, price:3659.0, closed:0, opened:5
    EXBIT - dt: 2006-01-05 09:11:00, size:5, price:3659.0, closed:0, opened:5
    ---
    ...
    

    Analyzing the log:

    1. Only one Trade is created during the entire test
    2. The size of the executed Trade (235 in the truncated log) is much larger than the maximum that the strategy tries to limit when sending orders (100 in this test)
    3. The cause of this seems to be that the Execution Bits of the partial Order are added up repeatedly

    In the log, see the increasing size of the trade (5, 10, 25, 45, 60 ...) and how the exbits with the same minute seem to be being added up even if they had already been added in the previous call (+5, +10, + 15, + 20, + 25...) . I think that with each call it should add an execution bit (ie +5, +5, + 5 ...)

    This log is to try to explain, but first I tried to debug the Backtrader source code, putting breakpoints in the strategy class for example, but my Python skills are not great (I actually learned this language to try to use Backtrader) ...

    So: is there an error in the code that deals with Execution Bits? Or am I missing something?


Log in to reply
 

});