0

A strategy where a simple Moving Average crossing of SPX (data0) on the close of one day makes a trade on the open next day: Crossing upward: 1) sell order of TQQQ (data1) and 2) a buy order of SQQQ (data2) (with the cash from sell included.) And the opposite trade when crossing downward. Am supposed to be "all in" (or 97% for margin issues) on either TQQQ or SQQQ at all time.

How can I include the cash from the sell-order (of TQQQ) when the size of the buy-order (of SQQQ) is calculated?

Are these two orders defined as the same trade, is that the issue? Can I somehow define them as two separate trades, would that solve it?

(From Jupyter Notebook:)

# creating strategy

class SmaCross(bt.Strategy):
    
    
    
    def log(self, txt, dt=None):
        ''' Logging function fot 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.dataclose = self.datas[0].close
    

    
    
    def __init__(self):
        # Keep a reference to the "close" line in the data[0] dataseries
        self.dataclose = self.datas[0].close
        
        # To keep track of pending orders
        self.order = None
        
        # parameters
        self.sma1 = bt.ind.EMA(data0, period=14)
        self.sma2 = bt.ind.SMA(data0, period=40)
        self.crossover = bt.ind.CrossOver(self.sma1, self.sma2)

        
        
    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]:
            if order.isbuy():
                self.log('BUY EXECUTED, at %.2f, shares: %.2f, total$: %.2f' %
                         (order.executed.price, order.executed.size, order.executed.value))
            elif order.issell():
                self.log('SELL EXECUTED, at %.2f, shares: %.2f, total$: %.2f' % 
                         (order.executed.price, order.executed.size, order.executed.value))

            self.bar_executed = len(self)

        elif order.status in [order.Canceled, order.Margin, order.Rejected]:
            self.log('Order Canceled/Margin/Rejected')

        # 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))
    
  
        
    # define the logic of strategy
    def next(self):
        # Simply log the closing price of the series from the reference
        self.log('Close: %.2f' % self.dataclose[0])
        self.log('Sma1: %.2f Sma2: %.2f Crossover?: %.1f' % (self.sma1[0], self.sma2[0], self.crossover[0]))
        
        # Check if an order is pending ... if yes, we cannot send a 2nd one
        if self.order:
            return

       
        if self.crossover > 0:  #if fast crosses slow to upside
            self.log('SELL SQQQ and BUY TQQQ. Crossover, %.2f' % self.crossover[0])
            if self.broker.getposition(data2).size:
                self.order = self.close(data2)

            if not self.broker.getposition(data1).size:
                self.order = self.buy(data1)



        if self.crossover < 0:  #crossing to the downside
            self.log('SELL TQQQ and BUY SQQQ. Crossover, %.2f' % self.crossover[0])
            if self.broker.getposition(data1).size:
                self.order = self.close(data1)

            if not self.broker.getposition(data2).size:
                self.order = self.buy(data2)


####

cerebro.addsizer(bt.sizers.PercentSizer, percents=97)

cerebro.broker.set_cash(100000)
# Set the commission - 0.1% ... divide by 100 to remove the %
cerebro.broker.setcommission(commission=0.001)
####

I expected the sell to execute first, then add the cash to the account value, and then execute the buy, with size calculated from 97% av the new account value. It seems to calculate the size of the buy from 97% of the 3% that are left from the previous 97% buy-order. In this log example below you see the cash from the sell gives: 322.083,28 But the buy only use: 31.216,72 (which is from what is left of the 97% previous buy of TQQQ). And then the opposite happens on the next trade.

2021-09-20, Sma1: 4460.34 Sma2: 4456.64 Crossover?: 0.0
2021-09-21, Close: 4354.19
2021-09-21, Sma1: 4446.18 Sma2: 4454.93 Crossover?: -1.0
2021-09-21, SELL TQQQ and BUY SQQQ. Crossover, -1.00
2021-09-22, SELL EXECUTED, at 67.43, shares: -8596.34, total$: 322083.28
2021-09-22, BUY EXECUTED, at 40.25, shares: 775.57, total$: 31216.72
2021-09-22, OPERATION PROFIT, GROSS 257567.78, NET 256666.05
2021-09-22, Close: 4395.64
2021-09-22, Sma1: 4439.44 Sma2: 4454.79 Crossover?: 0.0
2021-09-23, Close: 4448.98

.........

2021-10-20, Sma1: 4437.17 Sma2: 4439.22 Crossover?: 0.0
2021-10-21, Close: 4549.78
2021-10-21, Sma1: 4452.19 Sma2: 4440.56 Crossover?: 1.0
2021-10-21, SELL SQQQ and BUY TQQQ. Crossover, 1.00
2021-10-22, SELL EXECUTED, at 36.90, shares: -775.57, total$: 31216.72
2021-10-22, BUY EXECUTED, at 71.74, shares: 7767.03, total$: 557168.13
2021-10-22, OPERATION PROFIT, GROSS -2598.16, NET -2658.00
2021-10-22, Close: 4544.90
2021-10-22, Sma1: 4464.55 Sma2: 4442.43 Crossover?: 0.0
Timus
  • 10,974
  • 5
  • 14
  • 28
Mjoey
  • 1
  • I see now that it's probably logical that this happens. The two orders are two trades, that are sent on "day1" and executed on Open of "day2". And "Open" only happens once. I should find a way to execute first sell order, with "Open price", recalculate cash, and then execute buy order with "Open price". Even though this is not possible in real life, as at Open+1second, the price is not the same. But for sake of practical backtesting, it should be possible to enter a delay between these trades? – Mjoey Nov 23 '22 at 12:14

0 Answers0