PineForge unreleased
Deterministic PineScript v6 backtest runtime — C ABI reference
Loading...
Searching...
No Matches
report_sink_writer.hpp
Go to the documentation of this file.
1#pragma once
2
3// CR-150 P00 internal C++ declarations only. DESIGN.md revision 6,
4// section 3.7:782-901; C receipt mapping in section 6:1291-1317.
6
7#include <cstdint>
8#include <memory>
9#include <string>
10#include <string_view>
11
12namespace pineforge {
13namespace report_sink {
14
15/// Run properties selecting the fixed version-1 descriptors (3.7:795-809,
16/// 823-831; 6:1310-1311). E and T are unconditional. outputs selects O, V,
17/// K together; trace selects X; hashes selects H. series_slots supplies k
18/// for O's v0..v<k-1>, constant_count supplies m for K's c0..c<m-1>.
19/// Descriptor order is E,T,O,V,K,X,H; fields/types/units are exactly DESIGN's
20/// table, excluding the tag. No caller-defined schema is admitted in v1.
21struct RecordSet {
22 bool outputs;
23 bool trace;
24 bool hashes;
25 std::int64_t series_slots;
26 std::int64_t constant_count;
27};
28
29/// Trailer values for mapping to the C receipt (3.7:833-837; 6:1291-1298).
30/// Only types present in the header are emitted as counts, in header order.
31/// sha256_hex is 64 lowercase hex digits followed by NUL. Completion and
32/// trades_retained belong to the runtime receipt, not to this wire trailer.
33struct Trailer {
35 char sha256_hex[65];
36};
37
38/// One complete JSON f64 token, exactly Python 3 repr(double), or "null"
39/// for non-finite values (3.7:850-853, 858-875). Preserve -0.0 and shortest
40/// round-trip digits. First-digit decimal exponent -4 <= X < 16 uses fixed
41/// notation with a digit after the point; otherwise use scientific notation
42/// with an exponent sign and at least two digits. No locale-dependent text.
43/// DESIGN prescribes shortest scientific to_chars followed by re-layout;
44/// this declaration supplies no formatter implementation.
45std::string f64(double value);
46
47/// One complete quoted JSON string (3.7:878-879). Escape double quote and
48/// backslash; encode U+0000..U+001F as lowercase \\u00xx. Other bytes remain
49/// raw UTF-8; a byte not starting a valid UTF-8 sequence becomes U+FFFD.
50std::string escaped_string(std::string_view utf8);
51
52/// Checked 64 KiB buffered path/borrowed-fd writer, with opaque state.
53/// Every line is one compact JSON value plus LF, UTF-8 without BOM, at most
54/// 1 MiB including LF. No whitespace outside strings, blank lines or CR.
55/// Integers are base 10 without leading zeros/+ or exponents, within
56/// +/-(2^53-1) for i64 and 0..2^53-1 for u64; bool uses true/false; hex64
57/// uses exactly 16 lowercase hex digits. f64/strings use the helpers above.
58///
59/// Lifecycle: header at run begin, records in final order, trailer only on
60/// successful fill/capture. Failure/refusal/abort leaves no valid trailer.
61/// I/O errors fail the run with the catalog code planned in DESIGN 6:1330-1332
62/// (docker/run_failure_codes.json; 3.7:881-888). OPEN S2: DESIGN does not
63/// choose the internal C++ error transport or repeated-finalization policy;
64/// no exception type, retry or idempotence guarantee is declared here.
65/// O/V/K/X/H operations below implement the same fixed version-1 descriptors.
67public:
68 /// Descriptor strings are copied, as required by 6:1307. This is a
69 /// declaration of configuration; run-begin work belongs to write_header.
70 explicit LineWriter(const pf_report_sink_v1& sink);
71 /// Adopt without duplicating/closing fd; see report_sink.h for ownership,
72 /// FIFO SIGPIPE handling and sequential stream/back-pressure guarantees.
73 explicit LineWriter(const pf_report_sink_fd_v1& sink);
75
76 /// Open the owned path, or validate the borrowed stream fd without
77 /// creating anything, and emit, in order, schema, version, run_id,
78 /// engine, records (3.7:795-809, 881-883). schema="pf-report-sink",
79 /// version=1, run_id from the descriptor (NULL => ""), engine is
80 /// PINEFORGE_VERSION_STRING. Each descriptor has type,fields,types,units.
81 void write_header(const RecordSet& records);
82
83 /// Emit ["E",time_ms,equity,open_profit] (3.7:825), in time order.
84 /// time_ms is the script-bar OPEN. Caller supplies the final presented
85 /// point after hold-back; selected output is anchor + window points
86 /// (3.2:490-509; 3.6:756-773). The writer does not alter the held point.
87 void write_e(const pf_equity_point_t& point);
88
89 /// Emit T in exactly this field order (3.7:826): n,side,entry_time,
90 /// exit_time,entry_price,exit_price,qty,pnl,pnl_pct,max_runup,max_drawdown,
91 /// commission,entry_bar_index,exit_bar_index,open_at_end,entry_incarnation,
92 /// close_cause,entry_id,exit_id,exit_comment. n starts at 1; side is
93 /// "long"/"short" from trade.is_long, open_at_end is a JSON bool.
94 /// Numeric trade fields come from pf_trade_t; extra fields are the
95 /// existing runtime accessors' values. Strings are consumed by the call.
96 /// Caller supplies sealed rows, then final unsealed and range-end rows
97 /// in report order (3.3:547-551; 3.4:639-652).
98 void write_t(std::int64_t n, const pf_trade_t& trade,
99 std::uint64_t entry_incarnation, std::int64_t close_cause,
100 std::string_view entry_id, std::string_view exit_id,
101 std::string_view exit_comment);
102
103 /// Emit trailer,version,counts,sha256 in that key order, version=1
104 /// (3.7:833-837). SHA-256 is FIPS 180-4 over every byte before the
105 /// trailer, including the header and each LF; the trailer excludes
106 /// itself. Flush/check writes before returning the trailer values.
107 /// SHA-256 runs inside the engine with no new external dependency
108 /// (3.7:881-888). No destructor-generated success trailer is promised.
110
111 /// Bounded provenance of the successfully emitted E/T records. Zero and
112 /// incomplete until the trailer succeeds; no input or output I/O.
114
115 /// At a completed-bar boundary, flush the last complete records and
116 /// permanently abandon this stream without a trailer or a receipt.
117 /// Does not close a borrowed fd; its owner closes it after native cleanup.
118 void abandon();
119
120 // L6b-3 additions implementing the existing v1 descriptors. INT64_MIN
121 // represents a null output time. Spans are consumed during the call.
122 void write_o(std::int64_t row, std::int64_t open_ms, std::int64_t close_ms,
123 const double* values, std::int64_t count);
124 // event.message selects null versus present; message supplies the exact
125 // bytes of a present message, including embedded NULs.
126 void write_v(const pf_output_event_v1_t& event, std::string_view message);
127 void write_k(const double* values, std::int64_t count);
128 void write_x(const pf_trace_entry_t& entry, std::string_view name);
129 void write_h(std::uint64_t hash);
130
131private:
132 struct State;
133 std::unique_ptr<State> state_;
134};
135
136} // namespace report_sink
137} // namespace pineforge
LineWriter(const pf_report_sink_fd_v1 &sink)
Adopt without duplicating/closing fd; see report_sink.h for ownership, FIFO SIGPIPE handling and sequ...
void write_x(const pf_trace_entry_t &entry, std::string_view name)
void write_t(std::int64_t n, const pf_trade_t &trade, std::uint64_t entry_incarnation, std::int64_t close_cause, std::string_view entry_id, std::string_view exit_id, std::string_view exit_comment)
Emit T in exactly this field order (3.7:826): n,side,entry_time, exit_time,entry_price,...
void write_o(std::int64_t row, std::int64_t open_ms, std::int64_t close_ms, const double *values, std::int64_t count)
LineWriter(const pf_report_sink_v1 &sink)
Descriptor strings are copied, as required by 6:1307.
pf_report_sink_diagnostics_v1 diagnostics() const noexcept
Bounded provenance of the successfully emitted E/T records.
void abandon()
At a completed-bar boundary, flush the last complete records and permanently abandon this stream with...
void write_h(std::uint64_t hash)
void write_header(const RecordSet &records)
Open the owned path, or validate the borrowed stream fd without creating anything,...
void write_k(const double *values, std::int64_t count)
Trailer write_trailer()
Emit trailer,version,counts,sha256 in that key order, version=1 (3.7:833-837).
void write_e(const pf_equity_point_t &point)
Emit ["E",time_ms,equity,open_profit] (3.7:825), in time order.
void write_v(const pf_output_event_v1_t &event, std::string_view message)
std::string escaped_string(std::string_view utf8)
One complete quoted JSON string (3.7:878-879).
std::string f64(double value)
One complete JSON f64 token, exactly Python 3 repr(double), or "null" for non-finite values (3....
Single per-script-bar equity point.
Definition pineforge.h:326
One recorded event, copied by strategy_outputs_event_get.
Definition pineforge.h:1904
Fixed-size annualization provenance from the emitted v1 records.
Borrowed sequential stream sink descriptor (CX-SINK-B1/B2).
Definition report_sink.h:61
Path sink descriptor (DESIGN 6:1283-1289, 1307-1309).
Definition report_sink.h:26
Single per-bar trace entry.
Definition pineforge.h:347
Closed-trade record returned in pf_report_t::trades.
Definition pineforge.h:166
Run properties selecting the fixed version-1 descriptors (3.7:795-809, 823-831; 6:1310-1311).
Trailer values for mapping to the C receipt (3.7:833-837; 6:1291-1298).