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/

    Setting a Stop loss and target in a strategy (missing piece in a sample)

    Indicators/Strategies/Analyzers
    5
    12
    5268
    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.
    • U
      Usct last edited by Usct

      Hi,
      I want to test a simple strategy with

      1. Signal Generation ( many examples available)
      2. Set Buy/Sell order (examples available)
      3. Set Stop Loss and Profit Target (Couldn't find this piece, closest was https://github.com/mementum/backtrader/blob/master/samples/order_target/order_target.py
        but the stop loss piece is missing, please advise.

      Stop Loss could be : Based on ATR OR Based on fixed value/percent of price etc.
      Profit Target could be : Based on Fixed value/percent, price of an instrument etc.

      For example:
      The Buy Signal is generated at price of $100:

      1. Enter the trade by buying at $100 or better, set a stop loss of $90 and Target of $120
      2. Exit if Stop Loss is hit, i.e. price moves below $90 OR Exit if Target is achieved, i.e. price moves above $120

      The Sell Signal is generated at $100

      1. Enter the trade by Selling at $100 or better, set a stop loss of $110 and Target of $80
      2. Exit if Stop Loss is hit, i.e. price moves above $1100 OR Exit if Target is achieved, i.e. price moves below $80

      Thanks

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

        Such a sample already exists. It is ATR-Based

        See: https://www.backtrader.com/blog/posts/2016-07-30-macd-settings/macd-settings.html

        (Of course, it is included in the sources)

        B 1 Reply Last reply Reply Quote 0
        • U
          Usct last edited by

          Thanks a lot!
          Somehow it was not showing in search results and the code on git repo didn't have init part where all the magic is happening

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

            Checked the repository and the strategy in the aforementioned sample has a complete __init__. Should there be a problem, don't hesitate to share the details.

            1 Reply Last reply Reply Quote 0
            • U
              Usct last edited by

              Ok I got it,
              This source has complete code (and I couldn't find this earlier)
              https://github.com/mementum/backtrader/blob/master/samples/macd-settings/macd-settings.py

              While this one which has same comment in strategy def, doesn't have init part
              https://github.com/mementum/backtrader/blob/master/samples/order_target/order_target.py
              may be this is meant for something else
              Thanks for such a prompt and highly useful response

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

                @Usct

                order_target_percent is not aimed at stop-loss. The order_target_xxx family of methods allow to size an order using for example expected value or percentage of value but don't set stop losses.

                There is no need for __init__ in that sample. The logic for the orders is 100% in next and is explained here:

                • https://www.backtrader.com/blog/posts/2016-09-02-target-orders/target-orders.html
                I 1 Reply Last reply Reply Quote 0
                • I
                  ivy @backtrader last edited by

                  @backtrader Is there any way that I can add stop loss to the rebalance strategy? my reblalance strategy is to rebalance portfolio every week based on the post: https://medium.com/@danjrod/rebalancing-with-the-conservative-formula-44a4d5f15e4d, this strategy uses the order_target_percent, but I found that the drawdown is very high, so I want to know how can I set a stop loss in the rebalance strategy.
                  Thanks

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

                    There are trailing stops available. Have you looked at those?

                    RunBacktest.com

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

                      @run-out thanks, I have read this, but I still don't konw how to put it into the strategy, I am beginner of backtrader and python, here is the code

                      from __future__ import (absolute_import, division, print_function,
                                              unicode_literals)
                      import argparse
                      import datetime
                      
                      from pandas import Series, DataFrame
                      import numpy as np
                      import pandas as pd
                      
                      import matplotlib.pyplot as plt
                      %matplotlib inline
                      import backtrader as bt
                      import backtrader.indicators as btind
                      import backtrader.analyzers as btanal
                      import datetime as dt
                      from pandas import Series, DataFrame
                      import pyfolio as pf
                      
                      class St(bt.Strategy):
                          params = dict(
                              selcperc=0.4,  # percentage of stocks to select from the universe
                              rperiod=7,  # period for the returns calculation, default 1 period
                              vperiod=30,  # lookback period for volatility - default 36 periods
                              mperiod=7,  # lookback period for momentum - default 12 periods
                              reserve=0.01,  # 5% reserve capital
                      #         
                              rebal_day=15
                          )
                      
                          def log(self, arg):
                              print('{} {}'.format(self.datetime.date(), arg))
                      
                          def __init__(self):
                              # calculate 1st the amount of stocks that will be selected
                              self.selnum = int(len(self.datas) * self.p.selcperc)
                      
                      
                              # allocation perc per stock
                              # reserve kept to make sure orders are not rejected due to
                              # margin. Prices are calculated when known (close), but orders can only
                              # be executed next day (opening price). Price can gap upwards
                              self.perctarget = (1.0 - self.p.reserve) / self.selnum
                      
                              # returns, volatilities and momentums
                              rs = [bt.ind.PctChange(d, period=self.p.rperiod) for d in self.datas]
                              vs = [bt.ind.StdDev(ret, period=self.p.vperiod) for ret in rs]
                              ms = [bt.ind.ROC(d, period=self.p.mperiod) for d in self.datas]
                      
                              # simple rank formula: momentum / volatility
                              # the highest ranked: low vol, large momentum, large payout
                              self.ranks = {d: m / v for d, v, m in zip(self.datas, vs, ms)}
                              
                          
                          def next(self):
                              l =len(self)
                              self.rebalday = int(self.p.rebal_day)
                              
                              if l % self.rebalday == 0:
                                  
                                  # sort data and current rank
                                  ranks = sorted(
                                      self.ranks.items(),  # get the (d, rank), pair
                                      key=lambda x: x[1][0],  # use rank (elem 1) and current time "0"
                                      reverse=True,  # highest ranked 1st ... please
                                  )
                      
                                  # put top ranked in dict with data as key to test for presence
                                  rtop = dict(ranks[:self.selnum])
                      
                                  # For logging purposes of stocks leaving the portfolio
                                  rbot = dict(ranks[self.selnum:])
                      
                                  # prepare quick lookup list of stocks currently holding a position
                                  posdata = [d for d, pos in self.getpositions().items() if pos]
                      
                                  # remove those no longer top ranked
                                  # do this first to issue sell orders and free cash
                                  for d in (d for d in posdata if d not in rtop):
                                      self.log('Leave {} - Rank {:.2f}'.format(d._name, rbot[d][0]))
                                      self.order_target_percent(d, target=0.0)
                      
                                  # rebalance those already top ranked and still there
                                  for d in (d for d in posdata if d in rtop):
                                      self.log('Rebal {} - Rank {:.2f}'.format(d._name, rtop[d][0]))
                                      self.order_target_percent(d, target=self.perctarget)
                                      del rtop[d]  # remove it, to simplify next iteration
                      
                                  # issue a target order for the newly top ranked stocks
                                  # do this last, as this will generate buy orders consuming cash
                                  for d in rtop:
                                      self.log('Enter {} - Rank {:.2f}'.format(d._name, rtop[d][0]))
                                      self.order_target_percent(d, target=self.perctarget)
                      
                      
                      
                      class AcctStats(bt.Analyzer):
                          """A simple analyzer that gets the gain in the value of the account; should be self-explanatory"""
                       
                          def __init__(self):
                             
                              self.start_val = self.strategy.broker.get_value()
                              self.end_val = None
                       
                          def stop(self):
                              
                              self.end_val = self.strategy.broker.get_value()
                       
                          def get_analysis(self):
                              
                              return {"start": self.start_val, "end": self.end_val,
                                      "growth": self.end_val - self.start_val, "return": self.end_val / self.start_val}
                          
                              
                      class CommInfoFractional(bt.CommissionInfo):
                          def getsize(self, price, cash):
                              '''Returns fractional size for cash operation @price'''
                              return self.p.leverage * (cash / price)
                      
                      class AcctValue(bt.Observer):
                          alias = ('Value',)
                          lines = ('value',)
                       
                          plotinfo = {"plot": True, "subplot": True}
                       
                          def next(self):
                              self.lines.value[0] = self._owner.broker.getvalue()    # Get today's account value (cash + stocks)
                      class OandaCSVData(bt.feeds.GenericCSVData):
                          params = (
                              ('nullvalue', float('NaN')),
                              ('dtformat', '%Y-%m-%d'),
                              ('datetime', 0),
                              ('time', -1),
                              ('open', 1),
                              ('high', 2),
                              ('low', 3),
                              ('close', 4),
                              ('volume', 5),
                              ('openinterest', -1),
                          )
                              
                      cerebro = bt.Cerebro()    # I don't want the default plot objects
                      # strats = cerebro.optstrategy(St, rebal_day=range(1, 31))
                      
                      cerebro.broker.set_cash(10000000)    # Set our starting cash to $1,000,000
                      cerebro.broker.setcommission(0.002)
                      start = datetime.datetime(2018, 3, 14)
                      end = datetime.datetime(2020, 5, 19)
                      
                      
                      datalist = [
                          ('btc.csv', 'BTCUSDT'), #[0] = Data file, [1] = Data name
                          ('eth.csv', 'ETHUSDT' ]
                      
                      for i in range(len(datalist)):
                          data = OandaCSVData(dataname=datalist[i][0], fromdate=start, todate=end)
                      
                          cerebro.adddata(data, name=datalist[i][1])
                          
                      cerebro.broker.setcash(1000000)
                      cerebro.broker.setcommission(0.002)
                      cerebro.broker.addcommissioninfo(CommInfoFractional())
                      cerebro.addobserver(AcctValue)
                      cerebro.addstrategy(St)
                      cerebro.addanalyzer(AcctStats)
                      cerebro.addobservermulti(bt.observers.BuySell)
                      # cerebro.addsizer(PropSizer)
                      %time cerebro.run()
                      # cerebro.plot(iplot=True, volume=False)
                      cerebro.broker.getvalue()
                      
                      

                      I read the trailing order, but I still don't konw how to set a stop loss or take porfit into this strategy, still put in the next or somewhere else?
                      Hope someone would help me, thanks a lot.

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

                        I think you can put your trailing order in the for d in rtop loop.
                        Try something like:

                        self.order_target_percent(d, target=self.perctarget, transmit=False)
                        self.sell(d, size=1, exectype=bt.Order.StopTrail, trailpercent=0.02, transmit=True)
                        

                        The transmit False will hold the first order until the second is ready to submit. Please verify this code I have not checked it.

                        RunBacktest.com

                        I 1 Reply Last reply Reply Quote 1
                        • I
                          ivy @run-out last edited by

                          @run-out thank you for your help, I have solve it. Thanks again

                          1 Reply Last reply Reply Quote 1
                          • B
                            booboothefool @backtrader last edited by

                            @backtrader said in Setting a Stop loss and target in a strategy (missing piece in a sample):

                            Such a sample already exists. It is ATR-Based

                            See: https://www.backtrader.com/blog/posts/2016-07-30-macd-settings/macd-settings.html

                            (Of course, it is included in the sources)

                            Interesting. So if I understand correctly, in the example above, it's basically an ATR trailing stop, but it just uses a self.close() so it's kind of like an invisible stop loss, instead of updating a real stop loss by cancelling the old stop order and submitting a new stop order? Both ways will work? I wonder which is recommended?

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