War story
v2.2.3-split: holiday-glue fix (2026-07-06)
Root cause
Friday July 3 was the observed July 4 holiday. CME ran the half-session and glued it into Monday July 6's trade date, so Monday's live daily bar carries a Thursday-evening open stamp (that's also why the ES daily chart shows no separate Friday candle). Two pieces of logic treat "old bar stamp" as "market closed / data stale," and both misfired during Monday's LIVE session:
isHolidayClosedmeasurestimenow - time(the bar OPEN). The glued Monday bar reads ~87h old, so Auto preview engaged mid-session: PREVIEW MODE banner, D and W rows showing "?", and the classic preview shift demoting the FORMING period into C1. That is the ES symptom: weekly trigger levels equal to the current day's range instead of last week's.shouldStraddlecompares the CHART BAR's open period to now. The glued Monday bar opens in last week, now is this week, so it declared a phantom straddle for the W slot on the daily chart and shifted live weekly data the same way.
Neither check asks the question that actually matters: is the SERVED HTF data stale? On the live bar the security feed was serving the current week just fine. The July 1 quarter-roll case (the reason the straddle exists) is different: there the served bar really is the prior period, and that behavior is preserved below.
The CL 1h chart is the same event seen from the other side: with Friday glued into Monday's trade date, the daily series' prior bar is Thursday July 2 (the F2d hammer), so the D row reads C1 = F2d with the trigger at Thursday's high and the low line at 67.00. Verify against the CL DAILY chart after reloading: its prior candle should be that same Thursday hammer and its current candle should contain Friday's range. If the daily chart instead shows a separate Friday candle while the 1h D row still says F2d/Thursday, that is a different bug; flag it.
Edits (apply in the TradingView Pine editor to v2.2.2-split)
Edit 1: version bump
indicator("TheStrat Suite - v2.2.2-split", ...) -> "TheStrat Suite - v2.2.3-split"
(and the // TheStrat Suite - v2.2.2-split header comment line).
Edit 2: isHolidayClosed measures from the bar's scheduled CLOSE
Old:
// Holiday fallback: no new bar for 3x chart period or 4 hours, whichever is larger
isHolidayClosed = (timenow - time) > math.max(chartPeriodMs * 3, 4 * 60 * 60 * 1000)
New:
// Holiday fallback: now is 3x chart period or 4 hours (whichever is larger) past the bar's
// SCHEDULED CLOSE.
// FIX GLUE-1 (2026-07-06): measure from time_close, not time (the open). CME glues holiday
// half-sessions into the next trade date (Fri Jul 3 2026 lives inside Mon Jul 6's bar), so
// the LIVE Monday bar opened with a Thursday-evening stamp and (timenow - time) read ~87h:
// Auto preview engaged during a live session and demoted the forming week/day into C1
// ("weekly levels on the current day", D/W = "?", phantom PREVIEW MODE banner). A live bar's
// scheduled close is always in the future, so this can never fire while a session trades;
// genuinely halted markets still trip it once now exceeds the last scheduled close.
isHolidayClosed = not na(time_close) and (timenow - time_close) > math.max(chartPeriodMs * 3, 4 * 60 * 60 * 1000)
Edit 3: shouldStraddle keys off the SERVED HTF bar, not the chart bar
Replace the whole shouldStraddle function (keep the body logic; only the comment, the
signature, and the tB line change):
// FIX GLUE-2 (2026-07-06): staleness is a property of the SERVED HTF data, not of the chart
// bar's open stamp. shouldStraddle now takes the served current HTF period-open (pre-shift)
// and compares ITS calendar period to now. If the feed already serves the new period, no
// shift is needed no matter how old the chart bar's stamp is. Holiday-glued Monday bars
// (open stamped Thursday evening) made the old chart-bar comparison fire a phantom straddle
// during a live session, demoting the LIVE forming week into C1. The mid-bar calendar-roll
// case this fix was built for still works: there the served bar really is the prior period,
// so servedTime's period differs from now and the shift proceeds exactly as before.
// HARDENED 2026-07-02 (unchanged): "now" is evaluated under BOTH the raw and the +12h
// reading, and BOTH must differ from the served bar's period, killing period-end-afternoon
// phantoms.
shouldStraddle(string tf, int servedTime) =>
tB = servedTime + OFF_MS
tN = timenow + OFF_MS
tR = timenow
mB = month(tB, "America/New_York"), yB = year(tB, "America/New_York"), wB = weekofyear(tB, "America/New_York")
mN = month(tN, "America/New_York"), yN = year(tN, "America/New_York"), wN = weekofyear(tN, "America/New_York")
mR = month(tR, "America/New_York"), yR = year(tR, "America/New_York"), wR = weekofyear(tR, "America/New_York")
qB = math.ceil(mB / 3), qN = math.ceil(mN / 3), qR = math.ceil(mR / 3)
saB = mB <= 6 ? 1 : 2, saN = mN <= 6 ? 1 : 2, saR = mR <= 6 ? 1 : 2
result = false
if tf == "W" or tf == "1W"
result := (wN != wB or yN != yB) and (wR != wB or yR != yB)
else if tf == "12M"
result := yN != yB and yR != yB
else if tf == "6M"
result := (saN != saB or yN != yB) and (saR != saB or yR != yB)
else if tf == "3M"
result := (qN != qB or yN != yB) and (qR != qB or yR != yB)
else if tf == "M" or tf == "1M"
result := (mN != mB or yN != yB) and (mR != mB or yR != yB)
result
Edit 4: the two callsites pass the served time
In applyPreviewShift (the ccTime parameter is the served current HTF period-open):
doPreview = shouldApplyPreview(tf, previewEnabled) or (straddleEnabled and isStraddleTF(tf) and shouldStraddle(tf, ccTime))
In the T3 recon block (use realPeriodTime, NOT rawS.ccTime, which is rewritten by the shift):
if cfgS.enabled and not na(rawS) and rawS.isPreview and canRecon and not na(reconDT) and array.size(reconDT) > 0 and isStraddleTF(cfgS.tf) and shouldStraddle(cfgS.tf, rawS.realPeriodTime)
Expected after applying + reloading
- ES 1D: PREVIEW MODE banner gone during the live session. W and D table cells show real bar types instead of "?". Weekly trigger lines sit at LAST week's high/low, drawn from last week's open, no longer hugging the current day's range. D levels anchor to the last completed daily bar (the glued feed's prior bar, i.e. Thursday's).
- CL 1h: unchanged by the code edits (nothing was shifting there); its D row reflects the glued daily feed. Confirm it matches the CL daily chart's candles.
- The July 1 mid-week quarter/month straddle behavior on weekly charts is preserved: when the feed genuinely serves the prior M/3M bar mid-chart-bar, the shift plus daily reconstruction still runs.
SUPERSEDED: v2.2.4 (2026-07-06, later same day)
Live testing on CL 1D confirmed GLUE-1 worked (banner gone, D row correct) but the W slot still shifted. Root cause: the glue reaches the WEEKLY bar too. This week's weekly bar opens Thursday Jul 2 18:00 ET (Friday's half-session belongs to Monday's trade date), ~84h before its true period, so even the SERVED bar's open stamp parsed as last week. The +12h normalization assumes opens are at most a session early; the glue is 3.5 days early.
GLUE-2b replaces the calendar-key test entirely. A served HTF bar is stale exactly when now is past its scheduled close:
shouldStraddle(string tf) =>
int tc = time_close(tf)
not na(tc) and timenow > tc
Why this is robust:
- A live bar's scheduled close is always in the future, so it can never fire mid-session (glued or not). No week-numbering, no timezone parsing, no +12h assumptions.
- Direction-aware: a trade-date bar running AHEAD of the calendar (the glued Friday session living inside next week's bar) cannot fire it.
- The Jul 1 mid-week quarter roll still works: once the served Q2 bar's Jun 30 17:00 close passes, the shift arms for the rest of the straddling weekly chart bar.
- time_close(tf) comes from the same trade-date calendar that stamps the bars, so it tracks exactly the bar request.security serves.
Both callsites drop the servedTime argument. Shipped as TheStratSuite_v2.2.4-split.pine.