Backtrader Community

    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    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

    General Code/Help
    pandas dataframes strategy
    2
    2
    386
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • K
      krypterro last edited by

      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()
      
      
      K 1 Reply Last reply Reply Quote 0
      • K
        krypterro @krypterro last edited by

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

        1 Reply Last reply Reply Quote 0
        • 1 / 1
        • First post
          Last post
        Copyright © 2016, 2017, 2018, 2019, 2020, 2021 NodeBB Forums | Contributors