|
PineForge v0.10.12
Deterministic PineScript v6 backtest runtime — C ABI reference
|
pf_report_t is the single output of every backtest. This page maps each field to its meaning and units.
metrics / equity_curve fields were appended in ABI version 2 (PF_ABI_VERSION). pf_report_t is caller-allocated, so consumers must check pf_abi_version() == 2 before running — a .so with no pf_abi_version symbol is ABI v1 and predates these fields.| Field | Type | Meaning |
|---|---|---|
total_trades | int | Closed-trade count. Always equal to trades_len. |
trades | pf_trade_t* | Heap array, ordered by exit time ascending. |
trades_len | int | Length of trades. |
net_profit | double | Sum of all closed-trade PnL in account currency. |
max_runup and max_drawdown are the per-unit Maximum Favorable Excursion (MFE) and Maximum Adverse Excursion (MAE) for the trade — multiply by qty to recover the dollar values.
| Field | Meaning |
|---|---|
input_bars_processed | Source-feed bars consumed. |
script_bars_processed | Script-timeframe bars evaluated. Differs from input_bars_processed when needs_aggregation == 1. |
| Field | Meaning |
|---|---|
input_tf_seconds | Detected (or configured) input timeframe, in seconds. |
script_tf_seconds | Script timeframe, in seconds. |
script_tf_ratio | script_tf_seconds / input_tf_seconds. |
needs_aggregation | 1 if input was aggregated up to script TF; 0 if pass-through. |
bar_magnifier_enabled | 1 if the magnifier ran on this backtest. |
See Timeframes for how the runtime resolves these values from the inputs to run_backtest_full.
A Pine strategy can call request.security() from multiple call sites. The runtime tracks each site independently.
| Field | Meaning |
|---|---|
security_feeds_total | Higher-TF feed bars consumed across all sites. |
security_complete_total | Evaluations on completed parent bars. |
security_partial_total | Evaluations on still-forming parent bars. |
security_diag[i] | Per-site counters. See pf_security_diag_t. |
security_diag_len | Number of request.security sites. |
| Field | Meaning |
|---|---|
magnifier_sub_bars_total | Sub-bars synthesized by the magnifier. |
magnifier_sample_ticks_total | Sample ticks visited by the magnifier. |
A "sub-bar" is one synthetic intra-bar OHLC slice; a "sample tick" is one fill-resolution probe within that slice. With the default PF_MAGNIFIER_ENDPOINTS distribution and magnifier_samples = 4, expect ~4 * input_bars_processed sample ticks.
See Bar magnifier for the full sampling model.
Populated only when:
// @pf-trace name=expr pragmas, and1 before the run.| Field | Meaning |
|---|---|
trace[i].timestamp | Bar timestamp (Unix ms). |
trace[i].bar_index | Zero-based bar index within the run. |
trace[i].name_id | Index into trace_names. |
trace[i].value | Traced expression value on this bar. |
trace_names[k] | Name string for name_id == k. Lifetime: until strategy_free. |
Trace records are zero-cost when disabled — no allocation, no formatting, no per-bar branch.
pf_report_t::metrics is a pf_metrics_t — four embedded blocks computed at report time. The complete per-metric reference (definitions, units, NaN rules, validation status) lives on Trading metrics reference.
| Block | Type | Scope |
|---|---|---|
metrics.all | pf_trade_stats_t | All closed trades. |
metrics.longs | pf_trade_stats_t | Long trades only. |
metrics.shorts | pf_trade_stats_t | Short trades only. |
metrics.equity | pf_equity_stats_t | Equity-curve-derived stats (all-trades only, like TV). |
Trade stats (pf_trade_stats_t) cover counts (num_trades, num_wins, num_losses, num_even), profit aggregates (net_profit, gross_profit, gross_loss, profit_factor, expectancy), per-trade averages and extremes (avg_trade, avg_win, avg_loss, largest_win, largest_loss plus their _pct twins), commission_paid, win/loss streaks, and bar-duration averages. Loss-side fields (gross_loss, avg_loss, largest_loss) are positive magnitudes, matching the TV display convention. _pct fields are on a 0–100 percent scale. largest_win_pct / largest_loss_pct are the independent maxima of per-trade pnl_pct — not the percent of the largest-USD trade (TV convention, validated 2026-06-12). Bar-duration averages (avg_bars_in_*) count inclusively of the entry bar: exit_bar_index - entry_bar_index + 1 (TV convention, validated 2026-06-12).
Equity stats (pf_equity_stats_t) cover the equity drawdown / run-up extremes (currency + percent), buy_hold_return, Sharpe and Sortino in two constructions — sharpe_tv / sortino_tv (TV-style month-end resampling in the chart timezone, 2%/yr risk-free, annualized by sqrt(12)) and sharpe_bar / sortino_bar (per-script-bar returns annualized by observed bar density) — plus cagr, calmar, recovery_factor, time_in_market_pct, and open_pl.
NaN convention: any statistic whose denominator is empty or zero is NaN, never 0 or an infinity — e.g. profit_factor with zero gross loss, avg_win with no winning trades, sharpe_tv with fewer than two monthly returns or zero deviation, calmar with zero drawdown. A 0.0 in the report is always a real computed zero. See the per-field doxygen in <pineforge/pineforge.h> for the exact rule on every field.
One pf_equity_point_t per script bar:
equity_curve_len equals script_bars_processed on a clean run (an exception mid-run can truncate the curve — check strategy_get_last_error). The array is heap-allocated and freed by report_free. Note the length field is int64_t, not int.
Every heap pointer in pf_report_t is freed by a single call to report_free:
trace_names points into a string table owned by the strategy handle, not the report. The pointer is valid until strategy_free is called on the handle that produced it. If you keep trace data past that, copy the strings out first.