# Position Planner Pro — Position Planner Pro
_The planner stops signalling trades it cannot give a real stop to: BUY now requires structure inside your risk cap, weekly charts stage off the 30-week MA, and every taken trade is tracked from entry to exit._

## ⚠️ Behavior changes (read before trusting old signals)

| Change | Old | New | Why it matters |
|---|---|---|---|
| BUY blocked when the stop was ✂-clamped | A setup whose structural stop sat beyond the max-stop cap still printed BUY, using the clamped cap price as the stop | State shows **⌛ ARM** with the reason "no structural stop ≤ cap"; the BUY marker, table BUY state and both BUY alerts are suppressed | This is the biggest change. Expect materially **fewer BUY prints** — historically and live. Every remaining BUY has a genuine structural level inside the cap. New input, default ON; turn it off to restore old behavior |
| BUY marker follows the decision engine | Marker printed on setups the engine rejected (RS lagging, over-extended, clamped stop) | Marker prints only when the table actually says BUY | Fewer markers on history and live; marker and table can no longer disagree |
| Weekly-chart stage basis | A weekly chart auto-mapped the stage timeframe to Monthly → the 30-**month** SMA | Weekly charts stay on Weekly → the 30-**week** MA. Only above-weekly charts map to Monthly | Weinstein's actual definition. On weekly charts the stage classification, stage shading, trail EMA and stage alerts all change basis — weekly stage reads **will differ from before** |
| Higher-timeframe stage & trail read confirmed bars | Stage, Stage-2 shading, BUY confirmation and the trail EMA tracked the *developing* weekly bar — signals could appear mid-week and disappear | All computed from the last **closed** higher-timeframe bar | No more repainting. Stage flips, BUY and the "Entered Stage 2" / "Lost trail EMA" alerts now fire on the first chart bar *after* the weekly bar closes — slightly later, but they stay put. The table's Stage row still shows the live developing stage (display only) |
| Volume gate on double-bottom and pullback | These two triggers fired regardless of volume | Both now require volume at or above average | A dry neckline break is the classic Weinstein trap. Those setups now show as forming/ARM instead of triggering — backtests will show fewer of these entries |
| Bull-flag breakout can fire at all | The flag-high level included the live bar, so the breakout condition was mathematically impossible — "Bull flag · forming" never converted | Flag high/low measured on completed bars; the breakout fires | New setups, entries and auto-stops appear where the chart previously went quiet |
| Breakout-setup stop anchor | Anchored to the last pivot low, which could be months old — so it silently clamped to the percent cap | Anchors to the more recent of the last pivot low and the 20-bar swing low, minus the buffer | Breakout stops are tighter and more structural, so they clamp less often and the new BUY block rejects fewer of them |
| Stop / target alerts | Fired off the continuously re-computed live plan | With position tracking ON (default) they fire only while a tracked position is OPEN, against the entry/stop/targets frozen at BUY | Alerts now refer to the trade you actually took, not to a plan that has moved since |

## 🐛 Fixes

- The bull-flag breakout could never trigger — the consolidation high was measured including the live bar, so price could never cross it. Fixed; the flag path (and its auto-stop) is live code again.
- Stage, Stage-2 shading and the trail EMA repainted intraweek: signals and alerts could appear mid-week and vanish by Friday's close. All higher-timeframe reads are now confirmed-bar.
- Five crossover triggers (breakout, ignition, flag, double-bottom, pullback) and the trail-loss alert sat inside conditional blocks, where Pine v6's short-circuit `and` skipped their internal state updates — producing phantom and missed triggers. All are now evaluated every bar.
- Plan-zone boxes (stop, TP1–TP3) disappeared permanently after one hide cycle of "Show plan & table only on BUY" — their colors were cleared and never restored. They now reappear correctly.
- The suggested-stop R:R printed the R:R measured at *your* stop, overstating reward-to-risk on blue-sky setups where the suggested stop is wider. The chart label, the table cell and the cell's color now all use the correct figure.

## ✨ New features

**Clamped-stop BUY block** — suppresses BUY on any setup whose structural stop sits beyond your max-stop cap, downgrading it to ARM. · **Inputs:** `Block BUY when stop is clamped` (ON) · **What you'll see:** "⌛ ARM — {setup} — no structural stop ≤ cap", and "no structural stop ≤ cap" appears in the blockers list on NO-BUY states.

**Position tracking** — on a confirmed BUY the entry, stop, quantity and TP1–TP3 are frozen; stop-hit and target-hit events are measured against those frozen levels until the position closes (stop-out, TP3, or manual flat). · **Inputs:** `Track position (freeze plan on BUY)` (ON), `Manual flat (clear tracked position)` (OFF) · **What you'll see:** a new table row — "Position — OPEN · qty @ entry · SL x" or "FLAT". Full table 22 rows, Minimal 9; the row and the counts revert when tracking is off.

**Confirmed bars only** — gates all six triggers to confirmed bar closes. · **Inputs:** `Confirmed bars only` (OFF) · **What you'll see:** nothing on history (historical bars are always confirmed); live triggers stop flickering intrabar.

**Order-ready dynamic alerts** — JSON payloads fired at bar close. BUY carries symbol, timeframe, entry, stop, quantity, rupee risk and TP1; STOP/TP1/TP2/TP3 carry the level and quantity. · **Inputs:** none — create one TradingView alert on "Any alert() function call" · **What you'll see:** structured alert bodies you can route straight to an order handler. All existing named alert conditions are unchanged.

**Distance to trigger on ARM** — the ARM row now appends how far price is from the pending breakout level. · **Inputs:** none · **What you'll see:** "· +1.8% to base high" (or flag high / neckline). Omitted when the level does not apply or is already below price.

## ⚙️ Changed defaults

| Input | Old | New | Effect |
|---|---|---|---|
| `Max stop distance %` | 8.0 | 3.0 | Matches the hard ≤3% stop rule the tooltip always described. At full-capital sizing an 8% stop is roughly ₹2.3L of risk on a ₹28.4L account. **Existing chart instances keep their saved 8% value** — TradingView only applies a new default to freshly added copies. On live charts you must set this to 3 by hand (or remove and re-add the indicator). |

Note on the new inputs above: `Block BUY when stop is clamped` and `Track position` both ship ON, and both apply to existing chart instances the moment you update the script, because they are new inputs with no saved value.

## 🧪 Test plan (before publishing)

