This repository contains a PineScript (v5/v6-ish) to JavaScript transpiler, plus a small runtime shim so converted indicators can execute in Node.js.
- Transpile PineScript to runnable JavaScript (ESM).
- Supports a large subset of Pine v5/v6 syntax used by common public scripts.
- Runtime shims for common namespaces:
ta,math,array,str,color,table,request,timeframe,barmerge
- Series semantics:
- History indexing transpiles to
pinescript.offset(x, n). - Generated runtime wraps OHLCV/time arrays as a
SeriesArrayso arithmetic uses the current bar value.
- History indexing transpiles to
- Stateful execution:
var/varipare persisted across bars via astateobject.
- Multi-timeframe baseline:
request.security()resamples/aligned series usingtime[]when available.
src/:lexer.js- Tokenization for PineScript syntax (keywords, operators, indentation)parser.js- AST generation supporting Pine v5/v6 constructsgenerator.js- JavaScript code emission with runtime shimsbuiltins.js- JavaScript implementations of Pine built-in functionstranspiler.js- Main entry point (lexer → parser → generator)cli.js- Command-line interface for transpilation
test/indicator_tests.js- Bar-by-bar runtime smoke tests
npm installnode src/cli.js path/to/script.pine path/to/output.jsBy default, the CLI runs a lightweight reviewer after conversion.
- Syntax check: best-effort parse check for the generated JS.
- Optional smoke import: tries to
import()the generated module to catch obvious runtime errors.
Disable it if you only want the raw output:
node src/cli.js path/to/script.pine path/to/output.js --no-reviewDisable only the runtime import() smoke test:
node src/cli.js path/to/script.pine path/to/output.js --no-review-importEnvironment variables:
PINE_REVIEW=0disables the reviewerPINE_REVIEW_IMPORT=0disables the import smoke test
This repo includes a fully local optional reviewer (no server) that runs a laptop-friendly code model via Python.
- Default model:
Qwen/Qwen2.5-Coder-0.5B-Instruct - Optional larger model (slower / more RAM):
Qwen/Qwen2.5-Coder-1.5B-Instruct
Note: small models may produce low-quality or repetitive text. The reviewer will sanitize unusable outputs and suggest switching models via PINE_REVIEWER_MODEL.
Setup and run (Python):
pip install -r ai_reviewer/requirements.txtThen enable AI review in the CLI:
set PINE_REVIEW_AI=1
node src/cli.js path/to/script.pine path/to/output.js --review-aiModel selection:
PINE_REVIEWER_MODEL=Qwen/Qwen2.5-Coder-0.5B-InstructPINE_REVIEWER_PYTHON=python(optional)PINE_REVIEWER_SCRIPT=ai_reviewer/review.py(optional)PINE_REVIEWER_USE_CUDA=1(optional)
npm run test:indicators| Feature | Status | Notes |
|---|---|---|
Variable declarations (var, varip) |
✅ Supported | Persisted across bars via state object |
Typed declarations (float x, int y) |
✅ Supported | Generates let for mutability |
Generic types (array<float>, matrix<float>) |
✅ Supported | Basic parsing for <type> annotations |
Type blocks (type MyType) |
✅ Supported | Generates lightweight JS object factories |
| Function declarations | ✅ Supported | Supports typed parameters and default values |
Arrow functions (=>) |
✅ Supported | Implicit return for expression bodies |
| If/else statements | ✅ Supported | Full boolean expression conditions |
For loops (for i = 0 to n) |
✅ Supported | Transpiled to JS for-loops |
For-in loops (for x in array) |
✅ Supported | Transpiled to JS for-of loops |
| While loops | ✅ Supported | Parses full expressions as conditions |
| Switch statements | ✅ Supported | Generates chained ternary expressions |
Ternary operators (a ? b : c) |
✅ Supported | Full expression support |
Destructuring ([a, b] = tuple) |
✅ Supported | Also supports := reassignment |
Multi-declarations (var float a=na, var float b=na) |
✅ Supported | Comma-separated on one line |
| Semicolons in blocks | ✅ Supported | Statement separators inside {...} |
| Import statements | ✅ Supported | Parsed but not emitted (stubbed) |
| Function | Status | Notes |
|---|---|---|
ta.sma, ta.ema, ta.wma, ta.rma, ta.hma, ta.vwma |
✅ Supported | Standard moving averages |
ta.atr, ta.tr |
✅ Supported | ATR with ta.tr(true) signature support |
ta.rsi, ta.stoch |
✅ Supported | RSI and Stochastic implementations |
ta.cci, ta.crossover, ta.crossunder |
✅ Supported | CCI with dual signature support |
ta.pivothigh, ta.pivotlow |
✅ Supported | Returns null (no future reference) |
ta.highest, ta.lowest, ta.barssince |
✅ Supported | Hardened for scalar inputs |
ta.change, ta.roc, ta.percentrank |
✅ Supported | Rate of change and percent rank |
ta.change, ta.valuewhen, ta.iff |
✅ Supported | Core series manipulation |
ta.lowest, ta.highest, ta.median |
✅ Supported | Array-based implementations |
| Function | Status | Notes |
|---|---|---|
math.abs, math.max, math.min, math.round |
✅ Supported | Standard math helpers |
math.sqrt, math.pow, math.log, math.exp |
✅ Supported | Exposes Math.* |
math.avg, math.sum |
✅ Supported | Custom implementations |
math.pi, math.e |
✅ Supported | Constants |
| Function | Status | Notes |
|---|---|---|
array.new_float, array.new_int, array.new_bool, array.new_string |
✅ Supported | Creates typed arrays |
array.new_label, array.new_line, array.new_box, array.new_polyline |
✅ Supported | Pine v6 object arrays |
array.size, array.get, array.set |
✅ Supported | Core array operations |
array.push, array.pop, array.unshift, array.shift |
✅ Supported | Mutating operations |
array.clear, array.remove, array.insert |
✅ Supported | Modification operations |
array.from |
✅ Supported | Variadic array creation |
array.sort, array.reverse, array.min, array.max |
✅ Supported | Utility functions |
| Function | Status | Notes |
|---|---|---|
str.tostring, str.tonumber |
✅ Supported | Type conversion |
str.concat, str.substring, str.len |
✅ Supported | String manipulation |
str.contains, str.startswith, str.endswith |
✅ Supported | Pattern matching |
str.replace, str.replaceall, str.lower, str.upper |
✅ Supported | Transformations |
str.split, str.match |
✅ Supported | Advanced parsing |
| Function | Status | Notes |
|---|---|---|
color.rgb, color.new, color.from_gradient |
✅ Supported | Color construction |
color.hex, color.t, color.r, color.g, color.b |
✅ Supported | Color decomposition |
Predefined colors (color.red, color.green, etc.) |
✅ Supported | Named color constants |
| Function | Status | Notes |
|---|---|---|
table.new, table.cell, table.merge_cells |
✅ Supported | Table construction |
table.cell_set_text, table.cell_set_bgcolor |
✅ Supported | Cell manipulation |
| Function | Status | Notes |
|---|---|---|
line.new, line.delete, line.setxy |
✅ Supported | Line primitives |
label.new, label.delete, label.settext |
✅ Supported | Label primitives |
box.new, box.delete |
✅ Supported | Box primitives |
polyline.new, polyline.delete |
✅ Supported | Polyline primitives |
| Function | Status | Notes |
|---|---|---|
request.security(symbol, tf, series, opts) |
✅ Baseline | Resamples using time[] alignment |
request.financial(...) |
✅ Stub | Returns null |
input.timeframe(defval) |
✅ Supported | Returns input value |
barmerge.gaps_off, barmerge.gaps_on |
✅ Supported | Constants |
barmerge.lookahead_off, barmerge.lookahead_on |
✅ Supported | Constants |
| Function | Status | Notes |
|---|---|---|
na(x), nz(x, def) |
✅ Supported | Null handling |
input.int/float/bool/string/time/source/color |
✅ Supported | Input declarations |
plot, plotshape, plotbar, plotcandle |
✅ Supported | Charting output |
bgcolor, hline, fill |
✅ Supported | Visual output |
alertcondition |
✅ Supported | Alert registration |
barstate.islast, barstate.islastconfirmedhistory |
✅ Supported | Bar state checks |
The transpiler implements PineScript's series semantics:
- History indexing:
x[1],x[n]transpiled topinescript.offset(x, n) - Current bar value: Arithmetic (
high / low,close + open) uses the last array element viaSeriesArray.valueOf() - Implicit casting: OHLCV arrays wrapped in
SeriesArrayat runtime for seamless arithmetic
Variables declared with var or varip are persisted across bar executions:
// Generated code pattern:
if (state.myVar === undefined) state.myVar = initialValue;
// ... reads become state.myVar
// ... writes become state.myVar = newValue;request.security() implements basic resampling:
- Parses timeframe strings (
"60","1H","D","W","M") - Buckets
time[]into periods based on timeframe milliseconds - Aligns series to base timeframe with optional lookahead/gaps
Limitations:
- No session calendar handling
- No partial bar alignment
- No
barmergerepainting rules beyond basic options
The following PineScript features are not yet implemented or are partially supported. Contributions are welcome.
- Strategy functions:
strategy.entry,strategy.close,strategy.long,strategy.short,strategy.orderare not implemented. - Matrix operations:
matrix.new,matrix.mult,matrix.inv,matrix.transposeare stubbed with basic placeholder implementations. - Chart point objects:
chart.point.from_index,chart.point.neware stubbed. - Map data type: Pine v6
maptype declarations and operations (map.new,map.get,map.set) are not implemented. - User-defined types with methods:
type Foowithmethodblocks have limited support. - Library imports:
@versionsemantics beyond v5/v6 detection are not enforced.
- Session calendars:
timeframe.sessionand session-based resampling are not handled. - Partial bar alignment:
request.security()does not align to incomplete current bars. - Full barmerge repainting:
barmerge.lookahead_onandbarmerge.gaps_onbehavior is not fully TV-accurate. - Security on non-standard timeframes: Very small intervals (< 1m) or irregular intervals may not resample correctly.
- Polyline animations:
polyline.set_*methods are not implemented. - Line fill:
linefill.newand related functions are not implemented. - Tooltip formatting:
format.*options forplotare not fully mapped.
- Backtest engine: No built-in backtest output or equity curve generation.
- Plot export: No CSV/JSON export of plotted values.
Copy the following PineScript code into a file (e.g., test_script.pine) and run:
//@version=5
indicator("Test Script", overlay=true)
// Inputs
fastLen = input.int(10, "Fast Length")
slowLen = input.int(20, "Slow Length")
src = input.source(close, "Source")
// Calculations
fastMA = ta.sma(src, fastLen)
slowMA = ta.sma(src, slowLen)
// State persistence example
var float lastCross = na
if ta.crossover(fastMA, slowMA)
lastCross := bar_index
else if ta.crossunder(fastMA, slowMA)
lastCross := bar_index
// Multi-timeframe example
higherTF = input.timeframe("60", "Higher TF")
[st, dir] = request.security(syminfo.tickerid, higherTF, ta.supertrend(3, 10))
// Plots
plot(fastMA, "Fast MA", color=color.blue)
plot(slowMA, "Slow MA", color=color.orange)
plotshape(ta.crossover(fastMA, slowMA), "Crossover", style=shape.triangleup, location=location.belowbar, color=color.lime)
plotshape(ta.crossunder(fastMA, slowMA), "Crossunder", style=shape.triangledown, location=location.abovebar, color=color.red)
// Table output
var table testTable = table.new(position.top_right, 2, 2, bgcolor=color.new(color.black, 80))
if barstate.islastconfirmedhistory
table.cell(testTable, 0, 0, "Fast MA", text_color=color.white)
table.cell(testTable, 0, 1, "Slow MA", text_color=color.white)
table.cell(testTable, 1, 0, str.tostring(fastMA, "#.##"), text_color=color.aqua)
table.cell(testTable, 1, 1, str.tostring(slowMA, "#.##"), text_color=color.fuchsia)
// Alerts
alertcondition(ta.crossover(fastMA, slowMA), "MA Crossover", "Fast crossed above slow")
alertcondition(ta.crossunder(fastMA, slowMA), "MA Crossunder", "Fast crossed below slow")
node src/cli.js test_script.pine test_script.js
node test_script.js # Requires synthetic OHLCV data setupSee CONTRIBUTING.md.
See LICENSE.
Made with love by MeridianAlgo
Documentation and test suite created with AI assistance.