Runtime Bridge
pynescript.runtime.Runtime — package SoT bar-loop evaluate façade, PineSeries context, compile mode, and result packaging.
This page
Runtime Bridge
Abstract
Package Runtime SoT: the bar-loop host, series, and CustomEvaluator live under pynescript.runtime (src/pynescript/runtime/host.py). The Pro API still imports backend.runtime.Runtime, which re-exports the package implementation (sys.modules alias) so monorepo and wheel installs share one host.
Runtime owns symbol metadata namespaces (syminfo, timeframe, barstate, …), walks OHLCV bars, updates PineSeries for OHLC, visits a shared CustomEvaluator on the parsed AST each bar, drains strategy events, and packages multi-plot series for AXIS. Compile mode optionally swaps the AST walker for a Numba-backed subset engine. Optional timeout_seconds provides a wall-clock circuit breaker on the interpret path (checked every 32 bars) for edge/cron budgets. Flask /run and /run/batch accept the same field (omit / null / ≤ 0 → no timeout).
This is the bridge between Flask handlers and the language core — not a second semantics.
Conceptual model
Rendering…
Interface surface
Construction
Runtime(symbol: str = "AAPL", run_id: str | None = None)
- Sets
syminfo.tickerid/name/prefix(prefix fromEXCHANGE:SYMBOLform). - Generates
run_idasuuid4().hex[:16]when omitted.
Helpers: configure_footprint, update_bid_ask.
run(source_code, ohlcv_data, data_feed=None, data_provider=None, mode="interpret", …)
| Arg | Role |
|---|---|
source_code | Pine text |
ohlcv_data | list[dict] with open/high/low/close/time[/volume/bid/ask] |
data_feed / data_provider | request.* wiring; auto-resolved from chart bars when unset |
mode | "interpret" (default for direct calls) · "compile" · "auto" (Pro API schema default) |
timeout_seconds | Optional wall-clock budget (seconds); interpret path only — partial results + timed_out + error_kind=runtime when exceeded. Flask /run and /run/batch accept it (omit / ≤ 0 = no budget) |
libraries | Optional [{namespace, name, version, source}] registered via register_library_source before import ns/Name/ver. mode=auto forwards libs into interpret fallback. Flask /run caps at 32 |
inputs / profiler | input.* overrides (interpret); non-empty inputs or profiler force interpret under auto (compile_fallback_reason) |
realtime_* | Optional host simulation for varip multi-tick tests (realtime_last_bar, realtime_ticks, realtime_bars, realtime_from_bar) |
Interpret path (default)
- Resolve request sources (non-fatal on failure).
parse(source_code, mode="exec")— on failure{ "error": "Parse Error: …" }.- Init
PineSeriesfor OHLC + context dict (timeframe daily defaults, barstate, chart colors). - Construct
CustomEvaluator(context=…, data_feed=…, data_provider=…), reset var declarations + drawing registry +clear_alerts(). - Per bar:
- Update series + calendar fields from timestamp
- Update barstate flags (
isfirst/islast/ history confirmed) process_pending_orderswhen availableevaluator.visit(tree)— on failure{ "error": "Runtime Error at bar …" }- Lock pine defs after first bar (
_pine_defs_locked) to avoid multi-dispatch blow-ups - Drain strategy events; stamp
script_id/run_id - Capture plot outputs
- Build
series/plot_metafrom all plot indices (disambiguate duplicate titles with_2suffixes). - Export drawings via
DrawingRegistry.export_for_api(bar_times). - Export alerts via
export_alerts_from_evaluator(optionalalert_conditions).
Return keys: plots, series, plot_meta, events, drawings, alerts, count, script_id, run_id, mode (+ optional alert_conditions, timed_out, error_kind).
mode=auto
- Non-empty
inputs→ interpret immediately (compile_fallback_reason = "input.* overrides require interpret path"). _compile_eligible: reject top-levelimportand anyrequest.token (cached per source hash).- Eligible →
_run_compiled. On success setauto_backend=compile. On compile/runtime error, fall back to interpret and setcompile_fallback_reason. - Ineligible → interpret +
compile_fallback_reason(e.g."import statements not supported in compile path").
Value mismatch never triggers a backend switch — that is the parity harness’s job.
Compile path
- Import
pynescript.compiler.engine— missing →error_kind=compile. - Do not require
has_numba(). Object-mode scripts (strategy, UDT, drawings) compile to a Python/numpy loop. Missing Numba only fails pure-numeric emit (CompileNumbaRequiredError);autocaches that failure. - Host LRU by raw-source sha256 →
compile_scripton miss. - Pack OHLCV to float64 columns including
time=bar-open ms (_ohlcv_pack_cached). compiled.run(opens, highs, lows, closes, volumes, time=times).- Lift
__drawings/__events; GC compile drawings by declaration caps; merge visual series (bgcolor / plotshape / …) into titled keys. - Envelope:
mode: "compile",alerts: [],object_mode,compile_cached,compile_ms.generated_codeonly whenPYNESCRIPT_RETURN_GENERATED_CODE=1.
PineSeries (pynescript.runtime.series)
History deque (default maxlen 1000, raised by max_bars_back / PYNE_SERIES_MAX). PYNE_SERIES_CAP default on (list-length trim). PYNE_SERIES_RING default off (chronological O(1) lookback when 1). series[0] current / series[n] lookback, arithmetic on current values, None propagates as na-like absence. Negative indices raise — Pine Script™ does not allow them. backend.series re-exports the package type.
Context namespaces
Defined on the host: Syminfo, Chartinfo, Timeframe, Barstate, Chart — attribute names aim at Pine Script™ v5–v6 surface (timeframe.isdaily, syminfo.isin, …).
Internals
| Path | Role |
|---|---|
src/pynescript/runtime/host.py | Package SoT Runtime bar loop |
src/pynescript/runtime/evaluator.py | CustomEvaluator specialization |
src/pynescript/runtime/series.py | PineSeries |
backend/runtime.py | Compat shim → pynescript.runtime |
backend/evaluator.py / backend/series.py | Compat shims |
src/pynescript/ast/helper.py | parse |
src/pynescript/ast/evaluator/** | Builtin + strategy semantics |
src/pynescript/compiler/engine.py | Compile mode |
Flask wiring: backend/app.py constructs a fresh Runtime per request (and per batch job) so run_id and drawing registries do not leak across users.
Invariants and edge cases
- Parse once, visit many — AST is not re-parsed per bar.
- Defs locked after bar 0 — performance invariant for large function tables.
- Drawing registry reset at run start — isolation between requests.
- Primary
plotslist is plot index 0 for backward compatibility; AXIS should preferseries+plot_meta. - Errors are dicts, not exceptions, for control-flow at the HTTP boundary (
"error" in result). - Compile mode supports a subset;
mode=autofalls back to interpret withcompile_fallback_reasonrather than failing the request.
Worked example
from pynescript.runtime import Runtime
# or: from backend.runtime import Runtime # Pro API monorepo path
rt = Runtime(symbol="NASDAQ:AAPL")
out = rt.run(
'//@version=6\nindicator("x")\nplot(close, "C")',
[{"time": i, "open": 1, "high": 2, "low": 0.5, "close": 1.0 + i * 0.01, "volume": 1} for i in range(50)],
)
assert "error" not in out
assert out["count"] == 50
assert "C" in out["series"] or "plot_0" in out["series"]
Failure modes
| Message prefix | Cause |
|---|---|
Parse Error: | Grammar / syntax |
Runtime Error at bar | Evaluator exception |
Order fill error at bar | Broker sim pending orders |
Compile mode requires numba / CompileNumbaRequiredError | Numeric emit without Numba (object-mode scripts still run) |
Compile Error: / Compiled Runtime Error: | Engine path (error_kind=compile / runtime) |
Script execution timed out | timeout_seconds exceeded (timed_out, error_kind=runtime) |