1. **Max-stop default.** Add a fresh copy of the indicator to a clean chart and open Settings → Entry & Stop. **Pass:** "Max stop distance %" reads 3. Then open an *existing* saved chart carrying the old copy. **Pass (expected):** it still reads 8 — confirm this, then set it to 3 manually on every live chart. Log which charts you changed.
2. **Clamped-stop BUY block.** Find a Stage-2 name in a wide, high-ATR base (any recent NSE breakout with a ~6–10% distance to its swing low). With `Block BUY when stop is clamped` ON, look for setups where the stop label carries the ✂ mark. **Pass:** those bars show "⌛ ARM … no structural stop ≤ cap" and print **no** BUY marker. Toggle the input OFF and reload. **Pass:** the same bars now print BUY with a ✂ stop. Count BUY markers over the last 250 bars in both states — ON must be ≤ OFF.
3. **Weekly stage basis.** Put the indicator on a weekly chart of a long-trending large-cap (e.g. NSE:RELIANCE, NSE:TCS) with "Auto stage timeframe" ON. **Pass:** the Stage row and shading agree with a manually plotted 30-period SMA on that weekly chart, not with a 30-month one. Compare against your previous screenshot/knowledge of the same chart — a changed stage read here is expected, not a bug.
4. **Bull flag fires.** Set the stage/setup engine on, and scan a name that ran hard and then went sideways for 8–15 bars (pole ≥18%, consolidation ≤10% range are the shipped thresholds). **Pass:** at least one "Bull flag" trigger prints where the old build only ever showed "Bull flag · forming". Confirm the auto-stop label anchors under the flag low.
5. **Position tracking freezes the plan.** With `Track position` ON, step forward from a confirmed BUY bar. **Pass:** the Position row reads "OPEN · qty @ entry · SL x" and the entry/stop/qty in that row do **not** move as later bars print, even while the live plan above them re-computes. Continue to a stop-out or TP3. **Pass:** the row flips to FLAT and the stop/target alert fires against the frozen level, not the current one. Tick `Manual flat`, confirm FLAT, then untick.
6. **Plan-zone boxes survive a hide cycle.** Turn `Show plan & table only on BUY` ON, scroll to a stretch with no BUY (boxes vanish), then scroll back to a BUY bar. **Pass:** the stop and TP1–TP3 boxes render with their colors and borders again. In the old build they stayed invisible for the rest of the session.
7. **No repaint.** Set an alert on "Entered Stage 2" or "BUY (full confirm)" mid-week on a daily chart of a stock whose weekly bar is in progress. **Pass:** nothing fires until the first daily bar after the weekly close. Confirm the table's Stage row still updates live during the week.

## 📋 Publishing blurb (paste into TradingView release notes)

- BUY signals now require a real structural stop inside your max-stop cap. If the stop had to be clamped, the planner shows ARM instead of BUY — expect fewer, better BUY prints. Toggle it off in Entry & Stop if you prefer the old behavior.
- Default max stop distance changed from 8% to 3%. Existing charts keep their saved value — set it manually if you want the new default.
- Weekly charts now classify Weinstein stages off the 30-week MA instead of the 30-month MA. Weekly stage reads will differ from before; this is the correct basis.
- Stage, Stage-2 shading and the trail EMA no longer repaint. Signals confirm one higher-timeframe bar later and stay put. The Stage readout in the table still shows the live developing stage.
- Bull-flag breakouts can finally trigger — the level they had to cross was previously unreachable.
- The BUY marker now prints only when the decision engine actually says BUY, so marker and table always agree.
- Double-bottom and pullback triggers now need average-or-better volume, consistent with the other setups.
- New position tracking: on a BUY the entry, stop, quantity and targets are frozen, and stop/target alerts fire against that trade instead of the moving live plan.
- New order-ready alerts via "Any alert() function call" with symbol, entry, stop, qty and rupee risk in the payload.
- New "Confirmed bars only" toggle to stop intrabar flicker on live charts.
- Fixed: plan-zone boxes no longer disappear permanently after a hide cycle.
- Fixed: the suggested-stop R:R no longer overstates reward-to-risk on blue-sky setups.

---

# Wyckoff Pro+ — Wyckoff Trigger
_Structures now have a lifecycle: they retire when the job is done, they stop fabricating secondary tests out of stale state, and their exits can no longer un-fire._

## ⚠️ Behavior changes (read before trusting old signals)

| Change | Old | New | Why it matters |
|---|---|---|---|
| Fabricated secondary test after a second climax | A pending "distribution AR" pointer survived a fresh buying climax. On the next in-band pivot high the script printed a secondary test that never happened — and that phantom test then upgraded the next trigger to "⚡ UTAD — PRIMARY SHORT ENTRY" | The pointer is cleared on every fresh climax | On trending distributions with successive lower highs, spurious secondary-test labels and the high-conviction UTAD short they produced **no longer print on history**. Sequences without stale state are unchanged bar for bar |
| Structures retire | Floors and ceilings lived forever. Months after a markup completed, a deep correction printed "✖ Acc fail" at a level that had stopped mattering — and could chain a bogus high-conviction Shakeout off it | Accumulation retires at the measured-move target or N bars past the LPS, printing "Markup ✓" (mirror: "Markdown ✓" post-LPSY) | Ancient dashed floor/ceiling lines disappear once a structure completes. Late "Acc fail" / "Dist fail" prints and their downstream signals are gone. The dashboard falls back to a trend read instead of a perpetual "Range — Accumulation?" |
| Invalidation events wait for the close | "STRUCTURE FAIL — exit" alerts and their state teardowns fired on an intrabar dip and could roll back by the close — an irrevocable exit that un-happened | All four invalidation events respect the "Confirm on bar close" setting | Exit alerts no longer vanish intrabar. **No change on historical bars** — every historical bar is confirmed |
| Event pivots wait for the close | SC / AR / ST / BC / AR-D / DST / LPS / LPSY labels flickered and their alerts could fire on pivots that never confirmed | All ten now respect the same confirm-on-close gate | Live-chart flicker only; historical output is byte-identical |
| Opposing-climax contamination | A fresh climax on one side left the other side's fail-window state armed, so a Shakeout or Upthrust could re-arm inside the *new* structure | Each climax clears the opposing side's fail-window and lifecycle state | Spurious re-arm prints inside a new structure disappear. Single-sided history is unchanged |
| LPS must be near the ceiling | A pullback pivot 15% into markup could be labelled a creek retest | LPS requires the pivot low within a set percentage above the ceiling (mirrored for LPSY below the floor) | Far-from-structure "LPS" labels and their alerts disappear. Genuine near-ceiling LPS prints are unchanged |

## 🐛 Fixes

- A stale pending-secondary-test pointer survived a fresh climax, letting the script invent a distribution secondary test — and upgrade the next trigger to a primary short entry — with no real test in the data.
- Irrevocable "structure fail — exit" alerts bypassed the confirm-on-close setting and could fire on an intrabar dip that recovered.
- Wyckoff event pivots bypassed the same gate, so labels flickered and alerts fired on pivots that never confirmed.
- The measured-move target disappeared from the dashboard the moment the LPS printed — precisely when you need it. The target is now cached at the SOS and survives.
- A fresh climax on one side did not clear the other side's fail-window memory, allowing cross-contaminated Shakeout/Upthrust re-arms.
- The active range boundary lines leaked a drawing object per render when both a ceiling and a floor pair were live.

## ✨ New features

**Structure lifecycle / terminal states** — accumulation retires when the measured-move target is hit or N bars pass the LPS; distribution mirrors it after the LPSY. · **Inputs:** `Structure Expiry (bars after LPS / LPSY)` (40) · **What you'll see:** a "Markup ✓" or "Markdown ✓" label, the floor/ceiling lines removed, and a matching dashboard guidance row ("Cycle complete — trail longs, wait for new BC").

**Cached measured-move target + target line** — the target computed at the SOS/SOW is stored and survives the LPS teardown. · **Inputs:** `Show Measured-Move Target Line` (ON) · **What you'll see:** the dashboard "Target:" row keeps its number through markup, plus an optional dotted target line drawn alongside the range boundaries.

