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

Cancelling Bracket order following closing of the main side



  • For while I've been trying to solve following problem: When I place bracket order with stop and limit, and if later the trade is closed not by stop or limit orders, but by the strategy logic, they (the children orders - Stop and Limit) remain active and one of them gets executed later. To avoid this I need to cancel it by using the order reference and call self.cancel(sop_order) or similar.

    My logic suggests that to call it I need to use something like if trade.isclosed: or maybe if self.position.size == 0: and then call the order by its id and this is where I get super confused.

    I wrote about this previously but couldn't find a solution since.

    Any ideas how to implement this part the code?

    Thanks,
    Rumen


  • administrators

    The fundamental problem is that a position open by a bracket should not have additional logic to close the position independently.

    But if you insist in shooting yourself in the foot, the only thing you have to do is to keep a reference to any of the bracketing orders (stop-loss side or take-profit side) and cancel it whenever you close the position with the bracket-foreign logic.

    if logic_orders_to_close_the_position:
       self.cancel(take_profit_order)
    


  • Hi @Rumen-Nenov...did you get this figured out? I am at a similar junction you were when you wrote this. Thanks!



  • @Dallascat Do you just have the one bracket order or multiple different orders?



  • @run-out I am still fairly new to BT and python. Basically there are 3 signals generated - long, short or out. I have just learned on how to put in brackets if I want to use a PT and SL. But there are times when none of those gets hit and a signal to get out of the position hits (goes flat) and that leaves the PT and SL open if I use the brackets. So I assume I need to do it another way (issue PT and SL separately, and have them cancelled when the position == 0) instead of the brackets, unless there was a way to cancel the brackets if I went flat. Make sense?



  • @Dallascat said in Cancelling Bracket order following closing of the main side:

    @run-out I am still fairly new to BT and python. Basically there are 3 signals generated - long, short or out. I have just learned on how to put in brackets if I want to use a PT and SL. But there are times when none of those gets hit and a signal to get out of the position hits (goes flat) and that leaves the PT and SL open if I use the brackets. So I assume I need to do it another way (issue PT and SL separately, and have them cancelled when the position == 0) instead of the brackets, unless there was a way to cancel the brackets if I went flat. Make sense?

    Thank you for that explanation. Could you please include your code? Thanks.



  • @run-out Sure, this is just a simple strategy for my learning purposes of BT and python:

    `from future import (absolute_import, division, print_function,
    unicode_literals)
    from datetime import datetime
    import backtrader as bt

    Create a Strategy

    class TestStrategy(bt.Strategy):
    params = (('order_pct', 0.95), ("buy_price_adjust", 0.0), ("buy_limit_adjust", 0.02), ("buy_stop_adjust", 0.02))

    def log(self, txt, dt=None):
        """ Logging function for this strategy"""
        dt = dt or self.datas[0].datetime.date(0)
        print('%s, %s' % (dt.isoformat(), txt))
    
    def __init__(self):
        # Keep a reference to the "close" line in the data[0] dataseries
        self.bar_executed = len(self)
        self.dataclose = self.datas[0].close
    
        # Create an order list
        self.o_li = list()
    
        # To keep track of pending orders and buy price/commission
        self.order = None
        self.buyprice = None
        self.buycomm = None
    
        # Add EMA and TRIX
        self.ema = bt.indicators.ExponentialMovingAverage((self.datas[0].high + self.datas[0].low) / 2, period=50,
                                                          plotname="EMAMid")
        self.ema2 = bt.indicators.ExponentialMovingAverage((self.datas[0].high + self.datas[0].low) / 2, period=15,
                                                          plotname="EMAMid")
        self.atr = bt.indicators.ATR(period=50, movav=bt.indicators.MovAv.Exponential)
    
        # Indicators for plotting
        bt.indicators.ExponentialMovingAverage(self.datas[0], period=50)
    
    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 enough cash
        if order.status in [order.Completed, order.Canceled, order.Margin]:
            if order.isbuy():
                self.log(
                    'BUY EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f, Size %.2f' %
                    (order.executed.price,
                     order.executed.value,
                     order.executed.comm,
                     order.executed.size))
    
                self.buyprice = order.executed.price
                self.buycomm = order.executed.comm
            else:  # Sell
                self.log('SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f, Size %.2f' %
                         (order.executed.price,
                          order.executed.value,
                          order.executed.comm,
                          order.executed.size))
            self.bar_executed = len(self)
    
        elif order.status in [order.Canceled, order.Margin, order.Rejected]:
            self.log('Order Canceled/Margin/Rejected')
    
        # Clean up the order list.
        if not order.alive() and order in self.o_li:
            self.o_li.remove(order)
    
        # Write down: no pending order
        self.order = None
    
    def notify_trade(self, trade):
        if not trade.isclosed:
            return
    
        self.log('OPERATION PROFIT, GROSS %.2f, NET %.2f' % (trade.pnl, trade.pnlcomm))
    
    def next(self):
        # Check if an order is pending ... if yes, we cannot send a 2nd one
        if self.order:
            return
        # Check if we are in the market
        if not self.position:
    
            if self.ema[0] >= self.ema[-1] and (self.ema2[0] >= self.ema2[-1]):
                price = self.data.close[0] * (1.0 - self.p.buy_price_adjust)
                price_limit = price + self.atr[0]
                price_stop = price - self.atr[0]
    
                self.long_buy_order = self.buy_bracket(
                    data=self.datas[0],
                    size=None,
                    exectype=bt.Order.Limit,
                    plimit=price,
                    stopprice=price_stop,
                    stopexec=bt.Order.Stop,
                    limitprice=price_limit,
                    limitexec=bt.Order.Limit,
                )
    
            if self.ema[0] <= self.ema[-1] and (self.ema2[0] <= self.ema2[-1]):
                price = self.data.close[0] * (1.0 - self.p.buy_price_adjust)
                price_limit = price - self.atr[0]
                price_stop = price + self.atr[0]
    
                self.short_sell_order = self.sell_bracket(
                    data=self.datas[0],
                    size=None,
                    exectype=bt.Order.Limit,
                    plimit=price,
                    stopprice=price_stop,
                    stopexec=bt.Order.Stop,
                    limitprice=price_limit,
                    limitexec=bt.Order.Limit,
                )
    
        if self.position.size > 0 and (self.ema[0] <= self.ema[-1] or (self.ema2[0] <= self.ema2[-1])):
            self.close()
    
        if self.position.size < 0 and (self.ema[0] >= self.ema[-1] or (self.ema2[0] >= self.ema2[-1])):
            self.close()
    

    if name == 'main':
    # Variable for our starting cash
    startcash = 100000

    # Create a cerebro entity
    cerebro = bt.Cerebro()
    
    # Add a strategy
    cerebro.addstrategy(TestStrategy)
    
    # Create a Data Feed
    data = bt.feeds.YahooFinanceData(dataname='AAPL',
                                     fromdate=datetime(2019, 6, 1),
                                     todate=datetime(2020, 11, 9),
                                     buffered=True)
    
    # Add the Data Feed to Cerebro
    cerebro.adddata(data)
    
    # Set our desired cash start
    cerebro.broker.setcash(startcash)
    
    # Add a FixedSize sizer according to the stake
    cerebro.addsizer(bt.sizers.FixedSize, stake=50)
    
    # Set the commission
    cerebro.broker.setcommission(commission=0.0)
    
    # Add Analyzers:
    cerebro.addanalyzer(bt.analyzers.SharpeRatio, _name="mySharpe", riskfreerate=0.001)
    cerebro.addanalyzer(bt.analyzers.Returns, _name='returns')
    cerebro.addanalyzer(bt.analyzers.SQN, _name='sqn')
    
    # Print out the starting conditions
    print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())
    
    # Run over everything
    cerebro.run()
    
    portvalue = cerebro.broker.getvalue()
    pnl = portvalue - startcash
    
    # Print out the final result
    print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
    print('Final Portfolio Value: ${}'.format(portvalue))
    print('P/L: ${}'.format(pnl))
    
    # Plot the results
    cerebro.plot(style='bar', bardown='grey', barup="0.75", volume=False, grid=False)`


  • Sorry, I don't know why it didn't markdown the whole code.



  • @Dallascat said in Cancelling Bracket order following closing of the main side:

    self.long_buy_order

    Yes you have two possible exits from your position. The bracket orders and your custom code:

    if self.position.size > 0 and (self.ema[0] <= self.ema[-1] or (self.ema2[0] <= self.ema2[-1])):
            self.close()
    
        if self.position.size < 0 and (self.ema[0] >= self.ema[-1] or (self.ema2[0] >= self.ema2[-1])):
            self.close()
    

    I would recommend you change your order lists from self.long_buy_order and self.short_sell_order to self.order which you have already established in init. You may wish to initialize as a list.

    self.order = []
    

    By making these buy/sell lists into one list, you can now cancel out any orders in this list if you close the position in your custom code at the bottom

    for o in self.order: 
        self.cancel(o)
    

    So it would look like:

    if self.position.size > 0 and (self.ema[0] <= self.ema[-1] or (self.ema2[0] <= self.ema2[-1])):
            self.close()
            for o in self.order: 
                self.cancel(o)
    
        if self.position.size < 0 and (self.ema[0] >= self.ema[-1] or (self.ema2[0] >= self.ema2[-1])):
            self.close()
            for o in self.order: 
                self.cancel(o)
    


  • @run-out Awesome thanks! I will make those changes this weekend and get back!


Log in to reply
 

});