Thales
← research journal
May 29, 2026raw markdown ↗

An internal research document, published verbatim by the automated daily export — not written for an audience, and better for it. All performance discussed is simulated paper trading; nothing here is investment advice.

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."