Skip to content

Pine adapter: the same-pass position view after a close fill, the close's owed follow-up, open-marketable exits first, and same-side global exits, pinned by TradingView controls - #347

Merged
luisleo526 merged 4 commits into
mainfrom
px/wave-r3
Oct 6, 2026
Merged

luisleo526 merged 4 commits into
mainfrom
px/wave-r3

Conversation

@luisleo526

@luisleo526 luisleo526 commented Oct 6, 2026 •

Copy link
Copy Markdown
Collaborator

Pine adapter: four more TradingView rules pinned by synthetic controls

Four adapter-only changes (src/source/, include/pineforge/source/, tests and docs; no kernel,
grader, metric, tolerance or codegen change). Each rule part sits behind its own test-only switch,
default on, so a regression bisects per rule. One commit per change.

1. A re-issued exit filled at its call keeps the pass's position view (detail::PoocCloseFillViewSwitches)

Under process_orders_on_close, the adapter fills a re-issued strategy.exit of a one-lot short at the
call when its stop or limit is already through the bar's close. Unlike strategy.close_all(), it did not
freeze the script's position view, so the rest of the pass read strategy.position_size == 0 and a reset
strategy.position_avg_price. A late exit observer (strategy.position_size[1] < 0 and strategy.position_size == 0) then fired one bar early, and an exit cooldown blocked the next entry.

TradingView fills that exit at the close, after the pass. The adapter now freezes the position view
before it executes the exit, and once the fill has left the book flat, strategy.position_avg_price
keeps the pre-fill average until the pass ends (no codegen change). The hold is released right after the
script, before the bar's broker state is folded.

Evidence:

  • 15 synthetic controls on BINANCE:ETHUSDT.P 15m (lab tv --no-note, two byte-identical exports
    each, predictions written before export): 0 counterexamples. 11 are committed as
    tests/fixtures/pooc_close_fill_view: 9 equal TradingView row for row, entry ids included (the
    re-issued stop exit's size read, cooldown observer, ATR-stop cooldown at 100 % of equity, average read
    and limit leg, plus the long mirror, strategy.close, close_all and a first-issue exit, which never
    moved); 2 are known divergences (below).
  • Clearing the average part moves exactly 2 tapes, clearing the view part exactly the 5 re-issued short
    tapes, each away from TradingView. Every tape streams to its backtest's trades with both parts on and
    off.
  • The test also compares the per-bar broker-state hashes with the average part on and off: equal wherever
    the trades are, otherwise first different after the exit bar. With the release call removed the test
    fails on 6 tapes at the exit bar's fold, so a hold that outlives its pass cannot go unnoticed.
  • Two scraped population scripts (BINANCE:ETHUSDT.P and BINANCE:BTCUSDT 15m) now equal TradingView's full
    trade lists; nothing else in the population moves.

Known divergences:

  • close_all's own strategy.position_avg_price read: TradingView keeps the pre-fill average; the
    engine's freeze there covers the size only (short-close-all-average).
  • After the re-issued exit, TradingView also keeps the pre-fill strategy.opentrades, closedtrades,
    openprofit, netprofit and equity; the engine reads them after the fill
    (short-reissued-exit-reads).
  • A strategy.close that fills at the call under an active intraday cap freezes nothing (no tape).

2. A POOC close's owed follow-up after the script, frozen close sizes and their over-fill lot, and the open's lot-by-lot follow-up (MarginScheduleSwitches::close_follow_up_after_script, MarginOpeningSwitches::lot_by_lot_open_follow_up)

  • A carried process_orders_on_close short's margin-call follow-up owed to its bar's close is booked
    after the script, ahead of the close's fills, at the same print. A strategy.close(id) of the whole
    one-lot book or a close_all() placed there keeps the size the script saw, so the order crosses zero
    and opens the excess as a lot of its own. That lot is credited to the close ledger ahead of the entry
    beside it, so a later strategy.close of that entry closes all of the entry's units.
  • Beside that lot, a market entry filling at the close it was placed at is not refused by pyramiding 0,
    and a default-sized one is not refused by the fill-time affordability check.
  • At the next open, on a fractional lot grid, the close-sized margin call takes a book of several lots
    first in first out and its follow-up is checked lot by lot.

