Repository navigation
Description
Summary
With calc_on_order_fills = true, a script that calls ta.sma(), ta.stdev() or ta.bb() runs 2.5 to 3.2 times slower on v1.11.0 than on v1.10.0. The trades are identical on both versions; only the run time changes.
Without calc_on_order_fills the same scripts are 8 to 18% faster on v1.11.0, so the regression is limited to the executions that follow an order fill. ta.ema() is not affected; ta.highest() and ta.wma() hardly (1.05 and 1.11 times).
Versions
pineforge-engine v1.11.0 (a4472324) with pineforge-codegen 1.11.0, and v1.10.0 with Codegen 1.10.0. Both built from source as Release with the same options, Apple clang 17.0.0, macOS 15.8 arm64 (v1.11.0 with the two local substitutions described in #655; neither is on this path). BTCUSDT 1h bars from 2024-01-01 to 2026-01-01 (17,545 bars), initial capital 10000, no commission or slippage.
Time is the wall time of the strategy's run over the bars; translation, compilation and data loading are not included. One process at a time, the two versions taking turns, median of 5 runs each.
Reproduction
//@version=6
strategy("40 x sma", overlay = false, initial_capital = 10000, calc_on_order_fills = true)
total = 0.0
total += ta.sma(close, 20)
total += ta.sma(close, 21)
// ... one line per length, up to
total += ta.sma(close, 59)
plot(total)
up = ta.crossover(ta.ema(close, 3), ta.ema(close, 8))
down = ta.crossunder(ta.ema(close, 3), ta.ema(close, 8))
if up
strategy.entry("Long", strategy.long, qty = 0.01)
if down
strategy.entry("Short", strategy.short, qty = 0.01)
The other rows replace the 40 ta.sma lines with the call shown, lengths 20 to 59 again; ta.bb is [mid0, upper0, lower0] = ta.bb(close, 20, 2.0) followed by total += upper0, and so on. "No indicator" has plot(close) instead of the 42 lines. The control of each row is the same script without calc_on_order_fills = true.
Every script gives 2,241 closed trades and one open trade, the same list on both versions.
| 40 calls of | Without calc_on_order_fills: v1.10.0 |
v1.11.0 | With calc_on_order_fills = true: v1.10.0 |
v1.11.0 | v1.11.0 / v1.10.0 |
|---|---|---|---|---|---|
| no indicator | 337 ms | 274 ms | 318 ms | 269 ms | 0.85 |
ta.sma(close, n) |
329 ms | 281 ms | 824 ms | 2,114 ms | 2.56 |
ta.stdev(close, n) |
335 ms | 292 ms | 1,341 ms | 4,209 ms | 3.14 |
ta.bb(close, n, 2.0) |
348 ms | 296 ms | 1,860 ms | 5,923 ms | 3.19 |
ta.highest(high, n) |
346 ms | 285 ms | 669 ms | 701 ms | 1.05 |
ta.wma(close, n) |
419 ms | 384 ms | 730 ms | 810 ms | 1.11 |
ta.ema(close, n) |
313 ms | 277 ms | 326 ms | 277 ms | 0.85 |
Taking the "no indicator" row away, the time that calc_on_order_fills adds per filled order (2,242 fills) is:
| 40 calls of | v1.10.0 | v1.11.0 |
|---|---|---|
ta.sma |
0.23 ms | 0.82 ms |
ta.stdev |
0.46 ms | 1.76 ms |
ta.bb |
0.69 ms | 2.52 ms |
The window length matters little. With lengths 10 to 49, 100 to 139 and 1000 to 1039 over one year of bars (median of 3 runs), ta.sma is 2.7, 2.7 and 1.9 times slower on v1.11.0 and ta.stdev 2.9, 2.9 and 2.3 times.
What may be related
Codegen 1.10.0 and 1.11.0 generate identical C++ for the ta.sma and ta.stdev scripts (for ta.bb it differs only in the lines that assign the tuple's variables), so the change is in the engine. In v1.11.0 the two std::deque<double> members of KahanWindowSum (include/pineforge/window_sum.hpp) became OwnedDeque<double>, a std::deque with OwnedAllocator. The generated strategy keeps a copy of every indicator object in its script-state checkpoint (_pf_script_state_checkpoint_) and assigns the copies back when it restores, so each copy of an SMA, a standard deviation or a Bollinger band now goes through that allocator. This is a reading of the source, not a profile.
Expected
The run time of v1.10.0, or better, as for the scripts without calc_on_order_fills.
Workaround
None known, short of not using calc_on_order_fills.
Activity
letiep97 commentedon Nov 10, 2026
Three more rows from the same script and measurement (median of 3 runs), all with
calc_on_order_fills = true:total += ta.change(close, n), n from 20 to 59v0 = 0.0,v0 := 0.5 * close + 0.5 * nz(v0[1]),total += v0(40 such variables)total += f(close, n), n from 10 to 49, wheref(source, length)sumssource[i] * (length - i)in afor i = 0 to length - 1loopta.rsi,ta.atr,ta.cumandta.lowestare not slower (0.73 to 0.98). Sota.changebelongs withta.sma,ta.stdevandta.bb, and variables read with a history offset are affected a little.luisleo526 commentedon Nov 10, 2026
Thank you for the detailed timings and the repro.
We will reproduce this on v1.10.0 and v1.11.0 with your scripts, and find which change slowed the recompute path when
calc_on_order_fills = true. Our triage result will be posted here by 2026-11-11 18:00 (UTC+8). If the cause and a fix are found in time, the fix ships in 1.11.1; otherwise 1.11.1's notes will name this as a known regression.luisleo526 commentedon Nov 11, 2026
Update: we missed the 18:00 (UTC+8) triage time we gave here. The bisect between v1.10.0 and v1.11.0 on your scripts has not finished yet, so the cause is not identified.
One related measurement: a change under review for the moving-average window (exact window sums for
ta.sma) runs yourcalc_on_order_fills = trueshape about a third faster in our first timing. That timing ran on a busy machine and is being repeated. It is not a fix for the regression you found, and we have not confirmed the cause.New time for the triage result: 2026-11-12 12:00 (UTC+8), here. If the cause is found and the fix is small, it goes into the next engine release. Otherwise that release's notes will name this as a known regression.
luisleo526 commentedon Nov 12, 2026
Thanks for the report. Status update:
ta.sma,ta.stdevand Bollinger Bands) whencalc_on_order_fills = true. On our measurement machine those snapshots are about three times slower than in the previous version; that factor is ours, not a measurement of your setup.ta.highestandta.wma, is not explained yet.🤖 Generated with Claude Code
luisleo526 commentedon Nov 13, 2026
Status 2026-11-13: the cause is located (see above); the fix plan is in review. The fix date is posted here by 2026-11-14.
🤖 Generated with Claude Code