Deploying to a Server
Wrap a script in an HTTP endpoint, cache sensibly, and keep keys off the client.
The usual production shape is a small service that compiles scripts at start-up and serves their output over HTTP. Compile once at boot, keep the compiled scripts in a map, and let each request load bars and run. That layout keeps request latency dominated by the data fetch rather than by parsing, which is where you want it.
Cache aggressively but honestly. Indicator output only changes when a new bar closes, so a time-to-live slightly shorter than the timeframe is nearly free and cuts provider traffic by orders of magnitude. Key the cache on symbol, timeframe and the input set. Return a header saying whether the response was a hit so you can see the ratio in your logs instead of guessing.
Keep data credentials server-side. The service holds the vendor key and the browser holds nothing; the front end calls your endpoint, and your endpoint calls the provider. Add your own authentication and per-client rate limiting at that boundary, because an unauthenticated indicator endpoint is an open proxy to a metered upstream and will be found.
Treat provider failure as normal. Wrap fetches in a timeout, return a 502 with a machine-readable reason rather than a stack trace, and consider withFallback so a secondary source covers a primary outage. A stale-while-revalidate policy on the cache keeps the last good answer flowing while the upstream recovers.
For scheduled work, the same service can run scripts on a cron and push results to a queue, a database or a webhook. Keep the process single-purpose and stateless apart from the cache, so scaling is a matter of running more copies and nothing has to be coordinated between them.
Example
Serve indicator output from an Express endpoint
import express from 'express'
import { AlgoBeamTS, Provider, compile, readScript } from 'algobeam-ts'
const app = express()
const script = compile(await readScript('./scripts/momentum-ribbon.algo'))
const cache = new Map<string, { at: number; body: unknown }>()
const TTL_MS = 15_000
app.get('/api/indicator/:symbol', async (req, res) => {
const symbol = req.params.symbol.toUpperCase()
const timeframe = typeof req.query.tf === 'string' ? req.query.tf : '1h'
const key = symbol + ':' + timeframe
const hit = cache.get(key)
if (hit && performance.now() - hit.at < TTL_MS) {
res.setHeader('x-algobeam-cache', 'hit')
return res.json(hit.body)
}
try {
const algobeam = new AlgoBeamTS(Provider.Binance, symbol, timeframe, 300)
const result = await algobeam.run(script)
const body = {
symbol,
timeframe,
bars: result.bars.length,
plots: result.plots,
alerts: result.alerts,
}
cache.set(key, { at: performance.now(), body })
res.json(body)
} catch (error: unknown) {
res.status(502).json({ error: 'provider unavailable', detail: String(error) })
}
})
app.listen(8787, () => console.log('algobeam indicator service listening on :8787'))Copy it, change one input, and run it again — the numbers are deterministic, so a difference in the output is always a difference you made.