[Runtime]

Bar-loop evaluator, series model, builtins, strategy events, and the optional Numba compile path.

Runtime

Abstract

PYNE’s runtime is the deterministic bar-loop that turns a parsed ASDL AST into series values, drawing side-effects, and strategy events. It is deliberately host-agnostic: the same visitor mixins power the library API, the Flask Pro API (Runtime.run), browser Pyodide, and edge workers. A second path—source-to-source compilation into a Numba or object-mode bar loop—trades interpretive flexibility for throughput on long histories.

This section documents semantics, not the chart. AXIS consumes plots; HOOX consumes strategy events; neither is required to evaluate a script.

Conceptual model

Two execution modes share the same parse tree:

ModeEntryBar loopTypical use
InterpretNodeLiteralEvaluator.visit(tree) per barHost updates PineSeries / context, re-visits ASTFull language surface, strategy, drawings
Compilepynescript.compiler.engine.compile_scriptGenerated for __bar_idx in range(n)Numeric indicators (Numba) or object-mode subset

Interface surface

ConcernStart here
Series history, [], var/varip, naSeries & history
Expressions, statements, control flowExpressions & statements
Builtin namespaces (ta.*, strategy.*, …)Builtins hub
Library export / importLibraries
StrategyEvent parity contractEvents
Numba / object-mode compilerCompiler overview

Library callers typically use NodeLiteralEvaluator (or the Pro API Runtime) rather than wiring the bar loop by hand:

from pynescript.ast.evaluator import NodeLiteralEvaluator

ev = NodeLiteralEvaluator(context={"close": [1.0, 2.0, 3.0], "bar_index": 2})
result = ev.evaluate_script('//@version=5\nindicator("x")\nplot(close)')

Hosts that own OHLCV (Pro API, workers) implement the loop: push bar → fill pending orders → visit(tree) → drain events → collect plots. See backend/runtime.py for the reference implementation.

Internals

PathRole
src/pynescript/ast/evaluator/Mixin evaluators (base, names, expressions, statements, libraries, events)
src/pynescript/ast/evaluator/builtins/Builtin dispatch tables
src/pynescript/compiler/CompilerVisitor, Numba builtins, strategy broker, engine façade
backend/runtime.pyProduction bar loop + mode="compile" bridge
tests/test_evaluator.py, tests/test_parity.py, tests/test_strategy_*.pySemantic oracles

Composition of the full interpreter:

BaseEvaluator
  + LiteralEvaluator
  + NameEvaluator
  + ExpressionEvaluator
  + StatementEvaluator
  + BuiltinEvaluator
  → NodeLiteralEvaluator

BuiltinEvaluator itself aggregates mixins (TechnicalAnalysisMixin, StrategyBuiltinsMixin, PlottingFunctionsMixin, …) into one dispatch map built at construction time.

Invariants & edge cases

  1. Bar re-entrancy. The AST is visited once per bar. Function/type definitions lock after bar 0 (_pine_defs_locked) so multi-dispatch tables do not grow (O(\text{bars}^2)).
  2. Order of broker steps. Pending limit/stop fills run before script body evaluation on each bar (interpreter and compile object-mode agree).
  3. na is None. Unresolved missing data, OOB history, and arithmetic with missing operands produce Python None, not IEEE NaN—except on the Numba path, which uses np.nan as the array sentinel.
  4. Strategy state is per-evaluator. StrategyState lives on the instance (evaluator._strategy_state), never as a process-global singleton—required for concurrent runs and parity tests.
  5. Drawing/plot registries are side channels. Visual objects do not participate in pure expression values except where plot() returns a plot id for fill().

Worked example — minimal bar loop mental model

for bar_index, bar in ohlcv:
    open/high/low/close.update(bar)
    context[bar_index, time, barstate, …] = …
    process_pending_orders(OHLC)   # broker sim
    evaluator.visit(ast)           # script body
    events += drain_events()
    series.append(plot values)

Compile mode collapses this into one generated function over full arrays, with __bar_idx standing in for bar_index.

Failure modes

SymptomLikely cause
unexpected type of node: …AST node not implemented in any mixin’s visit_*
Unknown built-in function: …Missing dispatch key or wrong qualified-name resolution
Divergent strategy events vs TVPartial-fill / OCA / risk settings, or fill timing vs bar
Numba path raises / falls backScript uses UDT/map/drawing → object mode; or Numba not installed
Plots empty but no errorHost forgot to collect plot_outputs / PlotRegistry after visit

See also