TradingView compatibility
-
A stateful
tabuiltin variable is now evaluated once per bar, not once per reference site. Measured on TradingView: a read ofta.accdist,ta.nvi,ta.obv,ta.pvi,ta.pvt,ta.wadorta.vwapinside anifreturns exactly what an unconditional read returns on the same bar — Pine keeps one engine-level series per builtin variable, and only function call sites gate their own state. PyneCore evaluated every reference separately, so a gated read diverged. A new transformer replaces each bare read with a module-global cache and prepends one unconditional evaluation per referenced variable tomain, giving the machine a single stable call site per bar.ta.iiiandta.wvadare pure functions of the current bar and need no hoist.ta.sarstays deliberately call-gated: measured, a gatedta.sarisnaon TradingView for the entire run, which the per-call window already reproduces.
-
The hoist follows TradingView's measured scope rules.
ta.nvi,ta.obv,ta.pvi,ta.pvtandta.wadstay engine-global inside user functions too, so their reads are rewired everywhere in a script module.ta.vwapandta.accdistget a per-instance machine inside a function and are rewired only directly inmain's body. In a library module the dividing line isexport, in both run modes: reads inmain's body and in non-exported nested functions behave like in a script, while exported functions and a hand-written library's module-level functions keep their per-call-site gated machines. -
ta.tr()inside a conditional branch measured the wrong previous close. It kept the previous close in its own per-call-site state, which advances per call, so atr()sitting in anifcompared against the last bar it happened to run on. TradingView readsclose[1]there — a global series that advances on every bar — while the accumulatingrmaofta.atr()correctly stays per-call-site and call-gated. The runner now publishes that one-deep window andtr()carries no state of its own. -
array.every()returnsfalsefor an empty array instead of Python's vacuously trueall([]), matching TradingView.array.some()already agreed atfalse. -
Partial date strings are accepted, the ones TradingView fills in with the start of the period:
"2025","2025-06"together with its"2025-6","2025/06"and"2025.06"spellings, and"Jan 2025"/"January 2025". A leading month ("06 2025") keeps raising, because TradingView rejects it too. -
A library file run directly as the script executed its
maintwice per bar. Such a module registers itsmainas a library entry as well as the script entry; the duplicate run is now suppressed. It was masked until now by within-bar idempotent updaters liketa.nviandta.obv.
Runtime
- A
Seriesparameter ofmainis no longer value-passed into a nested function. The closure collector recorded type annotations only for annotated assignments, so aSeries[T]parameter — what aninput(close, ...)source compiles to — was invisible to the series-closure rule. The inner function received it by value and built its own per-call history, sosrc[i]there raised'float' object is not subscriptableinstead of reading the parent's history through the scope chain. Parameter annotations are now collected the same way, so plain-SeriesandPersistentparameters both follow the existing rules. As: Series[T]declared inmain's body already worked.
Broker
-
A pyramid parent consumed by its own bracket fill is retired. The FIFO-cleanup's "own entry" exemption skipped every fill labelled with the parent's own Pine id, but a position-attached bracket labels the TP/SL fill exactly that way. A non-flat pyramid parent close therefore left the consumed level's entry and exit intents active, and the next same-name phase diffed the exit as a modify, which the plugin rejects with "no confirmed entry row". Closing legs (TP/SL/TRAILING/CLOSE) are now exempt from the guard; a same-id flatten-and-reopen stays protected by the surviving-open-trades check.
-
A rejected exit modify over a parent that holds no open trade no longer kills the run. The venue has already closed what the replacement bracket would protect, so the stale tracking is retired and the run continues. A reject over a parent that still has an open trade keeps raising — swallowing that would leave real exposure unguarded.
-
Replayed partial bracket legs whose parent was never dispatched in this run are dropped, not fatal. A cross-cycle restart can replay engine-trigger partial leg rows whose parent entry belongs to a previous run (entry ids are re-used over a shared store). The stale-parent adoption branch cancelled the legs correctly but still fell through to the fresh bracket dispatch, where the no-parent guard raised a raw
RuntimeErrorand aborted the whole sync loop. Now the legs are cancelled, the reconstructed exit slot is dropped and the dispatch is skipped entirely — there is no position to protect. At the dispatch site the no-parent refusal became anOrderSkippedByPluginsoft-skip (reasonpartial_bracket_parent_untracked), so a parentless bracket intent drops per intent instead of taking down the run, and the parent reference is resolved before the envelope build so the skip leaves no half-registered envelope behind. -
Native fail-safe lifecycle events are logged unconditionally. State transitions, DEGRADED entry-gate engagement and ownership -> UNKNOWN external-edit detection now emit a broker-log warning in addition to the structured event sink. Live runs install no sink, so a degraded fail-safe was previously only visible through the secondary per-signal entry-block warnings.