Comments (8)
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.
from pybroker.
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.
from pybroker.
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?
from pybroker.
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.
from pybroker.
I uploaded an example notebook. This strategy is simple:
- if no long position and close price > ema_20, then buy ^SPX with all the cash at next bar's open price;
- if long position exists and close price < ema_20, then sell all the long position at next bar's open price.
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.
from pybroker.
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.
from pybroker.
Please download and check the notebook, I used Yfinance to get "^SPX" data live inside notebook, like pyb example notebooks.
from pybroker.
OK, I see. Thank you for the detailed explanation.
from pybroker.
Related Issues (20)
- Multi-timeframe? HOT 2
- Indicator might be wrong when applying filter on backtesting data HOT 2
- Add MAE (Maximum Adverse Excursion) and MFE (Maximum Favorable Excursion) metrics. HOT 1
- Add regular defined contribution to portfolio HOT 1
- Add Ruff for Python linter and code formatter HOT 3
- Real-time question HOT 1
- The feature names should match those that were passed during fit HOT 6
- Support custom fee structure HOT 3
- Add config option for disabling quantization in results HOT 1
- question about ctx.dt and indicator value HOT 3
- Error: Fill price 0 for prices below 0.005$ (like many cryptocurrencies) HOT 3
- fees and account amount HOT 5
- __init__() got an unexpected keyword argument 'adjust' HOT 1
- Data caching issue HOT 4
- 有考虑接入okx biance的数据么 HOT 1
- Add config option to save stop values in TestResult HOT 1
- caching data bug in month or week frequence HOT 2
- AKShare got an unexpected keyword argument 'timeframe' HOT 3
- Rotational Trading not working when using start_of_month HOT 1
- [Question] symbol for indicator but not for trading HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from pybroker.