Skip to content

Session flags match TradingView on multi-window and extended-hours charts (K-SESSION-WINDOWS) - #293

Merged
luisleo526 merged 6 commits into
mainfrom
r5/k-session
Sep 27, 2026
Merged

luisleo526 merged 6 commits into
mainfrom
r5/k-session

Conversation

@luisleo526

Copy link
Copy Markdown
Collaborator

Session flags match TradingView on multi-window and extended-hours charts

Each rule is pinned by TradingView tapes and has a test that fails before the fix.

  • Pre- and post-market read every session window (session_time.cpp). Pre-market runs from 04:00 to the session day's first open; post-market runs from its last close to 20:00. A break between windows is neither, and an overnight or 24-hour session has neither. Before, only the first window was read, with fixed bounds, so HKEX lunch bars read as post-market and an overnight window read as both. Pinned on TSE:7203, HKEX:700 and CBOT:ZC1! (15/60/240 minutes), ES1!, XAUUSD, EURUSD and ETHUSDT.P, with NASDAQ:AAPL as the single-window control.
  • A script bar's session-day facts read the bar at its own label (kernel; ADR-0001 present_session_day row amended), at max(label, slot eligible_open). On an extended-hours chart the regular day now ends on the 15:00 bar, and bars that open inside a break are in market.
  • session.isfirstbar / islastbar mark the chart's session day, including extended hours; new _regular host members carry the regular session. On AAPL 60 extended: isfirstbar 04:00, islastbar 19:00, isfirstbar_regular 10:00, islastbar_regular 15:00.
  • Aggregated bars that open inside a session break (TSE 12:00, ZC 08:00 at 60 minutes) are in market, as the kernel already read them. Pinned with no behaviour change.
  • Docs: the session.ismarket generation comment now matches the tree.

Verification (this exact tree)

  • preflight: 43 stages.
  • release: 724 rows (floor 724).
  • debug and sanitizers: 705/705.
  • kernel: 294 rows (floor 293), 0 residual findings.
  • docs: 0 Doxygen warnings.
  • Corpus parity: 312/312, 0 moved; subset 54/54.
  • Population replay of every probe reading these flags, plus 40 excellent controls: 0 tier moves.
  • Floors: kernel 291 → 293, release 720 → 724, registration 705/705/714, counted from ctest -N on top of the previous engine PR's rows.

The population parity sweep of this exact tree is posted as pineforge/parity.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LifAW9e5J6gFx7gatuxETE

luisleo526 and others added 6 commits September 28, 2026 01:42
…ight session has neither

TradingView flags no bar of TSE:7203 (0900-1130,1230-1530 Asia/Tokyo),
HKEX:700 (0930-1200,1300-1600 Asia/Hong_Kong) or CBOT:ZC1! (1900-0745,
0830-1320 America/Chicago) pre- or post-market, at 15, 60 and 240 minutes:
1,570 bars across nine `lab tv` every-flag tapes, the 60-minute bars that
open inside a break (TSE 12:00, ZC 08:00) included. Nor any bar of the
overnight and 24-hour charts CME_MINI:ES1! (1700-1600), OANDA:XAUUSD
(1800-1700), OANDA:EURUSD (1700-1700) and BINANCE:ETHUSDT.P (24x7). On
NASDAQ:AAPL, the one-window control, the extended-hours tapes flag the bars
opening 04:00 to before 09:30 ET pre-market and 16:00 to before 20:00
post-market, and the regular-hours tapes none.

pineforge::session_in_premarket / session_in_postmarket (session_time.cpp),
which generated code reaches through PineStrategyHost's class-scope
wrappers -- gated by the calendar's in-market answer on the chart, and alone
inside a request.security payload -- took the pre-market end and the
post-market start from the FIRST "HHMM-HHMM" window, with fixed 04:00 /
20:00 bounds (lane CG-SESSION-2 finding F6). So HKEX:700's and TSE:7203's
afternoons read post-market, the same windows written the other way round
made the morning pre-market, and an overnight first window made one instant
both (ZC 60: 60 bars both).

