PineForge v0.10.12
Deterministic PineScript v6 backtest runtime — C ABI reference
Loading...
Searching...
No Matches
pineforge.h
Go to the documentation of this file.
1/*
2 * SPDX-License-Identifier: Apache-2.0
3 *
4 * pineforge.h — public C ABI for the PineForge runtime.
5 *
6 * This header is the single source of truth for the harness ↔ compiled-
7 * strategy boundary. Every PineForge-generated .so exports a fixed set
8 * of C symbols declared below; the Python harness (validate_detailed_
9 * report.py) and any C/C++/FFI consumer of compiled strategies links
10 * against this contract.
11 *
12 * STABILITY GUARANTEE
13 * ───────────────────
14 * Within the same PINEFORGE_VERSION_MAJOR, this header's POD struct
15 * layouts and `extern "C"` symbol signatures are append-only. Fields
16 * are never reordered, removed, or retyped; new fields may only be
17 * appended at the end of structs. New functions may be added; existing
18 * functions are not removed or signature-changed.
19 *
20 * Across major versions all bets are off. Bump
21 * PINEFORGE_VERSION_MAJOR when breaking the ABI.
22 *
23 * SCOPE — WHAT THIS HEADER COVERS
24 * ───────────────────────────────
25 * ✓ Lifecycle of a compiled strategy (create / destroy)
26 * ✓ Running a backtest (auto-detect or fully configured)
27 * ✓ Per-strategy configuration (inputs, overrides, magnifier, trace)
28 * ✓ The shape of the report returned to the harness
29 *
30 * SCOPE — WHAT THIS HEADER DOES NOT COVER (BY DESIGN)
31 * ───────────────────────────────────────────────────
32 * ✗ The contract between codegen-emitted strategy code and the runtime
33 * internals (TA classes, math, series, strategy commands). That
34 * contract stays C++ — codegen and runtime ship together and are
35 * versioned in lockstep within the closed transpiler.
36 * ✗ Source-compiling strategies. Use the closed transpiler binary.
37 *
38 * The C++ headers under `<pineforge/engine.hpp>` etc. are *internal*
39 * implementation surface — not part of this stability guarantee.
40 */
41
42#ifndef PINEFORGE_H
43#define PINEFORGE_H
44
45#include <stdint.h>
46#include <stddef.h>
47
48/* ── Version ─────────────────────────────────────────────────────── */
49
50/* Macros (PINEFORGE_VERSION_MAJOR / _MINOR / _PATCH / _STRING / _FULL,
51 * PINEFORGE_GIT_SHA) live in the generated <pineforge/version.h>. */
52#include <pineforge/version.h>
53
54/* ── Visibility ──────────────────────────────────────────────────── */
55
56#if defined(_WIN32) || defined(__CYGWIN__)
57 #if defined(PINEFORGE_BUILD_SHARED)
58 #define PF_API __declspec(dllexport)
59 #else
60 #define PF_API __declspec(dllimport)
61 #endif
62#elif defined(__GNUC__) || defined(__clang__)
63 #define PF_API __attribute__((visibility("default")))
64#else
65 #define PF_API
66#endif
67
68/** Monotonic ABI version of pf_report_t / pf_trade_t layout. Bumped
69 * whenever a caller-visible struct grows. Consumers MUST verify
70 * pf_abi_version() == PF_ABI_VERSION before calling run_backtest.
71 * pf_report_t is caller-allocated: growth causes silent stack corruption
72 * in old callers that under-size the struct. pf_trade_t is runtime-
73 * allocated: growth causes array-stride misindexing in old readers that
74 * iterate the trades array with the stale sizeof. Value 2 = first
75 * versioned layout (metrics + equity curve); .so files predating this
76 * macro have no pf_abi_version symbol — treat dlsym failure as
77 * version 1. */
78#define PF_ABI_VERSION 2
79
80#ifdef __cplusplus
81extern "C" {
82#endif
83
84/** @defgroup pf_types Types
85 * @brief POD types and enums passed across the C ABI.
86 * @{
87 */
88
89/** Bar-magnifier sub-bar sampling distribution.
90 *
91 * Selects how intra-bar synthetic ticks are placed when the bar
92 * magnifier is enabled in #run_backtest_full. Layout-compatible with the
93 * internal C++ `pineforge::MagnifierDistribution` enum class — a
94 * `static_assert` in `c_abi.cpp` guarantees the integer values match. */
95typedef enum pf_magnifier_distribution_e {
96 PF_MAGNIFIER_UNIFORM = 0, /**< Uniform spacing across the parent bar. */
97 PF_MAGNIFIER_COSINE = 1, /**< Cosine-tapered density. */
98 PF_MAGNIFIER_TRIANGLE = 2, /**< Triangle-tapered density. */
99 PF_MAGNIFIER_ENDPOINTS = 3, /**< Default — exact O,H,L,C points plus uniform fill between. */
100 PF_MAGNIFIER_FRONT_LOADED = 4, /**< Sample density biased toward bar open. */
101 PF_MAGNIFIER_BACK_LOADED = 5 /**< Sample density biased toward bar close. */
103
104/** Single OHLCV bar pushed into the engine.
105 *
106 * Layout-compatible with the internal C++ `pineforge::Bar` struct. */
107typedef struct pf_bar_s {
108 double open; /**< Open price. */
109 double high; /**< High price. */
110 double low; /**< Low price. */
111 double close; /**< Close price. */
112 double volume; /**< Bar volume. */
113 int64_t timestamp; /**< Bar open time, Unix milliseconds. */
114} pf_bar_t;
115
116/** Closed-trade record returned in pf_report_t::trades.
117 *
118 * Layout-compatible with internal `pineforge::TradeC`. */
119typedef struct pf_trade_s {
120 int64_t entry_time; /**< Entry fill time (Unix ms). */
121 int64_t exit_time; /**< Exit fill time (Unix ms). */
122 double entry_price; /**< Entry fill price (incl. slippage). */
123 double exit_price; /**< Exit fill price (incl. slippage). */
124 double pnl; /**< Net realized PnL in account currency (commission-inclusive). */
125 double pnl_pct; /**< Net return-on-cost in percent: pnl (NET of commission) /
126 * entry cost (entry_price * qty * pointvalue) * 100. This is
127 * TradingView's "Net P&L %" convention, arbitrated 2026-06-12
128 * against a real TV export (trade #258 short: 102.44 USD on a
129 * 2276.66 entry => 4.50%). Degenerates to the old gross
130 * (exit/entry-1)*100 form for longs with zero commission;
131 * the previous short form (entry/exit-1)*100 was wrong on
132 * large moves. Sign always matches pnl. */
133 int is_long; /**< 1 if long, 0 if short. */
134 double max_runup; /**< Peak favorable price travel during the trade ($/unit qty). */
135 double max_drawdown; /**< Peak adverse price travel during the trade ($/unit qty). */
136 double qty; /**< Filled quantity. */
137 double commission; /**< Entry+exit commission actually deducted from pnl
138 * (account currency). pnl is already net of this. */
139 int32_t entry_bar_index;/**< Script-bar index of the entry fill (0-based). */
140 int32_t exit_bar_index; /**< Script-bar index of the exit fill (0-based). */
141} pf_trade_t;
142
143/** Trade-level statistics block — computed once each for all / long / short.
144 *
145 * Loss-side fields (`gross_loss`, `avg_loss`, `largest_loss`) are
146 * **positive magnitudes** (absolute values of the underlying negative PnL). */
147typedef struct pf_trade_stats_s {
148 int32_t num_trades; /**< Closed trades in this block (all / long-only / short-only). */
149 int32_t num_wins; /**< Trades with pnl > 0. */
150 int32_t num_losses; /**< Trades with pnl < 0. */
151 int32_t num_even; /**< Trades with pnl == 0.0 exactly; breaks both win and loss
152 * streaks; excluded from win/loss averages.
153 * Invariant: num_trades == num_wins + num_losses + num_even. */
154 double percent_profitable; /**< 100 * num_wins / num_trades, in PERCENT (0-100).
155 * NaN when num_trades == 0. */
156 double net_profit; /**< Sum of pnl (account currency, net of commission). */
157 double net_profit_pct; /**< net_profit as a percent of initial capital (0-100 scale).
158 * NaN when initial capital <= 0. */
159 double gross_profit; /**< Sum of winning pnl. */
160 double gross_profit_pct; /**< gross_profit as a percent of initial capital (0-100 scale).
161 * NaN when initial capital <= 0. */
162 double gross_loss; /**< Sum of |losing pnl| — POSITIVE magnitude (TV display convention). */
163 double gross_loss_pct; /**< gross_loss as a percent of initial capital (0-100 scale).
164 * NaN when initial capital <= 0. */
165 double profit_factor; /**< gross_profit / gross_loss. NaN when gross_loss == 0. */
166 double avg_trade; /**< net_profit / num_trades. NaN when num_trades == 0. */
167 double avg_trade_pct; /**< Mean of per-trade pnl_pct over all trades.
168 * NaN when num_trades == 0. */
169 double avg_win; /**< gross_profit / num_wins. NaN when num_wins == 0. */
170 double avg_win_pct; /**< Mean of per-trade pnl_pct over winning trades.
171 * NaN when num_wins == 0. */
172 double avg_loss; /**< gross_loss / num_losses (positive magnitude).
173 * NaN when num_losses == 0. */
174 double avg_loss_pct; /**< Mean of the NEGATED pnl_pct of the losing trades. Since
175 * pnl_pct is net return-on-cost (sign matches pnl), this is
176 * a genuinely POSITIVE magnitude. Basis = pf_trade_t::pnl_pct.
177 * NaN when num_losses == 0. */
178 double ratio_avg_win_avg_loss; /**< avg_win / avg_loss. NaN unless both sides non-empty. */
179 double largest_win; /**< Single largest pnl among winning trades.
180 * NaN when num_wins == 0. */
181 double largest_win_pct; /**< Maximum pnl_pct over winning trades — an INDEPENDENT
182 * maximum, not the pct of the largest-USD win (TV convention,
183 * validated 2026-06-12 vs TV export).
184 * NaN when num_wins == 0. */
185 double largest_loss; /**< Single largest |pnl| among losing trades (positive magnitude).
186 * NaN when num_losses == 0. */
187 double largest_loss_pct; /**< Maximum of -pnl_pct over losing trades (positive magnitude) —
188 * an INDEPENDENT maximum, not the pct of the largest-USD loss
189 * (TV convention, validated 2026-06-12 vs TV export: All
190 * "Largest loss %" came from a different trade than the
191 * largest USD loss). NaN when num_losses == 0. */
192 double commission_paid; /**< Sum of pf_trade_t::commission in the block. */
193 double expectancy; /**< (num_wins/num_trades)*avg_win - (num_losses/num_trades)*avg_loss,
194 * account currency per trade. NaN when num_trades == 0. */
195 int32_t max_consecutive_wins; /**< Longest winning run; even trades reset both streaks. */
196 int32_t max_consecutive_losses; /**< Longest losing run; even trades reset both streaks. */
197 double avg_bars_in_trade; /**< Mean of (exit_bar_index - entry_bar_index + 1) in SCRIPT
198 * bars, over all trades — inclusive of the entry bar (TV
199 * convention, validated 2026-06-12).
200 * NaN when num_trades == 0. */
201 double avg_bars_in_wins; /**< Mean bar duration of winning trades, inclusive of the entry
202 * bar (TV convention, validated 2026-06-12).
203 * NaN when num_wins == 0. */
204 double avg_bars_in_losses; /**< Mean bar duration of losing trades, inclusive of the entry
205 * bar (TV convention, validated 2026-06-12).
206 * NaN when num_losses == 0. */
208
209/** Equity-curve-derived statistics (all-trades only, like TV). */
210typedef struct pf_equity_stats_s {
211 double max_equity_drawdown; /**< Peak-to-trough equity drop, positive currency magnitude. */
212 double max_equity_drawdown_pct; /**< max_equity_drawdown relative to the peak in effect
213 * (PERCENT 0-100). */
214 double max_equity_runup; /**< Trough-to-peak rise where the trough resets on each new
215 * equity peak (mirrors the engine's intra-run extremes). */
216 double max_equity_runup_pct; /**< max_equity_runup relative to that trough (PERCENT 0-100). */
217 double buy_hold_return; /**< initial_capital * (last_close/first_open - 1), currency.
218 * NaN when first chart open is non-finite or <= 0. */
219 double buy_hold_return_pct; /**< buy_hold_return as PERCENT.
220 * NaN when first chart open is non-finite or <= 0. */
221 double sharpe_tv; /**< Month-end-resampled equity simple returns (chart timezone,
222 * open-time bucketing), risk-free 2%/yr (2/12 per month),
223 * annualized by sqrt(12). Uses sample (N-1) stddev.
224 * NaN with <2 monthly returns or zero deviation. */
225 double sortino_tv; /**< Same resampling as sharpe_tv; uses population downside
226 * deviation vs the monthly risk-free.
227 * NaN with <2 monthly returns or zero deviation. */
228 double sharpe_bar; /**< Per-script-bar returns, annualized by observed bar density
229 * (bars per year = (len-1)/calendar span), NOT a fixed
230 * calendar formula. Uses sample (N-1) stddev.
231 * NaN with <2 returns or zero deviation. */
232 double sortino_bar; /**< Same construction as sharpe_bar over per-bar returns;
233 * uses population downside deviation.
234 * NaN with <2 returns or zero deviation. */
235 double cagr; /**< PERCENT per year: 100*((final_equity/initial_capital)^(1/years)-1).
236 * NaN when span <= 0 or either side <= 0. */
237 double calmar; /**< cagr / max_equity_drawdown_pct — BOTH IN PERCENT, so the
238 * ratio is dimensionless. NaN when drawdown is 0. */
239 double recovery_factor; /**< net_profit / max_equity_drawdown (currency / currency).
240 * NaN when drawdown is 0. */
241 double time_in_market_pct; /**< PERCENT (0-100) of script bars with an open position
242 * at bar close. */
243 double open_pl; /**< Mark-to-market open profit at the final bar. */
245
246/** Composite metrics container: trade stats (all / long / short) +
247 * equity-curve stats. */
252
253/** Single per-script-bar equity point.
254 *
255 * `time_ms` is the script-bar **open** timestamp (Unix ms).
256 * `equity` = `initial_capital` + `net_profit` + `open_profit` at bar close. */
257typedef struct pf_equity_point_s {
258 int64_t time_ms; /**< Script-bar OPEN timestamp (Unix ms). */
259 double equity; /**< initial_capital + net_profit + open_profit. */
260 double open_profit; /**< Mark-to-market open P&L at bar close. */
262
263/** Per-`request.security()` site diagnostic counters.
264 *
265 * Layout-compatible with internal `pineforge::SecurityDiagC`. */
266typedef struct pf_security_diag_s {
267 int sec_id; /**< Stable id for the request.security site. */
268 int64_t feed_count; /**< Higher-TF feed bars consumed. */
269 int64_t complete_count; /**< Evaluations on completed parent bars. */
270 int64_t partial_count; /**< Evaluations on still-forming parent bars. */
272
273/** Single per-bar trace entry.
274 *
275 * Emitted when the source script contains `// @pf-trace name=expr`
276 * pragmas and tracing is enabled via #strategy_set_trace_enabled.
277 * Layout-compatible with internal `pineforge::TraceEntryC`. */
278typedef struct pf_trace_entry_s {
279 int64_t timestamp; /**< Bar timestamp (Unix ms). */
280 int32_t bar_index; /**< Zero-based bar index. */
281 int32_t name_id; /**< Index into pf_report_t::trace_names. */
282 double value; /**< Traced expression value on this bar. */
284
285/** Backtest report filled by #run_backtest / #run_backtest_full.
286 *
287 * Layout-compatible with internal `pineforge::ReportC`.
288 *
289 * ### Ownership and lifetime
290 * The struct itself is caller-owned (typically stack). The embedded
291 * arrays (`trades`, `security_diag`, `trace`, `trace_names`,
292 * `equity_curve`) are heap-allocated by the runtime; the caller must
293 * invoke #report_free exactly once on each filled report.
294 * `trace_names` string pointers remain owned by the strategy handle
295 * until #strategy_free. */
296
297typedef struct pf_report_s {
298 /* Trades */
299 int total_trades; /**< Closed-trade count (== trades_len). */
300 pf_trade_t* trades; /**< Heap array of closed trades. */
301 int trades_len; /**< Length of #trades. */
302 double net_profit; /**< Sum of all closed-trade PnL. */
303
304 /* Bar processing counts */
305 int64_t input_bars_processed; /**< Source-feed bars consumed. */
306 int64_t script_bars_processed; /**< Script-timeframe bars evaluated. */
307
308 /* Security diagnostics */
309 int64_t security_feeds_total; /**< Total higher-TF feed bars across all security sites. */
310 int64_t security_complete_total; /**< Total complete-bar evals across all security sites. */
311 int64_t security_partial_total; /**< Total partial-bar evals across all security sites. */
312
313 /* Bar magnifier diagnostics */
314 int64_t magnifier_sub_bars_total; /**< Sub-bars synthesized by the magnifier. */
315 int64_t magnifier_sample_ticks_total; /**< Sample ticks visited by the magnifier. */
316
317 /* Timeframe metadata */
318 int input_tf_seconds; /**< Detected/configured input timeframe (seconds). */
319 int script_tf_seconds; /**< Script timeframe (seconds). */
320 int script_tf_ratio; /**< script_tf_seconds / input_tf_seconds. */
321 int needs_aggregation; /**< 1 if input → script TF aggregation was performed. */
322 int bar_magnifier_enabled; /**< 1 if magnifier was active for this run. */
323
324 /* Per-security feed/eval counters */
325 pf_security_diag_t* security_diag; /**< One entry per request.security site. */
326 int security_diag_len; /**< Length of #security_diag. */
327
328 /* Per-bar trace records */
329 pf_trace_entry_t* trace; /**< Per-bar trace records (empty unless tracing enabled). */
330 int trace_len; /**< Length of #trace. */
331 const char** trace_names; /**< Names indexed by pf_trace_entry_t::name_id. */
332 int trace_names_len; /**< Length of #trace_names. */
333
334 /* Computed trading metrics. Trade-based blocks reported for all /
335 * long-only / short-only; equity-based stats are all-trades only.
336 * Loss-side fields are positive magnitudes. Undefined values are NaN
337 * (see per-field docs). */
339 /* Per-script-bar equity curve. time_ms is the script-bar OPEN
340 * timestamp; equity = initial_capital + net_profit + open_profit at
341 * bar close. Heap-allocated; freed by report_free. len ==
342 * script_bars_processed, EXCEPT after a mid-run error (check
343 * strategy_get_last_error): an exception can truncate the curve, and
344 * metrics then describe the truncated prefix. NOTE int64_t length
345 * (ctypes: c_int64). */
349
350/** @} */ /* end of pf_types */
351
352/** Opaque handle to a compiled strategy instance. */
353typedef void* pf_strategy_t;
354
355/* ───────────────────────────────────────────────────────────────────
356 * STRATEGY .SO EXPORTS — implemented per compiled strategy
357 * ───────────────────────────────────────────────────────────────────
358 *
359 * Each .so emitted by the codegen exports the following symbols. The
360 * runtime library itself does NOT define them — they are per-strategy
361 * implementations generated by the transpiler.
362 *
363 * Note on naming: these are the legacy unprefixed names retained for
364 * backward compatibility with the existing harness. Future major
365 * versions may introduce `pf_`-prefixed equivalents and deprecate the
366 * unprefixed forms.
367 */
368
369/** @defgroup pf_lifecycle Strategy lifecycle
370 * @brief Create, run, and destroy a compiled strategy instance.
371 * @{
372 *
373 * NOTE: Per-strategy symbols (strategy_create, run_backtest, etc.) are
374 * emitted by the codegen with internal C++ types (ReportC, Bar) that are
375 * layout-compatible but type-distinct from the public C PODs below.
376 * Guard with PINEFORGE_NO_STRATEGY_DECLS so engine.hpp can include this
377 * header for its POD types without conflicting with per-strategy TU
378 * definitions.
379 */
380
381#ifndef PINEFORGE_NO_STRATEGY_DECLS
382
383/** Allocate a new strategy instance.
384 *
385 * @param params_json Currently ignored; pass `NULL`.
386 * @return Strategy handle, or `NULL` on allocation failure.
387 *
388 * Caller owns the returned handle and must release it via #strategy_free. */
389PF_API pf_strategy_t strategy_create(const char* params_json);
390
391/** Release a strategy handle previously returned by #strategy_create.
392 *
393 * Safe to call with `NULL`. Invalidates any `pf_report_t::trace_names`
394 * pointers obtained from this strategy. */
396
397/** Run a backtest with auto-detected timeframe and no bar magnifier.
398 *
399 * @param s Strategy handle from #strategy_create.
400 * @param bars Non-NULL pointer to OHLCV bars (length @p n).
401 * @param n Bar count (>= 0).
402 * @param out Non-NULL output report. Fields are populated with heap
403 * allocations the caller must release via #report_free. */
405 pf_bar_t* bars,
406 int n,
407 pf_report_t* out);
408
409/** Run a backtest with explicit timeframe and magnifier configuration.
410 *
411 * @param s Strategy handle.
412 * @param bars Bar feed.
413 * @param n Bar count.
414 * @param input_tf Input timeframe ("1", "5", "15", "60", "1D", ...).
415 * Empty string → auto-detect from bar timestamps.
416 * @param script_tf Script timeframe. Empty string → defaults to @p input_tf.
417 * @param bar_magnifier Boolean (0 / non-zero) — enable bar magnifier.
418 * @param magnifier_samples Sub-bar samples per parent bar (typical: 4).
419 * @param magnifier_dist Sampling distribution (see #pf_magnifier_distribution_t).
420 * @param out Output report. Free with #report_free. */
422 pf_bar_t* bars,
423 int n,
424 const char* input_tf,
425 const char* script_tf,
426 int bar_magnifier,
427 int magnifier_samples,
428 pf_magnifier_distribution_t magnifier_dist,
429 pf_report_t* out);
430
431/** Free heap arrays attached to a filled report.
432 *
433 * Idempotent. Safe to call with `NULL` or an already-freed report.
434 * The `pf_report_t` struct itself is caller-owned. */
436
437/** @} */ /* end of pf_lifecycle */
438
439/** @defgroup pf_config Per-strategy configuration
440 * @brief Override @c input.*() values, `strategy(...)` params, and runtime knobs.
441 * @{
442 */
443
444/** Override a Pine @c input.*() value before the next run.
445 *
446 * @param s Strategy handle.
447 * @param key The input's title (or fallback identifier).
448 * @param value Serialized value — numbers as decimal strings,
449 * booleans as `"true"` / `"false"`.
450 *
451 * Calls after #run_backtest are accepted but only take effect on
452 * subsequent runs. */
454 const char* key,
455 const char* value);
456
457/** Override a `strategy(...)` declaration parameter.
458 *
459 * Recognised @p key values: `initial_capital`, `commission_value`,
460 * `default_qty_value`, `pyramiding`, `slippage`,
461 * `process_orders_on_close`, `close_entries_rule`, `default_qty_type`,
462 * `commission_type`. */
464 const char* key,
465 const char* value);
466
467/** Toggle volume-weighted bar-magnifier sampling.
468 *
469 * Has no effect unless the bar magnifier is enabled in
470 * #run_backtest_full. */
472 int on);
473
474#endif /* PINEFORGE_NO_STRATEGY_DECLS */
475
476/* ───────────────────────────────────────────────────────────────────
477 * RUNTIME LIBRARY EXPORTS — implemented in libruntime
478 * ─────────────────────────────────────────────────────────────────── */
479
480/** Toggle per-bar trace recording. Default off (zero-cost when off).
481 *
482 * Enables capture for `// @pf-trace name=expr` pragmas already compiled
483 * into the strategy `.so`. Trace records appear in pf_report_t::trace. */
485
486/** Set the earliest Unix-ms timestamp at which strategy order commands
487 * may fire.
488 *
489 * Earlier bars still execute user code and warm TA/series state, but
490 * `strategy.entry/order/exit/close` commands are ignored. */
492
493/** Set the strategy's chart timezone (IANA / POSIX TZ string).
494 *
495 * Pine builtins ``hour``, ``minute``, ``second``, ``dayofmonth``,
496 * ``dayofweek``, ``month``, ``year`` and ``weekofyear`` return the
497 * wall-clock for the chart's timezone — TV exports trade rows in chart
498 * TZ too. Engine bars are stored as Unix-ms (UTC), so without this
499 * override these builtins return UTC and silently diverge from TV by N
500 * hours when the chart is on a non-UTC zone (Asia/Taipei = UTC+8 is the
501 * validator default).
502 *
503 * Pass `NULL`, `""`, `"UTC"` or `"Etc/UTC"` for the legacy UTC
504 * behaviour (cheap, mutex-free). Any other value names a TZ resolved by
505 * the system tzdata; the per-bar decomposition then runs under a
506 * process-global mutex so multi-threaded harnesses don't corrupt each
507 * other's wall time.
508 *
509 * Should be called before #run_backtest / #run_backtest_full. Persists
510 * across runs on the same strategy handle until overridden. */
512
513/** Plumb the symbol's exchange timezone (IANA string) into syminfo. Feeds
514 * ``session.ismarket`` / ``time(session)`` predicates. Defaults to "UTC"
515 * (crypto). Distinct from #strategy_set_chart_timezone — the chart TZ
516 * drives wall-clock builtins and intraday-cap day rollover; this drives
517 * session membership. `NULL` is ignored. Call before #run_backtest*. */
519
520/** Set the symbol's session string (e.g. "0930-1600:23456", default
521 * "24x7"). Feeds ``session.ismarket`` / ``time(session)``. `NULL`
522 * ignored. Call before #run_backtest*. */
524
525/** Set the instrument tick size (``syminfo.mintick``, default 0.01). Drives the
526 * directional stop-entry snap and ``slippage = N*mintick`` economics. Set
527 * per-instrument (e.g. 0.25 for ES, 0.00001 for FX). Non-positive ignored.
528 * Call before #run_backtest*. */
530
531/** Set the instrument point value (``syminfo.pointvalue``, default 1.0) — the
532 * $-per-point-per-contract multiplier applied to every money path: realized
533 * PnL and MFE/MAE, open profit / mark-to-market equity (and the drawdown /
534 * runup extremes), percent-of-equity and cash position sizing, percent
535 * commission notionals, and the margin admission check. Set per-instrument
536 * (e.g. 50 for ES). Non-positive ignored. Call before #run_backtest*. */
538
539/** Inject a fundamental/exchange metadata value by Pine member name
540 * (e.g. "shares_outstanding_total", "target_price_average"). These have
541 * no OHLCV source; reads of un-injected members return na. Call before
542 * #run_backtest*. */
544 double value);
545
546/** Returns the error message captured by the most recent #run_backtest /
547 * #run_backtest_full call on this strategy.
548 *
549 * Returns an empty string when the run completed normally, or `NULL`
550 * only when `s` itself is `NULL`. The pointer is owned by the engine
551 * and remains valid until the next #run_backtest* call (which clears
552 * the captured error before it begins).
553 *
554 * The runtime catches every `std::exception` derivative inside the
555 * engine's run loop so the C ABI never unwinds a C++ exception across
556 * the `extern "C"` boundary. Consumers must check this after every
557 * run to surface engine-rejected configurations such as a script
558 * timeframe finer than the input timeframe, a `request.security`
559 * timeframe below the chart timeframe without a supported lower-TF
560 * emulation, or a missing input timeframe when securities are
561 * registered. */
563
564/** @} */ /* end of pf_config */
565
566/** @defgroup pf_version Version query
567 * @brief Runtime version metadata.
568 * @{
569 */
570
571/** Runtime version descriptor returned by #pf_version_get. */
572typedef struct pf_version_s {
573 int major; /**< Major version. */
574 int minor; /**< Minor version. */
575 int patch; /**< Patch version. */
576 const char* commit_sha; /**< Short git commit SHA, or `""` if unknown. */
578
579/** @return Linked runtime version. */
581
582/** @return Monotonic ABI version (see #PF_ABI_VERSION). */
584
585/** Full git-derived version descriptor.
586 *
587 * Returns `"MAJOR.MINOR.PATCH[-N-gSHA[-dirty]]"` for git checkouts, or
588 * plain `"MAJOR.MINOR.PATCH"` for tarball builds. The pointer is to a
589 * static string with program lifetime; do not free. */
590PF_API const char* pf_version_string(void);
591
592/** @} */ /* end of pf_version */
593
594#ifdef __cplusplus
595} /* extern "C" */
596#endif
597
598#endif /* PINEFORGE_H */
void strategy_set_syminfo_timezone(pf_strategy_t s, const char *tz)
Plumb the symbol's exchange timezone (IANA string) into syminfo.
void strategy_set_syminfo_mintick(pf_strategy_t s, double mintick)
Set the instrument tick size (syminfo.mintick, default 0.01).
void strategy_set_override(pf_strategy_t s, const char *key, const char *value)
Override a strategy(...) declaration parameter.
void strategy_set_input(pf_strategy_t s, const char *key, const char *value)
Override a Pine input.
const char * strategy_get_last_error(pf_strategy_t s)
Returns the error message captured by the most recent run_backtest / run_backtest_full call on this s...
void strategy_set_syminfo_session(pf_strategy_t s, const char *session)
Set the symbol's session string (e.g.
void strategy_set_syminfo_pointvalue(pf_strategy_t s, double pointvalue)
Set the instrument point value (syminfo.pointvalue, default 1.0) — the $-per-point-per-contract multi...
void strategy_set_chart_timezone(pf_strategy_t s, const char *tz)
Set the strategy's chart timezone (IANA / POSIX TZ string).
void strategy_set_magnifier_volume_weighted(pf_strategy_t s, int on)
Toggle volume-weighted bar-magnifier sampling.
void strategy_set_trade_start_time(pf_strategy_t s, int64_t timestamp_ms)
Set the earliest Unix-ms timestamp at which strategy order commands may fire.
void strategy_set_syminfo_metadata(pf_strategy_t s, const char *key, double value)
Inject a fundamental/exchange metadata value by Pine member name (e.g.
void strategy_set_trace_enabled(pf_strategy_t s, int on)
Toggle per-bar trace recording.
pf_strategy_t strategy_create(const char *params_json)
Allocate a new strategy instance.
void run_backtest(pf_strategy_t s, pf_bar_t *bars, int n, pf_report_t *out)
Run a backtest with auto-detected timeframe and no bar magnifier.
void strategy_free(pf_strategy_t s)
Release a strategy handle previously returned by strategy_create.
void report_free(pf_report_t *report)
Free heap arrays attached to a filled report.
void run_backtest_full(pf_strategy_t s, pf_bar_t *bars, int n, const char *input_tf, const char *script_tf, int bar_magnifier, int magnifier_samples, pf_magnifier_distribution_t magnifier_dist, pf_report_t *out)
Run a backtest with explicit timeframe and magnifier configuration.
pf_magnifier_distribution_t
Bar-magnifier sub-bar sampling distribution.
Definition pineforge.h:95
@ PF_MAGNIFIER_FRONT_LOADED
Sample density biased toward bar open.
Definition pineforge.h:100
@ PF_MAGNIFIER_COSINE
Cosine-tapered density.
Definition pineforge.h:97
@ PF_MAGNIFIER_ENDPOINTS
Default — exact O,H,L,C points plus uniform fill between.
Definition pineforge.h:99
@ PF_MAGNIFIER_BACK_LOADED
Sample density biased toward bar close.
Definition pineforge.h:101
@ PF_MAGNIFIER_TRIANGLE
Triangle-tapered density.
Definition pineforge.h:98
@ PF_MAGNIFIER_UNIFORM
Uniform spacing across the parent bar.
Definition pineforge.h:96
int pf_abi_version(void)
pf_version_t pf_version_get(void)
const char * pf_version_string(void)
Full git-derived version descriptor.
void * pf_strategy_t
Opaque handle to a compiled strategy instance.
Definition pineforge.h:353
#define PF_API
Definition pineforge.h:65
Single OHLCV bar pushed into the engine.
Definition pineforge.h:107
double volume
Bar volume.
Definition pineforge.h:112
double high
High price.
Definition pineforge.h:109
double low
Low price.
Definition pineforge.h:110
double close
Close price.
Definition pineforge.h:111
double open
Open price.
Definition pineforge.h:108
int64_t timestamp
Bar open time, Unix milliseconds.
Definition pineforge.h:113
Single per-script-bar equity point.
Definition pineforge.h:257
double open_profit
Mark-to-market open P&L at bar close.
Definition pineforge.h:260
double equity
initial_capital + net_profit + open_profit.
Definition pineforge.h:259
int64_t time_ms
Script-bar OPEN timestamp (Unix ms).
Definition pineforge.h:258
Equity-curve-derived statistics (all-trades only, like TV).
Definition pineforge.h:210
double sharpe_tv
Month-end-resampled equity simple returns (chart timezone, open-time bucketing), risk-free 2%/yr (2/1...
Definition pineforge.h:221
double time_in_market_pct
PERCENT (0-100) of script bars with an open position at bar close.
Definition pineforge.h:241
double max_equity_drawdown_pct
max_equity_drawdown relative to the peak in effect (PERCENT 0-100).
Definition pineforge.h:212
double max_equity_drawdown
Peak-to-trough equity drop, positive currency magnitude.
Definition pineforge.h:211
double max_equity_runup_pct
max_equity_runup relative to that trough (PERCENT 0-100).
Definition pineforge.h:216
double buy_hold_return
initial_capital * (last_close/first_open - 1), currency.
Definition pineforge.h:217
double open_pl
Mark-to-market open profit at the final bar.
Definition pineforge.h:243
double max_equity_runup
Trough-to-peak rise where the trough resets on each new equity peak (mirrors the engine's intra-run e...
Definition pineforge.h:214
double sortino_tv
Same resampling as sharpe_tv; uses population downside deviation vs the monthly risk-free.
Definition pineforge.h:225
double cagr
PERCENT per year: 100*((final_equity/initial_capital)^(1/years)-1).
Definition pineforge.h:235
double recovery_factor
net_profit / max_equity_drawdown (currency / currency).
Definition pineforge.h:239
double calmar
cagr / max_equity_drawdown_pct — BOTH IN PERCENT, so the ratio is dimensionless.
Definition pineforge.h:237
double buy_hold_return_pct
buy_hold_return as PERCENT.
Definition pineforge.h:219
double sharpe_bar
Per-script-bar returns, annualized by observed bar density (bars per year = (len-1)/calendar span),...
Definition pineforge.h:228
double sortino_bar
Same construction as sharpe_bar over per-bar returns; uses population downside deviation.
Definition pineforge.h:232
Composite metrics container: trade stats (all / long / short) + equity-curve stats.
Definition pineforge.h:248
pf_trade_stats_t all
Definition pineforge.h:249
pf_equity_stats_t equity
Definition pineforge.h:250
pf_trade_stats_t longs
Definition pineforge.h:249
pf_trade_stats_t shorts
Definition pineforge.h:249
Backtest report filled by run_backtest / run_backtest_full.
Definition pineforge.h:297
int input_tf_seconds
Detected/configured input timeframe (seconds).
Definition pineforge.h:318
int security_diag_len
Length of security_diag.
Definition pineforge.h:326
int64_t security_feeds_total
Total higher-TF feed bars across all security sites.
Definition pineforge.h:309
int bar_magnifier_enabled
1 if magnifier was active for this run.
Definition pineforge.h:322
pf_trace_entry_t * trace
Per-bar trace records (empty unless tracing enabled).
Definition pineforge.h:329
int trace_names_len
Length of trace_names.
Definition pineforge.h:332
int64_t input_bars_processed
Source-feed bars consumed.
Definition pineforge.h:305
int script_tf_seconds
Script timeframe (seconds).
Definition pineforge.h:319
double net_profit
Sum of all closed-trade PnL.
Definition pineforge.h:302
int64_t equity_curve_len
Definition pineforge.h:347
int trades_len
Length of trades.
Definition pineforge.h:301
pf_metrics_t metrics
Definition pineforge.h:338
int trace_len
Length of trace.
Definition pineforge.h:330
int64_t script_bars_processed
Script-timeframe bars evaluated.
Definition pineforge.h:306
int64_t magnifier_sample_ticks_total
Sample ticks visited by the magnifier.
Definition pineforge.h:315
int total_trades
Closed-trade count (== trades_len).
Definition pineforge.h:299
int64_t security_partial_total
Total partial-bar evals across all security sites.
Definition pineforge.h:311
pf_security_diag_t * security_diag
One entry per request.security site.
Definition pineforge.h:325
int64_t magnifier_sub_bars_total
Sub-bars synthesized by the magnifier.
Definition pineforge.h:314
const char ** trace_names
Names indexed by pf_trace_entry_t::name_id.
Definition pineforge.h:331
pf_equity_point_t * equity_curve
Definition pineforge.h:346
int64_t security_complete_total
Total complete-bar evals across all security sites.
Definition pineforge.h:310
int script_tf_ratio
script_tf_seconds / input_tf_seconds.
Definition pineforge.h:320
int needs_aggregation
1 if input → script TF aggregation was performed.
Definition pineforge.h:321
pf_trade_t * trades
Heap array of closed trades.
Definition pineforge.h:300
Per-request.security() site diagnostic counters.
Definition pineforge.h:266
int sec_id
Stable id for the request.security site.
Definition pineforge.h:267
int64_t feed_count
Higher-TF feed bars consumed.
Definition pineforge.h:268
int64_t complete_count
Evaluations on completed parent bars.
Definition pineforge.h:269
int64_t partial_count
Evaluations on still-forming parent bars.
Definition pineforge.h:270
Single per-bar trace entry.
Definition pineforge.h:278
double value
Traced expression value on this bar.
Definition pineforge.h:282
int64_t timestamp
Bar timestamp (Unix ms).
Definition pineforge.h:279
int32_t name_id
Index into pf_report_t::trace_names.
Definition pineforge.h:281
int32_t bar_index
Zero-based bar index.
Definition pineforge.h:280
Trade-level statistics block — computed once each for all / long / short.
Definition pineforge.h:147
double avg_win_pct
Mean of per-trade pnl_pct over winning trades.
Definition pineforge.h:170
double avg_win
gross_profit / num_wins.
Definition pineforge.h:169
double net_profit_pct
net_profit as a percent of initial capital (0-100 scale).
Definition pineforge.h:157
double largest_loss_pct
Maximum of -pnl_pct over losing trades (positive magnitude) — an INDEPENDENT maximum,...
Definition pineforge.h:187
int32_t num_losses
Trades with pnl < 0.
Definition pineforge.h:150
double gross_profit
Sum of winning pnl.
Definition pineforge.h:159
int32_t max_consecutive_losses
Longest losing run; even trades reset both streaks.
Definition pineforge.h:196
double avg_loss_pct
Mean of the NEGATED pnl_pct of the losing trades.
Definition pineforge.h:174
double commission_paid
Sum of pf_trade_t::commission in the block.
Definition pineforge.h:192
double percent_profitable
100 * num_wins / num_trades, in PERCENT (0-100).
Definition pineforge.h:154
double largest_win_pct
Maximum pnl_pct over winning trades — an INDEPENDENT maximum, not the pct of the largest-USD win (TV ...
Definition pineforge.h:181
double gross_loss
Sum of |losing pnl| — POSITIVE magnitude (TV display convention).
Definition pineforge.h:162
double gross_profit_pct
gross_profit as a percent of initial capital (0-100 scale).
Definition pineforge.h:160
double largest_loss
Single largest |pnl| among losing trades (positive magnitude).
Definition pineforge.h:185
double avg_bars_in_wins
Mean bar duration of winning trades, inclusive of the entry bar (TV convention, validated 2026-06-12)...
Definition pineforge.h:201
double avg_bars_in_losses
Mean bar duration of losing trades, inclusive of the entry bar (TV convention, validated 2026-06-12).
Definition pineforge.h:204
double profit_factor
gross_profit / gross_loss.
Definition pineforge.h:165
double avg_loss
gross_loss / num_losses (positive magnitude).
Definition pineforge.h:172
double largest_win
Single largest pnl among winning trades.
Definition pineforge.h:179
double avg_bars_in_trade
Mean of (exit_bar_index - entry_bar_index + 1) in SCRIPT bars, over all trades — inclusive of the ent...
Definition pineforge.h:197
double gross_loss_pct
gross_loss as a percent of initial capital (0-100 scale).
Definition pineforge.h:163
int32_t max_consecutive_wins
Longest winning run; even trades reset both streaks.
Definition pineforge.h:195
double expectancy
(num_wins/num_trades)*avg_win - (num_losses/num_trades)*avg_loss, account currency per trade.
Definition pineforge.h:193
double net_profit
Sum of pnl (account currency, net of commission).
Definition pineforge.h:156
int32_t num_trades
Closed trades in this block (all / long-only / short-only).
Definition pineforge.h:148
double avg_trade_pct
Mean of per-trade pnl_pct over all trades.
Definition pineforge.h:167
double ratio_avg_win_avg_loss
avg_win / avg_loss.
Definition pineforge.h:178
double avg_trade
net_profit / num_trades.
Definition pineforge.h:166
int32_t num_even
Trades with pnl == 0.0 exactly; breaks both win and loss streaks; excluded from win/loss averages.
Definition pineforge.h:151
int32_t num_wins
Trades with pnl > 0.
Definition pineforge.h:149
Closed-trade record returned in pf_report_t::trades.
Definition pineforge.h:119
int32_t exit_bar_index
Script-bar index of the exit fill (0-based).
Definition pineforge.h:140
double pnl_pct
Net return-on-cost in percent: pnl (NET of commission) / entry cost (entry_price * qty * pointvalue) ...
Definition pineforge.h:125
double exit_price
Exit fill price (incl.
Definition pineforge.h:123
int32_t entry_bar_index
Script-bar index of the entry fill (0-based).
Definition pineforge.h:139
double commission
Entry+exit commission actually deducted from pnl (account currency).
Definition pineforge.h:137
double pnl
Net realized PnL in account currency (commission-inclusive).
Definition pineforge.h:124
int is_long
1 if long, 0 if short.
Definition pineforge.h:133
double max_drawdown
Peak adverse price travel during the trade ($/unit qty).
Definition pineforge.h:135
double qty
Filled quantity.
Definition pineforge.h:136
double max_runup
Peak favorable price travel during the trade ($/unit qty).
Definition pineforge.h:134
int64_t entry_time
Entry fill time (Unix ms).
Definition pineforge.h:120
double entry_price
Entry fill price (incl.
Definition pineforge.h:122
int64_t exit_time
Exit fill time (Unix ms).
Definition pineforge.h:121
Runtime version descriptor returned by pf_version_get.
Definition pineforge.h:572
int patch
Patch version.
Definition pineforge.h:575
int minor
Minor version.
Definition pineforge.h:574
int major
Major version.
Definition pineforge.h:573
const char * commit_sha
Short git commit SHA, or "" if unknown.
Definition pineforge.h:576