**LPS proximity bound** — an LPS must retest near the ceiling it broke. · **Inputs:** `LPS max % above ceiling` (5) · **What you'll see:** no more "LPS" labels deep inside a markup leg. Mirrored automatically for LPSY below the floor.

**Volume-graded breakouts** — the SOS/SOW breakout bar is graded against a volume multiple, matching the existing SC/BC/ST grading. · **Inputs:** `Breakout Volume Multiple (SOS/SOW ★)` (1.5) · **What you'll see:** "SOS ★" / "SOW ★" labels with a tooltip note when the breakout came on strong volume. Grading is cosmetic — no signal appears or disappears.

**Guaranteed-at-close exit alerts** — the four invalidation events also fire a dynamic alert at bar close. · **Inputs:** none — create one alert on "Any alert() function call" · **What you'll see:** exit notifications regardless of the frequency chosen in the alert dialog. All existing named alert conditions are unchanged.

**Configurable failed-breakout retrace** — the retrace level that defines a failed SOS/SOW was hardcoded at the midpoint. · **Inputs:** `Failed-Breakout Retrace Level` (0.5) · **What you'll see:** nothing at the default — behavior is identical until you change it.

## ⚙️ Changed defaults

| Input | Old | New | Effect |
|---|---|---|---|
| — | — | — | No shipped default changed in this release. Everything above arrives as a **new** input, which means it takes effect on existing chart instances immediately (there is no saved value to override it). Review `Structure Expiry` (40 bars) and `LPS max % above ceiling` (5%) against how you read your usual charts; existing inputs you have customised keep their saved values as normal. |

## 🧪 Test plan (before publishing)

1. **No fabricated secondary test.** Find a trending distribution with two buying climaxes where the second makes a *lower* high and no distribution secondary test occurred between them (RSI>70 lower highs on a daily large-cap is the usual habitat). **Pass:** no secondary-test label prints on the pivot high after the second climax, and no "⚡ UTAD — PRIMARY SHORT ENTRY" appears there. Compare against a saved screenshot of the old build — the difference should be exactly the removal of that label and its downstream upgrade.
2. **Structures retire.** Load a name that completed a full accumulation → markup a year or more ago. **Pass:** a "Markup ✓" label prints at the target or ~40 bars past the LPS; the dashed floor and ceiling lines are gone after it; the dashboard's next-step row reads "Cycle complete — trail longs, wait for new BC"; and no "✖ Acc fail" prints during later corrections at the old floor.
3. **Invalidation alerts survive intrabar.** Keep `Confirm Spring/UTAD/SOS/SOW on bar close` ON. On a live chart in an active structure, watch an intrabar dip through the invalidation level that recovers before the close. **Pass:** no "STRUCTURE FAIL — exit" alert fires and the structure state is intact at the close. Confirm history is unchanged: flip the input off and on — the historical label set must be identical either way.
4. **Measured-move target survives the LPS.** Find a completed SOS → LPS sequence. **Pass:** the dashboard "Target:" cell shows a number on the LPS bar and every bar after it, until the terminal state. With `Show Measured-Move Target Line` ON, a dotted line sits at that price.
5. **Volume-graded SOS/SOW.** Compare a breakout on a clear volume spike against a quiet one, with `Breakout Volume Multiple` at 1.5. **Pass:** the spike prints "SOS ★" and the quiet one prints "SOS". Now raise the multiple to 5.0. **Pass:** all stars disappear but the **count of SOS labels is unchanged** — grading must not add or remove signals.
6. **LPS proximity.** Set `LPS max % above ceiling` to 5, then to 20, on a name in a strong markup. **Pass:** at 5 there are no LPS labels well above the ceiling; at 20 the far ones return. Near-ceiling LPS labels must be present in both.
7. **No orphan lines.** Sit on a live chart in an active range for a few minutes with `Show Active Range Boundaries` ON. **Pass:** exactly one ceiling and one floor line, with no stacking or ghosting as ticks arrive.
8. **Compile check.** Run `/pine-check` (fast regression) before syncing — no compile was performed in the fix session.

## 📋 Publishing blurb (paste into TradingView release notes)

- Fixed a state bug that could fabricate a distribution secondary test after a second climax — and upgrade the next trigger to a "PRIMARY SHORT ENTRY" that was never earned. Those labels disappear from history.
- Structures now retire. Once a markup or markdown completes, the script prints "Markup ✓" / "Markdown ✓" and removes the old floor and ceiling.
- No more months-late "Acc fail" / "Dist fail" prints at levels that stopped mattering, and no bogus high-conviction Shakeouts chained off them.
- Exit alerts no longer un-fire: the four structure-invalidation events now respect confirm-on-close, and also fire a guaranteed alert at bar close.
- Event pivots (SC, AR, ST, BC, AR-D, DST, LPS, LPSY) respect confirm-on-close too — no more live-chart label flicker. Historical output is unchanged.
- The measured-move target no longer disappears the moment the LPS prints. It is cached at the SOS and shown through markup, with an optional dotted target line.
- New LPS proximity bound: a pullback 15% into markup can no longer be labelled a creek retest.
- SOS/SOW breakouts are now volume-graded with a ★, matching the existing climax grading. Cosmetic only — no signal changes.
- The failed-breakout retrace level is now an input instead of a hardcoded midpoint.
- Fixed a drawing leak on the active range boundary lines.

---

# Lock In Expiry — Lock In Expiry
_Unlock dates get the SEBI rule right, stop landing on days the market is shut, and the dashboard finally tells you how much rupee supply is coming._

## ⚠️ Behavior changes (read before trusting old signals)

| Change | Old | New | Why it matters |
|---|---|---|---|
| Capex toggle and the non-promoter unlock | Ticking "issue mainly for capex" pushed the **non-promoter** unlock out to 12 months on post-Aug-2021 mainboard IPOs | The capex carve-out applies to promoter tranches only; non-promoter stays at 6 months | **The date moves 6 months EARLIER on every capex-flagged post-2021 mainboard IPO. Re-check any position you have sized or timed around that unlock.** Promoter legs are unchanged |
| Allotment date can no longer land on a weekend | Listing minus N calendar days could fall on a Saturday or Sunday, and all six month-anniversary expiries inherited the skew | The allotment snaps back to the preceding trading day | Wherever this happened, **all six expiry dates shift 1–2 days earlier** (then snap forward off non-trading days as before) |
| Anchor 50/50 split is regime-gated | The 30-day/90-day anchor split was applied to every IPO, including pre-Apr-2022 ones that had no such split | Pre-regime allotments show a single "⚓ Anchor 100%" tranche at 30 days and no 90-day line | Visible if you relax the max-age filter onto older IPOs: the phantom 90-day supply event disappears and the 30-day overhang doubles to its correct weight |
| Days-of-ADV overhang | Computed from chart-timeframe volume — a 5-minute chart overstated it by roughly 75×, a weekly chart understated it ~5× | Always computed from daily average volume, whatever chart you are on | The ×ADV figure in labels, the NEXT row and every tranche row is now comparable across timeframes |
| Alerts only on eligible symbols | Alert conditions ignored the eligibility filter, so unlock alerts could be created on a US stock or a decade-old listing | All eight alert conditions require the symbol to be Indian and inside the max-age window | You can leave the indicator enabled globally without generating meaningless alerts |
| Weekly/monthly alert timing | Landing and window alerts triggered one full higher-timeframe bar late | They trigger on the bar that actually contains the unlock | Up to 7 days earlier on weekly, a month on monthly. The impact scorecard's baseline price/volume re-latch with it, so **measured post-unlock returns, relative performance, volume and drawdown on weekly/monthly charts will differ**. Daily and intraday pick the same bar as before |
| Countdown is a calendar-date difference | "0 days left" appeared from about 09:16 the *day before* expiry | Counted midnight-to-midnight in exchange time | Every countdown can read one day higher intraday; "0 days left" now appears only on the expiry date itself. Alert timing is unaffected |
| SME classification needs consecutive evidence | Six scattered odd-lot prints in the detection window permanently collapsed the lot GCD and silently reclassified an SME as Mainboard — changing every lock-in duration | A collapse must be confirmed by N consecutive prints; a genuine migration still commits, now with a table warning | **Previously misclassified charts revert to SME**, which changes the non-promoter and promoter-minimum durations and therefore every expiry date on those charts |
| Future-dated listing/allotment | A future manual date passed eligibility and drew far-future expiries | The indicator goes silent instead | Turn on the diagnostics row to see why |

