PineForge-Codegen
Locally transpile PineScript v6 to C++ and run deterministic, TradingView-parity-graded backtests on OHLCV data via a bundled Docker engine — no API key required.
Transpile Pine:
transpile_pineconverts Pine v6 source into a C++ translation unit.Run a single backtest:
backtest_pinetests a Pine strategy against an OHLCV CSV with optional input overrides, strategy-header overrides, runtime settings, symbol/syminfo instrument handling, and report offloading.Sweep parameters:
backtest_pine_gridruns a cartesian sweep overinputs×overrides, ranks combinations, and returns the best result.Grade TradingView parity:
check_tradingview_paritycompares your TradingView Strategy Tester export against PineForge’s run trade by trade.Fetch Binance data:
fetch_binance_ohlcvwrites a backtest-ready OHLCV CSV from Binance spot or USDT-perp klines, plus an instrument sidecar.Discover symbols:
binance_symbolslists/filters Binance spot or USDT-perp symbols for validation.Inspect engine knobs:
list_engine_paramscatalogs strategy overrides and runtime arguments accepted by backtests.Check Pine v6 coverage:
list_coverage_topics,get_coverage_topic, andcheck_pine_featurereport which Pine identifiers/namespaces are supported, partial, unsupported, or via-transpiler.Manage the engine image:
pull_engine_imageandcheck_engine_imagepull or check the Docker runtime image.Stay local: strategy source and CSVs never leave the machine; only Binance public API calls and image-registry checks go outbound.
@pineforge/backtest-mcp
Self-contained stdio MCP server: an AI agent writes PineScript v6, and the
bundled pineforge-release image transpiles it to C++ and backtests it against
an OHLCV CSV (your own, or one fetched from Binance's public API) — all in one
container, in-process. Fully local — the image bundles the
pineforge-codegen
transpiler, so Pine → C++ → backtest run with no host Docker daemon. No API
key. Your strategy source and CSVs never leave the machine; only the Binance
tools, and check_tradingview_parity when you pass no bars, make outbound requests
(public endpoints).

Tools
name | runs on | purpose |
| in-process | Pine v6 → C++ translation unit (transpile-only) |
| local (no I/O) | Catalog of every |
| in-process | Single backtest of a Pine source against an OHLCV CSV |
| in-process | Cartesian sweep of |
| in-process (Binance public API only without your bars) | Grade your TradingView Strategy Tester export against PineForge's run of the same script, trade by trade |
| Binance public API | Write a backtest-ready CSV from Binance spot or USDT-perp klines |
| Binance public API | List / filter Binance symbols (5-min in-process cache) |
| local (no I/O) | Every Pine v6 coverage topic with a one-line status + summary |
| local (no I/O) | Look up whether a Pine identifier/namespace is supported in PineForge |
| local (no I/O) | Full detail + supported/partial/via_transpiler/unsupported lists for one topic |
| local (no I/O) | Docker image only: mode, baked-in flag and the bundled |
The table is the Docker image's tool list (11 tools). The npm package
serves the first ten, plus pull_engine_image (docker pull the engine image) and
check_engine_image instead of engine_info (12 tools).
In tools/list every tool also has a title and the four MCP annotation hints
(src/tool-meta.ts). Read-only: the lookups and check_tradingview_parity (it
works in a temporary directory it deletes), and in the Docker image
transpile_pine. Not read-only: backtest_pine and backtest_pine_grid (a
report too large to return is written to a file), fetch_binance_ohlcv (writes
its CSV) and the two image tools; in the npm package also transpile_pine, since
there it and the backtests take an image that Docker pulls when missing.
Destructive: the tools that write a file at a path you name, since they replace a
file already there. Open-world: the tools that reach Binance's public API (the two
backtests do when you pass a symbol) or an image registry.
Related MCP server: backtester-mcp
Install
Runs as a self-contained container over stdio — engine bundled, in-process, no
host Docker daemon, no API key. Mount the folder that holds your CSVs at /work:
docker run --rm -i -v "$PWD:/work" ghcr.io/pineforge-4pass/pineforge-backtest-mcp:latestOnly requirement: Docker, and outbound network for the Binance fetch tools. Wire it into your MCP client below.
Use absolute
/work/...paths in tool arguments (ohlcv_csv_path,output_path,report_path). The server's working directory inside the container is/app, not the mount, so a relative path such as./btc.csvpoints into the container and is lost when it exits (--rm).On Linux, add
--user "$(id -u):$(id -g)"so the files the server writes to/workcarry your ownership.-iis required; never add-t— a TTY corrupts the stdio JSON-RPC stream.
The image's :latest and npm's latest always carry a stable release. A
pineforge-release prerelease (such as 1.0.0-rc.1) produces a prerelease of this
server (X.Y.Z-alpha.N, -beta.N or -rc.N): the image :vX.Y.Z-rc.N, built FROM
that pineforge-release prerelease, and npm @pineforge/backtest-mcp@next, which,
like any npm install, runs the engine image named by PINEFORGE_IMAGE (default
ghcr.io/pineforge-4pass/pineforge-release:latest, the stable engine). Prereleases
are not listed in the MCP Registry. The first, 0.9.32-rc.1 on pineforge-release
1.0.0-rc.1, was published on 2026-09-30 (image :v0.9.32-rc.1, npm next).
npm / npx
npx -y @pineforge/backtest-mcpNeeds Node ≥ 20 and a running Docker daemon: each transpile and backtest is a
docker run --rm --network=none of the engine image (PINEFORGE_IMAGE, default
ghcr.io/pineforge-4pass/pineforge-release:latest). docker pull it first: the
server's own pull (implicit on the first call, or pull_engine_image) is cut off
after PINEFORGE_DOCKER_TIMEOUT_MS (120 s by default). Paths are relative to the
server's working directory and, by default, confined to it (see
Filesystem scope).
Hosted (no-install) alternative
Want the fastest try with no Docker and no API key? Paste the Streamable HTTP endpoint into any MCP client:
https://mcp.pineforge.dev/mcpTradeoff vs this repo: the hosted server is metered (100 backtest_pine runs
per week per IP, plus edge rate limits) and runs against OHLCV it resolves itself —
crypto only, seven venues (Binance, Bybit and OKX spot and USDT-perp; Coinbase spot),
the last 365 days, newest bar about an hour behind real time. Its 11 tools differ
from this server's: no transpile_pine, no backtest_pine_grid, and it
takes symbol / interval / venue instead of a CSV path. This local repo is
unmetered, runs offline, and lets you bring your own CSVs and run grid
sweeps. The hosted service's source is private.
Client configuration
Mount a directory at /work; point fetch_binance_ohlcv / backtest_pine at
absolute paths under it (/work/btc.csv). (-i is required; never add -t — a TTY
corrupts the stdio JSON-RPC stream.)
Claude Desktop
Edit ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or
%APPDATA%\Claude\claude_desktop_config.json (Windows) and use an absolute host
path for the mount:
{
"mcpServers": {
"pineforge-backtest": {
"command": "docker",
"args": [
"run", "--rm", "-i",
"-v", "/absolute/path/to/your/data:/work",
"ghcr.io/pineforge-4pass/pineforge-backtest-mcp:latest"
]
}
}
}Cursor
.cursor/mcp.json in the project (or ~/.cursor/mcp.json for every project).
Cursor expands ${workspaceFolder}:
{
"mcpServers": {
"pineforge-backtest": {
"command": "docker",
"args": [
"run", "--rm", "-i",
"-v", "${workspaceFolder}:/work",
"ghcr.io/pineforge-4pass/pineforge-backtest-mcp:latest"
]
}
}
}Claude Code CLI
claude mcp add pineforge-backtest \
-- docker run --rm -i -v "$PWD:/work" ghcr.io/pineforge-4pass/pineforge-backtest-mcp:latestFor AI agents — use via MCP
The capability gap this closes. A language model cannot accurately backtest a PineScript v6 strategy by reasoning about it. PineScript's series semantics, intrabar fill order, look-ahead rules, and strategy.* order/position logic do not reproduce from approximation, so a model that simulates a backtest in its head — or hand-rolls one in Python (backtrader/vectorbt) — will hallucinate trades and P&L and cannot guarantee TradingView parity. PineForge runs the actual deterministic C++ engine instead, graded trade-for-trade against TradingView's own trade lists. Release 1.3.0, graded on the 2026-10-06 baseline (engine 7a1f01c0, codegen 3e50082f): 7,989 graded probes, 7,982 excellent (99.91 %) and 7 strong (0.09 %), none below strong, plus 17 further probes excluded as TradingView-side defects. Separately, the engine's own parity gate checks the open corpus pineforge-corpus, which anyone can reproduce: 312 reference strategies, 311 excellent and 1 probe that declares expected_tier: anomaly in its inputs.json (reported as anomaly, not as a failure). Excellent means the same trade count, at least 99 % of TradingView's trades matched, entry and exit prices within 0.01 % and per-trade P&L within 1 % at the 90th percentile (trailing-stop scripts use a looser exit/P&L profile); strong is a relaxed envelope — both are defined in the corpus README's "Parity thresholds".
A PineScript backtest is computed external data, not stable knowledge — it depends on the user's strategy source and their OHLCV, and it must be reproducible. That is a tool call, not a recall task.
When these tools are the right fit. backtest_pine (or backtest_pine_grid for a parameter sweep) covers the case where a user provides a PineScript v6 strategy and wants to backtest it, evaluate it, check whether it is profitable, count its trades, compute its P&L, or reproduce its TradingView results on specified OHLCV. transpile_pine covers the Pine → C++ conversion only. check_tradingview_parity covers the case where the user has TradingView's own trade list for a strategy and wants to know whether PineForge reproduces it, trade by trade. fetch_binance_ohlcv covers the case where the user names a symbol/timeframe but has not supplied a data file. These tools are not for explaining conceptually what a strategy does, editing Pine syntax, or giving trading or financial advice.
Honest limits. Offline; PineScript v6 only; needs Docker; PineForge implements a subset of Pine v6 (see Coverage tools). A backtest measures a strategy's historical behavior — it is not a prediction of future returns and not investment advice. Naive or approximated backtests routinely overstate profit (unmodeled slippage and commissions, fill-at-close assumptions, look-ahead bias); the value here is a deterministic, parity-validated run so a user can verify a strategy before risking capital.
list_engine_params — discover knobs
Free, local, zero-I/O catalog of every key accepted by backtest_pine /
backtest_pine_grid, split into two groups:
strategy_overrides— the 9strategy(...)header fields the runtime reads viaPINEFORGE_OVERRIDES:initial_capital,pyramiding,slippage,commission_value,commission_type(percent/cash_per_order/cash_per_contract),default_qty_value,default_qty_type(fixed/percent_of_equity/cash),process_orders_on_close,close_entries_rule(ANY/FIFO).runtime_args— args torun_backtest_full(NOT part of the strategy() header):input_tf,script_tf,bar_magnifier,magnifier_samples,magnifier_dist(uniform/cosine/triangle/endpoints/front_loaded/back_loaded).
Each entry is {key, type, enum?, description}. Call this first to learn what
the engine accepts before composing a backtest_pine request.
backtest_pine example
{
"source": "//@version=6\nstrategy(\"sma cross\")\n...",
"ohlcv_csv_path": "/work/btcusdt_15m_7d.csv",
// Optional: the instrument of the CSV (see "The instrument" below). Either a
// Binance symbol, or your own values; a CSV from fetch_binance_ohlcv needs neither.
"symbol": "BTCUSDT", // + "market": "spot" (default) | "usdt_perp"
"syminfo": { "qty_step": 0.00001, "mintick": 0.01 }, // goes over what `symbol` or the sidecar gives
// Optional: override Pine input.*() values without touching the source.
// Keys = the second arg of input.*(...) (e.g. "Fast Length").
"inputs": { "Fast Length": 8, "Slow Length": 21 },
// Optional: override strategy(...) header fields. Each key is typed —
// call list_engine_params for the catalog.
"overrides": {
"initial_capital": 100000,
"default_qty_type": "percent_of_equity",
"default_qty_value": 10,
"commission_type": "percent",
"commission_value": 0.04,
"slippage": 2,
"pyramiding": 0,
"process_orders_on_close": true,
"close_entries_rule": "ANY"
},
// Optional: engine runtime args (NOT strategy() header). Use script_tf
// to aggregate the input CSV into a coarser strategy timeframe — the
// engine REJECTS script_tf finer than input_tf, and the tool call then
// fails (isError; "engine backtest failure (exit 4)" in the Docker image).
"runtime": {
"input_tf": "15",
"script_tf": "60",
"bar_magnifier": true,
"magnifier_samples": 8,
"magnifier_dist": "endpoints"
},
// Optional: where to write the full JSON report if it is too large to
// return inline (see below). In Docker use an absolute path under /work.
"report_path": "/work/report.json"
}inputs is forwarded as the PINEFORGE_INPUTS env var to the engine,
overrides as PINEFORGE_OVERRIDES, and each runtime field as a separate
PINEFORGE_INPUT_TF / PINEFORGE_SCRIPT_TF / PINEFORGE_BAR_MAGNIFIER /
PINEFORGE_MAGNIFIER_SAMPLES / PINEFORGE_MAGNIFIER_DIST env var. Empty /
unset → defaults from strategy.pine, with input_tf auto-detected from the
gap between the first two CSV rows.
The instrument
The engine runs a strategy on the instrument's lot size (qty_step) and tick size
(mintick). Without a lot size an order of 100% of equity can overshoot margin by a
hair, and the engine books a tiny sub-lot margin-call row (for example 2.6e-08 BTC,
entry equal to exit) that TradingView does not book. So backtest_pine (and
backtest_pine_grid, and check_tradingview_parity) applies the instrument to the
engine before the run, through its C ABI: qty_step and mincontract (the lot size),
mintick, pointvalue, type, currency, basecurrency. ticker, tickerid,
timezone and session are not applied (the engine keeps its defaults). Where it comes from:
symbol(+market:spotorusdt_perp; default: the market recorded in the CSV's sidecar when it names the same symbol, so a fetched perpetual stays a perpetual, elsespot, and then the result says so:market not given: spot assumed for <SYMBOL> (pass market "usdt_perp" for a USD-M perpetual); amarketthat disagrees with the sidecar is used, and the result says which one the CSV was fetched for). In one sentence: The lot size is TradingView's own reading for the symbol, from a measured table shipped with this server (Binance's LOT_SIZE.stepSize only for a symbol TradingView does not list; TradingView's usual 0.001 for a listing newer than the table); the tick size and currencies come from Binance's public exchangeInfo (or from the sidecar next to a CSV fetched by fetch_binance_ohlcv). The lot size has three tiers, by what the table (see below) knows of the symbol:a reading in the table (the usual case): TradingView's own, with no provenance warning;
a symbol the table lists as not on TradingView (
not_on_tv): Binance'sLOT_SIZE.stepSize, the only one there is, and the warninglot size for <label> is the exchange's lot step, not a TradingView reading: order quantities may differ from TradingView's;any other symbol Binance lists (a listing newer than the table): TradingView's usual 0.001, and the warning
lot size for <label> is TradingView's usual default (0.001), not a reading for this symbol (a listing newer than the readings): order quantities may differ from TradingView's(<label>is e.g.Binance spot BTCUSDT). A market the table has no usual lot size for takes Binance's step, as in 2.
Without Binance's record for the symbol (it does not list it, or Binance cannot be reached) and no reading in the table there is no lot size: the instrument is unresolved (below), never an invented grid. The tick size is the symbol's
PRICE_FILTER.tickSizeand the currencies are itsquoteAssetandbaseAsset, read from Binance's public exchangeInfo (one request, cached for 5 minutes); the point value is 1 and the typecrypto.Without a
symbol, the sidecar<csv path>.instrument.jsonthatfetch_binance_ohlcvwrites next to every CSV it fetches (itsinstrumentresult shows it). It is ignored, with the reason in the result, if the CSV has changed since (its first or last bar, or its content).syminfo: your own values, for a CSV of any other instrument:qty_step,mincontract,mintick,pointvalue,type,currency,basecurrency. They go over whatsymbolor the sidecar gives, and with neither they are the whole instrument. Giveqty_step(ormincontract; each defaults to the other) and, since the engine's default tick is 0.01,mintick(the result warns when it is missing).
What was applied is in the report's applied_runtime.syminfo and so in
fingerprint.provenance.runtime: a gridless and a gridded run never share a
fingerprint. Its source.kind says where the lot size is from: tradingview (a reading in the table),
exchange (Binance's, for a symbol TradingView does not list), default (TradingView's usual 0.001, for a
listing newer than the table) or user (syminfo, with source.base naming what it went over);
exchange and default come with the provenance warning above. Unresolved
means no lot size was found (no symbol, syminfo or sidecar, no Binance record for a symbol the table
has no reading for, or a symbol neither has): the run
still goes ahead with the engine's defaults (no lot grid, tick 0.01 unless syminfo gives one) and the
result starts with a warnings entry (instrument grid unavailable for ... : order quantity is not floored to a lot size, so the run can contain sub-lot margin-call rows that TradingView does not book).
If the unresolved instrument has nothing else the engine can be given either (no tick, no currency),
nothing is applied: the run is the image's own, its report has no applied_runtime.syminfo, and
warnings says no instrument was applied: the engine ran with its defaults; otherwise
applied_runtime.syminfo reads {"resolved": false, "reason": ...} next to what could be applied.
A run is never refused, and never fails, for want of an instrument. The instrument reaches the engine
through a per-run overlay of the engine's prefix (symbolic links to it). If the overlay cannot be built
(a host that cannot link the prefix, such as Windows, or any error while building it), or the container's
run fails with a message naming the overlay (a prefix layout the overlay does not mirror, a folder Docker
cannot mount), the run goes ahead without the instrument (in the second case once more, without it) and
warnings says the instrument could not be applied (<reason>); the engine ran with its defaults.
What the engine reports, and what its trades show. An image whose run_json.py lacks
apply_syminfo runs as it is: its report has no applied_runtime.syminfo, and the result says so. An image
whose engine library predates the lot grid runs and ignores it, while applied_runtime.syminfo reports it
applied (the library accepts the call): when trade quantities are not multiples of the reported lot size
the result says the engine reported the lot size applied, but N of M trade quantities are not multiples of it (an older engine ignores it) (a lone range-end mark is not counted). The check sees quantities only: it
cannot tell an older engine from a strategy whose partial exits are not floored to the lot size, which was
not checked here.
TradingView's lot size, not the exchange's. TradingView's syminfo.mincontract is
TradingView's own data, not a Binance field, and it is what TradingView floors an order to.
Measured on TradingView for every Binance symbol it lists (2026-10-03: 1,366 spot and 523
USD-M), it differs from LOT_SIZE.stepSize for 1,188 spot and 510 USD-M symbols (Binance's step
equals TradingView's for only 13% of spot symbols and 2.5% of USD-M ones). TradingView's usual 0.001 is what it reads for
90.6% of spot symbols (1,237 of 1,366) and 97.5% of USD-M ones (510 of 523), while Binance's step is often 1 or
0.1: USD-M BTCUSDT is 0.000001 on TradingView and 0.001 on Binance, so flooring to Binance's
step would shrink a 10,000 USDT position by up to about 1.2%. The tick size matched
TradingView's syminfo.mintick for every symbol measured.
A symbol outside the table is one of two things, and the table says which. It also lists, per
market, the symbols Binance has and TradingView does not (18 spot, 1 USD-M: not_on_tv), and
TradingView's usual lot size where at least 80% of that market's readings are it (both markets
today: defaults). A symbol on the not_on_tv list takes Binance's step (tier 2); any other
symbol outside the table is a listing newer than the readings and takes 0.001 (tier 3), the
likelier value there than Binance's step; both say so in warnings. A table without the two
lists (an older one, or PINEFORGE_TV_GRID pointing at one) has neither, so a symbol outside it
takes Binance's step with the exchange warning. Either way, pass syminfo.qty_step (read
syminfo.mincontract off its chart) to set the lot size yourself: a lot size you give is yours, even
when it equals the default or Binance's step, and the warning goes.
Returns the standalone pineforge-release image's report JSON (engine, input,
summary, trades, metrics, equity_curve, fingerprint, applied_inputs,
applied_overrides, applied_runtime, diagnostics, elapsed_seconds) plus a
_meta block, inline when it serializes to at most 200,000 bytes:
{
"engine": "pineforge",
"summary": { "total_trades": 49, "net_pnl": -190.85, ... },
"applied_inputs": { "Fast Length": "8", "Slow Length": "21" },
"applied_overrides": { "default_qty_value": "5" },
"applied_runtime": { "input_tf": "15", ..., "syminfo": { "resolved": true, "qty_step": 0.00001, ... } },
"trades": [ ... ],
"equity_curve": [ ... ],
"elapsed_seconds": 0.0042,
"_meta": { "strategy_cpp_bytes": 5079, "image": "local" } // npm/npx: the engine image name
}A long run (3,000 hourly bars is enough) does not fit an MCP tool result. Then
the full report is written to report_path — default pineforge-backtest-<timestamp>.json
in the server's working directory — and the tool returns a compact result instead:
{
"summary": { ... }, "applied_inputs": { ... }, "applied_overrides": { ... }, "applied_runtime": { ... },
"elapsed_seconds": 0.0008, "total_trades": 73,
"report_path": "...", "report_path_in_container": "/work/report.json",
"truncated": true, "note": "...", "_meta": { ... }
}In the Docker image, always pass an absolute report_path under /work: the file
then lands in your mounted folder. Without it the report is written under /app,
inside the container, and disappears with it. (report_path and the note text are
computed relative to the container's working directory, so trust the file you find in
your mounted folder, not those strings.) The inline limit is PINEFORGE_MAX_INLINE_BYTES.
To get a correct absolute host path back in report_path, run the server with /work
as its working directory and give it the host side of the mount in
PINEFORGE_HOST_WORKDIR. The image's entrypoint is a path relative to /app, so this
takes an explicit --entrypoint; relative tool paths then resolve inside the mount too:
docker run --rm -i -v "$PWD:/work" -w /work -e PINEFORGE_HOST_WORKDIR="$PWD" \
--entrypoint node ghcr.io/pineforge-4pass/pineforge-backtest-mcp:latest /app/dist/index.local.jsWith the plain docker run of Install (working directory /app),
PINEFORGE_HOST_WORKDIR still makes report_path absolute, but wrong: it is joined
with the report's path relative to /app.
backtest_pine_grid — parameter sweep
Transpiles the Pine source once (locally, in-container), then compiles and
runs that C++ for each combination in the cartesian product of inputs ×
overrides: every combination is a fresh g++ build of the same translation
unit, then its backtest. Returns a ranked list plus the top entry under best.
{
"source": "//@version=6\nstrategy(\"macd\")\n...",
"ohlcv_csv_path": "/work/btcusdt_15m_7d.csv",
// Each axis is {key: list-of-values}. All combinations are tried.
"inputs": {
"Fast Length": [8, 12, 19],
"Slow Length": [21, 26, 39]
},
"overrides": {
"default_qty_value": [1, 5],
"commission_value": [0.04]
},
// Optional knobs:
"fixed_inputs": { "Source": "close" }, // applied to every combo
"fixed_overrides": {}, // typed strategy() overrides
"runtime": { "input_tf": "15", // engine runtime args, fixed
"script_tf": "60" }, // across the sweep
"max_combinations": 64, // default 64, at most 1024; a bigger grid is an error
"concurrency": 2, // parallel runs: default 1, at most 8
"include_trades": false, // default false: omit per-trade lists
"sort_by": "net_pnl", // net_pnl (default) | win_rate_pct | max_drawdown | total_trades
"report_path": "/work/grid.json" // where an oversized sweep is written
}The result has total_combinations, succeeded, failed, sort_by, best, and
results (successful runs ranked by sort_by, descending, then failures). A sweep
too large to return inline is written to report_path and the tool returns best,
the top 10 in top_results, results_truncated and report_path.
check_tradingview_parity — grade your TradingView results
Give it a Pine v6 script and TradingView's own Strategy Tester export for it. It
runs the script on the same market and window and grades the two trade lists
trade by trade with the grader behind PineForge's published parity figures:
scripts/verify_corpus.py of pineforge-engine v1.2.0 (sha256
431452ecddc8184937951ddf9a4c5f29029731237301967b5b480800be6fd1a6, the same file in
v1.3.0), run through the corpus gate's own harness (scripts/run_strategy.py, also
unchanged in v1.3.0). Both are vendored unchanged under
parity/vendor/. Checked against the
open pineforge-corpus at
a35c7c4, on engine v1.1.0 and codegen c5d97ee5: the grading core returns the
published tier for all 309 probes the corpus gate grades, and the tool itself, called
over stdio with only the inputs below, returns it for a stratified sample of 30.
{
"pine": "//@version=6\nstrategy(\"my strategy\")\n...",
// The "List of trades" CSV as TradingView exports it, or the Strategy Tester
// XLSX report base64-encoded (it starts with UEsDB).
"tradingview_trades": "Trade number,Type,Date and time,Signal,Price USDT,...",
"symbol": "BINANCE:ETHUSDT.P", // TradingView ticker
"timeframe": "15", // TradingView resolution: 1, 5, 15, 60, 240, 1D, ...
"range_start": "2025-04-01T00:00:00Z", // first bar TradingView computed (UTC unless an offset is given)
"chart_timezone": "Asia/Taipei", // the timezone TradingView printed the trade times in
// Optional:
"range_end": "2025-10-01T00:00:00Z", // default: the export's last row
"inputs": { "Fast Length": 8 }, // TradingView's Inputs tab, as in backtest_pine
"strategy_overrides": { "commission_value": 0.04 }, // TradingView's Properties tab (list_engine_params)
"runtime": { "bar_magnifier": true }, // list_engine_params runtime args
"max_mismatches": 10, // mismatching trades to list: default 10, at most 50
"ohlcv_csv_path": "/work/eth_15m.csv", // or "ohlcv_csv": "<CSV text>": your own bars
"syminfo": { "qty_step": 0.001 } // the instrument's own values, as in backtest_pine
}input | notes |
| Pine v6 source, at most 256 KiB |
| "List of trades" CSV text (columns |
| TradingView ticker; required unless the XLSX states it or you pass bars |
| TradingView resolution; required unless the XLSX states it |
| ISO 8601 date or datetime of the first bar of the backtest; required unless the XLSX states it |
| optional; default: the export's last row (the result says so) |
| IANA name of the timezone the trade times are printed in ( |
| optional, the same keys as |
| optional, default 10, at most 50 |
| optional: the instrument's own values ( |
| your bars, so any market works: |
| optional 1-minute bars for a declared bar magnifier, covering the first chart bar's open through the last chart bar's close; same formats, limits and path rules as |
Limits (refused with a plain error above them):
what | limit |
| 262,144 bytes (256 KiB) of UTF-8 |
| 33,554,432 characters (32 × 1024²) as passed; the trade list the grader reads (the CSV, or the one rebuilt from the XLSX) at most 32 MiB of UTF-8 and 400,000 rows |
XLSX report | each decompressed part at most 64 MiB, all parts together at most 128 MiB; a sheet at most 400,000 rows and 256 columns; the sheets read (List of trades and Properties) at most 8,000,000 cells together, counting the empty cells inside each row; at most 2,000,000 shared strings |
| 67,108,864 characters (64 × 1024²) |
| no size limit (a TradingView chart export is converted in memory) |
Binance fetch | 100,000 chart and magnifier bars combined |
run time |
|
There is no quota and no history window here: the range is limited only by the bars you pass, or by the 100,000-bar Binance fetch.
Bars. Your ohlcv_csv / ohlcv_csv_path when given. Otherwise BINANCE:<SYMBOL>
is fetched as Binance spot klines and BINANCE:<SYMBOL>.P as USDT-M perpetual klines,
from the public API, at most 100,000 bars. Any other symbol without bars is an error
that asks for them. Scripts declaring use_bar_magnifier = true also fetch 1-minute
bars through the last chart bar's close, counted in the same limit, when the chart is
one the harness magnifies (coarser than 1 minute, at most 1 day) and
runtime.bar_magnifier is not false. With your own
bars, optionally pass magnifier_ohlcv_csv / magnifier_ohlcv_csv_path; if the script
declares the magnifier but runs without one, the result warns that fills inside bars
may differ from TradingView's.
Instrument. The run applies the instrument's lot size and tick size, as
backtest_pine does: for BINANCE:<SYMBOL> and BINANCE:<SYMBOL>.P,
TradingView's own lot size from the shipped table (Binance's LOT_SIZE.stepSize for a symbol
TradingView does not list, TradingView's usual 0.001 for a listing newer than the table, each with a
warning) and the tick size from Binance's exchangeInfo; with your own bars and no
ticker, the sidecar next to your bars file; any other exchange needs syminfo. Without a lot
size the engine floors no order, can book sub-lot margin-call rows TradingView does not, and
the trade lists then pair worse: the result warns. The result shows what was applied under
applied_instrument (and in one text line, Instrument: ...); the same instrument goes to the
grading core as request key instrument.
XLSX report. The "List of trades" sheet is read as the CSV would be (Excel dates
become YYYY-MM-DD HH:MM). The "Properties" sheet supplies the symbol, timeframe,
date range, initial capital, order size, pyramiding, commission, slippage and the fill
options it states; you can pass the same settings explicitly, but a value that
disagrees with the export is an error naming both. Strategy inputs listed in the
export are reported, not applied: pass inputs for any you changed. TradingView does
not document this layout, so sheet and key names are matched loosely and unknown keys
are ignored.
Result. A plain-text block and the same data as JSON (structuredContent): the
tier and what it means, every check with its value and thresholds, how many trades
matched and how many are TradingView-only or PineForge-only, the first mismatches side
by side with a hint where the data shows one (window edge, a position open at the range
end, size, commission or slippage, timezone), a timezone check, the window, and the
engine, codegen and grader versions. Trades pair when they have the same direction, an
entry within one hour and an entry price within $3. If most matched trades sit at the
same non-zero offset, or another timezone matches clearly more trades, the result says
so; the tier stays the one under the timezone you gave.
Tiers, as verify_corpus.py v1.2.0 grades them. Count Δ is
|TradingView − PineForge| / max(TradingView, PineForge) trades; the p90 values are
90th percentiles of per-trade relative differences over matched trades; coverage is
matched trades over all closed TradingView trades.
tier | rule |
excellent | equal trade counts; coverage ≥ 99 % or at most 1 unmatched trade; entry price p90 < 0.01 %; exit price p90 < 0.01 % (production profile: < 0.05 %); P&L p90 < 1 % (production profile: < 100 %); where TradingView shows several entries at one time and price, PineForge has as many |
strong | coverage ≥ 95 % or at most 1 unmatched trade; count Δ < 6 %; entry price p90 < 0.1 %; exit price p90 < 0.5 %; P&L p90 < 100 % |
moderate | coverage ≥ 75 % and at least 90 % of TradingView's trades matched |
weak | at least one trade matched |
minimal | no trade matched |
The production profile applies when the script sets trail_points, trail_offset or
trail_price on strategy.exit; every other script is graded on the strict profile.
Method: https://pineforge.dev/en/methodology/.
Retention. Everything runs on your machine; market data is fetched from Binance
only when you do not pass bars. The script and the trade list, and bars that had to
be converted or fetched, go to temporary folders that are deleted when the check
ends. With npm/npx the check runs in
the engine image (docker run --network=none, the grading core and your bars mounted
read-only).
fetch_binance_ohlcv — pull market data
Writes a backtest-ready CSV (header timestamp,open,high,low,close,volume,
timestamp = open time in UNIX ms UTC) from Binance's public endpoints. No
auth required. Requests > 1000 bars are paginated
automatically. output_path follows the same rules as ohlcv_csv_path: in Docker
use an absolute path under /work; with npx it must stay inside the working
directory unless PINEFORGE_ALLOW_ANYWHERE=1.
{
"symbol": "BTCUSDT",
"interval": "15m", // 1s (spot only), 1m, 3m, 5m, 15m, 30m, 1h, 2h, 4h, 6h, 8h, 12h, 1d, 3d, 1w, 1M
"market": "spot", // default; or "usdt_perp" for USDT-margined perpetual futures
"limit": 672, // total bars: default 1000, at most 100000; > 1000 paginates
"output_path": "/work/btcusdt_15m_7d.csv"
// Optional: "start_time" / "end_time" in UNIX ms UTC.
}It also writes <output_path>.instrument.json: the symbol's instrument (TradingView's lot
size from the shipped table, Binance's for a symbol TradingView does not list, TradingView's usual
0.001 for a listing newer than the table; the tick size and
currencies from Binance's public exchangeInfo), which backtest_pine and
check_tradingview_parity pick up (see The instrument); the result's
instrument and instrument_path show it. If exchangeInfo cannot be read the CSV is still
written, instrument says why and a warnings entry says what to pass to backtest_pine.
binance_symbols — discover / validate symbols
Returns the list of symbols available on the Binance public API for OHLCV
fetching. Cached 5 min in-process. Use this to validate a symbol before
calling fetch_binance_ohlcv.
{
"market": "usdt_perp", // required: "spot" or "usdt_perp"
"query": "BTC", // case-insensitive substring match
"quote_asset": "USDT",
"base_asset": "BTC",
"status": "TRADING",
"contract_type": "PERPETUAL", // futures-only filter
"limit": 50 // default 200, at most 2000
}Coverage tools
PineForge implements a subset of Pine v6, so check before you write or port a strategy:
list_coverage_topics— every coverage topic with a status (supported,partial,unsupported,via_transpiler) and a summary, plus the legend.get_coverage_topic{ "topic": "ta" }— the fullsupported/partial/via_transpiler/unsupportedlists for one topic id (for exampleta,strategy_orders,request_security).check_pine_feature{ "feature": "ta.supertrend" }— one identifier or namespace:supported/partial/unsupported/via_transpiler/not_found, with a note that quotes the catalog entry. Plots, tables and alerts (plot,bgcolor,table,alert) are accepted and have no effect;line,boxandlabelobjects are data the strategy can read back.
Every status describes what a backtest through this server can do. The server
installs no other symbol's bars, no recorded request data and no Pine library
sources, so where the engine supports more, the entry says so: request.security
on another symbol, for example, is supported by the engine, but here a request whose
value can reach a trade stops the run.
The data is embedded in this package and stamped by the coverage_version field
that list_coverage_topics returns (engine v1.3.0 + codegen 1.3.0 (2026-10-06) in
this version); the engine's
docs/coverage.md
at that tag is the reference it was checked against.
Filesystem scope
With npx, OHLCV, output and report paths must be inside the current working
directory of the MCP server process by default. The check runs on the resolved
path: .. segments and symbolic links are resolved first, so neither can point
outside it, and a symbolic link whose target does not exist is refused. A data
file or folder linked into the working directory from outside it is therefore
refused too. Override with:
export PINEFORGE_ALLOW_ANYWHERE=1The Docker image sets PINEFORGE_ALLOW_ANYWHERE=1 itself (the container is the
sandbox), so any path is accepted there — use absolute /work/... paths.
Other env vars
var | default | purpose |
|
| npm/npx only: engine image (runtime + bundled codegen) used for transpile + backtest |
|
| Allow OHLCV / output / report paths outside cwd |
|
| Hard kill for each engine run and for |
|
| Largest report returned inline; bigger ones are written to |
|
| Time limit of one |
|
| Time limit of each request to Binance (klines page, exchangeInfo); a backtest with a |
|
| Base URLs of Binance's spot and USD-M public APIs (klines, exchangeInfo): a mirror or proxy where api.binance.com is not reachable |
| unset | Path to a lot-size table made by |
| unset | Docker: the host dir mounted at |
With Docker, pass these as -e NAME=value.
Develop
npm ci
npm run build # tsc; also writes the gitignored src/version.ts
npm testThe TradingView lot-size table (src/tv-grid.generated.json) is generated, not edited. When
TradingView's measurements are refreshed (a table of schema pineforge-tv-syminfo-receipts/v1
with TradingView's own mincontract per symbol), regenerate it with one command and commit the result:
node scripts/sync-tv-grid.mjs <path to tv-grid-table.json>It keeps only the two Binance venues: each symbol's mincontract, the venue's not_on_tv list, and per
market TradingView's usual lot size (0.001, with the share of readings that are it and their count, only
when that share is at least 0.80). It writes sorted symbols one per line, records the table's generated_utc
and sha256 and its own content hash (which covers the readings, the lists and the defaults), and refuses a
table that is not complete, has no not_on_tv list for either Binance venue (an empty one is fine), or has a value outside 1e-12..1e12. The same table gives the same bytes;
PF_TV_GRID_SOURCE=<path to the table> npm test also checks that the committed file was made from it.
To build the image, pass the pineforge-release version to build on (a tag from
its Releases page, without the v):
docker build -f docker/Dockerfile --build-arg PINEFORGE_RELEASE_VERSION=<X.Y.Z> -t pineforge-backtest-mcp .License
This server is MIT-licensed (LICENSE). The image also bundles
pineforge-engine (Apache-2.0) and the pineforge-codegen transpiler
(source-available under the PineForge Source License 1.2 from codegen 1.3.0: free for
noncommercial use and for Personal Trading; investment management other than Personal
Trading, and other Commercial Use, needs a commercial license. Trading an account that
a proprietary-trading firm or funded-trader program provides or allocates, a challenge,
evaluation or simulated account included, is not Personal Trading, and the capital in
it, real or simulated, is investment capital). Earlier releases keep the license they
were published under: codegen 1.2.0 the PineForge Source License 1.1, and 1.1.0 and
before the PolyForm Noncommercial terms they shipped with. The codegen
LICENSE
is the controlling text. See LEGAL.md.
Available Tools
12 toolsbacktest_pineBacktest a Pine strategyADestructive
Run a real, deterministic backtest of a PineScript v6 strategy — prefer this over estimating its trades or P&L by reasoning, which is unreliable for Pine (series semantics, intrabar fills, and strategy.* order logic do not reproduce from approximation). Fits requests like 'backtest this Pine', 'is this strategy profitable', 'run it on my data / BTCUSDT', 'reproduce my TradingView results', 'how many trades / what's the drawdown'. Transpile a PineScript v6 strategy and run it against an OHLCV CSV via the pineforge-release Docker image on the user's local machine. Fully local — transpile + backtest run in-container; nothing leaves the box, no API key. Optional inputs overrides input.() named values from the Pine source (keys = the second arg of input.(...) calls, e.g. 'Fast Length'). Optional overrides overrides strategy(...) header fields (initial_capital, commission_value, default_qty_value, pyramiding, slippage, default_qty_type, commission_type, process_orders_on_close). Returns the parsed JSON report (summary, trades, applied_inputs, applied_overrides, applied_runtime, elapsed_seconds). The instrument matters: pass symbol (a Binance symbol) or syminfo (your own qty_step, mintick, ...); a CSV from fetch_binance_ohlcv carries its instrument and needs neither. The lot size is TradingView's own reading for the symbol, from a measured table shipped with this server (Binance's LOT_SIZE.stepSize only for a symbol TradingView does not list; TradingView's usual 0.001 for a listing newer than the table); the tick size and currencies come from Binance's public exchangeInfo (or from the sidecar next to a CSV fetched by fetch_binance_ohlcv). syminfo goes over all of it. What was applied is in applied_runtime.syminfo; when the lot grid is unknown, or its lot size is not a TradingView reading, the result has a warnings entry. If the report is too large to return inline it is written to report_path and a compact summary (with that path) is returned instead. Use backtest_pine_grid for sweeps.
| Name | Required | Description | Default |
|---|---|---|---|
| image | No | Docker image override. Defaults to ghcr.io/pineforge-4pass/pineforge-engine:latest. | |
| inputs | No | Map of Pine input.*() names → value (string/number/bool). Sent as PINEFORGE_INPUTS env var to the runtime. | |
| market | No | 'spot' (default; a warning says when it is assumed) or 'usdt_perp'; the Binance market `symbol` is looked up in. A CSV fetched by fetch_binance_ohlcv for the same symbol keeps the market it was fetched for. | |
| source | Yes | PineScript v6 source. | |
| symbol | No | Binance symbol the CSV holds, e.g. 'BTCUSDT'. The lot size is TradingView's own reading for the symbol, from a measured table shipped with this server (Binance's LOT_SIZE.stepSize only for a symbol TradingView does not list; TradingView's usual 0.001 for a listing newer than the table); the tick size and currencies come from Binance's public exchangeInfo (or from the sidecar next to a CSV fetched by fetch_binance_ohlcv). TradingView's reading differs from Binance's step for most symbols (USDT-M BTCUSDT is 0.000001 on TradingView, 0.001 on Binance), so order quantities are floored as on TradingView; 0.001 is what TradingView reads for 90.6% of Binance spot symbols and 97.5% of USDT-M ones, and a lot size that is Binance's or that usual 0.001 comes with a warning. Without an instrument the engine can book sub-lot margin-call rows TradingView does not. A CSV written by fetch_binance_ohlcv needs neither `symbol` nor `syminfo`: the instrument is recorded next to it (<csv>.instrument.json) and used. If nothing can be resolved the run still goes ahead without a lot grid and says so in `warnings` and applied_runtime.syminfo. | |
| runtime | No | Engine runtime args (NOT strategy() header) controlling timeframe semantics and intra-bar fill simulation. input_tf / script_tf set the chart and strategy timeframes — script_tf must be coarser than or equal to input_tf or the engine rejects the run. bar_magnifier + magnifier_samples + magnifier_dist enable sub-bar price-path sampling for tighter stop / limit fills. Each field is optional and only forwarded to the engine when set. Call list_engine_params for the full catalog. | |
| syminfo | No | The instrument's own values, for a CSV of any other instrument; they win over what `symbol` or the CSV's sidecar gives. Applied to the engine: qty_step (the lot grid) and mincontract, mintick, pointvalue, type, currency, basecurrency. Without a qty_step (or mincontract) the lot grid stays off and the result carries a warning; without a mintick the engine's 0.01 applies. ticker, tickerid, timezone and session are not applied (engine defaults). | |
| overrides | No | strategy(...) header overrides. Each key maps to a single argument of the Pine `strategy()` call; only the keys you set are applied. Sent as PINEFORGE_OVERRIDES env var. Call list_engine_params for the full catalog with types and enum values. | |
| report_path | No | Where to write the full JSON report IF it is too large to return inline. Large backtests (long trade lists + equity curves) are offloaded to this file and the tool returns a compact summary + report_path instead; read the file for the complete trades/equity. Defaults to pineforge-backtest-<timestamp>.json in the working dir. | |
| ohlcv_csv_path | Yes | Absolute or cwd-relative path to OHLCV CSV with header 'timestamp,open,high,low,close,volume' (timestamp = UNIX ms UTC). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructive/openWorld/non-idempotent, and the description adds real value on top: fully local execution, no API key, Docker image override, report offloading to report_path when too large, and a warnings mechanism when the lot grid is unknown. It does not contradict openWorldHint=true since it discloses the Binance exchangeInfo lookup. Slightly short of 5 only because it never states failure/timeout or resource expectations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with purpose and preferred usage, but the instrument/lot-size paragraph is very long and largely restates what the `symbol` schema property already says verbatim, so the description carries duplication. Dense and useful, yet not every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 10-parameter tool with nested objects and no output schema, the description does enumerate the return payload (summary, trades, applied_inputs, applied_overrides, applied_runtime, elapsed_seconds) and explains the offload behavior and warnings. That covers most of what an agent needs, though the enumerated report fields are not typed and richer return detail would help.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds cross-parameter precedence semantics the schema does not: syminfo wins over symbol, symbol wins over the CSV sidecar, and inputs keys are the second argument of input.*() calls. That resolution logic is genuinely useful beyond the per-field schema text.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Run a real, deterministic backtest of a PineScript v6 strategy') and immediately distinguishes itself from its closest sibling by naming backtest_pine_grid for sweeps. An agent can separate this from transpile_pine or the grid variant without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly says to prefer this over estimating P&L by reasoning, lists concrete triggering requests ('backtest this Pine', 'is this strategy profitable', 'reproduce my TradingView results'), and routes sweeps to backtest_pine_grid. Both the when-to-use and the alternative are stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
backtest_pine_gridSweep strategy parametersADestructive
Use when the user wants to optimize, sweep, tune, or compare PineScript parameter values (e.g. 'try fast length 8/12/19', 'find the best commission/qty settings') rather than test a single configuration — for one fixed configuration use backtest_pine. Run a parameter sweep: transpile the Pine source ONCE (locally, in-container), then compile (g++) and backtest that C++ against the OHLCV CSV once per combination in the cartesian product of inputs × overrides grids. Returns a ranked list of {inputs, overrides, summary, elapsed_seconds} entries sorted by sort_by descending, plus the top entry under best. Cap: max_combinations (default 64). Takes the same symbol / market / syminfo as backtest_pine and applies the instrument to every combination (the result's instrument shows it). The lot size is TradingView's own reading for the symbol, from a measured table shipped with this server (Binance's LOT_SIZE.stepSize only for a symbol TradingView does not list; TradingView's usual 0.001 for a listing newer than the table); the tick size and currencies come from Binance's public exchangeInfo (or from the sidecar next to a CSV fetched by fetch_binance_ohlcv). syminfo goes over all of it. Set concurrency > 1 to run backtests in parallel — each docker container has its own startup overhead, so 2-4 is usually plenty.
| Name | Required | Description | Default |
|---|---|---|---|
| image | No | Docker image override. Defaults to ghcr.io/pineforge-4pass/pineforge-engine:latest. | |
| inputs | No | Grid of input.*() names → list of values to sweep. Example: {"Fast Length": [8, 12, 19], "Slow Length": [21, 26, 39]} | |
| market | No | 'spot' (default; a warning says when it is assumed) or 'usdt_perp'; the Binance market `symbol` is looked up in. A CSV fetched by fetch_binance_ohlcv for the same symbol keeps the market it was fetched for. | |
| source | Yes | PineScript v6 source. | |
| symbol | No | Binance symbol the CSV holds, e.g. 'BTCUSDT'. The lot size is TradingView's own reading for the symbol, from a measured table shipped with this server (Binance's LOT_SIZE.stepSize only for a symbol TradingView does not list; TradingView's usual 0.001 for a listing newer than the table); the tick size and currencies come from Binance's public exchangeInfo (or from the sidecar next to a CSV fetched by fetch_binance_ohlcv). TradingView's reading differs from Binance's step for most symbols (USDT-M BTCUSDT is 0.000001 on TradingView, 0.001 on Binance), so order quantities are floored as on TradingView; 0.001 is what TradingView reads for 90.6% of Binance spot symbols and 97.5% of USDT-M ones, and a lot size that is Binance's or that usual 0.001 comes with a warning. Without an instrument the engine can book sub-lot margin-call rows TradingView does not. A CSV written by fetch_binance_ohlcv needs neither `symbol` nor `syminfo`: the instrument is recorded next to it (<csv>.instrument.json) and used. If nothing can be resolved the run still goes ahead without a lot grid and says so in `warnings` and applied_runtime.syminfo. | |
| runtime | No | Engine runtime args applied to every combo in the sweep. Same shape as backtest_pine.runtime — input_tf / script_tf / bar_magnifier / magnifier_samples / magnifier_dist. Currently fixed across the grid (not swept); add to the grid axes through future versions if you need to vary them. | |
| sort_by | No | summary.* field to rank by, descending. Default net_pnl. | |
| syminfo | No | The instrument's own values, for a CSV of any other instrument; they win over what `symbol` or the CSV's sidecar gives. Applied to the engine: qty_step (the lot grid) and mincontract, mintick, pointvalue, type, currency, basecurrency. Without a qty_step (or mincontract) the lot grid stays off and the result carries a warning; without a mintick the engine's 0.01 applies. ticker, tickerid, timezone and session are not applied (engine defaults). | |
| overrides | No | Grid of strategy(...) header overrides → list of values, one axis per key. Example: {"default_qty_value": [1, 5], "commission_value": [0.04]}. Call list_engine_params for the full catalog with types and enum values. | |
| concurrency | No | Parallel backtests. Default 1. | |
| report_path | No | Where to write the full sweep JSON IF it is too large to return inline. Oversized sweeps are offloaded here and the tool returns the best + top-ranked combinations + report_path; read the file for all combinations. Defaults to pineforge-grid-<timestamp>.json in the working dir. | |
| fixed_inputs | No | Inputs applied to every combo (overridden by per-combo `inputs` keys). | |
| include_trades | No | Include the per-trade list in each result. Default false (saves tokens). | |
| ohlcv_csv_path | Yes | Path to OHLCV CSV (same format as backtest_pine). | |
| fixed_overrides | No | Overrides applied to every combo (overridden by per-combo `overrides` keys). | |
| max_combinations | No | Hard cap on combinations. Default 64. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructive/non-idempotent/open-world, so the bar is lower; the description still adds real context beyond them: transpiles Pine once, compiles C++ and backtests per combination, spawns docker containers with per-container startup overhead, applies a hard max_combinations cap, and offloads oversized sweeps to report_path. It never explains why the tool is flagged destructive (arbitrary container execution) or what failure modes look like, which keeps it off a 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the when-to-use clause and then the mechanics, so the most decision-relevant content comes first. However, the instrument/lot-size passage (TradingView vs Binance step, 90.6%/97.5% statistics) is verbose and duplicates the schema's symbol description, costing it a point on economy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 16-parameter, nested-object tool with no output schema, the description covers the return shape (ranked {inputs, overrides, summary, elapsed_seconds} plus best), the sort_by ordering, the report_path offload path, and the instrument-resolution behavior. Nothing an agent needs to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3. The description clarifies the grid semantics (cartesian product of inputs x overrides, instrument applied to every combination) and the max_combinations default, but the long lot-size/instrument paragraph is largely verbatim duplication of the `symbol` schema description rather than added meaning.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Names a specific operation (run a parameter sweep over PineScript inputs/overrides) and explicitly contrasts itself with the sibling backtest_pine for single-configuration runs. An agent can distinguish it from every other sibling without reading either schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Opens with an explicit trigger ('when the user wants to optimize, sweep, tune, or compare...'), names the exclusion ('rather than test a single configuration'), and routes to the alternative ('for one fixed configuration use backtest_pine'). Also gives operational guidance (concurrency 2-4 is usually plenty) and points to list_engine_params via the schema for the override catalog.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
binance_symbolsList Binance symbolsARead-onlyIdempotent
List/validate symbols available on the Binance public API for OHLCV fetching. Filters: query (substring of the symbol), quote_asset (e.g. 'USDT'), base_asset (e.g. 'BTC'), status (e.g. 'TRADING'), contract_type (futures only, e.g. 'PERPETUAL'). Results are cached 5 min in process. Free.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max symbols to return. Default 200. | |
| query | No | Case-insensitive substring of the symbol. | |
| market | Yes | 'spot' or 'usdt_perp'. | |
| status | No | Filter by status. 'TRADING' returns active only. | |
| base_asset | No | Filter by base asset (e.g. 'BTC'). | |
| quote_asset | No | Filter by quote asset (e.g. 'USDT'). | |
| contract_type | No | Futures only. 'PERPETUAL' for usdt_perp swaps. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/non-destructive, so the safety profile is covered. The description adds genuinely useful operational context: results are cached 5 minutes in-process and the call is free, which tells the agent about latency and rate-limit risk that annotations do not convey.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with purpose, then filters, then caching/cost note in a tight two-sentence block. The filter enumeration is somewhat duplicative of the schema but stays short and readable.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description should say what a result row contains (symbol, base/quote asset, status, market) and it does not; it also omits the 'limit' parameter and its default of 200. Caching and cost are covered, but the return shape is left unspecified.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so every filter (query, quote_asset, base_asset, status, contract_type) is already documented in the schema, and the description largely repeats those examples. It adds no new semantics such as matching rules beyond substring, or the meaning of the required 'market' param relative to 'contract_type'.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('List/validate symbols') plus the scope ('Binance public API') and the downstream purpose ('for OHLCV fetching'), which ties it to the sibling fetch_binance_ohlcv. It does not explicitly name a sibling it excludes, but the intent is unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The phrase 'for OHLCV fetching' implies this is the lookup step before fetching candles, and 'contract_type (futures only)' hints at a market precondition. There is no explicit when-to-use statement, no when-not, and no alternative tool named.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
check_engine_imageCheck the engine imageAIdempotent
Check whether the local pineforge-release Docker image is up to date with the registry. Compares per-platform manifest digests via docker manifest inspect --verbose (no image layers downloaded). Returns up_to_date + recommend_pull. With auto_pull=true, runs docker pull in the same call when the local image is stale or missing. Note: this is independent of the MCP server's own version (@pineforge/backtest-mcp); the MCP version and the engine image version evolve separately.
| Name | Required | Description | Default |
|---|---|---|---|
| image | No | Image to check. Defaults to ghcr.io/pineforge-4pass/pineforge-engine:latest. | |
| auto_pull | No | If true and the image is stale or missing, run `docker pull` in the same call. Default false (report only). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare the safety profile (openWorld, idempotent, non-readOnly), so the bar is lower, and the description still adds real substance: the exact lookup mechanism, that no layers are downloaded, and that auto_pull triggers docker pull inline. It doesn't cover auth requirements or rate limits, but the mutation semantics behind readOnlyHint=false are explained.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core purpose and the method, then escalation behavior, then the disambiguating note about MCP version independence. Dense and mostly waste-free, though the version note lengthens it without being strictly required to invoke the tool.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description compensates by naming the return fields (up_to_date + recommend_pull) and explaining the side effects of auto_pull. The independence note removes a genuine ambiguity about what 'up to date' covers, so an agent has everything needed to call it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and both parameters are already documented in the schema, including the image default and auto_pull semantics. The description largely restates that behavior rather than adding new syntax or format guidance, so baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ('check whether the local pineforge-release Docker image is up to date with the registry') and explicitly scopes the comparison method (per-platform manifest digests, no layer downloads). An agent can distinguish this from pull_engine_image and from MCP-version concerns without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives clear context for when to call it (staleness check) and describes the auto_pull=true escalation path. It does not explicitly name pull_engine_image as the alternative for a pure pull, so the routing guidance is implicit rather than stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
check_pine_featureCheck a Pine featureARead-onlyIdempotent
Answer "does PineForge support X?" for a specific Pine v6 identifier or namespace (e.g. 'ta.supertrend', 'alert', 'array.new', 'request.financial'). Use it (a) BEFORE relying on any function you are unsure about while writing a strategy, and (b) to DIAGNOSE a backtest that compiled but behaved wrong or empty — plots, tables and alerts (plot, bgcolor, table, alert) are accepted and produce NO effect, while line, box and label objects are data the strategy can read back. Resolves by exact feature match, then longest namespace prefix, then alias, returning {query, status, topic, note} where status is supported / partial / unsupported / via_transpiler / not_found (via_transpiler = works end-to-end; unsupported = refused, not compiling, stopping the run, or no effect) and the note quotes the catalog entry, which says what THIS server can and cannot do (e.g. request.security on another symbol). Local, free, no engine run.
| Name | Required | Description | Default |
|---|---|---|---|
| feature | Yes | Pine identifier or namespace to look up, e.g. 'ta.supertrend', 'alert', 'array.new', 'strategy.entry', 'request.dividends'. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover safety (readOnly, idempotent, non-destructive). The description adds substantial behavioral detail beyond that: the resolution order (exact, longest prefix, alias), the return shape and status vocabulary, and the semantics of via_transpiler vs unsupported. It doesn't discuss rate limits or latency, but for a local lookup those are moot.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the core question, then the two usage scenarios, then matching/return details. Dense but every clause carries content. Slightly long, with the long parenthetical enumerations making it a touch harder to scan.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-param, no-output-schema lookup tool, the description covers purpose, timing, matching algorithm, and full status vocabulary including the via_transpiler/unsupported boundary. An agent has everything needed to call it and interpret results.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the schema already documents the single 'feature' parameter with its own examples. The description restates the input domain (identifier or namespace) and supplies the resolution matching rules, which adds meaning beyond the schema. Baseline is 3, raised to 4 for the matching semantics.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a precise question it answers ('does PineForge support X?') for a specific input type (Pine v6 identifier or namespace) with concrete examples. Clearly distinguishable from siblings like transpile_pine or check_tradingview_parity.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives two explicit when-to-use scenarios: (a) before relying on an unsure function, (b) to diagnose a compiled-but-wrong backtest. Also implicitly routes away from engine-running siblings by noting it runs locally with no engine run.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
check_tradingview_parityCheck TradingView parityARead-onlyIdempotent
Check how closely PineForge reproduces a TradingView backtest, trade by trade. Give the Pine v6 script and TradingView's own Strategy Tester export: the "List of trades" CSV, or the XLSX report as base64. PineForge runs the script on the same market and window and grades the two trade lists with the grader behind its published parity figures (pineforge-engine v1.2.0 scripts/verify_corpus.py, the same file in v1.3.0). Returns the tier (excellent, strong, moderate, weak, minimal), each check with its value and thresholds, matched and unmatched trade counts, the first mismatches side by side with hints, and a timezone check. Bars: pass ohlcv_csv or ohlcv_csv_path for any market; without them, BINANCE: (spot) and BINANCE:.P (USDT-M perpetual) bars are fetched from Binance's public API (at most 100,000 bars); any other symbol needs your bars. No quota and no history window beyond that. Scripts declaring use_bar_magnifier=true also fetch 1-minute bars through the last chart bar's close, counted in that limit, on charts the harness magnifies (coarser than 1 minute, at most 1 day) unless runtime.bar_magnifier is false. With your own bars, optionally pass magnifier_ohlcv_csv or magnifier_ohlcv_csv_path; a script that declares the magnifier but runs without one gets a warning that fills inside bars may differ from TradingView's. Settings the XLSX Properties sheet states are used; an explicit input that disagrees with one is an error. The run applies the instrument's lot size and tick size as backtest_pine does, for BINANCE: and BINANCE:.P. The lot size is TradingView's own reading for the symbol, from a measured table shipped with this server (Binance's LOT_SIZE.stepSize only for a symbol TradingView does not list; TradingView's usual 0.001 for a listing newer than the table); the tick size and currencies come from Binance's public exchangeInfo (or from the sidecar next to a CSV fetched by fetch_binance_ohlcv). syminfo gives your own values and goes over all of it. The result's applied_instrument shows what was applied, and warnings says when no lot size could be found or its lot size is not a TradingView reading. The run and the grading use the pineforge-release Docker image (no network inside the container).
| Name | Required | Description | Default |
|---|---|---|---|
| pine | Yes | The Pine v6 strategy source TradingView ran (at most 262,144 bytes of UTF-8). | |
| inputs | No | Pine input overrides (TradingView's Inputs tab), as in backtest_pine. | |
| symbol | No | TradingView ticker the backtest ran on, e.g. 'BINANCE:ETHUSDT.P'. Required unless the XLSX states it or you pass bars. | |
| runtime | No | Engine runtime args of list_engine_params (input_tf, script_tf, bar_magnifier, magnifier_samples, magnifier_dist). | |
| syminfo | No | The instrument's own values (qty_step, mintick, pointvalue, ...), as in backtest_pine: they win over what the symbol resolves, and are the whole instrument for a market other than Binance. Without a lot size the run floors nothing and the result warns. | |
| ohlcv_csv | No | Your bars as CSV text: header timestamp,open,high,low,close,volume (epoch ms), or TradingView's chart export time,open,high,low,close,Volume (epoch seconds or ISO 8601). At most 67,108,864 characters; use ohlcv_csv_path for more. | |
| range_end | No | ISO 8601 end of TradingView's range. Default: the export's last row (the result says so). | |
| timeframe | No | TradingView resolution of the chart: '1', '5', '15', '60', '240', '1D', '1W', ... Required unless the XLSX states it. | |
| range_start | No | ISO 8601 date or datetime of the first bar TradingView computed (UTC unless it has an offset). Required unless the XLSX states it. | |
| chart_timezone | No | IANA timezone TradingView printed the trade times in (its chart timezone), e.g. 'Asia/Taipei', 'UTC', 'America/New_York'. Required unless the export states it. | |
| max_mismatches | No | How many mismatching trades to list (default 10, at most 50); the counts are always complete. | |
| ohlcv_csv_path | No | Path to your bars CSV (same formats as ohlcv_csv, no size limit; same path rules as backtest_pine's ohlcv_csv_path, checked after resolving '..' and symlinks). | |
| strategy_overrides | No | TradingView's Properties tab: the 9 strategy() knobs of list_engine_params. | |
| tradingview_trades | Yes | TradingView's Strategy Tester export: the "List of trades" CSV text, or the XLSX report file base64-encoded (it starts with UEsDB). The CSV needs the columns Trade number, Type, Date and time and a Price column, as TradingView exports them. Limits: 33,554,432 characters as passed; the trade list graded at most 32 MiB of UTF-8 and 400,000 rows; XLSX parts at most 64 MiB each and 128 MiB together decompressed, sheets at most 400,000 rows and 256 columns, the sheets read at most 8,000,000 cells together, at most 2,000,000 shared strings. | |
| magnifier_ohlcv_csv | No | Optional 1-minute bars for a declared bar magnifier, in the same formats as ohlcv_csv and with the same 67,108,864-character limit. Cover the first chart bar's open through the last chart bar's close. | |
| magnifier_ohlcv_csv_path | No | Path to optional 1-minute magnifier bars (same formats and path rules as ohlcv_csv_path, no size limit). Pass this or magnifier_ohlcv_csv, not both. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare the safety profile (readOnly, idempotent, non-destructive, openWorld); the description adds substantial behavior on top: bar-fetch limits (100,000 bars), no quota/history window, the magnifier rule counting 1-minute bars in that limit, an error when an explicit input disagrees with an XLSX Properties value, warning semantics for a missing lot size, lot/tick-size resolution order, and that the run uses the pineforge-release Docker image with no network. This is far beyond what the annotations convey.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose and required inputs are front-loaded, which is good, but the body devolves into a single dense paragraph of semicolon-joined clauses covering lot size, tick size, table fallbacks, and magnifier edge cases. Much of this is edge-case detail that is hard to parse and dilutes the call-critical guidance.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 16-parameter tool with no output schema, the description still enumerates the return payload (tier, each check with value/thresholds, matched/unmatched counts, first mismatches with hints, timezone check, applied_instrument, warnings), so an agent knows what comes back. Nothing essential for a correct invocation is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds cross-parameter semantics the schema does not: syminfo overrides everything ('syminfo gives your own values and goes over all of it'), magnifier bars are counted in the same limit, and a magnifier-declaring script run without magnifier bars warns. It also clarifies the XLSX-vs-explicit-input conflict.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The first sentence gives a specific verb+resource+scope: 'Check how closely PineForge reproduces a TradingView backtest, trade by trade.' This is clearly distinct from backtest_pine (which runs a backtest) and transpile_pine (which converts code), so the agent can route without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It states the required shape of the request (a Pine v6 script plus a TradingView 'List of trades' CSV or base64 XLSX) and routes the bar-source choice ('pass ohlcv_csv or ohlcv_csv_path for any market; without them, BINANCE:<SYMBOL> ...'). However it never explicitly contrasts itself with aligned siblings like backtest_pine or states when not to use it, so it stops short of full when/when-not guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
fetch_binance_ohlcvFetch Binance OHLCV to a fileADestructive
Fetch OHLCV candles from Binance public API and write a backtest-ready CSV (header: timestamp,open,high,low,close,volume; timestamp = open time in UNIX ms UTC). Supports spot and usdt_perp (USDT-margined perpetual futures). Requests larger than 1000 bars are paginated automatically. Also records the symbol's instrument in .instrument.json (lot size: TradingView's own reading from a measured table shipped with this server, Binance's LOT_SIZE.stepSize for a symbol TradingView does not list, TradingView's usual 0.001 for a listing newer than the table; tick size and currencies from Binance's public exchangeInfo), which backtest_pine and backtest_pine_grid pick up; the result's instrument shows what was recorded. The output path must live inside the MCP cwd unless PINEFORGE_ALLOW_ANYWHERE=1.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Total bars to fetch. Default 1000. Paginated above 1000. | |
| market | No | 'spot' (default) or 'usdt_perp'. | |
| symbol | Yes | Binance symbol, e.g. 'BTCUSDT'. Use binance_symbols to validate. | |
| end_time | No | UNIX ms UTC. Defaults to now. | |
| interval | Yes | Kline interval. Spot supports 1s + 1m..1M; usdt_perp supports 1m..1M (no 1s). | |
| start_time | No | UNIX ms UTC. If unset, derived from end_time/now and limit. | |
| output_path | Yes | Path to write the CSV (will create parent dirs as needed). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare a destructive, non-idempotent write, and the description richly corroborates that: it writes a CSV, creates a side-car <output_path>.instrument.json, paginates requests above 1000 bars, and enforces that output must live inside the MCP cwd unless PINEFORGE_ALLOW_ANYWHERE=1. These are side-effect and constraint details the annotations cannot convey.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
It is dense and information-rich rather than padded, front-loading the core action and output format before the instrument-file and path details. The instrument.json sentence is long and parenthetical, which slightly hurts scanability, but every sentence carries real information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description carries the return burden and does note that the result's `instrument` field reports what was recorded. Combined with the CSV format and path constraints, it is nearly complete, though it could say more about error behavior on invalid symbols or ranges.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline is 3, but the description adds real meaning beyond the schema: the CSV header layout, timestamp = open time in UNIX ms UTC, pagination above 1000 bars, and the cwd constraint on output_path. It stops short of explaining how start_time and end_time interact beyond what the schema already says.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb (fetch) and resource (OHLCV candles from Binance) and its output (a backtest-ready CSV), and it clearly distinguishes itself from siblings like binance_symbols (used to validate) and backtest_pine/backtest_pine_grid (which consume the recorded instrument). An agent can route to it without opening another schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage context is clear: it feeds backtesting tools, and binance_symbols is named in the schema for symbol validation. It doesn't explicitly state when-not to use it or name alternatives to fetch data, so it falls just short of full routing guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_coverage_topicGet a Pine coverage topicARead-onlyIdempotent
Returns the full detail plus the exact supported[], partial[], via_transpiler[] and unsupported[] feature lists for ONE coverage topic id (ids from list_coverage_topics, e.g. 'ta', 'strategy_orders', 'request_security', 'drawing_plotting_alerts'). Use when you are about to work in a feature area and need to know precisely which functions there are implemented vs skipped — e.g. before using request.security, the ta.* library, or strategy risk knobs. Unknown ids return an error marker listing the valid ids. Local, free, no engine run.
| Name | Required | Description | Default |
|---|---|---|---|
| topic | Yes | Coverage topic id from list_coverage_topics (e.g. 'ta', 'strategy_orders', 'request_security', 'drawing_plotting_alerts'). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/non-destructive/no-open-world, so safety is covered. The description adds behavior beyond that: unknown ids return an error marker listing valid ids, and the operation is local, free, and runs no engine. It does not describe result size or pagination, but for a small lookup that is a minor gap.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the return shape, then usage, then edge-case behavior, with no filler sentences. It is a dense single paragraph rather than being broken up, but every clause carries information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description compensates by spelling out exactly which arrays come back (supported, partial, via_transpiler, unsupported) and how errors surface. Combined with usage triggers and cost/runtime notes, an agent has everything needed to call and interpret it.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and the single parameter already carries examples, so the schema does the heavy lifting. The description reinforces the parameter with its own example ids, which is useful but largely duplicative of the schema — baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (returns) and resource (full detail plus supported/partial/via_transpiler/unsupported feature lists for ONE coverage topic id). It explicitly scopes to a single topic and names the sibling list_coverage_topics as the source of ids, so it is distinguishable from siblings without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives an explicit trigger ('when you are about to work in a feature area and need to know precisely which functions are implemented vs skipped') with concrete examples (request.security, ta.*, strategy risk knobs). It also routes the agent to list_coverage_topics for obtaining valid ids, covering the alternative path.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_coverage_topicsList Pine coverage topicsARead-onlyIdempotent
START HERE before writing, porting, or backtesting a Pine v6 strategy on PineForge. PineForge implements a SUBSET of Pine v6, so checking coverage first avoids a strategy that compiles but silently misbehaves vs TradingView. Lists every coverage topic with a one-line status (supported / partial / unsupported / via_transpiler) and summary, plus the legend (note: via_transpiler still works end-to-end; unsupported means refused, not compiling, stopping the run where its value is read, or accepted with no effect) and the coverage version. Statuses describe what a backtest on THIS server can do. Cheap, free, local — no engine run, no I/O. Then drill in with get_coverage_topic for one area's per-feature lists, or check_pine_feature to look up a single identifier.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover safety (readOnly, idempotent, non-destructive, closed-world), but the description adds operational context they cannot: cheap, free, local, no engine run, no I/O. It also defines the status vocabulary's real meaning (via_transpiler works end-to-end; unsupported means refused/no-effect/halting at value read), which is behavior an agent cannot infer from the schema.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The action-relevant instruction is front-loaded and every sentence carries routing or caveat value. The long inline parenthetical explaining the legend and status semantics is somewhat heavy for a description, since the tool itself returns that legend, but it prevents misinterpretation of the results.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description steps in and describes the return payload precisely: topics with status plus summary, the legend, and the coverage version. Combined with the routing to two sibling tools, an agent has everything needed to call and interpret this tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so there is nothing for the description to disambiguate — baseline 4 applies. No parameter-level guidance is needed or missing.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Lists every coverage topic with a one-line status... plus the legend... and the coverage version') and frames the server's constraint (PineForge implements a SUBSET of Pine v6). An agent can immediately distinguish this from get_coverage_topic (per-area drill-down) and check_pine_feature (single identifier lookup).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Opens with an explicit trigger ('START HERE before writing, porting, or backtesting a Pine v6 strategy') and gives the rationale (avoids a strategy that compiles but silently misbehaves). It then names both alternative siblings and the exact condition that selects each one.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_engine_paramsList engine parametersARead-onlyIdempotent
Returns the full catalog of engine knobs accepted by backtest_pine / backtest_pine_grid in two groups: strategy_overrides (the 9 strategy(...) header fields the runtime reads via PINEFORGE_OVERRIDES — initial_capital, pyramiding, slippage, commission_value, commission_type, default_qty_value, default_qty_type, process_orders_on_close, close_entries_rule) and runtime_args (input_tf, script_tf, bar_magnifier, magnifier_samples, magnifier_dist — args to run_backtest_full, NOT part of the strategy() header). Each entry is {key, type, enum?, description}. Does not run the engine. Use this to discover what knobs the engine exposes before issuing a backtest.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/non-destructive, and the description adds the non-obvious fact that it 'Does not run the engine' plus the return shape ({key, type, enum?, description}). That is meaningful context beyond the safety profile, though it doesn't mention anything about staleness or caching of the catalog.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core purpose and the practical 'Does not run the engine' caveat. The middle sentence enumerates all 9 strategy_overrides and 5 runtime_args keys, which is dense but arguably earns its place as the tool's actual payload; it is the one spot that reads as bulky.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description fully compensates by naming both return groups, distinguishing strategy() header fields from run_backtest_full args, and giving the per-entry shape. An agent has everything needed to call and consume this tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so there is nothing for the description to disambiguate and the baseline of 4 applies. The description instead spends its words on return-value semantics, which is appropriate for a no-arg discovery call.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Returns the full catalog of engine knobs') and scopes it precisely to the consumers, backtest_pine / backtest_pine_grid. An agent can distinguish this from siblings like check_pine_feature or transpile_pine without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly says 'Use this to discover what knobs the engine exposes before issuing a backtest,' which names the moment of use and the downstream tool. It lacks an explicit when-not clause (e.g., when the caller already knows the knob names), so it stops short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
pull_engine_imagePull the engine imageAIdempotent
Run docker pull for the pineforge-release runtime image on the user's machine. Useful before the first backtest_pine call.
| Name | Required | Description | Default |
|---|---|---|---|
| image | No | Image to pull. Defaults to ghcr.io/pineforge-4pass/pineforge-engine:latest. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare openWorldHint=true and idempotentHint=true, covering network access and repeat-safety. The description adds real context by naming the mechanism (`docker pull`) and targeting a local machine image, but says nothing about Docker prerequisites, download size/time, or failure behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences with no filler; the action is front-loaded and the usage hint follows immediately. Nothing extraneous.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a one-parameter, no-output-schema tool with annotations covering safety and network scope, the description provides enough to invoke it correctly. Minor gaps: no mention of Docker being required or what a failed pull looks like.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the single `image` parameter already documents its default in the schema. The description adds no syntax, format, or override guidance beyond that, so the baseline of 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a concrete verb and resource: runs `docker pull` for the pineforge-release runtime image. An agent can distinguish this from sibling `check_engine_image` (which presumably only verifies) without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
"Useful before the first backtest_pine call" gives a timing hint, but it is hedged ("useful") and never names the alternative `check_engine_image` or states when the pull is unnecessary. Usage is implied rather than prescribed.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
transpile_pineTranspile Pine to C++AIdempotent
Transpile PineScript v6 source to a C++ translation unit locally, using the pineforge-codegen transpiler bundled in the pineforge-release Docker image. No API key, no network — source never leaves the machine. Returns the generated C++ as text. Use backtest_pine if you also want to run the strategy.
| Name | Required | Description | Default |
|---|---|---|---|
| image | No | Docker image override. Defaults to ghcr.io/pineforge-4pass/pineforge-engine:latest. | |
| source | Yes | PineScript v6 source (must include //@version=6). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Adds real behavioral context beyond the annotations: no API key, no network, source never leaves the machine, and the return is text C++. That is more than the hints convey. However the 'no network' claim sits uneasily against openWorldHint=true, and readOnlyHint=false is not explained for what reads as a pure code-generation operation, leaving some ambiguity.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences, front-loaded with what it does, then the local/no-key guarantees, then the routing to the alternative. No filler and nothing buried.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema exists, and the description compensates by stating the return is the generated C++ as text. Combined with the version requirement (v6) and the Docker dependency, an agent has what it needs to invoke it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so both parameters (source, image) are already documented in the schema, and the description adds no format or syntax detail beyond it. Baseline 3 applies when the schema does the heavy lifting.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (transpile) and resource (PineScript v6 source) and names the concrete output (a C++ translation unit). It explicitly distinguishes itself from the sibling backtest_pine, so an agent can pick the right tool without opening either schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly routes the agent to backtest_pine when it also wants to run the strategy, which is clear alternative-selection guidance. It stops short of stating prerequisites that its own siblings imply, such as needing the engine image present (check_engine_image/pull_engine_image), so it is not a full 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
3 tool updates
v0.9.36- Changed
backtest_pine3 fields changed- added
Input schema / properties / marketAdded value: +{ + "description": "'spot' (default; a warning says when it is assumed) or 'usdt_perp'; the Binance market `symbol` is looked up in. A CSV fetched by fetch_binance_ohlcv for the same symbol keeps the market it was fetched for.", + "enum": [ + "spot", + "usdt_perp" + ], + "type": "string" +} - added
Input schema / properties / symbolAdded value: +{ + "description": "Binance symbol the CSV holds, e.g. 'BTCUSDT'. The lot size is TradingView's own reading for the symbol, from a measured table shipped with this server (Binance's LOT_SIZE.stepSize only for a symbol TradingView does not list; TradingView's usual 0.001 for a listing newer than the table); the tick size and currencies come from Binance's public exchangeInfo (or from the sidecar next to a CSV fetched by fetch_binance_ohlcv). TradingView's reading differs from Binance's step for most symbols (USDT-M BTCUSDT is 0.000001 on TradingView, 0.001 on Binance), so order quantities are floored as on TradingView; 0.001 is what TradingView reads for 90.6% of Binance spot symbols and 97.5% of USDT-M ones, and a lot size that is Binance's or that usual 0.001 comes with a warning. Without an instrument the engine can book sub-lot margin-call rows TradingView does not. A CSV written by fetch_binance_ohlcv needs neither `symbol` nor `syminfo`: the instrument is recorded next to it (<csv>.instrument.json) and used. If nothing can be resolved the run still goes ahead without a lot grid and says so in `warnings` and applied_runtime.syminfo.", + "maxLength": 40, + "minLength": 2, + "type": "string" +} - added
Input schema / properties / syminfoAdded value: +{ + "additionalProperties": false, + "description": "The instrument's own values, for a CSV of any other instrument; they win over what `symbol` or the CSV's sidecar gives. Applied to the engine: qty_step (the lot grid) and mincontract, mintick, pointvalue, type, currency, basecurrency. Without a qty_step (or mincontract) the lot grid stays off and the result carries a warning; without a mintick the engine's 0.01 applies. ticker, tickerid, timezone and session are not applied (engine defaults).", + "properties": { + "basecurrency": { + "description": "syminfo.basecurrency, e.g. 'BTC'.", + "maxLength": 64, + "minLength": 1, + "pattern": "^[\\x20-\\x7e]+$", + "type": "string" + }, + "currency": { + "description": "syminfo.currency, e.g. 'USDT'.", + "maxLength": 64, + "minLength": 1, + "pattern": "^[\\x20-\\x7e]+$", + "type": "string" + }, + "mincontract": { + "description": "syminfo.mincontract: the smallest tradable quantity step (TradingView reports the lot size here). Defaults to qty_step when only that is given.", + "maximum": 1000000000000, + "minimum": 1e-12, + "type": "number" + }, + "mintick": { + "description": "Price tick size (syminfo.mintick). Fills round to it. Engine default 0.01.", + "maximum": 1000000000000, + "minimum": 1e-12, + "type": "number" + }, + "pointvalue": { + "description": "Money per price point per contract (syminfo.pointvalue). Engine default 1.", + "maximum": 1000000000000, + "minimum": 1e-12, + "type": "number" + }, + "qty_step": { + "description": "Lot size in base units: order quantities are floored to a multiple of it. This is what removes sub-lot rows. Defaults to mincontract when only that is given.", + "maximum": 1000000000000, + "minimum": 1e-12, + "type": "number" + }, + "type": { + "description": "syminfo.type, e.g. 'crypto', 'forex', 'stock'.", + "maxLength": 64, + "minLength": 1, + "pattern": "^[\\x20-\\x7e]+$", + "type": "string" + } + }, + "type": "object" +}
- Changed
backtest_pine_grid3 fields changed- added
Input schema / properties / marketAdded value: +{ + "description": "'spot' (default; a warning says when it is assumed) or 'usdt_perp'; the Binance market `symbol` is looked up in. A CSV fetched by fetch_binance_ohlcv for the same symbol keeps the market it was fetched for.", + "enum": [ + "spot", + "usdt_perp" + ], + "type": "string" +} - added
Input schema / properties / symbolAdded value: +{ + "description": "Binance symbol the CSV holds, e.g. 'BTCUSDT'. The lot size is TradingView's own reading for the symbol, from a measured table shipped with this server (Binance's LOT_SIZE.stepSize only for a symbol TradingView does not list; TradingView's usual 0.001 for a listing newer than the table); the tick size and currencies come from Binance's public exchangeInfo (or from the sidecar next to a CSV fetched by fetch_binance_ohlcv). TradingView's reading differs from Binance's step for most symbols (USDT-M BTCUSDT is 0.000001 on TradingView, 0.001 on Binance), so order quantities are floored as on TradingView; 0.001 is what TradingView reads for 90.6% of Binance spot symbols and 97.5% of USDT-M ones, and a lot size that is Binance's or that usual 0.001 comes with a warning. Without an instrument the engine can book sub-lot margin-call rows TradingView does not. A CSV written by fetch_binance_ohlcv needs neither `symbol` nor `syminfo`: the instrument is recorded next to it (<csv>.instrument.json) and used. If nothing can be resolved the run still goes ahead without a lot grid and says so in `warnings` and applied_runtime.syminfo.", + "maxLength": 40, + "minLength": 2, + "type": "string" +} - added
Input schema / properties / syminfoAdded value: +{ + "additionalProperties": false, + "description": "The instrument's own values, for a CSV of any other instrument; they win over what `symbol` or the CSV's sidecar gives. Applied to the engine: qty_step (the lot grid) and mincontract, mintick, pointvalue, type, currency, basecurrency. Without a qty_step (or mincontract) the lot grid stays off and the result carries a warning; without a mintick the engine's 0.01 applies. ticker, tickerid, timezone and session are not applied (engine defaults).", + "properties": { + "basecurrency": { + "description": "syminfo.basecurrency, e.g. 'BTC'.", + "maxLength": 64, + "minLength": 1, + "pattern": "^[\\x20-\\x7e]+$", + "type": "string" + }, + "currency": { + "description": "syminfo.currency, e.g. 'USDT'.", + "maxLength": 64, + "minLength": 1, + "pattern": "^[\\x20-\\x7e]+$", + "type": "string" + }, + "mincontract": { + "description": "syminfo.mincontract: the smallest tradable quantity step (TradingView reports the lot size here). Defaults to qty_step when only that is given.", + "maximum": 1000000000000, + "minimum": 1e-12, + "type": "number" + }, + "mintick": { + "description": "Price tick size (syminfo.mintick). Fills round to it. Engine default 0.01.", + "maximum": 1000000000000, + "minimum": 1e-12, + "type": "number" + }, + "pointvalue": { + "description": "Money per price point per contract (syminfo.pointvalue). Engine default 1.", + "maximum": 1000000000000, + "minimum": 1e-12, + "type": "number" + }, + "qty_step": { + "description": "Lot size in base units: order quantities are floored to a multiple of it. This is what removes sub-lot rows. Defaults to mincontract when only that is given.", + "maximum": 1000000000000, + "minimum": 1e-12, + "type": "number" + }, + "type": { + "description": "syminfo.type, e.g. 'crypto', 'forex', 'stock'.", + "maxLength": 64, + "minLength": 1, + "pattern": "^[\\x20-\\x7e]+$", + "type": "string" + } + }, + "type": "object" +}
- Added
check_tradingview_parity
2 tool updates
v0.9.30- Changed
backtest_pine2 fields changed- removed
Input schema / properties / inputs / additionalProperties / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "number" - }, - { - "type": "boolean" - } -] - added
Input schema / properties / inputs / additionalProperties / typeAdded value: +[ + "string", + "number", + "boolean" +]
- Changed
backtest_pine_grid4 fields changed- removed
Input schema / properties / fixed_inputs / additionalProperties / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "number" - }, - { - "type": "boolean" - } -] - added
Input schema / properties / fixed_inputs / additionalProperties / typeAdded value: +[ + "string", + "number", + "boolean" +] - removed
Input schema / properties / inputs / additionalProperties / items / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "number" - }, - { - "type": "boolean" - } -] - added
Input schema / properties / inputs / additionalProperties / items / typeAdded value: +[ + "string", + "number", + "boolean" +]
5 tool updates
v0.9.0- Changed
backtest_pine1 field changed- added
Input schema / properties / report_pathAdded value: +{ + "description": "Where to write the full JSON report IF it is too large to return inline. Large backtests (long trade lists + equity curves) are offloaded to this file and the tool returns a compact summary + report_path instead; read the file for the complete trades/equity. Defaults to pineforge-backtest-<timestamp>.json in the working dir.", + "type": "string" +}
- Changed
backtest_pine_grid1 field changed- added
Input schema / properties / report_pathAdded value: +{ + "description": "Where to write the full sweep JSON IF it is too large to return inline. Oversized sweeps are offloaded here and the tool returns the best + top-ranked combinations + report_path; read the file for all combinations. Defaults to pineforge-grid-<timestamp>.json in the working dir.", + "type": "string" +}
- Added
check_pine_feature - Added
get_coverage_topic - Added
list_coverage_topics
8 tool updates
v0.8.4- First observed
backtest_pine - First observed
backtest_pine_grid - First observed
binance_symbols - First observed
check_engine_image - First observed
fetch_binance_ohlcv - First observed
list_engine_params - First observed
pull_engine_image - First observed
transpile_pine
TDQS
Scored across 12 tools
Most tools have clearly distinct purposes: transpile vs. backtest vs. sweep vs. parity-check are unambiguous, and backtest_pine is explicitly contrasted with backtest_pine_grid. The coverage trio (list_coverage_topics, get_coverage_topic, check_pine_feature) is separated by granularity but still overlaps in the 'does PineForge support X?' space, and pull_engine_image/check_engine_image overlap slightly since check can auto_pull.
All names are snake_case, mostly following a verb_noun pattern (check_tradingview_parity, transpile_pine, backtest_pine, fetch_binance_ohlcv, list_coverage_topics, get_coverage_topic, pull_engine_image). The only real deviation is binance_symbols, which is noun-only with no verb, but grouping is otherwise predictable.
12 tools is well-scoped for a Pine transpile/backtest/parity server, with each tool earning its place across transpilation, execution, sweeps, data fetching, coverage discovery, and engine management. No redundant or filler tools.
The surface covers the full workflow: find/validate symbols, fetch data, check coverage, transpile, backtest, sweep, and verify TradingView parity, plus engine image management. Minor gaps exist (no way to cancel/abort a long backtest or directly ingest TradingView exports beyond parity-check inputs), but core lifecycle is complete.
Maintenance
Related MCP Connectors
Evidence-gated quant research for TradingView Pine Script strategies, run from your AI client.
AI-powered Pine Script v6 strategy generator built for prop-firm futures traders.
Static analysis for Pine Script v6 strategies — catches backtest-vs-live divergence traps.
Backtest crypto trading strategies by describing them in plain English. Your AI writes the rules; a deterministic engine runs them on real Binance data (perpetual futures and spot) with fees, slippage and funding counted. Optimize with a held-out check, forward test on live data, build portfolios, and get a full report for every result.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceexposes a remote MCP endpoint so agents can: run strategy backtests by symbol/timeframe/date range, pass strategy inputs programmatically, receive structured backtest results (trades, win rate, profit, drawdown), keep long-running runs observable via progress notifications, support Binance Futures tickers only, enforce a maximum of 1440 candles per backtest, apply a rate limit of 3 backtests per6-
- AlicenseNot gradedqualityDmaintenanceLocal-first backtesting engine with built-in overfitting detection (PBO, deflated Sharpe, bootstrap CI, walk-forward) and a native MCP server for AI agents to validate trading strategies.4Apache 2.0
- AlicenseAqualityCmaintenanceA type-safe MCP server that enables AI agents to control TradingView Desktop via Chrome DevTools Protocol, allowing chart state reading, symbol/timeframe changes, and OHLCV data fetching.1194 npm1MIT
- AlicenseBqualityAmaintenanceAI-powered trading toolkit with backtesting, live sentiment, Yahoo Finance data, and 30+ technical analysis tools, integrated as an MCP server for Claude and other AI clients.374,919MIT