Evidence:

  • 10 synthetic controls on OANDA:EURUSD 15m (two byte-identical exports each, predictions written
    before export), committed as tests/fixtures/margin_open_rules/lots/ (62 TradingView trades): 10/10
    row for row
    , forward == backtest. Off alone, the close switch loses 7 tapes and the lot-by-lot switch
    9.
  • One scraped population script (OANDA:EURUSD 15m) now equals TradingView's 3,761 rows exactly; nothing
    else in the population moves.

Known divergences and unpinned shapes:

  • The same close outside process_orders_on_close keeps its old size (margin_schedule_rules
    frozen/close-id stays a listed divergence); partial closes and closes of several lots keep today's
    sizing.
  • Beside the over-fill lot, priced entries and fills on a later bar keep every gate (no tape). One shape
    changes against the previous release, unpinned either way: an explicit stop entry placed at that close at
    pyramiding 0 is refused as a same-side add beside the lot, where the previous release filled it on the
    next bar.
  • On a fractional book of several lots a short's booked re-check now runs, with no follow-up after it
    (no tape decides that follow-up); whole-share books of several lots keep the previous rules. No tape
    runs pyramiding above 1, so the lot's pyramiding-record credit is unexercised.

3. A market entry's opening check at its open fill follows the exit legs marketable there (MarginOpeningSwitches::open_marketable_exit_first)

At a market entry that opens the book from flat at a bar's open, the exit legs it released that are
already marketable at that open (a short's stop at or below the open's tick or its limit at or above it,
a long's mirror) fill there first, and the opening margin check runs on the book they leave. A leg the bar
reaches only later on its path never preempts: the check's call books at the fill first. A
strategy.exit without from_entry placed with the entry counts as a released leg. The previous rule (a
1x short skips its opening margin call whenever its bar touches any exit leg) is refuted by TradingView
on its own test shape.

Evidence:

  • 31 synthetic controls on BINANCE:ETHUSDT.P and NYSE:F 15m (two byte-identical exports each,
    predictions written before export), committed as tests/fixtures/margin_open_rules/exit-order/ (53
    TradingView trades): 29 row for row, 2 listed divergences (below); forward == backtest on all.
    Turning the switch off loses 17 tapes. A legacy scenario (test_margin_call_1x_long_entry_fill E) now
    books TradingView's shape: 0.1996 "Margin call" at the open, then the stop.
  • 11 population scripts move, all toward TradingView at their first changed trade; one BINANCE:ETHUSDT.P
    15m script goes from 233 to 588/588 exact TradingView rows. Rows lost on two scripts all lie after
    their first, pre-existing miss (equity cascades).

Known divergences:

  • Taped: a short reversal and a short stop entry gapped at the open, which TradingView calls at the
    open print before their stop on the path, as it does the market entry; the engine keeps the previous
    rule there.
  • Without a tape: a global strategy.exit placed before the entry, while flat. TradingView ignores
    an exit called while no position is held and no entry order is working, so, by inference, it calls such
    a short at the open print. The engine keeps that call as a leg that never fills and still lets it
    preempt the short's call (the short is called at the path's extreme instead; the same with the switch
    off). The leg stays outside the released legs for an engine-only reason: counted, it would defer the
    check to a fill that never comes and drop the check, a long's too.
  • Adds, books of several entries, limit entries, process_orders_on_close and calc_on_order_fills keep
    the previous rule without a tape.

4. A global exit called while a position is held binds to it beside a same-side add too (ExitBindingRuleSwitches)

A strategy.exit with no from_entry, called while a position is held beside an entry order of the
held side (an add resting at a limit or a stop, placed earlier in the same calculation, or queued by a
fill recalculation), took that pending add as its parent. A profit or trailing leg then rested at the
add's limit and closed the position at the next open, so a script booked thousands of one-bar trades; a
loss leg, and any leg beside a stop add, waited for the add's fill, which never came, and the position ran
to the end of the range. The exit now binds to the held position and its average price, whatever the side
of the order beside it. Under calc_on_order_fills the bracket the exit stages in the entry's fill
recalculation no longer counts the held side's entry orders as competing orders (they moved its threshold
half a tick, so a limit the fill bar's high reaches exactly filled one bar late).

