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/

    Close position and selling short on the same bar not working correctly

    General Code/Help
    2
    3
    163
    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 Former User last edited by

      I'm testing a strategy that buys a correct number of shares, and closes the position fine with the same number of shares but when it sells short it does it with very few shares as if there was not enough money left. I understand it's because as the two operations are done at the same point the broker hasn't yet updated the amount of cash from closing the previous operation. How can I fix this?

      2020-10-14T23:59:59.999989, Initial portfolio value of 1700.00
      1991-04-12T23:59:59.999989, BUY 56 shares at 28.87
      1991-04-12T23:59:59.999989, BUY COMPLETED. Size: 56, Price: 28.87, Commission: 0.72
      1991-06-19T23:59:59.999989, CLOSE Long Position of 56 shares at 25.54
      1991-06-19T23:59:59.999989, SELL 3 shares at 25.54
      1991-06-19T23:59:59.999989, SELL COMPLETED. Size: 56, Price: 25.54, Commission: 0.28
      1991-06-19T23:59:59.999989, SELL COMPLETED. Size: 3, Price: 25.54, Commission: 0.49
      1991-06-20T23:59:59.999989, TRADE COMPLETED, Portfolio: 1512.03, Gross: -186.48, Net: -187.48
      

      runstrat.py

      from __future__ import (absolute_import, division, print_function,
                              unicode_literals)
      
      import sys
      import os.path
      import datetime
      import argparse
      
      import backtrader as bt
      
      from strategies.strategies import *
      from sizers import MaxRiskSizer
      from commissions import DegiroCommission
      
      
      def runstrategy():
          cerebro = bt.Cerebro()
          comminfo = DegiroCommission()
          cerebro.addstrategy(EmaCrossLongShort, fast=13, slow=49)
          cerebro.broker.set_cash(1700)
          cerebro.broker.set_coc(True)
      
          cerebro.addsizer(MaxRiskSizer)
          cerebro.broker.addcommissioninfo(comminfo)
      
          modpath = os.path.dirname(os.path.abspath(sys.argv[0]))
          datapath = os.path.join(modpath, 'data/CPE.csv')
          data = bt.feeds.YahooFinanceCSVData(
              dataname=datapath,
              # Do not pass values before this date
              #fromdate=datetime.datetime(1990, 10, 1),
              # Do not pass values after this date
              #todate=datetime.datetime(2020, 10, 15),
              reverse=False
          )
          cerebro.adddata(data)
      
          start_portfolio_value = cerebro.broker.getvalue()
          cerebro.run()
          end_portfolio_value = cerebro.broker.getvalue()
          pnl = end_portfolio_value - start_portfolio_value
      
          print(f'Starting Portfolio Value: {start_portfolio_value:.2f}')
          print(f'Final Portfolio Value: {end_portfolio_value:.2f}')
          print(f'PnL: {pnl:.2f}')
      
          cerebro.plot()
      
      if __name__ == '__main__':
          runstrategy()
      

      strategies.py

      import backtrader as bt
      import backtrader.indicators as btind
      
      class EmaCrossLongShort(bt.Strategy):
          '''This strategy buys/sells upong the close price crossing
          upwards/downwards an Exponential Moving Average.
          It can be a long-only strategy by setting the param "longonly" to True
          '''
          params = dict(
              fast=13,
              slow=48,
              printout=True,
              longonly=False,
          )
      
          def log(self, txt, dt=None):
              if self.p.printout:
                  dt = dt or self.data.datetime[0]
                  dt = bt.num2date(dt)
                  print(f'{dt.isoformat()}, {txt}')
      
          def __init__(self):
              self.orderid = None  # to control operation entries
      
              fast_ema, slow_ema = btind.MovAv.EMA(period=self.p.fast), btind.MovAv.EMA(period=self.p.slow)
              self.signal = btind.CrossOver(fast_ema, slow_ema)
              self.log(f'Initial portfolio value of {self.broker.get_value():.2f}')
      
          def start(self):
              pass
      
          def next(self):
              if self.orderid:
                  return  # if an order is active, no new orders are allowed
      
              if self.signal > 0.0:  # cross upwards
                  if self.position:
                      self.log(f'COVER Short Position of {abs(self.position.size)} shares '
                               f'at {self.data.close[0]:.2f}')
                      self.close()
      
                  self.log(f'BUY {self.getsizing()} shares at {self.data.close[0]}')
                  self.buy()
      
              elif self.signal < 0.0:
                  if self.position:
                      self.log(f'CLOSE Long Position of {self.position.size} shares '
                               f'at {self.data.close[0]:.2f}')
                      self.close()
      
                  if not self.p.longonly:
                      self.log(f'SELL {abs(self.getsizing())} shares at '
                               f'{self.data.close[0]}')
                      self.sell()
      
          def notify_order(self, order):
              if order.status in [bt.Order.Submitted, bt.Order.Accepted]:
                  return  # Await further notifications
      
              if order.status == order.Completed:
                  if order.isbuy():
                      buytxt = f'BUY COMPLETED. ' \
                               f'Size: {order.executed.size}, ' \
                               f'Price: {order.executed.price:.2f}, ' \
                               f'Commission: {order.executed.comm:.2f}'
                      self.log(buytxt, order.executed.dt)
                  else:
                      selltxt = 'SELL COMPLETED. ' \
                               f'Size: {abs(order.executed.size)}, ' \
                               f'Price: {order.executed.price:.2f}, ' \
                               f'Commission: {order.executed.comm:.2f}'
                      self.log(selltxt, order.executed.dt)
      
              elif order.status in [order.Expired, order.Canceled, order.Margin]:
                  self.log(f'{order.Status[order.status]}')
                  pass  # Simply log
      
              # Allow new orders
              self.orderid = None
      
          def notify_trade(self, trade):
              if trade.isclosed:
                  self.log(f'TRADE COMPLETED, '
                           f'Portfolio: {self.broker.get_value():.2f}, '
                           f'Gross: {trade.pnl:.2f}, '
                           f'Net: {trade.pnlcomm:.2f}')
      
              elif trade.justopened:
                  #self.log('TRADE OPENED, SIZE %2d' % trade.size)
                  pass
      

      sizers.py

      import backtrader as bt
      
      
      class MaxRiskSizer(bt.Sizer):
          params = (('risk', 0.95),)
      
          def __init__(self):
              if self.p.risk > 1 or self.p.risk < 0:
                  raise ValueError('The risk parameter is a percentage which must be'
                      'entered as a float. e.g. 0.5')
      
          def _getsizing(self, comminfo, cash, data, isbuy):
              #return comminfo.getsize(data.close[0], cash * self.p.risk)
              return round(cash * self.p.risk / data.close[0])
      

      commissions.py

      import backtrader as bt
      
      class DegiroCommission(bt.CommInfoBase):
          params = (('per_share', 0.004), ('flat', 0.5),)
      
          def _getcommission(self, size, price, pseudoexec):
              return self.p.flat + size * self.p.per_share
      
      hghhgghdf dfdf 1 Reply Last reply Reply Quote 0
      • hghhgghdf dfdf
        hghhgghdf dfdf @Guest last edited by

        @quake004

        def _getsizing(self, comminfo, cash, data, isbuy):
        
                return round(self.broker.get_value() * self.p.risk / data.close[0])
        
        1 Reply Last reply Reply Quote 0
        • ?
          A Former User last edited by

          That's right thanks. I've also seen the commission for long and short positions where different. I had to change commissions.py:

          return self.p.flat + abs(size) * self.p.per_share
          
          1 Reply Last reply Reply Quote 0
          • 1 / 1
          • First post
            Last post
          Copyright © 2016, 2017, 2018, 2019, 2020, 2021 NodeBB Forums | Contributors