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

Flipping to short from a long position



  • Hi! I am new to backtrader in particular and pretty new to python in general. I am trying to implement a backtest where I go long on rsi <30 and short if rsi >70, when implemented just as a longonly strategy where rsi >70 serves as an exitsignal I am getting it to work. What I am trying to do is reverse position and go from a long entry of rsi <30 to a close and a shortentry for rsi > 70.
    What I did was the following,

    if self.position:
    if self.rsi < 30 or self.rsi > 70:
    self.log('CLOSE CREATE, %.2f' % self.dataclose[0])
    self.order = self.close()
    
        else:
            if self.rsi < 30:
                self.log('BUY CREATE, %.2f' % self.dataclose[0])
                self.order = self.buy()
        
            if self.rsi > 70:
                self.log('SELL CREATE, %.2f' % self.dataclose[0])
                self.order = self.sell()
    

    However I dont think it is working as intended, this is an excerpt of the log output

    2017-10-01, BUY CREATE, 4253.00
    2017-10-01, BUY EXECUTED, Price: 4260.00, Cost: 4260.00, Comm 4.26
    2017-10-01, Close, 4243.02
    2017-10-01, Close Create, 4243.02
    2017-10-01, SELL EXECUTED, Price: 4260.00, Cost: 4260.00, Comm 4.26
    2017-10-01, OPERATION PROFIT, GROSS 0.00, NET -8.52
    

    This is the full code:

    import backtrader.feeds as btfeeds
    import os
    import datetime
    import backtrader.analyzers as btanalyzers
    
    class TestStrategy(bt.Strategy):
    
    def log(self, txt, dt=None):
        dt = dt or self.datas[0].datetime.date(0)
        print('%s, %s' % (dt.isoformat(), txt))
    
    def __init__(self):
        self.dataclose = self.datas[0].close
        self.order = None
        self.buyprice = None
        self.buycomm = None
        self.rsi = bt.indicators.RSI_SMA(self.data.close, period=21)
    
    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
    
        if order.status in [order.Completed]:
            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:
                self.log('SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %
                         (order.executed.price,
                          order.executed.value,
                          order.executed.comm))
                
    
            self.bar_executed = len(self)
    
        elif order.status in [order.Canceled, order.Margin, order.Rejected]:
            self.log('Order Canceled/Margin/Rejected')
    
        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):
        self.log('Close, %.2f' % self.dataclose[0])
    
        if self.order:
            return        
        
        if self.position:
            if self.rsi < 30 or self.rsi > 70:
                self.log('CLOSE CREATE, %.2f' % self.dataclose[0])
                self.order = self.close()
            
        else:
            if self.rsi < 30:
                self.log('BUY CREATE, %.2f' % self.dataclose[0])
                self.order = self.buy()
        
            if self.rsi > 70:
                self.log('SELL CREATE, %.2f' % self.dataclose[0])
                self.order = self.sell()
    if name == 'main':
    cerebro = bt.Cerebro()
    cerebro.addstrategy(TestStrategy)
    datapath = os.path.abspath(os.getcwd() + '/BTC_5min' + str(datetime.datetime.now().strftime("%Y_%m_%d")))
    
    # Create a Data Feed
    data = btfeeds.GenericCSVData(
        dataname=datapath,
        fromdate=datetime.datetime(2017, 10, 1),
        dtformat=('%Y-%m-%d %H:%M:%S'),
        datetime=0,
        high=2,
        low=3,
        open=1,
        close=4,
        volume=5,
        openinterest=-1,
        timeframe= bt.TimeFrame.Minutes,
        compression= 5
    )
    
    cerebro.adddata(data)
    
    cerebro.broker.setcash(100000.0)
    
    cerebro.addsizer(bt.sizers.FixedSize, stake=1)
    
    cerebro.broker.setcommission(commission=0.001)
    
    cerebro.addanalyzer(btanalyzers.SharpeRatio, _name='mysharpe')
    cerebro.addanalyzer(btanalyzers.DrawDown, _name='DrawDown')
    
    print('Starting Balance: %.2f' % cerebro.broker.getvalue())
    
    strats = cerebro.run(stdstats=False)
    strat = strats[0]
    cerebro.addobserver(bt.observers.Value)
    
    
    print('Sharpe Ratio:', strat.analyzers.mysharpe.get_analysis())
    print('DrawDown:', strat.analyzers.DrawDown.get_analysis())
    print('Final Balance: %.2f' % cerebro.broker.getvalue())
    
    
    cerebro.plot()
    

    Anyone seeing where I am going wrong? Sry if this is a stupid question.
    Kind regards Agarp



  • Your entry and exit orders are issued under the same conditions.

    exit:

            if self.rsi < 30 or self.rsi > 70:
                self.order = self.close()
    

    enter:

            if self.rsi < 30:
                self.order = self.buy()
        
            if self.rsi > 70:
                self.order = self.sell()
    

  • administrators

    @agarp said in Flipping to short from a long position:

    Anyone seeing where I am going wrong?

    In many places. Let me try to walk you through

    @agarp said in Flipping to short from a long position:

    I am new to backtrader in particular and pretty new to python in general.

    You are also new to programming and trading. And that plays a role. Take time. Don't try to create complex scripts with Sizers, commissions, analyzers. If you are not able to go from long to short, the rest is only distracting and not helping. So many trees are not letting you see the forest.

    Start slowly and build things incrementally.

    @agarp said in Flipping to short from a long position:

    if self.position:
    if self.rsi < 30 or self.rsi > 70:
    self.log('CLOSE CREATE, %.2f' % self.dataclose[0])
    self.order = self.close()
    
        else:
    

    This is impossible code in Python. I know you will internally argue is just a matter of formatting. Well ... formatting matters a lot in Python and in enabling others to read your code.

    @agarp said in Flipping to short from a long position:

    However I dont think it is working as intended, this is an excerpt of the log output

    2017-10-01, BUY CREATE, 4253.00
    2017-10-01, BUY EXECUTED, Price: 4260.00, Cost: 4260.00, Comm 4.26
    2017-10-01, Close, 4243.02
    2017-10-01, Close Create, 4243.02
    2017-10-01, SELL EXECUTED, Price: 4260.00, Cost: 4260.00, Comm 4.26
    2017-10-01, OPERATION PROFIT, GROSS 0.00, NET -8.52
    

    This log should be impossible, because it says that you executed different orders during the same day, which isn't compatible with your code. Ok ... your timeframe is intraday ... LOG the proper timestamp so that you (and others) can better understand what's happening.

    Furthermore, you show us a BUY and then the corresponding SELL created by a self.close() operation. Unless the RSI is moving like crazy (unlikely ... because you even softened it with an SMA), the next lines in the log will probably show another SELL. But we don't know because you decided which lines were important. (Don't post millions of lines, but don't post too few ...)

    In any case your major problem is not having a control flag in addition to the position check after close is executed, to let you evaluate the signal within the same cycle as if your position were already 0.



  • @backtrader
    Ok, cheers. Thanks for the reply!


Log in to reply
 

});