Hi — I think I have found something worth your attention, and it reproduces entirely inside
PineForge with no external comparison needed.
The observation
Take any strategy that uses priced (limit=) entries plus a strategy.exit bracket, and add
one line that can never execute:
if bar_index < 0
strategy.close("L")
bar_index is never negative, so this call is unreachable. Yet on engine 0.12.2 /
codegen 0.10.1, CME ES continuous 1-minute, 86,124 bars, mintick=0.25 / pointvalue=50
supplied via PINEFORGE_SYMINFO:
|
without the dead strategy.close |
with it |
| trades |
1,481 |
1,482 |
| wins |
785 |
787 |
| Net PnL |
-10,402.00 |
-10,584.00 |
qty distribution |
{1.0: 1481} |
{1:1422, 2:53, 3:5, 4:1, 5:1} |
trades with qty > 1 |
0 |
60 |
Adding unreachable code changes 60 position sizes and moves Net PnL by 182.00.
Why it happens
script_has_strategy_close_ is an AST scan, so mere textual presence of
strategy.close / strategy.close_all sets it — reachability is not considered. I confirmed
this with PINEFORGE_TRANSPILE_ONLY=1: the generated constructor contains no
script_has_strategy_close_ assignment for the baseline strategy, and
script_has_strategy_close_ = true; once the dead call is added.
The flag then gates the deferred-flip carry in src/engine_orders.cpp:
tv_deferred_flip = script_has_strategy_close_ && is_priced_entry
&& tv_carry_qty > 0.0 && (carry_was_long ? !is_long : is_long);
qty = tv_deferred_flip ? (tv_carry_qty + base_qty) : base_qty;
Conditions (b), (c) and (d) are satisfied by the strategy either way. Only (a) flips, and
with it 60 fills change size.
The question
The comment introducing the rule (around engine_orders.cpp:730-758) says the carry applies
when the original position "is later closed (by strategy.close, close_all, or any
exit) and the priced entry now fires from FLAT". My strategy closes via a strategy.exit
bracket, which reads to me like "any exit" — but condition (a) admits only strategy.close.
So: is the gate meant to be a proxy for "this script can go flat by means other than the
bracket", or should a strategy.exit bracket fill arm the carry on its own? Either way,
gating on textual presence of an unreachable call looks like something you would want to know
about.
Supporting evidence that the carry-armed behaviour is the TradingView-faithful one
This part does depend on an external comparison, so I flag its limits below.
I ran the same strategy on TradingView Desktop, CME_MINI:ES1! 15-minute, 439 closed trades:
430 at size 1, seven at size 2, one at size 3, one at size 4. Two identities hold on all 439
trades, so the sizes are real and not a reporting artifact:
commission == 2.25 * 2 * size (the size-4 trade shows commission 18.00 = 2.25 x 8 legs)
net_pnl == price_delta * 50 * size - commission
Eight of the nine oversized trades open at the exact timestamp the previous trade closed via
its own XL/XS bracket, and there is a visible runaway on 2026-06-04: size 2 at 20:45,
size 3 at 21:00, size 4 at 21:15. PyneCore 6.7.0 reproduces the same regime unprompted
(51 trades at size 2, 3 at size 3, out of 1,490).
So TradingView appears to arm the carry after a bracket close, which is the behaviour
PineForge produces only when the flag happens to be set.
Limits of this part: none of the three runs share data. The TradingView run is
CME_MINI:ES1! 15-minute 2025-09 to 2026-07; the PineForge and PyneCore runs are Databento
ES continuous 1-minute 2025 Q1. Different vendor, timeframe, calendar and roll convention. On
my account the TradingView ES 1-minute feed terminates at 2026-07-02, so I cannot run
TradingView on the same bars. I would not read the oversized rates across engines as
comparable; what I do consider solid is that TradingView produces sizes 2, 3 and 4 at all
under these settings.
For balance: on a separate divergence PineForge is the one that looks right
Of the 2,634.50 total P&L gap between PineForge and PyneCore at the correct symbol spec,
1,925.00 (73%) comes from 96 exit-fill price differences where PineForge is correct and
PyneCore is not — PineForge applies the favourable open clamp max(limit, open) on a
gapped take-profit, while PyneCore reclassifies it to a market order and adds a tick of
adverse slippage. I measured TradingView's rule with a controlled slippage 0-vs-1 pair: of
17 trades, the 7 fills that moved were every one a stop-side exit and the 10 that did not
move were every one a limit-side exit, so slippage applies to stops and not limits on
TradingView. PineForge matches. I am raising that separately with PyneCore.
The size difference discussed above is 506.50 (19%) of that same gap. So this is not a
one-sided report.
Reproduction
Baseline and probe strategies plus both JSON reports are available; the only textual
difference between them is the three lines quoted at the top. Command used:
docker run --rm \
-e PINEFORGE_SYMINFO=/in/syminfo.json \
-v strategy.pine:/in/strategy.pine:ro \
-v ohlcv.csv:/in/ohlcv.csv:ro \
-v syminfo.json:/in/syminfo.json:ro \
ghcr.io/pineforge-4pass/pineforge-release:engine0.12.2-codegen0.10.1
syminfo.json is {"syminfo": {"mintick": 0.25, "pointvalue": 50.0, "timezone": "UTC"}}.
Two minor container-interface notes
Both may well be intentional; they cost me time so I mention them in case they are not.
PINEFORGE_SYMINFO is not wired in pineforge-engine:0.10.10 — run_json.py accepts
--syminfo and calls apply_syminfo, but entrypoint.sh never passes it. It works in
pineforge-release:engine0.12.2. Since the pineforge-engine GHCR package still tags
latest at 0.10.10 and the move to pineforge-release appears only in release notes, I
spent a while concluding symbol specs were unreachable before finding the newer image.
margin_long / margin_short reach the generated constructor but are not accepted keys
in set_strategy_override, so they cannot be changed via PINEFORGE_OVERRIDES.
docs/pine_v6_audit_master.md does document them as compile-time only.
Happy to share any of the artifacts or run further A/Bs.
Hi — I think I have found something worth your attention, and it reproduces entirely inside
PineForge with no external comparison needed.
The observation
Take any strategy that uses priced (
limit=) entries plus astrategy.exitbracket, and addone line that can never execute:
bar_indexis never negative, so this call is unreachable. Yet on engine 0.12.2 /codegen 0.10.1, CME ES continuous 1-minute, 86,124 bars,
mintick=0.25/pointvalue=50supplied via
PINEFORGE_SYMINFO:strategy.closeqtydistribution{1.0: 1481}{1:1422, 2:53, 3:5, 4:1, 5:1}qty > 1Adding unreachable code changes 60 position sizes and moves Net PnL by 182.00.
Why it happens
script_has_strategy_close_is an AST scan, so mere textual presence ofstrategy.close/strategy.close_allsets it — reachability is not considered. I confirmedthis with
PINEFORGE_TRANSPILE_ONLY=1: the generated constructor contains noscript_has_strategy_close_assignment for the baseline strategy, andscript_has_strategy_close_ = true;once the dead call is added.The flag then gates the deferred-flip carry in
src/engine_orders.cpp:tv_deferred_flip = script_has_strategy_close_ && is_priced_entry && tv_carry_qty > 0.0 && (carry_was_long ? !is_long : is_long); qty = tv_deferred_flip ? (tv_carry_qty + base_qty) : base_qty;Conditions (b), (c) and (d) are satisfied by the strategy either way. Only (a) flips, and
with it 60 fills change size.
The question
The comment introducing the rule (around
engine_orders.cpp:730-758) says the carry applieswhen the original position "is later closed (by
strategy.close,close_all, or anyexit) and the priced entry now fires from FLAT". My strategy closes via a
strategy.exitbracket, which reads to me like "any exit" — but condition (a) admits only
strategy.close.So: is the gate meant to be a proxy for "this script can go flat by means other than the
bracket", or should a
strategy.exitbracket fill arm the carry on its own? Either way,gating on textual presence of an unreachable call looks like something you would want to know
about.
Supporting evidence that the carry-armed behaviour is the TradingView-faithful one
This part does depend on an external comparison, so I flag its limits below.
I ran the same strategy on TradingView Desktop,
CME_MINI:ES1!15-minute, 439 closed trades:430 at size 1, seven at size 2, one at size 3, one at size 4. Two identities hold on all 439
trades, so the sizes are real and not a reporting artifact:
commission == 2.25 * 2 * size(the size-4 trade shows commission 18.00 = 2.25 x 8 legs)net_pnl == price_delta * 50 * size - commissionEight of the nine oversized trades open at the exact timestamp the previous trade closed via
its own
XL/XSbracket, and there is a visible runaway on 2026-06-04: size 2 at 20:45,size 3 at 21:00, size 4 at 21:15. PyneCore 6.7.0 reproduces the same regime unprompted
(51 trades at size 2, 3 at size 3, out of 1,490).
So TradingView appears to arm the carry after a bracket close, which is the behaviour
PineForge produces only when the flag happens to be set.
Limits of this part: none of the three runs share data. The TradingView run is
CME_MINI:ES1!15-minute 2025-09 to 2026-07; the PineForge and PyneCore runs are DatabentoES continuous 1-minute 2025 Q1. Different vendor, timeframe, calendar and roll convention. On
my account the TradingView ES 1-minute feed terminates at 2026-07-02, so I cannot run
TradingView on the same bars. I would not read the oversized rates across engines as
comparable; what I do consider solid is that TradingView produces sizes 2, 3 and 4 at all
under these settings.
For balance: on a separate divergence PineForge is the one that looks right
Of the 2,634.50 total P&L gap between PineForge and PyneCore at the correct symbol spec,
1,925.00 (73%) comes from 96 exit-fill price differences where PineForge is correct and
PyneCore is not — PineForge applies the favourable open clamp
max(limit, open)on agapped take-profit, while PyneCore reclassifies it to a market order and adds a tick of
adverse slippage. I measured TradingView's rule with a controlled
slippage0-vs-1 pair: of17 trades, the 7 fills that moved were every one a stop-side exit and the 10 that did not
move were every one a limit-side exit, so slippage applies to stops and not limits on
TradingView. PineForge matches. I am raising that separately with PyneCore.
The size difference discussed above is 506.50 (19%) of that same gap. So this is not a
one-sided report.
Reproduction
Baseline and probe strategies plus both JSON reports are available; the only textual
difference between them is the three lines quoted at the top. Command used:
syminfo.jsonis{"syminfo": {"mintick": 0.25, "pointvalue": 50.0, "timezone": "UTC"}}.Two minor container-interface notes
Both may well be intentional; they cost me time so I mention them in case they are not.
PINEFORGE_SYMINFOis not wired inpineforge-engine:0.10.10—run_json.pyaccepts--syminfoand callsapply_syminfo, butentrypoint.shnever passes it. It works inpineforge-release:engine0.12.2. Since thepineforge-engineGHCR package still tagslatestat 0.10.10 and the move topineforge-releaseappears only in release notes, Ispent a while concluding symbol specs were unreachable before finding the newer image.
margin_long/margin_shortreach the generated constructor but are not accepted keysin
set_strategy_override, so they cannot be changed viaPINEFORGE_OVERRIDES.docs/pine_v6_audit_master.mddoes document them as compile-time only.Happy to share any of the artifacts or run further A/Bs.