Twelve fixes measured against TradingView, most of them found by running a large corpus of published Pine scripts side by side with PyneCore.
Strategy engine — TradingView parity
- Same-instant reversals are booked long side first. When two opposite entry orders waited in the order book together and filled at one moment, the long order is the closed record's entry and the short one its exit, whichever was placed first. The tie falls back to chronological order when the legs fill at different moments, and when the reversal order was placed by the
calc_on_order_fillsre-execution that the other leg's own fill ran. pyramidingis judged whenstrategy.entryruns, against the position the market orders already placed on the bar leave behind — not when its market order is processed. A rejected entry is gone for good; aclose_allahead of a same-id re-entry hands its slot over, and a pending reversal makes the next opposite-direction entry a reversal of its own. A call that only modifies this bar's unfilled order under the same id is exempt.strategy.exitlegs bound to a still-pending entry now belong to that entry: their tick offsets wait for it to fill instead of freezing off an older trade sharing thefrom_entryid, and the leg is dropped at the next bar open when the entry never opens. A leg whose entry still has an order waiting to fill also survives theclose_allthat spent its binding, and is walked on the entry bar itself.strategy.close/strategy.close_allsame-bar stacking keys on the call site, so closes from different statements stack while one statement executed twice on a bar modifies its own pending order.process_orders_on_close=trueno longer disablescalc_on_order_fills. On the bar where a resting exit fills, the body runs at the fill's node and again at the close.- A price-based entry freezes its reversal augmentation from the position as the market closes already placed on the bar leave it: close-before-entry flips nothing, entry-before-close flips the full position.
strategy.risk.max_intraday_lossis an intra-bar check: the position is flattened at the bar's unfavorable extreme with slippage applied against it, like a margin call. The halt blocks only the rest of the trading day, and the rule stays dormant while a day opens with non-positive equity.strategy.close(immediately=true)exports the slipped fill price, and its P&L follows that price exactly.- Big-money sizing quantizes its money budget to 10 significant decimal digits at every magnitude, not only below 1e7, and the gate reads the tick count through a decimal division that cannot fall a tick short. An entry the gate refuses to open keeps its closing leg when it reverses an open position.
commission_typeaccepts the undocumentedstrategy.cashvalue, charged as a flat fee per order.- A
calc_on_order_fillsre-execution no longer re-walks bar legs the emulator has already passed.
Builtins
ta.highestandta.lowestre-read the whole window on the bar a varying length grows back. The kept extreme only expired on age, so a value evicted while the length was short stayed forgotten once the length reached over it again.array.sum,array.avg,array.variance,array.stdev,array.standardizeandarray.covariancematch TradingView bit for bit: front-to-back plain double arithmetic, the distributed unbiased variance form, and pair-wisenadropping for covariance.
request.security and sessions
- The
lookahead_ondeveloping-bar transport and the closed-override branch now anchorlast_bar_index/last_bar_timeto the child file's final bar while the replayed period is historical. A recedinglast_bar_timeused to dragchart.right_visible_bar_timewith it, firing anif time == chart.right_visible_bar_timeblock on every child bar. - US half-day closes (Black Friday, Christmas Eve) confirm their daily HTF bar on the right bar. The chart's own bar grid now decides the single-period D/W/M confirmation; live has no next bar and keeps the schedule, so no value is emitted that a real-time run could not produce.
runtime.erroris a no-op inside arequest.securitychild. A chart-level resolution guard used to kill the worker and take the chart run down with it.
Data and runtime
- Exchange-rate feeds outrank chart prices as a currency-conversion source; chart data is the last resort.
- Lossless price storage: a v2 OHLCV file with a declared tick grid reads its OHLC back exactly. The old 6-significant-digit clean-up damaged legitimately off-grid prices — a split-adjusted
NASDAQ:AAPL30m price of 70.90875 came back as 70.9087 and put 104 of 21642 plotted bars off TradingView. - Bar-magnifier trade numbering: the open trade exported at end of data no longer restarts at 1 and collides with the first closed one.
pine_loopre-reads thetobound of aforloop before every iteration when that bound is not loop-invariant.shadowed_namespace: an import alias spelling a builtin namespace shadows it member by member, with the library's exports winning and everything else falling back to the builtin.
Live trading
- A fresh close client order id is minted for every reversal cycle. The pinned envelope survived its cycle, so a later reversal rebuilt a spent client order id and an idempotency-caching venue answered with the already-filled order instead of placing a new close.
- The pending-open marker retires when its entry re-dispatches on the plain path. A declined reversal close could re-arm the marker; if an external flatten then cleared the book, the next reversal-close fill replayed the spent marker as a duplicate raw entry.
New
timenowis pinned to the last bar of a historical replay. A backtest that reads the real clock is not reproducible: a script gating entries ontimenowmeasured a different bar set on every run.pyne runnow anchorstimenowto the last bar of the window it replays, so a run is determined by its data alone. Live and broker runs keep the system clock. The pin reachesrequest.securitysubprocesses throughPYNE_TIMENOW_MS.