A robust transpiler that converts TradingView Pine Script (v5/v6) into JavaScript code compatible with the TradingView Charting Library's Custom Indicators (PineJS) API.
This tool allows you to run Pine Script indicators directly within the Charting Library by transpiling them into standard JavaScript objects that implement the PineJS interface.
- Features
- Installation
- Quick Start
- API Reference
- TradingView Harness
- Transpiled Output Example
- Supported Features
- Architecture
- Environment Support
- Limitations & Known Issues
- Future Parity Roadmap
- Development
- Changelog
- Contributing
- License
- Pine Script v5/v6 Syntax Support: Handles variable declarations (
var,varip), types, control flow (if,for,while,switch), and functions. - Standard Library Mapping: Automatically maps Pine Script's
ta.*,math.*,time.*, andstr.*functions to theirPineJS.Stdequivalents. - StdPlus + Runtime Helpers: Includes built-in polyfills/helpers for missing or behavior-sensitive APIs (for example
bb,kc,crossover,hma, map/matrix helpers, and session/time helpers). - Zero Dependencies: The core transpiler logic is dependency-free and runs in any JavaScript environment.
- TypeScript First: Full TypeScript support with strict mode enabled and comprehensive type definitions.
- Corpus Governance Tooling: Lane/authenticity-aware corpus reports (
bun run corpus) and CI gate budgets (bun run corpus:gate) to keep parity stable as fixture count grows. - Chart Host Safety Gate: TradingView-like runtime contract checks (
bun run chart:safety) to catch construct/plot/visual payload regressions before webapp integration. - TradingView-Shaped Test Harness Export: Reusable
./test-harnesssub-export that validates constructor contract, plot/style alignment, and reducer safety against transpiled output.
Install directly from GitHub (npm publish coming soon):
# bun
bun add github:Opus-Aether-AI/pine-transpiler#v0.2.0
# npm
npm install github:Opus-Aether-AI/pine-transpiler#v0.2.0
# pnpm
pnpm add github:Opus-Aether-AI/pine-transpiler#v0.2.0
# yarn
yarn add github:Opus-Aether-AI/pine-transpiler#v0.2.0The package ships a pre-built dist/ so no post-install build step is required. Pin to the tag (#v0.2.0) to avoid breakage from main.
import { transpileToPineJS } from '@opusaether/pine-transpiler';
const pineScript = `
//@version=5
indicator("My Custom SMA", overlay=true)
len = input.int(14, "Length")
out = ta.sma(close, len)
plot(out, color=color.blue)
`;
const result = transpileToPineJS(pineScript, 'my-sma-indicator', 'My SMA');
if (result.success) {
// Use with TradingView Charting Library
const indicator = result.indicatorFactory(PineJS);
// Register indicator with chart...
} else {
console.error("Transpilation Failed:", result.error);
}Transpile Pine Script to a TradingView CustomIndicator factory.
This path compiles indicator runtime with new Function(...) when the
indicator is instantiated. Use it for local/dev or environments where
unsafe-eval is allowed.
function transpileToPineJS(
code: string, // Pine Script source code
indicatorId: string, // Unique identifier for the indicator
indicatorName?: string // Optional display name
): TranspileToPineJSResult;
// Returns:
interface TranspileToPineJSResult {
success: boolean;
indicatorFactory?: IndicatorFactory; // When success=true
error?: string; // When success=false
}Transpile Pine Script to a standalone ESM module string with
createIndicator(PineJS) export.
Use this for strict CSP production environments where unsafe-eval
is blocked.
function transpileToStandaloneFactory(
code: string,
indicatorId: string,
indicatorName?: string
): TranspileToStandaloneFactoryResult;
interface TranspileToStandaloneFactoryResult {
success: boolean;
factoryCode?: string; // Standalone ESM source when success=true
error?: string;
}Low-level function that returns raw JavaScript string (for advanced use cases).
function transpile(code: string): string;Validate if Pine Script code can be transpiled without executing.
function canTranspilePineScript(code: string): {
valid: boolean;
reason?: string;
};Execute native PineJS JavaScript code and return an indicator factory.
function executePineJS(
code: string,
indicatorId: string,
indicatorName?: string
): TranspileToPineJSResult;transpileToPineJS and transpileToStandaloneFactory are thin wrappers around a five-stage pipeline. The stages are exported so tools (LSPs, linters, custom backends) can compose them without re-wiring the sequence.
import {
parse, // (code) => Program (AST)
extractMetadata, // (ast) => MetadataVisitor (name, inputs, plots, …)
generateBody, // (ast, historicalAccess, helperUsage?) => string
buildFactory, // (metadata, body, opts) => IndicatorFactory
compile, // (code, opts) => {ast, metadata, mainBody, helperUsage, factory}
validateInputSize, // (code) => void throws if >1MB
HelperUsage, // tracker of runtime-helper categories emitted
MAX_INPUT_SIZE, // 1_000_000
} from '@opusaether/pine-transpiler';
// Stop at the AST — e.g. for an LSP that wants to inspect the parse:
const ast = parse(source);
// Or run the full pipeline and inspect intermediate stages:
const { metadata, mainBody, helperUsage, factory } =
compile(source, { indicatorId: 'demo' });HelperUsage is recorded by the generator as it emits each Pine builtin call. The factory builder reads it (via IndicatorFactoryOptions.helperUsage) to decide which helper libraries to inject into the preamble — replacing an earlier string-grep over the generated body. Categories tracked: math, session, stdplus, array, map, matrix, color, string, utility, state.
import {
// Top-level
transpileToPineJS,
transpileToStandaloneFactory,
transpile,
canTranspilePineScript,
executePineJS,
// Pipeline stages
parse,
extractMetadata,
generateBody,
buildFactory,
compile,
validateInputSize,
HelperUsage,
MAX_INPUT_SIZE,
// Standalone factory codegen
generateStandaloneFactory,
// Mappings
TA_FUNCTION_MAPPINGS,
MULTI_OUTPUT_MAPPINGS,
MATH_FUNCTION_MAPPINGS,
TIME_FUNCTION_MAPPINGS,
// Reflection
getMappingStats,
getAllPineFunctionNames,
// Types / constants
COLOR_MAP,
PRICE_SOURCES,
} from '@opusaether/pine-transpiler';import {
runTradingViewHarness,
type TradingViewHarnessOptions,
type TradingViewHarnessReport,
} from '@opusaether/pine-transpiler/test-harness';import { transpileToStandaloneFactory } from '@opusaether/pine-transpiler';
const built = transpileToStandaloneFactory(pineSource, 'ict_killzones', 'ICT Killzones');
if (!built.success) throw new Error(built.error);
// Persist `built.factoryCode` as a module at build time (example file:
// generated/ict-killzones.factory.js), then import it in the webapp:
//
// import { createIndicator } from './generated/ict-killzones.factory.js';
// const indicator = createIndicator(PineJS);Use this harness when you want to catch chart-host breakages before app
integration (for example non-constructable constructors, missing
metainfo.styles[plot.id], undefined plot slots, or reducer crashes).
import { runTradingViewHarness } from '@opusaether/pine-transpiler/test-harness';
const report = runTradingViewHarness({
fixtureName: 'ict-killzones.pine',
source: pineSource,
bars: 300,
barIndexStart: 10_000,
});
if (!report.pass) {
console.error(report);
}Here's what the transpiler produces from a simple Pine Script:
Input (Pine Script):
//@version=5
indicator("Simple SMA", overlay=true)
len = input.int(14, "Length")
src = close
out = ta.sma(src, len)
plot(out, color=color.blue)
Output (JavaScript):
// Generated preamble (historical access, helpers)
const _series_close = context.new_var(close);
const _getHistorical_close = (offset) => _series_close.get(offset);
// User code transpiled
let len = input(0);
let src = close;
let out = Std.sma(src, len, context);
plot(out);The transpiler:
- Converts
input.int()to indexedinput()calls - Maps
ta.sma()toStd.sma()with context injection - Resolves
color.blueto the hex color value - Generates historical access helpers for price sources
- Variables:
x = 1,var x = 1,varip x = 1, tuple assignments[a, b] = f(). - Types:
int,float,bool,string,color,array<T>. - Control Flow:
if,for,for...in,while,switchstatements and expressions. - User-Defined Functions:
f(x) => x * 2, multi-line with block syntax. - Inputs:
input(),input.int(),input.float(),input.bool(),input.string(),input.color(),input.source(). - Exports/Imports:
export var,export function,import "lib" as Lib. - Type Definitions:
type Pointwith fields and methods.
| Category | Functions |
|---|---|
| Moving Averages | sma, ema, wma, rma, vwma, swma, alma, hma, linreg, smma |
| Oscillators | rsi, stoch, tsi, cci, mfi, roc, mom, change, percentrank |
| Volatility | atr, tr, stdev, variance, dev |
| Bands | bb, bbw, kc, kcw, donchian |
| Trend | adx, supertrend, sar, pivothigh, pivotlow |
| Cross Detection | cross, crossover, crossunder, rising, falling |
| Volume | obv, cum, accdist, vwap |
| Range | highest, lowest, highestbars, lowestbars, median, mode |
| Multi-output | macd → [macdLine, signalLine, histogram], dmi → [plusDI, minusDI, dx, adx, adxr] |
abs, acos, asin, atan, ceil, cos, exp, floor, log, log10, max, min, pow, random, round, sign, sin, sqrt, tan, sum, avg, todegrees, toradians
time, year, month, dayofweek, dayofmonth, hour, minute, second, timeframe.*
Basic array.* support mapped to JavaScript arrays with type preservation.
map.new, map.put, map.put_all, map.get, map.contains, map.remove, map.size, map.keys, map.values, map.clear, map.copy
matrix.new, matrix.rows, matrix.columns, matrix.get, matrix.set, matrix.add_row, matrix.remove_row
The transpiler follows a classic compiler pipeline with four distinct phases:
┌─────────────────────────────────────────────────────────────────────────────┐
│ Pine Script Source │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 1. LEXER (src/parser/lexer.ts) │
│ • Tokenizes Pine Script input │
│ • Handles significant whitespace (INDENT/DEDENT) │
│ • Tab normalization (4 spaces) │
│ • 16 token types: IDENTIFIER, NUMBER, STRING, OPERATOR, etc. │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 2. PARSER (src/parser/parser.ts) │
│ • Recursive descent parser with precedence climbing │
│ • Builds Abstract Syntax Tree (30 node types) │
│ • Supports named arguments, generics, destructuring │
│ • Error recovery via synchronize() │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 3. AST (src/parser/ast.ts) │
│ │
│ Program │
│ ├── VariableDeclaration { name, value, modifier, typeAnnotation } │
│ ├── FunctionDeclaration { name, params, body } │
│ ├── IfStatement { condition, consequent, alternate } │
│ ├── ForStatement { iterator, start, end, step, body } │
│ ├── CallExpression { callee, arguments } │
│ └── ... 24 more node types │
│ │
│ Node Types: │
│ • Statements: VariableDeclaration, FunctionDeclaration, IfStatement, │
│ ForStatement, WhileStatement, ReturnStatement, SwitchStatement, etc. │
│ • Expressions: BinaryExpression, UnaryExpression, CallExpression, │
│ MemberExpression, ConditionalExpression, Identifier, Literal, etc. │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 4. GENERATOR (src/generator/) │
│ • MetadataVisitor: Extracts inputs, plots, sources, historical access │
│ • ASTGenerator: Converts AST to JavaScript string │
│ • Function Mappings: Resolves ta.*/math.*/time.* to PineJS.Std │
│ • StdPlus Injection: Polyfills for missing Std functions │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ JavaScript / PineJS Output │
└─────────────────────────────────────────────────────────────────────────────┘
src/
├── index.ts # Main entry, transpileToPineJS, runtime mocks
├── parser/
│ ├── lexer.ts # Tokenizer with indentation handling
│ ├── parser.ts # Recursive descent parser
│ └── ast.ts # AST node type definitions
├── generator/
│ ├── ast-generator.ts # AST to JavaScript code generation
│ └── metadata-visitor.ts # AST visitor for metadata extraction
├── mappings/
│ ├── technical-analysis.ts # ta.* function mappings (50+ functions)
│ ├── math.ts # math.* function mappings
│ ├── time.ts # time.* and timeframe.* mappings
│ ├── comparison.ts # Comparison operators
│ ├── utilities.ts # Utility functions
│ └── price-sources.ts # Price source mappings
├── stdlib/
│ └── index.ts # StdPlus polyfill library
└── types/
├── index.ts # Type exports
└── runtime.ts # Runtime type definitions
| Environment | Support |
|---|---|
| Node.js 18+ | ✅ Full support (ESM & CJS) |
| Browsers | ✅ Full support (ESM) |
| Deno | ✅ Via npm specifier |
| Bun | ✅ Full support |
The package ships with dual format support:
- ESM:
dist/index.js(default forimport) - CJS:
dist/index.cjs(forrequire()) - Types:
dist/index.d.ts
Full known-gaps list with workarounds lives in docs/LIMITATIONS.md. Real-world corpus pass rate tracked in docs/CORPUS-BASELINE.md. Headline items:
| Feature | Status | Notes |
|---|---|---|
strategy.* |
❌ Not Supported | Indicators only; no backtesting |
request.security |
Deterministic in-process subset (expression passthrough + HTF bucket merge for supported signatures); no external data fetch | |
request.financial, request.economic, request.earnings, request.dividends, request.splits, request.quandl, request.seed |
❌ Not Supported | External data sources |
line.*, label.*, box.*, table.* |
Stateful runtime-compatible handles with method subsets; no chart rendering output | |
polyline.* |
❌ Not Supported | Not implemented |
-
request.securityscope: subset support exists, but full Pine MTF parity is not complete (especially around cross-symbol fetching and completebarmergesemantics). -
Drawing/Table semantics: object lifecycle and common mutators/getters are supported for runtime compatibility, but rendering is intentionally host-owned.
-
Matrix coverage is subset-only: advanced matrix APIs outside
new/rows/columns/get/set/add_row/remove_roware not implemented yet. -
No Source Maps: generated JavaScript cannot be mapped back to Pine Script lines for debugging.
Coverage is enforced through multiple layers:
bun run test:coverageruns the unit/regression suite underscripts/check-coverage.tsand fails CI if aggregate line or function coverage drops below 95%. Current numbers: 95.81% functions / 98.62% lines across 1393 tests.- unit/regression suites:
bun test tests/ - TradingView-shaped harness suites:
bun run test:harness - corpus execution parity:
bun run corpus - strict numeric checks:
bun run corpus:strict - curated + community indicator matrices:
bun run corpus:matrix,bun run corpus:critical,bun run corpus:tv100,bun run corpus:tv200,bun run corpus:forex-xau - differential numeric parity report:
bun run corpus:differential - corpus quality/stability budgets:
bun run corpus:gate - chart-host safety contracts:
bun run chart:safety
The next accuracy/support roadmap is documented in docs/FUTURE_PARITY_ROADMAP.md, including:
- phase-by-phase coverage tracking
- parity KPIs and acceptance gates
- execution order for semantic, visual, and MTF parity work
# Install dependencies
bun install
# Run tests
bun test tests/
# Run TradingView-shaped harness integration tests
bun run test:harness
# Run tests in watch mode
bun test --watch tests/
# Build
bun run build
# Type check
bun run typecheck
# Lint
bun run lint
# Lint with auto-fix
bun run lint:fix# Full corpus scorecard (pass/total + top failures)
bun run corpus
# Strict numeric parity checks on core indicators
bun run corpus:strict
# 67-indicator parity matrix (PASS/FAIL/NOT_FOUND)
bun run corpus:matrix
# Critical real-world market script matrix
# (high-impact ICT/SMC/killzones/forex/XAU fixtures)
bun run corpus:critical
# TradingView top-100 community target matrix
# (PASS/FAIL for imported fixtures, NOT_IN_CORPUS for missing imports)
bun run corpus:tv100
# TradingView top-200 matrix
# (top-100 targets + 100 additional popular/customized fixtures)
bun run corpus:tv200
# Stability gate (lane/authenticity budgets for CI)
bun run corpus:gate
# Chart-host safety gate (constructor/plot/visual runtime contracts)
bun run chart:safety
# Visual parity baseline (5 fixtures, snapshot-based)
bun run corpus:visual
# Refresh corpus + visual snapshots after intentional changes
bun run corpus:snapThe corpus is now classified using tests/corpus/manifest.ts:
- lanes:
curated_core,upstream_authentic,synthetic_custom,quarantine - authenticity classes:
authentic,proxy,synthetic - categories and feature tags inferred from fixture source
Governance artifacts:
bun run test:harnessruns fixture-level descriptor + reducer-survival checks in a TradingView-shaped runtime harness.bun run corpusprints pass rate by source, lane, authenticity, category, and top feature coverage.bun run corpus:criticaltracks regression-critical real-world scripts (ICT/SMC/killzones/XAU) that must remain runtime-stable.bun run corpus:tv100/bun run corpus:tv200generate matrix artifacts for popular/community suites.bun run corpus:gateenforces stability budgets in CI (overall pass, parse-clean, unimplemented std calls, per-lane pass, per-authenticity pass).bun run chart:safetyenforces TradingView-like host contracts (new constructor(), per-bar plot shape, visual-event payload integrity) and writes failure artifacts to.tmp/chart-safety/.
Reference docs:
- docs/CORPUS-BASELINE.md
- docs/TRADINGVIEW_TOP100_MATRIX.md
- docs/TRADINGVIEW_TOP200_MATRIX.md
- docs/CRITICAL_INDICATOR_MATRIX.md
See CHANGELOG.md for a detailed history of changes, new features, and bug fixes.
See CONTRIBUTING.md for guidelines.
Key areas for contribution:
- Adding missing
ta.*function implementations to StdPlus - Improving test coverage for edge cases
- Adding source map generation
- Documentation improvements
AGPL-3.0 © Opus Aether
This project is licensed under the GNU Affero General Public License v3.0. This means:
- ✅ You can use, modify, and distribute this software
- ✅ If you run a modified version on a network server, you must make the source code available to users
- ✅ Any modifications must also be licensed under AGPL-3.0
- ✅ See LICENSE for full details
- Inspired by TradingView's Pine Script language
- Compatible with TradingView Charting Library
This project is an independent, open-source initiative and is not affiliated with, endorsed by, or connected to TradingView Inc.
- TradingView and Pine Script are trademarks of TradingView Inc.
- This transpiler is a clean-room implementation based on public documentation and behavior observation. It does not use or contain any proprietary code from TradingView.
- Use this tool at your own risk. The authors assume no responsibility for trading decisions or financial losses resulting from the use of this software.