Skip to content

v6.8.1

Choose a tag to compare

@wallneradam wallneradam released this 06 Aug 15:32
· 11 commits to main since this release

This release is about scripts that used to stop running. Most of what is fixed here does
not change a number — it removes a TypeError, a ValueError or an AttributeError that
halted a script outright, in situations TradingView handles and keeps going through: an
na array index, a float index, a keyword argument spelled the way Pine spells it, a
mutating call on an array.slice view, a for loop over a matrix, a tuple-shaped
request.seed.

The second theme is drawings. A bar's script body can run several times — after every fill
with calc_on_order_fills, on every intra-bar tick in live mode — and until now the
discarded runs left their drawings and plot columns behind. That is fixed, together with a
set of copy bugs that produced chart objects the chart never showed.

Behavior changes

  • array.slice validates its bounds. An invalid range now raises instead of being
    silently normalized, matching TradingView, which halts there with RE10045 (and
    RE10044 for a reversed pair). index_from == index_to stays a legal empty slice.
  • An order with an unsizable quantity is not placed. strategy.entry,
    strategy.order and the deferred-qty resolution require a positive finite quantity, so
    NaN and infinity are dropped like a zero instead of halting the run in the lot
    rounding.
  • color.rgb no longer raises on out-of-range arguments. It clips them, truncates
    fractional channels and accepts na, the way TradingView does.
  • A number of parameter names changed to Pine's spelling (str.contains, str.endswith,
    str.startswith, str.pos take str; str.format_time takes format / timezone;
    math.acos / asin / atan take angle; math.todegrees takes radians;
    math.toradians takes degrees; color.rgb takes red / green / blue;
    str.replace takes occurrence; na takes x; map.put_all takes id2;
    ta.alma / bb / bbw / cmo / mfi take series; ta.supertrend takes
    atrPeriod). Hand-written Pyne code passing any of the old names by keyword needs
    updating; every internal caller was positional.
  • strategy.exit lost its phantom oca_type parameter. It was a 13th parameter Pine
    does not have, and it shifted every parameter after it, so a positional call assigned
    comment to oca_type, alert_message to comment, and so on.
  • array.standardize no longer thresholds int arrays. TradingView standardizes int
    and float arrays identically; the old -1/0/1 output was wrong.
  • matrix.copy() is no longer a deep copy. Like array.copy(), it copies only its row
    storage; drawings, chart points and user-defined instances stay shared, which is what
    TradingView does.

Features

  • strategy.opentrades.capital_held: the summed entry value of the open trades. It does
    not follow the market and a short trade contributes just like a long one. The margin
    percentages do not scale it, but a strategy with no margin requirement on either side
    reports na.
  • array.new_table. PyneComp already compiled Pine's array.new<table> to it, so such a
    script used to reach a function that did not exist.
  • Four new label setters: label.set_point, label.set_xloc,
    label.set_text_font_family and label.set_text_formatting.
  • strategy.entry, strategy.close and strategy.close_all accept Pine's trailing
    disable_alert, and request.dividends / request.earnings accept currency. Without
    them a script naming any of these raised TypeError.
  • Overload dispatch selects on the exact argument type before widening. With f(float)
    and f(int) declared in either order, an int argument — literal, variable, series or a
    typed na — takes the int one; only a script with no int implementation widens to
    float. A container discriminates on its element type the same way, sampled from a real
    one or read off the declared subscript of an na.

Fixes

Scripts that used to halt

  • An na array index raised TypeError in array.get, set, remove, insert,
    slice, fill, percentrank, max, min and both percentile functions. Measured on
    TradingView: get and remove return na, set is a no-op, insert appends, slice
    and fill resolve an na bound to 0 or to the size, and percentrank / max / min
    behave as index 0.
  • A float index — from a division, from math.round — halted with "list indices must be
    integers". It is truncated now, across the array and matrix namespaces, always after
    the na guard.
  • matrix.get / set / row / col raised on an na index. They return element-typed
    na, an na array, or do nothing.
  • for row in m over a matrix halted with "'Matrix' object is not iterable". The iterator
    yields the row lists themselves, as TradingView documents, so writing through the loop
    variable changes the matrix.
  • Every structural mutation of an array.slice view died with "'SequenceView' object has
    no attribute 'append'" — push, insert, unshift, remove, pop, shift, clear.
    The view is a live, writable index range now, clipped to the parent's current length, so
    a stale sibling slice no longer raises IndexError after the parent shrinks.
  • request.seed returned a single na for a tuple expression, so the compiled
    destructuring halted with "na is not iterable". A tuple expression yields a tuple of na
    of the same arity.
  • str.split with an empty separator raised "empty separator". Pine returns the individual
    characters.
  • math.random without an explicit seed returned na on every draw: the na default was
    handed to the generator, which XOR-ed it into its state.
  • A matrix<float> m = na declaration made na(m) answer False, so the first matrix
    call on it died with "'bool' object has no attribute ...". A subscripted user generic is
    matched on its origin now. The same paths also gained PEP 604 union support, stopped
    letting a bool answer an int parameter, and no longer merge NA(int) with NA(str)
    in the selection cache.