The rule is now: pre-market runs from 04:00 to the session day's first open
and post-market from its last close to 20:00, over every window in any
order (extended_hours_bounds); a break between windows is neither; a
session with a window that wraps midnight -- its day opens on the previous
date -- or one that spans 24 hours has neither. A single same-day window
reads exactly as before, the 24-hour spellings too.

tests/test_session_prepost_windows_tape.cpp (kernel-only, new) replays the
seventeen tapes of tests/fixtures/session_windows (README.md there: every
tape's provenance; the CG-SESSION-2 and CG-ISMARKET tapes are copied
unchanged from pineforge-codegen) through the chart-bar forms, and pins a
synthetic day's off-market bars: the HKEX lunch is neither in either order,
corn has neither. At 1e360bd it fails 31 of 114 checks (log in the lane
scratch, tdd-f6-red.log): pre / post misses HKEX 15 0/120, reversed 100/0,
TSE 15 0/130, ZC 15 350/240 (both-order 150/40), HKEX 60 0/30, TSE 60 0/40,
ZC 60 100/70 with 60 both, ES1! 108/30, XAUUSD 117/20; the AAPL, EURUSD and
ETH tapes 0 before and after. The replay reads TSE:7203 at 1545, where
TradingView holds the 15:30 closing bar (CG-SESSION-2's open finding).

Session policy, in the language calendar functions ADR-0001 rules for
generated code (session_time.cpp); the kernel is untouched.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…l, not at an earlier instant of its interval

TradingView flags NASDAQ:AAPL 60 with extended hours (bars on the hour,
04:00-19:00 ET, `cgim-flags-aapl-60-ext`) out of market on the 16:00 bar,
and ends the regular day on the 15:00 bar (islastbar_regular); its first
regular bar is 10:00. TSE:7203's 60-minute bar that opens at 12:00, inside
the 11:30-12:30 break, and CBOT:ZC1!'s that opens at 08:00, inside
07:45-08:30, are in market (`ksw-flags-tse7203-60`, `ksw-flags-zc1-60`,
and the 240-minute tapes; `lab tv --no-note`).

present_session_day (native_execution_consumer.cpp) read every bar -- and
every held neighbour, through eligible_session_point -- at the first
eligible instant of the script interval holding its label. On the
extended chart the regular session's 09:30-anchored grid files the 16:00
bar under the 15:30 interval, so it read in session and closed the day in
place of 15:00 (lane CG-ISMARKET finding F1): in_session missed 10 bars
of the tape and closes_session_day 20. A bar is now read at its label, or
at its interval's first eligible instant when that is LATER: the break-open
bars keep reading in session at their reopen, and an instant before a
bar's own open is never the bar's. The certified-cycle fast path already
answered that way (an in-span label is at or after its interval's first
eligible instant), so it is unchanged.

ADR-0001 ruling (row `present_session_day`, amended here): generic
kernel. Reading a bar at its own label is a property of the facts, not a
platform rule -- an instant before a bar opened is not in that bar,
whatever grid the calendar keys the bar's interval to -- and nothing
branches on a venue: the session, the zone and the labels are the run's
data. The facts stay presentation and fold into no digest, so no hash,
epoch or layout moves. Pine's pre/post-market and the extended-hours day
stay in the adapter and session_time.cpp.

tests/test_native_session_day_bar_open.cpp (kernel-only, new): a bare
FeedTolerant host replays AAPL 60 / 15 with and without extended hours,
and TSE:7203 / CBOT:ZC1! 60 and 240 aggregated from TradingView's own
15-minute bars; every script label, in_session, opens_session_day and
closes_session_day is TradingView's session.ismarket /
isfirstbar_regular / islastbar_regular on every bar. At 1e360bd the
extended AAPL 60 tape fails 2 of 64 checks (in_session 10 bars,
closes_session_day 20; lane scratch tdd-f1-red.log); every other case
passes before and after, the break-open bars included. The 102 session,
calendar, stream and C-API ctest rows pass unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… and the _regular pair the regular one

TradingView's extended-hours tapes of NASDAQ:AAPL (`cgim-flags-aapl-60-ext`,
`cgs2-hist-aapl-15-ext`) part the two pairs: at 60 minutes isfirstbar is the
04:00 ET bar and islastbar the 19:00 one, isfirstbar_regular 10:00 and
islastbar_regular 15:00; at 15 minutes 04:00, 19:45, 09:30 and 15:45. On
every chart without extended hours (AAPL regular, TSE:7203, HKEX:700,
CBOT:ZC1!, CME_MINI:ES1!, OANDA:XAUUSD / EURUSD, BINANCE:ETHUSDT.P) each pair
is one flag.