Evidence:

  • 36 pairs of synthetic controls on NYSE:F 15m (a resting limit add, a resting stop add, or a market
    add at the pyramiding cap; profit, loss, profit and loss, and trailing legs; long and short;
    calc_on_order_fills off and on; predictions written before export): every add script's List of Trades
    is byte-identical to its control's, 0 counterexamples.
  • tests/fixtures/same_side_exit commits all 56 tapes: 38 equal TradingView row for row; 18 depart at
    one recorded row, the same row in a control and its add tapes (the trailing leg and
    calc_on_order_fills re-entries, another rule's). Off, the binding switch moves exactly the 32
    resting-add tapes and the bracket switch exactly the 4 calc_on_order_fills profit-and-loss add tapes.
    Every non-calc_on_order_fills add tape streams to its backtest over every run of the feed.
  • No population script moves.

Known divergences: the 18 rows above (trailing-stop and calc_on_order_fills re-entry gaps that the
controls share); an add that fills while the exit works (TradingView gives each entry its own exit levels
and books first in first out, the engine books one exit at the average price) is a separate rule, not
changed here.

Release note

Exits without from_entry now act on the position you hold when a same-direction add order is
waiting.
If a strategy held a position, had a limit or stop entry of the same direction resting (for
example a pyramiding add), and called strategy.exit with no from_entry, the exit attached itself to
the waiting add instead: a take-profit or trailing exit closed the position on the very next bar,
producing thousands of one-bar trades, while a stop-loss exit waited for the add to fill and could leave
the position open until the end of the backtest. Such exits now protect the held position at its average
price, as on TradingView, with or without calc_on_order_fills.

Verification

  • Full ctest of the head (Release): every row that runs outside CI passes (847 rows; the four ABI-receipt rows
    run in the CI release profile), and each intermediate commit passes on its own. test_pooc_close_fill_view_tapes 9 + 2 known;
    test_margin_open_rules_tapes 82/84 tapes, 244 TradingView rows, forward == backtest 84/84, every
    switch costs tapes (lot-by-lot 9, open exit order 17, close follow-up 7);
    test_same_side_exit_tapes 38 + 18 known; test_margin_schedule_rules_tapes 38/39, forward 39/39;
    test_margin_call_rules_tapes 258/266 (all switches off: 140); test_margin_opposite_tapes 63,504
    checks; test_exit_binding_tapes forward == backtest 36/36; test_cross_side_exit_tapes 11 + 4 known;
    test_adapter_quiet_bar 276/276 and test_publication_witness 156/156 pinned runs unchanged.
  • Interaction check: the previous release's whole test suite run against the previous engine, each change
    alone and the four together, with a per-run dump of every trade field (%.17g): every run that one change
    moves equals that change's run in the combined engine, every other run equals the previous release's, and
    no run is moved by two changes. Each change's own tests give the same trades alone and combined. The
    four changes' population movers do not overlap.
  • CI profiles: all passed on the head: release 866/866 tests (35 verify stages), sanitizers 841/841 (a timing-shape test exceeded its bound by 0.3 % once under parallel load and passed on the rerun), debug 847/847, kernel 307/307 with 0 residual findings, Doxygen 0 warnings, corpus parity 312/312 probes unchanged (subset 54/54)

Population gate

One whole-population sweep of this head's exact tree (exp-px-wave-r3-gate-20261006, attempt 1; engine
3219563e, codegen main 3e50082f, population cf857209), graded against the active baseline
pineforge-parity-baseline-20261005-engine-a9544d36:

  • 7,989 probes graded, coverage complete: 3 up (strong → excellent), 0 down, no hard-lane drop.
    • eth-scraped-15 job-2947-seanvtran-svt-30m-options-swing-indicator (hard lane): the same-pass position view after a fill at the close (change 1);
    • eurusd-15 job-2550-spiritedcheeta2101-quanloki-qqe-system-opposite-signal-only-points: the close's owed follow-up and the over-fill lot (change 2);
    • eth-scraped-15 blitz-locked-macd-pullback-sniper-trend-adx-filtered-strategy (hard lane): an exit marketable at the open goes first (change 3).
  • The same-side global-exit change moves no population trade.
  • Candidate snapshot e7921e2433a449d1b0859d2bac0536e5663a06820030b091e451529a37487b68 (7,989 graded).

