Navigation

    Backtrader Community

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

    dorien

    @dorien

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

    dorien Unfollow Follow

    Latest posts made by dorien

    • RE: Fractional order sizes not floating point

      Update. I wrote this sizer that will do fractional position sizing for crypto and that will reverse buy / sell orders. No pyramiding included atm, should not be too hard to add though:

      class Dorien_sizer(bt.Sizer):
          params = (('prop', 0.95),)
          
          def _getsizing(self, comminfo, cash, data, isbuy):
              """Returns the proper sizing"""
              pos = self.broker.getposition(data).size
              
              if isbuy:    # Buying
                  if pos == 0: 
                      target = cash * self.params.prop 
                      price = data.close[0]
                      size = target / price  
                      return size
                      
                  elif pos > 0:
                      
                      # don't allow pyramiding for now
                      size = 0
                      if size * price > cash:
                          return 0    # Not enough money for this trade
                      else:
                          return size
                  
                  # short open
                  elif pos < 0:
                      target = self.broker.getvalue() * self.params.prop    # Ideal total value of the position
                      price = data.close[0]
                      size = target / price    # How many shares are needed to get target
                      size = size - pos
                      return size
      
              else:    # Selling
                  if pos > 0:
                      size = pos * 2
                      return size   # Clear the position
                  elif pos <= 0:
                      size = pos
                      return size
      
      posted in General Code/Help
      D
      dorien
    • RE: Fractional order sizes not floating point

      One step in the write direction. Found this custom sizer and figured out how to use it. Now order sizes are flowing point based on the prop percentage. However, it works long only.

      Surely it must be easy to adapt this to properly reverse the orders (double size) in case of a sell when a long is open.

      class Antoine_sizer(bt.Sizer):
          params = (('prop', 0.99),)
          
          def _getsizing(self, comminfo, cash, data, isbuy):
              """Returns the proper sizing"""
              
              if isbuy:    # Buying
                  target = self.broker.getvalue() * self.params.prop    # Ideal total value of the position
                  price = data.close[0]
                  size_net = target / price    # How many shares are needed to get target
                  pos = self.broker.getposition(data).size
                  size = size_net * self.params.prop
      
                  if size * price > cash:
                      return 0    # Not enough money for this trade
                  else:
                      return size
      
              else:    # Selling
                  return self.broker.getposition(data).size    # Clear the position
      

      Then add

      cerebro.addsizer(Antoine_sizer)
      

      and use
      self.order = self.buy()
      self.order = self.sell()

      to give orders.

      Issue: no shorting.

      posted in General Code/Help
      D
      dorien
    • Fractional order sizes not floating point

      I'm doing backtesting for a bitcoin strategy. As we al know, contrary to stock market, we can buy 1.5 BTC. In my strategy, I followed the steps in the official article on fractional order sizes, yet my output shows that the order size is rounded:

      I aim to buy with a position size of 99%, but it keeps rounding it. E.g. instead of 5.00 I would like to see 5.12 BTC etc.

      To summarize, I did add:

      class CommInfoFractional(bt.CommissionInfo):
          def getsize(self, price, cash):
              '''Returns fractional size for cash operation @price'''
              return self.p.leverage * (cash / price)
      

      Output:

      2019-11-02 BUY EXECUTED, 9231.40 of size  5.00
      2019-11-08 SELL CREATE 8773.730000
      2019-11-09 SELL EXECUTED, 8773.74 of size -16.00
      2019-11-10 BUY EXECUTED, 8809.18 of size  16.00
      2019-11-25 BUY EXECUTED, 6900.23 of size  1.00
      2020-02-25 SELL CREATE 9315.840000
      2020-02-26 SELL EXECUTED, 9316.48 of size -16.00
      2020-02-27 BUY EXECUTED, 8786.00 of size  15.00
      2020-03-01 BUY EXECUTED, 8523.61 of size  1.00
      2020-03-13 BUY EXECUTED, 4800.01 of size  2.00
      2020-04-07 SELL EXECUTED, 7329.90 of size -1.00
      2020-05-08 SELL EXECUTED, 9986.30 of size -1.00
      2020-05-10 SELL CREATE 8722.770000
      ...
      

      My full code. With csv file available here.

      import datetime
      import backtrader as bt
      import backtrader.feeds as btfeeds
      from backtrader.feeds import GenericCSVData
      import quantstats
      import pandas as pd
      import argparse
      import logging
      import sys
      
      class myGenericCSV(GenericCSVData):
          
          # Add a line to the inherited ones from the base class
          lines = ('buy','sell')
      
          # add the parameter to the parameters inherited from the base class
          params = (('buy', 10),('sell', 11),)
           
      class CommInfoFractional(bt.CommissionInfo):
          def getsize(self, price, cash):
              '''Returns fractional size for cash operation @price'''
              return self.p.leverage * (cash / price)
           
      class firstStrategy(bt.Strategy):
          params = dict(
              target=0.99,  # percentage of value to use
          )
      
          # Get cash and balance
          # New broker method that will let you get the cash and balance for
          # any wallet. It also means we can disable the getcash() and getvalue()
          # rest calls before and after next which slows things down.
      
          # NOTE: If you try to get the wallet balance from a wallet you have
          # never funded, a KeyError will be raised! Change LTC below as approriate
          # if self.live_data:
          #     cash, value = self.broker.get_wallet_balance('USDT')
          # else:
          #     # Avoid checking the balance during a backfill. Otherwise, it will
          #     # Slow things down.
          #     cash = 'NA'
          
          def log(self, txt, dt=None):
              dt = dt or self.datas[0].datetime.date(0)
              print(f'{dt.isoformat()} {txt}') #Print date and close
        
          def notify_order(self, order):
              if order.status in [order.Submitted, order.Accepted]:
              # An active Buy/Sell order has been submitted/accepted - 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(f'BUY EXECUTED, {order.executed.price:.2f} of size {order.executed.size: .2f}')
                  elif order.issell():
                      self.log(f'SELL EXECUTED, {order.executed.price:.2f} of size {order.executed.size: .2f}')
                  self.bar_executed = len(self)
                  # self.log(f'{order.executed.size: .2f}')
                      # {order.executed.size, order.executed.price,
                      # order.executed.value, order.executed.comm)
      
              elif order.status in [order.Canceled, order.Margin, order.Rejected]:
                  self.log('Order Canceled/Margin/Rejected')
      
              # Reset orders
              self.order = None
          
          def loginfo(self, txt, *args):
              out = [self.datetime.date().isoformat(), txt.format(*args)]
              logging.info(','.join(out))
              
              
          def notify_trade(self, trade):
              if trade.justopened:
                  self.loginfo('Trade Opened  - Size {} @Price {}',
                               trade.size, trade.price)
              elif trade.isclosed:
                  self.loginfo('Trade Closed  - Profit {}', trade.pnlcomm)
      
              else:  # trade updated
                  self.loginfo('Trade Updated - Size {} @Price {}',
                               trade.size, trade.price)
          
          def next(self):
              self.order_target_percent(target=self.p.target)
              
              if not self.position:
                  if self.data.buy == 1:
                      self.order = self.order_target_percent(target=self.p.target) 
                      # self.order = self.buy(data_min, size = 0.99, , price = self.target_exit_price[stock], exectype = bt.Order.Limit) 
                      self.log(f'BUY CREATE {self.data.close[0]:2f}')
              else:
                  if self.data.sell == 1:
                      self.order = self.order_target_percent(target=-self.p.target)
                      self.log(f'SELL CREATE {self.data.close[0]:2f}')
      
      
      def run(args=None):
      
      
          cerebro = bt.Cerebro()
      
          # use the fractional scheme:
          cerebro.broker.addcommissioninfo(CommInfoFractional())
          cerebro.addstrategy(firstStrategy)
      
          data = myGenericCSV(
              dataname='btc_data1d.csv',
              # timeframe=bt.TimeFrame.,
              # fromdate=datetime.datetime(2022, 2, 9),
              fromdate=datetime.datetime(2019, 11, 1),
              todate=datetime.datetime(2022, 7, 25),
              nullvalue=0.0,
              dtformat=('%Y-%m-%d %H:%M:%S'),
              # lines = ('Time','Open','High','Low','Close','Volume','ATR','CC','Top','Btm','Buy','Sell','ODR','Trend','WM','Band','last_pivot'
              #     ),
      
              datetime=0,
              high=2,
              low=3,
              open=1,
              close=4,
              buy=10,
              volume=5,
              sell=11,
              openinterest=-1,
          )
      
          cerebro.adddata(data)
          startcash = 100000
          cerebro.broker.setcash(startcash)
          cerebro.addanalyzer(bt.analyzers.PyFolio, _name='PyFolio')
          cerebro.broker.setcommission(commission=0.003)
      
          # cerebro.add_order_history(orders, notify=True)     
          results = cerebro.run()
          strat = results[0]
      
          portfolio_stats = strat.analyzers.getbyname('PyFolio')
          returns, positions, transactions, gross_lev = portfolio_stats.get_pf_items()
          returns.index = returns.index.tz_convert(None)
      
          cerebro.plot()
          quantstats.reports.html(returns, output='stats.html', title='Strat')
      
      if __name__ == '__main__':
          run()
      
      posted in General Code/Help
      D
      dorien
    • RE: Basic store template

      Following up on this. I am now using ccxt store for my live data feed. But I am wondering how to deal with broker, can I use/create a different broker from the original store? I want to be able to send webhooks to my own service.

      posted in General Code/Help
      D
      dorien
    • RE: Trading the equity curve

      @metroholographix Yes I would like to know this as well..

      posted in Indicators/Strategies/Analyzers
      D
      dorien
    • Basic store template

      Hi, I want to send custom webhooks upon buy sell and close orders. I understand I should create a store for this. Since my use case is very simple, is there a barebones store example I can use as a template?

      posted in General Code/Help
      D
      dorien