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

take profit- stop loss



  • Hello, i'm very very new to python, i tried to use Backtrader using a strategy that i made, but i have some problems, the entries are very good and accurate but the problem is i don't know how to put a take profit or stop loss, i'm now stuck into selling based only on indicators which is kinda annoying because i wont be doing this on live trading with real money, of course i would be using stop loss and take profit and i would manage my money and risks and not rely on indicators to sell and i always get a bad final balance after i run my backtest not because my entries were bad but because my exit points were inaccurate, i use this to exit trades for example:

    if self.rsi > 70:
       print("Sell {} shares at {}".format(self.size, self.data.close[0]))
       self.close() 
    

    please don't laugh at me, i know its a terrible sell indicator but that all what i'm capable to do right now, feel free to help but please don't bully me :),
    Thank you for this great tool!



  • @8m Hey, no one's going to bully you. We all start somewhere! Also, rsi is a popular indicator and nothing wrong with that. Have a look at this article on bracket orders. It's a great starting place. Let us know if you have further questions or need help.



  • i have checked the page and to be honest i see it quite hard, because i want to specify the Tp and Sl with percent %% not a price, for example i want on every trade, a stop loss of 3% and a profit of 1.5%, this is the strategy for example:

    import os, math
    import sys
    import pandas as pd
    import backtrader as bt
    
    class EMACROSSOVER(bt.Strategy):
      
    
      params = (('period1', 20), ('devfactor', 2), ('fastvsma', 10), ('order_pct', 0.95), 
                  ('rsip', 14), ('slowvsma', 50)
                , ('slowsma', 25), ('fastsma', 10))
      
    
      def __init__(self):
            self.data.close = self.datas[0].close
            self.BB= bt.indicators.BollingerBands(self.datas[0], period=self.params.period1, devfactor=self.params.devfactor)
            self.fastvSMA= bt.indicators.SMA(self.data.volume, period=self.params.fastvsma, plotname='FvSMA')
            self.rsi= bt.indicators.RSI(self.data, period=self.params.rsip, plotname='RSI 14')
            self.slowvSMA= bt.indicators.SMA(self.data.volume, period=self.params.slowvsma, plotname='SvSMA')
            self.slowSMA= bt.indicators.SMA(self.data.close, period=self.params.slowsma, plotname='SSMA')
            self.fastSMA= bt.indicators.SMA(self.data.close, period=self.params.fastsma, plotname='FSMA')
       
    
    
     def next(self):
            if self.position.size == 0:
               if self.data.low[0] < self.BB.lines.bot[0]:
                   if self.fastvSMA > self.slowvSMA:
                         amount_to_invest = (self.params.order_pct * self.broker.cash)
                         self.size = math.floor(amount_to_invest / self.data.close)
                         print("Buy {} shares at {}".format(self.size, self.data.close[0]))
                         self.buy(size=self.size)
           
            if self.position.size > 0:
               if self.rsi > 70:
                  print("Sell {} shares at {}".format(self.size, self.data.close[0]))
                  self.close() 
    
    
    


  • I'll create a sample code based on your supplied code. This might be a useful example for anyone new to making bracket orders.

    @8m said in take profit- stop loss:

    self.data.close = self.datas[0].close

    You don't need this line since backtader already has a built in short cut with exactly the same name self.data.close.

    In order to make your bracket order you need to calculate your prices first.

    price = self.data.close[0] * (1.0 - self.p.buy_price_adjust)
    price_limit = price * (1.0 + self.p.buy_limit_adjust)
    price_stop = price * (1.0 - self.p.buy_stop_adjust)
    
    

    Then you use these prices in a bracket order. Assign the result of the bracket order to a variable. (add in your own sizer)

    self.long_buy_order = self.buy_bracket(
        data=self.datas[0],
        size=1,
        exectype=bt.Order.Limit,
        plimit=price,
        stopprice=price_stop,
        stopexec=bt.Order.Stop,
        limitprice=price_limit,
        limitexec=bt.Order.Limit,
    )
    

    You will now have bracket orders executing. However, it's important to keep track of the orders, particularly if you want to sell under other conditions such as:

    @8m said in take profit- stop loss:

    if self.rsi > 70:
    print("Sell {} shares at {}".format(self.size, self.data.close[0]))
    self.close()

    To keep track of the orders, we will use a list created in init.:

    def __init__(self):
        self.o_li = list()
    

    Then after the order we will populate this list with the orders you made which can be found in self.long_buy_order, which is a list of orders:

    self.o_li = [o for o in self.long_buy_order]
    

    We will remove orders from the list after they are closed/cancelled at the end of notify_order:

    if not order.alive() and order in self.o_li:
        self.o_li.remove(order)
    

    We can now use this list to check for outstanding orders as a precondition to trading:

    if self.position.size == 0 and len(self.o_li) == 0:
    

    And if you sell based on your rsi condtion you need to clean up the list:

    if self.position.size > 0:
        if self.rsi > 70:
            print("Sell shares at {}".format(self.data.close[0]))
            self.close()
            self.o_li = list()
    
    

    I know that's a lot, but here is my sample code for you to follow along with. Please take some time to try and understand the documentation for brackets.

    import backtrader as bt
    
    class Strategy(bt.Strategy):
    
        params = (
            ("period1", 2),
            ("devfactor", 2),
            ("rsip", 8),
            ("buy_price_adjust", 0.0),
            ("buy_limit_adjust", 0.005),
            ("buy_stop_adjust", 0.002),
        )
    
        def log(self, txt, dt=None):
            """ Logging function fot this strategy"""
            dt = dt or self.data.datetime[0]
            if isinstance(dt, float):
                dt = bt.num2date(dt)
            print("%s, %s" % (dt.date(), txt))
    
        def print_signal(self):
    
            self.log(
                "o {:5.2f}\th {:5.2f}\tl {:5.2f}\tc {:5.2f}\tv {:5.0f}".format(
                    self.datas[0].open[0],
                    self.datas[0].high[0],
                    self.datas[0].low[0],
                    self.datas[0].close[0],
                    self.datas[0].volume[0],
                )
            )
    
        def notify_order(self, order):
            if order.status in [order.Submitted, order.Accepted]:
                # Buy/Sell order submitted/accepted to/by broker - Nothing to do
                return
    
            # Check if an order has been completed
            # Attention: broker could reject order if not enougth cash
            if order.status in [order.Canceled, order.Margin]:
                if order.isbuy():
                    self.log("BUY FAILED, Cancelled or Margin")
                self.log
            if order.status in [order.Completed, order.Canceled, order.Margin]:
                if order.isbuy():
                    self.log(
                        "BUY EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f"
                        % (order.executed.price, order.executed.value, order.executed.comm)
                    )
    
                    self.buyprice = order.executed.price
                    self.buycomm = order.executed.comm
                else:  # Sell
                    self.log(
                        "SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f"
                        % (order.executed.price, order.executed.value, order.executed.comm)
                    )
    
                self.bar_executed = len(self)
    
            # Cleans up the order list.
            if not order.alive() and order in self.o_li:
                self.o_li.remove(order)
    
        def notify_trade(self, trade):
            if not trade.isclosed:
                return
    
            self.log("OPERATION PROFIT, GROSS %.2f, NET %.2f" % (trade.pnl, trade.pnlcomm))
    
        def __init__(self):
            self.o_li = list()
    
            self.BB = bt.indicators.BollingerBands(self.datas[0], period=self.params.period1, devfactor=self.params.devfactor)
            self.rsi = bt.indicators.RSI(self.data, period=self.params.rsip, plotname='RSI 14')
    
    
        def next(self):
    
            self.print_signal()
    
            if self.position.size == 0 and len(self.o_li) == 0:
                if self.data.low[0] < self.BB.lines.bot[0]:
                    price = self.data.close[0] * (1.0 - self.p.buy_price_adjust)
                    price_limit = price * (1.0 + self.p.buy_limit_adjust)
                    price_stop = price * (1.0 - self.p.buy_stop_adjust)
    
                    self.long_buy_order = self.buy_bracket(
                        data=self.datas[0],
                        size=1,
                        exectype=bt.Order.Limit,
                        plimit=price,
                        stopprice=price_stop,
                        stopexec=bt.Order.Stop,
                        limitprice=price_limit,
                        limitexec=bt.Order.Limit,
                    )
    
                    # Store orders in a list
                    self.o_li = [o for o in self.long_buy_order]
    
                    self.log(
                        "LONG BUY limit Targets: Buy {:8.2f}, Limit {:8.2f}, Stop {:8.2f}".format(
                            price, price_limit, price_stop
                        )
                    )
    
    
            if self.position.size > 0:
                if self.rsi > 70:
                    print("Sell shares at {}".format(self.data.close[0]))
                    self.close()
                    self.o_li = list()
    
    
    if __name__ == "__main__":
    
        cerebro = bt.Cerebro()
    
        data = bt.feeds.GenericCSVData(
            dataname="data/2006-day-001.txt",
            dtformat=("%Y-%m-%d"),
            timeframe=bt.TimeFrame.Days,
            compression=1,
        )
        cerebro.adddata(data)
    
        cerebro.addstrategy(Strategy)
    
        # Execute
        cerebro.run()
    


  • @run-out said in take profit- stop loss:

    self.print_signal()

    Thank you for this, I've been looking for a solution similar to this for days now!



  •     self.o_li = [o for o in self.long_buy_order]
    TypeError: 'BuyOrder' object is not iterable
    
    

    I'm getting this output when running:

            if pos == 0 and len(self.o_li) == 0:
                if close < mean:
                    self.long_buy_order = self.buy(price=buy1, size=size1, exectype=bt.Order.Limit)
                    self.log('Init... %.2f' % close)
    
                    self.o_li = [o for o in self.long_buy_order]
    
            if pos > 0 and len(self.o_li) == 1:
                if close < mean:
                    self.sell_order = self.sell(price=sell1, size=-size1, exectype=bt.Order.Limit)
                    self.log('Executing sell order... %.2f' % close)
                    self.o_li = list()
    


  • self.long_buy_order = self.buy_bracket(
                        data=self.datas[0],
                        size=1,
                        exectype=bt.Order.Limit,
                        plimit=price,
                        stopprice=price_stop,
                        stopexec=bt.Order.Stop,
                        limitprice=price_limit,
                        limitexec=bt.Order.Limit,
                    )
    
    

    In my example there are three orders placed at the same time in the bracket ordder and self.long_buy_order in my example is a list of three orders, which is iterable.

    @cept0r said in take profit- stop loss:

    self.long_buy_order = self.buy(price=buy1, size=size1, exectype=bt.Order.Limit)

    In your example you are using only one order by itself resulting in a singular order object, which is not iterable.

    When you are using a single buy order there's no need to use a list, you can just use the self.long_buy_order by itself.



  • I see, so I'm back to square one.. I'd love to use bracket orders but Binance Futures does not support them so I have to do this using only limit/market orders. Anyhow, thanks for the fast reply I'll get back to trying to solve my issue..

    While you're here I might as well try and ask..
    I don't want to hijack this but I'm sure a few more have similar issues.

    I'm trying to finish my grid ama band bot and im having issues at last part..

    How do I make sure I only put one limitbuy per "step"..
    So I have 4 bands below and 4 above and id like to make it long-only to begin with..

    But I'm issuing more than one order per step.

        def __init__(self):
            self.dataclose = self.datas[0].close
            self.ind = AmaBands(plot=True, subplot=False)
            self.step = False
            self.order = None
    
        def next(self):
            self.log('Close %0.2f, Mean %0.2f, Buy1 %0.2f, Sell1 %0.2f, Buy2 %0.2f, Sell2 %0.2f, Stop %0.2f'
                     % (self.dataclose[0], self.ind[0], self.ind.bot[0],
                        self.ind.top[0], self.ind.bot2[0], self.ind.top2[0],
                        self.ind.bot5[0]))
    
            orders = self.broker.get_orders_open()
    
            if self.order:
                return
    
            mean = self.ind[0]  # Current mean(moving average) value
    
            size1 = cerebro.broker.get_cash() * 0.035 / self.data
            size2 = size1 * 2
            size3 = size2 * 2
            size4 = size3 * 2
    
            pos = self.position.size
    
            stop = self.ind.bot5[0]
            close = self.dataclose[0]  # Current close
    
            # Buy levels
            buy1 = self.ind.bot[0]
            buy2 = self.ind.bot2[0]
            buy3 = self.ind.bot3[0]
            buy4 = self.ind.bot4[0]
    
            # Sell levels
            sell1 = self.ind.top[0]
            sell2 = self.ind.top2[0]
            sell3 = self.ind.top3[0]
            sell4 = self.ind.top4[0]
    
            """Initializing algorithm..."""
            if not self.position:
                if close < mean:
                    self.long = self.buy(price=buy1, size=size1, exectype=bt.Order.Limit)
                    self.log('Init... %.2f' % close)
            if pos > 0:
                if close < mean:
                    self.sell_order = self.sell(price=sell1, size=-size1, exectype=bt.Order.Stop)
                    self.log('Executing sell order... %.2f' % close)
    
            if pos < size2 and close <= buy1:
                self.long2 = self.buy(price=buy1, size=size1, exectype=bt.Order.Limit)
                self.log('Going long step 2... %.2f' % close)
            if pos < size2 and close >= sell1:
                self.closepos = self.close(price=sell2)
                self.log('Exiting position starting new cycle... %.2f' % close)
    

    I want to only issue one buy order at the first band if we're not in a position and currentclose goes below my mean. Then if price goes below my "buy1" id like to double my long at buy2 or if price reaches my sell1 id like to sell my current position and go back to step 1. Pretty much repeat this process for all 4 steps..



  • No problem. But since this is a different thread that might help others, would you mind copying it into a new post stand alone with a good title? Thanks.


Log in to reply
 

});