🤖 Generated with Claude Code

luisleo526 and others added 4 commits October 6, 2026 08:02
…sition view

Under process_orders_on_close, exit() fills a re-issued strategy.exit of a
one-lot short whose stop or limit is already through the bar's close at the
call (its current-close path, execute_current). Unlike an ordinary close_all
(KI-64), it did not freeze the script's position view, so the rest of the
pass read strategy.position_size == 0 and a reset strategy.position_avg_price.
A late observer, strategy.position_size[1] < 0 and strategy.position_size ==
0, then fired one bar early, and an exit cooldown blocked the next bar's
entry. The scraped job-2947 probe on BINANCE:ETHUSDT.P 15 lost its
2026-01-08 23:30 UTC short and every later trade this way.

TradingView fills that exit at the close, after the pass: the pass keeps the
pre-fill size and average. The adapter now freezes the script's position view
before it executes the exit, as close_all does. Once the fill has left the
book flat, the generated strategy.position_avg_price read
(position_entry_price_ behind a non-zero signed_position_size()) keeps the
pre-fill average until the pass ends. PineStrategyHost::
hold_script_average_price writes it, and release_script_average_price
restores the flat book's 0 right after on_source_bar, before the orders the
pass placed settle. No codegen change.

Each part has its own switch, PoocCloseFillViewSwitches::
current_exit_keeps_position_view and current_exit_keeps_average_price (on;
tests clear them). Both act only at that call site. close_all's own average
read and the intraday-cap close at the call are unchanged.

Pinned by 15 synthetic lab tv --no-note controls on BINANCE:ETHUSDT.P 15m,
2026-01-08..10. Each was exported twice, the two exports are byte-identical,
rangeProof is covered on both, and every prediction was written before its
export: 0 TradingView counterexamples. tests/fixtures/pooc_close_fill_view
holds 11 of them with their frozen generated strategies (codegen c1df8a6):
- Nine equal TradingView row for row, entry ids included. These are the
  re-issued stop exit's size read, its cooldown observer, its ATR-stop
  cooldown at 100 % of equity, its average read and its limit leg, plus the
  long mirror, strategy.close, close_all and a first-issue exit, which never
  moved.
- Two are known divergences the rule leaves open. close_all's average read
  is AVGPRE on TradingView and AVGOTHER in the engine. After the re-issued
  exit, TradingView keeps the pre-fill strategy.opentrades, closedtrades,
  openprofit, netprofit and equity; the engine reads them after the fill.

test_pooc_close_fill_view_tapes clears each part and requires the departures
recorded in switch_off_departures.json. The average part moves 2 tapes; the
view part moves the 5 re-issued short tapes. It also requires every tape's
stream to equal its backtest with both parts on and off. Last, it records
every tape's per-bar broker-state hash with the average part on and off:
position_entry_price_ is hashed, so the hashes must be equal wherever the
trades are, and otherwise first differ after the exit bar's fold. A hold
that outlived its pass fails it: with the release_script_average_price()
call removed, the trades stay the same and the hashes of the re-issued exit
tapes differ from the exit bar's fold on. The four cooldown controls of
tests/fixtures/margin_call_rules stay equal.

Fail-before: the test module does not compile against the parent tree
(pooc_close_fill_view_switches is not a member of detail). With both parts
cleared, the five re-issued short tapes depart from TradingView, as on the
parent.

Whole-suite per-run trade dumps (%.17g, every field) against the parent: the
dumps cover 670 processes and 56,558 runs. Every pre-existing run-pass keeps
its trades with both parts on and with both off. The only differences are
the new test, the repetition counts of timing-calibrated loops and the run
order of a test that runs two hosts on two threads; their distinct runs are
identical. No witness re-pin.

