Skip to content

session.ismarket asks the engine's session calendar at each bar's open time - #142

Merged
luisleo526 merged 8 commits into
mainfrom
cg/ismarket
Sep 26, 2026
Merged

luisleo526 merged 8 commits into
mainfrom
cg/ismarket

Conversation

@luisleo526

Copy link
Copy Markdown
Contributor

Summary

session.ismarket on a chart bar now asks the engine's own session calendar whether the session holds the bar's open time, which is how TradingView flags bars. Daily, weekly and monthly bars are always in market.

  • Chart bars: the engine calendar (native_calendar::session_day_at, built from the same session and timezone the kernel uses) is queried once per session day and cached per strategy, outside the checkpointed script state.
  • session.ispremarket / session.ispostmarket: a bar is in one of them only when it is not in market and falls inside that window. The old time-of-day predicates marked in-market bars of overnight sessions as pre-market.
  • Inside request.security: the three flags keep the time-of-day predicate and emit one warning per source location. No per-bar session fact exists for the security bar.
  • Reserved names: _PFSessionMarket, _pf_session_market_ and script_tf_ are reserved, so a script variable can no longer shadow the generated code.

Evidence

  • 15 TradingView tapes (ES1!, EURUSD, XAUUSD, ETH on their lane sessions and on :23456 / 0000-2400 sessions, AAPL regular and extended hours, XAUUSD daily) are replayed in tests/test_e2e_session_ismarket.py.
    • Before: 14/14/14/14/12 wrong bars on the :23456 masks, and 97 of 97 on 0000-2400.
    • After: 0 everywhere, apart from the pinned engine-side isfirstbar/islastbar divergence on the extended-hours tape.
  • Public corpus: 5 of 325 files change C++. Each books byte-identical trades before and after, on three run shapes.
  • Tests: pytest 3,383 passed (4 known skips); tests/test_compile_corpus.py 314 passed; gate selftest OK; PARITY OK over 277 fixtures.
  • Population gate: 6,963 probes, engine main + this branch, against the current baseline measurement.
    • 6,962 comparable probes, 0 tier or outcome changes, 0 hard-lane regressions, target score 0.
    • One heavy hard-lane probe timed out at the verifier's 90 s run budget on both attempts, and the gate recorded it as lost measurement. Its generated C++ is byte-identical under the previous codegen and this branch, and the engine is the same, so its trades are unchanged by construction. The run-budget fix is tracked separately.

The branch is rebased onto main, which adds only release-channel files; no transpiler file overlaps.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LifAW9e5J6gFx7gatuxETE

luisleo526 and others added 8 commits September 26, 2026 09:32
The engine lane H-MEASURE (row G2-36, branch r5/hmeasure at 2b494497)
recorded six `lab tv` tapes of TradingView's own session.ismarket on every
bar of CME_MINI:ES1!, OANDA:EURUSD, OANDA:XAUUSD and BINANCE:ETHUSDT.P 60,
across both 2025 US DST switches and the Thanksgiving week: all 1,138 bars
in market. The files are copied byte for byte; the README records their
provenance, sha256 and campaign notes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Generated session.ismarket called the time-of-day predicate
pine_session_ismarket(syminfo_.session, syminfo_.timezone, time), which
tests each instant's own weekday against a session's day mask and does not
parse "2400". On H-MEASURE's six TradingView tapes (all 1,138 bars in
market) it missed every Sunday-evening open under a ":23456" mask
(14/14/14/14/12 bars on ES1! x3, EURUSD, XAUUSD) and every "0000-2400" bar
(97/97 on ETH).

The engine's source host stores the kernel's in-session fact of the script
bar (NativeDecisionContext::in_session) as session_ismarket_ before every
source callback; codegen now reads it. A request.security payload runs on
its own bars, which carry no host fact, so it keeps the predicate at the
security bar's time (a depth flag set around the payload visitor). A
batch of fewer than two bars given no timeframe gets no session facts from
the engine (script_tf_ stays empty) and keeps the predicate too.
session_ismarket_ and script_tf_ join the reserved emitter names, so a
script variable of either name no longer shadows the host member.

tests/test_e2e_session_ismarket.py replays every tape end to end under the
lane sessions, the ":23456" masks and "0000-2400" / "0000-0000": every bar
is now TradingView's flag, and the pre-lane build (b0ed496) misses exactly
the pinned bars.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A public probe carries every session flag in each entry's Signal
(session.ismarket, ispremarket, ispostmarket, isfirstbar, islastbar and the
_regular twins). Exported with `lab tv` (ws-report-v1, rangeProof covered,
--no-note) on H-MEASURE's six charts and windows: CME_MINI:ES1! 60 across
both 2025 US DST switches and the Thanksgiving week, OANDA:EURUSD 60,
OANDA:XAUUSD 60 and BINANCE:ETHUSDT.P 60.

