PineForge v0.13.1-379-g9b50973
Deterministic PineScript v6 backtest runtime — C ABI reference
Loading...
Searching...
No Matches
pine_strategy_host.hpp
Go to the documentation of this file.
1#pragma once
2
7
8#include <cstdint>
9#include <limits>
10#include <map>
11#include <string>
12#include <vector>
13
14// Generated strategies require this switched source-host surface. There is
15// intentionally no compatibility execution path behind the capability gate.
16#define PINEFORGE_HAS_EXPLICIT_PINE_CAP_V1 1
17#define PINEFORGE_HAS_EXPLICIT_PINE_EXECUTION_ADAPTER_V1 1
18#define PINEFORGE_HAS_NATIVE_LOWERING_V1 1
19
20namespace pineforge::source {
21
22// Hash domain of the per-site request.security semantics below. Folded into
23// the source extension only when a site is registered, so a run without
24// request.security hashes exactly as before; bumped whenever the folded
25// field set changes.
26inline constexpr char kSourceSecurityDomain[] = "pineforge-source-security/v6";
27
28// One projected higher-timeframe bucket of the opt-in historical
29// request.security lookahead projection (PineSecurityEvalState below).
32 // The instant the projection is dispatched on: the first retained
33 // chart child's timestamp on the single-feed path, that child's first
34 // auxiliary bar on the split-feed path (the requested-context
35 // evaluator is fed the finer slice there). Keyed by instant, not by
36 // feed-call index, so both paths consume one projection per bucket.
37 int64_t first_child_ms = 0;
38 bool is_complete = false;
39};
40
41// One auxiliary bar a first-bucket-latched site holds back until the chart
42// body has run (PineSecurityEvalState::deferred_aux), with the cursor values
43// it would have been fed under.
46 int64_t next_input_ms = 0;
48};
49
50// Pine's publication semantics for ONE request.security site, kept beside the
51// kernel's generic evaluator state of the same sec_id
52// (BacktestEngine::SecurityEvalState: aggregator, current bar, feed counts,
53// native feed routing). The kernel evaluator knows none of these rules; this
54// host composes them around the kernel's aggregation, substitution and
55// dispatch primitives (src/source/pine_security_eval.cpp).
57 // barmerge.lookahead_on: the site reads the in-progress requested bucket.
58 // Every input peeks the running aggregate (a partial evaluation), a
59 // completion rewrites the slot its first peek opened instead of opening a
60 // second one, and generated TA sites take their recompute path on every
61 // sub-bar after a bucket's first (security_series_slot_is_new).
62 bool lookahead_on = false;
63 // barmerge.gaps_on: the site reads na on every input that completes no
64 // requested bucket (clear_security on the generated series).
65 bool gaps_on = false;
66 // Plain ``request.security`` (not ``_lower_tf``) with a requested TF
67 // STRICTLY FINER than script_tf (e.g. so2TF="5" read from a 15m
68 // chart) under ``lookahead=barmerge.lookahead_ON``: the security's
69 // own aggregator completes multiple times
70 // (script_seconds / requested_seconds) per calling/script bar. A
71 // history-offset read (``expr[1]`` inside the security call, see
72 // the ``*_hist`` push/read machinery in codegen) is meant to expose
73 // "the value already confirmed as of the close of the PREVIOUS
74 // calling bar" — TV's lookahead_on merge takes the FIRST intrabar
75 // of each calling bar, so the publish granularity is the CALLING
76 // bar, not the security's own (finer) period. Without this, the
77 // read-before-push ``hist[0]`` gets refreshed on every one of the
78 // R completions inside the current calling bar, so by the time
79 // on_bar() reads it the value has silently drifted to "one
80 // security-period behind the LAST completion of THIS SAME calling
81 // bar" (e.g. the middle of 3 sub-periods) instead of "the last
82 // completion of the PREVIOUS calling bar" — an aliasing bug
83 // confirmed against TradingView-exported trades on a triple-RSI
84 // DCA strategy using so2Rsi = request.security(sym, "5",
85 // ta.rsi(close,7)[1], lookahead=barmerge.lookahead_on) on a 15m
86 // chart (finer target under lookahead + offset).
87 //
88 // ``lookahead_OFF`` is deliberately NOT gated (field stays 0): TV's
89 // lookahead_off merge takes the LAST intrabar of the calling bar,
90 // so the exposed value — and any ``[k]`` history offset off it —
91 // advances at the security's own finer cadence (one hist.push per
92 // completed security period), which is exactly the ungated
93 // behavior. Gating lookahead_off regressed
94 // masayanfx-multi-time-score-strategy
95 // (request.security(sym, "5", ta.highest(high, 20)[1],
96 // barmerge.gaps_off, barmerge.lookahead_off) on a 15m chart) from
97 // 100.0% to 93.7% trade parity vs TradingView.
98 //
99 // When nonzero, this holds the requested TF's duration in seconds
100 // (script_seconds % this == 0 verified at validate time) and gates
101 // ``pine_feed_security_eval_state``'s aggregator branch: only the
102 // completion whose bucket END aligns to a script_tf boundary is
103 // passed through to ``evaluate_security`` as ``is_complete = true``
104 // (letting codegen's ``hist.push()`` fire); all other completions
105 // within the same calling bar are still evaluated (so the
106 // underlying TA state keeps advancing at native/security
107 // resolution) but are passed ``is_complete = false`` so they do not
108 // advance the exposed history buffer. Zero (the default) means "not
109 // applicable" (target TF coarser than or equal to script_tf, or
110 // lookahead_off — the already-correct cases) and leaves behavior
111 // unchanged.
113 // Plain ``request.security`` with a requested TF strictly finer than
114 // script_tf, served by the auxiliary finer feed (the split-feed
115 // path), under ``lookahead_off``: TradingView surfaces the LAST
116 // intrabar of the calling chart bar at that bar's close whatever
117 // the bucket's sub-bar count -- on the OANDA:XAUUSD 1D chart the
118 // Thanksgiving 2025-11-26 bar's last 3m bucket (21:57Z, holding
119 // the 21:59Z minute alone before the 22:00Z session close) is the
120 // value ``request.security(tickerid, "3", ta.rsi(close, 14))``
121 // reads at the daily close (72.64, lab tv dca-ltf-last-intrabar,
122 // 2026-09-05), where the aggregator's count / real-end /
123 // session-close rules leave that bucket partial until the next
124 // chart bar's first sub-bar and the close read the 21:54Z bucket
125 // (38.87). When set, pine_feed_security_eval_state
126 // finalizes and publishes the pending partial bucket on the
127 // calling bar's last auxiliary bar (TimeframeAggregator::
128 // complete_pending_partial), once: the next chart bar's first
129 // sub-bar resets the bucket without re-emitting it. Dense feeds
130 // whose final bucket completes on its count are untouched (no
131 // partial is pending), and so are lanes without the auxiliary
132 // slice, lookahead_on (its gated publication is untouched: on this
133 // shape it stays one bucket behind, as before -- the tape pins
134 // lookahead_off only), lower-TF arrays and calendar / same-TF
135 // requests. False (the default) means "not applicable".
137 // Heikin-Ashi same-symbol read: request.security(ticker.heikinashi(
138 // syminfo.tickerid), ...). When set, the completed (aggregated) bar's
139 // OHLC is replaced by its Heikin-Ashi candle before the security
140 // expression is evaluated, so close/open/high/low inside the call see
141 // HA values. HA is stateful (ha_open depends on the prior HA bar), so
142 // the running state lives here per sec_id.
143 bool heikinashi = false;
144 double ha_prev_open = 0.0;
145 double ha_prev_close = 0.0;
146 bool ha_seeded = false;
147 // One entry per projected HTF bucket, populated only for an explicitly
148 // opted-in finite historical batch. Empty for every default/streaming
149 // run and for sites outside the narrow HTF lookahead_on+gaps_off
150 // contract. The feed index advances once per retained input bar (bars
151 // before an opt-in security range start are dropped by both producer
152 // and consumer); the projection cursor advances only at the next
153 // bucket's first child.
154 std::vector<HistoricalSecurityProjection> historical_projections;
156 // Which projection (cursor) has already been dispatched: every later
157 // input of the same bucket is a no-op for the evaluator.
159 // request.security_lower_tf emulation: a requested TF finer than the
160 // evaluator input is synthesized from each input bar's OHLC path
161 // (``lower_tf_emulation``: ``lower_tf_ratio`` sub-bars of
162 // ``lower_tf_seconds``); ``lower_tf_requested`` marks a site on either
163 // lower-timeframe path.
164 bool lower_tf_requested = false;
165 bool lower_tf_emulation = false;
168 // ``request.security_lower_tf`` returns one element per
169 // synthesised sub-bar of the current chart bar, so the codegen
170 // needs to know which sub-bar inside the current chart bar is
171 // currently being processed by the per-sec_id evaluator method.
172 // ``lower_tf_array_requested`` is set by
173 // ``register_security_lower_tf_eval`` and forces an extra
174 // lower-TF-emulation validity check in
175 // ``validate_security_timeframes``. ``lower_tf_sub_bar_index``
176 // is reset to 0 at the start of every
177 // ``pine_feed_security_eval_state`` invocation in lower-TF
178 // emulation mode and incremented after each per-sub-bar
179 // dispatch so the codegen can clear its accumulator on index
180 // 0 and then push for every subsequent sub-bar.
183 // ``lower_tf_use_input`` selects the input-passthrough LTF path:
184 // when the requested TF is >= input_tf and < script_tf we hand
185 // the per-script-bar window of real input bars to the codegen
186 // (optionally roll-up aggregated when req > input). Mutually
187 // exclusive with ``lower_tf_emulation`` (synthesis) — only one
188 // is set per state. ``lower_tf_input_aggregation_ratio`` is
189 // ``req_seconds / input_seconds`` (>=1; 1 means raw passthrough,
190 // N means N raw input bars roll up into one returned LTF bar).
191 // ``lower_tf_input_buffer`` accumulates raw input bars within
192 // the current script-TF chunk and is flushed at chunk
193 // completion (or at end of feed for trailing partial chunks).
194 bool lower_tf_use_input = false;
196 std::vector<Bar> lower_tf_input_buffer;
197 // Plain ``request.security`` with a requested TF strictly finer than
198 // script_tf, served by the auxiliary finer feed (the split-feed
199 // path), under ``lookahead_on``: TradingView's merge takes the FIRST
200 // intrabar of the calling chart bar and holds it for the bar -- on
201 // the BINANCE:BTCUSDT 1D chart ``request.security(tickerid, "15",
202 // ta.rsi(close, 14)[1], lookahead_on)`` reads, on every daily bar,
203 // the 15m RSI of the previous day's LAST bucket, i.e. ``rsi[1]``
204 // evaluated on the day's first 15m bucket, na on the range's first
205 // bar (lab tv notrade-ltf-sample-btc1d, 2025-04-01..20, 18/18,
206 // 2026-09-05), so a plain ``expr`` reads the day's first bucket and
207 // ``expr[k]`` the k-th bucket before it, at the requested cadence.
208 // The legacy gate above (publish_gate_tf_seconds) publishes one
209 // bucket per calling bar -- the LAST one -- which reads right for
210 // ``expr[1]`` alone and one bucket late for ``expr``. When set, the
211 // evaluator publishes EVERY completed requested bucket (the exposed
212 // history advances per bucket, as under lookahead_off), and
213 // feed_aux_security_for_chart_bar feeds the calling bar's auxiliary
214 // bars only up to the one completing its FIRST bucket before the
215 // chart body runs; the rest of the slice is held in ``deferred_aux``
216 // and fed by feed_deferred_aux_security_for_chart_bar right after
217 // dispatch_bar, so the body reads the first-bucket evaluation while
218 // the TA state still sees every sub-bar, in order, before the next
219 // chart bar. publish_gate_tf_seconds stays 0 on this path; lanes
220 // without the auxiliary slice keep the gate. False (the default)
221 // means "not applicable".
223 // Per calling chart bar: whether this state's first bucket of the
224 // slice has been published (the deferral point), and the auxiliary
225 // bars held back until after the chart body, each with the
226 // security_next_input_ms_ / calling_bar_complete it was fed with.
228 // The label (bucket open) of the slice's first requested bucket:
229 // a completion published by this slice's first auxiliary bars that
230 // carries an OLDER label is the boundary emission of the previous
231 // slice's still-pending bucket (a tail the count / real-end /
232 // session-close rules left partial), not this bar's first bucket.
233 int64_t slice_open_label = 0;
234 // The label of the latest completed bucket this evaluator published
235 // through its aggregator (pine_feed_security_eval_state), whatever the
236 // state's current bucket is afterwards.
238 std::vector<DeferredAuxBar> deferred_aux;
239};
240
242public:
243 explicit PineStrategyHost(
245
246 std::uint64_t broker_state_hash_projection() const override;
247
248 void prepare_native_begin(const NativeBeginArgs&) final;
249 void on_native_run_begin() final;
250 void on_native_input(const Bar&, const NativeInputContext&) final;
251 void on_native_tick(const Bar&, const NativeTickContext&) final;
252 void on_native_bar_open(const Bar&, const NativeDecisionContext&) final;
253 void on_native_bar(const Bar&, const NativeDecisionContext&) final;
254 // R6: every calculation of the run arrives here. BarClose forwards to
255 // on_native_bar exactly as the kernel's default does; OrderFill is
256 // calc_on_order_fills, which the consumer now schedules (spec
257 // NativeCalculationTrigger::BarCloseAndFills) and the source layer only
258 // executes — language-state rollback, publication, first-open chain.
263 const NativeDecisionContext&) final;
265 const NativeExecutionTermsFacts&) const final;
267 const NativePrecommitView&) const final;
268 // R5: the kernel owns the margin mechanism; these three answer with the
269 // TradingView policy the adapter holds.
270 bool margin_check_allowed(const NativeMarginCheckPoint&) const final;
271 std::optional<NativeMarginDecision> resolve_margin_requirement(
272 const NativeMarginRequirementView&) const final;
273 std::optional<double> resolve_margin_call_units(
274 const NativeMarginCallView&) const final;
275 // R5 lane R4d: the kernel arms a relative strategy.exit leg at its
276 // parent's fill; TradingView's projection of that level is the adapter's.
277 std::optional<double> resolve_anchored_level(
278 const NativeAnchoredLevelView&) const final;
279 // RULING A48: the source host owns per-lot excursion accounting (MFE/MAE)
280 // on the switched route. It samples every completed source bar's H/L/C
281 // with the owner's entry-bar masks, and supplies the closing row's two
282 // magnitudes through the kernel's single generic capability.
283 bool owns_lot_excursions() const noexcept final { return true; }
285 const ClosedLotExcursionFacts&) const final;
286 // The R5 R2 sizing classification of the adapter, for tests and for hosts
287 // that need to know whether the core or the source owns a default
288 // quantity at this point.
289 bool adapter_core_sizes_default_opening(bool is_long) const;
290
291 virtual void on_source_bar(const Bar&) = 0;
294 // A stream's historical warmup remains a configuration window until the
295 // first realtime input. The legacy stream path permits the feed to
296 // finalize its session template in that window (the sparse-boundary
297 // request.security probe relies on it); retain that source-host surface
298 // while native staged ingress remains refused once realtime starts.
299 void set_syminfo_session(const std::string&);
300 void set_pine_risk_direction(int);
302 void set_pine_risk_max_drawdown(double, bool);
303 void set_pine_risk_max_intraday_loss(double, bool);
306
307 void strategy_entry(const std::string& id, bool is_long,
308 double limit_price = std::numeric_limits<double>::quiet_NaN(),
309 double stop_price = std::numeric_limits<double>::quiet_NaN(),
310 double qty = std::numeric_limits<double>::quiet_NaN(),
311 const std::string& comment = {},
312 const std::string& oca_name = {}, int oca_type = 0,
313 int qty_type = -1);
314 void strategy_close(const std::string& id, const std::string& comment = {},
315 double qty = std::numeric_limits<double>::quiet_NaN(),
316 double qty_percent = std::numeric_limits<double>::quiet_NaN(),
317 bool immediately = false);
318 void strategy_close(const std::string& id, const std::string& comment,
319 double qty, double qty_percent, bool immediately,
320 std::uint64_t callsite_token);
321 void strategy_close_all();
322 void strategy_exit(const std::string& id, const std::string& from_entry,
323 double limit_price, double stop_price,
324 double trail_points = std::numeric_limits<double>::quiet_NaN(),
325 double trail_offset = std::numeric_limits<double>::quiet_NaN(),
326 double trail_price = std::numeric_limits<double>::quiet_NaN(),
327 double qty_percent = 100.0, const std::string& comment = {},
328 double qty = std::numeric_limits<double>::quiet_NaN(),
329 const std::string& oca_name = {},
330 double profit_ticks = std::numeric_limits<double>::quiet_NaN(),
331 double loss_ticks = std::numeric_limits<double>::quiet_NaN());
332 void strategy_exit_cancel_bracket(const std::string& exit_id,
333 const std::string& from_entry,
334 const std::string& comment = {});
335 void strategy_cancel(const std::string& id);
336 void strategy_cancel_all();
337 void strategy_order(const std::string& id, bool is_long, double qty,
338 double limit_price = std::numeric_limits<double>::quiet_NaN(),
339 double stop_price = std::numeric_limits<double>::quiet_NaN(),
340 const std::string& oca_name = {}, int oca_type = 0);
341
342 int pine_bar_index() const;
343 int pine_last_bar_index() const;
344 double prev_chart_close() const;
345 bool is_first_tick() const noexcept;
346 bool is_last_tick() const noexcept;
347 bool history_advances_new_bar() const noexcept;
348 bool security_series_slot_is_new(int) const noexcept;
349 int last_bar_dual_entry_path() const;
350 double live_position_size() const override;
351 int pending_order_count() const;
354 std::vector<admission::Field> market_admission_fields() const;
355 int probe_fill_qty(int index, double fill_price, double* qty,
356 int* close_only, int* partition) const;
357 int pending_order_level_resolved(int index) const;
358 int pending_order_effective_levels(int index, double* stop, double* limit,
359 double* trail_activation) const;
360 const PendingIntentView& pending_intent_view() const noexcept;
361 int short_seed_collision_role_v1(native_order::RequestHandle) const noexcept;
364 void set_syminfo_metadata(const std::string&, double) override;
365 bool set_aux_security_feed(const Bar* bars, int n,
366 const std::string& input_tf) override;
367#ifdef PINEFORGE_HAS_AUX_SECURITY_FEED_V1
368 bool source_aux_security_feed_enabled() const override;
369 void source_aux_security_input_view(const Bar*&, int&) const override;
370#endif
371 int observe_last_bar_dual_entry_path_v1() const override;
372 int observe_pending_count_v1() const override;
373 int observe_pending_copy_v1(int, pf_pending_order_v1_t*) const override;
374 int observe_probe_fill_qty(int, double, double*, int*, int*) const override;
375 int observe_pending_level_resolved(int) const override;
376 int observe_pending_effective_levels(int, double*, double*, double*) const override;
377 double observe_trail_best_price_v1() const override;
378
379 // Fixture-only adapter projections retained for the L2 literal twins.
380 // They are read-only and never participate in execution or matching.
383 std::string id;
384 FixtureIntentKind type = FixtureIntentKind::MARKET;
385 double default_stop_placement_qty = std::numeric_limits<double>::quiet_NaN();
386 double default_stop_sizing_price = std::numeric_limits<double>::quiet_NaN();
387 double frozen_market_own_units = std::numeric_limits<double>::quiet_NaN();
388 double frozen_market_transaction_units = std::numeric_limits<double>::quiet_NaN();
389 // These are immutable adapter placement facts, exposed only to test
390 // facades which formerly read the retired source PendingOrder owner.
391 std::string from_entry;
392 bool is_long = true;
393 double qty = std::numeric_limits<double>::quiet_NaN();
394 double qty_percent = std::numeric_limits<double>::quiet_NaN();
395 std::int64_t created_bar = -1;
396 std::int64_t created_seq = 0;
397 std::uint64_t incarnation = 0;
400 double paired_flat_market_transaction_qty = std::numeric_limits<double>::quiet_NaN();
401 double frozen_default_qty = std::numeric_limits<double>::quiet_NaN();
402 double default_stop_placement_equity = std::numeric_limits<double>::quiet_NaN();
403 double default_stop_placement_signal_close = std::numeric_limits<double>::quiet_NaN();
404 double affordability_placement_equity = std::numeric_limits<double>::quiet_NaN();
405 // The adapter currently has no pair-review receipt for this request;
406 // an empty Draft truthfully represents that absence to a fixture.
408 };
409
410public:
411 // Toggle TradingView's forced-liquidation (margin call) emulation.
412 // Defaults ON to match TV; set false for the hold-the-position behaviour.
413 // TV runs the broker margin-call emulator by default. It is a no-op for
414 // the validation corpus (long-only positions at the default 100% margin
415 // can never be liquidated — the formula denominator ``margin/100 -
416 // direction`` is 0 — and no corpus short is sized at full equity).
417 //
418 // R5 lane L12 (2.ii k): this used to be BacktestEngine state. The kernel
419 // toggle is the presence of `spec.margin` since L4/L4b, so the TV emulator
420 // switch belongs to the adapter that implements that emulator. The value
421 // reaches `PineExecutionAdapter::source_margin_call_enabled_` at the
422 // native begin boundary, exactly as before.
423 void set_margin_call_enabled(bool enabled) {
424 guard_native_mutation("set_margin_call_enabled");
425 margin_call_enabled_ = enabled;
426 }
428
429 // Live-runtime tail semantics (spec §3.1, ABI v4): the caller's fed array
430 // ends with a still-forming bar rather than the chart's rightmost
431 // historical bar. When `on`, the LAST bar of every subsequent run() (this
432 // is persistent configuration, not a one-shot flag -- it stays set until
433 // a caller passes on=false, and reset_run_state() does not touch it)
434 // gets barstate.islast == false, session.islastbar computed from the
435 // bucket calendar (no i+1 bar to peek at), pine_last_bar_index() /
436 // last_bar_time_ frozen at the horizon bar (`horizon_bars - 1`), and no
437 // range-end close row/trade. Default off: every historical run is
438 // byte-identical to before this flag existed.
439 //
440 // R5 lane N14: this and probe tail suppression used to be BacktestEngine
441 // state. Both are the live runner's probe / settle protocol expressed in
442 // Pine language state (barstate, session flags, the range-end row), so
443 // they belong to the host that implements that protocol; the frozen C
444 // setters reach them through the kernel's virtual seams.
445 bool set_realtime_tail(bool on, int horizon_bars) override {
446 guard_native_mutation("set_realtime_tail");
447 realtime_tail_ = on;
448 realtime_tail_horizon_bars_ = horizon_bars;
449 return true;
450 }
451 bool realtime_tail() const { return realtime_tail_; }
452
453 // Live probe tail suppression (spec §3.2, ABI v4): when `on`, the LAST
454 // bar of every subsequent run() runs only the broker's pre-on_bar steps
455 // (intraday-cap deferred close, _push_source_series, request matching,
456 // evaluate_max_intraday_loss_over_path, update_per_trade_extremes) and
457 // returns — on_bar is never invoked for that bar, and nothing after it
458 // runs (no flush_same_bar_close, no POOC second pass, no
459 // process_margin_call, no settle_dormant_bracket_ reissues, no sizing
460 // refresh). Margin-call / intraday-cap closes therefore surface only at
461 // settlement (the next non-suppressed run), not against the
462 // still-forming probe bar. This is persistent configuration, like
463 // set_realtime_tail, and independent of it — do not couple the two
464 // flags. Honoured only on the standard dispatch path (single-TF run
465 // loop, run_simple_bar_loop). Silent no-op under calc_on_order_fills
466 // (COOF scheduler) and under the bar magnifier -- both gated in live v1.
467 // Semantics UNDEFINED on the non-magnifier aggregation path (input_tf <
468 // script_tf) until the partial-bucket forming-bar flag lands; see
469 // pineforge.h. Default off (@p on == 0): every historical run stays
470 // byte-identical to before this flag existed.
471 bool set_probe_suppress_tail_logic(bool on) override {
472 guard_native_mutation("set_probe_suppress_tail_logic");
474 return true;
475 }
477
478protected:
479 // Narrow test-facade configuration slots keep the frozen L0 oracle bodies
480 // unchanged while routing their setup through the source configuration
481 // projected at the native begin boundary.
483 const PineStrategyConfig& fixture_configuration() const noexcept { return config_; }
487 BarTime fixture_chart_time(std::int64_t timestamp_ms) const;
488 std::int64_t fixture_chart_day_key(std::int64_t timestamp_ms) const noexcept {
489 return adapter_.chart_day_key(timestamp_ms);
490 }
491 std::uint64_t fixture_applied_receipt_count() const;
492 bool fixture_cap_due_pending() const noexcept {
493 return adapter_.cap.due_cause().has_value();
494 }
496 public:
497 explicit FixtureQtyTypeSlot(PineStrategyHost& host) noexcept : host_(host) {}
499 host_.config_.default_qty_type = static_cast<int>(value);
500 return *this;
501 }
502 operator QtyType() const noexcept {
503 return static_cast<QtyType>(host_.config_.default_qty_type);
504 }
505 private:
506 PineStrategyHost& host_;
507 };
512 public:
513 explicit FixtureCommissionTypeSlot(PineStrategyHost& host) noexcept : host_(host) {}
515 host_.config_.commission_type = static_cast<int>(value);
516 return *this;
517 }
518 operator CommissionType() const noexcept {
519 return static_cast<CommissionType>(host_.config_.commission_type);
520 }
521 private:
522 PineStrategyHost& host_;
523 };
528 public:
529 explicit FixtureRiskDirectionSlot(PineStrategyHost& host) noexcept : host_(host) {}
530 FixtureRiskDirectionSlot& operator=(int value) noexcept {
531 host_.adapter_.set_risk_direction(value);
532 return *this;
533 }
534 private:
535 PineStrategyHost& host_;
536 };
540 class SourceIdLedgerView {
541 public:
542 struct value_type { double second = 0.0; };
544 public:
545 const value_type* operator->() const noexcept { return &value_; }
546 bool operator==(const const_iterator& other) const noexcept {
547 return present_ == other.present_;
548 }
549 bool operator!=(const const_iterator& other) const noexcept {
550 return !(*this == other);
551 }
552 private:
553 friend class SourceIdLedgerView;
554 bool present_ = false;
555 value_type value_{};
556 };
557 const_iterator find(const std::string& id) const noexcept {
558 const double units = host_ ? host_->adapter_.source_unclosed_qty_for(id) : 0.0;
559 const_iterator result;
560 result.present_ = units > 0.0;
561 result.value_.second = units;
562 return result;
563 }
564 const_iterator end() const noexcept { return {}; }
565 private:
566 friend class PineStrategyHost;
567 explicit SourceIdLedgerView(const PineStrategyHost* host) noexcept : host_(host) {}
568 const PineStrategyHost* host_ = nullptr;
569 };
571 return SourceIdLedgerView(this);
572 }
573 double signed_position_size() const;
576 const Series<double>& source_series(const std::string&) const;
577 const Series<double>& source_input_series(const std::string& key,
578 const Series<double>& fallback) const;
579 // Generated input.source() calls retain this established surface spelling.
580 const Series<double>& get_input_source(const std::string& key,
581 const Series<double>& fallback) const {
582 return source_input_series(key, fallback);
583 }
584 // Generated strategy.margin_liquidation_price reads this Pine-specific
585 // projection over the inherited native position state.
586 double margin_liquidation_price() const;
587 void fixture_publish_source_series(const Bar& bar, bool new_history_slot) {
588 scheduler_.fixture_publish_source_series(bar, new_history_slot);
589 }
590 int64_t time_close() const {
591 return pine_time_close(current_bar_.timestamp, script_tf_, syminfo_.session,
592 syminfo_.timezone, script_tf_);
593 }
594 // ab9714be pine_strategy_host.hpp:348-358: generated three-argument
595 // session predicates are class-scope calls whose chart timeframe changes
596 // the D/W/M meaning. Keep that Pine policy in the source host; the
597 // namespace-level overload remains the raw intraday time-of-day query.
598 bool pine_session_ismarket(const std::string& session,
599 const std::string& timezone,
600 std::int64_t bar_ms) const {
601 return pineforge::pine_session_ismarket(session, timezone, bar_ms, script_tf_);
602 }
603 bool pine_session_ispremarket(const std::string& session,
604 const std::string& timezone,
605 std::int64_t bar_ms) const {
606 return pineforge::pine_session_ispremarket(session, timezone, bar_ms, script_tf_);
607 }
608 bool pine_session_ispostmarket(const std::string& session,
609 const std::string& timezone,
610 std::int64_t bar_ms) const {
611 return pineforge::pine_session_ispostmarket(session, timezone, bar_ms, script_tf_);
612 }
613 const std::vector<FixtureIntentRow>& source_pending_view() const;
614 // request.security registration, as generated configure_security_evaluators()
615 // spells it. The kernel registers the aggregating evaluator; Pine's
616 // publication semantics for the site (barmerge.lookahead, barmerge.gaps,
617 // ticker.heikinashi, the lower-timeframe emulation a finer request
618 // selects) are this host's, recorded for the sec_id it just registered.
619 void register_security_eval(int sec_id, const std::string& requested_tf,
620 const std::string& input_tf, bool lookahead_on,
621 bool gaps_on = false, bool heikinashi = false);
622 // ``request.security_lower_tf`` registers the same per-sec_id eval
623 // state but with the additional contract that the requested TF must
624 // resolve to a finer-than-input TF emulation. This wrapper sets the
625 // ``lower_tf_array_requested`` flag so ``validate_security_timeframes``
626 // can throw a precise error if the chart's input TF turns out to be
627 // <= the requested TF (mirroring TradingView's "lower timeframe
628 // required" error for ``request.security_lower_tf``).
629 void register_security_lower_tf_eval(int sec_id, const std::string& requested_tf,
630 const std::string& input_tf);
631 // Sub-bar index (0-based) of the current ``request.security_lower_tf``
632 // synthesis within the current chart bar. Returns 0 outside the
633 // synthesis loop. Used by codegen to clear its per-call vector at
634 // sub-bar 0 and push one element per sub-bar after.
635 int security_lower_tf_sub_bar_index(int sec_id) const;
636 // TradingView's request.security / request.security_lower_tf timeframe
637 // rules for the registered sites against the run's evaluator input
638 // timeframe: the diagnostics a script author reads, the lower-timeframe
639 // emulation each array site selects, and the publication gates a
640 // finer-than-chart site runs under.
641 void validate_security_timeframes(const std::string& input_tf);
642 void source_stream_entry_comment(const PyramidEntry&, std::string&) const override;
643 // The adapter's durable state rides the kernel's generic host seam.
644 void hash_host_extension(BrokerStateHashSink&) const override;
645 // Deprecated spelling, kept forwarding so a caller written against it
646 // still reads this host's fold. final: the kernel folds
647 // hash_host_extension, so an override of this name would never be folded.
650 }
651
652private:
653 friend class PineScheduler;
655
656 StagedConfiguration staged_configuration() const;
657 static PineStrategyConfig apply_overrides(PineStrategyConfig,
658 const StrategyOverrides&);
659 static std::uint64_t adapter_event_high_water(const NativeStrategyHost&) noexcept;
660 static std::uint64_t adapter_terminal_receipt_high_water(const NativeStrategyHost&) noexcept;
661 std::uint64_t adapter_broker_fill_event_sequence() const noexcept {
662 return broker_fill_event_seq_;
663 }
664 void scheduler_prepare_script_run(const std::vector<Bar>&,
665 bool static_eligible, int expected_script_bars,
666 bool script_bar_geometry);
667 void scheduler_configure_security_evaluators();
668 bool scheduler_uses_aux_security_feed() const noexcept;
669 void scheduler_prepare_security_sequence(const std::vector<Bar>&);
670 // R5 lane R3b: the plain request.security sites of a batch run whose
671 // input and script timeframes coincide are declared to the kernel as
672 // NativeTimeframeSubscription series instances at the L6c begin-time hook
673 // -- one per site, sec_id by index, barmerge.gaps_on as the kernel's
674 // `gaps` -- and the kernel's own pump steps them; this host's evaluator
675 // registration, per-run preparation and pump stand down for that run.
676 // False, keeping this host's own drive, when the run or any site needs a
677 // Pine-only rule the kernel step does not have: a stream (the kernel
678 // takes confirmed bars only), the bar magnifier or an aggregated chart
679 // (the calling-bar deferrals), the auxiliary feed, the KI-55 range-start
680 // cut and its OTC-daily pins, the historical lookahead projection,
681 // barmerge.lookahead_on, ticker.heikinashi, request.security_lower_tf,
682 // sec_ids that are not the registration order, or a declaration the
683 // kernel refuses.
684 bool declare_security_sites_to_kernel();
685 // True while the running spec names this host's sites: the kernel steps
686 // them and the scheduler feeds nothing.
687 bool security_sites_kernel_routed() const noexcept;
688 void init_security_eval_states_for_run(const std::string& effective_input_tf);
689 void prepare_historical_security_lookahead_projections(
690 const Bar* input_bars, int n_input, const std::string& effective_input_tf);
691 void clear_historical_security_lookahead_projections();
692 bool scheduler_feed_security_input(const Bar&, std::int64_t next_input_ms,
693 bool calling_bar_complete,
694 bool defer_boundary_gate);
695 void scheduler_publish_security_boundary();
696 void scheduler_feed_deferred_security_input(const Bar&, std::int64_t next_input_ms);
697 void scheduler_feed_aux_security(int chart_index);
698 void scheduler_feed_deferred_aux_security(int chart_index);
699 void scheduler_finish_security_sequence();
700 // Pine's evaluator step for one request.security site and one evaluator
701 // input bar: the kernel's aggregation, native-bar substitution and
702 // dispatch primitives, composed under TradingView's publication rules
703 // (src/source/pine_security_eval.cpp).
704 void pine_feed_security_eval_state(SecurityEvalState& state, const Bar& input_bar,
705 bool calling_bar_complete = false);
706 // The site's Pine semantics; a sec_id registered outside this host's
707 // register_security_eval (none today) reads as a plain site.
708 PineSecurityEvalState& pine_security_state(int sec_id) {
709 return pine_security_states_[sec_id];
710 }
711 const PineSecurityEvalState& pine_security_state(int sec_id) const {
712 static const PineSecurityEvalState plain{};
713 const auto found = pine_security_states_.find(sec_id);
714 return found == pine_security_states_.end() ? plain : found->second;
715 }
716 // Drops the entries of sec_ids the evaluator registry no longer holds.
717 void prune_pine_security_states();
718 // KI-55 range-start gate for one evaluator: true when the input bar at
719 // `input_ts` belongs to an HTF bucket that opened before the cut --
720 // security_range_start_ms_ under the flag, else the run's first chart
721 // bar for a coarser-than-chart / chart-timeframe evaluator
722 // (security_first_chart_bar_ms_, split-feed runs with the auxiliary feed
723 // proving prior trading, or single-feed historical intraday forex/cfd D
724 // requests keyed to their session's actual open; false for lower TFs). The
725 // progressive feed and the historical lookahead projection builder must
726 // agree on this predicate so projected child indexes line up with the
727 // per-state feed cursor.
728 bool security_input_precedes_range_start(const SecurityEvalState& state,
729 int64_t input_ts) const;
730#ifdef PINEFORGE_HAS_AUX_SECURITY_FEED_V1
731 // True when the auxiliary request.security feed holds a bar in
732 // [from_ms, to_ms): the evidence that an HTF bucket whose nominal open
733 // precedes the run's first chart bar was in progress at the range start.
734 bool aux_security_traded_between(int64_t from_ms, int64_t to_ms) const;
735#endif
736 // The calling chart bar's publication boundary for a gated site
737 // (publish_gate_tf_seconds): re-publish the completed caller's final
738 // requested value before the retained boundary input is fed.
739 void publish_security_eval_state_at_calling_boundary(SecurityEvalState& state);
740 void scheduler_record_range_end(const Bar&);
741 // One report point per published source slot. The kernel records it
742 // (NativeReportPolicy::KernelRecordedAtHostMarks); this host owns only
743 // the Pine cadence that says where the points fall.
744 void scheduler_mark_report_point(std::int64_t script_bar_ts);
745 void scheduler_record_broker_hash();
746 void capture_script_continuation_hash();
747 void scheduler_update_session_state(
748 const Bar&, std::optional<std::int64_t> next_script_open_ms);
749 void scheduler_set_session_bar_state(bool in_session,
750 bool intraday_is_last_bar);
751 execution::AccountEffectProjection adapter_project_flatten(
752 double price, const std::string& id, const std::string& comment,
753 std::uint64_t incarnation) const;
754 void adapter_label_bracket_trades(
755 const native_order::ExecutionAppliedEvent&, bool from_bracket);
756 bool adapter_has_open_entry_id(const std::string&) const;
757 void scheduler_publish_source_bar(const Bar&, bool first_tick,
758 bool advance_source_index = true);
759 void scheduler_publish_suppressed_tail(const Bar&);
760 double compute_liquidation_price() const;
761 void project_short_seed_report_rows(const native_order::ExecutionAppliedEvent&);
762 bool scheduler_coof_enabled() const noexcept { return config_.calc_on_order_fills; }
763 // The range-end row an applied execution may complete. It stays ordered
764 // after the fill's calc_on_order_fills recalculation, which can advance
765 // the source bar counter, so it runs in whichever of the two callbacks is
766 // last for that event.
767 void record_applied_range_end();
768#ifdef PINEFORGE_HAS_AUX_SECURITY_FEED_V1
769 void clear_aux_security_chart_ranges();
770 void prepare_aux_security_chart_ranges(const Bar*, int, const std::string&);
771 std::int64_t aux_security_calling_close_ms() const;
772 void feed_aux_security_for_chart_bar(int);
773 void feed_deferred_aux_security_for_chart_bar(int);
774#endif
775
776protected:
777 // @source-state begin
780 public:
781 explicit SourceCloseObligationView(const PineExecutionAdapter& adapter) noexcept
782 : adapter_(&adapter) {}
783 bool pending() const noexcept {
784 return adapter_ && adapter_->cap.due_cause().has_value();
785 }
786 private:
787 const PineExecutionAdapter* adapter_ = nullptr;
788 };
789 // Retained protected spelling for source fixtures/generated code. The
790 // authoritative due request lives in IntradayCap; this read-only facade
791 // prevents a second mutable close-obligation owner.
796 // Generated strategies still use this source-series spelling directly.
797 // The state remains scheduler-owned and is hashed by PineScheduler.
808 const bool& is_last_tick_;
811 std::uint64_t source_callback_count_ = 0;
814#ifdef PINEFORGE_HAS_AUX_SECURITY_FEED_V1
815 std::vector<Bar> aux_security_bars_;
817 std::vector<std::size_t> aux_security_chart_begin_;
818 std::vector<std::size_t> aux_security_chart_end_;
819#endif
820 // Per-sec_id request.security semantics (PineSecurityEvalState), keyed and
821 // folded in sec_id order. An entry lives exactly as long as the kernel
822 // evaluator state registered under the same sec_id: generated
823 // configure_security_evaluators() opens with security_eval_states_.clear(),
824 // so the first registration into an empty registry starts this table over.
825 std::map<int, PineSecurityEvalState> pine_security_states_;
826 // @source-state end
827
828 // Provider configuration, not book state: it is staged before a run and
829 // projected into the adapter at begin, so it is waived from the durable
830 // fold like the other configuration slots.
832
833 // Live-runtime tail overrides (spec §3.1 / §3.2), provider configuration
834 // like margin_call_enabled_ above: consumed at begin and by the
835 // scheduler's publication of the last script bar, never by broker
836 // settlement. See set_realtime_tail / set_probe_suppress_tail_logic.
837 bool realtime_tail_ = false;
840
841 // Live-runtime tail (spec §3.1): once script_tf_seconds_ is known for
842 // this run, freeze pine_last_bar_index()/last_bar_time_ at the horizon
843 // bar instead of the fed array's actual last index. No-op unless
844 // realtime_tail_ is on and realtime_tail_horizon_bars_ > 0.
845 //
846 // `script_bar_geometry` says whether `bars` is script-bar geometry:
847 // true -- the single-TF run(bars, n) path and the !needs_aggregation
848 // run_tf_impl call (input_tf == script_tf), where the horizon
849 // indexes `bars` directly; last_bar_time_ is the EXACT
850 // timestamp of bars[horizon_bars - 1] when that bar exists,
851 // else extrapolated from the array's actual final bar.
852 // false -- `bars` is the *input* array under aggregation
853 // (needs_aggregation, input_tf < script_tf): indexing it by
854 // a script-bar horizon would land on the wrong input bar
855 // (final-rereview.md N1), so instead extrapolate from the
856 // first input bar's timestamp, one script-TF step per
857 // horizon bar (the pre-fix formula, restored for this path
858 // only).
859 void apply_realtime_tail_horizon(const Bar* bars, int n, bool script_bar_geometry);
860
861 // Opt-in KI-55 chart warmup parity (see set_syminfo_metadata,
862 // "chart_ema_na_warmup"). When enabled, chart-timeframe ta.ema instances
863 // first used by on_bar na-warm per TV built-in semantics. This selector is
864 // scoped independently from request.security so the two execution contexts
865 // cannot leak their warmup mode into each other. Default OFF.
867 // Independent opt-in KI-55 HTF warmup parity. When enabled,
868 // request.security series aggregate from security_range_start_ms_ instead
869 // of the feed start and their embedded ta.ema na-warm per TV built-in
870 // semantics. The cut is taken per evaluator on HTF-BUCKET opens, not on
871 // input timestamps: an input bar is dropped when the D/W/M (or intraday
872 // grid) bucket it belongs to opened before the range start, so the first
873 // HTF bar every series sees is a whole bucket that opened at/after the
874 // range start (security_input_precedes_range_start). Default OFF;
875 // consulted only by pine_feed_security_eval_state, the auxiliary
876 // lower-timeframe slice and the historical lookahead projection builder.
879 // The run's first chart bar (0 outside a run). Without the flag above
880 // this is the default cut for every coarser-than-chart and chart-
881 // timeframe request.security aggregation: TradingView's deep-backtest
882 // series of a timeframe hold the bars of that timeframe whose OPEN lies
883 // at or after the range start, and the bucket in progress at the range
884 // start is absent -- on every lane (round 8, family P: masayanfx
885 // multi-time-score; lab tv famp-sense-{es15full,nq15full,f15full,
886 // nifty15full,nifty1d,xau1d,xau15,eur15,eth15,btc1d}, 2026-09-05: "D"
887 // on CME_MINI:ES1! 15m first reads on the 05-01 20:45Z bar (bucket 0 is
888 // the 04-02 trade date; the 04-01 date opened 03-31 22:00Z before the
889 // 04-01 00:00Z range start), "240" on 04-04 13:45Z (the 22:00Z bucket
890 // dropped), "W" on the 08-29 / 08-28 bar on every 15m and 1D lane (the
891 // Mon 03-31 week dropped), while "60" on a 00:00Z start and "D" on the
892 // NYSE / NSE lanes (the chart's first bar IS the session open) keep
893 // their first bucket). Whether the bucket was in progress is read from
894 // the auxiliary 1m feed (did it trade between the bucket's nominal open
895 // and the first chart bar? the NSE week whose Monday was a holiday opens
896 // on Tuesday and is kept). Historical intraday single-feed forex/cfd D
897 // requests also omit their partial first session, using its actual trading
898 // open rather than its label. Other single-feed series keep their feed-start
899 // behavior; native feeds retain their own rules. Lower-TF evaluators are
900 // untouched (their slices begin at the first chart bar anyway), and the
901 // flag above keeps its explicit epoch plus the EMA na-warmup semantics.
903
904 // Nominal close (TradingView's time_close) of the CALLING chart bar the
905 // input bar being fed belongs to; 0 = the input bar is the chart bar
906 // (single-feed runs, streams). Set per native chart bar on the
907 // split-feed path, where a finer auxiliary slice advances
908 // request.security under a D/W/M chart bar whose close an OTC
909 // calendar bucket compares against the period's nominal close
910 // (TimeframeAggregator::feed(bar, next_input_ms, calling_close_ms)).
912
913 // Opt-in historical-only request.security lookahead projection. TradingView
914 // can merge a completed higher-timeframe bar onto the first chart child
915 // when a finite historical batch is already known. The normal path
916 // remains progressive, and stream warmup/realtime deliberately ignore this
917 // selector so future data can never leak into a live continuation.
920
921 // Boundary-fallback publication replays the completed caller's already
922 // evaluated final requested value. Force generated TA sites down their
923 // recompute path so the replay advances merged history only, never the
924 // requested-context TA cadence. True only inside
925 // publish_security_eval_state_at_calling_boundary's own scope.
927
928 // Read-only test projection cache; no future execution can observe it.
929 mutable std::vector<FixtureIntentRow> source_pending_view_cache_;
930
931 // Transient excursion-sampler caches (RULING A48). Pure caches: each is
932 // re-derived from the immutable placement snapshots and the delivered
933 // decision context, and cleared at the applied-notification boundary.
934 mutable bool excursion_priced_fill_ = false;
935 mutable bool excursion_level_fill_ = false;
936 mutable bool excursion_margin_call_ = false;
937 // Which of the owner's two margin-slice chronologies the pending slice was
938 // born in: true when it samples only the traversed waypoint prefix (the
939 // POOC pre-script pass, or the 1x-long opening slice taken inside
940 // process_pending_orders before a priced exit's fill), false when the
941 // non-POOC end-of-bar opening trim inherits the complete script bar.
942 mutable bool excursion_margin_prefix_ = false;
943 // A slice the owner books with no sample of the current bar at all: the
944 // carried position's open slice and the general pre-exit slice own only
945 // their carried extremes and the fill itself.
946 mutable bool excursion_margin_fill_only_ = false;
949 std::numeric_limits<double>::quiet_NaN();
950 // Held units the applied request's precommit saw (the owner's pre-fill
951 // position_qty_); cleared after the applied notification.
952 mutable double precommit_held_units_ =
953 std::numeric_limits<double>::quiet_NaN();
954 // The matcher basis behind a TRAIL fill. ab9714be pine_fills.cpp:5766-5770
955 // reads the arming peak off the PRE-slip fill price (apply_fill_slippage
956 // runs later, at :7136/:7184), while the booked
957 // ClosedLotExcursionFacts::fill_price the sampler sees is already slipped.
959 std::numeric_limits<double>::quiet_NaN();
960};
961
965
966} // namespace pineforge::source
The public native host: an abstract subclass of BacktestEngine with no PineScript on it.
FixtureCommissionTypeSlot & operator=(CommissionType value) noexcept
FixtureQtyTypeSlot & operator=(QtyType value) noexcept
FixtureRiskDirectionSlot & operator=(int value) noexcept
SourceCloseObligationView(const PineExecutionAdapter &adapter) noexcept
const_iterator find(const std::string &id) const noexcept
void validate_security_timeframes(const std::string &input_tf)
compat::pine::Calculation fixture_cap_calculation() const
void register_security_eval(int sec_id, const std::string &requested_tf, const std::string &input_tf, bool lookahead_on, bool gaps_on=false, bool heikinashi=false)
FixtureCommissionTypeSlot fixture_commission_type_slot() noexcept
bool set_realtime_tail(bool on, int horizon_bars) override
void set_syminfo_session(const std::string &)
std::optional< NativeMarginDecision > resolve_margin_requirement(const NativeMarginRequirementView &) const final
std::optional< double > resolve_margin_call_units(const NativeMarginCallView &) const final
const Series< double > & get_input_source(const std::string &key, const Series< double > &fallback) const
void prepare_native_begin(const NativeBeginArgs &) final
BarTime fixture_chart_time(std::int64_t timestamp_ms) const
compat::pine::CapClock fixture_cap_clock() const
void strategy_entry(const std::string &id, bool is_long, double limit_price=std::numeric_limits< double >::quiet_NaN(), double stop_price=std::numeric_limits< double >::quiet_NaN(), double qty=std::numeric_limits< double >::quiet_NaN(), const std::string &comment={}, const std::string &oca_name={}, int oca_type=0, int qty_type=-1)
const PendingIntentView & pending_intent_view() const noexcept
const std::vector< FixtureIntentRow > & source_pending_view() const
void on_native_tick(const Bar &, const NativeTickContext &) final
ClosedLotExcursion closed_lot_excursion(const ClosedLotExcursionFacts &) const final
SourceCloseObligationView position_close_obligation_
const Series< double > & source_input_series(const std::string &key, const Series< double > &fallback) const
int pending_order_effective_levels(int index, double *stop, double *limit, double *trail_activation) const
int observe_pending_effective_levels(int, double *, double *, double *) const override
bool set_aux_security_feed(const Bar *bars, int n, const std::string &input_tf) override
double observe_trail_best_price_v1() const override
void strategy_order(const std::string &id, bool is_long, double qty, double limit_price=std::numeric_limits< double >::quiet_NaN(), double stop_price=std::numeric_limits< double >::quiet_NaN(), const std::string &oca_name={}, int oca_type=0)
void configure_pine_strategy(const PineStrategyConfig &)
FixtureQtyTypeSlot fixture_default_qty_type_slot() noexcept
void strategy_exit_cancel_bracket(const std::string &exit_id, const std::string &from_entry, const std::string &comment={})
int observe_pending_level_resolved(int) const override
FixtureRiskDirectionSlot fixture_risk_direction_slot() noexcept
bool pine_session_ispremarket(const std::string &session, const std::string &timezone, std::int64_t bar_ms) const
bool owns_lot_excursions() const noexcept final
std::vector< std::size_t > aux_security_chart_begin_
bool pine_session_ismarket(const std::string &session, const std::string &timezone, std::int64_t bar_ms) const
const Series< double > & source_series(const std::string &) const
void apply_realtime_tail_horizon(const Bar *bars, int n, bool script_bar_geometry)
void hash_source_extension(BrokerStateHashSink &sink) const final
void on_native_applied(const native_order::ExecutionAppliedEvent &, const NativeDecisionContext &) final
std::vector< admission::Field > market_admission_fields() const
PineStrategyHost(compat::pine::CapAttachment cap=compat::pine::CapAttachment::None)
int security_lower_tf_sub_bar_index(int sec_id) const
int observe_last_bar_dual_entry_path_v1() const override
std::int64_t fixture_chart_day_key(std::int64_t timestamp_ms) const noexcept
std::uint64_t broker_state_hash_projection() const override
void strategy_cancel(const std::string &id)
std::vector< FixtureIntentRow > source_pending_view_cache_
bool margin_check_allowed(const NativeMarginCheckPoint &) const final
void fixture_publish_source_series(const Bar &bar, bool new_history_slot)
std::optional< double > resolve_anchored_level(const NativeAnchoredLevelView &) const final
void hash_host_extension(BrokerStateHashSink &) const override
void register_security_lower_tf_eval(int sec_id, const std::string &requested_tf, const std::string &input_tf)
std::uint64_t fixture_applied_receipt_count() const
bool security_series_slot_is_new(int) const noexcept
PineStrategyConfig & fixture_configuration() noexcept
void strategy_exit(const std::string &id, const std::string &from_entry, double limit_price, double stop_price, double trail_points=std::numeric_limits< double >::quiet_NaN(), double trail_offset=std::numeric_limits< double >::quiet_NaN(), double trail_price=std::numeric_limits< double >::quiet_NaN(), double qty_percent=100.0, const std::string &comment={}, double qty=std::numeric_limits< double >::quiet_NaN(), const std::string &oca_name={}, double profit_ticks=std::numeric_limits< double >::quiet_NaN(), double loss_ticks=std::numeric_limits< double >::quiet_NaN())
void on_native_input(const Bar &, const NativeInputContext &) final
bool source_aux_security_feed_enabled() const override
int short_seed_collision_role_v1(native_order::RequestHandle) const noexcept
void strategy_close(const std::string &id, const std::string &comment={}, double qty=std::numeric_limits< double >::quiet_NaN(), double qty_percent=std::numeric_limits< double >::quiet_NaN(), bool immediately=false)
std::map< int, PineSecurityEvalState > pine_security_states_
void on_native_bar_open(const Bar &, const NativeDecisionContext &) final
bool set_probe_suppress_tail_logic(bool on) override
MarketAdmissionJournal & market_admission_journal()
NativePrecommitVerdict validate_execution_precommit(const NativePrecommitView &) const final
bool pine_session_ispostmarket(const std::string &session, const std::string &timezone, std::int64_t bar_ms) const
void source_aux_security_input_view(const Bar *&, int &) const override
void on_native_bar(const Bar &, const NativeDecisionContext &) final
int observe_probe_fill_qty(int, double, double *, int *, int *) const override
const PineStrategyConfig & fixture_configuration() const noexcept
void set_syminfo_metadata(const std::string &, double) override
void set_strategy_override(const StrategyOverrides &)
SourceIdLedgerView source_id_ledger_view() const noexcept
virtual void on_source_bar(const Bar &)=0
int observe_pending_copy_v1(int, pf_pending_order_v1_t *) const override
std::vector< std::size_t > aux_security_chart_end_
bool adapter_core_sizes_default_opening(bool is_long) const
native_order::ExecutionTerms resolve_execution_terms(const NativeExecutionTermsFacts &) const final
int probe_fill_qty(int index, double fill_price, double *qty, int *close_only, int *partition) const
void on_native_recalculate(const Bar &, const NativeDecisionContext &, NativeCalculationReason, const native_order::ExecutionAppliedEvent *) final
void source_stream_entry_comment(const PyramidEntry &, std::string &) const override
NativeCalculationReason
Why the kernel is asking the host to calculate.
NativePrecommitVerdict
The host is consulted before generic opening-margin admission.
constexpr char kSourceSecurityDomain[]
PineStrategyHost::FixtureIntentRow FixtureIntentRow
PineStrategyHost::FixtureIntentKind FixtureIntentKind
PineStrategyHost PineNativeHost
bool pine_session_ispostmarket(const std::string &session, const std::string &tz, int64_t bar_ms)
admission::Draft MarketAdmissionDraft
bool pine_session_ispremarket(const std::string &session, const std::string &tz, int64_t bar_ms)
bool pine_session_ismarket(const std::string &session, const std::string &tz, int64_t bar_ms)
admission::Journal MarketAdmissionJournal
int64_t pine_time_close(int64_t bar_ms, const std::string &tf, const std::string &session, const std::string &tz, const std::string &chart_tf, const std::string &syminfo_tz=std::string())
Ephemeral read-only facts of one anchored-leg materialization (L7b), offered to the host exactly once...
Borrowed begin-call facts.
Read-only owning-value facts for one candidate.
Accepted input facts presented before the generic consumer aggregates the bar into its script interva...
Ephemeral factual view of one kernel-issued liquidation before its units are fixed.
Ephemeral factual view of one kernel check point, offered to the host before the check runs.
Ephemeral factual view of the numbers the kernel is about to compare, at one check point,...
Ephemeral factual view of one prepared execution before any physical effect.
One accepted realtime print before native matching at its current decision point.
One committed execution: the definition, the cursor, the resolved price and units,...
std::vector< HistoricalSecurityProjection > historical_projections