The transient flag is waived in broker_state_hash_waivers.txt: it is false
at every fold. Documentation anchors into the four edited files are carried
by an exact line map from the parent (245 moved, 0 stuck).

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
…frozen close sizes and their over-fill lot, and the open's lot-by-lot follow-up

R-A (MarginScheduleSwitches::close_follow_up_after_script): a carried
process_orders_on_close short's lagged follow-up owed to its bar's close is
held through the close's own check (rule 7 still drops it where the close is
short) and booked after the script, ahead of the close's fills, at the same
print. A strategy.close(id) of the whole one-lot book (flush_pending_closes)
or a close_all() placed there keeps the size the script saw: a transaction
that crosses zero and opens the excess as a lot of its own, under the close
order's label. That lot is credited to the close ledger and the pyramiding
records ahead of the entry beside it, so the next open's nameless margin
calls take it first in first out, as the kernel does from the book, and a
later strategy.close of that entry closes all of the entry's units. A
reversal entry's excess joins its own lot (apply_frozen_reversal_close).
Partial closes, closes of several lots and the twin outside
process_orders_on_close keep today's sizing (margin_schedule_rules
frozen/close-id stays a known divergence). No tape runs pyramiding above 1,
so the pyramiding-record credit is unexercised.

Beside that lot only a market entry filling at the close it was placed at is
exempt: from the pyramiding-0 refusal, and a default-sized one from the
fill-time affordability check too (a scraped OANDA:EURUSD 15 probe, job-2550,
2026-04-09 01:45 UTC: TradingView fills a default-sized "LONG" of 18624.97
beside the 0.56 over-fill lot). Priced entries and later fills keep every
gate; no tape decides those shapes. One of them changes against the parent
tree, which opens no over-fill lot: an explicit stop entry placed at that
close at pyramiding 0 fills on the next bar there, and is refused here as a
same-side add beside the lot.

R-B (MarginOpeningSwitches::lot_by_lot_open_follow_up): on a fractional lot
grid, the close-sized call at the next open takes a book of several lots the
close's fills opened, first in first out, and its follow-up is checked lot by
lot (open_print_lot_follow_up_units). One-lot books keep
open_print_follow_up_units, and whole-share books of several lots keep the
previous rules. On a fractional book of several lots a short's booked
re-check now runs as well (the parent tree never entered this schedule for
several lots); nothing follows it, since no tape decides that follow-up.

Tests: tests/fixtures/margin_open_rules lots/ holds 10 synthetic TradingView
controls (62 trades), each exported twice with identical output and
predicted before export. All 10 match row for row and stream to the
backtest's trades; the parent tree matches none of the first 8. Two of them
close the entry by id after the open's calls (strategy.close("L"), with an
explicit and with a default-sized L). Off alone, close_follow_up_after_script
loses 7 tapes and lot_by_lot_open_follow_up 9. margin_open_rules: 53 tapes,
all match. The job-2550 probe's trades now equal TradingView's 3761 rows.
Whole-suite per-run trade dumps (%.17g, every field) with both switches on
and off: only the lots/ tapes move. Documentation: both switches' rows, the
counts, and the anchor digits carried by an exact line map.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
… the exit legs marketable there

TradingView, pinned on 25 synthetic controls (each exported twice,
byte-identical, every prediction written before its export): at a market
entry that opens the book from flat at a bar's open, the exit legs it
released that are already marketable at that open -- a short's stop at or
below the open's tick or its limit at or above it, a long's mirror -- fill
there first, and the opening margin check then runs on the book they leave
(nothing after a whole exit). A leg the bar reaches only later on its path,
one tick after the open included, never preempts: the check's call books at
the fill first. Both sides. Four more controls make the strategy.exit global
(no from_entry) and place it with the entry: TradingView books each exactly
as its bound control.

ab9714b's rule -- a 1x short skips the opening trim when an exit leg touches
anywhere on the entry bar -- is refuted by TradingView on its own shape
(test_margin_call_1x_long_entry_fill scenario E's settings and bar: 0.008 ETH
called at the open, then the stop closes 3.492).

