Closed none2003 closed 11 months ago
It seems the execution price when buying is the price of last bar, even "ctx.buy_fill_price = PriceType.OPEN" has been set. Is this intentional?
Yes, this is intentional since calc_target_shares is generic and can be used to calculate buy_shares or sell_shares. You will need to pass in the previous day's open price using self.open[-1] if you intend to use the open price.
Hi @edtechre,
Well, my intention is:
I want to buy or sell a security tomorrow at the open price, once I found a calculated signal based on today's close price appears.
For example, on 2023/12/5, I'm backtesting a strategy on a security data from 2008/1/1 to 2008/3/1, now I found close price is cross up MA20 on 2008/1/10, and I want to buy this security at the open price of very next day (2008/1/11).
How would I do that?
And what's the point show the shares/pnl which doesn't match with the entry/exit price in the result.trades?
There is another question: If I have to pass the self.open[-1] to calc_target_shares manually, what's purpose of the setting in doc: ctx.buy_fill_price = PriceType.OPEN and ctx.buy_fill_price = PriceType.OPEN?
Hi @none2003,
Sorry that I did not previously address your concerns. I misunderstood your original posts and it seems there are two different issues here:
The first shares value is 112694 rather than 100000000 / 887.064(entry price) = 112731.
Personally, I think 112731 is right.
And even shares value of 112694 were correct, shouldn't the pnl be 112694 * 1012.578(exit price) - 100000000 = 14111465.13
I believe the discrepancy is due to rounding. My suspicion is that the entry price is not 887.064 but is some some other floating point value. Note that the values in result.trades are rounded up to the nearest cent before they are displayed.
If you can email me your data with the symbols anonymized, I can take a look and verify this.
I want to buy or sell a security tomorrow at the open price, once I found a calculated signal based on today's close price appears.
This is done by setting ctx.buy_fill_price = PriceType.OPEN.
If I have to pass the self.open[-1] to calc_target_shares manually, what's purpose of the setting in doc: ctx.buy_fill_price = PriceType.OPEN and ctx.buy_fill_price = PriceType.OPEN?
They serve different purposes, calc_target_shares is a helper function for calculating the number of shares to buy or sell. Passing in self.open[-1] will calculate the number of shares based on the last completed bar's open price. Setting ctx.buy_fill_price = PriceType.OPEN will set the fill price to the open price of the next bar.
It sounds like you want to calculate the number of shares (i.e. 20% of your portfolio) to a symbol using the next bar's open price. But this is not supported because it would introduce lookahead bias into your strategy, and would likely be unrealistic to implement in a real strategy anyway due to slippage.
I uploaded an example notebook. This strategy is simple:
The very first buy order is filled with open price 2645.10 of 2017-12-01, this is consistent with the settings (ctx.buy_fill_price = PriceType.OPEN). However, the "shares" number is 37770, I think it should be 100000000 / 2645.10009765625 = 37805.
The following sell order is filled with open price 2741.06 of 2018-02-05, this is also consistent with the settings (ctx.sell_fill_price = PriceType.OPEN). Let's assume share number 37770 were right, the pnl should be 37770 2741.06005859375 - 100000000 = 3529838.4131. And if using share number 37805, that will be 37805 2741.06005859375 - 100000000 = 3625775.5151. But in result.order, it gives 3624407.72, largely differs with both numbers above given.
Thank you for providing a notebook. However, I will need data to be able to debug.
Let's assume share number 37770 were right, the pnl should be 37770 * 2741.06005859375 - 100000000
I am not sure where 100000000 comes from. The PnL is not calculated using your initial cash, it is calculated as the PnL from entry to exit:
entry_amount = shares * entry_price
exit_amount = shares * exit_price
entry_pnl = exit_amount - entry_amount
If config.enable_fractional_shares was not set to true, then not all of your initial cash (100000000) will be invested since only whole shares will be traded.
Please download and check the notebook, I used Yfinance to get "^SPX" data live inside notebook, like pyb example notebooks.
The calculations there look correct to me and is consistent with my explanation of calc_target_shares and buy_fill_price above.
ctx.buy_shares = ctx.calc_target_shares(1)
By default, this uses the close price of the last completed bar. It is never the fill price of the next bar, because that would introduce lookahead bias. You can't calculate the number of shares to buy based on a future price you haven't seen yet.
For the first trade, close price = 2647.580078125 100000000 // 2647.580078125 = 37770 shares
Then the actual open price is 2645.100098. The exit price is 2741.060059. PnL = (2741.060059 - 2645.100098) * 37770 = ~3624408e6
This is consistent with the dataframe before the values are quantized by rounding up to the nearest cent.
OK, I see. Thank you for the detailed explanation.
Hi @edtechre,
I noticed a weirdness about some number in result.trades.
Steps to reproduce:
I made the following settings:
During trading:
to sell
ctx.sell_all_shares() ctx.sell_fill_price = PriceType.OPEN