PineForge v0.13.1-379-g9b50973
Deterministic PineScript v6 backtest runtime — C ABI reference
Loading...
Searching...
No Matches
native_order.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "execution.hpp"
6#include "market_driver.hpp"
8
9#include <algorithm>
10#include <array>
11#include <cmath>
12#include <cstddef>
13#include <cstdint>
14#include <functional>
15#include <limits>
16#include <memory>
17#include <optional>
18#include <string>
19#include <type_traits>
20#include <utility>
21#include <variant>
22#include <vector>
23
25inline namespace native_order_v6 {
26
27// Isolated working-request/value core: current LIVE requests and immutable
28// command history. It does not own positions, cash, paid fees, matching,
29// calendar, host phase, or a second physical book.
30//
31// Identity types remain native_order_v1. Request/core/event values are v6.
32// Physical execution::Action is unchanged; native Reduce uses a typed size
33// source instead of a dummy units field. ExecutionPlan is a transient widening
34// used at the core/consumer boundary.
35
37/// A signed book transaction: finite nonzero units on the side their sign names.
38/// It opens, and it closes against a live opposite book on the way, so a
39/// transaction larger than an opposite position still opens the remainder. An
40/// opening denial rejects the ENTIRE transaction, including that remainder.
42
43/// A host-maintained, run-scoped roster identity. Zero is invalid and is
44/// never allocated by WorkingRequestCore.
46 std::uint64_t value = 0;
47};
48inline bool operator==(CohortHandle left, CohortHandle right) noexcept {
49 return left.value == right.value;
50}
51inline bool operator!=(CohortHandle left, CohortHandle right) noexcept {
52 return !(left == right);
53}
54inline bool operator<(CohortHandle left, CohortHandle right) noexcept {
55 return left.value < right.value;
56}
57
58/// Exact target exposure for an explicit reversal request. This is distinct
59/// from execution::ReverseTo, which is the transient resolved execution plan.
60struct ReverseTo {
61 double signed_units = 0.0;
62};
63
64enum class HostSizedKind : std::uint8_t { Open = 0, Close = 1 };
65enum class Side : std::uint8_t { Long = 0, Short = 1 };
66/// An opening or a closing whose quantity the HOST resolves, in
67/// NativeStrategyHost::resolve_execution_terms. `side` is required for an
68/// opening, which must declare where it trades. A host-sized request that reaches
69/// a candidate with no units answered is MatchRejectReason::TermsUnresolved. It is
70/// the adapter's sizing seam; a bare host that wants the kernel to resolve a cash
71/// or equity basis uses Sized instead, and the C surface refuses HostSized under
72/// any owner but BindCohort.
73struct HostSized {
75 std::optional<Side> side;
76};
77
78/// Resolved host sizing normally remains subject to the run's quantity grid.
79/// A host may instead authenticate literal units for a pure reduction; the
80/// consumer still proves that the positive quantity is representable within
81/// the selected exposure before it can reach settlement.
82enum class ExecutionGridPolicy : std::uint8_t {
85};
86
87/// Native sizing bases (L3). The kernel resolves them into units at the
88/// sizing point. A source adapter lowers its own declaration-level default
89/// quantity onto them and keeps only its rounding and admission quirks
90/// (R5 R2); a host that owns its whole quantity still emits HostSized.
91struct CashValue {
92 double cash = 0.0; // account currency
93};
94/// A fraction in (0, 1] of MARKED equity at the sizing point. The other SizeBasis
95/// alternative, CashValue, is an absolute account-currency amount; both resolve as
96/// units = cash / (price * point_value * fx).
98 double fraction = 0.0; // of marked equity at the sizing point (0.10 = 10 %)
99};
100/// The two bases a Sized intent may name. Extended only by appending, so an
101/// existing aggregate initializer keeps its meaning.
102using SizeBasis = std::variant<CashValue, EquityFraction>;
103
104/// AtMatch resolves the basis at the matching candidate; AtAcceptance freezes
105/// the resolved units when the request is accepted.
106enum class SizeTime : std::uint8_t { AtMatch = 0, AtAcceptance = 1 };
107
108/// WHICH price the basis is converted at. This is the generic "size against the
109/// expected fill price" rule; it names no source language.
110///
111/// Resolved (today's behaviour) uses the price the kernel would otherwise
112/// settle at: the candidate's default resolved price for AtMatch, the raw
113/// acceptance-point price for AtAcceptance.
114///
115/// Signal is the decision-point price at placement -- the current bar's close,
116/// or the last print, at submit -- carried to the expected market fill: it is
117/// adjusted by the run's slippage for the request's own side
118/// (price +/- slippage_ticks * price_tick) and then rounded onto the run's
119/// price grid when NativeRunSpec::price_grid is on (HalfUp is the nearest
120/// tick; NativePriceGrid::None leaves it unrounded). It is frozen when the
121/// request is accepted, so it composes with SizeTime::AtAcceptance and, with
122/// SizeTime::AtMatch, the basis still converts at that frozen signal price at
123/// every later candidate. A Signal request accepted outside a decision point
124/// has no price to freeze and stays unresolvable.
125///
126/// SignalOnTick is the same decision-point rule measured on the INSTRUMENT's
127/// own tick ladder (NativeRunSpec::price_tick) instead of the run's fill grid:
128/// the decision price is rounded onto the ladder (nearest tick, ties away from
129/// zero), carried to the expected fill by the side's tick slippage, and rounded
130/// onto the ladder again. Two rulings are folded into it, both generic:
131///
132/// * price_tick is the instrument's own resolution and the run already
133/// declares it as the slippage multiplier, so a price the market PRINTS
134/// lives on that ladder whether or not the run also quantizes the prices it
135/// BOOKS. NativeRunSpec::price_grid governs fills; it does not govern what
136/// a signal was worth. A run that books unquantized fills can therefore
137/// still size against the printed price, which Signal cannot express.
138/// * the ladder is applied BEFORE the slippage, not only after. Slippage is
139/// a whole number of ticks, so it carries a ladder price to another ladder
140/// price: pre-rounding makes `round(p) +/- n*tick` exact and leaves the
141/// second rounding a pure binary64 re-normalization, where rounding only
142/// after composes two different quantizations of the same print.
143///
144/// Resolved and Signal are unchanged by this alternative, so every established
145/// Sized resolution keeps its value.
146enum class SizePrice : std::uint8_t { Resolved = 0, Signal = 1, SignalOnTick = 2 };
147
148/// A kernel-sized opening. units = cash / (price * point_value * fx), where
149/// cash is the basis value or fraction * marked equity, and price is the
150/// sizing-point price SizePrice names. A host override of
151/// resolve_execution_terms still has the last word: it sees the kernel-resolved
152/// units as the facts' RemainingUnits and may return its own.
153///
154/// SizeTime chooses WHEN the basis is resolved, SizePrice chooses WHICH price
155/// it converts at, and the two compose. Either way the request reaches the
156/// candidate with a deferred quantity and exactly one terms pass: the kernel
157/// resolves (or republishes the acceptance-frozen) units, publishes them as the
158/// facts' RemainingUnits before resolve_execution_terms and
159/// validate_execution_precommit run, and uses the host's units when the host
160/// returns any. An acceptance-time quantity is additionally an admission input
161/// at placement, so an opening the run cannot admit is
162/// RequestRejectReason::PlacementAdmission at submit rather than a rejected
163/// candidate later.
164struct Sized {
168 /// Which price the basis converts at; see SizePrice. Appended after `time`
169 /// because Sized has no positional aggregate initializer outside tests that
170 /// stop at the basis.
172 /// SnapToGrid floors the resolved units onto the run's quantity grid.
173 /// ExplicitUnits keeps the literal quotient, which the ordinary on-grid
174 /// terms gate then refuses when a grid is configured and the quotient is
175 /// off it; the two are identical on an ungridded run.
177 /// Divide the sizing cash by (1 + fee) when the run's fee kind is
178 /// NativeFeeKind::Percent. Every other fee kind is an exact no-op.
180};
181
182/// A reduction of exactly these units, finite and positive. Capped by the live
183/// exposure of the scope the reduce is bound to, and floored onto the run's
184/// quantity_grid like every other engine quantity.
186 double units = 0.0;
187};
188/// A reduction of exactly what the owner's fill opened, bound at the arm and
189/// reported by a QuantityBoundEvent. It is the arm-time size, which is why no
190/// SizeTime::AtArm exists.
192/// Gross claims the whole bound scope; NetOfSiblings first subtracts the units
193/// already claimed by the live sibling reduces bound to that same scope. The
194/// keys are opaque request/owner handles, never source identifiers.
195enum class ScopeClaim : std::uint8_t { Gross = 0, NetOfSiblings = 1 };
196/// Which measurement of the bound scope the fraction is taken of. AtMatch (the
197/// default) reads the scope as it stands at the matching candidate.
198/// AtAcceptance freezes the scope SIZE when the request is accepted -- the
199/// placement-time live basis -- so two 50 % siblings on one 10-unit lot both
200/// claim 5 under Gross even after the first has already executed. NetOfSiblings
201/// then subtracts the live sibling claims from that frozen basis.
202enum class ScopeBasis : std::uint8_t { AtMatch = 0, AtAcceptance = 1 };
203/// A fraction in (0, 1] of the bound scope, resolved at the matching candidate.
204/// The resolution is units = scope * fraction, one binary64 multiplication: a
205/// caller that spells its size as a percent converts percent -> fraction
206/// itself, so no second rounding step is introduced here.
208 double fraction = 1.0;
210 /// Appended last so every existing aggregate initializer keeps its meaning.
212};
213/// How much a Reduce takes: literal units, the owner's own opening, or a fraction
214/// of the bound scope.
215using ReductionSize = std::variant<ExplicitUnits, OwnerOpenedUnits, ScopeFraction>;
216/// A closing-only intent. It never opens, so it stays legal while the run's
217/// opening limits are already exceeded, and it uses CURRENT exposure rather than
218/// the acceptance-cycle book. On a flat book it terminalizes a NoEffectEvent: no
219/// execution identity, no fill, no fee, no physical action.
223/// What a request does. Six alternatives: Flatten (quantity-free whole-book
224/// close), Reduce (closing only), Transact (a signed book transaction), ReverseTo
225/// (an exact signed target exposure), HostSized (the host resolves the quantity)
226/// and Sized (the kernel resolves it from a cash or equity basis). The variant's
227/// size is pinned by the frozen C++ ABI fixtures, so appending an alternative is
228/// an epoch decision.
229using OrderIntent = std::variant<Flatten, Reduce, Transact, ReverseTo, HostSized, Sized>;
230
231/// Match at the next eligible matching point, with no level to reach. A market
232/// request accepted on bar N cannot fill on that bar's already delivered opening.
233struct Market {};
234/// `fill_through` makes the limit a touch trigger (market-if-touched): the
235/// level still gates when the request becomes executable, but its fill is not
236/// bounded by the level, so slippage may carry it past the level.
237struct Limit {
238 double price = 0.0;
239 bool fill_through = false;
240};
241/// A stop trigger: the modeled path has to reach `price` from the adverse side. A
242/// crossing books the level itself; a point already past it (a gapped open) books
243/// that print. Under a quantizing trigger grid the core re-validates the
244/// activation on the same ladder the matcher used, so a hit the matcher reports is
245/// never refused.
246struct Stop {
247 double price = 0.0;
248};
249/// A stop that, once reached, becomes a limit at `limit`. The stop gates and the
250/// limit bounds; an anchored FromOwnerFill level is refused for this trigger,
251/// because the anchor materializes one level and this trigger has two.
252struct StopLimit {
253 double stop = 0.0;
254 double limit = 0.0;
255};
256/// Trail offset spelled in price ticks instead of a price distance. The
257/// acceptance path resolves it against the run's price tick, writes the
258/// product into Trail::offset and clears the spelling, so a stored definition
259/// always carries a plain price distance and can never resolve twice.
261 double ticks = 0.0;
262};
263/// offset is a price distance: the stop rides `offset` behind the running
264/// best. Zero is legal and means "ride the best": the exit is the first
265/// adverse move past it. Negative and nonfinite offsets are rejected.
266struct Trail {
267 double offset = 0.0;
268 std::optional<double> arm_price;
269 std::optional<TrailTicks> ticks = std::nullopt;
270};
271using Trigger = std::variant<Market, Limit, Stop, StopLimit, Trail>;
272
273/// How a materialized anchored level is snapped onto the run's price tick
274/// ladder (NativeRunSpec::price_tick). Raw keeps `fill + offset` exactly,
275/// which is the established behaviour. HalfUp is the nearest tick with ties
276/// away from zero. Directional rounds toward the price region the resting leg
277/// needs, relative to the leg's trigger kind and side exactly as
278/// NativeGridRounding documents it: a buy limit down and a sell limit up, a
279/// stop the other way; a trail arm threshold is reached from the favourable
280/// side like a limit and rounds like one. Either rounding needs a positive
281/// price tick, checked at acceptance exactly like a tick-spelled offset. This
282/// is a generic instrument grid: a source language's own trigger projection
283/// is not spelled here (a host restates the level through its arm hook).
284enum class NativeAnchorRounding : std::uint8_t {
285 Raw = 0,
288};
289
290/// Where a request's trigger level comes from. Absolute is the level written
291/// in the trigger itself. FromOwnerFill defers it to the owner's fill: the
292/// level becomes fill + offset when the owner arms the request, so a bracket
293/// leg can be placed before its parent has a price. offset is signed (adverse
294/// is negative) and is spelled in price ticks when `ticks` is set; acceptance
295/// resolves a tick spelling exactly like TrailTicks, in place, once. The
296/// materialized level is then snapped per `rounding`, which is appended last
297/// so every existing aggregate initializer keeps its meaning.
298struct Absolute {};
299/// Defer a level to the owner's fill: at the arm the level becomes fill + offset,
300/// with `offset` signed (adverse is negative) and spelled in price ticks when
301/// `ticks` is set, then rounded by `rounding`. Legal on Limit and Stop prices and
302/// on a Trail arm threshold, and only under a WaitForApplied owner, which is the
303/// one relation that arms. The anchored field carries the placeholder 0.0 until
304/// then, and the ArmedEvent carries the materialized definition — from then on the
305/// request reads as the absolute level it now is.
311using TriggerAnchor = std::variant<Absolute, FromOwnerFill>;
312
313/// Replacement behaviour that is not expressible in the successor request.
314/// retain_trigger_state carries the predecessor's live trigger state (a
315/// tracking trail's best, an already active stop) into the successor instead
316/// of restarting it; predecessor and successor must hold the same trigger
317/// alternative.
320};
321
322/// Take whatever the point allows, with no per-point cap. The default capacity,
323/// and the only one the Pine adapter emits.
325/// Cap what this request may execute at ONE matching point at `units`. Fixed at
326/// submit and metered per point — including after an arm — so a request cannot be
327/// drained faster than its owner's schedule allows.
329 double units = 0.0;
330};
331using Capacity = std::variant<ImmediateRemaining, PointBudget>;
332
333/// Whether an owner-related request that arms is a working order before its
334/// arm. Working (the default) is the established book: the request is listed
335/// by native_working_requests() from acceptance on. PendingUntilArmed keeps
336/// it out of that enumeration until its ArmedEvent; it is still a live
337/// request the whole time -- accepted, addressable by handle (replace,
338/// cancel, trail_state), counted by cancel_all / cancel_where, and folded
339/// into the continuation identity -- and it never matches before the arm
340/// under either value. Visibility governs enumeration, not addressing.
341enum class NativeArmVisibility : std::uint8_t {
344};
345
346/// When an armed request may first match. The arm happens inside its owner's
347/// fill settlement, at the owner's fill print. AtArmPrint (the default) is the
348/// established book: the armed request is a candidate at that very cursor, so
349/// a level the fill print already satisfies matches at the print. AfterArmPrint
350/// gives the armed request the birth rule of a request submitted from the
351/// owner's fill callback: on the driver point that armed it, it sees only the
352/// path after the print -- a level the print already satisfies does not match
353/// there, a later crossing on that point still does -- and from the next
354/// driver point on it is an ordinary working request. It governs the level
355/// test of a priced trigger (limit, stop, stop-limit, trail arm); a market
356/// trigger has no level and is unaffected. Both are broker models: a
357/// contingent child that enters the book after its parent's trade cannot
358/// trade on that print, a simulated bracket commonly may.
359enum class NativeArmFirstMatch : std::uint8_t {
362};
363
364/// What an armed CLOSING request (Reduce, Flatten, a HostSized close) closes.
365/// OwnerLot (the default) is the established relation: the lot its owner's
366/// fill opened, and nothing a later add brings. Book binds it, at the arm, to
367/// the whole position that fill left -- the very book authority an
368/// Independent close submitted from the owner's fill callback acquires -- so
369/// a protective leg placed with its entry covers later adds and settles
370/// through the book like any other close. A HostSized close may wait for its
371/// owner only under Book: the owner-lot relation sizes from the units the
372/// owner opened, whereas a book close is sized by the host at the match.
373/// A waiting transaction closes nothing, so it must keep OwnerLot.
374enum class NativeArmScope : std::uint8_t {
376 Book = 1,
377};
378
379/// No owner: the request stands on its own book authority. Required for Sized, and
380/// the default.
381struct Independent {};
382/// The one owner relation that arms (the ArmedEvent). `visibility`, then
383/// `first_match`, then `scope` are appended last so every existing {parent},
384/// {parent, visibility} initializer keeps its meaning.
391/// Bind to ONE already-live opening, by its handle and the positive cycle it is
392/// live in. A reduce bound this way closes that lot and nothing a later add
393/// brings.
396 int64_t cycle = 0;
397};
398/// Bind to a fixed cohort of already-live openings. Every handle must be unique,
399/// nonzero, of this run and physically live in `cycle`; enrollment rejects the
400/// ENTIRE request if any member is invalid. The accepted definition keeps the
401/// original cohort in canonical handle order even as later retirement narrows the
402/// live subset, and execution still closes lots in physical FIFO order.
404 std::vector<RequestHandle> openings;
405 int64_t cycle = 0;
406};
407/// Bind to a host-built roster (cohort_open / cohort_add / cohort_remove), read at
408/// the match rather than frozen at submit. The C surface pairs it with exactly one
409/// intent, a host-sized close, whose units the on_close_units hook answers.
413using Owner = std::variant<Independent, WaitForApplied, BindOpening, BindOpenings, BindCohort>;
414
415enum class GroupEffect : std::uint8_t { Cancel = 0, Reduce = 1 };
416struct NoGroup {};
417struct Member {
418 std::uint64_t group = 0;
419 std::int64_t cohort = 0;
421};
422using Group = std::variant<NoGroup, Member>;
423
424/// Aggregate field order keeps market construction:
425/// Request{Transact{1.0}, "buy", "comment"}
426/// Request{Flatten{}, "flat", ""}
427/// Request{Reduce{ExplicitUnits{3}}, "close", ""}
428/// New fields default to Market / ImmediateRemaining / Independent / NoGroup.
429/// market_request() converts a physical execution::Action (explicit Reduce
430/// units only) into that same market Request. Host market-only methods must
431/// reject nondefault extras; they must not drop them.
442
444 if (std::holds_alternative<execution::Flatten>(action)) return Flatten{};
445 if (const auto* transact = std::get_if<order_action::Transact>(&action)) {
446 return Transact{transact->signed_units};
447 }
448 const auto& reduce = std::get<order_action::Reduce>(action);
449 return Reduce{ExplicitUnits{reduce.units}};
450}
451
453 std::string label = {},
454 std::string comment = {}) {
455 return Request{intent_from_physical(action), std::move(label), std::move(comment)};
456}
457
458inline bool has_market_defaults(const Request& request) noexcept {
459 return std::holds_alternative<Market>(request.trigger)
460 && std::holds_alternative<ImmediateRemaining>(request.capacity)
461 && std::holds_alternative<Independent>(request.owner)
462 && std::holds_alternative<NoGroup>(request.group)
463 && std::holds_alternative<Absolute>(request.anchor);
464}
465
466struct EventId {
468 uint64_t ordinal = 0;
469};
470
471inline bool operator==(const EventId& a, const EventId& b) {
472 return a.ordinal == b.ordinal && a.run == b.run;
473}
474inline bool operator!=(const EventId& a, const EventId& b) { return !(a == b); }
475
478 double t = 0.0;
479};
480
481inline bool operator==(const MatchCursor& a, const MatchCursor& b) noexcept {
482 return a.point.ordinal == b.point.ordinal && a.t == b.t
483 && a.point.interval_index == b.point.interval_index
484 && a.point.effective_time_ms == b.point.effective_time_ms
485 && a.point.provenance == b.point.provenance
486 && a.point.path_phase == b.point.path_phase;
487}
488inline bool operator!=(const MatchCursor& a, const MatchCursor& b) noexcept { return !(a == b); }
489
490inline std::uint8_t display_waypoint(const MatchCursor& cursor) noexcept {
491 return static_cast<std::uint8_t>(cursor.point.path_phase);
492}
493
496/// A resolved remaining quantity, in units. The kernel publishes a Sized intent's
497/// own resolution here, BEFORE the host's terms hook runs, so an override sees the
498/// kernel's number and may answer with a different one.
500 double q = 0.0;
501};
503/// A dynamic cohort has no currently live member. This is a live deferral,
504/// not a terminal receipt: the consumer retries it at the next candidate.
505struct NoTarget {};
508
512 double q = 0.0;
513};
520
522/// The authority an unarmed WaitForApplied child holds: accepted and live, but
523/// never a matching candidate until its owner's fill arms it — under either
524/// visibility.
534/// The authority an armed leg acquires under NativeArmScope::Book: the whole
535/// position its owner's fill left, on this cycle and side, bound at the fill's
536/// cursor. It is the same authority an Independent close has, which is what lets a
537/// protective leg placed with its entry also cover later adds.
551using Enrollment = std::variant<EnrollmentFromCommand, EnrollmentFromApplied>;
558/// Immutable fixed cohort. Current physical liveness is observed, never stored
559/// here; later fragments of an enrolled provenance remain authorized.
561 std::vector<RequestHandle> openings;
562 int64_t cycle = 0;
565};
571
572/// Native authorization receipt, converted to a call-local financial
573/// SelectedOpeningSet only at the consumer's settlement boundary.
575 int64_t cycle = 0;
576 std::vector<uint64_t> incarnations;
577};
578/// What one execution acted on: the whole book, one opening's exposure, or a bound
579/// selection. Reported on ExecutionAppliedEvent::scope. The financial
580/// execution::CloseScope remains the original two-alternative type; this is the
581/// native view, and a selected close's committed row range identifies the actual
582/// contributors.
583using ExecutionScope = std::variant<execution::Book, execution::OpeningExposure, SelectedExposure>;
584
585struct MarketReady {};
586struct LimitReady {};
587struct StopIdle {};
588struct StopActive {};
591struct TrailWaitArm {};
593 double best = 0.0;
594};
596 double best_at_trigger = 0.0;
597};
600
603 uint64_t point_ordinal = 0;
604 double initial = 0.0;
605 double left = 0.0;
606};
608 uint64_t point_ordinal = 0;
609};
611 uint64_t point_ordinal = 0;
612};
615
616struct PendingNone {};
618 double total = 0.0;
619 uint64_t count = 0;
621};
622using PendingAdjustments = std::variant<PendingNone, PendingDeferred>;
623
624struct PositionFlat {};
626 int64_t cycle = 0;
628};
629using PositionIdentity = std::variant<PositionFlat, PositionNonflat>;
630
637
640 std::optional<OpeningObservation> opening;
641 std::vector<OpeningObservation> openings;
642};
643
644/// Who authored a request. Host is every request a host submits, replaces or
645/// cancels, and it is the whole existing population: it folds nothing into the
646/// continuation digest, so no established hash moves. KernelLiquidation marks
647/// the margin model's own Reduce or Flatten; KernelRisk marks the Flatten a
648/// NativeRiskAction::FlattenAndBlock breach issues at its breach point
649/// (native_execution_consumer.cpp, pinned by tests/test_native_risk_limits.cpp).
650enum class RequestOrigin : std::uint8_t {
651 Host = 0,
654};
655
656/// The immutable accepted form of one request: its handle, the request value as
657/// accepted, its birth (acceptance ordinal and decision floor), its origin
658/// (RequestOrigin::Host, or the kernel's own for a liquidation or a risk flatten)
659/// and its authority. Every event carries a shared pointer to it, so a reader
660/// holds the definition the kernel matched against and not a later re-read.
665 std::optional<RequestHandle> predecessor;
666 /// Appended last so every existing aggregate initializer keeps its meaning.
668};
669using DefinitionRef = std::shared_ptr<const RequestDefinition>;
670
678 /// Placement-time sizing measurements frozen from the accepting command
679 /// context. Both stay empty for every request that did not ask to freeze
680 /// one, and an empty optional folds nothing into the continuation digest.
681 std::optional<double> sizing_units; // SizeTime::AtAcceptance
682 std::optional<double> sizing_scope; // ScopeBasis::AtAcceptance
683 std::optional<double> sizing_price; // SizePrice::Signal / SignalOnTick
684
685 const RequestHandle& handle() const noexcept { return definition->handle; }
686 const Request& request() const noexcept { return definition->request; }
687 const Birth& birth() const noexcept { return definition->birth; }
688 const std::optional<RequestHandle>& predecessor() const noexcept {
689 return definition->predecessor;
690 }
691};
692
693/// Whether a request may be considered at this driver point: the point's ordinal
694/// must be strictly past the birth acceptance ordinal AND its effective time at or
695/// past the birth's decision-time lower bound. This is the birth gate — it is why
696/// a request accepted on bar N cannot fill on that bar's already delivered
697/// opening — and a request born mid-path in on_native_applied is admitted at the
698/// current cursor instead, on the unconsumed suffix of that segment.
699inline bool point_eligible(const LiveRequest& live,
700 uint64_t point_ordinal,
701 int64_t effective_time_ms) noexcept {
702 return point_eligible(live.birth(), point_ordinal, effective_time_ms);
703}
704
705/// v2 exact binary64 grid: r=abs(q)/s, n=round(r) half away from zero, g=n*s.
706/// Intermediates finite, 1<=n<=2^53, abs(abs(q)-g) <= 4*ulp(max(abs(q),abs(g)))
707/// and < s/2. ulp is nextafter toward +inf. Does not rewrite q.
708inline bool quantity_on_grid(double q, double step) noexcept {
709 if (!std::isfinite(q) || !std::isfinite(step) || step <= 0.0) return false;
710 const double abs_q = std::abs(q);
711 const double r = abs_q / step;
712 if (!std::isfinite(r)) return false;
713 const double n = std::round(r);
714 if (!std::isfinite(n) || n < 1.0 || n > 0x1p53) return false;
715 const double g = n * step;
716 if (!std::isfinite(g)) return false;
717 const double err = std::abs(abs_q - g);
718 const double span = std::max(abs_q, std::abs(g));
719 const double ulp = std::nextafter(span, std::numeric_limits<double>::infinity()) - span;
720 const double ulp_bound = 4.0 * ulp;
721 const double half_step = step / 2.0;
722 if (!std::isfinite(err) || !std::isfinite(ulp_bound) || !std::isfinite(half_step))
723 return false;
724 return err <= ulp_bound && err < half_step;
725}
726
727/// Why an acceptance was refused, on a Rejected SubmitResult. These are decided at
728/// submit, before any request record exists: an invalid quantity or basis, an
729/// off-grid quantity, an owner the intent may not take, a trigger spelling the run
730/// cannot resolve, or a frozen quantity the run's own opening admission refuses
731/// (PlacementAdmission).
732enum class RequestRejectReason : std::uint8_t {
740 /// A Sized{SizeTime::AtAcceptance} whose acceptance-resolved quantity does
741 /// not pass the run's opening admission (allowed directions, max_abs_units,
742 /// max_open_lots, initial margin) at the sizing price.
744};
745
749
750/// What submit answers. Accepted carries a timeline ordinal and a RequestHandle
751/// (session, run, incarnation); Rejected carries a rejection ordinal, no handle
752/// and a reason. Acceptance is not a fill: no lot, no cash and no fee moves here,
753/// and the ordinary fill appears later as an ExecutionAppliedEvent on the same
754/// command history.
757 uint64_t event_ordinal = 0;
758 std::optional<RequestHandle> handle;
759 std::optional<RequestRejectReason> reason;
760};
761
764 uint64_t event_ordinal = 0;
765 std::optional<RequestHandle> successor;
766 std::optional<RequestRejectReason> reason;
767};
768
773
774/// Why a CANDIDATE was refused — a decision taken at a matching point, with the
775/// request still live unless the reason is terminal. OpeningDirection, MaxAbsUnits,
776/// MaxOpenLots, InitialMargin and RiskLimit are the run's admission gates;
777/// TermsUnresolved is a host-sized or basis-sized quantity that could not be
778/// resolved; NoOppositeExposure and InvalidTerms are shapes the answered terms
779/// cannot take.
780enum class MatchRejectReason : std::uint8_t {
790 /// An opening refused while the run spec's generic risk limits are
791 /// blocking (L9). Reduces never reach this gate.
793};
794
795/// Which price a candidate is being offered at: the point's own price, or the
796/// request's trigger level (a crossing that books the level itself). A host reads
797/// it off the terms facts, and the trail suites read it back off the applied event
798/// to tell a level fill from a path fill.
799enum class NativeCandidatePriceKind : std::uint8_t {
803};
804
805enum class OpeningShape : std::uint8_t {
809};
810
817
818/// The settlement shape one execution takes. Derived by the kernel from the intent
819/// and the answered terms; a host never constructs it.
822
824 return std::visit([](const auto& alternative) -> ExecutionPlan {
825 return alternative;
826 }, action);
827}
828
836
837/// Why a request left the book. Host is an explicit cancel; Group an OCA sibling's
838/// effect; OwnerGone a parent that was rejected, replaced or cancelled, which ends
839/// its waiting children; Superseded the kernel withdrawing its own liquidation
840/// before re-pricing it. Every reason is carried on a CancelledEvent.
841enum class CancelReason : std::uint8_t {
842 User = 0,
843 Group = 1,
846 /// A kernel-originated request the kernel itself withdrew: the liquidation
847 /// level or its units moved, or the requirement is no longer breached.
849};
850
856
857enum class ActivationKind : std::uint8_t {
858 Stop = 0,
862};
863
877
887
888enum class CommandSurface : std::uint8_t { General = 0, MarketOnly = 1 };
889
890/// One request was accepted: the definition, and the timeline ordinal it was
891/// accepted at. The first event of every request's history.
893 uint64_t ordinal = 0;
896 const RequestHandle& handle() const noexcept { return definition->handle; }
897 const Request& request() const noexcept { return definition->request; }
898 const Birth& birth() const noexcept { return definition->birth; }
899};
900
907
909 uint64_t ordinal = 0;
913 const RequestHandle& predecessor() const noexcept { return predecessor_definition->handle; }
914 const Request& predecessor_request() const noexcept { return predecessor_definition->request; }
915 const RequestHandle& successor() const noexcept { return successor_definition->handle; }
916 const Request& successor_request() const noexcept { return successor_definition->request; }
917 const Birth& successor_birth() const noexcept { return successor_definition->birth; }
918};
919
929
941
948
955
956/// A reduction or a flatten that had nothing to close: terminal, with no execution
957/// identity, no fill, no fee and no physical action. It is an outcome, not a
958/// failure.
960 uint64_t ordinal = 0;
965 const RequestHandle& handle() const noexcept { return definition->handle; }
966 const Request& request() const noexcept { return definition->request; }
967 const Birth& birth() const noexcept { return definition->birth; }
968};
969
985
986/// A candidate the run refused, with its MatchRejectReason and the cursor it was
987/// refused at. It carries an event ordinal but no execution identity, and the
988/// request stays live unless the reason is terminal.
990 uint64_t ordinal = 0;
996 std::optional<ExecutionTerms> attempted_terms;
997 const RequestHandle& handle() const noexcept { return definition->handle; }
998 const Request& request() const noexcept { return definition->request; }
999 const Birth& birth() const noexcept { return definition->birth; }
1000};
1001
1011
1021
1022/// One committed execution: the definition, the cursor, the resolved price and
1023/// units, the scope it acted on, the committed closed-row range, the cycle on
1024/// either side, and the ticket it booked under. Delivered to on_native_applied and
1025/// recorded in native_events(); the account observation that follows it shares its
1026/// ordinal.
1028 uint64_t ordinal = 0;
1030 double raw_price = 0.0;
1031 double resolved_price = 0.0;
1032 double current_ticket = 0.0;
1033 std::size_t first_trade_index = 0;
1034 std::size_t closed_trade_count = 0;
1036 double closed_units = 0.0;
1037 double opened_units = 0.0;
1038 double filled_working = 0.0;
1043 bool terminal = true;
1044 std::optional<AppliedTerminalReason> terminal_reason;
1045 int64_t cycle_before = 0;
1046 int64_t cycle_after = 0;
1049 const RequestHandle& handle() const noexcept { return definition->handle; }
1050 const Request& request() const noexcept { return definition->request; }
1051 const Birth& birth() const noexcept { return definition->birth; }
1052 int64_t effective_time_ms() const noexcept { return cursor.point.effective_time_ms; }
1053 int64_t interval_open_ms() const noexcept { return cursor.point.open_ms; }
1054 int64_t interval_last_traded_close_ms() const noexcept {
1055 return cursor.point.last_traded_close_ms;
1056 }
1057 int interval_index() const noexcept { return cursor.point.interval_index; }
1058 std::uint8_t provenance() const noexcept {
1059 return static_cast<std::uint8_t>(cursor.point.provenance);
1060 }
1061};
1062
1074
1086
1087/// A Reduce{OwnerOpenedUnits} was bound, at its owner's fill, to what that fill
1088/// opened. It is how a bracket child's size becomes a number, and it precedes the
1089/// ArmedEvent of the same drain.
1100
1101/// An anchored leg was materialized, exactly once: fill + offset, then the
1102/// rounding, then resolve_anchored_level, then the representability check, and the
1103/// resulting level written into the leg's trigger with its anchor becoming
1104/// Absolute. Every later reader — the live book, the C working rows, matching —
1105/// sees that level. Under NativeArmScope::Book it also carries the BookClose the
1106/// leg acquired.
1115
1116/// A kernel-issued liquidation that actually filled. It carries the margin
1117/// facts of that fill, so a host reconstructs the outcome without recomputing
1118/// the account: `mark` is the booked resolved price, `equity` and `required`
1119/// are the marked equity and the maintenance requirement of the SURVIVING book
1120/// at that price, `liquidation_price` is the level re-solved for what is left,
1121/// and `position_before` / `position_after` are the signed book on either side
1122/// of the reduction. `applied` names the ExecutionAppliedEvent that booked it.
1124 uint64_t ordinal = 0;
1129 double mark = 0.0;
1130 double equity = 0.0;
1131 double required = 0.0;
1132 double liquidation_price = 0.0;
1133 double units = 0.0;
1134 double position_before = 0.0;
1135 double position_after = 0.0;
1136 const RequestHandle& handle() const noexcept { return definition->handle; }
1137 const Request& request() const noexcept { return definition->request; }
1138};
1139
1140/// Which generic risk limit of NativeRunSpec::risk a NativeRiskEvent reports.
1147
1148/// One generic risk limit breaching (L9). It is not bound to a request: the
1149/// block it opens is an account fact, so this event carries no definition.
1150/// `limit` is the threshold in the unit the breach was measured in — account
1151/// currency for the two loss limits (a percent limit is already resolved
1152/// against its basis equity here), days or fills for the two counts — and
1153/// `observed` is the measured value that reached it. `day_ordinal` is the
1154/// risk day the breach happened on, on the spec's own day basis, and `cursor`
1155/// is the point it was measured at.
1164
1165/// Every alternative of one request's history, as one value. native_events()
1166/// returns them as the NativeEventKind::Command rows, and
1167/// strategy_native_events_v1 flattens the same set into one tagged POD. The
1168/// variant's size is pinned by the frozen C++ ABI fixtures.
1169using CommandEvent = std::variant<AcceptedEvent,
1184 ArmedEvent,
1188
1189/// Almost every prepared command yields one history event. Keep that ordinary
1190/// transactional payload inline; the overflow vector preserves the existing
1191/// arbitrary-length behavior for group/lifecycle plans that emit more events.
1193public:
1195 InlineCommandEvents(InlineCommandEvents&& other) noexcept : size_(other.size_) {
1196 for (std::size_t i = 0; i < size_ && i < inline_.size(); ++i) {
1197 inline_[i] = std::move(other.inline_[i]);
1198 }
1199 overflow_ = std::move(other.overflow_);
1200 other.size_ = 0;
1201 }
1203 if (this != &other) {
1204 clear();
1205 size_ = other.size_;
1206 for (std::size_t i = 0; i < size_ && i < inline_.size(); ++i) {
1207 inline_[i] = std::move(other.inline_[i]);
1208 }
1209 overflow_ = std::move(other.overflow_);
1210 other.size_ = 0;
1211 }
1212 return *this;
1213 }
1214 InlineCommandEvents(const InlineCommandEvents& other) : size_(other.size_) {
1215 for (std::size_t i = 0; i < size_ && i < inline_.size(); ++i) {
1216 inline_[i] = other.inline_[i];
1217 }
1218 overflow_ = other.overflow_;
1219 }
1221 if (this != &other) {
1222 clear();
1223 size_ = other.size_;
1224 for (std::size_t i = 0; i < size_ && i < inline_.size(); ++i) {
1225 inline_[i] = other.inline_[i];
1226 }
1227 overflow_ = other.overflow_;
1228 }
1229 return *this;
1230 }
1231
1232 std::size_t size() const noexcept { return size_; }
1233 bool empty() const noexcept { return size_ == 0; }
1234
1235 template<class Event>
1236 void emplace_back(Event&& event) {
1237 push_back(CommandEvent(std::forward<Event>(event)));
1238 }
1239
1241 if (overflow_.empty() && size_ < inline_.size()) {
1242 inline_[size_++].emplace(std::move(event));
1243 return;
1244 }
1245 if (overflow_.empty()) {
1246 overflow_.reserve(inline_.size() * 2U);
1247 for (std::size_t i = 0; i < size_; ++i) {
1248 overflow_.push_back(std::move(*inline_[i]));
1249 inline_[i].reset();
1250 }
1251 }
1252 overflow_.push_back(std::move(event));
1253 ++size_;
1254 }
1255
1256 void clear() noexcept {
1257 for (std::size_t i = 0; i < size_ && i < inline_.size(); ++i) {
1258 inline_[i].reset();
1259 }
1260 overflow_.clear();
1261 size_ = 0;
1262 }
1263
1264 CommandEvent& front() noexcept { return (*this)[0]; }
1265 const CommandEvent& front() const noexcept { return (*this)[0]; }
1266 CommandEvent& operator[](std::size_t index) noexcept {
1267 return overflow_.empty() ? *inline_[index] : overflow_[index];
1268 }
1269 const CommandEvent& operator[](std::size_t index) const noexcept {
1270 return overflow_.empty() ? *inline_[index] : overflow_[index];
1271 }
1272
1273private:
1274 std::array<std::optional<CommandEvent>, 2> inline_{};
1275 std::vector<CommandEvent> overflow_{};
1276 std::size_t size_ = 0;
1277};
1278
1279/// What acceptance needs from the run to judge a command: the point's ordinal and
1280/// coordinate, the price tick the tick spellings resolve against, the quantity
1281/// grid, and the live book. Built by the consumer; a host never constructs one.
1283 int64_t decision_time_ms = 0;
1284 std::optional<double> quantity_grid;
1285 std::optional<OpeningObservation> opening;
1287 std::vector<OpeningObservation> openings;
1288 /// Kernel-resolved acceptance-time units for a Sized{AtAcceptance} request.
1289 /// The execution consumer owns the account facts, so it supplies them here;
1290 /// a producer that leaves it unset accepts the request with a size the
1291 /// matching path then reports as TermsUnresolved. The accepted request
1292 /// carries the value in LiveRequest::sizing_units and still reaches the
1293 /// candidate with a deferred remaining, so the host keeps its one override
1294 /// pass. Appended last so the existing positional aggregate initializers
1295 /// keep their meaning.
1296 std::optional<double> sizing_units;
1297 /// The run's price tick, needed only to resolve a tick-spelled trail
1298 /// offset or trigger anchor. A tick spelling without a usable tick here is
1299 /// rejected rather than silently read as a price distance.
1300 std::optional<double> price_tick;
1301 /// Placement-time sizing measurements the execution consumer owns, supplied
1302 /// only for the intent that asks for them. Appended last, exactly like
1303 /// sizing_units, so the existing positional aggregate initializers keep
1304 /// their meaning.
1305 ///
1306 /// sizing_scope: the bound scope's exposure at acceptance, for a
1307 /// Reduce{ScopeFraction{ScopeBasis::AtAcceptance}}.
1308 /// sizing_price: the frozen signal price, for a Sized whose SizePrice is
1309 /// Signal or SignalOnTick.
1310 /// sizing_admissible: false when the acceptance-resolved quantity of a
1311 /// Sized{SizeTime::AtAcceptance} fails the run's placement admission.
1312 std::optional<double> sizing_scope;
1313 std::optional<double> sizing_price;
1315};
1316
1321 /// Generic current-point delivery. At Open, the consumer sets this only for
1322 /// a market/immediate request born by the pre-open provider. On a continuous
1323 /// OHLC segment, it also admits a request born by an applied callback onto
1324 /// the unconsumed suffix. It is transient and never retained in a request.
1326 /// Set only while resolving a CohortClose candidate. It carries the
1327 /// physical side of the currently live selected roster and is not retained
1328 /// in a request definition.
1329 std::optional<Side> cohort_side;
1330};
1331
1332/// The one policy point of an anchored materialization. The core knows it
1333/// only as a callable over its own values: the leg's live row, the owner's
1334/// fill, the leg's side, the resolved offset (price units) and the kernel
1335/// level after the anchor rounding. It is consulted exactly once per
1336/// materialization, before the ArmedEvent is built, so the ArmedEvent and
1337/// every later reader see the installed level. A returned value is the level
1338/// to install (the kernel's representability check still applies and a
1339/// failure is the existing PreparationError path); nullopt keeps the kernel
1340/// level. An empty callable is exactly the pre-hook behaviour.
1341using AnchoredLevelResolver = std::function<std::optional<double>(
1342 const LiveRequest& leg, const ExecutionAppliedEvent& owner_fill, Side leg_side,
1343 double offset, double kernel_level)>;
1344
1345/// What the consumer hands to prepare_owner_applied for an anchored leg's
1346/// materialization, the way acceptance receives CommandContext::price_tick.
1347/// price_tick is the ladder a rounded anchor snaps to; an anchor whose
1348/// rounding is Raw never reads it. resolve_level is the host's restatement,
1349/// carried as a value so the core stays host-free.
1354
1377
1378/// What the consumer hands to prepare_trigger under an opted-in price grid
1379/// (NativeRunSpec::price_grid == QuantizeFillsAndTriggers), the way acceptance
1380/// receives CommandContext::price_tick: the tick ladder and the rounding the
1381/// matcher tested the activation on. The core then re-validates the reached
1382/// print with the same grid arithmetic (L8b ruling: under that grid the
1383/// tick-quantized print IS the reached price, so a hit the matcher reports is
1384/// never refused), records the quantized print as the activation's reached
1385/// price, and keeps a trail's running best on the ladder. A default-constructed
1386/// value (no ladder) is exactly the raw compare and the raw print every
1387/// activation used before. Host-free: values only.
1389 double price_tick = 0.0; // finite and positive when a ladder is in force
1390 bool half_up = true; // nearest tick, ties away from zero; else directional
1391};
1392
1395 /// Carries the generic pre-open delivery authorization from the matching
1396 /// evaluation through the synchronous execution preparation.
1398 double raw_price = 0.0;
1399 double resolved_price = 0.0;
1407};
1408
1416
1418 std::size_t first_index = 0;
1419 std::size_t count = 0;
1420};
1421
1425
1426enum class InstallError : std::uint8_t {
1430};
1431
1432using InstallResult = std::variant<Installed, InstallError>;
1433
1441
1445
1446/// A working-request preparation that could not produce a representable
1447/// instruction — a nonrepresentable quantity, most often. Reported as a settlement
1448/// failure, which fails the run: it is a contract breach, not a refused candidate.
1454
1455template <class T>
1456using Preparation = std::variant<T, NoChange, PreparationError>;
1457
1458template <class R>
1463
1464template <class R>
1465using InstalledCommand = std::variant<CommandInstalled<R>, InstallError>;
1466
1467class PreparedSubmit;
1468class PreparedReplace;
1469class PreparedCancel;
1470class PreparedMutation;
1471class PreparedExecution;
1472
1474 bool birth_ok = false;
1475 bool waiting = false;
1476 bool driver_ok = false;
1477 bool needs_close_bind = false;
1478 bool ready_to_match = false;
1480 bool is_buy = false;
1481 const Trigger* trigger = nullptr;
1482 const TriggerState* trigger_state = nullptr;
1483 const Remaining* remaining = nullptr;
1484 const Allowance* allowance = nullptr;
1485 const Authority* authority = nullptr;
1486};
1487
1490 /// Canonical origin handles, ordered by origin incarnation rather than by
1491 /// host insertion order. A successor is normalized to its predecessor
1492 /// root before entering this table.
1493 std::vector<RequestHandle> origins;
1494};
1495
1496enum class CohortReceiptOperation : std::uint8_t { Add = 0, Remove = 1 };
1509
1511public:
1515 /// Move transfers the complete run. The source becomes empty/unbound;
1516 /// commands throw invalid_argument without mutation until reset rebinds it.
1517 /// Self move-assignment preserves the current run.
1520
1522
1523 const RunIdentity& identity() const noexcept { return identity_; }
1524 const std::vector<LiveRequest>& live() const noexcept { return live_; }
1525 const std::vector<CommandEvent>& history() const noexcept { return history_; }
1526 const std::vector<CohortRoster>& cohorts() const noexcept { return cohorts_; }
1527 const std::vector<CohortReceipt>& cohort_receipts() const noexcept {
1528 return cohort_receipts_;
1529 }
1530 const LiveRequest* find_live(const RequestHandle& handle) const;
1531 const CommandEvent* event_at(const EventId& id) const;
1532
1533 /// Command-boundary roster maintenance. A rejected add/remove records a
1534 /// durable generic receipt but never emits a market event.
1538 bool cohort_contains(CohortHandle cohort, const RequestHandle& opening) const;
1539
1540 /// R1 producer convenience: prepare then install one command. Bound opening
1541 /// enrollment requires CommandContext observations via prepare_submit.
1543 int64_t decision_time_ms,
1544 uint64_t& next_order_incarnation,
1545 uint64_t& next_timeline_ordinal,
1546 std::optional<double> quantity_grid = std::nullopt);
1547
1549 const Request& request,
1550 int64_t decision_time_ms,
1551 uint64_t& next_order_incarnation,
1552 uint64_t& next_timeline_ordinal,
1553 std::optional<double> quantity_grid = std::nullopt,
1554 ReplaceOptions options = {});
1555
1556 CancelResult cancel(const RequestHandle& target, uint64_t& next_timeline_ordinal);
1557
1558 /// Consumer-only typed prepare/install. Prepare may reserve storage and
1559 /// invalidate live/history references; it does not change logical state.
1560 /// Tokens are move-only, bound to this instance/run/epoch, and do not
1561 /// survive another prepare/install, reset, or move.
1563 const CommandContext& context,
1564 uint64_t& next_order_incarnation,
1565 uint64_t& next_timeline_ordinal,
1568 const Request& request,
1569 const CommandContext& context,
1570 uint64_t& next_order_incarnation,
1571 uint64_t& next_timeline_ordinal,
1572 ReplaceOptions options = {});
1573 /// `reason` lets the kernel withdraw its own request under the durable
1574 /// Superseded receipt. Host cancels keep the default User reason.
1575 PreparedCancel prepare_cancel(const RequestHandle& target, uint64_t& next_timeline_ordinal,
1577
1581
1583 const EvaluationContext& context,
1584 const TargetObservation& observation,
1585 uint64_t& next_timeline_ordinal);
1586 /// `grid` carries the run's activation grid (ActivationGrid); the default
1587 /// is the raw rule, so every existing caller keeps its meaning.
1589 const TriggerTransition& transition,
1590 DriverEligibilityClass driver_class,
1591 uint64_t& next_timeline_ordinal,
1592 std::optional<Side> cohort_side = std::nullopt,
1593 const ActivationGrid& grid = {});
1595
1597 const ExecutionProposal& proposal,
1598 uint64_t& next_timeline_ordinal);
1600 const CommittedExecutionFacts& facts) noexcept;
1602 const EvaluationContext& context,
1603 uint64_t& next_timeline_ordinal);
1605 const EvaluationContext& context,
1606 MatchRejectReason reason,
1607 uint64_t& next_timeline_ordinal);
1609 const RequestHandle& target,
1610 const EvaluationContext& context,
1611 MatchRejectReason reason,
1612 std::optional<ExecutionTerms> attempted_terms,
1613 uint64_t& next_timeline_ordinal);
1615 const EvaluationContext& context,
1616 const TermsResolvedInput& input,
1617 uint64_t& next_timeline_ordinal);
1618
1619 /// Append one MarginCallEvent to the immutable history. The target is the
1620 /// kernel-originated request that filled, which is already terminal by the
1621 /// time its margin facts are recorded, so no live row moves.
1623 uint64_t& next_timeline_ordinal);
1624
1625 /// Append one NativeRiskEvent to the immutable history. It names an
1626 /// account-level breach rather than a request, so it moves no live row and
1627 /// carries no definition; the kernel's own response (an opening block, and
1628 /// with FlattenAndBlock a KernelRisk Flatten) is separate from the record.
1630 uint64_t& next_timeline_ordinal);
1631
1632 /// The allowance that prepare_evaluation would install for this point.
1633 static Allowance evaluated_allowance(const LiveRequest& live, uint64_t point) noexcept;
1634 /// Consumer-only no-event form of the ordinary allowance
1635 /// refresh. It preserves prepare_evaluation's eligibility and liveness
1636 /// checks while avoiding a transient mutation envelope per driver point.
1637 void refresh_point_allowances(uint64_t point, const PositionIdentity& position) noexcept;
1639 const EvaluationContext& context,
1640 const TargetObservation& observation);
1642 const EvaluationContext& context,
1643 const TargetObservation& observation);
1644 /// Pure arithmetic over the cached pending total. Outputs are assigned only
1645 /// after every validation and subtraction succeeds.
1646 static bool effective_host_units(const PendingAdjustments& pending,
1647 double resolved_units,
1648 double* deduction,
1649 double* after,
1650 bool* exhausted) noexcept;
1651
1652 void reserve(std::size_t expected_events);
1653 std::vector<RequestHandle> group_recipients(const EventId& applied) const;
1654 std::vector<RequestHandle> waiting_children(const RequestHandle& parent) const;
1655 bool has_waiting_children(const RequestHandle& parent) const noexcept;
1656 std::vector<RequestHandle> bound_close_handles() const;
1657
1659 const RequestHandle& recipient,
1660 uint64_t& next_timeline_ordinal);
1661 /// `arm` carries the materialization facts of an anchored leg (the price
1662 /// tick a rounded anchor snaps to, the host's level restatement); a
1663 /// default-constructed value is exactly the pre-lane behaviour, so every
1664 /// existing caller keeps its meaning.
1666 const EventId& applied,
1667 const RequestHandle& child,
1668 const std::optional<OpeningObservation>& observation,
1669 uint64_t& next_timeline_ordinal,
1670 const ArmContext& arm = {});
1672 const RequestHandle& child,
1673 const TargetObservation& observation,
1674 uint64_t& next_timeline_ordinal);
1676 const RequestHandle& child,
1677 uint64_t& next_timeline_ordinal);
1678
1680 const EvaluationContext& context) const noexcept;
1682 const EvaluationContext& context) const noexcept;
1683 bool trigger_permits_driver(const Trigger& trigger,
1684 const TriggerState& state,
1685 DriverEligibilityClass driver_class,
1686 bool existing_matching_bit) const noexcept;
1688 std::optional<Side> cohort_side = std::nullopt) const noexcept;
1689
1690private:
1691 enum class TargetKind { Live, NotWorking, InvalidHandle };
1692 friend class PreparedSubmit;
1693 friend class PreparedReplace;
1694 friend class PreparedCancel;
1695 friend class PreparedMutation;
1696 friend class PreparedExecution;
1697
1698 static void require_identity(const RunIdentity& identity);
1699 static void require_grid(std::optional<double> quantity_grid);
1700 static void require_distinct_counters(uint64_t& next_order_incarnation,
1701 uint64_t& next_timeline_ordinal);
1702 std::optional<RequestRejectReason> validate_request(
1703 const Request& request,
1704 const CommandContext& context,
1705 const std::optional<RequestHandle>& replace_target) const;
1706 /// Resolves tick-spelled trail offsets and trigger anchors in place
1707 /// against context.price_tick. Runs after validate_request, on the staged
1708 /// copy that becomes the stored definition.
1709 static std::optional<RequestRejectReason> resolve_tick_spellings(
1710 Request& request, const CommandContext& context);
1711 LiveRequest make_live(DefinitionRef definition,
1712 const CommandContext& context,
1713 EventId accepted) const;
1714 enum class ReceiptLookup : std::uint8_t { Absent = 0, Present = 1, Conflict = 2 };
1715 bool collect_pending_chain(const PendingAdjustments& pending,
1716 const RequestHandle& recipient,
1717 std::vector<EventId>* ids,
1718 double* total) const;
1719 ReceiptLookup receipt_lookup(const EventId& cause, const RequestHandle& recipient,
1720 GroupEffect effect, uint64_t* outcome) const;
1721 bool authenticate_receipt_outcome(const CommandEvent& event, const EventId& cause,
1722 const RequestHandle& recipient, GroupEffect effect) const;
1723 bool trail_level_ok(double best, double offset, bool is_buy, double* stop) const noexcept;
1724 const RequestDefinition* definition_for(const RequestHandle& handle) const noexcept;
1725 std::optional<RequestHandle> canonical_cohort_origin(const RequestHandle& origin) const;
1726 std::size_t cohort_index(CohortHandle cohort) const noexcept;
1727
1728 uint64_t usable_ordinal(uint64_t next) const;
1729 uint64_t usable_incarnation(uint64_t next) const;
1730 TargetKind classify(const RequestHandle& handle, std::size_t* live_index) const;
1731 bool bump_epoch() noexcept;
1732 void require_epoch_room() const;
1733 void clear_unbound() noexcept;
1734
1735 struct InstanceBinding {
1736 uint64_t generation = 1;
1737 bool expired = false;
1738 };
1739 struct MutationPlan {
1740 std::weak_ptr<InstanceBinding> instance;
1741 uint64_t generation = 0;
1742 uint64_t epoch = 0;
1743 RunIdentity run;
1744 std::size_t history_size = 0;
1745 uint64_t last_ordinal = 0;
1746 bool consumed = false;
1747 InlineCommandEvents events;
1748 std::uint8_t live_change = 0; // 0 none, 1 push, 2 erase, 3 update
1749 std::size_t live_index = 0;
1750 LiveRequest live_row{};
1751 bool consume_incarnation = false;
1752 uint64_t incarnation_used = 0;
1753 bool add_receipt = false;
1754 EventId receipt_cause{};
1755 RequestHandle receipt_recipient{};
1756 GroupEffect receipt_effect = GroupEffect::Reduce;
1757 uint64_t receipt_outcome = 0;
1758 };
1759 std::optional<InstallError> validate_plan(const MutationPlan& plan) const noexcept;
1760 InstallResult commit(MutationPlan& plan) noexcept;
1761 MutationPlan begin_plan() const;
1762 void reserve_plan(const MutationPlan& plan);
1763 void bind_plan(MutationPlan& plan) const;
1764 void seal_plan(MutationPlan& plan);
1765 PreparedMutation finish_mutation(MutationPlan plan);
1766
1767 RunIdentity identity_;
1768 std::shared_ptr<InstanceBinding> instance_;
1769 std::vector<LiveRequest> live_;
1770 std::vector<CommandEvent> history_;
1771 uint64_t last_ordinal_ = 0;
1772 uint64_t last_incarnation_ = 0;
1773 uint64_t epoch_ = 0;
1774 std::vector<std::pair<uint64_t, std::size_t>> ordinal_index_;
1775 struct ReceiptKey {
1776 EventId cause;
1777 RequestHandle recipient;
1779 uint64_t outcome_ordinal = 0;
1780 };
1781 std::vector<ReceiptKey> receipts_;
1782 std::uint64_t next_cohort_handle_ = 1;
1783 std::vector<CohortRoster> cohorts_;
1784 std::vector<CohortReceipt> cohort_receipts_;
1785};
1786
1788public:
1789 PreparedSubmit() noexcept;
1791 PreparedSubmit& operator=(PreparedSubmit&&) noexcept;
1794 PreparedSubmit& operator=(const PreparedSubmit&) = delete;
1795 explicit operator bool() const noexcept;
1796 const SubmitResult& predicted() const;
1798
1799private:
1801 struct Impl;
1802 std::unique_ptr<Impl> impl_;
1803 explicit PreparedSubmit(std::unique_ptr<Impl> impl) noexcept;
1804};
1805
1807public:
1810 PreparedReplace& operator=(PreparedReplace&&) noexcept;
1813 PreparedReplace& operator=(const PreparedReplace&) = delete;
1814 explicit operator bool() const noexcept;
1815 const ReplaceResult& predicted() const;
1817
1818private:
1820 struct Impl;
1821 std::unique_ptr<Impl> impl_;
1822 explicit PreparedReplace(std::unique_ptr<Impl> impl) noexcept;
1823};
1824
1826public:
1827 PreparedCancel() noexcept;
1829 PreparedCancel& operator=(PreparedCancel&&) noexcept;
1832 PreparedCancel& operator=(const PreparedCancel&) = delete;
1833 explicit operator bool() const noexcept;
1834 const CancelResult& predicted() const;
1836
1837private:
1839 struct Impl;
1840 std::unique_ptr<Impl> impl_;
1841 explicit PreparedCancel(std::unique_ptr<Impl> impl) noexcept;
1842};
1843
1845public:
1848 PreparedMutation& operator=(PreparedMutation&&) noexcept;
1851 PreparedMutation& operator=(const PreparedMutation&) = delete;
1852 explicit operator bool() const noexcept;
1853
1854private:
1856 struct Impl;
1857 std::unique_ptr<Impl> impl_;
1858 explicit PreparedMutation(std::unique_ptr<Impl> impl) noexcept;
1859};
1860
1862public:
1865 PreparedExecution& operator=(PreparedExecution&&) noexcept;
1868 PreparedExecution& operator=(const PreparedExecution&) = delete;
1869 explicit operator bool() const noexcept;
1870
1871private:
1873 struct Impl;
1874 std::unique_ptr<Impl> impl_;
1875 explicit PreparedExecution(std::unique_ptr<Impl> impl) noexcept;
1876};
1877
1878static_assert(std::is_nothrow_move_constructible_v<Request>);
1879static_assert(std::is_nothrow_move_assignable_v<Request>);
1880static_assert(std::is_nothrow_move_constructible_v<LiveRequest>);
1881static_assert(std::is_nothrow_move_assignable_v<LiveRequest>);
1882static_assert(std::is_nothrow_move_constructible_v<CommandEvent>);
1883static_assert(std::is_nothrow_move_assignable_v<CommandEvent>);
1884static_assert(std::is_nothrow_move_constructible_v<SubmitResult>);
1885static_assert(std::is_nothrow_move_constructible_v<ReplaceResult>);
1886static_assert(std::is_nothrow_move_constructible_v<CancelResult>);
1887static_assert(std::is_nothrow_move_constructible_v<NoEffectEvent>);
1888static_assert(std::is_nothrow_move_constructible_v<MatchRejectedEvent>);
1889static_assert(std::is_nothrow_move_constructible_v<ExecutionAppliedEvent>);
1890static_assert(std::is_nothrow_move_constructible_v<MatchCursor>);
1891static_assert(std::variant_size_v<OrderIntent> == 6);
1892static_assert(std::variant_size_v<ReductionSize> == 3);
1893static_assert(std::variant_size_v<SizeBasis> == 2);
1894static_assert(std::variant_size_v<Remaining> == 5);
1895static_assert(std::variant_size_v<RemainingProjection> == 5);
1896static_assert(std::variant_size_v<Allowance> == 4);
1897static_assert(std::variant_size_v<CommandEvent> == 19);
1898static_assert(std::variant_size_v<ExecutionPlan> == 4);
1899static_assert(std::variant_size_v<ExecutionScope> == 3);
1900static_assert(std::variant_size_v<TriggerState> == 9);
1901static_assert(std::variant_size_v<TriggerAnchor> == 2);
1902
1903} // inline namespace native_order_v6
1904} // namespace pineforge::native_order
1905
1906namespace std {
1907template <>
1908struct hash<pineforge::native_order::CohortHandle> {
1909 std::size_t operator()(pineforge::native_order::CohortHandle value) const noexcept {
1910 return static_cast<std::size_t>(value.value ^ (value.value >> 32));
1911 }
1912};
1913} // namespace std
Almost every prepared command yields one history event.
const CommandEvent & operator[](std::size_t index) const noexcept
InlineCommandEvents & operator=(InlineCommandEvents &&other) noexcept
InlineCommandEvents & operator=(const InlineCommandEvents &other)
CommandEvent & operator[](std::size_t index) noexcept
std::vector< RequestHandle > bound_close_handles() const
PreparedCancel prepare_cancel(const RequestHandle &target, uint64_t &next_timeline_ordinal, CancelReason reason=CancelReason::User)
reason lets the kernel withdraw its own request under the durable Superseded receipt.
EligibilityFacts eligibility_facts(const LiveRequest &live, const EvaluationContext &context) const noexcept
SubmitResult submit(const Request &request, int64_t decision_time_ms, uint64_t &next_order_incarnation, uint64_t &next_timeline_ordinal, std::optional< double > quantity_grid=std::nullopt)
R1 producer convenience: prepare then install one command.
WorkingRequestCore & operator=(WorkingRequestCore &&other) noexcept
static bool effective_host_units(const PendingAdjustments &pending, double resolved_units, double *deduction, double *after, bool *exhausted) noexcept
Pure arithmetic over the cached pending total.
InstallResult install_mutation(PreparedMutation &&prepared) noexcept
std::vector< RequestHandle > waiting_children(const RequestHandle &parent) const
InstalledCommand< ReplaceResult > install_replace(PreparedReplace &&prepared) noexcept
Preparation< PreparedMutation > prepare_bound_expiry(const EventId &physical_cause, const RequestHandle &child, const TargetObservation &observation, uint64_t &next_timeline_ordinal)
const std::vector< CommandEvent > & history() const noexcept
Preparation< PreparedMutation > prepare_evaluation(const RequestHandle &target, const EvaluationContext &context, const TargetObservation &observation, uint64_t &next_timeline_ordinal)
Preparation< PreparedMutation > prepare_no_effect(const RequestHandle &target, const EvaluationContext &context, uint64_t &next_timeline_ordinal)
bool evaluation_eligible(const LiveRequest &live, const EvaluationContext &context) const noexcept
bool cohort_contains(CohortHandle cohort, const RequestHandle &opening) const
Preparation< PreparedExecution > prepare_execution(const RequestHandle &target, const ExecutionProposal &proposal, uint64_t &next_timeline_ordinal)
InstallResult install_execution(PreparedExecution &&prepared, const CommittedExecutionFacts &facts) noexcept
std::vector< RequestHandle > group_recipients(const EventId &applied) const
void refresh_point_allowances(uint64_t point, const PositionIdentity &position) noexcept
Consumer-only no-event form of the ordinary allowance refresh.
CohortHandle cohort_open()
Command-boundary roster maintenance.
void cohort_remove(CohortHandle cohort, RequestHandle origin)
bool has_waiting_children(const RequestHandle &parent) const noexcept
WorkingRequestCore(WorkingRequestCore &&other) noexcept
Move transfers the complete run.
InstalledCommand< CancelResult > install_cancel(PreparedCancel &&prepared) noexcept
bool refresh_allowance(const RequestHandle &target, const EvaluationContext &context, const TargetObservation &observation)
const CommandEvent * event_at(const EventId &id) const
Preparation< PreparedMutation > prepare_parent_terminal(const EventId &terminal_or_replaced, const RequestHandle &child, uint64_t &next_timeline_ordinal)
PreparedReplace prepare_replace(const RequestHandle &target, const Request &request, const CommandContext &context, uint64_t &next_order_incarnation, uint64_t &next_timeline_ordinal, ReplaceOptions options={})
Preparation< PreparedMutation > prepare_trigger(const RequestHandle &target, const TriggerTransition &transition, DriverEligibilityClass driver_class, uint64_t &next_timeline_ordinal, std::optional< Side > cohort_side=std::nullopt, const ActivationGrid &grid={})
grid carries the run's activation grid (ActivationGrid); the default is the raw rule,...
bool working_is_buy(const LiveRequest &live, std::optional< Side > cohort_side=std::nullopt) const noexcept
Preparation< PreparedMutation > prepare_terms(const RequestHandle &target, const EvaluationContext &context, const TermsResolvedInput &input, uint64_t &next_timeline_ordinal)
CancelResult cancel(const RequestHandle &target, uint64_t &next_timeline_ordinal)
const std::vector< LiveRequest > & live() const noexcept
const std::vector< CohortRoster > & cohorts() const noexcept
Preparation< PreparedMutation > prepare_match_rejected(const RequestHandle &target, const EvaluationContext &context, MatchRejectReason reason, std::optional< ExecutionTerms > attempted_terms, uint64_t &next_timeline_ordinal)
const LiveRequest * find_live(const RequestHandle &handle) const
bool refresh_cohort_allowance(const RequestHandle &target, const EvaluationContext &context, const TargetObservation &observation)
Preparation< PreparedMutation > prepare_margin_call(const MarginCallEvent &event, uint64_t &next_timeline_ordinal)
Append one MarginCallEvent to the immutable history.
Preparation< PreparedMutation > prepare_match_rejected(const RequestHandle &target, const EvaluationContext &context, MatchRejectReason reason, uint64_t &next_timeline_ordinal)
bool trigger_permits_driver(const Trigger &trigger, const TriggerState &state, DriverEligibilityClass driver_class, bool existing_matching_bit) const noexcept
const std::vector< CohortReceipt > & cohort_receipts() const noexcept
ReplaceResult replace(const RequestHandle &target, const Request &request, int64_t decision_time_ms, uint64_t &next_order_incarnation, uint64_t &next_timeline_ordinal, std::optional< double > quantity_grid=std::nullopt, ReplaceOptions options={})
Preparation< PreparedMutation > prepare_risk_event(const NativeRiskEvent &event, uint64_t &next_timeline_ordinal)
Append one NativeRiskEvent to the immutable history.
Preparation< PreparedMutation > prepare_group_effect(const EventId &applied, const RequestHandle &recipient, uint64_t &next_timeline_ordinal)
void cohort_add(CohortHandle cohort, RequestHandle origin)
static Allowance evaluated_allowance(const LiveRequest &live, uint64_t point) noexcept
The allowance that prepare_evaluation would install for this point.
WorkingRequestCore & operator=(const WorkingRequestCore &)=delete
Preparation< PreparedMutation > prepare_owner_applied(const EventId &applied, const RequestHandle &child, const std::optional< OpeningObservation > &observation, uint64_t &next_timeline_ordinal, const ArmContext &arm={})
arm carries the materialization facts of an anchored leg (the price tick a rounded anchor snaps to,...
InstalledCommand< SubmitResult > install_submit(PreparedSubmit &&prepared) noexcept
PreparedSubmit prepare_submit(const Request &request, const CommandContext &context, uint64_t &next_order_incarnation, uint64_t &next_timeline_ordinal, RequestOrigin origin=RequestOrigin::Host)
Consumer-only typed prepare/install.
std::variant< Flatten, order_action::Reduce, order_action::Transact > Action
Definition execution.hpp:17
std::variant< AllowanceUnset, AllowanceUnits, AllowanceAllScope, AllowanceDeferred > Allowance
NativeArmScope
What an armed CLOSING request (Reduce, Flatten, a HostSized close) closes.
std::variant< T, NoChange, PreparationError > Preparation
bool operator==(CohortHandle left, CohortHandle right) noexcept
MatchRejectReason
Why a CANDIDATE was refused — a decision taken at a matching point, with the request still live unles...
@ RiskLimit
An opening refused while the run spec's generic risk limits are blocking (L9).
std::variant< ImmediateRemaining, PointBudget > Capacity
ExecutionPlan to_execution_plan(const execution::Action &action)
NativeArmFirstMatch
When an armed request may first match.
std::variant< CommandInstalled< R >, InstallError > InstalledCommand
RiskLimitKind
Which generic risk limit of NativeRunSpec::risk a NativeRiskEvent reports.
std::variant< Market, Limit, Stop, StopLimit, Trail > Trigger
CancelReason
Why a request left the book.
@ Superseded
A kernel-originated request the kernel itself withdrew: the liquidation level or its units moved,...
ScopeBasis
Which measurement of the bound scope the fraction is taken of.
bool quantity_on_grid(double q, double step) noexcept
v2 exact binary64 grid: r=abs(q)/s, n=round(r) half away from zero, g=n*s.
std::variant< Absolute, FromOwnerFill > TriggerAnchor
std::variant< Installed, InstallError > InstallResult
NativeCandidatePriceKind
Which price a candidate is being offered at: the point's own price, or the request's trigger level (a...
std::variant< NoGroup, Member > Group
std::variant< ExplicitUnits, OwnerOpenedUnits, ScopeFraction > ReductionSize
How much a Reduce takes: literal units, the owner's own opening, or a fraction of the bound scope.
std::variant< BookTransaction, Wait, ArmedTransaction, UnboundBookClose, BookClose, OpeningClose, OpeningsClose, CohortClose > Authority
std::variant< EnrollmentFromCommand, EnrollmentFromApplied > Enrollment
SizePrice
WHICH price the basis is converted at.
std::uint8_t display_waypoint(const MatchCursor &cursor) noexcept
std::variant< AcceptedEvent, RejectedEvent, ReplacedEvent, ReplaceRejectedEvent, CancelledEvent, NotWorkingEvent, InvalidHandleEvent, NoEffectEvent, MatchRejectedEvent, ExecutionAppliedEvent, CloseBoundEvent, ActivatedEvent, ReservationReducedEvent, DeferredGroupAdjustmentEvent, QuantityBoundEvent, ArmedEvent, TermsResolvedEvent, MarginCallEvent, NativeRiskEvent > CommandEvent
Every alternative of one request's history, as one value.
std::variant< MarketReady, LimitReady, StopIdle, StopActive, StopLimitPending, StopLimitLive, TrailWaitArm, TrailTrack, TrailActive > TriggerState
std::variant< CashValue, EquityFraction > SizeBasis
The two bases a Sized intent may name.
ExecutionGridPolicy
Resolved host sizing normally remains subject to the run's quantity grid.
std::variant< RemainingUnbound, RemainingFlattenAll, RemainingUnits, RemainingDeferred, NoTarget > Remaining
std::variant< Independent, WaitForApplied, BindOpening, BindOpenings, BindCohort > Owner
OrderIntent intent_from_physical(const execution::Action &action)
bool operator<(CohortHandle left, CohortHandle right) noexcept
std::variant< execution::Flatten, order_action::Reduce, order_action::Transact, execution::ReverseTo > ExecutionPlan
The settlement shape one execution takes.
Request market_request(execution::Action action, std::string label={}, std::string comment={})
std::shared_ptr< const RequestDefinition > DefinitionRef
NativeArmVisibility
Whether an owner-related request that arms is a working order before its arm.
std::variant< PositionFlat, PositionNonflat > PositionIdentity
std::variant< RemainingProjectionUnbound, RemainingProjectionFlattenAll, RemainingProjectionUnits, RemainingProjectionDeferred, RemainingProjectionNoTarget > RemainingProjection
bool has_market_defaults(const Request &request) noexcept
ScopeClaim
Gross claims the whole bound scope; NetOfSiblings first subtracts the units already claimed by the li...
order_action::Transact Transact
A signed book transaction: finite nonzero units on the side their sign names.
std::variant< PendingNone, PendingDeferred > PendingAdjustments
std::function< std::optional< double >( const LiveRequest &leg, const ExecutionAppliedEvent &owner_fill, Side leg_side, double offset, double kernel_level)> AnchoredLevelResolver
The one policy point of an anchored materialization.
std::variant< Flatten, Reduce, Transact, ReverseTo, HostSized, Sized > OrderIntent
What a request does.
SizeTime
AtMatch resolves the basis at the matching candidate; AtAcceptance freezes the resolved units when th...
NativeAnchorRounding
How a materialized anchored level is snapped onto the run's price tick ladder (NativeRunSpec::price_t...
bool point_eligible(const LiveRequest &live, uint64_t point_ordinal, int64_t effective_time_ms) noexcept
Whether a request may be considered at this driver point: the point's ordinal must be strictly past t...
std::variant< BeginTrailTracking, ObserveTrailExtremum, ActivateStop, ActivateStopLimit, ActivateTrail > TriggerTransition
std::variant< execution::Book, execution::OpeningExposure, SelectedExposure > ExecutionScope
What one execution acted on: the whole book, one opening's exposure, or a bound selection.
bool operator!=(CohortHandle left, CohortHandle right) noexcept
RequestRejectReason
Why an acceptance was refused, on a Rejected SubmitResult.
@ PlacementAdmission
A Sized{SizeTime::AtAcceptance} whose acceptance-resolved quantity does not pass the run's opening ad...
Everything the kernel knows about WHERE a point is, as one owning value: its event ordinal,...
Where a request's trigger level comes from.
One request was accepted: the definition, and the timeline ordinal it was accepted at.
const RequestHandle & handle() const noexcept
What the consumer hands to prepare_trigger under an opted-in price grid (NativeRunSpec::price_grid ==...
What the consumer hands to prepare_owner_applied for an anchored leg's materialization,...
An anchored leg was materialized, exactly once: fill + offset, then the rounding, then resolve_anchor...
Bind to a host-built roster (cohort_open / cohort_add / cohort_remove), read at the match rather than...
Bind to ONE already-live opening, by its handle and the positive cycle it is live in.
Bind to a fixed cohort of already-live openings.
The authority an armed leg acquires under NativeArmScope::Book: the whole position its owner's fill l...
const RequestHandle & handle() const noexcept
Native sizing bases (L3).
A host-maintained, run-scoped roster identity.
std::vector< RequestHandle > origins
Canonical origin handles, ordered by origin incarnation rather than by host insertion order.
What acceptance needs from the run to judge a command: the point's ordinal and coordinate,...
std::optional< double > sizing_scope
Placement-time sizing measurements the execution consumer owns, supplied only for the intent that ask...
std::optional< double > sizing_units
Kernel-resolved acceptance-time units for a Sized{AtAcceptance} request.
std::optional< double > price_tick
The run's price tick, needed only to resolve a tick-spelled trail offset or trigger anchor.
A fraction in (0, 1] of MARKED equity at the sizing point.
bool pre_open_birth_eligible
Generic current-point delivery.
std::optional< Side > cohort_side
Set only while resolving a CohortClose candidate.
One committed execution: the definition, the cursor, the resolved price and units,...
bool pre_open_birth_eligible
Carries the generic pre-open delivery authorization from the matching evaluation through the synchron...
A reduction of exactly these units, finite and positive.
Defer a level to the owner's fill: at the arm the level becomes fill + offset, with offset signed (ad...
An opening or a closing whose quantity the HOST resolves, in NativeStrategyHost::resolve_execution_te...
Take whatever the point allows, with no per-point cap.
No owner: the request stands on its own book authority.
fill_through makes the limit a touch trigger (market-if-touched): the level still gates when the requ...
const RequestHandle & handle() const noexcept
const std::optional< RequestHandle > & predecessor() const noexcept
std::optional< double > sizing_units
Placement-time sizing measurements frozen from the accepting command context.
A kernel-issued liquidation that actually filled.
Match at the next eligible matching point, with no level to reach.
A candidate the run refused, with its MatchRejectReason and the cursor it was refused at.
A reduction or a flatten that had nothing to close: terminal, with no execution identity,...
const RequestHandle & handle() const noexcept
A dynamic cohort has no currently live member.
A reduction of exactly what the owner's fill opened, bound at the arm and reported by a QuantityBound...
Cap what this request may execute at ONE matching point at units.
A working-request preparation that could not produce a representable instruction — a nonrepresentable...
A Reduce{OwnerOpenedUnits} was bound, at its owner's fill, to what that fill opened.
A resolved remaining quantity, in units.
Replacement behaviour that is not expressible in the successor request.
const RequestHandle & successor() const noexcept
const RequestHandle & predecessor() const noexcept
The immutable accepted form of one request: its handle, the request value as accepted,...
RequestOrigin origin
Appended last so every existing aggregate initializer keeps its meaning.
Aggregate field order keeps market construction: Request{Transact{1.0}, "buy", "comment"}...
Exact target exposure for an explicit reversal request.
A fraction in (0, 1] of the bound scope, resolved at the matching candidate.
ScopeBasis basis
Appended last so every existing aggregate initializer keeps its meaning.
Native authorization receipt, converted to a call-local financial SelectedOpeningSet only at the cons...
SizePrice price
Which price the basis converts at; see SizePrice.
bool reserve_percent_fee
Divide the sizing cash by (1 + fee) when the run's fee kind is NativeFeeKind::Percent.
ExecutionGridPolicy grid_policy
SnapToGrid floors the resolved units onto the run's quantity grid.
A stop that, once reached, becomes a limit at limit.
A stop trigger: the modeled path has to reach price from the adverse side.
Trail offset spelled in price ticks instead of a price distance.
offset is a price distance: the stop rides offset behind the running best.
The one owner relation that arms (the ArmedEvent).
The authority an unarmed WaitForApplied child holds: accepted and live, but never a matching candidat...
std::size_t operator()(pineforge::native_order::CohortHandle value) const noexcept