An open-source engine that runs Pine Script locally. It compiles .pine source to
JavaScript and executes it against your own OHLCV data — no cloud account, no
execution quotas, no 500 ms loop timeout.
Requires Node.js 20+.
Pine Script v1 and v2 are implemented and passing 393 tests. That covers the
full v2 language, roughly 130 standard-library functions, the strategy broker
emulator, multi-timeframe security(), and the real-time tick model.
The //@version annotation selects the language version; a script without one
is v1. A script declaring a version that is not implemented is refused rather
than run under the wrong rules.
| Version | State |
|---|---|
| v1 | Implemented — TradingView states v1 and v2 are the same language, and the test suite asserts that over the whole corpus |
| v2 | Implemented |
| v3 | Planned — a five-item delta from v2 |
| v4 | Planned — var, while, switch, arrays, drawings, namespace migration |
| v5 | Planned — ta.*/math.* namespaces, matrices, maps, user-defined types, libraries |
The plan for v1–v5 lives in dev-docs/: an architecture assessment, a version delta spec sourced from TradingView's migration guides, and a 16-iteration roadmap.
git clone https://github.com/be-thomas/OpenPineScript.git
cd OpenPineScript
npm installnpm install runs generate:parser, which builds the ANTLR parser from
grammar/.
npm run opsv2 -- <script.pine> --data <data.csv> [flags]npm run opsv2 -- examples/sma_crossover.pine --data mock_data/AAPL_mock.csvCompiling: sma_crossover.pine...
Pine Script: v1
Running backtest: 506 bars...
✔ Done.
=== sma_crossover.pine — Summary ===
Bars processed : 506
Plots recorded : 1
npm run replv2npm run opsv2 -- examples/sma_crossover.pine --data mock_data/AAPL_mock.csv --show-transpiledlet opsv2_len = ctx.new_var("opsv2_len", 14);
let opsv2_src = ctx.new_var("opsv2_src", opsv2_close);
let opsv2_mySma = ctx.new_var("opsv2_mySma", ctx.call("sma@L4:C8", opsv2_sma, opsv2_src, opsv2_len));
ctx.call("plot@L6:C0", opsv2_plot, opsv2_mySma, { opsv2_color: opsv2_color.opsv2_red });Every identifier is prefixed to avoid collisions with the sandbox, and every stateful call carries its source location so per-call-site state (indicator lookback buffers, for example) stays independent.
npm run opsv2 -- strategy.pine --data data.csv --out-dir ./resultsresults/
├── chart.csv OHLCV plus one column per plot()
├── trades.csv entry and exit rows per trade
└── summary.json performance metrics
Point --compare-dir at a folder holding chart_data.csv, trades.csv, and
summary.json exported from TradingView:
npm run opsv2 -- strategy.pine --data data.csv \
--out-dir ./results \
--compare-dir ./tv_exports=== Comparison Report ===
Overall: PARTIAL Tolerance: 0.0001
Chart Data: PASS (506 rows compared, 0 mismatches)
Trades: FAIL (38 tv / 37 opsv2 — 1 discrepancy)
Trade #42: exit_price_mismatch tv=45000.5000 opsv2=45001.0000 Δ=0.5
Summary: PASS (net profit Δ 0.05%)
Report written: ./results/comparison_report.json
Every flag — --out-chart, --out-trades, --compare-chart, --tolerance,
--input, --dry-run — is documented in the CLI Usage Guide.
npm test393 tests across 23 files. The suite covers lexer token streams, parser trees, transpiler output, and runtime behaviour.
Two patterns carry most of the weight:
- Differential testing.
tests/v1/ta/naive_ta.tsis an independent naive reimplementation of the TA library. The engine is asserted to match it bar-for-bar over 5000 seeded bars under four different lookback regimes, rather than against hand-picked expected values. - The version matrix.
conformance/asserts every language rule at every version, including the versions where it is illegal — so adding a version cannot silently relax an older one.
| Path | Contents |
|---|---|
grammar/ |
ANTLR lexer and parser grammars (.g4) |
lexer/ |
Token source that turns indentation into block tokens |
parser/ |
Generated ANTLR parser and the parse entry point |
transpiler/ |
Parse tree to JavaScript |
runtime/ |
Execution context, series storage, standard library, broker emulator |
repl/ |
Interactive REPL |
mock_run/ |
CLI runner |
utils/ |
Shared helpers and the TradingView comparison engine |
tests/ |
Test suites |
validation/ |
Real-world Pine scripts used for parity checking |
spec/ |
Language specification and per-version progress checklists |
dev-docs/ |
Development plan for v1–v5 |
mock_data/ |
Sample OHLCV data |
TradingView enforces limits that protect a shared cloud tier. Running locally, those limits are not parity — they are rationing. The engine skips them:
- No 500 ms loop timeout and no cumulative execution cap. A genuine infinite loop will hang the process.
- No plot limit. TradingView allows 64.
- No
max_bars_backwindow. Lookback depth is bounded by available memory. - No script size limits.
Semantic rules — the ones that would silently change your numbers — are enforced, not skipped. Every skipped item is recorded with its blast radius in dev-docs/04-skipped-restrictions.md.
The core is developed by a single author and pull requests are not being accepted, but bug reports and feature requests drive the roadmap. See CONTRIBUTING.md, the Code of Conduct, and the Security Policy.
GNU GPL-3.0. See LICENSE.