The Pine host held one pair, the kernel's regular session-day facts, which
generated code read for both spellings (lane CG-ISMARKET finding F2): with
F1 in place the extended tapes still missed isfirstbar and islastbar on 20
bars each (lane scratch tdd-f2-red-behaviour.log).

PineStrategyHost now holds session_isfirstbar_regular_ /
session_islastbar_regular_, the kernel's facts as they are, and widens the
plain pair to the chart's session day (update_extended_session_day): the
regular day plus the pre- and post-market bars of its date the chart holds,
pre- and post-market being session_time's windows (F6), which exist only on
a session day that opens and closes on one date. The bar before is the
script bar published last; the bar after is the first retained input past
the script bar's interval (PineScheduler::retained_input_from), so the
chart, magnified and aggregated paths read one answer for the same bars, as
lane E25 requires; a batch's final bar closes its day by the kernel's
convention. A stream's realtime bar has nothing held after it: a pre- or
post-market bar then reads the chart timeframe's next slot and a regular bar
keeps the kernel's close. A chart holding no pre- or post-market bar reads
exactly the kernel's facts, as before. Pine policy, in the adapter; the
kernel is untouched and the new members fold into no hash (waivers beside
the other session flags).

tests/test_session_extended_day_tape.cpp (release, new) replays eleven
tapes of tests/fixtures/session_windows through the host: every flag of all
four is TradingView's, and a stream of the extended AAPL 60 tape keeps the
plain pair exact. It pins one kernel residual there: on a realtime day the
kernel's scheduled close steps the regular session's 09:30-anchored grid and
expects a 15:30 bar after the off-grid 15:00 one, so islastbar_regular never
fires (8 days). Fail-before at its parent 3d218d7 (F1): the unit does not compile
(`session_isfirstbar_regular_` undeclared, tdd-f2-red-compile.log).

Two existing rows fed synthetic post- and pre-market bars and pinned the
regular day on the plain flags. Their strings move to the _regular members
unchanged, and the plain flags now read the chart's day as TradingView's
extended tapes do: test_session_islastbar_aggregation (one session close:
islastbar 16:10, the last post-market bar; two sessions: islastbar Tuesday
16:10, isfirstbar Wednesday 09:15, the first pre-market bar; every path
still agrees bar for bar) and test_aggregated_path_regressions (the
calc_on_order_fills post-market case: 15:45 keeps islastbar_regular, and
one answer per bar still holds). None of the 312 corpus probes reads a
session flag.

Three documentation sentences are rewritten because the host now widens
the plain pair: the design record's OT7 row (lane F5, f064b1c: the adapter
selecting the kernel's facts into the three flags) and the Pine v6
coverage detail's session.isfirstbar_regular / islastbar_regular rows
(e244d65: 'Aliased to session.isfirstbar'), and ADR-0001's line that the
host computes no session-day rule of its own.

Generated code still lowers session.isfirstbar_regular / islastbar_regular
to the plain members (codegen visit_expr.py): routing them to the new
members is the transpiler's follow-up, until which a generated _regular read
on an extended-hours chart follows the chart's day.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…n market, as the kernel reads them

Lane CG-ISMARKET's finding F7 (an aggregated split-session bar whose
nominal open falls in a break reads out of market) had no tape. This
lane's `lab tv --no-note` exports give one (tests/fixtures/session_windows):
TSE:7203 60 opens a bar at 12:00, inside the 11:30-12:30 lunch break, and
CBOT:ZC1! 60 one at 08:00, inside 07:45-08:30; TradingView flags both in
market, neither pre- nor post-market nor a day's first or last bar. Its 60-
and 240-minute bars open at the same instants as the engine's aggregation
of TradingView's own 15-minute bars of the window.

