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

Sample Code All In Strategy



  • Hi!

    Does anyone have a sample code for an all in strategy which uses cheat-on-open ?

    That would be very helpful...

    thank you!



  • What in particular do you need? And what does All In means?

    I've written strategies using cheat-on-open, didn't see any difference from common approach in terms of strategy development.


  • administrators

    You simply need to use the opening price to calculate the stake for the available cash.



  • I would like to write an All In strategy for Bitcoin trading.

    On buy signal it should buy as many coins as possible (All In) on the open of next candle.

    On close signal it should sell all coins on the open of next candle (or sell on close would be fine too, but i think that needs cheating as well)

    You can see my attempt in the following code...

    I actually think this creates and executes orders at the right prices and dates. Just not sure if I did the indexing right on next_open(). And if I don't need next() anymore... Also, this somehow always only sells 1 BTC instead of the whole position..

    Would be so helpful to see a working example.

    Thank you so much

    def MyAllInSizer(self):
        if not self.position:
            size = self.broker.get_cash() / self.datas[0].open
        else:
            size = self.broker.getposition(data = self.datas[0]).size
    
        return size
        
     def next_open(self):
        
        if self.order:
            return
        
        if self.position:
            if self.buysig < 0:
                self.log('SELL CREATE, %.2f' % self.dataclose[-1])
                self.MyAllInSizer()
                print('Position Size: %.2f BTC' % self.MyAllInSizer())     
                # Keep track of the created order to avoid a 2nd order
                self.order = self.sell()
    
        elif self.buysig > 0:
            self.log('BUY CREATE, %.2f' % self.dataclose[-1])
            self.MyAllInSizer()
            print('Position Size: %.2f BTC' % self.MyAllInSizer())
            # Keep track of the created order to avoid a 2nd order
            self.order = self.buy()
    
    
    if __name__ == '__main__':
    
    #initiate brain
    cerebro = bt.Cerebro(cheat_on_open=True)
    

    Returns look as follows (part of it):

    2014-06-16 00:00:00, BUY CREATE, 588.55
    Position Size: 16.89 BTC
    2014-06-16 00:00:00, BUY EXECUTED, 592.00
    2014-08-24 00:00:00, SELL CREATE, 497.00
    Position Size: 1.00 BTC
    2014-08-24 00:00:00, SELL EXECUTED, 496.96
    2015-04-02 00:00:00, BUY CREATE, 246.69
    Position Size: 40.15 BTC
    2015-04-02 00:00:00, BUY EXECUTED, 246.68
    2015-05-02 00:00:00, SELL CREATE, 231.00
    Position Size: 1.00 BTC
    2015-05-02 00:00:00, SELL EXECUTED, 231.05
    2015-07-03 00:00:00, BUY CREATE, 254.54
    Position Size: 38.85 BTC
    2015-07-03 00:00:00, BUY EXECUTED, 254.54


  • administrators

    @alain said in Sample Code All In Strategy:

    self.order = self.sell()
    

    It should be obvious why it does only sell 1

    Incidentally ... it does also only buy 1

    self.order = self.buy()
    

    See the reference for the buy and sell methods: Docs - Strategy



  • @backtrader said in Sample Code All In Strategy:

    Docs - Strategy

    Ok, yes now this is obvious. thanks.

    Next try:

    def next_open(self):
        
        if self.order:
            return
        
        if self.position:
            if self.buysig < 0:
                self.log('SELL CREATE, %.2f' % self.dataclose[0])                
                sizer = self.broker.getposition(data = self.datas[0]).size
                print('Position Size: %.2f BTC' % sizer)     
                # Keep track of the created order to avoid a 2nd order
                self.order = self.sell(size = sizer)
    
        elif self.buysig > 0:
            self.log('BUY CREATE, %.2f' % self.dataclose[0])            
            sizer = self.broker.get_cash() / self.datas[0].open
            print('Position Size: %.2f BTC' % sizer)
            # Keep track of the created order to avoid a 2nd order
            self.order = self.buy(size = sizer)
    
    if __name__ == '__main__':
    cerebro = bt.Cerebro(cheat_on_open=True)
    

    Obviously when it only bought 1 BTC as before there were no orders canceled. But now I have this problem again, which means something is wrong and it doesn't use the next day's open to calculate the sizer ?



  • @alain said in Sample Code All In Strategy:

    I have this problem again

    What problem? On the first glance the code you posted should work good.

    Edit: I would use self.datas[0].open[0]


  • administrators

    @alain said in Sample Code All In Strategy:

    But now I have this problem again

    We don't know which problem it is that you are having again, because there is no log of what's happening to you. Your original problem was that onle 1 unit of BTC was being bought/sold.

    @alain said in Sample Code All In Strategy:

    self.log('SELL CREATE, %.2f' % self.dataclose[0])          
    

    It would help you (and the logs) if you logged the value you are actually using for the sizing calculations, which is the open when using cheat-on-open

    @alain said in Sample Code All In Strategy:

    self.order = self.sell(size = sizer)
    

    You can close a position by using self.close() (Docs - Strategy)

    @alain said in Sample Code All In Strategy:

    sizer = self.broker.getposition(data = self.datas[0]).size
    

    If you only have one data, there is no need to specify the data feed here.



  • @backtrader and @ab_trader thank you for your help. I still have the following problem: Even though I seem to calculate the right amount of BTC to go all in on the next open I sometimes get the buy orders rejected.

    Here I implemented the most simple strategy which is to buy on one candle, sell on the next one and so on...

    def next(self):
        self.log('open %.2f and close %.2f' % (self.datas[0].open[0], self.dataclose[0])) 
        
        
    def next_open(self):
        self.log('open %.2f and close %.2f' % (self.datas[0].open[0], self.dataclose[0])) 
        sizer = self.broker.get_cash() / self.datas[0].open[0] 
        print("sizer: ", sizer)
        print("cash: ", self.broker.get_cash())
        print("next_open price: ", self.datas[0].open[0])
        if self.position:
            self.order = self.close()
        
        else:
           self.order = self.buy(size = sizer)
    

    "It would help you (and the logs) if you logged the value you are actually using for the sizing calculations, which is the open when using cheat-on-open" . I did exactly that to check if I calculate the sizer with the right open price but I still sometimes get buy orders rejected as can be seen here:

    Starting Portfolio Value: 10000.00

    2017-04-01 00:00:00, open 1033.79 and close 1070.31
    2017-04-02 00:00:00, open 1070.93 and close 1083.40
    sizer: 9.337678466379689
    cash: 10000.0
    next_open price: 1070.93
    2017-04-02 00:00:00, Order Canceled/Margin/Rejected
    2017-04-02 00:00:00, open 1070.93 and close 1083.40
    2017-04-03 00:00:00, open 1083.33 and close 1077.88
    sizer: 9.230797633223487
    cash: 10000.0
    next_open price: 1083.33
    2017-04-03 00:00:00, BUY EXECUTED, 1083.33
    2017-04-03 00:00:00, open 1083.33 and close 1077.88
    2017-04-04 00:00:00, open 1077.04 and close 1146.42
    sizer: 0.0
    cash: 0.0
    next_open price: 1077.04
    2017-04-04 00:00:00, SELL EXECUTED, 1077.04
    2017-04-04 00:00:00, open 1077.04 and close 1146.42
    2017-04-05 00:00:00, open 1145.62 and close 1143.00
    sizer: 8.678216409356528
    cash: 9941.938282887024
    next_open price: 1145.62
    2017-04-05 00:00:00, Order Canceled/Margin/Rejected
    2017-04-05 00:00:00, open 1145.62 and close 1143.00

    Final Portfolio Value: 9941.94

    I also tried to only return the next open from the next_open() method and execute orders from next. But that didn't solve my problem of rejected orders either...

    Any ideas ?

    Thank you so much!


  • administrators

    This is a basic: floating point precision.

    a = 10000.0
    b = 1070.93
    c =  a / b
    d = c * b
    
    print('a:', a)
    print('b:', b)
    print('c:', c)
    print('d:', d)
    
    assert d == a
    

    I guess you know what the answer to that assertion is.



  • yes! I thought the issue could be something like that. Thank you @backtrader.
    What I find strange though is that if I buy and sell on the close by use of "cerebro.broker.set_coc(True)" and use the built in all in sizer "cerebro.addsizer(bt.sizers.AllInSizer)" I don't get any errors because of floating point precision:

    backtrader sizers

    def _getsizing(self, comminfo, cash, data, isbuy):

    position = self.broker.getposition(data)
    
    if not position:
        size = cash / data.close[0] * (self.params.percents / 100)
        
    else:
        size = position.size
        
    return size
    

    I don't see why this won't give me any floating point errors ? I mean I'm happy if it doesn't, but I'm not sure if anything in this code differs from my implementation above.



  • @backtrader Ok, I actually did find canceled orders because of floating point errors also when "buy on close" with "bt.sizers.AllInsizer).

    So I tried to fix this problem by simply subtracting a small number from the position size, so there would be enough cash to enter the position. However I found canceled orders even though I subtracted a pretty large number 0.1 :

    Same code with corrected sizer by 0.1 :

    def next(self):
        self.log('open %.2f and close %.2f' % (self.datas[0].open[0], self.dataclose[0])) 
        
        
    def next_open(self):
        self.log('open %.2f and close %.2f' % (self.datas[0].open[0], self.dataclose[0])) 
        sizer = self.broker.get_cash() / self.datas[0].open[0] - 0.1
        print("sizer: ", sizer)
        print("cash: ", self.broker.get_cash())
        print("next_open price: ", self.datas[0].open[0])
        if self.position:
            self.order = self.close()
        
        else:
           self.order = self.buy(size = sizer)
    

    I receive:

    2017-04-01 00:00:00, open 1033.79 and close 1070.31
    2017-04-02 00:00:00, open 1070.93 and close 1083.40
    sizer: 9.23767846637969
    cash: 10000.0
    next_open price: 1070.93
    2017-04-02 00:00:00, Order Canceled/Margin/Rejected
    2017-04-02 00:00:00, open 1070.93 and close 1083.40
    2017-04-03 00:00:00, open 1083.33 and close 1077.88
    sizer: 9.130797633223487
    cash: 10000.0
    next_open price: 1083.33
    2017-04-03 00:00:00, BUY EXECUTED, 1083.33
    2017-04-03 00:00:00, BUY EXECUTED, 9.130797633223487
    2017-04-03 00:00:00, open 1083.33 and close 1077.88
    2017-04-04 00:00:00, open 1077.04 and close 1146.42
    sizer: 0.0005840080219866872
    cash: 108.33300000000054
    next_open price: 1077.04
    2017-04-04 00:00:00, SELL EXECUTED, 1077.04
    2017-04-04 00:00:00, open 1077.04 and close 1146.42
    2017-04-05 00:00:00, open 1145.62 and close 1143.00
    sizer: 8.578765457033768
    cash: 9942.567282887025
    next_open price: 1145.62
    2017-04-05 00:00:00, BUY EXECUTED, 1145.62
    2017-04-05 00:00:00, BUY EXECUTED, 8.578765457033768
    2017-04-05 00:00:00, open 1145.62 and close 1143.00

    Which is weird to me, because 1070.93... * 9.2376... ~ 9892.18... which is clearly less than 10k.
    ?! What did I do wrong this time ?


  • administrators

    The problem here is that the broker, being unaware of the order being issued during next_open, tries to see if you are submitting an order which exceeds your current cash reserves (and the current price is already the close)

    You can disable the submission check with checksubmit=False. See Docs - Broker

    And this other topic for example: https://community.backtrader.com/topic/782/help-with-simple-bband-strategy


Log in to reply
 

});