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/

    order_target_percent is not working as expected. Please read further; complete code, inputs and outputs are provided

    General Code/Help
    1
    2
    286
    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.
    • Anant Parihar
      Anant Parihar last edited by

      Hi,

      I am running the Longshort strategy (provided as an example here: https://www.backtrader.com/blog/posts/2015-09-14-write-it-down/write-it-down/) on BTC USD data (using fractional sizing as explained here: https://www.backtrader.com/blog/posts/2019-08-29-fractional-sizes/fractional-sizes/).

      I'm using order_target_percent(0.5) in my code. I expect that for each buy operation, 50.0% of my cash should get used up. This does happen most of the time. Except when there is a buy immediately after a sell. The amount of cash that is used to make such buy operations is around 50%, but not exactly 50%.

      Check the buy operations on Row Id = 20 and Row Id = 46 in the linked output to see what I'm talking about. For ex, in row 20: I expect my cash value to go from 9908.73 to 4954.36 (which is 50%). Instead it goes from 9908.73 to 4947.38 (which is 49.92%). Buy operations on row 72 and 91 don't have this discrepancy.

      I suspect this could be due to rounding, but not sure. Let me know if any more info is required.

      Input data: https://drive.google.com/file/d/1rPEfRnf1z4DknzhQGSZuL6esK7CWdGGa/view?usp=sharing
      output: https://drive.google.com/file/d/1wjFaNYKJSC_Tez8yCneZlSmFaIxzb-Rd/view?usp=sharing

      Code:

      from __future__ import (absolute_import, division, print_function,
                              unicode_literals)
      
      import pandas as pd
      import numpy as np
      import datetime
      import os
      import seaborn as sns
      import sys
      import copy
      import argparse
      import logging
      
      
      import backtrader as bt
      import backtrader.feeds as btfeeds
      import backtrader.indicators as btind
      from backtrader.analyzers import SQN
      
      
      class CommInfoFractional(bt.CommissionInfo):
          def getsize(self, price, cash):
              '''Returns fractional size for cash operation @price'''
              return self.p.leverage * (cash / price)
      
      
      class act_buy_sell(bt.observer.Observer):
      
      	' For actual buy and sell prices'
          
          alias = ('Act_buy_sell',)
          lines = ('act_buy', 'act_sell', 'order_size')
      
          plotinfo = dict(plot=False, subplot=False)
      
          def next(self):
              for order in self._owner._orderspending:
                  
                  if order.data is not self.data:
                      continue
      
                  if order.isbuy():
                      self.lines.act_buy[0] = order.executed.price
                      self.lines.order_size[0] = order.executed.size
      
                  elif order.issell():
                      self.lines.act_sell[0] = order.executed.price
                      self.lines.order_size[0] = order.executed.size          
      
      
      
      class LongShortStrategy(bt.Strategy):
          '''This strategy buys/sells upong the close price crossing
          upwards/downwards a Simple Moving Average.
      
          It can be a long-only strategy by setting the param "onlylong" to True
          '''
          params = dict(
              period=15,
              stake=1,
              printout=False,
              onlylong=False,
              csvcross=False,
          )
      
          def start(self):
              pass
      
          def stop(self):
              pass
      
          def log(self, txt, dt=None):
              if self.p.printout:
                  dt = dt or self.data.datetime[0]
                  dt = bt.num2date(dt)
                  print('%s, %s' % (dt.isoformat(), txt))
      
          def __init__(self):
              # To control operation entries
              self.orderid = None
      
              # Create SMA on 2nd data
              sma = btind.MovAv.SMA(self.data, period=self.p.period)
              # Create a CrossOver Signal from close an moving average
              self.signal = btind.CrossOver(self.data.close, sma)
              self.signal.csv = self.p.csvcross
      
          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('CLOSE SHORT , %.2f' % self.data.close[0])
                      self.close()
      
                  self.log('BUY CREATE , %.2f' % self.data.close[0])
                  self.order_target_percent(target=self.p.stake)
      
              elif self.signal < 0.0:
                  if self.position:
                      self.log('CLOSE LONG , %.2f' % self.data.close[0])
                      self.close()
      
                  if not self.p.onlylong:
                      self.log('SELL CREATE , %.2f' % self.data.close[0])
                      self.order_target_percent(target=-self.p.stake)
      
          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 = 'BUY COMPLETE, %.2f' % order.executed.price
                      self.log(buytxt, order.executed.dt)
                  else:
                      selltxt = 'SELL COMPLETE, %.2f' % order.executed.price
                      self.log(selltxt, order.executed.dt)
      
              elif order.status in [order.Expired, order.Canceled, order.Margin]:
                  self.log('%s ,' % order.Status[order.status])
                  pass  # Simply log
      
              # Allow new orders
              self.orderid = None
      
          def notify_trade(self, trade):
              if trade.isclosed:
                  self.log('TRADE PROFIT, GROSS %.2f, NET %.2f' %
                           (trade.pnl, trade.pnlcomm))
      
              elif trade.justopened:
                  self.log('TRADE OPENED, SIZE %2d' % trade.size)
      
      
      def runstrategy(period = 15
                      ,onlylong = True
                      ,csvcross = True
                      ,stake = 1
                      ,comm = 0
                      ,plot = True
                      ,fractional = True):
      
          # Create a cerebro
          cerebro = bt.Cerebro()
      
          # Create the 1st data
          data = btfeeds.GenericCSVData(
          dataname=data #refer to attached data
          dtformat=('%Y-%m-%d'),
          fromdate=datetime.datetime(2017, 1, 1),
          todate=datetime.datetime(2017, 4, 9),
          openinterest=-1
          )
      
          # Add the 1st data to cerebro
          cerebro.adddata(data)
          cerebro.addobserver(act_buy_sell)
      
          if fractional:
              cerebro.broker.addcommissioninfo(CommInfoFractional())
      
          else:        
              # Add the commission - only stocks like a for each operation
              cerebro.broker.setcommission(commission=comm)
      
          # Add the strategy
          cerebro.addstrategy(LongShortStrategy,
                              period=period,
                              onlylong=onlylong,
                              csvcross=csvcross,
                              stake=stake)
      
          # Add the commission - only stocks like a for each operation
          cerebro.broker.setcash(10000)
      
          # cerebro.addanalyzer(SQN)
      
          cerebro.addwriter(bt.WriterFile, csv=True, rounding=2)
      
          # And run it
          cerebro.run()
      
          # Plot if requested
          if plot:
              cerebro.plot(volume=True, zdown=False)
      
      
      runstrategy(plot = False, stake = 0.5)
      1 Reply Last reply Reply Quote 0
      • Anant Parihar
        Anant Parihar last edited by

        @backtrader @ab_trader @run-out please have a look when you get the chance as this could potentially be a bug. Thanks!

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