Navigation

    Backtrader Community

    • Register
    • 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/

    Simply access some values like trade.pnl

    General Code/Help
    4
    6
    357
    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.
    • J
      JAS2210 last edited by

      Hello everyone,
      I am following the official guide (first pages).

      I would like to store trade.pnl values in a list for example. And the for example I want to get the sum of all pnl of the positions.

      I cannot do it inside this function of the class because every time trade.pnl is different? Where I have to puy an empty list and append this values? In the class "class Name_of_strategy(bt.Strategy):" or outside of the class?

          def notify_trade(self, trade):
              if not trade.isclosed:
                  return
      
              self.log('OPERATION PROFIT, GROSS %.2f, NET %.2f' %
                       (trade.pnl, trade.pnlcomm))
      

      I am lacking the real function of cerebro.run I think...

      Thank you :)

      vladisld 1 Reply Last reply Reply Quote 0
      • vladisld
        vladisld @JAS2210 last edited by vladisld

        @jas2210 It could be placed inside a strategy class of cause. However it is even better to use analyzers for such type of statistics gathering.

        More docs here: https://www.backtrader.com/docu/analyzers/analyzers/

        J 1 Reply Last reply Reply Quote 1
        • J
          JAS2210 @vladisld last edited by

          @vladisld Yes I suspected it.
          I just to know how to move from the values results in the iteretion of the cerebro engine to for example a dataframe in pandas or an array in numpy.
          Basically from lines object to an array if I am not wrong.

          Thanks for your time.

          Tony Shacklock run-out 2 Replies Last reply Reply Quote 0
          • Tony Shacklock
            Tony Shacklock @JAS2210 last edited by

            @jas2210
            I'm learning Backtrader too, and noticed the Writer class, which may help you with this. (It is described in an article written after the documentation.) Write it Down

            It writes a csv file, which includes time series data. I'm using it to help indicator development and plotting, but the file also contains trades and p&l, etc.

            This is the Quickstart example from the docs with the Writer added (and a function to extract time series data from the csv file).

            '''
            from future import absolute_import, division, print_function, unicode_literals

            from datetime import datetime
            import pandas as pd
            import matplotlib.pyplot as plt
            import backtrader as bt

            class TestStrategy(bt.Strategy):
            params = (('maperiod', 15),
            ('printlog', False),
            ('tradelog', {})
            )

            def log(self, txt, dt=None, doprint=False):
            
                if self.params.printlog or doprint:
                    dt = dt or self.datas[0].datetime.date(0)
                    print(f'{dt.isoformat():s}: {txt}')
            
            
            def __init__(self):
            
                # Keep a reference to the 'close' line in the data[0] dataseries.
                self.data_close = self.datas[0].close
            
                # Track pending orders.
                self.order = None
                self.buyprice = None
                self.buycomm = None
            
                # SMA indicator.
                self.sma = bt.indicators.SimpleMovingAverage(self.datas[0], period=self.params.maperiod)
                self.sma.csv = True
            
                # Inidcators to demostrate plotting.
                # bt.indicators.ExponentialMovingAverage(self.datas[0], period=25)
                # bt.indicators.WeightedMovingAverage(self.datas[0], period=25, subplot=True)
                # bt.indicators.StochasticSlow(self.datas[0])
                # bt.indicators.MACDHisto(self.datas[0])
                # rsi = bt.indicators.RSI(self.datas[0])
                # bt.indicators.SmoothedMovingAverage(rsi, period=10)
                # bt.indicators.ATR(self.datas[0], plot=False)
            
                self.n_trades = 0
            
            
            def notify_order(self, order):
            
                if order.status in [order.Submitted, order.Accepted]:
                    # The order has been submitted to or accepted by the broker. Do nothing.
                    return
            
                # Check if the order has been completed.
                if order.status in [order.Completed]:
                    if order.isbuy():
                        self.log(f'BUY: Fill price {order.executed.price:.2f}'
                                 + f', Cost {order.executed.value:.2f}'
                                 + f', Comm {order.executed.comm:.2f}')
                        self.buyprice = order.executed.price
                        self.buycomm = order.executed.comm
                    elif order.issell():
                        self.log(f'SELL: Fill price {order.executed.price:.2f}'
                                 + f', Cost {order.executed.value:.2f}'
                                 + f', Comm {order.executed.comm:.2f}')
                        self.buyprice = order.executed.price
                        self.buycomm = 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  # No pending order.
            
            
            def notify_trade(self, trade):
                if not trade.isclosed:
                    return
            
                self.log(f'Operation Profit: Gross {trade.pnl:.2f}, Net {trade.pnlcomm:.2f}',
                         doprint=True)
            
                self.params.tradelog[self.n_trades] = (trade.pnl,trade.pnlcomm )
                self.n_trades += 1
            
            
            def next(self):
                # Simply log the closing price of the series from the reference.
                self.log(f'Close {self.data_close[0]:.2f}')
            
                # If there is a pending order...
                if self.order:
                    return  # Do nothing.
            
                # If no existing position...
                if not self.position:
                    # Check for a buy signal.
                    if self.data_close[0] > self.sma[0]:
                        self.log(f'Buy at {self.data_close[0]:.2f}')
                        self.order = self.buy()
                # Else there is an existing position.
                else:
                    # Check for a sell condition.
                    if self.data_close[0] < self.sma[0]:
                        self.log(f'Sell at {self.data_close[0]:.2f}')
                        self.order = self.sell()
            
            
            def stop(self):
                self.log(f'(MA Period {self.params.maperiod:2d}) Ending value {self.broker.getvalue():.2f}',
                         doprint=True)
            

            def get_timeseries_data(writer_filepath):

            # From the Backtrader documentation: the time series block is preceded and
            #   followed by a row of '====='.
            
            raw_df = pd.read_csv(writer_filepath, skiprows=1)
            
            # Keep the time series rows.
            first_drop_row = (raw_df.loc[raw_df['Id'].str.find("===") > -1]
                                                                  .index.values[0])
            data_df = raw_df.iloc[0 : first_drop_row]
            
            # Make datetime column the index.
            data_df.set_index('datetime',inplace=True)
            data_df.index = data_df.index.astype('datetime64[ns]')
            
            return data_df
            

            if name == 'main':

            # Create the back test engine.
            cerebro = bt.Cerebro()
            
            # Dictionary to store trades.
            trade_log = {}
            
            # Add the strategy.
            cerebro.addstrategy(TestStrategy, maperiod=20, printlog=False,
                                tradelog=trade_log)
            
            # Alternatively, optimise.
            # strats = cerebro.optstrategy(TestStrategy, maperiod=range(10, 31))
            
            # Create a data feed.
            data_path = ('C:/Users/tony/Documents/SwingTree/Trading/Automation/Python/'
                                                    + 'BackTrader Sample Data/datas/orcl-1995-2014.txt')
            data = bt.feeds.YahooFinanceCSVData(dataname=data_path,
                                                fromdate=datetime(2000, 1, 1),
                                                todate=datetime(2000, 12, 31),
                                                reverse=False)
            
            # Add the data feed to Cerebro.
            cerebro.adddata(data)
            
            
            cerebro.broker.setcash(1000.0)
            cerebro.broker.setcommission(commission=0.001)
            cerebro.addsizer(bt.sizers.FixedSize, stake=10)
            
            # Add Analyzers.
            cerebro.addanalyzer(bt.analyzers.Transactions, _name='Transactions')
            cerebro.addanalyzer(bt.analyzers.GrossLeverage, _name='GrossLeverage')
            
            # Add the Writer.
            cerebro.addwriter(bt.WriterFile, csv=True, out='Test.csv')
            
            # Run the backtest.
            print(f'Starting porfolio value: {cerebro.broker.getvalue():.2f}')
            
            strat_results = cerebro.run()
            # cerebro.run(maxcpus=1)  # For optimisation.
            
            print(f'Final porfolio value: {cerebro.broker.getvalue():.2f}')
            
            # Plot the results.
            data_df = get_timeseries_data('Test.csv')
            plt.plot(data_df[['close', 'sma']])
            

            '''

            1 Reply Last reply Reply Quote 0
            • run-out
              run-out @JAS2210 last edited by

              @jas2210 Definitely use analyzers. You can collect a wide array of data into dictionaries/lists/tuples, etc.

              Then at the end of your run, you can extract the analyzer, and then turn it into a dataframe.

              Here's an example of an analyzer that collects closed trade information similar to what you are after:

              class TradeClosed(bt.analyzers.Analyzer):
                  """
                  Analyzer returning closed trade information.
                  """
              
                  def start(self):
                      super(TradeClosed, self).start()
              
                  def create_analysis(self):
                      self.rets = {}
                      self.vals = tuple()
              
                  def notify_trade(self, trade):
                      """Receives trade notifications before each next cycle"""
                      if trade.isclosed:
                          self.vals = (
                              self.strategy.datetime.datetime(),
                              trade.data._name,
                              round(trade.pnl, 2),
                              round(trade.pnlcomm, 2),
                              trade.commission,
                              (trade.dtclose - trade.dtopen),
                          )
                          self.rets[trade.ref] = self.vals
              
                  def get_analysis(self):
                      return self.rets
              

              You would implement when setting up cerebro using:

              self.cerebro.addanalyzer(TradeClosed, _name="trade_closed")
              

              You would then use the following line to get your data after the backtest runs:

              trade_dict = results[0].analyzers.getbyname("trade_closed").get_analysis()
              

              Finally, turn this into a dataframe:

              columns_df = [
                      "Date Closed",
                      "Ticker",
                      "PnL",
                      "PnL Comm",
                      "Commission",
                      "Days Open",
                  ]
              
              df = pd.DataFrame(trade_dict)
              df = df.T
              df.columns = columns_df
              

              RunBacktest.com

              Tony Shacklock 1 Reply Last reply Reply Quote 4
              • Tony Shacklock
                Tony Shacklock @run-out last edited by

                @run-out
                Thanks. That's much simpler.

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