feat(signal-notifier): remove V/OI gate, widen moneyness cap to 0.13, score-led ranking#29
Open
DevDizzle wants to merge 25 commits into
Open
feat(signal-notifier): remove V/OI gate, widen moneyness cap to 0.13, score-led ranking#29DevDizzle wants to merge 25 commits into
DevDizzle wants to merge 25 commits into
Conversation
Isolated generate_per_signal_seo() Gemini call (SEO_PROMPT_VERSION=
signal_seo_v1) writes seoMetadata onto overnight_signals/{date}_{ticker}
docs for the top-10 candidates via Stage 5 (.update, non-blocking,
deterministic per-ticker fallback). Kept fully separate from the report
markdown call so report_md / the V5.4 ranker is byte-for-byte unaffected.
Closes the per-ticker SEO gap (pages were falling back to thin
'{TICKER} Signal' titles). gammarips-review: SHIP.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…failures
Publisher could not resolve a slug — the planner embeds schedule_slot nested
in post_outline (a JSON string under output_key), but Publisher read top-level
state['schedule_slot'] (never set) and outline['slug'] (wrong nesting), so
slug='' and publish_to_firestore('') returned error while /generate still
returned 200 — blog_posts stayed empty.
- Parse the writer's YAML front matter (authoritative: slug/title/description/
keywords/cta; schedule row lacks description) as primary metadata source;
strip it from the stored markdown body so posts render cleanly.
- Loud ERROR log on unresolved slug; /generate now 500s on error/rejected so
failures surface and Cloud Scheduler retries.
Verified: real /generate published blog_posts/why-uoa-is-mostly-noise.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Front-matter parsing is the primary metadata source but the LLM occasionally emits a body whose front-matter block isn't at position 0, so it fails to parse; combined with the nested-in-outline schedule_slot (not in top-level state), slug resolved to '' and publish 500'd. Add a last-resort read from tools.fetch_next_schedule_slot() — in the cron drain the first pending row is the current post; manual retries resolve slug from state['slug'] earlier and never reach this path. Uses the existing plain tool fn (no ADK ToolContext). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… score-led ranking Three owner-directed signal-quality changes to the STRICT selection path, driven by the first leak-free realized-option-PnL backtest (1,375 fills; backfilled full 3-day option minute bars + replayed the exact +80/-60/trail bracket). No trader-mechanics change; every tradeability gate retained. - Remove `V/OI > 2` conviction gate. Realized PnL: dropped ~55-63% of real option winners for precision lift statistically <= 0 (90% CI [-0.061,-0.001]), not fillability-confounded, stable across chronological halves. Folklore gate that was the main cause of picker-slate starvation (~2 candidates/day). - Re-rank STRICT ORDER BY from directional-V/OI-DESC to overnight_score-led (overnight_score, recommended_oi, spread, ticker) -- now identical to FALLBACK. V/OI is a poor filter and a poor ranking key. Supersedes the 2026-05-01 primary. - Widen moneyness cap 0.10 -> 0.13 (STRICT only; FALLBACK_MONEYNESS_MAX decoupled and pinned at 0.10 -- prior `= MONEYNESS_MAX` would have leaked deep-OTM onto low-conviction skip days). Realized PnL: 10-13% increment +8.9% (CI [+.014,+.163]); the (0.14,0.15] bin is toxic so cap stops at 0.13. Mechanism correction, not a literature reversal: the deep-OTM-cliff lit is hold-to-expiry, our 3-day bracket is not that trade. Thin/single-regime -- reversible, monitored. All three cleared gammarips-review. Decision docs: docs/DECISIONS/2026-06-02-voi-gate-relaxation-proposal.md, docs/DECISIONS/2026-06-02-moneyness-cap-widen-to-13.md. Research (backtesting_and_research/): exit-design study FALSIFIED the deep-research "drop the -60% premium stop" claim (removing it = zero EV change, just fatter tails -- the wick-out is a hold-to-expiry artifact); sweep/ISO detection (H20) parked, blocked on Polygon data tier. Brief: H16-H21. Docs synced (CLAUDE.md, TRADING-STRATEGY.md, CHEAT-SHEET.md, INTELLIGENCE_BRIEF.md, NEXT_SESSION_PROMPT.md). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Captures the 2026-06-03 sector-persistence + webapp internal-linking work: enrichment now writes sector/industry to overnight_signals_enriched + the Firestore signal doc; the webapp ranks same-sector related signals. The columns are NULLABLE and NON-GATING (read by no gate/WHERE/ranking). - DATA-CONTRACTS.md: sector/industry metadata columns entry - DECISIONS/2026-06-03-sector-persistence-and-webapp-internal-linking.md (new) The enrichment code hunks for this ship in the following commit (same file as the FRED work, unavoidably entangled in one working tree). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e gate Captures what shipped live 2026-06-03 (enrichment rev 00039, signal-notifier rev 00031) after a FRED VXVCLS/VIXCLS outage NULLed VIX3M for an entire scan_date and wiped the signal slate (no trade that day). - enrichment-trigger: _fetch_fred_csv retry (3x/30s, backoff) for VIX3M + VIX; on VIX3M failure, bounded carry-forward of the last non-null vix3m_at_enrich from BigQuery (<=7 calendar days, strictly < scan_date, else fail-closed). Bundled here with the parallel sector/industry persistence hunks (same file; NON-GATING). - signal-notifier: live-VIX fallback FRED -> Stooq -> Yahoo with two-source corroboration (1.5 vol-pt agreement), plausibility bound, d<today guard, and vix_source provenance on todays_pick. - docs: TRADING-STRATEGY regime-gate note; DECISIONS/2026-06-03-vix3m-fred- retry-and-carry-forward.md. - research: premium_affordability_study.py (EV-neutral premium-cap finding). - .gitignore: ignore the regenerable realized_label.pkl artifact. gammarips-review: GO on both change sets (no lookahead — carry-forward reads strictly < scan_date; sector is gating-inert; ALTER is idempotent). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Pre-existing SEO tooling, committed to clean the working tree. Read-only analytics helpers (Google Search Console + GA4) under scripts/seo/ and the gammarips-seo subagent definition. No secrets; .venv and __pycache__ ignored. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ades
Publishes the per-trade V5.4 cohort to Firestore ledger_trades/{scan_date}_{ticker}
for the public scorecard ledger table. Same cohort filter + fixed-dollar sizing
as cohort_stats/current, so the table rows and aggregate tiles can never disagree.
- compute_and_write_ledger_trades(): reads closed V5.4 forward_paper_ledger rows
(V5_4_AGENT_RANKER, entry >= LIVE_COHORT_START_DATE, realized), parses the OCC
contract (_parse_occ_contract) into option_type/strike/expiration, writes one
idempotent Firestore doc per trade. Non-fatal; never raises into the email path.
- Called alongside compute_and_write_cohort_stats in run_notifier + /refresh_stats.
- DATA-CONTRACTS.md: ledger_trades collection schema.
Live (signal-notifier rev 00032); /refresh_stats backfilled all 6 closed trades.
gammarips-review: SAFE TO DEPLOY (read-only publish; cohort/sizing parity verified;
no gate/trader impact).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Inject a curated, leakage-safe forensic case-memory into the V5.4 Picker so it
reasons by analogy over past option winners/losers. Direct fenced-block
injection (the house pattern), NOT ADK MemoryService (session-recall RAG).
- build_case_memory.py (read-only): join realized_label.pkl (FILLED option
outcome + underlying path) with overnight_signals_enriched (greeks/IV/catalyst)
on (recommended_contract, scan_date); overlay 6 matched live ledger closes.
Emits case_memory/{bull.md 846, bear.md 529, exemplars.md ~50 curated,
build_manifest.json, case_index.parquet}.
- Outcome keyed on realized_ret>0 (option PnL) NOT is_win (stock direction) —
they disagree 44.2%. WHY is deterministic option physics (theta drag / delta
capture / inferred IV residual); no LLM-authored cause.
- quant.md: 12 hand-authored ledger-independent priors (Q1-Q12).
- Wire: {case_memory_block} in _build_picker_instruction; picker_v5.md adds a
"how to use case memory" section; Dockerfile ships case_memory/;
PICKER_PROMPT_VERSION=5.
- Fail CLOSED if v5 ships without the block (no silent v4 degrade);
RankResponse.case_memory_bytes for observability; deploy.sh preflight assert.
Audited by gammarips-review (SHIP-WITH-FIXES, all fixed). Same-ticker
outcome-import documented as accepted+bounded. Advisory/non-gating; owner
waived N>=15/30-day-OOS ceremony, leakage was not waived.
Decision: docs/DECISIONS/2026-06-03-picker-case-memory.md
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The 2026-06-03 fix required TWO public sources (Stooq+Yahoo) to corroborate within 1.5 vol-pts before trusting a live VIX when FRED is down. FRED was still timing out on 2026-06-04; only Yahoo answered (16.06) so the rule rejected it and the regime gate fail-closed — wiping scan 2026-06-03 (4 candidates, vix3m carry-forward 18.66, plainly contango). Drop the corroboration requirement: use the best public source that answers; when both answer take the MAX (conservative for the one-sided vix_now>vix3m=>skip gate — a low-biased source can't manufacture a false trade). Single source is sufficient; only a total blackout fail-closes. Remove VIX_FALLBACK_TOLERANCE. Lookahead-safe: every fallback bar still passes _vix_date_ok (d<=scan_date AND d<today). gammarips-review: GO. Deployed signal-notifier-00033-sjr; re-ran scan 2026-06-03 -> BBWI BULLISH emailed before the 10:00 ET entry. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e fix The 2026-06-02..04 "FRED outage" was self-inflicted. Our fredgraph.csv requests carried no start date, so FRED serialized each series' full history back to 1990 on every call — a payload that now exceeds the read timeout every morning. The retries couldn't help (each re-requests the same giant dump) and the carry-forward / two-source / Yahoo fallbacks only fired because the primary always timed out. Proven by live probe: bare ?id=VIXCLS times out at 30s deterministically; ?id=VIXCLS&cosd=<date> returns HTTP 200 in ~2s. Same for VXVCLS. Bound every FRED CSV fetch with cosd (start date) = scan_date/target_date − N days: - enrichment-trigger: _fred_csv_url() helper + FRED_CSV_LOOKBACK_DAYS=45 (VXVCLS gate + VIXCLS context); unparseable-date falls back to unbounded URL. - signal-notifier: cosd inlined in fetch_vix_close (live VIX leg, 45d). - forward-paper-trader: _fetch_vix_daily_fred(target_date) bounded 60d, 15s→30s timeout (telemetry-only, non-blocking). Parse helpers unchanged — still take the latest close on/before scan_date, so cosd only moves the window START earlier (lookahead-safe). gammarips-review: GO on all three services. Retry/carry-forward/source-fallback retained as defense-in-depth. Revs: enrichment-trigger-00041-trm, signal-notifier-00034-ds9, forward-paper-trader-00037-t65. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e (judge_v6)
Replace the two-stage V5.4 ranker (per-candidate Scorer fanout + Picker) with a
single memory-aware judge: one structured gemini-3.1-pro-preview call scores every
gate-cleared candidate AND selects the pick. Rename service+dir+code signal-ranker
→ signal-judge (BQ table signal_ranker_runs + Firestore v5_4_* keys intentionally
kept — migration/webapp landmines, no payoff).
Why: post-gate slates are mostly ≤5 candidates, so the Scorer's top-5 cut was a
no-op ~80% of days; ITM/earnings/spread rules were triple-encoded (gates + scorer +
picker). A 13-slate replay (workflow) showed the single judge agreed 9/13 with the
2-stage baseline and was structurally sounder 4-to-1 on divergences (each was the
judge rejecting a two-label-trap the 2-stage took). gammarips-review: SHIP. Live
gemini smoke on the 2026-06-03 slate reproduced the BBWI pick with anti-anchoring.
- judge_v6.md: trusts upstream gates, anti-anchoring ("score as if only candidate"),
per-candidate verdict array, mass-leakage skip, deterministic composite/tiebreak.
- JudgeOutput/PerCandidateVerdict schemas; ScorerOutput/PickerOutput kept for replay.
- run_judge: leakage-assert all candidates → ONE structured call → JUDGE_MAX_ATTEMPTS=3
bounded retry (replaces the lost MIN_SCORER_SUCCESS_FRAC partial-failure tolerance).
- persist_run: one row per verdict; judge mirrored into both scorer/picker REQUIRED
columns at version=6 → BQ DDL unchanged, cohort cleanly separable.
- Wire contract preserved → signal-notifier needs no logic change (only repointed
SIGNAL_RANKER_URL → SIGNAL_JUDGE_URL at the new service).
- Docs: TRADING-STRATEGY, CLAUDE.md + GEMINI.MD, CHEAT-SHEET, decision note.
Deployed: signal-judge-00001-4kn (DRY_RUN=false), signal-notifier-00035-bvh repointed.
Old signal-ranker kept live as rollback. 32 unit tests pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
V/OI>2 was removed as a gate 2026-06-02 (no realized-PnL selection value). Update the gate list the blog writer cites + spread 10%→8% so future posts don't describe V/OI as a gate. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ame + V/OI removal Durable-handoff pass over the authoritative/living docs (historical DECISIONS/ EXEC-PLANS left as the record): - MODELS.md: registry now shows gemini-3.1-pro-preview as the single signal-judge judge_v6 (scores + selects in one call); gemini-3.5-flash no longer powers any judge stage (Scorer removed). Env-swap table + cohort-attribution note updated (segment by scorer_prompt_version: 5=two-stage, 6=judge_v6). - ARCHITECTURE.md: added signal-judge + signal-notifier service entries (the map previously jumped enrichment → trader); fixed the data-flow gate stack. - DATA-CONTRACTS.md: policy-contract gates corrected (spread 0.08, moneyness 5-13%, V/OI gate removed) + judge/rename note; moneyness field 5-15% → 5-13%. - GLOSSARY.md: added signal-judge row; signal-notifier row de-stale'd. - TRADING-STRATEGY.md / CHEAT-SHEET.md: residual Scorer/Picker/signal-ranker mentions → single judge_v6 / signal-judge; SLO + fallback + skip-reason copy. - TESTING.md: enrichment spread 0.10 → 0.08. Kept deliberately: signal_ranker_runs table name, Firestore v5_4_* keys, the V5_4_AGENT_RANKER policy_version label. Not touched: the broader Agent-Arena "5-model debate" narrative (separate webapp/marketing pass). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ignals signal-judge now runs a randomized bracket tournament (tournament_v1, version 7) instead of the single judge_v6 call, and signal-notifier ungates the candidate pool so every enriched signal gets a chance. Why: the gated single judge over-fit junk (picked score-2 BBWI over $100M-flow names) and the selection/liquidity gates were choking real winners on stale scan-time OI (the sweep only becomes OI the next morning; we enter at 10:00 and ride the build). Features barely separate winners from losers (EV ~flat), so a rigid scorer can't extract edge that isn't there — a simple prompt over the full pool at least picks sensible, report-aligned trades. signal-judge (app/agent.py): 3 independent brackets; each shuffles the pool, batches of <=10, top-2 advance (top-1 in the final batch) until one remains (94->20->4->1); consensus across the 3 = pick (3/3 high, 2/3 medium, 1/3 low). Dead-simple prompt + daily report + per-contract JSON. No memory, no rubric. assert_no_leakage on every candidate; fail-closed on no winner. signal-notifier (main.py): candidate query ungated (moneyness/OI/vol/DTE/V-OI removed, LIMIT 200) + rich feature columns added (no outcome cols); active-days liquidity gate BYPASSED (also drops ~N Polygon calls). KEPT: no-earnings-in-hold and regime fail-closed (safety, not selection). /rank + service timeouts bumped for the longer tournament. persist: tournament has no rubric scores -> advancement proxy (round reached -> 1-10) into the REQUIRED columns, version 7, finalists+winner+runner_up only. BQ DDL unchanged; cohort separable (5=two-stage, 6=judge_v6, 7=tournament). gammarips-review (leakage): SHIP — no forward column can reach the model (60-col allowlist + assert_no_leakage catches all 10 outcome cols). Deployed signal-judge + signal-notifier-00036-c6t; live /rank on 94 candidates verified (MSFT, medium, version=7 rows persisted). See docs/DECISIONS/2026-06-04-bracket-tournament.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
V5.4 (gated single-judge) was a dud — 13 live closes, avg 0.0%. Owner-directed clean break: wipe the ledger and start the tournament cohort fresh as V6. - forward_paper_ledger TRUNCATED (13 V5.4 rows; dumped to .scratch as insurance). - policy_version V5_4_AGENT_RANKER -> V6_TOURNAMENT across all WRITE sites (forward-paper-trader POLICY_VERSION constant; signal-notifier todays_pick + ledger_trades writes) and all READ-FILTERS (signal-notifier cohort_stats + 14d summary, win-tracker, x-poster, blog-generator) so the app surfaces V6 not blank. - LIVE_COHORT_START_DATE -> 2026-06-04; cohort_stats/current refreshed -> V6, 0 trades. - webapp cohort-stats default -> V6 (committed separately in gammarips-webapp). Deployed: forward-paper-trader-00038-fd5, signal-notifier-00038-7wx. win-tracker/x-poster/blog code updated but NOT redeployed (harmless — V5.4 gone, no V6 closes yet; deploy before first V6 close surfaces). CLAUDE.md policy summary rewritten for V6. See docs/DECISIONS/2026-06-04-bracket-tournament.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
_best_contract scored contracts with a `vol/max(oi,1)` term that REWARDED low open interest as "unusual" — so it picked the swept lottery strike (OKTA $127 6/12: OI 5, ~35% live spread, untradeable) over the standard liquid strike (OKTA $130: OI 48, fillable). Unusual flow is the right signal for the NAME+direction; using it to pick the CONTRACT hands you the strike you can't fill. Rewrite the score: open interest PRIMARY (5x, accumulates, can't be faked by one sweep), volume secondary (2x), snapshot spread tertiary (1.5x — it read 0.5% on that 35%-wide OKTA strike, so it's noisy), keep sweet-spot delta + gamma (de- emphasized 20x->8x) + theta penalty. Flips OKTA to $130. No hard OI floor (scan OI is stale; a fresh sweep that builds reads ~0 — don't reject those). Live fillability check at pick time is the next, separate layer. Deployed overnight-scanner-00010-jk2; takes effect next overnight scan. See docs/DECISIONS/2026-06-04-contract-selection-liquidity.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adversarial multi-agent audit (every finding re-verified vs code+BQ data) found 16 silent bugs. 13 fixed; 3 deferred (need PIT data/schema). Trigger: OKTA $127 untradeable-ghost pick. CRITICAL #1 (root cause): polygon_client stopped substituting day low/high for a missing bid/ask — that produced fake/0% spreads on ~43% of picks (718/1815 = 0.0), defeating the spread gate and feeding the judge "0.5% spread" ghosts. Missing quote -> NULL spread; chosen contract now carries a real spread. Enrichment spread gate loosened 0.08->0.30 (was filtering fake 0s; real spreads are wider). HIGH #2: divergence flip resolves BEFORE conviction sub-scores (was scoring flipped names on the abandoned side -> ~87% suppressed below MIN_SCORE). #5: judge prompt strips still-stale volume/OI/V-OI (keeps now-real spread). #8: technicals window bounded to scan_date not date.today() (was leaking next-day bars = lookahead). #6/#7/#10: signal-notifier fallback moneyness band restored + dead gates/docs cleaned. MED #9/#12/#13: trader symmetric slippage + stale-TIMEOUT + late-fill guards (removed EV-optimism). #11: empty judge batch re-queues instead of dropping 10 candidates. #14: in_top_5 is a real top-5. LOW #15: overnight_score fail-safe default. #16: greeks store raw with None preserved (delta no longer NULL->0.0). Deferred (separate pass): #3 OI frozen-snapshot, #4 volume frozen-cumulative (need Polygon flat files / day-bars), #15-full (schema add). See docs/DECISIONS/2026-06-04-pipeline-bug-fixes.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Make the scaffold tell the truth — no V5.4/judge_v6/Scorer-Picker/case-memory as the live description anywhere; historical DECISIONS/EXEC-PLANS left as the record. - CLAUDE.md + .gemini/GEMINI.MD (lockstep): policy summary = V6 tournament; spread gate 8%->0.30; blog-generator marked DEPLOYED; bug-hunt summary added. - CHEAT-SHEET.md: retitled "V6 Tournament"; gate list + pick section rewritten. - docs/TRADING-STRATEGY.md: Status/regime/filter-stack/policy_version -> V6. - docs/MODELS.md: registry -> tournament_v1 judge; no Scorer/Picker; cohort 5/6/7. - docs/ARCHITECTURE.md + GLOSSARY.md: signal-judge/notifier/trader + spread 0.30. - docs/DATA-CONTRACTS.md: V6 policy contract + field-quality caveats + new cols. - NEXT_SESSION_PROMPT.md: bug-hunt handoff + the has_contract WATCH. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…he-data lesson Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The blog-generator's BQ filters were already V6_TOURNAMENT, but its prompt/forbidden-list NARRATIVE was two eras stale — it would re-draft V5.4 / Agent Arena / "gate stack" / "5-13% OTM moneyness" / "9:00 AM" content on the next weekly cron (Mon 05:00 ET) and on any /generate regen. - agent.py (writer forbidden-list): current floor is score>=4, alert is 7:30 AM ET; add Agent Arena / Scorer / Picker / gate stack / 5-13% OTM moneyness to the retired list. - fast_api_app.py: newsletter retired-concepts list (add V5.4 / Arena / Scorer / Picker / gate stack / moneyness); reddit lead_style + algotrading mod_traps drop "gate stack" → tournament/bracket; reddit topic list rewritten to the V6 enrichment bar + 2 safety rails + "no selection gates" (was the full V5.4 moneyness/spread/V-OI gate list). - tools.py / voice_rules.py: stale V5.4/V3-V4 comments + the X-poster "V5.4 cohort >= 30 closes" do-not rule → V6. Shared voice_rules.py is vendored into x-poster too, so this also de-stales X copy. Not deployed — engine code only. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Alert time moved to 07:30 ET (was 9:00 AM). Update the blog_schedule slot title_candidate + keyword so /generate re-drafts an honest title; the live blog_schedule/current row was updated to match. The /9am slug stays (URL artifact only). Post regenerated clean on V6 (reviewer 10.0). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Three owner-directed signal-quality changes to the
signal-notifierSTRICT selection path, driven by the first leak-free realized-option-PnL backtest (1,375 fills — backfilled full 3-day option minute bars + replayed the exact +80/−60/trail bracket). No trader-mechanics change; every tradeability gate retained. All three are one-line reverts and clearedgammarips-review. Already live in prod (signal-notifier 00028-pm7).Changes
V/OI > 2gate. Realized PnL: dropped ~55–63% of real winners for precision lift statistically ≤ 0 (90% CI [−0.061, −0.001]), not fillability-confounded, stable across chronological halves. Folklore gate; main cause of picker-slate starvation (~2 candidates/day).ORDER BY(overnight_score, recommended_oi, spread, ticker) — now identical to FALLBACK. V/OI is a poor filter and a poor ranking key. Supersedes the 2026-05-01 V/OI-DESC primary.0.10 → 0.13(STRICT only;FALLBACK_MONEYNESS_MAXdecoupled + pinned at 0.10 — prior= MONEYNESS_MAXwould have leaked deep-OTM onto low-conviction skip days). Realized PnL: 10–13% increment +8.9% (CI [+.014, +.163]); (0.14, 0.15] bin toxic so cap stops at 0.13. Mechanism correction, not a literature reversal — the deep-OTM-cliff lit is hold-to-expiry; our 3-day bracket is not that trade.Caveats / monitoring
MONEYNESS_MAX → 0.10). Monitor the 10–13% cohort: ledgerscan_date ≥ 2026-06-02⨝ enriched on (ticker, scan_date) WHEREmoneyness_pct ∈ (0.10, 0.13].Research included (
backtesting_and_research/)fetch_hold_window_bars.py,realized_option_label.py,gate_recall.py,gate_validity_checks.py,moneyness_band_study.py)./v3/tradesis 403 on our tier; revisit post-upgrade once EV is proven.Decision docs
docs/DECISIONS/2026-06-02-voi-gate-relaxation-proposal.mddocs/DECISIONS/2026-06-02-moneyness-cap-widen-to-13.mdDocs synced: CLAUDE.md, TRADING-STRATEGY.md, CHEAT-SHEET.md, INTELLIGENCE_BRIEF.md (H16–H21), NEXT_SESSION_PROMPT.md.
🤖 Generated with Claude Code