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

help on a customized strategy



  • Hi, I am new to bt and the algo trading, and I really need help with a strategy I am building.

    I have 1-min BTC data OHLCV + (day-based) forecast price predicted using the model. I am trying to run a backtest using 1min data. The data looks like this:

    ,date,time,open,high,low,close,volume,close_time,average,Forecast
    0,2017-08-17,2017-08-17 04:00:00,4261.48,4261.48,4261.48,4261.48,1.7751830000000002,1502942459999,4308.1725,4303.864329999999
    1,2017-08-17,2017-08-17 04:01:00,4261.48,4261.48,4261.48,4261.48,0.0,1502942519999,4308.1725,4303.864329999999
    2,2017-08-17,2017-08-17 04:02:00,4280.56,4280.56,4280.56,4280.56,0.261074,1502942579999,4308.1725,4303.864329999999
    3,2017-08-17,2017-08-17 04:03:00,4261.48,4261.48,4261.48,4261.48,0.012008,1502942639999,4308.1725,4303.864329999999
    4,2017-08-17,2017-08-17 04:04:00,4261.48,4261.48,4261.48,4261.48,0.140796,1502942699999,4308.1725,4303.864329999999
    5,2017-08-17,2017-08-17 04:05:00,4261.48,4261.48,4261.48,4261.48,0.0,1502942759999,4308.1725,4303.864329999999
    6,2017-08-17,2017-08-17 04:06:00,4261.48,4261.48,4261.48,4261.48,0.0,1502942819999,4308.1725,4303.864329999999
    7,2017-08-17,2017-08-17 04:07:00,4261.48,4261.48,4261.48,4261.48,0.0,1502942879999,4308.1725,4303.864329999999
    8,2017-08-17,2017-08-17 04:08:00,4261.48,4261.48,4261.48,4261.48,0.0,1502942939999,4308.1725,4303.864329999999
    9,2017-08-17,2017-08-17 04:09:00,4261.48,4261.48,4261.48,4261.48,0.0,1502942999999,4308.1725,4303.864329999999
    
    

    The forecast price varies by days, and I am trying to do backtest on minutes, that's why they look the same

    The strategy is:

    • Open trade at the daily open price.
    • Long if forecast > open price, short if forecast < open price.
    • Close trade at forecast price or at stop-loss (potential gain == potential loss), whichever reaches first.

    I have $10000 cash and trying to buy BTC as much as I can every time (not sure if this is called all-in)

    and I am trying to set the stop loss condition to just when the trade reaches back to the forecast price

    class Data_Extend(GenericCSVData):
        lines = ('Forecast',)  # add a 'Forecast' line to the inherited ones from the base class
        params = (('Forecast', 7),) # forecast column location
    
    # using a signal strategy here 
    class FStrategy(bt.SignalStrategy):
        params = dict(
            stake=1,
            printout=True,
            csvcross=True,
            period=7,
            startcash=10000,
            target_perc=1.0,
        )
    
        def log(self, txt, dt=None):
            if self.p.printout:
                dt = dt or self.data.datetime[0]
                dt = bt.num2date(dt)
                print('%s, %s' % (dt.isoformat(), txt))
    
        def notify_order(self, order):
            if order.status in [bt.Order.Submitted, bt.Order.Accepted]:
                return  # Await further notifications
    
            if order.status == order.Completed:
                if order.isbuy():
                    buytxt = 'BUY COMPLETE, %.2f' % order.executed.price
                    self.log(buytxt, order.executed.dt)
    
                else:
                    selltxt = 'SELL COMPLETE, %.2f' % order.executed.price
                    self.log(selltxt, order.executed.dt)
    
            elif order.status in [order.Expired, order.Canceled]:
                self.log('%s ,' % order.Status[order.status])
                pass  # Simply log
    
            # Allow new orders
            self.orderid = None
    
        def __init__(self):
            self.orderid = None
    
            # there is only 1 data feed, so it is the same anyway
            self.dataclose = self.datas[0].close
            self.dataopen = self.datas[0].open
            self.dataforecast = self.datas[0].Forecast
    
            # long condition (with exit)T
            self.signal_long = self.dataforecast > self.dataopen
            # multi head signal 1 ==> long when forecast > open
            self.signal_add(bt.SIGNAL_LONG, self.signal_long)
            # LONGEXIT: short indications are taken to exit long positions
            self.long_exit = self.dataforecast <= self.dataopen
            self.signal_add(bt.SIGNAL_LONGEXIT, self.long_exit)
    
            # short condition (with exit)
            self.signal_short = self.dataforecast < self.dataopen
            self.signal_add(bt.SIGNAL_SHORT, self.signal_short)
            # SHORTEXIT: long indications are taken to exit short positions
            self.short_exit = self.dataforecast <= self.dataopen
            self.signal_add(bt.SIGNAL_SHORTEXIT, self.short_exit)
    
        def next(self):
            if self.orderid:
                return  # if an order is active, no new orders are allowed
    
            if not self.position:  # not yet in market
                sizer = self.broker.get_cash() / self.datas[0].open[0]
                if self.signal_long:  # cross upwards
                    self.log('BUY CREATE @CLOSE , %.2f, OPEN %.2f, FORECAST %.2f' % (self.data.close[0], self.data.open[0], self.dataforecast[0]))
                    print('Position Size: %.2f BTC' % sizer)
                    self.order = self.buy(size=sizer)
                elif self.long_exit:
                    self.log('CLOSE LONG , %.2f, OPEN %.2f, FORECAST %.2f' % (self.data.close[0], self.data.open[0], self.dataforecast[0]))
                    self.order = self.close(size=sizer)
    
            else:  # in the market
                sizer = self.broker.getposition(data=self.datas[0]).size
                if self.signal_short:  # short signal
                    self.log('SELL CREATE @CLOSE , %.2f, OPEN %.2f, FORECAST %.2f' % (self.data.close[0], self.data.open[0], self.dataforecast[0]))
                    print('Position Size: %.2f BTC' % sizer)
                    self.order = self.sell(size=sizer)
                elif self.short_exit:
                    self.log('CLOSE SHORT , %.2f, OPEN %.2f, FORECAST %.2f' % (self.data.close[0], self.data.open[0], self.dataforecast[0]))
                    self.order = self.close(size=sizer)
    
    
        def stop(self):
            print('==================================================')
            print('Starting Value - %.2f' % self.broker.startingcash)
            print('Ending   Value - %.2f' % self.broker.getvalue())
            print('==================================================')
    

    In the next and init I log some info I want, and when I run using the data, the log looks like this:

    2019-01-01T23:59:59.999989, BUY CREATE @CLOSE , 3797.14, OPEN 3701.23, FORECAST 3728.05
    Position Size: 2.70 BTC
    2019-01-01T23:59:59.999989, BUY COMPLETE, 3797.14
    2019-01-02T23:59:59.999989, SELL CREATE @CLOSE , 3858.56, OPEN 3796.45, FORECAST 3742.63
    Position Size: 2.63 BTC
    2019-01-02T23:59:59.999989, SELL COMPLETE, 3858.56
    2019-01-03T23:59:59.999989, BUY CREATE @CLOSE , 3766.78, OPEN 3857.57, FORECAST 3863.28
    Position Size: 2.63 BTC
    2019-01-03T23:59:59.999989, BUY COMPLETE, 3766.78
    2019-01-05T23:59:59.999989, SELL CREATE @CLOSE , 3770.96, OPEN 3790.09, FORECAST 3770.63
    Position Size: 2.70 BTC
    2019-01-05T23:59:59.999989, SELL COMPLETE, 3770.96
    2019-01-06T23:59:59.999989, BUY CREATE @CLOSE , 3987.60, OPEN 3771.12, FORECAST 3797.46
    Position Size: 2.70 BTC
    2019-01-06T23:59:59.999989, BUY COMPLETE, 3987.60
    2019-01-07T23:59:59.999989, SELL CREATE @CLOSE , 3975.45, OPEN 3987.62, FORECAST 3925.53
    Position Size: 2.55 BTC
    2019-01-07T23:59:59.999989, SELL COMPLETE, 3975.45
    2019-01-08T23:59:59.999989, BUY CREATE @CLOSE , 3955.13, OPEN 3976.76, FORECAST 4001.77
    Position Size: 2.55 BTC
    2019-01-08T23:59:59.999989, BUY COMPLETE, 3955.13
    2019-01-10T23:59:59.999989, SELL CREATE @CLOSE , 3585.88, OPEN 3966.06, FORECAST 3965.74
    Position Size: 2.56 BTC
    2019-01-10T23:59:59.999989, SELL COMPLETE, 3585.88
    2019-01-11T23:59:59.999989, BUY CREATE @CLOSE , 3601.31, OPEN 3585.88, FORECAST 3670.85
    Position Size: 2.56 BTC
    2019-01-11T23:59:59.999989, BUY COMPLETE, 3601.31
    2019-01-12T23:59:59.999989, SELL CREATE @CLOSE , 3583.13, OPEN 3601.31, FORECAST 3528.85
    Position Size: 2.55 BTC
    2019-01-12T23:59:59.999989, SELL COMPLETE, 3583.13
    2019-01-13T23:59:59.999989, BUY CREATE @CLOSE , 3476.81, OPEN 3584.10, FORECAST 3611.51
    Position Size: 2.55 BTC
    2019-01-13T23:59:59.999989, BUY COMPLETE, 3476.81
    2019-01-15T23:59:59.999989, SELL CREATE @CLOSE , 3553.06, OPEN 3626.08, FORECAST 3600.16
    Position Size: 2.63 BTC
    2019-01-15T23:59:59.999989, SELL COMPLETE, 3553.06
    2019-01-16T23:59:59.999989, BUY CREATE @CLOSE , 3591.84, OPEN 3553.06, FORECAST 3578.68
    Position Size: 2.63 BTC
    2019-01-16T23:59:59.999989, BUY COMPLETE, 3591.84
    2019-01-17T23:59:59.999989, SELL CREATE @CLOSE , 3616.21, OPEN 3591.84, FORECAST 3585.79
    Position Size: 2.60 BTC
    2019-01-17T23:59:59.999989, SELL COMPLETE, 3616.21
    2019-01-18T23:59:59.999989, CLOSE LONG , 3594.87, OPEN 3613.32, FORECAST 3597.20
    2019-01-19T23:59:59.999989, BUY CREATE @CLOSE , 3665.30, OPEN 3594.87, FORECAST 3599.16
    Position Size: 2.62 BTC
    2019-01-19T23:59:59.999989, BUY COMPLETE, 3665.30
    
    Position Size: 1.71 BTC
    2019-12-29T23:59:59.999989, BUY COMPLETE, 7388.24
    ==================================================
    Starting Value - 10000.00
    Ending   Value - 12191.66
    ==================================================
    ===============================================================================
    TimeReturn:
      - 2019-01-31 00:00:00: -0.10192223974656367
      - 2019-02-28 00:00:00: 0.12747463642130064
      - 2019-03-31 00:00:00: 0.07017082291549426
      - 2019-04-30 00:00:00: -0.032682599108764476
      - 2019-05-31 00:00:00: 0.10385189267487394
      - 2019-06-30 00:00:00: 0.10896888494902579
      - 2019-07-31 00:00:00: -0.002396698888080584
      - 2019-08-31 00:00:00: -0.020199229864290236
      - 2019-09-30 00:00:00: 0.024766766780242477
      - 2019-10-31 00:00:00: 0.056789760260889555
      - 2019-11-30 00:00:00: -0.10789694777986258
      - 2019-12-31 00:00:00: 0.006156163558002525
    ===============================================================================
    SharpeRatio:
      - sharperatio: 0.24941200165758853
    ===============================================================================
    SQN:
      - sqn: 0.5616557528887063
      - trades: 126
    

    b66c9eaf-e27f-4d35-86ba-324f62787cef-image.png

    I do not really know if this is working well, I am especially confused with 3 things

    • size ==> is the size correct
    if not self.position:  # not yet in market
                sizer = self.broker.get_cash() / self.datas[0].open[0]
    else:  # in the market
                sizer = self.broker.getposition(data=self.datas[0]).size
    
    • close ==> is my way the right way to do it? I close both positions when
                elif self.long_exit:
                    self.log('CLOSE LONG , %.2f, OPEN %.2f, FORECAST %.2f' % (self.data.close[0], self.data.open[0], self.dataforecast[0]))
                    self.order = self.close(size=sizer)
                elif self.short_exit:
                    self.log('CLOSE LONG , %.2f, OPEN %.2f, FORECAST %.2f' % (self.data.close[0], self.data.open[0], self.dataforecast[0]))
                    self.order = self.close(size=sizer)
    
    • result ==> does the result look correct? I read many blogs, just wanna check

    Thank you if anybody can help.


Log in to reply
 

});