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/

    Confused with pnlcomm and pnl

    General Code/Help
    2
    7
    1474
    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.
    • A
      AliAskari last edited by

      I have just started working with backtrader and tried a simple strategy but I'm getting very weird results.

      I'm using the addwriter method to save the trade results to a csv file:

      cerebro.addwriter(bt.WriterFile, csv=True, out="trade_history.csv")
      

      and I'm also setting the commission to zero.

      This is part of the csv results

      0_1525004691472_eff42c45-5dc5-4373-bdd9-3061c6470795-image.png

      at the 85th iteration (don't know if it's the right term, correct me please) I buy ~500$ worth of BTC at a price of ~13400$, which means I have ~0.037 BTC which is then sold at ~13760. My calculations are telling me that after selling my cash balance should be more than what I had before entering the trade because the trade is clearly profitable but it is not the case and I'm in a 3$ loss! Could someone please explain what is going on?

      I have attached my script

      import backtrader as bt
      import backtrader.feeds as btfeed
      import numpy as np
      
      
      class custom_csv(btfeed.GenericCSVData):
          params = (
              ('datetime', 0),
              ('time', -1),
              ('open', 1),
              ('high', 2),
              ('low', 3),
              ('close', 4),
              ('volume', 5),
              ('openinterest', -1),
              ('timeframe', bt.TimeFrame.Minutes),
              ('compression', 1),
          )
      
      from datetime import datetime
      
      class ma_strat(bt.Strategy):
          params = (
              ('fast_ma', 12),
              ('slow_ma', 60),
          )
      
          def __init__(self):
              self.dataclose = self.datas[0].close
              self.volume = self.datas[0].volume
              self.latest_order = None
              self.first_run = True
              self.order_size = 0
              self.trade_number = 0
      
              self.fast_ma = bt.indicators.SimpleMovingAverage(self.datas[0], period=self.params.fast_ma)
              self.slow_ma = bt.indicators.SimpleMovingAverage(self.datas[0], period=self.params.slow_ma)
      
              self.fast_ma.csv = True
              self.slow_ma.csv = True
      
          def notify_order(self, order):
      
              if order.isbuy():
                  order_type = 'Buy'
              else:
                  order_type = 'Sell'
      
              if order.status == order.Completed:
                  self.latest_order = None
                  return
              elif order.status == order.Canceled:
                  print(order_type + ' order canceled')
                  self.latest_order = None
                  return
              elif order.status == order.Margin:
                  current_cash_balance = self.broker.get_cash()
                  print(order_type + ' order cancled due to margin Error')
                  print('Current balance: ' + str(current_cash_balance))
                  # Write down: no pending order
                  self.latest_order = None
                  return
              elif order.status == order.Rejected:
                  print(order_type + ' order rejected')
                  # Write down: no pending order
                  self.latest_order = None
                  return
      
      
      
          def notify_trade(self, trade):
      
              if trade.isopen:
                  self.trade_number += 1
                  print('Opening trade #%02d with size %.4f commision %.4f value %.2f price %.2f' %
                           (self.trade_number, trade.size, trade.commission, trade.value, trade.price))
              elif trade.isclosed:
                  print('Closing trade #%02d gross %.2f, net %.2f' %
                           (self.trade_number, trade.pnl, trade.pnlcomm))
      
          def next(self):
      
              # Check if an order is pending ... if yes, we cannot send a 2nd one
              if self.latest_order is not None:
                  return
      
              if not self.position:
                  if self.fast_ma[0] > self.slow_ma[0]:
                          current_cash_balance = self.broker.get_cash()
                          self.order_size = 0.5 * current_cash_balance / float(self.dataclose[0])
                          # place the buy order
                          self.latest_order = self.buy(size=self.order_size)
      
              else:
                  if self.fast_ma[0] < self.slow_ma[0]:
                          self.latest_order = self.sell(size=self.order_size)
      
      
      if __name__ == '__main__':
      
          # Create a cerebro entity
          cerebro = bt.Cerebro()
          cerebro.addwriter(bt.WriterFile, csv=True, out="trade_history.csv")
      
          csv_data_file =  'bitfinex_btc_price.csv'
          bitfinex_minut_data = custom_csv(dataname=csv_data_file)
      
          # Add the Data Feed to Cerebro
          unknown = cerebro.adddata(bitfinex_minut_data)
      
          cerebro.addstrategy(ma_strat)
      
          cerebro.broker.setcash(1000.0)
      
          print('Starting Portfolio Value: %.2f USD$' % cerebro.broker.getvalue())
      
          cerebro.run()
      
          print('Final Portfolio Value: %.2f USD$' % cerebro.broker.getvalue())
      1 Reply Last reply Reply Quote 0
      • B
        backtrader administrators last edited by

        Anyone would probably need some extra info.

        You have the data and that's not seen in your post. Seeing the data points (and around) at the buy/sell points could help.

        @aliaskari said in Confused with pnlcomm and pnl:

        def notify_trade(self, trade):

            if trade.isopen:
                self.trade_number += 1
                print('Opening trade #%02d with size %.4f commision %.4f value %.2f price %.2f' %
                         (self.trade_number, trade.size, trade.commission, trade.value, trade.price))
            elif trade.isclosed:
                print('Closing trade #%02d gross %.2f, net %.2f' %
                         (self.trade_number, trade.pnl, trade.pnlcomm))
        

        You also have this logging code, but the output of it (which for sure could help) is also not shown (since you have single orders, it would seem even better to log out during notify_order when the order is Completed)

        The output of the writer omits the most important parts:

        • datetime timestamp
        • prices in the data stream

        The only thing the screenshot shows is that the value decreases from the very first moment after your buy operation. Even if you believe that a screenshot may be more helpful than the raw data: it isn't. Put at least the header of the writer output and the lines at (and around) your sell/buy points and the

        1 Reply Last reply Reply Quote 0
        • A
          AliAskari last edited by AliAskari

          Thanks for your time @backtrader. I was not sure how to add csv file as there are a lot of columns. I have attached both the entire csv file and also a snapshot from the data around this particular trade.

          0_1525189002436_71adc4ea-e066-47a7-87da-550e93f2f849-image.png

          EDIT: edited to add the link to csv data file: https://www.dropbox.com/s/cpdbzf839kpx5mb/bitfinex_btc_price.csv?dl=0

          You also have this logging code, but the output of it (which for sure could help) is also not shown

          I modified the script to print more info and also used notify_order as suggested. Here is the output:

          Starting Portfolio Value: 1000.00 USD$
          Open trade #01 with size 0.0367 value 500.5144 price 13621.0000
          Close trade #01 with size -0.0367 value 500.5144 price 13569.0000
          Open trade #02 with size 0.0366 value 499.0812 price 13631.0000
          Close trade #02 with size -0.0366 value 499.0812 price 13548.0000
          

          I just realized that the price mentioned in the log does not match the buy/sell price in the csv file. The info pasted in the screenshot is from trade#2.

          i.e.

                      notify_order print log        csv 
          
          open        13621.0000              13400.9250
          close       13569.0000              13760.3550
          

          The only thing the screenshot shows is that the value decreases from the very first moment after your buy operation.

          I am not sure why you say the price decreases? By looking at the csv and the buy/sell columns it appears to me that the buy price is lower than the sell price.

          I am under the impression that those columns reflect the price at which the trade was completed. But apparently it is not the case.

          What am I missing here?

          Thanks!

          1 Reply Last reply Reply Quote 0
          • B
            backtrader administrators last edited by

            It is clear now why you are losing.

            Screenshots even if sometimes visually helpful don't really help. The header of the output csv with the offending lines (and a couple of the surrounding ones) allow to see what Buy and Sell are. But let me speculate:

            • They happen to be the output values of the BuySellObserver, the one plotting green/red triangles to mark your operations on the chart.

              Those triangles are plotted above and below your entry point to give you a visual indication of when the operation has happened (if the values were at the price, they would be mostly not visible most of the times, given they would be in between several price bars)

              And being above and below, the points you see are the observer points and not the actual operations (which are available to you via the notified orders)

            1 Reply Last reply Reply Quote 1
            • A
              AliAskari last edited by

              I see. Makes sense. Thanks!

              So if I want to capture the real buy/sell prices, along with other metrics, I'm better off writing my own logger and dump the log into a csv file.

              B 1 Reply Last reply Reply Quote 0
              • B
                backtrader administrators @AliAskari last edited by backtrader

                You can do it in different ways:

                • Pass stdstats=False to cerebro.run() (or to cerebro = bt.Cerebro()) and add your own observers. In this case you add a BuySellObserver with barplot=False, which will then use the exact buy and sell prices.

                  See:

                  • Observers and Statistics
                  • Observer Reference

                If you simply want to capture the prices and avoid them being plotted, you can also pass plot=False (or subclass the observer and set that as a default in plotinfo)

                • You can do it intercepting the notifications in notify_order and add them to any structure you like

                There would be other ways, but I would call them dirty (the fact is that you can do anything in Python, but it is usually better to play by the rules of the library/framework/pattern you are using)

                1 Reply Last reply Reply Quote 2
                • A
                  AliAskari last edited by

                  Thanks for your time, it's now working as expected.

                  1 Reply Last reply Reply Quote 0
                  • 1 / 1
                  • First post
                    Last post
                  Copyright © 2016, 2017, 2018 NodeBB Forums | Contributors
                  $(document).ready(function () { app.coldLoad(); }); }