## 🐛 Fixes

- The capex toggle wrongly shifted the non-promoter expiry six months late — SEBI's capex carve-out is a promoter-tranche rule.
- The allotment date could land on a weekend, putting a 1–2 day skew into all six month-anniversary expiries.
- Days-of-ADV was computed on chart-timeframe volume, making the overhang figure wildly wrong on anything other than a daily chart.
- Alert conditions ignored the eligibility filter, so unlock alerts could be armed on non-Indian symbols and long-listed stocks.
- The anchor 50/50 split was applied to pre-Apr-2022 IPOs that never had one, drawing a phantom 90-day tranche and halving the 30-day weight.
- On weekly and monthly charts, landing and window events fired one bar late, which also skewed the impact scorecard's measured returns.
- The countdown floored instead of using calendar dates, showing "0 days left" from the previous morning.
- A handful of stray odd-lot prints could permanently and silently reclassify an SME as Mainboard, changing every lock-in duration with no warning.
- Labels advertised the requested impact window ("20d") even on chart timeframes where it was clamped much shorter.

## ✨ New features

**Rupee unlock value column** — how much stock is unlocking, in ₹ crore (shares × close). · **Inputs:** none — appears when supply percentages and shares outstanding are known · **What you'll see:** a new "₹ Cr" column in the dashboard between ×ADV and impact, filled for the NEXT row and every tranche row; "—" when the inputs are missing.

**Exchange holiday list** — unlock dates snap off listed holidays as well as weekends, in both directions. · **Inputs:** `NSE holidays (DD-MM-YYYY, comma-separated)` (empty) · **What you'll see:** with the list filled in, no unlock line lands on a closed day, even across festival clusters that run into a weekend. Empty means weekend-only snapping, exactly as before.

**Trading-days countdown** — calendar days and sessions, side by side. · **Inputs:** uses the holiday list above · **What you'll see:** the banner reads "N days · M trading", the NEXT row reads "Nd / Mtd", and each pending tranche's Days cell carries both. Without a holiday list it is a weekday count; the list makes it exact.

**Explicit allotment date** — skip the listing-minus-N-days estimate when you know the real date. · **Inputs:** `Override allotment date` (OFF), `Allotment date` · **What you'll see:** nothing until you switch it on; the offset method is bit-identical to before. Both paths snap backward off weekends and listed holidays.

**Editable SME durations** — the SME lock-in periods are now gazette-correctable like the mainboard regimes. · **Inputs:** `SME non-promoter lock-in (months)` (12), `SME promoter minimum lock-in (months)` (36) · **What you'll see:** identical output at the defaults ("1 Year" / "3 Years").

**Anchor split regime date** — third regulatory regime input alongside the existing two. · **Inputs:** `Anchor 50/50 split from` (01-Apr-2022) · **What you'll see:** pre-regime IPOs show "⚓ Anchor 100%" at 30 days and no 90-day tranche.

**Classification-change warning** — flags a genuine SME→Mainboard migration inside the detection window. · **Inputs:** `GCD collapse needs N consecutive odd prints` (3) · **What you'll see:** "⚠️ classification changed in window" appended to the dashboard's first row.

**Diagnostics row** — tells you why the indicator is silent. · **Inputs:** `One-line reason when staying silent` (OFF) · **What you'll see:** "💤 Lock-in inactive · no listing data / not an Indian symbol / listing date is in the future / IPO too old — Nd, max Md". Default off so it does not put a row on every non-Indian chart.

**Effective impact window** — labels print the window actually used after clamping. · **Inputs:** none · **What you'll see:** "6.7d impact" on a 5-minute chart, "42d impact" on monthly, unchanged "20d" on daily and weekly.

## ⚙️ Changed defaults

| Input | Old | New | Effect |
|---|---|---|---|
| `Lot-detection window` | title: "(days after listing)" | title: "(trading days after listing)" | Title and tooltip only — the value (250) and the behavior are unchanged. It always counted daily bars; the label now says so. Existing chart instances keep every saved value they have. |

No numeric default changed in this release. Every item above arrives as a **new** input, so it applies to existing chart instances immediately — in particular, check `Anchor 50/50 split from` and `GCD collapse needs N consecutive odd prints` if you run this on older or thinly-traded listings.

## 🧪 Test plan (before publishing)

1. **Capex / non-promoter date — the one to check first.** Open a post-Aug-2021 mainboard IPO chart on which you have "Mainboard: issue mainly for capex" ticked. Note the current Non-Promoter expiry date, then update the script. **Pass:** the date moves exactly **6 months earlier**, and the promoter tranche dates do **not** move. Do this on every capex-flagged chart you own and write down the old and new dates before you trust any positioning around them.
2. **Weekend allotment snapping.** Use `Override listing date` to set a listing that falls on a Monday or Tuesday with the default 2-day allotment offset, so listing-minus-2 lands on a weekend. **Pass:** the allotment resolves to the preceding Friday and all six expiry dates shift with it. Repeat with a listing where the offset lands mid-week. **Pass:** no change versus the old build.
3. **Holiday list.** Enter a few real NSE holidays (e.g. `26-01-2026, 14-03-2026, 21-10-2026`) and pick a chart where an unlock lands on or next to one. **Pass:** no unlock line sits on a listed holiday, and a festival-cluster date that runs into a weekend still resolves to a valid trading day. Clear the field. **Pass:** output reverts to the weekend-only dates.
4. **Trading-day countdown.** With the holiday list filled and a pending unlock more than a month out. **Pass:** the banner shows "N days · M trading" with M < N; the NEXT row shows "Nd / Mtd"; and M matches a manual weekday count minus the listed holidays in the interval.
5. **₹-crore column.** On a recent IPO, fill in the supply-sizing percentages. **Pass:** the dashboard shows seven columns with "₹ Cr" between ×ADV and impact, the NEXT row and each tranche row carry a figure, and it reconciles with (supply % × shares outstanding × current price)/10^7. Clear the supply percentages. **Pass:** the cells show "—" rather than a wrong number.
6. **Alert eligibility.** Try to create each unlock alert on a US symbol (e.g. NASDAQ:AAPL) and on an Indian stock listed more than the max-age window ago. **Pass:** none of the eight conditions fire. Repeat on an in-window Indian IPO. **Pass:** they fire normally.
7. **Days of ADV is timeframe-stable.** Note the ×ADV figure for the next unlock on a daily chart. Switch to 5-minute, then weekly. **Pass:** the figure is the same on all three (previously it inflated ~75× intraday and shrank ~5× on weekly).
8. **Countdown boundary.** On the trading day *before* an expiry, check the banner during market hours. **Pass:** it reads "1 day", not "0 days left". On the expiry date itself. **Pass:** "0 days left".
9. **Higher-timeframe alert bar.** Put the indicator on a weekly chart of a stock with a past unlock. **Pass:** the landing event marks the weekly bar that *contains* the unlock date, not the one after it. Note that impact-scorecard numbers on weekly/monthly will differ from your previous readings — expected.
10. **SME classification.** Load an SME listing that previously displayed as Mainboard. **Pass:** it now reads SME, the durations revert to the SME periods, and expiry dates change accordingly. On a genuine SME→Mainboard migration. **Pass:** the collapse still commits and the first dashboard row appends "⚠️ classification changed in window".
11. **Diagnostics.** Turn on `One-line reason when staying silent` and load a US symbol, then an old Indian listing. **Pass:** one muted row explaining each case. Turn it off. **Pass:** the chart is clean again.

