PineForge v0.13.1-379-g9b50973
Deterministic PineScript v6 backtest runtime — C ABI reference
Loading...
Searching...
No Matches
pine_adapter.hpp
Go to the documentation of this file.
1#pragma once
2
3// Pine-only lowering facts. This header deliberately sits above the generic
4// native-host surface: no native kernel type learns a Pine source identifier,
5// sizing convention, or lifecycle vocabulary from it.
14
15#include <algorithm>
16#include <cstddef>
17#include <cstdint>
18#include <iterator>
19#include <limits>
20#include <map>
21#include <optional>
22#include <stdexcept>
23#include <string>
24#include <unordered_map>
25#include <unordered_set>
26#include <utility>
27#include <vector>
28
30
31// A2: Pine entry ids and from_entry values are source strings, never a
32// kernel-owned identifier type.
33using SourceId = std::string;
34
36class PineScheduler;
37
38inline constexpr char kSourceAdapterDomain[] = "pineforge-source-adapter/v3";
39
40// The adapter's forced-close vocabulary. These strings are TradingView report
41// shape, so they are written and read entirely inside the source layer: the
42// kernel records execution::CloseCause instead of decoding them (R5 lane L12,
43// 2.ii l).
44inline constexpr char kMarginCallLabel[] = "__margin_call__";
45inline constexpr char kIntradayLossComment[] = "Close Position (Max intraday Loss)";
46// The published C contract matched this PREFIX, not the whole comment, so the
47// classifier keeps matching the prefix.
48inline constexpr char kFillCapCommentPrefix[] =
49 "Close Position (Max number of filled orders";
50
51// TradingView's calc_on_order_fills cascade guard. It is a Pine literal, not a
52// kernel default: project() hands it to the generic cadence as
53// NativeRunSpec::max_recalculations_per_point, and the adapter's own first-open
54// execution chain keeps refusing at the same count.
55inline constexpr std::uint32_t kCoofLoopGuard = 1U << 20;
56
59 bool calc_on_order_fills = false;
60 double initial_capital = 1000000.0;
61 int default_qty_type = static_cast<int>(QtyType::FIXED);
62 double default_qty_value = 1.0;
63 int pyramiding = 1;
64 double commission_value = 0.0;
66 int slippage = 0;
67 double margin_long = 100.0;
68 double margin_short = 100.0;
70 bool src_series_active = false;
71};
72
74 double initial_capital = std::numeric_limits<double>::quiet_NaN();
75 double commission_value = std::numeric_limits<double>::quiet_NaN();
76 double default_qty_value = std::numeric_limits<double>::quiet_NaN();
77 int pyramiding = -1;
78 int slippage = -1;
84};
85
86// Value snapshot supplied by PineStrategyHost at the begin boundary. It owns
87// every source-side value the adapter needs to form a run spec; none of these
88// values are retained by generic native code.
92 std::string chart_timezone{};
93 double account_fx = 1.0;
94 std::vector<std::int64_t> account_fx_effective_from_ms{};
95 std::vector<double> account_fx_per_quote{};
96 std::optional<double> quantity_grid{};
97};
98
99enum class PineOrderFamily : std::uint8_t {
100 Entry = 0,
101 Close = 1,
106 Order = 6,
108 Risk = 8,
109};
110
112 double limit = std::numeric_limits<double>::quiet_NaN();
113 double stop = std::numeric_limits<double>::quiet_NaN();
114 double trail_points = std::numeric_limits<double>::quiet_NaN();
115 double trail_offset = std::numeric_limits<double>::quiet_NaN();
116 double trail_price = std::numeric_limits<double>::quiet_NaN();
117 double profit_ticks = std::numeric_limits<double>::quiet_NaN();
118 double loss_ticks = std::numeric_limits<double>::quiet_NaN();
119};
120
121enum class PineCancellationCause : std::int32_t {
122 None = 0,
127};
128
131 std::int32_t state = 0;
132 std::int32_t close_claim_release = 0;
133 std::uint64_t source_incarnation = 0;
134 std::int64_t source_sequence = 0;
135 std::uint64_t target_incarnation = 0;
136 std::int64_t target_owner = 0;
137 std::uint64_t target_revision = 0;
138 double close_claim_consumed = std::numeric_limits<double>::quiet_NaN();
139 double close_claim_retired = std::numeric_limits<double>::quiet_NaN();
140};
141
143 double equity = std::numeric_limits<double>::quiet_NaN();
144 double price = std::numeric_limits<double>::quiet_NaN();
145 double fx = std::numeric_limits<double>::quiet_NaN();
146 double mark = std::numeric_limits<double>::quiet_NaN();
147 double frozen_units = std::numeric_limits<double>::quiet_NaN();
148 bool at_fill = false;
149};
150
151// Immutable source placement evidence keyed by the native request handle.
152// It is written only at submit/replace/applied boundaries and is read by the
153// const terms/precommit/projection methods.
158 std::string comment{};
159 std::string oca_name{};
160 int oca_type = 0;
161 int qty_type = -1;
162 double requested_qty = std::numeric_limits<double>::quiet_NaN();
163 // Live projection of a generic OCA reduction. The original source
164 // operand above remains immutable; this receipt-backed value is only for
165 // the public pending projection.
166 double projection_remaining_qty = std::numeric_limits<double>::quiet_NaN();
167 double qty_percent = std::numeric_limits<double>::quiet_NaN();
168 bool is_long = true;
169 bool immediately = false;
170 bool opening = false;
171 bool deferred_cohort = false;
175 double frozen_market_own_units = std::numeric_limits<double>::quiet_NaN();
176 double frozen_market_transaction_units = std::numeric_limits<double>::quiet_NaN();
179 // Command-boundary facts read immutably by the fill-time terms and
180 // precommit policies.
181 bool direction_gate = false;
186 bool reverse_to = false;
187 bool replaced_opening = false;
190 double frozen_reversal_transaction = std::numeric_limits<double>::quiet_NaN();
191 std::int64_t placement_cycle = 0;
192 std::uint64_t sequential_group = 0;
193 std::uint8_t sequential_rank = 0;
197 std::int32_t preserved_close_all_bar = -1;
198 // Explicit bracket legs retain the source opening provenance that caused
199 // their submission. A pending parent rejected at a later candidate can
200 // then retire only its own deferred legs.
202 std::uint64_t source_sequence = 0;
203 std::uint64_t command_ordinal = 0;
204 std::uint64_t placement_open_epoch = 0;
205 // Command-boundary order is retained separately from native submission
206 // order: deferred source commands may materialize after a later command.
207 std::uint64_t command_sequence = 0;
208 // Source close-callsite lowering facts. They describe one admitted
209 // script-evaluation command; the native request remains the sole
210 // executable order.
211 std::uint64_t close_callsite_token = 0;
212 std::uint32_t close_batch_calls = 0;
214 double close_first_target = 0.0;
220 std::int64_t placement_script_open_ms = 0;
221 std::int64_t placement_sub_open_ms = 0;
222 // Immutable C-row projection facts. These are source placement facts,
223 // not a second executable order: the native request remains the sole
224 // owner of matching, trigger state, and terminal receipts.
225 std::int32_t projection_created_bar = -1;
226 // Set when a source command pinned projection_created_bar itself, so a
227 // later submit of the same instance (a bracket leg materialized when its
228 // parent entry applies) must not re-stamp it to the submitting bar.
230 // Set when a carried bracket leg is force-executed at its own level on the
231 // bar its parent entry opened (ab9714be src/source/pine_fills.cpp:7695-7728).
232 // The close of such a leg must not inherit the exit bar's path sample: the
233 // owner books that fill at step 1, before update_per_trade_extremes().
235 std::int32_t projection_position_side = static_cast<std::int32_t>(PositionSide::FLAT);
239 std::uint64_t projection_predecessor = 0;
243 // A flat child declared before a fresh priced parent was already visited
244 // (and skipped) by the legacy whole-bar broker pass. Keep that source
245 // chronology outside the native path matcher until the post-calculation
246 // point of the parent's fill bar.
253 // An adapter-owned immediate source policy may resolve a generic
254 // host-sized close at a previously established broker path price. The
255 // request remains owned and settled by the native core; only its
256 // immutable terms fact is source-specific.
257 double forced_execution_price = std::numeric_limits<double>::quiet_NaN();
259 double projection_default_stop_equity = std::numeric_limits<double>::quiet_NaN();
260 double projection_default_stop_signal_close = std::numeric_limits<double>::quiet_NaN();
261 double projection_explicit_equity = std::numeric_limits<double>::quiet_NaN();
262 double projection_explicit_signal_close = std::numeric_limits<double>::quiet_NaN();
263 double projection_affordability_equity = std::numeric_limits<double>::quiet_NaN();
264 double projection_affordability_signal_price = std::numeric_limits<double>::quiet_NaN();
265 double projection_affordability_held_qty = std::numeric_limits<double>::quiet_NaN();
268 // Resolved absolute trail activation used by the source fill policy when
269 // trail_points is lowered after its parent opening becomes live.
270 double trail_activation_level = std::numeric_limits<double>::quiet_NaN();
271 // A source reissue that changes only trail_offset keeps the already-seen
272 // raw best while the generic request itself remains live.
273 double retained_trail_best = std::numeric_limits<double>::quiet_NaN();
274 // Immutable source command observation used by the public admission
275 // journal/mirror. It never owns or drives matching.
277 // L4c policy receipts. They are immutable placement/live facts owned by
278 // the adapter, never a second executable pending-order representation.
290 std::int32_t coof_cascade_seg_i = -1;
293 double paired_flat_market_own_qty = std::numeric_limits<double>::quiet_NaN();
294 double paired_flat_market_signal_close = std::numeric_limits<double>::quiet_NaN();
295 double paired_flat_market_signal_equity = std::numeric_limits<double>::quiet_NaN();
296 double paired_flat_market_signal_margin_pct = std::numeric_limits<double>::quiet_NaN();
297 double paired_flat_market_signal_pointvalue = std::numeric_limits<double>::quiet_NaN();
298 double paired_flat_market_signal_fx = std::numeric_limits<double>::quiet_NaN();
300 double paired_flat_market_transaction_qty = std::numeric_limits<double>::quiet_NaN();
301 std::int32_t signal_close_mc_bar = -1;
303 std::uint64_t signal_close_mc_fill_seq = 0;
304 double signal_close_mc_remaining_qty = std::numeric_limits<double>::quiet_NaN();
309};
310
311// Source placement handles are monotonically allocated by the native core.
312// Retain their immutable evidence in incarnation order rather than in a
313// node-per-row hash table: historical re-issued brackets then remain cheap to
314// append and lookup without changing the observable key/value collection.
316public:
317 template<bool IsConst>
318 class Iterator {
319 using Owner = std::conditional_t<IsConst, const PlacementTable, PlacementTable>;
320 using Snapshot = std::conditional_t<IsConst, const PlacementSnapshot, PlacementSnapshot>;
321 struct Reference {
322 Reference(std::uint64_t key, Snapshot& value) : first(key), second(value) {}
323
324 std::uint64_t first = 0;
325 Snapshot& second;
326 };
327
328 public:
329 // libstdc++ dispatches std::find_if/any_of on iterator_traits; a
330 // proxy iterator must still publish the five Cpp17 typedefs (libc++
331 // tolerated their absence, GCC on the Cloud Run runner did not).
332 using iterator_category = std::forward_iterator_tag;
333 using value_type = Reference;
334 using difference_type = std::ptrdiff_t;
335 using pointer = Reference*;
336 using reference = Reference;
337
338 Iterator() = default;
339
340 Reference operator*() const { return {static_cast<std::uint64_t>(index_ + 1U),
341 *owner_->slots_[index_]}; }
342 Reference* operator->() const {
343 reference_.emplace(static_cast<std::uint64_t>(index_ + 1U),
344 *owner_->slots_[index_]);
345 return &*reference_;
346 }
348 ++index_;
349 skip_empty();
350 return *this;
351 }
353 Iterator before = *this;
354 ++*this;
355 return before;
356 }
357 bool operator==(const Iterator& other) const noexcept {
358 return owner_ == other.owner_ && index_ == other.index_;
359 }
360 bool operator!=(const Iterator& other) const noexcept { return !(*this == other); }
361
362 private:
363 friend class PlacementTable;
364 Iterator(Owner* owner, std::size_t index) : owner_(owner), index_(index) { skip_empty(); }
365 void skip_empty() {
366 while (owner_ && index_ < owner_->slots_.size() && !owner_->slots_[index_]) ++index_;
367 }
368
369 Owner* owner_ = nullptr;
370 std::size_t index_ = 0;
371 mutable std::optional<Reference> reference_;
372 };
373
376
377 std::size_t size() const noexcept { return size_; }
378 std::size_t max_size() const noexcept { return slots_.max_size(); }
379 // Largest incarnation ever retained. Incarnations are monotone, so a
380 // handle above this mark at an observation time was unknown to every
381 // adapter collection populated before that observation.
382 std::uint64_t high_water() const noexcept {
383 return static_cast<std::uint64_t>(slots_.size());
384 }
385 void reserve(std::size_t count) { slots_.reserve(count); }
386 void clear() noexcept {
387 slots_.clear();
388 size_ = 0;
389 sequence_index_.clear();
390 sequence_indexed_slots_ = 0;
391 }
392
393 iterator begin() noexcept { return iterator(this, 0); }
394 iterator end() noexcept { return iterator(this, slots_.size()); }
395 const_iterator begin() const noexcept { return const_iterator(this, 0); }
396 const_iterator end() const noexcept { return const_iterator(this, slots_.size()); }
397
398 iterator find(std::uint64_t incarnation) noexcept {
399 if (incarnation == 0 || incarnation > slots_.size() || !slots_[incarnation - 1U]) return end();
400 return iterator(this, static_cast<std::size_t>(incarnation - 1U));
401 }
402 const_iterator find(std::uint64_t incarnation) const noexcept {
403 if (incarnation == 0 || incarnation > slots_.size() || !slots_[incarnation - 1U]) return end();
404 return const_iterator(this, static_cast<std::size_t>(incarnation - 1U));
405 }
406
407 PlacementSnapshot& at(std::uint64_t incarnation) {
408 const auto found = find(incarnation);
409 if (found == end()) throw std::out_of_range("source placement handle is absent");
410 return found->second;
411 }
412 const PlacementSnapshot& at(std::uint64_t incarnation) const {
413 const auto found = find(incarnation);
414 if (found == end()) throw std::out_of_range("source placement handle is absent");
415 return found->second;
416 }
417
418 template<class... Args>
419 std::pair<iterator, bool> try_emplace(std::uint64_t incarnation, Args&&... args) {
420 if (incarnation == 0 || incarnation > slots_.max_size()) {
421 throw std::length_error("source placement incarnation is out of range");
422 }
423 const auto index = static_cast<std::size_t>(incarnation - 1U);
424 if (index >= slots_.size()) slots_.resize(index + 1U);
425 auto& slot = slots_[index];
426 if (slot) return {iterator(this, index), false};
427 slot.emplace(std::forward<Args>(args)...);
428 ++size_;
429 // A late handle filling a slot below the index watermark is folded
430 // now; the minima are order-independent.
431 if (index < sequence_indexed_slots_) {
432 try {
433 fold_sequence(*slot);
434 } catch (...) {
435 sequence_index_.clear();
436 sequence_indexed_slots_ = 0;
437 throw;
438 }
439 }
440 return {iterator(this, index), true};
441 }
442
443 // Replaces an existing row wholesale. Its identity fields may change, so
444 // the derived sequence index is rebuilt on the next lookup.
445 void replace(std::uint64_t incarnation, PlacementSnapshot&& snapshot) {
446 at(incarnation) = std::move(snapshot);
447 sequence_index_.clear();
448 sequence_indexed_slots_ = 0;
449 }
450
451 // Smallest command_sequence among the rows whose source_id is `source_id`:
452 // over the rows bound to `from_entry` when any is, else over all of them;
453 // UINT64_MAX when no row carries the id. Equal to a full scan of the
454 // table, but served from a derived per-id index extended over the rows
455 // appended since the previous lookup, so a per-bar caller no longer pays
456 // O(rows) per call. The index relies on a stored row's source_id /
457 // from_entry / command_sequence never being written in place (only
458 // replace() rewrites a row); it is a pure lookup over the hashed rows,
459 // never next-decision state.
460 std::uint64_t command_sequence_for(const std::string& source_id,
461 const std::string& from_entry) const {
462 for (; sequence_indexed_slots_ < slots_.size(); ++sequence_indexed_slots_) {
463 const auto& slot = slots_[sequence_indexed_slots_];
464 if (slot) fold_sequence(*slot);
465 }
466 constexpr auto absent = std::numeric_limits<std::uint64_t>::max();
467 const auto minima = sequence_index_.find(source_id);
468 if (minima == sequence_index_.end()) return absent;
469 const auto paired = minima->second.by_from_entry.find(from_entry);
470 if (paired != minima->second.by_from_entry.end() && paired->second != absent)
471 return paired->second;
472 return minima->second.any;
473 }
474
475private:
476 struct SequenceMinima {
477 std::uint64_t any = std::numeric_limits<std::uint64_t>::max();
478 std::unordered_map<std::string, std::uint64_t> by_from_entry;
479 };
480
481 void fold_sequence(const PlacementSnapshot& row) const {
482 auto& minima = sequence_index_[row.source_id];
483 minima.any = std::min(minima.any, row.command_sequence);
484 const auto inserted = minima.by_from_entry.emplace(row.from_entry, row.command_sequence);
485 if (!inserted.second)
486 inserted.first->second = std::min(inserted.first->second, row.command_sequence);
487 }
488
489 std::vector<std::optional<PlacementSnapshot>> slots_;
490 std::size_t size_ = 0;
491 // Derived lookup cache for command_sequence_for(); see there.
492 mutable std::unordered_map<std::string, SequenceMinima> sequence_index_;
493 mutable std::size_t sequence_indexed_slots_ = 0;
494};
495
496// Append-only roster of the opening requests a source id ever accepted, in
497// acceptance order. Over a long run it holds every historical entry, while
498// the pending-origin and live-exposure queries only concern the few handles
499// still live; the per-incarnation position index lets those queries visit
500// just their positions, in roster order, instead of the whole history.
502public:
503 using const_iterator = std::vector<native_order::RequestHandle>::const_iterator;
505 std::vector<native_order::RequestHandle>::const_reverse_iterator;
506
508 auto& positions = positions_[handle.incarnation];
509 positions.push_back(items_.size());
510 try {
511 items_.push_back(handle);
512 } catch (...) {
513 positions.pop_back();
514 throw;
515 }
516 }
517 const std::vector<native_order::RequestHandle>& members() const noexcept { return items_; }
518 const native_order::RequestHandle& operator[](std::size_t index) const { return items_[index]; }
519 const native_order::RequestHandle& back() const { return items_.back(); }
520 bool empty() const noexcept { return items_.empty(); }
521 std::size_t size() const noexcept { return items_.size(); }
522 const_iterator begin() const noexcept { return items_.begin(); }
523 const_iterator end() const noexcept { return items_.end(); }
524 const_reverse_iterator rbegin() const noexcept { return items_.rbegin(); }
525 const_reverse_iterator rend() const noexcept { return items_.rend(); }
526
527 // Ascending roster positions of the entries equal to `handle`.
529 std::vector<std::size_t>& out) const {
530 const auto found = positions_.find(handle.incarnation);
531 if (found == positions_.end()) return;
532 for (const auto position : found->second)
533 if (items_[position] == handle) out.push_back(position);
534 }
535 // Ascending roster positions of the entries carrying `incarnation`, or
536 // null when none does. Allocation-free.
537 const std::vector<std::size_t>* positions_of(std::uint64_t incarnation) const noexcept {
538 const auto found = positions_.find(incarnation);
539 return found == positions_.end() ? nullptr : &found->second;
540 }
541
542private:
543 std::vector<native_order::RequestHandle> items_;
544 // Derived lookup over items_ (never next-decision state).
545 std::unordered_map<std::uint64_t, std::vector<std::size_t>> positions_;
546};
547
548// Ordered member list of one source bracket family. Members are appended per
549// accepted leg and only removed when cancelled, so the list retains every
550// historical leg while removals target the few live ones near its tail. The
551// per-incarnation member count bounds a removal to the suffix that holds the
552// matching members.
554public:
555 using const_iterator = std::vector<native_order::RequestHandle>::const_iterator;
556
558 auto& count = counts_[handle.incarnation];
559 items_.push_back(handle);
560 ++count;
561 }
562 const std::vector<native_order::RequestHandle>& members() const noexcept { return items_; }
563 bool empty() const noexcept { return items_.empty(); }
564 std::size_t size() const noexcept { return items_.size(); }
565 const_iterator begin() const noexcept { return items_.begin(); }
566 const_iterator end() const noexcept { return items_.end(); }
567
568 // Erases every member equal to one of `doomed`, keeping the order of the
569 // rest: the same result as erase(remove_if(find in doomed)).
570 void remove_all(const std::vector<native_order::RequestHandle>& doomed) {
571 std::vector<std::uint64_t> incarnations;
572 std::size_t sharing = 0;
573 for (const auto& handle : doomed) {
574 if (std::find(incarnations.begin(), incarnations.end(), handle.incarnation)
575 != incarnations.end()) continue;
576 incarnations.push_back(handle.incarnation);
577 const auto found = counts_.find(handle.incarnation);
578 if (found != counts_.end()) sharing += found->second;
579 }
580 if (sharing == 0) return;
581 // Lowest position holding a member that shares a doomed incarnation.
582 std::size_t first = items_.size();
583 while (sharing > 0 && first > 0) {
584 --first;
585 if (std::find(incarnations.begin(), incarnations.end(), items_[first].incarnation)
586 != incarnations.end()) --sharing;
587 }
588 std::size_t out = first;
589 for (std::size_t in = first; in < items_.size(); ++in) {
590 if (std::find(doomed.begin(), doomed.end(), items_[in]) != doomed.end()) {
591 const auto found = counts_.find(items_[in].incarnation);
592 if (--found->second == 0) counts_.erase(found);
593 continue;
594 }
595 if (out != in) items_[out] = items_[in];
596 ++out;
597 }
598 items_.resize(out);
599 }
600
601private:
602 std::vector<native_order::RequestHandle> items_;
603 // Derived member multiplicity per incarnation (never next-decision state).
604 std::unordered_map<std::uint64_t, std::size_t> counts_;
605};
606
615 double seed_qty = std::numeric_limits<double>::quiet_NaN();
616 std::int64_t seed_cycle = 0;
617 bool active = false;
618 // Generic matching must execute the artifact before the final source
619 // short. For variable-size source books their acceptance handles are
620 // consequently opposite to their source report incarnations; retain the
621 // pending source-report projection until the remnant is closed.
623};
624
625// The legacy three-object qualification is evaluated at the next broker open
626// (`created_bar + 1 == bar_index`), so the complete source candidate waits
627// here until that live fact is available. It is not an executable order.
630 std::uint64_t expected_open_epoch = 0;
631 bool ready = false;
632};
633
636 std::string comment{};
637 double qty = std::numeric_limits<double>::quiet_NaN();
638 double qty_percent = std::numeric_limits<double>::quiet_NaN();
639 bool immediately = false;
640 std::uint64_t callsite_token = 0;
641 std::uint64_t command_ordinal = 0;
642};
643
650
652 std::int64_t current_day = std::numeric_limits<std::int64_t>::min();
653 std::int64_t last_loss_day = std::numeric_limits<std::int64_t>::min();
655 std::int64_t intraday_loss_day = std::numeric_limits<std::int64_t>::min();
656 double intraday_start_equity = std::numeric_limits<double>::quiet_NaN();
657 double intraday_realized = 0.0;
658 std::uint64_t observed_applied_ordinal = 0;
659};
660
662 int direction = 0; // 0 both, >0 long, <0 short
664 double max_drawdown = 0.0;
666 double max_intraday_loss = 0.0;
668 double max_position_size = 0.0;
669 bool halted = false;
670 // Runtime facts belong to the source policy, not to the generic
671 // settlement kernel. They are updated from public native projections at
672 // the same broker coordinates at which the retired source route updated
673 // its latches.
674 double observed_peak_equity = std::numeric_limits<double>::quiet_NaN();
676 std::int64_t intraday_block_day = std::numeric_limits<std::int64_t>::min();
678};
679
680// ab9714be pine_risk.cpp:256-292 (update_per_trade_extremes): the source
681// host's per-lot excursion sampler. Folds one completed source bar's H/L/C
682// into every open lot, honoring the entry-bar masks. Shared by the bar-close
683// walk, the stream tick walk and the margin-call submit preload.
684void sample_open_trade_extremes(std::vector<PyramidEntry>& lots,
685 PositionSide side, int bar_index, const Bar& bar);
686Bar margin_call_sample_bar(const Bar& bar, double fire_price, bool prefix_sample,
687 bool high_first, double mintick = 0.0, int slippage = 0);
688
690
691// Allocation-free view facade for Appendix C's later C projection. L2 does
692// not wire the C ABI; this only exposes named truthful sources to native
693// fixture tests and keeps observer reads side-effect-free.
695public:
696 int size() const noexcept;
697 int probe_fill_qty(int index, double fill_price, double* qty,
698 int* close_only, int* partition) const noexcept;
699 int level_resolved(int index) const noexcept;
700 int effective_levels(int index, double* stop, double* limit,
701 double* trail_activation) const noexcept;
702 int copy_v1(int index, pf_pending_order_v1_t* out) const noexcept;
703 int short_seed_collision_role(int index) const noexcept;
704 int last_bar_dual_entry_path() const noexcept;
705 double trail_best_price() const noexcept;
706
707private:
709 const PineExecutionAdapter* owner_ = nullptr;
710};
711
713public:
715 std::uint64_t incarnation = 0;
717 bool staged = false;
718 };
720 std::uint64_t token = 0;
721 bool active = false;
722 double target = 0.0;
723 int calls = 0;
725 std::string comment{};
726 std::uint64_t queue_sequence = 0;
727 };
728 // Keep the legacy host's construction surface valid until L3a. A null
729 // host means this compatibility carrier has no lowering authority.
730 explicit PineExecutionAdapter(
734
735 void bind(NativeStrategyHost& host) noexcept;
736 void reset_for_run();
737 void set_configuration(const PineStrategyConfig& config) noexcept;
739 void set_begin_mode(bool is_stream, bool bar_magnifier = false) noexcept;
740 void set_path_order(NativePathOrder path_order) noexcept;
741
743 const NativeBeginArgs&,
744 NativePathOrder path_order = NativePathOrder::Auto) const;
745
746 void entry(const SourceId& id, bool is_long,
747 double limit_price = std::numeric_limits<double>::quiet_NaN(),
748 double stop_price = std::numeric_limits<double>::quiet_NaN(),
749 double qty = std::numeric_limits<double>::quiet_NaN(),
750 const std::string& comment = {}, const std::string& oca_name = {},
751 int oca_type = 0, int qty_type = -1);
752 void close(const SourceId& id, const std::string& comment = {},
753 double qty = std::numeric_limits<double>::quiet_NaN(),
754 double qty_percent = std::numeric_limits<double>::quiet_NaN(),
755 bool immediately = false, std::uint64_t callsite_token = 0);
756 void close_all();
757 void exit(const SourceId& exit_id, const SourceId& from_entry,
758 double limit_price, double stop_price,
759 double trail_points = std::numeric_limits<double>::quiet_NaN(),
760 double trail_offset = std::numeric_limits<double>::quiet_NaN(),
761 double trail_price = std::numeric_limits<double>::quiet_NaN(),
762 double qty_percent = 100.0, const std::string& comment = {},
763 double qty = std::numeric_limits<double>::quiet_NaN(),
764 const std::string& oca_name = {},
765 double profit_ticks = std::numeric_limits<double>::quiet_NaN(),
766 double loss_ticks = std::numeric_limits<double>::quiet_NaN());
767 void exit_cancel_bracket(const SourceId& exit_id, const SourceId& from_entry,
768 const std::string& comment = {});
769 void cancel(const SourceId& id);
770 void cancel_all();
771 void order(const SourceId& id, bool is_long, double qty,
772 double limit_price = std::numeric_limits<double>::quiet_NaN(),
773 double stop_price = std::numeric_limits<double>::quiet_NaN(),
774 const std::string& oca_name = {}, int oca_type = 0);
775
776 native_order::ExecutionTerms resolve_terms(const NativeExecutionTermsFacts&) const;
777 // R5 lane R4d. anchor_relative_exits runs at the end of a source
778 // evaluation: every queued relative strategy.exit whose parent entry is a
779 // live kernel request is submitted as that parent's anchored bracket
780 // child. resolve_anchored_level is the kernel's once-per-arm level hook:
781 // the kernel computes fill + ticks on the tick ladder, and TradingView's
782 // spelling of that ladder point and its trigger projection (the half-tick
783 // threshold exit() also applies) are restated there.
785 std::optional<double> resolve_anchored_level(const NativeAnchoredLevelView&) const;
786 // How many relative legs this run placed as anchored kernel children, how
787 // many of those the fill-point re-run adopted as its own request, and how
788 // many were withdrawn instead (parent ended, definition changed, or the
789 // fill point asked for another request).
791 std::uint64_t anchored = 0;
792 std::uint64_t adopted = 0;
793 std::uint64_t withdrawn = 0;
794 };
796 return anchored_relative_stats_;
797 }
798 // R5 R2 classification, answered for the command a default-sized MARKET
799 // entry on this side would write at the CURRENT execution point: true when
800 // the core's native_order::Sized names the whole conversion, so the units
801 // that settle are the core's and only the source lot floor and the source
802 // admission gates run on top. Pure in the run's declaration, the staged
803 // instrument and that point; nothing is remembered.
804 bool core_sizes_default_opening(bool is_long) const;
806 // R5: the three policy answers the kernel's margin model asks of its
807 // host. `margin_check_allowed` is TradingView's scheduling -- the kernel
808 // offers its own check points and the adapter admits only the ones
809 // TradingView would also check at; `resolve_margin_requirement` is
810 // TradingView's money (its fee-adjusted equity basis and its
811 // ten-significant-digit requirement); `resolve_margin_call_units` is its
812 // sizing (the lot-floored 4x restore and the family R whole-drop band).
813 // TradingView's margin money at one mark: the requirement (on its
814 // ten-significant-digit ladder where a lot is worth under one unit of
815 // account) against its fee-adjusted live equity. One implementation, read
816 // by the adapter's own checkpoints and by the kernel's requirement hook.
818 bool valid = false;
819 double mark = 0.0;
820 double held = 0.0;
821 double unit_margin = 0.0;
822 double exact_required = 0.0;
823 double required = 0.0;
824 double equity = 0.0;
825 };
826 SourceMarginMoney source_margin_money(double mark_price,
827 std::int64_t sub_bar_open_ms) const;
828 double source_margin_units(const SourceMarginMoney&, bool opening_checkpoint) const;
829 double source_margin_fill_price(double fire, bool close_is_buy) const;
830
833 std::optional<NativeMarginDecision> resolve_margin_requirement(
834 const NativeMarginRequirementView&) const;
835 std::optional<double> resolve_margin_call_units(const NativeMarginCallView&) const;
836 // True when the request is a source exit leg carrying priced stop, limit
837 // or trailing terms (L10j): its trade row folds the pre-fill path extremes.
838 bool source_priced_exit(std::uint64_t incarnation) const noexcept;
839 bool source_post_parent_calc_level_fill(std::uint64_t incarnation) const noexcept;
840 std::optional<double> source_trail_offset_ticks(std::uint64_t incarnation) const noexcept;
841 bool source_margin_exit(std::uint64_t incarnation) const noexcept;
842 static bool source_kernel_liquidation(const native_order::DefinitionRef&) noexcept;
843 bool has_pending_market_exit(int current_interval_index = -1) const noexcept;
844 // The carried 1x long's opening money call precedes the bar's excursion
845 // sample only ahead of one resting full-position priced exit that the
846 // open does not reach (ab9714be pine_fills.cpp:164-218).
848 double held_units) const;
849 void on_bar_open(const Bar&, const NativeDecisionContext&);
850 void on_tick(const Bar&, const NativeTickContext&);
851 // Called from the generic calculation callback after the source script
852 // has returned while the current decision point remains executable.
853 void on_bar_close(const Bar&, const NativeDecisionContext&);
854 void on_applied(const native_order::ExecutionAppliedEvent&, const NativeDecisionContext&);
855 void source_batch_end();
856
857 // Fixture/public projection of the chart-timezone day decomposition used
858 // by the risk and intraday-cap ledgers. It carries no generic-kernel
859 // policy and lets native-route tests avoid reaching into retired host
860 // internals.
861 std::int64_t chart_day_key(std::int64_t timestamp_ms) const noexcept;
862
863 int short_seed_collision_role_v1(native_order::RequestHandle) const noexcept;
864 const PendingIntentView& pending_intent_view() const noexcept { return pending_view_; }
865 // Read-only fixture facade for command-boundary rows that have not yet
866 // become live native requests. It never participates in matching.
867 std::vector<FixturePendingSnapshot> fixture_pending_snapshots() const;
868 // Fixture-only read of the source cohort's currently live quantity. It
869 // projects the adapter's truthful opening facts; it does not recreate the
870 // deleted executable id ledger.
871 double source_unclosed_qty_for(const SourceId& id) const noexcept {
872 return cohort_exposure_for(id);
873 }
874 int source_entry_slot_count() const noexcept;
875 double fixture_close_logical_units(const SourceId&) const noexcept;
876 double fixture_close_reserved_units(const SourceId&) const noexcept;
877 double fixture_close_first_units(const SourceId&) const noexcept;
879 std::uint64_t, const SourceId&) const noexcept;
881 std::uint64_t, const SourceId&) const noexcept;
882 std::size_t fixture_close_reservation_count() const noexcept;
883 std::size_t fixture_close_first_count() const noexcept;
884 std::size_t fixture_close_logical_count() const noexcept {
885 return close_logical_units_.size();
886 }
887 std::size_t fixture_callsite_close_reservation_count() const noexcept;
888 std::size_t fixture_callsite_close_first_count() const noexcept;
889 double fixture_callsite_close_reserved_total() const noexcept;
890 double fixture_close_pending_debt() const noexcept {
891 return close_batch_pending_debt_;
892 }
893 double fixture_close_admitted_total() const noexcept {
894 return close_batch_admitted_total_;
895 }
896 std::vector<FixtureCloseCallsite> fixture_close_callsites() const;
897
898 void set_risk_direction(int direction) noexcept;
899 void set_risk_max_cons_loss_days(int value) noexcept;
900 void set_risk_max_drawdown(double value, bool percent) noexcept;
901 void set_risk_max_intraday_loss(double value, bool percent) noexcept;
902 void set_risk_max_position_size(double value) noexcept;
903 bool allows_risk_direction(bool is_long) const noexcept {
904 return risk_.direction == 0 || (is_long ? risk_.direction > 0 : risk_.direction < 0);
905 }
906 bool max_drawdown_is_percent() const noexcept { return risk_.max_drawdown_percent; }
907 bool max_intraday_loss_is_percent() const noexcept {
908 return risk_.max_intraday_loss_percent;
909 }
910 bool take_intraday_loss_relabel(std::uint64_t ordinal) noexcept;
911 void set_margin_call_enabled(bool enabled) noexcept;
912 // KI-62 (moved out of PyramidEntry by R5 lane L12, 2.ii j): the physical
913 // lots opened by request incarnation `incarnation` came from a
914 // same-direction MARKET pyramid add — not the base open, not a priced
915 // entry. When a from_entry priced bracket exit fills on such an add's OWN
916 // entry bar, and after it in TV's open-tick fill sequence, the exit covers
917 // (scratches) the add dur-0. The reader also gates on
918 // `entry_bar_index == bar`, so prior-bar slices are never covered. This
919 // is TradingView provenance, so it is adapter state keyed by the lot's
920 // entry incarnation rather than a field on the generic lot.
921 void mark_market_pyramid_add(std::uint64_t incarnation);
922 bool market_pyramid_add(std::uint64_t incarnation) const noexcept {
923 return market_pyramid_adds_.count(incarnation) != 0;
924 }
925 void enable_intraday_cap() noexcept;
926 void attach_execution_adapter() noexcept;
927 bool calc_on_order_fills() const noexcept { return config_.calc_on_order_fills; }
928 bool process_orders_on_close() const noexcept { return config_.process_orders_on_close; }
929 // Read-only L4c fixture observations. They expose callback coordinates
930 // already owned by the adapter; no test path can mutate the native book.
931 bool fixture_coof_recalc_active() const noexcept { return coof_recalc_active_; }
932 bool fixture_coof_cursor_is_bar_close() const noexcept {
933 return coof_recalc_active_ && coof_context_.coordinate.path_phase == NativePathPhase::Close;
934 }
937 void begin_source_evaluation() noexcept {
938 named_entry_cancel_tokens_.clear();
939 pending_same_bar_close_qty_ = 0.0;
940 source_batch_mutated_ = false;
941 }
942 bool fixture_named_entry_cancel_active(const SourceId& id) const noexcept {
943 return named_entry_cancel_tokens_.find(id) != named_entry_cancel_tokens_.end();
944 }
945 std::vector<native_order::RequestHandle> take_first_open_newborns();
946 // Pull terminal generic receipts before a source callback observes the
947 // next command boundary. This retires group-cancelled bracket siblings
948 // and pending origins rejected by native admission.
950 // Called by the fixture scheduler after one source script evaluation so
951 // re-priced carried bracket legs retain their original roster order before
952 // newly pending-entry legs are appended.
954 native_order::RequestHandle just_applied = {},
955 bool post_calculation = true,
956 bool pre_script_drain = false);
957 // Ordinary POOC same-direction adds are held until the source evaluation
958 // closes, so a later close_all in that same evaluation settles first and
959 // the add opens the next source position at the same close point.
963 bool explicit_brackets_only = false,
964 double current_open = std::numeric_limits<double>::quiet_NaN());
966 const NativeDecisionContext&, bool first_open,
967 std::uint64_t source_fill_sequence);
968 void end_coof_recalc() noexcept;
970 const native_order::ExecutionAppliedEvent&,
971 const NativeDecisionContext&) const noexcept;
972
973 std::uint64_t command_sequence_for_exit(const SourceId& exit_id,
974 const SourceId& from_entry = {}) const noexcept;
975 bool is_open_phase_exit(std::size_t trade_index) const noexcept;
976 // Reorders trade_exit_phase_[start..) to follow a same-bar exit-group sort:
977 // slot start + i takes the phase of the trade previously at indices[i].
978 void permute_exit_phases(std::size_t start, const std::vector<std::size_t>& indices);
979 void hash_state(BrokerStateHashSink&) const;
980
981 // Retained for the untouched legacy source host. New fixture state is
982 // represented by the private maps below rather than this carrier alone.
986
987private:
988 friend class PendingIntentView;
989 friend class PineStrategyHost;
990 friend class PineScheduler;
991 struct CohortFacts {
993 OriginRoster origins;
994 std::vector<native_order::RequestHandle> opened;
995 // Live source exposure by opening provenance. This is source-layer
996 // bookkeeping only: the generic core still resolves cohort authority
997 // at every candidate. It lets a command snapshot an existing id's
998 // percentage basis before later requests in the same script pass
999 // reduce that cohort.
1000 std::unordered_map<std::uint64_t, double> live_units_by_origin;
1001 std::int64_t cycle = 0;
1002 };
1003
1004 struct PendingBracketLeg {
1005 native_order::Request request;
1006 PlacementSnapshot snapshot;
1007 SourceId replacement_key;
1008 std::uint64_t family_key = 0;
1009 };
1010
1011 struct PendingEntry {
1012 native_order::Request request;
1013 PlacementSnapshot snapshot;
1014 SourceId replacement_key;
1015 };
1016
1017 struct DelayedMarketOrder {
1018 native_order::Request request;
1019 PlacementSnapshot snapshot;
1020 SourceId replacement_key;
1021 std::uint64_t release_open_epoch = 0;
1022 bool execute_at_open = false;
1023 };
1024
1025 // A36/A28: a sell-side stop that is already marketable at the open, while
1026 // a buy-side open-marketable stop of the same flat pair is still live,
1027 // is held out of kernel matching (legacy opposing-stop pass-0 deferral)
1028 // and admitted after the bar path so later-path same-direction legs can
1029 // pyramid first. forced_execution_price keeps the open fill.
1030 struct DeferredOpenMarketableSell {
1031 PlacementSnapshot snapshot;
1032 SourceId replacement_key;
1033 double fill_price = std::numeric_limits<double>::quiet_NaN();
1034 double path_position = 0.0;
1035 bool open_marketable = false;
1036 };
1037
1038 // The legacy same-bar MARKET transaction is a source-side command batch:
1039 // all BUY members are admitted before SELL members at the next broker
1040 // open, while each member retains its placement-time physical quantity.
1041 // Keep the batch outside the generic core; it contains source ids and the
1042 // targeted-close artifact that the generic request model must not learn.
1043 struct PendingSameBarCommand {
1044 native_order::Request request;
1045 PlacementSnapshot snapshot;
1046 SourceId replacement_key;
1047 bool opening = false;
1048 };
1049
1050 // A source command can remain observable through the enclosing source
1051 // evaluation after generic admission has already produced its terminal
1052 // receipt. This is a source projection row, never a second executable
1053 // request; it expires at the following broker open.
1054 struct SourceShadowPending {
1055 PlacementSnapshot snapshot;
1056 std::string label;
1057 };
1058
1059 struct PendingRelativeExit {
1060 SourceId exit_id;
1061 SourceId from_entry;
1062 double trail_points = std::numeric_limits<double>::quiet_NaN();
1063 double trail_offset = std::numeric_limits<double>::quiet_NaN();
1064 double trail_price = std::numeric_limits<double>::quiet_NaN();
1065 double qty_percent = 100.0;
1066 std::string comment;
1067 double qty = std::numeric_limits<double>::quiet_NaN();
1068 std::string oca_name;
1069 double profit_ticks = std::numeric_limits<double>::quiet_NaN();
1070 double loss_ticks = std::numeric_limits<double>::quiet_NaN();
1071 };
1072
1073 // R5 lane R4d: one relative leg of a queued strategy.exit, spelled as the
1074 // kernel's anchored bracket child: the host-sized book close exit()
1075 // submits at the parent's fill, placed ahead of it (FromOwnerFill under
1076 // WaitForApplied{parent, PendingUntilArmed, AfterArmPrint, Book}). Until
1077 // its parent fills it is a kernel request only: no placement row, no live
1078 // handle, so every source book (reservations, admission, the pending
1079 // projection) reads exactly what it read before. `installed_level` is the
1080 // trigger level the arm hook restated; the fill-point exit() re-run
1081 // adopts the armed request when it is the request it would submit (same
1082 // trigger bits, same group, a host-sized book close), and withdraws it
1083 // otherwise.
1084 struct AnchoredRelativeLeg {
1085 SourceId exit_id;
1086 SourceId from_entry;
1088 native_order::RequestHandle handle{};
1089 native_order::RequestHandle parent{};
1090 bool parent_long = true;
1091 // The source tick operand this leg was anchored from (profit, loss, or
1092 // the ceiled trail_points) and, for a trailing leg, the source offset.
1093 double operand_ticks = 0.0;
1094 double trail_offset = std::numeric_limits<double>::quiet_NaN();
1095 native_order::Request request;
1096 bool armed = false;
1097 double installed_level = std::numeric_limits<double>::quiet_NaN();
1098 };
1099
1100 struct PendingCoofRequest {
1101 native_order::Request request;
1102 PlacementSnapshot snapshot;
1103 SourceId replacement_key;
1104 bool opening = false;
1105 std::uint64_t family_key = 0;
1106 bool next_open = false;
1107 };
1108
1109 struct PendingMarginRevival {
1110 PlacementSnapshot snapshot;
1111 std::int32_t decline_bar = -1;
1112 };
1113
1114 struct NamedEntryCancelToken {
1115 std::uint64_t entry_incarnation = 0;
1116 std::uint64_t surviving_exit_incarnation = 0;
1117 };
1118
1119 struct CloseCallsiteState {
1120 bool active = false;
1121 std::uint64_t token = 0;
1122 int calls = 0;
1123 SourceId first_id{};
1124 double first_target = 0.0;
1125 bool first_ledger_consumed = false;
1126 bool first_carry_valid = false;
1127 double first_carry_qty = 0.0;
1128 SourceId id{};
1129 std::string comment{};
1130 double target = 0.0;
1131 bool retire_ledger_whole = false;
1132 std::uint64_t queue_sequence = 0;
1133 std::vector<SourceId> deferred_cleanup_ids;
1134 };
1135
1136 NativeStrategyHost& require_host() const;
1137 native_order::CohortHandle cohort_for(const SourceId& id);
1138 std::optional<native_order::RequestHandle> submit_or_replace(
1139 native_order::Request request, PlacementSnapshot snapshot, bool opening,
1140 const SourceId& replacement_key = {});
1141 void remember(const native_order::RequestHandle&, PlacementSnapshot);
1142 void retire(native_order::RequestHandle) noexcept;
1143 std::vector<native_order::RequestHandle> openings_for(const SourceId&) const;
1144 double cohort_exposure_for(const SourceId&) const noexcept;
1145 bool from_entry_filled_this_cycle(const SourceId&) const noexcept;
1146 double percent_commission_live_equity(double) const noexcept;
1147 double quantize_close_units(double basis, double percent) const noexcept;
1148 double quantize_percent_exit_units(double requested,
1149 double available) const noexcept;
1150 bool compute_exit_reservation(const SourceId& exit_id,
1151 const SourceId& from_entry,
1152 double requested_qty,
1153 double& qty_percent,
1154 double live_basis,
1155 double& reserved_qty) const;
1156 void reconcile_deferred_exit_reservations(const SourceId& from_entry,
1157 double live_basis);
1158 double active_staged_fx(std::int64_t) const noexcept;
1159 void apply_fx_open_margin_slice(const Bar&, const NativeDecisionContext&);
1160 void apply_fx_opening_margin_slice(const native_order::ExecutionAppliedEvent&,
1161 const NativeDecisionContext&);
1162 void submit_fx_margin_slice(const Bar&, const NativeDecisionContext&, double rate,
1163 bool execute_at_current);
1164 void schedule_preopen_margin_slice(const Bar&, const NativeDecisionContext&);
1165 bool submit_margin_call_slice(double mark_price, const NativeDecisionContext&,
1166 bool opening_checkpoint = false);
1167 bool submit_margin_call_units(double mark_price, const NativeDecisionContext&,
1168 double units,
1169 bool force_execution_price = true);
1170 bool submit_tv_money_long_margin_call(const Bar&, const NativeDecisionContext&);
1171 bool slipped_pooc_opening_money_scope(
1172 const Bar&, const NativeDecisionContext&) const;
1173 bool submit_slipped_pooc_opening_money_call(
1174 const Bar&, const NativeDecisionContext&);
1175 bool schedule_tv_money_long_margin_before_trail(
1176 const Bar&, const NativeDecisionContext&);
1177 bool market_orders_pending_at_close(const NativeDecisionContext& context,
1178 std::uint64_t except_incarnation = 0) const;
1179 bool carried_pooc_short_margin_before_script_scope(
1180 const NativeDecisionContext&) const;
1181 bool carried_pooc_short_priced_exit_after_adverse_scope(
1182 const Bar&) const;
1183 bool defer_rounded_pooc_short_margin_until_close(const Bar&) const;
1184 bool declined_reversal_at_open(const Bar&) const;
1185 bool schedule_margin_call_path(const Bar&, const NativeDecisionContext&);
1186 void defer_declined_reversal_exits_at_adverse(const Bar&,
1187 const NativeDecisionContext&,
1188 bool margin_scheduled);
1189 bool intraday_loss_breached(double mark_price) const noexcept;
1190 bool submit_intraday_loss_close(double mark_price, const NativeDecisionContext&,
1191 bool execute_current);
1192 void schedule_intraday_loss_path(const Bar&, const NativeDecisionContext&);
1193 void update_risk_state(double mark_price);
1194 bool intraday_loss_orders_blocked() const noexcept;
1195 compat::pine::CapClock cap_clock(const NativeDecisionContext&) const;
1196 compat::pine::Calculation cap_calculation(const NativeDecisionContext&) const;
1197 compat::pine::MatchedAttempt cap_attempt(
1198 const PlacementSnapshot&, std::uint64_t incarnation,
1199 const native_order::ExecutionAppliedEvent* applied = nullptr) const;
1200 bool cap_placement_denied(const NativeDecisionContext&);
1201 void observe_intraday_cap(const native_order::ExecutionAppliedEvent&,
1202 const PlacementSnapshot&, const NativeDecisionContext&);
1203 void observe_intraday_cap_noop(bool is_long, const NativeDecisionContext&);
1204 void execute_cap_close_now(const compat::pine::CloseNow&);
1205 void execute_due_cap_close(const NativeDecisionContext&);
1206 void maybe_activate_short_seed_plan();
1207 void activate_short_seed_plan_at_open(const NativeDecisionContext&);
1208 bool qualify_short_seed_plan(const ShortSeedPlan&) const;
1209 bool short_seed_context_is_live() const noexcept;
1210 void record_dropped_close(const SourceId&, const std::string&, double, double,
1211 bool, std::uint64_t);
1212 void record_opening_fee(const PlacementSnapshot&,
1213 const native_order::ExecutionAppliedEvent&);
1214 void consume_opening_fees(const native_order::ExecutionAppliedEvent&,
1215 const SourceId*);
1216 void consume_cohort_units(const SourceId&, const native_order::ExecutionAppliedEvent&);
1217 void consume_closed_trade_rows(const native_order::ExecutionAppliedEvent&,
1218 const PlacementSnapshot*);
1219 bool origin_is_pending(const native_order::RequestHandle&) const noexcept;
1220 // Roster positions, latest first, of the cohort's origins that are live
1221 // handles. origin_is_pending() accepts only live handles, so walking these
1222 // positions meets exactly the pending origins a reverse walk of the whole
1223 // roster meets, in the same order.
1224 std::vector<std::size_t> live_origin_positions(const CohortFacts&) const;
1225 void cancel_bracket_origin(native_order::RequestHandle);
1226 void cancel_bracket_siblings(native_order::RequestHandle);
1227 void cancel_exit_orders_for_full_close(const SourceId& from_entry);
1228 void retire_in_position_exits_at_flat(bool preserve_pending_parents,
1229 bool dormant_rows_only,
1230 const PlacementSnapshot* paired_close);
1231 void materialize_relative_exits(PlacementSnapshot,
1232 const native_order::ExecutionAppliedEvent&);
1233 void withdraw_anchored_relative_legs(const SourceId* exit_id,
1234 const SourceId* from_entry);
1235 bool anchorable_relative_exit(const PendingRelativeExit&,
1236 native_order::RequestHandle& parent,
1237 bool& parent_long) const;
1238 struct RelativeLegShape {
1240 native_order::Trigger trigger = native_order::Limit{};
1241 double signed_ticks = 0.0;
1242 double operand_ticks = 0.0;
1243 };
1244 std::vector<RelativeLegShape> relative_leg_shapes(const PendingRelativeExit&,
1245 bool parent_long) const;
1246 bool armed_relative_legs_adoptable(const PlacementSnapshot& opening,
1247 const native_order::ExecutionAppliedEvent&,
1248 const std::vector<PendingRelativeExit>&) const;
1249 // TradingView's trigger projection of an exit level, one spelling for
1250 // exit() and for the kernel's arm hook: the limit leg's tick-quantized
1251 // threshold (raw on-grid under calc_on_order_fills) and the one-shot
1252 // trail activation touch.
1253 double exit_limit_trigger(double limit_price, double tick, bool exit_is_buy) const noexcept;
1254 double one_shot_trail_trigger(double activation, double source_trail_offset, double tick,
1255 bool exit_is_buy, bool quantized_activation) const noexcept;
1256 void materialize_pending_bracket_legs(
1257 const native_order::ExecutionAppliedEvent&);
1258 void stage_flat_children_before_parent(const SourceId&, std::int32_t,
1259 std::int64_t);
1260 bool defer_coof_tail() const noexcept;
1261 bool source_path_uses_high_first(const Bar&) const noexcept;
1262 bool coof_fill_on_path_point() const noexcept;
1263 bool coof_fill_at_path_point(double waypoint) const noexcept;
1264 bool coof_current_fill_was_forced_waypoint() const noexcept;
1265 double coof_next_waypoint(int* path_index = nullptr) const noexcept;
1266 double next_coof_waypoint_price() const noexcept;
1267 bool coof_remaining_recrosses(double level, bool long_position) const noexcept;
1268 void flush_coof_tail(bool openings_only = false,
1269 bool include_next_open = false);
1270 native_order::Owner owner_for_close(const SourceId&, bool dynamic) const;
1271 bool same_bar_market_tx_scope() const;
1272 void flush_pending_same_bar_commands();
1273 // The placement-time default quantity: the core's own conversion, read as
1274 // a query (NativeStrategyHost::native_sized_units) and floored by the
1275 // source. The source's money band and affordability gates consume the
1276 // number before any request exists, which is why it is computed here at
1277 // all; the arithmetic itself exists once, in the core (R5 N11).
1278 double default_sizing_units(const PineSizingSnapshot&) const;
1279 // The three pieces of the default quantity the source keeps as its own
1280 // policy: the money the percentage is taken of (with its equity mark and
1281 // its ten-significant-digit rounding), whether a percentage fee is
1282 // reserved out of it, and the lot floor applied to the core's quotient.
1283 double default_sizing_cash(const PineSizingSnapshot&) const noexcept;
1284 bool default_sizing_reserves_percent_fee() const noexcept;
1285 double default_sizing_lot_floor(double units) const noexcept;
1286 // The core intent a default-quantity declaration spells -- the source
1287 // money as a CashValue basis, the core's fee reserve, the raw quotient --
1288 // and the sided intent a re-lowerable opening carries. std::nullopt keeps
1289 // the host-resolved shape for every path the core cannot name.
1290 std::optional<native_order::Sized> default_sizing_shape(
1291 const PineSizingSnapshot&) const noexcept;
1292 std::optional<native_order::Sized> default_sizing_intent(
1293 const PineSizingSnapshot&, bool is_long) const noexcept;
1294 // True when the price the core will freeze for a Sized opening accepted
1295 // NOW is bit-for-bit the price this snapshot already froze. A command
1296 // deferred to a later execution point, or one whose sizing price the
1297 // source took from somewhere other than the signal rule, fails it and
1298 // keeps its host-resolved intent.
1299 bool core_sizing_price_matches(const PineSizingSnapshot&, bool is_long) const;
1300 double default_market_sizing_price(double mark, bool is_long) const noexcept;
1301 native_order::Trigger trigger_for(double limit_price, double stop_price) const;
1302 native_order::Group group_for(const std::string&, int, std::int64_t = 0) const;
1303 PineSizingSnapshot sizing_snapshot() const;
1304 std::uint64_t key_for(const SourceId&, const SourceId& = {}) const noexcept;
1305 void refresh_pending_view() noexcept;
1306 OrderBirth capture_order_birth() const;
1307 void initialize_l4c_policy(PlacementSnapshot&, native_order::RequestHandle);
1308 void update_l4c_priority();
1309 void update_l4c_lifecycle(const native_order::ExecutionAppliedEvent&,
1310 const NativeDecisionContext&);
1311 using ReceiptHighWaterReader = std::uint64_t (*)(const NativeStrategyHost&) noexcept;
1312 void set_receipt_high_water_readers(ReceiptHighWaterReader event_reader,
1313 ReceiptHighWaterReader terminal_reader) noexcept;
1314
1315 // Derived receipt watermark used only to avoid materializing an owning
1316 // native-events snapshot when no terminal command was appended.
1317 std::uint64_t terminal_receipt_cursor_ = 0;
1318 ReceiptHighWaterReader event_high_water_reader_ = nullptr;
1319 ReceiptHighWaterReader terminal_receipt_high_water_reader_ = nullptr;
1320 bool is_declined_market_reversal(
1321 const native_order::MatchRejectedEvent&) const noexcept;
1322 bool follows_same_bar_declined_reversal(
1323 const PlacementSnapshot&, const NativePrecommitView&) const;
1324 bool bracket_belongs_to_reversal(
1325 const PlacementSnapshot&, const PlacementSnapshot&) const noexcept;
1326 void suspend_brackets_for_reversal(
1327 const PlacementSnapshot&, const exit_legs::Frame&, double open_price);
1328 void suspend_declined_reversal_brackets(
1329 const native_order::MatchRejectedEvent&);
1330 void suspend_coof_declined_reversal_at_open(
1331 const Bar&, const NativeDecisionContext&);
1332 void hold_reversal_pair_brackets(const SourceId&);
1333 void purge_brackets_after_applied_reversal(const PlacementSnapshot&);
1334 void revive_brackets_after_margin(
1335 const native_order::ExecutionAppliedEvent&, const NativeDecisionContext&);
1336 int projected_pending_size() const noexcept;
1337 bool projected_pending_at(int index, const PlacementSnapshot*& snapshot,
1338 native_order::RequestHandle& handle) const noexcept;
1339 int projected_raw_pending_size() const noexcept;
1340 bool projected_raw_pending_at(int index, const PlacementSnapshot*& snapshot,
1341 native_order::RequestHandle& handle) const noexcept;
1342 static bool same_projected_order(const PlacementSnapshot& left,
1343 const PlacementSnapshot& right) noexcept;
1344 void apply_open_market_admission(const NativeDecisionContext&);
1345 void defer_open_marketable_sells(const Bar& bar);
1346 void admit_deferred_open_marketable_sells();
1347 void rearm_throttled_reopens();
1348 void flush_pooc_marketable_limit_entry_fills(const Bar&, const NativeDecisionContext&);
1349 void flush_pooc_marketable_exit_fills(const Bar&, const NativeDecisionContext&);
1350 void record_market_review(admission::Checkpoint, int,
1351 const std::vector<native_order::RequestHandle>&);
1352 void refresh_pending_sizing_after_margin(
1353 const native_order::ExecutionAppliedEvent&, const NativeDecisionContext&);
1354 void apply_reversal_gap_bracket_policy(
1355 const Bar&, const NativeDecisionContext&, bool defer_trails = false);
1356 void reaccept_gapped_bracket_behind_same_id_add(
1357 const Bar&, const NativeDecisionContext&);
1358 void apply_terminal_explicit_market_policy(const NativeDecisionContext&);
1359 bool enqueue_pooc_fifo_close(const SourceId&, const std::string&,
1360 std::uint64_t, std::uint64_t);
1361 double close_reserved_other_units(const SourceId&,
1362 std::uint64_t) const noexcept;
1363 void observe_close_policy(const native_order::ExecutionAppliedEvent&,
1364 const PlacementSnapshot&);
1365
1366 // @source-state begin
1367 NativeStrategyHost* host_ = nullptr;
1368 PineStrategyConfig config_{};
1369 StagedConfiguration staged_{};
1370 NativePathOrder path_order_ = NativePathOrder::Auto;
1371 mutable std::uint64_t run_counter_ = 0;
1372 std::uint64_t source_sequence_ = 0;
1373 std::uint64_t command_ordinal_ = 0;
1374 std::uint64_t broker_open_epoch_ = 0;
1375 std::int64_t last_broker_open_ms_ = std::numeric_limits<std::int64_t>::min();
1376 std::uint64_t source_command_sequence_ = 0;
1377 std::int32_t entry_attempt_bar_ = -1;
1378 std::uint32_t entry_attempts_on_bar_ = 0;
1379 std::unordered_map<SourceId, CohortFacts> cohorts_by_id_;
1380 std::vector<SourceId> cohort_order_;
1381 PlacementTable placement_;
1382 std::unordered_map<std::uint64_t, native_order::RequestHandle> live_by_source_key_;
1383 std::unordered_map<std::uint64_t, BracketRoster> bracket_families_;
1384 std::vector<PendingBracketLeg> pending_bracket_legs_;
1385 std::vector<PendingEntry> pending_entries_;
1386 std::vector<DelayedMarketOrder> delayed_market_orders_;
1387 std::vector<DeferredOpenMarketableSell> deferred_open_marketable_sells_;
1388 // Flat stop entries the per-bar priced-entry throttle refused; re-armed
1389 // as their original stop at the bar close (validate_precommit is const).
1390 mutable std::vector<PlacementSnapshot> throttled_reopen_rearm_;
1391 int entry_openings_interval_index_ = -1;
1392 int entry_openings_this_interval_ = 0;
1393 std::vector<PendingSameBarCommand> pending_same_bar_commands_;
1394 std::vector<SourceShadowPending> source_shadow_pending_;
1395 double pending_same_bar_close_qty_ = 0.0;
1396 std::vector<PendingRelativeExit> pending_relative_exits_;
1397 // Mutable: the kernel's arm hook is const and records the level it
1398 // installed on the leg it restated.
1399 mutable std::vector<AnchoredRelativeLeg> anchored_relative_legs_;
1400 AnchoredRelativeStats anchored_relative_stats_{};
1401 std::int64_t anchored_cohort_sequence_ = 0;
1402 std::vector<PendingCoofRequest> pending_coof_requests_;
1403 std::vector<PendingMarginRevival> pending_margin_revivals_;
1404 std::vector<native_order::RequestHandle> live_handles_;
1405 std::vector<native_order::RequestHandle> first_open_newborns_;
1406 std::vector<DroppedCloseReceipt> dropped_close_receipts_;
1407 std::vector<OpenEntryFeeFact> open_entry_fees_;
1408 // Current executions settle synchronously, while their generic Applied
1409 // notification is delivered after the enclosing callback. Record the
1410 // source-cohort debit so a second immediate command sees the new basis,
1411 // then suppress just that duplicate debit at notification delivery.
1412 std::unordered_set<std::uint64_t> current_debited_applied_ordinals_;
1413 std::unordered_set<std::uint64_t> intraday_loss_relabel_ordinals_;
1414 std::unordered_map<SourceId, std::int64_t> consumed_partial_exit_cycles_;
1415 std::unordered_set<std::uint64_t> bracket_shadowed_openings_;
1416 std::unordered_map<SourceId, NamedEntryCancelToken> named_entry_cancel_tokens_;
1417 std::map<SourceId, double> close_logical_units_;
1418 std::map<SourceId, double> close_reserved_units_;
1419 std::map<SourceId, double> close_first_units_;
1420 std::map<std::uint64_t, std::map<SourceId, double>>
1421 close_callsite_reserved_units_;
1422 std::map<std::uint64_t, std::map<SourceId, double>>
1423 close_callsite_first_units_;
1424 std::map<std::uint64_t, CloseCallsiteState> close_batch_callsites_;
1425 std::int32_t close_batch_bar_ = -1;
1426 std::uint64_t close_batch_queue_sequence_ = 0;
1427 double close_batch_pending_debt_ = 0.0;
1428 double close_batch_admitted_total_ = 0.0;
1429 std::uint64_t receipt_cursor_ = 0;
1430 std::uint64_t last_applied_ordinal_ = 0;
1431 bool materializing_relative_ = false;
1432 // The parent whose fill the current materialize_relative_exits re-run
1433 // serves; only that parent's armed anchored legs are adoptable.
1434 native_order::RequestHandle materializing_parent_{};
1435 std::int64_t current_position_cycle_ = 0;
1436 int current_position_sign_ = 0;
1437 std::uint64_t next_sequential_group_ = 0;
1438 bool source_batch_mutated_ = false;
1439 bool coof_recalc_active_ = false;
1440 bool coof_first_open_ = false;
1441 std::uint64_t coof_market_entry_recalc_incarnation_ = 0;
1442 std::uint64_t coof_market_entry_recalc_fill_seq_ = 0;
1443 std::uint64_t coof_current_fill_seq_ = 0;
1444 double coof_fill_cursor_t_ = std::numeric_limits<double>::quiet_NaN();
1445 NativeDecisionContext coof_context_{};
1446 Bar coof_script_bar_{};
1447 bool coof_script_bar_valid_ = false;
1448 std::unordered_map<std::int64_t, double> pooc_close_basis_by_script_bar_;
1449 double pooc_open_basis_ = 0.0;
1450 std::int64_t pooc_open_script_bar_ = std::numeric_limits<std::int64_t>::min();
1451 std::int64_t close_all_pending_script_bar_ = std::numeric_limits<std::int64_t>::min();
1452 double last_fx_rate_ = std::numeric_limits<double>::quiet_NaN();
1453 std::int64_t position_open_script_bar_ = std::numeric_limits<std::int64_t>::min();
1454 std::uint64_t position_open_epoch_ = 0;
1455 std::int32_t position_open_bar_index_ = -1;
1456 NativePathPhase position_open_phase_ = NativePathPhase::None;
1457 bool position_open_priced_ = false;
1458 std::int64_t last_margin_call_script_bar_ = std::numeric_limits<std::int64_t>::min();
1459 // R5: the driver point ordinal whose kernel margin check the adapter's
1460 // own scheduling admitted. The kernel offers its check points at every
1461 // bar; exactly the one TradingView also checks at is let through, and
1462 // only for the driver point it was armed on.
1463 //
1464 // Deliberately NOT folded into the broker state hash. Driver point
1465 // ordinals are strictly monotone within a run and this value is only ever
1466 // compared for equality with the current point's, so from the first point
1467 // after the one it names it can never match again: it carries no
1468 // information across any observation boundary the hash identifies. Folding
1469 // it would move every adapter state hash (and R1's pinned hash-neutrality
1470 // evidence in tests/test_adapter_report_relower.cpp) for a value that is
1471 // dead by the time the next one is taken.
1472 std::uint64_t kernel_margin_path_point_ = std::numeric_limits<std::uint64_t>::max();
1473 // The driver point ordinal at which the adapter re-sizes a RESTING kernel
1474 // liquidation from the post-exit book (ab9714be pine_scheduler.cpp:267-282,
1475 // the legacy cancel-and-reschedule after a priced bracket leg of this
1476 // script bar fills). A point named here is admitted only while a slice
1477 // actually rests, because the legacy broker did nothing at all when none
1478 // did. Same hash argument as kernel_margin_path_point_ above: a strictly
1479 // monotone ordinal compared only for equality with the current point's.
1480 std::uint64_t kernel_margin_resize_point_ = std::numeric_limits<std::uint64_t>::max();
1481 // Close-time carried-POOC-short checkpoint deferred behind this bar's
1482 // market fills (ab9714be pine_scheduler.cpp:260 before :278).
1483 std::int64_t pooc_close_checkpoint_deferred_ms_ = std::numeric_limits<std::int64_t>::min();
1484 std::int32_t signal_close_mc_event_bar_ = -1;
1485 std::int64_t signal_close_mc_position_cycle_ = 0;
1486 std::uint64_t signal_close_mc_entry_incarnation_ = 0;
1487 std::uint64_t signal_close_mc_fill_seq_ = 0;
1488 double signal_close_mc_before_qty_ = std::numeric_limits<double>::quiet_NaN();
1489 double signal_close_mc_remaining_qty_ = std::numeric_limits<double>::quiet_NaN();
1490 std::uint64_t last_margin_call_event_ordinal_ = 0;
1491 std::uint64_t last_margin_call_entry_incarnation_ = 0;
1492 std::int64_t last_margin_call_position_cycle_ = 0;
1493 bool last_margin_call_at_script_close_ = false;
1494 double last_margin_call_closed_units_ = 0.0;
1495 double last_margin_call_remaining_units_ = 0.0;
1496 std::int64_t risk_coof_direct_script_bar_ = std::numeric_limits<std::int64_t>::min();
1497 std::uint64_t cap_latest_fill_ = 0;
1498 bool source_margin_call_enabled_ = true;
1499 Bar policy_script_bar_{};
1500 bool policy_script_bar_valid_ = false;
1501 std::unordered_set<std::uint64_t> market_pyramid_adds_;
1502 std::unordered_map<std::uint64_t, NativeTrailState> trail_state_at_open_;
1503 bool stream_mode_ = false;
1504 SourceDayLedger day_ledger_{};
1505 PineRiskState risk_{};
1506 ShortSeedPlan short_seed_{};
1507 PendingShortSeedPlan pending_short_seed_{};
1508 native_order::RequestHandle short_seed_long_candidate_{};
1509 int last_bar_dual_entry_path_ = 0;
1510 std::int64_t last_bar_dual_entry_script_open_ms_ =
1511 std::numeric_limits<std::int64_t>::min();
1512 PendingIntentView pending_view_{};
1513 std::vector<std::uint8_t> trade_exit_phase_;
1514 // @source-state end
1515 // Install-time magnifier fact from NativeBeginArgs. Scheduler already
1516 // folds retained_.bar_magnifier; this copy is the host-kind-free query
1517 // for qualify_short_seed_plan.
1518 bool bar_magnifier_ = false;
1519};
1520
1521} // namespace pineforge::source
The public native host: an abstract subclass of BacktestEngine with no PineScript on it.
const_iterator end() const noexcept
void push_back(const native_order::RequestHandle &handle)
const std::vector< native_order::RequestHandle > & members() const noexcept
std::size_t size() const noexcept
const_iterator begin() const noexcept
void remove_all(const std::vector< native_order::RequestHandle > &doomed)
std::vector< native_order::RequestHandle >::const_iterator const_iterator
void push_back(const native_order::RequestHandle &handle)
const native_order::RequestHandle & back() const
const native_order::RequestHandle & operator[](std::size_t index) const
const_reverse_iterator rbegin() const noexcept
void append_positions_of(const native_order::RequestHandle &handle, std::vector< std::size_t > &out) const
const_iterator begin() const noexcept
std::size_t size() const noexcept
std::vector< native_order::RequestHandle >::const_reverse_iterator const_reverse_iterator
const_reverse_iterator rend() const noexcept
const_iterator end() const noexcept
std::vector< native_order::RequestHandle >::const_iterator const_iterator
const std::vector< std::size_t > * positions_of(std::uint64_t incarnation) const noexcept
const std::vector< native_order::RequestHandle > & members() const noexcept
double trail_best_price() const noexcept
int level_resolved(int index) const noexcept
int copy_v1(int index, pf_pending_order_v1_t *out) const noexcept
int effective_levels(int index, double *stop, double *limit, double *trail_activation) const noexcept
int probe_fill_qty(int index, double fill_price, double *qty, int *close_only, int *partition) const noexcept
int short_seed_collision_role(int index) const noexcept
int last_bar_dual_entry_path() const noexcept
std::optional< double > resolve_anchored_level(const NativeAnchoredLevelView &) const
void set_risk_max_drawdown(double value, bool percent) noexcept
void hash_state(BrokerStateHashSink &) const
std::size_t fixture_close_reservation_count() const noexcept
std::size_t fixture_callsite_close_first_count() const noexcept
void set_configuration(const PineStrategyConfig &config) noexcept
void fixture_remove_entry_without_named_cancel(const SourceId &)
void set_margin_call_enabled(bool enabled) noexcept
bool allows_risk_direction(bool is_long) const noexcept
void begin_coof_recalc(const native_order::ExecutionAppliedEvent &, const NativeDecisionContext &, bool first_open, std::uint64_t source_fill_sequence)
PineExecutionAdapter(compat::pine::CapAttachment attachment=compat::pine::CapAttachment::None)
void on_bar_open(const Bar &, const NativeDecisionContext &)
void set_risk_direction(int direction) noexcept
void permute_exit_phases(std::size_t start, const std::vector< std::size_t > &indices)
bool fixture_coof_recalc_active() const noexcept
std::optional< NativeMarginDecision > resolve_margin_requirement(const NativeMarginRequirementView &) const
std::size_t fixture_callsite_close_reservation_count() const noexcept
void set_begin_mode(bool is_stream, bool bar_magnifier=false) noexcept
double fixture_close_pending_debt() const noexcept
bool fixture_named_entry_cancel_active(const SourceId &id) const noexcept
native_order::ExecutionTerms resolve_terms(const NativeExecutionTermsFacts &) const
NativePrecommitVerdict validate_precommit(const NativePrecommitView &) const
void set_risk_max_position_size(double value) noexcept
bool fixture_coof_cursor_is_bar_close() const noexcept
void on_tick(const Bar &, const NativeTickContext &)
void exit_cancel_bracket(const SourceId &exit_id, const SourceId &from_entry, const std::string &comment={})
std::vector< native_order::RequestHandle > take_first_open_newborns()
std::size_t fixture_close_logical_count() const noexcept
static bool source_kernel_liquidation(const native_order::DefinitionRef &) noexcept
double source_unclosed_qty_for(const SourceId &id) const noexcept
double source_margin_units(const SourceMarginMoney &, bool opening_checkpoint) const
std::uint64_t command_sequence_for_exit(const SourceId &exit_id, const SourceId &from_entry={}) const noexcept
double fixture_callsite_close_first_units(std::uint64_t, const SourceId &) const noexcept
void exit(const SourceId &exit_id, const SourceId &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 set_risk_max_cons_loss_days(int value) noexcept
void set_staged_configuration(const StagedConfiguration &staged)
bool source_margin_exit(std::uint64_t incarnation) const noexcept
double fixture_callsite_close_reserved_total() const noexcept
bool source_post_parent_calc_level_fill(std::uint64_t incarnation) const noexcept
double source_margin_fill_price(double fire, bool close_is_buy) const
bool max_drawdown_is_percent() const noexcept
bool is_open_phase_exit(std::size_t trade_index) const noexcept
double fixture_close_reserved_units(const SourceId &) const noexcept
std::int64_t chart_day_key(std::int64_t timestamp_ms) const noexcept
void set_risk_max_intraday_loss(double value, bool percent) noexcept
void bind(NativeStrategyHost &host) noexcept
std::vector< FixtureCloseCallsite > fixture_close_callsites() const
void release_delayed_orders(bool explicit_brackets_only=false, double current_open=std::numeric_limits< double >::quiet_NaN())
SourceMarginMoney source_margin_money(double mark_price, std::int64_t sub_bar_open_ms) const
void close(const SourceId &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::uint64_t callsite_token=0)
std::vector< FixturePendingSnapshot > fixture_pending_snapshots() const
const PendingIntentView & pending_intent_view() const noexcept
bool max_intraday_loss_is_percent() const noexcept
compat::pine::OrderPriority priority
bool take_intraday_loss_relabel(std::uint64_t ordinal) noexcept
NativeRunSpec project(const PineStrategyConfig &, const StagedConfiguration &, const NativeBeginArgs &, NativePathOrder path_order=NativePathOrder::Auto) const
bool core_sizes_default_opening(bool is_long) const
void on_applied(const native_order::ExecutionAppliedEvent &, const NativeDecisionContext &)
void flush_pending_bracket_legs(native_order::RequestHandle just_applied={}, bool post_calculation=true, bool pre_script_drain=false)
bool process_orders_on_close() const noexcept
double fixture_close_logical_units(const SourceId &) const noexcept
void entry(const SourceId &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)
bool source_priced_exit(std::uint64_t incarnation) const noexcept
std::optional< double > resolve_margin_call_units(const NativeMarginCallView &) const
bool margin_check_allowed(const NativeMarginCheckPoint &) const
double fixture_callsite_close_reserved_units(std::uint64_t, const SourceId &) const noexcept
void order(const SourceId &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)
bool has_pending_market_exit(int current_interval_index=-1) const noexcept
const AnchoredRelativeStats & anchored_relative_stats() const noexcept
PineCancellationReceipt * fixture_mutable_cancellation(int index) noexcept
std::size_t fixture_close_first_count() const noexcept
bool carried_long_money_precedes_priced_exit(const NativePrecommitView &, double held_units) const
std::optional< double > source_trail_offset_ticks(std::uint64_t incarnation) const noexcept
int short_seed_collision_role_v1(native_order::RequestHandle) const noexcept
void on_bar_close(const Bar &, const NativeDecisionContext &)
bool market_pyramid_add(std::uint64_t incarnation) const noexcept
double fixture_close_admitted_total() const noexcept
bool suppress_grouped_stop_recalc(const native_order::ExecutionAppliedEvent &, const NativeDecisionContext &) const noexcept
void mark_market_pyramid_add(std::uint64_t incarnation)
void set_path_order(NativePathOrder path_order) noexcept
double fixture_close_first_units(const SourceId &) const noexcept
bool operator==(const Iterator &other) const noexcept
bool operator!=(const Iterator &other) const noexcept
std::uint64_t high_water() const noexcept
const_iterator find(std::uint64_t incarnation) const noexcept
PlacementSnapshot & at(std::uint64_t incarnation)
std::uint64_t command_sequence_for(const std::string &source_id, const std::string &from_entry) const
void reserve(std::size_t count)
const_iterator begin() const noexcept
const_iterator end() const noexcept
void replace(std::uint64_t incarnation, PlacementSnapshot &&snapshot)
const PlacementSnapshot & at(std::uint64_t incarnation) const
std::size_t size() const noexcept
std::pair< iterator, bool > try_emplace(std::uint64_t incarnation, Args &&... args)
std::size_t max_size() const noexcept
iterator find(std::uint64_t incarnation) noexcept
NativePrecommitVerdict
The host is consulted before generic opening-margin admission.
NativePathPhase
Which leg of a modeled OHLC walk a point sits on.
std::variant< Market, Limit, Stop, StopLimit, Trail > Trigger
std::shared_ptr< const RequestDefinition > DefinitionRef
NativePathOrder
Generic ordering for a modeled OHLC path.
constexpr std::uint32_t kCoofLoopGuard
constexpr char kMarginCallLabel[]
constexpr char kSourceAdapterDomain[]
constexpr char kIntradayLossComment[]
Bar margin_call_sample_bar(const Bar &bar, double fire_price, bool prefix_sample, bool high_first, double mintick=0.0, int slippage=0)
std::string SourceId
constexpr char kFillCapCommentPrefix[]
void sample_open_trade_extremes(std::vector< PyramidEntry > &lots, PositionSide side, int bar_index, const Bar &bar)
admission::Draft MarketAdmissionDraft
std::unordered_map< std::string, std::string > InputsMap
Definition engine.hpp:309
admission::Journal MarketAdmissionJournal
Borrowed begin-call facts.
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.
A host-maintained, run-scoped roster identity.
One committed execution: the definition, the cursor, the resolved price and units,...
Aggregate field order keeps market construction: Request{Transact{1.0}, "buy", "comment"}...
One complete setup value, staged/copied by NativeStrategyHost before it is applied at begin.
native_order::RequestHandle opening
std::uint64_t recreated_after_named_cancelled_entry_incarnation
native_order::RequestHandle paired_reversal_parent
ReservationGrowthSource reservation_growth_source
native_order::RequestHandle bracket_origin
compat::pine::HistoricalBirthReach birth_reach
PineCancellationReceipt cancellation
native_order::RequestHandle preserved_by_close_all
ReservationExpansion reservation_expansion
compat::pine::ExitActivationPolicy exit_activation
native_order::RequestHandle final_short
native_order::RequestHandle long_entry
native_order::RequestHandle materialize_long
std::vector< double > account_fx_per_quote
std::vector< std::int64_t > account_fx_effective_from_ms
std::optional< double > quantity_grid