TradingView agrees with the engine where it answers per bar, so nothing
changes: the kernel reads such a bar at its interval's first eligible
instant, the reopen (kept by F1), and session_ismarket_, the session-day
flags and the gated pre- / post-market predicates are TradingView's on
every bar. What disagrees is a reading at the bar's open instant -- the
time-of-day predicate pine_session_ismarket, and the session calendar asked
at the open, which is how the transpiler lowers a chart's session.ismarket
(codegen session_market.py) -- 10 bars on each 60-minute tape, none at 240.

tests/test_session_break_open_bar_tape.cpp (release, new) replays TSE:7203
and CBOT:ZC1! 60 and 240, aggregated from their 15-minute tapes, through the
Pine host: labels and every flag are TradingView's, and the at-open
readings' misses are pinned (RESIDUAL) for the transpiler, whose change is to
lower session.ismarket to session_ismarket_. The rows it checks agree at
1e360bd as well (the kernel fact already read the bucket's first eligible
instant); the unit compiles from df4510e (F2) on, which adds the _regular
members it reads.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…lendar at the bar's open, the predicate only inside request.security

Lane CG-ISMARKET's finding F5: the comment above PineStrategyHost's session
flags said a generated session.ismarket calls pine_session_ismarket. Since
pineforge-codegen a425965 a chart's session.ismarket asks the session
calendar at the bar's open through the transpiler's own helper
(_pf_session_market_, native_calendar::session_day_at; e.g. the corpus's
validation/symbol-specified/AAPL/session-ismarket-nyse-rth-01/generated.cpp),
and only a read inside a request.security payload still calls the
class-scope pine_session_ismarket. The comment now says so, and says which
member each flag spelling reads today (the transpiler lowers the _regular
spellings to the plain pair).

The same stale sentence stood in ADR-0001 (the adapter's Pine language
state), docs/pages/pine-to-native.md (the session.ismarket row, whose
in_session sentence now also states F1's reading), the Pine v6 coverage
detail and the design record's row 36, and in the H-MEASURE tape row's
header; each now names the calendar reading on the chart, the predicate in
a request.security payload, and the break-open bar only the kernel's fact
holds in market (tests/test_session_break_open_bar_tape.cpp). The design record's row 36 sentence
(INT26, d093560: the generated predicate is codegen's to route) is
rewritten to what the transpiler does now. Two ADR and
design citations of the host header re-anchored. Comments and pages only.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…1 -> 293, release 720 -> 724, PR registration 705/705/714

Lane K-SESSION-WINDOWS adds four CTest rows. Two are source-free and
register in the kernel profile and in release (F6
test_session_prepost_windows_tape, F1 test_native_session_day_bar_open);
two reach the source layer and register in release only (F2
test_session_extended_day_tape, F7 test_session_break_open_bar_tape). All
four register in the Debug, sanitizers and native profiles as well.

Counted with ctest -N after configuring each profile exactly as ci_verify
does (configure only), on main 7f807b9 and on this rebased tree:

  profile     main 7f807b9   rebased   delta
  release     720             724       +4
  debug       701             705       +4
  sanitizers  701             705       +4
  native      710             714       +4
  kernel      292 registered  294       +2 (F6, F1)

The row diff between the two trees is exactly the lane's four rows (two
in the kernel profile), none of them XSYM-D's. So KERNEL_MIN_TESTS 291 ->
293 (294 registered, the WebSocket row still skips on a system libcurl),
RELEASE_MIN_TESTS 720 -> 724 and EXCLUDED_REGISTERED_MIN 701/701/710 ->
705/705/714, each keeping XSYM-D's accounting above the lane's, with the
self-test's literal and docs/ci.md's sentence naming both lanes. The two
constants moved lines, so their citations in CONTRIBUTING.md and
docs/pages/contributing-llm.md are carried by hand (ci_verify.py:284 ->
:293, :447 -> :463); check_doc_anchors resolves every anchor.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@luisleo526
luisleo526 merged commit 962960b into main Sep 27, 2026
16 checks passed
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