## 📋 Publishing blurb (paste into TradingView release notes)

- Important: on post-Aug-2021 mainboard IPOs with "issue mainly for capex" ticked, the **non-promoter unlock date moves 6 months earlier**. SEBI's capex carve-out applies to promoter tranches only. Recheck any dates you have positioned around.
- Allotment dates no longer land on weekends. Where they used to, all six expiry dates shift 1–2 days earlier.
- New optional NSE holiday list — unlock dates now snap off listed holidays as well as weekends, in both directions.
- New trading-days countdown alongside calendar days: "N days · M trading" in the banner, "Nd / Mtd" in the table.
- New ₹-crore unlock value column, so you can see the rupee size of the supply, not just the percentage.
- Days-of-ADV now always uses daily volume — the figure was badly wrong on intraday and weekly charts.
- Unlock alerts can no longer be created on non-Indian symbols or stocks past the age filter.
- On weekly and monthly charts, unlock alerts now fire on the bar that contains the unlock instead of the one after it.
- Countdowns are calendar-date based: "0 days left" appears on the expiry date itself, not from the previous morning.
- SME detection now needs consecutive confirming prints, so a few stray odd lots can no longer silently reclassify an SME and change all its lock-in durations. Genuine migrations are flagged in the table.
- New optional explicit allotment-date input, editable SME durations, an anchor-split regime date, and a diagnostics row that explains why the indicator is staying silent.

---

# Defensive Asset Screener — Defensive Asset Screener
_Runs on any timeframe again, on a score that finally means what it says, with sector benchmarks that match the stock._

## ⚠️ Behavior changes (read before trusting old signals)

| Change | Old | New | Why it matters |
|---|---|---|---|
| Chart timeframe | Every rolling statistic silently used chart bars — a 15-minute chart massively understated volatility then annualised it by √252; a weekly chart's "252-day" window was really five years. A mid-cycle fix then made the script **refuse to run** outside daily | A daily sampling engine feeds correlation, volatility, 52-week high/low, drawdown and the downturn tally, so the script runs on **any** chart timeframe again | Intraday and hourly charts now show the same correlation, volatility, 52-week and downturn stats as the daily chart. Daily-chart values are equivalent to before. Honest limits: above-daily charts sample once per chart bar, and short-history intraday charts show N/A until enough daily samples accumulate rather than showing something wrong |
| Score scale | Components summed to 135, not the documented 100, and a ×1.5 regime multiplier saturated everything above ~67 to 100 | Each component is gated by its method toggle, the maximum accumulates only enabled components, and the score is normalised to a true 0–100 | **Every score changes.** With all methods enabled, scores rescale by roughly 100/135 — an old 81 reads about 60. **Retune your alert thresholds.** The default threshold stays at 60, but 60 now means something different |
| Regime no longer moves the score | The volatility/drawdown regime multiplied the score, so a VIX spike could flip an asset "defensive" with no change in the asset itself | The score is regime-independent; the regime is a separate badge with its own four alerts | "Asset Became / No Longer Defensive" alerts now fire on genuine asset changes only. Asset rankings are stable across regimes |
| Verdict rule | The five detection-method toggles fed nothing visible — only the adjusted score drove the verdict | Verdict = manual override **OR** score ≥ threshold **OR** at least 2 methods fired | The Detection Methods toggles now visibly matter. Some assets flip verdict without any change in score |
| Beta window | Beta mixed a 252-day correlation with a 20-day volatility ratio — neither fast nor slow | Correlation and both volatilities use the same lookback window | Displayed volatility ratio and annualised volatility shift from 20-day-reactive to 252-day-stable, and the beta score component moves with them |
| Sector benchmarking | A bare "IT" substring matched UTIL**IT**IES and HOSPITAL**IT**Y, so Indian utilities were benchmarked against an IT index; "Health Technology" hit the technology branch before pharma, sending healthcare to IT (US: to QQQ, never XLV); FMCG tokens never matched "Consumer Non-Durables" | Branch order and tokens rebuilt on TradingView's actual sector vocabulary; industry is consulted for REITs and precious metals | Sector 1-year comparisons are against the right index. Four previously unreachable defensive-sector score bonuses can now fire |
| Downturn engine | Integer division made the outperformance ratio binary — 0 or 1 — so the score component was all-or-nothing and the table could show "0.0% (13/27d)" | True fractional ratio | Score component ⑤ is continuous and the "outperformed in downturns" method can fire on a partial record |
| Keyword matching | Substring matching flagged SHYAMMETL ("SHY"), TIPSINDLTD ("TIPS") and every USDT/USDC pair as defensive | Descriptive tokens stay substrings; ambiguous short tokens require whole-ticker equality; stablecoin tokens match only as the bare symbol or a crypto base currency | Those false positives are gone. GOLDBEES, GDXJ and INDIAVIX still match; BTCUSDT, ETHUSDC, USDTRY and USDCAD no longer do |
| Sector detection no longer clobbers | The sector branch overwrote earlier keyword or manual detections | It contributes to the classification instead of replacing it | Keyword and manual detections survive a sector miss |

## 🐛 Fixes