For a market entry opening the book from flat at the open (one lot, no add,
no book of several entries, neither process_orders_on_close nor
calc_on_order_fills) the adapter asks the legs that fill released whether
they are marketable at the open (priced_exit_precedes_opening). The released
legs (opening_released_leg) are those bound to the entry's request, those
bound to its id and materialized at the fill on the book's side, and a
global leg placed while the book was flat whose request follows the entry's.
Every other leg (a strategy.exit without from_entry placed before the entry
or while a position was held, a leg carried from an earlier bar or lot) is
still asked ab9714b's question, and every other fill (stop and limit
entries, reversals, adds, books of several entries,
process_orders_on_close, calc_on_order_fills) keeps that rule. The scope is
narrower than the pin's wording because the wider form (every market fill at
the open, every live leg) moved the state hashes of two synthetic witnesses,
test_adapter_quiet_bar (18 runs) and test_publication_witness (10 runs):
hash-only, every trade field equal in a whole-suite dump, through legs the
kernel never fills at that open, whose owed check then lingered. Which
exclusion each of the last 8 runs needed was not bisected.

Known divergences of the kept rule:
- Two are committed as tapes the test lists in kKnownDivergences:
  TradingView calls a short reversal (out of a 0.001 long) and a short stop
  entry gapped at the open (a sell stop above the close) before their stop
  on the path, as it does the market entry; the engine books the stop 3.5
  with no call.
- A global strategy.exit placed before the entry, while the book is flat,
  has no tape. TradingView voids an exit called while no position is held
  and no entry order is working (its exit-binding pins), so, by inference,
  it calls such a short at the open print. The engine keeps a leg there
  that never fills, at the open or later, and still asks ab9714b's
  question of it: the short's opening call is skipped whenever the bar
  touches the leg's stop, and the short is called at the path's extreme
  instead, the same with the switch off. The leg stays out of the released
  legs for an engine-only reason: counted, it defers the check to a fill
  that never comes, which drops the check, a long's too (in
  test_adapter_quiet_bar Random32 that moved only the state hash).
- Adds, books of several entries, limit entries, process_orders_on_close
  and calc_on_order_fills stay on that rule without a tape.

A partial exit at the open owes the check after its own fill
(open_exit_first_bar_: 0.0032 called after a 0.0004 stop, as TradingView
books it); the record is cleared at the next bar open if no released leg
fills. The opening check's body moves unchanged into run_opening_checkpoint
so both points run the same branch.

Switch MarginOpeningSwitches::open_marketable_exit_first, default on; off
restores the previous rule (every deterministic run-pass of the suite books
the parent tree's trades, field for field).

Tests: the 31 controls are tapes in tests/fixtures/margin_open_rules
exit-order/ (53 trades). margin_open_rules now holds 84 tapes and 244
trades: 82 match, the 2 listed divergences still differ, forward ==
backtest on all 84, and the switch off loses 17 (15 of the 25 pinned
controls, which the parent tree misses, and the two global controls that
tell the rules apart). Scenario E of test_margin_call_1x_long_entry_fill
(twin and base copy) books TradingView's shape, 0.1996 @100 "Margin call"
then 99.7504 @105; its twin-parity rewrite entry is extended. Docs: the
switch row and counts, and every anchor into the moved files re-anchored by
an exact line map.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
…it beside a same-side add too

A strategy.exit with no from_entry, called while a position is held beside an
entry order of the HELD side (an add resting at a limit or a stop, placed
earlier in the same calculation, or queued by a fill recalculation), still
took that pending add as its parent (observe_staged_parent in exit()): #345
skipped only an entry order of the other side. With the add's price basis, a
profit or trail leg rested at the add's limit plus its ticks and filled at the
next open, so a script booked thousands of one-bar trades; a loss leg, and any
leg beside a stop add, waited for the add's fill, which never came, and the
position ran to the end of the range. A market add at the pyramiding cap never
became a pending parent.

TradingView binds such an exit to the held position whatever the side of the
order beside it. In 36 pairs of synthetic `lab tv --no-note` controls on
NYSE:F 15m (a resting limit add, a resting stop add, or a market add at the
cap; profit, loss, profit and loss, and trail legs; long and short;
calc_on_order_fills off and on; every prediction written before its export),
each add script's List of Trades is byte-identical to its control's: 0
counterexamples. The skip now drops its side term, so the exit keeps the held
position's side and entry price.

Under calc_on_order_fills one more path departed: the profit-and-loss bracket
the exit stages in the entry's fill recalculation (the chart-tick scope)
counted the resting add as a competing order of the source book, which took
the bracket's chart-tick reach (its threshold moved half a tick beyond a level
the on-grid test misreads by one ULP), so a limit the fill bar's high reaches
exactly filled one bar late. flush_pending_bracket_legs now leaves the entry
orders of the held side out of the census of a held global exit's leg.

