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

Having "Margin" problem when reversing the position using order_target_percent



  • Before asking my question, would like to thank you for making such a wonderful backtesting program!

    I tried to create a sma crossover strategy for 1 stock. After purchasing the first order. The reverse order cannot be fulfilled and shown as "Margin" since order require more cash..Below is the code and example:

    class allWeather(bt.Strategy):
        #Single Stock
        stockParams={'GOOGL':100}
    
        params =(('sma1', 20),('sma2', 50),('oneplot', False))
    
    def log(self, txt, dt=None, doprint=False):
        ''' Logging function fot this strategy'''
        dt = dt or self.datas[0].datetime.date(0)
        print('%s, %s' % (dt.isoformat(), txt))
        
    def notify_order(self, order):
        print(order)
        if order.status == order.Submitted:
            return
        elif order.status ==order.Completed:
            dt, dn = self.datetime.date(), order.data._name
            self.log('{} Order {} Status {}: Size:{} Price:${}'.format(dn, order.ref, order.getstatusname(), order.size,order.executed.price))
        else:
            dt, dn = self.datetime.date(), order.data._name
            self.log('{} Order {} Status {}'.format(dn, order.ref, order.getstatusname()))
        
    
    def __init__(self):
        self.o=dict()  # orders per data (main, stop, limit, manual-close)
        self.inds = dict()
        for i, d in enumerate(self.datas):
            self.inds[d] = dict()
            self.inds[d]['sma1'] = bt.indicators.SimpleMovingAverage(
                d.close, period=self.params.sma1)
            self.inds[d]['sma2'] = bt.indicators.SimpleMovingAverage(
                d.close, period=self.params.sma2)
            self.inds[d]['cross'] = bt.indicators.CrossOver(self.inds[d]['sma1'],self.inds[d]['sma2'])
    
    def next_open(self):
        for i, d in enumerate(self.datas):
            pos = self.getposition(d).size
            dn = d._name
            percent = self.stockParams[dn]/100
            self.log('{} Close:{}, SMA:{:.2f}/{:.2f}/{:.2f}, Size:{}, V:{:.2f}'.format(dn, 
                   d.close[0],self.inds[d]['sma1'][0],self.inds[d]['sma2'][0],self.inds[d]['cross'][0], pos, self.broker.getvalue()))
            # Check if we are in the market, not pos = no position
            if not pos:
                if self.inds[d]['cross'][0] == 1:
                    # Keep track of the created order to avoid a 2nd order
                    self.o[d] = self.order_target_percent(data=d,target=percent,price=d.open[0])
                    self.log('{} Initial Buy - Order No:{} Size:{}'.format(dn, self.o[d].ref,self.o[d].size))
                if self.inds[d]['cross'][0] == -1:
                    # Keep track of the created order to avoid a 2nd order
                    self.o[d] = self.order_target_percent(data=d,target=-percent,price=d.open[0])
                    self.log('{} Initial Sell - Order No:{} Size:{}'.format(dn, self.o[d].ref,self.o[d].size)) 
            else:
                if self.inds[d]['cross'][0] == 1:
                    self.o[d] = self.order_target_percent(data=d,target=percent,price=d.open[0])
                    self.log('{} Reverse Buy - Order No:{} Size:{}'.format(dn, self.o[d].ref,self.o[d].size)) 
                elif self.inds[d]['cross'][0] == -1:
                    self.o[d] = self.order_target_percent(data=d,target=-percent,price=d.open[0])
                    self.log('{} Reverse Sell - Order No:{} Size:{}'.format(dn, self.o[d].ref,self.o[d].size)) 
    
                    
    def notify_trade(self, trade):
        if not trade.isclosed:
            return
        self.log('OPERATION PROFIT, GROSS %.2f, NET %.2f' %
                 (trade.pnl, trade.pnlcomm)) 
    
    def stop(self):
        self.close()
        self.log('Ending Portfolio Value %.2f' %(self.broker.getvalue()))
    

    Here is the log, in the format as follow:
    (Date, Stock, Close, SMA 20, SMA 50, SMA Indicator, Size, Portfolio Value)

    Extract Stock Data: GOOGL from 20170102 to 20190201
    2017-03-17, GOOGL Close:872.37, SMA:853.93/839.82/0.00, Size:0, V:1000000.00
    2017-03-20, GOOGL Close:867.91, SMA:855.44/841.12/0.00, Size:0, V:1000000.00
    2017-03-21, GOOGL Close:850.14, SMA:856.51/842.21/0.00, Size:0, V:1000000.00
    2017-03-22, GOOGL Close:849.8, SMA:856.55/842.71/0.00, Size:0, V:1000000.00
    2017-03-23, GOOGL Close:839.65, SMA:856.47/843.16/0.00, Size:0, V:1000000.00
    2017-03-24, GOOGL Close:835.14, SMA:855.91/843.44/0.00, Size:0, V:1000000.00
    2017-03-27, GOOGL Close:838.51, SMA:855.27/843.54/0.00, Size:0, V:1000000.00
    2017-03-28, GOOGL Close:840.63, SMA:854.72/843.72/0.00, Size:0, V:1000000.00
    2017-03-29, GOOGL Close:849.87, SMA:854.50/843.92/0.00, Size:0, V:1000000.00
    2017-03-30, GOOGL Close:849.48, SMA:854.16/844.36/0.00, Size:0, V:1000000.00
    2017-03-31, GOOGL Close:847.8, SMA:854.14/844.77/0.00, Size:0, V:1000000.00
    2017-04-03, GOOGL Close:856.75, SMA:854.07/845.24/0.00, Size:0, V:1000000.00
    2017-04-04, GOOGL Close:852.57, SMA:854.55/845.81/0.00, Size:0, V:1000000.00
    2017-04-05, GOOGL Close:848.91, SMA:854.62/845.98/0.00, Size:0, V:1000000.00
    2017-04-06, GOOGL Close:845.095, SMA:854.38/845.96/0.00, Size:0, V:1000000.00
    2017-04-07, GOOGL Close:842.1, SMA:853.75/845.70/0.00, Size:0, V:1000000.00
    2017-04-10, GOOGL Close:841.7, SMA:852.78/845.40/0.00, Size:0, V:1000000.00
    2017-04-11, GOOGL Close:839.88, SMA:851.64/845.33/0.00, Size:0, V:1000000.00
    2017-04-12, GOOGL Close:841.46, SMA:850.33/845.65/0.00, Size:0, V:1000000.00
    2017-04-13, GOOGL Close:840.18, SMA:848.99/846.08/0.00, Size:0, V:1000000.00
    2017-04-17, GOOGL Close:855.13, SMA:847.50/846.58/0.00, Size:0, V:1000000.00
    2017-04-18, GOOGL Close:853.99, SMA:846.64/847.32/-1.00, Size:0, V:1000000.00
    2017-04-18, GOOGL Initial Sell - Order No:1 Size:-1172
    Ref: 1
    OrdType: 1
    OrdType: Sell
    Status: 1
    Status: Submitted
    Size: -1172
    Price: 852.54
    Price Limit: None
    TrailAmount: None
    TrailPercent: None
    ExecType: 0
    ExecType: Market
    CommInfo: None
    End of Session: 736437.9999999999
    Info: AutoOrderedDict()
    Broker: None
    Alive: True
    Ref: 1
    OrdType: 1
    OrdType: Sell
    Status: 2
    Status: Accepted
    Size: -1172
    Price: 852.54
    Price Limit: None
    TrailAmount: None
    TrailPercent: None
    ExecType: 0
    ExecType: Market
    CommInfo: None
    End of Session: 736437.9999999999
    Info: AutoOrderedDict()
    Broker: None
    Alive: True
    2017-04-18, GOOGL Order 1 Status Accepted
    Ref: 1
    OrdType: 1
    OrdType: Sell
    Status: 4
    Status: Completed
    Size: -1172
    Price: 852.54
    Price Limit: None
    TrailAmount: None
    TrailPercent: None
    ExecType: 0
    ExecType: Market
    CommInfo: <backtrader.comminfo.CommInfoBase object at 0x000002944C666FD0>
    End of Session: 736437.9999999999
    Info: AutoOrderedDict()
    Broker: None
    Alive: False
    2017-04-18, GOOGL Order 1 Status Completed: Size:-1172 Price:$852.54
    2017-04-19, GOOGL Close:856.51, SMA:845.94/847.99/0.00, Size:-1172, V:998300.60
    2017-04-20, GOOGL Close:860.08, SMA:846.26/848.69/0.00, Size:-1172, V:995347.16
    2017-04-21, GOOGL Close:858.95, SMA:846.77/849.31/0.00, Size:-1172, V:991163.12
    2017-04-24, GOOGL Close:878.93, SMA:847.74/849.89/0.00, Size:-1172, V:992487.48
    2017-04-25, GOOGL Close:888.84, SMA:849.93/850.87/0.00, Size:-1172, V:969070.92
    2017-04-26, GOOGL Close:889.14, SMA:852.44/851.95/1.00, Size:-1172, V:957456.40
    2017-04-26, GOOGL Reverse Buy - Order No:2 Size:2243
    Ref: 2
    OrdType: 0
    OrdType: Buy
    Status: 1
    Status: Submitted
    Size: 2243
    Price: 891.39
    Price Limit: None
    TrailAmount: None
    TrailPercent: None
    ExecType: 0
    ExecType: Market
    CommInfo: None
    End of Session: 736445.9999999999
    Info: AutoOrderedDict()
    Broker: None
    Alive: True
    Ref: 2
    OrdType: 0
    OrdType: Buy
    Status: 7
    Status: Margin
    Size: 2243
    Price: 891.39
    Price Limit: None
    TrailAmount: None
    TrailPercent: None
    ExecType: 0
    ExecType: Market
    CommInfo: None
    End of Session: 736445.9999999999
    Info: AutoOrderedDict()
    Broker: None
    Alive: False
    2017-04-26, GOOGL Order 2 Status Margin
    2017-04-27, GOOGL Close:891.44, SMA:854.87/852.95/0.00, Size:-1172, V:957104.80
    2017-04-28, GOOGL Close:924.52, SMA:856.95/853.98/0.00, Size:-1172, V:954409.20
    2017-05-01, GOOGL Close:932.82, SMA:860.70/855.72/0.00, Size:-1172, V:915639.44

    First order filled with short size -1172, when reverse it would fill with size 2243 as to settle 1172 short and new order 1071 unit. The Portfolio value should be enough (957456.40/891.39 = 1074 unit) and there is no commission in my trade. However it got "margin" and my wild guessing is there is not enough cash when executing the new order even the shorted position should be liquidated to cash in this case?


  • administrators

    When you sell the asset price is 853.99 and when you try to reverse the position, the asset price is: 889.14. So things have gone against you.

    Selling has added cash to your account, the movement of the market has decreased that cash.

    Now you try to buy (as you point out) 2243 - 1172 = 1071. So yes, there may not be enough cash, because you calculate against the opening price, but the broker does checks against the closing price (cheating isn't perfect)

    Use broker.set_checksubmit(False) to disable the cash check. See: Docs - Broker



  • Wow that's fast reply and truly thank you for answering my question!

    Back to the question...I tried to add the broker.set_checksubmit(False) and run the simulation again.
    Those Margin trade are now become partial and size become 0
    I think should have enough cash given this trade needs $952,119, and my cash value should be $957456.40 after liquidating the -1172 short position? <---Please let me know if I'm wrong on this one

    Is there any way it turns back to +1071 instead of 0?

    2017-04-17, GOOGL Close:855.13, SMA:847.50/846.58/0.00, Size:0, V:1000000.0
    2017-04-18, GOOGL Close:853.99, SMA:846.64/847.32/-1.00, Size:0, V:1000000.0
    2017-04-18, GOOGL Initial Sell - Order No:1 Size:-1172
    Ref: 1
    OrdType: 1
    OrdType: Sell
    Status: 2
    Status: Accepted
    Size: -1172
    Price: 852.54
    Price Limit: None
    TrailAmount: None
    TrailPercent: None
    ExecType: 0
    ExecType: Market
    CommInfo: None
    End of Session: 736437.9999999999
    Info: AutoOrderedDict()
    Broker: None
    Alive: True
    2017-04-18, GOOGL Order 1 Status Accepted
    Ref: 1
    OrdType: 1
    OrdType: Sell
    Status: 4
    Status: Completed
    Size: -1172
    Price: 852.54
    Price Limit: None
    TrailAmount: None
    TrailPercent: None
    ExecType: 0
    ExecType: Market
    CommInfo: <backtrader.comminfo.CommInfoBase object at 0x0000017D4CBA7FD0>
    End of Session: 736437.9999999999
    Info: AutoOrderedDict()
    Broker: None
    Alive: False
    2017-04-18, GOOGL Order 1 Status Completed: Size:-1172 Price:$852.54
    2017-04-19, GOOGL Close:856.51, SMA:845.94/847.99/0.00, Size:-1172, V:998300.6
    2017-04-20, GOOGL Close:860.08, SMA:846.26/848.69/0.00, Size:-1172, V:995347.2
    2017-04-21, GOOGL Close:858.95, SMA:846.77/849.31/0.00, Size:-1172, V:991163.1
    2017-04-24, GOOGL Close:878.93, SMA:847.74/849.89/0.00, Size:-1172, V:992487.5
    2017-04-25, GOOGL Close:888.84, SMA:849.93/850.87/0.00, Size:-1172, V:969070.9
    2017-04-26, GOOGL Close:889.14, SMA:852.44/851.95/1.00, Size:-1172, V:957456.4
    2017-04-26, GOOGL Reverse Buy - Order No:2 Size:2243
    Ref: 2
    OrdType: 0
    OrdType: Buy
    Status: 2
    Status: Accepted
    Size: 2243
    Price: 891.39
    Price Limit: None
    TrailAmount: None
    TrailPercent: None
    ExecType: 0
    ExecType: Market
    CommInfo: None
    End of Session: 736445.9999999999
    Info: AutoOrderedDict()
    Broker: None
    Alive: True
    2017-04-26, GOOGL Order 2 Status Accepted
    Ref: 2
    OrdType: 0
    OrdType: Buy
    Status: 3
    Status: Partial
    Size: 2243
    Price: 891.39
    Price Limit: None
    TrailAmount: None
    TrailPercent: None
    ExecType: 0
    ExecType: Market
    CommInfo: <backtrader.comminfo.CommInfoBase object at 0x0000017D4CBA7FD0>
    End of Session: 736445.9999999999
    Info: AutoOrderedDict()
    Broker: None
    Alive: True
    2017-04-26, GOOGL Order 2 Status Partial
    Ref: 2
    OrdType: 0
    OrdType: Buy
    Status: 7
    Status: Margin
    Size: 2243
    Price: 891.39
    Price Limit: None
    TrailAmount: None
    TrailPercent: None
    ExecType: 0
    ExecType: Market
    CommInfo: <backtrader.comminfo.CommInfoBase object at 0x0000017D4CBA7FD0>
    End of Session: 736445.9999999999
    Info: AutoOrderedDict()
    Broker: None
    Alive: False
    2017-04-26, GOOGL Order 2 Status Margin
    2017-04-26, OPERATION PROFIT, GROSS -45532.20, NET -45532.20
    2017-04-27, GOOGL Close:891.44, SMA:854.87/852.95/0.00, Size:0, V:954467.8
    2017-04-28, GOOGL Close:924.52, SMA:856.95/853.98/0.00, Size:0, V:954467.8
    2017-05-01, GOOGL Close:932.82, SMA:860.70/855.72/0.00, Size:0, V:954467.8


  • administrators

    Try: https://community.backtrader.com/topic/1613/release-1-9-70-122

    There was a commit in the work which uses the opening price for the submission calculation.



  • @backtrader Thanks man, tried but unfortunately not working. At the end I used one more bar to settle the trade and initiate a new trade one day later. Let me know if you have any clever solution.

    *I further change multi-feed back to one feed

    def next_open(self):
            pos = self.getposition(self.data).size
            self.log('{} Close:{}, SMA:{:.2f}/{:.2f}/{:.2f}, Size:{}, V:{}'.format(data._name, 
                   self.data.close[0],self.inds['sma1'][0],self.inds['sma2'][0],self.inds['cross'][0], pos, self.broker.getvalue()))
            #no position
            if not pos:
                if self.inds['cross'][0] == 1 or self.inds['cross'][-1] == 1:
                    # Keep track of the created order to avoid a 2nd order
                    self.o = self.order_target_percent(data=self.data,target=1,price=data.open)
                    self.log('{} Initial Buy - Order No:{} Size:{}'.format(data._name, self.o.ref,self.o.size)) 
                elif self.inds['cross'][0] ==-1 or self.inds['cross'][-1] == -1:
                    # Keep track of the created order to avoid a 2nd order
                    self.o = self.order_target_percent(data=self.data,target=-1,price=data.open)
                    self.log('{} Initial Sell - Order No:{} Size:{}'.format(data._name, self.o.ref,self.o.size)) 
            else:
                if self.inds['cross'][0] == 1:
                    self.o = self.close()
                    #self.o = self.order_target_percent(data=self.data,target=1,price=data.open)
                    self.log('{} Reverse Buy - Order No:{} Size:{}'.format(data._name, self.o.ref,self.o.size)) 
                elif self.inds['cross'][0] == -1:
                    self.o = self.close()
                    #self.o = self.order_target_percent(data=self.data,target=-1,price=data.open)
                    self.log('{} Reverse Sell - Order No:{} Size:{}'.format(data._name, self.o.ref,self.o.size)) 
    

    2017-04-17, GOOGL Close:855.13, SMA:847.50/846.58/0.00, Size:0, V:1000000.0
    2017-04-18, GOOGL Close:853.99, SMA:846.64/847.32/-1.00, Size:0, V:1000000.0
    2017-04-18, GOOGL Initial Sell - Order No:1 Size:-1172
    2017-04-18 Order 1 Status Accepted
    2017-04-18 Order 1 Status Completed
    2017-04-19, GOOGL Close:856.51, SMA:845.94/847.99/0.00, Size:-1172, V:998300.5999999999
    2017-04-20, GOOGL Close:860.08, SMA:846.26/848.69/0.00, Size:-1172, V:995347.1599999999
    2017-04-21, GOOGL Close:858.95, SMA:846.77/849.31/0.00, Size:-1172, V:991163.1199999999
    2017-04-24, GOOGL Close:878.93, SMA:847.74/849.89/0.00, Size:-1172, V:992487.4799999999
    2017-04-25, GOOGL Close:888.84, SMA:849.93/850.87/0.00, Size:-1172, V:969070.9199999999
    2017-04-26, GOOGL Close:889.14, SMA:852.44/851.95/1.00, Size:-1172, V:957456.3999999999
    2017-04-26, GOOGL Reverse Buy - Order No:2 Size:1172
    2017-04-26 Order 2 Status Accepted
    2017-04-26 Order 2 Status Completed
    2017-04-26, OPERATION PROFIT, GROSS -45532.20, NET -45532.20
    2017-04-27, GOOGL Close:891.44, SMA:854.87/852.95/0.00, Size:0, V:954467.7999999998
    2017-04-27, GOOGL Initial Buy - Order No:3 Size:1072
    2017-04-27 Order 3 Status Accepted
    2017-04-27 Order 3 Status Completed
    2017-04-28, GOOGL Close:924.52, SMA:856.95/853.98/0.00, Size:1072, V:956011.4799999999
    2017-05-01, GOOGL Close:932.82, SMA:860.70/855.72/0.00, Size:1072, V:991473.2399999998