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.
-
@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 is15
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 ofbacktrader
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
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...)
-
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 buy1 item
(you have leftsize
as the default) for a price of11,000
(the initial10,000
plus your 1st1,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 anotify_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 atarget
percentage. To make sure you get abuy
executed for your full amount (percentage of1.0
) and not suffer from potential gaps between the currentclose
and the nextopen
(which will lead to order reject) and since you are simulating a simple scenario, you should activatecheat-on-close
cerebro.broker.set_coc(True)
See the following docs:
- For
order_target_percent
: Docs - Strategy - For
cheat-on-close
: Docs - Broker
cerebro
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. - For
-
@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.
-
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 everynext()
, then just useself.buy(size=1)
. Or yourtarget
should be increased on eachnext()
. -
Ah! I accidentaly used
target_order_size
insteadtarged_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?
-
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
. -
See: Community - Release 1.9.44.116
For Cheat-On-Open see Blog - Cheat On Open