- Every rolling statistic was chart-timeframe dependent despite being labelled "daily" — the fix wave made the script error out on non-daily charts, and this release replaces that with a proper daily sampling engine so it works everywhere.
- Integer division reduced the downturn-outperformance ratio to 0 or 1, breaking a whole score component and producing self-contradicting table cells.
- A bare "IT" substring routed Indian utilities and hospitality to an IT benchmark; "Health Technology" was captured by the technology branch before pharma, so healthcare never reached the healthcare sector index.
- Ticker keyword matching produced false positives on ordinary stocks (SHYAMMETL, TIPSINDLTD) and on every stablecoin-quoted crypto and USD forex pair.
- The sector branch assigned rather than accumulated the defensive flag, wiping out keyword and manual detections.
- The five detection methods were computed but never reached the visible verdict.
- The 0–100 score plots rendered on the price scale of an overlay indicator, drawing a flat line near zero on index charts; they now default to hidden and can be re-enabled from the Style tab.
- Adaptive table colors and emoji ignored your correlation and beta threshold inputs and used hardcoded bands.
- Telecom stocks were benchmarked against a media index, and India had no utilities/power branch at all.

## ✨ New features

**Detection-method checklist row** — see exactly which of the five methods fired. · **Inputs:** the existing `🛡️ Detection Methods` toggles · **What you'll see:** a new table row "Methods n/5" with ✅ fired · ✖ enabled but quiet · ⊘ disabled per method, each with a tooltip showing the current value against its threshold. Table grew to 13 rows.

**Verdict wiring** — the verdict is now score OR ≥2 methods. · **Inputs:** `Defensive Score Threshold` (60) · **What you'll see:** the chart label, chart background and table verdict all follow the same rule; two new data-window exports, "Verdict Defensive" and "Methods Fired".

**Confirmed daily bar mode** — compute everything from the previous confirmed daily bar. · **Inputs:** `Confirmed Daily Bar` (OFF) · **What you'll see:** no change until you enable it; then alerts stop firing intraday on values that can still move, at the cost of a one-day lag.

**India power/utilities benchmark and honest telecom** — utilities, power and electric map to a power index; telecom maps to the broad market rather than a media index. · **Inputs:** none · **What you'll see:** correct sector 1-year comparisons for Indian utilities and telecom names. An unresolvable sector proxy now degrades those cells to N/A instead of erroring the script.

**Threshold-aware colors** — table cell backgrounds and the 🟢/🟡/🔴 markers for long-term correlation, short-term correlation and beta key off your own threshold inputs. · **Inputs:** `Inverse Correlation Threshold` (-0.3), `Negative Beta Threshold` (-0.2) · **What you'll see:** the green band moves when you move the threshold, and the tooltips' band tables follow.

## ⚙️ Changed defaults

| Input | Old | New | Effect |
|---|---|---|---|
| `Defensive Score Threshold` | 60 on a 0–135-ish scale inflated by a ×1.5 regime multiplier | 60 on a true 0–100 scale | The number is the same but the scale beneath it is not. **Existing chart instances keep their saved threshold — and that saved value now means something different.** Expect to retune it, and to retune any alerts built on it |
| `Plot Defense Score` / `Threshold` plots | Drawn on the price scale | Default to hidden | They rendered as a flat line near zero on index charts. Re-enable from the Style tab after pinning to a separate scale |

Everything else keeps its saved value on existing chart instances as normal.

## 🧪 Test plan (before publishing)

1. **Timeframe invariance — the headline check.** Load a liquid ETF or large-cap (e.g. NSE:GOLDBEES, NSE:NIFTY, or AMEX:GLD) on a daily chart and note the long-term correlation, beta, annualised volatility, 52-week high/low and downturn rate. Switch to 60-minute, then 15-minute. **Pass:** the same values, within rounding. Switch to weekly and monthly. **Pass:** the script runs (no runtime error) — remember those timeframes sample once per chart bar, so a 252-window is 252 weeks there. On a fresh intraday chart with little history. **Pass:** long-window cells read N/A rather than a wrong number.
2. **Score renormalisation and threshold retune.** With all five detection methods enabled, note the score on three assets you know well. **Pass:** each is roughly 100/135 of its old value (an old 81 lands near 60), the score tooltip shows "raw points / enabled max / normalized", and the score never exceeds 100. Now disable two methods. **Pass:** the score stays on a 0–100 scale rather than shrinking. **Then re-set your alert thresholds** on every existing alert built on this script and record the new values.
3. **Regime is decoupled.** Watch an asset across a VIX spike (or lower `VIX Risk Threshold` to force High Risk). **Pass:** the regime badge flips but the score does not move, and no "Asset Became Defensive" alert fires from the regime change alone.
4. **Sector detection.** Load an Indian utility or power name (e.g. NSE:NTPC, NSE:POWERGRID). **Pass:** the sector benchmark reads the power index, not an IT index. Load an Indian pharma name (e.g. NSE:SUNPHARMA) and a US healthcare name (e.g. NYSE:JNJ). **Pass:** pharma/health routes to the health branch, not to IT/QQQ. Load an Indian telecom name (e.g. NSE:BHARTIARTL). **Pass:** benchmarked against the broad market, not a media index.
5. **Keyword false positives.** Load NSE:SHYAMMETL and NSE:TIPSINDLTD. **Pass:** neither is flagged defensive by the keyword method. Load BINANCE:BTCUSDT and a USD forex pair such as FX:USDCAD. **Pass:** not flagged. Load NSE:GOLDBEES and AMEX:GDXJ. **Pass:** still flagged.
6. **Methods checklist row.** With mixed toggles (say correlation and beta on, downturns off). **Pass:** the "Methods n/5" row shows ✅ for what fired, ✖ for enabled-but-quiet and ⊘ for the disabled one; the count matches the label's "Methods n/5"; and an asset with 2 methods fired but a sub-threshold score is still called DEFENSIVE.
7. **Downturn engine.** Find an asset with a partial downturn record (at least 20 down-days, outperforming on some of them). **Pass:** the downturn rate shows an intermediate percentage, not 0.0% or 100.0%, and the table no longer contradicts itself (no "0.0% (13/27d)").
8. **Single-window beta.** **Pass:** the beta tooltip states that correlation and both volatilities use the same lookback, and the Vol Ratio tooltip no longer claims a 20-day rolling window.
9. **Confirmed daily bar.** Turn on `Confirmed Daily Bar` intraday. **Pass:** all stats freeze at yesterday's close and do not move with the developing session.
10. **Compile check.** Run `/pine-check` before syncing — no compile was performed in the fix session. Expect score values and non-daily stats to differ by design.

## 📋 Publishing blurb (paste into TradingView release notes)

- The screener works on **any** chart timeframe again. All statistics are now computed from true daily samples, so a 15-minute chart shows the same correlation, beta, volatility and 52-week numbers as the daily chart.
- **The defense score is now a real 0–100.** It used to sum to 135 and then get multiplied by a regime factor that saturated everything above ~67. Expect every score to change — an old 81 now reads about 60. **Please retune your alert thresholds.**
- The market regime no longer moves the score. It is a separate badge with its own alerts, so a VIX spike can no longer flip an asset "defensive" with zero change in the asset.
- The Detection Methods toggles finally matter: an asset is Defensive when the score clears your threshold **or** at least 2 methods fire.
- New Methods checklist row in the table — ✅ fired, ✖ enabled but quiet, ⊘ disabled, with per-method tooltips.
- Sector benchmarking fixed. Indian utilities were being compared against an IT index because "UTILITIES" contains "IT"; healthcare was routed to technology before it ever reached the health branch.
- New India power/utilities benchmark, and telecom now benchmarks against the broad market instead of a media index.
- Keyword detection no longer flags SHYAMMETL, TIPSINDLTD or stablecoin-quoted crypto and USD forex pairs as defensive. Gold, VIX and bond tickers still match.
- The downturn-performance engine was returning only 0% or 100% because of an integer division. It is now a real fraction.
- Beta uses a single consistent window for correlation and both volatilities.
- Table colors and emoji now follow your own correlation and beta threshold inputs.
- New optional "Confirmed Daily Bar" mode for alerts that do not move intraday.

