Repository navigation
Pine adapter: explicit entry quantity floor, script equity mark, process-orders-on-close bar fills and the session clock - #334
Merged
Conversation
… bar fills and the session clock Four TradingView rules, each pinned on synthetic TradingView tapes with 0 counterexamples and each behind its own test-only switch (default on; no public API, input or strategy setting reaches it): ScriptRuleSwitches in include/pineforge/source/pine_adapter.hpp, SessionClockSwitches in include/pineforge/session_time.hpp. - explicit_qty_decimal_floor: an explicit strategy.entry quantity that floor_quantity_grid's 1e-6 tolerance snaps UP to a grid point used to come back raw and off the grid, and then never filled. TradingView floors its shortest decimal (decimal_floor_lot, shared with the default sizing): 3124.9999999999973 trades 3124. Only that snap changes; strategy.order quantities and lots that are not a power of ten keep the grid floor. - equity_tick_mark: script-visible strategy.equity / strategy.openprofit (PineStrategyHost::open_profit, which generated code calls) mark an open position at floor(close / mintick + 0.5) * mintick when the close lies off the tick grid (NYSE:F half-cent closes). The tick is syminfo_mintick_ (the engine's 0.01 default when a host sets none, the tick fills already use). On-grid closes, and the kernel's own marks, are unchanged; the adapter's money gates name BacktestEngine::open_profit explicitly. - pooc_bracket_skips_inert_exits: under process_orders_on_close the fresh-parent-first rank of a re-issued bracket no longer counts an exit whose entry is neither open nor working as part of the book, so a stop entry and its bracket that both fill inside one bar settle before the close-time script. - transition_wall_clock: time() / time_close() with a script's own session argument read TradingView's session clock across a daylight-saving switch: an occurrence that starts or ends at the switch's pre-transition wall time runs on the post-transition offset throughout; every other occurrence follows the wall clock. Taped in New York and London only. The chart's own bar close, read through the SYMBOL's session, keeps the plain windows (detail::symbol_session_time_close) until a tape pins a symbol session with an endpoint at its zone's switch hour. Tests: 41 TradingView tapes committed as fixtures (explicit_qty_floor, pooc_close_bar_fills, session_transition_clock), each with its exact Pine source and export. The explicit-quantity and bar-fill tapes run through their frozen generated strategies in one module (test_explicit_qty_floor_generated_tapes, test_pooc_close_bar_fills_generated_tapes); the hand-ported hosts stay for the script readouts trades cannot show (strategy.equity / openprofit / netprofit at the read bar, the guarded branch). Each switch turned off fails exactly its own tapes. test_qty_step_lot_grid_case pins the release harness's lot-grid case byte for byte, with and without the grid. Docs: line citations re-anchored, digits only.
luisleo526
marked this pull request as ready for review
October 4, 2026 13:58
This was referenced Oct 4, 2026
luisleo526
added a commit
that referenced
this pull request
Oct 5, 2026
…ents resolved without copies (#339) ## What `time(tf, session, tz)` with a script's own session argument reads TradingView's session clock outside UTC (#334). That path asked the per-day session cache about nine times a bar, and copied the session and zone strings on every `time()` / `time_close()` call. This PR reads each day's occurrences once per call, caches `utc_zone` per session-cache entry, and resolves the call's strings by reference. It changes one file, `src/session_time.cpp`: the language calendar functions under rule 1 of ADR 0001. It adds no switch, input, API or hashed state. ## Value identity (on this tree, against main) - the old and new session functions run side by side over the whole suite: 0 mismatches; - a differential over generated sessions, zones and timeframes, walking every DST switch of 2024–2026 minute by minute: 16.6 M queries, identical digest; - an independent review repeated it with 33.2 M queries: 0 different answers, identical cache and zone-memo history, clean under ASan + UBSan; - the 30 witness harvests: byte-identical, 0 re-pins; - the whole-suite per-trade dump (all 21 Trade fields at `%.17g`, every state hash): identical apart from rows that differ between two runs of one tree; - all 307 tape and witness executables: byte-identical; - corpus 312/312, 0 moved. ## Timing (on this tree, against main 44eab7b) Measured on a Xeon 8375C with 4 independent builds per tree, paired rotating blocks (81 per shape), and every process under exactly 22 environment variables. Positive means slower; `*` means the 95% interval covers 0. The **padded control** is this commit with every function outside `session_time.o` placed at main's address, checked symbol by symbol. | | native | ta-sma | bracket | udt | composite-vcp | |---|---|---|---|---|---| | batch median, ungathered (1 m / 1 m) | +1.23 | +1.26 | +0.87 | +0.83 | **−9.54** | | — padded control | +0.03* | +0.06* | +0.02* | −0.00* | **−7.93** | | per call, ungathered | +0.21 | +0.04* | +0.19 | +0.10 | **−5.16** | | — padded control | +0.05* | +0.01* | −0.02* | −0.02* | **−4.84** | | stream p95, ungathered | +0.46 | +1.53 | +0.46 | +0.17 | **−3.07** | | — padded control | +0.04* | +0.03* | −0.05* | +0.01* | **−3.46** | | batch median, gathered (1 m / 3 m) | +0.60 | +0.09* | +0.54 | +0.41 | **−5.80** | | — padded control | +0.06* | +0.06* | +0.05* | +0.09* | **−5.25** | - **Only composite-vcp's work changes.** Its instruction count drops from 232.5 M to 220.1 M over 2 × 5,000 stream pushes, and from 2,512 to 1,275 per `timeframe_time` call. - **The other four cases execute identical instructions.** Their differences against main are code placement: this file shrinks by 263 bytes and moves the hot functions by 192–256 bytes. With the layout held fixed (padded control), all four read 0 within noise on every metric. The commit message's table was measured with #336's tree as the base; the table above is this tree. ## Verification - Engine verify bundles on this exact tree: release 859, sanitizers 837, debug 840, kernel 307 with 0 boundary findings, parity 312/312, subset, docs. - One parity sweep of the full population on this head, paired with codegen main, is neutral: 7,989 comparable, no moves. - An independent review returned MERGE; its message fixes are applied. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
luisleo526
added a commit
that referenced
this pull request
Oct 5, 2026
* docs: release notes and version scoping for v1.2.0 CHANGELOG: the Unreleased section becomes 1.2.0 (2026-10-05), a minor release that adds the compiled execution-capabilities extension to the C ABI (PF_CAPABILITIES_API_VERSION, strategy_capabilities_api_version, strategy_capabilities_receipt). One bullet per change since v1.1.0 with its PR (#328, #330, #331, #332, #333, #334, #335, #336, #337, #338 and the session-clock change, written #VCP until its PR number exists), the recipe additions under State hashes (#328, #330, #335; domain tags and epoch unchanged), Report keys (docker/run_json.py is byte-identical to v1.1.0's, so no key changes) and Migration. Version scoping: README, install and public-contract pages name v1.2.0 and codegen 1.2.0; "since the next release" and "fixed on main" in the capabilities guide and the runner guide become 1.2.0 and 1.1.0 (#325). Lines that state what an earlier release shipped stay. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> * docs: 1.2.0's grades from the facts tokens, #339, and codegen's license - CHANGELOG: the session-clock change is #339 (merged as 52292db), in place of the #VCP token of 3082fbd; the "What a script author sees" bullet of 3082fbd now states release 1.2.0's grades (registry baseline pineforge-parity-baseline-20261005-engine-52292db9: 7,970 excellent / 19 strong of 7,989, none below strong) instead of the main scoreboard at 6b77f06. - README: the release sentence names 1.2.0 and renders releases[1.2.0], and the main scoreboard renders the same baseline, from pineforge-release facts/facts.json at 0c2f6838 (the facts export for the 1.2.0 release). - README, LEGAL.md, docs/pages/index.md: pineforge-codegen is under the PineForge Source License 1.1 from its release 1.2.0; releases up to 1.1.0 keep the PolyForm Noncommercial terms they shipped with. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> --------- Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Four TradingView rules, each pinned on synthetic TradingView tapes with 0
counterexamples and each behind its own test-only switch (default on; no
public API, input or strategy setting reaches it): ScriptRuleSwitches in
include/pineforge/source/pine_adapter.hpp, SessionClockSwitches in
include/pineforge/session_time.hpp.
floor_quantity_grid's 1e-6 tolerance snaps UP to a grid point used to come
back raw and off the grid, and then never filled. TradingView floors its
shortest decimal (decimal_floor_lot, shared with the default sizing):
3124.9999999999973 trades 3124. Only that snap changes; strategy.order
quantities and lots that are not a power of ten keep the grid floor.
(PineStrategyHost::open_profit, which generated code calls) mark an open
position at floor(close / mintick + 0.5) * mintick when the close lies off
the tick grid (NYSE:F half-cent closes). The tick is syminfo_mintick_ (the
engine's 0.01 default when a host sets none, the tick fills already use).
On-grid closes, and the kernel's own marks, are unchanged; the adapter's
money gates name BacktestEngine::open_profit explicitly.
fresh-parent-first rank of a re-issued bracket no longer counts an exit
whose entry is neither open nor working as part of the book, so a stop
entry and its bracket that both fill inside one bar settle before the
close-time script.
argument read TradingView's session clock across a daylight-saving switch:
an occurrence that starts or ends at the switch's pre-transition wall time
runs on the post-transition offset throughout; every other occurrence
follows the wall clock. Taped in New York and London only. The chart's own
bar close, read through the SYMBOL's session, keeps the plain windows
(detail::symbol_session_time_close) until a tape pins a symbol session
with an endpoint at its zone's switch hour.
Tests: 41 TradingView tapes committed as fixtures (explicit_qty_floor,
pooc_close_bar_fills, session_transition_clock), each with its exact Pine
source and export. The explicit-quantity and bar-fill tapes run through
their frozen generated strategies in one module
(test_explicit_qty_floor_generated_tapes,
test_pooc_close_bar_fills_generated_tapes); the hand-ported hosts stay for
the script readouts trades cannot show (strategy.equity / openprofit /
netprofit at the read bar, the guarded branch). Each switch turned off fails
exactly its own tapes. test_qty_step_lot_grid_case pins the release
harness's lot-grid case byte for byte, with and without the grid. Docs:
line citations re-anchored, digits only.
Measurement
Full population against the active baseline: 4 up, 0 down, all strong → excellent:
One-rule-off measurements attribute every trade-set move to its rule. The witness tables are byte-identical to main, with zero moved trades. CI profiles are posted as
pineforge/verify; the sweep is posted aspineforge/parity.🤖 Generated with Claude Code