For code/output blocks: Use ``` (aka backtick or grave accent) in a single line before and after the block. See:

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

  • 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
      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


  • administrators

    Such a sample already exists. It is ATR-Based


    (Of course, it is included in the sources)

  • 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

  • administrators

    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.

  • Ok I got it,
    This source has complete code (and I couldn't find this earlier)

    While this one which has same comment in strategy def, doesn't have init part
    may be this is meant for something else
    Thanks for such a prompt and highly useful response

  • administrators


    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:

  • @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:, 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.

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

  • @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,
    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
        def log(self, arg):
            print('{} {}'.format(, 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.end_val = None
        def stop(self):
            self.end_val =
        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] =    # 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))    # Set our starting cash to $1,000,000
    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.addsizer(PropSizer)
    # cerebro.plot(iplot=True, volume=False)

    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.

  • 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.

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

  • @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


    (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?

Log in to reply