---

# MFMSI Pro — US Market Sentiment - Multi-Factor Indicator
_Three factors that were quietly dead or lying are now live, and the composite tells you honestly when a feed has gone dark._

## ⚠️ Behavior changes (read before trusting old signals)

| Change | Old | New | Why it matters |
|---|---|---|---|
| RSI divergence factor | Structurally dead — the detector compared a pivot against itself, so the divergence score sat permanently at neutral 50 and its two alerts could never fire | The pivot store is updated after detection, so a new pivot is compared against the stored previous one | The divergence factor now moves, the composite shifts with it, and the two RSI-divergence alerts become fireable for the first time |
| Dead feeds | A dead or unavailable feed silently scored a neutral 50 at full weight, diluting the composite toward 50 with no indication | A dead feed's weight is **dropped** from the total and the remaining live factors renormalise to 100% | The composite reflects only what it can actually see. If every weighted factor is dead it holds at neutral 50 instead of printing 0 (max bearish). Notable: on the benchmark itself (e.g. SPY) the relative-strength weight is now dropped entirely rather than pinned at 50 |
| Percentile readouts | Integer division meant every percentile showed only 0th or 100th | Correct float arithmetic, now via the built-in percentile rank | Percentile cells span the full range. Ties rank marginally higher than before, and the warmup period shows NaN instead of a bogus 0 |
| Percentile windows on intraday charts | "252 bars" meant chart bars — about two weeks on an hourly chart | The PCR and VIX percentile and 52-week windows are anchored to daily data | A true trading year on any chart timeframe. Side benefit: dynamic PCR levels are valid from the first bar intraday instead of blanking for 252 bars. The composite's own percentile stays chart-local by design and says so in its tooltip |
| McClellan Oscillator | Built on raw net advances, which drift with the exchange's issue count | Ratio-adjusted: net advances as a share of total issues | Immune to issue-count drift. All thresholds are preserved, but the values differ from raw-net, so **breadth-thrust timings can shift on historical bars** |
| Price momentum in the composite | Bolted on after normalisation as an unweighted ±15 with an unbounded ROC kicker — the "sentiment" reading became a momentum chaser on volatile names | A properly weighted factor inside the normalisation | At the default 5% weight, momentum now moves the composite by at most about ±2.4 points instead of ±15. **Readings on trending names will be meaningfully less momentum-chased** |
| VIX and TRIN scoring | Step functions that produced plateaus and jumps | Continuous: VIX maps its daily percentile through the old anchors; TRIN is scored from a daily log z-score, contrarian to ±2σ then flipping toward the old reversal extremes | Smoother factor texture. TRIN also changes *level*, not just texture — it now measures relative to its own year rather than absolute bands |
| Adaptive PCR thresholds | The dynamic branch pulled the upper (fear) zone **inward** as volatility rose — the opposite of what the tooltip promised | Both extreme zones move toward the 52-week extremes as volatility rises, widening the neutral band | Fewer extreme PCR signals in high-volatility regimes, which is the documented intent. The static path already behaved correctly and is unchanged |
| PCR extreme signals | Crossed raw PCR against the thresholds; the "PCR EMA Length" input was computed and never used | Cross the smoothed PCR signal line, as the input tooltip always described | Slightly later but far less whipsawy PCR extreme signals and alerts. The dashboard still shows raw PCR against the thresholds |
| Flow score with toggles off | Disabled flow components still added 50 to the numerator while the denominator counted only enabled ones — MFI-only could push flow above 100 straight into the composite | Disabled components contribute nothing | Latent under all-on defaults; it detonated the moment you turned a flow component off. No change at defaults |
| Sector auto-detection | Used GICS/Morningstar names TradingView does not emit, so most stocks silently fell back to the broad-market ETF — counting the index twice while the dashboard implied a sector | Matches TradingView's actual sector taxonomy first, with the legacy names as fallbacks | Auto-detected symbols now get a real sector ETF, which changes the relative-strength factor on most stocks |
| Blank composite on new charts | Several routes let one na value poison the whole composite — most visibly, dynamic levels blanked the score for the first ~252 bars of every chart | Guarded to neutral where a feed is genuinely absent | The score prints where it previously left a gap |

## 🐛 Fixes

- The RSI divergence detector was structurally dead: it updated its pivot store before comparing, so it always compared a value to itself. The factor was permanently neutral and its two alerts could never fire.
- Percentile calculations used integer division, so every percentile readout could only be 0th or 100th.
- A dead or unavailable data feed scored neutral 50 at full weight, silently dragging the composite toward the middle with no indication anything was wrong.
- With any flow component toggled off, the flow score could exceed 100 and feed that inflated value straight into the composite.
- Several na routes blanked the whole composite, including a dynamic-levels path that blanked the score for the first ~252 bars of every chart by default, a zero-range bar poisoning money flow for 20 bars, and a degenerate self-ratio blanking the score on the benchmark itself.
- Candle-ratio integer truncation distorted the price/volume score's branch thresholds.
- A standard-deviation call sat inside a loop, executing many times per bar and corrupting its own rolling window.
- Adaptive PCR thresholds narrowed the fear zone in high volatility — the inverse of the documented behavior.
- The price-momentum bolt-on was unweighted and unbounded, letting it dominate a composite that advertised weighted factors.
- Sector auto-detection used a taxonomy TradingView does not emit, so most symbols fell through to the broad-market ETF.
- Feed symbols were unprefixed with invalid-symbol errors suppressed, so a wrong or unavailable symbol degraded silently.
- Two dead inputs (an unused OBV rate-of-change and a signal-size control that could never apply) were removed, and a third — PCR EMA length — was wired up as documented.

## ✨ New features

**Data-health row** — the dashboard tells you when a feed has gone dark. · **Inputs:** none · **What you'll see:** a "⚠️ DATA:" row listing the dead feeds, with a tooltip showing the sum of live factor weights after renormalisation.

**True renormalisation** — dead feeds drop out of the weight total instead of scoring neutral at full weight. · **Inputs:** the existing per-factor weight inputs · **What you'll see:** a composite built only from live factors; neutral 50 held if everything weighted is dead.

**Price trend as a real weighted factor** — momentum is normalised and blended through its own weight. · **Inputs:** `Price Trend Weight (%)` (5) · **What you'll see:** a much smaller momentum contribution. The raw momentum balance still drives the dashboard's NEUTRAL-BULL / NEUTRAL-BEAR tilt.

**Continuous VIX and TRIN scoring** — percentile and z-score based instead of step functions. · **Inputs:** existing VIX threshold inputs still drive dashboard status and alerts · **What you'll see:** no more score plateaus or jumps; TRIN measured against its own trailing year, with absolute bands as a fallback until enough history accumulates.

**Ratio-adjusted McClellan** — computed as a share of total issues. · **Inputs:** none · **What you'll see:** the same threshold scale, values immune to issue-count drift.

