Skip to content

Repository files navigation

pyne-worker

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

Deploy to Cloudflare

This button deploys this edge host only. Need parse, lint, compile, LSP, Flask Pro API, or the language SoT? Use hoox-sh/pynepip 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.

Limitations (read before you click Deploy)

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_DATA is provisioned from wrangler.jsonc.
  • TRADE_SERVICE binds a Worker named trade-worker. If that Worker is not in your account, remove the services block from wrangler.jsonc before retrying — trade forward is optional and fails closed.

Ecosystem

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 }

Overview

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.

Features

  • Full Pine v5/v6 surface — engine from hoox-pyne / hoox-sh/pyne
  • AuthX-API-Key / Worker secret API_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
  • HealthGET /health (R2 + service binding checks)
  • 30s wall timeout per /run
  • R2 OHLCVPOST /ingest, data/{SYMBOL}/{TIMEFRAME}/{YYYY}.jsonl
  • Compile modesinterpret · compile · auto on POST /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 webhooksALERT_WEBHOOK_URL or per-job webhook_url
  • LibrariesPOST /run libraries: [{namespace, name, version, source}] for import 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

Quick start

# 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_KEY

Deploy gotcha: Wrangler packages python_modules/pynescript, not the editable install. After pulling new PYNE APIs, always re-run ./scripts/sync_vendor.sh.

Endpoints

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)

POST /run

{
  "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.

Deploy a script + 1m bar-close cron

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:

  1. Feed — pull latest closed klines from Bybit (Binance fallback) for each job pair → R2
  2. 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.

Alert webhooks (L2)

On each cron tick (and on POST /run / POST /cron/run when alerts are present), firings are POSTed as JSON to:

  1. Per-job / per-script webhook_url (highest priority)
  2. 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

POST /ingest

{
  "symbol": "BTCUSDT",
  "timeframe": "1m",
  "bars": [
    {"open": 100, "high": 105, "low": 95, "close": 102, "time": 1000, "volume": 1000}
  ]
}

Auth

Set your API key as a Worker secret:

echo "your-secret-here" | wrangler secret put API_KEY

All 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).

Trade forwarding (mesh auth)

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_BINDING

Fallback 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 / SSRF notes

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.

Data pipeline

Live (default for cron)

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).

Bulk history (optional, off-edge)

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".

Architecture

  1. hoox-sh/pyne (pynescript) — Parser, AST, evaluator + compile path
  2. pynescript_backend — Thin edge wrap over package pynescript.runtime (strict bars; shared SoT bar-loop)
  3. entry.py — HTTP + scheduled() cron entrypoint, trade + alert forwarding
  4. handler.py — Routing, middleware, /run / scripts / cron
  5. scripts_registry.py — Deployed scripts + cron job config in R2
  6. scheduler.py — Bar-close job runner
  7. data_provider.py — R2 reader/writer (data/{SYM}/{TF}/{Y}.jsonl)
  8. trade_forwarder.py — StrategyEvent → trade-worker WebhookPayload
  9. alert_engine.py / alert_forwarder.py — last-bar filter + HTTP webhooks

Vendored deploy tree: python_modules/ (sync with ./scripts/sync_vendor.sh).

Related

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

License

AGPL-3.0-or-later. See LICENSE and NOTICE.

About

⚡ pyne-worker — Python Cloudflare® Worker for PYNE edge evaluate (/run, cron, R2, alerts). Website: hoox.sh/pyne · stack: hoox + pyne + axis

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages