PineForge unreleased
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/* Public C words with enum types must retain their published width even when
49 * a consumer enables -fshort-enums. The C99 fallback keeps the strict-C99
50 * header build valid; the two newer languages get a native static assertion. */
51#if defined(__cplusplus)
52#define PF_STATIC_ASSERT(condition) static_assert((condition), #condition)
53#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
54#define PF_STATIC_ASSERT(condition) _Static_assert((condition), #condition)
55#else
56#define PF_STATIC_ASSERT_JOIN_(a, b) a##b
57#define PF_STATIC_ASSERT_JOIN(a, b) PF_STATIC_ASSERT_JOIN_(a, b)
58#define PF_STATIC_ASSERT(condition) \
59 typedef char PF_STATIC_ASSERT_JOIN(pf_static_assertion_, __LINE__)[(condition) ? 1 : -1]
60#endif
61
62/* ── Version ─────────────────────────────────────────────────────── */
63/* Macros (PINEFORGE_VERSION_MAJOR / _MINOR / _PATCH / _STRING / _FULL,
64 * PINEFORGE_GIT_SHA) live in the generated <pineforge/version.h>. */
65#include <pineforge/version.h>
66
67/* pf_pending_order_v1_t / pf_field_desc_t -- the generated, C-compatible POD
68 * mirror of the engine's resting-order record (ABI v4, task 7). Regenerate
69 * with scripts/gen_pending_order_mirror.py; never edit by hand. */
71
72/* ── Visibility ──────────────────────────────────────────────────── */
73
74#if defined(_WIN32) || defined(__CYGWIN__)
75 #if defined(PINEFORGE_BUILD_SHARED)
76 #define PF_API __declspec(dllexport)
77 #else
78 #define PF_API __declspec(dllimport)
79 #endif
80#elif defined(__GNUC__) || defined(__clang__)
81 #define PF_API __attribute__((visibility("default")))
82#else
83 #define PF_API
84#endif
85
86/** Monotonic strategy-module compatibility version. Bumped for layout growth
87 * or a required module rebuild. Consumers MUST verify
88 * pf_abi_version() == PF_ABI_VERSION before calling run_backtest.
89 * pf_report_t is caller-allocated: growth silently corrupts an undersized buffer.
90 * pf_trade_t is runtime-allocated: growth changes the array stride.
91 * Value 2 introduced metrics and the equity curve. Modules predating this
92 * macro have no pf_abi_version symbol; treat dlsym failure as version 1.
93 * Value 3 appended pf_trade_t::open_at_end (the range-end close
94 * flag); a v2 reader would misindex every row after the first.
95 * Value 4 appended live-runtime accessors and the per-bar broker-state
96 * hash array to pf_report_t.
97 * Value 5 requires a 1.8.0 module rebuild for the native order layouts,
98 * without growing the caller-allocated C report/trade layouts. */
99#define PF_ABI_VERSION 5
100
101/** Feature probe for the opt-in split chart/request.security feed boundary.
102 * When defined, #strategy_set_aux_security_feed is available. */
103#define PINEFORGE_HAS_AUX_SECURITY_FEED_V1 1
104
105/** Feature probe for native higher-timeframe request.security feeds.
106 * When defined, #strategy_set_native_security_feed is available. */
107#define PINEFORGE_HAS_NATIVE_SECURITY_FEED_V1 1
108
109/** Feature probe for immutable native-run account-currency FX curves.
110 * When defined, #strategy_configure_native_fx_curve_v1 is available. */
111#define PINEFORGE_HAS_NATIVE_FX_CURVE_V1 1
112
113#ifdef __cplusplus
114extern "C" {
115#endif
116
117/** @defgroup pf_types Types
118 * @brief POD types and enums passed across the C ABI.
119 * @{
120 */
121
122/** Bar-magnifier sub-bar sampling distribution.
123 *
124 * Selects how intra-bar synthetic ticks are placed when the bar
125 * magnifier is enabled in #run_backtest_full. Layout-compatible with the
126 * internal C++ `pineforge::MagnifierDistribution` enum class — a
127 * `static_assert` in `c_abi.cpp` guarantees the integer values match. */
128typedef enum pf_magnifier_distribution_e {
129 PF_MAGNIFIER_UNIFORM = 0, /**< Uniform spacing across the parent bar. */
130 PF_MAGNIFIER_COSINE = 1, /**< Cosine-tapered density. */
131 PF_MAGNIFIER_TRIANGLE = 2, /**< Triangle-tapered density. */
132 PF_MAGNIFIER_ENDPOINTS = 3, /**< Default — exact O,H,L,C points plus uniform fill between. */
133 PF_MAGNIFIER_FRONT_LOADED = 4, /**< Sample density biased toward bar open. */
134 PF_MAGNIFIER_BACK_LOADED = 5 /**< Sample density biased toward bar close. */
136
137/** Single OHLCV bar pushed into the engine.
138 *
139 * Layout-compatible with the internal C++ `pineforge::Bar` struct. */
140typedef struct pf_bar_s {
141 double open; /**< Open price. */
142 double high; /**< High price. */
143 double low; /**< Low price. */
144 double close; /**< Close price. */
145 double volume; /**< Bar volume. */
146 int64_t timestamp; /**< Bar open time, Unix milliseconds. */
147} pf_bar_t;
148
149/** One provider-neutral realtime executed-trade update.
150 *
151 * `sequence` is optional: pass 0 when the normalized source has no stable
152 * ordering key. Non-zero values must increase strictly within a stream.
153 * `quantity` is expressed in the configured symbol's volume units and is
154 * accumulated into the input bar's volume. The source adapter owns all
155 * provider-specific fields and normalization. */
156typedef struct pf_trade_tick_s {
157 int64_t timestamp; /**< Source event time, Unix milliseconds. */
158 uint64_t sequence; /**< Normalized per-stream sequence, or 0. */
159 double price; /**< Executed trade price (> 0). */
160 double quantity; /**< Traded quantity in symbol volume units (>= 0). */
162
163/** Closed-trade record returned in pf_report_t::trades.
164 *
165 * Layout-compatible with internal `pineforge::TradeC`. */
166typedef struct pf_trade_s {
167 int64_t entry_time; /**< Entry fill time (Unix ms). */
168 int64_t exit_time; /**< Exit fill time (Unix ms). */
169 double entry_price; /**< Entry fill price (incl. slippage). */
170 double exit_price; /**< Exit fill price (incl. slippage). */
171 double pnl; /**< Net realized PnL in account currency (commission-inclusive). */
172 double pnl_pct; /**< Net return-on-cost in percent: pnl (NET of commission) /
173 * entry cost (entry_price * qty * pointvalue) * 100. This is
174 * TradingView's "Net P&L %" convention (synthetic one-unit
175 * short: 90.00 net PnL on a 2000.00 entry cost =>
176 * 4.50%). Degenerates to the old gross
177 * (exit/entry-1)*100 form for longs with zero commission;
178 * the previous short form (entry/exit-1)*100 was wrong on
179 * large moves. Sign always matches pnl. */
180 int is_long; /**< 1 if long, 0 if short. */
181 double max_runup; /**< Peak favorable whole-trade excursion, account currency, net of entry fees. */
182 double max_drawdown; /**< Peak adverse whole-trade excursion, account currency, net of entry fees. */
183 double qty; /**< Filled quantity. */
184 double commission; /**< Entry+exit commission actually deducted from pnl
185 * (account currency). pnl is already net of this. */
186 int32_t entry_bar_index;/**< Script-bar index of the entry fill (0-based). */
187 int32_t exit_bar_index; /**< Script-bar index of the exit fill (0-based). */
188 int32_t open_at_end; /**< 1 when this row is the RANGE-END close of a position
189 * that was still open after the final bar; 0 for an
190 * exit the script or a bracket produced. TradingView's
191 * deep-backtest report does not leave the last position
192 * open: it reports it as a closed trade whose exit leg is
193 * the range's last bar at that bar's CLOSE, with an
194 * empty exit Signal, and counts it in closedTrades
195 * (synthetic NYSE:F daily example: Entry short @ 25.00,
196 * Exit @ 26.00 on the final daily bar = the last close,
197 * closedTrades:1). The engine emulates that row
198 * (operator decision 2026-09-02): exit_time is the last
199 * script bar's label, exit_price its mintick-rounded
200 * close with no slippage, commission per the strategy's
201 * rules, pnl/pnl_pct/excursions as for any close.
202 * Appended in ABI v3. */
203} pf_trade_t;
204
205/** Trade-level statistics block — computed once each for all / long / short.
206 *
207 * Loss-side fields (`gross_loss`, `avg_loss`, `largest_loss`) are
208 * **positive magnitudes** (absolute values of the underlying negative PnL). */
209typedef struct pf_trade_stats_s {
210 int32_t num_trades; /**< Closed trades in this block (all / long-only / short-only). */
211 int32_t num_wins; /**< Trades with pnl > 0. */
212 int32_t num_losses; /**< Trades with pnl < 0. */
213 int32_t num_even; /**< Trades with pnl == 0.0 exactly; breaks both win and loss
214 * streaks; excluded from win/loss averages.
215 * Invariant: num_trades == num_wins + num_losses + num_even. */
216 double percent_profitable; /**< 100 * num_wins / num_trades, in PERCENT (0-100).
217 * NaN when num_trades == 0. */
218 double net_profit; /**< Sum of pnl (account currency, net of commission). */
219 double net_profit_pct; /**< net_profit as a percent of initial capital (0-100 scale).
220 * NaN when initial capital <= 0. */
221 double gross_profit; /**< Sum of winning pnl. */
222 double gross_profit_pct; /**< gross_profit as a percent of initial capital (0-100 scale).
223 * NaN when initial capital <= 0. */
224 double gross_loss; /**< Sum of |losing pnl| — POSITIVE magnitude (TV display convention). */
225 double gross_loss_pct; /**< gross_loss as a percent of initial capital (0-100 scale).
226 * NaN when initial capital <= 0. */
227 double profit_factor; /**< gross_profit / gross_loss. NaN when gross_loss == 0. */
228 double avg_trade; /**< net_profit / num_trades. NaN when num_trades == 0. */
229 double avg_trade_pct; /**< Mean of per-trade pnl_pct over all trades.
230 * NaN when num_trades == 0. */
231 double avg_win; /**< gross_profit / num_wins. NaN when num_wins == 0. */
232 double avg_win_pct; /**< Mean of per-trade pnl_pct over winning trades.
233 * NaN when num_wins == 0. */
234 double avg_loss; /**< gross_loss / num_losses (positive magnitude).
235 * NaN when num_losses == 0. */
236 double avg_loss_pct; /**< Mean of the NEGATED pnl_pct of the losing trades. Since
237 * pnl_pct is net return-on-cost (sign matches pnl), this is
238 * a genuinely POSITIVE magnitude. Basis = pf_trade_t::pnl_pct.
239 * NaN when num_losses == 0. */
240 double ratio_avg_win_avg_loss; /**< avg_win / avg_loss. NaN unless both sides non-empty. */
241 double largest_win; /**< Single largest pnl among winning trades.
242 * NaN when num_wins == 0. */
243 double largest_win_pct; /**< Maximum pnl_pct over winning trades — an INDEPENDENT
244 * maximum, not the pct of the largest-USD win (TV convention,
245 * validated 2026-06-12 vs TV export).
246 * NaN when num_wins == 0. */
247 double largest_loss; /**< Single largest |pnl| among losing trades (positive magnitude).
248 * NaN when num_losses == 0. */
249 double largest_loss_pct; /**< Maximum of -pnl_pct over losing trades (positive magnitude) —
250 * an INDEPENDENT maximum, not the pct of the largest-USD loss
251 * (TV convention, validated 2026-06-12 vs TV export: All
252 * "Largest loss %" came from a different trade than the
253 * largest USD loss). NaN when num_losses == 0. */
254 double commission_paid; /**< Sum of pf_trade_t::commission in the block. */
255 double expectancy; /**< (num_wins/num_trades)*avg_win - (num_losses/num_trades)*avg_loss,
256 * account currency per trade. NaN when num_trades == 0. */
257 int32_t max_consecutive_wins; /**< Longest winning run; even trades reset both streaks. */
258 int32_t max_consecutive_losses; /**< Longest losing run; even trades reset both streaks. */
259 double avg_bars_in_trade; /**< Mean of (exit_bar_index - entry_bar_index + 1) in SCRIPT
260 * bars, over all trades — inclusive of the entry bar (TV
261 * convention, validated 2026-06-12).
262 * NaN when num_trades == 0. */
263 double avg_bars_in_wins; /**< Mean bar duration of winning trades, inclusive of the entry
264 * bar (TV convention, validated 2026-06-12).
265 * NaN when num_wins == 0. */
266 double avg_bars_in_losses; /**< Mean bar duration of losing trades, inclusive of the entry
267 * bar (TV convention, validated 2026-06-12).
268 * NaN when num_losses == 0. */
270
271/** Equity-curve-derived statistics (all-trades only, like TV). */
272typedef struct pf_equity_stats_s {
273 double max_equity_drawdown; /**< Peak-to-trough equity drop, positive currency magnitude. */
274 double max_equity_drawdown_pct; /**< max_equity_drawdown relative to the peak in effect
275 * (PERCENT 0-100). */
276 double max_equity_runup; /**< Trough-to-peak rise where the trough resets on each new
277 * equity peak (mirrors the engine's intra-run extremes). */
278 double max_equity_runup_pct; /**< max_equity_runup relative to that trough (PERCENT 0-100). */
279 double buy_hold_return; /**< initial_capital * (last_close/first_open - 1), currency.
280 * NaN when first chart open is non-finite or <= 0. */
281 double buy_hold_return_pct; /**< buy_hold_return as PERCENT.
282 * NaN when first chart open is non-finite or <= 0. */
283 /** Month-end-resampled equity simple returns (chart timezone, open-time
284 * bucketing), risk-free 2%/yr (2/12 per month), annualized by sqrt(12).
285 * Uses sample (N-1) stddev. NaN with <2 monthly returns or zero deviation.
286 *
287 * Its pre-1.0 spelling `sharpe_tv` named this same double at this same
288 * offset (48) and was removed for 1.0; the serialized report key is still
289 * `sharpe_tv` (report-schema name). */
291 /** Same resampling as sharpe_monthly; uses population downside deviation
292 * vs the monthly risk-free. NaN with <2 monthly returns or zero deviation.
293 *
294 * Its pre-1.0 spelling `sortino_tv` (offset 56) was removed the same way;
295 * the serialized report key is still `sortino_tv`. */
297 double sharpe_bar; /**< Per-script-bar returns, annualized by observed bar density
298 * (bars per year = (len-1)/calendar span), NOT a fixed
299 * calendar formula. Uses sample (N-1) stddev.
300 * NaN with <2 returns or zero deviation. */
301 double sortino_bar; /**< Same construction as sharpe_bar over per-bar returns;
302 * uses population downside deviation.
303 * NaN with <2 returns or zero deviation. */
304 double cagr; /**< PERCENT per year: 100*((final_equity/initial_capital)^(1/years)-1).
305 * NaN when span <= 0 or either side <= 0. */
306 double calmar; /**< cagr / max_equity_drawdown_pct — BOTH IN PERCENT, so the
307 * ratio is dimensionless. NaN when drawdown is 0. */
308 double recovery_factor; /**< net_profit / max_equity_drawdown (currency / currency).
309 * NaN when drawdown is 0. */
310 double time_in_market_pct; /**< PERCENT (0-100) of script bars with an open position
311 * at bar close. */
312 double open_pl; /**< Mark-to-market open profit at the final bar. */
314
315/** Composite metrics container: trade stats (all / long / short) +
316 * equity-curve stats. */
321
322/** Single per-script-bar equity point.
323 *
324 * `time_ms` is the script-bar **open** timestamp (Unix ms).
325 * `equity` = `initial_capital` + `net_profit` + `open_profit` at bar close. */
326typedef struct pf_equity_point_s {
327 int64_t time_ms; /**< Script-bar OPEN timestamp (Unix ms). */
328 double equity; /**< initial_capital + net_profit + open_profit. */
329 double open_profit; /**< Mark-to-market open P&L at bar close. */
331
332/** Per-`request.security()` site diagnostic counters.
333 *
334 * Layout-compatible with internal `pineforge::SecurityDiagC`. */
335typedef struct pf_security_diag_s {
336 int sec_id; /**< Stable id for the request.security site. */
337 int64_t feed_count; /**< Higher-TF feed bars consumed. */
338 int64_t complete_count; /**< Evaluations on completed parent bars. */
339 int64_t partial_count; /**< Evaluations on still-forming parent bars. */
341
342/** Single per-bar trace entry.
343 *
344 * Emitted when the source script contains `// @pf-trace name=expr`
345 * pragmas and tracing is enabled via #strategy_set_trace_enabled.
346 * Layout-compatible with internal `pineforge::TraceEntryC`. */
347typedef struct pf_trace_entry_s {
348 int64_t timestamp; /**< Bar timestamp (Unix ms). */
349 int32_t bar_index; /**< Zero-based bar index. */
350 int32_t name_id; /**< Index into pf_report_t::trace_names. */
351 double value; /**< Traced expression value on this bar. */
353
354/** Backtest report filled by #run_backtest / #run_backtest_full.
355 *
356 * Layout-compatible with internal `pineforge::ReportC`.
357 *
358 * ### Ownership and lifetime
359 * The struct itself is caller-owned (typically stack). The embedded
360 * arrays (`trades`, `security_diag`, `trace`, `trace_names`,
361 * `equity_curve`, `broker_state_hash`) are heap-allocated by the
362 * runtime; the caller frees each filled report exactly once, with #report_free
363 * or, on the kernel archive, #strategy_native_report_free_v1. `trace_names`
364 * strings stay owned by the strategy handle until #strategy_free. */
365
366typedef struct pf_report_s {
367 /* Trades */
368 int total_trades; /**< Logical closed-trade count, including
369 * the range-end close of a position still open
370 * after the final bar. Equals trades_len on the
371 * owning-array route; with a sink trades_len is 0.
372 * The opt-in report envelope declares capture
373 * separately in <pineforge/report_mode.h>. */
374 pf_trade_t* trades; /**< Heap array of closed trades; script-driven exits
375 * first, then the range-end rows (open_at_end=1). */
376 int trades_len; /**< Resident length of #trades, not the logical total. */
377 double net_profit; /**< Sum of all closed-trade PnL. */
378
379 /* Bar processing counts */
380 int64_t input_bars_processed; /**< Source-feed bars consumed. */
381 int64_t script_bars_processed; /**< Script-timeframe bars evaluated. */
382
383 /* Security diagnostics */
384 int64_t security_feeds_total; /**< Total higher-TF feed bars across all security sites. */
385 int64_t security_complete_total; /**< Total complete-bar evals across all security sites. */
386 int64_t security_partial_total; /**< Total partial-bar evals across all security sites. */
387
388 /* Bar magnifier diagnostics */
389 int64_t magnifier_sub_bars_total; /**< Sub-bars synthesized by the magnifier. */
390 int64_t magnifier_sample_ticks_total; /**< Sample ticks visited by the magnifier. */
391
392 /* Timeframe metadata */
393 int input_tf_seconds; /**< Detected/configured input timeframe (seconds). */
394 int script_tf_seconds; /**< Script timeframe (seconds). */
395 int script_tf_ratio; /**< script_tf_seconds / input_tf_seconds. */
396 int needs_aggregation; /**< 1 if input → script TF aggregation was performed. */
397 int bar_magnifier_enabled; /**< 1 if magnifier was active for this run. */
398
399 /* Per-security feed/eval counters */
400 pf_security_diag_t* security_diag; /**< One entry per request.security site. */
401 int security_diag_len; /**< Length of #security_diag. */
402
403 /* Per-bar trace records */
404 pf_trace_entry_t* trace; /**< Per-bar trace records (empty unless tracing enabled). */
405 int trace_len; /**< Length of #trace. */
406 const char** trace_names; /**< Names indexed by pf_trace_entry_t::name_id. */
407 int trace_names_len; /**< Length of #trace_names. */
408
409 /* Computed trading metrics. Trade-based blocks reported for all /
410 * long-only / short-only; equity-based stats are all-trades only.
411 * Loss-side fields are positive magnitudes. Undefined values are NaN
412 * (see per-field docs). */
414 /* Per-script-bar equity curve (bare host: KernelRecorded); time_ms is the script-bar OPEN
415 * timestamp; equity = initial_capital + net_profit + open_profit at
416 * bar close. Free via #report_free or #strategy_native_report_free_v1.
417 * On a completed KernelRecorded run len == script_bars_processed; a
418 * Failed or Aborted run can expose a shorter prefix (check
419 * strategy_get_last_error), and metrics describe that prefix. NOTE int64_t
420 * length (ctypes: c_int64). With a report sink: NULL / 0 length.
421 * The opt-in <pineforge/report_mode.h> envelope exposes logical point
422 * count and capture flags separately; this field is resident length. */
425 /* Per-script-bar broker-state hash when recording is on and report
426 * points exist (KernelRecorded for a bare native host); freed by
427 * #report_free or #strategy_native_report_free_v1. NULL / 0-length when off or no points were recorded.
428 * On a completed run, len == script_bars_processed and the last row
429 * equals #strategy_broker_state_hash after the run for Pine and bare
430 * native hosts alike: each point latches the continuation, and the
431 * scalar folds the last latch. A Failed or Aborted run can have a
432 * shorter recorded prefix; its last row need not equal the scalar.
433 * ABI v4. With a report sink: NULL / 0 length. */
437
438/** @} */ /* end of pf_types */
439
440/** Opaque handle to a compiled strategy instance. */
441typedef void* pf_strategy_t;
442
443/** @defgroup pf_lifecycle Strategy lifecycle
444 * @brief Create, run, and destroy a compiled strategy instance.
445 * @{
446 *
447 * NOTE: Per-strategy symbols (strategy_create, run_backtest, etc.) are
448 * emitted by the codegen with internal C++ types (ReportC, Bar) that are
449 * layout-compatible but type-distinct from the public C PODs below.
450 * Guard with PINEFORGE_NO_STRATEGY_DECLS so engine.hpp can include this
451 * header for its POD types without conflicting with per-strategy TU
452 * definitions.
453 */
454
455/** What #strategy_execution_contract answers for a valid handle. */
456typedef enum pf_execution_contract_e {
457 PF_EXECUTION_CONTRACT_LEGACY = 1, /**< The retired legacy execution path. */
458 PF_EXECUTION_CONTRACT_NATIVE_MARKET_V1 = 2 /**< The native market kernel; every
459 * handle this runtime creates. */
461
462/** Native execution contract query: a #pf_execution_contract_t, or -1 for a
463 * NULL handle. Absence of this symbol on a known ABI4/stream-v1 library
464 * means legacy. */
466
467/** The bits of #pf_native_run_spec_v1::optional_mask: each names the one
468 * optional field it makes meaningful. A bit outside this set is refused. */
469typedef enum pf_native_spec_optional_e {
470 PF_NATIVE_SPEC_OPTIONAL_QUANTITY_GRID = 1u << 0, /**< `quantity_grid`. */
471 PF_NATIVE_SPEC_OPTIONAL_MAX_ABS_UNITS = 1u << 1, /**< `max_abs_units`. */
472 PF_NATIVE_SPEC_OPTIONAL_INITIAL_MARGIN_FRACTION = 1u << 2, /**< `initial_margin_fraction`. */
473 PF_NATIVE_SPEC_OPTIONAL_MAX_OPEN_LOTS = 1u << 3 /**< `max_open_lots`. */
475
476/** Versioned native run specification. All string pointers are non-null.
477 * session may be empty (all-day literal). session_key, timeframes, timezone
478 * and tickerid are nonempty. `optional_mask` carries
479 * #pf_native_spec_optional_t bits.
480 *
481 * Its three enum-valued words are named in <pineforge/native_c_api.h>:
482 * `fee_kind` is a #pf_native_fee_kind_t, `close_execution` a
483 * #pf_native_close_execution_t and `allowed_open_directions` a
484 * #pf_native_open_directions_t (zero-filled, that word admits no opening at
485 * all). #strategy_configure_native_v1 hands the whole value to the kernel,
486 * which answers an invalid spec with -1 and a Failed native handle
487 * (PF_NATIVE_FAILURE_INVALID_SPECIFICATION). A Ready handle still fails by
488 * the kernel's Contract rule. A Running handle is refused at the C boundary
489 * without changing its native state, including from a callback. A Completed
490 * handle or one Failed by a cooperative abort may be configured for another
491 * run outside callbacks; the kernel judges reuse constraints (an aborted
492 * handle is reused with the same session key and a higher run number), and a
493 * handle whose configure call failed remains Failed. A Completed
494 * host's `on_hash_extension` callback cannot reconfigure it during a read-only hash query.
495 * This call has no typed out-parameters. The
496 * extended path validates before configuring: #strategy_configure_native_ext_v1
497 * keeps the handle Unconfigured on a validation refusal, and
498 * #strategy_configure_native_ext_result_v1 additionally writes the exact
499 * #pf_native_spec_error_t / #pf_native_spec_field_t pair. */
512
513/** Apply a native v1 specification. Returns 0 on success, -1 on failure.
514 * Legacy handles refuse without native state mutation. */
516
517/** Immutable timestamped account-currency FX curve for a native run.
518 * The two arrays have exactly @c n elements and are copied by the runtime. */
519typedef struct pf_native_fx_curve_v1 {
520 uint32_t struct_size;
521 uint32_t n;
522 const int64_t* effective_from_ms;
523 const double* account_per_quote;
525
526/** Why a native FX curve was refused — the C twin of
527 * `pineforge::NativeFxCurveError`. The typed additive route writes this word
528 * beside the offending array index. */
538
539/** Stage an immutable account-currency FX curve on a Ready native handle.
540 *
541 * Timestamps must be strictly increasing and rates finite and positive.
542 * Pass @c n == 0 to clear the staged curve; the scalar @c account_fx remains
543 * the pre-first fallback. Curves are applied when the native run begins.
544 * Legacy handles and non-Ready native handles refuse without mutation.
545 *
546 * @return 0 only when staging is applied; -1 for invalid input, an invalid
547 * handle, a non-native or non-host native handle, or a non-Ready host. The
548 * typed additive #strategy_configure_native_fx_curve_ext_v1 writes the
549 * #pf_native_fx_curve_error_t and offending index when the kernel judged the
550 * curve. */
552 pf_strategy_t s, const pf_native_fx_curve_v1* curve);
553
554/** Typed additive spelling of #strategy_configure_native_fx_curve_v1. On a
555 * kernel validation refusal, writes the #pf_native_fx_curve_error_t and the
556 * offending element index (length mismatch uses index 0). C-layer argument,
557 * handle and layout refusals leave the out-parameters untouched. */
559 pf_strategy_t s, const pf_native_fx_curve_v1* curve,
560 pf_native_fx_curve_error_t* error, uint64_t* index);
561
562/* ───────────────────────────────────────────────────────────────────
563 * STRATEGY .SO EXPORTS — implemented per compiled strategy
564 * ───────────────────────────────────────────────────────────────────
565 *
566 * Each .so emitted by the codegen exports the following symbols. The
567 * runtime library itself does NOT define them — they are per-strategy
568 * implementations generated by the transpiler.
569 *
570 * Note on naming: these are the legacy unprefixed names retained for
571 * backward compatibility with the existing harness. Future major
572 * versions may introduce `pf_`-prefixed equivalents and deprecate the
573 * unprefixed forms.
574 */
575#ifndef PINEFORGE_NO_STRATEGY_DECLS
576
577/** Allocate a new strategy instance.
578 *
579 * @param params_json Currently ignored; pass `NULL`.
580 * @return Strategy handle, or `NULL` on allocation failure.
581 *
582 * Caller owns the returned handle and must release it via #strategy_free. */
583PF_API pf_strategy_t strategy_create(const char* params_json);
584
585/** Release a strategy handle previously returned by #strategy_create.
586 *
587 * Safe to call with `NULL`. Invalidates any `pf_report_t::trace_names`
588 * pointers obtained from this strategy. */
590
591/** Run a backtest with auto-detected timeframe and no bar magnifier.
592 *
593 * @param s Strategy handle from #strategy_create.
594 * @param bars Non-NULL pointer to OHLCV bars (length @p n).
595 * @param n Bar count (>= 0).
596 * @param out Non-NULL output report. Fields are populated with heap
597 * allocations the caller must release via #report_free. */
599 pf_bar_t* bars,
600 int n,
601 pf_report_t* out);
602
603/** Run a backtest with explicit timeframe and magnifier configuration.
604 *
605 * @param s Strategy handle.
606 * @param bars Bar feed.
607 * @param n Bar count.
608 * @param input_tf Input timeframe ("1", "5", "15", "60", "1D", ...).
609 * Empty string → auto-detect from bar timestamps.
610 * @param script_tf Script timeframe. Empty string → defaults to @p input_tf.
611 * @param bar_magnifier Boolean (0 / non-zero) — enable bar magnifier.
612 * @param magnifier_samples Sub-bar samples per parent bar (typical: 4).
613 * @param magnifier_dist Sampling distribution (see #pf_magnifier_distribution_t).
614 * @param out Output report. Free with #report_free. */
616 pf_bar_t* bars,
617 int n,
618 const char* input_tf,
619 const char* script_tf,
620 int bar_magnifier,
621 int magnifier_samples,
622 pf_magnifier_distribution_t magnifier_dist,
623 pf_report_t* out);
624
625/** Free heap arrays attached to a filled report.
626 *
627 * Idempotent. Safe to call with `NULL` or an already-freed report.
628 * The `pf_report_t` struct itself is caller-owned. */
630
631/** @} */ /* end of pf_lifecycle */
632
633/** @defgroup pf_config Per-strategy configuration
634 * @brief Override @c input.*() values, `strategy(...)` params, and runtime knobs.
635 * @{
636 */
637
638/** Override a Pine @c input.*() value before the next run.
639 *
640 * @param s Strategy handle.
641 * @param key The input's title (or fallback identifier).
642 * @param value Serialized value — numbers as decimal strings,
643 * booleans as `"true"` / `"false"`.
644 *
645 * Calls after #run_backtest are accepted but only take effect on
646 * subsequent runs. */
648 const char* key,
649 const char* value);
650
651/** Override a `strategy(...)` declaration parameter.
652 *
653 * Recognised @p key values: `initial_capital`, `commission_value`,
654 * `default_qty_value`, `pyramiding`, `slippage`,
655 * `process_orders_on_close`, `close_entries_rule`, `default_qty_type`,
656 * `commission_type`. */
658 const char* key,
659 const char* value);
660
661/** Toggle volume-weighted bar-magnifier sampling.
662 *
663 * Has no effect unless the bar magnifier is enabled in
664 * #run_backtest_full. */
666 int on);
667
668/** Report the script's `strategy(..., use_bar_magnifier = true)` declaration.
669 *
670 * Exported only by a strategy whose declaration is that literal; every
671 * other `.so` lacks the symbol. TradingView backtests such a script on its
672 * bar magnifier, so a host runs it on a finer input with the magnifier on
673 * (#run_backtest_full).
674 *
675 * @return 1. */
677
678#endif /* PINEFORGE_NO_STRATEGY_DECLS */
679
680/* ───────────────────────────────────────────────────────────────────
681 * RUNTIME LIBRARY EXPORTS — implemented in libpineforge
682 * ─────────────────────────────────────────────────────────────────── */
683/** Toggle per-bar trace recording. Default off (zero-cost when off).
684 *
685 * Enables capture for `// @pf-trace name=expr` pragmas already compiled
686 * into the strategy `.so`. Trace records appear in pf_report_t::trace. */
688
689/** Set the earliest Unix-ms timestamp at which strategy order commands
690 * may fire.
691 *
692 * Earlier bars still execute user code and warm TA/series state, but
693 * `strategy.entry/order/exit/close` commands are ignored. */
695
696/** @} */ /* end of pf_config */
697
698/** @addtogroup pf_lifecycle
699 * @{
700 */
701
702/** Read process-wide scratch-arena diagnostics, outside canonical report bytes.
703 * Writes the peak bytes of simultaneously claimed chunks since attachment
704 * and the exact supplied backing size (including any unallocatable tail).
705 * Returns 0; with no arena attached both values are 0. Returns -1 with both
706 * outputs unchanged if either pointer is NULL, the pointers alias, or the
707 * snapshot cannot be read. Never throws or changes a strategy's last error.
708 * Query after report finalization and before arena detach. The counters
709 * belong to the attachment, not a strategy or repeat; detach resets them.
710 * No pf_report_t, sink receipt, trailer or broker-state hash layout changes. */
711PF_API int pf_report_scratch_arena_stats(uint64_t* high_water_bytes,
712 uint64_t* arena_bytes);
713
714/** Read the handle's admitted-run command-cache and trade-ledger diagnostics.
715 * resident_bytes is the current sum of live command-cache control/slabs and
716 * the ledger's fixed workspace, allocated directory and decoded-entry pages.
717 * Each allocation owner contributes once; borrowed shared-cache storage does
718 * not add another charge. Arena chunks and backing-file extents are separate.
719 * allocation_bypass_count counts optional command-slab and ledger-directory/
720 * page allocation failures, once per sticky bypass, including released owners.
721 * Both outputs are 0 before the first admitted run; admission resets them.
722 * Returns 0, or -1 with both outputs unchanged for a NULL handle/output or
723 * aliased outputs. Never allocates, throws or changes last_error.
724 * Query on the invoking thread after report finalization and before freeing
725 * the strategy. No existing report, receipt or diagnostics layout changes. */
726PF_API int pf_report_cache_stats(pf_strategy_t s, uint64_t* resident_bytes,
727 uint64_t* allocation_bypass_count);
728
729/** Admit a jailed handle only with an attached scratch arena.
730 * Attach the inherited environment arena, if configured (fixed fd 5:
731 * PF_SCRATCH_ARENA_FD=5, positive PF_SCRATCH_ARENA_BYTES);
732 * validate its backing and mapping configuration before execution.
733 * On Linux, require a sealed memfd whose seal mask contains
734 * F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL. F_SEAL_EXEC and other non-write
735 * seals may be added (e.g. MFD_NOEXEC_SEAL). F_SEAL_WRITE or
736 * F_SEAL_FUTURE_WRITE is refused at attachment with
737 * report_sink_failed/arena_backing. These requirements hold regardless of
738 * PF_SCRATCH_ARENA_JAILED; off Linux, admit any attached arena.
739 * Returns 0 accepted; -1 for a NULL handle or an unavailable/invalid arena.
740 * Arena failures set the handle's typed report_sink_failed/arena_backing
741 * failure. Never creates a backing file or falls back to TMPDIR.
742 * Existing unjailed callers do not need to call this opt-in admission.
743 * The first successful attach sets SIGXFSZ to SIG_IGN process-wide; the prior disposition is not restored.
744 * Same fd/size reattaches are no-ops. Hosts relying on their SIGXFSZ handler
745 * or default termination must account for this before attaching an arena. */
747
749/** Read bounded sink provenance without reading the sink or replaying rows.
750 * Include report_sink.h for the descriptor; initialize struct_size and
751 * version=1. Returns 0 filled; -1 with *out unchanged for a bad handle,
752 * size, version or NULL output. Completion follows the sink trailer.
753 * Queries never execute callbacks, advance a cursor or change last_error. */
756
757/** Install the invocation's cooperative SIGTERM handler. The handler only
758 * stores a volatile sig_atomic_t flag; batch runs observe it at completed
759 * input-bar boundaries, independently of #strategy_request_abort.
760 * Opt-in, process-wide, for one serialized child invocation. Call on the
761 * invoking thread with SIGTERM blocked, then unblock it; pending SIGTERM is
762 * preserved. Returns 0, or -1 on signal-setup failure. Repeated installation
763 * preserves an already requested stop. This opt-in installs only the abort
764 * handler for SIGTERM; it does not change any ABI layout. */
766
767/** Read the invocation stop flag: 0 or 1. No allocation or state mutation. */
769
770/** Restore the disposition saved by #pf_run_abort_install_sigterm_v1.
771 * Call with SIGTERM blocked, after native cleanup and failure publication.
772 * Returns 0 (also when uninstalled), or -1 on signal-setup failure. */
774
775/** Return the physical entry incarnation for one closed-trade row.
776 *
777 * Partial-close/FIFO fragments emitted from the same physical entry share
778 * this value. Distinct broker entry objects receive distinct monotonically
779 * increasing values even when Pine reuses the same user-visible entry ID.
780 * The value is scoped to one strategy run and is intended as report
781 * provenance, not as a stable cross-run identifier.
782 *
783 * @param s Strategy handle whose most recent run filled a report.
784 * @param trade_index Zero-based row index into that report's `trades`
785 * array — the script's closed trades followed by the
786 * range-end rows (`open_at_end`, ABI v3), which carry
787 * the incarnation of the lot they mark like any other
788 * close.
789 * @return Non-zero physical-entry identity, or 0 for an invalid index or a
790 * legacy/synthetic trade without request record provenance. */
792 pf_strategy_t s, int trade_index);
793
794/** @} */ /* end of pf_lifecycle */
795
796/** @defgroup pf_streaming Historical to realtime streaming
797 * @brief Warm on confirmed OHLCV and continue the same strategy instance on
798 * normalized ordered trades from any data source.
799 * @{
800 */
801
802/** Warm a strategy with confirmed OHLCV, then switch the same instance to a
803 * realtime trade stream without resetting position, equity, pending orders,
804 * Pine variables, TA state, request.security state, or timeframe aggregation.
805 *
806 * The warmup must contain at least one complete fixed-duration input bar.
807 * Normalized ticks start at or after the next input bar's open. This
808 * lifecycle uses close-only strategy calculation (the Pine strategy default)
809 * while resting broker orders are evaluated on every normalized trade.
810 *
811 * A generated Pine strategy using this entry point rejects calc_on_order_fills,
812 * historical probe/tail overrides, timestamped FX and auxiliary/native
813 * security feeds. A C native host can also use this entry point and accepts
814 * the native FX curve, auxiliary feeds and calculation trigger where its
815 * run specification permits them.
816 * @return 0 on success, -1 on failure. Inspect #strategy_get_last_error. */
818 const pf_bar_t* warmup_bars,
819 int n_warmup,
820 const char* input_tf,
821 const char* script_tf);
822
823/** Native live extension version (1). Additive to ABI v4; callers must probe
824 * this symbol before using the confirmed-bar/action API in older modules. */
826
827/** One physical emulator lot action. is_long describes the position side:
828 * buy = is_entry == is_long. A reversal is exits followed by an entry.
829 * price/time are emulator reference values, not external broker fills.
830 * Strings are borrowed until the next stream mutation or queue clear. */
831typedef struct pf_stream_order_action {
832 uint64_t sequence;
834 int32_t bar_index;
835 int32_t is_entry;
836 int32_t is_long;
837 double quantity;
838 double price;
839 const char* order_id;
840 const char* comment;
843
844/** Consume one confirmed input-timeframe bar. Its script-timeframe aggregate
845 * uses the existing batch OHLC fill kernel when complete. Close-only strategy
846 * calculation; no bar magnifier or synthetic trade ticks. The first tick/bar
847 * locks the feed mode. Off-grid/out-of-order data, invalid OHLCV and missing
848 * in-session bars fail. Calendar-closed intervals may be skipped. No padding.
849 * Returns 0/-1. On ANY input-processing failure discard/recover the instance:
850 * the operation is not transactional and can have advanced native state. */
852
853/** Queued physical fills since the last clear, in execution order. Historical
854 * warmup and report-only range-end rows never enqueue. Sequence starts at 1
855 * after warmup and is not reset by clear. Returns -1 on a null handle. */
857/** Copy one action; return 0 on success, -1 on invalid handle/index/output. */
860/** Clear observed events after the caller durably journals them. */
862/** Versioned deterministic fingerprint of observable broker/stream state.
863 * Excludes the consumable queue and arbitrary private strategy members.
864 * This is a replay check, not a complete state snapshot or cryptographic hash.
865 * Fresh replay must use deterministic strategy code, the same pinned engine
866 * build and configuration. The fingerprint's recipe belongs to the script
867 * ABI epoch: from 1.0.0 it is fixed for every 1.x engine
868 * (engine_script_run_v19 to 1.10, then v20), and a new recipe is a new epoch; a value from a
869 * build before 1.0.0 is not comparable. Returns 0 for NULL.
870 *
871 * A refused read also returns 0, so a 0 alone does not tell it from an
872 * answer: a handle that carries selected-window provenance refuses this
873 * query once its run has sealed, and #strategy_state_query_status_v1 names
874 * that refusal. The recorded broker-state rows
875 * (#strategy_set_broker_state_hash_recording) are not values of this token:
876 * it has no recorded counterpart, so a refused read leaves no witness. */
878
879/** Push one normalized realtime trade. Returns 0 on success, -1 on failure. */
881 const pf_trade_tick_t* tick);
882
883/** Push an ordered batch of realtime trades. Semantically identical to
884 * repeated #strategy_stream_push_tick calls, with lower FFI overhead. */
886 const pf_trade_tick_t* ticks,
887 int n);
888
889/** Advance the stream clock and close every input bar whose end is <= the
890 * supplied time. Quiet in-session intervals become zero-volume carry-forward
891 * bars; intervals outside the configured syminfo session are skipped. */
893
894/** End a realtime stream. When @p finalize_partial_input_bar is non-zero, the
895 * currently forming input bar is dispatched before ending; normally callers
896 * should first advance to a confirmed boundary and pass zero here. */
897PF_API int strategy_stream_end(pf_strategy_t s, int finalize_partial_input_bar);
898
899/** Snapshot the cumulative warmup + realtime report. The embedded arrays are
900 * caller-owned after return and must be released with #report_free, or with
901 * #strategy_native_report_free_v1 by a host that links the kernel archive,
902 * which has no #report_free. */
904
905/** @} */ /* end of pf_streaming */
906
907/** @defgroup pf_live Live-runtime surface (ABI v4)
908 * Default-off flags and read-only accessors used by pineforge-live. None of
909 * them changes a historical run unless enabled. @{ */
910
911#define PINEFORGE_HAS_SCRATCH_TABLES_V1 1
912
913#define PINEFORGE_HAS_CACHE_POLICY_V1 1
914
915/** Append-only cache-kind vocabulary. Zero is reserved and is rejected as
916 * an unknown kind. Values are contiguous, starting at one; new kinds take
917 * the next unused value and existing values are never renumbered.
918 * A kind whose cache a later release deletes remains accepted as a
919 * documented retired no-op, never an unknown-kind error. */
920typedef uint32_t pf_cache_kind_t;
921#define PF_CACHE_KIND_LEDGER_CACHE 1u
922#define PF_CACHE_KIND_TRADE_PAGES 2u
923#define PF_CACHE_KIND_DEPENDENCY_PAGES 3u
924#define PF_CACHE_KIND_EXIT_PAGES 4u
925#define PF_CACHE_KIND_BOOK_PAGES 5u
926#define PF_CACHE_KIND_RETAINED_SEQUENCE_PAGES 6u
927#define PF_CACHE_KIND_COHORT_ID_PAGES 7u
928#define PF_CACHE_KIND_EXIT_RANK_PAGES 8u
929#define PF_CACHE_KIND_ERASED_MINIMA_PAGES 9u
930#define PF_CACHE_KIND_CONSUMED_CYCLE_PAGES 10u
931#define PF_CACHE_KIND_POSITION_TAIL_PAGES 11u
932#define PF_CACHE_KIND_SUCCESSOR_ROOT_PAGES 12u
933#define PF_CACHE_KIND_RECEIPT_TAIL_PAGES 13u
934#define PF_CACHE_KIND_CHAIN_SEGMENT_PAGES 14u
935#define PF_CACHE_KIND_EQUITY_CURVE_PAGES 15u
936#define PF_CACHE_KIND_TA_HISTORY_PAGES 16u
937#define PF_CACHE_KIND_FEED_CALENDAR_PAGES 17u
938#define PF_CACHE_KIND_RECORDED_SERIES_PAGES 18u
939#define PF_CACHE_KIND_PROJECTION_EXIT_PAGES 19u
940
941/** One optional resident cache size. Ordinary C alignment applies: kind is
942 * uint32_t and capacity_bytes is uint64_t, with no packed wire layout.
943 * Every capacity is in bytes. For PF_CACHE_KIND_LEDGER_CACHE, the engine
944 * selects the largest row count R with
945 * R * TradeLedger::kLedgerEntryBytes + ceil(R / 256) * sizeof(void*) <= capacity_bytes
946 * (312 bytes per decoded entry and 8 bytes per page pointer on LP64).
947 * Omitting this entry selects the provisional default of 67,108,864 bytes
948 * (64 MiB), which holds 215,070 entries on LP64 and never grows with the
949 * retained history; a failed optional allocation retries only after the
950 * retained population has doubled since the failed attempt, and the first
951 * failure remains observable until clear. Retirement and a rewind release
952 * every optional page. Every present ledger entry selects an explicit finite
953 * row policy, including an allowance equal to the default or to 65,536 rows
954 * (20,449,280 bytes on LP64).
955 * Zero selects the existing zero-row
956 * bypass minimum. A nonzero capacity converting to fewer than one row fails
957 * with setting_rejected, input="cache_policy", reason="value_below_minimum";
958 * above the existing 1,048,576-row ceiling it fails with the same input and
959 * reason="value_above_maximum". No clamping or sub-row bypass occurs.
960 * The ledger keeps private pages; its allowance includes their page directory.
961 * Allocated pointers and pages are reported once in cache resident bytes.
962 * A finite policy's optional directory/page allocation failure permanently
963 * bypasses further allocation for that ledger, preserving backing reads and
964 * writes. The omitted default keeps its old hits on a failed allocation and
965 * retries as described above; its first allocation failure remains
966 * observable until clear. Required backing I/O failures still propagate.
967 * Other kinds include optional line metadata in their byte allowances and
968 * bypass when below one line. Reads still use backing at zero capacity.
969 * reserved must be zero; a nonzero word is a typed setting_rejected error. */
975
976/** Versioned resident cache policy. Set struct_size = sizeof(pf_cache_policy_v1)
977 * and version = 1; both reserved words and every entry's reserved word must
978 * be zero. Nonzero reserved words fail with setting_rejected,
979 * input="cache_policy", reason="unparseable_value".
980 * The caller owns this descriptor and its entries; the setter copies their
981 * values before returning and retains neither pointer.
982 * entry_count is a uint64_t count; entries may be NULL only when it is zero.
983 * Each successful replacement starts from internal defaults, then applies
984 * the entries. Omitted kinds use defaults (the 64 MiB ledger default); duplicate
985 * and unknown kinds fail. NULL/empty descriptors restore the ledger default.
986 * No entry limits logical records, retained history or backing extent. */
994
995/** Copy cache settings before run admission; NULL restores internal defaults.
996 * A run freezes its settings before its first store begin/append. Calls in
997 * begin callbacks, during execution or during an active stream fail without
998 * changing either configuration or the admitted run. An idle call between
999 * runs affects the next run, including stream warmup, and never resizes the
1000 * preceding run's retained stores or borrowed views.
1001 * Returns 0 on success and -1 on invalid descriptor, nonzero reserved word,
1002 * duplicate/unknown kind, ledger capacity outside its setting range,
1003 * unrepresentable size or busy/frozen handle. A valid handle records the
1004 * typed setting_rejected configuration error through
1005 * #strategy_get_last_error_code and #strategy_get_last_error_args.
1006 * Optional cache allocation failure bypasses caching; it is not admission
1007 * failure. This policy does not change scratch-table maxima or arena shares. */
1009 const pf_cache_policy_v1* policy);
1010
1011/** Disk scratch write batching, independent of resident cache policy.
1012 * Set struct_size = sizeof(pf_scratch_io_policy_v1), version = 1 and both
1013 * reserved words to zero. write_batch_bytes is successful write traffic in
1014 * bytes: default 65536; every uint64_t value is valid. Positive values retain
1015 * the before-next-batch sync/discard order. Zero syncs/discards each positive
1016 * transfer before write/write_some returns success, including short writes.
1017 * This is not a logical history limit or a physical-memory guarantee.
1018 * The fixed clean-read and userspace sequence windows remain unchanged. */
1025
1026/** Copy settings before admission; NULL restores the 65536-byte default.
1027 * The descriptor is copied and no pointer is retained. Active begin,
1028 * callback, execution and stream calls reject atomically. Idle replacement
1029 * affects the next run and does not retune retained stores.
1030 * Returns 0 on success, -1 for a null handle, invalid descriptor or frozen
1031 * settings. Valid handles report setting_rejected, input="scratch_io_policy":
1032 * size/reserved errors use reason="unparseable_value", unsupported version
1033 * uses "invalid_enum_option", and active handles use "frozen".
1034 * A loader must resolve this symbol and fail if it is missing/incompatible,
1035 * including when requesting a NULL reset. */
1037 const pf_scratch_io_policy_v1* policy);
1038
1039/** Storage policy for the nine historical scratch-table regions. */
1044
1045/** Version 1 scratch-table admission maxima, in keys/rows and key bytes.
1046 * Set struct_size = sizeof(pf_scratch_tables_v1), version = 1, reserved = 0.
1047 * K_q/B_q are reserved zero; q's two region descriptors remain empty.
1048 * Each other K/B pair includes all distinct ever-stored keys, including erased
1049 * keys. U/B_U cover all cohort IDs, V/B_V all exit-rank IDs (V <= 27), and R
1050 * all rooted successors, including internally generated replacements.
1051 * Counts and UTF-8/binary key-byte totals are admitted maxima supplied by
1052 * the host, not estimates inferred from the number of bars.
1053 * Bounded mode reserves one fixed slab before run preparation; exceeding
1054 * the manifest fails the run with scratch_capacity_exceeded. The layout
1055 * and every offset must fit uint64_t, size_t and off_t. Allocation failure
1056 * remains out_of_memory. This policy is independent of the report sink.
1057 * In bounded mode, all eleven maxima zero select the P0 policy at each
1058 * begin: finite batches use that run's input-bar count, and streams without
1059 * a declared lifetime use the fixed stream tier (warmup is not lifetime).
1060 * Any nonzero maximum makes the entire manifest explicit, zeros included.
1061 * The default is an admission policy, not a measured workload bound. */
1062typedef struct pf_scratch_tables_v1 {
1063 uint32_t struct_size;
1064 uint32_t version;
1065 uint32_t backend;
1066 uint32_t reserved;
1067 uint64_t K_q;
1068 uint64_t B_q;
1069 uint64_t K_e;
1070 uint64_t B_e;
1071 uint64_t K_c;
1072 uint64_t B_c;
1073 uint64_t U;
1074 uint64_t B_U;
1075 uint64_t V;
1076 uint64_t B_V;
1077 uint64_t R;
1079
1080/** Copy scratch-table configuration while idle; NULL restores disk defaults.
1081 * Never retains a pointer into the caller's descriptor. Returns 0 on
1082 * success, -1 for a null handle or invalid size/version/backend/reserved/V,
1083 * -2 during a stream, and -3 during a batch run (including begin callbacks).
1084 * A refusal preserves the installed configuration and active run ownership.
1085 * Finite streams must supply maxima for their complete admitted lifetime.
1086 * The embedding host sets RLIMIT_FSIZE and owns disk-scratch SIGXFSZ policy.
1087 * This setter does not attach an arena; see #strategy_require_report_scratch_arena_v1 for the SIGXFSZ side effect of arena attachment. */
1089 const pf_scratch_tables_v1* config);
1090
1091/** Request cooperative abort of the run in progress (see c_abi.cpp). */
1093/** 0 = completed, 1 = NOT_COMPLETED (aborted), -1 = @p s is NULL. */
1095/** Live-runtime tail semantics (spec §3.1): the LAST bar of the array fed to
1096 * every subsequent run() is a still-forming bar, not the chart's rightmost
1097 * historical bar. This is persistent configuration, not a one-shot flag --
1098 * it stays in effect until a caller passes @p on == 0, so a handle reused
1099 * for a later plain historical replay must be explicitly turned back off.
1100 * Effects when @p on is non-zero:
1101 * 1. `barstate.islast` is false for that bar.
1102 * 2. `session.islastbar` is computed from the bucket calendar (no next
1103 * bar to peek at, so it evaluates whether the next bucket -- this
1104 * bar's timestamp plus one script-TF step -- falls out of session).
1105 * 3. `bar_index` stays put; `last_bar_index` is frozen at the horizon
1106 * bar (`horizon_bars - 1`), when @p horizon_bars > 0. `last_bar_time`
1107 * is exact when the horizon bar is in the script-bar array, one
1108 * script-TF step per missing bar past the array's last bar
1109 * otherwise; under aggregation (input_tf < script_tf) it is
1110 * extrapolated from the first bar instead, and the aggregation-path
1111 * caveat below applies.
1112 * 4. The range-end synthetic close row/trade is skipped (no
1113 * `open_at_end` row); the final equity point keeps `open_profit`.
1114 * 5. Interior bars (every bar before the last) are unaffected, except
1115 * effect 2 under aggregation (below).
1116 * Scope: the Pine source host's scheduler applies these where it publishes
1117 * a script bar to the generated script. Effects 1, 3 and 4 hold on every
1118 * path. Effect 2 is its session-predicate step, which runs for every script
1119 * bar of a run whose timeframe is known -- a timeframe-aware overload, or
1120 * `run(bars, n)` with at least two bars, whose timeframe it detects from
1121 * them; with fewer, `session.ismarket`/`session.islastbar` keep their
1122 * reset-state `false` whatever this flag says. The bucket-calendar
1123 * lookahead stands in for the next bar wherever the scheduler holds none:
1124 * with input_tf == script_tf that is the array's last bar alone, but under
1125 * aggregation (input_tf < script_tf) the scheduler holds no next script
1126 * bar at all, so the lookahead decides `session.islastbar` on every bar of
1127 * the run. Callers must feed an input_tf == script_tf array, matching
1128 * #strategy_set_probe_suppress_tail_logic's scope note.
1129 * `strategy_stream_begin` refuses a handle with this flag set.
1130 * Default off (@p on == 0): every historical run stays byte-identical to
1131 * before this flag existed. The mode belongs to the Pine source host; on
1132 * a host that models no still-forming tail bar (a bare kernel host) the
1133 * call is accepted and inert, as it always was there. */
1134PF_API void strategy_set_realtime_tail(pf_strategy_t s, int on, int horizon_bars);
1135/** Live probe tail suppression (spec §3.2): the LAST script bar of the array
1136 * fed to every subsequent run() is matched but not calculated. The kernel
1137 * matches that bar like any other, so resting stop/limit orders settle
1138 * against it; the Pine source host's scheduler then publishes it through
1139 * its suppressed-tail step instead of the generated script -- source
1140 * history and `bar_index` advance, the fills already matched on the bar
1141 * are settled, the report point is marked, `on_bar` is not invoked and no
1142 * range-end row is written. The host's own bar-close policy is not the
1143 * script and still runs on that bar -- its risk-state update and its
1144 * TradingView margin-call checkpoint -- so a margin call due against the
1145 * forming bar is booked against it.
1146 * Apart from what that policy books, the run's last-bar fills are exactly
1147 * the settled book's fills against the forming bar, and the post-run
1148 * pending-order book is the book in force during that bar. This is
1149 * persistent configuration, like #strategy_set_realtime_tail, and
1150 * independent of it -- do not assume the two flags are coupled; set each
1151 * explicitly.
1152 * Scope (ABI v4 / live v1): honoured on every batch path -- the
1153 * single-timeframe and the timeframe-aware overloads, with or without the
1154 * bar magnifier. Under aggregation (input_tf < script_tf) the suppressed
1155 * bar is the run's last COMPLETED script bar, and a trailing partial
1156 * bucket is never calculated at all. Under `calc_on_order_fills` only the
1157 * bar's close calculation is suppressed: a fill on the forming bar still
1158 * triggers the fill recalculation, which runs the script on it.
1159 * `calc_on_order_fills` and the bar magnifier stay gated in live v1 and a
1160 * probe must not enable them; callers must feed an input_tf == script_tf
1161 * array.
1162 * `strategy_stream_begin` refuses a handle with this flag set: clear it
1163 * (on = 0) first.
1164 * Default off (@p on == 0): every historical run stays byte-identical to
1165 * before this flag existed. Like #strategy_set_realtime_tail the mode
1166 * belongs to the Pine source host; on a host that models no still-forming
1167 * tail bar the call is accepted and inert, as it always was there. */
1169/** Force this run's intrabar path order (ABI v4 live-runtime surface): the
1170 * leg order every modeled OHLC path the engine walks uses -- stop/limit
1171 * fill priority, exit trail walking, dual-entry-stop arbitration,
1172 * bar-magnifier sub-bar sampling, and the excursion columns a source host
1173 * reports on a closed row. Values:
1174 * - `0` AUTO (default): the unchanged TV-emulator rule -- the leg nearer
1175 * `open` (by `|high-open|` vs `|open-low|`) goes first.
1176 * - `1` HIGH_FIRST: force `O -> H -> L -> C` regardless of the bar's own
1177 * shape.
1178 * - `2` LOW_FIRST: force `O -> L -> H -> C` regardless of the bar's own
1179 * shape.
1180 * The values are #pf_native_path_order_t's (<pineforge/native_c_api.h>):
1181 * #PF_NATIVE_PATH_ORDER_AUTO, #PF_NATIVE_PATH_ORDER_HIGH_FIRST and
1182 * #PF_NATIVE_PATH_ORDER_LOW_FIRST. Any other @p mode is clamped to AUTO:
1183 * this setter answers nothing, so it cannot refuse one.
1184 * A live probe runs the SAME forming bar under BOTH forced orders and emits
1185 * only the fills that agree between the two -- a fill that depends on which
1186 * leg TradingView's own (unobservable, still-forming) bar will resolve to
1187 * is path-dependent and must be suppressed rather than guessed.
1188 * This is persistent configuration, like #strategy_set_realtime_tail -- it
1189 * stays in effect until a caller passes @p mode == 0, so a handle reused
1190 * for a later plain historical replay must be explicitly set back to AUTO.
1191 * Scope: the mode is carried by the RUN, in `NativeRunSpec::path_order`. A
1192 * native-bound source host projects this setting into that field at begin
1193 * -- at run() and at #strategy_stream_begin alike -- and a bare native host
1194 * declares the field itself; every modeled path the native driver walks
1195 * then resolves its leg order from that field. So the setting takes effect
1196 * at the next begin rather than mid-run, and a stream is NOT exempt: a
1197 * confirmed bar pushed with #strategy_stream_push_bar is sealed on the same
1198 * forced waypoint sequence a run() walks (measured on a 99/101 bracket and
1199 * an AUTO-low-first touch bar: both routes exit at 99.00 under AUTO and at
1200 * 101.00 under HIGH_FIRST). What a forced order cannot reach is an OBSERVED
1201 * TICK (#strategy_stream_push_tick): one price is not a modeled path and
1202 * has no legs to order, so the same tape fills identically under all three
1203 * modes; the forming bar those ticks build is ordered only when it seals,
1204 * on the waypoint sequence above.
1205 * Default AUTO (@p mode == 0): every historical run stays byte-identical to
1206 * before this flag existed. */
1208/** The dual-entry-stop arbitration decided on the LAST bar the most recent
1209 * run() dispatched: a flat position resting exactly one long stop-only
1210 * ENTRY and one short stop-only ENTRY, both touched on that bar.
1211 * Values mirror
1212 * `internal::DualEntryStopPathWinner`'s enumerator order:
1213 * - `0` None -- no such pair was arbitrated on that bar (not flat, no
1214 * matching pair, or neither/only one side touched).
1215 * - `1` LongFirst -- the long stop's first-touch position on the intrabar
1216 * path came first (or the two tied, which the engine always resolves
1217 * in the long leg's favour).
1218 * - `2` ShortFirst -- the short stop's first-touch position came first.
1219 * - `-1` -- @p s is NULL.
1220 * This is a per-BAR snapshot, not a live read of the engine's per-pass
1221 * working state: it is written once, at the arbitration itself, and then
1222 * holds for the rest of that bar even though the working state goes back
1223 * to None the moment the winning side fills (position no longer flat) or
1224 * its stop-entry admission is declined -- neither of which undoes the fact
1225 * that TradingView's broker emulator arbitrated a real pair that bar. A
1226 * caller therefore gets the right answer whether it reads this after a
1227 * `strategy_set_probe_suppress_tail_logic` forming-bar probe (a single
1228 * native request-matching pass) or after an ordinary
1229 * `process_orders_on_close` run with no tail suppression (two passes, the
1230 * winner already filled by the second).
1231 * A live probe reads this after a forming-bar run to see which side the
1232 * engine's own broker-emulator tie-break picked, without having to re-run
1233 * and infer it from which of the two possible fills came back.
1234 * Only the standard (non-`calc_on_order_fills`) dispatch path updates this
1235 * value; it is a silent no-op under the COOF scheduler, mirroring
1236 * #strategy_set_probe_suppress_tail_logic's dispatch-path-scope caveat. */
1238/** Toggle per-report-point broker-state hash recording (spec §3.4, ABI v4).
1239 *
1240 * When @p on is non-zero, every subsequent run() appends
1241 * #strategy_broker_state_hash's value to pf_report_t::broker_state_hash
1242 * after each report point (KernelRecorded for bare hosts); completed runs
1243 * match pf_report_t::script_bars_processed, failed ones may not. Cleared (recorded array
1244 * emptied, not the flag itself) at the start of every run(); the flag is
1245 * persistent configuration, like #strategy_set_realtime_tail, and stays
1246 * set until a caller passes @p on == 0.
1247 * Also covers #strategy_stream_begin's warmup run() and every script bar
1248 * dispatched afterward by the realtime tick stream, so
1249 * #strategy_stream_fill_report's cumulative report satisfies the same
1250 * len == script_bars_processed invariant. Set this BEFORE
1251 * #strategy_stream_begin to also record the warmup leg -- reset_run_state()
1252 * (which stream_begin's internal run() invokes) empties the recorded
1253 * array, not the flag, but a flag flipped on only after stream_begin
1254 * returns misses the warmup bars already dispatched.
1255 * Default off (@p on == 0): pf_report_t::broker_state_hash is NULL /
1256 * 0-length and every historical run stays byte-identical to before this
1257 * flag existed.
1258 *
1259 * What a row is, and what it is for. A row is the run's CONTINUATION
1260 * IDENTITY at that bar, not its trade outcome: it folds the broker state and,
1261 * ahead of it, the state a resume would continue from -- the driving mode
1262 * (batch run, stream warmup, stream realtime) included, on purpose. So the
1263 * array is a replay check WITHIN one driving mode and deliberately not across
1264 * modes: two runs driven the same way over the same bars record the same
1265 * rows, and a run driven the same way that ended at bar k recorded, as its
1266 * last row, the row the longer run recorded after bar k; but a run(), a
1267 * #strategy_stream_begin with one warmup bar and one with every bar as warmup
1268 * record DIFFERENT rows from index 0 over the same bars booking the same
1269 * trades. Only the len == script_bars_processed identity above holds across
1270 * drivings. What pins that a stream books what a batch books is the outcome
1271 * itself -- the trades, the position and the equity -- and not this array. */
1273/** Return the broker-state hash of the FINAL state after the most recent
1274 * run() (see #strategy_set_broker_state_hash_recording's doc and
1275 * pf_report_t::broker_state_hash for the per-bar recording; this accessor
1276 * works whether or not recording was enabled). Compare runs of one
1277 * configuration: the recipe belongs to the script ABI epoch, fixed from 1.0.0
1278 * for every 1.x engine (engine_script_run_v19 to 1.10, then v20), so two values differ only
1279 * when the states they fold differ. This is not a serialized checkpoint.
1280 * Returns 0 when @p s is NULL.
1281 *
1282 * A refused read also returns 0, so a 0 alone does not tell it from an
1283 * answer: once a handle that carries selected-window provenance has sealed
1284 * its run, this query is refused and #strategy_state_query_status_v1 names
1285 * it (#PF_STATE_QUERY_SELECTED_WINDOW_AFTER_SEAL_V1). A caller that needs
1286 * that run's terminal witness enables
1287 * #strategy_set_broker_state_hash_recording BEFORE the run and reads the last
1288 * pf_report_t::broker_state_hash row; no row means no witness. */
1290
1291/** What #strategy_state_query_status_v1 answers: whether the two state queries,
1292 * #strategy_broker_state_hash and #strategy_stream_state_hash, may answer a
1293 * handle now, and whether the report-only terminal-quote writers may still
1294 * store. A fixed-width word rather than an enum, so its width does not
1295 * depend on the consumer's compiler flags. */
1297#define PF_STATE_QUERY_ALLOWED_V1 0 /**< Both queries may answer, and the quote writers store. */
1298#define PF_STATE_QUERY_SELECTED_WINDOW_AFTER_SEAL_V1 1 /**< Both are refused, and so are the quote writers: a selected window's run has sealed. */
1299#define PF_STATE_QUERY_INVALID_HANDLE_V1 2 /**< The handle is NULL. */
1300
1301/** The current eligibility of BOTH #strategy_broker_state_hash and
1302 * #strategy_stream_state_hash on @p s, as a #pf_state_query_status_v1_t:
1303 * #PF_STATE_QUERY_ALLOWED_V1 when either may answer,
1304 * #PF_STATE_QUERY_SELECTED_WINDOW_AFTER_SEAL_V1 when the handle carries
1305 * selected-window provenance and its run has sealed (a refused query returns
1306 * 0, which is also a legal hash value: this tells the two apart), and
1307 * #PF_STATE_QUERY_INVALID_HANDLE_V1 for a NULL @p s. A handle answers
1308 * #PF_STATE_QUERY_ALLOWED_V1 unless it was admitted as a selected-window run,
1309 * which only a separately documented export can do. This is the state of the
1310 * handle at the call, not a last-error slot: it allocates nothing, changes
1311 * nothing, runs no host code and never records, replaces or clears
1312 * #strategy_get_last_error or its code, so a run's first cause stays what the
1313 * run made it. To keep a selected run's terminal witness, enable
1314 * #strategy_set_broker_state_hash_recording BEFORE the run and read the last
1315 * pf_report_t::broker_state_hash row. A broker-state row is not a value of
1316 * the #strategy_stream_state_hash token, and no row means no witness.
1317 *
1318 * The same status is the observable refusal of the report-only terminal-quote
1319 * writers (docs/report-terminal-quote.md), under the same condition: once a
1320 * selected-window run has sealed (completed, failed or aborted), the Pine
1321 * host stores neither reserved syminfo metadata key,
1322 * report_terminal_quote_time_ms and report_terminal_quote_close, whatever the
1323 * value, nor a clear of the quote. In C++ the host throws
1324 * pineforge::SelectedWindowQueryAfterSeal before any store; the C export
1325 * #strategy_set_syminfo_metadata keeps its void signature and swallows every
1326 * exception, so a caller learns of the refusal from this status:
1327 * #PF_STATE_QUERY_SELECTED_WINDOW_AFTER_SEAL_V1 means a quote-key write stored
1328 * nothing. Like the state queries, the refusal records, replaces and clears no
1329 * #strategy_get_last_error or its code, latches no run failure and leaves the
1330 * captured report as it was. A handle without selected-window provenance, and
1331 * a selected handle before its first admitted begin, accept the writes as
1332 * before. Proof: tests/test_selected_quote_seal.cpp. */
1334
1335/** Number of orders resting in the engine's pending-order book after the
1336 * most recent run() (ABI v4 live-runtime surface, task 7, spec 3.6): the
1337 * book in force for the NEXT bar. 0 when @p s is NULL. Read-only; a
1338 * historical run is byte-identical whether or not a caller reads it. */
1340/** Copy the @p index-th resting order (0-based, the engine's own book
1341 * order -- insertion order; broker fill priority is decided at fill time
1342 * from `created_seq`, not from this index) into @p out as a
1343 * pf_pending_order_v1_t value snapshot. Copies
1344 * min(@p size_in, sizeof(pf_pending_order_v1_t)) bytes: an older reader
1345 * with a smaller struct receives a prefix (struct_version and size first),
1346 * a newer reader with a larger one receives the whole v1 and must not
1347 * read past pf_pending_order_v1_t::size. Strings are NUL-terminated
1348 * char[64] copies with a `_truncated` flag and a `_hash64` (FNV-1a 64 of
1349 * the full string); enums are int32 values; NaN sentinels are copied
1350 * verbatim. Returns 0 on success, -1 -- with nothing written -- when
1351 * @p s or @p out is NULL, @p index is out of range, or @p size_in < 8
1352 * (too small to hold even the `struct_version` + `size` header; every
1353 * larger @p size_in is honoured as a prefix copy). The layout is
1354 * self-described by #strategy_pending_order_layout. */
1355PF_API int strategy_pending_order_get(pf_strategy_t s, int index, void* out, size_t size_in);
1356/** The field table of pf_pending_order_v1_t as THIS runtime compiled it --
1357 * one pf_field_desc_t {name, type, offset, size} per field, in struct
1358 * order, starting with `struct_version` and `size`. Static storage: the
1359 * pointer stays valid for the life of the process and needs no handle.
1360 * @p count (may be NULL) receives the row count. An FFI consumer builds
1361 * its struct from this table rather than from a hand-typed copy, so the
1362 * mirror can grow (append-only) without breaking it. */
1364/** Which sizing rule #strategy_pending_order_fill_qty's quantity came from,
1365 * written to its `partition` out-parameter; each value is documented there.
1366 * The value set is the Pine source host's pending-order sizing, the only
1367 * host that answers a probe. */
1375
1376/** Engine-computed fill quantity of the @p index-th resting order (ABI v4
1377 * live-runtime surface, task 8, spec 3.6): the contracts the entry kernel
1378 * would OPEN if that order filled at @p fill_price, sized by the engine's
1379 * own rules so a live runtime never re-implements them. @p fill_price is
1380 * slipped the way the kernel slips it (`native_matching::apply_slippage`
1381 * then the directional grid snap; an entry with a
1382 * limit leg takes the unslipped limit-or-better route). @p partition
1383 * receives which sizing rule produced the value, a #pf_fill_qty_partition_t:
1384 * - `0` EXPLICIT -- a script-supplied qty: for `strategy.entry` the
1385 * lot-floored contracts (`apply_qty_step`) of a fixed qty, or the
1386 * explicit percent/cash budget sized at the fill for a per-call
1387 * qty_type override; a `strategy.order` explicit qty is dispatched
1388 * verbatim (no lot step).
1389 * - `1` FROZEN_PLACEMENT -- a quantity fixed before the fill, never
1390 * re-derived from the fill price: the default percent_of_equity /
1391 * cash MARKET (or strategy.order) size frozen at the signal close
1392 * (`frozen_default_qty`); a MARKET's frozen broker transaction (a
1393 * finalized flat pair's `paired_flat_market_transaction_qty`; a
1394 * same-bar-market member's `sbmt_tx_qty` from flat or as a kept
1395 * over-cap add); or what one of the two MARKET reversal kernels
1396 * opens -- a same-bar-market member against an opposite live position
1397 * opens the remainder `sbmt_tx_qty - min(sbmt_tx_qty, live qty)`
1398 * (the same-bar market reversal path), and the exact SHORT-seed
1399 * collision's final short re-opens the residual
1400 * `pyramid_entries[0].qty - pyramid_entries[1].qty` after closing both
1401 * lots (the short-seed collision path). Both kernels are
1402 * modelled; each reports `close_only` 1 when it opens nothing.
1403 * - `2` DEFAULT_STOP_PLACEMENT -- the DEFAULT percent_of_equity pure
1404 * STOP entry's placement size, at any percentage
1405 * (`default_stop_placement_qty`, round-7 family K), when the
1406 * placement-size rule says the fill consumes it: created flat, filling
1407 * from flat, positive fill.
1408 * - `3` AT_FILL -- default sizing at the slipped fill.
1409 * @p close_only receives 1 when the kernel's close-only predicate fires
1410 * -- the fill closes against the live opposite position and that
1411 * predicate opens no leg of its own: the order's
1412 * `affordability_close_only` (entry leg declined at placement), the
1413 * priced-entry `prior_cycle_close_only` rule (opposite live position whose
1414 * cycle the order was not placed in -- `created_position_side !=` the
1415 * live side -- and not a `reverses_same_bar_market_from_flat`), the
1416 * same-cycle frozen explicit-FIXED transaction the close consumes exactly,
1417 * a finalized flat MARKET pair against an opposite position, or one of the
1418 * two reversal kernels above opening nothing. Where the order was created
1419 * FLAT the engine's close-only branch can open a remainder: a
1420 * transaction larger than the live position still opens the remainder, so
1421 * a consumer compares @p qty with the live position. A replaced
1422 * default-percent short is dispatched
1423 * with its `frozen_default_qty`:
1424 * @p qty is that transaction, @p close_only 0. The probe answers for the
1425 * order filling against the CURRENT book and position; fills that an
1426 * earlier order in the same pass would make first are not simulated.
1427 * NOT folded into @p qty: the deferred-flip
1428 * carry (`tv_carry_qty`, added on a priced entry from FLAT whose
1429 * placement side is the opposite of the
1430 * requested side) -- read `tv_carry_qty` / `created_position_side` from
1431 * the mirror. Returns 0 on success; 1 -- with @p qty NaN, @p close_only 0,
1432 * @p partition -1 (#PF_FILL_QTY_PARTITION_EXIT) -- when the order is an EXIT (its fill quantity is
1433 * decided against the live position at the fill, not by a partition); -1
1434 * with nothing written when @p s is NULL, @p index is out of range, or any
1435 * out-pointer is NULL. Read-only: no historical run changes because a
1436 * caller probed it. */
1437PF_API int strategy_pending_order_fill_qty(pf_strategy_t s, int index, double fill_price,
1438 double* qty, int* close_only, int* partition);
1439/** 1 when the @p index-th resting order's entry-relative offsets
1440 * (`profit_ticks` / `loss_ticks` / `trail_points`) resolve now (ABI v4,
1441 * task 8): entries, plain orders and exits with an empty `from_entry`
1442 * always; an exit bound to a `from_entry` only once that id has filled in
1443 * the CURRENT position cycle -- the same condition the level-resolution
1444 * and eligibility passes use.
1445 * 0 otherwise; -1 when @p s is NULL or
1446 * @p index is out of range. */
1448/** The price levels the @p index-th resting order would fire at, as the
1449 * engine's fill path resolves them (ABI v4, task 8). A leg the order
1450 * carries as a price (`stop_price`, `limit_price`, `trail_price`) is
1451 * reported verbatim -- it is already on the price grid. A leg carried as
1452 * a tick offset is resolved against the live position's average entry
1453 * price only when #strategy_pending_order_level_resolved is 1 AND a
1454 * position is live, with the POSITION side's sign exactly as the fill
1455 * path: `limit = entry + dir * profit_ticks * mintick`, `stop = entry -
1456 * dir * loss_ticks * mintick` (dir = +1 long, -1 short; both
1457 * `level_on_price_grid`), and `trail_activation = entry +/- ceil(
1458 * trail_points - 8e-3 * mintick) * mintick` snapped to the tick grid
1459 * (`trail_points` wins over `trail_price` when both are set, as in
1460 * the trail activation rule). NaN for a leg that is unset or not yet
1461 * resolvable. Returns 0; -1 with nothing written when @p s is NULL,
1462 * @p index is out of range, or any out-pointer is NULL. */
1464 double* limit, double* trail_activation);
1465/** The trail extreme the exit trail legs ride (`trail_best_price_`: the
1466 * running high of a long / low of a short since the position filled,
1467 * bar extremes folded in as the fill path folds them). NaN when @p s is
1468 * NULL and NaN until a position has filled. */
1470/** The live position's volume-weighted average entry price
1471 * (`position_entry_price_`). NaN when @p s is NULL and NaN when the
1472 * position is flat (the engine keeps 0 there; a live reader must not
1473 * mistake it for a price). */
1475/** The live position's cycle id (`position_cycle_seq_`): 0 when flat, a
1476 * fresh nonzero id per open or reversal, unchanged across same-direction
1477 * adds -- the id `created_position_cycle_seq` on a mirrored order refers
1478 * to. -1 when @p s is NULL. */
1480/** Task 9: closed-trade id / exit-comment string accessors, indexing the
1481 * same REPORT row space as #strategy_closed_trade_entry_incarnation
1482 * (`trades_` then the range-end rows, `open_at_end`). The returned pointer
1483 * is valid until the next run() (or stream call) on this handle, like
1484 * #strategy_get_last_error. NULL on NULL @p s, out-of-range @p trade_index
1485 * or policy-erased rows; retained sealed rows are readable. This legacy API
1486 * materializes rows until the next run; full-history reads use growing RAM. */
1488/** See #strategy_closed_trade_entry_id for the row-space/lifetime/NULL
1489 * contract. The returned string is the engine's own INTERNAL exit id, not
1490 * always the script's `strategy.exit`/`strategy.close` id verbatim:
1491 * - a real `strategy.exit` bracket leg -- the user's own id, unchanged.
1492 * - a `strategy.close(id, ...)` close -- `"__close__" + id`.
1493 * - `strategy.close_all()` / a bare-id `strategy.close()` -- the literal
1494 * `"__close__"` (empty target id appended).
1495 * - a margin-call forced liquidation -- the sentinel `"__margin_call__"`.
1496 * - an intraday-cap close (`risk.max_intraday_loss`, or the
1497 * max-filled-orders cap) -- empty (`""`).
1498 * A caller matching exit ids back to its own `strategy.close` calls should
1499 * strip the `"__close__"` prefix rather than compare verbatim. */
1501/** See #strategy_closed_trade_entry_id. */
1503/** Why a closed report row exited — `execution::CloseCause`, the value
1504 * #strategy_closed_trade_close_cause answers for a valid row. The names are
1505 * the kernel's; the descriptive labels in that function's documentation are
1506 * the same numbers. */
1507typedef enum pf_close_cause_e {
1508 PF_CLOSE_CAUSE_UNSPECIFIED = 0, /**< No cause recorded (UNKNOWN). */
1509 PF_CLOSE_CAUSE_SCRIPT = 1, /**< A market close or a reversal. */
1510 PF_CLOSE_CAUSE_BRACKET = 2, /**< A bracket leg. */
1511 PF_CLOSE_CAUSE_LIQUIDATION = 3, /**< A margin-call liquidation slice (MARGIN_CALL). */
1512 PF_CLOSE_CAUSE_RISK_LIMIT = 4, /**< A risk-limit flatten (INTRADAY_LOSS_CAP). */
1513 PF_CLOSE_CAUSE_FILL_CAP = 5, /**< A filled-order cap (INTRADAY_FILL_CAP). */
1514 PF_CLOSE_CAUSE_RANGE_END = 6 /**< The range-end close of a position still open. */
1516
1517/** Task 9: why the @p trade_index-th REPORT-row closed trade exited, a
1518 * #pf_close_cause_t.
1519 * Values:
1520 * - `0` UNKNOWN -- reserved for the documented "no cause" value on a
1521 * VALID trade. Every in-range row currently falls through the
1522 * derivation below to at worst `1` SCRIPT, so no live derivation
1523 * today actually returns `0`; it is not used for a bad index (see
1524 * `-1` below, final review F7).
1525 * - `1` SCRIPT -- a `strategy.close` / `strategy.close_all` market close,
1526 * or a reversal-driven close.
1527 * - `2` BRACKET -- a `strategy.exit` stop/limit/trail/profit/loss leg,
1528 * or a native host's close its owner's fill armed.
1529 * - `3` MARGIN_CALL -- a forced liquidation slice.
1530 * - `4` INTRADAY_LOSS_CAP -- `risk.max_intraday_loss`.
1531 * - `5` INTRADAY_FILL_CAP -- the max-filled-orders intraday cap.
1532 * - `6` RANGE_END -- the still-open position closed at the end of a
1533 * flag-off run (`open_at_end`, ABI v3) -- this always wins over every
1534 * other cause below it.
1535 * Derivation order (see `BacktestEngine::closed_trade_close_cause`,
1536 * engine_trade_accessors.cpp): `open_at_end` -> 6; then the cause the
1537 * CLOSER recorded on the row (`execution::CloseCause`, whose numbers are
1538 * exactly these values) -- a kernel-originated liquidation or risk flatten
1539 * states it through the settling fill, and so does a host request its
1540 * owner's fill armed (a `WaitForApplied` owner relation, as
1541 * `native_toolkit::submit_bracket` builds, with an intent that only closes:
1542 * `Reduce`, `Flatten` or a host-sized close), which records `2` BRACKET; a
1543 * host running its own forced-close policy states it on the row it
1544 * produced, which is where the Pine adapter's margin-call (3),
1545 * max-intraday-loss (4) and filled-order-cap (5) rows get their value (the
1546 * adapter classifies its own bracket rows by order family, so it clears the
1547 * kernel's `2` on them first); then the row's `exit_from_bracket` flag -- true
1548 * only for a REAL `strategy.exit` leg, either an exit-order fill
1549 * whose id does NOT carry the internal `"__close__"` prefix that a deferred
1550 * `strategy.close`/`close_all` order is also given (that path reuses the
1551 * same exit fill machinery), or a whole-position bracket
1552 * revived and fired at the margin-call event price
1553 * (through the margin-call bracket revival) -- -> 2;
1554 * otherwise 1.
1555 * A Pine/source run's values are unchanged. A bare native host that declares
1556 * a kernel margin model now reads `3` for its own liquidation rows (and `4`
1557 * for a kernel risk flatten) where the retired string derivation, which only
1558 * recognised the adapter's sentinels, answered `1`, and it reads `2` for a
1559 * bracket leg its parent request armed, where it read `1` before the host excursion and bracket-cause integration.
1560 * `-1` when @p s is NULL, or when @p trade_index is out of range (final
1561 * review F7: matches every sibling indexed live accessor's -1-on-bad-index
1562 * convention -- #strategy_pending_order_fill_qty,
1563 * #strategy_pending_order_level_resolved,
1564 * #strategy_pending_order_effective_levels). `0` therefore never
1565 * ambiguously means "bad index"; a caller can tell "row exists but reads
1566 * UNKNOWN" apart from "bad index" without a separate
1567 * #strategy_closed_trade_entry_incarnation `report_trade_count` bounds
1568 * check first, though doing that check is still good practice. */
1570/** Task 9: the script-facing signed position size (`strategy.position_size`;
1571 * Freeze-aware -- while a same-bar `process_orders_on_close` close is
1572 * frozen for the current bar, this reads the PRE-close position, matching
1573 * what the script itself observes). NaN when @p s is NULL. */
1575/** Task 9: initial capital plus realized net profit
1576 * (`strategy.initial_capital + strategy.netprofit`). NOT Pine's
1577 * `strategy.equity`, which adds open profit on top of this (unlike the
1578 * last point of `pf_report_t::equity_curve`, which does). NaN when @p s is
1579 * NULL. */
1581/** ABI v4 live-runtime surface (1.8.0): the configured run's initial capital,
1582 * before realized or open profit. Includes staged native specs before begin,
1583 * and generated defaults or overrides once the source host configures the run.
1584 * NaN when @p s is NULL or the read raises, matching
1585 * #strategy_current_equity's invalid-handle sentinel so a failed query is
1586 * not mistaken for a capital value. */
1588/** Raw whole-lot realized P&L, excluding initial capital and report-only
1589 * terminal rows. Reads the engine accumulator directly, including before
1590 * report retrieval; independent of report sorting and global-exit recuts.
1591 * Nonvirtual, additive accessor; ABI version unchanged. NaN when @p s is NULL. */
1593/** Effective symbol pointvalue used by the engine report. NaN on NULL. */
1595/** Opt in to the separate pf-report-replay/v1 companion before a run.
1596 * Copies @p path and @p run_id (at most 64 KiB each; NULL run ID means empty).
1597 * NULL path disables.
1598 * Does not modify primary sink descriptors or existing C layouts.
1599 * Returns 0 on success, -1 for invalid arguments, -2 during streaming,
1600 * -3 during an active call or after ledger retirement. Check the result. */
1601PF_API int strategy_set_report_replay_v1(pf_strategy_t s, const char* path, const char* run_id);
1602/** Task 9: total SCRIPT bars dispatched by the most recent run() (mirrors
1603 * `pf_report_t::script_bars_processed`, engine_report.cpp) -- includes a
1604 * stream's warmup leg and every realtime tick-driven bar dispatched
1605 * afterward by #strategy_stream_push_tick / #strategy_stream_push_ticks.
1606 * `-1` when @p s is NULL. */
1608/** @} */
1609
1610/** @addtogroup pf_config
1611 * @{
1612 */
1613
1614/** Set the strategy's chart timezone (IANA / POSIX TZ string).
1615 *
1616 * Pine builtins ``hour``, ``minute``, ``second``, ``dayofmonth``,
1617 * ``dayofweek``, ``month``, ``year`` and ``weekofyear`` return the
1618 * wall-clock for the chart's timezone — TV exports trade rows in chart
1619 * TZ too. Engine bars are stored as Unix-ms (UTC), so without this
1620 * override these builtins return UTC and silently diverge from TV by N
1621 * hours when the chart is on a non-UTC zone (Asia/Hong_Kong = UTC+8 is the
1622 * validator default).
1623 *
1624 * Pass `NULL`, `""`, `"UTC"` or `"Etc/UTC"` for the legacy UTC
1625 * behaviour (cheap, mutex-free). Any other value names a TZ resolved by
1626 * the system tzdata; uncached decomposition uses the engine image's mutex
1627 * and checks the process's actual TZ. Sequential switches between engine
1628 * images are coherent. Independent images do not share that mutex;
1629 * concurrent conversions across images are not serialized by it.
1630 *
1631 * Should be called before #run_backtest / #run_backtest_full. Persists
1632 * across runs on the same strategy handle until overridden. */
1634
1635/** Plumb the symbol's exchange timezone (IANA string) into syminfo. Feeds
1636 * ``session.ismarket`` / ``time(session)`` predicates. Defaults to "UTC"
1637 * (crypto). Distinct from #strategy_set_chart_timezone — the chart TZ
1638 * drives wall-clock builtins and intraday-cap day rollover; this drives
1639 * session membership. `NULL` is ignored. Call before #run_backtest / #run_backtest_full. */
1641
1642/** Set the symbol's session string (e.g. "0930-1600:23456", default
1643 * "24x7"). Feeds ``session.ismarket`` / ``time(session)``. `NULL`
1644 * ignored. Call before #run_backtest / #run_backtest_full. */
1646
1647/** Set the instrument class (``syminfo.type``: "forex", "stock", "crypto",
1648 * "futures", "index", "fund", "cfd", ...; default "crypto"). Scripts branch
1649 * on it for instrument conventions (e.g. the forex pip size). `NULL` /
1650 * empty ignored. Call before #run_backtest / #run_backtest_full. */
1652
1653/** Set one of the remaining string members of ``syminfo`` by Pine member
1654 * name: "ticker", "tickerid", "currency", "basecurrency", "description",
1655 * "volumetype" (and "type"). Returns 0 when set, -1 for an unknown key,
1656 * empty value or NULL. Call before #run_backtest / #run_backtest_full. */
1658 const char* value);
1659
1660/** Set the instrument tick size (``syminfo.mintick``, default 0.01). Drives the
1661 * directional stop-entry snap and ``slippage = N*mintick`` economics. Set
1662 * per-instrument (e.g. 0.25 for ES, 0.00001 for FX). Non-positive ignored.
1663 * Call before #run_backtest / #run_backtest_full. */
1665
1666/** Set the instrument point value (``syminfo.pointvalue``, default 1.0) — the
1667 * $-per-point-per-contract multiplier applied to every money path: realized
1668 * PnL and MFE/MAE, open profit / mark-to-market equity (and the drawdown /
1669 * runup extremes), percent-of-equity and cash position sizing, percent
1670 * commission notionals, and the margin admission check. Set per-instrument
1671 * (e.g. 50 for ES). Non-positive ignored. Call before #run_backtest / #run_backtest_full. */
1673
1674/** Inject a fundamental/exchange metadata value by Pine member name (e.g.
1675 * "shares_outstanding_total"); un-injected reads return na. Quantity step: a
1676 * declared "qty_step", or a finite "mincontract" > 0 while no "qty_step" is
1677 * declared, sets the order quantity grid. Call before #run_backtest[_full].
1678 * series_origin_ms declares the reference series' first bar open
1679 * in epoch ms and enables chart EMA SMA seeding plus request.security warmup
1680 * from the first whole bucket opening at/after that origin. */
1682 double value);
1683
1684/** Install a timestamped quote-to-account currency conversion curve.
1685 *
1686 * Each value is account-currency units per one unit of the symbol's quote
1687 * currency and becomes active, inclusively, at the corresponding Unix-ms
1688 * timestamp. The latest active value carries forward; broker events before
1689 * the first point use the scalar `account_currency_fx` metadata fallback.
1690 * Installing a curve also selects the converted account-currency broker
1691 * ledger, including during that pre-first fallback interval; it is not
1692 * equivalent to a same-currency run merely because a rate happens to be 1.
1693 * Arrays are copied. Timestamps must be strictly increasing and rates
1694 * positive and finite. Pass `n == 0` to clear the curve and restore scalar
1695 * behavior. Timestamped curves currently support ordinary historical runs.
1696 * Broker-open rate changes: carried 1× longs and 1× shorts are supported;
1697 * leveraged positions fail closed at the crossing. Streaming,
1698 * calc-on-order-fills, and bar-magnifier runs
1699 * also fail closed.
1700 *
1701 * @return 0 on success, -1 for a null strategy or invalid arrays. */
1703 pf_strategy_t s, const int64_t* effective_from_ms,
1704 const double* account_per_quote, int n);
1705
1706#ifdef PINEFORGE_HAS_AUX_SECURITY_FEED_V1
1707/** Copy a finer feed used exclusively by same-symbol request.security calls.
1708 *
1709 * The next ordinary #run_backtest_full call must receive native chart bars
1710 * with @c input_tf equal to @c script_tf and bar magnifier disabled. Chart
1711 * OHLCV, broker fills, and @c bar_index continue to advance only from that
1712 * native chart feed; @p bars advance only request.security evaluators.
1713 * Every auxiliary bar must map to exactly one native chart bar and every
1714 * native chart bar must have at least one auxiliary bar, otherwise the run
1715 * fails closed via #strategy_get_last_error. Arrays are copied. Pass
1716 * @p n == 0 to clear the auxiliary feed.
1717 *
1718 * @return 0 on success, -1 for a null strategy or invalid input. */
1720 const pf_bar_t* bars,
1721 int n,
1722 const char* input_tf);
1723#endif
1724
1725#ifdef PINEFORGE_HAS_NATIVE_SECURITY_FEED_V1
1726/** Copy the exchange's OWN bars of one higher timeframe for same-symbol
1727 * request.security calls that request exactly that timeframe.
1728 *
1729 * On intraday charts of CME futures and US/Indian equities TradingView's
1730 * request.security(syminfo.tickerid, "D", close) returns the exchange's
1731 * daily bar -- the 15:00 CT settlement on ES1!/NQ1!, the official closing
1732 * print on NASDAQ/NYSE/NSE -- which no aggregation of the intraday feed can
1733 * reproduce. With a native feed installed for @p timeframe ("D", "1D", "W",
1734 * ...), every completed request.security bucket of that timeframe carries
1735 * the native bar's OHLCV (matched by the bucket's own label: the session-day
1736 * stamp for D/W/M, the grid open for intraday) instead of the aggregate;
1737 * bucket TIMING -- when the bar completes relative to the chart -- and every
1738 * other timeframe's request are unchanged, and so are chart OHLCV, broker
1739 * fills and bar_index. A "W" / "M" request with no feed of its own reads
1740 * the daily feed's bars aggregated per week / month (TradingView builds its
1741 * W/M bars from the native daily bars, never from intraday prints), keyed by
1742 * the period's first session-day. A completed bucket with no native bar
1743 * keeps its aggregate (counted, not fatal). Bars must be strictly
1744 * increasing; arrays are copied; pass @p n == 0 to clear the feed for
1745 * @p timeframe. Historical runs only: stream_begin() fails closed while a
1746 * native feed is installed.
1747 *
1748 * @return 0 on success, -1 for a null strategy or invalid input. */
1750 const char* timeframe,
1751 const pf_bar_t* bars,
1752 int n);
1753#endif
1754
1755/** Feature probe for #strategy_set_symbol_feed: the first of four opt-in
1756 * setters of another symbol's data for request.security (lane XSYM-D),
1757 * each behind its own feature probe. They are the Pine source host's
1758 * doors: a strategy that registers a request site for another symbol reads
1759 * its bars, its facts and its recorded request values from what these
1760 * installed before the run; during a run each answers -1, as a host with
1761 * no source layer does. Every array is copied; a later call replaces what
1762 * an earlier one installed under the same key. Historical runs only:
1763 * stream_begin() fails closed while a symbol feed or a recorded series is
1764 * installed. */
1765#define PINEFORGE_HAS_SYMBOL_FEED_V1 1
1766/** Feature probe for #strategy_set_symbol_feed_column. */
1767#define PINEFORGE_HAS_SYMBOL_FEED_COLUMN_V1 1
1768/** Feature probe for #strategy_set_symbol_facts. */
1769#define PINEFORGE_HAS_SYMBOL_FACTS_V1 1
1770/** Feature probe for #strategy_set_recorded_series. */
1771#define PINEFORGE_HAS_RECORDED_SERIES_V1 1
1772
1773#ifdef PINEFORGE_HAS_SYMBOL_FEED_V1
1774/** Copy another symbol's own bars of one timeframe, keyed by the exact
1775 * symbol string a request site passes at run time (@p key) and by
1776 * @p timeframe ("15", "60", "1D", ...; "D" and "1D" are one key).
1777 *
1778 * Each bar carries its own close, @p close_ms[i]: after its open
1779 * (@c bars[i].timestamp) and at or before the next bar's open. No calendar
1780 * is guessed for the symbol. Bars must be strictly increasing with finite
1781 * prices; a volume the symbol does not publish is NaN. The feed reaches
1782 * the kernel as a NativeRunSpec instrument feed: on each chart bar, a site
1783 * with lookahead off reads the last bar that has closed by the chart bar's
1784 * close, one with lookahead on the last bar that opened by its open, and the
1785 * site's expression runs over every bar of the feed, in order. Installing a
1786 * feed drops the columns of the feed it replaces; @p n == 0 installs a feed
1787 * without bars, whose sites read na. Historical runs only: stream_begin()
1788 * fails closed while a symbol feed is installed.
1789 *
1790 * @return 0 on success, -1 for a null strategy, invalid input (the reason in
1791 * #strategy_get_last_error) or a host with no source layer. */
1792PF_API int strategy_set_symbol_feed(pf_strategy_t s, const char* key, const char* timeframe,
1793 const pf_bar_t* bars, const int64_t* close_ms, int n);
1794#endif
1795
1796#ifdef PINEFORGE_HAS_SYMBOL_FEED_COLUMN_V1
1797/** Copy one named column of an installed symbol feed: @p n values, one per
1798 * bar of the feed of (@p key, @p timeframe), in bar order (a footprint
1799 * delta, @c "fp_delta_100_70"). NaN is an absent value. A column of the same
1800 * name is replaced. Historical runs only: stream_begin() fails closed while
1801 * a symbol feed is installed.
1802 *
1803 * @return 0 on success, -1 for a null strategy, a feed that is not
1804 * installed, a length that is not the feed's, a name the kernel
1805 * refuses, or a host with no source layer. */
1807 const char* timeframe, const char* name,
1808 const double* values, int n);
1809#endif
1810
1811#ifdef PINEFORGE_HAS_SYMBOL_FACTS_V1
1812/** Set one fact of the symbol a request site names by @p key: @p field is
1813 * one of @c "canonical", @c "valid" (@c "true" / @c "false"), @c "type",
1814 * @c "timezone", @c "session", @c "currency" or @c "mintick" (a positive
1815 * decimal). Inside that site's expression @c syminfo.* reads these facts
1816 * (@c syminfo.tickerid reads @p key itself). A symbol whose @c valid is
1817 * @c "false" makes a site with @c ignore_invalid_symbol read na and fails
1818 * any other site's run closed. Facts only describe an installed feed, so
1819 * historical runs only: stream_begin() fails closed while a symbol feed is
1820 * installed.
1821 *
1822 * @return 0 on success, -1 for a null strategy, an unknown field, a value
1823 * the field refuses, or a host with no source layer. */
1824PF_API int strategy_set_symbol_facts(pf_strategy_t s, const char* key, const char* field,
1825 const char* value);
1826#endif
1827
1828#ifdef PINEFORGE_HAS_RECORDED_SERIES_V1
1829/** Copy a recorded request series: the values a request returned on the
1830 * chart bars that opened at @p chart_open_ms (strictly increasing), keyed
1831 * by the request key a generated strategy reads it under. On a chart bar
1832 * with no row the series reads na, and on every bar when @p n == 0.
1833 * Historical runs only: stream_begin() fails closed while a recorded series
1834 * is installed.
1835 *
1836 * @return 0 on success, -1 for a null strategy, invalid input or a host
1837 * with no source layer. */
1839 const int64_t* chart_open_ms, const double* values,
1840 int n);
1841#endif
1842
1843/** Returns the error message captured by the most recent #run_backtest /
1844 * #run_backtest_full call on this strategy.
1845 *
1846 * Returns an empty string when the run completed normally, or `NULL`
1847 * only when `s` itself is `NULL`. The pointer is owned by the engine
1848 * and remains valid until the next #run_backtest / #run_backtest_full call (which clears
1849 * the captured error before it begins).
1850 *
1851 * The runtime catches every `std::exception` derivative inside the
1852 * engine's run loop so the C ABI never unwinds a C++ exception across
1853 * the `extern "C"` boundary. Consumers must check this after every
1854 * run to surface engine-rejected configurations such as a script
1855 * timeframe finer than the input timeframe, a `request.security`
1856 * timeframe below the chart timeframe without a supported lower-TF
1857 * emulation, or a missing input timeframe when securities are
1858 * registered. */
1860
1861/** Compile-time probe for the two run-failure code getters below (and the
1862 * generated-code helpers of `<pineforge/run_failure.hpp>`). A runtime library
1863 * without them lacks the symbols: look them up with `dlsym`. */
1864#define PINEFORGE_HAS_RUN_FAILURE_CODES_V1 1
1865
1866/** Stable code of the most recent failure on @p s: lower snake_case ASCII
1867 * matching `^[a-z][a-z0-9_]{2,47}$`, from the closed vocabulary published as
1868 * `run_failure_codes.json` (schema `pineforge-run-failure-catalog/v1`); a
1869 * code is never reused for another meaning.
1870 *
1871 * Returns `""` exactly when the most recent run (or setter) on @p s recorded
1872 * no failure, and `NULL` only when @p s is `NULL`. It is not keyed on the
1873 * text: a script that stops with an empty `runtime.error()` message fails
1874 * with #strategy_get_last_error `""` and the code `strategy_runtime_error`,
1875 * and a non-empty error that no coded site wrote reads
1876 * `engine_unclassified_error`, never `""`. A text a script writes never
1877 * selects a code. The pointer has the lifetime of #strategy_get_last_error's
1878 * (until the next run or setter on @p s). */
1880
1881/** The typed arguments of the failure #strategy_get_last_error_code names, as
1882 * one canonical JSON object: UTF-8, keys sorted, no whitespace; an integer
1883 * is a JSON integer, a number the shortest round-trip double, every other
1884 * kind a JSON string. `"{}"` when the code has no arguments, `""` when there
1885 * is no failure, `NULL` only when @p s is `NULL`. An argument is never a
1886 * value the script computed while it ran. Same lifetime as
1887 * #strategy_get_last_error_code. */
1889
1890/** @} */ /* end of pf_config */
1891
1892/** @defgroup pf_version Version query
1893 * @brief Runtime version metadata.
1894 * @{
1895 */
1896
1897/** Runtime version descriptor returned by #pf_version_get. */
1898typedef struct pf_version_s {
1899 int major; /**< Major version. */
1900 int minor; /**< Minor version. */
1901 int patch; /**< Patch version. */
1902 const char* commit_sha; /**< Short git commit SHA, or `""` if unknown. */
1903} pf_version_t;
1904
1905/** @return Linked runtime version. */
1907
1908/** @return Monotonic ABI version (see #PF_ABI_VERSION). */
1910
1911/** Full version descriptor, the `PINEFORGE_VERSION_FULL` of the generated
1912 * `pineforge/version.h`.
1913 *
1914 * Returns `"MAJOR.MINOR.PATCH[-rc.N][-N-gSHA[-dirty]]"` for a tagged git
1915 * checkout, or exactly the `VERSION` file (`"MAJOR.MINOR.PATCH"`, or
1916 * `"MAJOR.MINOR.PATCH-rc.N"` for a release candidate) for a tarball build
1917 * or one configured with `PINEFORGE_VERSION_SOURCE=FILE`. #pf_version_get
1918 * carries only the numeric MAJOR.MINOR.PATCH. The pointer is to a static
1919 * string with program lifetime; do not free. */
1920PF_API const char* pf_version_string(void);
1921
1922/** @} */ /* end of pf_version */
1923
1924/** @defgroup pf_settings Generated Strategy Settings
1925 * Additive, opt-in validation and effective-settings receipts.
1926 * @{ */
1927
1928/** Current generated-strategy settings extension version. */
1929#define PF_SETTINGS_API_VERSION 1u
1930
1931/** Current compiled-strategy execution-capabilities extension version. */
1932#define PF_CAPABILITIES_API_VERSION 1u
1933
1934/** Status codes for the opt-in generated-strategy settings extension. */
1943
1944/** @} */ /* end of pf_settings */
1945
1946#ifndef PINEFORGE_NO_STRATEGY_DECLS
1947
1948/** Generated-strategy settings extension version (currently 1).
1949 * Older compiled strategies lack this symbol. This is independent of
1950 * #pf_abi_version and does not change the legacy entry points. */
1952
1953/** Compiled execution-capabilities extension version (currently 1).
1954 * Older strategies lack this symbol. Independent of #PF_ABI_VERSION and
1955 * #PF_SETTINGS_API_VERSION; implemented by the generated strategy, not runtime. */
1957
1958/** Read immutable canonical JSON describing compiled execution declarations
1959 * and analyzed request/data requirements, never inferred from runtime state.
1960 * Uses #pf_settings_status_t and the buffer/error protocol of
1961 * #strategy_get_effective_settings: @p required includes the NUL, NULL/0
1962 * queries return #PF_SETTINGS_BUFFER_TOO_SMALL, and no partial JSON is returned.
1963 * @p s and @p required must be non-NULL. Receipt bytes remain identical across
1964 * settings changes, batch runs, stream runs and fresh/reused handles.
1965 * A consumer must separately validate effective settings and staged run data. */
1967 size_t capacity, size_t* required,
1968 char* error, size_t error_capacity);
1969
1970/** Allocate a strategy without allowing any exception across the C boundary.
1971 * @p params_json is reserved: pass NULL or an empty string. Nonempty values
1972 * are refused, unlike the legacy factory which ignores them.
1973 * @p out is required and is set to NULL before allocation.
1974 * All checked calls return #pf_settings_status_t; an optional caller-owned
1975 * error buffer receives a NUL-terminated, possibly truncated message.
1976 * A successful call clears that buffer. No error buffer allocation is needed. */
1977PF_API int strategy_create_checked(const char* params_json, pf_strategy_t* out,
1978 char* error, size_t error_capacity);
1979
1980/** Validate and install a declared input before execution.
1981 * Rejects unknown or ambiguous keys, invalid options/booleans, non-finite
1982 * numbers, trailing bytes, range violations and unsupported input forms.
1983 * Invalid values leave the existing setting unchanged. Pine's numeric step
1984 * is UI metadata, not a restriction on programmatically supplied values.
1985 * Calls after execution starts are refused; use the legacy setter for its
1986 * historical subsequent-run behavior. */
1988 const char* value, char* error,
1989 size_t error_capacity);
1990
1991/** Validate and install a supported strategy override before execution.
1992 * Accepts the legacy documented enum aliases, but never silently defaults
1993 * an unknown key, invalid enum, invalid boolean or invalid numeric value. */
1995 const char* value, char* error,
1996 size_t error_capacity);
1997
1998/** Read a deterministic UTF-8 JSON receipt of effective settings.
1999 * Lists every declared input and supported override, including type,
2000 * options, min/max/step constraints, default and effective value. Values
2001 * use canonical serialized strings. No new execution state is retained.
2002 * @p required is required and receives the byte count INCLUDING the NUL.
2003 * Pass NULL/0 for @p json/capacity to query that count; this returns
2004 * #PF_SETTINGS_BUFFER_TOO_SMALL. No partial JSON is returned.
2005 * The receipt describes source settings, not a native run-spec capability. */
2007 size_t capacity, size_t* required,
2008 char* error, size_t error_capacity);
2009
2010/** Exception-contained counterpart of #run_backtest_full.
2011 * Validates pointers and bar count, then uses the identical batch dispatch.
2012 * Returns #PF_SETTINGS_RUN_FAILED for a runtime-reported failure and
2013 * #PF_SETTINGS_EXCEPTION for an escaping C++ exception. Initialize @p out
2014 * to zero and release any populated arrays with #report_free, including
2015 * after a failed call. */
2017 const char* input_tf, const char* script_tf,
2018 int bar_magnifier, int magnifier_samples,
2019 pf_magnifier_distribution_t magnifier_dist,
2020 pf_report_t* out, char* error,
2021 size_t error_capacity);
2022
2023#endif
2024
2025/** @defgroup pf_outputs Recorded outputs
2026 * What a module records besides trades, read after a run or between the
2027 * inputs of a stream (docs/outputs.md). A module that records declares named
2028 * series slots, outputs and run constants, and per run writes one row per
2029 * bar it publishes: a double per slot (NaN = na), events with an optional
2030 * message, and one value per run constant. Nothing is recorded until
2031 * #strategy_outputs_set_enabled turns recording on, and nothing a run
2032 * computes depends on it: trades, the report, #PF_ABI_VERSION and every
2033 * hash are what they are with recording off. What a slot, an output or a
2034 * constant means is in the manifest the module exports
2035 * (#strategy_outputs_manifest).
2036 *
2037 * Part B, the readers, is implemented by the runtime and present in every
2038 * module built on it; a module that declares nothing answers them as empty.
2039 * Part A is generated per module and present only in one that records:
2040 * detect recording with dlsym("strategy_outputs_api_version").
2041 *
2042 * Threading: every strategy_outputs_* call runs on the thread that drives
2043 * the handle, never during one of its run or stream calls. A NULL handle
2044 * answers -1 (nothing for a void function).
2045 * @{ */
2046
2047/** Present in a pineforge.h that declares the group. */
2048#define PINEFORGE_HAS_OUTPUTS_V1 1
2049/** What #strategy_outputs_api_version answers. */
2050#define PF_OUTPUTS_API_VERSION 1u
2051
2052/** The run phase an event was recorded in. */
2053typedef enum pf_output_phase_e {
2054 PF_OUTPUT_PHASE_BATCH = 0, /**< a batch run */
2055 PF_OUTPUT_PHASE_WARMUP = 1, /**< a stream's warm-up, inside strategy_stream_begin */
2056 PF_OUTPUT_PHASE_REALTIME = 2 /**< a stream after its warm-up */
2059
2060/** One recorded event, copied by #strategy_outputs_event_get. Every 8-byte
2061 * field sits at a multiple of 8, so the 72 bytes before `message` are laid
2062 * out alike on every target. */
2063typedef struct pf_output_event_v1_s {
2064 uint32_t struct_version; /**< 1 */
2065 uint32_t size; /**< sizeof(pf_output_event_v1_t) as the runtime compiled it */
2066 uint64_t sequence; /**< 1-based per run; once cleared, never issued again */
2067 int32_t output_index; /**< the output, as the manifest's `outputs[].index` */
2068 int32_t bar_index; /**< the row, 0-based */
2069 int64_t bar_open_ms; /**< the row's open, Unix ms */
2070 int64_t bar_close_ms; /**< the row's close, Unix ms; INT64_MIN when unknown */
2071 uint32_t ordinal_in_bar; /**< per output, from 0 within the row */
2072 uint32_t phase; /**< a #pf_output_phase_t */
2073 uint32_t confirmed; /**< 0 when recorded on a partially finalized bar */
2074 uint32_t reserved0; /**< 0 */
2075 double value; /**< the event's value; NaN = na */
2076 uint64_t message_hash64; /**< FNV-1a 64 of every byte the module recorded as the
2077 message; 0 without one */
2078 const char* message; /**< NUL-terminated, or NULL without one: a message holding a
2079 NUL byte reads to its first NUL, while message_hash64
2080 covers every recorded byte. Borrowed until the next call
2081 that runs, streams or clears on the handle */
2083
2084/** Turn recording on (@p on nonzero) or off. A change clears the record; a
2085 * call that changes nothing changes nothing, even while a run is in progress.
2086 * Returns 0, or -1 with nothing changed and the refusal recorded as code
2087 * `outputs_rejected`: reason `not_declared`, "outputs: this module declares
2088 * no outputs" (turning on a module that records nothing; turning it off
2089 * answers 0), or reason `run_in_progress`, "outputs: recording cannot
2090 * change during a run" (a change while a batch or a stream, from
2091 * #strategy_stream_begin to #strategy_stream_end, is running). As with
2092 * #strategy_set_trace_enabled, a success leaves #strategy_get_last_error
2093 * and its code as they were; a refusal keeps a failed run's own. */
2095/** Series slots per row; 0 while recording is off. */
2097/** Rows recorded: one per bar the module published this run. A module that
2098 * opens at most one row per calculation, as a generated one does, never
2099 * records more than #strategy_script_bars_processed. */
2101/** Copy the open and close times (Unix ms) of rows from @p from_bar on.
2102 * Requires 0 <= @p from_bar <= rows, @p capacity >= 0 and @p written; either
2103 * array may be NULL. Writes min(capacity, rows - from_bar) items, sets
2104 * *written to that count and returns 0; otherwise -1 and nothing written. */
2106 int64_t* open_ms, int64_t* close_ms,
2107 int64_t capacity, int64_t* written);
2108/** Copy one slot's values (NaN = na) of rows from @p from_bar on, as
2109 * #strategy_outputs_bar_times_copy does; @p out is required when
2110 * @p capacity > 0, and @p series must be below the series count. */
2111PF_API int strategy_outputs_series_copy(pf_strategy_t s, int series, int64_t from_bar,
2112 double* out, int64_t capacity, int64_t* written);
2113/** Events queued since the run began or since the last clear. */
2115/** Copy event @p index into @p out: min(@p size_in, sizeof(pf_output_event_v1_t))
2116 * bytes, so an older or newer caller's struct works. Returns 0, or -1 with
2117 * nothing written for an index out of range, a NULL @p out, or a @p size_in
2118 * below the 8 bytes of struct_version and size. */
2119PF_API int strategy_outputs_event_get(pf_strategy_t s, int index, void* out, size_t size_in);
2120/** Drop the queued events, in every mode; rows and the sequence are kept. A
2121 * streaming caller clears after #strategy_stream_begin and after each input
2122 * it has read; a batch caller need not clear. */
2124/** Copy the run constants (NaN = na, or not written this run) into @p out:
2125 * the first min(@p capacity, n) of them. Returns n, the declared count (0
2126 * while recording is off), or -1 for @p capacity < 0 or a NULL @p out with
2127 * @p capacity > 0. */
2128PF_API int strategy_outputs_constants_copy(pf_strategy_t s, double* out, int capacity);
2129
2130#ifndef PINEFORGE_NO_STRATEGY_DECLS
2131/** Part A: #PF_OUTPUTS_API_VERSION. Present only in a module that records. */
2133/** Part A: the module's outputs manifest, canonical JSON
2134 * (`pineforge-outputs-manifest/v1`): what every output, slot and constant
2135 * means. The buffer and status protocol of #strategy_capabilities_receipt. */
2136PF_API int strategy_outputs_manifest(pf_strategy_t s, char* json, size_t capacity,
2137 size_t* required, char* error,
2138 size_t error_capacity);
2139/** Part A: the module's signal-safety receipt, canonical JSON
2140 * (`pineforge-signal-safety/v1`), per output. The protocol of
2141 * #strategy_capabilities_receipt. */
2142PF_API int strategy_signal_safety_receipt(pf_strategy_t s, char* json, size_t capacity,
2143 size_t* required, char* error,
2144 size_t error_capacity);
2145#endif
2146
2147/** @} */ /* end of pf_outputs */
2148
2149/** Feature probe for the borrowed symbol-feed setter. */
2150#define PINEFORGE_HAS_SYMBOL_FEED_BORROWED_V1 1
2151/** Install another symbol's bars without copying them. The caller keeps both
2152 * arrays alive until the feed is replaced or cleared, or #strategy_free.
2153 * Validation and return values match #strategy_set_symbol_feed. Historical
2154 * runs only: stream_begin() fails closed while a symbol feed is installed. */
2156 pf_strategy_t s, const char* key, const char* timeframe,
2157 const pf_bar_t* bars, const int64_t* close_ms, int n);
2158
2159/** Exact C++ engine layout accepted by the live report accumulator.
2160 * Bump whenever that layout changes, independently of #PF_ABI_VERSION. */
2161#define PF_STREAM_ENGINE_LAYOUT_VERSION 4u
2162
2163/** Optional module declaration that its strategy handles point directly to
2164 * a BacktestEngine with the returned #PF_STREAM_ENGINE_LAYOUT_VERSION.
2165 * The engine runtime exports this with its streaming implementation.
2166 * Modules with opaque or wrapped handles must omit it or return 0.
2167 * Absence or 0 requires using #strategy_stream_fill_report. A nonzero
2168 * different version is refused by the native runner before strategy creation;
2169 * rebuild the module with the exact paired engine. Stream ABI and
2170 * execution-capabilities versions alone do not prove layout.
2171 * This query reads no strategy state and changes no report or hash. */
2173
2174/** Set this module's creation-time recorded-series limit on the calling thread.
2175 * Call before strategy_create/strategy_create_checked (or C++ construction):
2176 * the base engine snapshots it before the derived constructor declares.
2177 * NULL or 0 means unlimited, the default. A non-NULL positive int64 is the
2178 * operator's maximum.
2179 * Returns a pf_settings_status_t; negative values return
2180 * #PF_SETTINGS_INVALID_ARGUMENT without changing the previous setting.
2181 * Errors follow the checked-settings buffer protocol. Success also clears
2182 * the typed declaration refusal read by #pf_output_series_limit_failure.
2183 * The host must restore NULL after its factory call; existing hosts retain
2184 * their snapshot. Different threads have independent defaults and refusals. */
2186 char* error, size_t error_capacity);
2187/** Read an outputs_limit / too_many_series declaration refusal even when a
2188 * factory returned NULL. Returns 1 and writes the positive operator maximum
2189 * from the refused setting (NULL or 0 selects unlimited), 0 when no refusal
2190 * has occurred since the last successful setter (leaving the destination
2191 * unchanged), or -1 for NULL. Query on the creation thread before restoring
2192 * the default. No exception or allocation crosses the ABI. */
2194
2195/** Set this module's map-pair creation policy on the calling thread.
2196 * Call before strategy_create/strategy_create_checked (or C++ construction).
2197 * NULL or 0 means unlimited, the default. A positive value is the maximum
2198 * number of distinct pairs in EACH map; replacement never consumes a pair.
2199 * Each host captures the policy and installs it during script preparation
2200 * and callbacks, including maps first created on a later bar. Each map then
2201 * retains its own limit through copy, clear and rollback.
2202 * Restore NULL after the factory call; existing hosts keep their policy.
2203 * Returns a pf_settings_status_t and follows the checked-settings buffer
2204 * protocol. A negative maximum returns #PF_SETTINGS_INVALID_ARGUMENT
2205 * atomically. A refused put raises RE10070 with maxSize/currSize/bar_index
2206 * through the pine_map_capacity_exceeded run-failure catalog entry. */
2207PF_API int pf_map_pairs_limit_set_checked(const int64_t* maximum,
2208 char* error, size_t error_capacity);
2209
2210#ifdef __cplusplus
2211} /* extern "C" */
2212#endif
2213
2214/* The C-level native host API (the versioned C host API). Included last, after the PODs
2215 * it builds on (pf_bar_t, pf_report_t, pf_strategy_t) and outside this
2216 * header's `extern "C"` block, which it opens for itself. Both headers guard
2217 * their own include, so either include order resolves. Its PF_API symbols are
2218 * pinned separately by scripts/check_c_abi_runtime.py, which is why they do
2219 * not move this header's own declaration count. */
2220#include <pineforge/native_c_api.h>
2221
2222/** Feature probe: include <pineforge/report_sink.h> for the additive sink ABI. */
2223#define PINEFORGE_HAS_REPORT_SINK_V1 1
2224
2225#endif /* PINEFORGE_H */
const char * strategy_get_last_error_args(pf_strategy_t s)
The typed arguments of the failure strategy_get_last_error_code names, as one canonical JSON object: ...
void strategy_set_syminfo_timezone(pf_strategy_t s, const char *tz)
Plumb the symbol's exchange timezone (IANA string) into syminfo.
const char * strategy_get_last_error_code(pf_strategy_t s)
Stable code of the most recent failure on s: lower snake_case ASCII matching ^[a-z][a-z0-9_]{2,...
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...
int strategy_set_native_security_feed(pf_strategy_t s, const char *timeframe, const pf_bar_t *bars, int n)
Copy the exchange's OWN bars of one higher timeframe for same-symbol request.security calls that requ...
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_symbol_feed_column(pf_strategy_t s, const char *key, const char *timeframe, const char *name, const double *values, int n)
Copy one named column of an installed symbol feed: n values, one per bar of the feed of (key,...
int strategy_set_symbol_facts(pf_strategy_t s, const char *key, const char *field, const char *value)
Set one fact of the symbol a request site names by key: field is one of "canonical",...
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.
int strategy_set_recorded_series(pf_strategy_t s, const char *key, const int64_t *chart_open_ms, const double *values, int n)
Copy a recorded request series: the values a request returned on the chart bars that opened at chart_...
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.
int strategy_set_symbol_feed(pf_strategy_t s, const char *key, const char *timeframe, const pf_bar_t *bars, const int64_t *close_ms, int n)
Copy another symbol's own bars of one timeframe, keyed by the exact symbol string a request site pass...
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 members of syminfo by Pine member name: "ticker", "tickerid",...
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",...
int strategy_declares_bar_magnifier(void)
Report the script's strategy(..., use_bar_magnifier = true) declaration.
int pf_run_abort_requested_v1(void)
Read the invocation stop flag: 0 or 1.
int strategy_execution_contract(pf_strategy_t s)
Native execution contract query: a pf_execution_contract_t, or -1 for a NULL handle.
uint64_t strategy_closed_trade_entry_incarnation(pf_strategy_t s, int trade_index)
Return the physical entry incarnation for one closed-trade row.
int strategy_configure_native_fx_curve_v1(pf_strategy_t s, const pf_native_fx_curve_v1 *curve)
Stage an immutable account-currency FX curve on a Ready native handle.
pf_execution_contract_t
What strategy_execution_contract answers for a valid handle.
Definition pineforge.h:456
pf_strategy_t strategy_create(const char *params_json)
Allocate a new strategy instance.
int strategy_require_report_scratch_arena_v1(pf_strategy_t s)
Admit a jailed handle only with an attached scratch arena.
pf_native_fx_curve_error_t
Why a native FX curve was refused — the C twin of pineforge::NativeFxCurveError.
Definition pineforge.h:529
int strategy_configure_native_fx_curve_ext_v1(pf_strategy_t s, const pf_native_fx_curve_v1 *curve, pf_native_fx_curve_error_t *error, uint64_t *index)
Typed additive spelling of strategy_configure_native_fx_curve_v1.
int pf_run_abort_install_sigterm_v1(void)
Install the invocation's cooperative SIGTERM handler.
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.
int strategy_report_sink_diagnostics_v1(pf_strategy_t s, struct pf_report_sink_diagnostics_v1 *out)
Read bounded sink provenance without reading the sink or replaying rows.
int pf_report_scratch_arena_stats(uint64_t *high_water_bytes, uint64_t *arena_bytes)
Read process-wide scratch-arena diagnostics, outside canonical report bytes.
void report_free(pf_report_t *report)
Free heap arrays attached to a filled report.
int strategy_configure_native_v1(pf_strategy_t s, const pf_native_run_spec_v1 *spec)
Apply a native v1 specification.
int pf_report_cache_stats(pf_strategy_t s, uint64_t *resident_bytes, uint64_t *allocation_bypass_count)
Read the handle's admitted-run command-cache and trade-ledger diagnostics.
pf_native_spec_optional_t
The bits of pf_native_run_spec_v1::optional_mask: each names the one optional field it makes meaningf...
Definition pineforge.h:469
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 pf_run_abort_restore_sigterm_v1(void)
Restore the disposition saved by pf_run_abort_install_sigterm_v1.
@ PF_EXECUTION_CONTRACT_LEGACY
The retired legacy execution path.
Definition pineforge.h:457
@ PF_EXECUTION_CONTRACT_NATIVE_MARKET_V1
The native market kernel; every handle this runtime creates.
Definition pineforge.h:458
@ PF_NATIVE_FX_CURVE_ERROR_NOT_FINITE_POSITIVE
Definition pineforge.h:533
@ PF_NATIVE_FX_CURVE_ERROR_NOT_STRICTLY_INCREASING
Definition pineforge.h:532
@ PF_NATIVE_FX_CURVE_ERROR_ALLOCATION_FAILURE
Definition pineforge.h:534
@ PF_NATIVE_FX_CURVE_ERROR_LENGTH_MISMATCH
Definition pineforge.h:531
@ PF_NATIVE_FX_CURVE_ERROR_WRONG_PHASE
Definition pineforge.h:535
@ PF_NATIVE_FX_CURVE_ERROR_NONE
Definition pineforge.h:530
@ PF_NATIVE_SPEC_OPTIONAL_MAX_ABS_UNITS
max_abs_units.
Definition pineforge.h:471
@ PF_NATIVE_SPEC_OPTIONAL_MAX_OPEN_LOTS
max_open_lots.
Definition pineforge.h:473
@ PF_NATIVE_SPEC_OPTIONAL_INITIAL_MARGIN_FRACTION
initial_margin_fraction.
Definition pineforge.h:472
@ PF_NATIVE_SPEC_OPTIONAL_QUANTITY_GRID
quantity_grid.
Definition pineforge.h:470
double strategy_net_profit_sum(pf_strategy_t s)
Raw whole-lot realized P&L, excluding initial capital and report-only terminal rows.
double strategy_current_equity(pf_strategy_t s)
Task 9: initial capital plus realized net profit (strategy.initial_capital + strategy....
pf_scratch_table_backend_t
Storage policy for the nine historical scratch-table regions.
Definition pineforge.h:1040
void strategy_set_probe_suppress_tail_logic(pf_strategy_t s, int on)
Live probe tail suppression (spec §3.2): the LAST script bar of the array fed to every subsequent run...
const char * strategy_closed_trade_exit_id(pf_strategy_t s, int trade_index)
See strategy_closed_trade_entry_id for the row-space/lifetime/NULL contract.
pf_state_query_status_v1_t strategy_state_query_status_v1(pf_strategy_t s)
The current eligibility of BOTH strategy_broker_state_hash and strategy_stream_state_hash on s,...
void strategy_set_path_order(pf_strategy_t s, int mode)
Force this run's intrabar path order (ABI v4 live-runtime surface): the leg order every modeled OHLC ...
int64_t strategy_script_bars_processed(pf_strategy_t s)
Task 9: total SCRIPT bars dispatched by the most recent run() (mirrors pf_report_t::script_bars_proce...
void strategy_request_abort(pf_strategy_t s)
Request cooperative abort of the run in progress (see c_abi.cpp).
int strategy_closed_trade_close_cause(pf_strategy_t s, int trade_index)
Task 9: why the trade_index-th REPORT-row closed trade exited, a pf_close_cause_t.
uint32_t pf_cache_kind_t
Append-only cache-kind vocabulary.
Definition pineforge.h:920
int32_t pf_state_query_status_v1_t
What strategy_state_query_status_v1 answers: whether the two state queries, strategy_broker_state_has...
Definition pineforge.h:1296
int strategy_set_scratch_tables_v1(pf_strategy_t s, const pf_scratch_tables_v1 *config)
Copy scratch-table configuration while idle; NULL restores disk defaults.
double strategy_position_avg_price(pf_strategy_t s)
The live position's volume-weighted average entry price (position_entry_price_).
const char * strategy_closed_trade_entry_id(pf_strategy_t s, int trade_index)
Task 9: closed-trade id / exit-comment string accessors, indexing the same REPORT row space as strate...
const char * strategy_closed_trade_exit_comment(pf_strategy_t s, int trade_index)
See strategy_closed_trade_entry_id.
int strategy_pending_orders_len(pf_strategy_t s)
Number of orders resting in the engine's pending-order book after the most recent run() (ABI v4 live-...
int64_t strategy_position_cycle_seq(pf_strategy_t s)
The live position's cycle id (position_cycle_seq_): 0 when flat, a fresh nonzero id per open or rever...
int strategy_pending_order_effective_levels(pf_strategy_t s, int index, double *stop, double *limit, double *trail_activation)
The price levels the index-th resting order would fire at, as the engine's fill path resolves them (A...
int strategy_pending_order_level_resolved(pf_strategy_t s, int index)
1 when the index-th resting order's entry-relative offsets (profit_ticks / loss_ticks / trail_points)...
pf_fill_qty_partition_t
Which sizing rule strategy_pending_order_fill_qty's quantity came from, written to its partition out-...
Definition pineforge.h:1368
int strategy_pending_order_fill_qty(pf_strategy_t s, int index, double fill_price, double *qty, int *close_only, int *partition)
Engine-computed fill quantity of the index-th resting order (ABI v4 live-runtime surface,...
int strategy_last_bar_dual_entry_path(pf_strategy_t s)
The dual-entry-stop arbitration decided on the LAST bar the most recent run() dispatched: a flat posi...
double strategy_position_size(pf_strategy_t s)
Task 9: the script-facing signed position size (strategy.position_size; Freeze-aware – while a same-b...
int strategy_pending_order_get(pf_strategy_t s, int index, void *out, size_t size_in)
Copy the index-th resting order (0-based, the engine's own book order – insertion order; broker fill ...
double strategy_report_pointvalue(pf_strategy_t s)
Effective symbol pointvalue used by the engine report.
int strategy_set_report_replay_v1(pf_strategy_t s, const char *path, const char *run_id)
Opt in to the separate pf-report-replay/v1 companion before a run.
const pf_field_desc_t * strategy_pending_order_layout(int *count)
The field table of pf_pending_order_v1_t as THIS runtime compiled it – one pf_field_desc_t {name,...
int strategy_set_cache_policy_v1(pf_strategy_t s, const pf_cache_policy_v1 *policy)
Copy cache settings before run admission; NULL restores internal defaults.
int strategy_last_run_status(pf_strategy_t s)
0 = completed, 1 = NOT_COMPLETED (aborted), -1 = s is NULL.
double strategy_trail_best_price(pf_strategy_t s)
The trail extreme the exit trail legs ride (trail_best_price_: the running high of a long / low of a ...
pf_close_cause_t
Why a closed report row exited — execution::CloseCause, the value strategy_closed_trade_close_cause a...
Definition pineforge.h:1507
double strategy_initial_capital(pf_strategy_t s)
ABI v4 live-runtime surface (1.8.0): the configured run's initial capital, before realized or open pr...
void strategy_set_broker_state_hash_recording(pf_strategy_t s, int on)
Toggle per-report-point broker-state hash recording (spec §3.4, ABI v4).
uint64_t strategy_broker_state_hash(pf_strategy_t s)
Return the broker-state hash of the FINAL state after the most recent run() (see strategy_set_broker_...
int strategy_set_scratch_io_policy_v1(pf_strategy_t s, const pf_scratch_io_policy_v1 *policy)
Copy settings before admission; NULL restores the 65536-byte default.
void strategy_set_realtime_tail(pf_strategy_t s, int on, int horizon_bars)
Live-runtime tail semantics (spec §3.1): the LAST bar of the array fed to every subsequent run() is a...
@ PF_SCRATCH_TABLE_BOUNDED_MEMORY
Definition pineforge.h:1042
@ PF_SCRATCH_TABLE_DISK_SCRATCH
Definition pineforge.h:1041
@ PF_FILL_QTY_PARTITION_EXIT
An exit: no partition.
Definition pineforge.h:1369
@ PF_FILL_QTY_PARTITION_DEFAULT_STOP_PLACEMENT
Definition pineforge.h:1372
@ PF_FILL_QTY_PARTITION_EXPLICIT
Definition pineforge.h:1370
@ PF_FILL_QTY_PARTITION_FROZEN_PLACEMENT
Definition pineforge.h:1371
@ PF_FILL_QTY_PARTITION_AT_FILL
Definition pineforge.h:1373
@ PF_CLOSE_CAUSE_LIQUIDATION
A margin-call liquidation slice (MARGIN_CALL).
Definition pineforge.h:1511
@ PF_CLOSE_CAUSE_RANGE_END
The range-end close of a position still open.
Definition pineforge.h:1514
@ PF_CLOSE_CAUSE_SCRIPT
A market close or a reversal.
Definition pineforge.h:1509
@ PF_CLOSE_CAUSE_RISK_LIMIT
A risk-limit flatten (INTRADAY_LOSS_CAP).
Definition pineforge.h:1512
@ PF_CLOSE_CAUSE_BRACKET
A bracket leg.
Definition pineforge.h:1510
@ PF_CLOSE_CAUSE_UNSPECIFIED
No cause recorded (UNKNOWN).
Definition pineforge.h:1508
@ PF_CLOSE_CAUSE_FILL_CAP
A filled-order cap (INTRADAY_FILL_CAP).
Definition pineforge.h:1513
int strategy_outputs_set_enabled(pf_strategy_t s, int on)
Turn recording on (on nonzero) or off.
int strategy_outputs_manifest(pf_strategy_t s, char *json, size_t capacity, size_t *required, char *error, size_t error_capacity)
Part A: the module's outputs manifest, canonical JSON (pineforge-outputs-manifest/v1): what every out...
int64_t strategy_outputs_bars_len(pf_strategy_t s)
Rows recorded: one per bar the module published this run.
void strategy_outputs_events_clear(pf_strategy_t s)
Drop the queued events, in every mode; rows and the sequence are kept.
pf_output_phase_t
The run phase an event was recorded in.
Definition pineforge.h:2053
int strategy_outputs_series_count(pf_strategy_t s)
Series slots per row; 0 while recording is off.
int strategy_outputs_bar_times_copy(pf_strategy_t s, int64_t from_bar, int64_t *open_ms, int64_t *close_ms, int64_t capacity, int64_t *written)
Copy the open and close times (Unix ms) of rows from from_bar on.
int strategy_signal_safety_receipt(pf_strategy_t s, char *json, size_t capacity, size_t *required, char *error, size_t error_capacity)
Part A: the module's signal-safety receipt, canonical JSON (pineforge-signal-safety/v1),...
int strategy_outputs_series_copy(pf_strategy_t s, int series, int64_t from_bar, double *out, int64_t capacity, int64_t *written)
Copy one slot's values (NaN = na) of rows from from_bar on, as strategy_outputs_bar_times_copy does; ...
uint32_t strategy_outputs_api_version(void)
Part A: PF_OUTPUTS_API_VERSION.
int strategy_outputs_constants_copy(pf_strategy_t s, double *out, int capacity)
Copy the run constants (NaN = na, or not written this run) into out: the first min(capacity,...
int strategy_outputs_events_len(pf_strategy_t s)
Events queued since the run began or since the last clear.
int strategy_outputs_event_get(pf_strategy_t s, int index, void *out, size_t size_in)
Copy event index into out: min(size_in, sizeof(pf_output_event_v1_t)) bytes, so an older or newer cal...
@ PF_OUTPUT_PHASE_REALTIME
a stream after its warm-up
Definition pineforge.h:2056
@ PF_OUTPUT_PHASE_BATCH
a batch run
Definition pineforge.h:2054
@ PF_OUTPUT_PHASE_WARMUP
a stream's warm-up, inside strategy_stream_begin
Definition pineforge.h:2055
pf_settings_status_t
Status codes for the opt-in generated-strategy settings extension.
Definition pineforge.h:1935
@ PF_SETTINGS_UNSUPPORTED
Definition pineforge.h:1938
@ PF_SETTINGS_RUN_FAILED
Definition pineforge.h:1941
@ PF_SETTINGS_EXCEPTION
Definition pineforge.h:1939
@ PF_SETTINGS_BUFFER_TOO_SMALL
Definition pineforge.h:1940
@ PF_SETTINGS_INVALID_ARGUMENT
Definition pineforge.h:1937
@ PF_SETTINGS_OK
Definition pineforge.h:1936
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_order_actions_len(pf_strategy_t s)
Queued physical fills since the last clear, in execution order.
int strategy_stream_push_ticks(pf_strategy_t s, const pf_trade_tick_t *ticks, int n)
Push an ordered batch of realtime trades.
uint64_t strategy_stream_state_hash(pf_strategy_t s)
Versioned deterministic fingerprint of observable broker/stream state.
int strategy_stream_api_version(void)
Native live extension version (1).
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_push_bar(pf_strategy_t s, const pf_bar_t *bar)
Consume one confirmed input-timeframe bar.
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.
int strategy_stream_order_action_get(pf_strategy_t s, int index, pf_stream_order_action_t *out)
Copy one action; return 0 on success, -1 on invalid handle/index/output.
void strategy_stream_order_actions_clear(pf_strategy_t s)
Clear observed events after the caller durably journals them.
pf_magnifier_distribution_t
Bar-magnifier sub-bar sampling distribution.
Definition pineforge.h:128
@ PF_MAGNIFIER_FRONT_LOADED
Sample density biased toward bar open.
Definition pineforge.h:133
@ PF_MAGNIFIER_COSINE
Cosine-tapered density.
Definition pineforge.h:130
@ PF_MAGNIFIER_ENDPOINTS
Default — exact O,H,L,C points plus uniform fill between.
Definition pineforge.h:132
@ PF_MAGNIFIER_BACK_LOADED
Sample density biased toward bar close.
Definition pineforge.h:134
@ PF_MAGNIFIER_TRIANGLE
Triangle-tapered density.
Definition pineforge.h:131
@ PF_MAGNIFIER_UNIFORM
Uniform spacing across the parent bar.
Definition pineforge.h:129
int pf_abi_version(void)
pf_version_t pf_version_get(void)
const char * pf_version_string(void)
Full version descriptor, the PINEFORGE_VERSION_FULL of the generated pineforge/version....
void * pf_strategy_t
Opaque handle to a compiled strategy instance.
Definition pineforge.h:441
#define PF_API
Definition pineforge.h:83
int pf_output_series_limit_set_checked(const int64_t *maximum, char *error, size_t error_capacity)
Set this module's creation-time recorded-series limit on the calling thread.
int strategy_set_input_checked(pf_strategy_t s, const char *key, const char *value, char *error, size_t error_capacity)
Validate and install a declared input before execution.
int run_backtest_full_checked(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, char *error, size_t error_capacity)
Exception-contained counterpart of run_backtest_full.
int strategy_get_effective_settings(pf_strategy_t s, char *json, size_t capacity, size_t *required, char *error, size_t error_capacity)
Read a deterministic UTF-8 JSON receipt of effective settings.
uint32_t strategy_capabilities_api_version(void)
Compiled execution-capabilities extension version (currently 1).
int strategy_create_checked(const char *params_json, pf_strategy_t *out, char *error, size_t error_capacity)
Allocate a strategy without allowing any exception across the C boundary.
int strategy_set_symbol_feed_borrowed_v1(pf_strategy_t s, const char *key, const char *timeframe, const pf_bar_t *bars, const int64_t *close_ms, int n)
Install another symbol's bars without copying them.
#define PF_STATIC_ASSERT(condition)
Definition pineforge.h:52
uint32_t strategy_stream_engine_layout_version(void)
Optional module declaration that its strategy handles point directly to a BacktestEngine with the ret...
int strategy_capabilities_receipt(pf_strategy_t s, char *json, size_t capacity, size_t *required, char *error, size_t error_capacity)
Read immutable canonical JSON describing compiled execution declarations and analyzed request/data re...
uint32_t strategy_settings_api_version(void)
Generated-strategy settings extension version (currently 1).
int pf_map_pairs_limit_set_checked(const int64_t *maximum, char *error, size_t error_capacity)
Set this module's map-pair creation policy on the calling thread.
int strategy_set_override_checked(pf_strategy_t s, const char *key, const char *value, char *error, size_t error_capacity)
Validate and install a supported strategy override before execution.
int pf_output_series_limit_failure(int64_t *maximum)
Read an outputs_limit / too_many_series declaration refusal even when a factory returned NULL.
Single OHLCV bar pushed into the engine.
Definition pineforge.h:140
double volume
Bar volume.
Definition pineforge.h:145
double high
High price.
Definition pineforge.h:142
double low
Low price.
Definition pineforge.h:143
double close
Close price.
Definition pineforge.h:144
double open
Open price.
Definition pineforge.h:141
int64_t timestamp
Bar open time, Unix milliseconds.
Definition pineforge.h:146
One optional resident cache size.
Definition pineforge.h:970
uint32_t kind
Definition pineforge.h:971
uint32_t reserved
Definition pineforge.h:972
uint64_t capacity_bytes
Definition pineforge.h:973
Versioned resident cache policy.
Definition pineforge.h:987
const pf_cache_policy_entry_v1 * entries
Definition pineforge.h:991
uint32_t struct_size
Definition pineforge.h:988
uint32_t reserved[2]
Definition pineforge.h:992
uint64_t entry_count
Definition pineforge.h:990
Single per-script-bar equity point.
Definition pineforge.h:326
double open_profit
Mark-to-market open P&L at bar close.
Definition pineforge.h:329
double equity
initial_capital + net_profit + open_profit.
Definition pineforge.h:328
int64_t time_ms
Script-bar OPEN timestamp (Unix ms).
Definition pineforge.h:327
Equity-curve-derived statistics (all-trades only, like TV).
Definition pineforge.h:272
double sharpe_monthly
Month-end-resampled equity simple returns (chart timezone, open-time bucketing), risk-free 2%/yr (2/1...
Definition pineforge.h:290
double time_in_market_pct
PERCENT (0-100) of script bars with an open position at bar close.
Definition pineforge.h:310
double max_equity_drawdown_pct
max_equity_drawdown relative to the peak in effect (PERCENT 0-100).
Definition pineforge.h:274
double max_equity_drawdown
Peak-to-trough equity drop, positive currency magnitude.
Definition pineforge.h:273
double max_equity_runup_pct
max_equity_runup relative to that trough (PERCENT 0-100).
Definition pineforge.h:278
double buy_hold_return
initial_capital * (last_close/first_open - 1), currency.
Definition pineforge.h:279
double open_pl
Mark-to-market open profit at the final bar.
Definition pineforge.h:312
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:276
double cagr
PERCENT per year: 100*((final_equity/initial_capital)^(1/years)-1).
Definition pineforge.h:304
double recovery_factor
net_profit / max_equity_drawdown (currency / currency).
Definition pineforge.h:308
double calmar
cagr / max_equity_drawdown_pct — BOTH IN PERCENT, so the ratio is dimensionless.
Definition pineforge.h:306
double buy_hold_return_pct
buy_hold_return as PERCENT.
Definition pineforge.h:281
double sharpe_bar
Per-script-bar returns, annualized by observed bar density (bars per year = (len-1)/calendar span),...
Definition pineforge.h:297
double sortino_monthly
Same resampling as sharpe_monthly; uses population downside deviation vs the monthly risk-free.
Definition pineforge.h:296
double sortino_bar
Same construction as sharpe_bar over per-bar returns; uses population downside deviation.
Definition pineforge.h:301
Composite metrics container: trade stats (all / long / short) + equity-curve stats.
Definition pineforge.h:317
pf_trade_stats_t all
Definition pineforge.h:318
pf_equity_stats_t equity
Definition pineforge.h:319
pf_trade_stats_t longs
Definition pineforge.h:318
pf_trade_stats_t shorts
Definition pineforge.h:318
Immutable timestamped account-currency FX curve for a native run.
Definition pineforge.h:519
const int64_t * effective_from_ms
Definition pineforge.h:522
const double * account_per_quote
Definition pineforge.h:523
Versioned native run specification.
Definition pineforge.h:500
const char * tickerid
Definition pineforge.h:504
uint32_t allowed_open_directions
Definition pineforge.h:508
const char * volumetype
Definition pineforge.h:504
const char * basecurrency
Definition pineforge.h:504
const char * input_tf
Definition pineforge.h:503
const char * session_key
Definition pineforge.h:502
double initial_margin_fraction
Definition pineforge.h:509
const char * type
Definition pineforge.h:504
const char * script_tf
Definition pineforge.h:503
const char * chart_timezone
Definition pineforge.h:505
const char * timezone
Definition pineforge.h:505
const char * ticker
Definition pineforge.h:504
const char * session
Definition pineforge.h:505
const char * description
Definition pineforge.h:504
const char * currency
Definition pineforge.h:504
One recorded event, copied by strategy_outputs_event_get.
Definition pineforge.h:2063
int64_t bar_close_ms
the row's close, Unix ms; INT64_MIN when unknown
Definition pineforge.h:2070
uint32_t ordinal_in_bar
per output, from 0 within the row
Definition pineforge.h:2071
double value
the event's value; NaN = na
Definition pineforge.h:2075
uint32_t size
sizeof(pf_output_event_v1_t) as the runtime compiled it
Definition pineforge.h:2065
int32_t bar_index
the row, 0-based
Definition pineforge.h:2068
uint64_t sequence
1-based per run; once cleared, never issued again
Definition pineforge.h:2066
uint32_t struct_version
1
Definition pineforge.h:2064
const char * message
NUL-terminated, or NULL without one: a message holding a NUL byte reads to its first NUL,...
Definition pineforge.h:2078
int64_t bar_open_ms
the row's open, Unix ms
Definition pineforge.h:2069
uint32_t phase
a pf_output_phase_t
Definition pineforge.h:2072
int32_t output_index
the output, as the manifest's outputs[].index
Definition pineforge.h:2067
uint32_t confirmed
0 when recorded on a partially finalized bar
Definition pineforge.h:2073
uint64_t message_hash64
FNV-1a 64 of every byte the module recorded as the message; 0 without one.
Definition pineforge.h:2076
Fixed-size annualization provenance from the emitted v1 records.
Backtest report filled by run_backtest / run_backtest_full.
Definition pineforge.h:366
int input_tf_seconds
Detected/configured input timeframe (seconds).
Definition pineforge.h:393
int security_diag_len
Length of security_diag.
Definition pineforge.h:401
int64_t security_feeds_total
Total higher-TF feed bars across all security sites.
Definition pineforge.h:384
int bar_magnifier_enabled
1 if magnifier was active for this run.
Definition pineforge.h:397
pf_trace_entry_t * trace
Per-bar trace records (empty unless tracing enabled).
Definition pineforge.h:404
int trace_names_len
Length of trace_names.
Definition pineforge.h:407
int64_t input_bars_processed
Source-feed bars consumed.
Definition pineforge.h:380
int script_tf_seconds
Script timeframe (seconds).
Definition pineforge.h:394
double net_profit
Sum of all closed-trade PnL.
Definition pineforge.h:377
int64_t equity_curve_len
Definition pineforge.h:424
int trades_len
Resident length of trades, not the logical total.
Definition pineforge.h:376
pf_metrics_t metrics
Definition pineforge.h:413
int trace_len
Length of trace.
Definition pineforge.h:405
int64_t script_bars_processed
Script-timeframe bars evaluated.
Definition pineforge.h:381
int64_t magnifier_sample_ticks_total
Sample ticks visited by the magnifier.
Definition pineforge.h:390
int total_trades
Logical closed-trade count, including the range-end close of a position still open after the final ba...
Definition pineforge.h:368
int64_t security_partial_total
Total partial-bar evals across all security sites.
Definition pineforge.h:386
pf_security_diag_t * security_diag
One entry per request.security site.
Definition pineforge.h:400
int64_t magnifier_sub_bars_total
Sub-bars synthesized by the magnifier.
Definition pineforge.h:389
int64_t broker_state_hash_len
Definition pineforge.h:435
uint64_t * broker_state_hash
Definition pineforge.h:434
const char ** trace_names
Names indexed by pf_trace_entry_t::name_id.
Definition pineforge.h:406
pf_equity_point_t * equity_curve
Definition pineforge.h:423
int64_t security_complete_total
Total complete-bar evals across all security sites.
Definition pineforge.h:385
int script_tf_ratio
script_tf_seconds / input_tf_seconds.
Definition pineforge.h:395
int needs_aggregation
1 if input → script TF aggregation was performed.
Definition pineforge.h:396
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:374
Disk scratch write batching, independent of resident cache policy.
Definition pineforge.h:1019
Version 1 scratch-table admission maxima, in keys/rows and key bytes.
Definition pineforge.h:1062
Per-request.security() site diagnostic counters.
Definition pineforge.h:335
int sec_id
Stable id for the request.security site.
Definition pineforge.h:336
int64_t feed_count
Higher-TF feed bars consumed.
Definition pineforge.h:337
int64_t complete_count
Evaluations on completed parent bars.
Definition pineforge.h:338
int64_t partial_count
Evaluations on still-forming parent bars.
Definition pineforge.h:339
One physical emulator lot action.
Definition pineforge.h:831
Single per-bar trace entry.
Definition pineforge.h:347
double value
Traced expression value on this bar.
Definition pineforge.h:351
int64_t timestamp
Bar timestamp (Unix ms).
Definition pineforge.h:348
int32_t name_id
Index into pf_report_t::trace_names.
Definition pineforge.h:350
int32_t bar_index
Zero-based bar index.
Definition pineforge.h:349
Trade-level statistics block — computed once each for all / long / short.
Definition pineforge.h:209
double avg_win_pct
Mean of per-trade pnl_pct over winning trades.
Definition pineforge.h:232
double avg_win
gross_profit / num_wins.
Definition pineforge.h:231
double net_profit_pct
net_profit as a percent of initial capital (0-100 scale).
Definition pineforge.h:219
double largest_loss_pct
Maximum of -pnl_pct over losing trades (positive magnitude) — an INDEPENDENT maximum,...
Definition pineforge.h:249
int32_t num_losses
Trades with pnl < 0.
Definition pineforge.h:212
double gross_profit
Sum of winning pnl.
Definition pineforge.h:221
int32_t max_consecutive_losses
Longest losing run; even trades reset both streaks.
Definition pineforge.h:258
double avg_loss_pct
Mean of the NEGATED pnl_pct of the losing trades.
Definition pineforge.h:236
double commission_paid
Sum of pf_trade_t::commission in the block.
Definition pineforge.h:254
double percent_profitable
100 * num_wins / num_trades, in PERCENT (0-100).
Definition pineforge.h:216
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:243
double gross_loss
Sum of |losing pnl| — POSITIVE magnitude (TV display convention).
Definition pineforge.h:224
double gross_profit_pct
gross_profit as a percent of initial capital (0-100 scale).
Definition pineforge.h:222
double largest_loss
Single largest |pnl| among losing trades (positive magnitude).
Definition pineforge.h:247
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:263
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:266
double profit_factor
gross_profit / gross_loss.
Definition pineforge.h:227
double avg_loss
gross_loss / num_losses (positive magnitude).
Definition pineforge.h:234
double largest_win
Single largest pnl among winning trades.
Definition pineforge.h:241
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:259
double gross_loss_pct
gross_loss as a percent of initial capital (0-100 scale).
Definition pineforge.h:225
int32_t max_consecutive_wins
Longest winning run; even trades reset both streaks.
Definition pineforge.h:257
double expectancy
(num_wins/num_trades)*avg_win - (num_losses/num_trades)*avg_loss, account currency per trade.
Definition pineforge.h:255
double net_profit
Sum of pnl (account currency, net of commission).
Definition pineforge.h:218
int32_t num_trades
Closed trades in this block (all / long-only / short-only).
Definition pineforge.h:210
double avg_trade_pct
Mean of per-trade pnl_pct over all trades.
Definition pineforge.h:229
double ratio_avg_win_avg_loss
avg_win / avg_loss.
Definition pineforge.h:240
double avg_trade
net_profit / num_trades.
Definition pineforge.h:228
int32_t num_even
Trades with pnl == 0.0 exactly; breaks both win and loss streaks; excluded from win/loss averages.
Definition pineforge.h:213
int32_t num_wins
Trades with pnl > 0.
Definition pineforge.h:211
Closed-trade record returned in pf_report_t::trades.
Definition pineforge.h:166
int32_t exit_bar_index
Script-bar index of the exit fill (0-based).
Definition pineforge.h:187
double pnl_pct
Net return-on-cost in percent: pnl (NET of commission) / entry cost (entry_price * qty * pointvalue) ...
Definition pineforge.h:172
double exit_price
Exit fill price (incl.
Definition pineforge.h:170
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:188
int32_t entry_bar_index
Script-bar index of the entry fill (0-based).
Definition pineforge.h:186
double commission
Entry+exit commission actually deducted from pnl (account currency).
Definition pineforge.h:184
double pnl
Net realized PnL in account currency (commission-inclusive).
Definition pineforge.h:171
int is_long
1 if long, 0 if short.
Definition pineforge.h:180
double max_drawdown
Peak adverse whole-trade excursion, account currency, net of entry fees.
Definition pineforge.h:182
double qty
Filled quantity.
Definition pineforge.h:183
double max_runup
Peak favorable whole-trade excursion, account currency, net of entry fees.
Definition pineforge.h:181
int64_t entry_time
Entry fill time (Unix ms).
Definition pineforge.h:167
double entry_price
Entry fill price (incl.
Definition pineforge.h:169
int64_t exit_time
Exit fill time (Unix ms).
Definition pineforge.h:168
One provider-neutral realtime executed-trade update.
Definition pineforge.h:156
double quantity
Traded quantity in symbol volume units (>= 0).
Definition pineforge.h:160
uint64_t sequence
Normalized per-stream sequence, or 0.
Definition pineforge.h:158
int64_t timestamp
Source event time, Unix milliseconds.
Definition pineforge.h:157
double price
Executed trade price (> 0).
Definition pineforge.h:159
Runtime version descriptor returned by pf_version_get.
Definition pineforge.h:1898
int patch
Patch version.
Definition pineforge.h:1901
int minor
Minor version.
Definition pineforge.h:1900
int major
Major version.
Definition pineforge.h:1899
const char * commit_sha
Short git commit SHA, or "" if unknown.
Definition pineforge.h:1902