We Transpiled PineScript v6 to C++ So Backtests Are Actually Reproducible A developer built PineForge, a transpiler that converts TradingView's PineScript v6 source code into C++ for offline backtesting on any OHLCV CSV data. The system achieved trade-for-trade parity with TradingView on 245 out of 246 reference strategies, validating over 375,000 trades with zero engine bugs. The single failing strategy was traced to a confirmed TradingView-side anomaly in bar-close ordering for a multi-timeframe edge case. TradingView's PineScript is the most widely used language for writing trading strategies. Millions of scripts. One problem: you can't run them anywhere except TradingView. That means: We built PineForge to fix this. The core idea: transpile PineScript v6 source to C++, compile it, run it offline on any OHLCV CSV. Here's how the pipeline works and what we learned building it. PineScript v6 source │ ▼ Lexer / Tokenizer │ ▼ Parser → AST │ ▼ Semantic Analyzer type inference, series detection, scope resolution │ ▼ Codegen │ ▼ C++ class extends BacktestEngine │ ▼ g++ / clang++ → native binary Each Pine script becomes a C++ class that extends our BacktestEngine base. TA call-sites ta.sma , ta.rsi , ta.crossover , etc. resolve to inlined C++ implementations. Pine's series type — which is really a lazy reverse-index into a rolling buffer — becomes a fixed-size ring buffer with bounds checking. The trickiest part wasn't the TA functions. It was Pine's execution model . Pine evaluates top-to-bottom on every bar, with implicit state accumulation. There's no explicit loop — the runtime loops over bars for you, and every var -prefixed declaration persists across bars. js //@version=6 strategy "Example" var float cumulative = 0.0 cumulative += close sma20 = ta.sma close, 20 if ta.crossover close, sma20 strategy.entry "Long", strategy.long In C++, this becomes something like: class ExampleStrategy : public BacktestEngine { float cumulative = 0.0f; // var → class member, initialized once void onBar override { cumulative += close ; // series access via method float sma20 = ta sma close series, 20 ; if ta crossover close series, sma20 series { strategy entry "Long", Direction::LONG ; } } }; var declarations become class members. Non- var locals get re-initialized every bar. Series lookbacks close 1 , close 5 become ring buffer accesses with automatic history tracking. Writing a transpiler is one thing. Making it match TradingView trade-for-trade is another. We built a corpus of 246 reference strategies — everything from classic MACD crossovers to multi-timeframe trend followers with complex entry/exit logic. For each: Current result: 245/246 strategies at strict parity. 375,000+ trades validated. Zero engine bugs. The one failing strategy hits a confirmed TradingView-side anomaly their bar-close ordering in a specific multi-timeframe edge case . We've documented it; it's not our bug. Getting from "mostly works" to 245/246 required fixing: strategy.close all timing calc on every tick mode barstate.isconfirmed semantics barstate.islast in historical replay request.security bar alignmentTradingView's "Bar Magnifier" premium feature lets you simulate limit fills inside a bar. We implemented this with six distribution modes: | Mode | Description | |---|---| uniform | Equal probability across the bar range | cosine | Bell-shaped, price spends more time near midpoint | triangle | Linear taper from open to close | endpoints | Bimodal, price near open/close | front loaded | Higher probability near open | back loaded | Higher probability near close | All modes support optional volume weighting. A limit order at $100 inside a $95–$105 bar fills at exactly $100 if the simulated path crosses it — no last-tick approximation. Pine strategies have parameters. Finding good ones via grid search is slow. We wired in Optuna https://optuna.org/ with a custom objective interface: python Any objective you want the optimizer to chase def objective trades : returns = t.pnl pct for t in trades sharpe = mean returns / std returns sqrt 252 max dd = compute max drawdown trades return sharpe - 2.0 max dd penalize drawdown heavily TPE sampler, pruning via MedianPruner, parallel trials via n jobs . The optimizer calls the compiled C++ binary directly — no Python overhead on the hot path. One Docker container. No API key. No account. Transpile Pine to C++ docker run --rm -v $ pwd :/workspace pineforge/engine:latest \ transpile --input /workspace/my strategy.pine --output /workspace/out/ Backtest against your OHLCV CSV docker run --rm -v $ pwd :/workspace pineforge/engine:latest \ backtest \ --strategy /workspace/out/my strategy \ --data /workspace/BTCUSDT 1h.csv \ --from 2022-01-01 --to 2024-01-01 Output: JSON trade list, equity curve, summary stats. Pipe it anywhere. .so binaries; buyers tune exposed inputs, never see source. AES-256-GCM encrypted, Ed25519-signed, machine-bound licenses.The engine is open-core Apache 2.0 . The codegen is on GitHub. The 246-strategy parity corpus is public. If you've ever hit TradingView's runtime ceiling — wrong fills, irreproducible results, locked data — this is the escape hatch. Questions about the transpiler architecture, parity methodology, or the optimizer integration? Ask in the comments. Worth being explicit about this since it trips people up. pineforge-engine — Apache 2.0. The C++ runtime, the backtest engine, the ABI-stable .so interface. Full open source. CI runs on Ubuntu + macOS, 93.06% line coverage, 16 ctest binaries on every commit. Free to audit, fork, deploy. pineforge-codegen — the Python transpiler package — is source-available under Short version: the engine is fully open. The transpiler is free for personal trading, source-available, paid for commercial use.