# Post-Refresh Fundamentals-Signal Test Protocol

> Authored 2026-05-29 (II) AFK. Consolidates everything that session learned
> into a single executable checklist, so the next session (with refreshed,
> period-correct fundamentals) can run a rigorous, pre-registered orthogonal-
> signal test without re-deriving the traps. Canonical record: `RESEARCH.md`.

## Why this exists
The 2026-05-29(II) session hardened the PiT fundamentals pipeline and built an
IC-triage tool, but could NOT run the strongest candidate (Sloan accruals)
because the period-correct re-download is **hook-blocked inside AFK**
(`pretool_guard.sh` denies `thales fetch-`). The legacy parquet has
period-collapsed flow concepts (quarterly net income silently paired with YTD
cash flow), so accruals computed on it are corrupt — `compute_accrual_scores`
**fails safe** (returns `{}`) until the period schema exists. This protocol is
the turnkey plan for the moment the data is refreshed out-of-session.

## Step 0 — Refresh the data (OUT of AFK; one command, ~15 min)
```
thales fetch-fundamentals          # repopulates with fiscal_period / period_start
thales validate-fundamentals       # duplicate/stale/unit/no-annual checks + coverage
```
Confirm the coverage report now shows non-zero **annual-flow** counts (the
legacy schema showed 0 because it lacked `fiscal_period`). Spot-check a few
symbols against `data/fundamentals/quarterly_fundamentals.backup-2026-05-29.parquet`.

## Step 1 — IC-triage FIRST (cheap; do this BEFORE any backtest)
The session's central lesson: a 5-window walk-forward is far too low-powered to
tell a weak edge from noise (asset-growth showed +0.0135 Sharpe / 4-of-5 windows
yet collapsed to IR +0.01 under a 109-obs IC test). So triage before spending a
SHIP-gate run:
```
thales ic-triage                    # raw, 21d + (rerun with) --fwd-horizon 126
thales ic-triage --sector-neutral   # the professional lens
```
Add an `accruals_low` signal to the menu (oriented −accrual, since LOW accruals
outperform) using `compute_accrual_scores`. **Decision rule:**
- mean IC < 0 (wrong sign) or |IR| < ~2 at BOTH horizons, raw AND sector-neutral
  → do NOT spend a SHIP run; log the null. (This is what killed asset-growth.)
- Right sign with |IR| ≳ 2 → proceed, but first decompose IC **by year** to rule
  out regime-decay (size_small looked decent full-sample but was a 2017–20
  artifact, dead since 2021).

## Step 2 — HARD prerequisites for the accruals signal (validated on real SEC data)
1. **Exclude Financials (and consider Utilities).** Bank/insurer "operating cash
   flow" is intermediation/trading flow — huge and often NEGATIVE (JPM FY2025
   OCF = −$147B), so `(NI−OCF)/TA` is meaningless for them. Pass the Financials
   tickers via `compute_accrual_scores(..., exclude_symbols=...)`. This is not
   optional — it is why Sloan and the literature drop financials/utilities.
2. **FY flows only.** Use `fiscal_period="FY"` (already enforced) — never mix
   quarterly NI with YTD OCF.
3. **Continuous tilt, not a hard drop.** Hard fundamental drops were REJECTED
   (quality_filter). Use the `accrual_tilt` machinery (already wired, gated OFF):
   `w *= exp(-strength·clip(z))`, missing-data names neutral.
4. **Pre-register exact params + the full gate BEFORE running. ONE test. No
   sweeps.** `thales research draft-hypothesis --slug accrual_tilt --reason "..."`.

## Step 3 — Full SHIP gate (only if Step 1 says "proceed")
Screen → escalate only if Sharpe Δ ≥ +0.10, CI not crossing 0, no MaxDD blowup:
```
thales evaluate -p strategy.construction.accrual_tilt.enabled=true \
  -p strategy.construction.accrual_tilt.strength=<preregistered> \
  --name accrual_tilt --desc "<hypothesis>" --tag afk-<date>
thales cpcv --workers 8                       # standard universe
# if it passes: survivorship-free confirmation (the honest test)
thales cpcv --survivorship-free --workers 8
```
SHIP requires ALL: walk-forward Sharpe verdict BETTER & ΔSharpe ≥ +0.15; CPCV
PBO ≤ 0.30; OOS Sharpe Δ ≥ +0.10; IS-OOS corr ≥ +0.01 (sign stable across n=6
AND n=8, AND seed=123); survivorship-free confirmation holds; no window
regresses ≥0.05; crisis veto (`thales stress`, no named crisis MaxDD worse by
≥5pp vs the 2026-05-29 baseline crisis profile); DSR reported; a stated
mechanism. Default verdict for ambiguous = PROMISING, not SHIP.

## Candidate priority (from this session's triage)
1. **Accruals (Sloan)** — strongest prior, genuinely orthogonal, well-covered;
   the reason to refresh. IC-triage it first.
2. **Low-vol (Ang et al)** — IC-triage CANNOT assess it (its edge is
   risk-ADJUSTED; raw-return IC is the wrong lens and is negative by
   construction). If pursued, test as a vol-scaled tilt and judge on Sharpe, not
   IC — but beware redundancy with HRP + vol-targeting already in production.
3. **Asset growth** — REJECTED in Russell-1000 2017–26 (IR −1.09 universe /
   +0.01 within-sleeve, wrong-sign, robust to sector-neutralization). Do not
   pursue here.
4. **Accounting-size / 52w-high / 1m-reversal** — all NOISE; size was a
   2017–20-only artifact. Do not pursue.

## Standing caveat
No backtest closes the in-sample gap. Any SHIP candidate is "ship-ready pending
a 30-day live paper-trade A/B," not "deploy tomorrow."