TradingView flags all 1,138 bars in market and none pre- or post-market;
every session day opens on an F1 bar and closes on an L1 bar, the _regular
flags equal to the plain ones. The README records each tape's sha256 and
export time.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
session.ispremarket / ispostmarket were time-of-day windows only: 04:00 to
the session's first open, and its close to 20:00. For an overnight session
the "open" is the evening restart, so the windows covered in-market hours:
on this lane's TradingView tapes of every session flag the predicates read
108/108/98 bars of ES1! ("1700-1600", America/Chicago) and 117 of XAUUSD
("1800-1700", America/New_York) as pre-market and 30/29/29 and 20 as
post-market, where TradingView flags every bar in market and none in an
extended session.

A bar in market is in neither extended session: both flags now require
the kernel's in-session fact to be false, as session.ismarket reads it, and
bars out of market keep the windows. A request.security payload keeps the
plain predicates at the security bar's time, as it does for ismarket.

tests/test_e2e_session_ismarket.py now replays both tape sets and compares
all seven flags (ismarket, ispremarket, ispostmarket, isfirstbar,
islastbar and the _regular twins) on every bar; the pre-lane build misses
exactly the pinned ismarket, ispremarket and ispostmarket bars, and the
session-day flags were already TradingView's.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The every-flag probe on NASDAQ:AAPL 60, 2025-03-03 .. 03-15, exported
through the pinescript-scrapper WebSocket exporter (ws-report-v1,
rangeProof covered). The exporter always asks for the regular session;
the extended-hours tape ran a scratch copy that differs in one line, the
symbol descriptor's session, with CGIM_TV_SESSION=extended. The README
records both files' sha256 and the exporter's commit.

The extended chart's bars open on the hour and TradingView flags each by
its open time: the 09:00 bar, which holds the 09:30 open, is pre-market
(M0P1) and the 16:00 bar post-market (M0Q1). isfirstbar / islastbar are
the extended day's 04:00 and 19:00 bars, the _regular twins the regular
day's 10:00 and 15:00 bars. The regular chart's 09:30..15:30 bars are all
in market, first and last at 09:30 and 15:30.

The README's header now covers exporters other than lab tv: every
metrics.json carries the sha256 of strategy.pine as sourceArtifactHash.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The previous step read the kernel's per-bar fact session_ismarket_. An
independent review found, and this lane's extended-hours NASDAQ:AAPL tape
proves, that the fact asks the bar's grid interval rather than the bar:
after a 09:30-16:00 session the 16:00 bar shares the 15:30 interval, so the
fact put it in market (and out of post-market) on all 10 days, where
TradingView flags it post-market (M0Q1). TradingView flags a bar by its
own open time: the extended chart's 09:00 bar, which holds the 09:30 open,
is pre-market.

A chart bar's session.ismarket now calls the emitted helper
_pf_session_ismarket (codegen/session_market.py): a D/W/M bar is in market;
otherwise the engine's native_calendar answers for the bar's open time, on
the calendar the kernel builds for the run from the same session and
timezone, resolved once per session day as the kernel does. That keeps the
Sunday-evening opens under ":23456" and the "0000-2400" day in market, as on
the six-chart tapes, and puts the 16:00 bar out of market, as on the AAPL
tape; the time-of-day predicate was already right there. It needs no
timeframe, so a one-bar run, which the engine gives no session facts, is
answered too. ispremarket / ispostmarket keep "in market is in neither".

A request.security payload still reads the time-of-day predicates at the
security bar's open time, and now warns that they can differ from
TradingView. The helper and script_tf_ are reserved emitter names.

The public-corpus TUs that change are still the five that read session.*;
each books byte-identical trades before and after on the corpus 15m feed
(default and declared 0930-1600 America/New_York syminfo) and a 60m
resample with the declared session. The per-session-day memo keeps their
run time at the pre-lane build's.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The every-flag probe on OANDA:XAUUSD 1D, 2025-01-01 .. 04-01, exported with
`lab tv` (ws-report-v1, rangeProof covered, --no-note). TradingView stamps
each of the 64 daily bars at 17:00 America/New_York, the break of the
1800-1700 session, and flags every one M1P0Q0F1L1f1l1: in market, and the
first and last bar of its session day. A daily bar holds whole session days.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A second review of the calendar lowering found no blocker and asked for:

- A per-strategy cache instead of a per-thread one. Strategies with
  different sessions taking turns on one thread re-parsed the calendar on
  every read, every read paid a thread-local lookup in the loaded strategy
  module, and a throwing parse left the old calendar under the new key.
  The emitted _PFSessionMarket type now precedes the class and each
  strategy holds one mutable _pf_session_market_, declared after the
  checkpointed script state; the key is written only after a successful
  parse. A day the calendar cannot resolve, or an exception while resolving
  it, is out of session, as the kernel reads it.
- An accurate payload warning: on a D/W/M chart the kept predicate is a
  constant, not the security bar's time of day. The docs now say that a
  user function the security evaluator calls reads the chart bar, that the
  pre/post windows are the first window's, and that the calendar also puts
  a masked overnight session's bars after the weekly close out of market.

A new TradingView tape covers the helper's D/W/M branch: every XAUUSD daily
bar, stamped at the 17:00 ET break, is in market (without the branch the
calendar reads all 64 out). Two engine-free tests pin the emission (one
type before the class, one cache outside the checkpoint, nothing when no
chart flag is read) and one warning per payload read site.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@luisleo526
luisleo526 merged commit a425965 into main Sep 26, 2026
9 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