PineForge v0.12.3-30-g11e61d4
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. Value 3 appends pf_trade_t::open_at_end (the range-end
78 * close flag); a v2 reader iterating trades with the v2 stride would
79 * misindex every row after the first. */
80#define PF_ABI_VERSION 3
81
82/** Feature probe for the opt-in split chart/request.security feed boundary.
83 * When defined, #strategy_set_aux_security_feed is available. */
84#define PINEFORGE_HAS_AUX_SECURITY_FEED_V1 1
85
86#ifdef __cplusplus
87extern "C" {
88#endif
89
90/** @defgroup pf_types Types
91 * @brief POD types and enums passed across the C ABI.
92 * @{
93 */
94
95/** Bar-magnifier sub-bar sampling distribution.
96 *
97 * Selects how intra-bar synthetic ticks are placed when the bar
98 * magnifier is enabled in #run_backtest_full. Layout-compatible with the
99 * internal C++ `pineforge::MagnifierDistribution` enum class — a
100 * `static_assert` in `c_abi.cpp` guarantees the integer values match. */
101typedef enum pf_magnifier_distribution_e {
102 PF_MAGNIFIER_UNIFORM = 0, /**< Uniform spacing across the parent bar. */
103 PF_MAGNIFIER_COSINE = 1, /**< Cosine-tapered density. */
104 PF_MAGNIFIER_TRIANGLE = 2, /**< Triangle-tapered density. */
105 PF_MAGNIFIER_ENDPOINTS = 3, /**< Default — exact O,H,L,C points plus uniform fill between. */
106 PF_MAGNIFIER_FRONT_LOADED = 4, /**< Sample density biased toward bar open. */
107 PF_MAGNIFIER_BACK_LOADED = 5 /**< Sample density biased toward bar close. */
109
110/** Single OHLCV bar pushed into the engine.
111 *
112 * Layout-compatible with the internal C++ `pineforge::Bar` struct. */
113typedef struct pf_bar_s {
114 double open; /**< Open price. */
115 double high; /**< High price. */
116 double low; /**< Low price. */
117 double close; /**< Close price. */
118 double volume; /**< Bar volume. */
119 int64_t timestamp; /**< Bar open time, Unix milliseconds. */
120} pf_bar_t;
121
122/** One provider-neutral realtime executed-trade update.
123 *
124 * `sequence` is optional: pass 0 when the normalized source has no stable
125 * ordering key. Non-zero values must increase strictly within a stream.
126 * `quantity` is expressed in the configured symbol's volume units and is
127 * accumulated into the input bar's volume. The source adapter owns all
128 * provider-specific fields and normalization. */
129typedef struct pf_trade_tick_s {
130 int64_t timestamp; /**< Source event time, Unix milliseconds. */
131 uint64_t sequence; /**< Normalized per-stream sequence, or 0. */
132 double price; /**< Executed trade price (> 0). */
133 double quantity; /**< Traded quantity in symbol volume units (>= 0). */
135
136/** Closed-trade record returned in pf_report_t::trades.
137 *
138 * Layout-compatible with internal `pineforge::TradeC`. */
139typedef struct pf_trade_s {
140 int64_t entry_time; /**< Entry fill time (Unix ms). */
141 int64_t exit_time; /**< Exit fill time (Unix ms). */
142 double entry_price; /**< Entry fill price (incl. slippage). */
143 double exit_price; /**< Exit fill price (incl. slippage). */
144 double pnl; /**< Net realized PnL in account currency (commission-inclusive). */
145 double pnl_pct; /**< Net return-on-cost in percent: pnl (NET of commission) /
146 * entry cost (entry_price * qty * pointvalue) * 100. This is
147 * TradingView's "Net P&L %" convention, arbitrated 2026-06-12
148 * against a real TV export (trade #258 short: 102.44 USD on a
149 * 2276.66 entry => 4.50%). Degenerates to the old gross
150 * (exit/entry-1)*100 form for longs with zero commission;
151 * the previous short form (entry/exit-1)*100 was wrong on
152 * large moves. Sign always matches pnl. */
153 int is_long; /**< 1 if long, 0 if short. */
154 double max_runup; /**< Peak favorable price travel during the trade ($/unit qty). */
155 double max_drawdown; /**< Peak adverse price travel during the trade ($/unit qty). */
156 double qty; /**< Filled quantity. */
157 double commission; /**< Entry+exit commission actually deducted from pnl
158 * (account currency). pnl is already net of this. */
159 int32_t entry_bar_index;/**< Script-bar index of the entry fill (0-based). */
160 int32_t exit_bar_index; /**< Script-bar index of the exit fill (0-based). */
161 int32_t open_at_end; /**< 1 when this row is the RANGE-END close of a position
162 * that was still open after the final bar; 0 for an
163 * exit the script or a bracket produced. TradingView's
164 * deep-backtest report does not leave the last position
165 * open: it reports it as a closed trade whose exit leg is
166 * the range's last bar at that bar's CLOSE, with an
167 * empty exit Signal, and counts it in closedTrades
168 * (orb-lite on NYSE:F 1D: Entry short 2026-03-16 @ 11.82,
169 * Exit 2026-04-30 @ 12.08 = the last close,
170 * closedTrades:1). The engine emulates that row
171 * (operator decision 2026-09-02): exit_time is the last
172 * script bar's label, exit_price its mintick-rounded
173 * close with no slippage, commission per the strategy's
174 * rules, pnl/pnl_pct/excursions as for any close.
175 * Appended in ABI v3. */
176} pf_trade_t;
177
178/** Trade-level statistics block — computed once each for all / long / short.
179 *
180 * Loss-side fields (`gross_loss`, `avg_loss`, `largest_loss`) are
181 * **positive magnitudes** (absolute values of the underlying negative PnL). */
182typedef struct pf_trade_stats_s {
183 int32_t num_trades; /**< Closed trades in this block (all / long-only / short-only). */
184 int32_t num_wins; /**< Trades with pnl > 0. */
185 int32_t num_losses; /**< Trades with pnl < 0. */
186 int32_t num_even; /**< Trades with pnl == 0.0 exactly; breaks both win and loss
187 * streaks; excluded from win/loss averages.
188 * Invariant: num_trades == num_wins + num_losses + num_even. */
189 double percent_profitable; /**< 100 * num_wins / num_trades, in PERCENT (0-100).
190 * NaN when num_trades == 0. */
191 double net_profit; /**< Sum of pnl (account currency, net of commission). */
192 double net_profit_pct; /**< net_profit as a percent of initial capital (0-100 scale).
193 * NaN when initial capital <= 0. */
194 double gross_profit; /**< Sum of winning pnl. */
195 double gross_profit_pct; /**< gross_profit as a percent of initial capital (0-100 scale).
196 * NaN when initial capital <= 0. */
197 double gross_loss; /**< Sum of |losing pnl| — POSITIVE magnitude (TV display convention). */
198 double gross_loss_pct; /**< gross_loss as a percent of initial capital (0-100 scale).
199 * NaN when initial capital <= 0. */
200 double profit_factor; /**< gross_profit / gross_loss. NaN when gross_loss == 0. */
201 double avg_trade; /**< net_profit / num_trades. NaN when num_trades == 0. */
202 double avg_trade_pct; /**< Mean of per-trade pnl_pct over all trades.
203 * NaN when num_trades == 0. */
204 double avg_win; /**< gross_profit / num_wins. NaN when num_wins == 0. */
205 double avg_win_pct; /**< Mean of per-trade pnl_pct over winning trades.
206 * NaN when num_wins == 0. */
207 double avg_loss; /**< gross_loss / num_losses (positive magnitude).
208 * NaN when num_losses == 0. */
209 double avg_loss_pct; /**< Mean of the NEGATED pnl_pct of the losing trades. Since
210 * pnl_pct is net return-on-cost (sign matches pnl), this is
211 * a genuinely POSITIVE magnitude. Basis = pf_trade_t::pnl_pct.
212 * NaN when num_losses == 0. */
213 double ratio_avg_win_avg_loss; /**< avg_win / avg_loss. NaN unless both sides non-empty. */
214 double largest_win; /**< Single largest pnl among winning trades.
215 * NaN when num_wins == 0. */
216 double largest_win_pct; /**< Maximum pnl_pct over winning trades — an INDEPENDENT
217 * maximum, not the pct of the largest-USD win (TV convention,
218 * validated 2026-06-12 vs TV export).
219 * NaN when num_wins == 0. */
220 double largest_loss; /**< Single largest |pnl| among losing trades (positive magnitude).
221 * NaN when num_losses == 0. */
222 double largest_loss_pct; /**< Maximum of -pnl_pct over losing trades (positive magnitude) —
223 * an INDEPENDENT maximum, not the pct of the largest-USD loss
224 * (TV convention, validated 2026-06-12 vs TV export: All
225 * "Largest loss %" came from a different trade than the
226 * largest USD loss). NaN when num_losses == 0. */
227 double commission_paid; /**< Sum of pf_trade_t::commission in the block. */
228 double expectancy; /**< (num_wins/num_trades)*avg_win - (num_losses/num_trades)*avg_loss,
229 * account currency per trade. NaN when num_trades == 0. */
230 int32_t max_consecutive_wins; /**< Longest winning run; even trades reset both streaks. */
231 int32_t max_consecutive_losses; /**< Longest losing run; even trades reset both streaks. */
232 double avg_bars_in_trade; /**< Mean of (exit_bar_index - entry_bar_index + 1) in SCRIPT
233 * bars, over all trades — inclusive of the entry bar (TV
234 * convention, validated 2026-06-12).
235 * NaN when num_trades == 0. */
236 double avg_bars_in_wins; /**< Mean bar duration of winning trades, inclusive of the entry
237 * bar (TV convention, validated 2026-06-12).
238 * NaN when num_wins == 0. */
239 double avg_bars_in_losses; /**< Mean bar duration of losing trades, inclusive of the entry
240 * bar (TV convention, validated 2026-06-12).
241 * NaN when num_losses == 0. */
243
244/** Equity-curve-derived statistics (all-trades only, like TV). */
245typedef struct pf_equity_stats_s {
246 double max_equity_drawdown; /**< Peak-to-trough equity drop, positive currency magnitude. */
247 double max_equity_drawdown_pct; /**< max_equity_drawdown relative to the peak in effect
248 * (PERCENT 0-100). */
249 double max_equity_runup; /**< Trough-to-peak rise where the trough resets on each new
250 * equity peak (mirrors the engine's intra-run extremes). */
251 double max_equity_runup_pct; /**< max_equity_runup relative to that trough (PERCENT 0-100). */
252 double buy_hold_return; /**< initial_capital * (last_close/first_open - 1), currency.
253 * NaN when first chart open is non-finite or <= 0. */
254 double buy_hold_return_pct; /**< buy_hold_return as PERCENT.
255 * NaN when first chart open is non-finite or <= 0. */
256 double sharpe_tv; /**< Month-end-resampled equity simple returns (chart timezone,
257 * open-time bucketing), risk-free 2%/yr (2/12 per month),
258 * annualized by sqrt(12). Uses sample (N-1) stddev.
259 * NaN with <2 monthly returns or zero deviation. */
260 double sortino_tv; /**< Same resampling as sharpe_tv; uses population downside
261 * deviation vs the monthly risk-free.
262 * NaN with <2 monthly returns or zero deviation. */
263 double sharpe_bar; /**< Per-script-bar returns, annualized by observed bar density
264 * (bars per year = (len-1)/calendar span), NOT a fixed
265 * calendar formula. Uses sample (N-1) stddev.
266 * NaN with <2 returns or zero deviation. */
267 double sortino_bar; /**< Same construction as sharpe_bar over per-bar returns;
268 * uses population downside deviation.
269 * NaN with <2 returns or zero deviation. */
270 double cagr; /**< PERCENT per year: 100*((final_equity/initial_capital)^(1/years)-1).
271 * NaN when span <= 0 or either side <= 0. */
272 double calmar; /**< cagr / max_equity_drawdown_pct — BOTH IN PERCENT, so the
273 * ratio is dimensionless. NaN when drawdown is 0. */
274 double recovery_factor; /**< net_profit / max_equity_drawdown (currency / currency).
275 * NaN when drawdown is 0. */
276 double time_in_market_pct; /**< PERCENT (0-100) of script bars with an open position
277 * at bar close. */
278 double open_pl; /**< Mark-to-market open profit at the final bar. */
280
281/** Composite metrics container: trade stats (all / long / short) +
282 * equity-curve stats. */
287
288/** Single per-script-bar equity point.
289 *
290 * `time_ms` is the script-bar **open** timestamp (Unix ms).
291 * `equity` = `initial_capital` + `net_profit` + `open_profit` at bar close. */
292typedef struct pf_equity_point_s {
293 int64_t time_ms; /**< Script-bar OPEN timestamp (Unix ms). */
294 double equity; /**< initial_capital + net_profit + open_profit. */
295 double open_profit; /**< Mark-to-market open P&L at bar close. */
297
298/** Per-`request.security()` site diagnostic counters.
299 *
300 * Layout-compatible with internal `pineforge::SecurityDiagC`. */
301typedef struct pf_security_diag_s {
302 int sec_id; /**< Stable id for the request.security site. */
303 int64_t feed_count; /**< Higher-TF feed bars consumed. */
304 int64_t complete_count; /**< Evaluations on completed parent bars. */
305 int64_t partial_count; /**< Evaluations on still-forming parent bars. */
307
308/** Single per-bar trace entry.
309 *
310 * Emitted when the source script contains `// @pf-trace name=expr`
311 * pragmas and tracing is enabled via #strategy_set_trace_enabled.
312 * Layout-compatible with internal `pineforge::TraceEntryC`. */
313typedef struct pf_trace_entry_s {
314 int64_t timestamp; /**< Bar timestamp (Unix ms). */
315 int32_t bar_index; /**< Zero-based bar index. */
316 int32_t name_id; /**< Index into pf_report_t::trace_names. */
317 double value; /**< Traced expression value on this bar. */
319
320/** Backtest report filled by #run_backtest / #run_backtest_full.
321 *
322 * Layout-compatible with internal `pineforge::ReportC`.
323 *
324 * ### Ownership and lifetime
325 * The struct itself is caller-owned (typically stack). The embedded
326 * arrays (`trades`, `security_diag`, `trace`, `trace_names`,
327 * `equity_curve`) are heap-allocated by the runtime; the caller must
328 * invoke #report_free exactly once on each filled report.
329 * `trace_names` string pointers remain owned by the strategy handle
330 * until #strategy_free. */
331
332typedef struct pf_report_s {
333 /* Trades */
334 int total_trades; /**< Closed-trade count (== trades_len), including
335 * the range-end close of a position still open
336 * after the final bar (pf_trade_t::open_at_end). */
337 pf_trade_t* trades; /**< Heap array of closed trades; script-driven exits
338 * first, then the range-end rows (open_at_end=1). */
339 int trades_len; /**< Length of #trades. */
340 double net_profit; /**< Sum of all closed-trade PnL. */
341
342 /* Bar processing counts */
343 int64_t input_bars_processed; /**< Source-feed bars consumed. */
344 int64_t script_bars_processed; /**< Script-timeframe bars evaluated. */
345
346 /* Security diagnostics */
347 int64_t security_feeds_total; /**< Total higher-TF feed bars across all security sites. */
348 int64_t security_complete_total; /**< Total complete-bar evals across all security sites. */
349 int64_t security_partial_total; /**< Total partial-bar evals across all security sites. */
350
351 /* Bar magnifier diagnostics */
352 int64_t magnifier_sub_bars_total; /**< Sub-bars synthesized by the magnifier. */
353 int64_t magnifier_sample_ticks_total; /**< Sample ticks visited by the magnifier. */
354
355 /* Timeframe metadata */
356 int input_tf_seconds; /**< Detected/configured input timeframe (seconds). */
357 int script_tf_seconds; /**< Script timeframe (seconds). */
358 int script_tf_ratio; /**< script_tf_seconds / input_tf_seconds. */
359 int needs_aggregation; /**< 1 if input → script TF aggregation was performed. */
360 int bar_magnifier_enabled; /**< 1 if magnifier was active for this run. */
361
362 /* Per-security feed/eval counters */
363 pf_security_diag_t* security_diag; /**< One entry per request.security site. */
364 int security_diag_len; /**< Length of #security_diag. */
365
366 /* Per-bar trace records */
367 pf_trace_entry_t* trace; /**< Per-bar trace records (empty unless tracing enabled). */
368 int trace_len; /**< Length of #trace. */
369 const char** trace_names; /**< Names indexed by pf_trace_entry_t::name_id. */
370 int trace_names_len; /**< Length of #trace_names. */
371
372 /* Computed trading metrics. Trade-based blocks reported for all /
373 * long-only / short-only; equity-based stats are all-trades only.
374 * Loss-side fields are positive magnitudes. Undefined values are NaN
375 * (see per-field docs). */
377 /* Per-script-bar equity curve. time_ms is the script-bar OPEN
378 * timestamp; equity = initial_capital + net_profit + open_profit at
379 * bar close. Heap-allocated; freed by report_free. len ==
380 * script_bars_processed, EXCEPT after a mid-run error (check
381 * strategy_get_last_error): an exception can truncate the curve, and
382 * metrics then describe the truncated prefix. NOTE int64_t length
383 * (ctypes: c_int64). */
387
388/** @} */ /* end of pf_types */
389
390/** Opaque handle to a compiled strategy instance. */
391typedef void* pf_strategy_t;
392
393/* ───────────────────────────────────────────────────────────────────
394 * STRATEGY .SO EXPORTS — implemented per compiled strategy
395 * ───────────────────────────────────────────────────────────────────
396 *
397 * Each .so emitted by the codegen exports the following symbols. The
398 * runtime library itself does NOT define them — they are per-strategy
399 * implementations generated by the transpiler.
400 *
401 * Note on naming: these are the legacy unprefixed names retained for
402 * backward compatibility with the existing harness. Future major
403 * versions may introduce `pf_`-prefixed equivalents and deprecate the
404 * unprefixed forms.
405 */
406
407/** @defgroup pf_lifecycle Strategy lifecycle
408 * @brief Create, run, and destroy a compiled strategy instance.
409 * @{
410 *
411 * NOTE: Per-strategy symbols (strategy_create, run_backtest, etc.) are
412 * emitted by the codegen with internal C++ types (ReportC, Bar) that are
413 * layout-compatible but type-distinct from the public C PODs below.
414 * Guard with PINEFORGE_NO_STRATEGY_DECLS so engine.hpp can include this
415 * header for its POD types without conflicting with per-strategy TU
416 * definitions.
417 */
418
419#ifndef PINEFORGE_NO_STRATEGY_DECLS
420
421/** Allocate a new strategy instance.
422 *
423 * @param params_json Currently ignored; pass `NULL`.
424 * @return Strategy handle, or `NULL` on allocation failure.
425 *
426 * Caller owns the returned handle and must release it via #strategy_free. */
427PF_API pf_strategy_t strategy_create(const char* params_json);
428
429/** Release a strategy handle previously returned by #strategy_create.
430 *
431 * Safe to call with `NULL`. Invalidates any `pf_report_t::trace_names`
432 * pointers obtained from this strategy. */
434
435/** Run a backtest with auto-detected timeframe and no bar magnifier.
436 *
437 * @param s Strategy handle from #strategy_create.
438 * @param bars Non-NULL pointer to OHLCV bars (length @p n).
439 * @param n Bar count (>= 0).
440 * @param out Non-NULL output report. Fields are populated with heap
441 * allocations the caller must release via #report_free. */
443 pf_bar_t* bars,
444 int n,
445 pf_report_t* out);
446
447/** Run a backtest with explicit timeframe and magnifier configuration.
448 *
449 * @param s Strategy handle.
450 * @param bars Bar feed.
451 * @param n Bar count.
452 * @param input_tf Input timeframe ("1", "5", "15", "60", "1D", ...).
453 * Empty string → auto-detect from bar timestamps.
454 * @param script_tf Script timeframe. Empty string → defaults to @p input_tf.
455 * @param bar_magnifier Boolean (0 / non-zero) — enable bar magnifier.
456 * @param magnifier_samples Sub-bar samples per parent bar (typical: 4).
457 * @param magnifier_dist Sampling distribution (see #pf_magnifier_distribution_t).
458 * @param out Output report. Free with #report_free. */
460 pf_bar_t* bars,
461 int n,
462 const char* input_tf,
463 const char* script_tf,
464 int bar_magnifier,
465 int magnifier_samples,
466 pf_magnifier_distribution_t magnifier_dist,
467 pf_report_t* out);
468
469/** Free heap arrays attached to a filled report.
470 *
471 * Idempotent. Safe to call with `NULL` or an already-freed report.
472 * The `pf_report_t` struct itself is caller-owned. */
474
475/** @} */ /* end of pf_lifecycle */
476
477/** @defgroup pf_config Per-strategy configuration
478 * @brief Override @c input.*() values, `strategy(...)` params, and runtime knobs.
479 * @{
480 */
481
482/** Override a Pine @c input.*() value before the next run.
483 *
484 * @param s Strategy handle.
485 * @param key The input's title (or fallback identifier).
486 * @param value Serialized value — numbers as decimal strings,
487 * booleans as `"true"` / `"false"`.
488 *
489 * Calls after #run_backtest are accepted but only take effect on
490 * subsequent runs. */
492 const char* key,
493 const char* value);
494
495/** Override a `strategy(...)` declaration parameter.
496 *
497 * Recognised @p key values: `initial_capital`, `commission_value`,
498 * `default_qty_value`, `pyramiding`, `slippage`,
499 * `process_orders_on_close`, `close_entries_rule`, `default_qty_type`,
500 * `commission_type`. */
502 const char* key,
503 const char* value);
504
505/** Toggle volume-weighted bar-magnifier sampling.
506 *
507 * Has no effect unless the bar magnifier is enabled in
508 * #run_backtest_full. */
510 int on);
511
512#endif /* PINEFORGE_NO_STRATEGY_DECLS */
513
514/* ───────────────────────────────────────────────────────────────────
515 * RUNTIME LIBRARY EXPORTS — implemented in libpineforge
516 * ─────────────────────────────────────────────────────────────────── */
517
518/** Toggle per-bar trace recording. Default off (zero-cost when off).
519 *
520 * Enables capture for `// @pf-trace name=expr` pragmas already compiled
521 * into the strategy `.so`. Trace records appear in pf_report_t::trace. */
523
524/** Set the earliest Unix-ms timestamp at which strategy order commands
525 * may fire.
526 *
527 * Earlier bars still execute user code and warm TA/series state, but
528 * `strategy.entry/order/exit/close` commands are ignored. */
530
531/** @} */ /* end of pf_config */
532
533/** @addtogroup pf_lifecycle
534 * @{
535 */
536
537/** Return the physical entry incarnation for one closed-trade row.
538 *
539 * Partial-close/FIFO fragments emitted from the same physical entry share
540 * this value. Distinct broker entry objects receive distinct monotonically
541 * increasing values even when Pine reuses the same user-visible entry ID.
542 * The value is scoped to one strategy run and is intended as report
543 * provenance, not as a stable cross-run identifier.
544 *
545 * @param s Strategy handle whose most recent run filled a report.
546 * @param trade_index Zero-based row index into that report's `trades`
547 * array — the script's closed trades followed by the
548 * range-end rows (`open_at_end`, ABI v3), which carry
549 * the incarnation of the lot they mark like any other
550 * close.
551 * @return Non-zero physical-entry identity, or 0 for an invalid index or a
552 * legacy/synthetic trade without PendingOrder provenance. */
554 pf_strategy_t s, int trade_index);
555
556/** @} */ /* end of pf_lifecycle */
557
558/** @defgroup pf_streaming Historical to realtime streaming
559 * @brief Warm on confirmed OHLCV and continue the same strategy instance on
560 * normalized ordered trades from any data source.
561 * @{
562 */
563
564/** Warm a strategy with confirmed OHLCV, then switch the same instance to a
565 * realtime trade stream without resetting position, equity, pending orders,
566 * Pine variables, TA state, request.security state, or timeframe aggregation.
567 *
568 * The warmup must contain at least one complete fixed-duration input bar.
569 * Normalized ticks start at or after the next input bar's open. This
570 * lifecycle uses close-only strategy calculation (the Pine strategy default)
571 * while resting broker orders are evaluated on every normalized trade.
572 *
573 * @return 0 on success, -1 on failure. Inspect #strategy_get_last_error. */
575 const pf_bar_t* warmup_bars,
576 int n_warmup,
577 const char* input_tf,
578 const char* script_tf);
579
580/** Push one normalized realtime trade. Returns 0 on success, -1 on failure. */
582 const pf_trade_tick_t* tick);
583
584/** Push an ordered batch of realtime trades. Semantically identical to
585 * repeated #strategy_stream_push_tick calls, with lower FFI overhead. */
587 const pf_trade_tick_t* ticks,
588 int n);
589
590/** Advance the stream clock and close every input bar whose end is <= the
591 * supplied time. Quiet in-session intervals become zero-volume carry-forward
592 * bars; intervals outside the configured syminfo session are skipped. */
594
595/** End a realtime stream. When @p finalize_partial_input_bar is non-zero, the
596 * currently forming input bar is dispatched before ending; normally callers
597 * should first advance to a confirmed boundary and pass zero here. */
598PF_API int strategy_stream_end(pf_strategy_t s, int finalize_partial_input_bar);
599
600/** Snapshot the cumulative warmup + realtime report. The embedded arrays are
601 * caller-owned after return and must be released with #report_free. */
603
604/** @} */ /* end of pf_streaming */
605
606/** @addtogroup pf_config
607 * @{
608 */
609
610/** Set the strategy's chart timezone (IANA / POSIX TZ string).
611 *
612 * Pine builtins ``hour``, ``minute``, ``second``, ``dayofmonth``,
613 * ``dayofweek``, ``month``, ``year`` and ``weekofyear`` return the
614 * wall-clock for the chart's timezone — TV exports trade rows in chart
615 * TZ too. Engine bars are stored as Unix-ms (UTC), so without this
616 * override these builtins return UTC and silently diverge from TV by N
617 * hours when the chart is on a non-UTC zone (Asia/Taipei = UTC+8 is the
618 * validator default).
619 *
620 * Pass `NULL`, `""`, `"UTC"` or `"Etc/UTC"` for the legacy UTC
621 * behaviour (cheap, mutex-free). Any other value names a TZ resolved by
622 * the system tzdata; the per-bar decomposition then runs under a
623 * process-global mutex so multi-threaded harnesses don't corrupt each
624 * other's wall time.
625 *
626 * Should be called before #run_backtest / #run_backtest_full. Persists
627 * across runs on the same strategy handle until overridden. */
629
630/** Plumb the symbol's exchange timezone (IANA string) into syminfo. Feeds
631 * ``session.ismarket`` / ``time(session)`` predicates. Defaults to "UTC"
632 * (crypto). Distinct from #strategy_set_chart_timezone — the chart TZ
633 * drives wall-clock builtins and intraday-cap day rollover; this drives
634 * session membership. `NULL` is ignored. Call before #run_backtest*. */
636
637/** Set the symbol's session string (e.g. "0930-1600:23456", default
638 * "24x7"). Feeds ``session.ismarket`` / ``time(session)``. `NULL`
639 * ignored. Call before #run_backtest*. */
641
642/** Set the instrument class (``syminfo.type``: "forex", "stock", "crypto",
643 * "futures", "index", "fund", "cfd", ...; default "crypto"). Scripts branch
644 * on it for instrument conventions (e.g. the forex pip size). `NULL` /
645 * empty ignored. Call before #run_backtest*. */
647
648/** Set one of the remaining string ``syminfo.*`` members by Pine member
649 * name: "ticker", "tickerid", "currency", "basecurrency", "description",
650 * "volumetype" (and "type"). Returns 0 when set, -1 for an unknown key,
651 * empty value or NULL. Call before #run_backtest*. */
653 const char* value);
654
655/** Set the instrument tick size (``syminfo.mintick``, default 0.01). Drives the
656 * directional stop-entry snap and ``slippage = N*mintick`` economics. Set
657 * per-instrument (e.g. 0.25 for ES, 0.00001 for FX). Non-positive ignored.
658 * Call before #run_backtest*. */
660
661/** Set the instrument point value (``syminfo.pointvalue``, default 1.0) — the
662 * $-per-point-per-contract multiplier applied to every money path: realized
663 * PnL and MFE/MAE, open profit / mark-to-market equity (and the drawdown /
664 * runup extremes), percent-of-equity and cash position sizing, percent
665 * commission notionals, and the margin admission check. Set per-instrument
666 * (e.g. 50 for ES). Non-positive ignored. Call before #run_backtest*. */
668
669/** Inject a fundamental/exchange metadata value by Pine member name
670 * (e.g. "shares_outstanding_total", "target_price_average"). These have
671 * no OHLCV source; reads of un-injected members return na. Call before
672 * #run_backtest*. */
674 double value);
675
676/** Install a timestamped quote-to-account currency conversion curve.
677 *
678 * Each value is account-currency units per one unit of the symbol's quote
679 * currency and becomes active, inclusively, at the corresponding Unix-ms
680 * timestamp. The latest active value carries forward; broker events before
681 * the first point use the scalar `account_currency_fx` metadata fallback.
682 * Installing a curve also selects the converted account-currency broker
683 * ledger, including during that pre-first fallback interval; it is not
684 * equivalent to a same-currency run merely because a rate happens to be 1.
685 * Arrays are copied. Timestamps must be strictly increasing and rates
686 * positive and finite. Pass `n == 0` to clear the curve and restore scalar
687 * behavior. Timestamped curves currently support ordinary historical runs.
688 * Broker-open rate changes on margin-call-enabled carried positions are
689 * TV-pinned for 1x longs; carried shorts and leveraged positions fail closed
690 * at the crossing. Streaming, calc-on-order-fills, and bar-magnifier runs
691 * also fail closed.
692 *
693 * @return 0 on success, -1 for a null strategy or invalid arrays. */
695 pf_strategy_t s, const int64_t* effective_from_ms,
696 const double* account_per_quote, int n);
697
698#ifdef PINEFORGE_HAS_AUX_SECURITY_FEED_V1
699/** Copy a finer feed used exclusively by same-symbol request.security calls.
700 *
701 * The next ordinary #run_backtest_full call must receive native chart bars
702 * with @c input_tf equal to @c script_tf and bar magnifier disabled. Chart
703 * OHLCV, broker fills, and @c bar_index continue to advance only from that
704 * native chart feed; @p bars advance only request.security evaluators.
705 * Every auxiliary bar must map to exactly one native chart bar and every
706 * native chart bar must have at least one auxiliary bar, otherwise the run
707 * fails closed via #strategy_get_last_error. Arrays are copied. Pass
708 * @p n == 0 to clear the auxiliary feed.
709 *
710 * @return 0 on success, -1 for a null strategy or invalid input. */
712 const pf_bar_t* bars,
713 int n,
714 const char* input_tf);
715#endif
716
717/** Returns the error message captured by the most recent #run_backtest /
718 * #run_backtest_full call on this strategy.
719 *
720 * Returns an empty string when the run completed normally, or `NULL`
721 * only when `s` itself is `NULL`. The pointer is owned by the engine
722 * and remains valid until the next #run_backtest* call (which clears
723 * the captured error before it begins).
724 *
725 * The runtime catches every `std::exception` derivative inside the
726 * engine's run loop so the C ABI never unwinds a C++ exception across
727 * the `extern "C"` boundary. Consumers must check this after every
728 * run to surface engine-rejected configurations such as a script
729 * timeframe finer than the input timeframe, a `request.security`
730 * timeframe below the chart timeframe without a supported lower-TF
731 * emulation, or a missing input timeframe when securities are
732 * registered. */
734
735/** @} */ /* end of pf_config */
736
737/** @defgroup pf_version Version query
738 * @brief Runtime version metadata.
739 * @{
740 */
741
742/** Runtime version descriptor returned by #pf_version_get. */
743typedef struct pf_version_s {
744 int major; /**< Major version. */
745 int minor; /**< Minor version. */
746 int patch; /**< Patch version. */
747 const char* commit_sha; /**< Short git commit SHA, or `""` if unknown. */
749
750/** @return Linked runtime version. */
752
753/** @return Monotonic ABI version (see #PF_ABI_VERSION). */
755
756/** Full git-derived version descriptor.
757 *
758 * Returns `"MAJOR.MINOR.PATCH[-N-gSHA[-dirty]]"` for git checkouts, or
759 * plain `"MAJOR.MINOR.PATCH"` for tarball builds. The pointer is to a
760 * static string with program lifetime; do not free. */
761PF_API const char* pf_version_string(void);
762
763/** @} */ /* end of pf_version */
764
765#ifdef __cplusplus
766} /* extern "C" */
767#endif
768
769#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).
int strategy_set_aux_security_feed(pf_strategy_t s, const pf_bar_t *bars, int n, const char *input_tf)
Copy a finer feed used exclusively by same-symbol request.security calls.
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.
int strategy_set_syminfo_string(pf_strategy_t s, const char *key, const char *value)
Set one of the remaining string syminfo.
int strategy_set_account_currency_fx_series(pf_strategy_t s, const int64_t *effective_from_ms, const double *account_per_quote, int n)
Install a timestamped quote-to-account currency conversion curve.
void strategy_set_syminfo_type(pf_strategy_t s, const char *type)
Set the instrument class (syminfo.type: "forex", "stock", "crypto", "futures", "index",...
uint64_t strategy_closed_trade_entry_incarnation(pf_strategy_t s, int trade_index)
Return the physical entry incarnation for one closed-trade row.
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.
int strategy_stream_begin(pf_strategy_t s, const pf_bar_t *warmup_bars, int n_warmup, const char *input_tf, const char *script_tf)
Warm a strategy with confirmed OHLCV, then switch the same instance to a realtime trade stream withou...
int strategy_stream_push_tick(pf_strategy_t s, const pf_trade_tick_t *tick)
Push one normalized realtime trade.
int strategy_stream_push_ticks(pf_strategy_t s, const pf_trade_tick_t *ticks, int n)
Push an ordered batch of realtime trades.
int strategy_stream_advance_time(pf_strategy_t s, int64_t timestamp_ms)
Advance the stream clock and close every input bar whose end is <= the supplied time.
int strategy_stream_end(pf_strategy_t s, int finalize_partial_input_bar)
End a realtime stream.
int strategy_stream_fill_report(pf_strategy_t s, pf_report_t *out)
Snapshot the cumulative warmup + realtime report.
pf_magnifier_distribution_t
Bar-magnifier sub-bar sampling distribution.
Definition pineforge.h:101
@ PF_MAGNIFIER_FRONT_LOADED
Sample density biased toward bar open.
Definition pineforge.h:106
@ PF_MAGNIFIER_COSINE
Cosine-tapered density.
Definition pineforge.h:103
@ PF_MAGNIFIER_ENDPOINTS
Default — exact O,H,L,C points plus uniform fill between.
Definition pineforge.h:105
@ PF_MAGNIFIER_BACK_LOADED
Sample density biased toward bar close.
Definition pineforge.h:107
@ PF_MAGNIFIER_TRIANGLE
Triangle-tapered density.
Definition pineforge.h:104
@ PF_MAGNIFIER_UNIFORM
Uniform spacing across the parent bar.
Definition pineforge.h:102
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:391
#define PF_API
Definition pineforge.h:65
Single OHLCV bar pushed into the engine.
Definition pineforge.h:113
double volume
Bar volume.
Definition pineforge.h:118
double high
High price.
Definition pineforge.h:115
double low
Low price.
Definition pineforge.h:116
double close
Close price.
Definition pineforge.h:117
double open
Open price.
Definition pineforge.h:114
int64_t timestamp
Bar open time, Unix milliseconds.
Definition pineforge.h:119
Single per-script-bar equity point.
Definition pineforge.h:292
double open_profit
Mark-to-market open P&L at bar close.
Definition pineforge.h:295
double equity
initial_capital + net_profit + open_profit.
Definition pineforge.h:294
int64_t time_ms
Script-bar OPEN timestamp (Unix ms).
Definition pineforge.h:293
Equity-curve-derived statistics (all-trades only, like TV).
Definition pineforge.h:245
double sharpe_tv
Month-end-resampled equity simple returns (chart timezone, open-time bucketing), risk-free 2%/yr (2/1...
Definition pineforge.h:256
double time_in_market_pct
PERCENT (0-100) of script bars with an open position at bar close.
Definition pineforge.h:276
double max_equity_drawdown_pct
max_equity_drawdown relative to the peak in effect (PERCENT 0-100).
Definition pineforge.h:247
double max_equity_drawdown
Peak-to-trough equity drop, positive currency magnitude.
Definition pineforge.h:246
double max_equity_runup_pct
max_equity_runup relative to that trough (PERCENT 0-100).
Definition pineforge.h:251
double buy_hold_return
initial_capital * (last_close/first_open - 1), currency.
Definition pineforge.h:252
double open_pl
Mark-to-market open profit at the final bar.
Definition pineforge.h:278
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:249
double sortino_tv
Same resampling as sharpe_tv; uses population downside deviation vs the monthly risk-free.
Definition pineforge.h:260
double cagr
PERCENT per year: 100*((final_equity/initial_capital)^(1/years)-1).
Definition pineforge.h:270
double recovery_factor
net_profit / max_equity_drawdown (currency / currency).
Definition pineforge.h:274
double calmar
cagr / max_equity_drawdown_pct — BOTH IN PERCENT, so the ratio is dimensionless.
Definition pineforge.h:272
double buy_hold_return_pct
buy_hold_return as PERCENT.
Definition pineforge.h:254
double sharpe_bar
Per-script-bar returns, annualized by observed bar density (bars per year = (len-1)/calendar span),...
Definition pineforge.h:263
double sortino_bar
Same construction as sharpe_bar over per-bar returns; uses population downside deviation.
Definition pineforge.h:267
Composite metrics container: trade stats (all / long / short) + equity-curve stats.
Definition pineforge.h:283
pf_trade_stats_t all
Definition pineforge.h:284
pf_equity_stats_t equity
Definition pineforge.h:285
pf_trade_stats_t longs
Definition pineforge.h:284
pf_trade_stats_t shorts
Definition pineforge.h:284
Backtest report filled by run_backtest / run_backtest_full.
Definition pineforge.h:332
int input_tf_seconds
Detected/configured input timeframe (seconds).
Definition pineforge.h:356
int security_diag_len
Length of security_diag.
Definition pineforge.h:364
int64_t security_feeds_total
Total higher-TF feed bars across all security sites.
Definition pineforge.h:347
int bar_magnifier_enabled
1 if magnifier was active for this run.
Definition pineforge.h:360
pf_trace_entry_t * trace
Per-bar trace records (empty unless tracing enabled).
Definition pineforge.h:367
int trace_names_len
Length of trace_names.
Definition pineforge.h:370
int64_t input_bars_processed
Source-feed bars consumed.
Definition pineforge.h:343
int script_tf_seconds
Script timeframe (seconds).
Definition pineforge.h:357
double net_profit
Sum of all closed-trade PnL.
Definition pineforge.h:340
int64_t equity_curve_len
Definition pineforge.h:385
int trades_len
Length of trades.
Definition pineforge.h:339
pf_metrics_t metrics
Definition pineforge.h:376
int trace_len
Length of trace.
Definition pineforge.h:368
int64_t script_bars_processed
Script-timeframe bars evaluated.
Definition pineforge.h:344
int64_t magnifier_sample_ticks_total
Sample ticks visited by the magnifier.
Definition pineforge.h:353
int total_trades
Closed-trade count (== trades_len), including the range-end close of a position still open after the ...
Definition pineforge.h:334
int64_t security_partial_total
Total partial-bar evals across all security sites.
Definition pineforge.h:349
pf_security_diag_t * security_diag
One entry per request.security site.
Definition pineforge.h:363
int64_t magnifier_sub_bars_total
Sub-bars synthesized by the magnifier.
Definition pineforge.h:352
const char ** trace_names
Names indexed by pf_trace_entry_t::name_id.
Definition pineforge.h:369
pf_equity_point_t * equity_curve
Definition pineforge.h:384
int64_t security_complete_total
Total complete-bar evals across all security sites.
Definition pineforge.h:348
int script_tf_ratio
script_tf_seconds / input_tf_seconds.
Definition pineforge.h:358
int needs_aggregation
1 if input → script TF aggregation was performed.
Definition pineforge.h:359
pf_trade_t * trades
Heap array of closed trades; script-driven exits first, then the range-end rows (open_at_end=1).
Definition pineforge.h:337
Per-request.security() site diagnostic counters.
Definition pineforge.h:301
int sec_id
Stable id for the request.security site.
Definition pineforge.h:302
int64_t feed_count
Higher-TF feed bars consumed.
Definition pineforge.h:303
int64_t complete_count
Evaluations on completed parent bars.
Definition pineforge.h:304
int64_t partial_count
Evaluations on still-forming parent bars.
Definition pineforge.h:305
Single per-bar trace entry.
Definition pineforge.h:313
double value
Traced expression value on this bar.
Definition pineforge.h:317
int64_t timestamp
Bar timestamp (Unix ms).
Definition pineforge.h:314
int32_t name_id
Index into pf_report_t::trace_names.
Definition pineforge.h:316
int32_t bar_index
Zero-based bar index.
Definition pineforge.h:315
Trade-level statistics block — computed once each for all / long / short.
Definition pineforge.h:182
double avg_win_pct
Mean of per-trade pnl_pct over winning trades.
Definition pineforge.h:205
double avg_win
gross_profit / num_wins.
Definition pineforge.h:204
double net_profit_pct
net_profit as a percent of initial capital (0-100 scale).
Definition pineforge.h:192
double largest_loss_pct
Maximum of -pnl_pct over losing trades (positive magnitude) — an INDEPENDENT maximum,...
Definition pineforge.h:222
int32_t num_losses
Trades with pnl < 0.
Definition pineforge.h:185
double gross_profit
Sum of winning pnl.
Definition pineforge.h:194
int32_t max_consecutive_losses
Longest losing run; even trades reset both streaks.
Definition pineforge.h:231
double avg_loss_pct
Mean of the NEGATED pnl_pct of the losing trades.
Definition pineforge.h:209
double commission_paid
Sum of pf_trade_t::commission in the block.
Definition pineforge.h:227
double percent_profitable
100 * num_wins / num_trades, in PERCENT (0-100).
Definition pineforge.h:189
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:216
double gross_loss
Sum of |losing pnl| — POSITIVE magnitude (TV display convention).
Definition pineforge.h:197
double gross_profit_pct
gross_profit as a percent of initial capital (0-100 scale).
Definition pineforge.h:195
double largest_loss
Single largest |pnl| among losing trades (positive magnitude).
Definition pineforge.h:220
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:236
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:239
double profit_factor
gross_profit / gross_loss.
Definition pineforge.h:200
double avg_loss
gross_loss / num_losses (positive magnitude).
Definition pineforge.h:207
double largest_win
Single largest pnl among winning trades.
Definition pineforge.h:214
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:232
double gross_loss_pct
gross_loss as a percent of initial capital (0-100 scale).
Definition pineforge.h:198
int32_t max_consecutive_wins
Longest winning run; even trades reset both streaks.
Definition pineforge.h:230
double expectancy
(num_wins/num_trades)*avg_win - (num_losses/num_trades)*avg_loss, account currency per trade.
Definition pineforge.h:228
double net_profit
Sum of pnl (account currency, net of commission).
Definition pineforge.h:191
int32_t num_trades
Closed trades in this block (all / long-only / short-only).
Definition pineforge.h:183
double avg_trade_pct
Mean of per-trade pnl_pct over all trades.
Definition pineforge.h:202
double ratio_avg_win_avg_loss
avg_win / avg_loss.
Definition pineforge.h:213
double avg_trade
net_profit / num_trades.
Definition pineforge.h:201
int32_t num_even
Trades with pnl == 0.0 exactly; breaks both win and loss streaks; excluded from win/loss averages.
Definition pineforge.h:186
int32_t num_wins
Trades with pnl > 0.
Definition pineforge.h:184
Closed-trade record returned in pf_report_t::trades.
Definition pineforge.h:139
int32_t exit_bar_index
Script-bar index of the exit fill (0-based).
Definition pineforge.h:160
double pnl_pct
Net return-on-cost in percent: pnl (NET of commission) / entry cost (entry_price * qty * pointvalue) ...
Definition pineforge.h:145
double exit_price
Exit fill price (incl.
Definition pineforge.h:143
int32_t open_at_end
1 when this row is the RANGE-END close of a position that was still open after the final bar; 0 for a...
Definition pineforge.h:161
int32_t entry_bar_index
Script-bar index of the entry fill (0-based).
Definition pineforge.h:159
double commission
Entry+exit commission actually deducted from pnl (account currency).
Definition pineforge.h:157
double pnl
Net realized PnL in account currency (commission-inclusive).
Definition pineforge.h:144
int is_long
1 if long, 0 if short.
Definition pineforge.h:153
double max_drawdown
Peak adverse price travel during the trade ($/unit qty).
Definition pineforge.h:155
double qty
Filled quantity.
Definition pineforge.h:156
double max_runup
Peak favorable price travel during the trade ($/unit qty).
Definition pineforge.h:154
int64_t entry_time
Entry fill time (Unix ms).
Definition pineforge.h:140
double entry_price
Entry fill price (incl.
Definition pineforge.h:142
int64_t exit_time
Exit fill time (Unix ms).
Definition pineforge.h:141
One provider-neutral realtime executed-trade update.
Definition pineforge.h:129
double quantity
Traded quantity in symbol volume units (>= 0).
Definition pineforge.h:133
uint64_t sequence
Normalized per-stream sequence, or 0.
Definition pineforge.h:131
int64_t timestamp
Source event time, Unix milliseconds.
Definition pineforge.h:130
double price
Executed trade price (> 0).
Definition pineforge.h:132
Runtime version descriptor returned by pf_version_get.
Definition pineforge.h:743
int patch
Patch version.
Definition pineforge.h:746
int minor
Minor version.
Definition pineforge.h:745
int major
Major version.
Definition pineforge.h:744
const char * commit_sha
Short git commit SHA, or "" if unknown.
Definition pineforge.h:747