[Runtime Bridge]
backend.runtime.Runtime — bar-loop evaluate façade, PineSeries context, compile mode, and result packaging.
Runtime Bridge
Abstract
backend.runtime.Runtime is the HTTP stack’s evaluate façade. It 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.
This is the bridge between Flask handlers and the language core — not a second semantics.
Conceptual model
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) or "compile" |
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. - 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).
Return keys: plots, series, plot_meta, events, drawings, count, script_id, run_id, mode.
Compile path
- Import
pynescript.compiler.engine— missing → error string. - Require
has_numba(). compile_script+compiled.run(opens, highs, lows, closes, volumes).- Pop internal keys (
__drawings,__events, position meta). - Return similar envelope with
mode: "compile", plusobject_mode/generated_codewhen present.
PineSeries (backend/series.py)
History deque (default maxlen 1000), series[0] current / series[n] lookback, arithmetic on current values, None propagates as na-like absence. Negative indices raise — Pine does not allow them.
Context namespaces
Defined in runtime.py: Syminfo, Chartinfo, Timeframe, Barstate, Chart — attribute names aim at Pine v5/v6 surface (timeframe.isdaily, syminfo.isin, …).
Internals
| Path | Role |
|---|---|
backend/runtime.py | Runtime |
backend/evaluator.py | CustomEvaluator specialization |
backend/series.py | PineSeries |
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 (documented in engine — ta/math/plots family); unsupported scripts should use interpret.
Worked example
from backend.runtime import Runtime
rt = Runtime(symbol="NASDAQ:AAPL")
out = rt.run(
'//@version=5\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 | Missing dependency |
Compile Error: / Compiled Runtime Error: | Engine path |