Skip to content

v6.9.0

Choose a tag to compare

@wallneradam wallneradam released this 05 Sep 19:17
· 4 commits to main since this release

This build was never installable. The published package was missing a data file it reads at
runtime, so import pynecore failed with FileNotFoundError. Install
v6.9.1 instead — it carries everything
below plus the packaging fix.

Pine int is a static type — the runtime is double-only, like TradingView

TradingView runs every number as a double; int only exists in the type checker. PyneCore now
does the same: an int-typed value travels as a float carrying an integral value, na(int) is
a plain nan, and truncation happens at the slots that genuinely consume an integer instead of
at storage. This is what makes int-typed expressions with a fractional payload (14 / 8 is
int-typed and equals 1.75 on TradingView) behave identically here.

  • bar_index, last_bar_index, last_bar_time, the syminfo int fields, input.int(),
    time()/year()/dayofweek(), array.size(), str.length(), ta.barssince(), the
    strategy trade counters and color.r/g/b hand back floats
  • Series[int] and Persistent[int] slots store floats; NA(int) / na_int are the native
    nan (pickling stays exact)
  • int() in a script is Pine's cast (truncate toward zero, na stays na); inside a
    @pyne lib module it is the lib's own native truncation
  • Truncation at the consumer: range() arguments, the index or slice bound of a Python list,
    str or tuple, Series[...] offsets, str.substring/str.replace positions,
    math.random seeds, table coordinates, strategy.closedtrades/opentrades indexes,
    array.new_int sizes, timeframe.from_seconds counts and drawing-object x coordinates
  • Storage preserves the value: array.new_int(1, 7 / 4) holds 1.75, as on TradingView
  • The plot CSV export, str.tostring, str.format and log.* print an integral value without
    a fractional part; the OHLCV reader parses extra fields as floats
  • Series subscripts take a float offset on a single fast path (a 60% slowdown on a for-loop
    indicator measured during development is gone)

The consequences for hand-written Pyne code are documented under
Types — int:
a plain f-string shows bar=0.0, so format with str.tostring or a format spec, and use
round() where a genuine Python int is needed.

Static Pine type inference and overload pinning

A new analysis pass infers Pine's static types over the script and stamps them on the AST —
scalars, shaped containers (array<T>, matrix<T>, map<K, V>), tuples and user classes —
following the measured TradingView algebra (int op int stays int for every operator, division
and modulo included). On top of it:

  • Overload calls are resolved statically and pinned, so an int-typed value with a fractional
    payload still picks the int implementation; the per-bar dispatch becomes a cache read.
    PYNE_NO_TYPE_PIN=1 disables the pin. A pin position with no runtime witness takes a
    wildcard instead of refusing the whole site.
  • An unannotated helper is typed per call-site context (the join of the default's type and the
    argument's type, as TradingView does it); contexts that disagree carry the pin as instance data.
  • Modules publish a type interface (__pycache__/<stem>.<hash>.pynetypes.json), imported user
    libraries are typed from their declared signatures, and cached bytecode is dependency-aware:
    a changed interface invalidates the importer's .pyc.
  • New @pyne edge gate: PYNE_EDGE_STRICT=1 rejects constructs outside the edge subset and
    reports one diagnostic per typing failure, pointing at the user's line. Off by default; lib
    modules are never gated.

Three-state bool as a per-script switch

Pine v4/v5 keep a bool that may be na; v6 has no such state. The choice now belongs to the
script: script.indicator/strategy/library(..., na_bool=True) keeps the three-state bool, the
default is v6's two-state bool. NA(bool) is False with the switch off and a real na with
it on; Series[bool] history, array.new_bool, UDT bool fields, ta.change and nz follow.
An exported library function runs with the bool semantics of the module it was defined in, and
a library UDT field defaulting to na(bool) follows the calling script's mode.

Fixes

  • math.round(number, precision) follows the measured TradingView rule: half away from zero,
    the tie decided on the exact product with a 1e-10 tolerance, a fractional precision used
    continuously, precision capped at 16, a negative precision clamped to zero. Exact against the
    measured outputs for every precision up to 15.
  • math.round_to_mintick decides its tie-break on the native int tick pair, so the float
    pricescale/minmove cannot flip a measured tie.
  • Rolling ta.percentile_nearest_rank / ta.percentile_linear_interpolation follow
    TradingView's na rank order: an na keeps a slot of its own, the interpolation is
    unconditional, and an na bar no longer blanks the result. ta.median returns the mean of the
    two middles, ta.change takes the bool branch first.
  • strategy.closedtrades / strategy.opentrades accessors handle an na trade number instead
    of raising.
  • box, label and line coordinates normalize at the library boundary (x truncated to the
    bar grid, y untouched).
  • Broker engine: teardown joins the event-stream task before closing the store and the
    transport; resting spot exit legs are cancelled before a full close and OCA sibling fills are
    recognized regardless of event order; a read outage no longer halts the run — overdue reads
    are abandoned and new exposure deferred, with repeated outage warnings throttled; stale
    pending partial legs are retired on a re-used entry id; a venue-attributed cancel on a
    reduce-only bracket leg is trimmed like an OCA sibling instead of tripping the
    unexpected-cancel policy; envelope anchors persist the run_tag (store migration 7) so a
    rotated bot no longer misnames the previous bot's orders; a skipped partial bracket close is
    logged and its legs re-armed.
  • Loader: _lower_tree returns the allocated slot layout beside the tree for consumers that
    build the state vector themselves.

Tests

  • New aot_unsupported pytest marker for scripts that exercise constructs a compiled backend
    cannot answer; harness helpers in @pyne test scripts carry the __test_helper_ prefix.