Two switches (ExitBindingRuleSwitches, on; tests clear them):
global_exit_binds_held_position (extended, sizeof unchanged) and the new
held_exit_bracket_ignores_same_side_entries (sizeof 5 -> 6). Kernel untouched;
no stream gate. Not in scope: an add that fills while the exit works
(TradingView gives each entry its own exit levels and books FIFO; the engine
books one exit at the average price) is a separate rule.

Tests: tests/fixtures/same_side_exit holds the 36 add tapes and their 20
controls (exact Pine bytes, TradingView's List of Trades, provenance, frozen
generated C++ of codegen c1df8a69); test_same_side_exit_tapes requires 38
tapes equal to TradingView row for row and the other 18 to depart exactly at
their recorded row (the same row in a control and its add tapes: the trailing
leg and calc_on_order_fills re-entries, another rule's); with
global_exit_binds_held_position off exactly the 32 resting-add tapes move,
with held_exit_bracket_ignores_same_side_entries off exactly the 4
calc_on_order_fills profit-and-loss add tapes; no control and no cap tape
moves. The calc_on_order_fills add tapes are refused by a forward stream;
every other add tape streams (1 and 30 bars of history, every run of the feed
between two nights) to the backtest's trades. test_exit_binding_tapes sets
the new switch in every_part() and lists it.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
sys.path.insert(0, str(scripts))
from run_strategy import Strategy, _check_abi, inputs_run_kwargs # noqa: E402

class TapeStrategy(Strategy):
_check_abi(self.lib)
self._setup_signatures()

class StreamStrategy(Strategy):
sys.path.insert(0, str(scripts))
from run_strategy import Strategy, _check_abi, _load_bars, inputs_run_kwargs # noqa: E402

class TapeStrategy(Strategy):
_check_abi(self.lib)
self._setup_signatures()

class StreamStrategy(Strategy):
@luisleo526
luisleo526 marked this pull request as ready for review October 6, 2026 01:22
@luisleo526
luisleo526 merged commit 7a1f01c into main Oct 6, 2026
16 checks passed
@luisleo526
luisleo526 deleted the px/wave-r3 branch October 6, 2026 01:25
luisleo526 added a commit that referenced this pull request Oct 6, 2026
* docs: release notes and version scoping for v1.3.0

CHANGELOG 1.3.0: confirmed-bar admission in pineforge-live (#342), the Pine
adapter changes of #341, #345 and #347, tests and docs (#343, #344); no
report key changes; the state-hash recipe additions of #341 and #347, domain
tags and the v19 epoch unchanged; migration.

Version scoping: README, install.md and public-contract.md name v1.3.0, its
tarballs, the engine1.3.0-codegen1.3.0 image and the 1.3.0 pair. The
confirmed-bar receipt and its admission, marked unreleased after 1.2.0 by
#342, ship in 1.3.0 (README, strategy-capabilities.md, runner/README.md).

Codegen license lines (README, LEGAL.md, docs/pages/index.md): the PineForge
Source License 1.2 from codegen 1.3.0; 1.2.0 keeps 1.1, releases up to 1.1.0
the PolyForm Noncommercial terms.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>

* docs: release 1.3.0's grades in the README and the changelog, from the facts tokens

The README's release sentence moves to the releases[1.3.0] tokens, and its
main-scoreboard lines re-render the values defe41f (#343) rendered at
pineforge-release 3e39d64, and the 1.3.0 changelog says which registry
baseline grades the release and why that baseline is the release's code,
all rendered from pineforge-release facts/facts.json at ff21de0 (#32). The
changelog sentence extends the last line of the "What a script author sees"
bullet that 0e1ced7 added.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant