Skip to content

v6.10.1

Latest

Choose a tag to compare

@wallneradam wallneradam released this 19 Sep 07:34
· 8 commits to main since this release

Performance

request.security in backtests

This release continues the request.security() work of 6.10.0. Outputs are
unchanged on the tested scripts; each optimisation has its own switch to turn
it off.

  • Children run only the part of main() they need. Every static context
    used to re-run the whole main() on every bar in its child process, with
    only its own write taking effect. The child now runs a backward slice of
    main(): the context's own expression and every statement it depends on,
    including all persistent state carried across bars. Code the analysis cannot
    model (try, comprehensions, unresolved collection targets) leaves that
    context unsliced. Multi-context scripts run several times faster.
    PYNE_NO_SECURITY_SLICE=1 disables it.
  • One child process per feed group. Call sites whose arguments resolve to
    the same feed used to spawn one child each, each loading the same data and
    running its own slice of main(). Contexts whose signal arguments are
    provably the same are now grouped at compile time and served by a single
    child. This covers helper functions with a ternary between two calls and
    security(...) if flag else na. Lower-timeframe contexts are never grouped.
    Multi-call scripts on a shared timeframe run two to three times faster.
    PYNE_NO_SECURITY_MERGE=1 restores one child per context.
  • Developing-round skip. request.security(sym, tf, x[k], lookahead_on)
    with k >= 1 has the same value on every chart bar of one higher-timeframe
    period, so the developing round now runs only on the period's first chart
    bar. The skip applies only when the context's write is reached on every bar
    (no guard, no early return or raise ahead of it), and the sliced code declares
    no varip and calls no ta.valuewhen. On one corpus script the
    run time went from 91 s to 20 s. PYNE_NO_SECURITY_DEV_SKIP=1 disables it.
  • Cheaper rollback of a discarded re-execution (a developing re-tick, a
    calc_on_order_fills fill, a live intrabar tick). Function-instance state is
    undone slot by slot instead of copying every series buffer back, and the
    restore no longer deep-copies on every re-execution. A security-heavy
    indicator went from 75 s to 31 s with the developing-round skip disabled, and
    from 20 s to 15 s with it enabled.

Behaviour change

Module-level objects are read-only inside functions

A script may define anything at module level and read it anywhere, but writing
into a module-level object from inside a function now raises a SyntaxError
when the script is loaded. Rejected: a global statement, an assignment,
augmented assignment or del rooted at a module-level name, a mutating method
call on one (append, pop, clear, ...), and a mutating array.*,
matrix.* or map.* builtin on one. A local variable or parameter of the same
name shadows the module-level binding and is fine.

Such an object lives outside everything PyneCore rolls back, so a counter or
buffer kept at module level counted executions rather than bars whenever a bar
was re-executed (request.security developing bars, calc_on_order_fills,
live intrabar ticks). Keep bar-to-bar state in a Persistent (var) or an
IBPersistent (varip) inside the function instead. A write through an alias
or a parameter is not detected, and its behaviour is unpredictable. See
"Module-Level Objects Are Read-Only Inside Functions" in the scripting guide.

Fixes

Rollback keeps object identity

Arrays, maps, matrices and UDT instances are references in Pine, but the var
rollback (calc_on_order_fills re-executions, live intrabar ticks,
request.security developing re-ticks) replaced every slot with an independent
copy of its baseline. Two variables naming one object named two objects after
the first rollback, whether in the same scope, in a function instance or inside
a container. The restore now writes the saved content back into the same object,
recursively, with UDT cycles handled:

  • Aliases survive a rollback.
  • A discarded execution's mutation of an array held in a UDT field no longer
    survives the rollback (UDTs were copied shallowly).
  • A matrix.set() of a discarded execution no longer leaks into the restored
    state (the snapshot shared the matrix's rows).

request.security

  • Security children now receive the runner's programmatic input overrides.
    They previously computed the context with the .toml or source default
    values.
  • A child that had already exited could leave the chart waiting forever:
    its exit code was read before the process was reaped, and the resulting
    None was treated as a clean exit. The death is now always detected.

Drawings

  • polyline.new stores its own copy of the points. Clearing, refilling or
    pushing to the caller's array afterwards, or mutating one of its
    chart.point objects, no longer reshapes a polyline that was already drawn,
    matching TradingView.

Live trading

  • The broker's order event stream (watch_orders) is reopened with bounded
    backoff (1 s up to 30 s) after an unexpected error. Before, the stream ended
    and fills and cancels were no longer pushed to the bot while the strategy
    kept trading. A manual-intervention halt still stops it.