PYNE CONSOLIDATED DOCUMENTATION — LLM CONTEXT PACK Generated: 2026-08-16 Source: docs/pyne Public: https://hoox.sh/pyne/docs Repository: https://github.com/hoox-sh/pyne FILE: docs/pyne/agent/chat.mdx Copyright (C) - jangoblockchained This file is part of pynescript. SPDX-License-Identifier: AGPL-.-or-later --- title: "pyne-agent-worker chat" description: "POST /v/chat, search, sessions, and the optional pyne-worker validate loop." --- pyne-agent-worker chat Abstract Chat is POST /v/chat. When APIKEY is unset, open mode is allowed for local dev only. Admin embed/index always require APIKEY. Sister repo: hoox-sh/pyne-agent-worker. Endpoints | Method | Path | Auth | Role | | --- | --- | --- | --- | | GET | /health | no | Liveness + mode | | GET | / | no | Demo chat UI | | POST | /v/chat | yes | NL → Pine | | GET/POST | /v/search | yes | Vectorize search | | POST | /v/sessions | yes | Create session | | GET | /v/sessions/:id | yes | Session + messages | | POST | /v/admin/embed | yes | Batch embeddings | | POST | /v/admin/index | yes | Embed + R + Vectorize | | GET | /plugin/axis-pine-agent.js | no | AXIS plugin module | \ Required when APIKEY is set. POST /v/chat ``json { "message": "v RSI strategy with ATR trailing stop", "pineversion": "v", "style": "strategy", "sessionid": null, "validate": true, "maxretries": } ` Response includes reply, extracted pine, validation (attempts / pyne-worker errors), rag hits, and a trademark disclaimer. Validate loop (optional) Only when PYNESERVICE or PYNEWORKERURL is set: `text generate (Workers AI) → extract `pine → POST pyne-worker /run (synthetic OHLCV) → ok? return → else fix prompt + retry (max_retries) ` Without a binding, validation.available is false and the Worker returns the first draft. See also pyne-agent-worker Deploy pyne-worker /run` PyneTS — in-process TS evaluate, not this chat API --- FILE: docs/pyne/agent/deploy.mdx Copyright (C) - jangoblockchained This file is part of pynescript. SPDX-License-Identifier: AGPL-.-or-later --- title: "pyne-agent-worker deploy" description: "Standalone Workers AI deploy, optional RAG, and optional pyne-worker validate binding." --- pyne-agent-worker deploy Abstract Deploy from hoox-sh/pyne-agent-worker. Minimum is Workers AI. Vectorize / R / D improve quality; they are not required to chat. Standalone ``bash cd ~/Git/pyne-agent-worker bun install Optional RAG / sessions npx wrangler vectorize create pyne-agent-kb --dimensions= --metric=cosine npx wrangler r bucket create pyne-agent-kb npx wrangler d create pyne-agent-sessions npx wrangler d execute pyne-agent-sessions --remote --file=schemas/sessions.sql cp .env.example .dev.vars bun run dev echo "your-secret" | npx wrangler secret put APIKEY bun run deploy ` No HOOX mesh and no pyne-worker binding. Optional validate loop If you already run pyne-worker: `jsonc // wrangler.jsonc — uncomment services, OR: // "vars": { "PYNEWORKERURL": "https://pyne-worker..workers.dev" } ` `bash echo "pyne-api-key" | npx wrangler secret put PYNEWORKERAPIKEY ` Knowledge (private) `bash bun run ingest:docs -- --dir /secure/pine-docs-v --version v bun run ingest:docs -- --pyne-docs ../pynescript/docs/pyne bun run ingest:corpus -- --dir /secure/open-pine-corpus --max bun run ingest:builtins -- --metadata ../pynescript/src/pynescript/langserver/providers/builtinmetadata.json bun run scripts/build-index.ts \ --embed-endpoint https://pyne-agent-worker..workers.dev/v/admin/embed ` Never commit TradingView built-in sources. scripts/legal-check.sh runs on predeploy. AXIS `text https://pyne-agent-worker..workers.dev/plugin/axis-pine-agent.js `` Set endpoint + API key in the plugin config. See also pyne-agent-worker Chat API pyne-worker — optional validate host PyneTS AXIS plugin --- FILE: docs/pyne/agent/index.mdx Copyright (C) - jango_blockchained This file is part of pynescript. SPDX-License-Identifier: AGPL-.-or-later --- title: "pyne-agent-worker" description: "Natural-language PYNE Agent on Cloudflare Workers AI. Standalone chat, optional pyne-worker validate loop, AXIS plugin. Sister repo." --- pyne-agent-worker Abstract pyne-agent-worker is the edge authoring host: natural language in, Pine source out. It is a sister repository — it does not live in this PYNE checkout. | | | | --- | --- | | Repo | hoox-sh/pyne-agent-worker | | Role | Write scripts via chat (PYNE Agent) + optional RAG | | Runtime | Cloudflare Workers (TypeScript) + Workers AI | | AXIS | GET /plugin/axis-pine-agent.js | | In this repo? | No | You do not need pyne-worker, trade-worker, or the HOOX mesh to chat. The agent writes source. It does not import PyneTS and it does not run the bar-loop. Conceptual model | Mode | Requirement | Behavior | | --- | --- | --- | | Standalone | AI binding (+ optional KB) | POST /v/chat → Pine; validation skipped | | HOOX-enhanced | + pyne-worker URL or service binding | generate → POST /run → fix retries | GET /health reports "mode": "standalone" | "hoox". Taxonomy | Name | What it is | | --- | --- | | PYNE | Language SoT (this repo) | | PyneTS | TypeScript library (@hoox-sh/pynets) — parse / Runtime.run | | pyne-worker | Python Cloudflare evaluate host | | pyne-agent-worker | NL authoring host (this Worker) | Hard rule This git tree never contains TradingView built-in Pine sources. Knowledge is operator-ingested into private R + Vectorize (ingest:docs / ingest:corpus / ingest:builtins). scripts/legal-check.sh refuses tracked .pine on deploy. Relationship to PYNE PYNE remains the evaluate oracle. The agent writes. Optional validate uses the evaluate contract against pyne-worker or the Pro API. AXIS install URL: https:///plugin/axis-pine-agent.js Pages Chat API Deploy See also pyne-worker PyneTS Evaluate contract AXIS plugin Ecosystem --- FILE: docs/pyne/api/app-lifecycle.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "App Lifecycle" description: "Flask application setup: MAXCONTENTLENGTH, CORS policy, blueprints, error handlers, and process entry." --- App Lifecycle Abstract backend/app.py constructs a single global Flask app, applies security-minded defaults (body size, CORS allowlist), registers free and Pro routes, and exposes a module main for local development. There is no multi-app factory pattern today — import app for WSGI (gunicorn) or run the module for the built-in server. Conceptual model Interface surface Process entry ``bash make run equivalent python -m backend.app ` Environment: | Variable | Default | Role | | --- | --- | --- | | HOST | ... | Bind address (localhost-first for dev) | | PORT | | Listen port | | ALLOWEDORIGINS | https://pynescript.ai, https://app.pynescript.ai, localhost regex | CORS origins (comma-separated; regex allowed). / any opens all. Code always appends localhost, private-LAN, and product (hoox.sh / pynescript.ai / pynescript-axis.pages.dev) regexes unless the value is | | APIKEYSTORE | /data/apikeys.json | JSON key-store path (auth.APIKeyStore default) | | ADMINTOKEN | unset | Fail-closed admin minting (see auth) | debug=False always on the dev runner. Health GET / and GET /health are the same handler. AXIS Settings probes /health. Payload includes compile-cache diagnostics: `json { "status": "healthy", "service": "pynescript-pro-api", "version": "..", "timestamp": , "websocket": true, "features": { "alerts": true, "alertwebhooks": true, "warmcompile": true, "defaultrunmode": "auto" }, "compile": { "hasnumba": true, "diskcacheenabled": true, "prewarmenabled": true, "defaultmode": "auto" }, "endpoints": { "…": "…" } } ` version here is the API service label (..), not the hoox-pyne package version. Hard limits MAXCONTENTLENGTH = — reject oversized bodies before JSON parse fills memory (audit -- / S). Free compute also gated by backend/middleware/freelimits.py: max bars (FREEMAXBARS, default ), script chars (FREEMAXSCRIPTCHARS, KiB), per-IP rate (FREERATELIMIT / FREERATEWINDOWSEC, / s), concurrency (FREEMAXCONCURRENT, ). Chart/mock data sources only. CORS methods: GET, POST, OPTIONS, HEAD. Allow headers: Content-Type, Authorization, X-Admin-Token, Accept. supportscredentials=False. Localhost regex used in the default origin list: `text ^https?://(?:localhost|\.\.\.)(?::\d+)?$ ` Free CORS prefixes (/, /health, /run, /compile, /lsp/, /ws/) reflect the request Origin even when it is not in ALLOWEDORIGINS so AXIS VPS UI → local pyne preflight succeeds. Same-origin / no Origin header (curl, server-to-server) remains usable under flask-cors behavior. Blueprints `python app.registerblueprint(previewbp) urlprefix=/preview app.registerblueprint(backtestbp) urlprefix=/backtest app.registerblueprint(lspbp) urlprefix=/lsp app.registerblueprint(gitoauthbp) /api/git/oauth/ ` Preview + backtest live in backend/api/preview.py. Optional flask-sock registers WS /ws/run when installed. Error handlers | Code | Body code | Message | | --- | --- | --- | | | NOTFOUND | Endpoint {path} not found. | | | INTERNALERROR | Internal server error. | Both return JSON with status: "error". Internals | Path | Role | | --- | --- | | backend/app.py | App object, free routes, auth routes, CORS, health | | backend/api/preview.py | Pro preview + backtest blueprints | | backend/api/lsphttp.py | Free AXIS LSP-HTTP | | backend/middleware/schemas.py | Request validation for /run and auth | | backend/middleware/freelimits.py | Bar / script / rate / concurrency caps | | backend/requirements.txt | Flask, flask-cors, numpy, matplotlib, … | Recommended production: gunicorn/uvicorn-class WSGI with multiple workers; for multi-worker key consistency prefer SQLite/Redis stores over the default in-memory/file singleton assumptions — see auth. Invariants and edge cases . Import side effects: creating app configures CORS immediately from env. . Body too large → Flask before route logic (not custom JSON). . Unknown fields on schema-validated routes → UNKNOWNFIELDS (/run, auth); preview routes currently use looser getjson() (schemas exist but are not always applied in handlers). . Dev bind default is loopback — set HOST=... only when intentional. Worked example — smoke test `bash curl -s http://...:/health | jq '.status, .features.defaultrunmode' "healthy" "auto" ` Failure modes | Symptom | Cause | | --- | --- | | Browser CORS errors | Origin not in ALLOWED_ORIGINS | | on large OHLCV | Exceeded MB — downsample or paginate | | Import errors for matplotlib | Missing backend deps — pip install -r backend/requirements.txt` | See also Auth and keys Run endpoint Docker / CI --- FILE: docs/pyne/api/auth-and-keys.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Auth and Keys" description: "API key model, tiers, requireapikey / trackusage decorators, JSON/SQLite/Redis stores, and admin createkey." --- Auth and Keys Abstract Pro routes authenticate with a raw API key presented as Authorization: Bearer …, Authorization: ApiKey …, or ?apikey=. Keys are minted as pyn + url-safe secret, stored with a short keyid and SHA- hash metadata, and tracked against monthly-ish call limits by tier. Free /run does not require a key. Conceptual model Interface surface Tiers (TIERLIMITS) | Tier | callslimit | | --- | --- | | free | (treated as unlimited in isratelimited — limit means “no cap”) | | hobby | | | pro | | | team | | | enterprise | inf | callsremaining() returns inf when limit is . Endpoints POST /auth/createkey Body schema CREATEKEYSCHEMA: optional tier (default "hobby"). Response: ``json { "status": "success", "apikey": "pyn…", "keyid": "hexchars", "tier": "hobby", "message": "Store this API key securely. It will not be shown again." } ` Decorated with @requireadmintoken: Requires non-empty ADMINTOKEN env (unset → , minting disabled) Client must send matching X-Admin-Token (or Authorization: Bearer … / AdminToken …) Comparison uses hmac.comparedigest GET /auth/usage @requireapikey — returns keyid, tier, and usage block (callsused, callslimit, callsremaining, timestamps). POST /auth/validate Body: { "apikey": "…" } — validates without incrementing usage. Returns tier info, active, ratelimited. Decorators | Decorator | Behavior | | --- | --- | | requireapikey | Resolve key → / or set g.apikey | | trackusage | Wraps requireapikey; increments calls when response status < (tuple responses) or always for bare Response | | requireadmintoken | Fail-closed admin gate (ADMINTOKEN + X-Admin-Token) | Client presentation `bash curl -H "Authorization: Bearer $APIKEY" \ -H "Content-Type: application/json" \ -d @body.json \ http://...:/preview/chart ` Internals APIKey dataclass Fields: keyid, keyhash, tier, callsused, callslimit, createdat, lastused. Helpers: isactive() (always True today), isratelimited(), gettierinfo(), incrementcalls(). Stores | Module | Use | | --- | --- | | middleware/auth.py → APIKeyStore + getkeystore() | Facade; selects backend via STOREBACKEND | | middleware/keystoresqlite.py | Hash-only rows, WAL, multi-worker friendly | | middleware/keystoreredis.py | Hash-only Redis; multi-replica | getkeystore() backends (STOREBACKEND): | Value | Persistence | | --- | --- | | json (default) | File at APIKEYSTORE (default /data/apikeys.json); hash-only object keys. Legacy files keyed by the raw pyn… secret are migrated on load and the raw secret is dropped | | sqlite | APIKEYSTORESQLITE (or .db beside JSON path); hash-only | | redis | REDISURL required; hash-only | Prod compose defaults to sqlite on /data/apikeys.db so gunicorn workers share state. Key minting: `text rawkey = "pyn" + secrets.tokenurlsafe() keyid = sha(rawkey).hexdigest()[:] keyhash = sha(rawkey).hexdigest() ` Schemas CREATEKEYSCHEMA, VALIDATEKEYSCHEMA in middleware/schemas.py — strict types, reject unknown fields. Invariants and edge cases . /run is unauthenticated — rate-limit at reverse proxy if exposed publicly. . Limit means unlimited, not “zero calls” — free tier limit value is with that semantics. . Usage increments only after handler returns — failed auth never bills; handler xx under trackusage skips increment when returned as (response, status). . Revocation exists on the store API (revokekey); no public HTTP revoke route in app.py. . Admin minting is fail-closed — unset ADMINTOKEN or wrong X-Admin-Token → . Worked example `bash create (requires ADMINTOKEN env + matching header) export ADMINTOKEN=change-me curl -s -X POST http://...:/auth/createkey \ -H "X-Admin-Token: $ADMINTOKEN" \ -H 'Content-Type: application/json' \ -d '{"tier":"hobby"}' validate curl -s -X POST http://...:/auth/validate \ -H 'Content-Type: application/json' \ -d '{"apikey":"pyn…"}' ` Failure modes | Code | Meaning | | --- | --- | | UNAUTHORIZED | Missing/invalid key | | RATELIMITED | callsused >= callslimit (finite limits) | | FORBIDDEN | ADMINTOKEN unset or X-Admin-Token mismatch (fail-closed) | | INVALIDTIER | createkey with unknown tier string | | INVALIDKEY` | validate endpoint miss | See also App lifecycle Preview Security (DevOps) --- FILE: docs/pyne/api/contract.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Evaluate Contract" description: "Shared request/response evaluate contract across Flask Pro API, pyne-worker, and in-process Runtime.run (Python + PyneTS)." --- Evaluate Contract Abstract The evaluate contract is the JSON dialect that lets AXIS, HOOX, CLI tools, and edge workers call any PYNE-compatible host interchangeably. Flask POST /run is the reference implementation; Python pyne-worker aims for the same HTTP fields. PyneTS speaks the in-process form (RuntimeResult). This page freezes the semantic envelope — not transport auth headers. Conceptual model Invariant: given the same script + OHLCV + mode, hosts should agree on plot series and strategy events within known parity limits (see numerical validation and PyneTS parity). AXIS engines call Python hosts today, not PyneTS. Interface surface Request (single evaluate) | Field | Type | Required | Description | | --- | --- | --- | --- | | script | string | yes | Full Pine source | | data | Bar[] | yes | Chronological OHLCV (ohlcv alias on pyne-worker) | | symbol | string | no | Default CHART / host-specific | | mode | "interpret" \| "compile" \| "auto" | no | Runtime.run default when omitted: PYNERUNTIMEMODE or interpret. Flask RUNSCHEMA default auto | | inputs | object | no | input. overrides keyed by title (forces interpret under auto) | | libraries | {namespace, name, version, source}[] | no | In-process import ns/Name/ver. Flask /run and /run/batch accept this (max ). | | profiler | bool | no | Per-line interpret timings (forces interpret) | | timeoutseconds | number | no | Optional wall-clock interpret budget (checked every bars; sets timedout). Flask /run and /run/batch accept it; omit / null / ≤ → no timeout. | | webhookurl | string | no | Flask L alert webhook (overrides ALERTWEBHOOKURL) | | datasource | string | no | Optional external history provider id (free paths: chart / mock only) | | dataoptions | object | no | Provider configuration | Bar | Field | Type | Required | Notes | | --- | --- | --- | --- | | time | number | recommended | Unix ms preferred | | open | number | yes | | | high | number | yes | | | low | number | yes | | | close | number | yes | | | volume | number | no | Default host-dependent | | bid / ask | number | no | Runtime bid/ask hooks | Response (success) | Field | Type | Description | | --- | --- | --- | | status | "success" | Flask sets this; workers may omit and use HTTP only — prefer including it | | plots | (number\|null)[] | Primary series (plot ) | | series | Record | Named multi-series | | plotmeta | Record | Display metadata | | events | StrategyEvent[] | Strategy / trade-like events | | drawings | Drawing[] | line/label/box export | | alerts | AlertEvent[] | alert() / true alertcondition() firings (interpret; [] on compile) | | alertconditions | object[] | optional condition evaluations | | alertforward | object | optional L webhook delivery summary | | count | number | Bars evaluated | | scriptid | string | Stable hash prefix of source | | runid | string | Per-invocation id | | mode | string | Effective mode (interpret \| compile) | | autobackend | string | auto only: which path ran | | compilefallbackreason | string | auto only: why compile was skipped or failed | | objectmode | bool | Compile path: pure-Python bar loop (no njit) | | timedout | bool | Interpret budget exceeded (Runtime.run(timeoutseconds=…)) | | datasource | string | Flask echo of resolved source label | PlotMeta (Flask) ``json { "title": "C", "color": "F", "linewidth": , "index": , "kind": "plot" } ` kind is plot \| hline \| fill \| bgcolor \| plotshape \| plotchar \| plotarrow. Fill bands add plot / plot title refs; hline may add price. Extra style keys (style, linestyle, location, text, char, size) appear when the interpret collector has them. StrategyEvent (minimum) Hosts should preserve: `json { "type": "string", "barindex": , "scriptid": "…", "runid": "…" } ` Additional fields follow evaluator todict() / TS port parity — treat unknown keys as forward-compatible. AlertEvent (minimum) `json { "message": "string", "freq": "onceperbar", "barindex": , "time": , "source": "alert", "scriptid": "…", "runid": "…" } ` See Alerts for frequency rules and webhooks. Response (failure) Flask /run: `json { "status": "error", "code": "EXECUTIONERROR", "message": "Runtime Error at bar …", "errorkind": "runtime" } ` Optional errortype, errorbar, logs, profile, meta pass through when Runtime set them. errorkind is parse \| compile \| runtime \| data \| order \| mode. Workers may use equivalent { error: string } or HTTP xx/xx with message body. Clients should accept: . Envelope status === "error" with message, or . Presence of top-level error string (raw Runtime.run shape). Batch evaluate (Flask extension) POST /run/batch: Request: scripts: (string | {id, script})[] (max ) + shared data + optional symbol / datasource / dataoptions / mode / profiler / libraries / timeoutseconds / webhook fields. Not on the batch schema: inputs. Response: `json { "status": "success" | "partial", "results": [ / per-script success or error object with id / ], "count": , "ok": , "datasource": "chart" } ` Workers may implement batch as N single evaluates; AXIS should not assume batch exists on every host. Internals — reference mapping | Contract field | Flask source | | --- | --- | | plots / series / plotmeta | pynescript.runtime.host.Runtime.run packing loop | | events | evaluator.strategystate.drainevents() → todict() (compile: _events) | | drawings | DrawingRegistry.exportforapi (compile: drawings) | | alerts | exportalertsfromevaluator (interpret; compile returns []) | | alertforward | backend/alertforwarder.maybeforwardrunalerts | | scriptid | sha(source)[:] | | runid | Runtime.runid | | compilefallbackreason | Runtime.runauto | | timedout | Interpret bar loop vs timeoutseconds (Flask /run and /run/batch when the field is set and > ) | Schema enforcement on Flask free tier: RUNSCHEMA / RUNBATCHSCHEMA in backend/middleware/schemas.py (strict, reject extras). Webhook fields: webhookurl, forwardalerts, alertlastbar, alertbatch. libraries and timeoutseconds are on both /run and /run/batch. Tests: tests/testbackend.py (testrunsuccess, testrunexportsalerts, webhook cases), tests/testalertforwarder.py. Divergences to remember | Surface | Divergence | | --- | --- | | Preview / backtest | Columnar data dict, not Bar[]; not the evaluate contract | | Compile mode | Extra keys objectmode, compilecached, compilems, nopythonfallbackreason. generatedcode only if PYNESCRIPTRETURNGENERATEDCODE= | | timeoutseconds | In-process Runtime.run, edge workers, and Flask /run / /run/batch (optional; omit = no budget) | | Health | Flask GET / and GET /health (CORS-free); not part of evaluate | | Auth | Flask free /run open (bar/rate/concurrency caps); workers may require HOOX mTLS / API gateways | | Error codes | Flask uses code enums; workers should map to stable strings when possible | Invariants and edge cases . Bar order is chronological ascending — hosts do not sort for you. . na / missing appear as JSON null in series arrays. . Duplicate plot titles get suffixes (, …) on Flask; clients should key on returned map keys, not assumed titles. . Idempotent scriptid for identical source text across hosts. . runid is not stable across retries — use for log correlation only. . Parity is best-effort on floating edges; pin fixtures when testing hosts against each other. Worked example — host-agnostic client sketch `typescript type EvaluateRequest = { script: string; data: Array; symbol?: string; mode?: "interpret" | "compile" | "auto"; libraries?: Array; }; type EvaluateSuccess = { plots: Array; series: Record>; plotmeta: Record>; events: Array>; drawings: Array>; alerts: Array>; count: number; scriptid: string; runid: string; mode: string; autobackend?: "compile" | "interpret"; compilefallback_reason?: string; }; ` POST that body to Flask /run or the worker’s evaluate route; branch on status / error. Failure modes | Client bug | Symptom | | --- | --- | | Sending columnar preview data to /run | Schema data must be a list | | Assuming batch on worker | / unknown field | | Ignoring series and only reading plots` | Multi-plot scripts look single-series | See also Run endpoint Runtime bridge pyne-worker pyne-agent-worker Pro API usage (end user) Events --- FILE: docs/pyne/api/endpoints/backtest.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Backtest Endpoint" description: "POST /backtest/quick — usage-tracked strategy metrics, equity curve, and optional mock OHLCV." --- Backtest Endpoint Abstract POST /backtest/quick is a Pro, usage-tracked route that runs a simplified strategy simulation over columnar OHLCV (or generated mock bars), returns trade lists + summary metrics, and embeds a base equity-curve PNG. It is optimized for speed/demo UX; it is not a full fidelity substitute for event-aware Runtime strategy evaluation on /run. Conceptual model Blueprint: backtestbp, prefix /backtest. Interface surface Request ``json { "script": "//@version=\nstrategy(\"s\")…", "data": { "open": [], "high": [], "low": [], "close": [], "volume": [] }, "initialcapital": ., "mockdata": false, "mockbars": } ` | Field | Default | Notes | | --- | --- | --- | | script | required | Empty → NOSCRIPT | | data | {} | Columnar; may be omitted if mockdata | | initialcapital | | Starting equity | | mockdata | false | Force synthetic bars | | mockbars | | Length of mock series | If neither usable close nor mockdata → NODATA. Success `json { "status": "success", "result": { "equitycurve": [, …], "trades": [ { "entrytime": , "entryprice": , "exittime": , "exitprice": , "direction": "long", "pnl": , "pnlpct": , "size": } ], "summary": { "totalpnl": , "totalpnlpct": , "sharperatio": , "maxdrawdown": , "maxdrawdownpct": , "winrate": , "profitfactor": , "totaltrades": , "winningtrades": , "losingtrades": , "avgwin": , "avgloss": }, "equitychart": "" }, "tierinfo": {}, "meta": { "bars": , "initialcapital": , "completedat": } } ` Errors | code | HTTP | When | | --- | --- | --- | | NOSCRIPT | | Empty script | | NODATA | | No data and not mock | | BACKTESTERROR | | Exception in simulation | | UNAUTHORIZED / RATELIMITED | / | Auth | Internals runquickbacktest wraps runbacktest(..., plotchart=True) and returns BacktestResult.todict() (backend/services/backtest.py). avgbarsintrade exists on the dataclass but is not exported in summary. MVP simulation characteristics: . Optionally parse(script) (errors soft-ignored for MVP path). . Precompute long/short entry signals from dual SMA cross ( vs ) starting at bar . . Exit heuristics via RSI-like average and opposite signals / end of series. . Equity curve + trade PnL with optional commission/slippage parameters on the lower-level API. . Metrics: Sharpe (√ scaling), max drawdown, win rate, profit factor. . Chart via renderequitycurve. generatemockohlcv(nbars) produces synthetic columns for demos. | Path | Role | | --- | --- | | backend/api/preview.py | quickbacktest route | | backend/services/backtest.py | Simulation + metrics | | backend/services/chartrenderer.py | Equity PNG | Invariants and edge cases . Script content is lightly used in the MVP sim — do not treat results as broker-accurate for arbitrary strategies. Prefer /run events for engine-faithful strategy traces. . Columnar data, same family as preview, not /run bar lists. . Mock path ignores incomplete user data when mockdata or empty close. . One usage increment per successful HTTP response under trackusage. Worked example `bash curl -s http://...:/backtest/quick \ -H "Authorization: Bearer $APIKEY" \ -H 'Content-Type: application/json' \ -d '{ "script": "//@version=\nstrategy(\"demo\")\n// body optional for MVP", "mockdata": true, "mockbars": , "initialcapital": }' | jq '.result.summary' ` Failure modes | Symptom | Cause | | --- | --- | | Implausible trades vs script logic | MVP signal model, not full evaluator | | Empty trade list | No crosses in series / short history | | Missing equitychart | Renderer exception swallowed → empty string | See also Runtime bridge — faithful strategy events via /run` Chart renderer Strategy builtins --- FILE: docs/pyne/api/endpoints/preview.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Preview Endpoints" description: "Pro chart and indicator thumbnail routes under /preview with usage tracking." --- Preview Endpoints Abstract Preview routes render PNG thumbnails (base) for desk UIs and docs. They require an API key via @trackusage, accept OHLCV-shaped objects (column arrays), and do not run the full Runtime bar loop for /preview/chart (data is plotted directly). /preview/indicator evaluates a small set of closed-form ta. expressions in pure Python for the series, then plots. Conceptual model Blueprint: previewbp with urlprefix="/preview" in backend/api/preview.py. Interface surface Auth Both routes: @trackusage → must pass requireapikey and consume one call on success. POST /preview/chart Body (documented; schemas exist as PREVIEWCHARTSCHEMA) ``json { "script": "optional string", "data": { "open": [], "high": [], "low": [], "close": [], "volume": [] }, "options": { "type": "line", "color": "F", "showvolume": false, "width": , "height": } } ` | Option | Default | Clamp | | --- | --- | --- | | type | "line" | "ohlcv" selects candlestick renderer | | color | F | line chart only | | width | | max | | height | | max | | showvolume | false | twin-axis volume on line chart | Success `json { "status": "success", "chart": "", "meta": { "type": "line", "bars": , "lastvalue": , "firstvalue": , "change": , "changepct": , "renderedat": }, "tierinfo": {} } ` Errors | code | When | | --- | --- | | NODATA | Missing data (both preview routes) | | NOCLOSEDATA | Empty close array (chart and indicator) | | RENDERERROR | matplotlib / renderer exception (/preview/chart only; indicator parse/render exceptions fall back to plotting close) | Note: script is accepted for API symmetry but not executed on this path today — the chart is pure data visualization. POST /preview/indicator Body `json { "expression": "ta.sma(close, )", "data": { "close": [/ … /] }, "options": { "color": "FF", "width": , "height": } } ` Supported expression prefixes in computeindicator: | Expression | Series | | --- | --- | | ta.sma(…, period) | SMA of close | | ta.ema(…, period) | EMA | | ta.rsi(…, period) | RSI | | ta.macd(…) | MACD histogram (//) | | other | falls back to raw close | Parse failures in the helper fall back to plotting close. Internals | Path | Role | | --- | --- | | backend/api/preview.py | Route handlers + indicator math | | backend/services/chartrenderer.py | PNG encode | | backend/middleware/auth.py | trackusage | Schemas PREVIEW in schemas.py document intended validation; current handlers use request.getjson() or {} directly. Invariants and edge cases . Data shape differs from /run: preview uses columnar dicts; /run uses list of bar dicts. . Not a full Pine interpreter for indicator expressions — string prefix parsing only. . Width/height clamps protect worker memory/CPU. . Successful responses include tierinfo from the authenticated key. Worked example `bash curl -s http://...:/preview/chart \ -H "Authorization: Bearer $APIKEY" \ -H 'Content-Type: application/json' \ -d '{ "data": {"close": [,,,,], "volume": [,,,,]}, "options": {"type": "line", "show_volume": true} }' | jq '.meta' `` Failure modes | Symptom | Cause | | --- | --- | | | Missing key | | | Tier limit | | Empty-looking PNG | Renderer empty-chart path when all values null | See also Chart renderer Auth and keys Run --- FILE: docs/pyne/api/endpoints/run.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "POST /run" description: "Free evaluate endpoints: single-script /run and multi-script /run/batch over shared OHLCV." --- POST /run and /run/batch Abstract /run is the primary free evaluate entry: accept Pine source + bar list, optionally wire request. data sources, execute via package pynescript.runtime.Runtime (Pro API uses backend.runtime re-export), and return plots/series/events/drawings/alerts. /run/batch runs up to eight scripts on the same OHLCV (AXIS multi-indicator), isolating per-script errors so one failure does not discard siblings. Free paths enforce bar/script caps, rate limit, concurrency, and chart/mock-only data (backend/middleware/freelimits.py). Optional L webhooks POST last-bar (default) alert() / alertcondition() firings to webhookurl or env ALERTWEBHOOKURL (SSRF-safe). Conceptual model Interface surface POST /run Request (RUNSCHEMA) | Field | Type | Required | Default | Notes | | --- | --- | --- | --- | --- | | script | string | yes | | Pine source | | data | list | yes | | OHLCV bar objects | | symbol | string | no | "CHART" | Syminfo / request wiring | | datasource | string | no | "" | mock / ccxt / yahoo / … | | dataoptions | object | no | {} | Provider options | | mode | string | no | "auto" | "interpret" \| "compile" \| "auto" | | inputs | object | no | {} | Pine input. overrides by title | | profiler | bool | no | false | Per-line timing on interpret | | webhookurl | string | no | "" | L alert webhook (overrides ALERTWEBHOOKURL) | | forwardalerts | bool | no | true | When false, skip outbound webhook | | alertlastbar | bool | no | true | Webhook only last OHLCV bar firings | | alertbatch | bool | no | true | One batch POST vs per-alert | | libraries | list | no | [] | [{namespace, name, version, source}] — AXIS git-publish emulator (max ) | | timeoutseconds | number | no | omit / null | Optional interpret wall-clock budget. Passed to Runtime.run only when set and > . Omit = no timeout. | Query ?mode= is accepted when the body omits mode. Unknown fields → UNKNOWNFIELDS . Empty script/data still checked after schema with NOSCRIPT / NODATA. import and any request. token are compile-ineligible; mode=auto falls back to interpret and keeps libraries on that path. Bar shape (runtime expectation): ``json { "time": , "open": , "high": , "low": , "close": , "volume": } ` Optional bid / ask per bar update runtime bid/ask state. Success response `json { "status": "success", "plots": [], "series": {}, "plotmeta": {}, "events": [], "drawings": [], "alerts": [], "scriptid": "hex", "runid": "hex", "count": , "mode": "compile", "autobackend": "compile", "datasource": "chart", "alertforward": { "forwarded": , "failed": , "filter": "lastbar", "url": "https://hooks.example.com/pine", "batch": true } } ` | Field | Meaning | | --- | --- | | plots | Primary series (first plot) — backward compatible list | | series | Named multi-plot map title → values per bar | | plotmeta | Per-title color, linewidth, index | | events | Strategy events with scriptid / runid stamps | | drawings | Exported line/label/box registry for AXIS | | alerts | alert() / true alertcondition() firings (interpret; empty on compile) | | alertconditions | Optional full condition evaluations (when present) | | alertforward | Present when a webhook URL was configured; delivery summary | | scriptid | sha(source)[:] | | runid | Per-Runtime instance id | | autobackend | compile \| interpret when requested mode was auto | | compilefallbackreason | Why auto used interpret (eligibility or compile error) | | objectmode / compilecached / compilems | Compile diagnostics when that path ran | | nopythonfallbackreason | Numeric JIT failed; engine re-emitted object mode (still compile) | Errors | HTTP | code | When | | --- | --- | --- | | | MISSINGFIELD / INVALIDFIELD / UNKNOWNFIELDS / INVALIDBODY | Schema | | | NOSCRIPT / NODATA | Empty after defaults | | | DATASOURCEERROR | resolverequestsources failed | | | WEBHOOKURLBLOCKED | webhookurl is private/loopback/metadata (SSRF denylist) | | | DATASOURCEFORBIDDEN | Free path rejected live provider (ccxt / yahoo / …) | | | TOOMANYBARS / SCRIPTTOOLARGE | Exceeded FREEMAXBARS () or FREEMAXSCRIPTCHARS ( KiB) | | | RATELIMITED / TOOMANYREQUESTS | Free IP rate ( / s) or concurrency () | | | EXECUTIONERROR | Runtime returned error key; body may include errorkind / errortype / errorbar | POST /run/batch Request (RUNBATCHSCHEMA) | Field | Type | Required | Notes | | --- | --- | --- | --- | | scripts | list | yes | strings or {id, script} objects | | data | list | yes | Shared OHLCV | | symbol / datasource / dataoptions / mode / profiler | optional | same as /run (no inputs) | | libraries | list | no | same AXIS git-publish list as /run (max ; applied to every script) | | timeoutseconds | number | no | same optional interpret budget as /run (applied to every script) | | webhookurl / forwardalerts / alertlastbar / alertbatch | optional | same L webhook fields as /run (applied per script result) | Hard cap: RUNBATCHMAXSCRIPTS = → TOOMANYSCRIPTS. Response `json { "status": "success" | "partial", "results": [ { "id": "…", "status": "success|error", … } ], "count": , "ok": , "datasource": "chart" } ` HTTP for partial success (per-script errors inside results). Envelope validation failures still . Internals `python runtime = Runtime(symbol=str(symbol)) result = runtime.run(script, ohlcv, datafeed=…, dataprovider=…, mode=…) ` See runtime bridge for bar-loop details and compile mode. Paths: backend/app.py (runpinescript, runpinescriptbatch, compileprewarm, WS /ws/run), backend/middleware/schemas.py, backend/middleware/freelimits.py, backend/alertforwarder.py. Invariants and edge cases . No API key on these routes — free-tier guards still apply (see Pro API usage); protect further at the edge if needed. . Batch isolates exceptions per script (try/except around runtime.run). . Whitespace-only datasource coerced to None → chart default; free paths reject live providers (ccxt/yahoo/…). . Compile mode may omit alert side effects (alerts: []); use interpret/auto for alerts. . Schema rejects extras — clients must not send AXIS-only fields without updating schema. . Webhook delivery is best-effort — evaluate still returns status: success if the hook fails; check alertforward. . Structured errors may include errorkind (parse|compile|runtime|data|order|mode), errortype, errorbar. timedout is set on the HTTP body when Runtime exceeds timeoutseconds. Worked example `bash curl -s http://...:/run \ -H 'Content-Type: application/json' \ -d '{ "script": "//@version=\nindicator(\"t\")\nplot(close)", "data": [ {"time": , "open": , "high": , "low": ., "close": ., "volume": }, {"time": , "open": ., "high": ., "low": ., "close": ., "volume": } ], "symbol": "TEST:DEMO" }' ` Failure modes | Symptom | Cause | | --- | --- | | UNKNOWNFIELDS | Typo’d property (e.g. ohlcv instead of data) | | Parse errors in message | Invalid Pine — same engine as CLI | | Batch status: partial | At least one script failed; inspect results[i].message | POST /compile/prewarm Free readiness hook (same rate/concurrency gates). Body optional: `json { "scripts": ["//@version=\nindicator(\"x\")\nplot(close)"], "force": false } ` Caps at sources. Returns hasnumba, builtinswarmed, scriptsok / scriptsfailed, prewarmms. Does not execute on OHLCV. WS /ws/run When flask-sock is installed: JSON text frames {type: "run", id, script, data, mode?}. Reply {type: "result", …} or {type: "error"}. Ping/pong supported. Same executerunpayload as HTTP /run. Alert webhooks (L) `bash Server default (optional) export ALERTWEBHOOKURL=https://hooks.example.com/pine curl -s http://...:/run \ -H 'Content-Type: application/json' \ -d '{ "script": "//@version=\nindicator(\"a\")\nif barindex == lastbarindex\n alert(\"fire\")\nplot(close)", "mode": "interpret", "webhook_url": "https://hooks.example.com/pine", "data": [ {"time": , "open": , "high": , "low": ., "close": ., "volume": }, {"time": , "open": ., "high": ., "low": ., "close": ., "volume": } ] }' `` Full engine rules and batch payload shape: Alerts. See also Alerts Contract Runtime bridge App lifecycle --- FILE: docs/pyne/api/index.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Pro API" description: "Flask Pro API — /run with mode=auto, plotmeta/fill, alerts + L webhooks, preview, backtest, and shared evaluate contract." --- Pro API The PYNE Pro API is a Flask HTTP service that runs Pine scripts over caller-supplied OHLCV, returns plots / series / plotmeta (incl. fill bands) / strategy events / drawings / alerts, optional L alert webhooks, and gates thumbnail + backtest routes behind API keys. Default evaluate path is mode=auto (compile when safe, interpret on fallback). It is intentionally not the language server: different auth, scaling, and failure domains. Abstract Where the LSP answers static editor questions, the Pro API answers dynamic evaluate questions: ``text POST JSON { script, data: OHLCV[], mode?, libraries?, webhookurl?, … } → pynescript.runtime.Runtime.run (Pro API re-export; mode default: auto) → { plots, series, plotmeta, events, drawings, alerts, mode, autobackend?, compilefallbackreason?, scriptid, runid, alertforward?, … } ` Free-tier evaluate (POST /run, POST /run/batch) validates bodies with hand-rolled schemas, enforces a MB body cap, bar/script/rate/concurrency guards (backend/middleware/freelimits.py), and restricts CORS. GET / and GET /health are the same unauthenticated readiness payload. Optional L webhooks POST last-bar alert() / alertcondition() firings to webhookurl or env ALERTWEBHOOKURL (see Alerts). Pro routes under /preview/ and /backtest/ use trackusage (Bearer / ApiKey / query key). Chart PNGs are matplotlib Agg renders encoded as base. The same evaluate contract is mirrored by pyne-worker so AXIS and HOOX can swap hosts without redesigning payloads — see contract. Conceptual model Interface surface | Method | Path | Auth | Role | | --- | --- | --- | --- | | GET | / · /health | none | Health + endpoint map + compile cache section | | POST | /run | none (free) | Single-script evaluate (mode default auto; libraries[]; alerts + optional webhooks) | | POST | /run/batch | none (free) | ≤ scripts, shared OHLCV | | WS | /ws/run | none (free) | Same evaluate contract over JSON frames (flask-sock optional) | | POST | /preview/chart | API key + usage | Line / OHLCV PNG | | POST | /preview/indicator | API key + usage | Expression series PNG | | POST | /backtest/quick | API key + usage | Quick strategy metrics + equity PNG | | POST | /auth/createkey | admin decorator | Mint pyn… key (fail-closed ADMINTOKEN) | | GET | /auth/usage | API key | Usage counters | | POST | /auth/validate | none (body key) | Validate without consuming quota | | POST | /compile/prewarm | none (free, rate-gated) | Product warm-compile — skip cold Numba JIT | | POST | /lsp/ | none (free) | AXIS editor completion / hover / diagnostics | | POST | /api/git/oauth/device/ | none (device flow) | Optional GitHub/GitLab device OAuth for SPA git (blueprint) | /run response highlights | Field | Role | | --- | --- | | series / plots / plotmeta | Bar values + display meta (color, linewidth, kind, fill band refs) | | events | Strategy broker events | | drawings | Drawing registry / compile _drawings | | alerts | Structured alert() / alertcondition() firings | | mode / autobackend | Effective path (interpret \| compile) | | compilefallbackreason | Why auto chose interpret (eligibility or compile error) | | alertforward | Present when a webhook URL was configured | | errorkind | On failure: parse \| compile \| runtime \| data \| order \| mode | Local runner: make run → python -m backend.app (default ...:). Tracks in this tab . App lifecycle — Flask app, CORS, limits, blueprints . Auth and keys — tiers, stores, decorators . Run · Preview · Backtest . Chart renderer . Runtime bridge — Runtime / series / evaluator glue . Contract — shared evaluate schema across Flask and workers Internals (map) | Path | Role | | --- | --- | | backend/app.py | App object, /run, /health, /compile/prewarm, /auth, CORS | | backend/api/preview.py | Preview + backtest blueprints | | backend/api/gitoauth.py | Optional device OAuth for SPA git | | backend/api/lsphttp.py | Free AXIS editor LSP-HTTP | | backend/middleware/ | Auth, schemas, freelimits, optional Redis/SQLite stores | | src/pynescript/runtime/host.py | Package SoT bar-loop (mode=auto, alerts pack) | | backend/runtime.py | Compat re-export of the package host | | backend/alertforwarder.py | L HTTP webhook delivery | | backend/evaluator.py / backend/series.py | Compat shims | | backend/services/` | Charts + backtest simulation | See also Evaluate scripts (end user) Pro API usage guide Alerts Runtime hub LSP hub — editor path, not HTTP --- FILE: docs/pyne/api/runtime-bridge.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Runtime Bridge" description: "pynescript.runtime.Runtime — package SoT bar-loop evaluate façade, PineSeries context, compile mode, and result packaging." --- 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 timeoutseconds provides a wall-clock circuit breaker on the interpret path (checked every bars) for edge/cron budgets. Flask /run and /run/batch accept the same field (omit / null / ≤ → no timeout). This is the bridge between Flask handlers and the language core — not a second semantics. Conceptual model Interface surface Construction ``python Runtime(symbol: str = "AAPL", runid: str | None = None) ` Sets syminfo.tickerid / name / prefix (prefix from EXCHANGE:SYMBOL form). Generates runid as uuid().hex[:] when omitted. Helpers: configurefootprint, updatebidask. run(sourcecode, ohlcvdata, datafeed=None, dataprovider=None, mode="interpret", …) | Arg | Role | | --- | --- | | sourcecode | Pine text | | ohlcvdata | list[dict] with open/high/low/close/time[/volume/bid/ask] | | datafeed / dataprovider | request. wiring; auto-resolved from chart bars when unset | | mode | "interpret" (default for direct calls) · "compile" · "auto" (Pro API schema default) | | timeoutseconds | Optional wall-clock budget (seconds); interpret path only — partial results + timedout + errorkind=runtime when exceeded. Flask /run and /run/batch accept it (omit / ≤ = no budget) | | libraries | Optional [{namespace, name, version, source}] registered via registerlibrarysource before import ns/Name/ver. mode=auto forwards libs into interpret fallback. Flask /run caps at | | inputs / profiler | input. overrides (interpret); non-empty inputs or profiler force interpret under auto (compilefallbackreason) | | realtime | Optional host simulation for varip multi-tick tests (realtimelastbar, realtimeticks, realtimebars, realtimefrombar) | Interpret path (default) . Resolve request sources (non-fatal on failure). . parse(sourcecode, mode="exec") — on failure { "error": "Parse Error: …" }. . Init PineSeries for OHLC + context dict (timeframe daily defaults, barstate, chart colors). . Construct CustomEvaluator(context=…, datafeed=…, dataprovider=…), reset var declarations + drawing registry + clearalerts(). . Per bar: Update series + calendar fields from timestamp Update barstate flags (isfirst / islast / history confirmed) processpendingorders when available evaluator.visit(tree) — on failure { "error": "Runtime Error at bar …" } Lock pine defs after first bar (pinedefslocked) to avoid multi-dispatch blow-ups Drain strategy events; stamp scriptid / runid Capture plot outputs . Build series / plotmeta from all plot indices (disambiguate duplicate titles with suffixes). . Export drawings via DrawingRegistry.exportforapi(bartimes). . Export alerts via exportalertsfromevaluator (optional alertconditions). Return keys: plots, series, plotmeta, events, drawings, alerts, count, scriptid, runid, mode (+ optional alertconditions, timedout, errorkind). mode=auto . Non-empty inputs → interpret immediately (compilefallbackreason = "input. overrides require interpret path"). . compileeligible: reject top-level import and any request. token (cached per source hash). . Eligible → runcompiled. On success set autobackend=compile. On compile/runtime error, fall back to interpret and set compilefallbackreason. . Ineligible → interpret + compilefallbackreason (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 → errorkind=compile. . Do not require hasnumba(). Object-mode scripts (strategy, UDT, drawings) compile to a Python/numpy loop. Missing Numba only fails pure-numeric emit (CompileNumbaRequiredError); auto caches that failure. . Host LRU by raw-source sha → compilescript on miss. . Pack OHLCV to float columns including time= bar-open ms (ohlcvpackcached). . 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: [], objectmode, compilecached, compilems. generatedcode only when PYNESCRIPTRETURNGENERATEDCODE=. PineSeries (pynescript.runtime.series) History deque (default maxlen , raised by maxbarsback / PYNESERIESMAX). PYNESERIESCAP default on (list-length trim). PYNESERIESRING default off (chronological O() lookback when ). series[] 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 v–v 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 runid 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 — performance invariant for large function tables. . Drawing registry reset at run start — isolation between requests. . Primary plots list is plot index for backward compatibility; AXIS should prefer series + plotmeta. . Errors are dicts, not exceptions, for control-flow at the HTTP boundary ("error" in result). . Compile mode supports a subset; mode=auto falls back to interpret with compilefallbackreason rather than failing the request. Worked example `python from pynescript.runtime import Runtime or: from backend.runtime import Runtime Pro API monorepo path rt = Runtime(symbol="NASDAQ:AAPL") out = rt.run( '//@version=\nindicator("x")\nplot(close, "C")', [{"time": i, "open": , "high": , "low": ., "close": . + i ., "volume": } for i in range()], ) assert "error" not in out assert out["count"] == assert "C" in out["series"] or "plot" 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 (errorkind=compile / runtime) | | Script execution timed out | timeoutseconds exceeded (timedout, errorkind=runtime`) | See also Contract Run endpoint Series and history Strategy events --- FILE: docs/pyne/api/services/chart-renderer.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Chart Renderer" description: "Matplotlib Agg PNG services: line charts, OHLCV candles, equity curves, base encoding." --- Chart Renderer Abstract backend/services/chartrenderer.py is a headless plotting façade. It forces matplotlib into Agg, draws dark-theme charts for desk thumbnails, and returns base-encoded PNG strings suitable for JSON transport. Callers are the preview and backtest routes; the module has no Flask dependency. Conceptual model Interface surface renderlinechart | Param | Default | Role | | --- | --- | --- | | values | required | Y series; None → NaN gaps | | dates | None | Optional x tick labels (≤ ~ ticks) | | title | "Chart" | Title string | | color | F | Line + fill | | height / width | / | width → figsize inches (width/); height is unused for figsize — line charts use .″ (.″ with volume), OHLCV .″, empty .″. dpi | | showvolume | false | Twin axis bars from ohlcv["volume"] | | ohlcv | None | Volume source when enabled | Empty / all-null values → renderemptychart placeholder. renderohlcvchart Candlestick + volume subplot pair from dict keys open, high, low, close, volume. Green/red body colors (CAF / F). Shared x-axis. Figsize (width/, .) — the height argument is ignored. renderequitycurve Single series with profit/loss fill relative to zero baseline and a dashed reference at the first equity point. Title fixed "Equity Curve". Return type All public renderers → str (base PNG without data-URI prefix). Clients typically use: ``text data:image/png;base,{chart} ` Internals | Detail | Implementation | | --- | --- | | Backend | matplotlib.use("Agg") before pyplot import | | Theme | fig EEE, axes , muted grids | | Cleanup | plt.close(fig) after savefig | | Empty state | Centered “No data available” text | | Path | Role | | --- | --- | | backend/services/chartrenderer.py | Renderers | | backend/api/preview.py | Consumers | | backend/services/backtest.py | Equity chart hook | Invariants and edge cases . Not interactive — no GUI backend; safe for servers. . NaNs break lines at gaps (numpy nan). . Candle body height uses + e- to keep zero-range bodies visible. . Thread safety: matplotlib global state is historically process-local; multi-threaded gunicorn workers should avoid concurrent pyplot use on one worker or serialize renders. . Exceptions in callers often become HTTP RENDERERROR or empty equity chart strings. Worked example `python from backend.services.chartrenderer import renderlinechart b = renderline_chart([., ., ., .], title="demo", color="AE") assert isinstance(b, str) and len(b) > `` Failure modes | Symptom | Cause | | --- | --- | | ImportError Agg | Incomplete matplotlib install | | Huge base | High width/height — routes clamp preview sizes | | Blank image | Empty input path succeeded with placeholder | See also Preview endpoints Backtest --- FILE: docs/pyne/contributing.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Contributing" description: "Hard constraints, workflow, and style rules for contributors and agents working on PYNE." --- Contributing Abstract Contributions to PYNE are welcome when they respect the repo’s mechanical invariants: generated code boundaries, src-layout packaging, dual console scripts, test corpus costs, and encryption secrets. This page distills CONTRIBUTING.md and AGENTS.md into an operational contract for humans and agents. Conceptual model Interface surface Baseline workflow . Fork and branch from main . Install: make install or hatch envs . Run tests: make test / hatch run test:test . Implement with tests (prefer TDD for non-trivial behavior) . Lint/format: make lint / make fmt or hatch run lint:style + lint:typing . Open a PR with rationale + test evidence Official short form also lives in root CONTRIBUTING.md. Everyday commands | Goal | Command | | --- | --- | | Editable install + LSP | make install | | Full tests | make test | | LSP only | make test-lsp | | Backend only | make test-backend | | Lint / format | make lint / make fmt | | Fast build sanity | make build-check | | API | make run | | LSP process | make run-lsp | Hard constraints Grammar & generated code Edit only src/pynescript/ast/grammar/antlr/resource/.g for grammar work Never hand-edit …/antlr/generated/ or …/asdl/generated/ Regenerate via hatch lint:gen-parser / project scripts; see grammar guides under .opencode/context/ No stale backups Do not recreate removed backups such as builder.py.bak or evaluator/builtins/technicalrefactored.py. Live builder.py and technical.py are sole sources of truth. Future annotations Every new Python file must start with: ``python from future import annotations ` Enforced by ruff isort required-imports. Console scripts | Preferred | Alias | Role | | --- | --- | --- | | pyne | pynescript | Click CLI | | pyne-lsp | pynescript-lsp | Language Server (pygls) | Import package remains pynescript. Do not conflate CLI and LSP entrypoints in docs, packaging, or process managers. Optional example-script fixture tests/conftest.py only expands pinescriptfilepath when --example-scripts-dir points at a local directory of .pine files. No third-party corpus is shipped in the repository. tests/data/library/ is reference material, not auto-parametrized. Builtin metadata builtinmetadata.json is generated from code. After adding builtins: `bash python scripts/generatebuiltinmetadata.py ` Do not hand-maintain the catalog long-term. Interpret / compile parity When changing pynescript.runtime.Runtime, the compiler, or plot/series export, check series parity between mode="interpret" and mode="compile": `bash Local corpus harness (default scripts × bars) python scripts/compareinterpcompile.py --bars --limit Broader pass from a path list; ignore one-sided hline/fill keys python scripts/compareinterpcompile.py --file-list path.txt --limit --workers --timeout-sec --ignore-hline-keys --ignore-fill-keys ` Always-on unit coverage lives in tests/testinterpcompileparity.py (harness helpers + a small smoke subset). Optional longer path: `bash pytest tests/testinterpcompileparity.py -m interpcompilefull or: PYNEINTERPCOMPILEFULL= pytest tests/testinterpcompileparity.py ` Report artifact: .cache/interpcompileparity.json. Value mismatches on shared series keys are regressions. First-party hline/fill/bgcolor/plotshape keys match (..); --ignore-hline-keys / --ignore-fill-keys are optional for leftover corpus noise. End-user mode semantics: Evaluate scripts — Runtime modes. Secrets & crypto scripts/build/.metadata.key is gitignored CI must supply stable CRYPTOKEY from METADATAKEY / METADATAKEY for reproducible .enc blobs Never commit Fernet keys or API admin tokens Internals (where to change what) | Want to… | Look at | | --- | --- | | Parse / unparse | src/pynescript/ast/helper.py | | Add builtin | src/pynescript/ast/evaluator/builtins/.py + metadata generator | | LSP feature | src/pynescript/langserver/features/ + server.py | | CLI subcommand | src/pynescript/main.py | | Grammar | resource/.g then regenerate | | Pro API route | backend/api/preview.py, backend/app.py | | Runtime modes / compile fallback | src/pynescript/runtime/host.py (Runtime.run); backend/runtime.py is a shim | | Interp/compile series parity | scripts/compareinterpcompile.py, tests/testinterpcompileparity.py | | TS library | sister repo hoox-sh/pynets (pynets/ submodule here) | Style Ruff line length ; broad rule set in pyproject.toml Black target py (via toolchain config) Mypy strict-ish; exemptions for generated grammar, evaluator.builtins., tests Voice for docs: academic nerdy-cool per docs/WRITING.md — no empty marketing adjectives Release (this repo) Match root CONTRIBUTING.md and .github/workflows/publish.yml / release.yml / ghcr.yml: . Bump version in src/pynescript/about.py and update CHANGELOG.md. . Align vscode-extension/package.json when shipping the VSIX together. . Ensure CI is green on main. . Tag and push: git tag vX.Y.Z && git push origin vX.Y.Z. . Publish (publish.yml) builds sdist/wheel (hooxpyne-.whl) and uploads to PyPI (PYPIAPITOKEN or Trusted Publishing OIDC, environment pypi). . Build & Release (release.yml) attaches Nuitka CLI/LSP binaries + VSIX to the GitHub Release. . GHCR (ghcr.yml) publishes ghcr.io/hoox-sh/pyne/{cli,lsp,api}:X.Y.Z on v tags. Dry-run PyPI: Actions → Publish → Run workflow → dryrun=true. Local: python -m build && twine check dist/. AXIS charting releases live in hoox-sh/axis (historical fork: jango-blockchained/axis). PyneTS contributions Work in hoox-sh/pynets, not a pine-worker/ directory in this repo Bun for install / test / typecheck; bun run generate after PYNE .g changes Python remains the oracle — see PyneTS parity Do not copy PyneTS sources into this tree; PYNE consumes the pynets/ submodule only The pynets/ submodule pin is v.. (interpret + JS compile), matching standalone @hoox-sh/pynets. Python remains the oracle. Failure modes for contributors | Mistake | Fallout | | --- | --- | | Editing generated ANTLR/ASDL | Overwritten / review hell | | Skipping corpus cost awareness | -minute “unit” tests | | Hand-editing metadata JSON | Drift from dispatch | | Changing compile/export without parity check | Silent chart/series drift vs interpret | | Committing .metadata.key | Secret leak; rotate immediately | | PR without lint | CI red on ruff/mypy | See also Local development CI PyneTS pyne-worker pyne-agent-worker Roadmap Root AGENTS.md, CONTRIBUTING.md, CODEOF_CONDUCT.md` --- FILE: docs/pyne/core/asdl-schema.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "ASDL Schema" description: "Algebraic AST definition in Pinescript.asdl, generated dataclasses, and how schema changes flow into the pipeline." --- ASDL Schema The abstract syntax tree is not free-form dicts. It is an algebraic data type declared in ASDL and materialized as Python dataclasses. Abstract src/pynescript/ast/grammar/asdl/resource/Pinescript.asdl defines the Pinescript module: roots (Script, Expression), statements, expressions (including control structures that are also expressions), operators, params, args, cases, and comments. Codegen writes grammar/asdl/generated/PinescriptASTNode.py. src/pynescript/ast/node.py re-exports that module so the rest of the codebase imports from pynescript.ast import node as ast (or from pynescript.ast.node import Script). ASDL is the contract between builder (produces nodes), evaluator/LSP (consume nodes), and unparser (serializes nodes). Changing a field without regenerating and updating all three is a schema break. Conceptual model Each product sum type becomes a Python class hierarchy under AST with: fields — logical children (walked by iterfields / visitors) attributes — location metadata (lineno, coloffset, endlineno, endcoloffset) where declared Interface surface Module roots (mod) | Constructor | Fields | Use | | --- | --- | --- | | Script | stmt body, string annotations | Full file (mode="exec") | | Expression | expr body | Single expression (mode="eval") | Statements (stmt) | Constructor | Notable fields | | --- | --- | | FunctionDef | name, param args, body, expr? returns, method?, export?, annotations | | TypeDef | UDT type Name body (fields + methods as stmts) | | EnumDef | enum Name members | | Assign | target, optional value/type/mode (Var/VarIp), export?, annotations | | ReAssign | target := value, or obj.f = / a[i] = (not bare name =) | | AugAssign | += … ops | | Import | namespace/name/version, optional alias | | Expr | Expression statement | | Break / Continue | Loop control | All stmt and most other attributed products carry location attributes. Expressions (expr) Operators and primaries: BoolOp, BinOp, UnaryOp, Conditional (? :), Compare Call, Constant, Attribute, Subscript, Name, Tuple Structures as expressions: ForTo, ForIn, While, If, Switch — same shapes can appear as values (Pine’s expression-oriented control flow) Qualify — const/input/simple/series applied to a type/expression Specialize — generic specialization form (value + args) Auxiliaries | Product | Role | | --- | --- | | declmode | Var \| VarIp | | typequal | Const \| Input \| Simple \| Series | | exprcontext | Load \| Store | | param | Function/method parameter | | arg | Call argument (optional keyword name) | | case | Switch arm (pattern?, body) | | cmnt / Comment | Comment with value and kind (annotation classification) | Operators are empty product constructors used as tags — the unparser maps class names back to tokens: Arithmetic / bool / compare: Add, Sub, Mult, Div, Mod, And, Or, Eq, NotEq, Lt, LtE, Gt, GtE Unary: Not, UAdd, USub, Invert (~) Bitwise (Pine v+): BitAnd, BitOr, BitXor, LShift, RShift Importing nodes ``python from pynescript.ast import node as ast script = ast.Script(body=[ ast.Expr(value=ast.Call( func=ast.Name(id="plot", ctx=ast.Load()), args=[ast.Arg(value=ast.Name(id="close", ctx=ast.Load()))], )) ]) ` Prefer constructing via parse() unless synthesizing trees for tests. Internals Paths | Path | Edit? | | --- | --- | | src/pynescript/ast/grammar/asdl/resource/Pinescript.asdl | Yes — schema source of truth | | src/pynescript/ast/grammar/asdl/generated/PinescriptASTNode.py | No — regenerated | | src/pynescript/ast/grammar/asdl/tool/generate.py | Generator entry | | src/pynescript/ast/node.py | Thin re-export (from .grammar.asdl.generated import ) | Generated class shape Excerpt of generated form (do not edit this file; shown for orientation): `python @dataclasses.dataclass class FunctionDef(stmt): name: identifier = field(default=None) args: list[param] = field(defaultfactory=list) body: list[stmt] = field(defaultfactory=list) returns: expr | None = field(default=None) method: int | None = field(default=None) export: int | None = field(default=None) annotations: list[string] = field(defaultfactory=list) fields: ClassVar[list[str]] = [ "name", "args", "body", "returns", "method", "export", "annotations", ] ` stmt base injects location attributes. Hashability is forced to object.hash so nodes can live in sets/maps by identity when needed. returns is the optional leading type spec (int f(x) => …), not a runtime return value. Regeneration When the schema changes: `bash Project entry (wraps asdl/tool/asdlgen.py, then ruff format) python -m pynescript.ast.grammar.asdl.tool.generate equivalent: asdlgen.py resource/Pinescript.asdl -o generated/PinescriptASTNode.py ` Do not invoke bare pyasdl … -o generated/ — the project generator writes a file (PinescriptASTNode.py), not a directory of modules. Then update, in the same change set: . builder.py construction sites . unparser.py visit methods . Evaluator expression/statement handlers . Any isinstance checks in LSP features Dual nature of structures ASDL places If, ForTo, ForIn, While, Switch under expr, while the builder often wraps structure-as-statement under Expr(value=…). Annotation collection (StatementCollector) treats assign/reassign/augassign/expr that hold structure values specially so nested statements remain visible for //@… pairing. Invariants . Schema first. New language constructs need an ASDL constructor (or a clear encoding into an existing one) before evaluator semantics. . Fields vs attributes. Visitors walk fields only. Location is metadata; missing locations are fillable via fixmissinglocations. . Optional flags as int?. method and export are integer flags (truthy when set), not booleans — match builder assignments (export = ). . FunctionDef.returns is a type expression. Engines must not visit() it as an executable value. . Generated file is not a style playground. Formatting/lint of PinescriptASTNode.py is overwritten on regen. Worked examples Map ASDL to a short script `pine //@version= f(x) => x + a = f(close) ` Approximate tree: `text Script( annotations=["//@version=", ...], body=[ FunctionDef(name="f", args=[Param(name="x")], returns=None, body=[Expr(BinOp(...))]), Assign(target=Name("a", Store), value=Call(...)), ], ) ` Dump fields programmatically `python from pynescript.ast.helper import parse, iterfields tree = parse("x = ") for name, value in iterfields(tree): print(name, type(value).name) ` Failure modes | Failure | Cause | | --- | --- | | AttributeError on new field | Schema regenerated but consumer not updated | | Builder constructs wrong product | ASDL and grammar out of sync | | Unparser omits new node | Missing visitNewNode → generic_visit may emit nothing useful | | Hand-edit of generated nodes lost | Edited generated/ instead of .asdl` | See also Builder Visitor / transformer Unparser Grammar (ANTLR) --- FILE: docs/pyne/core/builder.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "AST Builder" description: "PinescriptASTBuilder: ANTLR parse-tree visitor that constructs ASDL nodes, locations, and comment kinds." --- AST Builder The builder is the semantic boundary between ANTLR’s concrete parse tree and PYNE’s ASDL abstract tree. Abstract src/pynescript/ast/builder.py defines three cooperating mixins: . PinescriptASTLocator — maps ParserRuleContext.start/stop tokens to lineno / coloffset / end. . PinescriptCommentParser — classifies //… text into annotation kinds (@=S, @F, , plain //, …). . PinescriptASTBuilder — subclasses generated PinescriptParserVisitor, implements visit for every meaningful rule, returns ASDL nodes. helper.parse constructs a builder, runs builder.visit(rulecontext), then (in exec mode) uses StatementCollector + comment tokens to attach annotations. The builder itself does not walk the default token channel for comments; that is a post-pass. Conceptual model Interface surface You rarely instantiate the builder directly; parse() does. For tools that already hold a parse tree: ``python from pynescript.ast.builder import PinescriptASTBuilder from pynescript.ast.grammar.antlr.parser import PinescriptParser ... construct parser, parse to ctx ... node = PinescriptASTBuilder().visit(ctx) ` Locator API The live path writes attributes directly. Do not describe setLocations as a dict apply — that was an older micro-opt leftover. | Method | Role | | --- | --- | | setLocations(node, ctx) | Hot path. Sets node.lineno / coloffset / end from ctx.start / ctx.stop (no intermediate dict). | | getLocations(ctx) | Unused helper that still returns a four-key dict. Not called by visit methods. | End column calculation accounts for embedded newlines in the stop token text. The builder instance is stateless; helper.SHAREDBUILDER reuses one object for every parse. Comment kinds parseComment returns (kind, parts): | Pattern | Kind prefix | Suffix | Example | | --- | --- | --- | --- | | //@version = | @= | S (script) | version assignment | | //@description … / //@strategyalertmessage … | @ | S | script description / alert | | //@function / //@returns | @ | F | function docs | | //@type | @ | T | type docs | | //@variable | @ | V | variable docs | | //@param name … | @ | F | param docs | | //@field name … | @ | T | field docs | | // region / endregion | | — | editor regions | | other // | // | — | ordinary comment | helper.addannotations filters by suffix when attaching to nodes. Store context setstorectx walks assignment targets (Name, nested Tuple) and sets ctx = Store() so load/store distinction is available to later passes (Python-ast style). Representative visit methods | Rule visitor | Emits | | --- | --- | | visitStartscript | Script(body) | | visitStartexpression | Expression(body) | | visitFunctiondeclaration / visitMethoddeclaration | FunctionDef (method= for methods; optional returns= type spec) | | visitTypedeclaration | TypeDef | | visitEnumdeclaration | EnumDef | | visitVariabledeclaration | Assign shell (target/type/mode); parent fills value | | visitSimplenameinitialization / visitCompoundnameinitialization | Assign (export= if EXPORT) | | visitSimplereassignment / visitCompoundreassignment | ReAssign (:=, or = on attr/subscript) | | visit_augassignment | AugAssign | | visitIfstructure / visitFor / visitWhile / visitSwitch | Structure expressions | | visitexpression (disjunction → … → primary) | Operator trees with correct associativity | | visitLiteral | Constant (numbers via ast.literaleval, strings, bools, colors) | | visitPrimaryexpressioncall | Call + Arg list (positional/keyword) | Expression climbing follows the grammar’s layered rules (conditional → disjunction → conjunction → bitwise or/xor/and → equality → inequality → shift → additive → multiplicative → unary (~/not/+/-) → primary). returns is visited from ctx.typespecification() and must not be evaluated as a value by later engines. Internals Path Implementation: src/pynescript/ast/builder.py Visitor base: src/pynescript/ast/grammar/antlr/visitor.py → generated PinescriptParserVisitor Node types: src/pynescript/ast/node.py Annotation pairing: src/pynescript/ast/collector.py + helper.addannotations Statement list flattening visitStatements flattens each statement visitor’s result: some rules return a list (simple multi-statement lines joined by commas), so the body is always a flat list[stmt]. Export flag When EXPORT is present on library declarations or name initialization, the builder sets export = on the corresponding node (integer flag per ASDL int?). Assign vs ReAssign visitVariabledeclaration always builds Assign (optional type / var/varip mode). = after a bare or typed name is initialization. visitSimplereassignment emits ReAssign for obj.f = / a[i] = / name := — never for name =. See Grammar. Field / enum members Field definitions become Assign-like statements inside TypeDef.body (with optional VarIp mode). Enum members become assignment-shaped stmts under EnumDef.body. The unparser special-cases method members of types when pretty-printing. Invariants . Every constructed stmt/expr that has a source span should receive setLocations (direct attribute writes) so LSP diagnostics and getsourcesegment work. . Visitor method names must track generated rule names. Renaming a parser rule without updating the builder is a silent genericvisit fallback (wrong or empty trees). . Do not edit builder.py.bak. Live builder is builder.py only (stale backups are forbidden under project policy). . Grammar and builder co-evolve. A resource-only grammar change that adds rules is incomplete until visit exists. Worked examples What x = close + becomes Rough structure after build: `text Assign( target=Name(id="x", ctx=Store()), value=BinOp( left=Name(id="close", ctx=Load()), op=Add(), right=Constant(value=), ), ) ` Export const initialization Grammar: EXPORT? variabledeclaration EQUAL structureexpression Builder sets assign.export = when ctx.EXPORT() is present — required for library export of typed constants. Typed UDF (FunctionDef.returns) `pine int ilog(int n) => // ... ` visitFunctiondeclaration stores the leading typespecification on FunctionDef.returns (here a Name(id="int")). Methods do the same with method=. The unparser prints it back as a leading type. Debugging a missing visitor `python from pynescript.ast.helper import parse, dump print(dump(parse(yoursnippet), indent=)) ` If a subtree is None or a raw unexpected type, find the rule in PinescriptParser.g and the matching visitRulename in builder.py. Failure modes | Symptom | Cause | | --- | --- | | AttributeError: '…Context' object has no attribute '…' | Full parser regen changed accessors; builder outdated | | Locations all zero / missing | Forgot setLocations on new visit method | | Annotations not attached | Comment kind suffix wrong, or statement not yielded by StatementCollector | | Tuples not assignable | Store context not propagated through Tuple.elts` | | Structure expression lost | Wrong rule branch between statement vs expression path | See also Grammar (ANTLR) ASDL schema Helper API Error model --- FILE: docs/pyne/core/error-model.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Error Model" description: "SyntaxError, IndentationError, SyntaxErrorDetails, and PinescriptErrorListener bridge from ANTLR to diagnostics." --- Error Model Parse failures in PYNE are not bare ANTLR console messages. They are structured exceptions with file, line, column, source excerpt, and a caret — suitable for CLI, tests, and LSP adapters. Abstract Two layers cooperate: . src/pynescript/ast/error.py — SyntaxErrorDetails, SyntaxError, IndentationError. . src/pynescript/ast/grammar/antlr/errorlistener.py — PinescriptErrorListener implements ANTLR’s ErrorListener.syntaxError, builds details from the recognizer + offending token, and raises the project SyntaxError. helper.parse installs this singleton listener on both lexer and parser after removing default listeners. Indentation problems raised from PinescriptLexerBase use the same exception hierarchy (IndentationError subclass). Note: these names intentionally shadow Python builtins (SyntaxError, IndentationError) inside the package — import carefully: ``python from pynescript.ast.error import SyntaxError as PineSyntaxError ` Conceptual model Interface surface SyntaxErrorDetails (NamedTuple) | Field | Type | Meaning | | --- | --- | --- | | filename | str | Path or | | lineno | int | -based line | | offset | int | -based column | | text | str | Source line (or excerpt) | | endlineno | int \| None | Multi-line span end | | endoffset | int \| None | End column | SyntaxError(Exception) `python class SyntaxError(Exception): def init(self, message: str, details): ... ` details may be: a single SyntaxErrorDetails instance, or positional components matching the NamedTuple fields. Attributes: message, details. str renders: `text {message} File "{filename}", line {lineno} {strippedline} {spaces}^ ` Offset is adjusted when the displayed line is lstrip()’d so the caret still points at the logical column. IndentationError(SyntaxError) Empty subclass for indent-stack failures from the lexer base (wrong nest, inconsistent levels). Catch either specifically or via the base SyntaxError. PinescriptErrorListener `python from pynescript.ast.grammar.antlr.errorlistener import PinescriptErrorListener listener = PinescriptErrorListener.INSTANCE singleton lexer.removeErrorListeners() lexer.addErrorListener(listener) parser.removeErrorListeners() parser.addErrorListener(listener) ` | Method | Role | | --- | --- | | syntaxError(...) | Build details; raise SyntaxError | | getFilenameFrom(recognizer) | Walk Parser → TokenStream → Lexer → InputStream/FileStream | | getInputTextFrom(recognizer, lineno) | Full buffer or single line | | splitLines | Portable newline split (\r\n/\n/\r) | If the ANTLR exception e is already a project SyntaxError, its details are updated; otherwise the new error’s cause is set to e. Internals Paths | Path | Role | | --- | --- | | src/pynescript/ast/error.py | Exception types | | src/pynescript/ast/grammar/antlr/errorlistener.py | ANTLR bridge | | src/pynescript/ast/grammar/antlr/resource/PinescriptLexerBase.py | May raise IndentationError / SyntaxError during tokenization | | src/pynescript/ast/helper.py | Wires listener; maps filename onto streams | Filename resolution order (InputStream) . getSourceName() if present . sourceName attribute . inputstream.name (set by helper to the user filename) . "" FileStream uses fileName. End span from offending token `text symbollen = stop - start + endlineno = token.line + newlinesintext endoffset = adjusted if multiline else column + symbollen ` Mirrors the builder’s location math so diagnostics and AST spans speak the same coordinate system. Relationship to linter E PineLinter.checksyntax catches any Exception from parse and wraps it as LintWarning(code="E", severity="error"). It does copy details.lineno / details.offset onto the warning when present, and prefers the short .message over the caret str. For the full caret rendering, catch pynescript.ast.error.SyntaxError at the call site instead of only reading linter output. LSP / tooling Adapters typically map: | Exception field | LSP Diagnostic | | --- | --- | | lineno / offset | Range.start | | endlineno / endoffset | Range.end | | message | message | | IndentationError | same, possibly different code | Invariants . Default ANTLR listeners must stay removed on the parse path — otherwise errors print twice and may not raise. . Singleton listener is stateless enough to share (INSTANCE); thread-local needs would require new instances. . Raising aborts parse immediately — no error recovery producing a partial tree through the public helper. . Package SyntaxError ≠ builtin — except SyntaxError without import may catch the wrong class. Worked examples Catching a parse error `python from pynescript.ast.helper import parse from pynescript.ast.error import SyntaxError as PineSyntaxError try: parse("f( =>\n", filename="bad.pine") except PineSyntaxError as e: print(e) caret form print(e.details.lineno, e.details.offset) print(e.details.filename) ` Distinguishing indentation `python from pynescript.ast.error import IndentationError as PineIndentError from pynescript.ast.error import SyntaxError as PineSyntaxError try: parse("if true\nplot()\n") missing indent under if — may indent-error depending on tokens except PineIndentError as e: print("indent", e.message) except PineSyntaxError as e: print("syntax", e.message) ` Manual construction (tests) `python from pynescript.ast.error import SyntaxError, SyntaxErrorDetails details = SyntaxErrorDetails("t.pine", , , " plot(\n", , ) err = SyntaxError("missing RPAR", details) assert "t.pine" in str(err) assert "^" in str(err) ` Failure modes | Failure | Cause | | --- | --- | | TypeError: unexpected type of input | Listener received a recognizer/stream combo it does not support | | Caret misaligned | Tabs vs spaces; display strips leading WS while offset counts raw | | Filename always | Stream name not set (bypassed helper) | | Exception swallowed as generic E | Only used linter path (line/column may still be filled from details) | | except SyntaxError misses Pine errors | Caught builtin only | | Partial trees | Not available via parse()` after error — redesign would need recovery listeners | See also Helper API Grammar (ANTLR) Linter LSP diagnostics Language core hub --- FILE: docs/pyne/core/grammar-antlr4.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Grammar (ANTLR)" description: "Resource .g grammars, generated artifacts, INDENT/DEDENT, regeneration, and the never-edit-generated rule." --- Grammar (ANTLR) Concrete syntax for Pine Script in PYNE is defined by a split lexer/parser ANTLR grammar plus hand-written base classes that inject Python-like indentation tokens. Abstract Two grammars live under src/pynescript/ast/grammar/antlr/resource/: PinescriptLexer.g — keywords, operators, literals (including v triple-quoted strings), comments, hidden whitespace. PinescriptParser.g — statements, expressions, type/enum/function/method declarations, control structures. Python runtime classes under generated/ are outputs of antlr -Dlanguage=Python. They are committed so end users never need a JDK. Never edit files under generated/ — changes evaporate on the next regeneration and fight the build pipeline. Hand-written bases (copied into generated/ by the generate tool): resource/PinescriptLexerBase.py — INDENT/DEDENT, newline elision, multiline string handling. resource/PinescriptParserBase.py — parser superClass hooks. Conceptual model Interface surface Application code should not import generated symbols directly when a wrapper exists: ``python from pynescript.ast.grammar.antlr.lexer import PinescriptLexer from pynescript.ast.grammar.antlr.parser import PinescriptParser from pynescript.ast.grammar.antlr.visitor import PinescriptParserVisitor from pynescript.ast.grammar.antlr.errorlistener import PinescriptErrorListener ` The public parse path is higher level — Helper API — which constructs lexer, token stream, parser, and strips default console error listeners in favor of PinescriptErrorListener. Entry rules (PinescriptParser.g) | Rule | Purpose | | --- | --- | | start → startscript | Full script (statements? EOF) | | startexpression | Single expression + optional NEWLINE + EOF | | startcomments | Comment stream only | Lexer structural facts | Token / channel | Behavior | | --- | --- | | INDENT / DEDENT | Imaginary tokens emitted by PinescriptLexerBase (declared in tokens { }) | | COMMENT | // line comments → COMMENTCHANNEL (not the default channel) | | HASHCOMMENT | … line comments remapped -> type(COMMENT) (must not swallow RRGGBB) | | BACKTICKS | Markdown fence ` remapped to COMMENT (paste noise) | | WS | Spaces/tabs/form-feed → HIDDEN | | UNICODENOISE | Curly quotes, bullets, arrows → HIDDEN | | ERRORTOKEN | Catch-all . for recovery/reporting | | TRIPLEDQSTRING / TRIPLESQSTRING | v multiline strings, typed as STRING | | LSHIFT / RSHIFT / TILDE / AMP / PIPE / CARET | Bitwise > ~ & | ^ (multi-char ops declared before ) | Keywords include var, varip, method, export, enum, type, qualifiers const/input/simple/series, and control forms if/for/while/switch. Many of those words are soft in identifier position (name alternatives) so as = input(...) parses. Parser structural facts Compound vs simple statements. Functions, methods, types, enums, and structure-valued assignments are compound; imports, break/continue, expression statements, and simple assignments terminate with NEWLINE. Trailing structures. a = , for i = to n is trailingstructurestatements (comma-separated simples ending in a structure). Structures are dual-use. if/for/while/switch appear both as statements and as expressions (structureexpression) — the classic Pine “if returns a value” form. Local blocks. Either indented (NEWLINE INDENT statements DEDENT) or inline single statement. Library export. EXPORT? prefixes function/method/type/enum and June- export const T name = … compound initializers. UDF return types. functiondeclaration / methoddeclaration accept an optional leading typespecification (int ilog(int n) => …). Builder stores it on FunctionDef.returns. Bitwise layer. Between and and comparisons: | ^ & then >, then additive. Unary ~ is Invert. Assign vs reassignment (..). = on a bare name is initialization (Assign via variabledeclaration). Reassignment of a name is := (ReAssign). = is reassignment only for attribute / subscript targets (obj.f =, a[i] =). Left-factored typed names. variabledeclaration is declarationmode? typespecification? namestore with the typed alternative first so int x = does not consume int as the identifier. Same left-factor on foriterator and parameter lists. Internals Paths | Path | Edit? | Role | | --- | --- | --- | | src/pynescript/ast/grammar/antlr/resource/PinescriptLexer.g | Yes | Lexer grammar | | src/pynescript/ast/grammar/antlr/resource/PinescriptParser.g | Yes | Parser grammar | | src/pynescript/ast/grammar/antlr/resource/PinescriptLexerBase.py | Yes | Indentation machine | | src/pynescript/ast/grammar/antlr/resource/PinescriptParserBase.py | Yes | Parser base | | src/pynescript/ast/grammar/antlr/generated/ | No | ANTLR output + copied bases | | src/pynescript/ast/grammar/antlr/tool/generate.py | Tool | Runs antlr, copies .py bases | | src/pynescript/ast/grammar/antlr/errorlistener.py | Hand | Maps ANTLR errors → ast.error.SyntaxError | LexerBase responsibilities PinescriptLexerBase is not optional sugar; it is part of the language definition: Ignore leading / collapse consecutive newlines; ensure trailing newline. Suppress newlines inside open () / [], after operators, and for soft wrap lines whose indent width is not a multiple of four. Push INDENT/DEDENT with tab length and indent length = . Special-case multiline string tokens so wrap indentation inside strings is not mis-tokenized as structure. Indent mistakes surface as pynescript.ast.error.IndentationError (subclass of the project SyntaxError). Regeneration `bash Project-aware entry (sets -o generated, -lib resource, copies Base.py) python -m pynescript.ast.grammar.antlr.tool.generate hatch lint:gen-parser is only the antlr CLI (antlr {args}) — pass flags yourself. hatch run lint:gen-parser -- -o …/generated -lib …/resource -listener -visitor -Dlanguage=Python …/resource/.g ` tool/generate.py invokes $(sys.executable)/../antlr with -o generated -lib resource -listener -visitor -Dlanguage=Python on resource/.g, then copies resource/.py into generated/. Requires a working antlr CLI (hatch lint env pulls antlr-cli). Generated modules are excluded from mypy/ruff strictness in pyproject.toml — do not “lint-fix” them by hand. Downstream after grammar change . Regenerate (or selectively refresh lexer — see case study). . Add/adjust visit methods in src/pynescript/ast/builder.py for new or renamed rules. . If the shape of the language changed (new statement/expression kind), update Pinescript.asdl and regenerate ASDL nodes — ASDL schema. . Smoke-test with parse / unparse on a minimal first-party snippet before broader regression. Invariants . Never hand-edit generated/. Patches belong in resource/.g or resource/Base.py. . Visitor contract. Generated PinescriptParserVisitor method names follow ANTLR rule names; the builder subclass must track renames. . Comments are off the default channel. Annotation logic in helper.collectcommentnodes iterates tokenstream.tokens for COMMENT type after fill(), and keeps only texts containing @. HASHCOMMENT / BACKTICKS are remapped to that same token type. . Indent unit is four spaces. Soft-wrap lines that are not multiples of four are newline-elided, not nested blocks. Worked examples Minimal grammar-level mental model `text //@version= indicator("x") if close > open plot() else plot() ` Lexer emits NEWLINE, INDENT, DEDENT around the if bodies. Parser builds ifstructure → builder yields ast.If with body / orelse statement lists. v triple-quoted strings Resource lexer rules (illustrative — see live PinescriptLexer.g): `g TRIPLEDQSTRING : '"""' ( ~'"' | '"' ~'"' | '""' ~'"' ) '"""' -> type(STRING) ; TRIPLESQSTRING : '\'\'\'' ( ~'\'' | '\'' ~'\'' | '\'\'' ~'\'' ) '\'\'\'' -> type(STRING) ; ` ANTLR quoting pitfall: putting "'''" or '"""' as fragment literals can produce tool errors (quote came as a complete surprise). Factor starters when needed: `g fragment TRIPLESQSTART: '\'' '\'' '\''; ` Content and source indentation inside the triple quotes are preserved literally (no automatic dedent). Typed names and = vs := (..) Pine lets type names occupy identifier positions (int is both a type and a legal NAME alternative via soft keywords). Alternatives must try the typed form first: `g variabledeclaration : declarationmode? typespecification namestore | declarationmode? namestore ; simplenameinitialization: EXPORT? variabledeclaration EQUAL expression; simplereassignment : assignmenttargetattribute EQUAL expression | assignmenttargetsubscript EQUAL expression | primaryexpression COLONEQUAL expression ; ` | Source | Node | | --- | --- | | x = / var float x = . / export const int N = | Assign (mode/type/export as present) | | x := | ReAssign | | obj.field = / arr[i] = | ReAssign (attribute/subscript =) | | x += | AugAssign | Putting namestore before typespecification namestore made int x = parse int as the variable and then fail on x. Do not invert those alternatives. Case study: targeted lexer refresh (-) Full regeneration once produced a PinescriptParser.py whose context accessors diverged from what the hand-written builder expected (e.g. missing templatespecsuffix()), breaking even trivial parses. The practical recovery: . Edit only resource/ grammar. . Generate the lexer in a clean temp directory (avoids nested src/ mirror paths). . Copy only PinescriptLexer.py (+ refreshed LexerBase.py) into generated/. . Leave committed PinescriptParser.py and visitors untouched unless prepared to patch the builder in the same change. . Verify immediately: `python from pynescript.ast.helper import parse, unparse ast = parse('indicator("x")\ns = """\nfoo\n bar\n"""\n') assert "foo" in unparse(ast) ` Failure modes | Failure | Cause | Mitigation | | --- | --- | --- | | mismatched input after """ | Old lexer ATN without triple rules | Refresh generated lexer from resource | | Full regen breaks builder | Parser context API drift | Selective copy; or update all visit together | | antlr not found | CLI not on PATH | Use hatch env or full path to antlr-cli | | Nested generated/src/pynescript/... junk | Running antlr with wrong -o cwd | Generate from clean temp + explicit -o | | IndentationError mid-file | Mixed tabs/spaces or wrong nest | Align to -space blocks; check soft-wrap rules | | Silent loss of hand patches | Edited generated/ | Revert; re-apply in resource/` | See also ASDL schema Builder Error model Helper API Missing features --- FILE: docs/pyne/core/helper-api.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Helper API" description: "parse, sha LRU cache, unparse, dump, walk, literaleval, and location utilities — the public AST surface." --- Helper API src/pynescript/ast/helper.py is the stable library face of the language core. It orchestrates ANTLR, the builder, annotations, and the unparser behind functions deliberately similar to CPython’s ast module. Abstract Call parse to obtain an ASDL tree; dump / walk / iter to inspect it; unparse to regenerate source; literaleval for constant-folding-safe evaluation of literal expressions. Location helpers (copylocation, fixmissinglocations, getsourcesegment, incrementlineno) support tooling that rewrites or reports on trees. clearparsecache / parsecacheinfo control the process-local sha LRU. Everything higher in the stack (CLI, LSP, Pro API, workers) ultimately routes through this module for front-end work. Conceptual model Interface surface parse(source, filename="", mode="exec") -> AST | Parameter | Meaning | | --- | --- | | source | Full script or expression text | | filename | Error reporting name; resolved to absolute path if the file exists | | mode | "exec" → startscript → Script; "eval" → startexpression → Expression | Raises: ValueError — invalid mode pynescript.ast.error.SyntaxError — lexer/parser failure (via PinescriptErrorListener) Side effects during exec mode: . Temporarily raises sys.getrecursionlimit to at least . . Collects COMMENT tokens containing @ and attaches annotations to eligible statements. If the source has no @, the annotation pass is skipped. filename is not part of the cache key (only diagnostics). Parse cache Successful trees are stored in a process-local, thread-safe OrderedDict LRU keyed by (sha(source.encode("utf-")), mode). | Control | Default | Meaning | | --- | --- | --- | | PYNEPARSECACHE | on | / false / off / no disables | | PYNEPARSECACHEMAX | | Max entries (must be ≥ ) | | clearparsecache() | — | Drop all entries and reset hit/miss counters | | parsecacheinfo() | — | {enabled, size, maxsize, hits, misses} | On a cache hit, the same AST object is returned (after scrubbing stale pinecallsite attrs). Treat it as read-only: a NodeTransformer or incrementlineno mutates the cached entry for every later caller. After intentional mutation, call clearparsecache() or parse with the cache disabled. unparse(node) -> str Delegates to unparsenode — a per-thread reused NodeUnparser (warm visitor cache). Public return value is still a new str. See Unparser. dump(node, , annotatefields=True, includeattributes=False, indent=None) -> str Recursive pretty-print of node trees. With indent (int spaces or string), multi-line layout is used for non-trivial nodes. literaleval(nodeorstring, context=None, datafeed=None, dataprovider=None) -> Any Strings are parse(..., mode="eval") first. Unwraps Expression.body. Uses NodeLiteralEvaluator — not full script evaluation. Non-literal graphs raise. Tree navigation | Function | Behavior | | --- | --- | | iterfields(node) | Yields (name, value) for set fields | | iterchildnodes(node) | Direct AST children (incl. list items) | | walk(node) | BFS over the full subtree | Location utilities | Function | Behavior | | --- | --- | | copylocation(new, old) | Copy lineno/col/end when both define the attribute | | fixmissinglocations(node) | Fill gaps from parent defaults starting at (, ) | | incrementlineno(node, n=) | Shift all line numbers | | getsourcesegment(source, node, , padded=False) | Slice original source by node span; None if incomplete ends | all export list clearparsecache, copylocation, dump, fixmissinglocations, getsourcesegment, incrementlineno, iterchildnodes, iterfields, literaleval, parse, parsecacheinfo, unparse, walk. Internals Path src/pynescript/ast/helper.py — all public helpers above. Related: src/pynescript/ast/collector.py — StatementCollector for annotation pairing src/pynescript/ast/unparser.py — unparsenode (thread-local NodeUnparser) src/pynescript/ast/evaluator — NodeLiteralEvaluator (literaleval only) Parse pipeline (parse) . Validate mode ∈ {exec, eval}. . Raise recursion limit to ≥ if needed. . Bind a thread-local lexer + token stream + parser (ThreadParseEngine). Process-wide reuse is unsafe (indent / token-stream bleed). . SLL first (BailErrorStrategy). On ParseCancellationException, reset and re-parse LL (DefaultErrorStrategy). Trees match pure-LL on success. . PinescriptASTBuilder.visit via the shared stateless builder. . Exec mode: if source contains @, collect statements + @ comments and addannotations. Default ANTLR console listeners are removed; only PinescriptErrorListener.INSTANCE is installed. Annotation algorithm (addannotations) . Merge comments and statements; sort by (lineno, coloffset). . Group consecutive comments vs statements. . Keep only kinds starting with @. . Script-level: first group members with kind ending in S → script.annotations. . Pair remaining comment groups with following statements; attach F/T/V filters to FunctionDef / TypeDef / Assign. Comment nodes are not left in Script.body; only string annotation lists are stored on targets. Collection itself skips tokens whose text has no @ (plain // and // region never become Comment nodes). Stream helpers | Internal | Role | | --- | --- | | parseinputstream | InputStream(source) + stream.name = filename | | parsefilestream | FileStream for on-disk scripts | | getabsolutepath | Resolve existing paths; leave ` alone | Invariants . mode is a closed set. Only exec and eval. . Default listeners are removed. Console ANTLR spam is replaced by raising project SyntaxError. . dump requires an AST instance. Non-nodes raise TypeError. . Round-trip tests should use parse + unparse, not raw builder access, so annotation behavior matches production. . Cache identity is shared. Do not mutate a cached tree unless you also clearparsecache(). . filename is diagnostic-only and is not part of the sha key. Worked examples Inspect a tree `python from pynescript.ast.helper import parse, dump, walk tree = parse(""" //@version= indicator("demo") plot(close) """) print(dump(tree, indent=)) print(sum( for in walk(tree)), "nodes") ` Source segment `python from pynescript.ast.helper import parse, getsourcesegment src = "a = \nb = a + \n" tree = parse(src) assignb = tree.body[] print(getsourcesegment(src, assignb)) "b = a + \n" or similar span ` Literal evaluation `python from pynescript.ast.helper import literaleval assert literaleval(" + ") == assert literaleval("'hi'") == "hi" ` Parse cache `python from pynescript.ast.helper import parse, parsecacheinfo, clearparsecache src = 'indicator("x")\nplot(close)\n' a = parse(src) b = parse(src) assert a is b same object on hit print(parsecacheinfo()) enabled, size, maxsize, hits, misses clearparsecache() ` Failure modes | Failure | Notes | | --- | --- | | Deeply nested ternaries hit recursion | Mitigated by temporary limit ≥ ; pathological depth can still fail | | getsourcesegment returns None | Missing endlineno / endcoloffset | | literaleval raises | Non-literal AST (names, calls beyond allowed set) | | Filename in errors | Expected when parsing pure strings without a path | | Annotations missing | Comment not @… form, or not immediately preceding eligible stmt | | Transformer “leaks” across parse() calls | Cache hit returned the same object you mutated — clear or disable cache | | PYNEPARSECACHE_MAX ignored | Non-integer or < ` falls back to | See also Builder Unparser Visitor / transformer Error model --- FILE: docs/pyne/core/index.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Language Core" description: "ANTLR grammar, ASDL AST, builder, visitors, unparser, type system, linter, and error model for PYNE." --- Language Core The language core is the front half of the evaluate contract: source text becomes a typed tree; the tree can be walked, rewritten, dumped, and unparsed without ever entering the bar loop. Abstract PYNE treats Pine Script as a formal pipeline, not a host plugin. Lexing and parsing are ANTLR-driven. The concrete syntax tree is lowered by a hand-written visitor (PinescriptASTBuilder) into ASDL-generated dataclasses. Public helpers in helper.py mirror Python’s ast module: parse, unparse, dump, walk, literaleval, plus a process-local sha LRU parse cache (clearparsecache, parsecacheinfo). Static tools (linter, type registry, LSP) consume the same tree. Evaluation is out of scope here — see Runtime. Hard rule: edit grammar only under resource/. Paths under generated/ are regenerated artifacts and must not be hand-edited. Conceptual model | Stage | Role | Primary path | | --- | --- | --- | | Grammar | Concrete syntax | src/pynescript/ast/grammar/antlr/resource/.g | | ASDL | Algebraic node shape | src/pynescript/ast/grammar/asdl/resource/Pinescript.asdl | | Builder | CST → AST | src/pynescript/ast/builder.py | | Helper | Public parse/unparse API | src/pynescript/ast/helper.py | | Nodes | Re-export generated types | src/pynescript/ast/node.py | Interface surface Consumers almost always enter through pynescript.ast.helper: ``python from pynescript.ast.helper import parse, unparse, dump, walk, literaleval tree = parse('//@version=\nindicator("x")\nplot(close)') print(dump(tree, indent=)) print(unparse(tree)) ` Successful parse results are cached by sha(source) + mode (default ON; PYNEPARSECACHE= disables). Treat returned trees as read-only. Modes: | mode | Entry rule | Root node | | --- | --- | --- | | "exec" (default) | startscript | Script | | "eval" | startexpression | Expression | Full page inventory: | Page | Topic | | --- | --- | | Grammar (ANTLR) | Resource vs generated; typed names; = vs := | | ASDL schema | Algebraic AST; codegen | | Builder | CST visitor → ASDL nodes | | Helper API | parse, cache, dump, walk, locations | | Unparser | Precedence-aware source regen | | Visitor / transformer | Read vs rewrite | | Type system | Qualifiers, UDT registry | | Linter | Static rules, codes | | Error model | SyntaxError, indent, listener | Internals ` src/pynescript/ast/ helper.py public pipeline + sha LRU parse cache builder.py PinescriptASTBuilder (setLocations = direct attrs) unparser.py NodeUnparser + Precedence visitor.py NodeVisitor transformer.py NodeTransformer collector.py StatementCollector (annotation pairing) linter.py PineLinter typesystem.py Type, TypeRegistry, MethodResolver error.py SyntaxError / IndentationError node.py from grammar.asdl.generated import grammar/ antlr/ resource/ EDIT HERE: .g + Base.py generated/ NEVER HAND-EDIT errorlistener.py tool/generate.py asdl/ resource/ EDIT HERE: Pinescript.asdl generated/ NEVER HAND-EDIT tool/generate.py ` Thin wrappers grammar/antlr/lexer.py, parser.py, visitor.py re-export generated classes so application code imports stable paths. parse uses a thread-local ANTLR engine and a two-stage SLL then LL strategy; successful trees go into a sha LRU (PYNEPARSECACHE, default ). Invariants . Resource is source of truth. Lexer/parser .g and Pinescript.asdl define syntax and node shape; generated Python is a build product (committed so install does not require Java). . Round-trip is a test oracle. For the supported surface, unparse(parse(src)) should re-parse and preserve semantics (not byte-identical formatting). . Locations are first-class. Statements and expressions carry lineno, coloffset, optional end for diagnostics and LSP. . Annotations are not free-floating. Comment tokens on COMMENTCHANNEL are reified as Comment nodes and attached to Script / FunctionDef / TypeDef / Assign via kind suffixes (S, F, T, V). . Recursion budget. Deep ternary chains raise the Python recursion limit temporarily during parse (cap ≥ ). . Cached trees are shared by identity. Mutating a parse() result (transformer, incrementlineno) mutates the LRU entry. Call clearparsecache() after intentional mutation, or disable the cache. . FunctionDef.returns is a type spec, not an executable expression. Optional leading typespecification on UDFs/methods is stored there (..+). Worked examples Minimal script: `python from pynescript.ast.helper import parse, dump src = """ //@version= indicator("demo") x = ta.sma(close, ) plot(x) """ tree = parse(src) assert tree.class.name == "Script" assert any("version" in a for a in (tree.annotations or [])) print(dump(tree, includeattributes=True, indent=)) ` Expression-only eval mode: `python from pynescript.ast.helper import parse, literaleval expr = parse(" + ", mode="eval") assert literaleval(expr) == ` Failure modes | Symptom | Likely cause | Where to look | | --- | --- | --- | | SyntaxError with caret | Lexer/parser reject | Error model | | IndentationError | Bad INDENT/DEDENT from LexerBase | PinescriptLexerBase.py | | Missing visit_ after grammar change | Builder not updated for new rule | Builder | | Hand-edit of generated/ lost on regen | Violated resource-only rule | Grammar | | Full regen breaks builder.py` | Parser context accessors changed | Prefer targeted lexer refresh (v case study) | See also Grammar (ANTLR) Helper API Runtime LSP Compatibility --- FILE: docs/pyne/core/linter.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Linter" description: "PineLinter static rules: syntax via parse, version, deprecations, naming, style codes." --- Linter The core linter is a lightweight static checker that sits on the parse pipeline plus regex rules. It is not a full dataflow analyzer; it is the first automated gate for “does this even look like modern Pine?” Abstract src/pynescript/ast/linter.py exports: LintWarning — dataclass (code, message, line, column, severity) PineLinter — stateful runner accumulating warnings lintscript(source, filename) / lintfile(filepath) — convenience entry points Rules run in fixed order: syntax → version → deprecated patterns → naming → style. Syntax failures become severity "error" with code E; other rules are mostly "warning". Conceptual model Interface surface ``python from pynescript.ast.linter import lintscript, PineLinter, LintWarning warnings = lintscript(""" indicator("x") plot(close) """) for w in warnings: print(w) warning: [W] Missing @version ... at line ` LintWarning | Field | Type | Meaning | | --- | --- | --- | | code | str | Stable id (E, W, C, …) | | message | str | Human-readable explanation | | line | int \| None | -based when known | | column | int \| None | Reserved / optional | | severity | str | "warning" (default) or "error" | str format: {severity}: [{code}] {message} at {location}. PineLinter.lint(source, filename="") -> list[LintWarning] Resets self.warnings, runs all checks, returns the list (also stored on the instance). File helper `python from pynescript.ast.linter import lintfile issues = lintfile("strategies/meanreversion.pine") ` Reads UTF- text then delegates to lintscript. Rule catalog Syntax | Code | Severity | Condition | | --- | --- | --- | | E | error | parse(source, filename) raises any exception | Does not attempt recovery; one syntax error check per run. Location is taken from pynescript.ast.error.SyntaxError.details (lineno, offset) when present; otherwise a line N regex on the exception text. The warning message prefers the short .message attribute so caret dumps do not land in chips. Version | Code | Condition | | --- | --- | | W | No //@version = N (flexible whitespace) match | | W | Version integer | | C | Line matches ^\s+if\s+ — flagged as “single-line if without braces” heuristic | | C | File does not end with newline | Internals Path src/pynescript/ast/linter.py Dependencies: pynescript.ast.parse (re-exported path via from pynescript.ast import parse — subject to the helper sha LRU) Standard library re, dataclasses Design stance The linter intentionally uses regex over AST for several rules so it still partially works when parse fails (version/deprecations/style still run after a failed syntax check — note: checksyntax records E but does not abort the pipeline). That means: False positives/negatives on fancy formatting are possible. Deep semantic issues (wrong series type, undefined names) belong to evaluator/LSP diagnostics, not these codes. Integration points CLI, editor save hooks, and CI can call lintscript without spinning up the full evaluator. LSP diagnostics may layer additional semantic checks beyond this module. Invariants . lint() always clears prior warnings on that instance before running. . Codes are stable public strings — treat renames as breaking for tooling. . Syntax errors are not fatal to the function — you get E plus any later regex hits. . No mutation of source — pure analysis. Worked examples Clean modern script ``python from pynescript.ast.linter import lintscript src = """//@version= indicator("ok") length = basis = ta.sma(close, length) plot(basis) """ assert lintscript(src) == [] may still flag C if naming heuristic fires ` Note: basis = ta.sma(...) triggers C under the current rule (lowercase LHS). Prefer documenting that heuristic when teaching style. Missing version `python ws = lintscript('indicator("x")\nplot(close)\n') assert any(w.code == "W" for w in ws) ` Programmatic filter `python errors = [w for w in lintscript(src) if w.severity == "error"] if errors: raise SystemExit() ` Failure modes | Issue | Explanation | | --- | --- | | C on legitimate indented if blocks | Regex is coarse; multi-line if with body still matches ^\s+if\s+ | | C noise | Many valid snakecase or lower identifiers | | E still not a caret dump | Linter stores the short .message plus line/column; catch pynescript.ast.error.SyntaxError for the full caret str | | Encoding errors in lintfile | Non-UTF- files raise at read time | | False security deprecation | Pattern looks for quoted EXCHANGE:SYM form only | See also Helper API — underlying parse` Error model LSP diagnostics End-user troubleshooting --- FILE: docs/pyne/core/type-system.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Type System" description: "Pine v type model: qualifiers, builtins, collections, UDTs, TypeRegistry, and MethodResolver." --- Type System PYNE’s type layer models Pine’s qualified types and user-defined types (UDTs) as Python objects usable by analysis and evaluation — not as a full independent typechecker IR (yet), but as the shared vocabulary for “what kind of value is this?” Abstract src/pynescript/ast/typesystem.py defines: Qualifiers — const, simple, series, input (TypeQualifier) Builtin kinds — int, float, bool, string, color, na, enum Composite types — array, matrix, map UDTs — UserDefinedType with Fields and MethodSignatures Runtime instances — ObjectInstance for UDT values Registry / resolution — TypeRegistry, MethodResolver (.new, .copy, user methods) ASDL already has Qualify / Specialize expression forms and typequal constructors; the type system module is the semantic counterpart used when evaluating UDT construction and when tools reason about types. Conceptual model Interface surface Qualifiers ``python from pynescript.ast.typesystem import TypeQualifier TypeQualifier.CONST compile-time constant TypeQualifier.SIMPLE bar-invariant TypeQualifier.SERIES per-bar series TypeQualifier.INPUT input. style parameter ` Type.str renders "series float" when a qualifier is set. Builtins and factories `python from pynescript.ast.typesystem import ( BuiltinTypeKind, inttype, floattype, booltype, stringtype, colortype, TypeQualifier, ) t = floattype(TypeQualifier.SERIES) "series float" ` | Factory | Kind | | --- | --- | | inttype | INT | | floattype | FLOAT | | booltype | BOOL | | stringtype | STRING | | colortype | COLOR | Collections `python from pynescript.ast.typesystem import ArrayType, MatrixType, MapType, inttype, stringtype ArrayType(inttype()) array MatrixType(floattype()) matrix MapType(stringtype(), inttype()) map ` UDTs `python from pynescript.ast.typesystem import UserDefinedType, Field, MethodSignature, floattype, inttype point = UserDefinedType("point") point.addfield(Field("x", floattype(), defaultvalue=.)) point.addfield(Field("y", floattype(), defaultvalue=., varip=False)) point.addmethod(MethodSignature("length", [], returntype=floattype())) ` Field supports optional defaultvalue and varip (mirrors varip fields in type declarations). TypeRegistry `python from pynescript.ast.typesystem import TypeRegistry reg = TypeRegistry() reg.registertype(point) assert reg.isbuiltintype("float") assert reg.isuserdefinedtype("point") assert reg.gettype("float") is not None ` Builtins are seeded in initbuiltintypes (int, float, bool, string, color, na, enum). ObjectInstance + MethodResolver `python from pynescript.ast.typesystem import ObjectInstance, MethodResolver inst = ObjectInstance(point) inst.setfield("x", .) resolver = MethodResolver(reg) .new / .copy handled specially copy = resolver.resolvemethod(inst, "copy", []) ` | Method name | Behavior | | --- | --- | | new | Construct instance; positional args fill fields in declaration order | | copy | Shallow copy of field dict | | other | Lookup UserDefinedType.methods; missing → AttributeError | Unknown field get/set on ObjectInstance raises AttributeError with type name context. Relation to ASDL typequal | ASDL | TypeQualifier | | --- | --- | | Const | CONST | | Input | INPUT | | Simple | SIMPLE | | Series | SERIES | Builder produces Qualify(qualifier=…, value=…) for annotated types in the AST; the type system module is used when those need runtime/type-registry meaning (especially UDTs in builtins/evaluator). Internals Path src/pynescript/ast/typesystem.py — sole definition site for the classes above. Not star-exported from pynescript.ast; import the module directly. Consumers (outside this doc’s page set, for orientation): Evaluator UDT / collection code (evaluator/builtins/, matrixevaluator, etc.) Future/static analysis hooks (LSP hover may grow toward this model) Compatibility Type.iscompatiblewith is currently shallow: BuiltinType compares by equality; the base method returns False for other pairs. Treat richer subtyping (series/simple lattice, numeric promotion) as not fully encoded here — bar-loop semantics still own much of the coercion behavior. na and enum Registered as builtin kinds for name lookup. Enum definitions in scripts appear as AST EnumDef; linking enum members into BuiltinTypeKind.ENUM instances is evaluator-side work. Invariants . Registry lookup prefers builtins over UDTs of the same name. . Field presence is schema-true. Setting an undeclared field is an error (no open bags). . .new does not type-check arguments against field types in the resolver — it assigns positionally. . varip is metadata on Field, not a separate type qualifier enum member. Worked examples Model a simple UDT from Pine `pine type candle float o float h float l float c ` `python from pynescript.ast.typesystem import UserDefinedType, Field, floattype, TypeRegistry, MethodResolver, ObjectInstance candle = UserDefinedType("candle") for name in "o h l c".split(): candle.addfield(Field(name, floattype())) reg = TypeRegistry() reg.registertype(candle) c = MethodResolver(reg).resolvemethod(ObjectInstance(candle), "new", [, , ., .]) assert c.getfield("h") == ` Qualified series float `python from pynescript.ast.typesystem import floattype, TypeQualifier assert str(floattype(TypeQualifier.SERIES)) == "series float" ` Failure modes | Failure | Cause | | --- | --- | | AttributeError: Field '…' not found | Typo or outdated UDT schema | | AttributeError: Method '…' not found | Method never addmethod’d; not new/copy | | Silent type confusion | iscompatiblewith too weak — do not rely on it alone for safety | | Name shadowing | UDT registered with a builtin name is unreachable via get_type | See also ASDL schema — Qualify, TypeDef, EnumDef` Runtime — bar-loop use of values Builder — parsing type declarations Linter — style-level checks (not full typing) --- FILE: docs/pyne/core/unparser.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Unparser" description: "NodeUnparser: precedence-aware AST → Pine Script source regeneration." --- Unparser The unparser is the inverse of the builder: ASDL nodes become source text with correct operator parenthesization and Pine idioms (=>, :=, var/varip, export, method vs function). Abstract src/pynescript/ast/unparser.py implements NodeUnparser(NodeVisitor) and a Precedence enum. helper.unparse(node) calls unparsenode, which reuses a thread-local NodeUnparser (warm type-visitor cache; visit still resets the buffer each call). Output is semantically faithful, not a pretty-printer of original trivia: whitespace is normalized; comments that never became annotations are not reconstructed; annotation strings that were attached are re-emitted. Round-trip tests (parse → unparse → parse) are the primary oracle for grammar/builder/unparser coherence. Conceptual model Parenthesization: higher Precedence values bind tighter (TEST loosest, ATOM tightest). A node is wrapped in () when the precedence stored on it (the parent context) is strictly greater than the node’s own operator level. Equal levels do not wrap (left-associative chains). Interface surface ``python from pynescript.ast.helper import parse, unparse src = """ //@version= f(x) => x plot(f(close)) """ print(unparse(parse(src))) ` Precedence (low → high binding) Matches IntEnum order in unparser.py: | Level | Operators / forms | | --- | --- | | TEST | ternary ? : (weakest) | | OR | or | | AND | and | | BITOR | \| | | BITXOR | ^ | | BITAND | & | | EQ | == != | | INEQ / CMP | = | | SHIFT | > | | EXPR | reserved slot (unused by current op tables) | | ARITH | + - | | TERM | / % | | FACTOR / NOT | unary + - not ~ | | ATOM | names, literals, calls, subscripts | Precedence.next() steps one level tighter for right-hand associativity control. Bitwise tokens are emitted from visitBinOp / visitUnaryOp maps (BitAnd/BitOr/BitXor/LShift/RShift/Invert), not standalone visitBitAnd methods. Buffer helpers (internal but useful when subclassing) | Method | Role | | --- | --- | | write(text) | Append fragments | | fill(text="") | Newline + indent + text | | block() | Indent context manager | | delimit(start, end) | Wrap sub-output | | requireparens(prec, node) | Conditional parentheses | | itemsview / interleave | Comma-separated lists | | buffered() | Capture substring generation | Node coverage (high level) | Node | Emission sketch | | --- | --- | | Script | annotations then body | | FunctionDef | optional export/method, optional returns type spec, name(args) => body (inline single Expr or indented block) | | TypeDef | type Name with fields then methods | | EnumDef | enum Name body | | Assign | annotations, export?, var/varip?, type?, target = value | | ReAssign | target := value | | AugAssign | target op= value | | ForTo / ForIn / While / If / Switch | Pine control syntax; else if chain collapse | | Import | import ns/name/version as alias? | | BinOp / BoolOp / UnaryOp / Compare / Conditional | precedence-aware, including bitwise | ^ & > ~ | | Call / Attribute / Subscript / Name / Constant / Tuple | primaries | Internals Path src/pynescript/ast/unparser.py — Precedence, NodeUnparser, unparsenode Entry: helper.unparse → unparsenode → thread-local NodeUnparser.visit Visit vs traverse visit resets source, precedences, and indent, then calls traverse. traverse accepts lists (statement bodies) or single nodes, then dispatches via typevisitorcache (type-object keys, not class-name strings). This split lets statement lists and expression trees share code without double-buffering. visitFunctionDef emits returns (when set) as a leading type spec: export method int name(args) => …. If / else if chains visitIf detects orelse == [Expr(If(...))] and emits else if rather than nested else + if blocks — matching idiomatic Pine. Type body ordering visitTypeDef partitions body into field statements vs FunctionDef with method set, emitting fields first then methods. Constants String constants use JSON-style quoting helpers where needed; colors and numbers re-serialize from their Python values. Exact original quote style (single vs double) is not guaranteed. Invariants . Precedence tables must stay aligned with the parser’s expression layers (including bitwise | ^ & > between and and comparisons). A mismatch causes either redundant parens (benign) or wrong binding after re-parse (severe). . Annotations are part of the round-trip surface when attached to Script/FunctionDef/TypeDef/Assign. . Unparser does not type-check. Ill-typed but well-shaped trees still print. . Indent unit is four spaces (" " self.indent). Worked examples Precedence `python from pynescript.ast.helper import parse, unparse Builder produces BinOp(Add, left=, right=BinOp(Mult, , )) or inverse depending on parse print(unparse(parse(" + ", mode="eval"))) Expect something that re-parses to the same value: e.g. " + " ` Function forms `python Single-expression body → f(x) => expr Multi-statement body → f(x) =>\n stmt\n stmt With return type → int f(x) => expr Method → method int f(self) => expr ` Reassignment vs declaration `pine // Assign with mode → var float x = . // ReAssign → x := . // AugAssign → x += ` Failure modes | Symptom | Cause | | --- | --- | | Missing visit output | Falls through generic_visit which only walks children — may emit empty string for leaf-like unhandled nodes | | Extra parentheses everywhere | Over-conservative precedence | | Re-parse differs in binding | Under-parenthesized output | | Lost comments | Only annotation strings are stored; plain // comments are dropped after parse | | Method printed as function | FunctionDef.method` flag not set by builder | See also Helper API Builder ASDL schema Visitor / transformer --- FILE: docs/pyne/core/visitor-transformer.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Visitor & Transformer" description: "NodeVisitor and NodeTransformer: read-only traversal vs in-place AST rewrite with caching dispatch." --- Visitor & Transformer Tree walks in PYNE follow the same dual pattern as CPython’s ast module: a visitor for analysis, a transformer for structural rewrite. Abstract NodeVisitor (src/pynescript/ast/visitor.py) — dispatch visit, default genericvisit walks children via iterfields. Method lookups are cached per visitor instance, keyed by type(node) (the type object, not the class-name string). NodeTransformer (src/pynescript/ast/transformer.py) — same dispatch, but genericvisit replaces list items and fields according to return values (None removes, list splices, AST replaces). Specialized visitors elsewhere: | Class | Path | Role | | --- | --- | --- | | PinescriptASTBuilder | builder.py | CST visitor (ANTLR), not NodeVisitor | | NodeUnparser | unparser.py | Source emission | | StatementCollector | collector.py | Yield statements for annotations | | Evaluator classes | evaluator/.py | Runtime interpretation | Conceptual model Interface surface NodeVisitor ``python from pynescript.ast.visitor import NodeVisitor from pynescript.ast.helper import parse class CallCounter(NodeVisitor): def init(self): super().init() self.count = def visitCall(self, node): self.count += self.genericvisit(node) tree = parse("plot(ta.sma(close, ))") c = CallCounter() c.visit(tree) assert c.count >= ` | Method | Contract | | --- | --- | | visit(node) | Resolve visit with cache; fallback genericvisit | | genericvisit(node) | Recurse into AST fields and lists of AST | Returns are whatever the override returns (analysis often returns None). NodeTransformer `python from pynescript.ast import node as ast from pynescript.ast.transformer import NodeTransformer from pynescript.ast.helper import parse, unparse class RenameClose(NodeTransformer): def visitName(self, node: ast.Name): if node.id == "close": return ast.Name(id="open", ctx=node.ctx) return node tree = parse("plot(close)") tree = RenameClose().visit(tree) assert "open" in unparse(tree) ` Return value semantics when transforming a list parent (e.g. body): | Return | Effect | | --- | --- | | None | Remove this element | | AST | Replace element | | non-AST iterable | Splice multiple nodes in place of one | | same node | Keep | For a single AST field, None deletes the attribute (delattr); otherwise setattr replaces. StatementCollector Generator-style visitor used only for annotation attachment: Yields FunctionDef, TypeDef, EnumDef, Assign, ReAssign, AugAssign, Import, Expr, Break, Continue. Descends into nested structures and into structure-valued assignments. Does not yield the structure nodes themselves as statements when they appear only as values — it yields their inner bodies. `python from pynescript.ast.collector import StatementCollector from pynescript.ast.helper import parse stmts = list(StatementCollector().visit(parse("f() => \nx = "))) ` Internals Paths | File | Symbols | | --- | --- | | src/pynescript/ast/visitor.py | NodeVisitor | | src/pynescript/ast/transformer.py | NodeTransformer | | src/pynescript/ast/collector.py | StatementCollector, Structure tuple | | src/pynescript/ast/helper.py | iterfields, iterchildnodes, walk | Dispatch cache `text cls = type(node) visitor = cache.get(cls) or getattr(self, "visit" + cls.name, genericvisit) cache[cls] = visitor ` NodeUnparser keeps a second type-keyed cache (typevisitorcache) used by traverse, bypassing NodeVisitor.visit so it does not reset the source buffer mid-render. Subclass instances that dynamically add methods after first visit of a type will not see them unless the cache is cleared — prefer defining methods on the class body. In-place lists Transformers mutate list fields with oldvalue[:] = newvalues rather than rebinding, so aliased references to the same list see updates. Invariants . Always call super().init() on subclasses so visitorcache exists. . Transformers should return the node from overrides that use genericvisit (the default returns node after rewriting children). . Do not assume identity stability after a transformer pass if you replaced roots. . walk is not a visitor — it is a pure BFS helper without dispatch. Worked examples Strip all Expr statements that call plot `python from pynescript.ast import node as ast from pynescript.ast.transformer import NodeTransformer from pynescript.ast.helper import parse, unparse class DropPlots(NodeTransformer): def visitExpr(self, node): if isinstance(node.value, ast.Call): func = node.value.func if isinstance(func, ast.Name) and func.id == "plot": return None return self.genericvisit(node) tree = DropPlots().visit(parse("x = \nplot(x)\n")) print(unparse(tree)) ` Collect all string constants `python from pynescript.ast.visitor import NodeVisitor from pynescript.ast.helper import parse class Strings(NodeVisitor): def init(self): super().init() self.found = [] def visitConstant(self, node): if isinstance(node.value, str): self.found.append(node.value) s = Strings() s.visit(parse('indicator("hello")')) ` Failure modes | Failure | Cause | | --- | --- | | AttributeError: visitorcache | Forgot super().init() | | Transformer silently no-ops | Override returns nothing (None) unintentionally → node deleted | | Infinite recursion | visitX calls self.visit(node) on same node without change | | Partial rewrite | Overrode visit without genericvisit — children untouched | | Collector misses nested assign | Structure not in Structure` tuple and not assigned as value | See also Helper API Unparser Builder ASDL schema --- FILE: docs/pyne/devops/ci.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Continuous integration" description: "GitHub Actions for lint, Python .–. tests, package build, Docker api+cli smoke, and VS Code extension." --- Continuous integration Abstract CI is the public invariant check for the pyne repo after AXIS extraction. Workflows live under .github/workflows/. AXIS PWA/ee CI lives in the sister axis repo — not here. | Workflow file | name: | Role | | --- | --- | --- | | ci.yml | CI | Lint, test matrix, package, Docker api+cli, VSIX | | release.yml | Build & Release | Tag v + dispatch: wheels, CLI/LSP Nuitka binaries, CLI image tarball, VSIX, GitHub Release | | publish.yml | Publish | Tag v + dispatch: PyPI hoox-pyne (token or OIDC) | | ghcr.yml | GHCR | Tag v + dispatch: ghcr.io/hoox-sh/pyne/{api,cli,lsp} multi-arch | | docs-versions.yml | Docs versions | Every minutes: refresh PyPI / npm / Open VSX / GitHub release stamps. Commits only on change. No docs pageload. | Conceptual model Interface surface Triggers | Workflow | On | Purpose | | --- | --- | --- | | CI | push/PR to main | Required confidence | | Build & Release | tags v + dispatch | Binaries + VSIX + GitHub Release | | Publish | tags v + dispatch (dryrun default true) | PyPI hoox-pyne | | GHCR | tags v + dispatch | Public images api / cli / lsp | ci.yml jobs (current) | Job | Runtime | What it asserts | | --- | --- | --- | | lint | Python . | Ruff E/F/W gate on src/ tests/ (+ auth middleware); mypy non-blocking | | test | Matrix .–. | .[lsp,pro,compile] + backend deps; core (testlinter / testevaluator / testcli); runtime (parity, strategy, series, incremental TA, package Runtime); LSP (testlangserver + testlspfeatures); backend; Codecov on . | | package | . | python -m build + twine; wheel smoke (pynescript --help); artifact | | docker | buildx | Build api + cli targets; API health + admin fail-closed; CLI --help / info / check (--network=none) | | vscode-ext-test | Node | npm ci → compile → vsce package → VSIX artifact | Concurrency: group: ci-${{ github.ref }}, cancel-in-progress: true. Default permissions: contents: read. Not in this repo’s CI AXIS PWA / Playwright (axis repo — no axis-nightly.yml here) GHCR push (separate ghcr.yml, tag-triggered) Cloudflare Worker deploy Full tests/ corpus parametrized suite (run locally: make test) Internals | Path | Role | | --- | --- | | .github/workflows/ci.yml | PR/main fan-out (name: CI) | | .github/workflows/release.yml | Multi-artifact tag release (name: Build & Release) | | .github/workflows/publish.yml | PyPI (token preferred) (name: Publish) | | .github/workflows/ghcr.yml | Multi-arch image push (name: GHCR) | | tests/testcli.py | CLI unit suite (CI-critical) | Secrets | Secret | Used by | Notes | | --- | --- | --- | | PYPIAPITOKEN | publish.yml | Personal PyPI account (jango-blockchained) | | METADATAKEY / CRYPTOKEY | release.yml LSP encrypt | Fernet metadata | | VSCEPAT | optional Marketplace | VSIX still attaches without it | | GITHUBTOKEN | release upload | default | Invariants . Fail-fast is off on the Python matrix. . Codecov only on .. . CLI Docker image is non-root — smoke mounts must be world-readable (chmod on mktemp). . Corpus parametrized tests are intentionally skipped in CI for time; local make test is broader. . Package smoke installs the wheel and runs pynescript --help. Worked examples ``bash Lint as CI (correctness gate) ruff check src/ tests/ backend/middleware/auth.py --select E,F,W --ignore E,E,F,W,E Core unit slice pip install -e ".[lsp,pro,compile]" python -m pytest tests/testlinter.py tests/testevaluator.py tests/testcli.py -q Docker CLI smoke docker buildx bake cli docker run --rm --network=none pynescript-cli:latest info ` Failure modes | Symptom | Cause | Fix | | --- | --- | --- | | Docker CLI PermissionError on smoke.pine | mktemp + non-root user | CI chmods / (already fixed) | | Wheel missing pynescript | wrong package name | install hoox-pyne` / built wheel | | Ruff F on main | incomplete imports | fix before push; gate is E,F,W | See also Release PyPI publish Docker --- FILE: docs/pyne/devops/docker.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Docker" description: "Buildx multi-target images, Compose profiles, and Cloud Run production contract for the PYNE Pro API." --- Docker Abstract Containers package the Pro API (Flask + evaluator), the CLI (pynescript), and the LSP for reproducible local runs and Cloud Run deploys. A single multi-stage Dockerfile exposes named targets; Docker Buildx Bake (docker-bake.hcl) drives local loads and multi-platform release builds. Compose wires volume mounts and optional Redis/LSP/CLI profiles for desk development, with a production overlay for gunicorn. Conceptual model Interface surface Dockerfile targets | Target | Process model | Audience | | --- | --- | --- | | api | entrypoint-api.sh → gunicorn (...:$PORT) | Production / Cloud Run / compose prod | | api-dev | python -m backend.app with HOST=... | Local compose (source mounts) | | lsp | ENTRYPOINT pyne-lsp (stdio) | Compose profile lsp; GHCR ghcr.io/hoox-sh/pyne/lsp | | cli | entrypoint-cli.sh → pyne (fallback pynescript; ephemeral; /work CWD) | CI gates, one-shot parse/lint/compile/run | All targets: Python .-slim, non-root appuser (uid ). API targets also set APIKEYSTORE=/data/apikeys.json, MPLBACKEND=Agg, healthcheck via curl. The cli image installs .[compile,data] only (no Flask/matplotlib) and is smaller/faster to build than api. Buildx Bake (docker-bake.hcl) ``bash Local load (host platform) docker buildx bake api production image → pynescript-api:latest docker buildx bake cli CLI image → pynescript-cli:latest (ENTRYPOINT: pyne) docker buildx bake lsp LSP image → pynescript-lsp:latest (ENTRYPOINT: pyne-lsp) docker buildx bake default group: api + api-dev docker buildx bake all api + api-dev + lsp + cli Multi-platform (amd + arm). Set REGISTRY to push: REGISTRY=ghcr.io/hoox-sh/pyne TAG=.. docker buildx bake release release group = api-release + cli-release + lsp-release → ghcr.io/hoox-sh/pyne/{api,cli,lsp}:.. Prefer: make docker-push-ghcr (workflow GHCR on v tags) ` Make wrappers: `bash make docker-build bake api make docker-build-cli bake cli make docker-build-lsp bake lsp make docker-build-all bake all targets make docker-buildx bake release (multi-platform) make docker-push-ghcr gh workflow run ghcr.yml make docker-cli ARGS="check script.pine" compose profile cli ` One-time multi-platform builder (if needed): `bash docker buildx create --use --name pynescript ` Compose `bash cp .env.example .env optional overrides make docker-up api-dev on host : make docker-up-full + redis profile (api + redis) make docker-prod requires ADMINTOKEN; gunicorn, no source mounts make docker-smoke curl http://...:/ make docker-down stdio LSP (not a long-running compose service) docker compose --profile lsp run --rm lsp CLI (ephemeral; mounts repo at /work) docker compose --profile cli run --rm cli check script.pine ≡ make docker-cli ARGS="check script.pine" ` | Service | Profile | Notes | | --- | --- | --- | | api | default | Port ${APIPORT:-}:, ro mounts of src/ + backend/, volume apidata → /data | | redis | redis | redis:-alpine, AOF, ${REDISPORT:-} | | lsp | lsp | stdio; use docker compose run --rm lsp (not detached up) | | cli | cli | entrypoint-cli.sh → pyne; mounts .:/work + src + clidata | Network name: pynescript-dev. Production overlay (docker-compose.prod.yml): target api, volumes: !override so only apidata:/data remains (dev source binds are dropped), SQLite key store on /data, mem/cpu caps, requires ADMINTOKEN. Environment variables | Variable | Default | Meaning | | --- | --- | --- | | PORT | | Listen port inside the container | | HOST | ... | Bind address (dev Flask runner) | | APIPORT | | Host port published to container | | FLASKENV | production / development | App mode by target | | STOREBACKEND | json (prod overlay: sqlite) | json \| sqlite \| redis | | APIKEYSTORE | /data/apikeys.json | JSON key store path (STOREBACKEND=json) | | APIKEYSTORESQLITE | /data/apikeys.db | SQLite path (STOREBACKEND=sqlite) | | ALLOWEDORIGINS | compose default includes https://hoox.sh, https://hoox.sh/axis, and local AXIS : | CORS allow-list. VPS AXIS is : — do not copy into VPS env | | ADMINTOKEN | unset | Required for POST /auth/createkey (X-Admin-Token) and prod compose | | REDISURL | empty in prod; compose dev default redis://redis:/ | Required when STOREBACKEND=redis | | GUNICORNWORKERS / GUNICORNTHREADS / GUNICORNTIMEOUT | / / (entrypoint default; prod compose overlay defaults to ) | Prod entrypoint knobs | | GUNICORNBIND | ...:${PORT} | Optional gunicorn bind override | Published image names GitHub Container Registry (workflow GHCR, tags v): ` ghcr.io/hoox-sh/pyne/api:.. ghcr.io/hoox-sh/pyne/cli:.. ghcr.io/hoox-sh/pyne/lsp:.. ` Cloud Build (cloudbuild.yaml, --target api only): ` gcr.io/$PROJECTID/pynescript/pynescript-pro-api:$COMMITSHA gcr.io/$PROJECTID/pynescript/pynescript-pro-api:latest ` Substitution PYNESCRIPTVERSION in that YAML currently defaults to ".." — override at submit time to match about.py (..). Internals Build stages . base-os / base — slim image, non-root appuser; API/LSP base also installs curl + freetype/png . builder — build-essential, pip cache mounts, install backend/requirements.txt then pip install ".[lsp]" into /install . lsp-builder — pip install ".[lsp]" only (no Flask / matplotlib) . cli-builder — pip install ".[compile,data]" only (no Flask stack) . runtime / api / api-dev / lsp / cli — final CMDs / ENTRYPOINTs and healthchecks (lsp has none — stdio) .dockerignore Keeps context small by excluding tests/ (corpus), docs/, brand/, node modules, compiler .nbc/.nbi caches, venvs, and IDE cruft. Compose healthchecks API: curl -fsS http://...:/. Redis: redis-cli ping. Invariants & edge cases . Host port vs container . Local compose publishes API on :. Cloud Run uses :. VPS deploy (scripts/deployvps.sh) health-checks API : and AXIS :. Local AXIS PWA is : — do not treat as the VPS PWA port. . Read-only mounts in compose mean dependency changes need image rebuild; source edits are visible via PYTHONPATH precedence. . Production target is immutable — no live mounts; use the prod overlay or Cloud Run. . Non-root appuser cannot write arbitrary paths; keep APIKEYSTORE under /data. . Cloud Build always builds --target api (gunicorn). Do not point it at api-dev. . LSP is stdio — not an HTTP service; use docker compose run --rm lsp rather than expecting a published port. . CLI is ephemeral — entrypoint-cli.sh execs pyne (fallback pynescript); pass subcommands after the image name. Workdir defaults to /work (compose mounts the repo there). . Prod overlay is fail-closed — ADMINTOKEN is required (:? interpolation); volumes: !override drops the dev source binds so only apidata:/data remains. Worked examples Production-like local API (bake) `bash docker buildx bake api docker run --rm -p : \ -e FLASKENV=production \ -e ADMINTOKEN=change-me \ -e ALLOWEDORIGINS=https://hoox.sh,https://hoox.sh/axis \ pynescript-api:latest curl -s http://...:/ | jq . ` Compose with Redis `bash docker compose --profile redis up --build ` Optional LSP container `bash docker compose --profile lsp run --rm lsp ` CLI image `bash docker buildx bake cli docker run --rm pynescript-cli:latest --help docker run --rm -v "$PWD:/work" -w /work pynescript-cli:latest check script.pine docker run --rm -v "$PWD:/work" -w /work pynescript-cli:latest lint script.pine --json or via compose: make docker-cli ARGS="run script.pine --bars " ` Production overlay `bash export ADMINTOKEN=change-me docker compose -f docker-compose.yml -f docker-compose.prod.yml up --build -d ` Failure modes | Symptom | Cause | Fix | | --- | --- | --- | | Healthcheck fails | process not listening / curl missing | Use image targets from this Dockerfile; check docker compose logs api | | Connection refused on host port | Flask bound to ... | Dev target sets HOST=...; do not override to localhost | | Permission denied writing keys | Running as appuser outside /data | Set APIKEYSTORE=/data/... and mount apidata | | Import errors after mount | PYTHONPATH / stale site-packages | PYTHONPATH=/app/src:/app; rebuild after dependency changes | | OOM under load | Small memory cap | Raise APIMEMLIMIT / Cloud Run memory; reduce concurrency | | CORS failures from PWA | Origin not allowed | Set ALLOWEDORIGINS | | Huge build context | Missing .dockerignore | Ensure .dockerignore` is present (corpus/docs excluded) | See also GCP Security Pro API lifecycle Local development --- FILE: docs/pyne/devops/gcp.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "GCP & Cloud Run" description: "Cloud Build pipeline, Cloud Run Pro API sizing, cost model, and substitution secrets for PYNE deployments." --- GCP & Cloud Run Abstract Production-shaped hosting for the Pro API targets Google Cloud Run, fed by Cloud Build (cloudbuild.yaml). The pipeline builds a container, pushes multi-tags to Container Registry, deploys a managed service, and can optionally compile the LSP binary in the same build graph. This page codifies the as-checked-in config and the cost envelope from docs/gcpcostestimate.md — treat dollar figures as planning estimates, not invoices. Conceptual model Interface surface Substitutions (cloudbuild.yaml) | Substitution | Default | Meaning | | --- | --- | --- | | SERVICENAME | pynescript-pro-api | Cloud Run service | | REGION | us-central | Deploy region | | REPOSITORY | pynescript | Image path segment | | METADATAKEY | (secret/subst) | Fernet key → CRYPTOKEY for LSP stage | | PYNESCRIPTVERSION | ".." (stale default in YAML) | OCI label; override to .. at submit | Build steps . build — docker build with BuildKit; tags $COMMITSHA + latest; cache-from latest . push — docker push --all-tags . deploy — gcloud run deploy with managed platform flags . build-lsp — python:.-slim image runs scripts/build/cibuild.py --jobs= with CRYPTOKEY=${METADATAKEY} Cloud Run flags (as configured) | Flag | Value | Rationale | | --- | --- | --- | | --allow-unauthenticated | on | Public HTTP API (auth via API keys at app layer) | | --min-instances | | Scale to zero | | --max-instances | | Cap spend / blast radius | | --memory | Mi | Evaluator headroom | | --cpu | | Single vCPU per instance | | --concurrency | | Parallel requests per instance | | --timeout | s | Platform kill. Entrypoint gunicorn default is s (GUNICORNTIMEOUT); set the env to on Cloud Run or long evals die at the platform first. | | --set-env-vars | FLASKENV=production | App mode | Machine / budget knobs ``yaml options: machineType: EHIGHCPU logging: CLOUDLOGGINGONLY timeout: s ` Internals | Path | Role | | --- | --- | | cloudbuild.yaml | Pipeline definition | | Dockerfile target api | Production image recipe used by Cloud Build | | backend/app.py | Health /, CORS, body size limit | | docs/gcpcostestimate.md | Scenario costs (April draft) | Cost envelope (planning) | Scale | Rough monthly | Notes | | --- | --- | --- | | Free / hobby | ~$ | Within Cloud Run free tier if tiny | | MVP (~ pro users) | ~$ | Cloud Run + micro SQL | | Growth (~) | ~$ | Add Redis caching | | Scale (~) | ~$ | Dominated by evaluator CPU-sec | Dominant cost driver: script execution CPU time (~– CPU-sec per typical backtest over k bars). Memory ~–MB per concurrent Python worker. Free-tier leverage (GCP) Cloud Run: M requests, K CPU-sec, K GB-sec / month Cloud Build: build-minutes / day Artifact Registry / GCS small free slices Invariants & edge cases . App-layer auth still required when Cloud Run allows unauthenticated invoke — see Security. . Scale-to-zero cold starts add latency; min-instances= trades money for snappiness. . LSP build step does not deploy the binary to Cloud Run; it is a release-adjacent compile in the same YAML. . Mi may be tight for large OHLCV payloads — watch OOM kills; raise memory before raising concurrency. . Cost estimate doc also lists Railway / Render / Fly / self-host alternatives if GCP is overkill. Worked examples Manual deploy sketch (operator) `bash gcloud builds submit --config cloudbuild.yaml \ --substitutions=METADATAKEY="$METADATAKEY" ` Smoke health after deploy `bash curl -s "https://SERVICE-URL/" expect JSON status healthy, service pynescript-pro-api `` Failure modes | Symptom | Cause | Fix | | --- | --- | --- | | Deploy timeout | Nuitka step + docker on min budget | Split LSP compile to release pipeline | | / queue | max-instances or concurrency | Raise caps carefully; add queue | | High bill | No cache; chatty clients | Redis TTL; Cloud Tasks for backtests | | Auth bypass illusion | Unauthenticated Cloud Run | Enforce API keys in Flask | | Image pull errors | Wrong project / registry perms | IAM on GCR + Cloud Run service agent | See also Docker Observability Security Pro API --- FILE: docs/pyne/devops/index.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "DevOps" description: "CI matrices, containers, Nuitka LSP binaries, metadata crypto, Cloud Run, and operational invariants for PYNE." --- DevOps Abstract PYNE is a multi-surface product: pure-Python library + Click CLI (pyne / alias pynescript), pygls Language Server (pyne-lsp), Flask Pro API, VS Code extension (hoox-sh.pyne ..), and Docker targets (api / cli / lsp). Edge workers and the AXIS charting PWA are sister repos — not built in this tree. This tab documents the operational graph: local loops, GitHub Actions, release tags, Docker images, Nuitka CLI/LSP binaries, Fernet-encrypted LSP metadata, GCP Cloud Build/Run, observability hooks, and security controls. Conceptual model Invariant: CI green on main is necessary but not sufficient for a full release. Tag v fires Build & Release (binaries + VSIX + GitHub Release), Publish (hoox-pyne .. to PyPI), and GHCR (ghcr.io/hoox-sh/pyne/{api,cli,lsp}). Cloud Build deploys the API image separately. Interface surface | Concern | Entry | Doc | | --- | --- | --- | | Local install & loops | Makefile, hatch envs | Local development | | PR / push CI | .github/workflows/ci.yml (name: CI) | CI | | Versioned CLI + LSP + VSIX | .github/workflows/release.yml (name: Build & Release) | Release | | PyPI (hoox-pyne) | .github/workflows/publish.yml (name: Publish) | PyPI publish | | GHCR images | .github/workflows/ghcr.yml (name: GHCR) | Docker | | First ship checklist | personal PyPI + API token | Publish checklist | | Containers | Dockerfile targets api/api-dev/lsp/cli | Docker | | Compiled CLI / LSP | scripts/build/compile.py --target | Nuitka build | | Builtin metadata crypto | Fernet key + .enc | Metadata crypto | | Cloud Run | cloudbuild.yaml | GCP | | Logs / health | Flask /, gunicorn, Cloud Logging | Observability | | Auth, CORS, secrets | backend/middleware/auth.py | Security | Internals (repo map) | Path | Role | | --- | --- | | Makefile | Human-facing orthography: install, test, lint, build, docker, worker | | pyproject.toml | Hatch envs (test, lint, docs), optional extras (lsp, data) | | .github/workflows/ | ci.yml, release.yml, publish.yml, ghcr.yml | | scripts/build/ | Nuitka compile + CI build + Fernet metadata stage | | scripts/generatebuiltinmetadata.py | Regenerates LSP builtinmetadata.json from live builtins | | Dockerfile / docker-bake.hcl | Multi-target Buildx images (api, api-dev, lsp) | | docker-compose.yml | Local API (+ optional Redis / LSP profiles) | | cloudbuild.yaml | Build → GCR push → Cloud Run deploy; optional LSP compile | | vscode-extension/ | Node extension package / vsce | Invariants & edge cases . Two console scripts, two entrypoints. Preferred: pyne (Click) ≠ pyne-lsp (pygls). Aliases: pynescript / pynescript-lsp. Ops scripts must not conflate them. . Generated artifacts are not hand-edited. ANTLR/ASDL under generated/ and builtinmetadata.json are code-derived. . Fernet key is gitignored. Without a stable CRYPTOKEY / METADATAKEY secret, every CI encrypt produces a different .enc blob (harmless functionally, bad for reproducibility). . Python matrix vs Nuitka pin. CI tests .–.; Nuitka release currently pins . (release.yml PYTHONVERSION, nuitka>=..,<.). . AXIS CI is not in this repo. Playwright / PWA security gates live in hoox-sh/axis. There is no axis-nightly.yml here. Worked examples ``bash Fast local confidence loop make install make lint make test-lsp make build-check import check only, ~s, no Nuitka compile API in Docker — local dev stack (api-dev + source mounts) make docker-up make docker-smoke Production image bake (load local) + optional prod compose overlay make docker-build export ADMINTOKEN=… && make docker-prod ` Failure modes | Symptom | Likely cause | Fix | | --- | --- | --- | | CI lint red, local green | Different ruff/mypy versions | Match CI install pins in workflow | | Metadata decrypt fails in binary | Missing key at runtime | Set PYNESCRIPTMETADATAKEY or embed key at build | | Cloud Run after deploy | Image missing deps / gunicorn bind | Confirm Dockerfile target api ENTRYPOINT + PORT / GUNICORNBIND | | VSIX empty / missing | npm ci / vsce not run | Use make build-vscode or release job artifacts | | Nuitka Anaconda link error | Static libpython missing | conda install libpython-static or keep --static-libpython=no` | See also Contributing LSP architecture Pro API contract pyne-worker pyne-agent-worker --- FILE: docs/pyne/devops/local-dev.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Local development" description: "Install, test, lint, package, and run PYNE — Make targets after AXIS extraction." --- Local development Abstract A PYNE checkout is a Python-first workspace: src-layout package + Flask Pro API, optional VS Code extension (Node), and the pynets/ submodule (Bun/TS library). The AXIS charting PWA is a sister repo (axis) — do not recreate frontend/ here. Prefer the standalone hoox-sh/pynets checkout for TS work. Conceptual model Interface surface Make targets (primary) | Target | Effect | | --- | --- | | make install | pip install -e ".[lsp,pro]" | | make test | full pytest tests/ | | make test-cli | tests/testcli.py | | make test-lsp | langserver + lspfeatures | | make test-backend | tests/testbackend.py | | make lint / make fmt | Ruff on src/, tests/, backend/ | | make package | sdist + wheel (python -m build) | | make build | Nuitka LSP binary | | make build-cli | Nuitka CLI binary | | make build-check | import check for lsp+cli (~s) | | make build-vscode | compile + package VSIX | | make run | Flask Pro API (: typical) | | make run-lsp | python -m pynescript.langserver | | make docker-build | bake api | | make docker-build-cli | bake cli | | make docker-build-lsp | bake lsp (pynescript-lsp:latest; GHCR name ghcr.io/hoox-sh/pyne/lsp) | | make docker-build-all | api + api-dev + lsp + cli | | make docker-buildx | multi-platform release bake (api + cli + lsp) | | make docker-push-ghcr | gh workflow run ghcr.yml | | make docker-cli ARGS="…" | compose profile cli (ephemeral) | | make deploy-vps | rsync + restart API; health API :, AXIS : (VPS; local AXIS is :) | | make docker-up | compose API on : | | make docker-up-full | + redis profile | | make docker-prod | gunicorn overlay (ADMINTOKEN required) | | make docker-smoke | curl health on : | | make docker-down / docker-logs | stop / logs | Hatch ``bash hatch run test:test hatch run test:test-cov hatch run lint:style hatch run lint:typing hatch run lint:gen-parser ` Quick desk loop `bash python -m venv .venv && source .venv/bin/activate pip install -e ".[lsp,compile,pro]" pyne info pyne check examples/rsistrategy.pine make test-cli make run Pro API ` From PyPI (no checkout) `bash pip install "hoox-pyne[lsp,compile]" pyne --version .. ` Internals | Path | Why it matters | | --- | --- | | src/pynescript/ | Editable package root | | tests/conftest.py | Optional --example-scripts-dir only; no third-party corpus shipped | | backend/requirements.txt | Pro API deps (also covered by [pro] extra) | | pynets/ | TS library submodule; prefer standalone hoox-sh/pynets | | vscode-extension/ | PYNE VS Code extension | | Dockerfile | targets api, api-dev, lsp, cli | Python Requires-Python: >=. CI matrix: .–. Hatch test matrix: .–. (no . cell) Recommended: .+ with venv Optional extras | Extra | Adds | | --- | --- | | lsp | pygls / lsprotocol → pyne-lsp (alias pynescript-lsp) | | compile | numpy / numba | | data / datafeed | ccxt | | pro | Flask + gunicorn + matplotlib + redis + compile | Invariants . No frontend/ in this repo — AXIS is external. . from future import annotations required on new Python modules. . Grammar edits only under resource/; regenerate via hatch, don’t hand-edit generated parser. . Corpus fixture runs hundreds of cases — prefer focused unit tests for CI paths. . CLI Docker runs as uid ; bind-mounts must be readable by others. Failure modes | Symptom | Fix | | --- | --- | | No module named pynescript | activate venv; pip install -e . or hoox-pyne | | Pro API ImportError flask | pip install "hoox-pyne[pro]" or backend requirements | | Nuitka missing | pip install nuitka; make build-check` first | | Docker CLI can’t read file | chmod host path; don’t use -only dirs | See also Installation CI Docker Nuitka build --- FILE: docs/pyne/devops/metadata-crypto.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Metadata encryption" description: "Fernet-encrypted LSP builtinmetadata.json — key lifecycle, CI secrets, integrity hashes, and runtime decrypt." --- Metadata encryption Abstract LSP completion, hover, and signature help depend on a large builtin metadata document derived from the live evaluator surface. In open development the JSON is plaintext and git-tracked. For compiled Nuitka binaries, the build pipeline can ship an encrypted blob so the binary does not leave a trivially scrapable metadata file on disk. Mechanism: Fernet (symmetric, cryptography package) + optional SHA integrity sidecar. Conceptual model Interface surface Files | Path | Tracked? | Role | | --- | --- | --- | | src/pynescript/langserver/providers/builtinmetadata.json | yes | Plaintext for dev / hatch | | …/builtinmetadata.json.enc | often yes | Encrypted payload for binaries | | …/builtinmetadata.json.sha | yes | Truncated SHA of plaintext | | scripts/build/.metadata.key | no (gitignored) | Local Fernet key material | | scripts/generatebuiltinmetadata.py | yes | Regenerates JSON from code | Environment variables | Variable | Context | Meaning | | --- | --- | --- | | CRYPTOKEY | Build scripts / CI / Cloud Build | Key bytes for encrypt stage | | PYNESCRIPTMETADATAKEY | Runtime decrypt fallback | Env-supplied key if file missing | | GitHub secrets.METADATAKEY | Actions | Stable secret → CRYPTOKEY | | Cloud Build METADATAKEY | cloudbuild.yaml substitution | CRYPTOKEY=${METADATAKEY} | Code entrypoints Encrypt stage: scripts/build/compile.py (encryptmetadata / resolvefernetkey), scripts/build/cibuild.py (stagemetadata) Decrypt: src/pynescript/langserver/providers/metadatadecrypt.py Loader preference: plaintext if present, else encrypted (getmetadata returns {} on miss; getmetadatacached raises) Internals Generation (always code-derived) ``bash python scripts/generatebuiltinmetadata.py ` Do not hand-edit the JSON for permanent feature work — re-run the generator after adding builtins. Encrypt (local sketch) `python from cryptography.fernet import Fernet key = Fernet.generatekey() or load stable secret fernet = Fernet(key) encrypted = fernet.encrypt(plaintextbytes) ` Build scripts chmod the key file to o when writing .metadata.key. Decrypt integrity check After Fernet decrypt, if a .sha sidecar exists, the loader compares sha(plaintext)[:] and raises on mismatch. Dev vs binary | Mode | Behavior | | --- | --- | | Editable install / repo checkout | Prefer plaintext JSON | | Nuitka onefile | Encrypted data dir + key resolution via MEIPASS / env | | Missing both | FileNotFoundError directing to compile script | Invariants & edge cases . Stable key ⇒ reproducible .enc. Random key every CI run yields a different ciphertext even if plaintext is identical — not a functional bug, but pollutes diffs and caches. . Plaintext remains in git for open-source transparency of the language surface. Encryption protects the bundled binary layout, not the secret of the API catalog. . Truncated hash is integrity, not secrecy. It detects bitrot / wrong plaintext, not attackers with the key. . Never commit .metadata.key. Rotate if leaked; regenerate .enc with the new key for binary builds. . After adding builtins: regenerate metadata, re-encrypt with the same CI key, commit updated JSON (and .enc if tracked). Worked examples Regenerate + local encrypt `bash python scripts/generatebuiltinmetadata.py pip install cryptography python scripts/build/compile.py --check stages metadata paths as configured ` CI snippet `yaml name: Build LSP binary run: python scripts/build/cibuild.py --jobs env: CRYPTOKEY: ${{ secrets.METADATAKEY }} ` Runtime with env key `bash export PYNESCRIPTMETADATAKEY="$(cat scripts/build/.metadata.key)" pynescript-lsp ` Failure modes | Symptom | Cause | Fix | | --- | --- | --- | | No metadata decryption key found | Missing file + env | Supply PYNESCRIPTMETADATAKEY or rebuild with key | | Metadata integrity check failed | Stale hash or wrong ciphertext | Re-encrypt from current plaintext; commit both | | Completion empty in binary only | Data dir not included in Nuitka | Fix --include-data-dir | | Divergent .enc every CI | Unset CRYPTOKEY | Configure METADATAKEY` secret | See also Nuitka build LSP builtin metadata Release Security --- FILE: docs/pyne/devops/nuitka-build.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Nuitka build (LSP + CLI)" description: "Compile pyne-lsp and pyne CLI with Nuitka — onefile vs standalone, CI build script, Anaconda quirks, and artifact layout." --- Nuitka build (LSP + CLI) Abstract Both the Language Server and the Click CLI can run as pure Python or as Nuitka-compiled onefile binaries for offline distribution. Compilation freezes the entry module and followed imports into artifacts under dist/. | Binary | Entry | Typical use | | --- | --- | --- | | pynescript-lsp (PATH name; product CLI is pyne-lsp) | langserver/main.py | Editors / VSIX | | pynescript (PATH name; product CLI is pyne) | pynescript/main.py | Shell / CI gates | Primary tools: scripts/build/compile.py (local) and scripts/build/cibuild.py (CI/Cloud Build). Both accept --target lsp|cli|all. Conceptual model Interface surface Commands ``bash Prerequisites pip install nuitka cryptography Full LSP onefile build (default path via Make) make build ≡ python scripts/build/compile.py --target lsp --jobs= CLI onefile binary make build-cli ≡ python scripts/build/compile.py --target cli --jobs= Fast import check only (~s) — both targets make build-check ≡ python scripts/build/compile.py --target all --check Explicit modes python scripts/build/compile.py --target cli --standalone python scripts/build/compile.py --target all --jobs python scripts/build/cibuild.py --target cli --jobs= --skip-metadata --skip-vsix ` Build modes (approx. wall time) | Mode | Time | Notes | | --- | --- | --- | | --check | ~s | Import resolution / no full compile | | --standalone | – min | Directory distribution, faster iteration | | --onefile | – min | Self-extracting single binary | | CI cores | – min | High parallelism | Expected layout `text dist/ ├── lsp/ │ └── pynescript-lsp onefile (path may vary by script) ├── vsix/ │ └── pynescript-lsp.vsix optional bundle └── pynescript-lsp cibuild final move target (script-dependent) ` Also see scripts/build/README.md. Internals Key Nuitka flags (compile.py) --static-libpython=no — default; critical for many Linux/Anaconda envs --python-flag=nosite,nodocstrings --include-data-dir=…/providers=pynescript/langserver/providers --lto=auto, --jobs=N --onefile or --standalone --follow-imports on local compile path Entry module Compilation targets src/pynescript/langserver (package entry), product name pynescript-lsp. Metadata stage Before compile, scripts may: . Run scripts/generatebuiltinmetadata.py if plaintext metadata missing . Fernet-encrypt to builtinmetadata.json.enc . Write truncated SHA sidecar . Write scripts/build/.metadata.key (gitignored) or consume CRYPTOKEY env in CI Anaconda If static libpython is unavailable: `bash conda install libpython-static or rely on --static-libpython=no (already the default in compile.py) ` Invariants & edge cases . C compiler required (gcc/clang/MSVC). Containers need build-essential or equivalent if compiling inside Docker. . Python .+; release workflow pins . with nuitka>=..,<.. Local/Cloud Build sketches often use .. . Providers data dir must ship — completion/hover collapse without metadata. . Onefile startup cost includes extract-to-temp; standalone is snappier for local soak tests. . Do not commit scripts/build/.metadata.key. Worked examples Local onefile with explicit jobs `bash python scripts/build/compile.py --jobs "$(nproc)" find dist -name 'pynescript-lsp' -type f ` CI-shaped build `bash export CRYPTOKEY="$METADATAKEYSTABLE" python scripts/build/cibuild.py --jobs= ` Verify without compile `bash python scripts/build/compile.py --check ` Failure modes | Symptom | Cause | Fix | | --- | --- | --- | | Link error on libpython | Anaconda static lib missing | Install static lib or keep --static-libpython=no | | Binary missing builtins | Data dir not included | Confirm --include-data-dir` providers path | | Huge binary | Full stdlib pull-in | Review Nuitka plugins / unused deps | | CI timeout | Low cores / onefile | Raise jobs; use larger runner; prefer standalone intermediate | | Decrypt fail at runtime | Key not embedded / env unset | See Metadata crypto | See also Metadata crypto Release LSP builtin metadata --- FILE: docs/pyne/devops/observability.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Observability" description: "Health endpoints, gunicorn process model, Cloud Logging, coverage artifacts, and operational signals for PYNE." --- Observability Abstract PYNE’s production surface today optimizes for simple, hostable signals rather than a full OpenTelemetry mesh: HTTP health checks, process managers (gunicorn), cloud platform logs, CI artifacts (coverage, Playwright reports), and application-level API key usage counters. Treat this page as the map of what exists in-repo, not a claim of enterprise APM parity. Conceptual model Interface surface Health backend/app.py exposes: ``http GET / → { "status": "healthy", "service": "pynescript-pro-api", … } ` Used by: Dockerfile target api HEALTHCHECK (curl -fsS http://...:/) docker-compose.yml api healthcheck Load balancers / Cloud Run startup-ish probes (platform-dependent) Process model (production) `text gunicorn --bind : --workers --threads --timeout backend.app:app docker/entrypoint-api.sh: GUNICORNTIMEOUT default docker-compose.prod.yml: GUNICORNTIMEOUT default ` | Knob | Effect on signals | | --- | --- | | workers | Process isolation; multiplies memory | | threads | Concurrent requests within worker | | timeout | Entrypoint default s. Cloud Run --timeout=s is tighter unless you raise the platform flag or lower GUNICORNTIMEOUT. | Logs | Environment | Where logs go | | --- | --- | | Local make run | Process stdout/stderr | | Docker | Container logs (docker logs, compose) | | Cloud Build | logging: CLOUDLOGGINGONLY | | Cloud Run | Cloud Logging (request + container) | Application modules use standard library / Flask logging patterns; there is no mandatory structured-log schema enforced repo-wide. if a future branch introduces OpenTelemetry exporters. CI / quality signals | Signal | Source | | --- | --- | | Ruff / mypy | CI lint job | | Pytest results | runtime + LSP + backend steps in CI | | Codecov (optional) | Python . matrix cell | | VSIX | CI vscode-ext-test artifact | | Docker smoke | CI api health + admin ; CLI --help / info / check | Usage metering (app-layer) backend/middleware/auth.py tracks per-key: callsused / callslimit / tier lastused timestamps These are business metrics for rate limits, not Prometheus time series — export them if you need dashboards. Internals | Path | Role | | --- | --- | | backend/app.py | Health route, CORS, MAXCONTENTLENGTH | | backend/middleware/auth.py | Key store, rate limit counters | | backend/services/backtest.py | Backtest metrics objects for API responses | | Dockerfile (api target) | HEALTHCHECK + gunicorn entrypoint | | cloudbuild.yaml | Cloud Logging-only build logs | | logs/ (repo) | Local combined/error log files if generated by tools — not the production contract | Invariants & edge cases . Health ≠ readiness of evaluator warm caches. A on / does not mean the first /run will be fast (cold import / JIT / numba). . Timeouts double-bind. Cloud Run s vs gunicorn default s — the platform limit wins on Cloud Run unless you set GUNICORNTIMEOUT=. . Scale-to-zero hides idle metrics. No traffic ⇒ no samples; use synthetic uptime checks if SLOs matter. . Coverage artifacts expire (– days retention on Actions uploads). . Do not scrape secrets from logs. API keys and admin tokens must never be logged at info level. Worked examples Local health loop `bash make run curl -s http://...:/ | python -m json.tool ` Docker health `bash docker inspect --format='{{json .State.Health}}' CONTAINER ` Tail Cloud Run (gcloud) `bash gcloud run services logs read pynescript-pro-api --region us-central --limit ` Failure modes | Symptom | Cause | Fix | | --- | --- | --- | | Health green, /run | Exception in evaluator path | Inspect request logs; reproduce with payload | | Worker kills | Soft timeout | Optimize script / raise timeout consistently | | Missing CI artifact | Path wrong / tests passed | if-no-files-found: ignore` hides absence | | Rate limit false positives | Shared key / limit too low | Adjust tier limits; multi-key | | Silent deploy failure | Build logging-only + unread | Check Cloud Build history UI | See also GCP Docker Security API lifecycle --- FILE: docs/pyne/devops/publish-checklist.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Publish checklist" description: "Publish hoox-pyne under personal PyPI account jango-blockchained from hoox-sh/pyne Actions." --- Publish checklist Abstract Ship hoox-pyne from GitHub hoox-sh/pyne while owning the PyPI project under the personal account jango-blockchained. A PyPI organization is not required (and is not used while org approval is pending). . Identity map | Surface | Value | | --- | --- | | GitHub org / repo | hoox-sh/pyne (Actions host) | | PyPI account (owner) | jango-blockchained (personal) | | PyPI project | hoox-pyne | | Import / CLIs | import pynescript; CLIs pyne / pyne-lsp (aliases pynescript / pynescript-lsp) | | Package version | src/pynescript/about.py → .. | | VS Marketplace / Open VSX publisher | hoox-sh (hoox-sh.pyne ..) | . Confirm repo host (hoox-sh/pyne) The GitHub repo already lives at hoox-sh/pyne. If a checkout still points at the old personal remote, retarget it: ``bash git remote set-url origin https://github.com/hoox-sh/pyne.git git remote -v gh repo view --json nameWithOwner,url expect: hoox-sh/pyne ` Automation secrets | Item | Action | | --- | --- | | Actions enabled | Org/repo Settings → Actions allowed for this repo | | Environment pypi | Recreate if missing: gh api -X PUT repos/hoox-sh/pyne/environments/pypi | | Secrets | Re-set if missing: METADATAKEY, CRYPTOKEY (same Fernet material as scripts/build/.metadata.key) | | Optional | VSCEPAT for Marketplace publish on tag | | Workflow permissions | Read/write as needed for release.yml (contents: write on release job) | `bash gh secret set METADATAKEY -R hoox-sh/pyne < scripts/build/.metadata.key gh secret set CRYPTOKEY -R hoox-sh/pyne < scripts/build/.metadata.key optional marketplace: gh secret set VSCEPAT -R hoox-sh/pyne ` . PyPI under personal account jango-blockchained Preferred — API token . pypi.org as jango-blockchained → API tokens → add token. . gh secret set PYPIAPITOKEN -R hoox-sh/pyne . First upload creates project hoox-pyne owned by your user. Optional — Trusted Publishing Log in as jango-blockchained (personal), then pending publisher: | Field | Value | | --- | --- | | PyPI project name | hoox-pyne | | Owner | hoox-sh (GitHub repo owner — Actions host) | | Repository | pyne | | Workflow name | publish.yml | | Environment name | pypi | Leave PYPIAPITOKEN unset to force OIDC. Do not put your PyPI username in the GitHub Owner field. Details: PyPI publish. . Local package smoke (no upload) `bash version already .. in src/pynescript/about.py pip install build twine rm -rf dist/ python -m build twine check dist/ expect: hooxpyne-..-py-none-any.whl hooxpyne-...tar.gz python -m venv /tmp/pyne-smoke /tmp/pyne-smoke/bin/pip install dist/.whl /tmp/pyne-smoke/bin/python -c "import pynescript; print(pynescript.version)" /tmp/pyne-smoke/bin/pyne --help ` . GitHub Actions dry-run . Push main with publish-ready tree to hoox-sh/pyne. . Confirm CI green (lint, test matrix, package, docker, vscode-ext). . Actions → Publish → Run workflow → dryrun=true (build + twine only, no upload). . Cut v.. . Move CHANGELOG.md Unreleased notes into [..] - YYYY-MM-DD. . Align VSIX version in vscode-extension/package.json (already ..) if shipping Marketplace/VSIX with this tag. . Tag and push: `bash git tag -a v.. -m "v.." git push origin v.. ` . Watch in parallel: Publish → PyPI hoox-pyne Build & Release → GitHub Release assets + optional Marketplace GHCR → ghcr.io/hoox-sh/pyne/{api,cli,lsp}:.. . Post-publish verify `bash pip index versions hoox-pyne or: pip install hoox-pyne==.. python -c "import pynescript; print(pynescript.version)" GitHub Release: https://github.com/hoox-sh/pyne/releases/tag/v.. PyPI: https://pypi.org/project/hoox-pyne/../ ` Hardcoded owner map (repo automation) Workflows use relative github. context (no hard-coded owner) except documentation/comments. Project metadata and docs must point at hoox-sh/pyne: | Location | Role | | --- | --- | | pyproject.toml [project.urls] | PyPI package links | | .github/workflows/publish.yml header | Trusted Publisher fields | | Dockerfile org.opencontainers.image.source | Image source label | | package.json / vscode-extension/package.json repository | Clone / issues URLs | | docs/pyne/docs.json navbar GitHub | Docs site link | | scripts/build/README.md gh secret set -R | Secret target repo | | CONTRIBUTING.md + this page | Human publish runbook | Intentionally not changed | Item | Why | | --- | --- | | .github/FUNDING.yml | GitHub Sponsors username (personal) | | Sister repos jango-blockchained/{axis,hoox} | Separate transfers | | Author strings jango_blockchained | Copyright / author field | | PyPI account jango-blockchained | Package owner; not the VS Code namespace | See also PyPI publish Release — LSP + VSIX CI Root CONTRIBUTING.md` --- FILE: docs/pyne/devops/pypi-publish.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "PyPI publish" description: "Ship hoox-pyne to PyPI under the personal jango-blockchained account (API token or Trusted Publishing). Import package stays pynescript." --- PyPI publish Abstract The Python distribution name on PyPI is hoox-pyne. After install, the import path stays pynescript; preferred CLIs are pyne / pyne-lsp (aliases: pynescript / pynescript-lsp). PyPI ownership is the personal account jango-blockchained (not a PyPI organization — org approval is not required). Automation still runs on GitHub hoox-sh/pyne via .github/workflows/publish.yml. Auth modes (first match wins): . API token — repo secret PYPIAPITOKEN from the personal PyPI account (recommended while no PyPI org is needed). . Trusted Publishing (OIDC) — pending publisher created while logged in as jango-blockchained, with GitHub Owner = hoox-sh (where Actions run). Why not pynescript or pyne on PyPI? pynescript is owned by elbakramer/pynescript; plain pyne/PyNE is an unrelated .. process-networking library. We ship as hoox-pyne; the import package stays pynescript. Install surface ``bash pip install hoox-pyne pip install "hoox-pyne[lsp]" pip install "hoox-pyne[pro]" python -c "import pynescript; print(pynescript.version)" pyne --help pyne-lsp stdio language server (alias: pynescript-lsp) ` One-time setup (personal PyPI account) A. Recommended — API token (no org, no OIDC) . Sign in at pypi.org as jango-blockchained (FA required). . Account settings → API tokens → Add API token. Name: e.g. hoox-sh-pyne-gha Scope: Entire account for the first upload of hoox-pyne; after the project exists you can rotate to a project-scoped token. . Copy the token (pypi-…) once. . On GitHub: `bash Environment (optional reviewers / wait timer) gh api -X PUT repos/hoox-sh/pyne/environments/pypi Never commit the token — Actions secret only gh secret set PYPIAPITOKEN -R hoox-sh/pyne paste pypi-... token ` . First successful tag (or workflowdispatch with dryrun=false) creates hoox-pyne under your personal account. B. Optional — Trusted Publishing (OIDC) Use this when you prefer no long-lived token. Log into PyPI as jango-blockchained (personal user), then: | Field | Value | | --- | --- | | PyPI project name | hoox-pyne | | Owner | hoox-sh ← GitHub repo owner (workflow host), not your PyPI username | | Repository | pyne | | Workflow name | publish.yml | | Environment name | pypi | Leave PYPIAPITOKEN unset so the publish job uses OIDC. Do not put jango-blockchained in the GitHub Owner field unless the workflow actually runs under github.com/jango-blockchained/pyne. OIDC is bound to the repo that executes the job (hoox-sh/pyne). GitHub environment Repo Settings → Environments → pypi on hoox-sh/pyne. Optional: required reviewers on production uploads. Cut a release `bash . Version + changelog on main src/pynescript/about.py → .. vscode-extension/package.json version (same pin when shipping VSIX) CHANGELOG.md . Local smoke (no upload) pip install build twine rm -rf dist/ python -m build twine check dist/ expect: hooxpyne-..-.whl and hooxpyne-...tar.gz . Push main, then tag git tag -a v.. -m "v.." git push origin v.. . Watch Actions → Publish ` If a tag already exists and the wheel was never uploaded, re-run: Actions → Publish → Run workflow → dryrun=false (builds from that workflow’s default branch / checked-out tag context as configured), or retag a new patch version. Dry-run without upload: Actions → Publish → Run workflow → dryrun=true. Workflow map | Job | When | What | | --- | --- | --- | | build | tag v or dispatch | python -m build + twine check + artifact | | publish-pypi | tag v or dispatch with dryrun=false | Token if PYPIAPITOKEN set, else OIDC | Failure modes | Symptom | Cause | Fix | | --- | --- | --- | | invalid token | Wrong/revoked PYPIAPITOKEN | Create new token as jango-blockchained; re-gh secret set | | / no trusted publisher | OIDC path without pending publisher | Add pending publisher or set API token | | Wrong GitHub Owner in pending publisher | Used PyPI username as Owner | Set Owner to hoox-sh (GitHub org of the repo) | | Environment not found | Missing GitHub pypi env | gh api -X PUT repos/hoox-sh/pyne/environments/pypi | | File already exists | Version already on PyPI | Bump about.version and tag a new version | | Tag publish skipped | dryrun dispatch only | Use a real v tag or dryrun=false | See also Release — CLI/LSP binaries + VSIX Publish checklist CI Root CONTRIBUTING.md` --- FILE: docs/pyne/devops/release.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Release" description: "Tag-driven CLI + LSP multi-platform Nuitka binaries, wheels, Docker CLI image, VSIX packaging, GitHub Releases, and Marketplace publish." --- Release Abstract A PYNE release is a multi-artifact event, not a single wheel upload. The primary automation (.github/workflows/release.yml) builds: PyPI sdist + wheel (hoox-pyne) with CLI smoke CLI platform-native Nuitka binaries (pynescript) LSP platform-native Nuitka binaries (pynescript-lsp) CLI Docker image tarball (pynescript-cli-image.tar.gz) VS Code VSIX Assets attach to a GitHub Release; PyPI upload is handled by Publish (publish.yml). Multi-arch container images are a separate workflow (GHCR / ghcr.yml) — not attached as tarballs except the CLI image save in this workflow. Marketplace / Open VSX publish is optional via VSCEPAT / OVSXPAT. Conceptual model Interface surface Triggers | Event | Behavior | | --- | --- | | Push tag matching v | Full build + create release + marketplace/Open VSX publish | | workflowdispatch with version input | Same jobs; Create Release also runs on dispatch (not tag-only) | Jobs | Job | Matrix / OS | Output | | --- | --- | --- | | build-package | ubuntu-latest | hoox-pyne-dist (sdist + wheel) | | build-cli | ubuntu-latest, windows-latest, macos-latest | pynescript-cli-linux-x, …-windows-x.exe, …-macos-arm | | build-lsp | ubuntu-latest, windows-latest, macos-latest | pynescript-lsp-linux-x, …-windows-x.exe, …-macos-arm | | build-docker-cli | ubuntu-latest | pynescript-cli-docker (gzipped docker save) | | build-vscode | ubuntu-latest | pynescript-vscode-extension VSIX | | release | needs package + vscode success; tag or dispatch | GitHub Release body + staged assets (name: pyne vX.Y.Z) | | publish-vscode | needs VSIX; tag or dispatch | vsce publish if VSCEPAT; ovsx publish if OVSXPAT | Environment ``yaml env: PYTHONVERSION: "." Nuitka .–. graph; comment in workflow NUITKAJOBS: ` Build steps rely on: `bash LSP pip install -e ".[lsp]" "nuitka>=..,=..,<." python scripts/build/compile.py --target cli --check python scripts/build/cibuild.py --target cli --jobs --skip-metadata --skip-vsix ` Local make-side packaging `bash make package sdist + wheel (python -m build) make build Nuitka LSP make build-cli Nuitka CLI make build-vscode npm install && compile && vsce package make docker-build-cli ` Artifacts land under dist/, dist/lsp/, dist/cli/, dist/vsix/, and vscode-extension/.vsix depending on script path. Internals | Path | Role | | --- | --- | | .github/workflows/release.yml | Orchestration | | scripts/build/cibuild.py | CI-oriented Nuitka + metadata encrypt | | scripts/build/compile.py | Local/full compile options (--onefile, --standalone, --check) | | vscode-extension/package.json | Extension version / engines | | src/pynescript/about.py | Hatch dynamic version for Python package | Release asset contract (from release body) | Artifact | Install sketch | | --- | --- | | hooxpyne-.whl / .tar.gz | pip install ./hooxpyne-.whl | | pynescript-cli-linux-x | chmod +x → /usr/local/bin/pynescript | | pynescript-cli-windows-x.exe | Place on PATH as pynescript.exe | | pynescript-cli-macos-arm | Place on PATH (Apple Silicon runners) | | pynescript-cli-image.tar.gz | gunzip -c … \| docker load → pynescript-cli:latest | | pynescript-lsp-linux-x | chmod +x → /usr/local/bin/pynescript-lsp | | pynescript-lsp-windows-x.exe | Place on PATH | | pynescript-lsp-macos-arm | Place on PATH (Apple Silicon runners) | | pyne-vscode-.vsix (hoox-sh.pyne ..) | code --install-extension pyne-vscode-.vsix | Invariants & edge cases . CRYPTOKEY must be stable across builds if you care about byte-identical builtinmetadata.json.enc. Supply secrets.METADATAKEY. . Tag name is the version source for the GitHub Release title (v prefix stripped). . Marketplace publish needs VSCEPAT. Without it, packaging may still succeed while publish fails. . Artifact retention is short ( days on build jobs) — releases must attach files immediately. . macOS artifact is ARM from macos-latest — Intel Mac users may need separate builds if required. . Draft vs prerelease: workflow currently sets draft: false, prerelease: false for tag releases. Worked examples Cut a release `bash ensure main is green git checkout main && git pull bump versions in extension / about as needed git tag -a v.. -m "v.." git push origin v.. watch Actions → Build & Release, Publish, GHCR ` Smoke-test a downloaded binary `bash chmod +x pynescript-cli-linux-x ./pynescript-cli-linux-x --help ./pynescript-cli-linux-x check script.pine chmod +x pynescript-lsp-linux-x ./pynescript-lsp-linux-x --help or wire stdio into an editor client ` Failure modes | Symptom | Cause | Fix | | --- | --- | --- | | Binary not found after Nuitka | Path layout drift in cibuild.py | Inspect dist/; fix finder step | | Decrypt errors in field | Key mismatch between encrypt and runtime | Align METADATAKEY with embedded key strategy | | Empty GitHub Release assets | Artifact download path mismatch | Match files: globs to download-artifact layout | | Marketplace | Bad/expired VSCEPAT | Rotate PAT with Marketplace scopes | | Windows .exe not marked executable | Finder uses -type f -executable` | Fallback non-executable find branch in workflow | See also Nuitka build Metadata crypto CI VS Code extension --- FILE: docs/pyne/devops/security.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Security" description: "API keys, CORS, body limits, secrets hygiene, Fernet metadata keys, and threat-aware defaults for PYNE ops." --- Security Abstract PYNE security is layered: transport/hosting (Cloud Run, containers), application (API keys, admin tokens, CORS, body size caps), build secrets (Fernet metadata keys, VSCE PAT), and supply chain (CI dependency installs, extension packaging). This page documents controls that exist in code and workflows — not a full threat model certification. Conceptual model Interface surface HTTP application controls (backend/app.py) | Control | Default / mechanism | | --- | --- | | Body size | MAXCONTENTLENGTH = ( MiB) — DoS/OOM mitigation | | CORS | ALLOWEDORIGINS env (comma-separated); default includes product hosts + localhost regex | | Methods | GET, POST only via flask-cors config | | Headers | Content-Type, Authorization, X-Admin-Token | | Credentials | supportscredentials=False | API keys (backend/middleware/auth.py) Keys stored as hashes, not raw secrets (store path via APIKEYSTORE) Tiers: free, hobby (k), pro (k), team (k), enterprise (∞) Rate limit by monthly call counters (callsused / callslimit) Decorators: requireapikey, requireadmintoken Build / release secrets | Secret | Purpose | | --- | --- | | METADATAKEY / METADATAKEY | Stable Fernet key for LSP metadata encrypt | | VSCEPAT | VS Code Marketplace / vsce package | | GITHUBTOKEN | Release asset upload | | PYNESCRIPTMETADATAKEY | Runtime decrypt env fallback | Container hardening (Dockerfile target api) Multi-stage build (no compiler toolchain in final image) Non-root appuser (uid ) Production env flags + gunicorn entrypoint Healthcheck without privileged ops Intended mutable path /data (key store); prod compose uses SQLite there for multi-worker Admin minting POST /auth/createkey requires ADMINTOKEN env and matching X-Admin-Token header (constant-time compare) Unset ADMINTOKEN → (fail closed, not open access) Key store backends (STOREBACKEND) | Value | Use | | --- | --- | | json (default) | Single-process / tests; file may hold raw keys | | sqlite | Multi-worker single host (shared volume); hash-only | | redis | Multi-replica; needs REDISURL; hash-only | Internals | Path | Role | | --- | --- | | backend/app.py | CORS, body limit, blueprint registration | | backend/middleware/auth.py | Key store selection, tiers, decorators, admin gate | | backend/middleware/keystoreredis.py / keystoresqlite.py | Hash-only shared stores | | backend/middleware/schemas.py | Request validation schemas | | src/pynescript/langserver/providers/metadatadecrypt.py | Encrypted metadata load | | .github/workflows/.yml | Secret consumption (METADATAKEY, PYPIAPITOKEN, VSCEPAT, OVSXPAT) | | AXIS PWA security tests | Sister repo hoox-sh/axis — not in this tree | Audit notes encoded in source Comments in app.py reference a -- audit (findings S body size, S/S CORS). Prefer reading the code over assuming defaults never change. Invariants & edge cases . Cloud Run --allow-unauthenticated does not mean open data. It means IAM does not gate invoke; the Flask app must. . Free tier with callslimit= is special-cased as unlimited remaining in some helpers — verify tier semantics before production billing. . Localhost CORS regex permits any port on localhost/... — fine for desk, wrong for prod ALLOWEDORIGINS. . Fernet protects binary metadata packaging, not user script confidentiality. . Admin token routes are high privilege — rotate and store separately from user API keys. . Never log raw API keys. Hash lookups only. Worked examples Harden CORS for a single origin ``bash export ALLOWEDORIGINS=https://app.example.com python -m backend.app ` Point key store at a volume `bash export APIKEYSTORE=/data/apikeys.json ensure process user can write /data ` Rotate metadata key (ops procedure sketch) . Generate new Fernet key; store as secret METADATAKEY . Rebuild LSP with CRYPTOKEY set . Ship new binary; retire old key after client upgrade window . Do not leave dual-key decrypt unless you implement it Failure modes | Symptom | Cause | Fix | | --- | --- | --- | | Browser CORS error | Origin not listed | Set ALLOWEDORIGINS | | Payload Too Large | Body > MB | Reduce OHLCV batch; raise limit knowingly | | / on /run | Missing/invalid key | Check Authorization header scheme | | Rate limited | Tier exhausted | Upgrade tier / wait reset | | Secret in fork PR logs | Misconfigured workflow echo | Never echo secrets; use masked env | | Root-owned files in volume | Old image ran as root | Align UID with appuser` | See also Auth and keys (API) Metadata crypto Docker GCP --- FILE: docs/pyne/enduser/getting-started/configuration.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Configuration" description: "Package extras, console scripts, Pro API environment variables, data providers, and editor LSP settings for PYNE." --- Configuration Abstract PYNE is mostly convention-over-config at the library layer: parse and evaluate take explicit arguments rather than a global config file. Configuration appears at three boundaries: install extras (what code is available), process environment (Pro API host, CORS, API keys store), and editor / LSP client settings (diagnostics, formatting, binary path). This page inventories those knobs for end users. Conceptual model There is no pynescript.toml (or similar) for core use. Parse, evaluate, and CLI flags are per-invocation. Interface surface Package extras (pyproject.toml) | Extra | Dependencies | Enables | | --- | --- | --- | | (none) | antlr-runtime, click, requests, tqdm | CLI + AST + linter + literaleval + pynescript.runtime | | lsp | pygls, lsprotocol | pyne-lsp (alias pynescript-lsp) | | dev-lsp | pygls, lsprotocol, pytest-lsp | LSP + protocol tests | | data | ccxt | Exchange historical data | | datafeed | ccxt | Same as data (alias for realtime feed paths) | | compile | numpy, numba | Compile / run / prewarm Numba path | | pro | Flask stack (+ redis optional) | Self-hosted Pro API | Console scripts | Name | Entry point | Notes | | --- | --- | --- | | pyne | pynescript.main:cli | Preferred desk CLI | | pyne-lsp | pynescript.langserver.main:main | Preferred language server | | pynescript | same as pyne | Legacy alias | | pynescript-lsp | same as pyne-lsp | Legacy alias | Invoke without scripts on PATH: ``bash python -m pynescript --help python -m pynescript.langserver ` CLI flags that act as “config” Per-invocation only (no persistence): | Command | Notable options | | --- | --- | | check | --encoding, -q/--quiet, --ext (directory walk) | | format / fmt | -w/--write, --check, -o/--output-file | | parse-and-dump | --encoding, --indent, --output-file | | parse-and-unparse | --encoding, --output-file | | lint | --encoding, --fail-on {errors,warnings,all,never}, --json, -q | | compile | --emit, -o, --time/--no-time | | prewarm | --force, --json | | run | --bars (default ), --json, -q — compile-only synthetic OHLCV | | data | --provider, --period, --interval, --api-key, --secret, --exchange, --format | lint has no --fix. Structural rewrite is pyne format -w (parse → unparse). Pro API environment variables Consumed by backend/app.py and middleware: | Variable | Default | Purpose | | --- | --- | --- | | HOST | ... | Bind address when running main | | PORT | | Bind port | | ALLOWEDORIGINS | https://pynescript.ai, https://app.pynescript.ai, plus localhost regex | CORS allowlist (comma-separated; regex allowed). Code also appends localhost, private-LAN, and product-origin (hoox.sh / pynescript.ai / pynescript.online) regexes unless | | ADMINTOKEN | unset | Required for POST /auth/createkey; if unset, create returns | | APIKEYSTORE | /root/pynescript/data/apikeys.json | Path for key store (override for local) | | ALERTWEBHOOKURL | unset | Default L alert webhook for /run when body omits webhookurl | | ALERTWEBHOOKTIMEOUT | | Webhook HTTP timeout (seconds) | Also relevant: | Variable | Context | | --- | --- | | MAXCONTENTLENGTH | Hardcoded MiB on Flask app config (not env) — large OHLCV POSTs fail above this | Runtime / evaluate environment variables Used by pynescript.runtime.Runtime (src/pynescript/runtime/host.py) and the compiler host. backend.runtime is a compat re-export only. | Variable | Default | Purpose | | --- | --- | --- | | PYNERUNTIMEMODE | interpret | Default when Runtime.run(..., mode=None) omits mode (Pro API schema default is still auto; pyne run ignores this — compile-only) | | PYNESERIESCAP | on | Trim append-only host series lists; set / false / off to disable (oracle / debug) | | PYNESERIESMAX | unset | Absolute series history depth (overrides default + maxbarsback) | | PYNETAINCREMENTAL | on | Bar-mode incremental ta. hot path; set to force full recompute | | PYNESERIESRING | off | Chronological tail view; skip dual list write. Keep off unless you need the ring path | | PYNEPARSECACHE | on | Process-local LRU of successful parse() trees; set to disable | | PYNEPARSECACHEMAX | | Max parse-cache entries | | PYNECOMPILEDISKCACHE | often in deploy | Persist compiled IR across processes | | PYNECOMPILECACHEDIR | XDG cache / Docker /data/compile-cache | Disk IR location | | PYNECOMPILEPREWARM | deploy-dependent | Host cold-start prewarm of Numba builtins | See Series & history, Technical builtins, and Compiler overview. Local run: `bash export HOST=... export PORT= export ALLOWEDORIGINS="http://localhost:,http://...:" export ADMINTOKEN="dev-only-token" export APIKEYSTORE="$PWD/.data/apikeys.json" python -m backend.app or: make run ` Data providers CLI pynescript data / library providers: | Provider | Auth | Notes | | --- | --- | --- | | mock | none | Deterministic offline bars (default) | | yahoo | none | Yahoo Finance path | | alphavantage | --api-key (falls back to demo) | Limited with demo key | | ccxt | optional key/secret; --exchange | Requires [data] extra | Pro API /run optional fields: | Field | Values | Role | | --- | --- | --- | | datasource | "", mock, ccxt, ccxtpro, yahoo, alphavantage | Wires request. resolution | | dataoptions | object | exchange, apikey, seed, … | | mode | auto (default), interpret, compile | Prefer warm compile + fallback; strict compile; full interpret | | inputs | object | input. overrides by title (forces interpret under auto) | | libraries | list | [{namespace, name, version, source}] — max ; also on /run/batch; interpret / auto fallback | | timeoutseconds | number | Optional interpret wall-clock budget; omit / null / ≤ → no timeout | | profiler | bool | Per-line timings; forces interpret | | webhookurl | string | Per-request L alert webhook (overrides ALERTWEBHOOKURL) | | forwardalerts / alertlastbar / alertbatch | bool | Webhook delivery controls (see Alerts) | timeoutseconds is accepted on Runtime.run, POST /run, and POST /run/batch. Extra keys that are not in the schema still → UNKNOWNFIELDS. Operators can also POST /compile/prewarm or run pyne prewarm (and pyne prewarm script.pine …) to warm Numba builtins / script IR caches before interactive traffic. Requires pip install "hoox-pyne[compile]" for full Numba warm-up. Free-tier Pro API guards (unauthenticated /run): FREEMAXBARS, FREEMAXSCRIPTCHARS, FREEMAXCONCURRENT, FREERATELIMIT / FREERATEWINDOWSEC — see Pro API usage. Editor / LSP client settings VS Code extension keys (from clients/README.md): | Setting | Default | Meaning | | --- | --- | --- | | pynescript.lsp.enabled | true | Toggle server | | pynescript.lsp.command | auto | auto tries pyne-lsp → pynescript-lsp → python -m pynescript.langserver; or an absolute path | | pynescript.lsp.python | python | Interpreter when command is auto and no binary is on PATH | | pynescript.lsp.args | [] | Extra args to the language server | | pynescript.formatting.enabled | true | Format document | | pynescript.diagnostics.enabled | true | Lint squiggles | | pynescript.completion.snippets | true | Snippet completions | Extension id: hoox-sh.pyne. Neovim (clients/neovim.lua): `lua settings = { pinescript = { formatting = { enabled = true }, diagnostics = { enabled = true }, completion = { snippets = true }, }, } ` Zed: languageservers.pynescript.command / arguments: ["--stdio"] — see Editors. AXIS / frontend coupling (optional) When running SuperChart Lite PWA against local Flask: | Make target | Port (typical) | | --- | --- | | make run | API : | | make run-frontend | PWA : | | make worker-dev | CF Worker : | CORS must allow the PWA origin via ALLOWEDORIGINS. Internals (repo paths) | Path | Config surface | | --- | --- | | pyproject.toml | extras, scripts, Python requires | | src/pynescript/main.py | CLI options | | src/pynescript/runtime/host.py | Runtime.run (mode, libraries, timeoutseconds, inputs) | | backend/app.py | Flask, CORS, HOST/PORT, body size | | backend/middleware/auth.py | APIKEYSTORE, key tiers | | backend/middleware/schemas.py | Request field defaults (mode, symbol, …) | | clients/.json|lua|el | Editor LSP wiring | | vscode-extension/ | Extension contribution points | Invariants & edge cases Unknown JSON fields on Pro API are rejected (UNKNOWNFIELDS) — schemas are strict. CORS defaults exclude arbitrary production domains — set ALLOWEDORIGINS deliberately. Key store default path is production-oriented (/root/...) — always override locally. Lint --fail-on only affects process exit code; it does not change which rules run. Recursion limit during parse is raised to at least temporarily inside helper.parse for deep nests; not user-configurable. Worked examples Desk-only machine `bash python -m venv .venv && source .venv/bin/activate pip install hoox-pyne no env vars required pyne lint strategy.pine --fail-on errors ` Editor workstation `bash pip install "hoox-pyne[lsp]" ensure which pyne-lsp (alias: pynescript-lsp) paste clients/neovim.lua or clients/zed.json as documented ` Local API + AXIS `bash pip install -e ".[lsp]" pip install -r backend/requirements.txt export APIKEYSTORE="$PWD/.data/apikeys.json" export ALLOWEDORIGINS="http://localhost:" make run other terminal: make run-frontend ` Scripted Alpha Vantage fetch `bash export AVKEY=yourkey pynescript data EUR/USD --provider alphavantage --api-key "$AVKEY" --period mo ` Failure modes | Symptom | Config fix | | --- | --- | | Browser CORS error on /run | Add origin to ALLOWEDORIGINS | | createkey always | Set ADMINTOKEN and send X-Admin-Token | | API keys vanish / permission error | Point APIKEYSTORE to writable path | | Payload too large | Split bars or stay under MiB body limit | | Editor “server failed to start” | Fix pynescript.lsp.command PATH; install [lsp] | | ccxt import errors | Install [data]` extra | See also Installation Pro API usage Editors CLI commands Runtime modes API auth (systems) AXIS docs --- FILE: docs/pyne/enduser/getting-started/installation.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Installation" description: "Install hoox-pyne from PyPI (live) or a source checkout — CLI, LSP, compile, data, and Pro API extras." --- Installation Abstract PYNE is live on PyPI as hoox-pyne (currently ..+). The import package remains pynescript (src-layout under src/pynescript/). Preferred console scripts are pyne / pyne-lsp; legacy pynescript / pynescript-lsp remain installed as aliases. Do not install: plain pyne / PyNE from PyPI without the hoox- prefix — unrelated .. process-networking library (our wheel is hoox-pyne) upstream pynescript (elbakramer) — different project ``bash pip install hoox-pyne pip install "hoox-pyne[lsp]" language server pip install "hoox-pyne[compile]" Numba compile path pip install "hoox-pyne[data]" ccxt market data pip install "hoox-pyne[pro]" Flask Pro API stack ` Conceptual model | Install flavor | What you get | When | | --- | --- | --- | | pip install hoox-pyne | CLI + library (pynescript.runtime SoT) | Desk use, scripts, CI lint | | pip install "hoox-pyne[lsp]" | + pygls LSP server | Editors | | pip install "hoox-pyne[compile]" | + numpy / numba | compile / prewarm / run Numba modes | | pip install "hoox-pyne[data]" | + ccxt | Exchange data providers | | pip install "hoox-pyne[pro]" | + Flask Pro API stack | Self-hosted /run | | pip install -e ".[dev-lsp]" | Editable + LSP test harness | Contributing to LSP | | Docker CLI image | Full CLI image | CI gates / isolated runs | | Hatch env | Matrix tests, lint, docs | Full monorepo workflow | Interface surface Prerequisites Python ≥ . (classifiers declare .–.) pip or an equivalent installer; virtualenv strongly recommended Optional: Docker for the CLI image; Node + only if building the VS Code extension Core install (PyPI) `bash python -m venv .venv source .venv/bin/activate Windows: .venv\Scripts\activate pip install -U pip pip install "hoox-pyne[lsp,compile]" pyne --version pyne info ` Project page: pypi.org/project/hoox-pyne · owner account jango-blockchained. Extras Defined in pyproject.toml under [project.optional-dependencies]: `bash pip install "hoox-pyne[lsp]" pip install "hoox-pyne[compile]" pip install "hoox-pyne[data]" alias: datafeed pip install "hoox-pyne[pro]" pip install "hoox-pyne[lsp,compile,data]" ` Source checkout `bash git clone https://github.com/hoox-sh/pyne.git cd pyne pip install -e ".[lsp,pro]" or: make install ` Docker CLI `bash docker pull when published to a registry; or build from source: git clone https://github.com/hoox-sh/pyne.git && cd pyne make docker-build-cli docker run --rm -v "$PWD:/work" -w /work pynescript-cli:latest check script.pine ≡ make docker-cli ARGS="check script.pine" ` Console scripts | Command | Purpose | | --- | --- | | pyne | Preferred Click CLI (check, format, lint, compile, prewarm, run, data, info) | | pyne-lsp | Preferred language server (requires [lsp]) | | pynescript | Alias of pyne (same callable) | | pynescript-lsp | Alias of pyne-lsp (same callable) | `bash pyne --version pyne --help pyne check script.pine which pyne-lsp only after [lsp] ` Standalone binaries and a CLI image tarball also ship on GitHub Releases. Internals (repo paths) | Path | Role | | --- | --- | | pyproject.toml | Package metadata (name = "hoox-pyne"), extras, scripts | | src/pynescript/ | Library + CLI + LSP package tree | | src/pynescript/about.py | version | | src/pynescript/main.py | CLI implementation | | src/pynescript/runtime/ | Package Runtime SoT (bar-loop host, series, evaluator) | | src/pynescript/langserver/main.py | pyne-lsp entry | | Dockerfile target cli | Ephemeral CLI image | | Makefile | package, docker-build-cli, install, … | | CONTRIBUTING.md | Publish / Hatch workflow | Invariants & edge cases PyPI name ≠ import name. Install hoox-pyne; import pynescript. CLIs: pyne / pyne-lsp. License. AGPL-.-or-later. Network use triggers AGPL source-offer obligations (see GNU AGPL §). LSP is not a CLI subcommand. Use pyne-lsp (or python -m pynescript.langserver). Pro API. pip install "hoox-pyne[pro]" or monorepo pip install -r backend/requirements.txt + make run (default :). Source extensions. Prefer .pyne for stack sources; .pine / .pinev / .pinev still work. Worked examples Smoke-test CLI and library `bash python - <<'PY' from pynescript.ast import parse, unparse from pynescript import about print("version", about.version) src = '//@version=\nindicator("t")\nplot(close)\n' print(unparse(parse(src))) PY pyne check examples/rsistrategy.pine pyne lint examples/rsistrategy.pine optional (needs [compile] for Numba): pyne prewarm pyne run examples/rsistrategy.pine --bars ` Monorepo desk + Pro API `bash git clone https://github.com/hoox-sh/pyne.git cd pyne pip install -e ".[lsp,pro]" make run Flask on : ` Failure modes | Failure | Diagnosis | Fix | | --- | --- | --- | | ModuleNotFoundError: pynescript | Wrong env / not installed | Activate venv; pip install hoox-pyne | | No module named 'pygls' | LSP without extra | pip install "hoox-pyne[lsp]" | | No module named 'numba' | compile path without extra | pip install "hoox-pyne[compile]" | | No module named 'ccxt' | data path without extra | pip install "hoox-pyne[data]" | | Wrong package / missing APIs | Installed upstream pynescript or plain pyne | pip uninstall pynescript pyne then pip install hoox-pyne | | pyne / pynescript not found | Scripts not on PATH | python -m pynescript or fix venv bin/` | See also Quick start CLI guide Editors / LSP PyPI publish (maintainers) End User hub --- FILE: docs/pyne/enduser/getting-started/quick-start.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Quick Start" description: "Parse, dump, unparse, lint, and evaluate Pine Script with the pyne CLI and library in under ten minutes." --- Quick Start Abstract This page is a linear path from a working install to the five desk operations most users need: parse, dump, unparse, lint, and evaluate. Full option trees live in CLI commands; deeper evaluation semantics live in Evaluate scripts. Conceptual model Interface surface Assumes: ``bash pip install hoox-pyne https://pypi.org/project/hoox-pyne/ · ..+ optional: pip install "hoox-pyne[lsp]" editors pip install "hoox-pyne[compile]" compile / run / prewarm pip install "hoox-pyne[data]" ccxt providers ` `bash pyne info pyne check script.pine pyne lint script.pine aliases still work: pynescript info … ` Sample script in-tree: examples/rsistrategy.pine. Prefer //@version= and .pyne / .pine for new work (.pinev remains a supported legacy association). Internals (repo paths) | Step | Implementation | | --- | --- | | CLI parse/dump | src/pynescript/main.py → parseanddump | | CLI unparse | parseandunparse | | CLI lint | lint → pynescript.ast.linter.lintscript | | Library helpers | src/pynescript/ast/helper.py | | Bar-loop Runtime SoT | src/pynescript/runtime/ (from pynescript.runtime import Runtime) | | Example scripts | examples/parsedumpunparse.py, examples/evaluateexpressions.py | Invariants & edge cases Always declare //@version= at the top (v still parses); the linter warns (W) if the pragma is missing. parse-and-unparse is a formatter via AST, not a semantic optimizer. literaleval is for expressions / built-in calls with optional series context — not a full multi-bar strategy backtester. For bar loops, use pynescript.runtime.Runtime (or the Pro API /run). Filename arguments to the CLI must exist and be readable; lint may also read stdin (- or no file). Worked examples . Parse and dump the AST `bash pyne parse-and-dump examples/rsistrategy.pine ` Pretty-print with indent and optional file output: `bash pyne parse-and-dump examples/rsistrategy.pine --indent --output-file /tmp/rsi.ast.txt ` Library equivalent: `python from pynescript.ast import parse, dump with open("examples/rsistrategy.pine", encoding="utf-") as f: tree = parse(f.read(), "examples/rsistrategy.pine") print(dump(tree, indent=)) ` . Round-trip unparse (normalize) `bash pyne parse-and-unparse examples/rsistrategy.pine pyne parse-and-unparse messy.pine --output-file clean.pine ` `python from pynescript.ast import parse, unparse source = """ //@version= indicator("My RSI") rsi(close, ) """ print(unparse(parse(source))) ` Canonical demo: examples/parsedumpunparse.py. . Lint before upload `bash pyne lint examples/rsistrategy.pine pyne lint --fail-on warnings examples/rsistrategy.pine echo '//@version=\nindicator("x")\nplot(close)' | pyne lint - ` Library: `python from pynescript.ast.linter import lintscript issues = lintscript(open("examples/rsistrategy.pine").read(), "rsistrategy.pine") for w in issues: print(w) severity: [CODE] message at line N ` . Evaluate expressions `python from pynescript.ast.helper import literaleval print(literaleval(" + ")) print(literaleval("math.sqrt()")) prices = [, , , , , , , , , ] print(literaleval(f"ta.rsi({prices}, )")) Series history with context ctx = { "close": [, , , , ], "open": [, , , , ], } print(literaleval("close[] - close[]", ctx)) ` Broader demo: examples/evaluateexpressions.py. . Fetch sample market data (CLI) `bash mock provider (no network) pyne data AAPL --provider mock Yahoo (network) pyne data AAPL --provider yahoo --period mo --interval d CCXT (needs hoox-pyne[data]) pyne data BTC/USDT --provider ccxt --exchange binance ` . Optional: local Pro API /run `bash from monorepo with backend deps make run ` `bash curl -s -X POST http://...:/run \ -H 'Content-Type: application/json' \ -d '{ "script": "//@version=\nindicator(\"t\")\nplot(close)", "mode": "auto", "data": [ {"time": , "open": , "high": , "low": ., "close": ., "volume": }, {"time": , "open": ., "high": ., "low": ., "close": ., "volume": } ] }' | python -m json.tool ` Omit mode to use the schema default auto (warm compile when eligible, else interpret). Response may include series, plotmeta, events, drawings, and alerts. See Pro API usage for batch mode, webhooks, free-tier guards, and prewarm. Prefer saving stack sources as .pyne (.pine still works). . Library bar-loop vs CLI smoke Library Runtime.run defaults to interpret. pyne run is compile-only on synthetic bars and does not accept --mode. `python from pynescript.runtime import Runtime ohlcv = [ {"time": , "open": , "high": , "low": ., "close": ., "volume": }, {"time": , "open": ., "high": ., "low": ., "close": ., "volume": }, ] out = Runtime(symbol="DEMO").run( '//@version=\nindicator("t")\nplot(close)\n', ohlcv, mode="interpret", timeoutseconds=., ) print(out.get("mode"), list(out.get("series", {}))) ` `bash pip install "hoox-pyne[compile]" pyne run examples/rsistrategy.pine --bars ` See modes. . Optional: start LSP for editors `bash pip install "hoox-pyne[lsp]" pyne-lsp stdio; normally launched by the editor alias: pynescript-lsp ` Wire Neovim / Zed / Emacs using Editors and clients/. Failure modes | Step fails | Check | | --- | --- | | Dump raises SyntaxError | Script version / unsupported construct — try a minimal indicator + plot(close) | | Unparse output differs cosmetically | Expected; compare semantic structure via dump | | Lint exits non-zero | --fail-on threshold; inspect codes E, W, … | | literaleval NotImplementedError | Expression not in literal/builtin subset; use full Runtime | | data provider error | Network, API key, missing ccxt` extra | See also Installation Configuration CLI guide Library API Evaluate scripts Runtime modes CLI command reference --- FILE: docs/pyne/enduser/guides/cli.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "CLI Guide" description: "Use the pyne Click CLI to check, format, lint, compile, run, prewarm, fetch data, and more." --- CLI Guide Abstract The preferred pyne console script (alias pynescript) is a Click group over the same helpers used by the library API. Install with pip install hoox-pyne (live on PyPI). It is optimized for shell pipelines, CI gates, one-shot inspection, and light compile/run smokes. Full multi-bar evaluation with real OHLCV belongs on the package pynescript.runtime.Runtime or Pro API. Exhaustive flags: CLI commands. Conceptual model | CLI | Library / subsystem | | --- | --- | | check | parse only (exit codes) | | format / fmt | parse + unparse | | parse-and-dump / dump | parse + dump | | lint | lintscript | | compile / prewarm / run | pynescript.compiler (+ [compile] extra) | | data | pynescript.util.data.getprovider | | info | version + optional extras | Interface surface ``bash pyne --help pyne --version pyne --help alias: pynescript … ` | Command | Input | Output | | --- | --- | --- | | check PATHS… | files / dirs / stdin | ok/fail lines + exit code | | format PATH | file or - | formatted source (-w / --check) | | parse-and-dump PATH | file | AST dump | | parse-and-unparse PATH | file | Pine source | | lint [PATH\|-] | file or stdin | diagnostics (--json) | | compile PATH | file | emit Python or compile-check | | prewarm [PATH…] | optional files | warm Numba / IR cache | | run PATH | file | synthetic-bar execute | | data SYMBOL | symbol + provider opts | table / json / csv | | info | — | version + extras | Not part of this Click group: language server — use pyne-lsp (alias pynescript-lsp). Install options | Method | When | | --- | --- | | pip install hoox-pyne | Default desk / CI use | | pip install "hoox-pyne[compile]" | Need Numba compile / run modes | | GitHub Release binary pynescript-cli- | No Python install | | Docker pynescript-cli | Isolated CI / one-shot checks | `bash Docker docker buildx bake cli docker run --rm -v "$PWD:/work" -w /work pynescript-cli:latest check script.pine Compose (repo root) make docker-cli ARGS="lint script.pine --json" Standalone binary (from GitHub Release) chmod +x pynescript-cli-linux-x ./pynescript-cli-linux-x info ` Internals (repo paths) | Path | Role | | --- | --- | | src/pynescript/main.py | All command implementations | | src/pynescript/ast/helper.py | parse, dump, unparse | | src/pynescript/ast/linter.py | lintscript | | src/pynescript/compiler/ | compile / prewarm / run | | src/pynescript/util/data.py | providers + DataProviderError | | examples/ | sample .pine inputs | | tests/testcli.py | CLI unit suite (CI) | Invariants & edge cases Encoding default is UTF- for file reads/writes. --output-file - means stdout (Click allowdash=True). Lint without a path reads stdin — useful in git hooks and cat script.pine | pyne lint. --fail-on defaults to errors (syntax/E); warnings fails on any warning severity too; all fails if any issue exists. data default provider is mock — offline-safe. Alpha Vantage without key prints a warning and uses demo. lint is one file or stdin — no directory walk. Use check for recursive parse-only (--ext). run is compile-only on synthetic bars (--bars default ). It does not call Runtime.run and has no --mode. Worked examples Inspect structure of a strategy `bash pyne parse-and-dump examples/rsistrategy.pine --indent | less ` Compare two scripts’ shapes in CI: `bash pyne parse-and-dump a.pine --output-file /tmp/a.ast pyne parse-and-dump b.pine --output-file /tmp/b.ast diff -u /tmp/a.ast /tmp/b.ast ` Format / normalize `bash pyne format messy.pine -w pyne format strategy.pine --check CI drift gate (exit if would change) pyne parse-and-unparse messy.pine --output-file clean.pine ` Lint gate in CI `bash set -e lint is one file or stdin — not a directory walk for f in strategies/.pine strategies/.pyne; do pyne lint --fail-on warnings "$f" done parse-only gate over a tree: pyne check strategies/ --ext .pine ` Stdin pipeline: `bash git show HEAD:strategies/rsi.pine | pyne lint --fail-on errors - ` Market data smoke checks `bash pyne data AAPL pyne data AAPL --provider yahoo --period y --interval d pyne data BTC/USDT --provider ccxt --exchange binance pyne data EUR/USD --provider alphavantage --api-key "$AVKEY" ` Typical table keys (from main.py): symbol, provider, bars, period / interval, first close, last close, change, high / low, avg volume. --format json emits full OHLCV lists. Compile, prewarm, synthetic run `bash pip install "hoox-pyne[compile]" pyne compile script.pine compile-check (Numba or object-mode) pyne compile script.pine --emit print generated Python pyne prewarm warm shared Numba kernels pyne prewarm script.pine also compile that source into IR caches pyne run script.pine --bars compile-only execute on synthetic OHLCV ` Compose with library for evaluation pyne run is compile-only smoke. Real bars + mode / libraries / timeoutseconds belong on the package Runtime: `bash pyne format strat.pine -o /tmp/s.pine python - <<'PY' from pathlib import Path from pynescript.runtime import Runtime hand off to Runtime or your own visitor print(Path("/tmp/s.pine").readtext()[:]) PY ` Failure modes | Exit / message | Cause | Mitigation | | --- | --- | --- | | Click path errors | missing file | check PATH exists and is a file | | Parse exception during dump | invalid syntax | fix source; use smaller repro | | Lint failed with errors. | --fail-on threshold | read Ex / Wx lines | | DataProviderError → ClickException | network / auth / missing dep | install [data], fix keys | | Empty dump / unexpected tree | mode always exec for CLI | expression-only needs library mode="eval"` | See also CLI commands reference Runtime modes Library API Quick start Troubleshooting --- FILE: docs/pyne/enduser/guides/editors.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Editors & LSP" description: "Wire pyne-lsp into VS Code, Neovim, Zed, Emacs, Helix, and other LSP clients for diagnostics, completion, hover, and formatting." --- Editors & LSP Abstract pyne-lsp (alias pynescript-lsp) is a stdio Language Server (pygls) over the same parse/lint/metadata stack as the desk tools. Editors do not reimplement Pine Script intelligence; they speak LSP. This guide covers install, client configs in clients/, and feature expectations. Server architecture is documented under LSP. Conceptual model | Feature | Role | | --- | --- | | Diagnostics | Lint rules + parse errors as squiggles | | Completion | Builtins (ta., strategy., …) + snippets | | Hover | Signature / docs from metadata | | Definition / references | Symbol index in document | | Document symbols | Outline / breadcrumb | | Formatting | Full document + range via unparser path | Builtin metadata is generated (scripts/generatebuiltinmetadata.py); encrypted blobs may appear in release builds. Interface surface Install server ``bash pip install "hoox-pyne[lsp]" monorepo: pip install -e ".[lsp]" or: make install command -v pyne-lsp preferred command -v pynescript-lsp alias python -m pynescript.langserver equivalent entry ` The process speaks LSP over stdio by default (PynescriptLanguageServer.startio()). Do not expect a useful interactive TTY UI. VS Code / Cursor / Antigravity . Build or install the extension from vscode-extension/: `bash cd vscode-extension npm ci npm run compile package: npx vsce package (or make build-vscode) ` . Open a .pyne (preferred for HOOX / PYNE stack sources), .pine, .pinev, .pinev, or .pinescript file — LSP activates when enabled. The VS Code extension also activates on workspaceContains:/.{pyne,pine,…}. Marketplace / VSIX: extension id hoox-sh.pyne (publisher hoox-sh, package name pyne). VS Marketplace. Local package: cd vscode-extension && npm ci && npm run package then code --install-extension pyne-vscode-...vsix. The VSIX is self-contained; you still need pip install "hoox-pyne[lsp]" (or a pyne-lsp binary) on the machine. Settings (from vscode-extension/package.json): | Key | Default | Notes | | --- | --- | --- | | pynescript.lsp.enabled | true | Master switch | | pynescript.lsp.command | auto | auto → pyne-lsp → pynescript-lsp → python -m pynescript.langserver; or an absolute path / docker | | pynescript.lsp.python | python | Interpreter when command is auto and no binary is on PATH | | pynescript.lsp.args | [] | Extra server args | | pynescript.formatting.enabled | true | | | pynescript.diagnostics.enabled | true | | | pynescript.completion.snippets | true | | Default formatter id: hoox-sh.pyne. Neovim (nvim-lspconfig) `lua require("lspconfig").pynescript.setup({}) ` Manual config from clients/neovim.lua: `lua return { cmd = { "pyne-lsp" }, -- alias: pynescript-lsp filetypes = { "pinescript" }, rootdir = function(fname) return vim.fs.root(fname, { ".git", ".pyne", ".pine", ".pinev", ".pinev", "pyproject.toml" }) or vim.fn.getcwd() end, settings = { pinescript = { formatting = { enabled = true }, diagnostics = { enabled = true }, completion = { snippets = true }, }, }, } ` Suggested maps: gd definition, gr references, K hover, format via vim.lsp.buf.format. Ensure filetype detection maps .pyne / .pine → pinescript if your distro does not ship it. Zed Merge clients/zed.json or: `json { "languages": { "Pine Script": { "languageservers": ["pynescript"] } }, "languageservers": { "pynescript": { "command": "pyne-lsp", "arguments": ["--stdio"] } } } ` Emacs (lsp-mode) `elisp (use-package lsp-mode :hook ((pinescript-mode . lsp)) :config (lsp-register-client (make-lsp-client :new-connection (lsp-stdio-connection '("pyne-lsp" "--stdio")) :major-modes '(pinescript-mode) :server-id 'pynescript))) ` Or (load-file "clients/emacs.el") from a checkout. Helix ~/.config/helix/languages.toml: `toml [[language]] name = "pinescript" scope = "source.pinescript" file-types = ["pyne", "pine", "pinev", "pinev"] roots = ["pyproject.toml"] command = "pyne-lsp" args = ["--stdio"] ` Sublime Text (LSP package) `json { "clients": { "pynescript": { "command": ["pyne-lsp", "--stdio"], "selector": "source.pinescript", "initializationOptions": {} } } } ` Generic Any LSP client: `text Command: pyne-lsp alias: pynescript-lsp Args: (none or --stdio depending on client) Transport: stdio ` Internals (repo paths) | Path | Role | | --- | --- | | src/pynescript/langserver/main.py | CLI entry main() | | src/pynescript/langserver/server.py | PynescriptLanguageServer | | src/pynescript/langserver/features/ | diagnostics, completion, hover, … | | clients/README.md | Canonical client matrix | | clients/neovim.lua, zed.json, emacs.el | Drop-in snippets | | vscode-extension/ | VS Code extension | | scripts/generatebuiltinmetadata.py | Completion/hover corpus | Invariants & edge cases Python .+ required on the machine running the server. Server must be on PATH or configured with absolute command. One server per workspace is typical; large monorepos with many .pine files still parse on demand. Formatting uses the unparser pipeline — expect normalization, not clang-format style knobs. Diagnostics reuse linter codes (E, W, …); severity maps to LSP DiagnosticSeverity. Nuitka binary (dist/lsp/pynescript-lsp) may replace the Python entry in packaged distributions — point the editor at that binary if you ship it. Worked examples Verify server starts (stdio smoke) `bash Editors start this; manual smoke is limited. python -c "from pynescript.langserver.server import PynescriptLanguageServer; print(PynescriptLanguageServer)" ` Neovim minimal init.lua fragment `lua vim.api.nvimcreateautocmd("FileType", { pattern = "pinescript", callback = function() vim.lsp.start({ name = "pynescript", cmd = { "pyne-lsp" }, rootdir = vim.fn.getcwd(), }) end, }) ` Force a specific venv binary in VS Code `json { "pynescript.lsp.command": "/home/you/project/.venv/bin/pyne-lsp" } ` Failure modes | Symptom | Fix | | --- | --- | | Client: executable not found | Install [lsp]; fix PATH / command setting | | No module named pygls` | Reinstall extra in the same env as the command | | No diagnostics | Enable diagnostics; confirm filetype; check server logs | | Stale completions | Regenerate metadata in dev builds; restart server | | Format does nothing | Enable formatting; ensure document is valid enough to parse | | High CPU on huge files | Split libraries; wait for parse; check for pathological nesting | See also Installation LSP architecture VS Code extension LSP clients (systems) Troubleshooting --- FILE: docs/pyne/enduser/guides/evaluate-scripts.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Evaluate Scripts" description: "Run Pine expressions and multi-bar scripts with literaleval, pynescript.runtime.Runtime (libraries=, timeoutseconds, mode), data providers, and strategy events." --- Evaluate Scripts Abstract Evaluation is the second half of the PYNE pipeline: given an AST (or source) and market context, produce plots, series, strategy events, and drawings. End users typically choose among three graded tools: . literaleval — pure/builtin expressions, optional series context . AST evaluator (NodeLiteralEvaluator / full evaluator mixins) — script-shaped evaluation in-process . pynescript.runtime.Runtime (package SoT; backend.runtime re-exports) or Pro API POST /run — bar-loop over OHLCV with shared evaluate contract. TypeScript: @hoox-sh/pynets Runtime.run — PyneTS. Defaults differ by surface — modes. This guide stays at the consumer level; series semantics and builtin inventories are under Runtime. Conceptual model | Mode | Bar loop | Strategy | Typical use | | --- | --- | --- | --- | | literaleval | no (single shot) | limited | TA on arrays, math, strings | | Evaluator in tests / tools | optional | yes via state | Unit tests, notebooks | | Runtime.run / /run | yes | yes | Charts, AXIS, backtests | | mode="compile" | yes (numeric + object) | yes (object mode) | Faster bar loop when eligible | | mode="auto" | yes | yes | Prefer compile; fall back to interpret | Runtime modes (interpret | compile | auto) pynescript.runtime.Runtime.run accepts a mode argument that selects how the bar loop executes: | Mode | Behavior | | --- | --- | | interpret | AST walker (full host semantics: input. overrides, request., libraries, profiler). | | compile | Transpile to a Numba numeric kernel when possible, otherwise a pure-Python object-mode bar loop. Requires the compiler package. | | auto | Try compile first; on eligibility failure, compile error, or compiled runtime error, fall back to interpret. | Defaults differ by surface: Direct Runtime.run(..., mode=None) → PYNERUNTIMEMODE env, else interpret. Pro API POST /run body omit mode → schema default auto (warm compile + fallback). ``python from pynescript.runtime import Runtime runtime = Runtime(symbol="AAPL") result = runtime.run( source, ohlcvdata=ohlcv, mode="auto", omit → PYNERUNTIMEMODE else "interpret" timeoutseconds=., interpret wall-clock budget; None = no limit libraries=[ import ns/Name/ver (max on POST /run) {"namespace": "ns", "name": "Lib", "version": , "source": '//@version=\nlibrary("Lib")\nexport x = .\n'}, ], inputs={"len": }, input. by title — forces interpret under auto ) result["mode"] / result.get("autobackend") → "compile" | "interpret" on fallback: result.get("compilefallbackreason") on timeout: timedout + errorkind="runtime" with partial plots ` timeoutseconds is accepted on the library host and on POST /run / POST /run/batch (omit / null / ≤ → no budget). pyne run does not call Runtime.run at all. Warm compile / prewarm Product hosts cache compiled IR (in-process LRU + optional disk under PYNECOMPILECACHEDIR). Pro API defaults mode=auto and can warm Numba builtins once per worker (PYNECOMPILEPREWARM, POST /compile/prewarm, or pyne prewarm [PATH…]). First-hit latency still pays JIT when cold; subsequent runs of the same source reuse IR. See Compiler overview. Related host knobs (interpret path): PYNESERIESCAP / PYNESERIESMAX (history trim) and PYNETAINCREMENTAL (bar-mode TA hot path)—see Series & history and Configuration. From .., interpret also skips unused derived OHLCV (hl / hlc / ohlc / tr) and inlines Assign / plot / Call after bar . Same-machine bench @ bars vs ..: minimal .×, ta.sma .×, multi-plot TA combo .×. Prefer mode="auto" on Pro /run when the script is compile-eligible; interpret remains the full-surface oracle. When compile falls back (or is skipped) mode="auto" (and explicit prefilters) skip or abandon compile for stable reasons. Common compilefallbackreason values: | Reason (examples) | Why | | --- | --- | | input. overrides require interpret path | Non-empty inputs= are interpret-only; compile does not apply overrides. | | import statements not supported in compile path | Library import needs the interpreter registry. | | request. not supported in compile path | Multi-symbol / external data plumbing is interpret-only. | | compiler package unavailable | pynescript.compiler not importable in this install. | | Compile Error: … / Numba required messages | Deterministic transpile/env failures (cached per source for auto). | | Compiled runtime errors | Data-dependent failures still fall back once; not permanently cached as “never compile.” | Other force-interpret paths: profiler=True — line timings need the AST walker; compile/auto are coerced to interpret. mode="compile" (strict) — does not fall back; you get an error payload instead of a silent interpret run. Numba is not required for compile eligibility: strategy / UDT / map-heavy scripts can still run in object-mode compile. Missing Numba only blocks pure-numeric emit. Plot series parity Both backends export the same Runtime contract shape: series (title → per-bar values), plots, plotmeta, events, drawings, plus mode / diagnostics. For charting, shared plot series keys should agree within floating-point tolerance (na/None/NaN treated as NA). First-party hline / fill / bgcolor / plotshape keys match interpret compile (..). Harness --ignore-hline-keys / --ignore-fill-keys stay optional for leftover corpus noise, not first-party fixtures. Deeper compiler notes: Compiler overview. Compare interpret vs compile (corpus harness) From the repo root (monorepo with backend/ + editable pynescript): `bash Smoke: first corpus scripts × synthetic bars python scripts/compareinterpcompile.py --bars --limit Full list from a file, no script limit, workers, s per script; ignore one-sided hline/fill keys when judging value parity python scripts/compareinterpcompile.py --file-list path.txt --limit --workers --timeout-sec --ignore-hline-keys --ignore-fill-keys ` The harness runs each script with mode="interpret" and mode="compile", then nan-aware allclose on common result["series"] keys (rtol=e-, atol=e-). Report: .cache/interpcompileparity.json. Exit when there are no value/NaN mismatches on shared keys (botherrorsame counts as success unless --strict-errors). Interface surface Expression evaluation `python from pynescript.ast.helper import literaleval literaleval(" + ") literaleval("math.max(, , )") literaleval('str.upper("hi")') literaleval("array.size([, , ])") prices = [, , , , , , , , , ] literaleval(f"ta.sma({prices}, )") literaleval(f"ta.rsi({prices}, )") bb = literaleval(f"ta.bb({prices}, , )") middle, upper, lower ` Series history context (Pine-style [] current / [] previous as implemented): `python context = { "close": [, , , , ], "open": [, , , , ], "high": [, , , , ], "low": [, , , , ], } literaleval("close[]", context) literaleval("close[] - close[]", context) ` Optional live/historical wiring: `python literaleval(expr, context, datafeed=feed, dataprovider=provider) ` Script evaluation helper `python from pynescript.ast.evaluator import NodeLiteralEvaluator ev = NodeLiteralEvaluator() result = ev.evaluatescript( """ //@version= indicator("demo") // body depends on what the evaluator implements for statements """ ) ` Libraries: `python ev.registerlibrarysource(namespace="MyNs", name="Lib", version=, source=libsource) mod = ev.lookuplibrary(namespace="MyNs", name="Lib", version=) ` Bar-loop Runtime (package SoT · HTTP contract shape) From .. the bar-loop host lives in the installable package: `python from pynescript.runtime import Runtime SoT (backend.runtime re-exports for Pro API) runtime = Runtime(symbol="AAPL") ohlcv = [ {"time": , "open": , "high": , "low": , "close": ., "volume": }, {"time": , "open": ., "high": , "low": , "close": ., "volume": }, ] result = runtime.run( sourcecode='//@version=\nindicator("t")\nplot(close)\n', ohlcvdata=ohlcv, mode="auto", interpret | compile | auto (see Runtime modes) timeoutseconds=., libraries=[{"namespace": "ns", "name": "Lib", "version": , "source": libsrc}], ) result: plots, series, plotmeta, events, drawings, scriptid, runid, count, ... auto may set autobackend + compilefallbackreason or {"error": "...", "errorkind": "parse|compile|runtime|data|order|mode", ...} ` backend.runtime.Runtime remains a thin compat import for monorepo Pro API code. Dual-host TA goldens (ATR / Supertrend / Keltner) and tests/testtaincremental gate interpretcompile numerical parity. Pro API wraps the same runtime — see Pro API usage. Data providers and feeds Historical CLI/library: `python from pynescript.util.data import getprovider prov = getprovider("mock") or yahoo / alphavantage / ccxt with kwargs bars = prov.fetch("AAPL", "mo", "d") bars: dict with close/open/... lists ` Realtime (requires ccxt / pro): `python examples/realtimedatafeed.py from pynescript.util.datafeed import getdatafeed feed = getdatafeed("ccxtpro", exchange="binance") async with feed: async for candle in feed.watchohlcv("BTC/USDT", "m"): ... ` Educational bar executor examples/executescript.py implements a teaching RSI strategy executor with a custom visitor and pandas history (examples/historicaldata.py / yfinance). It is not the production Runtime, but demonstrates bar iteration patterns. Internals (repo paths) | Path | Role | | --- | --- | | src/pynescript/ast/helper.py | literaleval | | src/pynescript/ast/evaluator/base.py | Context, series, visitor base | | src/pynescript/ast/evaluator/init.py | NodeLiteralEvaluator composition | | src/pynescript/ast/evaluator/builtins/ | ta., strategy., arrays, … | | src/pynescript/ast/evaluator/events.py | Event emission hooks | | src/pynescript/runtime/host.py | Multi-bar Runtime.run SoT (interpret / compile / auto, timeoutseconds) | | src/pynescript/runtime/series.py | PineSeries history model | | backend/runtime.py / backend/series.py | Compat re-exports of package Runtime | | src/pynescript/compiler/ | Transpile + Numba/object bar loops | | src/pynescript/util/data.py | Providers + resolverequestsources | | src/pynescript/util/datafeed.py | Realtime feeds | | scripts/compareinterpcompile.py | Interpret vs compile series parity harness | | tests/testinterpcompileparity.py | Always-on smoke + optional full mark | | examples/evaluateexpressions.py | Expression gallery | | examples/executescript.py | Didactic strategy loop | | examples/rsistrategy.pine | Sample strategy source | Invariants & edge cases Determinism. Same source + same OHLCV + same mode should yield reproducible series (mock providers seedable via options where supported). na / warmup. TA functions need enough bars; early bars may be na/None-like — guard like Pine (not na(x)). mode="compile" uses numeric Numba when the script is pure-numeric; otherwise object-mode compile. Strict compile errors instead of falling back; use auto for production “prefer fast path” behavior (see Runtime modes). mode="auto" may set compilefallbackreason and still return a successful interpret result — check autobackend if you need to know which path ran. Series parity. Shared series keys should match within harness tolerances. First-party hline/fill/bgcolor/plotshape keys match; ignore flags are for leftover corpus noise. History indexing. Confirm []/[] semantics against your evaluator version before porting TV scripts that assume closed-bar rules. Strategy events include run/script ids when stamped by Runtime — useful for HOOX downstream. Body size / bar count. HTTP path caps payload size ( MiB); very long histories should be downsampled or chunked. Worked examples RSI on a synthetic series `python from pynescript.ast.helper import literaleval closes = [float(x) for x in range(, )] print(literaleval(f"ta.rsi({closes}, )")) ` Multi-indicator expressions `python highs = [c + for c in closes] lows = [c - for c in closes] macd, signal, hist = literaleval(f"ta.macd({closes}, , , )") atr = literaleval(f"ta.atr({highs}, {lows}, {closes}, )") ` Full script via Runtime `python from pathlib import Path from pynescript.runtime import Runtime script = Path("examples/rsistrategy.pine").readtext(encoding="utf-") Build ohlcv list from your provider... rt = Runtime(symbol="EXAMPLE") out = rt.run(script, ohlcvdata=ohlcv, mode="interpret") if "error" in out: raise RuntimeError(out["error"]) print(out.get("count"), len(out.get("events", [])), out.get("series", {}).keys()) ` Strategy event inspection `python for ev in out.get("events", []): shape depends on StrategyState / event schema print(ev) ` request. with resolved sources When calling /run or Runtime, pass datasource / providers so request.security and friends resolve; without configuration they may use chart bars or mocks. Failure modes | Symptom | Interpretation | | --- | --- | | NotImplementedError / incomplete builtin | Expression outside supported surface — check missing features | | {"error": "Parse Error: ..."} | Runtime caught parse failure | | {"error": …} with mode="compile" | Strict compile path failed — retry with auto or interpret, or fix unsupported construct | | compilefallbackreason set under auto | Compile skipped/failed; result is from interpret if no top-level error | | EXECUTIONERROR via HTTP | Exception during bar loop | | Empty plots | No plot/plotshape executed; wrong declaration type | | Numerical mismatch vs TV | Warmup, float path, or builtin parity gap — numerical validation | | Interpret vs compile series MISMATCH | Run scripts/compareinterpcompile.py on the script; check report buckets | | timedout + partial plots | timeoutseconds budget exceeded on interpret | | UNKNOWNFIELDS | Extra key not in RUNSCHEMA / RUNBATCHSCHEMA | | DataProviderError` | Provider misconfig | | Async feed errors | Missing ccxt.pro / network | See also Pro API usage Runtime modes Library API Runtime series model Compiler overview Strategy builtins Evaluate contract AXIS — visualizing series HOOX — consuming strategy events on the edge --- FILE: docs/pyne/enduser/guides/library-api.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Library API" description: "Embed pynescript: parse, dump, unparse, walk, transform, and lint Pine Script from Python." --- Library API Abstract The public Python surface for language work lives primarily under pynescript.ast: parse source into an ASDL-backed tree, serialize it (dump), regenerate source (unparse), traverse (walk, visitors), rewrite (NodeTransformer), and lint (lintscript). Expression evaluation (literaleval) and full script execution (pynescript.runtime.Runtime) sit adjacent; this page focuses on structural APIs. Evaluation workflows are covered in Evaluate scripts. Install via pip install hoox-pyne (import pynescript). Conceptual model Design intent mirrors Python’s ast module: stable tree operations, location attributes, and visitor protocols — specialized for Pine’s script model (annotations, series-aware semantics live at evaluation time). Interface surface Imports ``python Re-exported convenience (node classes, helpers, visitors, transformers) from pynescript.ast import parse, unparse, dump, walk from pynescript.ast import NodeVisitor, NodeTransformer from pynescript.ast.helper import literaleval, iterchildnodes, getsourcesegment from pynescript.ast.linter import lintscript, lintfile, PineLinter, LintWarning ` pynescript.ast.init star-imports helper, node, visitor, transformer, and error. parse(source, filename="", mode="exec") -> AST | Arg | Meaning | | --- | --- | | source | Full script or expression text | | filename | Error reporting; absolutized if file exists | | mode | "exec" → script statements; "eval" → single expression | Raises ValueError on bad mode; SyntaxError (via error listener) on grammar failure. Successful trees are cached in a process-local LRU (sha(source) + mode). Disable with PYNEPARSECACHE=. Cached trees are read-only (shared by identity). `python tree = parse('//@version=\nindicator("x")\nplot(close)\n') expr = parse("close > open", mode="eval") ` unparse(node) -> str Round-trips an AST through NodeUnparser. `python normalized = unparse(parse(messysource)) ` dump(node, , annotatefields=True, includeattributes=False, indent=None) -> str indent may be an int (spaces per level) or a string. None is a single line. Human-readable tree serialization (debug, tests, CLI). `python print(dump(tree, indent=, includeattributes=True)) ` walk(node) -> Iterator[AST] Breadth-first (deque) traversal yielding every node. `python from pynescript.ast import walk names = [n for n in walk(tree) if n.class.name == "Name"] ` Location helpers | Function | Role | | --- | --- | | copylocation(new, old) | Copy lineno/col offsets | | fixmissinglocations(node) | Fill missing positions | | incrementlineno(node, n=) | Shift lines | | getsourcesegment(source, node, , padded=False) | Slice original text | | iterfields / iterchildnodes | Schema-aware iteration | Visitors and transformers `python from pynescript.ast import NodeVisitor, NodeTransformer, parse class CallCounter(NodeVisitor): def init(self): self.calls = def visitCall(self, node): self.calls += self.genericvisit(node) class RenameClose(NodeTransformer): def visitName(self, node): Name fields: id (identifier), ctx (Load | Store | …) if node.id == "close": node.id = "price" return node tree = parse(source) CallCounter().visit(tree) tree = RenameClose().visit(tree) ` Linter `python from pynescript.ast.linter import lintscript, LintWarning warnings: list[LintWarning] = lintscript(source, "strat.pine") for w in warnings: print(w.code, w.severity, w.line, w.message) ` | Code family | Examples | | --- | --- | | E | Syntax error (error severity) | | W–W | Missing / old @version | | W–W | Deprecated patterns | | C–C | Naming / style (line length , trailing newline, …) | PineLinter.lint runs: syntax → version → deprecated → naming → style. literaleval (bridge to evaluation) `python from pynescript.ast.helper import literaleval literaleval("ta.sma([,,,,], )") literaleval("close[]", {"close": [., ., .]}) ` Optional datafeed / dataprovider for request. in literal contexts. Internals (repo paths) | Path | Role | | --- | --- | | src/pynescript/ast/helper.py | Public parse/dump/walk/unparse/literaleval | | src/pynescript/ast/builder.py | Parse tree → ASDL nodes | | src/pynescript/ast/unparser.py | NodeUnparser | | src/pynescript/ast/visitor.py | NodeVisitor | | src/pynescript/ast/transformer.py | NodeTransformer | | src/pynescript/ast/node.py | Node re-exports / helpers | | src/pynescript/ast/grammar/asdl/resource/Pinescript.asdl | Algebraic schema | | src/pynescript/ast/linter.py | Static rules | | src/pynescript/ast/error.py | Error types | | examples/parsedumpunparse.py | Round-trip demo | Pipeline inside parse: . InputStream / FileStream . PinescriptLexer + PinescriptParser (error listener) . PinescriptASTBuilder.visit . In exec mode: StatementCollector, comment collection, @ annotation attach Invariants & edge cases Annotation comments (//@version, etc.) attach to script / function / type / assign nodes by kind suffix — see addannotations in helper.py. Deep nesting: temporary sys.setrecursionlimit(max(old, )) during parse. dump requires a true AST node — TypeError otherwise. Transformers mutate or replace nodes depending on your visit methods; prefer returning new nodes if you need purity. Round-trip fidelity is tested against a large corpus but is not a proof of bit-identical source; whitespace and some sugar may normalize. Do not edit generated grammar modules; extend via resource grammars if contributing. Worked examples End-to-end inspect `python from pynescript.ast import parse, dump, unparse, walk source = open("examples/rsistrategy.pine", encoding="utf-").read() tree = parse(source, "examples/rsistrategy.pine") print(dump(tree, indent=)[:], "...") print("---") print(unparse(tree)) print("node count", sum( for in walk(tree))) ` Collect all call names `python from pynescript.ast import parse, NodeVisitor class Calls(NodeVisitor): def init(self): self.names = [] def visitCall(self, node): func = node.func Attribute vs Name depending on ta.rsi vs f() self.names.append(func) self.genericvisit(node) c = Calls() c.visit(parse(source)) ` Lint programmatically with fail semantics `python from pynescript.ast.linter import lintscript def assertclean(path: str) -> None: text = open(path, encoding="utf-").read() issues = lint_script(text, path) errors = [i for i in issues if i.severity == "error"] if errors: raise SystemExit("\n".join(map(str, errors))) ` Expression mode for tooling `python from pynescript.ast import parse, dump print(dump(parse("ta.rsi(close, )", mode="eval"), indent=)) ` Failure modes | Exception / result | Meaning | | --- | --- | | SyntaxError | Lexer/parser rejection | | ValueError: invalid argument mode | mode not in {exec,eval} | | Empty / odd tree | Empty body scripts; annotations-only edge cases | | Unparse mismatch | Node kinds without unparser support — file issue against unparser | | Linter false positives | Style rules are heuristic (regex naming) — treat Cx` as advisory | See also Evaluate scripts CLI guide Helper API (core) Visitor / transformer (core) Linter (core) --- FILE: docs/pyne/enduser/guides/pro-api-usage.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Pro API Usage" description: "Call the PYNE Flask Pro API as a consumer: health, /run, /run/batch, chart preview, indicator preview, quick backtest, and auth." --- Pro API Usage Abstract The Pro API is the HTTP evaluate contract for PYNE: POST Pine source plus OHLCV, receive plots, series, events, drawings, and alerts. Free-tier evaluate endpoints (/run, /run/batch) are usable without a key but are guarded (bar/script caps, IP rate limit, concurrency, chart/mock-only free data, SSRF-safe webhooks). Optional L alert webhooks POST last-bar firings to webhookurl or ALERTWEBHOOKURL. Pro preview/backtest routes track usage via API keys. This page is for callers (AXIS, scripts, workers). Server lifecycle and auth design are under API. Conceptual model | Endpoint | Auth | Purpose | | --- | --- | --- | | GET / / GET /health | none | Health + endpoint map (defaultrunmode: auto) | | POST /run | none (free) | Single script evaluate | | POST /run/batch | none (free) | ≤ scripts, shared OHLCV | | POST /compile/prewarm | none (free) | Warm Numba builtins / script IR | | WS /ws/run | none (free) | AXIS WebSocket evaluate channel | | POST /preview/chart | Pro usage | Chart thumbnail | | POST /preview/indicator | Pro usage | Indicator-style preview | | POST /backtest/quick | Pro usage | Equity / summary | | POST /auth/createkey | admin token | Mint key | | GET /auth/usage | Pro | Usage stats | | POST /auth/validate | body key | Validate key | | POST /lsp/{completion,hover,diagnostics} | none (free) | AXIS HTTP LSP bridge — see LSP | Default local bind: ...:. Interface surface Run the server (local) ``bash pip install -r backend/requirements.txt export APIKEYSTORE="$PWD/.data/apikeys.json" python -m backend.app or: make run ` Health `bash curl -s http://...:/ | python -m json.tool ` POST /run Schema (RUNSCHEMA): | Field | Type | Required | Default | | --- | --- | --- | --- | | script | string | yes | | | data | list (OHLCV bars) | yes | | | symbol | string | no | "CHART" | | datasource | string | no | "" | | dataoptions | object | no | {} | | mode | string | no | "auto" | | inputs | object | no | {} — input. by title (forces interpret under auto) | | libraries | list | no | [] — [{namespace, name, version, source}], max | | timeoutseconds | number | no | omit — interpret wall-clock budget; passed only when set and > | | profiler | bool | no | false — forces interpret | | webhookurl | string | no | "" (else env ALERTWEBHOOKURL) | | forwardalerts | bool | no | true | | alertlastbar | bool | no | true | | alertbatch | bool | no | true | Unknown extra keys → UNKNOWNFIELDS. Query ?mode= overrides only when the body omits mode. Bar objects are expected to carry at least open/high/low/close/time (volume optional depending on script). `bash curl -s -X POST http://...:/run \ -H 'Content-Type: application/json' \ -d '{ "script": "//@version=\nindicator(\"Demo\")\nplot(ta.sma(close, ))", "symbol": "AAPL", "mode": "interpret", "data": [ {"time": , "open": , "high": , "low": , "close": ., "volume": }, {"time": , "open": ., "high": , "low": , "close": ., "volume": }, {"time": , "open": ., "high": ., "low": , "close": , "volume": } ] }' ` Success shape (conceptual): `json { "status": "success", "plots": [], "series": {}, "plotmeta": {}, "events": [], "drawings": [], "alerts": [], "scriptid": "...", "runid": "...", "count": , "mode": "interpret", "datasource": "chart" } ` When a webhook URL is configured, the response may also include alertforward (delivery counts). See Alerts. Error codes include NOSCRIPT, NODATA, DATASOURCEERROR, EXECUTIONERROR, plus schema codes INVALIDBODY, MISSINGFIELD, INVALIDFIELD, UNKNOWNFIELDS. POST /run/batch Shared OHLCV, multiple scripts (max ). Envelope: `json { "scripts": [ {"id": "sma", "script": "//@version=\nindicator(\"s\")\nplot(ta.sma(close, ))"}, {"id": "rsi", "script": "//@version=\nindicator(\"r\")\nplot(ta.rsi(close, ))"} ], "data": [/ bars /], "symbol": "AAPL", "mode": "interpret", "libraries": [] } ` String entries in scripts are accepted and auto-id’d as script, …. Per-script errors do not necessarily fail the whole HTTP status (route returns with per-item status — verify against deployment). POST /preview/chart (Pro) `json { "script": "", "data": {"close": [, , ], "volume": [, , ]}, "options": { "type": "line", "color": "F", "width": , "height": , "showvolume": false } } ` Response includes base PNG chart + meta. Width/height clamped (e.g. max ×). POST /preview/indicator (Pro) `json { "expression": "ta.sma(close, )", "data": {"close": [/ ... /]}, "options": {} } ` POST /backtest/quick (Pro) `json { "script": "//@version=\nstrategy(\"x\")\n...", "data": {}, "initialcapital": ., "mockdata": true, "mockbars": } ` POST /compile/prewarm (free) Warm Numba builtins / optional script list so the first interactive /run with mode=auto|compile skips cold JIT. Soft-fails without Numba (object-mode compile still works). `bash curl -s -X POST http://...:/compile/prewarm \ -H 'Content-Type: application/json' \ -d '{"force": true}' | python -m json.tool ` Optional body: scripts (list of Pine sources to compile-warm), force (re-run builtins). Response includes timing / prewarm stats. Deploy hosts may also set PYNECOMPILEPREWARM for once-per-worker start. See Compiler overview. Auth helpers `bash Mint key (admin) curl -s -X POST http://...:/auth/createkey \ -H "Content-Type: application/json" \ -H "X-Admin-Token: $ADMINTOKEN" \ -d '{"tier":"hobby"}' Validate curl -s -X POST http://...:/auth/validate \ -H "Content-Type: application/json" \ -d '{"apikey":"pyn..."}' ` Send Pro requests with the key as required by middleware (Authorization: Bearer … / documented API-key header — see Auth and keys). There is no pynescript.api.PynescriptAPI client in this package. Call the HTTP contract with requests / curl as above. Internals (repo paths) | Path | Role | | --- | --- | | backend/app.py | Routes /, /run, /run/batch, /compile/prewarm, auth | | backend/api/preview.py | /preview/, /backtest/quick | | backend/middleware/schemas.py | Strict request schemas (mode default auto, alert flags) | | backend/middleware/auth.py | Keys, usage, admin token | | src/pynescript/runtime/host.py | Package Runtime SoT (interpret / compile / auto) | | backend/runtime.py | Compat shim → pynescript.runtime | | backend/alertforwarder.py | L webhook POST batching | | backend/services/chartrenderer.py | PNG rendering | | backend/services/backtest.py | Quick backtest + mock OHLCV | Invariants & edge cases Max body MiB — oversized JSON → Flask . Strict schemas — unknown fields rejected. CORS — browser clients need listed origins; server-to-server without Origin is fine. mode default auto — prefer warm compile; fall back to interpret (autobackend, optional compilefallbackreason). Strict compile errors instead of falling back; use interpret for alerts, input. overrides, libraries, and full request.. libraries (..+) — [{namespace, name, version, source}] binds import ns/Name/ver (max ). registerlibrarysource finalizes exports in ..; auto-mode keeps the list on interpret fallback. Same field on Flask /run, /run/batch, pyne-worker /run, and deployed scripts. timeoutseconds — optional interpret wall-clock budget on /run and /run/batch. Omit / null / ≤ → no timeout. Exceeded runs surface timedout on the HTTP body. Alerts — alerts[] on success (interpret). L webhooks: body webhookurl or env ALERTWEBHOOKURL; defaults last-bar + batch POST. See Alerts. Batch cap — TOOMANYSCRIPTS if exceeded. Free-tier guards (..+) — process-local limits on unauthenticated /run, /run/batch, /compile/prewarm (and WS run): | Env | Default | Role | | --- | ---: | --- | | FREEMAXBARS | | Max OHLCV bars | | FREEMAXSCRIPTCHARS | KiB | Max Pine source length | | FREEMAXCONCURRENT | | Simultaneous free runs per worker | | FREERATELIMIT / FREERATEWINDOWSEC | / s | Sliding-window IP rate limit | | Free datasource | chart / mock / none only | No outbound ccxt/yahoo on free paths | Webhook URLs are SSRF-filtered. Raise limits via env for local demos; put a reverse proxy in front for multi-worker production. AXIS multi-indicator UIs prefer /run/batch to share bar payloads; read series + plotmeta (including fill band refs) for charts. Worked examples Python requests client for /run `python import requests SCRIPT = """ //@version= indicator("SMA", overlay=true) plot(ta.sma(close, )) """ bars = [ {"time": i, "open": + i, "high": + i, "low": + i, "close": . + i, "volume": } for i in range() ] r = requests.post( "http://...:/run", json={"script": SCRIPT, "data": bars, "symbol": "DEMO", "mode": "auto"}, timeout=, ) r.raiseforstatus() payload = r.json() assert payload["status"] == "success", payload print(payload["count"], payload.get("mode"), list(payload.get("series", {}))) optional: payload.get("alerts"), payload.get("alertforward") ` Batch two indicators `python r = requests.post( "http://...:/run/batch", json={ "scripts": [ {"id": "sma", "script": "//@version=\nindicator(\"s\")\nplot(ta.sma(close, ))"}, {"id": "ema", "script": "//@version=\nindicator(\"e\")\nplot(ta.ema(close, ))"}, ], "data": bars, }, timeout=, ) print(r.json()) ` Bind published libraries `python r = requests.post( "http://...:/run", json={ "script": '//@version=\nimport Demo/Lib/ as L\nindicator("t")\nplot(L.x)\n', "data": bars, "mode": "interpret", "libraries": [ { "namespace": "Demo", "name": "Lib", "version": , "source": '//@version=\nlibrary("Lib")\nexport x = .\n', } ], }, timeout=, ) ` Wire optional CCXT data source `json { "script": "...", "data": [/ chart bars /], "symbol": "BTC/USDT", "datasource": "ccxt", "dataoptions": {"exchange": "binance"} } ` Requires server environment with ccxt installed. Failure modes | Code / HTTP | Meaning | Action | | --- | --- | --- | | MISSINGFIELD | No script/data | Fix body | | UNKNOWNFIELDS | Extra keys | Strip undocumented fields | | DATASOURCEERROR | Provider config | Fix dataoptions / deps | | EXECUTIONERROR | Runtime exception | Simplify script; check message | | on createkey | ADMINTOKEN unset/wrong | Export token | | | Body too large | Fewer bars / compress strategy | | CORS browser error | Origin not allowed | Update ALLOWED_ORIGINS | | Empty series | Script never plotted | Add plot / check declaration | See also Evaluate scripts Runtime modes Configuration API contract Auth and keys AXIS — AXIS over /run` results HOOX — edge mesh after strategy events --- FILE: docs/pyne/enduser/guides/troubleshooting.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Troubleshooting" description: "Diagnose pynescript install, parse, lint, evaluation, LSP, data providers, and Pro API failures with concrete checks." --- Troubleshooting Abstract Failures cluster by layer: environment, parse, lint, evaluation, data I/O, LSP, HTTP. Work top-down: confirm the binary/module, then a minimal script, then full strategy complexity. This page is a runbook; deep protocol and runtime docs are linked per section. Conceptual model Interface surface (diagnostic commands) ``bash python -c "import pynescript; from pynescript import about; print(about.version)" python -c "from pynescript.ast import parse, unparse; print(unparse(parse('//@version=\nindicator(\"t\")\nplot(close)\n')))" pyne --version pyne lint - /tmp/a.ast pyne parse-and-unparse a.pine | pyne parse-and-dump /dev/stdin stdin may not work for parse-and-dump (file path required) — use a temp file: pyne parse-and-unparse a.pine --output-file /tmp/a.pine pyne parse-and-dump /tmp/a.pine > /tmp/a.ast diff -u /tmp/a.ast /tmp/a.ast ` Structural diff empty ⇒ cosmetic only. . Lint noise / CI red | Code | Action | | --- | --- | | E | Fix syntax first | | W | Add //@version= | | W | Upgrade version pragma | | C–C | Style; use --fail-on errors if style should not gate CI | `bash pyne lint script.pine --fail-on errors ` . literaleval fails Use mode="eval"-compatible expressions, not full strategy() scripts Pass series as Python lists inside the expression or via context Missing builtin → incomplete implementation Escalate to Runtime.run for multi-bar scripts. . Runtime / /run execution error `bash curl -s -X POST http://...:/run -H 'Content-Type: application/json' -d '{"script":"//@version=\nindicator(\"t\")\nplot(close)","data":[{"time":,"open":,"high":,"low":,"close":,"volume":}]}' ` If minimal works but strategy fails: remove request., drawings, then strategy entries until isolated. Check mode; try "interpret". Library Runtime.run defaults to interpret; POST /run defaults to auto; pyne run is compile-only synthetic smoke — they will not match each other unless you set mode explicitly. See modes. . Schema validation errors UNKNOWNFIELDS means you sent a key not in the schema — remove it. Do not rely on servers ignoring extras. . CORS from AXIS / browser `bash export ALLOWEDORIGINS="http://localhost:,http://...:" ` Restart Flask after changing env. . LSP will not start `bash pip install "hoox-pyne[lsp]" python -c "import pygls, lsprotocol" command -v pyne-lsp alias: pynescript-lsp Run with editor logging enabled; check stderr for import traces ` Ensure the editor’s command matches the venv. For Neovim, confirm filetypes include your buffer’s filetype. . Data provider failures | Provider | Checklist | | --- | --- | | mock | Should always work offline | | yahoo | Network; symbol format | | alphavantage | API key; demo limits | | ccxt | pip install "hoox-pyne[data]"; exchange id; symbol BTC/USDT | `bash pyne data AAPL --provider mock ` . Version / parity confusion PYNE is independent of TradingView. Behavioral gaps are expected for edge builtins, broker emulators, and UI-only calls. Use compatibility and implementation status. Failure modes (quick matrix) | Symptom | Layer | First check | | --- | --- | --- | | Command not found | env | python -m pynescript | | Import error pygls | extra | [lsp] | | Import error ccxt | extra | [data] | | SyntaxError | parse | minimal script + version | | Lint CI fail | lint | --fail-on level | | Empty plots | eval | plot() present; enough bars | | pyne run ≠ Runtime.run | modes | CLI is compile-only synthetic; library default is interpret | | UNKNOWNFIELDS + timeoutseconds | HTTP | field is valid on /run and /run/batch (..+); other extra keys still | | UNKNOWNFIELDS | HTTP | schema | | createkey | HTTP | ADMIN_TOKEN` | | | HTTP | body size | | Squiggles missing | LSP | diagnostics enabled + filetype | | Numerical drift | eval | warmup / parity docs | See also Installation Configuration CLI guide Evaluate scripts Pro API usage Editors FAQ --- FILE: docs/pyne/enduser/index.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "End User Hub" description: "Install PYNE (pip install hoox-pyne), run CLI/library eval with mode=auto, wire editors (.pyne/.pine), and call Pro API alerts + webhooks." --- End User Hub This track is for people who consume PYNE: parse and reformat Pine Script, lint before upload, evaluate expressions or full scripts against OHLCV (mode=auto / interpret / compile), export alerts (optional L webhooks), wire an editor via LSP (.pyne / .pine), or call the HTTP evaluate contract. Contributor internals (grammar regeneration, AST builder, bar-loop semantics) live under Core, Runtime, and DevOps. Abstract PYNE exposes one language pipeline through several thin surfaces: `` .pyne / .pine source → parse (ANTLR + ASDL AST) → optional: dump | unparse | lint | walk/transform → optional: evaluate (literaleval | Runtime mode=auto|interpret|compile) → plots / fill / events / drawings / alerts / metrics → optional L webhooks (Pro API / edge) ` The desk path is the pyne Click CLI (alias pynescript) and pynescript.ast / pynescript.runtime library API (PyPI distribution name hoox-pyne). The TypeScript desk path is PyneTS (bun add @hoox-sh/pynets). The editor path is pyne-lsp (alias pynescript-lsp; stdio; first-class .pyne association). The HTTP path is the Flask Pro API (POST /run with alerts export + webhooks, preview, backtest). The optional chart is AXIS; the optional trade mesh is HOOX. Evaluation does not require either. Defaults for mode differ by surface — modes. Conceptual model | Surface | Entry | Typical job | | --- | --- | --- | | CLI | pyne … (alias pynescript) | One-shot parse, format, lint, compile, run (synthetic bars), prewarm, data | | Library | from pynescript.ast import parse, unparse | Embed parse/transform/eval in tools | | PyneTS | import { parse, Runtime } from "@hoox-sh/pynets" | Same names in TypeScript / Bun | | LSP | pyne-lsp (alias pynescript-lsp) | Diagnostics, completion, hover (.pyne / .pine) | | Pro API | POST /run | Evaluate contract: plots, alerts, optional webhooks | | AXIS | separate product | Chart PWA AXIS over evaluate results | | HOOX | separate product | Edge execution mesh after strategy events | Choose your path . Getting started New install, first parse, and environment knobs: Installation — PyPI pip install hoox-pyne (import pynescript), extras (lsp, compile, data, pro), Hatch checkout, smoke checks. Quick start — Parse → dump → unparse → lint → literaleval in under ten minutes. Configuration — Extras, console scripts, Pro API env vars (ALERTWEBHOOKURL, compile cache), editor settings. . Operational guides Daily workflows against the same pipeline: CLI — check, format, lint, compile, prewarm, run, data, … Library API — parse, unparse, dump, walk, visitors, transformers, linter. Evaluate scripts — Expression eval, bar-loop Runtime (mode=auto), strategy events, alerts export, mock vs live data. Editors — VS Code (.pyne first-class), Neovim, Zed, Emacs, Helix; clients/ snippets. Pro API usage — /run (+ webhookurl / L webhooks), /run/batch, preview, backtest as a consumer. Troubleshooting — Parse failures, recursion limits, missing extras, CORS, auth. . Reference CLI commands — Full option trees for every Click command. Modes — interpret / compile / auto defaults: library interpret, POST /run auto, pyne run compile-only. Glossary — AST, series, bar-loop, Runtime, ASDL, and related terms. FAQ — Version support, TradingView parity, licensing, AXIS/HOOX boundaries. Interface surface (consumer map) | Want | Command / import | Docs | | --- | --- | --- | | Install core | pip install hoox-pyne (import pynescript) | Installation | | Install LSP | pip install "hoox-pyne[lsp]" | Editors | | Install market data deps | pip install "hoox-pyne[data]" | CLI data | | Parse file | pyne parse-and-dump path.pyne | CLI | | Normalize source | pyne parse-and-unparse path.pine | CLI | | Lint | pyne lint path.pine | CLI | | Library parse | from pynescript.ast import parse, unparse | Library API | | Expression eval | from pynescript.ast.helper import literaleval | Evaluate | | Bar-loop eval | from pynescript.runtime import Runtime (mode default interpret) | Evaluate · modes | | Compile-only smoke | pyne run script.pine --bars | CLI | | Warm Numba / IR | pyne prewarm / POST /compile/prewarm | CLI | | Alerts + webhooks | /run → alerts[] / webhookurl | Alerts | | Start LSP | pyne-lsp (stdio) | Editors | | Local Pro API | make run / python -m backend.app | Pro API usage | Preferred console scripts (aliases in parentheses) are registered in pyproject.toml: | Script | Module | Role | | --- | --- | --- | | pyne (pynescript) | pynescript.main:cli | Desk CLI (Click group) | | pyne-lsp (pynescript-lsp) | pynescript.langserver.main:main | Language server (pygls) | Do not conflate them: lint and parse live on pyne; editor features live on pyne-lsp. Internals (repo paths) Consumer-relevant code only — deeper design is linked from each guide: | Path | Role | | --- | --- | | src/pynescript/main.py | Click CLI: check, format, lint, compile, prewarm, run, data, info | | src/pynescript/ast/helper.py | Public parse, unparse, dump, walk, literaleval | | src/pynescript/ast/linter.py | lintscript / PineLinter | | src/pynescript/ast/evaluator/ | Bar-aware and literal evaluators | | src/pynescript/runtime/ | Package Runtime SoT (bar-loop host, series, CustomEvaluator) | | src/pynescript/langserver/ | LSP features and server | | src/pynescript/util/data.py | Historical data providers (mock, yahoo, alphavantage, ccxt) | | backend/app.py | Flask Pro API entry (/run, auth, blueprints) | | backend/runtime.py | Compat shim → pynescript.runtime | | examples/ | Worked scripts (parse, RSI strategy, datafeed) | | clients/ | Editor client configs (Neovim, Zed, Emacs) | | vscode-extension/ | VS Code extension package | Invariants & edge cases Round-trip is first-class. parse → unparse should preserve semantics; use it to normalize formatting, not as a semantic rewrite. Mode split. parse(..., mode="exec") yields a script tree; mode="eval" parses a single expression (used by literaleval). No proprietary host required. Evaluation works offline with mock or supplied OHLCV; AXIS/HOOX are optional. Extras are opt-in. Core parse/lint needs only base deps (antlr-python-runtime, click, …). LSP needs [lsp]; CCXT paths need [data] / [datafeed]. Optional example dir. pinescriptfilepath only expands when --example-scripts-dir points at local .pine files; no third-party corpus is shipped. Worked examples Minimal library round-trip: `python from pynescript.ast import parse, unparse source = """ //@version= indicator("My RSI") plot(ta.rsi(close, )) """ tree = parse(source) print(unparse(tree)) ` Minimal CLI: `bash pip install hoox-pyne pyne parse-and-dump examples/rsistrategy.pine pyne lint examples/rsistrategy.pine ` Minimal HTTP evaluate (local server running): `bash curl -s http://...:/ \ | python -m json.tool ` Failure modes | Symptom | Likely cause | Where to go | | --- | --- | --- | | pyne: command not found | Package not on PATH / venv inactive | Installation | | pyne-lsp missing | Installed without [lsp] extra | Editors | | SyntaxError on parse | Grammar mismatch or truncated source | Troubleshooting | | /run NO_DATA` | Missing OHLCV list | Pro API usage | | Numerical drift vs TradingView | Builtin / series edge case | Compatibility | See also PYNE product map — full stack overview Installation Quick start Runtime modes Evaluate contract (API) AXIS docs — optional chart HOOX docs — optional edge trade mesh --- FILE: docs/pyne/enduser/reference/cli-commands.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "CLI Commands Reference" description: "Complete option reference for the pyne Click CLI: check, format, lint, compile, run, prewarm, data, info, and more." --- CLI Commands Reference Abstract Machine-oriented reference for every command on the preferred pyne Click group (alias pynescript; src/pynescript/main.py). Install via hoox-pyne (pip install hoox-pyne). For workflows see the CLI guide. The language server is a separate console script (pyne-lsp; alias pynescript-lsp). Version option: pyne --version / -V reports pynescript.about.version (e.g. ..). Global --no-color honors NOCOLOR. Command index | Command | Aliases | Purpose | | --- | --- | --- | | check | | Parse-only validation (CI-friendly exit codes) | | format | fmt | Parse → unparse (structural format) | | parse-and-dump | dump, ast | AST dump | | parse-and-unparse | unparse | Round-trip source | | lint | | Static diagnostics (JSON / fail-on) | | compile | | Transpile or compile-check (Numba path) | | prewarm | | Warm Numba builtins / script IR caches (H) | | run | | Compile + execute on synthetic OHLCV | | data | | Fetch market bars | | info | ls | Version + optional extras | ``bash pyne --help pyne --version pyne -h alias: pynescript … ` --- pyne check Validate that path(s) parse as Pine Script. | Kind | Name | Default | Description | | --- | --- | --- | --- | | argument | PATHS… | (optional) | Files and/or directories; omit or - → stdin | | option | --encoding | utf- | Text encoding | | option | -q, --quiet | off | Exit code only | | option | --ext | .pine | When PATH is a directory, only this suffix | `bash pyne check script.pine pyne check scripts/ --ext .pyne pyne check -q strategy.pine exit / only cat x.pine | pyne check - ` Exit: all ok; any parse failure. --- pyne format (fmt) Canonicalize Pine by parse → unparse (structural, not a full style formatter). | Kind | Name | Default | Description | | --- | --- | --- | --- | | argument | PATH | required | File or - (stdin) | | option | --encoding | utf- | | | option | -w, --write | off | Write back to PATH (not for stdin) | | option | --check | off | Exit if format would change (no write) | | option | -o, --output-file | - | Output when not using --write | `bash pyne format script.pine stdout pyne fmt script.pine -w pyne format script.pine --check CI drift gate ` --- pyne parse-and-dump (dump / ast) | Kind | Name | Default | Description | | --- | --- | --- | --- | | argument | PATH | required | Input file | | option | --encoding | utf- | | | option | --indent | | Dump indent | | option | -o, --output-file | - | Output path | `bash pyne dump strategy.pine pyne parse-and-dump strategy.pine --indent -o tree.txt ` --- pyne parse-and-unparse (unparse) | Kind | Name | Default | Description | | --- | --- | --- | --- | | argument | PATH | required | Input file | | option | --encoding | utf- | | | option | -o, --output-file | - | Output path | `bash pyne unparse messy.pine -o clean.pine ` --- pyne lint | Kind | Name | Default | Description | | --- | --- | --- | --- | | argument | PATH | optional | File; omit or - → stdin | | option | --encoding | utf- | | | option | --fail-on | errors | errors \| warnings \| all \| never | | option | --json | off | JSON array of findings | | option | -q, --quiet | off | Summary only | `bash pyne lint strategy.pine pyne lint --json --fail-on never strategy.pine pyne lint --fail-on warnings strategy.pine cat strategy.pine | pyne lint - ` --- pyne compile Compile via pynescript.compiler.compilescript. Requires pip install "hoox-pyne[compile]" for full Numba path; --emit works without JIT. | Kind | Name | Default | Description | | --- | --- | --- | --- | | argument | PATH | required | Pine file | | option | --encoding | utf- | | | option | --emit | off | Print generated Python only (no exec/JIT) | | option | -o, --output-file | - | Where to write --emit output | | option | --time / --no-time | time on | Print compile timing | `bash pyne compile script.pine --emit pyne compile script.pine --emit -o out.py pyne compile script.pine warm load / compile-check ` --- pyne prewarm H warm-compile product path: pay cold JIT before first interactive run. | Kind | Name | Default | Description | | --- | --- | --- | --- | | argument | PATH… | optional | Pine files to compile into IR caches | | option | --encoding | utf- | | | option | --force | off | Re-run builtin warm-up | | option | --json | off | Machine-readable summary | `bash pyne prewarm shared Numba kernels only pyne prewarm strategy.pine lib/.pine pyne prewarm --json ` --- pyne run Compile and execute on deterministic synthetic OHLCV (same compile pipeline as Pro API mode=compile). No --mode. Does not call Runtime.run. Prefer package pynescript.runtime.Runtime / Pro API for real bars, libraries=, and timeoutseconds. | Kind | Name | Default | Description | | --- | --- | --- | --- | | argument | PATH | required | Pine file | | option | --encoding | utf- | | | option | --bars | | Synthetic bar count | | option | --json | off | Plot series summary as JSON | | option | -q, --quiet | off | Errors / exit only | `bash pyne run strategy.pine --bars pyne run strategy.pine --json ` --- pyne data | Kind | Name | Default | Description | | --- | --- | --- | --- | | argument | SYMBOL | required | Ticker / market symbol | | option | --provider | mock | mock \| yahoo \| alphavantage \| ccxt | | option | --period | y | e.g. mo, y | | option | --interval | d | e.g. h, d | | option | --api-key / --secret | empty | Provider credentials | | option | --exchange | binance | CCXT exchange id | | option | --format | table | table \| json \| csv | | option | -o, --output-file | - | Output path | `bash pyne data AAPL pyne data AAPL --provider yahoo --period mo pyne data BTC/USDT --provider ccxt --exchange binance --format csv ` CCXT needs pip install "hoox-pyne[data]". --- pyne info (ls) | Kind | Name | Description | | --- | --- | --- | | option | --json | Machine-readable payload | Prints package version, Python, platform, and whether numba / rich are available. --- Related: pyne-lsp (not a subcommand) `bash pip install "hoox-pyne[lsp]" pyne-lsp alias: pynescript-lsp python -m pynescript.langserver ` Internals | Item | Path | | --- | --- | | CLI group | src/pynescript/main.py | | Entry | pyproject.toml → pyne / pynescript = pynescript.main:cli | | parse/dump/unparse | src/pynescript/ast/helper.py | | lint | src/pynescript/ast/linter.py | | compile / prewarm | src/pynescript/compiler/ | | data | src/pynescript/util/data.py | Invariants Aliases: dump/ast → parse-and-dump; unparse → parse-and-unparse; fmt → format; ls → info. Prefix match works when unique (pynescript ch → check). check walks directories; dump/format take a single file. Docker: pynescript-cli image ENTRYPOINT is pynescript (non-root). Standalone binaries: GitHub Release pynescript-cli- (Nuitka). Failure modes | Situation | Result | | --- | --- | | Parse failure (check / dump) | Non-zero exit | | Format --check drift | Exit | | Lint threshold | Non-zero per --fail-on | | Compile without numba (full path) | Clear error; --emit still works | | Provider failure | Click error with message | | Missing ccxt for data | Import/provider error → install [data]` | See also CLI guide Runtime modes Installation Compiler overview Docker --- FILE: docs/pyne/enduser/reference/faq.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "FAQ" description: "Frequently asked questions about pynescript install, Pine version support, TradingView parity, CLI vs library, LSP, Pro API, licensing, and ecosystem boundaries." --- FAQ Abstract Short answers to questions that recur when adopting PYNE as a consumer. Where behavior is evolving, answers point at status docs rather than overclaiming parity. Conceptual model Questions group into: install & package shape, language coverage, evaluation fidelity, tooling surfaces, and product boundaries (AXIS / HOOX / workers). Interface surface No dedicated FAQ API — pointers only. Internals (repo paths) Answers cite: pyproject.toml — version, extras, license src/pynescript/main.py — CLI reality src/pynescript/runtime/host.py — Runtime.run docs/missingfeatures.md / reference status pages — coverage backend/app.py — HTTP contract Invariants & edge cases Prefer reading code + status docs over README marketing tables when they disagree; mark drift with issues. Trademark disclaimer lives on the product index once — not repeated here in full. Questions What is PYNE vs pynescript vs pyne? PYNE — product documentation brand for the open evaluate stack (grammar → AST → runtime → LSP → Pro API → workers). hoox-pyne — PyPI distribution name (live, ..+). Install with pip install hoox-pyne. Do not install plain pyne/PyNE from PyPI without the hoox- prefix (unrelated) or the upstream pynescript package (elbakramer). pynescript — import package (import pynescript, including pynescript.runtime). pyne / pyne-lsp — preferred console scripts; pynescript / pynescript-lsp remain aliases. What Python versions are supported? requires-python = ">=.". Classifiers list .–.. CI exercises that matrix; pin to .+ for production. How do I install just the parser? ``bash pip install hoox-pyne ` No extra required for parse / unparse / lint / basic literaleval. How do I install the LSP? `bash pip install "hoox-pyne[lsp]" ` Run pyne-lsp (alias pynescript-lsp) — not a Click subcommand of pyne. Why is pynescript lsp missing? The registered entry point is the separate script pynescript-lsp. Some docs historically mentioned a lsp subcommand; trust pyproject.toml [project.scripts] and main.py for ground truth. Which Pine versions are supported? v is the recommended surface (declare //@version=); v remains fully supported. See implementation status, missing features, and pine v surface. Is this affiliated with TradingView? No. Independent open-source toolchain. Pine Script and TradingView are trademarks of TradingView, Inc. Will my TradingView strategy produce identical PnL? Not guaranteed. Broker emulator rules, fill models, request. data, and some builtins differ. Use PYNE for offline analysis, CI, and self-hosted evaluate; validate critical strategies numerically (numerical validation). Can I format Pine like Black? parse-and-unparse / LSP format normalize via the AST unparser. There is no rich style config comparable to Black’s profile system. Does the CLI evaluate strategies? pyne run script.pine --bars compiles and executes on synthetic OHLCV. It is not Runtime.run (no real bars, no mode/libraries/timeoutseconds/inputs). For real OHLCV use: from pynescript.runtime import Runtime — library default mode is interpret POST /run — schema default mode is auto examples/executescript.py as a didactic sample (not the production host) See modes. How do I lint in CI? `bash pynescript lint --fail-on errors path/to/script.pine ` Loop files yourself; the CLI is single-file / stdin. What lint rules exist? Syntax (E), version (W–W), deprecated patterns (W–), style/naming (C–C). See src/pynescript/ast/linter.py and the library API guide. How do I get market data? `bash pynescript data AAPL --provider yahoo pip install "hoox-pyne[data]" for ccxt ` Or providers in Python via pynescript.util.data.getprovider. What is the Pro API free tier? POST /run and POST /run/batch are exposed as free evaluate endpoints (still your CPU if self-hosted). Default body mode is auto (prefer warm compile; fall back to interpret). Responses include alerts[] when scripts call alert() / true alertcondition() (interpret path). Optional L webhooks: body webhookurl or env ALERTWEBHOOKURL. Preview/backtest routes are Pro-tier with API keys. How do interpret and compile compare? Use the dual-host harness (repo root): `bash python scripts/compareinterpcompile.py --bars --limit ` See Interpret compile parity. Prefer mode=auto in production; force interpret when you need alerts, input. overrides, or full request.. What file extension should I use? Prefer .pyne for HOOX / PYNE stack sources. .pine / .pinev / .pinev remain supported by the VS Code extension and parsers. How complete is real-world script runtime? PYNE aims for practical fidelity on first-party fixtures and unit tests. It does not claim % TradingView platform parity (chart host, proprietary data, every edge-case builtin). See Compatibility and implementation status. Third-party script corpora are not shipped in this repository. How do I self-host the API? Install backend requirements, set APIKEYSTORE / ALLOWEDORIGINS / optional ADMINTOKEN, run python -m backend.app or make run. See configuration and Pro API usage. What is AXIS? Optional charting PWA that can call the evaluate contract and render series. Docs: AXIS. PYNE evaluates without AXIS. What is HOOX? Optional edge trading mesh (trade-worker, etc.). Docs: HOOX. Strategy events from PYNE can feed HOOX; not required for parsing/eval. Alerts: alert() / alertcondition() firings export on Pro API and pyne-worker /run. Optional HTTP webhooks via webhookurl or ALERTWEBHOOKURL (last-bar by default). See Alerts. What is PyneTS vs pyne-worker vs pyne-agent-worker? PyneTS (@hoox-sh/pynets) — TypeScript / Bun library + CLI. parse / unparse / Runtime.run. Not a Worker. Python is the oracle. pyne-worker — Python Cloudflare Worker. Production POST /run. Vendors pynescript.runtime. pyne-agent-worker — NL authoring (POST /v/chat). Standalone chat; optional generate → /run validate. The name pine-worker is a leftover TypeScript experiment (hoox-sh/pine-worker). It is not colocated here and is not a product docs target. See the ecosystem map and modes. What is the default mode? It depends on the surface — this is the usual footgun: | Surface | Default | | --- | --- | | pynescript.runtime.Runtime.run(..., mode=None) | PYNERUNTIMEMODE else interpret | | POST /run (body omits mode) | auto | | pyne run | compile-only (not Runtime.run, no --mode) | See modes. Is there Runtime.evaluate? No. The host API is Runtime.run. Python CLI pyne run is compile-only smoke and is not Runtime.run. PyneTS pynets run does call Runtime.run. What license is pynescript? AGPL-.-or-later per pyproject.toml. Network use of modified versions requires offering corresponding source (AGPL §). Proprietary embedding usually needs a commercial license; consult your counsel. Can I use this commercially? AGPL permits commercial use under its terms, but distribution or network service of modified versions requires source availability. Hosted Pro API terms (if any) are separate from the library license. Why is parse slow on huge files? ANTLR parse + AST build scale with file size and nesting. Deep expression nests also stress recursion (limit temporarily raised to ≥). Split libraries when possible. Round-trip changed my spacing — is that a bug? Usually no. Unparser emits a normalized style. File a bug if semantics change (identifiers, call structure, annotations lost). Where is the builtin list for autocomplete? Generated metadata used by the LSP (scripts/generatebuiltinmetadata.py). Do not hand-edit encrypted release blobs. How do I report a grammar bug? Minimal .pine repro + expected TV behavior + PYNE version. Prefer failing pytest if contributing. Is Jupyter supported? Yes — from pynescript.ext.jupyter import loadipythonextension. The %%pinescript cell magic lints, parses, and unparses; it is not a bar-loop Runtime. You can also call parse / literal_eval / Runtime.run directly in notebooks. Can I transform scripts programmatically? Yes — NodeVisitor / NodeTransformer on the AST, then unparse. See library API. Worked examples “Is my install healthy?” `bash python -c "from pynescript.ast import parse, unparse; print(unparse(parse('//@version=\nindicator(\"t\")\nplot(close)\n')))" pynescript lint - <<< $'//@version=\nindicator("t")\nplot(close)\n' ` “Does HTTP evaluate work?” `bash curl -s http://...:/ | python -m json.tool ` “Is LSP importable?” `bash python -c "import pygls; from pynescript.langserver.server import PynescriptLanguageServer" ` Failure modes If an FAQ answer appears wrong against your checkout: . Check package version (pynescript --version). . Read the cited source file. . Prefer main.py / pyproject.toml` over secondary docs. . Open an issue or PR against the MDX page. See also End User hub Installation Troubleshooting Glossary Compatibility CLI commands --- FILE: docs/pyne/enduser/reference/glossary.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Glossary" description: "Definitions of PYNE end-user terms: AST, ASDL, bar-loop, series, Runtime, LSP, Pro API, extras, and related concepts." --- Glossary Abstract Shared vocabulary for the End User track. Terms are defined as used in this repo, not as MarketingView marketing copy. Cross-links point at deeper systems manuals where useful. Conceptual model Terms fall into four buckets: . Language representation — grammar, AST, unparser . Execution — series, bar-loop, builtins, strategy events . Surfaces — CLI, library, LSP, Pro API . Ecosystem — AXIS, HOOX, PyneTS, pyne-worker, pyne-agent-worker Interface surface This page is pure reference — no commands required. Internals (repo paths) | Term cluster | Primary paths | | --- | --- | | Parse / AST | src/pynescript/ast/helper.py, builder.py, grammar/ | | Evaluate | src/pynescript/ast/evaluator/, src/pynescript/runtime/ (backend/runtime.py is a compat shim) | | Lint | src/pynescript/ast/linter.py | | LSP | src/pynescript/langserver/ | | HTTP | backend/app.py | Invariants & edge cases Pine Script / TradingView are trademarks of TradingView, Inc. PYNE is independent (see product index). “Parity” is empirical (tests/corpus), not a legal claim of equivalence. Terms ASDL Abstract Syntax Description Language. Schema language used to generate Python AST node classes (Pinescript.asdl → generated node module). Gives a single algebraic description of script structure. Annotation Special comments such as //@version= (preferred) attached to script or declaration nodes during parse (addannotations in helper.py). AST Abstract Syntax Tree. In-memory tree of ASDL-generated nodes produced by parse. Root is typically a Script (mode="exec") or Expression (mode="eval"). AXIS Optional charting PWA “AXIS” product (frontend/, docs at /axis/docs). Consumes evaluate results (series/plots); not required for evaluation. Bar / bar-loop A discrete OHLCV time step. A bar-loop evaluator re-executes script logic once per bar with updated series history (pynescript.runtime.Runtime.run). Builtin Host-provided namespace function or value (ta.rsi, strategy.entry, math.sqrt, …). Implemented under ast/evaluator/builtins/ and catalogued for LSP metadata. CLI The preferred pyne Click command group (alias pynescript: check, format, lint, compile, prewarm, run, data, info). pyne run is compile-only synthetic smoke — not Runtime.run. Compile mode mode="compile" on Runtime / /run: attempts a Numba (or similar) compiled subset of bar logic for speed. Not full language coverage. Data provider Historical OHLCV source (mock, yahoo, alphavantage, ccxt) via pynescript.util.data. Data feed Realtime stream abstraction (datafeed, e.g. CCXT Pro) for live candles/trades. Dump dump(node) — pretty textual representation of an AST for debugging and tests (not executable Pine). Evaluate / evaluator Execution of AST nodes to values. Ranges from literaleval (expression-safe) to full statement/builtin evaluators and package pynescript.runtime.Runtime (Pro API / workers share the same host). Extra (package extra) Optional dependency set in pyproject.toml: lsp, dev-lsp, compile, data, datafeed, pro. HOOX Edge trade execution mesh (separate monorepo / docs under /docs). Can consume strategy events from evaluate pipelines; optional. Interpret mode Default Runtime mode: AST walking per bar without the compiled subset path. libraries= Optional Runtime.run / POST /run list of {namespace, name, version, source} dicts registered before import ns/Name/ver resolves. Compile-ineligible. HTTP cap is . Not available on pyne run. literaleval Helper that parses an expression (or accepts an AST) and evaluates literals plus supported builtins/series context — analogous in spirit to Python’s ast.literaleval but Pine-aware and broader. Lint / LintWarning Static diagnostics with code, message, line, severity (error|warning). Entry: lintscript. LSP Language Server Protocol. pyne-lsp process (alias pynescript-lsp) implementing diagnostics, completion, hover, navigation, formatting. NodeVisitor / NodeTransformer Patterns for traversing or rewriting ASTs (visitor.py, transformer.py). OHLCV Open, high, low, close, volume — bar fields passed to Runtime / /run (plus time). PyneTS TypeScript / Bun library (@hoox-sh/pynets). Public names match Python: parse, unparse, Runtime.run. Not a Worker and not pyne-worker. See PyneTS. pyne-agent-worker Sister Cloudflare Worker that turns natural language into Pine source (POST /v/chat). Standalone by default; optional validate via pyne-worker. See pyne-agent-worker. pyne-worker Python Cloudflare Worker (sister repo) embedding pynescript for edge POST /run. See pyne-worker. HOOX mesh notes: isolate profile. Plot / series / plotmeta Evaluate outputs: plotted values, named multi-series maps, and metadata for AXIS UIs (AXIS). Pro API Flask application in backend/ exposing /run, preview, backtest, and auth endpoints. PYNE Product name for this evaluation stack (docs under /pyne/docs). PyPI distribution is hoox-pyne; the import package is pynescript. Round-trip parse → unparse (optionally → parse again). Used to normalize formatting and test fidelity. Runtime pynescript.runtime.Runtime — multi-bar execution engine (src/pynescript/runtime/host.py). backend.runtime re-exports the same class for the Pro API. Library default mode is interpret; POST /run defaults to auto. See modes. Series Time-indexed value stream with history access (close[]). Implementation includes PineSeries and evaluator series objects. Strategy event Structured record of entries/exits/orders emitted during strategy evaluation for downstream systems (HOOX, analytics). timeoutseconds Optional Runtime.run wall-clock budget (interpret path). Also a POST /run / /run/batch body field (..+). Exceeded runs return partial plots with timedout. Omit / null / ≤ → no timeout. Unparse unparse(node) — regenerate Pine source text from an AST. //@version Version pragma annotation; linter expects v+ for modern scripts. Warmup Bars required before a TA function has enough history; early outputs may be na. Worked examples Using terms together: ``text Source --parse--> AST --unparse--> normalized source AST + OHLCV --Runtime bar-loop--> series + strategy events AST --lintscript--> LintWarnings AST --LSP--> editor diagnostics/completion series --AXIS--> chart events --HOOX--> exchange orders `` Failure modes Misusing terms often causes support confusion: | Confused pair | Distinction | | --- | --- | | dump vs unparse | dump is debug AST text; unparse is Pine source | | literaleval vs Runtime | single-shot expressions vs multi-bar scripts | | pyne vs pyne-lsp | desk CLI vs language server (aliases: pynescript) | | AXIS vs PYNE | AXIS vs evaluate engine | | data provider vs data feed | historical fetch vs realtime stream | See also End User hub FAQ PYNE product map AXIS docs HOOX docs --- FILE: docs/pyne/enduser/reference/modes.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Runtime modes" description: "interpret / compile / auto defaults differ by surface. Library interpret, POST /run auto, pyne run compile-only. PyneTS .. has interpret + JS compile." --- Runtime modes Abstract mode selects how the bar loop executes: interpret (AST walk), compile (transpiled loop), or auto (try compile, fall back to interpret). The default is not the same on every surface. This is the most common footgun when moving a script from the library to the Pro API or between Python and PyneTS. There is no Runtime.evaluate. Conceptual model Interface surface What each mode means | Mode | Python | PyneTS (this checkout) | | --- | --- | --- | | interpret | AST walker. Full host: input., request., libraries, profiler, alerts | AST walker only (pynets/src/runtime/interpret.ts). Result mode is always "interpret" | | compile | Numba nopython or object-mode Python loop | Not implemented — no src/runtime/compile/ in this tree | | auto | Try compile; on ineligibility / compile error / compiled runtime error → interpret | Not implemented | Defaults by surface | Surface | Default mode | Calls Runtime.run? | | --- | --- | --- | | pynescript.runtime.Runtime.run(..., mode=None) | PYNERUNTIMEMODE else interpret | yes | | Pro API POST /run body omits mode | schema auto | yes (host wrap) | | ?mode= query (legacy) | overrides body | yes | | CLI pyne run | compile only — not Runtime.run | no | | new Runtime() / pynets run | interpret only (no --mode; --bars default ) | yes | | AXIS engine server | whatever you pass; UI default often auto | HTTP /run | | AXIS engine pyodide | object-mode in Wasm (no Numba) | in-browser Python | Runtime.run (Python, exact) ``python Runtime(symbol="AAPL").run( sourcecode, ohlcvdata, datafeed=None, dataprovider=None, mode=None, env PYNERUNTIMEMODE else "interpret" inputs=None, profiler=False, timeoutseconds=None, libraries=None, realtimelastbar=False, realtimeticks=, realtimebars=, realtimefrombar=None, ) ` | Argument | Surfaces | Effect on mode | | --- | --- | --- | | mode | library / POST /run | See defaults table. pyne run has no --mode. | | inputs | library / POST /run | Non-empty overrides force interpret (compile does not apply them). | | libraries | library / POST /run / POST /run/batch (max ) | [{namespace, name, version, source}] for import ns/Name/ver. Compile-ineligible; auto keeps the list on interpret fallback. Not on pyne run. | | timeoutseconds | library / POST /run / POST /run/batch | Interpret wall-clock budget. Exceeded → partial plots + timedout. Omit / null / ≤ → no timeout. Not on pyne run. | | profiler | library / POST /run | Forces interpret (line timings). | | realtime | library only | Interpret forming-bar window (realtimelastbar, realtimeticks, realtimebars, realtimefrombar). | inputs / profiler / libraries / timeoutseconds / realtime force or keep the interpret host (or force interpret after compile miss). Runtime.run (PyneTS) `ts new Runtime(symbol, { inputs, broker, timeframe }) .run(source, ohlcv, extra?) ` There is no constructor/extra.mode and no libraries option on this host. RuntimeResult.mode is always "interpret". Internals | Surface | Implementation | | --- | --- | | Python library | src/pynescript/runtime/host.py | | Python CLI run | src/pynescript/main.py → compilescript + synthetic bars | | Pro API | backend/app.py RUNSCHEMA default auto | | PyneTS | pynets/ v.. — interpret + JS compile (mode interpret / compile / auto). Python remains the oracle | | PyneTS CLI | pynets/src/cli.ts → new Runtime("AAPL", { broker }).run | Invariants & edge cases . Do not document pyne run as mode=auto. It is compile-only smoke (--bars default ). . Do not document pynets run as Python pyne run. PyneTS Runtime.run defaults to interpret; .. also has JS compile / auto. Default --bars is (Python pyne run defaults to ). . result.mode is what executed. After auto, also read autobackend and compilefallbackreason. . Foreign request.security is na on every host. Compile does not invent bars to become "more complete". . AXIS Pyodide compile / auto cannot be Numba (Wasm). Worked examples Force interpret everywhere you care about input. + libraries + alerts: `python Runtime(symbol="AAPL").run(src, bars, mode="interpret", inputs={"len": }) ` `ts new Runtime("AAPL", { inputs: { len: } }).run(src, bars); ` `bash Python desk smoke (compile only) pyne run script.pine --bars PyneTS desk smoke (Runtime.run; default interpret, --bars ) pynets run script.pine --bars ` Failure modes | Symptom | Cause | Fix | | --- | --- | --- | | Pro API "misses" an input | auto compiled a path that drops overrides | Pass mode: "interpret" | | pyne run ≠ notebook Runtime.run | Different host | Use the library or Pro API | | Expect PyneTS ≡ Python compile | JS emit is object-mode analog, not Numba | Treat Python Runtime as the oracle | | mode omitted, two hosts disagree | Different defaults | Set mode` explicitly | See also Evaluate scripts Python compiler PyneTS compile Configuration Evaluate contract --- FILE: docs/pyne/index.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "PYNE Documentation" description: "Open-source Pine Script evaluation — grammar, AST, interpret/compile runtime, alerts + webhooks, LSP, Pro API. pip install hoox-pyne · github.com/hoox-sh/pyne" --- import { PackageVersions } from "/snippets/package-versions.jsx" PYNE Documentation PYNE is the open evaluation stack for TradingView Pine Script: a formal ANTLR grammar, an ASDL algebraic AST, a deterministic bar-loop evaluator (interpret / compile / mode=auto), a Language Server Protocol surface, a Flask Pro API, and edge workers that share one evaluate contract — including alerts and optional L webhooks. For natural-language authoring (PYNE Agent), see the sister project pyne-agent-worker — standalone Cloudflare Workers AI agent with optional AXIS plugin; does not require pyne-worker. Pine Script and TradingView are trademarks of TradingView, Inc. This project is independent and not affiliated with or endorsed by TradingView, Inc. Cloudflare is a trademark of Cloudflare, Inc._ Abstract Where closed hosts couple the language to a proprietary charting host, PYNE treats the language as an inspectable pipeline: `` Source (.pyne / .pine) → ANTLR lexer/parser (resource grammar) → ASDL AST (builder) → bar-loop (interpret | compile | auto) → plots / fill / events / drawings / alerts → optional L webhooks (Pro API + edge) ` Live on PyPI as hoox-pyne (import pynescript). Source: github.com/hoox-sh/pyne. `bash pip install "hoox-pyne[lsp]" pyne --version aliases: pynescript / pynescript-lsp ` The same pipeline powers the desk CLI, the LSP binary, the Pro API, browser Pyodide (via AXIS), and Cloudflare workers. Round-trip fidelity (parse → unparse) and interpretcompile plot parity are first-class invariants — see compatibility and implementation status. Local open-corpus set– (--): parse .%, Runtime interpret % excl. intentional demos (set /). The repository does not ship third-party script corpora. TypeScript consumers import PyneTS (@hoox-sh/pynets) — same public names; Python remains the oracle. Product map | Surface | Role | Start here | | --- | --- | --- | | Library + CLI | Parse, lint, unparse, evaluate. Library Runtime.run defaults to interpret; pyne run is compile-only smoke | End User hub · modes | | Language core | Grammar, AST, visitors, type system | Core | | Runtime | Series caps, builtins, strategy, alerts, compiler parity | Runtime | | LSP | Diagnostics, completion, hover, format (.pyne / .pine) | LSP | | Pro API | HTTP /run (alerts + L webhooks), preview, backtest | API | | Ops | CI, Docker, Nuitka, secrets, GCP | DevOps | | PyneTS | TypeScript / Bun library (parse / unparse / Runtime.run) | PyneTS | | AXIS (separate product) | Charting PWA AXIS | AXIS docs | | HOOX | Edge trade mesh | HOOX docs | Conceptual model Invariant: evaluation never requires a proprietary chart host. AXIS is an optional chart; HOOX is an optional execution mesh. Tracks End User Install the package (pip install hoox-pyne), run the CLI, embed the library API, wire an editor (.pyne / .pine), or call the Pro API as a consumer — including alerts export and webhooks. → End User hub Core / Runtime / LSP / API Contributor and systems manuals: how the front-end, semantics, editor protocol, and HTTP bridge are built. Runtime covers series caps, incremental TA, drawing GC, fill() export, foreign request.security → na, and parity. DevOps CI matrices, Fernet metadata encryption, Nuitka LSP binaries, containers, Cloud Build. → DevOps hub Reference Compatibility guarantees, missing surface, numerical validation, PyneTS, ecosystem map, roadmap. Sister surfaces PyneTS — TypeScript / Bun library. Standalone hoox-sh/pynets; PYNE consumes it only as the pynets/ submodule. See PyneTS. pyne-worker (sister repo) — Python Cloudflare Worker for production POST /run (alerts + cron + R). pyne-agent-worker (sister repo) — NL authoring host (POST /v/chat); optional validate via pyne-worker. AXIS — installable PWA; documented at hoox.sh/axis/docs. Family map — Ecosystem · modes. Offline & agent exports Auto-generated like HOOX manuals (bun run docs:exports): | Kind | Path | | --- | --- | | Full-corpus LLM pack | llm.txt | | LLM site map (llmstxt.org) | llms.txt | | Track PDFs (A) | /exports/pyne--manual.pdf` | See also Installation Quick start Runtime modes Alerts & webhooks Grammar pipeline Evaluate contract pyne-worker pyne-agent-worker Roadmap --- FILE: docs/pyne/lsp/architecture.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "LSP Architecture" description: "PynescriptLanguageServer lifecycle, Workspace document model, capability declaration, and STDIO transport." --- LSP Architecture Abstract The language server is a single-process pygls application. It owns a Workspace of open TextDocumentState records, re-parses on every mutation, and dispatches LSP methods to pure-ish feature handlers that take (params, source) rather than holding server globals. Capabilities are declared once at initialize; transport defaults to STDIO for editor integration. Conceptual model Interface surface Process entry ``bash Editable install pip install -e ".[lsp]" python -m pynescript.langserver same as pyne-lsp / pynescript-lsp make run-lsp ` Entry: src/pynescript/langserver/main.py constructs PynescriptLanguageServer() and calls server.startio() (STDIO JSON-RPC). pyproject.toml registers: `text pyne-lsp = "pynescript.langserver.main:main" pynescript-lsp = "pynescript.langserver.main:main" alias ` Server class PynescriptLanguageServer (server.py): Subclasses pygls.lsp.server.LanguageServer with name="Pynescript" and version=version from src/pynescript/about.py (..). Instantiates self.pineworkspace = Workspace(). Registers handlers in setupmethodhandlers() via @self.feature(...). Document sync | Event | Behavior | | --- | --- | | didOpen | putdocument → parse/lint → push diagnostics | | didChange | Incremental or full-text apply → re-parse/lint → push diagnostics | | didClose | Remove document; publish empty diagnostic list | | didSave | Re-publish diagnostics for current buffer | Sync options (config.getservercapabilities): TextDocumentSyncKind.Incremental openclose=True save with includetext=True willsave / willsavewaituntil off Capability set (declared) From config.py: Diagnostic provider (identifier="pynescript-diagnostics", workspacediagnostics=False in options; workspace pull handler still exists) Completion: trigger ".", resolveprovider=True Hover, definition, references Document + workspace symbols Full + range formatting Inlay hints (resolveprovider=False) Semantic tokens full (legend of standard token types/modifiers; range=false) Signature help and code action are not advertised (config.py omits them until handlers exist). workspace/executeCommand is registered but returns None. File filters (getfilteroptions): .pine, .pinev, .pinev under language id pinescript. .pyne is not in this helper — VS Code still maps it via the extension contribution (extensions: .pyne first). Internals Workspace Workspace (workspace.py) maps uri → TextDocumentState: `text TextDocumentState uri, source, version ast | None diagnostics: list[LintWarning] parseerror, parseerrorline ` parseandlint: . parse(source, filename=uri) — on success, lintscript(source, filename=uri). . On exception: clear AST, store parseerror string, extract line via regex line[:\s]+(\d+). Incremental edits: applytextedit splits on \n, pads the line list when the range sits at/past EOF (append without a trailing newline), clamps columns, then replaces the range. Out-of-range positions no longer leave a stale buffer. Whole-document change events replace source wholesale. Identical text after apply skips re-parse/lint (version still updates). Diagnostics conversion (lintwarningstodiagnostics): Map severity strings → DiagnosticSeverity. Source tag "PineScript", code from lint rule. Append synthetic E error for parse failures at the extracted line. Feature dispatch pattern Handlers are functions, not server methods, e.g.: `python @self.feature(lsp.TEXTDOCUMENTCOMPLETION) def textcompletion(params): source = self.pineworkspace.getsource(params.textdocument.uri) return completionfeature.handlecompletion(params, source) ` Definition / references / symbols / hover / completion / inlay / semantic tokens receive the workspace-cached AST (tree=doc.ast) so they skip a redundant parse. Pass tree=None when the last parse failed. Omit tree only in isolated tests (handler then parses from source). Pull diagnostics textDocument/diagnostic → RelatedFullDocumentDiagnosticReport with resultid=f"{uri}-{version}". workspace/diagnostic → one full report per open document from getalldiagnostics(). Workspace symbols workspace/symbol walks each open document’s AST via collectworkspacesymbols: FunctionDef → Function, TypeDef → Class, EnumDef → Enum, Assign to Name → Variable. Filter is case-insensitive substring on params.query. Invariants and edge cases . Parse failure is non-fatal for the process. AST-dependent features return None or []; diagnostics still surface E. . Source of truth is the open buffer, not disk. Save only re-publishes; it does not re-read files. . Incremental sync pads/clamps rather than dropping the edit. Ranges at EOF grow the line list; columns are clamped to the current line length. . Version is stored and used in pull-diagnostic resultid; the server does not reject out-of-order versions itself. . Nuitka onefile embeds providers data and may rely on encrypted metadata — see builtin metadata. Architecture of handlers is identical to the pure-Python path. Worked example — minimal initialize Client → server (conceptual): `json { "method": "initialize", "params": { "capabilities": {}, "clientInfo": { "name": "example", "version": "" } } } ` Server returns InitializeResult with serverInfo.name = "Pynescript Language Server" and the full ServerCapabilities table from getserver_capabilities(). Failure modes | Symptom | Likely cause | | --- | --- | | No diagnostics on open | Client did not send didOpen or language id is not pinescript | | Completions empty | Metadata file missing/undecryptable; see builtin metadata | | Format no-ops | Parse error (handler returns []) or unparsed text equals source | | Extension can't spawn server | pynescript-lsp not on PATH`; check VS Code extension | See also Diagnostics Builtin metadata VS Code extension Nuitka build --- FILE: docs/pyne/lsp/builtin-metadata.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Builtin Metadata" description: "JSON catalog for LSP completion and hover; generation pipeline; Fernet encryption, CRYPTOKEY / METADATAKEY, and decrypt path." --- Builtin Metadata Abstract Builtin metadata is the LSP documentation corpus: a map from fully qualified names (ta.sma, strategy.entry, …) to labels, signatures, briefs, snippets, and categories. It is generated from code (not hand-edited as source of truth), loaded by the language server for completion/hover/inlay, and optionally Fernet-encrypted for Nuitka onefile distribution so casual string extraction does not dump the full catalog. This is obscurity for distribution hygiene, not a cryptographic access-control boundary. Conceptual model Interface surface (metadata record) Typical entry in builtinmetadata.json: ``json { "ta.sma": { "label": "ta.sma", "kind": "function", "detail": "ta.sma(series, int) → series float", "brief": "…", "documentation": "…", "snippet": "ta.sma(${:param}, ${:param})", "category": "ta.technicalanalysis" } } ` Categories drive completion headers (ta.technicalanalysis → “Technical Analysis (ta.)”, builtin → “Built-in Variables”, etc.). Loader API providers/builtinmetadata.py: | Function | Behavior | | --- | --- | | getmetadata() | Singleton cache; plaintext JSON first, else .enc decrypt, else {} | | getbuiltin(name) | Single entry or None | | getbuiltinsbycategory / getallcategories | Category queries | | fuzzyfilter | Scored substring match for completion | Consumers Completion list / module / resolve Hover cards Inlay return-type extraction from detail after → Internals | Path | Role | | --- | --- | | scripts/generatebuiltinmetadata.py | Introspect builtins → JSON | | src/pynescript/langserver/providers/builtinmetadata.json | Dev plaintext | | …/builtinmetadata.json.enc | Encrypted blob for release | | …/builtinmetadata.json.sha | First hex chars of SHA- of plaintext | | …/metadatadecrypt.py | Fernet load + integrity check | | scripts/build/compile.py | encryptmetadata(), Nuitka include of providers dir | | scripts/build/cibuild.py | stagemetadata() for CI | | scripts/build/.metadata.key | Gitignored Fernet key material | Generation After adding evaluator builtins: `bash python scripts/generatebuiltinmetadata.py ` Do not hand-edit the JSON as the long-term source of truth; regenerate from code. Encryption (build) scripts/build/compile.py → encryptmetadata() / resolvefernetkey(): . Require cryptography and plaintext JSON. . Resolve key (first hit wins): env CRYPTOKEY → PYNESCRIPTMETADATAKEY → METADATAKEY → existing scripts/build/.metadata.key → generate a new Fernet key and write that file (mode o). . Encrypt → builtinmetadata.json.enc. . Write truncated SHA- of plaintext to .sha. A random key is generated only on first local encrypt when no env/file key exists. For byte-stable encrypted artifacts across CI runs, supply a stable secret: | Context | Mechanism | | --- | --- | | GitHub Actions | secrets.METADATAKEY → env (documented as CRYPTOKEY in workflows / AGENTS) | | Cloud Build | substitution ${METADATAKEY} → CRYPTOKEY | | Runtime decrypt | File .metadata.key next to providers or env PYNESCRIPTMETADATAKEY | Without a stable key, each CI encrypt produces a different .enc blob — not a functional bug, but noisy diffs and cache misses. Decrypt path metadatadecrypt.py: . Resolve key: PyInstaller/Nuitka data dir .metadata.key, else providers dir, else PYNESCRIPTMETADATAKEY. . Fernet-decrypt .enc. . If .sha present, compare sha(plaintext).hexdigest()[:]. . json.loads → dict. Both getmetadata() and getmetadatacached() prefer plaintext when both artifacts exist (developer-friendly). Difference: getmetadata() returns {} on total failure; getmetadatacached() raises FileNotFoundError if neither file is present. Invariants and edge cases . Regenerate after builtin changes or completion/hover/inlay drift. . Empty dict on failure fails soft (no completions) rather than crashing the server. . Integrity mismatch raises in decrypt — compiled binary should not silently serve corrupt docs. . Key not in git — never commit .metadata.key. . Fernet is symmetric — anyone with the binary + key material can recover plaintext; design goal is casual reverse-engineering friction and future paid-metadata swap hooks. Worked example — local dev `bash python scripts/generatebuiltinmetadata.py edit-free check python -c "from pynescript.langserver.providers.builtinmetadata import getbuiltin; print(getbuiltin('ta.sma'))" ` Worked example — encrypted binary path `bash build (encrypts + Nuitka) — see DevOps python scripts/build/compile.py export PYNESCRIPTMETADATAKEY="$(cat scripts/build/.metadata.key)" or embed .metadata.key in the onefile data dir during compile ` Failure modes | Symptom | Cause | | --- | --- | | Completions empty in release binary | Missing key / wrong PYNESCRIPTMETADATAKEY | | Metadata integrity check failed | .enc and .sha out of sync | | CI churn on .enc | New random key each build without CRYPTOKEY / METADATAKEY | | Stale docs for new ta.` | Forgot to re-run generator | See also Metadata crypto (DevOps) Nuitka build Completion Hover --- FILE: docs/pyne/lsp/clients.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Editor Clients" description: "Wire pyne-lsp into Neovim, Zed, Emacs, Helix, Sublime, and generic LSP hosts." --- Editor Clients Abstract Any editor that speaks LSP over STDIO can host PYNE intelligence. First-class snippets live in clients/ (Neovim Lua, Zed JSON, Emacs Lisp) plus documented Helix/Sublime fragments. The VS Code path is a dedicated extension (vscode-extension); this page covers the rest. Prerequisite: pyne-lsp (or pynescript-lsp) on PATH (Python .+): ``bash pip install "hoox-pyne[lsp]" development pip install -e ".[lsp]" ` Conceptual model Interface surface — repo files | File | Editor | | --- | --- | | clients/neovim.lua | Neovim (nvim-lspconfig or manual) | | clients/zed.json | Zed settings.json fragment | | clients/emacs.el | Emacs lsp-mode + pinescript-mode | | clients/README.md | Human-oriented install notes | | vscode-extension/ | VS Code / Cursor-compatible | Neovim Return table from clients/neovim.lua: `lua return { cmd = { 'pyne-lsp' }, filetypes = { 'pinescript' }, rootdir = function(fname) return vim.fs.root(fname, { '.git', '.pine', '.pinev', '.pinev', 'pyproject.toml' }) or vim.fn.getcwd() end, settings = { pinescript = { formatting = { enabled = true }, diagnostics = { enabled = true }, completion = { snippets = true }, }, }, } ` Suggested maps when attaching: gd definition, gr references, K hover, format via vim.lsp.buf.format. Register filetype if needed: `vim au BufRead,BufNewFile .pyne,.pine,.pinev,.pinev,.pinescript setfiletype pinescript ` Zed Merge clients/zed.json into ~/.config/zed/settings.json: `json { "languages": { "Pine Script": { "languageservers": ["pynescript"] } }, "languageservers": { "pynescript": { "command": "pyne-lsp", "arguments": ["--stdio"], "languages": ["Pine Script"] } } } ` Zed does not ship a first-party Pine grammar in this repo. Map .pyne / .pine to the "Pine Script" language name (or whatever your grammar extension registers) so the languageservers key attaches. Emacs clients/emacs.el registers an lsp-mode client: `elisp (lsp-register-client (make-lsp-client :new-connection (lsp-stdio-connection '("pyne-lsp" "--stdio")) :major-modes '(pinescript-mode) :server-id 'pynescript)) ` Defines a minimal pinescript-mode with font-lock keywords and auto-mode-alist for \\.pine\\' / \\.pinev[-]+\\' (add \\.pyne\\' locally if you use the product suffix). Optional keys: C-c C-c format, M-. definition, M-? references. Helix ~/.config/helix/languages.toml: `toml [[language]] name = "pinescript" scope = "source.pinescript" file-types = ["pyne", "pine", "pinev", "pinev"] roots = ["pyproject.toml"] command = "pyne-lsp" args = ["--stdio"] ` Sublime Text With Package Control LSP: `json { "clients": { "pynescript": { "command": ["pyne-lsp", "--stdio"], "selector": "source.pinescript", "initializationOptions": {} } } } ` Generic / Cursor `bash pyne-lsp aliases: pynescript-lsp --stdio container (stdio): docker run --rm -i -v "$PWD:/work" -w /work ghcr.io/hoox-sh/pyne/lsp:.. ` Point the host’s “external language server” command at that binary. Cursor users can also load the VS Code extension (hoox-sh.pyne) in compatible mode when packaged as VSIX. Internals Server entry always uses STDIO (startio in main.py). Capability negotiation is identical across hosts — differences are client UI only (how inlay hints render, whether pull diagnostics are used, etc.). Tests: tests/testlspfeatures.py (handlers with fake params), tests/test_langserver.py (ee with pytest-asyncio) — not editor-specific. Invariants and edge cases . Language id should be pinescript for VS Code parity; some editors invent their own names — map filetypes carefully. . Root directory heuristics vary; wrong root rarely breaks single-file Pine editing (workspace is URI-keyed open buffers). . Settings keys under pinescript in Neovim are conventional; the Python server does not currently enforce a rich workspace/configuration schema for all of them. . Nuitka onefile can replace the module entry for air-gapped machines — same command name if installed on PATH. Failure modes | Symptom | Cause | | --- | --- | | Server exits immediately | Missing pygls / incomplete install .[lsp] | | Filetype never attaches | Extension not mapped to pinescript` | | Features missing in Helix | Older Helix without inlay / semantic support — grammar still useful | See also VS Code extension Architecture End-user editors guide --- FILE: docs/pyne/lsp/features/completion.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Completion" description: "textDocument/completion and completionItem/resolve from builtin metadata, module-dot triggers, and fuzzy filter." --- Completion Abstract Completion is catalog-driven, not evaluator-driven. The server loads builtin metadata (plaintext JSON in development, Fernet-encrypted in compiled binaries), then also offers Pine keywords (PINEKEYWORDS) and user enum types / members collected from the workspace AST. Resolve re-hydrates documentation for a single item. Conceptual model Interface surface | Method | Capability | | --- | --- | | textDocument/completion | triggercharacters=["."], returns CompletionList | | completionItem/resolve | resolveprovider=True | Handler: features/completion.py → handlecompletion / handlecompletionresolve. Trigger logic . Compute textbeforecursor on the current line. . gettriggerchar — if the previous character is . (or (, ,, space), treat as trigger. . Collect user enums from the cached AST (collectuserenums). . If the last token contains . (e.g. ta. or Side.): user-enum prefix → buildenummembercompletion else buildmodulecompletion(module) for catalog namespaces . Else merge buildkeyworditems, buildenumnameitems, and buildcompletionlist(prefix=prefix). Word boundaries for Pine identifiers: [a-zA-Z][a-zA-Z-.] (protocol/utils.py). Item fields Built by providers/completionitems.py: | Field | Source | | --- | --- | | label | metadata label (e.g. ta.sma) | | kind | CompletionItemKind.Function | | detail | signature / detail string | | documentation | Markdown from metadata | | inserttext | Snippet if ${...} present, else plain label | | inserttextformat | Snippet or PlainText | | filtertext | dotted parts + brief | | sorttext | modules first (\x…), root second (\x…) | Category headers may appear as Folder-kind items with empty insert text and labels like --- Technical Analysis (ta.) (N) ---. Internals | Path | Role | | --- | --- | | features/completion.py | Request context + dispatch | | providers/completionitems.py | List / item / module builders | | providers/builtinmetadata.py | Load cache, fuzzyfilter, getbuiltin | | protocol/utils.py | Word + trigger helpers | Fuzzy filter scores fuzzyfilter(query, items, limit=): | Match | Score | | --- | --- | | Exact label | | | Prefix | | | Substring in label | | | Category | | | Brief | | Results sorted by score descending, capped at limit. Resolve handlecompletionresolve looks up params.label in metadata; if found, rebuilds a full CompletionItem. Unknown labels return unchanged. Invariants and edge cases . No local function / variable completion yet — user enums and keywords are included; local myFunc is not, unless it appears in metadata. . Empty metadata dict yields an empty list (failed decrypt / missing files). . Module completion uses startswith(module + ".") — nested namespaces beyond one dot still work if labels are fully qualified. . Snippets are generated at metadata build time (scripts/generatebuiltinmetadata.py); incompleteness of placeholders is a generator concern, not the LSP handler. Worked example User types ta. → module completion for all ta. labels. User types sma without a module → fuzzy filter may return ta.sma among others (substring score). Resolve on ta.sma reloads full documentation markup for the detail pane. Failure modes | Symptom | Cause | | --- | --- | | Zero items always | Metadata not loaded — check builtin metadata | | Dot trigger ignored | Client did not set completion trigger characters from server capabilities | | Snippets inserted raw | Client disabled snippet support; extension setting pynescript.completion.snippets | See also Hover — shares metadata Builtin metadata Inlay hints — uses return types from detail --- FILE: docs/pyne/lsp/features/diagnostics.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Diagnostics" description: "Push and pull diagnostics: parse errors, lint rules, severity mapping, and code-action stubs." --- Diagnostics Abstract Diagnostics are the primary static feedback channel. The workspace re-parses and re-lints on every open/change/save, converts LintWarning objects (and parse failures) into lsp.Diagnostic, then pushes them with textDocument/publishDiagnostics. Clients that support LSP .+ pull diagnostics can also request textDocument/diagnostic or workspace/diagnostic. Conceptual model Interface surface Push (always on) Triggered from server.py on didOpen, didChange, didSave. Close clears diagnostics (items=[]). Pull | Method | Response | | --- | --- | | textDocument/diagnostic | RelatedFullDocumentDiagnosticReport with kind=full, resultid="{uri}-{version}" | | workspace/diagnostic | One WorkspaceFullDocumentDiagnosticReport per open URI | Capability: diagnosticprovider with identifier="pynescript-diagnostics", interfiledependencies=False. Diagnostic shape | Field | Value | | --- | --- | | range | -based line; column from warning or ; end spans remainder of line (capped) | | severity | error / warning / information / hint | | message | Human-readable lint or parse message | | source | "PineScript" | | code | Lint code (e.g. W) or E for parse errors | The richer conversion in features/diagnostics.py also supports: codedescription hrefs for codes starting with E / W (docs URLs) tags: W → Unnecessary, W → Deprecated Push and pull both call Workspace.lintwarningstodiagnostics, which delegates lint rows to features/diagnostics.lintwarningstodiagnostics (noise filters + tags) and then appends synthetic E for parse failures. Internals | Path | Role | | --- | --- | | src/pynescript/langserver/workspace.py | parseandlint, E append, incremental edits | | src/pynescript/langserver/features/diagnostics.py | Lint conversion, C/C noise filters, tags, createquickfix | | src/pynescript/ast/linter.py | Rule engine producing LintWarning | Severity map ``text error → DiagnosticSeverity.Error warning → Warning info|information → Information hint → Hint (default) → Warning ` Parse errors On exception from parse: ast = None, diagnostics = [] (lint skipped) Message stored as parseerror Synthetic diagnostic code E, severity Error, line from regex on the exception text Quick fixes (feature module) createquickfix (not yet fully wired through workspace/executeCommand / codeAction handlers in server.py): W → insert //@version=\n at document start C (long line) → suggest format document command Capability advertises QuickFix / Refactor / SourceOrganizeImports kinds for future wiring. Invariants and edge cases . Lint does not run when parse fails — only E is shown. . Warnings without a line number are dropped (None conversion). . End column heuristic uses the remainder of the line and clamps to characters — not a precise token span. . interfiledependencies=False: no cross-file analysis; each URI is independent. . Push and pull should agree for a given buffer version; both read the same TextDocumentState. . Noise filters: C on names that already lack (camelCase fastMA) is dropped; C on block if (next non-empty line more indented) is dropped. Worked example Source: `pinescript indicator("x") plot(close) ` If the linter emits a missing-version warning (code W), the client receives a Warning diagnostic on the relevant line with source: "PineScript" and may offer the version-header quick fix when code actions are connected. On a hard parse failure at line , expect: `json { "severity": , "code": "E", "source": "PineScript", "message": "" } ` Failure modes | Symptom | Cause | | --- | --- | | Stale squiggles after edit | Client using full-document sync incorrectly, or version mismatch | | Empty diagnostics on broken file | Unexpected: parse errors should still emit E — check client filter on source` | | No workspace pull results | Only open documents are tracked; closed files are absent | See also Linter (core) Error model Architecture --- FILE: docs/pyne/lsp/features/formatting.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Formatting" description: "Document and range formatting via AST parse → NodeUnparser round-trip." --- Formatting Abstract Formatting is canonical unparse, not a style linter. The handler parses the buffer, runs NodeUnparser, and if the result differs from the source, replaces the document (or the requested line range) with a single TextEdit. There is no prettier-like option matrix; FormattingOptions from the client are currently unused. Conceptual model Interface surface | Method | Handler | | --- | --- | | textDocument/formatting | handleformatting | | textDocument/rangeFormatting | handlerangeformatting | Capabilities: both providers True in config.py. Full document . Parse with filename "". . formatted = NodeUnparser().visit(tree). . If identical to source → []. . Else one TextEdit spanning (,) → last line/col with new_text=formatted. Range . Unparse the entire document. . Slice formatted lines and source lines by the client range’s start/end line indices. . If the in-range slices match → []. . Else replace params.range with the formatted line slice. This is line-aligned, not AST-node-aligned: partial mid-line ranges may produce surprising splices when unparse changes line structure. Internals | Path | Role | | --- | --- | | features/formatting.py | LSP handlers | | src/pynescript/ast/helper.py | parse | | src/pynescript/ast/unparser.py | NodeUnparser | VS Code command pynescript.formatDocument delegates to editor.action.formatDocument, which hits this provider when the language client is active. Invariants and edge cases . Parse failure → [] (no partial format; silent no-op from the client’s perspective). . Idempotence goal: format twice should stabilize if unparse is canonical; any non-idempotent unparse is a core bug, not an LSP bug. . Comments / trivia: fidelity is whatever the unparser preserves — not a full CST pretty-printer. . Range formatting still requires a full successful parse of the whole file. . Tab size / insert spaces from FormattingOptions are ignored today. Worked example Before: ``pinescript //@version= indicator("x") plot( close ) ` After full format (illustrative — exact whitespace follows NodeUnparser): `pinescript //@version= indicator("x") plot(close) ` If source already matches unparse output, the server returns an empty edit list and the editor leaves the buffer alone. Failure modes | Symptom | Cause | | --- | --- | | Format does nothing on broken syntax | Expected: exception → []` | | Range format rewrites wrong lines | Unparse shifted line count; prefer full-document format | | Client “format on save” loops | Unparse not idempotent — fix unparser, not client | See also Unparser Architecture VS Code extension --- FILE: docs/pyne/lsp/features/hover.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Hover" description: "textDocument/hover — Markdown documentation cards for Pine builtins from metadata." --- Hover Abstract Hover answers “what is this identifier?” for builtins, then user enums, then keywords. The handler extracts the word under the cursor (including dotted forms), looks up metadata / enum members / PINEKEYWORDS, and returns a Markdown card. Local functions and variables still return null (use definition). Conceptual model Interface surface Method: textDocument/hover Capability: hoverprovider=True Handler: features/hover.py → handlehover Lookup order . Exact word at cursor via getwordatposition (identifiers may include .). . Builtin lookup: full word, then leaf after ., then module.word if the preceding token ends with .. . Else user-enum type or Enum.member from the cached AST. . Else keyword brief from PINEKEYWORDS. . Build hover with range covering start, end) of the word on the line. Card structure ``markdown `pinescript {detail} ` {brief} --- {first documentation paragraph, ≤ chars} Example: … See also: ta.ema, … ` Examples and related maps are hardcoded for a small high-value set (ta.sma, ta.ema, ta.rsi, ta.macd, strategy entry/exit, etc.). Others show signature + brief only. External TradingView doc links are intentionally omitted (builddocslink returns empty) to keep hover self-contained. Internals | Path | Role | | --- | --- | | features/hover.py | Word resolution + Markdown assembly | | providers/builtinmetadata.py | getbuiltin | | protocol/utils.py | Word extraction | Invariants and edge cases . No source → null. Missing buffer aborts early. . Line OOB → null. Cursor past last line is ignored. . Empty word → null. Whitespace / punctuation yields no hover. . User functions / locals / UDTs are not inferred for hover (enums are). Use [definition / outline instead. . Documentation truncation is first-paragraph / -char only — full docs live in metadata and completion resolve. Worked example Cursor on sma in plot(ta.sma(close, )): Word may be ta.sma if the dotted identifier is one match, or sma with module recovery from ta.. Response contents.kind = markdown with fence of the metadata detail line. Failure modes | Symptom | Cause | | --- | --- | | Hover never appears | Metadata empty or word not in catalog | | Wrong symbol | Identifier regex swallowed neighboring text — rare with .` pattern | | Stale docs after adding builtin | Regenerate metadata — builtin metadata | See also Completion Builtin metadata Navigation --- FILE: docs/pyne/lsp/features/inlay-hints.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Inlay Hints" description: "Inferred type annotations on simple assignments — const, series, and input kinds." --- Inlay Hints Abstract Inlay hints surface inferred types next to simple name = expr assignments when no explicit type annotation is present. Inference is deliberately shallow: constants, bare series builtins (close, …), input. constructors, and ta/math/str/color calls whose metadata detail contains a → return type. Ambiguous operators are skipped rather than guessed wrong. Conceptual model Example mental model (from module docstring): ``text length = → length: const int rsi = ta.rsi(close, ) → rsi: series float (from metadata detail) n = input.int(, "n") → n: input int ` Interface surface Method: textDocument/inlayHint Capability: InlayHintOptions(resolveprovider=False) Handler: features/inlayhints.py → handleinlayhints (receives cached tree=doc.ast) Each hint: | Field | Value | | --- | --- | | position | End of target identifier (lineno-, coloffset + len(id)) | | label | ": {typelabel}" | | kind | InlayHintKind.Type | | tooltip | Inferred type: {typelabel} | Returns [] for empty source, None if parse fails (client typically treats as no hints). Internals Constant mapping | Python value | Label | | --- | --- | | bool | const bool | | int | const int | | float | const float | | str | const string | Builtin bare names open, high, low, close, volume, hl, hlc, ohlc → series float; time / bar index vars → series int; na → na. Calls input.{int,float,bool,string,color,symbol,session,source,time,timeframe,price} → input {attr} ta|math|str|color.{attr} → parse detail after →, else fallback "series" Walk Recursive over fields (same pattern as workspace symbol collection) — not limited to script top-level; nested function assigns can receive hints. | Path | Role | | --- | --- | | features/inlayhints.py | Collection + inference | | providers/builtinmetadata.py | Return-type detail for modules | Invariants and edge cases . Explicit name: type = … suppresses hints (node.type is not None). . Tuple / attribute targets are ignored. . BinOp / Compare / Conditional return None — no speculative arithmetic types. . Metadata quality bounds accuracy — missing → in detail yields coarse series. . No resolve step — tooltips are final. Worked example `pinescript //@version= indicator("hints") length = src = close avg = ta.sma(src, length) period = input.int(, "Period") ` Expected hints (when parse + metadata succeed): length → : const int src → : series float avg → type from ta.sma detail (typically series float) or series period → : input int avg will not get a hint derived from length’s type through the call graph — only the call’s known return shape. Failure modes | Symptom | Cause | | --- | --- | | No hints on file | Parse error → None | | ta. always : series | Metadata detail lacks → segment | | Hints mid-identifier | Incorrect coloffset` on AST node — builder issue | See also Completion Builtin metadata Type system --- FILE: docs/pyne/lsp/features/navigation.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Navigation" description: "Go-to-definition, find-references, document outline, and workspace symbol search over the ASDL AST." --- Navigation Abstract Navigation features operate on user AST symbols, not the builtin catalog. Definition and references walk a freshly parsed tree with NodeVisitor subclasses; document symbols build a hierarchical outline; workspace symbols scan all open documents. Locations currently anchor to line starts (coarse ranges) — enough for jump lists, not character-precise rename. Conceptual model Interface surface | Method | Handler | Returns | | --- | --- | --- | | textDocument/definition | definitions.handledefinition | list[Location] \| None | | textDocument/references | references.handlereferences | list[Location] (possibly empty) | | textDocument/documentSymbol | symbols.handledocumentsymbols | list[DocumentSymbol] | | workspace/symbol | server.collectworkspacesymbols | list[SymbolInformation] | Capabilities: definitionprovider, referencesprovider, document + workspace symbol options with work-done progress flags. Definition (DefinitionFinder) Matches target word against: FunctionDef.name TypeDef.name Assign targets (Name or nested names) Ignores mere call sites (visitCall returns early when the callee name matches). References (ReferencesFinder) Name with Load / Store contexts Function and type definitions when includedeclaration is true Call sites of bare Name callees params.context.includedeclaration controls whether declarations appear. Document symbols Hierarchical: | AST | SymbolKind | Children | | --- | --- | --- | | FunctionDef | Function | Variables assigned inside body | | TypeDef | Class | Fields (Assign targets) | | EnumDef | Enum | Members | | Top-level Assign | Variable | — | Function detail is a coarse function name() string; assignments may show callee module attribute as detail when RHS is a call. Workspace symbols Flat SymbolInformation for functions, types, and assigned names across open buffers; filtered by lowercase query substring. Internals | Path | Role | | --- | --- | | features/definitions.py | Go-to-definition visitor | | features/references.py | Find-all-references visitor | | features/symbols.py | Document outline | | server.py | Workspace symbol aggregation | Handlers accept the workspace-cached AST (tree=doc.ast from server.py). They re-parse from source only when tree is omitted (tests / isolated calls). Pass tree=None after a failed parse so the handler does not retry. Invariants and edge cases . Parse failure → empty / null (definition None, others []). . Builtin names have no definition in-file; clients should not expect ta.sma to resolve to a library file URI. . Ranges are line-coarse for many locations (character=); selection ranges for document symbols use coloffset when present. . Scope is naive — same identifier in nested functions may collect multiple definitions; no shadowing analysis. . Closed documents disappear from workspace symbol search. Worked example ``pinescript //@version= indicator("nav") length = mySma(src, len) => ta.sma(src, len) v = mySma(close, length) ` Definition on mySma at the call site → function line. References on length → assign + call argument (and declaration if included). Outline → length variable, mySma function (with locals), v variable. Failure modes | Symptom | Cause | | --- | --- | | Jump lands column | Expected with current Location encoding | | Missing references inside function when targeting another function | visit_FunctionDef` only walks body when the def name differs from target — intentional for declaration handling; calls inside same-named functions are a known edge | | Empty outline | Parse error or empty source | See also Architecture Hover — builtins vs user symbols Diagnostics --- FILE: docs/pyne/lsp/features/semantic-tokens.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Semantic Tokens" description: "textDocument/semanticTokens/full — AST visitor, custom legend, and LSP delta encoding." --- Semantic Tokens Abstract Semantic tokens let the server paint identifiers with roles richer than TextMate scopes (library namespaces vs user variables, function defs vs properties). PYNE advertises a full semantic-tokens provider with a short custom legend (not the full LSP standard set) and implements textDocument/semanticTokens/full by walking the workspace AST. Conceptual model Interface surface Capability (config.py → semantictokensprovider): ``text legend.tokentypes (index order is contract): namespace, type, class, function, method, variable, parameter, property, keyword, string, number, operator, comment legend.tokenmodifiers (bit = declaration): declaration, definition, readonly, defaultLibrary range: false full: true ` Handler: features/semantictokens.py → handlesemantictokens(params, source, tree=…). Response type: lsp.SemanticTokens(data=[...]) — LSP-encoded five-tuples as a flat int array (relative line/character deltas). What is emitted | AST | Token type | Modifiers | | --- | --- | --- | | FunctionDef.name | function | definition \| declaration | | TypeDef.name | class | definition \| declaration | | EnumDef.name | type | definition \| declaration | | Assign to Name | variable | declaration | | Attribute whose value is a builtin namespace (ta, math, strategy, …) | namespace + method | defaultLibrary (+ readonly on ns) | | Other Attribute.attr | property | — | Builtin namespace set (BUILTINNS): ta, math, str, array, matrix, map, strategy, request, input, color, line, label, box, table, polyline, log, ticker, timeframe, chart, runtime, syminfo, barstate, session, time. Internals | Path | Role | | --- | --- | | features/semantictokens.py | Collector + encode | | config.py | Legend + capability — indices must match TT | | server.py | TEXTDOCUMENTSEMANTICTOKENSFULL; passes cached doc.ast | Attribute columns prefer endcoloffset - len(attr) when present; otherwise parent coloffset (approximate). TextMate from vscode-extension/syntaxes/pinescript.tmLanguage.json still colors keywords, strings, and comments — the visitor does not emit those yet even though the legend reserves the slots. Invariants and edge cases . Never throws — missing source, tree=None, or parse exception yields empty data. . No range provider — clients must not request semanticTokens/range. . Legend is fixed at initialize; changing indices later would break cached clients. . Empty data is valid protocol (empty file or parse failure), not an error. . Tokens are sorted by (line, col) before delta encoding. Worked example `pinescript //@version= indicator("tok") length = plot(ta.sma(close, length)) ` Expect tokens for length (variable/declaration), ta (namespace/defaultLibrary), sma (method/defaultLibrary). Keywords such as indicator remain TextMate-only. Failure modes | Symptom | Cause | | --- | --- | | Semantic highlighting off despite tokens | Client editor.semanticHighlighting.enabled false | | Colors look “shifted” after a legend edit | Client cached old type indices — restart the language client | | Client errors on range request | range=False in capabilities; client should not call it | | Attribute token sits on the wrong column | Missing endcoloffset` on the AST node — approximate path | See also VS Code extension — TextMate grammar Architecture Inlay hints — complementary visual annotations --- FILE: docs/pyne/lsp/index.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Language Server" description: "PYNE Language Server Protocol — diagnostics, completion, hover, formatting for .pyne / .pine and editor clients." --- Language Server The PYNE Language Server is the editor-facing half of the evaluation stack: a pygls process that speaks LSP over STDIO (or TCP in tooling), parses Pine with the same ANTLR → ASDL pipeline as the runtime, and never requires a proprietary chart host. First-class file associations include .pyne (product extension) plus .pine / .pinev / .pinev / .pinescript. Abstract Editors want static intelligence at typing cadence. Evaluation over OHLCV is dynamic and bar-loop expensive. PYNE therefore splits the stack: | Process | Cadence | Shared substrate | | --- | --- | --- | | LSP (pyne-lsp) | keystroke / open / save | parse, lint, unparse, builtin metadata | | Pro API (backend.app) | request / batch | parse, evaluate, plots / events / alerts | | CLI / library | batch / embed | same AST + evaluator | The LSP publishes diagnostics from the linter and parse errors, completes and hovers from Fernet-ready builtin metadata (plus keywords and user enums), navigates user symbols via AST visitors, emits semantic tokens from the cached tree, and formats by round-tripping through the unparser. Feature modules live under src/pynescript/langserver/features/; the VS Code extension (hoox-sh.pyne ..) and clients/ configs are thin protocol hosts — VS Code maps .pyne first, then legacy Pine suffixes, to language id pinescript. The stdio image is ghcr.io/hoox-sh/pyne/lsp. Conceptual model Invariant: open/change/close always re-parse and re-lint the in-memory buffer. Features that need an AST re-parse on demand if the workspace cache is missing source. Interface surface | Capability | LSP method(s) | Module | | --- | --- | --- | | Sync | textDocument/didOpen|didChange|didClose|didSave | server.py + workspace.py | | Diagnostics (push + pull) | publishDiagnostics, textDocument/diagnostic | workspace.py, features/diagnostics.py | | Completion (+ resolve) | textDocument/completion, completionItem/resolve | features/completion.py | | Hover | textDocument/hover | features/hover.py | | Definition / references | textDocument/definition, textDocument/references | definitions.py, references.py | | Symbols | textDocument/documentSymbol, workspace/symbol | symbols.py, server.py | | Formatting | textDocument/formatting, rangeFormatting | features/formatting.py | | Inlay hints | textDocument/inlayHint | features/inlayhints.py | | Semantic tokens | textDocument/semanticTokens/full | features/semantictokens.py | Declared capabilities: src/pynescript/langserver/config.py (getservercapabilities). Internals | Path | Role | | --- | --- | | src/pynescript/langserver/server.py | PynescriptLanguageServer, method registration | | src/pynescript/langserver/workspace.py | Document map, incremental edits, parse+lint | | src/pynescript/langserver/config.py | ServerCapabilities | | src/pynescript/langserver/features/ | Per-method handlers | | src/pynescript/langserver/providers/ | Metadata + completion builders | | src/pynescript/langserver/main.py | STDIO entry (pyne-lsp / alias pynescript-lsp) | | vscode-extension/ | Language client + TextMate grammar (hoox-sh.pyne ..) | | clients/ | Neovim, Zed, Emacs snippets (Helix / Sublime are documented, not files) | Console scripts are separate: pyne (Click CLI; alias pynescript) vs pyne-lsp (pygls; alias pynescript-lsp). Do not conflate them — see architecture. Tracks in this tab . Architecture — server lifecycle, workspace, capabilities . Diagnostics through Inlay hints — feature manuals . Builtin metadata — generation + Fernet .enc / CRYPTOKEY . VS Code extension — client packaging . Clients — Neovim, Zed, Emacs, Helix, Sublime See also Pro API — HTTP evaluate path sharing the same AST Evaluate contract Linter Unparser Editors guide (end user) --- FILE: docs/pyne/lsp/vscode-extension.mdx Copyright (C) - jango_blockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "VS Code Extension" description: "PYNE VS Code extension: HOOX branding, TextMate grammar, LanguageClient wiring, settings, and VSIX build." --- VS Code Extension Abstract PYNE (hoox-sh.pyne ..) is a LanguageClient host plus a rich TextMate grammar, branded as part of the HOOX open trading stack. It does not reimplement diagnostics or completion in TypeScript; it resolves pyne-lsp (then alias pynescript-lsp, then python -m pynescript.langserver) or a configured command over STDIO via vscode-languageclient, maps .pyne (first-class) plus .pine / .pinev / .pinev / .pinescript to language id pinescript, and exposes a small settings surface for enablement and feature toggles. Conceptual model Interface surface Package identity From vscode-extension/package.json: | Field | Value | | --- | --- | | name | pyne | | displayName | PYNE Language Support | | version | .. | | publisher | hoox-sh | | extension id | hoox-sh.pyne | | icon | media/icon.png (HOOX mark) | | engines.vscode | ^.. | | main | ./out/extension.js (esbuild bundle) | | dependency | vscode-languageclient ^ | | galleryBanner | dark | Language contribution id: pinescript aliases: PYNE, pyne, pinescript, Pine extensions: .pyne (first), .pine, .pinev, .pinev, .pinescript configuration: language-configuration.json grammar: syntaxes/pinescript.tmLanguage.json (scopeName: source.pinescript) — namespaces, annotations, hex colors, UDT/enum, multiline strings, plot/strategy builtins Activation ``text onLanguage:pinescript workspaceContains:/.pyne workspaceContains:/.pine workspaceContains:/.pinev workspaceContains:/.pinev ` Settings (pynescript.) | Key | Default | Meaning | | --- | --- | --- | | lsp.enabled | true | Skip activation when false | | lsp.command | auto | auto = PATH pyne-lsp → pynescript-lsp → python -m pynescript.langserver. Or an absolute path. "docker" plus lsp.args can launch ghcr.io/hoox-sh/pyne/lsp (${workspaceFolder} is expanded). | | lsp.python | python | Interpreter for module launch when neither binary is on PATH (pip install "hoox-pyne[lsp]") | | lsp.args | [] | Extra server args | | formatting.enabled | true | Passed as init option | | diagnostics.enabled | true | Passed as init option | | completion.snippets | true | Passed as init option | Initialization options object: `json { "formattingEnabled": true, "snippetsEnabled": true, "diagnosticsEnabled": true } ` The Python server does not read these flags today. They are client-side documentation for hosts; capability advertisement is unconditional in config.py. Commands | Command palette | ID | Action | | --- | --- | --- | | PYNE: Restart Language Server | pynescript.restartServer | Stop then start the language client | | PYNE: Format Document | pynescript.formatDocument | editor.action.formatDocument (.pyne / .pine) | | PYNE: Show Language Server Output | pynescript.showLspOutput | Open Output channel / status-bar target | | PYNE: Show Resolved LSP Launch Command | pynescript.showLspCommand | Show and copy resolved pyne-lsp launch | Client options Document selector: pinescript for file and untitled schemes File watcher: /.{pyne,pine,pinev,pinev,pinescript} Diagnostic collection name: pynescript Default formatter id: hoox-sh.pyne Internals | Path | Role | | --- | --- | | vscode-extension/src/extension.ts | Activate / deactivate / client | | vscode-extension/package.json | Contributes + scripts | | vscode-extension/syntaxes/pinescript.tmLanguage.json | Grammar | | vscode-extension/language-configuration.json | Brackets / comments | | out/extension.js | Compiled JS | Server launch (resolveLspLaunch in extension.ts): . If pynescript.lsp.command is not auto, spawn that command with lsp.args. . Else try pyne-lsp, then pynescript-lsp, on PATH. . Else try pynescript.lsp.python (then python / python) with -m pynescript.langserver. . Else status-bar error: install hoox-pyne[lsp]. No --parent-dir / PYTHONPATH rewrite. Transport is STDIO. Release packaging expects pyne-lsp on PATH (pip, Nuitka onefile, or docker run -i ghcr.io/hoox-sh/pyne/lsp:..). Build `bash cd vscode-extension npm ci npm run compile esbuild.mjs → out/extension.js npm run package pyne-vscode-...vsix or monorepo: make build-vscode ` Requires Node . compile:tsc is typecheck-only (tsc --noEmit). Invariants and edge cases . lsp.enabled === false short-circuits activate — no client, no restart command registration beyond early return (commands not registered). . Binary vs module: production users should install hoox-pyne[lsp] or the Nuitka onefile (pynescript-lsp- on GitHub Releases) and set pynescript.lsp.command if not on PATH. . Grammar works without LSP — open a .pyne or .pine file offline and still get TextMate highlighting. . .pyne is first-class in package.json extensions + activation; legacy .pine / .pinescript remain fully supported. . File watcher covers every associated suffix (pyne, pine, pinev, pinev, pinescript). Worked example — extension development `bash pip install -e ".[lsp]" cd vscode-extension && npm ci && npm run compile Launch Extension Development Host pointing at vscode-extension ` Open a fixture .pine file; confirm Problems panel shows parse/lint diagnostics from the Python server. Failure modes | Symptom | Cause | | --- | --- | | “Language client failed to start” | pyne-lsp missing / wrong command | | No squiggles, highlighting OK | LSP disabled or server crash; check Output → PYNE Language Server | | Restart does nothing | Client never started (lsp.enabled` false) | See also Clients — non-VS Code hosts Architecture Nuitka build Editors guide --- FILE: docs/pyne/pyne-worker/deploy.mdx Copyright (C) - jangoblockchained This file is part of pynescript. SPDX-License-Identifier: AGPL-.-or-later --- title: "pyne-worker deploy" description: "Vendor the PYNE engine, wrangler secrets, R bars, and m cron. Sister checkout only." --- pyne-worker deploy Abstract Deploy from the hoox-sh/pyne-worker checkout. Wrangler packages pythonmodules/pynescript, not an editable PYNE install. After every engine bump, run ./scripts/syncvendor.sh. Quick start ``bash cd ~/Git/pyne-worker pip install -e ".[dev]" pytest -v ./scripts/syncvendor.sh npx wrangler deploy echo "my-secret-key" | npx wrangler secret put APIKEY ` Deploy to Cloudflare deploys this host only. Dashboard prompts: APIKEY (required), optional ALERTWEBHOOKURL, INTERNALKEYBINDING. R bucket OHLCVDATA comes from wrangler.jsonc. If TRADESERVICE points at a missing trade-worker, delete the services block and retry. Vendor sync `bash ./scripts/syncvendor.sh npx wrangler deploy ` Skip sync and the edge still runs the last snapshot (may lag hoox-pyne ..+). Script + m cron `bash export WORKER=https://pyne-worker..workers.dev python scripts/fetchandingest.py \ --symbol BTCUSDT --timeframe m \ --ingest-url "$WORKER/ingest" --api-key "$APIKEY" curl -sS -X POST "$WORKER/scripts" \ -H "Content-Type: application/json" -H "X-API-Key: $APIKEY" \ -d '{"id":"btc-sma","script":"//@version=\nindicator(\"s\")\nplot(close)","symbol":"BTCUSDT","timeframe":"m","mode":"auto","enabled":true}' curl -sS -X POST "$WORKER/cron/run" \ -H "Content-Type: application/json" -H "X-API-Key: $APIKEY" \ -d '{"force":true}' ` Cron in wrangler.jsonc is . Each tick pulls closed klines (Bybit, Binance fallback) and evaluates only when the last closed bar advanced. See also pyne-worker Evaluate pyne-agent-worker — optional generate → /run` loop PyneTS --- FILE: docs/pyne/pyne-worker/index.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. SPDX-License-Identifier: AGPL-.-or-later --- title: "pyne-worker" description: "Production Python Cloudflare Worker for POST /run. Thin wrap over pynescript.runtime. Sister repo — not in this checkout." --- pyne-worker Abstract pyne-worker is the production edge evaluate host: a Python Cloudflare Worker that vendors pynescript.runtime and speaks the same evaluate contract as Flask POST /run. It is a sister repository — it does not live in this PYNE checkout. | | | | --- | --- | | Repo | hoox-sh/pyne-worker | | Role | Edge POST /run + alerts + libraries + cron | | Runtime | Cloudflare Workers (Python) | | Engine | Vendored hoox-pyne (pynescript.runtime) | | In this repo? | No | This Worker is a thin wrap. CLI, LSP, Flask Pro API, Numba desk compile, and the language SoT stay in hoox-sh/pyne. Conceptual model Interface surface | This Worker | Use PYNE (hoox-pyne) instead | | --- | --- | | POST /run + alerts + libraries | pyne CLI, pyne-lsp, VS Code | | R bars, m cron, L webhooks | Flask Pro API, Docker, /run/batch | | Vendored engine (./scripts/syncvendor.sh) | Live package, Numba, corpus harness | | s / KB / K bars / MB envelope | Uncapped research on your machine | Taxonomy | Name | What it is | | --- | --- | | PYNE | Language SoT (this repo) | | PyneTS | TypeScript library (@hoox-sh/pynets) | | pyne-worker | Python Cloudflare evaluate host | | pyne-agent-worker | NL authoring host (optional validate via this Worker) | Do not create pynescript/pyne-worker/ or pynescript/pine-worker/. The leftover name pine-worker is a TypeScript Cloudflare experiment (hoox-sh/pine-worker). It is not this host and is not documented on this site. TypeScript library work is PyneTS. Relationship to PYNE Python Runtime remains the oracle. The Worker vendors a snapshot; it can lag about.py. After a PYNE release, operators re-run ./scripts/sync_vendor.sh in the Worker checkout before wrangler deploy. Shared JSON: evaluate contract. Alerts: Alerts. Quick links Evaluate (POST /run) Deploy GitHub README See also pyne-agent-worker PyneTS Pro API usage Ecosystem --- FILE: docs/pyne/pyne-worker/run.mdx Copyright (C) - jangoblockchained This file is part of pynescript. SPDX-License-Identifier: AGPL-.-or-later --- title: "pyne-worker evaluate" description: "POST /run, libraries, alerts, scripts, ingest, and m cron on the Python edge host." --- pyne-worker evaluate Abstract The Worker’s evaluate path is POST /run with the same envelope as Flask: script + OHLCV + mode → plots / series / events / alerts. Auth is X-API-Key (APIKEY secret). Wall timeout defaults to s. Sister repo: hoox-sh/pyne-worker. Contract: evaluate contract. Endpoints | Method | Path | Auth | Role | | --- | --- | --- | --- | | GET | /health | no | Liveness + binding flags | | POST | /run | yes | Evaluate (script or scriptid) | | POST | /ingest | yes | Upload OHLCV to R | | POST | /scripts | yes | Deploy a script | | GET | /scripts | yes | List deployed scripts | | GET | /scripts/:id | yes | Get deployed script | | DELETE | /scripts/:id | yes | Delete deployed script | | GET | /cron/jobs | yes | List bar-close jobs | | PUT | /cron/jobs | yes | Replace job list | | POST | /cron/run | yes | Trigger scheduler | | POST | /feed/refresh | yes | Pull klines into R | POST /run Accepts data (Pro API name) or ohlcv. Omit bars and pass symbol + timeframe to load R history. ``json { "script": "//@version=\nindicator(\"t\")\nplot(close)", "ohlcv": [ {"open": , "high": , "low": , "close": , "time": , "volume": } ], "symbol": "BTCUSDT", "mode": "auto" } ` | Field | Notes | | --- | --- | | script | Inline Pine | | scriptid | Load a deployed script instead of script | | ohlcv / data | Bars; or omit and use R | | mode | interpret (default) · compile · auto | | inputs | input. overrides (forces interpret under auto) | | libraries | [{namespace, name, version, source}] — max | | profiler | Per-line interpret timings | | timeoutseconds | Wall budget (default , cap ) | | webhookurl | Per-job L alert URL (else ALERTWEBHOOKURL) | Success includes status, plots, series, plotmeta, alerts, events, drawings, inputs, logs, meta. Failures use error / errorkind. Deployed scripts can store libraries and inputs. scriptid and cron reuse them. Alerts and trades alert() / alertcondition() export on /run (same engine as Pro API). L HTTP POST: secret ALERTWEBHOOKURL or per-job webhookurl. Optional TRADE_SERVICE binding forwards strategy events to trade-worker. Remove the services block if that Worker is not in the account (fails closed). Limits | Cap | Value | | --- | --- | | Script | KB | | Bars | K | | Envelope | MB | | /run wall | s | | Rate | req / s | See also pyne-worker Deploy Alerts Flask /run PyneTS — in-process RuntimeResult, not HTTP pyne-agent-worker — optional generate → /run` loop --- FILE: docs/pyne/pynets/cli.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "PyneTS CLI" description: "pynets check / format / run / dump / info — Rich TTY, JSON on pipes. run calls Runtime.run, unlike Python pyne run." --- PyneTS CLI Abstract pynets is a Bun CLI for parse, format, AST dump, and evaluate. TTY output is a Rich-inspired panel (PYNE volt). Pipes and --json stay machine-readable. Exit codes: ok, syntax/runtime, usage. Unlike Python pyne run (compile-only smoke), pynets run calls Runtime.run. Default --mode interpret. --mode compile / auto select JS emit (@hoox-sh/pynets .., including this repo’s pynets/ pin). Conceptual model Interface surface ``text usage: pynets [file] [--bars N] [--mode interpret|compile|auto] [--commission N] [--slippage N] [--pyramiding N] [--json] [--plain] [--rich] [--full] [--indent N] ` | Command | Action | File required | | --- | --- | --- | | check | Parse-only validation | yes | | format | Parse → unparse (pretty on TTY) | yes | | run | Runtime.run on synthetic OHLCV | yes | | dump | ASDL-shaped AST | yes | | info | Version and runtime extras | no | | help / -h | Usage | no | | Flag | Meaning | | --- | --- | | --bars N | Synthetic bar count for run (default , max ) | | --mode MODE | interpret (default) \| compile \| auto | | --commission N | Broker commission fraction (e.g. .) | | --slippage N | Broker slippage in price units | | --pyramiding N | Max same-direction adds | | --json | Machine-readable run / info | | --indent N | AST dump indent (default ) | | --full | Print the full dump (no -line cap) | | --plain | Disable Rich (wins over FORCECOLOR) | | --rich | Force Rich (put after the command) | | -h, --help | Help | Path resolution resolveUserFile takes only the path you typed (cwd-relative or absolute). No search path. Missing file → exit (error: file not found). Synthetic bars run builds a ramp: close = + i, high/low = close ± ., volume = , time = + i . Symbol is hardcoded AAPL. This is a desk smoke, not a data provider. Internals | Path | Role | | --- | --- | | src/cli.ts | Arg parse, commands, exit codes | | src/cli/rich.ts | TTY paint, help, tables, sparkline | | src/index.ts | parse / unparse / dump / Runtime | run constructs new Runtime("AAPL", { broker, mode }).run(source, syntheticBars(bars)). Invariants & edge cases . Pipe-safe. Non-TTY check prints ok. Non-TTY run prints JSON. . --rich must come after the command (pynets run x.pine --rich), because flags before the command are stripped for Rich detection. . Unknown flags and extra positional args are usage errors (exit ). . run default mode is interpret, matching new Runtime() — not Pro API auto. . --bars is clamped; negative / non-integer → usage error. Worked examples `bash pynets check script.pine pynets format script.pine pynets dump script.pine --full pynets run script.pine --bars pynets run script.pine --mode compile --json pynets run strat.pine --commission . --pyramiding pynets info --json ` CI: `bash pynets check script.pine stdout: ok exit / / ` Failure modes | Symptom | Cause | Fix | | --- | --- | --- | | Exit missing file | No path / ENOENT | Pass a real file | | Exit on check | Syntax error | Read the ANTLR message | | run JSON has error / error_kind: parse | Script does not parse | pynets check first | | Expected compile-only like pyne run | Different CLI | See modes | | Rich garbage in logs | TTY detected | --plain` or pipe | See also Install Runtime Python CLI Modes --- FILE: docs/pyne/pynets/compile.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "PyneTS compile" description: "JS bar-loop emit for mode compile | auto. Python object-mode analog — not Numba. auto falls back to interpret." --- PyneTS compile Abstract PyneTS compile (mode: "compile" / "auto") emits a JavaScript bar-loop function. It is the analog of Python object-mode compile, not Numba nopython. There is no LLVM, no numba, no disk IR cache like PYNECOMPILECACHEDIR. Landed in @hoox-sh/pynets ... Interpret is unchanged. This PYNE checkout’s pynets/ pin is v.. and includes src/runtime/compile/. Python Runtime remains the oracle. Conceptual model | | Python compile | PyneTS compile | | --- | --- | --- | | Backend | Numba nopython or object-mode Python loop | JS emit only | | Default Runtime.run | interpret (env override) | interpret | | auto | try compile, fallback interpret | same | | import / foreign request. | limited; often fallback | inline registered libs; foreign → na | | Alerts / drawings / strategy | object-mode path | h.strategy, drawings, alertcondition | Interface surface ``ts import { compileScript, transpile, compileEligible, clearCompileCache, compileCacheStats, runScript, CompileError, } from "@hoox-sh/pynets"; new Runtime("AAPL", { mode: "compile" }).run(src, bars); new Runtime("AAPL", { mode: "auto" }).run(src, bars); ` | Name | Role | | --- | --- | | compileEligible(tree) | { ok, reason? } | | transpile / compileScript | Emit + wrap CompiledScript | | compiled.run(open, high, low, close, volume, time, extras) | Columnar execute | | clearCompileCache / compileCacheStats | Process-local emit cache | CLI: pynets run script.pine --mode compile or --mode auto. What emit covers (..) Strategy object-mode (h.strategy / fills / events), UDF series state (src[], var locals), array / map / matrix, UDT (Type.new, field get-set, method), drawings (label / line / box), named UDF kwargs, input overrides, color., enums, request.security same-symbol passthrough (foreign → na), barstate. / syminfo stubs, calendar, str. / str.format, import (inline registered sources; unresolved → na), session. / chart., log., ticker.new / standard / heikinashi, request.currencyrate, alertcondition. mode: "auto" no longer skips compile when inputs are set. Internals | Path | Role | | --- | --- | | src/runtime/compile/engine.ts | compileScript, runScript, cache | | src/runtime/compile/emit.ts | JS text emit | | src/runtime/compile/emitcall.ts | Builtin / UDF calls | | src/runtime/compile/emitudf.ts | User functions | | src/runtime/compile/runtime.ts | Host helpers used by emitted code | | src/runtime/interpret.ts runCompiled / runAuto | Runtime.run wiring | Do not edit src/runtime/compile/ and src/runtime/interpret.ts in the same agent turn. Parent wires Runtime.run({ mode }). Invariants & edge cases . Not Numba. Do not document nopython, njit, or PYNECOMPILECACHEDIR for this package. . Python still wins if compiled plots disagree with Python interpret on the same script+bars — unless Python compile is the thing being compared, in which case compare apples to apples (object vs object). . import / foreign request. stay interpret-or-na. No invented bars. . result.mode reports what ran (interpret or compile). After auto fallback, read compilefallbackreason. . Compile failures in mode: "compile" return an error envelope; auto falls back. Worked examples `ts const interpret = new Runtime("AAPL", { mode: "interpret" }).run(src, bars); const compiled = new Runtime("AAPL", { mode: "compile" }).run(src, bars); const auto = new Runtime("AAPL", { mode: "auto" }).run(src, bars); if (compiled.error) { // inspect compiled.error / errorkind } console.log(auto.mode, auto.autobackend, auto.compilefallbackreason); ` `bash pynets run script.pine --mode auto --json ` Failure modes | Symptom | Cause | Fix | | --- | --- | --- | | CompileIneligibleError / fallback | Surface not emitted | interpret, or extend emit (parity first) | | Plots differ interpret vs compile | Emit bug | Add test/compile_.test.ts`; Python is still SoT | | Expecting Numba speedups | Wrong mental model | This is JS object-mode | | Edited compile + interpret together | Review / merge hazard | Split the change | See also PyneTS runtime Parity Python compiler overview Python Numba path Modes --- FILE: docs/pyne/pynets/index.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "PyneTS" description: "TypeScript / Bun library port of PYNE — parse, unparse, Runtime.run. Python remains the semantic source of truth." --- PyneTS PyneTS (@hoox-sh/pynets, currently ..) is the TypeScript / Bun library for Pine Script: parse, unparse, and Runtime.run. Public names match Python pynescript. Python pynescript.runtime is the source of truth — when semantics disagree, Python wins. This is not a Cloudflare Worker, not a Numba compile path, and not a TradingView platform substitute. This PYNE checkout pins the pynets/ submodule at annotated v.. (@hoox-sh/pynets .. — interpret + JS compile, matching npm). Python pynescript.runtime remains the semantic oracle. bun add / npm install / esm.sh. CLI still needs Bun. na is null. Lookback, TA call-sites, foreign security → na. check / format / run / dump / info. Pipes stay JSON. JS bar-loop emit. mode interpret | compile | auto. Compare plots against Python Runtime.run on the same bars. PYNE · PyneTS · pyne-worker · pyne-agent-worker · AXIS · HOOX. Abstract Most TypeScript Pine ports rewrite the language. PyneTS does not. It is the library-shaped port of PYNE: the same ANTLR grammar, the same ASDL field names (kind, lineno, coloffset, …), the same interpret contract. `` Source (.pine / .pyne) → shared PYNE .g (TS ANTLR target — not forked here) → ASDL AST → bar-loop (interpret | JS compile | auto) → plots / series / events / drawings / alerts ` Standalone checkout: github.com/hoox-sh/pynets. PYNE consumes it only as the pynets/ git submodule — never copy sources into hoox-sh/pyne. `ts import { parse, unparse, Runtime } from "@hoox-sh/pynets"; const src = //@version= indicator("sma") plot(ta.sma(close, )) ; const tree = parse(src); unparse(tree); const out = new Runtime("AAPL").run(src, [ { close: }, { close: }, { close: }, { close: }, ]); // out.plots, out.series, out.count // new Runtime("AAPL", { mode: "compile" }) — JS bar-loop emit (not Numba) // mode: "auto" tries compile, then interpret ` Conceptual model Invariant: PyneTS is a library you import. pyne-worker is the production Python Cloudflare isolate for POST /run. AXIS engines still call Python (Flask, Pyodide wheel, Worker proxy) — they do not import @hoox-sh/pynets today. Interface surface | Name | Role | | --- | --- | | parse(source) | ANTLR → ASDL tree. Throws PinescriptSyntaxError | | unparse(tree) | Tree → normalized source | | dump(tree) | Debug dump | | tokenize(source) | Lexer tokens | | new Runtime(symbol, options).run(source, bars, extra?) | Bar-loop evaluate | | Runtime.stream(source) | Push-driven re-eval | | Runtime.runProvider(source, provider) | Fetch bars then run | | compileScript / transpile | JS emit (not Numba) | | CLI pynets | check format run dump info | Default mode is interpret. Constructor and per-call extra accept mode: "interpret" | "compile" | "auto". What this is not | Confused with | Reality | | --- | --- | | pyne-worker | Python Cloudflare Worker. Vendors pynescript.runtime. POST /run. See pyne-worker. | | pyne-agent-worker | NL authoring host. Not an evaluate library. See agent. | | Python pyne run | Compile-only smoke on synthetic bars. PyneTS pynets run calls Runtime.run. | | Numba | Python compile backend. PyneTS compile is JS emit (object-mode analog). | | AXIS engine | AXIS does not load this package. | Internals (repo paths) Standalone (/home/jango/Git/pynets or the published package): | Path | Role | | --- | --- | | src/index.ts | Public barrel — do not re-export generated ANTLR | | src/ast/helper.ts | parse / unparse / dump / tokenize | | src/ast/nodes.ts | Hand-written ASDL nodes | | src/generated/ | ANTLR TS — do not hand-edit; bun run generate | | src/runtime/interpret.ts | Runtime, interpret host | | src/runtime/compile/ | JS emit (transpile / compileScript) | | src/cli.ts | TTY CLI | | test/interpret_.test.ts | Focused interpret tests | PYNE checkout: pynets/ submodule. Grammar SoT stays in PYNE resource/.g. Invariants & edge cases . Python wins. Do not invent TradingView platform behaviour that Python does not implement. . na is null. Non-finite in/out is na. na == na is true. Any other comparison involving na is false. . Lookback: x[] current, x[] previous, out of range or negative → na. . TA is one sample per bar, state keyed by call-site (Python incremental kernels). . request.security foreign / HTF without a feed → na. The chart series is never silently reused as another symbol. . Do not fork the grammar. Edit .g only in PYNE, then regen here. . Do not edit src/runtime/compile/ and src/runtime/interpret.ts in the same agent turn. . License: AGPL-.-or-later. Worked examples Library (Bun) `ts import { parse, unparse, Runtime } from "@hoox-sh/pynets"; const src = indicator("t") plot(close[]) plot(na == na ? : ); const tree = parse(src); console.log(unparse(tree)); const out = new Runtime("TEST").run(src, [ { close: }, { close: }, ]); // last bar: plots[] = (previous close), plots[] = ` Node + / browser `js import { parse, Runtime } from "@hoox-sh/pynets"; ` `html import { Runtime } from "https://esm.sh/@hoox-sh/pynets"; ` Bun imports TypeScript source (package.json "bun": "./src/index.ts"). Node and browsers use dist/ (bun run build; prepack runs it). The CLI still needs Bun on PATH. Failure modes | Symptom | Cause | Fix | | --- | --- | --- | | Plots disagree with Python | Port bug or invented host behaviour | Fix TS. Python is the oracle. | | request.security returns na | No foreign/HTF feed | Expected. Do not invent bars. | | mode: "compile" misses a builtin | JS emit surface lag | Use mode: "auto" or interpret | | Importing in a Worker and expecting /run | This is a library | Use pyne-worker | | Editing src/generated/ | Overwritten on regen | bun run generate after PYNE .g change | | pynets CLI on Node-only host | bin is src/cli.ts (Bun) | Run under Bun, or call Runtime from Node via dist/` | See also Install Runtime contract CLI JS compile Parity Evaluate scripts (Python) Modes matrix Ecosystem pyne-worker pyne-agent-worker --- FILE: docs/pyne/pynets/install.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Install PyneTS" description: "Add @hoox-sh/pynets with Bun, npm, or esm.sh. CLI requires Bun. Node and browsers use the ESM bundle." --- Install PyneTS Abstract @hoox-sh/pynets is published to the npm registry. Bun imports TypeScript source. Node + and browsers consume the ESM bundle in dist/. The pynets CLI is a Bun script. Package: npmjs.com/package/@hoox-sh/pynets · source: github.com/hoox-sh/pynets. Current version ... Conceptual model | Consumer | Entry | Notes | | --- | --- | --- | | Bun >= . | ./src/index.ts | No bundle required | | Node >= | ./dist/index.js | Run bun run build / prepack | | Browser | ./dist/browser.js | Same package; exports.browser | | CLI | ./src/cli.ts | Needs Bun on PATH | Interface surface Bun (recommended) ``bash bun add @hoox-sh/pynets bunx pynets -- help bunx pynets info ` `ts import { parse, unparse, Runtime } from "@hoox-sh/pynets"; ` Node + `bash npm install @hoox-sh/pynets ` `js import { parse, Runtime } from "@hoox-sh/pynets"; ` npm pack / publish runs prepack → bun run build. If you clone the repo and import from Node without building, resolution fails. Browser (esm.sh) `html import { Runtime } from "https://esm.sh/@hoox-sh/pynets"; ` This is a library import, not a hosted evaluate API. From a PYNE checkout `bash git clone --recurse-submodules https://github.com/hoox-sh/pyne.git cd pyne/pynets bun install bun test ` This PYNE pin is annotated v.. (@hoox-sh/pynets .., interpret + JS compile). That matches npm. dist/ is gitignored — run bun run build in pynets/ if you need the Node/browser ESM bundle from the submodule. Python Runtime remains the oracle. Develop the library `bash git clone https://github.com/hoox-sh/pynets.git cd pynets bun install bun test bun run typecheck bun run build ` Grammar regen (needs Java + PYNE .g): `bash bun run generate ` Resolution order in scripts/generate-antlr.ts: PYNETSGRAMMAR → PYNESCRIPTROOT → parent PYNE checkout → sibling ../pynescript or ../pyne. Internals | Path | Role | | --- | --- | | package.json exports | bun → src/, types → dist/index.d.ts, browser → dist/browser.js | | package.json bin.pynets | ./src/cli.ts | | scripts/build.ts | Node + browser ESM → dist/ (gitignored) | | scripts/generate-antlr.ts | ANTLR TS regen | | .github/workflows/publish.yml | npm publish on v tags | Local tooling is Bun only. Do not add npm/yarn/pnpm lockfiles. The npm registry is used only to publish. Invariants & edge cases . The CLI is not a Node-native binary. npx pynets without Bun will fail. . This package is not a Cloudflare Worker. There is no wrangler here. . Do not npm install pynets (unscoped). The name is @hoox-sh/pynets. . Do not confuse with pip install hoox-pyne (Python). . Engines field: "bun": ">=..", "node": ">=". Worked examples Smoke the CLI `bash bunx pynets info --json { "name": "pynets", "package": "@hoox-sh/pynets", "version": "..", ... } echo 'indicator("x") plot(close)' > /tmp/x.pine bunx pynets check /tmp/x.pine TTY: panel; pipe: "ok" ` Library one-liner `ts import { Runtime } from "@hoox-sh/pynets"; const out = new Runtime("AAPL").run( indicator("sma")\nplot(ta.sma(close, )), [{ close: }, { close: }, { close: }, { close: }], ); if (out.error) throw new Error(out.error); console.log(out.plots); ` Failure modes | Symptom | Cause | Fix | | --- | --- | --- | | Cannot find module '@hoox-sh/pynets' | Typo / old @hoox/pynets name | Use @hoox-sh/pynets | | Node import fails in a fresh clone | dist/ not built | bun run build | | pynets: command not found | No Bun / not on PATH | bunx pynets or bun run src/cli.ts | | Grammar regen fails | No Java / no PYNE .g | Set PYNESCRIPTROOT or PYNETS_GRAMMAR` | | AXIS still evaluates via Python | Expected | AXIS does not consume this package | See also PyneTS hub CLI Python install Ecosystem --- FILE: docs/pyne/pynets/parity.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "PyneTS parity" description: "How to compare @hoox-sh/pynets Runtime.run plots against Python pynescript.runtime. Python wins ties." --- PyneTS parity Abstract Parity here means TypeScript Runtime.run vs Python Runtime.run on the same source and the same bars — not vs TradingView. Interpretcompile alignment inside PyneTS is a second, narrower check (test/compile.test.ts). Workflow when adding or fixing a builtin: read the Python handler, port na / lookback / call-site semantics, wire the name, add a focused test/interpret.test.ts, compare plots if PYNE is present. Conceptual model Interface surface Commands ``bash PyneTS cd /path/to/pynets bun test bun test test/interpretta.test.ts bun run typecheck Python oracle (sister checkout) cd /path/to/pynescript python -c "from pynescript.runtime import Runtime; ..." ` Use /pynets-parity when adding or fixing builtins. Use /pynets-generate when touching grammar output. What to compare | Field | Rule | | --- | --- | | plots / series | Equal length; null Python None; finite numbers match | | events | Kind, direction, bar, id — same contract as strategy events | | errorkind | Same class of failure (parse vs runtime) | | TradingView screenshots | Out of scope as an oracle | na encoding: Python interpret uses None; JSON / TS uses null; Python Numba uses nan. Compare after normalizing non-finite → na. Internals | Path | Role | | --- | --- | | test/interpret.test.ts | Focused interpret cases | | test/compile.test.ts | JS emit vs interpret | | test/helpers/firstparty.ts | First-party fixtures (live in PYNE) | | PYNE src/pynescript/ast/evaluator/builtins/ | Handler SoT | | PYNE tests/testfirstpartytagoldens.py | Python goldens | Do not treat historical pine-worker/test/parity/ JSON as the PyneTS harness. That tree lives in the leftover TypeScript Worker sister (hoox-sh/pine-worker), not here. This PYNE pynets/ pin (v..) includes interpret tests and compile-vs-interpret cases (test/compile.test.ts). Python Runtime remains the oracle. Invariants & edge cases . Python wins ties. If TS and Python disagree, fix TS (unless Python is proven wrong — then fix both + tests). . Do not "improve" semantics. Port na, lookback, and call-site keys as Python implements them. . Same-symbol request.security may passthrough; foreign without data is na on both sides. . Compile-vs-interpret inside PyneTS can pass while TS-vs-Python still fails — check both. . First-party fixtures live in PYNE; do not copy corpora into hoox-sh/pyne. Worked examples Minimal Python vs TS Python: `python from pynescript.runtime import Runtime src = 'indicator("t")\nplot(close[])' bars = [{"close": }, {"close": }] print(Runtime(symbol="TEST").run(src, bars)) ` TypeScript: `ts import { Runtime } from "@hoox-sh/pynets"; const src = 'indicator("t")\nplot(close[])'; const bars = [{ close: }, { close: }]; console.log(new Runtime("TEST").run(src, bars)); ` Last-bar plot should be on both. Adding a builtin . Read src/pynescript/ast/evaluator/builtins/… (Python). . Port into src/runtime/ta.ts / math.ts / … and evalCall in interpret.ts. . Add test/interpret.test.ts. . If PYNE is present, compare Runtime.run plots on the same bars. . bun test and bun run typecheck. Failure modes | Symptom | Cause | Fix | | --- | --- | --- | | Off-by-one vs Python | Lookback / bar_index | Match PineSeries to Python | | null vs | na collapsed | Keep null; do not coerce | | Foreign security equals chart | Invented bars | Return na | | Compile matches interpret, both wrong | Shared host bug | Fix interpret first (Python SoT) | | Copied sources into PYNE | Submodule rule | PYNE consumes pynets/` only | See also PyneTS hub Runtime Python interpretcompile parity Compatibility Contributing --- FILE: docs/pyne/pynets/runtime.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "PyneTS runtime" description: "Runtime.run envelope: na is null, series lookback, call-site TA, request.security without data → na, stream and providers." --- PyneTS runtime Abstract Runtime is the TypeScript counterpart of pynescript.runtime.Runtime. It is not a port of runtime/host.py line-by-line; it matches the public envelope: run(source, ohlcv) → plots / series / events / drawings. Default mode is interpret (AST walk). compile / auto, stream, runProvider, and registerLibrarySource are on @hoox-sh/pynets .., including this repo’s pynets/ pin. Python Runtime remains the oracle. There is no Runtime.evaluate. Conceptual model Interface surface ``ts new Runtime(symbol = "AAPL", options?: RuntimeOptions) runtime.run(source, ohlcv, extra?: RuntimeOptions | InputOverrides): RuntimeResult runtime.stream(source): RuntimeStream runtime.runProvider(source, provider, extra?): Promise runtime.registerLibrarySource(namespace, name, version, source): void ` RuntimeOptions | Field | Meaning | | --- | --- | | inputs | input. overrides (Record) | | broker | { commission?, slippage?, pyramiding? } | | timeframe | Chart TF string, or null | | libraries | LibraryRegistry instance | | mode | interpret (default) \| compile \| auto | Per-call extra merges over the constructor. A plain object of input values (no mode / broker / …) is treated as InputOverrides. OHLCVBar All fields optional: open, high, low, close, volume, time. Missing numeric fields become na at use. RuntimeResult | Field | Meaning | | --- | --- | | series | Titled plot series → Array | | plots | First / default plot column | | plotmeta | { title }[] | | count | Bar count consumed | | scriptname / scripttype | From indicator / strategy | | mode | "interpret" or "compile" (what actually ran) | | autobackend | When mode: "auto" | | compilefallbackreason | Why auto fell back | | events | StrategyEvent[] | | fills | Broker fills | | drawings | line / label / box / … | | logs | log. records | | strategy | Book scalars when the script is a strategy | | error / errorkind | Soft failure (parse \| runtime \| …) | Parse and runtime failures return an error envelope; they do not throw from run (except unexpected host bugs). interpret(source, bars) (helper) does throw if out.error. Stream `ts const s = new Runtime("AAPL").stream(src); s.on("bar", (out) => { / full re-eval on bars so far / }); s.on("error", (err) => {}); s.push({ close: }); s.push({ close: }); s.close(); ` Each push re-runs the script on all bars so far (Python-shaped, not incremental-only). Providers `ts import { Runtime, MemoryProvider } from "@hoox-sh/pynets"; const provider = new MemoryProvider({ AAPL: [{ close: }, { close: }, { close: }], }); const out = await new Runtime("AAPL").runProvider(src, provider, { limit: }); ` Also: StaticMapProvider, JsonBarProvider. request.security without a matching feed stays na. Internals | Path | Role | | --- | --- | | src/runtime/interpret.ts | Host, Runtime, interpretTree | | src/runtime/series.ts | NA, PineSeries | | src/runtime/ta.ts | Incremental TaEngine | | src/runtime/strategy.ts | Broker, fills, risk, OCA | | src/runtime/request.ts | request.security policy | | src/runtime/library.ts | In-process import ns/Name/ver | | src/runtime/provider.ts | Bar providers | | src/runtime/drawings.ts | Drawing book + GC | Invariants & edge cases . na is null. A non-finite number in or out becomes na. . na == na is true. Any other comparison involving na is false. . Lookback: close[] is the previous bar; OOB → na. . Call-site TA. Two ta.sma(close, ) at different AST nodes do not share state. . Foreign / HTF request.security without data → na. Same-symbol simple OHLCV may passthrough; the chart is never invented as another ticker. . v/v bare aliases (sma, ema, rsi, …) resolve when Python does. . Libraries: registerLibrarySource then import namespace/Name/version. Unresolved aliases stub to na (soft), they do not crash the run. Worked examples na and lookback `ts const out = new Runtime("TEST").run( indicator("t") plot(close[]) plot(na == na ? : ), [{ close: }, { close: }], ); // last bar: plots[] === , plots[] === ` Inputs and strategy `ts const out = new Runtime("AAPL", { inputs: { len: }, broker: { commission: ., pyramiding: }, }).run(src, bars); if (out.error) throw new Error(out.error); console.log(out.events, out.fills, out.strategy); ` Failure modes | Symptom | Cause | Fix | | --- | --- | --- | | errorkind: "parse" | Grammar failure | parse(source) / pynets check | | All request.security values null | No foreign feed | Expected. Pass a provider or accept na | | Plots off-by-one vs TV | Lookback / na policy | Compare to Python, not to TradingView | | Stream looks "slow" | Full re-eval each push | Expected host model | | mode in the result is interpret after auto | Compile ineligible / error | Read compilefallback_reason` | See also JS compile Parity Python series and history request. Evaluate scripts --- FILE: docs/pyne/reference/compatibility.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Compatibility guarantee" description: "Grouped map of what PYNE implements for Pine Script v/v — working, partial, residual, and by-design out of scope. hoox-pyne ..." --- Compatibility guarantee Abstract PYNE implements Pine Script v/v language core as an inspectable pipeline: parse → AST → bar-loop evaluate. The map below is the product-facing compatibility surface for hoox-pyne ... It is not TradingView platform identity (chart host, proprietary data, editor-only UI) and not bit-identical results vs the hosted platform on every script or bar. Name-level dispatch lives in Pine v surface (docs/pinevfullsurfaceinventory.md). Gaps: Missing features. Checkmarks: Implementation status. Conceptual model Legend | Mark | Meaning | | --- | --- | | Working | Implemented, usable, covered by first-party tests | | Partial | Callable but mock, host-bound, or incomplete vs hosted Pine | | Residual | Known open hole on an otherwise landed surface | | Out of scope | Intentionally not a TV platform clone | Dispatch snapshot (--): callables, missing vs the public TV v function list ( symbols). A registered name is not the same as hosted-platform semantics. Language, series, TA hot path, collections, strategy broker, drawings, alerts, libraries, interpret Runtime. request. feeds (honest na), compile eligibility, compile alerts, PyneTS vs Python oracle. Plot MISMATCH corpus tail (Pp), PYNESERIESRING default off. Pixel chart, editor UX, foreign live data, bit-identical TV bars. Interface surface Corpus (set– · scripts · --) Open-source sets, not shipped in git. Not a TradingView platform score. | Suite | Rate | Notes | | --- | ---: | --- | | Parse + unparse | .% (/) | Residual: one intentional invalid line-wrap demo | | Runtime interpret ( bars) | % excl. EXPECTEDFAIL | OK + listed demos | | set Runtime | / | Interpret (and prior compile sweep) | EXPECTEDFAIL are path-listed demos (runtime.error library guards, lower-TF security, pathological loops) — not silent suppression. Grouped map | Area | Status | Notes | | --- | --- | --- | | ANTLR grammar v/v | Working | Multiline strings, soft keywords, bitwise, typed UDF returns (FunctionDef.returns) | | Parse → unparse | Working | Structural fidelity; whitespace may differ | | var / varip / := | Working | Execution-site init; start-of-bar carry | | Functions, methods, type annotations | Working | | | Control flow, switch, enums | Working | Strict bool; na is false in conditions | | UDTs + .new | Working | Field defaults on compile (..) | | Libraries export / import ns/Name/ver | Working | In-process registerlibrarysource; Runtime.run(..., libraries=) | | String interpolation, tuple unpack, method chaining | Working | | | Series [] history | Working | OOB / negative → na (not wrap) | | Area | Status | Notes | | --- | --- | --- | | pynescript.runtime.Runtime | Working | Package SoT; backend.runtime re-exports | | Modes interpret / compile / auto | Working | Defaults differ by surface — modes | | Series caps | Working | PYNESERIESCAP default on | | Incremental ta. | Working | Default on; PYNETAINCREMENTAL= full recompute | | Derived OHLCV skip | Working | Unused hl/hlc/ohlc/tr not written; ta.vwap still updates hlc | | timeoutseconds | Working | Library / edge / Flask /run + /run/batch (omit = no budget) | | Ring buffer | Residual | PYNESERIESRING default off | | Area | Status | Notes | | --- | --- | --- | | MAs | Working | sma/ema/rma/wma/hma/vwma/kama/dema/tema/alma incremental | | Oscillators | Working | rsi/macd/stoch/cci/cmo/tsi/roc/wpr | | Volatility / structure | Working | atr (Wilder rma(tr)), bb, kc, stdev, highest/lowest, linreg, adx/dmi, supertrend, sar | | Volume | Working | Incremental obv/wad/wvad/cmf/klinger/mfi/vwap/nvi/pvi | | Supertrend | Working | Locked mid±factor·ATR (inc ≡ compile ≡ Numba). TV band ratchet is out of scope | | Area | Status | Notes | | --- | --- | --- | | array. | Working | Negative indices; UDT sortfield + binarysearch | | matrix. | Working | LA (det/inv/eigen…), predicates, sortfield | | map. | Working | | | Area | Status | Notes | | --- | --- | --- | | entry / close / cancel / events | Working | StrategyEvent stream | | exit stop/limit + OCA | Working | OHLC path, not tick path | | profit / loss | Working | Ticks × mintick from entry avg | | fromentry / qtypercent | Working | Interpret + compile | | Trail | Working | trailoffset / trailpoints / trailprice on OHLC high/low (dual-host). Tick path is out of scope | | Risk cascade | Working | allowentryin, max size/drawdown/cons-loss-days/intraday | | Open/closed trade fields + MAE/MFE | Working | Extremes from bar high/low | | Pending-fill average (pyramiding ≤ ) | Working | F | | Licensed broker / exchange fills | Out of scope | In-process model only | | Area | Status | Notes | | --- | --- | --- | | plot / plotshape / plotchar / plotarrow / hline / bgcolor / fill | Working | Registry + columnar capture | | line / box / label / table / polyline / linefill | Working | .all, set / delete fold on compile | | maxcount GC | Working | Interpret registry | | forceoverlay | Working | Export payload | | Pixel paint | Out of scope | AXIS / clients consume series + drawings | | Compile hline/fill/bgcolor/plotshape keys | Working | First-party key sets match interpret. Harness ignore flags stay optional CLI | | Area | Status | Notes | | --- | --- | --- | | Same-symbol simple OHLCV / HTF | Working | Allowlisted ta.sma/ema/rsi/atr on HTF | | Foreign / complex request.security | Partial | Resolves to na (no invented chart-close) | | request.footprint / financial / economic | Partial | Mock / feed-scaled; not a live vendor | | input. including active / enum | Working | Values + metadata | | Bid/ask omitted | Partial | na when host does not supply | | Real multi-symbol market data (B) | Out of scope | Needs a host adapter | | Area | Status | Notes | | --- | --- | --- | | alert() / alertcondition() | Working | Frequency rules; /run export | | L webhooks | Working | webhookurl / ALERTWEBHOOKURL | | Compile alerts | Partial | Interpret is the alert oracle | | Numba + object-mode compile | Working | mode=auto falls back; import / request. / inputs= skip compile | | Warm compile / disk IR | Working | Prewarm CLI/API; cache meta v | | Interp compile plot parity | Partial | Harness + first-party goldens; residual corpus MISMATCH tail (Pp) | | CLI / LSP / Pro API / Docker | Working | pyne / pyne-lsp; Flask /run | | Flask /run libraries | Working | Max ; also /run/batch | | Flask timeoutseconds | Working | Optional on /run and /run/batch; omit / ≤ = no timeout | | Surface | Status | Notes | | --- | --- | --- | | Python Runtime | Working | Language oracle | | PyneTS @hoox-sh/pynets .. | Partial | Interpret + JS compile on npm; Python Runtime is the oracle | | pynets/ submodule in this repo | Partial | Pin v.. — same surface as npm (interpret + JS compile) | | pyne-worker | Working | Thin Python CF host; vendors engine (may lag ..). Docs: pyne-worker | | pyne-agent-worker | Working | NL authoring; optional /run validate. Docs: agent | Residual and out of scope (compact) | Item | Class | | --- | --- | | Interpretcompile value MISMATCH corpus tail | Residual (Pp) | | PYNESERIESRING default-on | Residual (flagged off) | | Pixel chart / editor word-wrap / TV UI | Out of scope | | Foreign live fundamentals without a feed | Out of scope | | TV Supertrend band ratchet | Out of scope | | Tick-path trail / broker | Out of scope | | Bit-identical every recursive smoother vs TV | Out of scope | | Parallel bars / whole-script vectorization | Out of scope | Dual-host plot parity Same script + OHLCV under mode="interpret" and mode="compile"; series compared with nan-aware allclose (rtol=e-, atol=e-). | Artifact | Role | | --- | --- | | scripts/compareinterpcompile.py | Corpus harness → .cache/interpcompileparity.json | | tests/testinterpcompileparity.py | Always-on smoke | | tests/testfirstpartytagoldens.py | ATR / Supertrend / Keltner dual-host | Known contract differences (not silent value bugs): . Foreign / complex request. → na on both hosts when no feed. . First-party hline / fill / bgcolor / plotshape keys match; harness --ignore-hline-keys / --ignore-fill-keys remain optional for leftover corpus noise. . Pivot-starved Auto Fib → matching runtime.error (botherrorsame), not invented pivots. Internals | Artifact | Role | | --- | --- | | docs/compatibilityguarantee.md | Historical long-form; this page is the map | | COMPATIBILITY.md | Repo-root distillation of this map | | docs/pinevfullsurfaceinventory.md | Name-level dispatch tables | | docs/knowndivergences.md | Semantic deltas vs public Pine docs | | tests/fixtures/parity/ | First-party strategy fixtures | Invariants & edge cases . Parse success ≠ evaluate success. Round-trip can still depend on request. data. . Dispatch success ≠ hosted semantics. missing vs the public function list does not mean TV-identical bars. . Interpret is the oracle. Compile matches where eligible; mode=auto falls back. . Plots are not a renderer. Registry + series export; pixels are AXIS / clients. . Floating-point is statistical for some recursive smoothers — Numerical validation. . Libraries need in-process registration; there is no TV CDN. Worked examples Round-trip ``python from pynescript.ast.helper import parse, unparse src = open("strategy.pine").read() assert unparse(parse(src)) structural; whitespace may differ ` Interpret compile `bash python scripts/compareinterpcompile.py --bars --limit python scripts/compareinterpcompile.py --ignore-hline-keys --ignore-fill-keys pytest tests/testinterpcompileparity.py -q ` Failure modes | Observation | Read as | | --- | --- | | Parse error on a new TV release-notes feature | Surface gap — Missing features | | Numerical drift on a custom smoother | Bar-mode vs list-mode; na; known divergences | | Interpret vs compile series mismatch | Foreign request., UDF last-assign na, leftover corpus keys | | Strategy equity ≠ TV | Commission / slippage / OHLC vs tick path | | import unresolved | Library not in libraries= / registry | | /run UNKNOWNFIELDS | Extra key not in RUNSCHEMA` | See also Implementation status Missing features Pine v surface inventory Numerical validation Runtime modes Roadmap --- FILE: docs/pyne/reference/ecosystem.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Ecosystem" description: "PYNE, PyneTS, pyne-worker, pyne-agent-worker, AXIS, and HOOX — what each one is and is not." --- import { PackageVersions } from "/snippets/package-versions.jsx" Ecosystem Abstract The HOOX family splits language, chart, and execution. Mixing the names is the most common docs failure: pyne-worker is not pynets, and AXIS does not place HOOX orders. | Product | Role | Docs | | --- | --- | --- | | PYNE (hoox-pyne, import pynescript) | Python language SoT: parse, LSP, Pro API, Runtime.run | this site | | PyneTS (@hoox-sh/pynets) | TypeScript / Bun library + CLI | PyneTS | | pyne-worker | Python Cloudflare isolate — POST /run | pyne-worker · HOOX isolate | | pyne-agent-worker | NL → Pine (Workers AI) | agent · AXIS plugin | | AXIS | Charting PWA (engines call Python) | AXIS | | HOOX | Edge trade mesh | HOOX | Conceptual model Invariant: evaluation never requires a proprietary chart host. AXIS is an optional chart. HOOX is an optional execution mesh. PyneTS is an optional TypeScript import. Interface surface Published versions (static; a -minute cron rewrites the table when a registry moves): Checkouts (this repo vs sisters) | Checkout | What you open | | --- | --- | | hoox-sh/pyne | PYNE SoT (hoox-pyne ..) | | hoox-sh/pynets | Standalone @hoox-sh/pynets .. (JS compile + stream) | | pynets/ submodule in this repo | Pin v.. — interpret + JS compile (same surface as npm) | | hoox-sh/pyne-worker | Python edge /run | | hoox-sh/pyne-agent-worker | NL authoring (not in this tree) | | hoox-sh/axis | AXIS PWA | | hoox-sh/hoox | Mesh monorepo | Evaluate contract Flask POST /run, pyne-worker POST /run, and in-process Runtime.run share the evaluate contract: script + OHLCV + mode → plots / events / alerts. PyneTS speaks the library form of that envelope (RuntimeResult), not HTTP. Internals Docs are authored in product trees and synced to hoox.sh: | Tree | Public base | | --- | --- | | pynescript/docs/pyne/ | /pyne/docs | | axis/docs/ | /axis/docs | | hoox/docs/ | /docs | Do not author in hoox-landing-page/content/ — that is a sync artifact. Invariants & edge cases . Python wins language semantics. PyneTS does not invent TradingView behaviour. . AXIS ≠ engine. AXIS never embeds a closed interpreter; engines are plugins. . AXIS ≠ execution. Drawing a strategy on AXIS does not call trade-worker. . Alerts ≠ orders. alert() webhooks are not StrategyEvent forwards unless you point them at the HOOX gateway yourself. . Do not pip install pyne or pip install pynescript for this project. Dist name is hoox-pyne. . Do not treat the submodule package name as the published npm name. This checkout pins v..; npm is @hoox-sh/pynets ... Python Runtime remains the oracle. . pine-worker is a leftover TypeScript Cloudflare experiment (hoox-sh/pine-worker). It is not a product evaluate host and has no pages on this site. Worked examples Pick a surface | You want | Use | | --- | --- | | Parse / lint / LSP on a laptop | pip install "hoox-pyne[lsp]" | | Embed evaluate in TypeScript | bun add @hoox-sh/pynets | | HTTP evaluate at the edge | Deploy pyne-worker | | Chart + editor | AXIS PWA + Flask or Pyodide | | Live CEX orders from strategy events | pyne-worker TRADESERVICE → HOOX | | Chat → script | pyne-agent-worker | Failure modes | Mix-up | What goes wrong | | --- | --- | | Opening pyne-worker/ or pine-worker/ inside PYNE | Directories do not exist — clone the sister repos; TS library work is @hoox-sh/pynets | | Expecting AXIS to flatten a position | No trade-worker hop | | Pointing AXIS at pynets as an engine | No such engine. Use Flask / Pyodide / Worker proxy | | pyne run vs Runtime.run vs pynets run | Different defaults — see modes | See also PYNE index PyneTS pyne-worker pyne-agent-worker Modes Evaluate contract AXIS HOOX HOOX live trading AXIS and HOOX --- FILE: docs/pyne/reference/implementation-status.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Implementation status" description: "Checklist-style status of Pine Script series, strategy, builtins, and language constructs in PYNE." --- Implementation status Abstract This annex summarizes the feature matrix maintained in docs/pinescriptimplementationstatus.md: a large // inventory of series variables, strategy fields, and related surfaces. It is the human-readable twin of the full surface inventory (dispatch-oriented, auto-countable). Status here is implementation judgment, not marketing completion percentage. Conceptual model Interface surface Legend | Mark | Meaning | | --- | --- | | | Implemented and usable | | | Partial / stub / mock | | | Missing | | ️ | By design out of scope | | | Editor/platform N/A | Component rollup | Component | Posture (- consolidation) | | --- | --- | | Parser (ANTLR) | Complete for v/v core + recent multiline / export work | | Evaluator | Full bar-loop with var/varip, reassignment | | Builtins | + historical count; callables on live dispatch inventory (--) | | TA | + ta. keys in inventory | | Collections | array / matrix / map complete on exercised surface | | Strategy events | StrategyEvent, parity corpus, risk enforcement | | Drawing / plot | Registry effects; AXIS is external | | Linter | Present (pynescript lint) | | Data providers | Mock, Yahoo, AlphaVantage, CCXT + datafeed wiring | | LSP | Diagnostics, completion, hover, format, symbols, defs/refs, … | | PyneTS | @hoox-sh/pynets .. (npm + this repo’s pynets/ pin v..) — interpret + JS compile. Python Runtime remains the oracle — see PyneTS | | pyne-worker | Python Cloudflare evaluate host (sister repo) — docs | | pyne-agent-worker | NL authoring host (sister repo) — docs | | Numba compile path | MVP + object-mode fallback | | Incremental ta. | Default on (PYNETAINCREMENTAL); .. volume obv/wad/cmf/klinger; .. nvi/pvi | | Interpret bar-loop | .. dispatch inlining + unused derived-series skip | Series & context (illustrative families) Fully listed in the source doc; representative groups: Price: open/high/low/close/volume/hl/ohlc/… Time: year…second, timeclose, timenow, … Barstate / chart: barindex, barstate., lastbar syminfo. ticker, mintick, session, fundamentals fields tracked as session. / dividends. / earnings. context fields tracked as strategy. position, trades, equity, risk, OCA, commission constants July enhancements called out in source Full strategy event emission with bar context strategy.long / strategy.short constants var / varip + := reassignment Parity fixtures for cross-implementation validation Risk helpers and performance series expansion Internals | Path | Role | | --- | --- | | docs/pinescriptimplementationstatus.md | Exhaustive checklist (do not paste wholesale here) | | docs/pinevfullsurfaceinventory.md | Dispatch-centric inventory | | src/pynescript/ast/evaluator/builtins/ | Per-namespace implementations | | scripts/regenerateinventorysummary.py | Inventory regeneration aid | Invariants & edge cases . Checklist lag: a may trail a commit by a day; prefer tests when shipping. . Context fields may be defaults rather than live exchange feeds. . Partial stubs still appear callable — inventory marks when docstrings/heuristics say mock. . Namespace counts ≠ quality. Many drawing setters are thin mutators; TA is heavier. Worked examples Smoke a strategy series field ``python from pynescript.ast.helper import parse evaluate via Runtime / library API with OHLCV — see runtime docs ` Regenerate inventory summary `bash python scripts/regenerateinventorysummary.py `` Failure modes | Symptom | Action | | --- | --- | | Doc says , runtime AttributeError | File bug; fix dispatcher or demote status | | Partial mock accepted silently | Assert datafeed wiring in tests | | Status doc conflicts inventory | Prefer live dispatch + tests | See also Compatibility map — grouped working / partial / residual / out of scope Missing features Pine v surface Roadmap --- FILE: docs/pyne/reference/missing-features.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Missing features" description: "Known gaps and closed Pine Script v items — H/H/T/T/F/F/L/C shipped; Pp corpus MISMATCH tail remains." --- Missing features Abstract “Missing” in PYNE is a moving frontier. As of -- (hoox-pyne ..), core v support remains very high (~%+ for core language), with post-launch TV items closed (multiline strings, library export const, footprint mocks, risk enforcement, matrix LA, compile-mode strategy, drawing GC, …) plus open-source corpus parse/Runtime hardening, incremental bar-mode TA (including ..–.. volume kernels), alert engine + L webhooks, series caps, warm-compile, pending-fill averaging, and package Runtime SoT (pynescript.runtime; backend.runtime is a shim). Remaining gaps cluster into plot parity residual MISMATCH corpus tail (Pp), by-design platform differences, and editor-only features. Incremental ta.nvi/ta.pvi and Supertrend mid±factor·ATR goldens have landed. Authoritative narrative: docs/missingfeatures.md. Canonical IDs: docs/ROADMAP.md (--). Conceptual model Interface surface Recently completed (do not re-open as “missing”) Summarized — full prose in source doc: | Area | Notes | | --- | --- | | Multiline """ / ''' | Lexer + LexerBase + unparser | | Library export const + types/enums | Parse/AST/runtime registry | | UDT sortfield on array/matrix sort | Implemented | | UDT sortfield on array.binarysearch (August ) | Implemented (interpret + compile, ..) | | input..active | Metadata-driven | | Dynamic request. | Shared resolvers; mock/feed scaling | | Enums runtime + input.enum | Type kind + LSP partial | | Strategy open/closed trades, risk, OCA, commission | Golden tests | | Drawing .all, plot registry, maxcount GC | Effects recorded; GC on package + Pro + AXIS | | Numba compile + object mode + warm-compile (H) | Disk IR cache, prewarm API/CLI, deploy defaults | | Official matrix LA & predicates | det/inv/eigen/… | | missing vs public TV v function reference list ( symbols, --) | | Runtime pinedefslocked + append-only currentseries | Host hygiene (backend + pyne-worker) | | Incremental ta. (MAs, oscillators, bb/kama/stochrsi, volume obv/wad/cmf/klinger/nvi/pvi) | Bar mode; PYNETAINCREMENTAL= to disable | | Series caps (T) | PYNESERIESCAP default ON + goldens | | Pending-fill averaging (F) | Pyramiding ≤ ; interpret + compile broker goldens | | Alert engine + L webhooks (L) | Dual-host /run export, ALERTWEBHOOKURL / webhookurl | | request.security foreign-na policy | Foreign/complex → na both backends; same-symbol OHLCV passthrough | | Plot parity harness (Pp partial) | scripts/compareinterpcompile.py, first-party goldens; residual corpus MISMATCH tail | | fill() export for AXIS | series + plotmeta / compile drawings | Still incomplete / by design | Item | Status posture | | --- | --- | | Real (non-mock) multi-symbol request. market data (B) | ️ By design for library core; feeds optional. Foreign-na policy landed (no invent). | | request.security foreign data | ️ Not filled with real series — emits na for foreign/complex (honest). Same-symbol simple OHLCV may lower. | | Auto Fib / pivot-heavy scripts | ️ Need real pivot structure (or registered ZigZag lib). Flat synthetic bars → intentional runtime.error (insufficient pivots); both hosts should agree (botherrorsame in parity harness). | | Pixel chart host | External (AXIS / clients) | | Some unlimited-history / trim edge cases | High-level support; exotic TV behaviors may differ | | Editor word-wrap / UI-only release notes | N/A | | PyneTS full builtin parity (L) | ️ interpret + JS compile landed in @hoox-sh/pynets ..; Python remains oracle | | Bit-identical every recursive smoother vs TV | Numerical bounds, not bits | | Dual Runtime host copies (H) | largely done — package pynescript.runtime SoT + backend. shims + pyne-worker thin wrap. Residual = worker-only extras (logs/profile, CF first-plot), not a forked bar loop | | Warm-compile product path (H) | SLOs / prewarm / deploy IR cache defaults (-) | | Runtime residual (C) | set– Runtime interpret % excl. EXPECTEDFAIL (--); third-party corpus not shipped | | Series cap (T) | PYNESERIESCAP default ON | | Residual TA inc (T) | R bb/kama/cmo/stochrsi + wma/hma/linreg; .. obv/wad/cmf/klinger; .. nvi/pvi | | Pending-fill (F) | R interpret + compile goldens | | Plot parity residual (Pp) | ️ Harness + first-party goldens (MACD/OBV/ao/aroon + plot keys); corpus value MISMATCH tail remains | | TV Wilder RMA-ATR / supertrend (F) | interpret ATR is ta.rma(ta.tr); Supertrend mid±factor·ATR locked. TV ratchet is out of scope | | Alert webhooks (L) | Pro API + pyne-worker; see Alerts | Post-v launch themes (historical gap list) v launch items (dynamic requests, strict bool, enums, polylines, log., negative indices, truediv, …) are largely supported or intentionally stubbed where platform-bound. – monthly TV updates introduced footprint types, active inputs, multiline strings, export const, UDT sort fields — tracked and largely closed in this repo’s July work. Corpus + performance snapshot (--) Open-source set– ( scripts; measured locally, not shipped in-repo): | Metric | Value | | --- | --- | | Parse + unparse | .% ( / ) — residual: intentional invalid line-wrap docs demo | | Runtime interpret | % excl. EXPECTEDFAIL — OK + intentional demos | | set Runtime | / (%) | | EXPECTEDFAIL class | library runtime.error demos, lower-TF security guards, pathological loops, truncated mid-call scrapes | | tasma bar loop (≈.k bars) | ~.× vs full recompute | | taatr bar loop | ~× | | macd+atr+rsi+sma combo | ~.× | Not TradingView platform parity. OK% excludes only path-listed intentional demos (never soft-suppress unknown failures). Plans: .opencode/plans/---runtime-performance.md. Skill: .grok/skills/pynescript-perf/. Golden: tests/testtaincremental.py, tests/testseriescap.py. Internals | Path | Role | | --- | --- | | docs/missingfeatures.md | Living gap analysis | | tests/testvfeatures.py | v feature coverage | | tests/testpinesurfacegaps.py | Surface gap probes | | tests/testtaincremental.py | Incremental TA ≡ full recompute | | tests/testseriescap.py | Series cap goldens (T) | | scripts/compareinterpcompile.py | Plot parity harness (Pp) | | docs/pinevfullsurfaceinventory.md | Full name-level inventory | | resource/.g + builder | Grammar-level feature work | | .../technicalsubmodules/core.py | smaincupdate, macdincupdate, atrincupdate, … | Invariants & edge cases . Closing a syntax gap requires grammar + builder + unparser + tests, not only an evaluator stub. . Mock data can green tests while production feeds differ — declare data source explicitly. . Compile does not fill foreign request.security data — multi-asset plots stay na on compile unless same-symbol simple OHLCV; do not treat invent-as-close as a fix. . Auto Fib needs pivots — without swing structure (or ZigZag library registration), expect structured insufficient-pivot errors, not fib lines. . “ missing vs reference list” ≠ identical semantics for every edge case. . Perf changes must keep bar-by-bar semantics — no whole-script vectorization; prefer golden tests vs current oracle. . Update this annex when missingfeatures.md changes major status lines. Worked examples Detect multiline string support ``python from pynescript.ast.helper import parse, unparse src = '''//@version= indicator("m") s = """a b""" plot() ''' tree = parse(src) assert '"""' in unparse(tree) or "'''" in unparse(tree) ` Footprint mock call Prefer tests in tests/testvfeatures.py as executable specification for request.footprint methods. Incremental TA parity smoke `bash .venv/bin/python -m pytest tests/testtaincremental.py -q Disable hot path if needed: PYNETAINCREMENTAL= python -c "from pynescript.runtime import Runtime; ..." ` Failure modes | Symptom | Likely gap class | | --- | --- | | Lexer error on triple quotes | Stale generated lexer (should be fixed on main) | | Import member missing | Library export registration | | Empty footprint rows | Mock seed / datafeed not configured | | Risk not blocking entry | Old runtime path; ensure risk kwargs applied | | Runtime TIMEOUT on method-heavy scripts | Missing pinedefslocked on host (fixed --) | | Corpus PARSEFAIL mid-block | Truncated scrape — not a grammar hole | See also Implementation status Pine v surface Roadmap Compatibility Technical analysis (ta.`) Alerts Interpret compile parity --- FILE: docs/pyne/reference/numerical-validation.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Numerical validation" description: "Methodology and error bounds for PYNE technical and math builtins versus reference Pine Script implementations." --- Numerical validation Abstract PYNE’s technical and mathematical builtins are validated for numerical agreement with reference Pine Script implementations under IEEE realities. The long-form report (docs/numericalvalidationreport.md, v., Nov ) documents methodology, per-family error tables, and edge cases. Headline (from that report): max observed relative error .% (ADX, extreme volatility); average error < .%; deterministic ops (SMA, OBV, discrete math) often exact. Treat percentages as report-era measurements, not eternal SLAs — re-validate after changing smoothers or bar-mode semantics. Conceptual model Interface surface Acceptable thresholds (report) | Band | Relative error | Verdict | | --- | --- | --- | | Excellent | < .% | Pass | | Good | < .% | Pass | | Acceptable | < .% | Review | | Unacceptable | ≥ .% | Fail | Metric definitions Absolute error: |ref − pyne| Relative error: |ref − pyne| / |ref| × % (guard zero refs) Max / mean / std over bars and scenarios Category highlights | Family | Notes | | --- | --- | | Moving averages | SMA exact; EMA/WMA/… tiny FP error | | Oscillators | RSI/stoch slightly higher smoothing error still ≪ .% | | Trend | ADX highest in report (.% max) | | Volume | OBV exact cumulative; MFI inherits typical-price FP | | Stats | percentrank exact; variance/stdev excellent | | Math | Discrete exact; transcendentals ~e- class | Dual-host formula alignment (interpret compile) Where fixed, these kernels share one formula contract across interpret (evaluator / incremental) and compile (numbabuiltins) so plot series match bit-identical or maxdiff ≈ on dual-run goldens: | Builtin | Shared contract (summary) | | --- | --- | | ta.rsi | Wilder: SMA seed of first period deltas, then RMA gain/loss; first valid at bar period | | ta.roc | Standard lookback % change; na until lookback (no early .) | | ta.wma | Full non-na window required; no partial-window reweight (nested ROC/WMA safe) | | ta.cum | Running sum; NaNs as ; user series (ad = ta.cum(...)) must not alias builtin A/D | | ta.highestbars / lowestbars | Negative bars-back offset; short/all-NaN → -; oldest extreme on ties | Goldens: tests/testcompilernumba.py (TestInterpCompilePlotParityFixes) and scripts/compareinterpcompile.py. Residual dual-host drift (if any) tends to cluster in nested EMA seed families (DEMA/TEMA SMA-seed vs first-value) rather than these aligned kernels — see Compatibility. Error distribution (report aggregate) | Error range | Share (approx.) | | --- | --- | | Exact | ~% | | < .% | ~% | | .–.% | ~% | | .–.% | ~% | | ≥ .% | % | Internals | Path | Role | | --- | --- | | docs/numericalvalidationreport.md | Full tables + bias analysis | | tests/testtaindicators.py | Executable TA tests | | tests/testindicators.py / builtins tests | Additional numeric guards | | tests/testcompilernumba.py | Interpret compile formula goldens (RSI/ROC/WMA/…) | | scripts/compareinterpcompile.py | Corpus plot-series parity harness | | Bar-mode vs list-mode (pinebarmode) | Scalar-per-bar vs full series — compare like with like | Methodology (report) . Generate synthetic k-bar OHLCV across regimes (trend, range, vol, gaps) . Validate with real histories (equities, crypto, FX samples) . Export reference outputs; run PYNE; element-wise compare . Search systematic bias; inspect tails Invariants & edge cases . na propagation must match Pine truthiness — numerical compare should mask na pairs. . Bar-mode scalars vs full-series list mode can look like “bugs” if misaligned. . Seeded mocks (request.seed) stabilize stochastic feeds for regression. . Recursive smoothers accumulate FP differently across languages; bounds > bits. . Extreme prices (near-zero, huge) tested; still within excellent band in report. Worked examples Relative error helper ``python def relerr(a: float, b: float) -> float: if a == and b == : return . denom = abs(a) if a != else abs(b) return abs(a - b) / denom . ` Run TA tests `bash pytest tests/testtaindicators.py tests/testtaindicators.py -q ` Failure modes | Symptom | Investigation | | --- | --- | | Sudden ADX drift | Check Wilder smoothing / seed bars | | SMA not exact | Off-by-one window or float input casts | | Good unit tests, bad TV export compare | Timezone/session alignment of bars | | Only bar differs | Warm-up / na` initialization | See also Compatibility Runtime technical builtins Implementation status --- FILE: docs/pyne/reference/pine-v6-surface.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Pine v surface inventory" description: "Summary of the full Pine Script v surface inventory — dispatch counts, namespaces, status schema — with pointer to the large source document." --- Pine v surface inventory Abstract The full surface inventory enumerates every registered evaluator builtin, major series/variable, language construct, and known gap against Pine Script v. It is deliberately large (tens of thousands of tokens when fully expanded). This page is the navigable summary; the exhaustive tables live in-repo at: docs/pinevfullsurfaceinventory.md Generated snapshot referenced here: --, from live NodeLiteralEvaluator dispatch, builtinmetadata.json, and design docs. Do not paste the entire inventory into product docs — regenerate and link. Conceptual model Interface surface Status schema | Status | Definition | | --- | --- | | implemented | Handler/parser path exists and is usable | | partial | Mock/stub, incomplete semantics, or metadata-only | | missing | Expected on TV v; not resolving | | ️ by design | Intentionally not full TV platform | | N/A | Editor/UI-only | Each inventory row also carries: Name, Namespace, Kind, Metadata (LSP JSON present?), Source (e.g. dispatch), Notes. Summary counts (--) | Metric | Count | | ---: | ---: | | Dispatch builtins (callable) | | | Dispatch partial-heuristic (stub/mock docstring) | | | Top-level namespaces | | | Official TV v function reference list | symbols — missing in dispatch | Top namespaces by dispatch keys | Namespace | Count | | --- | ---: | | ta | | | matrix | | | strategy | | | array | | | box | | | label | | | table | | | math | | | line | | | str | | | input | | | map | | | request | | | footprint | | | ticker | | | (remaining namespaces) | smaller | Kinds covered function · series/var · constant · declaration · control · operator · type system · literal · semantics · export · import Internals | Path | Role | | --- | --- | | docs/pinevfullsurfaceinventory.md | Full tables + architecture diagrams | | scripts/regenerateinventorysummary.py | Regeneration helper | | scripts/generatebuiltinmetadata.py | LSP metadata from live surface | | Evaluator buildbuiltinmap() | Source of dispatch truth | How to use the full file . Open docs/pinevfullsurfaceinventory.md in the repo or Sphinx/site mirror. . Search by namespace ( ta, strategy, …). . Trust dispatch source rows for “is there a callable?”; read Notes for mock/feed caveats. . After large builtin PRs, regenerate summary counts before quoting them publicly. Invariants & edge cases . Inventory size is a feature — summarization must not drop rows when claiming coverage. . Metadata presence ≠ semantic completeness. . Partial heuristic count () is docstring-based — real partials may be higher. . Language constructs (control/export) may be parser-complete without every runtime edge. Worked examples Quote coverage honestly “Against the public Pine v function reference ( symbols), pynescript registered missing dispatch entries as of --; total callables across namespaces. See docs/pinevfullsurfaceinventory.md.” Programmatic check (sketch) ``bash regenerate after evaluator changes python scripts/regenerateinventory_summary.py `` Failure modes | Mistake | Consequence | | --- | --- | | Pasting KB inventory into Mintlify page | Unreadable docs; broken UX | | Citing counts without date | Stale marketing | | Equating dispatch hit with TV gold | False precision claims | See also Implementation status Missing features Compatibility Runtime builtins --- FILE: docs/pyne/reference/roadmap.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Roadmap" description: "PYNE forward plan — dual-host residual, runtime residual, plot parity harness; H/T/F/L landed (-)." --- Roadmap Abstract The roadmap is a living prioritization document, not a contract. Source: docs/ROADMAP.md (updated --). Core v language/builtins are essentially closed. Product warm-compile (H), series caps (T), pending-fill (F), L webhooks, corpus Runtime residual (C), package Runtime SoT (H largely done), and interpret Round (..) have landed (set– parse .%, Runtime interpret % excl. intentional demos). What remains: H worker-only extras and interpretcompile plot residuals (Pp corpus tail). Incremental nvi/pvi (T) and Supertrend goldens (F) have landed. Conceptual model ``text P docs honesty → P dual-host H (package SoT + shims + worker thin wrap) → P plot parity residual (Pp) → H warm compile · T series caps · F pending-fill · C corpus T nvi/pvi · F Supertrend goldens → P long-horizon (L / L; L ) ` Interface surface Status snapshot | Component | Status | | --- | --- | | Parser ANTLR | v/v + multiline / export const | | Evaluator | incl. full var/varip, ReAssign | | Builtins / TA / collections | broad ( missing vs public TV ref list) | | Strategy events + broker (OCA, commission, risk) | | | Numba + object-mode compile | MVP+ (disk IR cache, mode=auto, timearr, cache recovery) | | Pro API + auth + Docker | (mode default auto; targets api / api-dev / lsp / cli) | | PyPI hoox-pyne | live ..+ (pypi.org/project/hoox-pyne); CLIs pyne / pyne-lsp | | CLI surface (check/format/compile/run/prewarm/…) | + Nuitka binaries + CLI Docker image | | Alert engine + L webhooks | dual-host export + outbound webhooks | | Drawing max_count GC | package + Pro API + AXIS Pyodide | | Package Runtime SoT (pynescript.runtime) | .. — Pro API + worker thin wrap | | Strategy exits (fromentry, qtypercent, trail, risk cascade) | .. | | request.security HTF simple TA + foreign-na | same-symbol OHLCV/HTF allowlist; foreign → na | | Free-tier Pro API guards | bar/script caps, rate, concurrency, mock-only free data | | Dual-host TA goldens (ATR / Supertrend / Keltner) | first-party + testtaincremental CI | | Interpret dispatch + unused derived skip + volume inc (Round ) | .. — bench @ bars: minimal .×, tasma .×, tacombo .× | | Series caps (PYNESERIESCAP) | T | | Warm-compile product path | H (SLOs, prewarm, deploy IR cache) | | Pending-fill averaging (pyramiding ≤ ) | F | | Interpretcompile plot parity | ️ harness + goldens landed; residual MISMATCH tail | | Runtime timeoutseconds | .. — wall-clock circuit breaker on interpret | | UDT array.binarysearch sortfield | .. — interpret + compile (Pine August ) | | Runtime.run(..., libraries=) / POST /run libraries | ..+ — AXIS import ns/Name/ver; export finalize in ..; auto fallback keeps libs | | pyne-worker .. | edge /run libraries + plotmeta/logs; engine vendor .. | | PyneTS (@hoox-sh/pynets) | standalone TS library .. — interpret + JS compile; Python remains oracle | | pyne-agent-worker | sister NL authoring host (POST /v/chat) | | Full TV platform identity | ️ out of scope | Completed themes (do not re-plan as greenfield) Error message / typing hygiene %%pinescript Jupyter magic + sample data helpers pynescript lint Yahoo / AlphaVantage / CCXT + realtime datafeed wiring StrategyEvent emission + parity fixtures Colocated TS extra tool extracted (..). Product TypeScript library is PyneTS. Alert engine + dual-host export + L webhooks Round : series caps, incremental TA (bb/kama/cmo/stochrsi), parse cache, warm compile, pending-fill, drawing GC Round / ..: interpret dispatch inlining, unused derived OHLCV skip, incremental obv/wad/cmf/klinger Open backlog (IDs stable) | ID | Item | Pri | Status | | --- | --- | --- | --- | | H | Dual-host Runtime (package SoT + backend shims + pyne-worker thin wrap ; residual = worker-only extras) | P | largely done | | H | Product warm-compile (SLOs, prewarm API/CLI, IR cache on in deploy) | P | | | C | Corpus set– residual (parse .%; Runtime % excl. EXPECTED) | P | -- | | Pp | Compile/interpret plot parity residual | P | ️ first-party goldens landed; residual corpus MISMATCH tail | | T | Cap currentseries to maxbarsback / SERIESMAX | P | PYNESERIESCAP default ON | | T | Incremental TA for remaining heavy kernels | P | R + .. volume inc + .. nvi/pvi | | F | ATR Wilder / TV supertrend re-baseline only with dedicated goldens | P | mid±factor·ATR locked; TV ratchet out of scope | | F | Pending-fill averaging when pyramiding ≤ | P | | | L | vv converter maturity | P | open | | L | Webhook alerts productization | P | Pro + pyne-worker | | L | Legacy TS Worker builtin parity | P | sister hoox-sh/pine-worker only — not a product-docs target; new TS work is PyneTS | | B | Real (non-mock) request. market data | — | ️ by design; foreign-na policy landed | Longer horizon (not next): ML wrappers, automatic refactor, parallel/distributed eval experiments. Priority recommendation | Horizon | Focus | | --- | --- | | Short | Pp corpus MISMATCH tail; H worker extras (logs/profile) | | Medium | Further nested incremental TA where profiled | | Long | L converter maturity; TypeScript library work is PyneTS (L ) | Landed residual notes (-) Plot parity: Always-on smoke in tests/testinterpcompileparity.py. Full corpus compare: python scripts/compareinterpcompile.py → .cache/interpcompileparity.json. Flags --ignore-hline-keys / --ignore-fill-keys drop structural residuals. Corpus residual (C): set– parse .% (/); Runtime interpret % excl. EXPECTEDFAIL ( OK + intentional demos); set /. Third-party corpora are not shipped in git. Foreign-na: request.security on foreign/complex expressions → na both backends; ChartOHLCVProvider refuses non-chart symbols. Internals | Path | Role | | --- | --- | | docs/ROADMAP.md | Canonical roadmap prose | | docs/PROGRESSREPORT.md | Historical completion narrative | | docs/COMPILERPLAN.md | Compile/numba plan | | scripts/compareinterpcompile.py | Plot parity harness | | .opencode/plans/ | Engineering plans (strategy events, consolidation) | | sister hoox-sh/pyne-worker | Python edge evaluate host — docs | | sister hoox-sh/pyne-agent-worker | NL authoring host — docs | | pynets/ submodule | TS library pin v.. (interpret + JS compile); Python remains oracle | Invariants & edge cases . Roadmap dates lag code — always diff against missingfeatures.md and tests. . Sister repos (pyne-worker, pyne-agent-worker) may absorb “integration” items. . AXIS is a separate product tree (docs/axis) — chart UX roadmap is not PYNE-core. . Avoid reintroducing deleted backups (builder.py.bak, etc.) as “plan items.” . Do not re-open H / H / T / T / F / F / L / C as greenfield — they are marked (H residual = worker extras only; Pp is corpus tail) in docs/ROADMAP.md (--). Worked examples Track a roadmap item with a test `bash pick a gap, write failing test first, implement, update missingfeatures.md pytest tests/testvfeatures.py -k footprint -v ` Plot parity smoke `bash pytest tests/testinterpcompileparity.py -q python scripts/compareinterp_compile.py opt-in full compare ` TypeScript library gate (PyneTS) `bash standalone checkout — not present as pine-worker/ in this repo cd ../pynets && bun test && bun run typecheck `` See PyneTS. Failure modes | Anti-pattern | Why it hurts | | --- | --- | | Planning features already | Duplicated work (H/T/F/L are done) | | Roadmap without tests | Unverifiable “done” | | Ignoring by-design limits | Infinite platform chase | See also Missing features Implementation status Interpret compile parity Alerts pyne-worker pyne-agent-worker Contributing --- FILE: docs/pyne/runtime/alerts.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Alerts" description: "alert() / alertcondition() engine, host export, and L HTTP webhooks (Pro API + pyne-worker)." --- Alerts Abstract Pine alert() and alertcondition() fire into a host side-channel, not strategy events. The evaluator records structured firings with TradingView-style frequency rules. Hosts (Pro API and pyne-worker) export them on evaluate responses and can POST them to HTTP webhooks (roadmap L). Conceptual model | Layer | Module | | --- | --- | | Engine | src/pynescript/ast/evaluator/builtins/alerts.py (exportalerts / exportalertsfromevaluator) | | Package host | src/pynescript/runtime/host.py (clearalerts per run, alerts on the envelope) | | Pro API webhook | backend/alertforwarder.py (backend/runtime.py is only a Runtime re-export) | | Edge host | sibling pyne-worker alertengine.py + alertforwarder.py | Pine surface alert(message, freq) Default freq: alert.freqonceperbar (canonical token onceperbar). Constants (also bare strings): alert.freqonceperbar, alert.freqonceperbarclose, alert.freqall. onceperbar — first fire per bar for the same (source, title, message, freq) key. onceperbarclose — only when the host marks the bar confirmed (barstate.isconfirmed / islast; hosts without barstate treat bars as confirmed). all — every call. alertcondition(condition, title, message) Records each evaluation in alertconditions (debug / UI). When condition is true, also fires a host alert with source: "alertcondition" and once-per-bar semantics. Event shape (todict / API) ``json { "message": "cross up", "freq": "onceperbar", "barindex": , "time": , "title": "optional", "source": "alert" } ` source is "alert" or "alertcondition". Hosts stamp scriptid / runid (and edge cron may add symbol / timeframe). Host export Pro API (POST /run) Interpret path: full history of firings in alerts (and optional alertconditions). Compile path: alerts: [] today (alert side effects are interpret-only). See POST /run. pyne-worker POST /run returns the same alerts array. Cron filters to the last closed bar before webhook delivery so historical bars do not re-fire every minute. Health: features.alerts, features.alertwebhooks. L webhooks Both hosts POST JSON to an HTTPS endpoint. | Host | Default URL | Per-request / per-job | | --- | --- | --- | | Pro API | env ALERTWEBHOOKURL | body webhookurl | | pyne-worker | secret/var ALERTWEBHOOKURL | script/job webhookurl | Pro API request flags | Field | Default | Meaning | | --- | --- | --- | | webhookurl | "" | Override destination | | forwardalerts | true | Skip POST when false | | alertlastbar | true | Only firings on the last OHLCV bar | | alertbatch | true | One batch body vs one POST per alert | Optional timeout: env ALERTWEBHOOKTIMEOUT (seconds, default ). Batch body `json { "type": "pinealertbatch", "source": "pyne-pro-api", "count": , "content": "cross up", "alerts": [ { "type": "pinealert", "source": "pyne-pro-api", "message": "cross up", "freq": "onceperbar", "alertsource": "alert", "barindex": , "time": } ] } ` Edge worker uses "source": "pyne-worker". Discord-friendly content is set when a message is present. Response meta Successful /run may include: `json "alertforward": { "forwarded": , "failed": , "filter": "lastbar", "url": "https://hooks.example.com/pine", "batch": true, "count": } ` Webhook failures never fail the evaluate status; inspect alertforward / alertforwarderror. Worked example `bash curl -sS -X POST http://...:/run \ -H 'Content-Type: application/json' \ -d '{ "script": "//@version=\nindicator(\"a\")\nif barindex == lastbarindex\n alert(\"fire\")\nplot(close)", "mode": "interpret", "webhookurl": "https://hooks.example.com/pine", "data": [ {"time": , "open": , "high": , "low": ., "close": ., "volume": }, {"time": , "open": ., "high": ., "low": ., "close": ., "volume": } ] }' ` Invariants . Not strategy events — trade mesh uses events[]; alerts are independent. . Per-run clear — hosts call clearalerts() so runs do not leak firings. . Last-bar default for webhooks — full-history still available in the alerts response field. . Compile path — prefer mode: "interpret"` (or auto fallback) when you need alerts. See also Strategy events POST /run Pro API usage Runtime bridge Roadmap (L ) --- FILE: docs/pyne/runtime/builtins/collections.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Collections" description: "array., matrix., and map. runtime semantics, method dispatch, and type tags." --- Collections Abstract Pine collections—arrays, matrices, and maps—are mutable values that live in the evaluator context (often under var so identity persists across bars). PYNE maps them to Python list, a dedicated Matrix type, and a Map / dict-like type respectively, with builtin functions and instance-method sugar (a.push(x) → array.push(a, x)). Conceptual model Interface surface Arrays (array.) Represented as Python lists. Highlights from the dispatch map: | Category | Functions | | --- | --- | | Construct | array.new, array.new, array.from | | Mutate | push, pop, shift, unshift, insert, remove, set, fill, clear, reverse, sort | | Access | get, first, last, size, includes, indexof, lastindexof, slice | | Aggregate | sum, avg, min, max, median, mode, range, stdev, variance, covariance, percentiles, percentrank | | Search | binarysearch, binarysearchleftmost, binarysearchrightmost — UDT arrays take sortfield (const int index, default , or const string name; array must be sorted by that field ascending) | | Higher-order | every, some, copy, concat, join, abs | Indexing: Pine array indices are -based from the start of the list (unlike series history). Negative indices are rejected for series; array paths use list semantics consistent with the builtin handlers. UDT sortfield (.. / Pine August ): array.sort, array.sortindices, and array.binarysearch compare one field of user-defined type elements. Pass a const int field index ( is the first declared field, and the default for search) or a const string field name. Binary search requires the array to already be sorted by that field in ascending order. Method form (a.binarysearch(value, "id")) and sortfield= kwargs work on interpret and compile. Series-like values passed into array ops unwrap via .history (reversed to chronological) when duck-typed. Matrices (matrix.) Matrix supports: Construction: matrix.new Element access: matrix.get / set, and m[row, col] via subscript Shape: rows, columns, elementscount Row/column ops: add/remove/copy, sum/avg/min/max/mode, fill Aggregates: sum/avg/min/max/mode all Transforms: transpose, fill diagonal, and further linear helpers in the evaluator map Method sugar: m.rows() → matrix.rows(m). Maps (map.) | Function | Role | | --- | --- | | map.new | Empty map | | put / putall / get / remove / clear | Mutation | | contains / keys / values / size / copy | Query | Keys follow Pine map rules as implemented (hashable Python keys). Compile object-mode lowers maps to ordinary Python dicts. Internals | Path | Role | | --- | --- | | src/pynescript/ast/evaluator/builtins/arrays.py | Array builtins | | src/pynescript/ast/evaluator/builtins/matrix.py | Matrix type | | src/pynescript/ast/evaluator/builtins/matrixevaluator.py | matrix. handlers | | src/pynescript/ast/evaluator/builtins/map.py | Map type | | src/pynescript/ast/evaluator/builtins/mapevaluator.py | map. handlers | | src/pynescript/ast/evaluator/names.py | Method markers for list/Matrix | | tests/testcollections.py, testmapcollections.py, testmatrix.py | Coverage | Multi-dispatch type tags (array.float, matrix.string, …) interact with method overloading and na exclusion lists so na-typed optionals do not bind to collection tostring overloads. Invariants & edge cases . Identity under var. var a = array.newfloat() keeps one list across bars; reassigning a := array.newfloat() replaces it. . Drawing-typed arrays. array.newlabel etc. store drawing object references; delete semantics remain drawing-registry concerns. . Empty array overload match. Empty arrays match any array. element tag in dispatch (no sample element). . Matrix unpack hazard. StatementEvaluator refuses to treat matrices as general iterables for tuple unpack—prevents corrupting multi-assign from row iteration. . Compile mode. Maps/UDTs force object mode; pure array numeric work may still numeric-compile depending on visitor detection—verify generated code when performance-critical. Worked examples Rolling buffer ``pine //@version= indicator("buf") var floats = array.newfloat() array.push(floats, close) if array.size(floats) > array.shift(floats) plot(array.avg(floats)) ` Matrix element `pine var m = matrix.new(, , .) matrix.set(m, , , close) plot(matrix.get(m, , )) ` Map counter `pine var counts = map.new() map.put(counts, "n", nz(map.get(counts, "n")) + ) ` Binary search on a UDT array `pine //@version= indicator("udt search") type Item int id float v var a = array.new() if barindex == array.push(a, Item.new(, .)) array.push(a, Item.new(, .)) array.push(a, Item.new(, .)) array.sort(a, order.ascending, "id") plot(array.binarysearch(a, , "id")) plot(a.binarysearchleftmost(, sort_field="id")) ` Failure modes | Symptom | Cause | | --- | --- | | map.get requires map and key | Wrong arity or non-Map receiver | | Index errors | OOB get/set without Pine na` guard in that handler | | Method not found | Typo or non-list receiver after series unwrap failed | | Shared mutation across runs | Reused evaluator context without reset | See also Expressions & statements Drawing & plotting — arrays of labels/lines Compiler object mode --- FILE: docs/pyne/runtime/builtins/drawing-plotting.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Drawing and plotting" description: "plot side effects, DrawingRegistry objects, and export shapes for hosts and AXIS." --- Drawing and plotting Abstract Visual effects in Pine are side channels: they do not change pure arithmetic results, but they are first-class runtime outputs. PYNE records plots in PlotRegistry and drawing primitives (lines, boxes, labels, tables, polylines, linefills) in DrawingRegistry, then serializes them for the Pro API and optional AXIS. Evaluation never requires a UI—tests assert on registries alone. Conceptual model Interface surface Plotting (PlottingFunctionsMixin) | Function | Kind | Notes | | --- | --- | --- | | plot | plot | Returns Plot id for fill(plot, plot); series values in host export | | plotshape / plotchar / plotarrow | markers | Interpret: bool/condition series + style/location/char meta. Compile: drawings events (object mode) | | plotbar / plotcandle | OHLC visuals | open/high/low/close fields | | hline | horizontal | Both interpret and compile export a constant price series keyed by title, and a drawing/drawings event | | bgcolor / barcolor | coloring | Interpret: color string series. Compile: drawings only (object mode) | | fill | fill | Both export a titled series key (values often all-null); band color + plot refs live in meta/drawings | Style constants: plot.linestylesolid / dashed / dotted. Plot style tokens: plot.styleline, linebr, stepline, steplinebr, steplinediamond, histogram, columns, cross, area, areabr, circles. Each call constructs a Plot dataclass (kind, series, title, color, linewidth, text formatting, forceoverlay, …) and appends it to PlotRegistry.plots. Hosts often also capture per-bar plot values on the evaluator (plotoutputs / plotvaluecols) for time-aligned series maps—see src/pynescript/runtime/evaluator.py and packaging in src/pynescript/runtime/host.py (backend.evaluator / backend.runtime re-export those modules). PYNELIGHTPLOTS= skips columnar capture, plot-registry handles, and input. metadata (corpus OK/fail only). Title defaults and series keys Runtime packages plots into result["series"] / result["plotmeta"] (interpret) or result["series"] + result["drawings"] (compile). Empty or missing titles get defaults; collisions get , , … suffixes so map keys stay unique. | Call | Default title | Notes | | --- | --- | --- | | plot | plot, plot, … | Compile emits plot{n} at emit time; interpret empty title → plot{index} at JSON packaging | | hline | hline | Uniquified hline, hline, … when untitled levels repeat | | fill | fill | Same uniquify rule (Background / Background, …) | | bgcolor | bgcolor | Interpret series + meta | | plotshape | shape | Interpret series + meta | Explicit title= (or positional title) wins when present. hline — constant series + drawings (both modes) Interpret (src/pynescript/runtime/evaluator.py + Runtime packaging): Captures kind hline with the price as the series cell each bar. JSON export forward-fills nulls with the known constant so AXIS can draw a full-width level. plotmeta[title] includes kind: "hline", price, color, linewidth, … Compile (object mode): Forces object mode; appends a plot array (safefloat(price) every bar) under the uniquified title and a _drawings event (kind: "hline", price, title, color). Host wraps NaN → null; constant levels remain numeric across the bar range. Parity tests: tests/testmultiplotcross.py (testhlineserieskeyscompilematchesinterpret), tests/testcompilerobjects.py. fill — titled series key for key-set parity (both modes) Interpret: Meta stores sibling series titles as plot / plot (resolved from plot() handles). Capture may hold a color string on the column; Runtime JSON packaging typically emits all-null series cells for fills (non-numeric / non-bgcolor strings → null). Band color and plot refs stay in plotmeta (and registry fill objects when ids are enabled). Compile: Forces object mode; registers a series key with expression None (float NaN → JSON null) under the uniquified title so series key sets match interpret. Band details (plot refs, color) go on the drawings fill event—not a numeric column. Parity tests: testfillbackgroundserieskeyscompilematchesinterpret, testfilltitleserieskeyanddrawings. Import stubs must not leak as series strings Unresolved import … libraries become chainable stubs marked _pineimportstub = True (repr like `). If a script plots a stub member, Runtime’s jsonplotvalue maps those cells to null—never the stub repr or a color-like string—so AXIS and interpret/compile comparisons never see "" as a plot series value. Drawing objects (DrawingBuiltinsMixin) Object types: Line, Box, Label, Table, Polyline, LineFill, ChartPoint. Typical factories: line.new, box.new, label.new, table.new, polyline.new, linefill.new, plus getters/setters/deletes and .all / last-bar helpers where implemented. Instance methods resolve through namespace markers: `text la.gettext() → label.gettext(la) ` DrawingRegistry.exportforapi(bartimes) maps xloc=barindex coordinates to wall times for chart clients, normalizes colors, and skips deleted objects. xloc.barindex past the last bar (classic barindex + on barstate.islast) is extrapolated from the series period (..); empty bartimes passes the bare bar index. Line / box / label payloads include forceoverlay for AXIS pane routing (..). linefill.new serializes as type: "linefill" quads (t/p … t/p from the two line endpoints, plus color / bgcolor). Registry lifecycle `python DrawingRegistry.reset() clears drawings + PlotRegistry ` Hosts must reset at run start so labels from a previous evaluation do not leak. Drawing GC (max_count) Interpret path enforces TradingView-style garbage collection on drawing factories (line.new, label.new, box.new, polyline.new, …): | Declaration kwargs (indicator / strategy) | Default | Hard cap | | --- | --- | --- | | maxlinescount | | | | maxlabelscount | | | | maxboxescount | | | | maxpolylinescount | | | When more active objects of a type exist than the cap, the oldest are marked deleted=True so they leave .all and DrawingRegistry.exportforapi. Caps are applied from the script declaration at run start (DrawingRegistry.configurefromdeclaration); shrinking a cap immediately collects. `pine //@version= indicator("labels", overlay=true, maxlabelscount=) if barstate.islast label.new(barindex, high, "x") ` Compile object-mode drawings are a simplified event list—GC caps apply on the interpret registry path; prefer interpret when you need full last-bar / .all / cap behavior. fill + plotmeta for AXIS For dual plot + fill bands: . Each plot() contributes a numeric series under its title (default plot, …). . fill(p, p, …) contributes a titled series key (often all-null cells) so key-sets stay stable, plus meta/drawings that carry band color and plot refs. . Hosts such as AXIS should read plotmeta[title] for kind, colors, and sibling plot titles (plot / plot) rather than treating fill series as prices. Interpret packaging lives in src/pynescript/runtime/host.py / evaluator.py; compile emits matching series keys + drawings fill events. Internals | Path | Role | | --- | --- | | src/pynescript/ast/evaluator/builtins/plotting.py | Plot, PlotRegistry, plot handlers | | src/pynescript/ast/evaluator/builtins/drawing.py | Drawing types, registry, builtins, exportforapi, foldcompiledrawingmutations | | src/pynescript/runtime/evaluator.py | Host plot capture: hline / fill / bgcolor / plotshape series + meta | | src/pynescript/runtime/host.py | JSON packaging: series keys, stub nulling, hline fill-forward, fill plot refs | | src/pynescript/compiler/compiler.py | hline/fill series + _drawings; plotshape/bgcolor events | | src/pynescript/ast/evaluator/names.py | DRAWINGMETHODNS | | tests/testplottingeffects.py, testdrawingallandlastbar.py, testmultiplotcross.py, testbgcolorplotshapeexport.py, testcompilerobjects.py | Behavior / parity | Compile object-mode notes (plotshape / bgcolor) Drawings and marker-style plot calls force object mode (pure-Python bar loop; drawings append-only event list)—not full DrawingRegistry / interpret series parity. | Call | Compile behavior | Interpret parity gap | | --- | --- | --- | | hline | Series key (constant) + drawings | Aligned on series keys + drawings export | | fill | Series key (all-null) + drawings | Aligned on series key sets; color/refs differ (meta vs event) | | plotshape / plotchar / plotarrow | drawings event only (series, optional title, …) | Interpret also builds bool/condition series + plotmeta style/location/char | | bgcolor / barcolor | _drawings event only (color, bar index) | Interpret builds color-string series + plotmeta.kind | | label / line / box / polyline / table | Handle dict + _drawings; set and .delete (kind: "delete") fold via DrawingRegistry.foldcompiledrawingmutations (..) | Interpret still has full registry, GC caps, last-bar helpers | Numeric mode still supports plain plot float arrays; any of the drawing/marker calls above flips object mode for that script. Invariants & edge cases . plot returns an id. Required for fill; treating it as “void” breaks band fills. Hosts that skip PlotRegistry unless fill( appears in source must still soft-fail fill gracefully. . Deleted flag. Soft-delete keeps history for debugging; active() filters deleted plots and GC-collected drawings. . maxcount GC. Oldest active drawings are deleted when caps are exceeded (interpret registry). . Series in drawings. Prices may be series wrappers—export coerces .current and maps NaN → omit. . Compile path. Numeric mode supports plot arrays; drawings force object mode with a simplified event list. set / .delete fold onto live handles before export (not interpret GC / .all). Prefer interpret when you need bgcolor/plotshape series columns, not only drawing events. . Import stubs → null. _pineimportstub and "" strings never appear as plot series cells after Runtime packaging. . No GPU/canvas in-process. Registries are data; AXIS/HOOX render elsewhere. Worked examples Dual plot + fill `pine //@version= indicator("BB mid") basis = ta.sma(close, ) p = plot(basis) p = plot(basis .) fill(p, p, color=color.new(color.blue, )) ` Label on last bar `pine //@version= indicator("lbl", overlay=true) if barstate.islast label.new(barindex, high, str.tostring(close)) ` Hosts read DrawingRegistry.labels or API export after the run. Failure modes | Symptom | Cause | | --- | --- | | Empty drawings in API | Forgot DrawingRegistry.reset timing / export; or script never called .new | | fill no-ops | plot ids not retained from plot() returns | | Stale labels across HTTP runs | Missing registry reset between Runtime.run calls (Runtime resets—custom hosts must) | | Object mode missing drawing | Compile still folds set / delete; leftover events usually mean fold did not run | | Only labels/lines visible | Default maxcount; raise on indicator/strategy (≤ hard cap) | | fill series all null | Expected for band keys; use plotmeta` / drawings for color + plot refs | See also Builtins hub Pro API preview / chart renderer AXIS docs Compiler overview --- FILE: docs/pyne/runtime/builtins/index.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Builtins" description: "Dispatch architecture for ta., strategy., collections, plotting, request., and other standard namespaces." --- Builtins Abstract Pine’s standard library is not a single module: it is a flat qualified-name dispatch table ("ta.sma", "strategy.entry", "array.push", …) assembled from category mixins. Each entry is a handler (args, kwargs?) → value with side effects when the language requires them (orders, plots, drawings, inputs). This hub orients the surface; child pages go deep on technical, strategy, collections, drawing/plotting, and request/input. Conceptual model BuiltinEvaluator merges mixin maps in a fixed order and then registers ticker, logging, color, timeframe, and script-declaration functions. The strategy() declaration handler is wrapped so broker settings apply to StrategyState. Interface surface | Namespace / area | Mixin / module | Docs | | --- | --- | --- | | ta. | TechnicalAnalysisMixin + technicalsubmodules/ | Technical | | strategy. | StrategyBuiltinsMixin, StrategyConstantsMixin | Strategy | | array. / matrix. / map. | ArrayBuiltinsMixin, matrix/map evaluators | Collections | | plot / hline / line / label / … | PlottingFunctionsMixin, DrawingBuiltinsMixin | Drawing & plotting | | request. / input. | RequestBuiltinsMixin, InputBuiltinsMixin | Request & input | | math., min/max, nz, … | NumericBuiltinsMixin | (this page) | | str. | StringBuiltinsMixin | (this page) | | color. | registercolorfunctions | (this page) | | syminfo / ticker. | registertickerfunctions | (this page) | | timeframe. | registertimeframefunctions | (this page) | | alert / logging | AlertsMixin, registerloggingfunctions | Alerts | | indicator / strategy / library | registerscriptdeclarationfunctions | Libraries | | Footprint / volumerow | FootprintBuiltinsMixin | Request & input | Numeric and string essentials Numeric: math. constants from BaseEvaluator (math.pi, math.e, golden-ratio helpers); functions cover abs, min/max, rounding, logs, trig, and NA helpers (nz, na predicates via utility). String: str. formatting, conversion (str.tostring respects format. constants), and manipulation aligned with common Pine scripts in the builtin corpus. Color, ticker, timeframe Colors: hex constants (color.red → F, etc.) plus composition helpers. syminfo / ticker: host-provided Syminfo object or flat context keys; ticker construction for request.security symbols. timeframe: period flags (isintraday, isdaily, …) default daily in base constants; hosts override from bar spacing. Alerts alert(message, freq) and alertcondition(condition, title, message) record structured firings (message, freq, barindex, time, source) with TV-style frequency rules (onceperbar, onceperbarclose, all). Hosts export them on /run and can POST last-bar batches to webhooks (L). Side channel only — not strategy events. Full detail: Alerts. Internals | Path | Role | | --- | --- | | src/pynescript/ast/evaluator/builtins/init.py | BuiltinEvaluator composition | | src/pynescript/ast/evaluator/builtins/base.py | Dispatch mixin, callbuiltin, registration | | src/pynescript/ast/evaluator/builtins/.py | Category implementations | | scripts/generatebuiltinmetadata.py | LSP/metadata surface (not hand-edited JSON) | Handlers are looked up by the fully qualified string built during name/attribute evaluation. Zero-arg series (e.g. strategy.equity) are registered as builtins that ignore empty args. Invariants & edge cases . One map, many mixins. New builtins require a map entry and (usually) metadata regeneration for LSP. . Kwargs and positionals. Handlers accept both; Pine named arguments arrive as kwargs when the call AST carries them. . Bar mode. Stateful ta.crossover / similar use per-bar call indices reset by the host (crosscalli). Incremental ta. (PYNETAINCREMENTAL, default on) uses a sibling tacalli slot. . Mock fallbacks. Bare equity-style request.security strings may still mock offline; foreign / fundamental prefixes and complex expressions return na (see request / input). . Compile path subset. Numba builtins reimplement only a fraction of this table; object mode covers more via Python helpers—see Compiler. Worked example — resolution path ``pine plot(ta.sma(close, )) ` . Attribute/call chain builds qualified name ta.sma. . callbuiltin("ta.sma", [closeseries, ]). . Handler coerces series → list, computes SMA, finalizes scalar or list. . plot registers a Plot with that series value and returns the plot id. Failure modes | Symptom | Fix | | --- | --- | | Unknown built-in function | Typo, version surface gap, or failed attribute chain | | Always mock prices | Wire datafeed / data_provider` into evaluator context | | LSP knows a function runtime lacks | Metadata ahead of implementation—check missing features | | Handler arity errors | Positional vs keyword mismatch in script | See also Technical Strategy Collections Drawing & plotting Request & input Alerts Builtin metadata (LSP) --- FILE: docs/pyne/runtime/builtins/request-input.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "request. and input." description: "Multi-symbol/timeframe requests, fundamental na semantics, host series bind, and input parameter resolution." --- request. and input. Abstract Two namespaces couple scripts to the host environment: input. declares parameters (with defaults and UI metadata), and request. pulls data from other symbols, timeframes, or fundamental/economic sources. PYNE implements both as builtins with explicit extension points—datafeed, dataprovider, and inputoverrides—so the same script can run offline or online with real market data. Design rule: missing multi-symbol or fundamental data yields na, never silent substitution of chart OHLCV (no close-as-dividend, no chart volume as UPVOL). Conceptual model Interface surface input. (InputBuiltinsMixin) | Builtin | Purpose | | --- | --- | | input | Generic defval + title/tooltip/inline/group/confirm/active | | input.bool / int / float / string / color | Typed scalars | | input.price / source / time / timeframe / session / symbol | Domain inputs | | input.enum / input.textarea | Enumerations and multiline text | Runtime value: each call returns the resolved value (override if title matches, else default). Metadata is appended to inputdeclarations for settings panels and LSP-adjacent hosts. Overrides live on the evaluator as inputoverrides: dict[title, value]. request. (RequestBuiltinsMixin) | Builtin | Role | | --- | --- | | request.security | Other symbol / timeframe expression | | request.securitylowertf | Lower-TF array expansion | | request.dividends / earnings / splits | Corporate actions | | request.financial / economic / quandl | Fundamentals / external series | | request.currencyrate | FX conversion helper | | request.seed | Deterministic pseudo-series | | request.footprint | Volume footprint object (v surface) | request.security — interpret path Resolution order (simplified): . Normalize symbol (series → last element; ticker. → symbol string) and timeframe. . Classify chart vs foreign (ischartsymbol vs host syminfo / provider symbol). . Try datafeed.fetchlatestohlcv / ticker, then dataprovider.fetch. . Same-symbol coarser timeframe (HTF resample, ..+): bucket chart OHLCV to the requested TF; last completed HTF bar only (lookaheadoff-style — the forming bucket is never returned). Allowlisted simple TA on HTF series — ta.sma / ta.ema / ta.rsi / ta.atr (bare close/high/… source + const length; ta.atr(n) length-only) — evaluates on the unique completed HTF series. Nested ta., UDFs, and non-const lengths stay na. barmerge.gaps / lookahead are accepted but unused. Policy meta is exposed on result["meta"]["requestsecurity"]. . Foreign + pre-evaluated expression (UDF result, list/tuple of chart values, non-string expr) without multi-symbol data → na (not chart close as “dividends”). . Fundamental / non-equity prefixes (DIVIDEND, FACTSET, EARNINGS, ESD) with no feed hit → na (no mock OHLCV). . Bare equity-style string names may still use legacy mock prices for offline demos when no feed is wired. ChartOHLCVProvider (wired by Runtime via resolverequestsources when unset) only serves the chart ticker. Foreign tickers get empty series, so interpret returns na unless a real multi-symbol feed is configured. request.security — compile path Compile lowering is intentionally narrow (compiler overview): | Case | Emit | | --- | --- | | Same-symbol (syminfo.ticker / empty chart id) and simple OHLCV expr (close, high[], …) | Passthrough chart array sample | | Foreign tickers (UPVOL.NY, ESDFACTSET, …) | np.nan | | Complex third arg (UDFs, yearsum(close), non-allowlisted ta., arithmetic) | np.nan | | Other request. APIs | np.nan (object mode) | No inventing chart-close-as-dividends or chart-volume-as-advance/decline series. Footprint (FootprintBuiltinsMixin) Types Footprint and VolumeRow expose buy/sell volume, delta, VAH/VAL/POC rows, and per-row imbalance helpers—aligned with the v footprint surface inventory. Mode selection (mode=auto) Pro API /run defaults body mode to auto. Runtime.compileeligible rejects compile when the source contains request. (or top-level import), so auto prefers interpret for any script that uses request.: ``text request. present → compilefallbackreason = "request. not supported in compile path" → autobackend = interpret ` Forced mode=compile still runs the narrow lowering above (simple same-symbol only; else na). Input overrides also force interpret under auto. See Compiler overview for warm compile, numeric vs object mode, and eligibility. Host series bind (interpret) Assigning host OHLCV / time series into a user name never aliases the host buffer by reference. bindseriesname copies the current scalar into a fresh series for the user name so patterns like: `pine lastt := na(close) ? lastt[] : time ` cannot corrupt time[j] history. That bug previously broke TTM / yearsum-style windows (e.g. dividend-yield scripts). Internals | Path | Role | | --- | --- | | src/pynescript/ast/evaluator/builtins/input.py | Input handlers + declarations | | src/pynescript/ast/evaluator/builtins/request.py | request. + chart/foreign / na policy | | src/pynescript/ast/evaluator/statements.py | bindseriesname (no OHLCV alias) | | src/pynescript/util/data.py | ChartOHLCVProvider, resolverequestsources | | src/pynescript/compiler/compiler.py | Same-symbol OHLCV-only security lower | | src/pynescript/runtime/host.py | Package Runtime SoT; feed wiring; compileeligible / runauto; copies policy onto meta.requestsecurity | | backend/runtime.py | Compat re-export of package Runtime (not the implementation) | | tests/testdividendyieldparity.py, testdatafeedwiring.py | na parity + chart provider | Invariants & edge cases . Inputs are pure values at runtime. Titles matter only for override keys and UI metadata—not for Pine type identity. . Foreign without data → na. Prefer honest missing data over inventing chart series as multi-asset results. . Chart provider is chart-only. Multi-asset accuracy needs a real datafeed / dataprovider. . Mocks are limited. Equity-style bare symbols may still mock offline; fundamental prefixes and foreign pre-evaluated exprs do not. . Dynamic symbols. List/series symbols resolve to the latest element—supports loops constructing ticker ids. . Lower TF. request.securitylowertf returns array-like structures; length scales with simulated lower-TF density when mocking. . mode=auto + request. → interpret. Compile path is not a full multi-asset substitute (compileeligible rejects request.). . HTF is last-completed only. Forming HTF buckets are never returned; gaps / lookahead kwargs do not change that. Worked examples Parameterized length `pine //@version= indicator("len") len = input.int(, "Length", minval=) plot(ta.sma(close, len)) ` Host: `python ev.inputoverrides = {"Length": } ` Multi-timeframe close / simple HTF TA (chart symbol) `pine //@version= indicator("HTF") htf = request.security(syminfo.tickerid, "D", close) htfsma = request.security(syminfo.tickerid, "D", ta.sma(close, )) plot(htf) plot(htfsma) ` Same-symbol coarser TF: interpret buckets chart OHLCV (last completed HTF bar) and evaluates allowlisted simple ta. (sma / ema / rsi / atr) on that series. Compile may passthrough simple same-symbol OHLCV; complex foreign/security still → na. Inspect meta.requestsecurity.policies (htfohlcvresample, htfsimpletaresample, foreignna, complexhtfna, …). Intentional na — dividend yield (fundamentals missing) `pine //@version= indicator("div") yearsum(src) => ta.cum(src) divticker = ticker.new("ESDFACTSET", "X;Y;DIVIDENDS") divttm = request.security(divticker, "D", yearsum(close), barmerge.gapson, lookahead=barmerge.lookaheadon) plot(divttm) ` Without a fundamentals feed: interpret and compile both plot na for divttm—not chart close as fake TTM dividends. Covered by tests/testdividendyieldparity.py. Intentional na — CVI / UPVOL-style foreign OHLCV `pine //@version= indicator("cvi") up = request.security("UPVOL.NY", "D", close) plot(up) ` Foreign ticker + no multi-symbol data → na. Compile never rewrites this as chart close; inventing advance/decline volume from the host series is forbidden. Failure modes | Symptom | Cause | | --- | --- | | All-na multi-asset plots | Foreign symbol / fundamental expr; no feed (expected) | | Always ~ mock prices | Bare equity string + no feed; legacy mock path | | Input override ignored | Title string mismatch (including empty title) | | Footprint fields zero | request.footprint without configured footprint data | | time[] / history wrong after assign | Should be fixed: host bind no longer aliases OHLCV | | Lookahead surprises | Host data alignment / gaps—not automatic TV replay guarantees | | autobackend=interpret with request.` | Eligibility prefilter; use interpret or wire multi-symbol feed | See also Compiler overview — auto eligibility, same-symbol security lower Builtins hub Series & history Pro API run endpoint Runtime hub --- FILE: docs/pyne/runtime/builtins/strategy.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Strategy builtins" description: "Orders, fills, OCA, commission, risk gates, position metrics, and StrategyState." --- Strategy builtins Abstract PYNE’s strategy layer is a per-run broker simulation driven by strategy. calls during the bar loop. It is not a live exchange adapter: orders become fills against bar OHLC (market immediately; limit/stop via pending book), commissions and slippage adjust PnL, and risk helpers can block entries. Structured StrategyEvent records form the parity contract with pyne-worker and the HOOX trade mesh. Conceptual model Fill-before-script matches the interpreter Runtime and the compile-path strategy broker. Interface surface Order placement | Builtin | Role | | --- | --- | | strategy.entry | Open/add (or reverse) by direction; market or pending limit/stop | | strategy.exit | Bracket-style exit (pending limit/stop + OCA); market exit when no levels | | strategy.close / strategy.closeall | Flatten by id or all (qtypercent supported) | | strategy.order | Lower-level order with OCA name/type, partial fill cap | | strategy.cancel / strategy.cancelall | Remove pending orders | Directions accept Pine constants (strategy.long / strategy.short) and common string aliases. strategy.exit surface (..+) | Feature | Status | | --- | --- | | Pending stop/limit brackets + OCA | Interpret + compile — fills via processpendingorders OHLC path | | fromentry multi-leg filter | Interpret + compile — unknown id is soft no-op | | qtypercent | Interpret + compile close — % of target (whole pos or fromentry); wins over qty; ≤/na → no-op; > capped | | Trail (trailoffset / trailpoints / optional trailprice) | Interpret + compile — distances are ticks (× mintick). Pending stop ratchets from bar high/low (OHLC approx). trailpoints= / na is ignored so a valid trailoffset still applies (..). | | profit / loss | Ticks from the exit’s entry average (ticks mintick). Long: entry ± offset; short flips the sign. na / ≤ ignore that leg. Absolute limit / stop win when both are set. | ``pine //@version= strategy("Exit demo", overlay=true, initialcapital=) if barindex == strategy.entry("L", strategy.long, qty=) if barindex == strategy.exit("X", fromentry="L", stop=low ., limit=high ., qtypercent=) // Tick form (same surface): profit/loss are offsets from entry avg, not prices. // strategy.exit("X", fromentry="L", profit=, loss=) ` Position and performance series Zero-arg builtins include strategy.positionsize (signed: +long / −short), positionavgprice, positionentryname, opentrades / closedtrades counts, netprofit, openprofit, equity, cash, gross win/loss stats, averages, max drawdown/runup, and max contracts held. Trade queries Indexed accessors: strategy.closedtrades.entry / exit / profit / size / commission strategy.opentrades.entry / size / profit / commission Open/closed trade fields include bar/time/id/comment plus approximate MAE/MFE (maxdrawdown / maxrunup from bar high/low) Risk (interpret + compile halt cascade) | Builtin | Effect | | --- | --- | | strategy.risk.maxpositionsize | Cap size as % of equity | | strategy.risk.maxintradayloss | Intraday loss gate | | strategy.risk.maxintradayfilledorders | Order count cap (day-scoped on compile) | | strategy.risk.maxdrawdown | Absolute / percent drawdown halt | | strategy.risk.maxconslossdays | Consecutive losing calendar days → entriesblocked | | strategy.risk.allowentryin | "all" \| "long" \| "short" | Blocked entries still emit a diagnostic-style event with comment riskblocked where implemented. Compile broker shares the common halt cascade above (not a full TradingView risk engine). Declaration strategy(title, …) applies broker settings onto StrategyState: initialcapital, commission type/value, slippage ticks, pyramiding, avgpricemodel, etc. Average price model (pynescript extension) Not part of official TradingView Pine. Controls how strategy.positionavgprice evolves when size changes: | avgpricemodel | On same-direction adds | On partial reduce | | --- | --- | --- | | "stock" (default) | Arithmetic VWAP of fills | Re-average remaining open legs (FIFO multi-trade style) | | "futures" | Same arithmetic VWAP | Sticky net AEP until flat (linear USDT-M / BTCUSDT-like) | | "inverse" | Arithmetic today; harmonic add planned | Sticky until flat (same reduce rule as futures) | `pine strategy("Perp style", avgpricemodel="futures") // or tokens: // strategy(..., avgpricemodel=strategy.avgpricefutures) ` Linear add formula (stock and futures): \[ avg' = \frac{avg \cdot |size| + p \cdot q}{|size| + q} \] Under "futures", partial closes realize PnL against the sticky average (not per-leg FIFO entry prices), and closed-trade entryprice records that same average. Commission is never folded into the average numerator. There is no strategy.order.avgfutureprice series—use strategy.positionavgprice with the mode switch. Leverage (pynescript extension — simpler futures UI) Exchanges expose leverage (e.g. × / × / ×). Prefer that over TV-style marginlong / marginshort percentages: `pine //@version= // TV-safe: strategy() kwargs need const (literals / const vars). // Host UI can still inject runtime input overrides into PYNE. strategy( "BTCUSDT style", avgpricemodel="futures", leverage=, defaultqtytype=strategy.percentofequity, defaultqtyvalue=, // use full equity as margin initialcapital=) // Optional Inputs-tab control for trade logic (not TV strategy Properties): // lev = input.float(, "Leverage", minval=, maxval=) ` | Setting | Effect | | --- | --- | | leverage=N (default ) | Buying power multiplier | | percentofequity / cash default qty | qty = margin × leverage / price | | fixed default qty | Unchanged (contracts as written) | | strategy.cash / capital held | Margin locked = notional / leverage | | strategy.marginliquidationprice | Simple isolated estimate when leverage > | | strategy.leverage | Read-back of configured multiplier | leverage= also sets internal margin % to / = (TV marginlong/marginshort equivalent). If only marginlong is set, leverage is derived as / marginlong. input before strategy() — TV vs PYNE | | TradingView | PYNE | | --- | --- | --- | | Statements before strategy() | Allowed for const vars (and types/enums/imports) | Allowed | | input.() return qualifier | input (not const) | Plain Python value (no qualifier system) | | strategy(leverage=input.float(...)) | Compile error — most strategy() params require const | Interpret: works. Compile: folds const-like defval into the broker ctor (input.float() → leverage=) | | User-adjustable leverage on TV | Settings → Properties (“Long/Short leverage”, mapped from marginlong/marginshort) | Declaration leverage= or host-injected inputs | Qualifier hierarchy on TV: const or missing close | | Events missing scriptid | Host forgot to stamp after drainevents (Runtime does this) | | PnL off vs TV | Commission type, slippage ticks, mintick, or fill price model | | Interpret vs compile event drift | Broker subset / timing—diff with tests/testcompiler_strategy.py | See also Events Strategy broker (compile) pyne-worker Pro API backtest --- FILE: docs/pyne/runtime/builtins/technical.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Technical analysis (ta.)" description: "ta. indicators: series helpers, bar mode, interpretcompile parity, and submodule layout." --- Technical analysis (ta.) Abstract The ta. namespace is the largest pure-compute surface in PYNE: moving averages, oscillators, volatility bands, volume studies, pattern helpers, and cross/rise/fall predicates. Implementations live under technicalsubmodules/ and are composed into TechnicalAnalysisMixin. Numerical behavior is validated against TradingView reference series (see numerical validation); the design goal is IEEE-limit parity, not “approximate TA.” Interpret and compile (Numba) hosts share the same formulas for the kernels listed under Interpret compile parity. Conceptual model Hosts in production set bar mode so indicator calls return scalars for the active bar, matching how Pine expressions compose (ta.ema(a) - ta.ema(b)). Incremental hot path When pinebarmode and pinetaincremental are enabled (Runtime default; disable with PYNETAINCREMENTAL=), hot ta. builtins update call-site state once per bar instead of recomputing full history. Call sites are indexed like crossovers (tacalli reset each bar). Nested forms such as ta.ema(ta.sma(close, ), ) stay correct because each call keeps its own slot. Golden suite: tests/testtaincremental.py (incremental last values ≡ full recompute). | Family | Incremental builtins (non-exhaustive) | | --- | --- | | Moving averages | sma, ema, rma, wma, hma, vwma, swma, kama, dema, tema, alma | | Oscillators | rsi, macd, stoch/stochrsi, cci, cmo, tsi, roc, wpr | | Volatility / structure | atr, tr, stdev, bb, highest/lowest, linreg, adx/dmi, supertrend, kc, sar | | Volume (..–..) | obv, wad/wvad, cmf, klinger, mfi, vwap, accdist, nvi, pvi | | Other | change/mom/cum, median, percentrank, rising/falling, barssince, pivots | ta.nvi / ta.pvi are incremental (O()/bar; PYNETAINCREMENTAL= full-list). ta.atr is Wilder RMA of true range on interpret (full + incremental) and Numba (ta.rma(ta.tr, length) — ..+). Supertrend is locked mid±factor·ATR (not the TV band ratchet). Interface surface Dispatch keys (non-exhaustive; map is authoritative in technical.py): | Family | Examples | | --- | --- | | Moving averages | ta.sma, ta.ema, ta.wma, ta.rma, ta.hma, ta.vwma, ta.swma, ta.alma | | Oscillators | ta.rsi, ta.stoch, ta.cci, ta.cmo, ta.mfi, ta.roc, ta.wpr, ta.tsi, ta.rci | | Trend / channels | ta.macd, ta.adx, ta.dmi, ta.supertrend, ta.sar, ta.linreg | | Volatility | ta.atr, ta.tr, ta.bb, ta.bbw, ta.kc, ta.kcw, ta.stdev, ta.variance | | Volume | ta.obv, ta.vwap, ta.mfi (shared), volume submodule helpers | | Structure | ta.highest / lowest / highestbars / lowestbars, ta.pivothigh / pivotlow | | Predicates | ta.crossover, ta.crossunder, ta.cross, ta.rising, ta.falling | | Stats / other | ta.change, ta.mom, ta.cum, ta.median, ta.mode, percentiles, ta.barssince, ta.valuewhen, ta.correlation | Argument conventions Typical form: ta.fn(series, length). Some functions allow period-only calls (e.g. ta.highest()) defaulting source to high / context series via expectseries(..., allowperiodonly=True). Multi-value returns (e.g. MACD, BB, Supertrend) unpack as tuples/lists; assignment uses StatementEvaluator unpack rules. Fractional lengths floor to int (TradingView-compatible). Stateful crosses ta.crossover / ta.crossunder need previous-bar pairs. In bar-mode runs the host resets a call-site index (crosscalli) each bar so multiple cross calls in one script keep independent state. Internals | Path | Role | | --- | --- | | src/pynescript/ast/evaluator/builtins/technical.py | Dispatch map aggregation | | .../technicalsubmodules/core.py | Series coerce, expect helpers, bar finalize, incremental kernels | | .../movingaverages.py, oscillators.py, volatility.py, volume.py, … | Kernels + inc wiring | | .../common.py, basic.py, advanced.py, patterns.py, strategies.py, synthesizer.py, economics.py | Additional families | | src/pynescript/compiler/numbabuiltins.py | Compile-path Numba kernels (must track interpret formulas) | | tests/testtaindicators.py, tests/testindicators.py | Regression | | tests/testtaincremental.py | Inc ≡ full recompute golden (full CI gate) | | tests/testfirstpartytagoldens.py | Dual-host first-party goldens (ATR / Supertrend / Keltner) | | tests/testinterpcompileparity.py | Always-on dual-host smoke + full-corpus mark | | scripts/compareinterpcompile.py | Corpus harness (report under .cache/interpcompileparity.json) | | tests/testcompilernumba.py (TestInterpCompilePlotParityFixes, highestbars offsets, …) | Targeted formula locks | | docs/numericalvalidationreport.md | Published precision summary (TV / IEEE bounds) | History for wrapper series is reversed to chronological order and may be truncated (SERIESMAX) before full kernels run. Incremental path only needs series[-] per call, so truncation does not freeze state. Interpret compile parity Numba kernels are separate code from the interpreter, but the dual-host contract is same formula, same warm-up mask, same na rules for the surface below. Drift is a bug, not an allowed “approx compile.” Prove regressions with the interpcompile harness and the numerical validation page (TV / IEEE bounds). | Topic | Shared behavior | | --- | --- | | ta.rsi | Wilder on both hosts: SMA seed of the first period deltas, then RMA of gains/losses. First finite bar is index period (period deltas need period+ prices). Not a simple window average. | | ta.roc | Standard TV formula (src - src[length]) / src[length]. Warm-up is na, never an early .. Zero or missing baseline → na. Interpret no longer used a wrong lookback denominator or early zero. | | ta.wma | Requires a full non-na window of length N. Nested forms such as ta.wma(ta.roc(...), …) stay na until the inner series has produced N finite samples (no partial-window reweight). | | ta.cum | Running sum; Pine na / IEEE NaN treated as (skipped contribution), matching TradingView cumulative-sum semantics. | | ta.highestbars / ta.lowestbars | Return negative bars-back offsets: if the extreme is the current bar, - one bar ago, …, -(length-) at the far edge. Short history (i + < length) returns -. Matches Aroon-style scripts that index with high[ta.highestbars(...)] / math.abs(ta.lowestbars(...)). | | math.avg (multi-arg) | Arithmetic mean of the arguments. Any na argument → na (do not skip). Not a rolling ta.sma. | | ta.linreg | Least-squares fit over the window; endpoint at x = n− when offset= (TV): meany + slope ((n−) − meanx) / compile intercept + slope (n − − offset). Length < → na. | | ta.mfi | Warm-up aligned on both hosts: needs length + typical-price samples (direction vs previous bar). Equal typical prices contribute to neither side; only-pos / only-neg MF → / (or when both empty). | | ta.rci | Rank Correlation Index (Spearman of time vs value ranks) is implemented on the compile path (numbarci) as well as interpret; stays in numeric mode when called. | ..+: ta.atr is Wilder RMA of TR on interpret + Numba (not EMA-of-TR). EMA dual-host seed is full-list SMA seed matching incremental/Numba. First-party dual-host goldens (ATR / Supertrend / Keltner) and testtaincremental gate residual drift. Any remaining gaps are tracked under missing features—not as silent “close enough” for the rows above. Parity harness ``bash Always-on smoke (stable scripts under tests/testinterpcompileparity.py) pytest tests/testinterpcompileparity.py -q Corpus compare (default ~ scripts × bars; writes report JSON) python scripts/compareinterpcompile.py --bars --limit python scripts/compareinterpcompile.py --glob 'average.pine' --bars Optional longer pytest path pytest tests/testinterpcompileparity.py -m interpcompilefull Formula locks (RSI/ROC/WMA/cum/math.avg/highestbars, …) pytest tests/testcompilernumba.py -k 'InterpCompilePlotParity or highestbarslowestbarsnegative' -q ` Report path: .cache/interpcompileparity.json. Methodology and acceptable relative-error bands vs TradingView: Numerical validation. Invariants & edge cases . Warm-up → na. Insufficient bars (e.g. SMA length \(N\) needs \(N\) samples; RSI needs period+ prices) yield None / leading na in full-series mode. . NA in window. Many kernels propagate na if any window element is missing (SMA-like sums, WMA, MFI money-flow samples). ta.cum is the TV exception (na → contribution). . Default sources. ta.atr pulls high/low/close from currentseries / context when not passed explicitly. . Compile kernels must track interpret. Numba numba implementations are separate source; for the parity table above they are formula-locked by dual-host tests—do not “approximate” for speed. . No chart look-ahead. Kernels only see history available at the current bar index; request.security lookahead is a separate concern. . Incremental ≡ full recompute (oracle). ATR seed is already Wilder RMA on both hosts; changing seed rules is a correctness project, not a silent perf flag. Worked examples Classic overlay `pine //@version= indicator("SMA ") v = ta.sma(close, ) plot(v) ` In bar mode each visit returns one float (or na); the host stacks them into a series for the response envelope. Cross entry signal `pine //@version= strategy("X") fast = ta.ema(close, ) slow = ta.ema(close, ) if ta.crossover(fast, slow) strategy.entry("L", strategy.long) ` Cross state is bar-local; ensure the runtime resets cross call indices when reusing an evaluator. Multi-value unpack `pine [macdLine, signal, hist] = ta.macd(close, , , ) plot(hist) ` Failure modes | Symptom | Cause | | --- | --- | | Always na | Length longer than available history; or series not updated | | Wrong vs TV by large margin | Wrong source series; mock OHLCV; or non-bar-mode list composition bug | | Cross never fires | Call-index state not reset / shared incorrectly across bars | | Compile diverges from interpret | Kernel drift in numbabuiltins.py (RSI must stay Wilder; ROC warm-up must stay na; WMA full window; highestbars sign; math.avg na rule)—re-run the parity harness | | Early ROC / nested WMA finite too soon | Legacy quirks: ROC returned . before lookback, or WMA reweighted partial/na windows—fixed on both hosts | | high[ta.highestbars(...)] mostly na | Offsets are negative; only offset indexes the current bar without math.abs | | math.avg finite when an arg is na | Bug: multi-arg avg must propagate na`, not skip | See also Series & history Strategy builtins Numba path Numerical validation Missing features (residual dual-host gaps) Compiler overview --- FILE: docs/pyne/runtime/compiler/numba.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Numba path" description: "JIT bar loops, numbabuiltins kernels, NA-as-NaN, and performance characteristics." --- Numba path Abstract Numeric compile mode emits a single @numba.njit function whose body is the script’s bar loop. Pine builtins that cannot cross the JIT boundary are replaced by hand-written kernels in numbabuiltins.py that take full arrays plus the current index (or a small float state vector for inc forms). The result is large speedups on multi-thousand-bar series after a one-time JIT warm-up—at the cost of a restricted language subset and np.nan as the missing-value sentinel. Kernel contracts below match interpret ta. / calendar helpers where parity was fixed; intentional deviations should be documented on the kernel docstring and here. Conceptual model Interface surface Kernels are defined with @numba.njit(cache=True) in numbabuiltins.py. Generated entry points use @numba.njit(cache=False) in the in-memory IR string; when the engine writes a disk IR module it rewrites that decorator to cache=True so Numba can store machine code next to a real file path (see Cache). Generated code imports from pynescript.compiler.numbabuiltins import so call sites are bare names. Prefer inc when the visitor allocates fixed state (emast, …). Kernel table (interpret-parity highlights) | Kernel | Semantics | | --- | --- | | numbasma(arr, period, i) | Mean of last period bars ending at i; nan if warm-up or any window nan | | numbaema / numbaemainc | SMA seed, then EMA (α = /(period+)). Inc seed is NaN-safe: while the accumulator is still nan and j >= period-, try SMA over arr[j-period+ : j+] only when every sample is finite. Nested EMA / DEMA no longer stuck all-NaN after leading NaNs | | numbarmainc(arr, period, i, st) | Wilder RMA with the same NaN-window-safe SMA seed as numbaemainc. After seed, NaN inputs hold the previous RMA. Required for rma(tr) / expanded ADX so compile is not all-NaN or ~ after warm-up | | numbarsi / numbarsiinc | Wilder RSI, not rolling simple-window RSI. SMA of first period deltas (bars ..period), then RMA of gain/loss (α = /period). First valid bar is i == period (period deltas need period+ prices). avgloss == → . | | numbahighest / numbalowest | Window extrema; full window required (i >= period-); NaN samples skipped | | numbahighestbars / numbalowestbars (+ inc) | TV negative bars-back offsets: if current is extreme, - one bar ago, …, -(length-). Returns -. when window not full, invalid length, or all-NaN. Oldest extreme wins ties (strict > / `). Disk write rewrites @numba.njit(cache=False) → cache=True so Numba’s file locator can cache machine code under the disk IR pycache/. Truncated/corrupt pickle loads raise EOFError / UnpicklingError. The engine purges known .nbi/.nbc via clearnumbafunctioncaches() and recompiles once instead of failing the script. `python from pynescript.compiler import clearnumbafunctioncaches, cleardiskcompilecache clearnumbafunctioncaches() cleardiskcompilecache() ` Internals | Path | Role | | --- | --- | | src/pynescript/compiler/numbabuiltins.py | Kernels (cache=True) | | src/pynescript/compiler/compiler.py | emitnumericmode, call lowering, timearr calendar | | src/pynescript/compiler/engine.py | Disk IR rewrite, Numba cache recovery, prewarm | | tests/testcompilernumba.py | Correctness / parity | Expanding coverage is primarily new kernels + visitor call mapping, not changes to the bar-loop skeleton. Invariants & edge cases . No Python objects in numeric mode. Strings, UDTs, maps, drawings force object mode before njit is applied. . Warm-up cost. First compilescript invokes a dummy run; production servers should cache CompiledScript and prewarm (PYNECOMPILEPREWARM default on, POST /compile/prewarm). . EMA/RMA seed uses sliding all-finite SMA windows—not “seed once at bar period- only if that fixed slice is clean.” Leading NaN on tr / nested EMA sources must eventually produce values. . RSI is Wilder, first valid at i == period. Do not compare against a simple N-bar average-gain RSI. . highestbars/lowestbars return negative offsets and - on incomplete windows; ties keep the oldest bar. . Disk entry cache requires the rewrite to a real file path; pure exec of cache=False IR does not populate Numba’s on-disk function cache for the entry function. . Anaconda static libpython. Packaging notes in AGENTS/build docs apply when shipping JIT-heavy binaries. Worked examples Benchmark mental model `text interpret: O(bars × astnodes × pythondispatch) numeric: O(bars × loweredops) in machine code after JIT ` For simple SMA scripts, internal notes cite order-of-magnitude speedups versus list-based interpretation on long series (see repo docs/COMPILERPLAN.md qualitative claims). Using from Pro API `python Runtime(...).run(source, ohlcv, mode="compile") ` Converts bar dicts → arrays (including time= bar-open ms), compiles, reshapes plots into the standard envelope, flags objectmode when applicable. generatedcode is omitted unless PYNESCRIPTRETURNGENERATEDCODE=. Failure modes | Symptom | Cause | | --- | --- | | TypingError from Numba | Unsupported construct leaked into numeric emit | | All-nan plots | Period longer than series; history index bugs; or (fixed) NaN-poisoned EMA/RMA seed on nested sources | | Nested DEMA / rma(tr) all-NaN | Pre-fix seed; current kernels sliding-window all-finite SMA seed | | RSI diverges vs interpret | Expect Wilder seed/RMA, not rolling window RSI | | highestbars sign / magnitude off | TV negative offsets; incomplete window → - | | EOFError / UnpicklingError at load | Corrupt .nbi/.nbc — engine purges and retries; manual clearnumbafunctioncaches() | | ImportError numba | Environment missing dependency | | Object mode unexpectedly | Visitor detected drawing/UDT/map—inspect visitor.objectmode | Performance notes Prefer one compile, many runs with different OHLCV. Keep scripts in the numeric subset for realtime ticks. Host prewarm of numbabuiltins` + common scripts shifts cold JIT off the first user request. Object mode still wins over AST walking for UDT-heavy scripts but will not match peak njit throughput. See also Compiler overview Technical builtins (interpret) Series & history --- FILE: docs/pyne/runtime/compiler/overview.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Compiler overview" description: "Source-to-source compilation: CompilerVisitor, numeric vs object mode, run(time=…), and engine API." --- Compiler overview Abstract The compile path lowers Pine’s ASDL AST to executable Python that walks bars with an explicit index (baridx) over contiguous numpy arrays—avoiding per-node visitor overhead. Pure numeric scripts wrap the loop in @numba.njit; scripts that touch UDTs, maps, drawings, or strategy select object mode (a tight Python/numpy loop, no AST walk). Numba is required only for numeric mode; object mode runs with numpy alone. The public façade is pynescript.compiler.engine (transpile, compilescript, runscript), also reachable as Runtime.run(..., mode="compile") or mode="auto". This is an MVP with growing surface—not a full substitute for the interpreter on every script (notably multi-asset request. and full strategy analytics). Conceptual model Generated entry point is always: ``text executescriptcompiled(openarr, higharr, lowarr, closearr, volarr, timearr) ` Host packing lives in engine.CompiledScript.run (and pynescript.runtime.Runtime.runcompiled). Interface surface `python from pynescript.compiler.engine import transpile, compilescript, runscript, hasnumba src = transpile(pinetext) generated Python string cs = compilescript(pinetext) CompiledScript result = cs.run(open, high, low, close, volume, time=None) result: dict of plot title → float array, optional drawings, events, … ` | API | Role | | --- | --- | | transpile | Parse + visit → source string (inspect/debug) | | compilescript | exec generated code, warm-up call, return CompiledScript | | runscript | One-shot compile+run (prefer cache CompiledScript for batches) | | hasnumba | Whether numeric (njit) mode can load | | prewarmnumbabuiltins / prewarmscripts | Host cold-start (H warm path) | | compilecachestats / compiledeployconfig | Cache + deploy diagnostics | | clearcompilecache | In-process LRU only | | cleardiskcompilecache | Disk IR / index under cache dir | | clearnumbafunctioncaches | Purge Numba .nbi/.nbc under known dirs | CompiledScript fields: source, generatedcode, execute, plottitles, plotkinds (per-plot plot / hline / fill tags used to synthesize numeric-mode drawings), objectmode, optional nopythonfallbackreason. CompiledScript.run signature `python def run( self, open, high, low, close, volume=None, default: ones time=None, bar-open Unix ms; optional th series ) -> dict[str, Any]: ` | Argument | Behavior | | --- | --- | | OHLCV | Coerced to contiguous float; lengths must match (ValueError otherwise) | | volume=None | Filled with ones | | time=None | Synthetic barindex ms (unit-test / pure-compile default) | | time=arr | Real bar-open timestamps; length must match OHLCV | Runtime.runcompiled packs OHLCV dicts and passes real bar times so calendar/time/timestamp/time[n] stay aligned with interpret. Pure cs.run(o,h,l,c) without time= is fine for pure-price scripts and fixtures. When time is omitted, the engine synthesizes np.arange(n) . (-minute bar opens from epoch). Hosts that care about year/month/time[n] must pass bar-open Unix ms. Warm compile (product defaults) Pro API /run defaults to mode=auto (compile when eligible, interpret on fallback with compilefallbackreason). Disk IR cache is on (PYNECOMPILEDISKCACHE=); Docker sets PYNECOMPILECACHEDIR=/data/compile-cache. Operators call POST /compile/prewarm or pynescript prewarm so first interactive latency skips Numba cold JIT. Design notes live in repo docs/COMPILERPLAN.md (not a product page). Runtime.compileeligible (used by mode=auto) skips compile when the source contains a top-level import or any request. token — those stay interpret even though the emitter can lower same-symbol request.security. Explicit mode=compile still attempts the visitor. Mode selection CompilerVisitor sets objectmode = True when it sees: User-defined types / enums Map / array / matrix handles that cannot stay float Drawing constructors (label.new, line.new, …) and assigned hline handles Strategy usage (object mode + CompileStrategyBroker) String/color series, library imports, or other non-numeric constructs Statement-form hline(price) / fill(...) with a proven-numeric price stay nopython: they emit a constant (or all-nan) series plus plotkinds metadata; the engine synthesizes _drawings after the JIT loop. Mixed scripts that later flip object mode replay those events into the body. Otherwise numeric mode is preferred. Numeric mode requires Numba at load/warm-up; missing Numba raises CompileNumbaRequiredError. Object mode does not need Numba (still star-imports numbabuiltins for safe / non-jitted helpers). On numeric warm-up TypingError / nopython failure, the engine re-emits with forceobjectmode=True and records nopythonfallbackreason—so mode="compile" can still run without hard-failing on partial numeric coverage. Numba is not a hard requirement for all compile. Object-mode scripts (strategy, drawings, UDT, maps) execute without it. Only a pure-numeric emit that never flips to object mode needs Numba installed. What numeric mode lowers today Series assigns, arithmetic/logic, history (close[n]), if/for/while, selected ta. (and bare TA aliases), scalar math helpers, plot, input. defaults, var/varip carry—executed under @numba.njit(cache=False) on the generated entry (cache=False because code is exec’d from a string without a stable disk locator). Disk-cached IR modules may rewrite to cache=True for cross-process reuse of machine code. Object mode extras UDT instances as field dicts Maps as Python dicts drawings list of structured events Optional strategy broker: pending fills, position, events, equity snapshots Compile-path limits (notable) | Construct | Compile behavior | | --- | --- | | request.security / bare security / request.securitylowertf / request.seed | Same-symbol, simple OHLCV expression only (chart identity + bare close/high/… style). Foreign symbol or complex expression → np.nan (no inventing chart close as foreign data). Other request. → object-mode np.nan stub | | Bare builtins vs user series | User-defined series arrays win in visitName: ad = ta.cum(...) / tr = … shadow bare ad/tr formulas so plot(ad) is not silently re-bound to the builtin | | hline(...) | Statement-form + numeric price → nopython constant series (hline, hline, …) + synthesized drawings. Assigned / non-numeric handles → object mode | | fill(..., title=…) | Statement-form → nopython all-nan series key (band color in plotmeta / drawings). Expression-form / mixed object scripts emit _drawings in-loop | Compile-path request.security never invents foreign OHLCV from chart close. Foreign tickers and complex expressions (UDFs, ta. inside the third arg, multi-value constructs beyond simple chart series) lower to np.nan. Use interpret + a real datafeed / dataprovider for multi-asset work. Internals | Path | Role | | --- | --- | | src/pynescript/compiler/compiler.py | CompilerVisitor, emit numeric/object, security/name/fill/hline lowering | | src/pynescript/compiler/numbabuiltins.py | JIT kernels + object-mode safe | | src/pynescript/compiler/strategybroker.py | Compile broker | | src/pynescript/compiler/engine.py | Façade, caches, CompiledScript.run, Numba cache recovery | | src/pynescript/runtime/host.py | Package SoT Runtime.runcompiled envelope; packs OHLCV + time= | | backend/runtime.py | Compat re-export of pynescript.runtime.host | | scripts/compareinterpcompile.py | Interpretcompile series parity harness | | tests/testcompilernumba.py, testcompilerobjects.py, testcompilerstrategy.py | Coverage | | docs/COMPILERPLAN.md | Design history and remaining work (repo, not hosted) | Data layout Unlike interpreter PineSeries deques: `text openarr, higharr, lowarr, closearr, volarr, timearr userarr = np.full(nbars, np.nan) or dtype=object for UDT ploti = np.full(nbars, np.nan) for baridx in range(nbars): userarr[_baridx] = … ploti[baridx] = … ` Bare time / timeclose / lastbartime / calendar extractors read timearr (synthetic when omitted). var lowers to “init only when still na / first write” patterns so carry matches Pine without declaration sets. Numeric mode returns a tuple of plot arrays (host maps titles); object mode returns a dict (plots + _drawings / strategy extras). Caches and corrupt-Numba recovery Three layers: . In-process source LRU (sha of raw and/or sanitized source) + secondary IR cache keyed by generated-code hash. . Disk IR (default on): modules under PYNECOMPILECACHEDIR or $XDGCACHEHOME/pynescript/compile. The source→IR index JSON carries "v": (engine.DISKMETAVERSION). Bump that integer when generated IR semantics change so stale modules are ignored (source hash alone is stable across emitter fixes). . Numba function cache (.nbi / .nbc) next to numbabuiltins and under disk-IR pycache/. Truncated/corrupt Numba pickle files raise EOFError / pickle.UnpicklingError on load. The engine wraps execute/warm paths with purge + single retry via clearnumbafunctioncaches (does not fail the script for a bad cache alone). Manual clean slate: `python from pynescript.compiler import ( clearcompilecache, cleardiskcompilecache, clearnumbafunctioncaches, ) clearcompilecache() cleardiskcompilecache() clearnumbafunctioncaches() shell: rm -rf ~/.cache/pynescript/compile \ src/pynescript/compiler/pycache/numbabuiltins.nb ` Clear disk + Numba caches after compiler emitter or kernel edits if results look stale. Invariants & edge cases . Same AST front-end. No second parser—compile bugs are lowering bugs. . OHLCV (+ time when provided) length equality enforced in CompiledScript.run. . Warm-up ignores exceptions on a dummy -bar series (JIT or first-run); non-nopython failures may surface on the first real run. . Result normalization converts Numba typed maps to plain dicts; drawings / events stay Python lists. . User series shadow bare builtins in visitName (ad, tr, n, …) once arr is allocated. . request.security is not multi-asset on compile. Only chart-symbol simple OHLCV passthrough; everything else is na. . Interpret still owns the full surface. Exotic builtins, real multi-symbol feeds, and rich strategy metrics remain interpret-first until lowered. Worked example Pine: `pine //@version= indicator("c") s = ta.sma(close, ) plot(s, "sma") ` Generated shape (illustrative; numeric mode returns a tuple of series): `python @numba.njit(cache=False) def executescriptcompiled(openarr, higharr, lowarr, closearr, volarr, timearr): nbars = len(closearr) sarr = np.full(nbars, np.nan) plot = np.full(nbars, np.nan) for _baridx in range(nbars): sarr[_baridx] = numbasma(closearr, , _baridx) plot[baridx] = sarr[baridx] return (plot,) Host packs titles → {'sma': plot} ` Interpretcompile parity Full contract (tolerances, buckets, intentional diffs): Interpret compile parity. `bash From repo root — nan-aware allclose on common series keys python scripts/compareinterpcompile.py --bars --limit python scripts/compareinterpcompile.py --glob 'ta.pine' --bars python scripts/compareinterpcompile.py --files tests/fixtures/parity/pine/strategyentrylong.pine python scripts/compareinterpcompile.py --ignore-hline-keys --ignore-fill-keys --strict-keys ` Writes .cache/interpcompileparity.json. First-party hline/fill keys match (..); ignore flags are for leftover corpus noise. Failure modes | Symptom | Cause | | --- | --- | | numba is required for numeric compile mode (CompileNumbaRequiredError) | Install numba or force/land in object mode (drawings/UDT/strategy already do) | | Empty generated code | Visitor returned blank—unsupported top-level shape (CompileEmitError) | | Missing executescriptcompiled | Emit bug or failed exec (CompileLoadError) | | EOFError / UnpicklingError during JIT load | Corrupt Numba .nb—engine purges and retries; if stuck, call clearnumbafunctioncaches | | Silent semantic drift | Kernel seed differences—run scripts/compareinterpcompile.py | | All-nan from request.security | Foreign symbol or non-simple expression on compile path (by design) | | Wrong values for ad / tr after assignment | Fixed by user-series shadowing; if stale, clear compile/Numba caches | | Strategy metrics missing | Use interpret or inspect events / broker fields only | | Calendar/time off by hours | Host omitted time=; synthetic m bar opens used | Remaining work (summary) Expand Numba ta. surface, richer drawings, nested UDT methods, broader request. (still interpret-first for multi-asset; mode=auto already skips any request. source), and grow systematic interpretcompile parity coverage per lowered construct. In-process + disk IR cache (DISKMETAVERSION = ) and product prewarm are landed. See repo docs/COMPILER_PLAN.md` for the living checklist. See also Numba path Strategy broker Interpret compile parity Runtime hub request. / input. (interpret) Numerical validation --- FILE: docs/pyne/runtime/compiler/parity.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Interpret compile parity" description: "Plot-series parity between Runtime interpret and compile modes: harness, tolerances, and known sentinel differences." --- Interpret compile parity Abstract PYNE ships two bar engines that should agree on numeric plot series for scripts in the compile surface: the AST interpreter and the generated Numba/object-mode loop. This page is the short contract for that agreement—how to measure it, what “equal” means, and where intentional differences live (None vs nan, host timearr, structural keys). Strategy event parity (Python vs TypeScript worker) is a separate oracle (tests/testparity.py + fixtures). Here the focus is plot values on one host: pynescript.runtime.Runtime. Conceptual model Both modes receive the same synthetic (or host) OHLCV. Compile additionally builds timearr from bar time fields so time / calendar expressions line up with the interpret time series. Interface surface Corpus harness ``bash From repo root — first-party fixtures by default python scripts/compareinterpcompile.py --bars --limit python scripts/compareinterpcompile.py --files tests/fixtures/parity/pine/strategyentrylong.pine ` | Flag / output | Role | | --- | --- | | Default inputs | tests/fixtures/parity/pine/.pine (first-party) | | Tolerances | rtol=e-, atol=e- (nan-aware) | | Report | .cache/interpcompileparity.json | | Exit | No value/nan mismatches on common series keys | Buckets: OK, fillbackgroundonly, botherrorsame, expectederror, botherror, MISMATCH, interperror, compileerror, structuralonly. Matched errors on both backends (botherrorsame) and intentional demo failures (expectederror, e.g. auto-fib / pivot-depth) count as success unless --strict-errors. First-party hline/fill/bgcolor/plotshape keys match interpret (..). --ignore-hline-keys / --ignore-fill-keys remain optional for leftover corpus key noise; --strict-keys fails on any one-sided key. Focused unit tests | Test | Scope | | --- | --- | | tests/testdividendyieldparity.py | Host-series copy-on-assign + time history (interpret/compile) | | tests/testcompilernumba.py | Kernel-level numeric correctness | | tests/testparity.py | Strategy event fixtures (not plot allclose) | Product path Pro API /run defaults to mode=auto: compile when compileeligible passes (no top-level import, no request. token), otherwise interpret with compilefallbackreason. Parity harness always forces explicit interpret vs compile so fallbacks do not hide drift. What must match . Common plot keys — float series after Nonenan normalization. . Warm-up na — leading missing samples align (interpret None / compile nan). . time / bar-open ms — when OHLCV includes time, or both use the synthetic i fallback. . Structured errors — scripts that runtime.error (e.g. insufficient pivots) should fail on both with messages that normalize equal (botherrorsame). Intentional / soft differences | Topic | Policy | | --- | --- | | Sentinels | Interpret: None; Numba: np.nan. Compare with nan-aware equality, not ==. | | Hline / fill / bgcolor keys | May appear on one mode only; ignore with harness flags unless you need strict key sets. | | Unresolved imports / stubs | Non-numeric plot cells serialize as na (null)—not string stubs. | | Outside compile surface | UDT-heavy / full request. / rich strategy analytics stay interpret-first; auto falls back. | | Disk / Numba caches | Stale IR after kernel edits can fake mismatches—clear compile + Numba caches (see harness docstring). | Internals | Path | Role | | --- | --- | | scripts/compareinterpcompile.py | End-to-end series compare | | tests/testinterpcompileparity.py | Always-on smoke + harness flag tests | | src/pynescript/runtime/host.py | runcompiled, OHLCV + timearr packing | | backend/runtime.py | Compat re-export of the package host | | src/pynescript/compiler/engine.py | Compile façade + result normalize | | src/pynescript/compiler/numbabuiltins.py | JIT kernels under test | Worked example `text python scripts/compareinterpcompile.py \ --files tests/fixtures/parity/pine/strategyentrylong.pine \ --bars → OK (or MISMATCH with first differing bar/key in the JSON report) ` Failure modes | Symptom | Likely cause | | --- | --- | | Systematic offset after warm-up | EMA/RSI seed or window clamp drift in a kernel | | Only-compile empty plots | Script still object-mode / plot not lowered; or cache corruption | | time diverges | Host omitted times on one path only; check time_arr` packing | | Structural-only keys | hline/fill/background naming — use ignore flags or align collectors | See also Compiler overview Numba path Series & history Runtime hub Numerical validation PyneTS parity — TypeScript vs Python plots --- FILE: docs/pyne/runtime/compiler/strategy-broker.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Compile strategy broker" description: "CompileStrategyBroker: pending orders, OHLC fills, OCA, commission, and event export." --- Compile strategy broker Abstract When CompilerVisitor detects strategy usage, object-mode emission instantiates CompileStrategyBroker—a lightweight broker aligned with the interpreter’s fill-before-script cadence, but designed for generated loops (no AST, no full StrategyState metric surface). It supports market entry/close and pending limit/stop/stop-limit orders with per-bar OHLC triggers, OCA cancel/reduce, commission, and slippage. Conceptual model Interface surface Construction (from strategy() kwargs when lowered) ``python CompileStrategyBroker( initialcapital=., commissionvalue=., commissiontype="percent", percent | cashperorder | cashpercontract slippageticks=, mintick=., pyramiding=, defaultqtytype="fixed", defaultqtyvalue=., avgpricemodel="stock", stock | futures | inverse (pynescript extension) leverage=., futures UI: buying power / margin divisor ) ` avgpricemodel is lowered from strategy(..., avgpricemodel=...). The compile broker is a single net lot: partial reduce already keeps positionavgprice sticky (futures-like). Multi-leg stock reweight on partial close is interpret-first until the compile path grows an open-trade list. leverage scales percent/cash default qty (margin × leverage / price) and free cash (equity − notional/leverage). Bar context `python Preferred generated hot path (one call; skips pending walk when empty) broker.beginbar(barindex, open, high, low, close, bartime=…) Equivalent two-step still exists: broker.setbar(barindex, bartime, mark, open=…, high=…, low=…, close=…) broker.processpendingorders(open=…, high=…, low=…, close=…) ` Orders | Method | Role | | --- | --- | | entry(...) | Market or pending entry; reverse flattens opposite | | close / closeall | Reduce/flat with optional price | | Pending book | PendingOrder with type, direction, qty, limit/stop, OCA, maxfillperbar, isentry | Fill logic (triggerprice) | Type | Long trigger | Short trigger | Fill price idea | | --- | --- | --- | --- | | market | — | — | close (immediate path) | | limit | low = limit | gap-aware vs open | | stop | high >= stop | low <= stop | gap-aware vs open | | stop-limit | stop traded and limit available | symmetric | limit price | Partial fills: maxfillperbar caps quantity per bar; residual stays pending. OCA After a fill, siblings in the same ocaname: cancel — remove sibling, emit cancel reduce — decrease sibling qty by fill size none — no interaction Economics Slippage: ± slippageticks mintick by direction on fills. Commission: percent of notional, cash per order, or cash per contract. Position: signed positionsize, positionavgprice, netprofit, equity helpers for return payload. Events emit appends dicts with keys compatible with strategy event serialization (kind, id, direction, qty, ordertype, limit, stop, ocaname, comment, barindex, bartime, ohlc). Object-mode return includes 'events': strategy.toevents() plus position/netprofit/equity snapshots. Internals | Path | Role | | --- | --- | | src/pynescript/compiler/strategybroker.py | Broker implementation | | src/pynescript/compiler/compiler.py | Emits strategy wiring in object mode | | src/pynescript/ast/evaluator/builtins/strategy.py | Interpreter counterpart (richer) | | tests/testcompilerstrategy.py | Compile strategy coverage | NA helpers treat None, blank/na strings, and float NaN as missing prices when classifying order types. Invariants & edge cases . Same order as interpreter: beginbar (or setbar → processpendingorders) → script body. . Closed-trade list is partial. The compile broker now keeps closedtraderecords and some closedtrades accessors (profit, comments, max drawdown/runup). Prefer interpret for the full strategy.closedtrades. / opentrades. metric surface. . Direction normalization accepts strategy.long, long, , buy, etc. . Reverse on opposite entry via closeall(comment="reverse") then open. . Event dicts are mutable lists—fine for run output; not frozen dataclasses. Worked examples Generated prologue (illustrative) `python strategy = CompileStrategyBroker(initialcapital=.) for _baridx in range(nbars): strategy.beginbar(_baridx, float(openarr[baridx]), float(higharr[baridx]), float(lowarr[baridx]), float(closearr[baridx]), bartime=int(timearr[_baridx])) lowered strategy.entry / close calls return {..., 'events': strategy.toevents(), 'positionsize': _strategy.positionsize, 'netprofit': strategy.netprofit, 'equity': strategy.equity} ` Limit buy pending `pine strategy.entry("L", strategy.long, limit=low - syminfo.mintick) ` Object-mode lowers to a pending limit; subsequent bars fill when low trades through. Failure modes | Symptom | Cause | | --- | --- | | No fills | Triggers never met; or market path not invoked | | OCA sibling remains | ocatype none / name mismatch | | PnL vs interpret drift | Commission/slippage/mintick defaults differ | | Missing events in API envelope | Host only maps plots—not _events` | See also Strategy builtins (interpret) Events Compiler overview Numba path --- FILE: docs/pyne/runtime/events.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Strategy events" description: "StrategyEvent shape, emission points, parity corpus, and host serialization." --- Strategy events Abstract A strategy event is the immutable, structured record of one strategy. action (or fill side-effect) at a specific bar. Events are the wire format between PYNE’s Python evaluator, PyneTS (StrategyEvent on RuntimeResult), and downstream HOOX trade workers (via pyne-worker). Changing the dataclass without updating the TS library and tests is a contract break. Conceptual model Interface surface StrategyEvent fields Defined in src/pynescript/ast/evaluator/events.py: | Field | Type (logical) | Notes | | --- | --- | --- | | kind | entry \| exit \| close \| closeall \| cancel \| cancelall \| order | Discriminator | | id | str \| None | Order / entry id | | direction | long \| short \| None | | | qty | float \| None | | | ordertype | market \| limit \| stop \| None | Stop-limit may appear as composite handling elsewhere | | limit / stop | float \| None | Prices when relevant | | ocaname | str \| None | OCA group | | comment | str \| None | Includes system comments (riskblocked, fill:…) | | barindex | int | From context at emit | | bartime | int | From context | | ohlc | -tuple → list in JSON | Bar snapshot | | scriptid | str | Host-stamped (source hash) | | runid | str | Host-stamped run uuid | Frozen dataclass → todict() always includes keys (JSON nulls for unspecified fields) so parity diffs are stable. Emission sources . Explicit builtins — strategy.entry, exit, close, closeall, cancel, cancelall, order push events as they execute. . Broker fills — processpendingorders emits fill-related order / entry / cancel (OCA) events. . Risk gates — blocked entries may emit with comment="riskblocked". Host collection pattern ``python evaluator.resetevents() ... processpendingorders + visit(tree) for ev in evaluator.strategystate.drainevents(): d = ev.todict() d["scriptid"] = scriptid d["runid"] = runid allevents.append(d) ` Runtime.run implements this and returns events in the response envelope. Compile path CompileStrategyBroker accumulates plain dict events (_events in the return mapping) with the same field names for kinds it supports. Exit/trail/OCA/risk halt comments (riskblocked) are on both hosts for the ..+ surface; remaining metric accessors still trail the interpreter. Internals | Path | Role | | --- | --- | | src/pynescript/ast/evaluator/events.py | StrategyEvent | | src/pynescript/ast/evaluator/builtins/strategy.py | Emit sites + drainevents | | src/pynescript/compiler/strategybroker.py | Compile-mode emit | | tests/teststrategyevents.py | Unit behavior | | tests/testparity.py + tests/fixtures/parity/ | Cross-port oracle | | pynets/src/runtime/interpret.ts StrategyEvent | TS library twin | Parity corpus scripts (strategyentrylong.pine, …) regenerate expected JSON via: `bash python tests/fixtures/parity/generatefixtures.py ` Tests strip scriptid / runid before compare. Invariants & edge cases . Immutability. Do not mutate events after construction; drain copies and clears the buffer. . Per-bar reset. resetevents / drain prevents cross-bar leakage in tests that share evaluators. . OHLC list vs tuple. todict converts to list for JSON round-trips. . Kind vocabulary is closed. New kinds require TS + fixtures + this doc. . Not alerts. alert() / alertcondition() use a separate alerts channel—not StrategyEvent. Worked examples Expected single entry Script enters long on a fixed bar; fixture JSON lists one event: `json { "kind": "entry", "id": "L", "direction": "long", "qty": ., "ordertype": "market", "barindex": } ` (plus nullables and ohlc—see real fixtures for full keys). Cancel after OCA Fill of one OCA sibling yields cancel events for others with comment reflecting ocacancel / ocareduce. Failure modes | Symptom | Cause | | --- | --- | | Empty events with live strategy | Forgot drain; or strategy never called | | Parity flake on ids | Comparing scriptid/runid | | TS/Python mismatch | Field rename without dual update | | Duplicate events | Not clearing buffer; double processpendingorders` | See also Strategy builtins Strategy broker PyneTS pyne-worker HOOX live trading HOOX docs --- FILE: docs/pyne/runtime/expressions-statements.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Expressions and statements" description: "How the AST walker evaluates operations, calls, control flow, assignments, and user definitions." --- Expressions and statements Abstract Evaluation is pure visitor dispatch: each ASDL node type maps to a visit method on a mixin. Expressions produce values (with Pine na and series rules); statements mutate context, register types/functions, or emit side effects via builtins. This page is the operational semantics of the interpreter path—what runs inside evaluator.visit(tree) each bar. Conceptual model Interface surface Expressions (ExpressionEvaluator) | Node | Behavior | | --- | --- | | BoolOp (and / or) | Manual short-circuit: and stops at first falsy (na / / False); or stops at first truthy. Returns a Python bool. | | BinOp | NA-safe + - / %; division by zero → na | | UnaryOp | not, unary +/- with NA and list map | | Compare | Chained comparisons with NA-safe operators | | IfExp | Ternary: condition, then, else | | Call | Builtin dispatch, user functions, methods, multi-dispatch overloads | | Tuple / lists | Literal sequences for unpacking and multi-arg returns | Binary ops coerce PineSeries-like objects to .current before operating. List operands broadcast or zip with trailing alignment (see Series & history). Names (NameEvaluator) | Node | Behavior | | --- | --- | | Name | Context lookup; bare series builtins (na, year, …); else string id (lazy) | | Attribute | Qualified context keys, builtins (strategy.positionsize), library modules, UDT fields/methods, array/drawing/matrix method markers, Python getattr fallback | | Subscript | Series history / array / matrix indexing | Critical: qualified names like strategy.opentrades.entryprice are resolved via AST path construction (astqualifiedname) without evaluating intermediate zero-arg series that would collapse the path to an int. Statements (StatementEvaluator) | Construct | Behavior | | --- | --- | | Script | Walk body; finalize library exports if library(...) active | | Assign | var/varip once-per-declaration; const; ordinary assign; tuple unpack; export registration. History-tracked var series get a start-of-bar carry so x[] is the prior persist. | | ReAssign (:=) | Update existing binding (and UDT fields) | | If / For / While | Standard control flow; loop variables in context | | FunctionDef | Store callable in context; multi-dispatch overload lists; export; methods tagged _pinemethod_. After bar the host walks a hot body that skips Function/Type/Enum/Import (already bound). | | TypeDef / EnumDef | Type registry / enum member dicts | | Import | Resolve LibraryRegistry → bind alias to LibraryModule | | Declarations | indicator / strategy / library via declaration builtins | Function invocation binds parameters into context (with restore of prior bindings on exit) so nested calls and bar-local state interact cleanly. After bar , hosts set pinedefslocked so re-visiting the script does not re-register overload tables. The locked walk inlines Assign and Expr(Call) (no generic visit frame) and UDF/call-expression history keys use pinesiteid stamped on the AST. Calls and multi-dispatch Method markers returned from attribute evaluation: | Marker | Meaning | | --- | --- | | ("methodcall", instance, name) | UDT method | | ("arraymethod", list, name) | array.(self, …) | | ("nsmethod", obj, "label.gettext") | Drawing/matrix namespace method | | ("extmethod", receiver, name) | Free method with receiver as first arg | Overloads with typed first parameters (e.g. matrix.float vs array.label) select via coarse type tags; na receivers deliberately exclude matrix/array/drawing tags so optional UDT fields do not mis-dispatch to tostring on collections. Internals | Path | Role | | --- | --- | | src/pynescript/ast/evaluator/expressions.py | Ops, calls, conditionals | | src/pynescript/ast/evaluator/names.py | Names, attributes, subscripts | | src/pynescript/ast/evaluator/statements.py | Script structure, assign, defs, import | | src/pynescript/ast/evaluator/literals.py | Constants, colors, strings | | src/pynescript/ast/evaluator/base.py | Context, math constants, library registry hook | | src/pynescript/ast/evaluator/types.py | EvaluatorProtocol typing for mixins | Invariants & edge cases . Context is the single store. Builtins read OHLCV and strategy series from self.context; hosts must keep it coherent each bar. . String fallback names. An unbound Name returns its id string so later attribute/call resolution can still hit the builtin map ("ta.sma" patterns via attribute chains). . Parameter unbind. Missing-before-bind params use a sentinel so unbind pops rather than leaving None ghosts. . na normalization. String "na" / "nan" / "none" may coerce to None at dispatch boundaries for UDT optional args. . User functions re-enter the visitor. Recursive and higher-order patterns are limited by Python stack, not a Pine-specific trampoline. Worked examples Ternary and comparison chain ``pine //@version= indicator("cmp") v = close > open ? close - open : open - close plot(v) ` Each operator path is NA-safe: if close or open were missing, comparisons and arithmetic yield na. User function with series `pine //@version= indicator("fn") f(x, n) => ta.sma(x, n) plot(f(close, )) ` f is stored as a Python callable that rebinds x/n and visits the function body AST each call. Method multi-dispatch sketch `pine method log(this, string s) => ... method log(this, float x) => ... ` Both register under the same name with _pineoverloads_; call sites pick by receiver/arg type tags. Failure modes | Error | Meaning | | --- | --- | | unexpected type of node | Missing visit for an AST construct | | Unsupported binary operator | Op class not in the dispatch table | | History series[-] is na | Negative Pine offsets soft-fail to None (not Python wraparound, not a raise) | | Wrong overload / destroyed receiver | Historical na` string path; now normalized—report residual mismatches as bugs | | Stack overflow | Deep recursion in user functions | See also Series & history Builtins hub Libraries Type system (core) --- FILE: docs/pyne/runtime/index.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Runtime" description: "Bar-loop evaluator — series caps, incremental TA, alerts, fill export, foreign request.security → na, and interpretcompile parity." --- Runtime Abstract PYNE’s runtime is the deterministic bar-loop that turns a parsed ASDL AST into series values, drawing side-effects, strategy events, and alerts. 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; Pro API defaults to mode=auto. This section documents semantics, not the chart. AXIS consumes plots / fill / drawings; hosts export alerts (optional L webhooks); HOOX consumes strategy events. None of those hosts is required to evaluate a script. Conceptual model Execution modes share the same parse tree (Pro API / Runtime also expose auto): | Mode | Entry | Bar loop | Typical use | | --- | --- | --- | --- | | Interpret | NodeLiteralEvaluator.visit(tree) per bar | Host updates PineSeries / context, re-visits AST | Full language surface, strategy, drawings, alerts, request. | | Compile | pynescript.compiler.engine.compilescript | Generated for _baridx in range(n) over OHLCV + timearr | Numeric indicators (Numba) or object-mode subset | | Auto | Runtime.run(..., mode="auto") | Compile when safe; interpret on fallback | Pro API default — prefer throughput without hard-failing exotic scripts | Compile inputs are contiguous arrays: openarr … volarr plus timearr (bar-open Unix ms; synthetic barindex when the host omits times) so time / calendar builtins match the interpret time series. Disk IR cache + product warm-compile (prewarm API/CLI) cut cold JIT latency. See Compiler overview and Interpret compile parity. request.security (landed): interpret resamples same-symbol simple OHLCV and allowlisted ta.sma / ema / rsi / atr on a coarser TF (last completed HTF bucket only). Compile passthrough is same-symbol simple OHLCV only. Foreign symbols and complex / nested expressions resolve to na on both hosts (no inventing chart close as foreign fundamentals). Policy tags land on result["meta"]["requestsecurity"]. Real multi-symbol feeds remain adapter work (roadmap B). Series caps / incremental TA: host list trim defaults on (PYNESERIESCAP); bar-mode TA hot path defaults on (PYNETAINCREMENTAL). Drawing GC honors maxcount on the interpret registry; fill exports series keys + plotmeta for AXIS bands. Interpret hot path (.. / Round ): the AST walker inlines Assign / Expr(Call) after the first bar (function/type/import decls stay locked), skips unused derived OHLCV series (hl / hlc / ohlc / tr unless named, input.source, or ta.vwap), and reuses plot cells by call-site. Same-machine bench @ bars vs ..: minimal .×, ta.sma .×, tacombo .×. PYNESERIESRING stays off. Host flags and knobs | Knob | Default | Effect | | --- | --- | --- | | PYNESERIESCAP | on | Trim chronological currentseries lists (maxbarsback / ) | | PYNETAINCREMENTAL | on | Call-site incremental ta. in bar mode | | PYNESERIESRING | off | Chronological ring lookback; skip dual list write when on | | PYNELIGHTPLOTS | off | Skip plot columns + input meta (corpus OK/fail only) | | PYNERUNTIMEMODE | interpret | Default when Runtime.run(mode=) is omitted (auto is the Pro API body default) | | timeoutseconds= | None | Interpret wall-clock budget; checked every bars → timedout + errorkind=runtime | | libraries= | [] | {namespace, name, version, source} registered before import (auto forwards into interpret fallback) | | realtime | historical | Interpret-only varip / forming-bar simulation (realtimelastbar / ticks / bars / frombar) | Interface surface | Concern | Start here | | --- | --- | | Series history, [], var/varip, PYNESERIESCAP, na | Series & history | | Expressions, statements, control flow | Expressions & statements | | Builtin namespaces (ta., strategy., …) | Builtins hub | | Drawing / plot / fill / maxcount GC | Drawing & plotting | | request. / input. (foreign → na) | request / input | | Library export / import | Libraries | | StrategyEvent parity contract | Events | | alert() / webhooks | Alerts | | Numba / object-mode compiler | Compiler overview | | Interpret vs compile plot parity | Parity testing | Library callers typically use NodeLiteralEvaluator or the package pynescript.runtime.Runtime (Pro API re-export) rather than wiring the bar loop by hand: ``python from pynescript.ast.evaluator import NodeLiteralEvaluator from pynescript.runtime import Runtime package SoT (..+) ev = NodeLiteralEvaluator(context={"close": [., ., .], "barindex": }) result = ev.evaluatescript('//@version=\nindicator("x")\nplot(close)') Full bar loop (preferred host API): Runtime(symbol="CHART").run( source, ohlcvdata=bars, mode="auto", timeoutseconds=None, libraries=None, ) ` Hosts that own OHLCV (Pro API, workers) use the package host: push bar → fill pending orders → visit(tree) → drain events → collect plots. SoT: src/pynescript/runtime/ (host.py, series.py, evaluator.py). backend.runtime / backend.series / backend.evaluator are identity re-export shims for the Pro API. Internals | Path | Role | | --- | --- | | src/pynescript/runtime/ | Package Runtime SoT — host, series, CustomEvaluator | | 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.py | Compat re-export (sys.modules alias) of pynescript.runtime.host | | tests/testevaluator.py, tests/testparity.py, tests/teststrategy.py, tests/testfirstpartytagoldens.py | Semantic oracles + dual-host TA goldens | 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 . Bar re-entrancy. The AST is visited once per bar. Function/type definitions lock after bar (pinedefslocked) so multi-dispatch tables do not grow \(O(\text{bars}^)\). . Order of broker steps. Pending limit/stop fills run before script body evaluation on each bar (interpreter and compile object-mode agree). Pending-fill averaging when pyramiding ≤ matches both brokers (F). . 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. Cross-mode plot compares must normalize (scripts/compareinterpcompile.py). . Series caps. Unbounded currentseries growth is capped (PYNESERIESCAP default ON, maxbarsback / SERIESMAX) so long histories stay memory-bounded (T). . Incremental TA. Hot-path kernels (MAs, oscillators, bb/kama/cmo/stochrsi, volume obv/wad/cmf/klinger/nvi/pvi, …) use call-site incremental updates in bar mode (PYNETAINCREMENTAL= to disable). . Strategy state is per-evaluator. StrategyState lives on the instance (evaluator.strategystate), never as a process-global singleton—required for concurrent runs and parity tests. . Drawing/plot registries are side channels. Visual objects do not participate in pure expression values except where plot() returns a plot id for fill(). Hosts export fill bands for AXIS via series + plotmeta (and drawings on compile). Drawing objects honor maxcount GC (package + Pro API + AXIS Pyodide). . Alerts are a host side-channel. alert() / alertcondition() firings are not strategy events; dual-host export + L webhooks are documented under Alerts. . Host series are not aliased on assign. last = time (or open/high/…) copies the current scalar into a fresh series for last—see Series & history. . Omitted bid/ask stay na. Host bid / ask update only when those keys appear on a bar dict; they are not synthesized from close. Worked example — minimal bar loop mental model `text for barindex, bar in ohlcv: open/high/low/close.update(bar) context[barindex, time, barstate, …] = … processpendingorders(OHLC) broker sim evaluator.visit(ast) script body events += drainevents() series.append(plot values) ` Compile mode collapses this into one generated function over full arrays (openarr…volarr, timearr), with _baridx standing in for barindex. Failure modes | Symptom | Likely 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 TV | Partial-fill / OCA / risk settings, or fill timing vs bar | | Numba path raises / falls back | Script uses UDT/map/drawing → object mode; or Numba not installed | | Plots empty but no error | Host forgot to collect plotoutputs / PlotRegistry after visit; or PYNELIGHTPLOTS= | | timedout + partial series | timeoutseconds elapsed (checked every interpret bars) | See also Language core — grammar, AST, types before evaluation Alerts — alert() engine + L webhooks Pro API runtime bridge Interpret compile parity — scripts/compareinterp_compile.py` Numerical validation Roadmap — H/H/T/T/F/F/L landed; Pp corpus tail remains pyne-worker AXIS docs — optional AXIS for series/drawings --- FILE: docs/pyne/runtime/libraries.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Libraries" description: "In-process library export/import, LibraryRegistry, and evaluate-order requirements." --- Libraries Abstract TradingView libraries publish as namespace/Name/version with exported members. PYNE implements an in-process analogue: evaluate a library("Title") script (or register source by path), collect exports (export const, exported functions, types), then import binds an alias to a LibraryModule for alias.member access. There is no network publish step—the registry is the host’s responsibility. Conceptual model Interface surface Library script ``pine //@version= library("MyMath") export add(float a, float b) => a + b export const float PHI = . ` On script completion, StatementEvaluator finalizes pendinglibraryexports onto the active LibraryModule and calls LibraryRegistry.register. Consumer import `pine //@version= indicator("use") import user/MyMath/ as M plot(M.add(close, M.PHI)) ` Resolution (LibraryRegistry.lookup): . Exact (namespace, name, version) if provided and registered. . Else title-only match (MyMath) for local evaluation workflows without publisher path. API on the evaluator `python ev.registerlibrarysource(namespace, name, version, sourcestr) mod = ev.lookuplibrary(namespace=..., name=..., version=...) ` registerlibrarysource stores Pine text for lazy load on the first matching import. loadlibrarysource evaluates definitions only (skips showcase body) and finalizes pendinglibraryexports onto the LibraryModule so export const / exported functions bind (..). Evaluating a library script on the same evaluator still populates exports eagerly. Host evaluate (..+) Hosts pass published sources without a pre-eval step: `python Runtime(symbol="TEST").run( consumersource, ohlcv, mode="interpret", or "auto" — import is compile-ineligible libraries=[ {"namespace": "ns", "name": "Lib", "version": , "source": librarysource}, ], ) ` The same list is accepted on Pro API and pyne-worker POST /run as libraries. pyne-worker also stores the array on deployed scripts so cron / scriptid reuse it. Pro API slices to entries (backend/app.py); Runtime.run itself does not cap. mode=compile has no import path — mode=auto treats import as compile-ineligible and forwards libraries= into interpret fallback (..). LibraryModule Dataclass with title, optional namespace/version, and exports: dict[str, Any]. Attribute access reads exports; missing members raise AttributeError with a clear message. NameEvaluator routes alias.member when the base value is a LibraryModule. Internals | Path | Role | | --- | --- | | src/pynescript/ast/evaluator/libraries.py | LibraryModule, LibraryRegistry | | src/pynescript/ast/evaluator/statements.py | export registration, import visit, library finalize | | src/pynescript/ast/evaluator/base.py | registry fields on BaseEvaluator | | src/pynescript/ast/evaluator/init.py | registerlibrarysource, lookuplibrary | | src/pynescript/runtime/host.py | Runtime.run(..., libraries=) + auto-mode forward | | tests/testlibraryexportimport.py | Round-trip coverage | Exportable values include callables (user functions), constants, and type-related bindings depending on declaration paths. Exported methods participate in the same call machinery as local functions once retrieved from the module. Invariants & edge cases . Evaluate library before consumer (or register source for lazy import). Empty registry → import failure. . Same evaluator instance (or shared registry) must see both scripts—registries are not process-global unless the host shares them. . Version is part of the path key. Mismatched version does not fall back to another version automatically when namespace is specified. . Title registration always updates bytitle so simple local titles work without publisher metadata. . No TV account auth. Publishing/versioning outside the process is out of scope. Worked examples Explicit registration `python from pynescript.ast.evaluator import NodeLiteralEvaluator ev = NodeLiteralEvaluator() ev.evaluatescript(librarysource) registers by title ev.evaluatescript(consumersource) import by title or path ` Path registration without pre-eval `python ev.registerlibrarysource("user", "MyMath", , librarysource) import user/MyMath/ loads definitions and finalizes exports on first visit ` Failure modes | Symptom | Cause | | --- | --- | | Import resolves to nothing | Library never registered / wrong title | | Library 'X' has no exported member 'y' | Missing export or typo | | Stale exports | Re-evaluated library without re-import; host cache | | Version conflict | Multiple libs same title—last register` wins for title key | See also Expressions & statements Builtins declarations End-user library API guide --- FILE: docs/pyne/runtime/series-and-history.mdx Copyright (C) - jangoblockchained This file is part of pynescript. pynescript is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version of the License, or (at your option) any later version. pynescript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with pynescript. If not, see . SPDX-License-Identifier: AGPL-.-or-later --- title: "Series and history" description: "Pine series model: history operator, na propagation, var/varip persistence, and bar-mode scalars." --- Series and history Abstract Pine’s core data structure is the series: a value that evolves bar by bar, with random access to past values via the history operator []. PYNE implements that model with host-managed series wrappers (PineSeries in the Pro API runtime), plain Python lists in unit tests, and flat numpy arrays on the compile path. Understanding history indexing and na is prerequisite to every builtin and strategy claim. Conceptual model Pine indices are most-recent-first: | Pine | Meaning | List representation (chronological) | | --- | --- | --- | | close[] | Current bar | list[-] | | close[] | Previous bar | list[-] | | close[n] with \(n \ge \text{len}\) | Out of range | na (None) | Negative offsets are not Python wraparound; they soft-fail to na (None). Interface surface History operator (visitSubscript) Implemented in NameEvaluator.visitSubscript: Float indices coerce to int (e.g. depth / ); NaN index → na. Lists map Pine index \(i\) to Python index -(i + ). Scalars: x[] is x; x[i] for \(i > \) is na (no history buffer). Matrices use [row, col] (tuple/list of length ), not series semantics. Series wrappers Hosts expose OHLCV as objects with: .current — scalar for the active bar .history — most-recent-first buffer (deque or list) Arithmetic in ExpressionEvaluator coerces such wrappers via asscalaroperand so close + does not attempt object addition. Assigning host series (copy, not alias) Binding a host multi-bar series into a new name copies the current scalar into a fresh series for that name—it does not alias the underlying open / high / low / close / volume / time buffers: ``pine last = time // copy of current time into series last last := last + // mutates only last, never the host time buffer ` Implemented in StatementEvaluator.bindseriesname: if the RHS has .current / .history / .update (a host or tracked series), the RHS is reduced to its current scalar before allocating or updating the LHS series. Same-bar reassignment (x = then x := expr) overwrites the current sample so x[] remains the previous bar’s final value. This prevents scripts such as dividend TTM time tracking from corrupting time[j] history when they rebind through a local. var / varip / const | Qualifier | Semantics in PYNE | | --- | --- | | var / varip | Initializer runs on first execution of that declaration site (tracked in vardeclarations), not only barindex == . Later bars skip re-init so the value carries. History-tracked var series get a start-of-bar carry (commitunwrittenhistory) so x[] is last bar’s persist even when no := ran this bar. On realtime ticks, varip re-evaluates the RHS. | | const (v) | Always initializes when the statement runs; not a cross-bar carry lock like var. | | bare assign | Re-evaluates every bar; series “history” is the host series or prior list values. | Nested var inside if barstate.islast or a function therefore initializes on first path-taken bar—matching Pine’s execution-based persistence. na propagation | Operation | Rule | | --- | --- | | Binary arithmetic / comparison | Any None operand → None (or element-wise for lists) | | Division by zero | → None (not exception) | | Soft type errors ("a" + ) | → None | | Unary | None stays None | | History OOB | → None | | Bare name na | Builtin / sentinel resolving to None | | nz(x, r) (Numba path) | nan → replacement | List-valued series apply operators element-wise, aligning on the trailing edge when lengths differ (pad leading None). ta.highestbars / ta.lowestbars offsets These return a bars-back offset (not the extreme price): | Result | Meaning | | --- | --- | | | Extreme is on the current bar | | -, -, … | Extreme is , , … bars ago (down to -(length-)) | | - sentinel (warm-up / invalid / all-na) | Short history (barindex+ < length), bad length, or no finite samples in the window | On ties, the oldest extreme in the window wins (leftmost bar). Interpret and Numba kernels share this contract (highestbars / numbahighestbars). History subscript interaction: Pine history indices are non-negative (series[n] with \(n \ge \)). Negative indices soft-fail to na on interpret series wrappers so warm-up / auto-step-- / highestbars misuse do not abort the bar loop. On the compile path, dynamic offsets are coerced float→int (NaN→) and the computed array index is clamped to [, n) so expressions like high[-ta.highestbars(...)] (future-looking form) soft-fail to nan near series end rather than raising. Bar mode vs full-series mode Technical helpers (technicalsubmodules/core.py) distinguish: Full-series mode (unit tests with explicit lists): ta.sma may return a full list of values. Bar mode (pinebarmode): returns the current scalar so expressions like ta.ema(close,) - ta.ema(close,) stay numeric per bar. History buffers for indicators are truncated to a rolling window (SERIESMAX = for wrapper histories) to avoid \(O(n^)\) full-history recomputation every bar. Host series caps (PYNESERIESCAP) Pro API / pynescript.runtime.Runtime optionally trims chronological currentseries lists and related host history so long charts do not grow \(O(\text{bars})\) memory per series. | Knob | Default | Meaning | | --- | --- | --- | | PYNESERIESCAP | on | Enable trimming. Disable with / false / no / off (oracle / debug only). | | Cap size | (DEFAULTSERIESMAX) | Raised when the script declares a larger maxbarsback=…. | | PYNESERIESMAX | unset | Absolute override of the cap (positive int). | | PineSeries history floor | | Separate from list caps; raised by maxbarsback / PYNESERIESMAX. | Correctness notes (see src/pynescript/runtime/series.py; backend/series.py is a re-export shim): Window kernels (ta.sma, highest, …) need length ≤ cap. Recursive smoothers under incremental TA (default PYNETAINCREMENTAL on) carry state and stay safe independent of list length once warm. With full recompute (PYNETAINCREMENTAL=) and bars ≫ cap, EMA/RMA-style paths can diverge from a full-history oracle—prefer incremental (default) or raise the cap / disable series cap for goldens. Out-of-range history offsets still return na (None); never . Unused derived series (..) The interpret host updates open / high / low / close / volume / time every bar. Derived series (hl, hlc, ohlc, tr, timeclose) are written only when the source names them, uses input.source, or calls ta.vwap / vwap (default source is hlc even when that identifier is absent). ta.ao rebuilds hl from high/low if the derived list is empty. PYNESERIESRING (chronological tail view) remains default off. When on, the host skips the second chronological list write. Internals | Path | Role | | --- | --- | | src/pynescript/ast/evaluator/names.py | visitName, visitAttribute, visitSubscript | | src/pynescript/ast/evaluator/expressions.py | NA-safe binary/unary ops, series coerce | | src/pynescript/ast/evaluator/statements.py | var/varip/const assign | | src/pynescript/ast/evaluator/builtins/technicalsubmodules/core.py | asseries, bar mode finalize | | src/pynescript/ast/evaluator/builtins/utility.py | maxbarsback, lastbarindex, na helpers | | src/pynescript/runtime/host.py | Bar loop; derived-series skip; series-cap trim (backend.runtime re-exports) | | src/pynescript/runtime/series.py | PineSeries, resolveseriescap, PYNESERIESCAP / PYNESERIESMAX / PYNESERIESRING | Compile path: series are np.full(nbars, np.nan) written at baridx; history is arr[_baridx - n] with OOB → np.nan (see Compiler overview). Host timearr is a chronological float vector of bar-open Unix ms (same length as OHLCV); bare time lowers to timearr[_baridx], and time[n] uses the same history indexing rules as close[n]. When the host omits bar times, Runtime synthesizes i so length always matches—calendar / time scripts stay defined on both modes. Invariants & edge cases . Chronology of .history. Most-recent-first; reverse when converting to chronological lists for ta. / array.. Compile arrays are chronological (index = first bar). . No negative Pine indices. Soft-fail to na / nan (not Python wraparound, and not a raise)—different from Python lists. See highestbars note above. . None vs float('nan'). Interpreter prefers None; Numba kernels use np.nan. Cross-mode comparisons must normalize. . maxbarsback. Raises the host series cap when larger than the default ; still not a TV-style hard chart depth, but it affects trim policy. . PYNESERIESCAP default on. Disable only for debugging / full-history oracles; pair with incremental TA for production long histories. . Derived skip. ta.vwap() still requires a live hlc series; the host treats vwap as a derived-series consumer. . Tuple returns from multi-value ta.. Unpacking [a, b, c] = ta.macd(...) uses current when it is a sequence of matching arity; otherwise history heuristics apply. . Host series assign is by value. New names never share the host OHLCV/time buffer reference. Worked examples History lag `pine //@version= indicator("lag") delta = close - close[] plot(delta) ` On bar , close[] is na → delta is na. On bar +, arithmetic is ordinary floats (or series lists in batch tests). var counter `pine //@version= indicator("count") var int n = n := n + plot(n) ` n initializes once; subsequent bars reassignment (:=) increments. Declaration sites are recorded in vardeclarations. Element-wise NA Given two list series of different lengths, a + b aligns tails and pads the longer prefix with None—preserving “most recent bars line up” intuition. Failure modes | Symptom | Cause | | --- | --- | | All na after first bar | Host not updating series / barindex between visits | | var re-inits every bar | Fresh evaluator or resetvardeclarations each bar incorrectly | | TypeError on close + | Series wrapper missing .current / not coerced | | Compile vs interpret plot mismatch | nan vs None, or seed differences in EMA/RSI kernels — see parity | | time[j] wrong after rebinding last = time | Expected only if host series were aliased; verify copy-on-assign path | | highestbars always - | Warm-up (barindex+ < length) or all-na window | | Long-chart OOM / growing RSS | Series cap disabled or PYNESERIESMAX huge; leave PYNESERIES_CAP` on | | EMA drift after many bars with cap | Full recompute + short cap — enable incremental TA or raise cap | See also Expressions & statements Technical builtins Numba path Interpret compile parity Runtime hub ---