Drawings

  • A discarded body execution left its drawings behind. They piled up in the registries,
    filled the script's max_lines_count budget and evicted live drawings. A new drawing
    snapshot restores the six registries and every live drawing's fields — written back into
    the same objects, so a handle held by a script variable stays attached to its chart
    object. Plot data was not cleared either, so a re-executed bar exported the discarded
    run's value under the real title and the later runs as <title> 0 / <title> 1 columns.
  • The var-slot snapshot cloned every var-held drawing, producing duplicates that carried
    the same id, sat in no registry and never reached the chart. Measured on a 30-bar run:
    three objects ended up carrying id 1, and no script variable pointed at the registered
    one, so every later label.set_* updated an object the chart never shows.
  • A Pine .copy() on a label, line or box produced a field-wise clone with the source's
    id that entered no registry, never reached the chart and never counted toward
    max_*_count. The same call on an array, a map or a matrix raised TypeError. Copies
    now dispatch to the owning namespace, which assigns a fresh id, registers the clone and
    enforces the cap.
  • calc_on_order_fills did not re-execute the body at all when the script had no root
    variable slots; a live every-tick script re-executed on fills with the option off; and
    the last warmup bar continued by the live feed was counted twice, permanently offsetting
    every var counter and drawing for the rest of the session.

Values and semantics

  • str.replace picked the wrong occurrence: occurrence=1 replaced the first match, and
    a source without an nth occurrence was still modified. Occurrences are 0-based and
    enumerated by an overlapping left-to-right scan, so "aa" occurs at both index 0 and 1
    of "aaa".
  • array.sort and sort_indices left values around a single na element unsorted,
    because Python's comparisons against na are all False. na goes to the end of a
    numeric array and to the front of a string one.
  • array.standardize raised on na elements instead of skipping them.
  • The percentile functions raised on an empty array and disagreed between their array and
    rolling ta faces about an na percentage.
  • array.fill through a slice wrote nested lists into the parent array.
  • time(timeframe="1M", timeframe_bars_back=1) landed in the month before the intended
    one — always on the first of a month following a 30-day one, and on the first three days
    of March — because the offset subtracted a month's nominal 30.4375 days before snapping
    to the grid. A positive offset walks the grid one bar at a time now.
  • color.rgb rejected everything but integers in 0-255. Measured on TradingView: a
    fractional channel is truncated (127.5 gives 127), an out-of-range argument is
    clipped, and an na argument counts as 0 for a channel and as fully transparent.
  • strategy.order ran the backtest-only lot floor unconditionally, so in broker mode a
    positive sub-lot quantity vanished before an intent was built.

Internals and docs

  • The na guards use the single positive test not (x == x), which catches both
    representations in one comparison. Measured on the per-bar guard: 18.3 ns down to 5.8 ns
    for a non-na value, the case every bar takes. na(float) itself went from 0.147 s to
    0.039 s per million calls.
  • The strategy tests now require the whole TradingView reference to be consumed. The
    comparator is a context manager that pairs entry rows with exit rows up front, drops the
    trade left open at the end of a backtest, and fails if a trade is missing from the tail
    of a run. Measured on the current corpus: 14576 closed trades compared across 12 tests.
  • The library reference documents na handling, index coercion, copy behavior, array
    views, string replacement, strategy parameters and drawing semantics; math.random,
    array.new_table and strategy.opentrades.capital_held are documented, and the
    ta.kc / kcw parameter tables no longer disagree with the code.