**Daily-anchored percentile windows** — PCR and VIX percentiles and 52-week ranges pull from daily data. · **Inputs:** `Percentile Lookback (bars)` (252) · **What you'll see:** a true trading year on any chart timeframe, and dynamic PCR levels valid from the first intraday bar.

**Confirmed bars only** — gates every signal, alert and state transition to confirmed closes. · **Inputs:** `Confirmed bars only` (OFF) · **What you'll see:** no change until you switch it on; then no intrabar flicker across all 20 alert conditions, the signal labels and the signal-state chain.

## ⚙️ Changed defaults

| Input | Old | New | Effect |
|---|---|---|---|
| `PCR Type` menu | included "PCCA" and "PCCP" | PCCE / PCC / PCCI / CUSTOM | Those two options never existed in the live catalog. **A saved "PCCA" or "PCCP" selection falls back to PCCE silently — open Settings on existing charts and confirm your PCR type** |
| `Custom PCR Symbol` | unprefixed | `USI:PCCE` | All feed symbols now carry explicit exchange prefixes (USI: and CBOE:), so a bad symbol shows up in the new data-health row instead of degrading silently |
| `Price Trend Weight (%)` | did not exist — momentum was an unweighted ±15 addend | 5 | New input. Momentum's influence on the composite drops from up to ±15 points to roughly ±2.4 at the default |
| Component score plots | drawn on the price scale | default to hidden | 0–100 series cannot render usefully on an overlay indicator's price scale. Re-enable individually from the Style tab |
| `Multi-Timeframe Context` | presented as an active feature | retitled "(currently inactive)" | The setting is computed but not wired into the composite or the signals. The title and tooltip now say so |

Existing chart instances keep their saved values for every pre-existing input; the new ones take effect immediately.

## 🧪 Test plan (before publishing)

1. **RSI divergence is alive.** Put the indicator on a US large-cap with a clear recent divergence (e.g. NASDAQ:AAPL or NASDAQ:NVDA, daily). Open the dashboard's RSI divergence cell and step across the divergence. **Pass:** the divergence score moves **off 50** and the status changes from a permanent NEUTRAL. Create the two RSI-divergence alerts. **Pass:** they can be armed and they fire on the divergence bar (previously impossible).
2. **Percentiles span the range.** With `Show Percentile Rankings` on and `Percentile Lookback` at 252, check the composite, PCR and VIX percentile cells across several days. **Pass:** they show intermediate values (e.g. 34th, 67th) and not only 0th/100th. During warmup. **Pass:** NaN rather than a bogus 0.
3. **Dead feeds drop weight.** Load a chart during a session when at least one breadth feed is unavailable, or temporarily point `Custom PCR Symbol` at a nonsense ticker with CUSTOM selected. **Pass:** the "⚠️ DATA:" row appears naming the dead feed, its tooltip reports the live-weight sum, and the composite does **not** drift toward 50 from that feed. Then load the benchmark itself (AMEX:SPY). **Pass:** relative strength is reported as dropped, not scored at neutral 50.
4. **McClellan is ratio-adjusted.** Compare breadth-thrust markers on a long history against your previous screenshots. **Pass:** thresholds are unchanged (±50 thrust, ±100 extremes) but some thrust timings differ — expected — and the input and dashboard tooltips describe the ratio-adjusted calculation.
5. **Momentum is demoted.** Take a strongly trending, volatile name and note the composite. Set `Price Trend Weight (%)` to 0, then back to 5, then to 15. **Pass:** the composite moves by only a couple of points between 0 and 5, and the reading on the trending name is visibly less momentum-driven than the old build. The dashboard's NEUTRAL-BULL / NEUTRAL-BEAR tilt still responds.
6. **Percentile windows on intraday.** Load a 60-minute chart with `Dynamic Historical Levels` on. **Pass:** the dynamic PCR levels and the PCR/VIX percentiles are populated from the very first bars, and the values match the daily chart's. Previously they blanked for 252 bars and, once populated, covered about two weeks.
7. **PCR type menu.** Open Settings on an existing chart that had PCCA or PCCP saved. **Pass:** the menu shows PCCE / PCC / PCCI / CUSTOM and the selection has fallen back to PCCE — set it deliberately and note the change.
8. **Adaptive thresholds widen.** With `Adaptive Thresholds` and `Dynamic Historical Levels` on, compare a calm period against a high-volatility one. **Pass:** in high volatility both extreme zones sit closer to the 52-week extremes and the neutral band is wider, producing fewer extreme signals.
9. **PCR signals use the smoothed line.** Turn on `Show PCR Extreme Signals` and compare against the old build. **Pass:** signals arrive slightly later and there are fewer rapid reversals. The dashboard's PCR status still shows raw PCR against the thresholds.
10. **Flow score with toggles off.** Disable CMF and OBV, leaving MFI only. **Pass:** the flow score stays within 0–100 and the composite does not jump. Re-enable both. **Pass:** the composite returns to its all-on value.
11. **Sector detection.** Load several US names across sectors (e.g. NYSE:JNJ, NYSE:XOM, NASDAQ:MSFT, NYSE:PG). **Pass:** each reports a sector ETF appropriate to its sector rather than falling back to the broad-market ETF.
12. **Confirmed bars only.** Enable it on a live chart. **Pass:** signal labels and alert conditions stop changing intrabar and settle at the close. Historical output is unchanged.

## 📋 Publishing blurb (paste into TradingView release notes)

- The RSI divergence factor was structurally dead — it compared a pivot against itself, so it sat at a permanent neutral and its two alerts could never fire. It now works, and the composite shifts accordingly.
- Percentile readouts could only ever show 0th or 100th because of an integer division. They now span the full range.
- A dead data feed used to score a silent neutral 50 at full weight. Dead feeds now drop their weight entirely, the live factors renormalise, and a "⚠️ DATA:" row on the dashboard names what went dark.
- The McClellan Oscillator is now ratio-adjusted, so it survives changes in the exchange's issue count. Thresholds are unchanged but some historical thrust timings differ.
- Price momentum was bolted on after normalisation as an unweighted ±15 with an unbounded kicker — the sentiment score was chasing momentum on volatile names. It is now a proper weighted factor at 5% by default.
- PCR and VIX percentile windows are anchored to daily data, so "252" means a trading year on any chart timeframe — not two weeks on an hourly chart. Dynamic levels are valid from the first intraday bar.
- VIX and TRIN are scored continuously instead of in steps. TRIN is now measured against its own trailing year rather than absolute bands.
- Adaptive PCR thresholds now widen in high volatility as the tooltip always promised (the fear zone used to narrow), and PCR extreme signals now use the smoothed PCR line the "PCR EMA Length" input described: later, but far less whipsawy.
- Fixed a latent bug where turning off a flow component pushed the flow score above 100 and into the composite.
- Sector auto-detection rebuilt on TradingView's actual taxonomy — most stocks used to silently fall back to the broad-market ETF.
- The PCR menu now lists only the symbols that exist (PCCE / PCC / PCCI / CUSTOM). If you had "PCCA" or "PCCP" saved, please re-select your PCR type.
- New "Confirmed bars only" toggle to stop intrabar flicker across all signals and alerts.
