Don't understand "buy" amount



  • Hi,

    I would like to test an extremely easy strategy: on every 15th day of month, buy the symbol for fixed amount of money. But I must be missing something...

    Here is my code:

    class LazyStrategy(bt.Strategy):
        params = {'day_to_buy': 15}
        
        def log(self, txt, dt=None):
                dt = dt or self.datas[0].datetime.date(0)
                print('%s, %s' % (dt.isoformat(), txt))
    
        def next(self):
            if self.data.datetime.date().day == self.params.day_to_buy:
                self.log('BUY CREATE, %.2f' % self.datas[0].close[0])
                self.buy(size=1000)
    
    cerebro = bt.Cerebro()
    cerebro.broker.setcash(100000.0)
    cerebro.broker.setcommission(commission=0.009)
    
    cerebro.addstrategy(LazyStrategy)
    
    new_data = df.assign(date=pd.to_datetime(df.date)).set_index('date', drop=True)
    data = btfeeds.PandasData(dataname=new_data, datetime=None, openinterest=None)
    cerebro.adddata(data)
    
    cerebro.run()
    print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
    

    I mostly copied that from the quickstart example. But this doesn't reflect the fact I that I would like to buy the symbol for a specific amount of money (each time condition in next is true). When I tried to add some values to buy, it didn't work either (I got an error). And like this the final portfolio value is still same as on the beginning.

    Thank you



  • Would be useful to have some output. Maybe prices are so high that it is not enough capital to buy 1000 shares.


  • administrators

    @Farence-Kotraphali said in Don't understand "buy" amount:

    def next(self):
        if self.data.datetime.date().day == self.params.day_to_buy:
            self.log('BUY CREATE, %.2f' % self.datas[0].close[0])
            self.buy(size=1000)
    

    Nobody can guarantee that day_to_buy (which is 15 in your case) is a trading day, unless the asset trades 7 days a week (which is unsual)

    As pointed out by @ab_trader some output does always help (like for example which timestamps and prices the strategy see in each next iteration)



  • Hey! Thanks for the response!

    It seems that @ab_trader is right - changing to size=1 helped. Stupid mistakes, thanks.

    And may I know how to check for a trading day?

    Next time I am going to put an output as well, sorry.

    I must admit I found it quite hard to work with backtrader. Even though I would say I am quite experienced python programmer and the OOP design of backtrader seems nice, I didn't expect that it will take so long for me to get to some working state. The quickstart example is quite complicated to me as a newcomer. Maybe it is because my knowledge of trading is very bad right now. But all I want to do is to test a long time passive investing strategy - the most easy one IMO.
    Still buy, for the same amount every month, never sell, get evaluation based on dividends and a hope that the market is going to go up on average over decades. And I wasn't able to program even something as easy like this. Don't take this as something I think you made wrong - just a feedback from an honest user :hand_splayed: .

    I am having a different problem right now, let's assume this:

        def next(self):
            if self.data.datetime.date().day == self.params.day_to_buy:
                cerebro.broker.cash += 1000
                self.log('BUY CREATE, %.2f' % self.datas[0].close[0])
                self.buy(price=cerebro.broker.cash)
    

    what I would expect this to do is "increase broker's cash by 1000 and then buy maximum number of what you can buy for everything you have". But for some reason, I don't get that result. When I debug it, sometimes self.buy(price=cerebro.broker.cash) doesn't do anything (cash is not lowered, but sometimes it does:

    Start Portfolio Value: 0.00
    2010-09-15, BUY CREATE, 103.30
    2010-10-15, BUY CREATE, 107.56
    2010-11-15, BUY CREATE, 109.72
    2010-12-15, BUY CREATE, 113.42
    2011-02-15, BUY CREATE, 121.65
    2011-03-15, BUY CREATE, 117.68
    2011-04-15, BUY CREATE, 120.72
    2011-06-15, BUY CREATE, 116.16
    2011-07-15, BUY CREATE, 120.46
    2011-08-15, BUY CREATE, 110.26
    2011-09-15, BUY CREATE, 111.04
    2011-11-15, BUY CREATE, 115.34
    2011-12-15, BUY CREATE, 111.72
    2012-02-15, BUY CREATE, 123.12
    2012-03-15, BUY CREATE, 128.70
    2012-05-15, BUY CREATE, 122.02
    2012-06-15, BUY CREATE, 123.28
    2012-08-15, BUY CREATE, 128.98
    2012-10-15, BUY CREATE, 131.90
    2012-11-15, BUY CREATE, 124.14
    2013-01-15, BUY CREATE, 134.66
    2013-02-15, BUY CREATE, 139.32
    2013-03-15, BUY CREATE, 143.32
    2013-04-15, BUY CREATE, 142.14
    2013-05-15, BUY CREATE, 152.14
    2013-07-15, BUY CREATE, 154.04
    2013-08-15, BUY CREATE, 152.36
    2013-10-15, BUY CREATE, 155.48
    2013-11-15, BUY CREATE, 165.00
    2014-01-15, BUY CREATE, 169.20
    2014-04-15, BUY CREATE, 168.76
    2014-05-15, BUY CREATE, 171.67
    2014-07-15, BUY CREATE, 180.85
    2014-08-15, BUY CREATE, 179.43
    2014-09-15, BUY CREATE, 182.46
    2014-10-15, BUY CREATE, 170.83
    2014-12-15, BUY CREATE, 182.93
    2015-01-15, BUY CREATE, 182.54
    2015-04-15, BUY CREATE, 192.90
    2015-05-15, BUY CREATE, 194.73
    2015-06-15, BUY CREATE, 191.62
    2015-07-15, BUY CREATE, 193.14
    2015-09-15, BUY CREATE, 181.98
    2015-10-15, BUY CREATE, 185.45
    2015-12-15, BUY CREATE, 188.03
    2016-01-15, BUY CREATE, 172.15
    2016-03-15, BUY CREATE, 185.46
    2016-04-15, BUY CREATE, 190.56
    2016-06-15, BUY CREATE, 190.52
    2016-07-15, BUY CREATE, 198.01
    2016-08-15, BUY CREATE, 201.05
    2016-09-15, BUY CREATE, 196.66
    2016-11-15, BUY CREATE, 200.41
    2016-12-15, BUY CREATE, 208.23
    2017-02-15, BUY CREATE, 215.68
    2017-03-15, BUY CREATE, 219.37
    Final Portfolio Value: 56000.00
    

    0_1490283842903_upload-c574d8b4-a46a-4bd9-8124-9b14710c8b82

    How can cash be the same as portfolio value, if everything is spent in each step? And apparently, money were only added to the broker cash (56 times thousand...)


  • administrators

    You are manually increasing the cash in the broker each day you operate.

    cerebro.broker.cash += 1000
    

    And then you tell the platform something very strange

    self.buy(price=cerebro.broker.cash)
    

    In all trading platforms: price refers to the expected acquisition price and not to the total amount of money you want to buy. The 1st time you tell the platform to buy 1 item (you have left size as the default) for a price of 11,000 (the initial 10,000 plus your 1st 1,000.

    The platform tries to see if there is enough to buy that before accepting the order you just issued. Since you also have a commission (0.9%), there isn't enough money to buy and the order is rejected. (You would see that in you had a notify_order method receiving the notifications)

    And that happens over and over again. That's why there is no single Buy (upwards looking green triangle) along the development of the prices.

    You are looking for: order_target_percent, which tells the system to invest in an asset to reach a target percentage. To make sure you get a buy executed for your full amount (percentage of 1.0) and not suffer from potential gaps between the current close and the next open (which will lead to order reject) and since you are simulating a simple scenario, you should activate cheat-on-close

    cerebro.broker.set_coc(True)
    

    See the following docs:

    As stated above you will miss some months, because the 15th of each month must not be a trading day. See

    ...
    2014-05-15, BUY CREATE, 171.67
    2014-07-15, BUY CREATE, 180.85
    ...
    

    It is probably not relevant to get your simulation up and running and get an initial perspective. But the logic to be in each month on the 15th or the 1st trading day thereafter needs a couple of extra lines.



  • @backtrader this approach n next()

    cerebro.broker.cash += 1000
    

    does it work good in bt?

    Earlier I was thinking about adding dividends directly to the cash. Then I found out another discussion about dividends implemented as commission scheme, and decided that adding directly to cash doesn't work.


  • administrators

    Doing that will probably break the meaning of the results delivered by some analyzers, but there is nothing against doing it.

    This is the thread in which is reasoned how to use credit interest rate to simulate dividends without affecting the logic of trading:



  • Thank you for detailed information. I tried another iteration, but still without success. Now, only a first order is executed and that's it.

        def next(self):
            if self.data.datetime.date().day == self.params.day_to_buy:
                self.counter += 1
                cerebro.broker.cash += self.params.cash_to_buy
                self.log('%s. BUY CREATE, %.2f' % (self.counter, self.datas[0].close[0]))
                self.order = self.order_target_size(target=1)
    
                
        def start(self):
            self.order = None  # sentinel to avoid operrations on pending order
                
        def stop(self):
            self.log('(cash to buy %2d) Ending Value %.2f' % (self.params.cash_to_buy, self.broker.getvalue()))
            
        def notify_order(self, order):
            #import pdb; pdb.set_trace()
            if order.status in [order.Submitted, order.Accepted]:
                # Buy/Sell order submitted/accepted to/by broker - Nothing to do
                self.order = None
                self.log('ORDER ACCEPTED/SUBMITTED')
                return
            
            if not order.alive():
                self.order = None  # indicate no order is pending
    
            if order.status in [order.Expired]:
                self.log('BUY EXPIRED')
    
            # Check if an order has been completed
            # Attention: broker could reject order if not enougth cash
            if order.status in [order.Completed, order.Canceled, order.Margin]:
                if order.isbuy():
                    self.log(
                        'BUY EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %
                        (order.executed.price,
                         order.executed.value,
                         order.executed.comm))
                else:
                    raise ValueError('Ehh? We do not sell our precious stocks!')
            else:
                self.log('BUY NOT EXECUTED, WHATEVER *** REASON')
    

    the output:

    Start Portfolio Value: 0.00
    2010-09-15, 1. BUY CREATE, 103.30
    2010-09-16, ORDER ACCEPTED/SUBMITTED
    2010-09-16, ORDER ACCEPTED/SUBMITTED
    2010-09-16, BUY EXECUTED, Price: 103.32, Cost: 10.33, Comm 0.09
    2010-10-15, 2. BUY CREATE, 107.56
    2010-11-15, 3. BUY CREATE, 109.72
    2010-12-15, 4. BUY CREATE, 113.42
    2011-02-15, 5. BUY CREATE, 121.65
    2011-03-15, 6. BUY CREATE, 117.68
    2011-04-15, 7. BUY CREATE, 120.72
    2011-06-15, 8. BUY CREATE, 116.16
    2011-07-15, 9. BUY CREATE, 120.46
    2011-08-15, 10. BUY CREATE, 110.26
    2011-09-15, 11. BUY CREATE, 111.04
    2011-11-15, 12. BUY CREATE, 115.34
    2011-12-15, 13. BUY CREATE, 111.72
    2012-02-15, 14. BUY CREATE, 123.12
    2012-03-15, 15. BUY CREATE, 128.70
    2012-05-15, 16. BUY CREATE, 122.02
    2012-06-15, 17. BUY CREATE, 123.28
    2012-08-15, 18. BUY CREATE, 128.98
    2012-10-15, 19. BUY CREATE, 131.90
    2012-11-15, 20. BUY CREATE, 124.14
    2013-01-15, 21. BUY CREATE, 134.66
    2013-02-15, 22. BUY CREATE, 139.32
    2013-03-15, 23. BUY CREATE, 143.32
    2013-04-15, 24. BUY CREATE, 142.14
    2013-05-15, 25. BUY CREATE, 152.14
    2013-07-15, 26. BUY CREATE, 154.04
    2013-08-15, 27. BUY CREATE, 152.36
    2013-10-15, 28. BUY CREATE, 155.48
    2013-11-15, 29. BUY CREATE, 165.00
    2014-01-15, 30. BUY CREATE, 169.20
    2014-04-15, 31. BUY CREATE, 168.76
    2014-05-15, 32. BUY CREATE, 171.67
    2014-07-15, 33. BUY CREATE, 180.85
    2014-08-15, 34. BUY CREATE, 179.43
    2014-09-15, 35. BUY CREATE, 182.46
    2014-10-15, 36. BUY CREATE, 170.83
    2014-12-15, 37. BUY CREATE, 182.93
    2015-01-15, 38. BUY CREATE, 182.54
    2015-04-15, 39. BUY CREATE, 192.90
    2015-05-15, 40. BUY CREATE, 194.73
    2015-06-15, 41. BUY CREATE, 191.62
    2015-07-15, 42. BUY CREATE, 193.14
    2015-09-15, 43. BUY CREATE, 181.98
    2015-10-15, 44. BUY CREATE, 185.45
    2015-12-15, 45. BUY CREATE, 188.03
    2016-01-15, 46. BUY CREATE, 172.15
    2016-03-15, 47. BUY CREATE, 185.46
    2016-04-15, 48. BUY CREATE, 190.56
    2016-06-15, 49. BUY CREATE, 190.52
    2016-07-15, 50. BUY CREATE, 198.01
    2016-08-15, 51. BUY CREATE, 201.05
    2016-09-15, 52. BUY CREATE, 196.66
    2016-11-15, 53. BUY CREATE, 200.41
    2016-12-15, 54. BUY CREATE, 208.23
    2017-02-15, 55. BUY CREATE, 215.68
    2017-03-15, 56. BUY CREATE, 219.37
    2017-03-21, (cash to buy 1000) Ending Value 56011.13
    Final Portfolio Value: 56011.13
    

    I tried to tweak for an hour or so but still without any success.

    I used set_coc(True) as you recommended and let's ignore that problem with trading day.



  • Definition of self.order_target_size():

    Place an order to rebalance a position to have final size of target

    First order issued and executed, position size is 1.
    Your target is still 1 for all next orders, so no other orders is executed.
    If you want to buy 1 share on every next(), then just use self.buy(size=1). Or your target should be increased on each next().



  • Ah! I accidentaly used target_order_size instead targed_order_percent. Thanks, it seems to work now.

    There is another problem regarding what you asked before: cerebro.broker.cash += self.params.cash_to_buy. It seems that the money addition is not acknowledged immediately by the platform but in the next step. So this doesn't work for the first trade, because there are no money acknowledged on the third line.

    cerebro.broker.set_cash(0)
    cerebro.broker.cash += 1000
    self.order = self.order_target_percent(target=1)
    

    when I use this instead:

    cerebro.broker.set_cash(1000)
    self.order = self.order_target_percent(target=1)
    cerebro.broker.cash += 1000
    

    then it works (and the logic is preserved - having +1000 for following trades). Is there any canonical way how to add money to the broker?


  • administrators

    When you are in next the broker has already evaluated the orders. You see a closed bar.

    The cheat-on-close trick is there to tell the broker to consider the previous close for execution instead of the current (and more approximate to reality) price.

    The canonical way is set_cash.


  • administrators


Log in to reply
 

Looks like your connection to Backtrader Community was lost, please wait while we try to reconnect.