Skip to content

v6.8.14

Latest

Choose a tag to compare

@wallneradam wallneradam released this 30 Aug 06:36
· 1 commit to main since this release

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_fills re-execution that the other leg's own fill ran.
  • pyramiding is judged when strategy.entry runs, 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; a close_all ahead 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.exit legs 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 the from_entry id, 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 the close_all that spent its binding, and is walked on the entry bar itself.
  • strategy.close/strategy.close_all same-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=true no longer disables calc_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_loss is 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_type accepts the undocumented strategy.cash value, charged as a flat fee per order.
  • A calc_on_order_fills re-execution no longer re-walks bar legs the emulator has already passed.

Builtins

  • ta.highest and ta.lowest re-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.standardize and array.covariance match TradingView bit for bit: front-to-back plain double arithmetic, the distributed unbiased variance form, and pair-wise na dropping for covariance.

request.security and sessions

  • The lookahead_on developing-bar transport and the closed-override branch now anchor last_bar_index/last_bar_time to the child file's final bar while the replayed period is historical. A receding last_bar_time used to drag chart.right_visible_bar_time with it, firing an if time == chart.right_visible_bar_time block 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.error is a no-op inside a request.security child. 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:AAPL 30m 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_loop re-reads the to bound of a for loop 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

  • timenow is pinned to the last bar of a historical replay. A backtest that reads the real clock is not reproducible: a script gating entries on timenow measured a different bar set on every run. pyne run now anchors timenow to 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 reaches request.security subprocesses through PYNE_TIMENOW_MS.