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 &lt; (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 &lt; .%; 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 | &lt; .% | Pass |
| Good | &lt; .% | Pass |
| Acceptable | &lt; .% | 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 | ~% |
| &lt; .% | ~% |
| .–.% | ~% |
| .–.% | ~% |
| ≥ .% | % |
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 &gt; 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 &lt; → 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
---
