MIS v2 — Logic Capture (Phase 0)
Purpose: capture the valuable logic in the current MIS before the clean rebuild, so v2 ports the logic, not the bloat. Sam's rule: "don't lose the logic / how it looked / the suggestions." This is the faithful inventory. It also separates logic to PORT from structural bugs to FIX (don't port the broken thresholds).
Source:MIS/src/Code.js(1356 lines, builders) +MIS/src/emailDailySnapshot.js(5161 lines, engine/render). Line refs are to those files.
Status: Phase 0 of the approved plan (.claude/plans/valiant-frolicking-trinket.md). Read this before Phase 1.
1. Reference_Rules — the tunable constants (PORT as-is; these are the dials)
Code.js:93-117. Eight rows, Parameter · Value · Notes:
| Parameter | Value | Meaning |
|---|---|---|
| VIX_Low | 14 | Risk-On band ceiling |
| VIX_Normal | 19.5 | Neutral band ceiling |
| VIX_High | 27 | Risk-Off |
| VIX_Extreme | 31 | De-risk |
| Regime_Factor_Normal | 1.0 | Baseline sizing multiplier |
| Risk_Per_Trade | 0.01 | 1% of equity per trade |
| Portfolio_Start | 25000 | Starting equity (drives sizing) |
| Compression_Ratio | 0.9 | Today-range ≤ 0.9×30D-vol ⇒ "Compression" |
v2: keep this tab + these named-range lookups. Add the rules the dumps + thesis surfaced: max ~12 positions, trade windows 9:45-11 / 2:30-4, −20% hard stop, the EDGE statement, the Do-Not-Buy list pointer.
2. Data layer (PORT the derived math; SWAP the source)
Every price/vol field is GOOGLEFINANCE(...) — the 15-min-delay root cause. In v2 the source becomes Finnhub (cached, rate-limited), but the derived math below is correct and ports verbatim:
- 3D% J, 10D% K(label says 30D but window is TODAY()-45→ actually ~30-trading-day), 30D-avg L, Above/Below-30D M — Code.js:309-313.
- 13W hi/lo + % from (N-Q, TODAY()-91), 52W hi/lo + % (R-U, TODAY()-365) — :315-322.
- Vol%(30D) V = STDEV(30d close)/AVG(30d close); Today-Range% W = (high-low)/price — :324-325.
- Volume vs avg BH = today vol / 30D avg vol — :363-365.
Note: the column header "10D %" sits on a
-14-day window and "30D %" on-45; fix the labels in v2 (logic ok, names lie). Confirm the intended windows = 10/22/65 trading days perproject_mis_cashflow_thesis.
3. Momentum_Engine — the scoring chain (PORT the structure, RECALIBRATE the constants)
Code.js:220-379, 60 columns. The load-bearing derivations:
- Compression/Expansion
X=Today-Range ≤ Vol30D × 0.9 → Compressionelse Expansion (:326). - Trend
Y= Above-30D → Uptrend / Downtrend (:327). - Momentum Regime
Z= 3D&10D both + → 🚀Up / both − → 🐢Down / else Steady (:328). - Flow Proxy
AC= (3D+ ∧ 10D+ ∧ Above-30D) → Accumulating else Distributing (:331). - Flow Strength Index
AD=ROUND(50 + (Above-30D ? +8 : −8) + (Accumulating ? +18 : −14), 0)(:332). - Composite Score
AG= (builder)AD × 0.6 + EventImpact(AF) × 3(:336) — ⚠ STALE. The LIVE sheet's Composite is a WEIGHTED SUM driven byReference_RulesWeight_* dials (LeanIn 15·Breakout 12·Trim −5·ReversalWatch 8·HoldWait −3·Contrarian 6·TrendUp ±8·Above30D ±6·Compression 4·Accumulating ±7·VolPenalty 40·MacroBias ±4·EventImpact×3·Flow 12). The−(V×VolPenalty40)term is the real grade-collapse driver → fix VolPenalty→15. v2 uses the LIVE weighted version (full formula inMIS_V2_TAB_COVERAGE.md"the big find" +MIS/v2/MIS_V1_FORMULA_ARCHIVE.md). - Action Flag
AH=AG≥42 → ✅In Sync · AG≥28 → 🟡Balanced · else 🚫Avoid(:338). - SignalTag
AE= In-Sync→"Lean In" · (Uptrend∧Compression)→"Breakout Watch" · AD≥60→"Momentum Ride" · else "Hold/Watch" (:334). - z-score / Outlier / Contrarian
AP/AQ/AR: 3D z vs sector mean/stdev; |z|≥2 → Outlier; Outlier ∧ 3D<0 → Contrarian "Yes" (:344-348). - RS_SPY_30D
AS= 30D% − SPY changepct; RS_Sector_30DAT= 30D% − sector mean 3D (:349-350). - Perf_Grade
AV=AG≥75 A · ≥60 B · ≥45 C · else D(:352). - VSM (Vol Stress)
AY= Today-Range% / Vol30% (:355). - SACS (Alpha Conviction)
AZ=AG × 0.5 + RS_Sector_30D(AT) × 25(:356). - AQS
BD= avg(AG, VSM, SACS) (:360). - Stop(6%)
AI= price×0.94; Target(10%)AJ= price×1.10 (:339-340) — crude; superseded by §4 ATR sizing.
🔴 STRUCTURAL BUGS — do NOT port these constants; FIX in v2
- Composite caps ~45.
ADmaxes ≈ 76 (50+8+18);× 0.6 ≈ 45.6;AF(catalysts) is usually 0. SoAGrarely exceeds ~46 → Perf_Grade can almost never reach A (≥75) or B (≥60) → everything reads C/D, most "Avoid." This is the core scoring defect behind the 10-day review's "grade deflation." v2: rescale Composite to a real 0-100, or redefine the grade/flag bands to the actual distribution. - Flow Strength is 4 discrete values (76 / 48 / 36 / …) → the infamous "stuck at 76." v2: make Flow continuous (volume-vs-avg, RS, trend slope), not a ±8/±18 step function.
- Action Flag thresholds (42/28) are pinned to the broken Composite range — recalibrate after fixing #1.
- SACS double-counts AG (AZ = AG×0.5 + …) so SACS inherits the cap. Decide SACS's independent inputs in v2.
Port = the derivation graph + the intent of each signal. Fix = the magic constants that make the graph collapse to C/D.
4. Position sizing — Snapshot ATR cascade (PORT faithfully — this is sound)
Code.js:440-488:
- ATR14_proxy H = STDEV(21d close) × 1.5 (fallback price × 0.03).
- Stops = price − {1.0, 1.3, 2.0} × ATR (I/J/K).
- % Risk @1.3× L = (price − stop1.3) / price.
- Shares @1.3× M = FLOOR( Portfolio_Start × 0.01 / |price − stop1.3| ) ← 1% risk sizing.
- Dollar risk N = shares × |price − stop1.3|.
This is the clean per-trade risk math. v2 keeps it; the FSE pulls Stop/Target/Shares from here (single source).
5. Daily_Snapshot — regime truth (PORT)
Code.js:423-435: Regime = VIX vs Reference_Rules bands (LOW/NORMAL/HIGH). VIX = INDEXCBOE:VIX. 10Y = INDEXCBOE:TNX / 10. Treasury regime = ≥4.5 Risk-Off / ≥3.8 Neutral / else Risk-On. Portfolio_Start mirrored to A9 for sizing.
6. LiveGuard resolver — THE PROTO-FSE (PORT the gate order + severity → becomes FINAL_STATE_ENGINE)
emailDailySnapshot.js:1353-1424. This is the decision tree that v2's FINAL_STATE_ENGINE formalizes as the single classifier:
- Inputs: regime (snapshot), momentum signals, news index (per-ticker riskFlag/overrideFlag), data freshness (
now − LAST_DATA_REFRESH > guards.freshnessMinutes → STALE). - ShockFlag:
pct ≤ shockDownPct → SHOCK_DOWN·pct ≥ shockUpPct → SHOCK_UP. - TradeBlock = BLOCK if any of: news
TICKER_BLOCK, newsEARNINGS_RISK, (SHOCK_DOWN∧ regime≠LOW), orSTALE. - Severity (sort key, desc): TICKER_BLOCK 500 · MACRO_OVERRIDE 400 · EARNINGS_RISK 350 · SHOCK_DOWN 300 · STALE 200 · NEG_NEWS 100.
- Reason = concatenation of active flags. Output sorted by severity, then ticker.
- Breakout candidates (
:1475): compression ∧ uptrend ∧ not-Avoid, sorted by SACS.
v2 FSE = this resolver, made the ONLY authority, emitting
FinalState ∈ {ADD, STARTER, WATCH, BLOCKED, REJECTED, EARNINGS_RISK}+ PrimaryRejectCode + Stop/Target/RR/Shares (from §4). No other surface (HOLDINGS/OPEN-RISK email section, etc.) may classify independently — that's the "OK at −24% vs EXIT" contradiction the 10-day review caught.
7. Render section order (PORT the useful sections, drop the redundancy)
From the email engine (misBuildDailyBrief_ ≈ :1700-1896, misBuildHtmlBrief_ ≈ :4828-5161): Bottom Line · Holdings · Macro Pulse · Game Plan (regime/VIX/bias/SACS dist) · Recommendation Follow-up (self-grading — KEEP, high value) · Regime/Vol · Forward Look (compression setups, earnings-this-week) · Tape Breadth · Sector Pressure · Outliers · Trade Actions (ADD/STARTER/WATCH + brackets) · News/Catalyst · Position Reviews (RIDE/REDUCE/EXIT) · Live Guard · Mag 7 · 30-day charts · Data Health · Legend.
v2: morning carries holdings+macro; afternoons show only what changed (the FINAL_STATE_HISTORY delta). Lead with the FORWARD_NOTE, not "SPY up 0.70%."
8. Old → new tab map
- PORT (logic sound): Reference_Rules, Tickers (universe+Include), EARNINGS_MASTER, Daily_Snapshot, Snapshot (sizing), LIVE_GUARD→FSE, NEWS_CATALYST, HIST_MOMENTUM (crown-jewel history), Sector_Map (sector→ETF + breadth).
- PORT-AND-FIX: Momentum_Engine (rescale Composite/Flow/grades per §3), VIX/HIST tabs (the
#NUM!+ 10Y-bleeding-into-VIX regression). - CREATE: FINAL_STATE_ENGINE, FINAL_STATE_HISTORY, Position_Intent, Ticker_Memory (+ Do-Not-Buy / wash-sale / re-entry dims), FORWARD_NOTE.
- RETIRE: Setup_Engine + Playbook_V2 (header-only orphans), the two unnamed orphan tabs (#34 abandoned 4/3, #35 sparkline scratch), execution_playbook (replaced by FSE).
9. Bugs to FIX in v2 (carried from the 10-day review — do NOT reproduce)
- "OK" verdict on a −24% position while FSE says EXIT → single classifier (§6).
- CALENDAR OVERLAY "no events in 21 days" (stale/empty) → real macro calendar feed.
- "live" label over 6-12-day-stale broker data → label honestly / Finnhub quotes.
- Bitcoin change frozen
+0.00%; ETH/Brent garbage; ETF proxies mislabeled as spot → fix or label as proxy. - Tape Breadth frozen across the day → recompute intraday.
- Universe count disagrees with itself (182/181/189) → one source.
- Composite/grade collapse to C/D (§3) → rescale.
- Morning brief misfired at 2:28 PM → trigger hygiene.
10. SWEEP ADDENDUM — everything else (full-history sweep, 2026-05-26)
Three parallel sweeps (MIS nested repo · workspace docs+memory+.remember · GitHub issues+outputs). The net-new beyond §1-9. Harvest the LOGIC/IDEAS from the old sheets; the NEW v2 sheet (
1N2v-MDDi…) is the target — do NOT reuse old sheet IDs (1HEmRev/1cosuFr/1F31zare legacy; v21F31zis Excel-roundtrip-corrupted, discard).
10a. The CANONICAL FSE resolver (UPGRADES §6 — use THIS, not LiveGuard-only)
Per MIS_FSE_ARCHITECTURE.md: FSE assigns 3 states — ScannerState (technical), RiskState (gates), FinalState (what Sam may do). Resolver = first gate that fires wins:
1. formula error / stale → REJECTED/DATA_STALE · 2. Live Guard → BLOCKED · 3. held position past loss threshold → REDUCE/EXIT · 4. concentration violation → PORTFOLIO_OVERLAP · 5. ATR stop too wide → SETUP_DISTANCE_FAIL · 6. $risk > max → PORTFOLIO_RISK_FAIL · 7. Grade < B → WATCH · 8. SACS < 60 → can't be Lean In; 40-59 = STARTER only if all gates pass · 9. extended > 1.5× ATR intraday → CONDITIONAL/EXTENDED · 10. no stop/trigger → WATCH · 11. passes all → STARTER, or ADD only if live trigger confirmed.
Reject codes: DATA_STALE·LIVE_GUARD_BLOCK·NEWS_BLOCK·SHOCK_DOWN·EARNINGS_RISK·SETUP_DISTANCE_FAIL·PORTFOLIO_RISK_FAIL·GRADE_FAIL·SACS_FAIL·FLOW_FAIL·SECTOR_FAIL·NO_TRIGGER·EXTENDED·PORTFOLIO_OVERLAP·MISSING_STOP/TARGET/SIZE·FORMULA_ERROR.
Email top line = 6 permission tiers (NOT binary): 1 No new trades · 2 Watch only · 3 Starter only (delayed/limit) · 4 Selective adds · 5 Reduce risk · 6 Exit mode.
10b. SACS — the CORRECT weighted formula (FIX the grade collapse)
The v1 code SACS in §3 (AG×0.5 + AT×25) inherits the Composite cap. The canonical weighted SACS (MIS_FSE_ADDENDUM) is what to build: SACS = Composite×0.3 + Flow×0.3 + min(100, RR_13W×20)×0.2 + Grade_score×0.2, normalized 0-100. Grade bands (real distribution): A≥40 · B≥25 · C≥10 · D<10 (NOT the ≥75/≥60 that collapses everything to C/D). Lean In = SACS≥60; STARTER = 40-59 + all gates. Also: VolPenalty = 15 (locked; was 40, caused ±40-pt swings) and R:R must vary by setup (currently hardcoded 2.0 → placeholder; gate needs ≥2.5).
10c. Methods Sam HAD but UNDER-USES / ABANDONED — REVIVE these (his explicit ask)
- VIX history block (5/7/10-day) — Sam-favorite, removed; show closes + 🚨 days that touched extreme + min/max per window.
- Volume — captured (Momentum BF-BH: today / 30D-avg / vs-avg) but never rendered; surface
Vol 1.4x avg/🔥 2.8xon Forward Look + Outliers. - Blind Side Analysis — re-evaluates current holdings as if fresh trades (kills holder-bias); tab existed, underused. Excellent — build it in.
- Sector concentration alert — pie exists, no threshold logic (e.g. "6 of 8 ADD = Energy → cap, recommend top 2"). Wire the cap + warning.
- Closed-trades / trade journal + backtest substrate — HIST_MOMENTUM (~5,299 rows) + Hist Daily Snapshot (~1,000) + Run Log (~1,461) sitting on legacy sheets; migrate → powers #9 backtest/scorecard + SACS-vs-realized-return validation.
- Earnings depth — currently next-date-only/block; add last-earnings, dividend date, insider activity, earnings surprise (Finnhub endpoints).
- 4-field entry primitive (Mildred model: ticker/price/date/buy-sell → everything computed) — restore; the bot fills it.
- Intraday macro tape (8 indicators, 30-60 min) + short / inverse-ETF playbook (down-market) — parked; revive when relevant.
10d. Build discipline (from the FSE architecture — non-negotiable)
Anti-80% rule: do NOT advance to the next module until the current acceptance test passes. One ticker end-to-end before scaling. Session Stop Rule: stop when the test passes / a blocker is found / safety uncertain / scope tempts to broaden. 15-point acceptance test gates "decision-grade": Flow distributed (not clustered) · SACS weighted · Snapshot full gate fields · execution surface can't show BUY on a rejected name · blocked names appear once · Grade-C never under High Conviction · Diagnostics can block · FSE one row/ticker/run · email reads FinalState from FSE only · delta from FINAL_STATE_HISTORY · answers the 10-sec questions · ADD requires live price · test basket (INTC·AMD·NVDL·MRVL·JBLU·SOXX·BLK·SCHG) resolves correctly.
10e. Holdings seed + secrets
MIS/data/SCHWAB_898_TRADE_LOG_SEED.csv(47-trade seed) + the broker paste = the HOLDINGS_CLEAN source across 3 accounts (Fid Margin Z29720600 · Fid Cash Z29835692 · Schwab …898). This is very likely the CSV Sam is sending. The 5/17 paste failed before (only 10 of 24 rows landed) — verify the full set lands.- Secrets (Schwab/Finnhub) were plaintext in the old CONTROL tab → v2 uses PropertiesService, never cells/git.
10f. Locked behavioral rules to bake in (memory)
No-Monday-morning-QB (surface the warning BEFORE the trade, inline) · direction (↑/↓ + today's % @ price) on every signal · tab-duplicity defense (Include must be literal "Y"; IFERROR(primary, IFERROR(secondary, warned-default)), never silent 0) · check-the-sheet before reaching for an external API.
Phase 0 capture + full-history sweep · 2026-05-26 · Claude Code. The complete "everything MIS" reference. Next: the deep 2-ticker FSE build on this foundation (Anti-80% rule).