Backtrader Community

    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    1. Home
    2. krypterro
    For code/output blocks: Use ``` (aka backtick or grave accent) in a single line before and after the block. See: http://commonmark.org/help/
    K
    • Profile
    • Following 0
    • Followers 0
    • Topics 3
    • Posts 5
    • Best 0
    • Controversial 0
    • Groups 0

    krypterro

    @krypterro

    0
    Reputation
    6
    Profile views
    5
    Posts
    0
    Followers
    0
    Following
    Joined Last Online

    krypterro Unfollow Follow

    Latest posts made by krypterro

    • Setting Sizer within Strategy Next for Bitcoin

      We are using the following code in our main application, and it works fine to deal with Fractional assests such as BTC. Basically we are taking the price of BTC, dividing by 100, and that is a single unit for our strategy. The strategy needs to read read from the current balance, then decide how many units it needs to buy. All easy code, but...how do we do this, in the strategy's Class in the next function, so we can dynamically decide how many units to buy based on other data?

      the_stake = number_of_units * .01
      cerebro.addsizer(bt.sizers.FixedSize, stake=the_stake)
      

      Here is the code we use to generate the units if needed.

                      # Determine Size
                      btc_price = self.dataclose[0]
                      balance = self.broker.getvalue()
                      unit_price = btc_price / 100
                      money_in_play = balance * .5
                      units_to_buy = money_in_play // unit_price
                      stake = units_to_buy / 100
                      btc_cost = stake * btc_price
      
      posted in General Code/Help
      K
      krypterro
    • RE: Importing Pandas Data in Minute Intervals

      @krypterro
      The following code works, but still does not process buy/sell functions in the strategy. I am using the generic sma example strategy from Backtrader docs. Works on hourly data perfectly, but not per minute data, but now with no errors. I am pulling data from MySQL, into a Pandas DataFrame with the code below, and using bta-lib in Pandas before passing the DataFrame into cerebro. How do I get cerebro to work with minute data if not this way?

      from __future__ import (absolute_import, division, print_function,
                              unicode_literals)
      import random
      import datetime  # For datetime objects
      import os.path  # To manage paths
      import sys  # To find out the script name (in argv[0])
      import threading
      
      import pandas as pd
      import btalib
      from colorama import init
      from colorama import Fore, Back, Style
      from loguru import logger
      
      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 Strategy2 import MinStrategy
      from Toolbox import Hammers
      
      def main():
          go_time = datetime.datetime.now()
          init()
          db1 = DB1()
          os.system('clear')
          
          runs = 1
          interval = 'minutes'
          start_time = datetime.datetime.strptime('1 Aug 2020', '%d %b %Y')
          
          # period select
          print('retrieving data...')
          btc_list = Binance.get_btc_minutes_cut(db1, start_time)
          print('loading dataframe...')
          data_df = pd.DataFrame(btc_list)
          df = data_df.set_index('stamp')
          print('begining analysis...')
          print('--------------------')
          print(Fore.CYAN)
          # ---------------------
          
          threads = list()
          for index in range(runs):
              x = threading.Thread(target=runner(db1, df, interval), args=(index,))
              threads.append(x)
              x.start()
      
          for index, thread in enumerate(threads):
              thread.join()
          
          # ---------------------
          print(Style.RESET_ALL)
          print('BackTesting Complete')
          stop_time = datetime.datetime.now() - go_time
          run_time = stop_time.total_seconds()
          print('Toal Run Time:', run_time)
          avg_run = run_time / runs
          print('Average Run Time:', avg_run)
          
      def runner(db1, df, interval):
          start_time = df.index[0]
          end_time = df.index[-1]
          span_list = [1,2,4,6,15,60,120,240]
          in_data = {}
          in_data['strategy'] = 'btcemaroll'
          in_data['cash_in'] = 10000
          in_data['commission'] = .002
          in_data['span'] = random.choice(span_list)
          in_data['pip_value'] = 0.01
          in_data['range_under'] = random.randint(1, 2000)
          in_data['range_over'] = random.randint(2000, 3000)
          in_data['diff_under'] = random.randint(1, 100)
          in_data['diff_over'] = random.randint(100, 300)
          in_data['min_profit'] = .01 #random.randint(1, 50)
          
          # add bta-lib indicators
          #df['sma'] = btalib.sma(df.high, period=in_data['span']).df
          df['ema'] = btalib.ema(df.close, period=in_data['span']).df
      
          # Create a cerebro obj and populate
          cerebro = bt.Cerebro()
          #cerebro.addstrategy(MinStrategy, in_data)
          cerebro.addstrategy(MinStrategy, in_data)
      
          # ---------------
          # pandas data setup
          data = PandasData_Extend(dataname=df, timeframe=bt.TimeFrame.Minutes, compression = 1)
          cerebro.adddata(data)
          # ---------------
          
          cerebro.broker.setcash(in_data['cash_in'])
          cerebro.broker.setcommission(commission=in_data['commission'])
      
          # 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('Starting Portfolio Value: ', as_currency(cerebro.broker.getvalue()))
          print('Start Time:', start_time)
          print('End Time:', end_time)
          start_cap = cerebro.broker.getvalue()
          
          # 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 = round(drawdown_data['drawdown'],2)
          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())
          
          # build dictionary for results db
          out_data = {}
          out_data['cash_in'] = final_total
          out_data['max_drawdown_hours'] = drawdown_data['len']
          out_data['max_drawdown_per'] = drawdown_percentage
          out_data['max_drawdown_money'] = round(drawdown_data['moneydown'],2)
          out_data['sharpe'] = sharpe_ratio
          out_data['cash_out'] = round(cerebro.broker.getvalue(),2)
          out_data['net_profit'] = net_profit
          
          # Print out the final result
          #Binance.add_backtest(db1, interval, start_time, end_time, in_data, out_data)
          print('Minimum Profit:', in_data['min_profit'], '%')
          print('Span:', in_data['span'], interval)
          print('Max Drawdown Length:', drawdown_data['len'], 'hours')
          print('Max Drawdown Percentage:', drawdown_percentage, '%')
          print('Max Drawdown Money:', drawdown_money)
          print('Sharpe Ratio:', sharpe_ratio)
          print('Final Portfolio Value: ', final_total)
          print('Net Profit: ', net_profit, '%')
          print('------------------------')
          
      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','volume','close_time','quote_av','trades','tb_base_av','tb_quote_av','ema'))
          params = (
              ('btc_id', -1),
              ('btc_hour', -1),
              ('volume', -1),
              ('close_time', -1),
              ('quote_av', -1),
              ('trades', -1),
              ('tb_base_av', -1),
              ('tb_quote_av', -1),
              ('ema', -1)
              )
      
      main()
      
      posted in General Code/Help
      K
      krypterro
    • Importing Pandas Data in Minute Intervals

      I am unable to get my strategy to recognize my minute data, although hourly works fine. It seems I need to switch the cerebro default from daily to minutes, but the code out found here to do that...

      cerebro.adddata(data, timeframe=bt.Timeframe.Minutes, compression=1)
      

      throws this error:

      Traceback (most recent call last):
       File "autotest2.py", line 196, in <module>
         main()
       File "autotest2.py", line 54, in main
         x = threading.Thread(target=runner(db1, df, interval), args=(index,))
       File "autotest2.py", line 103, in runner
         cerebro.adddata(data, timeframe=bt.Timeframe.Minutes, compression=1)
      AttributeError: module 'backtrader' has no attribute 'Timeframe'
      

      Has something changed in the current version? How do I utilize Pandas data in minutes properly so that cerebro recognizes and processes them correctly?

      posted in General Code/Help timeframe interval klines ticks
      K
      krypterro
    • RE: Extending Pandas Columns

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

      posted in General Code/Help
      K
      krypterro
    • 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()
      
      
      posted in General Code/Help pandas dataframes strategy
      K
      krypterro