PineForge unreleased
Deterministic PineScript v6 backtest runtime — C ABI reference
Loading...
Searching...
No Matches
source/market_admission.hpp
Go to the documentation of this file.
1#pragma once
2// The TradingView market-admission observation journal: every field of
3// `Configuration` is a strategy() declaration parameter and every event is a
4// TradingView admission review, so this is source-layer state (R5 lane N14:
5// moved from include/pineforge/market_admission.hpp, where no kernel
6// translation unit consumed it). It is hashed through the source host's
7// extension fold (src/source/pine_state_hash.cpp), never by the kernel.
10#include <array>
11#include <cstddef>
12#include <cstdint>
13#include <functional>
14#include <limits>
15#include <memory>
16#include <optional>
17#include <string>
18#include <variant>
19#include <vector>
20
21namespace pineforge::detail { class CommandScratchStore; }
22
24inline namespace market_admission_v2 {
25constexpr double absent = std::numeric_limits<double>::quiet_NaN();
26enum class CommandKind : int64_t { Entry, Raw, Cancel, CancelAll };
33
34// Immutable observed configuration, not selectable compatibility profiles.
36 bool process_on_close = false;
37 bool calc_on_fills = false;
38 bool magnifier = false;
39 bool fill_recalculation = false;
40 bool scheduler = false;
41 int slippage = 0;
42 int pyramiding = 0;
45 double long_margin = 0;
46 double short_margin = 0;
47 double commission_value = 0;
49 double pointvalue = 1;
50 double fx = 1;
51 double quantity_step = 0;
52 double mintick = 0;
55 double drawdown_limit = 0;
57 double position_limit = 0;
58 bool fill_cap_active = false;
59 bool risk_halted = false;
60};
62 double limit = absent;
63 double stop = absent;
64};
66 double limit = absent;
67 double stop = absent;
71};
73 double quantity = absent;
74 double equity = absent;
75 double price = absent;
76 double mark = absent;
77 double fx = absent;
78};
106 uint64_t sequence = 0;
108 int bar = -1;
109 // Zero only for the batch review; per-order receipts name their origin.
110 uint64_t target_command = 0;
111};
113 uint64_t sequence = 0;
114 uint64_t cause_fill = 0;
115 int bar = -1;
116 uint64_t target_command = 0;
117};
118
119class Draft {
120public:
121 void bind(std::shared_ptr<const CommandObservation> observation);
122 void reviewed(ReviewReceipt receipt);
123 void sizing_revised(SizingRevision receipt);
124 const std::shared_ptr<const CommandObservation>& observation() const { return observation_; }
125 const std::optional<ReviewReceipt>& review() const { return review_; }
126 const std::optional<SizingRevision>& sizing_revision() const { return sizing_revision_; }
127private:
128 std::shared_ptr<const CommandObservation> observation_;
129 std::optional<ReviewReceipt> review_;
130 std::optional<SizingRevision> sizing_revision_;
131};
133 uint64_t incarnation = 0;
134 int64_t priority = 0;
135 int bar = -1;
136 int type = 0;
138 // Raw requested direction captured with the physical book row. This is
139 // needed to reconstruct a flat-born MARKET peer; placement_side is the
140 // held position side and is FLAT for that peer.
141 bool buy = false;
142 std::string id;
143 std::string oca_name;
144 int oca_type = 0;
148};
150 std::shared_ptr<const CommandObservation> observation;
153 std::vector<BookObservation> before;
154 std::vector<uint64_t> removed;
155};
156// Actual resolution output kinds. No selector/member bit is retained.
171 int64_t position_cycle = 0;
172 std::vector<BookObservation> book;
173 std::vector<BookObservation> reviewed;
174 std::vector<InstructionResolution> resolutions;
175 std::vector<uint64_t> causes;
176};
185using Event = std::variant<CommandEvent, ReviewEvent, SizingEvent>;
186using FieldValue = std::variant<uint64_t, int64_t, double, std::string>;
187struct Field { std::string path; FieldValue value; };
188using FieldVisitor = std::function<void(const Field&)>;
189void reflect(const Draft& value, const std::string& path, const FieldVisitor& visit);
190void reflect(const Event& value, const std::string& path, const FieldVisitor& visit);
191
193public:
194 explicit JournalRecordSource(const Event&) noexcept;
195 std::uint64_t size() const noexcept override { return length_; }
196 detail::ScratchStatus read(std::uint64_t, detail::ScratchBuffer) const noexcept override;
197private:
198 const Event* event_;
199 std::uint64_t length_ = 0;
200 std::uint64_t rows_ = 0;
202 // Resume at a field boundary. Only Event has arrays (at most four);
203 // BookObservation/Draft/CommandObservation contain no nested arrays.
204 mutable std::uint64_t next_offset_ = 0;
205 mutable std::uint64_t next_row_ = 0;
206 mutable std::array<std::uint64_t, 4> array_elements_{};
207 mutable std::array<std::uint64_t, 4> array_rows_{};
208};
209JournalRecordSource encode_journal_record(const Event&) noexcept;
210detail::ScratchStatus decode_journal_record(const detail::ScratchByteSource&,
211 detail::ScratchBuffer, Event&);
212class Journal;
213void bind_journal_codec(Journal&, detail::CommandScratchStore&);
214detail::ScratchStatus append_journal_record(Journal&, const detail::ScratchByteSource&) noexcept;
215const detail::ScratchReadback& journal_readback(const Journal&);
216
217class Allocation;
218class Journal {
219public:
220 // The retired window's v4 fold, not a second event history. The rejection
221 // bar is an index into the same folded observations, for the command gate.
222 struct Prefix {
223 std::size_t count = 0;
224 uint64_t digest = 1469598103934665603ULL;
225 uint64_t last_sequence = 0;
227 };
228 Journal() noexcept;
230 Journal(const Journal& other);
231 Journal& operator=(const Journal& other);
232 Journal(Journal&& other);
233 Journal& operator=(Journal&& other);
234 // Raw allocations must be appended or abandoned. Production producers use
235 // reserve() so failed construction/unwinding abandons the unfinished event.
236 uint64_t next_sequence();
238 void abandon(uint64_t sequence) noexcept;
239 void append(Event event);
240 const std::vector<Event>& events() const;
241 uint64_t sequence_frontier() const { return next_sequence_; }
242 void reset();
243 // Keep actual causes still referenced by history, instructions or settlement.
244 void retain(const std::vector<uint64_t>& sequences);
245 void reflect(const std::string& path, const FieldVisitor& visit) const;
246 const Prefix& retired_prefix() const { return retired_prefix_; }
247 std::size_t event_count() const;
249 return active_allocations_ != 0 || outstanding_count_ != 0;
250 }
251 // Explicit diagnostic projection, like the native Full event observer.
252 // Install before a run; keeps original reflection paths for tools which
253 // deliberately inspect every event. Execution and v4 hash bytes are equal
254 // under both storage policies. Ordinary hosts always use the window.
256 // Called only at a completed script-bar boundary, after every retained
257 // event has entered the v4 fold and no producer can insert before it.
258 void retire_prefix(std::size_t count, uint64_t digest, uint64_t last_sequence);
259 // Current v4 continuation, read fieldwise without an owning event snapshot.
260 // Filtering requires a validated nonzero prefix and an unchanged rejection
261 // gate before its surviving tail can be refolded. Only reset starts a new
262 // lifetime.
263 Prefix folded_window() const;
264 int last_rejected_command_bar() const;
265private:
266 friend class Allocation;
269 const detail::ScratchByteSource&) noexcept;
271 struct CodecState;
272 std::unique_ptr<CodecState> codec_;
273 void release_allocation(uint64_t sequence) noexcept;
274 uint64_t next_sequence_ = 1;
275 uint64_t active_allocations_ = 0;
276 uint64_t outstanding_count_ = 0;
277 std::vector<uint64_t> outstanding_sequences_;
278 mutable std::vector<Event> events_; // explicit diagnostic projection when bound
279 Prefix retired_prefix_;
280 mutable Prefix folded_window_;
281 mutable bool fold_valid_ = false;
282 mutable bool filtered_ = false;
283 // Derived on successful append; sequence order, not the greatest bar.
284 struct RejectionGate {
285 uint64_t sequence = 0;
286 int bar = -1;
287 bool valid = true;
288 };
289 mutable RejectionGate rejection_gate_;
290 // Observation-only configuration, persistent across reset like recording;
291 // not independent broker state and deliberately absent from the v4 fold.
292 bool retain_full_observation_history_ = false;
293};
294// Ephemeral ownership of an unfinished event. Completed/reclaimed IDs cannot
295// be re-created by keeping this handle: append consumes the journal allocation.
297public:
298 Allocation(const Allocation&) = delete;
299 Allocation& operator=(const Allocation&) = delete;
300 Allocation(Allocation&& other) noexcept;
302 ~Allocation() noexcept;
303 uint64_t sequence() const { return sequence_; }
304private:
305 friend class Journal;
306 Allocation(Journal& journal, uint64_t sequence) noexcept
307 : journal_(&journal), sequence_(sequence) {}
308 Journal* journal_;
309 uint64_t sequence_;
310};
311// Ephemeral call frame; only its completed immutable record enters the journal.
313public:
314 CommandCapture(Allocation allocation, CommandObservation input, std::vector<BookObservation> before,
315 std::function<void(CommandEvent)> complete);
318 ~CommandCapture() noexcept(false);
319 const CommandObservation& input() const { return input_; }
320 void outcome(Outcome outcome) { outcome_ = outcome; }
321 void bind(Draft& draft, std::optional<SizingObservation> sizing,
322 double explicit_equity, double explicit_price);
323private:
324 Allocation allocation_;
325 CommandObservation input_;
326 std::vector<BookObservation> before_;
327 Outcome outcome_ = Outcome::NoAdmission;
328 std::function<void(CommandEvent)> complete_;
329};
331public:
332 ReviewCapture(Allocation allocation, ReviewEvent event,
333 std::function<void(ReviewEvent)> complete);
334 ReviewCapture(const ReviewCapture&) = delete;
336 ~ReviewCapture() noexcept(false);
337 ReviewReceipt receipt_for(const Draft& draft) const;
338 void reject(uint64_t incarnation) {
339 InstructionResolution result;result.incarnation=incarnation;result.kind=ResolutionKind::Rejected;
340 event_.resolutions.push_back(result);
341 }
342 InstructionResolution transaction(uint64_t incarnation,uint64_t peer,int64_t priority,
343 int64_t peer_priority,double quantity) {
344 InstructionResolution result{incarnation,ResolutionKind::PairedTransaction,peer,priority,peer_priority,quantity};
345 event_.resolutions.push_back(result);return result;
346 }
347private:
348 Allocation allocation_;
349 ReviewEvent event_;
350 std::function<void(ReviewEvent)> complete_;
351};
352std::vector<Field> fields(const Draft& draft);
353uint64_t read_unsigned(const std::vector<Field>& fields, const std::string& path);
354int64_t read_integer(const std::vector<Field>& fields, const std::string& path);
355double read_double(const std::vector<Field>& fields, const std::string& path);
356std::string read_string(const std::vector<Field>& fields, const std::string& path);
357uint64_t sequence(const Event& event);
358} // inline namespace market_admission_v2
359} // namespace pineforge::admission
Allocation & operator=(Allocation &&)=delete
Allocation & operator=(const Allocation &)=delete
CommandCapture(Allocation allocation, CommandObservation input, std::vector< BookObservation > before, std::function< void(CommandEvent)> complete)
void bind(Draft &draft, std::optional< SizingObservation > sizing, double explicit_equity, double explicit_price)
CommandCapture & operator=(const CommandCapture &)=delete
const std::shared_ptr< const CommandObservation > & observation() const
const std::optional< ReviewReceipt > & review() const
void bind(std::shared_ptr< const CommandObservation > observation)
const std::optional< SizingRevision > & sizing_revision() const
detail::ScratchStatus read(std::uint64_t, detail::ScratchBuffer) const noexcept override
friend detail::ScratchStatus append_journal_record(Journal &, const detail::ScratchByteSource &) noexcept
void reflect(const std::string &path, const FieldVisitor &visit) const
friend const detail::ScratchReadback & journal_readback(const Journal &)
friend void bind_journal_codec(Journal &, detail::CommandScratchStore &)
void retire_prefix(std::size_t count, uint64_t digest, uint64_t last_sequence)
void retain(const std::vector< uint64_t > &sequences)
InstructionResolution transaction(uint64_t incarnation, uint64_t peer, int64_t priority, int64_t peer_priority, double quantity)
ReviewCapture(Allocation allocation, ReviewEvent event, std::function< void(ReviewEvent)> complete)
ReviewCapture & operator=(const ReviewCapture &)=delete
std::variant< uint64_t, int64_t, double, std::string > FieldValue
JournalRecordSource encode_journal_record(const Event &) noexcept
uint64_t read_unsigned(const std::vector< Field > &fields, const std::string &path)
std::string read_string(const std::vector< Field > &fields, const std::string &path)
int64_t read_integer(const std::vector< Field > &fields, const std::string &path)
std::vector< Field > fields(const Draft &draft)
std::function< void(const Field &)> FieldVisitor
void bind_journal_codec(Journal &, detail::CommandScratchStore &)
detail::ScratchStatus decode_journal_record(const detail::ScratchByteSource &, detail::ScratchBuffer, Event &)
double read_double(const std::vector< Field > &fields, const std::string &path)
const detail::ScratchReadback & journal_readback(const Journal &)
void reflect(const Draft &value, const std::string &path, const FieldVisitor &visit)
std::variant< CommandEvent, ReviewEvent, SizingEvent > Event
detail::ScratchStatus append_journal_record(Journal &, const detail::ScratchByteSource &) noexcept
admission::Draft MarketAdmissionDraft
admission::Journal MarketAdmissionJournal
std::shared_ptr< const CommandObservation > observation