The fastest deterministic PineScript v6 backtest runtime — validated trade-for-trade against TradingView.
🚀 Get API Key (free) · 🎮 Live Playground
📖 API Documentation · ⚡ 60-second Tutorial · 🧪 Coverage Map · 🔬 Benchmarks
The fastest way to use PineForge: let your AI agent write, run, and optimize strategies for you via the @pineforge/codegen-mcp MCP server.
The workflow:
- Agent writes (or you paste) PineScript v6 source
- MCP transpiles Pine → C++ via the hosted API (source leaves your machine; OHLCV never does)
- Engine runs locally in Docker — microsecond-class, bit-reproducible results
- Agent reads the trade list, suggests improvements, sweeps parameters
Prerequisites: Node ≥ 20, Docker, a PineForge API key (free tier at pineforge.dev)
claude mcp add pineforge-codegen \
--transport stdio \
--env PINEFORGE_API_KEY=pf_... \
-- npx -y @pineforge/codegen-mcpOnce connected, your AI agent can:
| What to ask | Tool used |
|---|---|
| "Fetch BTC/USDT 15m data for the last 30 days" | fetch_binance_ohlcv |
| "Backtest this SMA-cross strategy on that data" | backtest_pine |
| "Sweep fast length 8–21, slow 21–55, rank by net PnL" | backtest_pine_grid |
| "What broker overrides are available?" | list_engine_params |
Try it first. Paste any Pine v6 strategy at codegen.pineforge.dev to see the generated C++ before running anything locally.
Free tier included. Sign up at pineforge.dev — no credit card required to start.
- 🎯 TradingView-exact. 227 of 228 reference strategies (~313,000 trades) match TV trade-for-trade. The lone outlier is a stress probe at the 1× margin boundary where TV's broker emulator is non-deterministic — engine is correct. 100 of 100 PineForge excellent vs PyneCore + PineTS on the public three-way benchmark (~167,000 TV trades; PyneCore: 85 of 100; PineTS indicator-only).
- ⚡ Microsecond-class. Median 56× faster than PyneCore across 49 commonly-timed strategies (full 41,307-bar OHLCV via dlopen+run; see benchmarks/results/speed.md). Parameter sweeps load one
.soand re-run with new inputs — no recompile, no fork, no IPC. - 🔒 Stable C ABI. 10 functions, 6 POD types, one header (
<pineforge/pineforge.h>). Append-only across minor versions,static_assert-pinned struct layouts, hidden-visibility hygiene. Drop a strategy.soin any harness; it just runs. - 🧪 Reproducible to the bit. Deterministic float ordering, deterministic bar magnifier, no internal RNG seeded from time. Two runs with the same inputs produce bit-identical trade lists.
- 🧰 FFI-friendly. Call from Python (
ctypes), Rust (libloading), Go (cgo), Node, Julia. Worked examples for pure C, Python sweep, Rust, multi-strategy harness, and magnifier A/B ship in the docs. - 🌍 Cross-platform CI. Linux + macOS × Release + Debug. Universal mac binary. Static library, no runtime DSO surprises at deploy time.
PineForge ships as a static C library (libpineforge.a) with a stable 10-symbol C ABI. Call from C, Python, Rust, Go, Node, Julia — one harness, swap strategies forever.
#include <pineforge/pineforge.h>
int main(void) {
pf_strategy_t s = strategy_create(NULL);
pf_bar_t bars[] = { /* OHLCV ... */ };
pf_report_t r = {0};
run_backtest(s, bars, sizeof(bars)/sizeof(*bars), &r);
printf("%d trades, net %.2f\n", r.trades_len, r.net_profit);
report_free(&r);
strategy_free(s);
return 0;
}That's the entire integration. Every PineForge-compiled strategy .so exports the same 10 symbols — write your harness once, swap strategies forever.
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j
ctest --test-dir build --output-on-failure # 30 tests, ~1 s
bash tutorial/run.sh # MACD backtest end-to-end| Resource | What it covers |
|---|---|
| 📖 cdocs.pineforge.dev | Full C ABI reference, lifecycle, report schema, configuration knobs, magnifier, FFI bindings, ABI stability contract |
| 🚀 Getting Started | 60-second build + install + smoke test |
| 🧪 Tutorial: MACD on BTC/USDT | End-to-end annotated walkthrough |
| 🔌 FFI from Python | Complete ctypes mirror — paste-ready |
| 🦀 Calling from Rust | Idiomatic libloading wrapper |
| 🧰 CMake integration | find_package(PineForge) recipe |
| 🔒 ABI stability | Versioning contract + symbol inventory |
| 🗺️ Pine v6 coverage map | What's implemented, what's not, and why |
The site auto-rebuilds on every push to main and every release tag.
PineForge is the C++ runtime that PineForge-compiled strategies link against. It implements PineScript v6 strategy semantics — order matching, fills, the magnifier, technical indicators, time/session math — as a static C++ library with a stable C ABI.
The runtime is parity-tested trade-for-trade against TradingView's "List of Trades" CSV exports on a reference corpus: 227 excellent + 1 documented anomaly = 228 strategies under the canonical verifier. The corpus ships as a public Apache-2.0 submodule.
This repository ships:
libpineforge.a— the static runtime library<pineforge/pineforge.h>— the public C ABI (the canonical, stability-pinned consumer surface)<pineforge/*.hpp>— the internal C++ headers (used by the closed PineForge transpiler; not part of the stability guarantee)- A 30-binary ctest suite (29 C++ + 1 pure-C ABI sanity test) that runs in CI on every commit (~81% line coverage of
src/measured viabash scripts/coverage.sh) **corpus/** (public git submodule) — 228 reference strategies under a singlecorpus/validation/tree. Each folder shipsstrategy.pine,generated.cpp,tv_trades.csv, andengine_trades.csv. Runbash scripts/run_corpus.shaftergit submodule update --init corpus.[benchmarks/](benchmarks/)— three-way engine comparison (PineForge ↔ PyneCore ↔ PineTS) on 100 strategies (50 public + 50 promoted corpus probes) and 10 canonical indicators. The harness code and reports live here; fixtures (pinned OHLCV, everystrategies/* folder with TV exports and trade CSVs) ship only via an optionalbenchmarks/assetssubmodule — a separate optional submodule (not yet public). With that init’d,bash benchmarks/run_all.shreproduces the headline numbers with zero external API calls. PyneCore Python is official cloud-compiler output (no hand-ports). Headline: PineForge hits canonical excellent tier on 50/50 strategies (first 50) vs PyneCore’s 47/50; on the expanded 100-strategy suite (~167,000 TV trades verified), PineForge holds 100/100 excellent vs PyneCore’s 85/100. Median speedup: 56× vs PyneCore across 49 commonly-timed strategies.
**[docs/coverage.md](docs/coverage.md)** is the complete, current map
of which Pine v6 surface this runtime covers — every TA class, every
order primitive, every request.security() semantic, plus a structured
inventory of what's deliberately not implemented (with feasibility tags
for each gap). Read it before integrating PineForge as a backend or
auditing the parity claim.
This is the runtime, not the compiler. PineForge's PineScript-to-C++ transpiler is closed-source and ships separately. This library is what every compiled strategy .so links against: it provides the implementations of ta.ema, strategy.entry, request.security, the bar magnifier, and so on, behind a stable C ABI.
This is a backtest engine, not a charting library. PineScript drawing primitives (plot, bgcolor, label, …) compile cleanly but do nothing at runtime. The runtime computes trade execution and reports — it does not render.
This is not a TradingView clone. PineForge intentionally diverges from TradingView in a handful of places where TV's behaviour is undocumented or platform-specific (the bar magnifier, deterministic float ordering). Where it converges, it converges exactly on the reference corpus (227/228 excellent + 1 documented anomaly. Init the public corpus submodule per [CONTRIBUTING.md](CONTRIBUTING.md)). Where it diverges, it documents the divergence.
- CMake ≥ 3.16
- A C++17 compiler (GCC ≥ 9, Clang ≥ 10, Apple Clang ≥ 12)
- Eigen 3.3+ — used for matrix-typed PineScript. The build will fetch Eigen via CMake
FetchContentif no system install is found.
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j
ctest --test-dir build --output-on-failureExpect 30 tests to pass. The largest (test_integration, test_request_security) take a few hundred milliseconds; everything else completes faster.
cmake --install build --prefix /usr/localInstalls:
lib/libpineforge.ainclude/pineforge/*.hppandinclude/pineforge/pineforge.hlib/cmake/PineForge/PineForge{Config,Targets,ConfigVersion}.cmake
find_package(PineForge 0.1 REQUIRED)
target_link_libraries(my_target PRIVATE PineForge::pineforge)#include <pineforge/pineforge.h>
int main(void) {
pf_version_t v = pf_version_get();
/* ... */
}<pineforge/pineforge.h> is the single canonical consumer header. Every compiled PineForge strategy .so exports exactly the 10 symbols declared there:
| Symbol | Role |
|---|---|
strategy_create |
Allocate a strategy instance |
strategy_free |
Release the instance |
run_backtest |
Run with auto-detected timeframe |
run_backtest_full |
Run with timeframe + magnifier configuration |
report_free |
Free arrays inside a filled pf_report_t |
strategy_set_input |
Override a Pine input.*() value |
strategy_set_override |
Override a strategy(...) declaration param |
strategy_set_magnifier_volume_weighted |
Toggle volume-weighted magnifier |
strategy_set_trace_enabled |
Toggle per-bar trace recording |
pf_version_get |
Runtime version |
POD types (pf_bar_t, pf_trade_t, pf_report_t, pf_security_diag_t, pf_trace_entry_t, pf_version_t) and the pf_magnifier_distribution_t enum complete the surface.
Stability guarantee: within the same PINEFORGE_VERSION_MAJOR, struct layouts and extern "C" signatures are append-only. New fields may be appended; existing fields are never reordered, removed, or retyped. New functions may be added; existing functions are never removed or signature-changed. Compile-time static_asserts in src/c_abi.cpp pin the layouts against drift.
The C++ headers (<pineforge/engine.hpp>, <pineforge/ta.hpp>, ...) are internal implementation surface — used by the closed PineForge transpiler, not part of the stability guarantee, and not recommended for external consumption.
include/pineforge/ - public C ABI + internal C++ headers
src/ - implementation (~25 .cpp files split by concern)
├── c_abi.cpp runtime-side C ABI implementations + layout asserts
├── engine_*.cpp BacktestEngine implementation, split by concern:
│ ├── engine_path_resolve.cpp intra-bar OHLC path-resolution helpers
│ ├── engine_lower_tf.cpp lower-timeframe emulation
│ ├── engine_orders.cpp execute_market_*, partial exits
│ ├── engine_fills.cpp process_pending_orders fill loop
│ ├── engine_security.cpp request.security registration
│ ├── engine_run.cpp run() entrypoints, magnified bar loop
│ ├── engine_report.cpp fill_report, trace recording
│ ├── engine_strategy_commands.cpp strategy.entry/exit/close/cancel/order
│ ├── engine_trade_accessors.cpp strategy.opentrades.*
│ └── engine_risk.cpp risk gates + per-trade extremes
├── engine_internal.hpp private cross-TU header (path-resolve helpers, types)
├── ta_*.cpp 66 indicator classes split by category:
│ ├── ta_moving_averages.cpp RMA, SMA, EMA, WMA, HMA, VWMA, ALMA, SWMA
│ ├── ta_oscillators.cpp RSI, Stoch, CCI, MFI, CMO, TSI, WPR, COG, RCI, ...
│ ├── ta_volatility_trend.cpp ATR, BB, KC, MACD, DMI, SAR, Supertrend, ...
│ ├── ta_extremes_volume.cpp Highest/Lowest, OBV, AccDist, NVI/PVI/PVT, VWAP, ...
│ └── ta_misc.cpp Linreg, PercentRank, BarsSince, ValueWhen, ...
└── magnifier.cpp / matrix.cpp / session_time.cpp / str_utils.cpp / timeframe.cpp / timezone.cpp / math.cpp
tests/ - 30 ctest binaries (29 C++ + 1 pure-C ABI sanity)
corpus/ - public submodule: 228 strategies; see CONTRIBUTING.md
├── data/ - reference 36k-bar OHLCV feed (Binance ETH/USDT:USDT 15m)
└── CMakeLists.txt - opt-in subproject that compiles every generated.cpp into strategy.so
benchmarks/ - three-way comparison harness vs PyneCore + PineTS
├── assets/ - public submodule: data/ (OHLCV) + strategies/ (100 folders, TV-linked CSVs + generated.cpp + strategy_pyne.py)
├── runners/ - 3 runtime runners (run_pynecore.py + run_pinets_canonical.mjs + run_pineforge_canonical.cpp)
├── speed/ - Google Benchmark harness + subprocess timers + aggregator
├── results/ - summary.md, trade_comparison.md, indicator_comparison.md, speed.md
├── compare.py - 3-way trade-list comparator (mirrors scripts/verify_corpus.py)
└── run_all.sh - one-shot reproducer: cmake build + run all 3 engines + diff (zero API keys)
scripts/ - reproducibility tooling
├── run_strategy.py - load any strategy.so via ctypes, write engine_trades.csv
├── run_corpus.sh - one-shot: build all 228 .so + run + verify
└── verify_corpus.py - diff each engine_trades.csv against its tv_trades.csv
cmake/ - PineForgeConfig.cmake.in for downstream find_package()
cmake/smoke_consumer/ - Minimal find_package(PineForge) CI smoke project
.github/workflows/ - CI: Linux + macOS × Release + Debug
Every compiled strategy .so that statically links libpineforge.a exports exactly the 10 documented C ABI symbols and zero internal C++ symbols. This is enforced at the library level:
libpineforge.ais built with-fvisibility=hidden -fvisibility-inlines-hidden- Public symbols are tagged
PF_API(visibility=default) - Internal C++ classes (
BacktestEngine,ta::,pineforge::internal::) are not tagged, so they stay hidden in any final.so - CI runs
scripts/check_c_abi_runtime.py(runtimePF_APIsplit vs transpiler-emitted symbols). Full trade-list parity sweeps use the publiccorpussubmodule locally; CI runs the engine-only ctest set, not the full corpus parity sweep (~3 min full sweep is run-locally-or-on-release territory).
PineForge follows semantic versioning at the C ABI level:
PATCH: implementation changes that don't affect ABIMINOR: append-only ABI additions (new functions, new struct fields appended to existing structs)MAJOR: breaking ABI changes
A pre-compiled strategy .so against runtime 0.X.Y will keep working against any later runtime within 0.X.Z. Across major versions, all bets are off.
The validation corpus lets anyone rebuild and rerun every compiled
PineForge strategy from a fresh clone. The corpus ships
generated.cpp for every probe, so no transpiler access is required —
just the engine, the corpus submodule, and a C++17 compiler.
Scale: 228 strategies × ~313,000 trades verified against TradingView.
git clone https://github.com/fullpass-4pass/pineforge-engine.git
cd pineforge-engine
git submodule update --init corpus
# Build + run + verify (no codegen needed — generated.cpp ships in corpus)
JOBS=8 scripts/run_corpus.sh
# Optional: regen the report
python3 scripts/regen_validation_report.pyThat builds libpineforge.a plus one strategy.so per probe, runs each
against the reference OHLCV feed, rewrites each engine_trades.csv,
and prints the canonical corpus summary described in
corpus/README.md. Headline result: 227 / 228 excellent + 1 documented TV-side anomaly (anomaly-equity-mirror-strategy-equity-01, TV broker non-deterministic at 1× equity boundary). Total trades: TV 312,829, engine 312,920 (+91 ≈ 0.03 % over TV).
[benchmarks/](benchmarks/) runs 100 strategies through PineForge, PyneCore, and PineTS to spot engine-specific defects vs TV-side semantics. Strategy folders + the 53,930-bar Binance ETH/USDT-USDT 15m OHLCV live under the benchmarks/assets public submodule. PyneCore Python sources are official PyneSys cloud-compiler output (no hand-ports). PineTS handles indicators only — their strategy backtester is upstream roadmap.
Scale: 100 strategies × ~167,000 TV trades verified.
git submodule update --init benchmarks/assets # public submodule with 100 strategies + OHLCV
cmake -B build -DPINEFORGE_BUILD_BENCH_STRATEGIES=ON # build engine + 100 bench dylibs
cmake --build build --target pineforge bench_strategies -j
cd benchmarks && uv sync && npm install && cd .. # bench Python + Node deps
bash benchmarks/run_all.sh # ~5 min; no API keys
cat benchmarks/results/summary.mdCurrent standings (canonical align-then-trim window, 4-dimension diff vs TV):
| PineForge | PyneCore | TV ground truth | |
|---|---|---|---|
| Strategies | 100 | 100 | 100 |
| Trades emitted | 167,381 | 253,031 | 167,301 |
| 🟢 excellent | 100 / 100 | 85 / 100 | — |
| 🟢 strong | 0 / 100 | 2 / 100 | — |
| 🟡 moderate | 0 / 100 | 10 / 100 | — |
| 🟠 weak | 0 / 100 | 3 / 100 | — |
The 15 PyneCore-only outliers from excellent involve strategy.exit(stop=…, limit=…) brackets, trail_* exits, strategy.close(qty_percent=…) partial exits, and bar-magnifier paths — categories where PyneCore's broker emulator differs from TV and PineForge does not. See benchmarks/results/summary.md for the per-strategy table and methodology.
Last refresh: 2026-05-16 against engine v0.4.1, PyneCore 6.4.6, PineTS 0.9.16. Per-strategy speed table at benchmarks/results/speed.md — median 56× faster than PyneCore on 49 commonly-timed strategies.
- v0.4.1 — corpus rewritten as 228 clean-room probes; corpus submodule flipped public; 5 engine bug fixes (OCA same-direction RAW_ORDER, intraday-cap latch, etc.) + chart-TZ infra + 5 new ctests. 227 excellent + 1 documented anomaly = 228/228 strong-or-better.
- v0.1 — initial public release. C ABI defined and pinned. Reported 165 strict-excellent + 2 strong = 167/168 TV parity on the internal corpus (private submodule, 168 strategies including 5 parity probes + 1 anomaly probe); the lone outlier is a 1×-margin stress probe on an undocumented TV edge case. 48/50 strategies hit canonical excellent tier in the three-way benchmark. CI runs on Ubuntu + macOS (ctest + install smoke; no corpus).
- v0.2 — same-id stop/replace deferred to post-bar OHLC resolution (PR #13); RMA warmup seed aligned to Pine reference formula,
-ffp-contract=offbuild flag added (PR #14). - v0.3 — magnifier wrong-side gap fill fixed for entry bar; directional mintick rounding in
apply_slippage(PR #15). ctest suite expanded to 30 binaries.
Apache License 2.0. See LICENSE. Third-party notices: NOTICE. Extended licensing notes (optional benchmark AGPL deps, trademarks): LEGAL.md. Community standards: CODE_OF_CONDUCT.md. Security contact: SECURITY.md.
See CONTRIBUTING.md (includes the Apache-2.0 contribution license grant). The short version:
- Every contribution must keep the parity test green.
- Public-API changes (anything exported from
<pineforge/pineforge.h>) require a major-version bump. - Internal C++ helpers can change freely as long as the ABI surface stays put.
{ "mcpServers": { "pineforge-codegen": { "command": "npx", "args": ["-y", "@pineforge/codegen-mcp"], "env": { "PINEFORGE_API_KEY": "pf_..." } } } }