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

Extending Pandas Columns



  • I have gone through every example I can find here and I can't seem to get past the below error. I am adding the lines and parameters, and I am initiating the column variable, what am I missing? I am just trying to get the extra columns into the data to make these vars available in the Strategy.

    Traceback (most recent call last):
      File "backtest1.py", line 171, in <module>
        main()
      File "backtest1.py", line 93, in main
        thestrats = cerebro.run()
      File "/usr/lib/python3.8/site-packages/backtrader/cerebro.py", line 1127, in run
        runstrat = self.runstrategies(iterstrat)
      File "/usr/lib/python3.8/site-packages/backtrader/cerebro.py", line 1217, in runstrategies
        strat = stratcls(*sargs, **skwargs)
      File "/usr/lib/python3.8/site-packages/backtrader/metabase.py", line 88, in __call__
        _obj, args, kwargs = cls.doinit(_obj, *args, **kwargs)
      File "/usr/lib/python3.8/site-packages/backtrader/metabase.py", line 78, in doinit
        _obj.__init__(*args, **kwargs)
      File "/home/krypterro/btcbot/Strategy.py", line 20, in __init__
        self.order_act = self.datas[0].order_act
      File "/usr/lib/python3.8/site-packages/backtrader/lineseries.py", line 461, in __getattr__
        return getattr(self.lines, name)
    AttributeError: 'Lines_LineSeries_DataSeries_OHLC_OHLCDateTime_Abst' object has no attribute 'order_act'
    
    

    backtest.py

    from __future__ import (absolute_import, division, print_function,
                            unicode_literals)
    
    import datetime  # For datetime objects
    import os.path  # To manage paths
    import sys  # To find out the script name (in argv[0])
    
    import pandas as pd
    import btalib
    from tqdm import tqdm
    from colorama import init
    from colorama import Fore, Back, Style
    
    import backtrader as bt
    import backtrader.analyzers as btanalyzers
    import backtrader.feeds as btfeeds
    import backtrader.strategies as btstrats
    
    from DBA import Binance
    from DB import DB1
    from Strategy import TestStrategy
    from Toolbox import Hammers
    
    
    init()
    db1 = DB1()
    os.system('clear')
    
    cash = 10000
    def main():
        cnt = 0
        btc_list = Binance.get_btc_hours(db1)
        row_count = len(btc_list)
        new_list = []
        print(Fore.GREEN)
        for i in tqdm (range (row_count), desc="Processing"):
        #for i in range(row_count):
            if i > 2:
                row_0_data = dict(btc_list[i])
                back_1_data = dict(btc_list[i-1])
                back_2_data = dict(btc_list[i-2])
                # build indicators
                row_0_data['decline'] = back_1_data['high'] - row_0_data['low']
                row_0_data['rally'] = row_0_data['high'] - back_1_data['low']
                row_0_data['buy_high'] = Hammers.get_buy_high(row_0_data['high'], back_1_data['high'])
                row_0_data['buy_under'] = Hammers.get_buy_under(back_1_data['low'], row_0_data['low'])
                row_0_data['btc_mi'] = row_0_data['close'] - back_2_data['close']
                row_0_data['btc_mt'] = Hammers.get_mt(row_0_data['btc_mi'], back_1_data['btc_mi'], back_2_data['btc_mi'])
                row_0_data['level'] = 0
                row_0_data['level'] = Hammers.get_level(row_0_data, back_1_data)
                new_list.append(row_0_data)
                # setup order
                row_0_data['pip_value'] = Hammers.get_pip_value(row_0_data)
                order = Hammers.get_order(row_0_data, back_1_data)
                row_0_data['order_act'] = order['act']
                try:
                    row_0_data['order_target'] = round(order['target'],2)
                except:
                    pass
        
        data_df = pd.DataFrame(new_list)
        data_df = data_df.set_index('stamp')
        data = bt.feeds.PandasData(dataname=data_df)
        
    
        #print(data_df.tail(10))
        
        
        # Create a cerebro obj and populate
        cerebro = bt.Cerebro()
        #cerebro.addstrategy(btstrats.SMA_CrossOver)
        cerebro.addstrategy(TestStrategy)
        cerebro.adddata(data)
        cerebro.broker.setcash(cash)
        
        # Set the commission
        cerebro.broker.setcommission(commission=0.002)
        
        # Analyzer
        cerebro.addanalyzer(btanalyzers.SharpeRatio, _name='out_sharpe')
        cerebro.addanalyzer(btanalyzers.DrawDown, _name='out_drawdown')
        #cerebro.addanalyzer(btanalyzers.AnnualReturn, _name='out_annual')
    
        # Add a FixedSize sizer according to the stake
        #cerebro.addsizer(bt.sizers.FixedSize, stake=10)
        
        # Print out the starting conditions
        print('Starting Portfolio Value: ', as_currency(cerebro.broker.getvalue()))
        start_cap = cerebro.broker.getvalue()
        print(Fore.CYAN)
        
        # Run over everything
        thestrats = cerebro.run()
        thestrat = thestrats[0]
    
        sharpe_data = dict(thestrat.analyzers.out_sharpe.get_analysis())
        sharpe_ratio = sharpe_data['sharperatio']
        drawdown_data = dict(thestrat.analyzers.out_drawdown.get_analysis())
        drawdown_data = drawdown_data['max']
        
        #annual_data = dict(thestrat.analyzers.out_annual.get_analysis())
        
        # format text
        drawdown_percentage = int(drawdown_data['drawdown'])
        drawdown_percentage = str(drawdown_percentage) + '%'
        drawdown_money = as_currency(drawdown_data['moneydown'])
        net_profit = (cerebro.broker.getvalue() - start_cap) / cerebro.broker.getvalue() * 100
        net_profit = round(net_profit,2)
        final_total = as_currency(cerebro.broker.getvalue())
        # Print out the final result
        print('Max Drawdown Length:', drawdown_data['len'], 'hours')
        print('Max Drawdown Percentage:', drawdown_data['drawdown'])
        print('Max Drawdown Money:', drawdown_money)
        print(Fore.GREEN)
        print('Sharpe Ratio:', sharpe_ratio)
        print('Final Portfolio Value: ', final_total)
        print('Net Profit: ', net_profit, '%')
        print(Style.RESET_ALL)
        print('BackTesting Complete')
        
    
    def as_currency(amount):
        if amount >= 0:
            return '${:,.2f}'.format(amount)
        else:
            return '-${:,.2f}'.format(-amount)
    
    
    class PandasData_Extend(btfeeds.PandasData):
        lines = ('btc_id', 'btc_hour', 'symbol', 'open', 'high', 'low', 'close', 'range_usd', 'sma24', 'ema', 'volume_btc', 'volume_usd', 'decline', 'rally', 'buy_high', 'buy_under', 'btc_mi', 'btc_mt', 'level', 'pip_value', 'order_act', 'order_target')
        params = (
            ('datetime', None),
            ('btc_id', 0),
            ('btc_hour', 1),
            ('symbol', 2),
            ('open', 3),
            ('high', 4),
            ('low', 5),
            ('close', 6),
            ('range_usd', 7),
            ('sma24', 8),
            ('ema', 9),
            ('volume_btc', 10),
            ('volume_usd', 11),
            ('decline', 12),
            ('rally', 13),
            ('buy_high', 14),
            ('buy_under', 15),
            ('btc_mi', 16),
            ('btc_mt', 17),
            ('level', 18),
            ('pip_value', 19),
            ('order_act', 20),
            ('order_target', 21)
        )
        
    def print_full(x):
        pd.set_option('display.max_rows', len(x))
        pd.set_option('display.max_columns', None)
        pd.set_option('display.width', 2000)
        pd.set_option('display.float_format', '{:20,.2f}'.format)
        pd.set_option('display.max_colwidth', None)
        print(x)
        pd.reset_option('display.max_rows')
        pd.reset_option('display.max_columns')
        pd.reset_option('display.width')
        pd.reset_option('display.float_format')
        pd.reset_option('display.max_colwidth')
    
        
    main()
    

    Strategy.py

    import backtrader as bt
    from Toolbox import Hammers
    
    class TestStrategy(bt.Strategy):
        params = (
            ('maperiod', 240),
            ('period', 10),
            ('onlydaily', False)
        )
    
        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):
            # custom vars
            
            
            self.order_act = self.datas[0].order_act
            
            
            # Keep a reference to the "close" line in the data[0] dataseries
            self.dataclose = self.datas[0].close
    
            # To keep track of pending orders and buy price/commission
            self.order = None
            self.buyprice = None
            self.buycomm = None
    
            # Add a MovingAverageSimple indicator
            self.sma = bt.indicators.SimpleMovingAverage(
                self.datas[0], period=self.params.maperiod)
    
        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, 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
                else:  # Sell
                    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]:
                self.log('Order Canceled/Margin/Rejected')
    
            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):
            # Simply log the closing price of the series from the reference
            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:
    
                # Not yet ... we MIGHT BUY if ...
                if self.order_act == 'buy':
    
                    # BUY, BUY, 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()
    
            else:
    
                if self.order_act == 'sell':
                    # SELL, SELL, 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()
    
    


  • @krypterro
    The solution to the above problem is
    data = PandasData_Extend(dataname=data_df)
    I was not extending the class properly.



});