Production Python Cloudflare® Worker for the PYNE stack — evaluate TradingView® Pine Script™ on the edge with the same bar-loop contract as the Pro API.
Version: 0.6.0 · Runtime: Cloudflare Workers (Python) · Engine: hoox-pyne (pynescript ≥ 0.4.4)
Website: hoox.sh/pyne · Docs: hoox.sh/pyne/docs · Repo: hoox-sh/pyne-worker
Edge deploy (example): https://pyne-worker.cryptolinx.workers.dev
This button deploys this edge host only. Need parse, lint, compile, LSP, Flask Pro API, or the language SoT? Use hoox-sh/pyne — pip install "hoox-pyne[lsp,compile,pro]".
Pine Script™ and TradingView® are trademarks of TradingView, Inc. Cloudflare® is a trademark of Cloudflare, Inc. This project is an independent effort and is not affiliated with or endorsed by TradingView, Inc. or Cloudflare, Inc.
pyne-worker is a thin Cloudflare® Workers wrap around the vendored pynescript bar-loop. It is not a substitute for the main PYNE repo.
| This Worker | Use hoox-sh/pyne instead |
|---|---|
POST /run evaluate + alerts + libraries |
CLI (pyne), LSP (pyne-lsp), VS Code extension |
| R2 bars, 1m cron, L2 webhooks | Flask Pro API, Docker images, /run/batch, chart preview |
Vendored engine snapshot (sync via ./scripts/sync_vendor.sh) |
Live package, Numba desk compile, corpus / parity harness |
| 30s / 100KB / 100K bars / 5MB envelope | Uncapped research on a machine you control |
Optional TRADE_SERVICE → trade-worker |
Full HOOX mesh (gateway, D1, risk, execution) |
One-click notes:
- Dashboard prompts for
API_KEY(required),ALERT_WEBHOOK_URL,INTERNAL_KEY_BINDING(see.dev.vars.example). - R2 bucket
OHLCV_DATAis provisioned fromwrangler.jsonc. TRADE_SERVICEbinds a Worker namedtrade-worker. If that Worker is not in your account, remove theservicesblock fromwrangler.jsoncbefore retrying — trade forward is optional and fails closed.
Part of the HOOX open trading stack:
| Product | Role | Repo | Website |
|---|---|---|---|
| HOOX | Edge trading framework (Workers mesh) | hoox-sh/hoox | hoox.sh · docs |
| PYNE | Pine Script™ toolchain + Pro API | hoox-sh/pyne | hoox.sh/pyne · docs |
| PyneTS | TypeScript / Bun library (@hoox-sh/pynets) — not a Worker |
hoox-sh/pynets | docs |
| pyne-worker | Python edge evaluate host (this repo) | hoox-sh/pyne-worker | docs |
| pyne-agent-worker | NL → PYNE scripts (Workers AI™; optional validate via this worker) | hoox-sh/pyne-agent-worker | AXIS plugin · PYNE agent |
| AXIS | Installable charting PWA | hoox-sh/axis | hoox.sh/axis · docs |
| trade-worker | Multi-exchange order routing | hoox-sh/trade-worker | — |
Note: pyne-agent-worker is fully usable without this evaluate host (standalone chat). When PYNE_WORKER_URL or a service binding points here, the agent can generate → POST /run → retry for higher-quality scripts.
AXIS / HOOX / CLI
│ evaluate contract (POST /run)
▼
┌───────────────────┐ ┌────────────────────┐
│ pyne-worker │────▶│ trade-worker │ strategy events
│ (Python edge) │ │ (execution) │
└─────────┬─────────┘ └────────────────────┘
│ engine
▼
hoox-sh/pyne (import: pynescript)
Local sibling layout (typical):
~/Git/hoox # edge stack (hoox-sh/hoox)
~/Git/pyne # PYNE core (GitHub: hoox-sh/pyne) — published clone name
~/Git/pynescript # same repo, alternative clone name
~/Git/pyne-worker # this repo
~/Git/pine-worker # TS edge sibling
~/Git/axis # charting PWA
pyproject.toml [tool.uv.sources] points at ../pynescript. If the sibling is cloned as pyne, either clone/symlink it as pynescript or set the path and uv sync:
pynescript = { path = "../pyne", editable = true }pyne-worker is the production edge host for PYNE:
- Runs the full pynescript bar-loop (
mode=interpret|compile|auto) on Cloudflare® Workers - Speaks the shared evaluate contract with Flask Pro API, AXIS, and HOOX (docs)
- Deployed scripts + 1m bar-close cron + live Bybit kline feed → R2
alert()/alertcondition()export + L2 HTTP webhooks- Strategy trade events forwarded to trade-worker via service binding
Strong real-world Pine coverage via the open PYNE engine — not a claim of bit-identical TradingView® platform parity. See PYNE docs and the corpus status in hoox-sh/pyne.
- Full Pine v5/v6 surface — engine from hoox-pyne / hoox-sh/pyne
- Auth —
X-API-Key/ Worker secretAPI_KEY - Rate limiting — sliding window (100 req / 60s)
- Input validation — script size (100KB), bars (100K), payload (5MB)
- Structured logging — JSON per-request with IDs + timing
- Health —
GET /health(R2 + service binding checks) - 30s wall timeout per
/run - R2 OHLCV —
POST /ingest,data/{SYMBOL}/{TIMEFRAME}/{YYYY}.jsonl - Compile modes —
interpret·compile·autoonPOST /run - Script registry — deploy by
script_id, list/get/delete - 1m bar-close cron —
* * * * *+POST /cron/run(runs only on new bar) - Live market feed — closed klines (Bybit → R2, Binance fallback)
- Trade forwarding — strategy events → trade-worker
- Alert engine + L2 webhooks —
ALERT_WEBHOOK_URLor per-jobwebhook_url - Libraries —
POST /runlibraries: [{namespace, name, version, source}]forimport ns/Name/ver(AXIS git-publish emulator; pynescript 0.3.7+) - Drawings / plot_meta / logs — same evaluate-contract envelope as the Pro API
- $0 tier OK for light use; Paid Workers recommended for heavy 1m cron
Product docs: Alerts · Evaluate contract
# Install (editable pynescript sibling via pyproject)
pip install -e ".[dev]"
# Tests
pytest -v
# Sync engine into python_modules/ (required before Cloudflare deploy)
./scripts/sync_vendor.sh
# If the sibling checkout is named pyne (GitHub: hoox-sh/pyne) rather than pynescript:
# PYNESCRIPT_SRC=/path/to/pyne/src/pynescript ./scripts/sync_vendor.sh
# (repo may be cloned as pyne or pynescript)
# Deploy
npx wrangler deploy
echo "my-secret-key" | wrangler secret put API_KEYDeploy gotcha: Wrangler packages python_modules/pynescript, not the editable install.
After pulling new PYNE APIs, always re-run ./scripts/sync_vendor.sh.
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /health |
No | Health + feature flags |
| POST | /run |
Yes | Evaluate Pine (mode, script or script_id) |
| POST | /ingest |
Yes | Upload OHLCV to R2 (1m, 1h, 1d, …) |
| POST | /scripts |
Yes | Deploy a Pine script (R2 registry) |
| GET | /scripts |
Yes | List deployed scripts (no source) |
| GET | /scripts/:id |
Yes | Get full deployed script |
| DELETE | /scripts/:id |
Yes | Remove deployed script |
| GET | /cron/jobs |
Yes | List bar-close cron jobs |
| PUT | /cron/jobs |
Yes | Replace cron jobs config |
| POST | /cron/run |
Yes | Manually trigger bar-close scheduler |
| POST | /feed/refresh |
Yes | Pull latest klines into R2 (Bybit primary) |
{
"script": "//@version=6\nindicator('test')\nplot(close)",
"ohlcv": [
{"open": 100, "high": 105, "low": 95, "close": 102, "time": 1000, "volume": 1000},
{"open": 102, "high": 108, "low": 101, "close": 106, "time": 2000, "volume": 1200}
],
"symbol": "BTCUSDT",
"mode": "auto"
}| Field | Notes |
|---|---|
script |
Inline Pine source |
script_id |
Load deployed script from R2 (instead of script) |
ohlcv / data |
Bars; or omit and set symbol + timeframe to read R2 |
mode |
interpret (default) · compile · auto (compile then fall back) |
inputs |
Optional input.* overrides (title → value); forces interpret under auto |
libraries |
Optional [{namespace, name, version, source}] — binds import ns/Name/ver (max 32; interpret/auto only) |
profiler |
Per-line interpret timings (AXIS gutter) |
timeout_seconds |
Wall-clock budget (default 30, max 30) |
max_bars |
Tail length when loading long R2 history |
Also accepts "data" instead of "ohlcv" (PYNE Pro API compat). Success response includes status, plots, series, plot_meta, alerts, events, drawings, inputs, logs, meta, count/bars, and structured error / error_kind on failure.
Library example (import ns/Lib/1):
{
"script": "//@version=6\nindicator('use')\nimport ns/Lib/1 as x\nplot(x.FOO)",
"mode": "interpret",
"ohlcv": [{"open": 1, "high": 2, "low": 0.5, "close": 1.5, "time": 1}],
"libraries": [
{
"namespace": "ns",
"name": "Lib",
"version": 1,
"source": "//@version=6\nlibrary(\"Lib\")\nexport const float FOO = 1.5\n"
}
]
}Deployed scripts may store the same libraries (and inputs) array; POST /run with script_id and cron both reuse them.
export WORKER=https://pyne-worker.cryptolinx.workers.dev # or your deploy URL
# 1) Ingest 1m bars (from your machine — Binance blocks many CF IPs)
python scripts/fetch_and_ingest.py \
--symbol BTCUSDT --timeframe 1m \
--ingest-url "$WORKER/ingest" \
--api-key "$API_KEY"
# 2) Deploy Pine
curl -sS -X POST "$WORKER/scripts" \
-H "Content-Type: application/json" -H "X-API-Key: $API_KEY" \
-d '{
"id": "btc-sma",
"script": "//@version=6\nstrategy(\"s\")\nplot(close)",
"symbol": "BTCUSDT",
"timeframe": "1m",
"mode": "auto",
"enabled": true,
"max_bars": 5000
}'
# 3) Optional: explicit cron job list (else all enabled scripts are scheduled)
curl -sS -X PUT "$WORKER/cron/jobs" \
-H "Content-Type: application/json" -H "X-API-Key: $API_KEY" \
-d '{"jobs":[{"script_id":"btc-sma","symbol":"BTCUSDT","timeframe":"1m","mode":"auto","enabled":true}]}'
# 4) Manual trigger (same path as Cron Trigger * * * * *)
curl -sS -X POST "$WORKER/cron/run" \
-H "Content-Type: application/json" -H "X-API-Key: $API_KEY" \
-d '{"force":true}'Cron is configured in wrangler.jsonc as * * * * * (every minute UTC).
Each cron tick:
- Feed — pull latest closed klines from Bybit (Binance fallback) for each job pair → R2
- Eval — run deployed scripts only if last closed bar time advanced
No laptop feeder required for live 1m. Bulk history still optional via fetch_and_ingest.py.
On each cron tick (and on POST /run / POST /cron/run when alerts are present),
firings are POSTed as JSON to:
- Per-job / per-script
webhook_url(highest priority) - Else Worker env
ALERT_WEBHOOK_URL
export WORKER=https://pyne-worker.cryptolinx.workers.dev
# Default destination for all jobs
echo "https://hooks.example.com/pine" | wrangler secret put ALERT_WEBHOOK_URL
# Or per script when deploying
curl -sS -X POST "$WORKER/scripts" \
-H "Content-Type: application/json" -H "X-API-Key: $API_KEY" \
-d '{
"id":"btc-alerts",
"script":"//@version=6\nindicator(\"a\")\nalertcondition(ta.crossover(close, ta.sma(close,20)), \"X\", \"cross\")\nplot(close)",
"symbol":"BTCUSDT","timeframe":"1m",
"webhook_url":"https://hooks.example.com/btc",
"forward_alerts":true
}'Batch body shape:
{
"type": "pine_alert_batch",
"source": "pyne-worker",
"count": 1,
"content": "cross",
"alerts": [
{
"type": "pine_alert",
"message": "cross",
"title": "X",
"freq": "once_per_bar",
"alert_source": "alertcondition",
"symbol": "BTCUSDT",
"timeframe": "1m",
"bar_index": 42,
"time": 1700000000000
}
]
}Opt out: "forward_alerts": false on the job/script or request body.
# Manual feed only
curl -sS -X POST "$WORKER/feed/refresh" \
-H "Content-Type: application/json" -H "X-API-Key: $API_KEY" \
-d '{"symbol":"BTCUSDT","timeframe":"1m","limit":200}'Docs: PYNE alerts + L2 webhooks
{
"symbol": "BTCUSDT",
"timeframe": "1m",
"bars": [
{"open": 100, "high": 105, "low": 95, "close": 102, "time": 1000, "volume": 1000}
]
}Set your API key as a Worker secret:
echo "your-secret-here" | wrangler secret put API_KEYAll authenticated routes (/run, /ingest, /scripts, /cron/*, /feed/*) must include:
X-API-Key: your-secret-here
When API_KEY is unset (dev mode), auth is disabled. Production must set API_KEY — without it every management route is open.
Auth comparison is constant-time (hmac.compare_digest). Rate limit: 100 req / 60s per key (in-memory per isolate).
Strategy events are POSTed to trade-worker /webhook via the TRADE_SERVICE binding. That route requires mesh auth (requireTradeExecuteAuth), so pyne-worker must set a matching internal key:
echo "same-as-trade-worker" | wrangler secret put INTERNAL_KEY_BINDINGFallback secret names (same value as trade-worker execute key): TRADE_EXECUTE_KEY_BINDING, TRADE_INTERNAL_KEY.
Without an internal key, actionable events are not POSTed — they fail with a clear error instead of a silent 401. Optional var DEFAULT_EXCHANGE (default binance) and per-script / request-body exchange select the exchange field on payloads.
webhook_url (per-script / per-job / request body) and ALERT_WEBHOOK_URL are validated before delivery:
- HTTPS only (public hosts)
- Blocks
localhost,*.local/*.internal, private / loopback / link-local / metadata IPs - Blocks embedded credentials (
https://user:pass@…)
R2 keys for OHLCV use sanitized symbol + timeframe only (data/{SYMBOL}/{TF}/{YYYY}.jsonl) to prevent path traversal.
Every minute the Worker itself refreshes closed candles via Bybit public API into R2, then evaluates scripts. Binance is a fallback (often blocked on CF IPs).
Preload long history from Binance (runs locally / GH Actions):
export WORKER=https://pyne-worker.cryptolinx.workers.dev
# Fetch BTCUSDT 1m (or 1d / 1h) for current year
python scripts/fetch_and_ingest.py --symbol BTCUSDT --timeframe 1m
python scripts/fetch_and_ingest.py --symbol BTCUSDT --timeframe 1d
# Upload via the /ingest endpoint
python3 -c "
import gzip, json, urllib.request, os
with gzip.open('data/BTCUSDT/1d/2026.jsonl.gz', 'rt') as f:
bars = [json.loads(line) for line in f if line.strip()]
body = json.dumps({'symbol': 'BTCUSDT', 'timeframe': '1d', 'bars': bars}).encode()
req = urllib.request.Request(
os.environ['WORKER'] + '/ingest',
data=body,
headers={'Content-Type': 'application/json', 'X-API-Key': os.environ['API_KEY']},
method='POST',
)
with urllib.request.urlopen(req) as resp:
print(resp.read().decode())
"A daily GitHub Actions workflow (.github/workflows/data-ingest.yml)
can fetch top symbols on a schedule.
Note: Binance blocks many Cloudflare Workers IPs (HTTP 403), so auto-fetch
from the Worker itself is limited. Live cron uses Bybit first; bulk preload
locally or pass inline "ohlcv" / "data".
- hoox-sh/pyne (
pynescript) — Parser, AST, evaluator + compile path pynescript_backend— Thin edge wrap over packagepynescript.runtime(strict bars; shared SoT bar-loop)entry.py— HTTP +scheduled()cron entrypoint, trade + alert forwardinghandler.py— Routing, middleware,/run/ scripts / cronscripts_registry.py— Deployed scripts + cron job config in R2scheduler.py— Bar-close job runnerdata_provider.py— R2 reader/writer (data/{SYM}/{TF}/{Y}.jsonl)trade_forwarder.py— StrategyEvent → trade-worker WebhookPayloadalert_engine.py/alert_forwarder.py— last-bar filter + HTTP webhooks
Vendored deploy tree: python_modules/ (sync with ./scripts/sync_vendor.sh).
| Link | What |
|---|---|
| hoox.sh | HOOX product home |
| hoox.sh/pyne | PYNE product + playground entry |
| hoox.sh/pyne/docs | PYNE manuals (runtime, API, alerts) |
| hoox-sh/pyne | Pine engine (this worker’s dependency) |
| hoox.sh/pyne/docs/pyne-worker | This host’s product docs |
| hoox-sh/pynets | TypeScript library (@hoox-sh/pynets) |
| hoox-sh/pyne-agent-worker | NL authoring (optional /run validate) |
| hoox-sh/trade-worker | Exchange execution |
| hoox-sh/hoox | Edge trading framework |
| hoox-sh/axis | Charting PWA |
| PyPI: hoox-pyne | Installable engine |