Skip to content

v6.8.3

Latest

Choose a tag to compare

@wallneradam wallneradam released this 10 Aug 11:51
· 4 commits to main since this release

Nine measured TradingView defects, one measured performance win. Every behaviour below was
verified against TradingView itself before it was changed.

TradingView semantics

Float-to-bool conversion is tolerant. Pine converts a float to bool by the same rule its
comparison operators use: a value within 1e-10 of zero is false. PyneCore used Python's plain
truthiness, so arithmetic residue — the 2.4e-15 a stochastic pinned at its floor leaves behind —
fired signals TradingView never draws. Every bool context (if, while, ?:, and, or, not)
and the explicit bool() cast now use the measured band, behind a type guard that keeps ints, na,
strings and object references exact. Measured on BINANCE:BTCUSDT 30m: 1e-10 is false and
1.000001e-10 is true at both signs, na is false, and every bool context agrees.

Series history counts bars, not calls. A series whose owner did not run on a bar — a stateful
function called from one arm of a lazy ternary, a body only some bars enter — still advances one
slot and repeats the value it last saw. Measured on BINANCE:BTCUSDT 30m: a fractal test called only
when its sibling test is false matches the forward-filled model on all 23949 bars, while the
compacted model misses 769. Library modules keep their compacted rolling windows, where src[k] is
the k-th most recent non-na value.

Heikin Ashi candles are no longer snapped to the mintick grid. A synthesized Heikin Ashi candle
is an average of already cleaned feed values, so it carries no float32 storage artifact and does not
sit on the grid. Snapping it moved ta.ema(haClose, 34) by up to 2.2e-3; the raw average reproduces
TradingView's own plot to 1.5e-11.

ta.valuewhen no longer returns na when the current source is na. The remembered value survives
every bar the condition is false, and an na source on a condition bar is recorded verbatim: it
consumes an occurrence and is returned as na. This unblocks the common ta.valuewhen(pivot, pivot, n)
idiom, whose source is na on every non-confirmation bar.

ta.dmi smoothed a true range that is na on the first bar, which is not what ta.atr computes,
and its directional-movement selection compared exactly instead of tolerantly, like Pine's own
operators. Both measured over 22396 bars.

plotshape and plotchar default to the Shapes and Chars titles TradingView exports,
instead of Shape and Char.

parse_datestring accepts a colon between the date and the time and an unpadded hour, both of
which TradingView resolves.

Arrays

array.binary_search_leftmost and array.binary_search_rightmost follow the measured index
laws.
Both returned whichever element the binary search happened to land on for a duplicated
value, and leftmost used the rightmost miss rule: it answered the index of the last smaller element
without clamping, so a value below the whole array produced -1. That -1 reached array.get(),
which indexes Python-style and silently returned the last element. Measured on TradingView with
[10, 20, 30, 40] and duplicates: a hit answers the first duplicate for leftmost and the last for
rightmost; a miss steps one to the left for leftmost, clamped at 0, and to the first greater element
for rightmost, which may equal the array size. TradingView's own RelativeValue library depends on
that clamp — the unclamped -1 made it average the wrong end of a past period whenever a session's
opening bars were missing.

Negative indices work in array.get, array.set, array.remove and array.insert. They are a
Pine v6 feature and follow Python's rule exactly: get(a, -1) is the last element, while get(a, -5)
on a four-element array is out of range. array.insert extends the range by one position, since the
array size itself is a valid insert index.

array.fill rejects out-of-range bounds instead of resizing the array. index_from must address
an existing element and index_to may reach one position past the last one; anything else is an
error, as it is on TradingView. Previously a bound outside the array silently resized it — filling
[10, 20, 30, 40] with index_to = 99 grew it to 99 elements, and with index_to = -1 shrank it to
one. Reversed bounds fill nothing, matching the measured behaviour.

Strategy

strategy.exit no longer sizes its slice against a pyramid add that never happens. A
same-direction market entry issued while the pyramiding limit is already reached is dropped at order
processing without ever touching the position, so it must not enlarge the bound size a qty_percent
leg reserves. The inflated reservation was sticky as well, leaving the sibling leg with an oversized
share on every later bar.

A strategy.exit without from_entry binds to the open position, not to a pending entry order.
The distinction shows up on a reversal, where both exist at once with different ids: the exit covers
the position being reversed out of, and the position the reversal opens gets its own bracket from the
next bar's script run — so its stop cannot fire on the bar the reversal filled. Measured on
CAPITALCOM:EURUSD 60 over 580 trades: of the entries whose stop level was breached on their own fill
bar, all 5 opened from flat exited on that bar and the 1 opened by a reversal held. Binding pending
entries first also left the position being reversed out of with no bracket at all for the length of
that bar.

Data and sessions

request.currency_rate answers 1.0 for an empty or identical currency pair. That is Pine's "no
conversion" case, and Pine's own default-currency inputs pass an empty string — so a script using one
got na on every bar.

request.security at the chart timeframe places off-grid bars correctly. A security whose
session does not open on the chart's grid puts its bars off it, so its value belongs to the chart bar
whose close the security bar's close reaches, not to the one whose open it shares. Confirmation now
rides the child's close instants there too, exactly as the higher-timeframe path already did.

Symbol information carries session corrections. These are single-day exceptions to the weekly
schedule, so a bar on an exchange early close ends at that close instead of a full period later. The
exception is not derivable from the bars — the day's last one is an ordinary-looking stub — so it
comes from the calendar the data source publishes and round-trips through the symbol TOML.

A library method call is no longer broken by a local function of the same name. The string form
of a method call resolves its name at runtime against the receiver's type, never against the
enclosing module, so injecting a local function's captured arguments there was wrong whenever the
names collided.

Performance

Session data is derived once instead of every bar. A script's session strings are constants, yet
every bar re-parsed them and rebuilt the same localized datetimes. On a strategy with ten
request.security contexts the session helpers alone ran about 50 times per bar. The parse is now
memoized behind a wrapper that still resolves the exchange timezone per call, so a symbol change
takes effect; the specification became hashable, which lets the per-bar session lookup be memoized
too. math.max and math.min scan their operands for na with a plain loop instead of a generator
expression, which was pure overhead in the two most-called builtins of a rolling min/max loop.

Measured on the same script, data and window, alternating between the two revisions: 13.5s to 9.8s
over 5761 bars and 158.7s to 109.6s over 27583 bars, with byte-identical trades.