PineForge unreleased
Deterministic PineScript v6 backtest runtime — C ABI reference
Loading...
Searching...
No Matches
native_run_spec.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <pineforge/bar.hpp>
6
7#include <cstdint>
8#include <optional>
9#include <string>
10#include <type_traits>
11#include <variant>
12#include <vector>
13
14namespace pineforge {
15inline namespace native_run_spec_v3 {
16
17/// Encodings coincide with the versioned native-v1 C transport. These values
18/// describe native execution; they do not configure source strategy policies.
19enum class NativeFeeKind : std::uint32_t {
23};
24
25/// When a request born at a script calculation may first match.
26/// NextEligiblePoint (the default) waits for a later eligible matching point — the
27/// next modeled opening, an observed print, or a carried open — and never fills on
28/// the bar's already presented open/high/low/close. AfterCalculation additionally
29/// offers a modeled close point after that calculation, still obeying the birth
30/// ordinal and floor; it is not a replay of observed prints. The Pine adapter
31/// projects process_orders_on_close onto AfterCalculation. Pinned by
32/// tests/test_native_resting_driver_contract.cpp.
33enum class NativeCloseExecution : std::uint32_t {
36};
37
38/// Abort presentation is a run-level policy rather than an exception-path
39/// convention. Generic hosts retain an error diagnostic by default; a host
40/// that models cooperative cancellation can opt into a quiet status result.
41enum class NativeAbortReporting : std::uint32_t {
42 Error = 0,
43 Quiet = 1,
44};
45
46/// Who records the per-script-bar report series. HostRecorded leaves the
47/// equity curve, its metrics and any range-end row entirely to the host, which
48/// is what every host that drives its own recording already does.
49/// KernelRecorded asks the consumer to mark one equity point per script
50/// calculation, so a bare host gets a truthful curve, finite drawdown/run-up
51/// metrics and a report whose walk is not degenerate. Recording is reporting:
52/// it books no cash and places no order.
53/// KernelRecordedAtHostMarks records the very same series, at the points the
54/// host marks: a host whose report cadence is not one point per calculation —
55/// a source adapter that re-enters its script on a fill, or publishes a bar
56/// its script never calculates — keeps that cadence and still stops owning
57/// what a report point is. The consumer never records on its own initiative
58/// under it, so it leaves the continuation identity exactly where
59/// HostRecorded leaves it (see hash_spec in native_execution_consumer.cpp).
60/// A C++ host marks through NativeStrategyHost::mark_native_report_point.
66
67/// Which opening directions the run admits at all. Both is the default and the
68/// whole established surface; a refused opening is
69/// MatchRejectReason::OpeningDirection, and it rejects the ENTIRE transaction,
70/// including a proposed close remainder. Closing-only reductions stay legal under
71/// None. Pinned by tests/test_native_resting_matching_contract.cpp.
72enum class NativeOpenDirections : std::uint32_t {
73 None = 0,
74 Long = 1,
75 Short = 2,
76 Both = 3,
77};
78
79/// When the kernel asks the host to calculate. BarClose is the whole default
80/// surface: exactly one calculation per script bar, at its close, which is
81/// what every host that drives its own cadence already gets. The other two
82/// are a strict superset of the one before them, so a host never loses the
83/// close calculation by opting in.
84///
85/// BarCloseAndFills additionally recalculates once at the cursor of each
86/// applied execution, from the existing applied-notification drain and
87/// bounded by max_recalculations_per_point. EveryModeledPoint additionally
88/// recalculates at every modeled point of the delivered path (each confirmed
89/// OHLC waypoint, each intrabar sample) and at every observed print.
90///
91/// This is a generic cadence, not a source-language policy: TradingView's
92/// waypoint-only COOF refill, its two-fills-at-open rule and its script-state
93/// rollback stay in the source layer, which never sets this field.
94enum class NativeCalculationTrigger : std::uint32_t {
98};
99
100/// What a bar-open callback is handed. Complete keeps the established view:
101/// on_native_bar_open receives the whole script bar, which is what a host that
102/// schedules against the bar's own high/low needs. OpenOnly masks that
103/// lookahead for hosts that must decide at the open with open-only
104/// information: H = L = C = open and volume 0. It changes no matching, no
105/// fill and no other callback; mid-bar callbacks answer current_partial_bar()
106/// for the lookahead-free bar so far.
107enum class NativeOpenBarView : std::uint32_t {
110};
111
112/// A generic instrument price grid. The kernel otherwise treats price_tick as
113/// the slippage multiplier only, so an unset grid leaves every booked price
114/// exactly as the path presented it. QuantizeFills books the fill on the tick
115/// ladder; QuantizeFillsAndTriggers additionally tests a resting trigger
116/// against the tick-quantized path. Source-language tick quirks are not
117/// spelled here: they remain source-layer policy on top of None.
118enum class NativePriceGrid : std::uint32_t {
119 None = 0,
122};
123
124/// HalfUp is the nearest tick with ties away from zero. Directional rounds
125/// toward the price region the resting order needs: a buy limit rounds down
126/// and a sell limit up, a stop the other way, which is also the adverse side
127/// of a market fill.
128enum class NativeGridRounding : std::uint32_t {
131};
132
133/// Which units a kernel-issued liquidation reduces (L4). RestoreMinimum is
134/// the fewest units that restore the marked equity to the maintenance
135/// requirement at the sizing mark; ShortfallMultiple books that same restore
136/// scaled by `shortfall_multiple` (never 4.0 by default: a broker wanting a
137/// multiple declares it); Flatten closes the whole position. A host whose
138/// slice rule is not one of these answers it through
139/// resolve_margin_call_units and declares none of them (the Pine adapter does).
140enum class NativeLiquidationSizing : std::uint32_t {
144};
145
146/// When the kernel tests the maintenance requirement. PathAdverseExtreme
147/// evaluates it against the most adverse price the remaining modeled script
148/// path still reaches and rests the reduction at the liquidation level, so the
149/// fill lands where the account actually runs out of margin. CalculationOnly
150/// tests the mark only at a script calculation point and rests nothing.
151/// PathAdverseExtremeMark is the period-mark broker: it measures the breach at
152/// that same adverse mark and rests the reduction AT THAT MARK, so the fill
153/// lands on the adverse waypoint the breach was measured at instead of on a
154/// solved level. It never solves a level, which is also what makes it the one
155/// mode that still checks where no level exists (a LONG at full maintenance —
156/// see NativeMarginModel). The resting price is the default resolved price of
157/// that fill, so resolve_execution_terms still has the last word on it.
163
164/// Which equity the maintenance requirement is tested against. MarkedEquity is
165/// the account's marked equity exactly as marked_equity() computes it: the open
166/// entries' commissions have already reduced it. MarkedEquityBeforeOpenCommission
167/// is the same mark-to-market equity taken before that reduction (initial
168/// capital + realized net profit + open profit), i.e. a broker whose margin
169/// equity does not charge the still-open entries' commission against the
170/// account. Nothing else about the account model changes: this is one term of
171/// one comparison, never a second accounting truth.
176
177/// Which base the liquidation level is solved from. MarkedEquity is the
178/// intercept of marked_equity(): initial capital + realized net profit minus
179/// the open entries' commissions. RealizedOnly drops that last term, so the
180/// level is solved from initial capital + realized net profit alone. The two
181/// agree whenever no open entry has paid a commission.
182enum class NativeLiquidationLevelBase : std::uint32_t {
185};
186
187/// A generic per-side broker margin model (L4). It is entirely opt-in: a spec
188/// that leaves `NativeRunSpec::margin` unset keeps the one-scalar
189/// `initial_margin_fraction` gate and has no liquidation path at all.
190///
191/// `initial_long` / `initial_short` are the opening-admission fractions (not
192/// percents) applied to the resulting absolute notional of an opening, per
193/// side. Both must be finite and never negative.
194///
195/// ZERO is the MAINTENANCE-ONLY spelling for that side: the kernel enforces no
196/// opening requirement on it, at the candidate gate and at placement alike, and
197/// the HOST owns opening admission there. Opening admission and liquidation are
198/// two different broker functions: a host that runs its own pre-trade check
199/// (and says so at the candidate gate by answering
200/// NativePrecommitVerdict::AdmitWithHostMargin) still wants the kernel's
201/// liquidation mechanism, and this is how it asks for one without the other.
202/// A side may waive either function but not both: `initial_* == 0.0` is legal
203/// only where that side's `maintenance_*` is set (and positive), and a side
204/// with neither is refused as MarginSideUndeclared. A model whose `initial_*`
205/// are positive behaves exactly as it always has.
206///
207/// `maintenance_long` / `maintenance_short` are the liquidation fractions.
208/// Absent means that side never liquidates. Present means the kernel solves
209/// for the liquidation level -- the price at which the marked equity falls
210/// below the maintenance requirement -- and rests a kernel-originated Reduce
211/// with Stop{level} while the requirement is breached on the modeled path.
212/// A maintenance fraction equal to 1.0 has no finite level for a LONG: at
213/// full maintenance a long's equity and requirement move together, so the
214/// breach is a constant and no price solves it. That degenerate slope rests
215/// nothing under PathAdverseExtreme -- there is no price to rest at -- while
216/// PathAdverseExtremeMark, which never solves a level, still measures the
217/// breach at the adverse mark and still consults the host's requirement hook.
218/// Source-language money rules beyond that are source-layer policy, never
219/// spelled here.
220///
221/// `liquidation_min_units` is the broker's minimum liquidation trade: a
222/// computed reduction below it flattens the position instead.
223///
224/// `basis` and `level_base` are the two places brokers legitimately disagree
225/// about money: which equity the requirement is tested against, and which base
226/// the reported level is solved from. Both default to the marked-equity model
227/// the kernel has always used, and both fold into the run spec's digest only
228/// when moved off it.
229///
230/// `liquidation_label` / `liquidation_comment` name the TICKET the kernel's own
231/// liquidation is booked under. A broker's forced liquidation carries the
232/// broker's identifiers, and a reporting layer that classifies a closed row by
233/// its ticket id -- which is the ordinary way to say "this row was a margin
234/// call" -- can only do so if those identifiers are the broker's. Empty keeps
235/// the kernel's own ("__kernel_liquidation__" / "Margin liquidation"); a set
236/// value is used verbatim for the request's label and comment and therefore
237/// for the closed row's exit id and comment. Like the two money bases, each
238/// folds into the model's digest only when set, so a model that does not name
239/// its ticket digests exactly as it did before they existed.
254
255/// One risk threshold (L9). `value` is account currency when `percent` is
256/// false, and a percentage of the limit's own basis equity — the running peak
257/// for a drawdown, the day's opening equity for an intraday loss — when it is
258/// true. Percent is out of 100: 2.5 means two and a half percent.
260 double value = 0.0;
261 bool percent = false;
262};
263
264/// Which day a risk limit's "day" is. SessionDay is the run's own session
265/// calendar: the trading date of the session that contains the instant
266/// (native_calendar::session_day_ordinal), so an overnight session is one day.
267/// CalendarDayInTimezone is the plain civil date in the spec's scheduling
268/// timezone, which is what a host that reports by wall-clock date wants. The
269/// two differ exactly where a session crosses midnight.
270enum class NativeRiskDay : std::uint32_t {
273};
274
275/// What a breach does. BlockOpenings refuses every opening while the block
276/// lasts and leaves the live book alone. FlattenAndBlock first closes the book
277/// with one kernel-originated Flatten and then blocks.
278enum class NativeRiskAction : std::uint32_t {
281};
282
283/// Generic account risk limits (L9), entirely opt-in: a spec that leaves
284/// `NativeRunSpec::risk` unset evaluates nothing, appends no event and folds
285/// nothing into the continuation digest. The existing per-opening caps
286/// (max_abs_units, max_open_lots, allowed_open_directions) are unchanged and
287/// independent of this block.
288///
289/// Every limit is measured at three points of each script bar — its open, its
290/// own close calculation, and after each applied drain — against the marked
291/// equity there:
292/// max_drawdown equity has fallen from its running peak by at
293/// least the limit (percent: of that peak)
294/// max_intraday_loss equity is below the day's opening equity by at
295/// least the limit (percent: of that opening
296/// equity)
297/// max_consecutive_loss_days N consecutive days each closing with a realized
298/// loss; a day that closes with a realized profit
299/// restarts the count, a day with no realized
300/// result leaves it where it was
301/// max_fills_per_day N applied fills within the day, counted as they
302/// settle, so the fills of one matching point all
303/// settle and the limit blocks the next admission
304///
305/// A drawdown or consecutive-loss-days breach blocks openings to the end of
306/// the run; the two intraday limits block to the end of their own day.
307/// TradingView's own strategy.risk.* rules are not this model: its chart-day
308/// key, its cancel-pending behaviour and its epsilon stay in the source layer,
309/// which never sets this block.
311 std::optional<NativeLossLimit> max_drawdown;
312 std::optional<NativeLossLimit> max_intraday_loss;
313 std::optional<std::uint32_t> max_consecutive_loss_days;
314 std::optional<std::uint32_t> max_fills_per_day;
317};
318
319/// Native hosts normally require every confirmed bar to name a canonical input
320/// slot. A host whose feed carries provider labels can retain the caller's
321/// strictly-increasing timestamps as its decision labels instead. This is a
322/// feed-shape policy, not a source-language one, and it remains a run-spec
323/// value so the two modes never share a continuation.
324enum class NativeSlotLabelPolicy : std::uint32_t {
327 /// Deprecated spelling of FeedTolerant; identical value, kept so existing
328 /// hosts and the source adapter compile unchanged.
330};
331
332/// Generic ordering for a modeled OHLC path. Auto retains the open-proximity
333/// rule; the forced modes make the first excursion explicit for replay/live
334/// hosts without relying on process-global or source-language state.
335enum class NativePathOrder : std::uint32_t {
336 Auto = 0,
339};
340
341/// Explicit, opt-in admission exceptions for a tolerated input-feed shape.
342/// They are separate from slot labels because a host may need the tolerant
343/// price / unavailable-volume admission while retaining canonical calendar
344/// labels. This is a feed-shape policy, not a source-language one.
345enum class NativeFeedTolerance : std::uint32_t {
346 None = 0,
347 /// Match the batch structural check: finite OHLC values need not be
348 /// positive, and NaN volume means unavailable activity.
350 /// Source-compatible stream warmups admit finite, non-negative interim
351 /// OHLC values. The final warmup close remains strictly positive.
353};
354
355/// Deprecated spelling of the tolerance type. Same type, same values, same
356/// hash; kept so existing hosts and the source adapter compile unchanged.
358
359/// Whether one NativeFeedTolerance bit is set in a run's mask. Prefer it to
360/// testing the bits by hand; `enabled` is the run's own value and `requested` the
361/// single bit being asked about.
363 NativeFeedTolerance enabled, NativeFeedTolerance requested) noexcept {
364 return (static_cast<std::uint32_t>(enabled)
365 & static_cast<std::uint32_t>(requested)) != 0u;
366}
367
368/// Deprecated spelling of native_feed_tolerance_enabled.
370 NativeFeedTolerance enabled, NativeFeedTolerance requested) noexcept {
371 return native_feed_tolerance_enabled(enabled, requested);
372}
373
374/// An owned lower-timeframe execution path. It is deliberately a run-spec
375/// value rather than a caller borrow: public begin arguments expire when the
376/// begin call returns, whereas native matching may need the lower bars later
377/// while sealing an aggregated script bar.
379 /// No intrabar path: the confirmed OHLC waypoints are the whole modeled walk, and
380 /// on_native_sub_bar never fires. The default.
381 struct none {};
382 enum class SampleEligibility : std::uint32_t {
383 /// Native hosts retain continuous matching between generated samples
384 /// unless they explicitly request point-only sample eligibility.
387 };
388 /// A RETAINED finer feed: the host's own lower-timeframe bars, the literal they
389 /// are at, and the sampling policy over them. It is the only alternative that has
390 /// sub-bars of its own, so it is what makes on_native_sub_bar reachable, and,
391 /// matched as ContinuousSegments, it is what gives the margin model a delivered
392 /// sample to re-evaluate at instead of a whole-bar waypoint
393 /// (NativeMarginCheckKind::IntrabarSample). C spelling: PF_NATIVE_INTRABAR_LOWER_TF.
404 /// A synthesized path has no retained lower feed. The driver samples each
405 /// script bar's own OHLC path through the generic sampler declared in
406 /// include/pineforge/magnifier.hpp. Its point-only eligibility is inherent
407 /// to this mode, so there is no separate SampleEligibility member.
415 /// The three alternatives as one value. Exhaustive; use the accessors below rather
416 /// than std::get_if at call sites.
417 using value_type = std::variant<none, lower_tf, synthesized>;
418
420
421 /// Whether this run declares no intrabar path at all.
422 bool is_none() const noexcept { return std::holds_alternative<none>(value); }
423 /// The retained finer feed, or nullptr when this is not the lower_tf alternative.
424 const lower_tf* lower() const noexcept { return std::get_if<lower_tf>(&value); }
425 /// The mutable retained finer feed, or nullptr; a staged spec's feed is copied by
426 /// configure_native, so mutating it afterwards changes nothing about the run.
427 lower_tf* lower() noexcept { return std::get_if<lower_tf>(&value); }
428 /// The synthesized sampling policy, or nullptr when this is not that alternative.
429 const synthesized* synthesized_path() const noexcept {
430 return std::get_if<synthesized>(&value);
431 }
432 /// The mutable synthesized sampling policy, or nullptr.
433 synthesized* synthesized_path() noexcept { return std::get_if<synthesized>(&value); }
434};
435
436/// One declared higher-timeframe series of the run's own symbol, the native
437/// equivalent of request.security(syminfo.tickerid, tf, ...). The kernel
438/// aggregates the accepted input into `tf` buckets and delivers each completed
439/// bucket to the host; nothing here configures a source language.
440///
441/// A subscription is a series INSTANCE, not a period: several may declare the
442/// same `tf`, each gets its own evaluator and bucket state, and each is
443/// identified — in NativeTimeframeBarContext::subscription and in
444/// native_series_bar() — by its own index in NativeRunSpec::subscriptions.
445/// The one thing same-period instances cannot each own is a different
446/// `authoritative_bars` feed; see DuplicateSubscriptionTimeframe.
447///
448/// `tf` must pair with NativeRunSpec::input_tf exactly as script_tf does
449/// (native_calendar::compatibility) and may not be strictly finer than the
450/// input: a lower-timeframe array is a different contract.
451///
452/// `authoritative_bars` are the exchange's own bars of that timeframe, at most
453/// one per completed bucket. When present, a completed bucket takes its
454/// OHLCV from the bar keyed to the same period, and those stamps become the
455/// period partition: the feed store is TradingView-calibrated, so "W"/"M"
456/// buckets are built from installed DAILY bars and a session with no stamp of
457/// its own folds into the next trade date's bar. A host that supplies them
458/// inherits those rules (docs/pages/native-engine.md).
459///
460/// `lookahead` is Pine's barmerge.lookahead_off (false, the default: the
461/// bucket is delivered on the input bar that completes it -- its own last
462/// contributing input bar when that bar closes it, or, for a bucket only a
463/// later input reveals as complete (NativeCompletionKind::LazyComplete), the
464/// next period's first input bar, which contributes nothing to it) or
465/// lookahead_on (true: the completed bucket's final values are delivered at
466/// its FIRST contributing input bar).
467///
468/// `gaps` is Pine's barmerge.gaps_off (false, the default: a delivered bucket
469/// stands until the next delivery replaces it) or gaps_on (true: the series is
470/// CLEARED on every accepted input that delivers no bucket of its own, so
471/// native_series_bar() answers nullopt — the empty that stands for na — on
472/// exactly the bars the series does not publish on). It changes nothing about
473/// which buckets complete, when they are delivered, or what they contain.
474///
475/// `source` names the bars the series is built from. Input (the default, and
476/// the whole surface described above) aggregates the accepted input.
477/// AuxiliaryFeed aggregates NativeRunSpec::auxiliary_feed instead — the finer
478/// bars the input does not have — so `tf` then pairs with the FEED's timeframe
479/// exactly as script_tf pairs with input_tf, and may be finer than the input,
480/// equal to it or coarser. See NativeAuxiliaryFeed for the routing rule.
481/// InstrumentFeed reads ANOTHER instrument's own bars: the
482/// NativeRunSpec::instrument_feeds entry keyed by (`instrument`, `tf`),
483/// delivered bar for bar and aggregated by nothing. See NativeInstrumentFeed
484/// for its delivery rule; `lookahead` and `gaps` keep their meaning there.
485enum class NativeSeriesSource : std::uint8_t {
486 Input = 0,
489};
490
492 std::string tf;
493 std::vector<Bar> authoritative_bars;
494 bool lookahead = false;
495 bool gaps = false;
497 /// InstrumentFeed only: the instrument key of the feed the series reads,
498 /// compared byte for byte with NativeInstrumentFeed::instrument. Empty for
499 /// every other source, which is the whole established surface.
500 std::string instrument;
501};
502
503/// An auxiliary feed of the run's OWN symbol at a timeframe strictly finer
504/// than the input: the bars the input feed does not have. It drives nothing by
505/// itself — no matching point, no calculation, no script bar — and is read
506/// only by the declared series whose `source` is AuxiliaryFeed.
507///
508/// `tf` must parse, and input_tf must pair over it exactly as a script_tf pairs
509/// over an input_tf, strictly coarser: "1" under a "15" input, "15" under "D".
510/// `bars` are strictly increasing in time and may be empty (a stream that
511/// learns every bar live, NativeStrategyHost::append_auxiliary_bars).
512///
513/// Routing is by time and by nothing else. When an input bar is accepted, every
514/// feed bar not yet consumed that opened BEFORE that input's period ended
515/// (NativeInterval::next_period_open_ms of the input's own interval) is folded,
516/// in feed order, into each AuxiliaryFeed series, ahead of that input's
517/// aggregation, matching and calculation — the delivery point a series built
518/// from the input has. So bars inside the input ride on it; bars in a hole of
519/// the input ride on the next accepted input; bars earlier than the first
520/// input are folded on that first input, which is how a host supplies history
521/// the input does not reach; and bars later than the last input's period are
522/// never folded. There is no chart-slice mapping, no label re-keying and no
523/// deferred publication: those are a source layer's.
525 std::string tf;
526 std::vector<Bar> bars;
527};
528
529/// One named column an instrument feed carries beside its OHLCV: one value
530/// per bar, in bar order. The kernel stores, validates and digests it and
531/// reads nothing in it; a NaN value is an absent one.
533 std::string name;
534 std::vector<double> values;
535};
536
537/// Another instrument's own bars of one timeframe, installed as data. A run
538/// may hold any number of them, keyed by (`instrument`, `tf`); a declared
539/// series reads one through NativeSeriesSource::InstrumentFeed. The feed
540/// drives nothing by itself -- no matching point, no calculation, no script
541/// bar -- and the kernel guesses no calendar for the instrument: every bar
542/// carries its own close, `close_ms[i]`, beside its open, `bars[i].timestamp`.
543///
544/// `instrument` is an opaque key (no venue or symbol grammar is read). `tf`
545/// must parse; it is part of the key and pairs with nothing, so a feed may be
546/// finer or coarser than the input. `bars` are strictly increasing, their
547/// prices finite, their volume finite and nonnegative or NaN (the instrument
548/// publishes none). `close_ms` has one entry per bar, each after its own open
549/// and at or before the next bar's open. `columns` each hold one value per bar.
550///
551/// Delivery is by interval and by nothing else. On each accepted input, a
552/// series reading the feed is handed, in feed order, every bar it has not yet
553/// been handed that is visible at that input:
554/// lookahead = false: bars whose close_ms <= the input's
555/// NativeInterval::last_traded_close_ms -- the bars that have closed by
556/// the time the input has;
557/// lookahead = true: bars whose open <= the input's NativeInterval::open_ms
558/// -- the bar in progress at the input's open included, with the final
559/// values the feed gives it.
560/// The input's interval is its calendar interval: under a raw label
561/// partition, whose inputs carry no duration, the interval the run's session,
562/// zone and input timeframe give the input's label. The last bar handed over
563/// is the series' visible value
564/// (native_series_bar()), each delivery reaches on_native_timeframe_bar, and
565/// the delivery point is the one every series has: after on_native_input,
566/// before the input is aggregated, matched or calculated. So bars earlier
567/// than the first input are handed over on that first input (history), a
568/// bar the feed carries past the last input's horizon never is, and every
569/// bar is handed over exactly once, in order: a host that counts deliveries
570/// knows the feed index of each. `gaps` is the subscription contract's: true
571/// clears the series on every accepted input that hands it nothing.
572/// NativeTimeframeBarContext::interval is the bar's own: open_ms and
573/// eligible_open_ms its open, last_traded_close_ms and next_period_open_ms
574/// its close_ms, next_input_open_ms the next feed bar's open (its close_ms
575/// for the last bar); completion is Confirmed.
576///
577/// Opt-in: absent (empty) is the whole default surface, and the continuation
578/// digest folds the feeds only when there are any. Historical runs only:
579/// stream_begin refuses a spec that installs one.
581 std::string instrument;
582 std::string tf;
583 std::vector<Bar> bars;
584 std::vector<std::int64_t> close_ms;
585 std::vector<NativeInstrumentColumn> columns;
586};
587
588/// What the kernel keeps of a run's event record, the rows native_events()
589/// returns (R5 lane V19-B). It is reporting: no fill, no decision and no
590/// state the continuation folds depends on it; it bounds what a run holds.
591///
592/// Window, the default, keeps the command journal only until every reader has
593/// consumed it. A host that polls native_events() acknowledges what it has
594/// read (NativeStrategyHost::native_acknowledge_events) and the kernel drops
595/// the acknowledged events at the next script-bar boundary; a host that never
596/// acknowledges is served by its callbacks alone, and its window closes at
597/// every script-bar end. The kernel never drops an event its own live state
598/// still reads -- a deferred group-adjustment chain, an applied notification
599/// not yet delivered -- and keeps no driver point and no account observation.
600/// Memory is O(live + replaces): plain replaces retain a small chain index.
601///
602/// Commands keeps the whole command journal and every account observation,
603/// and no driver point. Full keeps everything: the command journal, every
604/// driver point and every account observation, which is the record every run
605/// kept before this field existed and is O(run) in memory. A host that reads
606/// the whole run's events once it has ended asks for Full (or Commands).
607///
608/// Folded into the spec digest only when it is not Window, so a spec that
609/// leaves the default keeps the digest it had before the field existed.
610enum class NativeEventRetention : std::uint32_t {
612 Full = 1,
614};
615
616/// One complete setup value, staged/copied by NativeStrategyHost before it is
617/// applied at begin. This aggregate owns no host phase, consumed-run counter,
618/// parsed-calendar authority, physical account, or C transport presence mask.
619/// Empty required strings and zero capital/value/FX defaults make an incomplete
620/// value invalid; price_tick == 0 explicitly selects unquantized prices. No
621/// timezone/timeframe/instrument facts are inferred.
624 std::string input_tf;
625 std::string script_tf;
626 /// A public begin with fewer than two bars may not establish a timeframe.
627 /// This preserves that explicit state without inventing a clock literal.
629 /// Strict native hosts retain the canonical slot-label rule. A provider
630 /// feed may opt into raw, strictly-increasing caller labels. The declared
631 /// type spellings below are the ones the native C++ ABI guard pins.
635
636 std::string ticker;
637 std::string tickerid;
638 std::string type;
639 std::string currency;
640 std::string basecurrency;
641 std::string description;
642 std::string volumetype;
643
644 std::string timezone;
645 std::string session; // Empty and "24x7" are distinct all-day literals.
646 std::string chart_timezone; // Optional observation metadata; empty stays empty.
647
648 double initial_capital = 0.0;
649 double point_value = 0.0;
650 double account_fx = 0.0; // One positive scalar, not a timestamped FX series.
651 double price_tick = 0.0; // Finite, nonnegative; zero means unquantized prices.
652 std::uint32_t slippage_ticks = 0; // <= INT_MAX; raw +/- ticks*tick, no snap.
653 /// Opt-in instrument grid. None keeps unquantized prices; both quantizing
654 /// modes require price_tick > 0 and round before slippage.
658 double fee_value = 0.0; // A percent for Percent (0.1 charges 0.1 % of the
659 // absolute account notional), else cash per unit/execution.
660 std::optional<double> quantity_grid; // Positive; admission only, no resize.
663 std::optional<double> max_abs_units; // Positive resulting-book opening cap.
664 std::optional<std::uint64_t> max_open_lots; // Positive surviving+new lot cap.
666 std::optional<double> initial_margin_fraction; // Positive fraction, not percent;
667 // no maintenance liquidation.
668 /// Opt-in generic margin model. Mutually exclusive with
669 /// initial_margin_fraction, which remains the one-scalar spelling. Folded
670 /// into the continuation digest only when it is present, so a spec that
671 /// declares none keeps its pre-margin-model identity byte for byte.
672 std::optional<NativeMarginModel> margin;
673 /// Opt-in generic account risk limits. Absent is the whole default
674 /// surface: nothing is measured, no risk event is appended, and the
675 /// continuation digest is the pre-risk one. Folded into it only when the
676 /// block is present, exactly as `margin` is.
677 std::optional<NativeRiskLimits> risk;
679 /// Report a position still open at run end as a mark-to-market closed row
680 /// at the last close. KernelRecorded only, and reporting only: the live
681 /// book, the realized sums and every hash are left exactly as the run left
682 /// them. Inert under HostRecorded, whose host owns the whole report series.
684 /// Calculation timing. BarClose is the established cadence and the whole
685 /// default surface; the other triggers only add calculations, never move
686 /// or remove one. max_recalculations_per_point bounds the fill cascade at
687 /// one matching point: further executions at that point are still applied
688 /// and still delivered to on_native_applied, they just stop driving a new
689 /// calculation. Zero is a legal bound and means "deliver, never
690 /// recalculate". All three fold into the continuation hash only once the
691 /// trigger or the open-bar view is non-default, so a spec that leaves the
692 /// cadence alone keeps the continuation identity it had before these
693 /// fields existed.
698 /// Declared higher-timeframe series. Empty is the whole default surface:
699 /// no evaluator is registered, no feed is prepared, and the run spec's
700 /// continuation digest is the pre-subscription one.
701 std::vector<NativeTimeframeSubscription> subscriptions;
702 /// Opt-in auxiliary finer feed. Absent is the whole default surface:
703 /// nothing is stored, nothing is routed, and the continuation digest is the
704 /// pre-feed one. Folded into it only when the block is present, exactly as
705 /// `margin` and `risk` are.
706 std::optional<NativeAuxiliaryFeed> auxiliary_feed;
707 /// Opt-in feeds of other instruments. Empty is the whole default surface:
708 /// nothing is stored, nothing is delivered, and the continuation digest is
709 /// the pre-feed one. Folded into it only when there are any, exactly as
710 /// `auxiliary_feed` is. See NativeInstrumentFeed.
711 std::vector<NativeInstrumentFeed> instrument_feeds;
712 /// What native_events() can still return; see NativeEventRetention.
713 /// Folded into the spec digest only when it is not Window.
715 /// Opt-in quantity tolerance, in units (finite, positive): two quantities
716 /// within it of each other are one quantity to the settlement. A close
717 /// that comes within it of a FIFO boundary -- the binary64 sum of the
718 /// lots through one of them -- ends at that boundary, charged its request,
719 /// and a lot of at most this size that binary64 cannot take off a close's
720 /// rest closes whole with the close that reaches it (docs/native-
721 /// settlement.md, "Quantity tolerance"). Absent is the whole default
722 /// surface: the settlement stays exact, a quantity it cannot book exactly
723 /// is that request's MatchRejectReason::UnrepresentableQuantity, and the
724 /// continuation digest is the pre-tolerance one; the value folds only
725 /// when present (R5 lane K-ULP4).
726 std::optional<double> quantity_tolerance;
727};
728
729/// Which field a validation refused, in deterministic first-error order. It is
730/// what lets a host report "which field" instead of "invalid"; every enumerator
731/// names a NativeRunSpec member or one of its nested blocks.
760
761/// Why a field was refused. Read it beside NativeRunSpecValidation::field: the
762/// error says what is wrong and the field says where. Each feature's own suite
763/// pins the refusals of the fields it owns — the subscription and auxiliary-feed
764/// rows in tests/test_native_auxiliary_feed.cpp, the margin rows in
765/// tests/test_native_margin_model.cpp, the risk rows in
766/// tests/test_native_risk_limits.cpp.
767enum class NativeRunSpecError : std::uint8_t {
797 // A declared higher-timeframe series whose literal does not parse, or
798 // whose pairing with input_tf is not one script_tf would accept.
800 // A declared series strictly finer than input_tf. Lower-timeframe arrays
801 // are a separate contract; this is never silently promoted.
803 // Two declared series of the same period whose `authoritative_bars`
804 // differ. Same-period series are otherwise independent instances, but the
805 // feed store is keyed by the timeframe's duration, so they could not own
806 // their own conflicting bars. Declaring the same bars twice, or leaving
807 // one (or both) empty, is accepted.
809 // Authoritative bars that are not strictly increasing in time.
811 // Subscriptions declared with no detected input timeframe to pair with.
813 // Both the generic margin model and the one-scalar initial-margin gate
814 // were set. They are two spellings of the same admission authority.
818 // A margin equity basis / liquidation level base outside its enumeration.
821 // A calculation trigger / open-bar view outside its enumeration.
824 // A risk day basis / breach action outside its enumeration.
827 // A declared risk count of zero. A limit of "no losing day at all" or
828 // "no fill at all" is a blocked run, never a threshold, so it is named
829 // rather than silently enforced.
831 // A margin-model side that declares neither an opening requirement
832 // (`initial_*` is zero) nor a liquidation one (`maintenance_*` is unset).
833 // The two are separate broker functions and a side may waive either, but
834 // waiving both states nothing at all, so it is named rather than read as
835 // "unlimited leverage, never liquidated".
837 // An auxiliary feed whose literal does not parse, or whose pairing under
838 // input_tf is not one an input would accept under a script_tf.
840 // An auxiliary feed that is not strictly finer than input_tf. The input
841 // already carries its own timeframe and everything coarser.
843 // Auxiliary bars that are not strictly increasing in time, or that carry
844 // a non-finite price or volume.
847 // An auxiliary feed declared with no detected input timeframe to be
848 // finer than.
850 // A series source outside its enumeration.
852 // A series built from the auxiliary feed in a spec that declares none.
854 // A series built from the auxiliary feed and strictly finer than it.
856 // Not a field at all: the CALL was refused before any field was judged,
857 // because the host was not in the phase that call is legal in. Only a
858 // setup call answers it -- validate_native_run_spec and the two
859 // validate_native_* functions below judge a value, never a phase, and
860 // never report it. NativeFxCurveError::WrongPhase is the same word for
861 // the same reason on the curve. Read with NativeRunSpecField::None: there
862 // is no "where", only a "when".
864 // An event retention outside its enumeration.
866 // A cooperative abort left no run specification to reuse. This is a
867 // lifecycle refusal, not a calendar validation failure, so it names no
868 // field.
870 // A reused host may not change its bound session identity.
872 // A reused host may only consume a run number above its high-water mark.
874 // An instrument feed whose timeframe literal does not parse.
876 // Two instrument feeds with one (instrument, timeframe) key.
878 // Instrument bars that are not strictly increasing in time.
880 // An instrument bar with a non-finite price, or a volume that is neither
881 // NaN nor finite and nonnegative.
883 // close_ms of another length than the bars, or a close at or before its
884 // own bar's open, or after the next bar's open.
886 // A column with an empty or repeated name, or another length than the bars.
888 // An InstrumentFeed series naming an (instrument, tf) no feed installs.
890 // An InstrumentFeed series declaring authoritative bars: its bars are the
891 // feed's own, so a second copy states nothing.
893 // An instrument key on a series whose source is not InstrumentFeed.
895};
896
897/// Allocation-free facts suitable for the host's durable failure variant.
898/// Render text at the presentation boundary, never store exception.what().
902
903 constexpr bool ok() const noexcept { return error == NativeRunSpecError::None; }
904 constexpr explicit operator bool() const noexcept { return ok(); }
905};
906
907/// Complete validation, with deterministic first-error field order. Every
908/// string is semantic UTF-8 without embedded NUL (all cross C-string v1).
909/// Required: identity key, tickerid, scheduling timezone, and both timeframe
910/// literals unless timeframe_undetected is explicitly set.
911/// Empty chart timezone is preserved as optional observation metadata.
912/// Calendar parsing/compatibility remain in native_calendar. Batch monthly
913/// pairings are accepted here; stream-only restrictions belong to begin.
914/// Calendar parsing may allocate. Allocation/other dependency exceptions are
915/// converted into typed failure facts; the supplied spec is never changed.
917
918/// Validate the WHOLE value first, then canonicalize its admitted numeric
919/// negative zero (fee_value) to positive zero. Failure preserves every input
920/// bit/string/optional. Positive-only fields cannot admit either zero sign;
921/// price_tick admits and preserves both zero signs, and absent optionals have no
922/// payload. Literal strings/numbers are never otherwise rewritten. There is no
923/// second validated/live configuration wrapper.
924/// Host usage: copy input into a candidate, normalize candidate, then stage
925/// that same spec atomically; own copy-allocation/lifecycle failure handling.
927
928/// Exactly the part of validate_native_run_spec that judges declared
929/// higher-timeframe series, against a stated input timeframe: the pairing rule
930/// (as script_tf pairs, never strictly finer), the literals, the order of any
931/// authoritative bars, the one conflicting-feed refusal, and the
932/// undetected-timeframe rule. A host that declares its series at begin
933/// (NativeStrategyHost::declare_timeframe_subscriptions) is judged by this same
934/// function, so a list accepted there is one configure_native would also have
935/// accepted. Calendar parsing may allocate; failures are converted into typed
936/// facts and nothing is changed.
938 const std::vector<NativeTimeframeSubscription>& subscriptions,
939 const std::string& input_tf, bool timeframe_undetected) noexcept;
940
941/// The same judgement for a run that declares an auxiliary feed: the feed is
942/// judged first (validate_native_auxiliary_feed), then every series, a series
943/// built from the feed pairing with the FEED's timeframe. The three-argument
944/// form above is this one with no feed, where such a series is refused as
945/// SubscriptionWithoutAuxiliaryFeed.
947 const std::vector<NativeTimeframeSubscription>& subscriptions,
948 const std::string& input_tf, bool timeframe_undetected,
949 const std::optional<NativeAuxiliaryFeed>& auxiliary_feed) noexcept;
950
951/// The same judgement for a run that also installs instrument feeds: the
952/// feeds are judged first (validate_native_instrument_feeds), then the
953/// auxiliary feed and every series, an InstrumentFeed series naming one of
954/// these feeds. The four-argument form above is this one with no instrument
955/// feed, where such a series is refused as SubscriptionWithoutInstrumentFeed.
957 const std::vector<NativeTimeframeSubscription>& subscriptions,
958 const std::string& input_tf, bool timeframe_undetected,
959 const std::optional<NativeAuxiliaryFeed>& auxiliary_feed,
960 const std::vector<NativeInstrumentFeed>& instrument_feeds) noexcept;
961
962/// Exactly the part of validate_native_run_spec that judges the instrument
963/// feeds: each key's strings and timeframe literal, the one-feed-per-key
964/// rule, and each feed's bars, closes and columns. Empty is always valid.
966 const std::vector<NativeInstrumentFeed>& instrument_feeds) noexcept;
967
968/// Exactly the part of validate_native_run_spec that judges the auxiliary
969/// feed, against a stated input timeframe: the literal, the strictly-finer
970/// pairing under the input, the order and structure of its bars, and the
971/// undetected-timeframe rule. An absent feed is always valid. A host that
972/// declares its feed at begin (NativeStrategyHost::declare_auxiliary_feed) is
973/// judged by this same function.
975 const std::optional<NativeAuxiliaryFeed>& auxiliary_feed,
976 const std::string& input_tf, bool timeframe_undetected) noexcept;
977
978/// Exact FNV-1a content digest for a retained intrabar path. It includes the
979/// mode, lower bars in caller order when present, and every sampling parameter,
980/// so continuation identity cannot silently reuse a path from another begin.
981std::uint64_t native_intrabar_path_digest(const IntrabarPath& path) noexcept;
982
983/// Exact FNV-1a content digest for the declared higher-timeframe series. It
984/// includes each subscription's timeframe literal, publication modes and
985/// authoritative bars in caller order, so a continuation cannot silently reuse
986/// another begin's series. Callers fold it only when `subscriptions` is
987/// non-empty, keeping the default spec's continuation identity unchanged, and
988/// `gaps` folds only where a series set it, keeping every series declared
989/// before that field existed at the digest it already had. `source` folds the
990/// same way: only where a series left the input.
992 const std::vector<NativeTimeframeSubscription>& subscriptions) noexcept;
993
994/// Exact FNV-1a content digest for the declared auxiliary feed: its timeframe
995/// literal and every bar in caller order, so a continuation cannot silently
996/// reuse another begin's feed. Callers fold it only when `auxiliary_feed` is
997/// present, keeping the default spec's continuation identity unchanged.
998std::uint64_t native_auxiliary_feed_digest(const NativeAuxiliaryFeed& feed) noexcept;
999
1000/// Exact FNV-1a content digest for the installed instrument feeds: each key,
1001/// every bar, close and column value in caller order, so a continuation
1002/// cannot silently reuse another begin's feeds. Callers fold it only when
1003/// `instrument_feeds` is non-empty, keeping the default spec's continuation
1004/// identity unchanged.
1006 const std::vector<NativeInstrumentFeed>& instrument_feeds) noexcept;
1007
1008/// Machine-independent digest of a run spec: exactly the fields the consumer
1009/// folds into the continuation identity for the spec, and nothing else — no
1010/// timezone resources, no session identity beyond the spec's own. Two specs
1011/// with equal digests drive identical continuation identities on every machine
1012/// with the same tz resources.
1013///
1014/// It is the consumer's own spec fold (`hash_spec`) over a freshly seeded
1015/// accumulator: the same FNV-1a offset basis `continuation_hash()` starts from,
1016/// with nothing folded before the spec (no semantic-version markers, no state)
1017/// and the relative-generation base set to `spec.identity.run_number`, exactly
1018/// as the consumer seeds it for the run this spec describes. The folded
1019/// generation distance is therefore zero and the digest is a property of the
1020/// spec value alone. It is NOT a continuation hash and never comparable with
1021/// one: a raw continuation hash also folds the resolved timezone identity,
1022/// which since R5 lane E23 is the zone's content rather than its paths — the
1023/// same value on every machine carrying the same tzdata release, a different
1024/// one after a tzdata update that rewrites the zone's rules. Only this digest
1025/// is independent of the installed zone data, so only this one is pinnable as
1026/// a source constant.
1027std::uint64_t native_run_spec_digest(const NativeRunSpec& spec) noexcept;
1028
1029/// Exact FNV-1a content digest for the generic margin model. It includes every
1030/// per-side fraction, the liquidation policy and its parameters, so two runs
1031/// that differ only in their margin model cannot share a continuation identity.
1032/// Callers fold it only when `margin` is present, keeping the default spec's
1033/// continuation identity unchanged.
1034std::uint64_t native_margin_model_digest(const NativeMarginModel& margin) noexcept;
1035
1036/// Exact FNV-1a content digest for the generic risk limits. It includes every
1037/// threshold, its percent flag, the day basis and the breach action, so two
1038/// runs that differ only in their risk limits cannot share a continuation
1039/// identity. Callers fold it only when `risk` is present, keeping the default
1040/// spec's continuation identity unchanged.
1041std::uint64_t native_risk_limits_digest(const NativeRiskLimits& risk) noexcept;
1042
1043static_assert(std::is_trivially_copyable_v<NativeRunSpecValidation>);
1044static_assert(std::is_nothrow_move_constructible_v<NativeRunSpec>);
1045static_assert(std::is_nothrow_move_assignable_v<NativeRunSpec>);
1046
1047} // inline namespace native_run_spec_v3
1048} // namespace pineforge
NativeRunSpecError
Why a field was refused.
NativeLiquidationSizing
Which units a kernel-issued liquidation reduces (L4).
NativeRunSpecValidation validate_native_timeframe_subscriptions(const std::vector< NativeTimeframeSubscription > &subscriptions, const std::string &input_tf, bool timeframe_undetected) noexcept
Exactly the part of validate_native_run_spec that judges declared higher-timeframe series,...
NativeCalculationTrigger
When the kernel asks the host to calculate.
NativeRiskDay
Which day a risk limit's "day" is.
NativeSeriesSource
One declared higher-timeframe series of the run's own symbol, the native equivalent of request....
NativeRunSpecValidation validate_native_auxiliary_feed(const std::optional< NativeAuxiliaryFeed > &auxiliary_feed, const std::string &input_tf, bool timeframe_undetected) noexcept
Exactly the part of validate_native_run_spec that judges the auxiliary feed, against a stated input t...
constexpr bool native_feed_tolerance_enabled(NativeFeedTolerance enabled, NativeFeedTolerance requested) noexcept
Whether one NativeFeedTolerance bit is set in a run's mask.
std::uint64_t native_instrument_feeds_digest(const std::vector< NativeInstrumentFeed > &instrument_feeds) noexcept
Exact FNV-1a content digest for the installed instrument feeds: each key, every bar,...
NativeRunSpecValidation normalize_native_run_spec(NativeRunSpec &spec) noexcept
Validate the WHOLE value first, then canonicalize its admitted numeric negative zero (fee_value) to p...
NativeReportPolicy
Who records the per-script-bar report series.
NativeCloseExecution
When a request born at a script calculation may first match.
NativeLiquidationLevelBase
Which base the liquidation level is solved from.
constexpr bool native_legacy_tolerance_enabled(NativeFeedTolerance enabled, NativeFeedTolerance requested) noexcept
Deprecated spelling of native_feed_tolerance_enabled.
NativeEventRetention
What the kernel keeps of a run's event record, the rows native_events() returns (R5 lane V19-B).
NativeOpenBarView
What a bar-open callback is handed.
NativeRunSpecValidation validate_native_instrument_feeds(const std::vector< NativeInstrumentFeed > &instrument_feeds) noexcept
Exactly the part of validate_native_run_spec that judges the instrument feeds: each key's strings and...
std::uint64_t native_margin_model_digest(const NativeMarginModel &margin) noexcept
Exact FNV-1a content digest for the generic margin model.
NativePriceGrid
A generic instrument price grid.
NativePathOrder
Generic ordering for a modeled OHLC path.
NativeSlotLabelPolicy
Native hosts normally require every confirmed bar to name a canonical input slot.
@ LegacyTolerant
Deprecated spelling of FeedTolerant; identical value, kept so existing hosts and the source adapter c...
NativeOpenDirections
Which opening directions the run admits at all.
NativeAbortReporting
Abort presentation is a run-level policy rather than an exception-path convention.
NativeFeedTolerance
Explicit, opt-in admission exceptions for a tolerated input-feed shape.
@ WarmupNonNegativeOHLC
Source-compatible stream warmups admit finite, non-negative interim OHLC values.
@ BatchStructuralBars
Match the batch structural check: finite OHLC values need not be positive, and NaN volume means unava...
std::uint64_t native_risk_limits_digest(const NativeRiskLimits &risk) noexcept
Exact FNV-1a content digest for the generic risk limits.
std::uint64_t native_run_spec_digest(const NativeRunSpec &spec) noexcept
Machine-independent digest of a run spec: exactly the fields the consumer folds into the continuation...
NativeFeedTolerance NativeLegacyTolerance
Deprecated spelling of the tolerance type.
std::uint64_t native_intrabar_path_digest(const IntrabarPath &path) noexcept
Exact FNV-1a content digest for a retained intrabar path.
NativeFeeKind
Encodings coincide with the versioned native-v1 C transport.
std::uint64_t native_timeframe_subscriptions_digest(const std::vector< NativeTimeframeSubscription > &subscriptions) noexcept
Exact FNV-1a content digest for the declared higher-timeframe series.
NativeLiquidationCheck
When the kernel tests the maintenance requirement.
NativeRunSpecField
Which field a validation refused, in deterministic first-error order.
std::uint64_t native_auxiliary_feed_digest(const NativeAuxiliaryFeed &feed) noexcept
Exact FNV-1a content digest for the declared auxiliary feed: its timeframe literal and every bar in c...
NativeGridRounding
HalfUp is the nearest tick with ties away from zero.
NativeMarginEquityBasis
Which equity the maintenance requirement is tested against.
NativeRunSpecValidation validate_native_run_spec(const NativeRunSpec &spec) noexcept
Complete validation, with deterministic first-error field order.
MagnifierDistribution
Definition magnifier.hpp:7
A RETAINED finer feed: the host's own lower-timeframe bars, the literal they are at,...
No intrabar path: the confirmed OHLC waypoints are the whole modeled walk, and on_native_sub_bar neve...
A synthesized path has no retained lower feed.
An owned lower-timeframe execution path.
const lower_tf * lower() const noexcept
The retained finer feed, or nullptr when this is not the lower_tf alternative.
@ ContinuousSegments
Native hosts retain continuous matching between generated samples unless they explicitly request poin...
bool is_none() const noexcept
Whether this run declares no intrabar path at all.
std::variant< none, lower_tf, synthesized > value_type
The three alternatives as one value.
synthesized * synthesized_path() noexcept
The mutable synthesized sampling policy, or nullptr.
lower_tf * lower() noexcept
The mutable retained finer feed, or nullptr; a staged spec's feed is copied by configure_native,...
const synthesized * synthesized_path() const noexcept
The synthesized sampling policy, or nullptr when this is not that alternative.
An auxiliary feed of the run's OWN symbol at a timeframe strictly finer than the input: the bars the ...
One named column an instrument feed carries beside its OHLCV: one value per bar, in bar order.
Another instrument's own bars of one timeframe, installed as data.
std::vector< NativeInstrumentColumn > columns
A generic per-side broker margin model (L4).
Generic account risk limits (L9), entirely opt-in: a spec that leaves NativeRunSpec::risk unset evalu...
std::optional< NativeLossLimit > max_intraday_loss
std::optional< std::uint32_t > max_consecutive_loss_days
std::optional< NativeLossLimit > max_drawdown
Allocation-free facts suitable for the host's durable failure variant.
One complete setup value, staged/copied by NativeStrategyHost before it is applied at begin.
NativeSlotLabelPolicy slot_label_policy
Strict native hosts retain the canonical slot-label rule.
NativeCalculationTrigger calculation
Calculation timing.
std::vector< NativeTimeframeSubscription > subscriptions
Declared higher-timeframe series.
std::optional< NativeRiskLimits > risk
Opt-in generic account risk limits.
std::vector< NativeInstrumentFeed > instrument_feeds
Opt-in feeds of other instruments.
std::optional< NativeMarginModel > margin
Opt-in generic margin model.
std::optional< std::uint64_t > max_open_lots
bool timeframe_undetected
A public begin with fewer than two bars may not establish a timeframe.
std::optional< double > quantity_tolerance
Opt-in quantity tolerance, in units (finite, positive): two quantities within it of each other are on...
NativePriceGrid price_grid
Opt-in instrument grid.
std::optional< NativeAuxiliaryFeed > auxiliary_feed
Opt-in auxiliary finer feed.
NativeEventRetention event_retention
What native_events() can still return; see NativeEventRetention.
bool report_open_position_at_end
Report a position still open at run end as a mark-to-market closed row at the last close.
std::string instrument
InstrumentFeed only: the instrument key of the feed the series reads, compared byte for byte with Nat...