PineTS: a Pine Script runtime for Node.js and the browser
Run Pine Script® anywhere. PineTS is LuxAlgo's open-source Pine Script® runtime for Node.js and the browser.
At a glance
- What is it?
- PineTS is LuxAlgo's TypeScript transpiler and runtime for Pine Script, so an indicator written for TradingView can be executed on your own data and its plot series read as plain JavaScript. It accepts both native Pine source and a JavaScript-flavoured syntax, and the native v6 path is labelled experimental in the documentation.
- Who is it for?
- PineTS fits a quant who already has Pine Script indicators or strategies and wants to run them against their own data, on their own infrastructure, and feed the results into a bot, an alert or a machine learning pipeline.
- Can I use it commercially?
- Yes, with strict conditions. AGPL-3.0 is a network copyleft licence: if people use a modified version over a network, for example as a hosted service, you must offer them its source code under the same licence.
- Is it still maintained?
- Yes. The repository last received commits 2 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The pitch is that the chart is not the only place a script can run
The project describes itself as an open-source Pine Script runtime for Node.js and the browser, with the slogan that you write the indicator once and PineTS runs it on your data, in your process. The framing line is that Pine Script is built for the chart while PineTS is built for everything around it.
A table in the README makes the argument in four rows. Indicators on your own infrastructure run in Node.js, Deno, Bun or the browser. Values you can pass to a bot, an alert or an ML pipeline come back as raw plot series in plain JavaScript. Data comes from Binance, a CSV or your own API through built in providers or a raw OHLCV array. And the script you already wrote stays the script you already wrote, with native Pine Script v6 and no rewrite.
That last row is the whole commercial argument. Porting an indicator library from Pine to a JavaScript charting library is a recurring tax on anyone who publishes indicators, and a runtime that executes the original source removes it.
The disclaimer is prominent and worth reading in full. PineTS is an independently developed open source compiler and runtime engine, and LuxAlgo Global, the PineTS project and TradingView are not affiliated, sponsored, endorsed or in any way officially associated. Pine Script and TradingView are registered trademarks of TradingView.
That is the honest framing of the project. It is a clean-room implementation of a language, published by a commercial vendor of trading tools, with no relationship to the platform whose language it implements.
Two syntaxes, one runtime
The design decision that makes PineTS pleasant to use is that you can write in either of two syntaxes and both compile to the same runtime.
The first is native Pine Script. The same RSI strategy written the TradingView way:
//@version=6
indicator("RSI Strategy")
rsi = ta.rsi(close, 14)
sma = ta.sma(rsi, 10)
plot(rsi, "RSI")
plot(sma, "Signal")The second is what the project calls PineTS syntax, which is JavaScript friendly, with a //@PineTS marker at the top:
//@PineTS
indicator('RSI Strategy');
const rsi = ta.rsi(close, 14);
const sma = ta.sma(rsi, 10);
plot(rsi, 'RSI');
plot(sma, 'Signal');The JavaScript form runs as a callback receiving a context object with $.data for the price series and $.pine for the namespace of built in functions, and it can return values directly, so a function form of an indicator can return an object of series rather than only plotting them.
The reason to support both is worth stating plainly. Native Pine is what you already have in a large indicator library, and it is the form that has to work identically. The JavaScript form is what you write when you are porting a single indicator by hand, and it avoids the transpiler entirely for that case.
The features list says 60+ TA functions, covering SMA, EMA, RSI, MACD and Bollinger Bands, and it lists time series semantics with lookbacks, var and let persistence, and bar state. It also claims high precision, saying it matches the calculation precision of TradingView.
The native v6 path carries a qualifier in the features list, marked as experimental. That is a meaningful word to find there, and it belongs in any evaluation of the project.
The API surface is one constructor and one run call
The runtime is reached through a small API, and the shape of it is what makes the library easy to embed in something else.
The constructor takes a provider, a symbol, an interval and a bar count:
import { PineTS, Provider } from 'pinets';
const pineTS = new PineTS(Provider.Binance, 'BTCUSDT', '1h', 100);
// Calculate a simple moving average
const { plots } = await pineTS.run(`
//@version=6
indicator("My First Indicator")
sma20 = ta.sma(close, 20)
plot(sma20, "SMA 20")
`);
console.log('SMA values:', plots['SMA 20'].data);The result is a plots object, and the documentation says it is a map of series you can log, store, or feed into the rest of your stack. That is the design point. A Pine indicator on a chart produces a picture, and a plot series is data, and data is what an alerting service or a model wants.
The provider argument is an enum, and the features list names the options as Binance, FMP and Alpaca, or your own API, or any OHLCV array passed in directly.
The JavaScript callback form of run takes the same shape:
const { plots } = await pineTS.run(($) => {
const { close, high, low } = $.data;
const { ta, plot, plotchar } = $.pine;
const ema9 = ta.ema(close, 9);
const ema21 = ta.ema(close, 21);
const atr = ta.atr(14);
const bullish = ta.crossover(ema9, ema21);
const bearish = ta.crossunder(ema9, ema21);
plot(ema9, 'Fast EMA');
plot(ema21, 'Slow EMA');
plotchar(bullish, 'Buy Signal');
plotchar(bearish, 'Sell Signal');
return { ema9, ema21, atr, bullish, bearish };
});Note that the return value is what makes this usable in a pipeline. The function form gives back a plain object of series, so an indicator becomes a value you can compose with others, rather than a chart you have to screenshot.
Streaming, multiple timeframes, and the two output shapes
Beyond batch calculation, the runtime offers live streaming and multiple timeframe support, and the package manifest shows the two output formats the library ships.
The main entry point is dist/pinets.min.cjs, the module field points at dist/pinets.min.es.js, there is a browser build at dist/pinets.min.browser.es.js, and the types are at dist/types/index.d.ts. The exports map then routes a node condition to the ESM or CJS build, a browser condition to the browser build, and a default to the ESM build. So the same package serves a Node process, a bundler and a browser without a separate entry point for each.
Streaming is the second interface. The stream method recalculates on new bars and emits plot updates:
const stream = pineTS.stream(
`
//@version=6
indicator("Live RSI")
plot(ta.rsi(close, 14), "RSI")
`,
{ live: true, interval: 1000 },
);The event based callback receives a context and reads the plot from it, and the live flag with an interval in milliseconds controls the polling cadence.
Multiple timeframes come through request.security(), listed in the features under the name for multi timeframe indicators. This is the feature that most often breaks in a port, because it involves the higher timeframe series being aligned onto the lower timeframe bars, and it is a place where a homegrown implementation quietly diverges from the reference.
The data providers are worth noting as a design choice. Rather than bundling a data layer, PineTS takes a provider or a raw OHLCV array, which means the same runtime works against live exchange data, a CSV export, or a database query you ran yourself. For backtesting, where you want deterministic historical data you fetched once, that separation is the right shape.
Coverage badges and an experimental label are the honest parts
The README carries two coverage badges rather than a feature count, and that is unusual in a good way. There is a coverage badge pointing at a file in the .github/badges directory, and an API Coverage badge pointing at a section of the README, with a link to the API coverage anchor in the table of contents.
For a transpiler, API coverage is the only number that matters, because the failure mode is not a crash, it is a function that behaves slightly differently from the reference. An indicator that renders a wrong RSI in a way nobody notices is worse than one that refuses to run, and a coverage badge is a public statement about how much of the Pine namespace is actually implemented.
The experimental label on the native v6 path belongs in the same category of honesty. The features list says Native Pine Script v6, run original TradingView code directly, and marks it experimental. The transpiler is the part of the project that is most likely to differ from the reference implementation, and the documentation says so where a reader will see it.
There is a commercial side too, and the licence badge makes it explicit: AGPL-3.0 / Commercial. The repository carries both a LICENSE file and a LICENSE-COMMERCIAL.md, which is the standard arrangement for a company that wants the community to run the open version while selling a licensed one.
For an adopting team, the coverage badge is the first thing to open, and the API coverage section of the README is the second, because it tells you which Pine functions are exact, which are approximated, and which are missing.
Where PineTS is the wrong tool
Three situations rule it out.
The first is if you need a backtesting engine. The topics attached to the repository include backtesting, trading-strategy-simulation and trading-algorithms, and it is easy to read that as the project doing portfolio simulation, position sizing and equity curves. What the documentation describes is a language runtime. It executes a script over a series and returns the series. If you need a strategy evaluated over a full backtest with realistic fills, you still need to build that, and the PineTS runtime is a component of it rather than a replacement.
The second is if you need exact TradingView parity and cannot accept the risk of drift. The documentation says the native v6 path is experimental, and the API coverage badges say that some of the Pine namespace is not fully implemented. For a live trading system, a silent difference in an indicator's output is a real risk, and the mitigation is to read the coverage table for the specific functions your indicators use rather than to assume the whole namespace is there.
The third is if your indicators are not in Pine. If you are starting fresh and choosing a language for technical analysis code, Pine's design is built around a chart, with series semantics, plot commands and a restricted language, and writing new analysis in it means writing it in a language designed for a specific host.
There is also the licensing question for a commercial deployment. The AGPL-3.0 is a strong copyleft, and the presence of a separate commercial licence file is the vendor telling you that. If you intend to embed this in a product you sell, the choice between the two is a decision to make before you build, not after.
The release cadence and the shape of the package
The maintenance record is the part that speaks to whether this is a project you can depend on.
The recent releases are v0.12.0 on 2026-10-19, v0.11.1 on 2026-10-12 and v0.11.0 on 2026-10-01, and the last push to the repository was on 2026-10-19 with the repository not archived. So the project shipped three releases in three weeks of October 2026, on a 0.x version line.
A 0.x version means the public interface can still change between minor releases, which for a library that other code depends on is a genuine consideration. The package is at version 0.12.0 in the manifest, and the recent tags follow the same numbering, so the manifest and the tags agree here.
The build is a conventional TypeScript package. The test script runs vitest with a verbose reporter under a cross-env TEST_ENV flag, and there is a coverage variant plus a coverage badge variant. The repository layout shows a src directory, a tests directory, a docs directory, a scripts directory, a rollup.config.js for the bundling, separate tsconfig files for the build and for declarations, and a vitest.config.ts.
The published files list is narrow, only dist/*.min.* and dist/types, so the source and the tooling do not ship. That is the right choice for a runtime that people embed, and it means the repository is the place to read what you are actually running.
The docs live outside the repository, at docs.luxalgo.com/developers/pinets, which is where the API reference and the coverage detail are kept.
Editorial conclusion
PineTS fits a quant who already has Pine Script indicators or strategies and wants to run them against their own data, on their own infrastructure, and feed the results into a bot, an alert or a machine learning pipeline. It is the wrong choice if you need a general purpose trading framework, because it is a language runtime rather than a backtesting engine with portfolio modelling, and it is the wrong choice if you need guaranteed parity with TradingView, since the native v6 path is documented as experimental. Before adopting it, read the API coverage badge in the repository, because a partial implementation of the Pine namespace is where a port will silently diverge, and check the AGPL-3.0 terms against the commercial licence file beside it, since the badge advertises both.
Frequently asked questions
What is PineTS and what does it do?
It is LuxAlgo's open source TypeScript transpiler and runtime for Pine Script, executing native v5 or v6 source with the same time series model of lookbacks, incremental technical analysis and plot outputs. The result is a map of plot series you can read as plain JavaScript.
How do I install and run PineTS?
Install it with npm install pinets, then construct a PineTS instance with a provider, symbol, interval and bar count, and call the run method with either native Pine source or a JavaScript callback. The documentation gives the example of an SMA 20 indicator run against Binance BTCUSDT on the 1h interval with 100 bars.
Can I write PineTS indicators in JavaScript?
Yes. The library accepts a JavaScript friendly syntax marked with //@PineTS, where the callback receives $.data for the price series and $.pine for built in functions, and can return series directly. Both syntaxes compile to the same runtime.
Is PineTS affiliated with TradingView?
No. The disclaimer states that PineTS is an independently developed open source compiler and runtime engine, and that LuxAlgo Global, the PineTS project and TradingView are not affiliated, sponsored, endorsed or officially associated, with Pine Script and TradingView being registered trademarks of TradingView.
How current is PineTS and under what licence?
The last push was on 2026-10-19 and the repository is not archived, with releases v0.12.0, v0.11.1 and v0.11.0 in October 2026. The licence badge shows AGPL-3.0 / Commercial, and the repository carries both a LICENSE file and a LICENSE-COMMERCIAL.md alongside coverage and API coverage badges.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/luxalgo-pinets)Was this useful?