research: park Phase B fip breadth; race guard and compact evidence

Log the 21:14 orphan as a snapshot-build race, rewrite the context table to
authoritative ICs only, and soften the vol-tilt warning. Add extender completion
manifest + breadth refuse guard; strip intermediate/orphaned reports; park the
thread (no book sim, no deploy).
This commit is contained in:
2026-07-19 00:32:20 +02:00
parent 7d60e54f5a
commit 2311999e57
16 changed files with 562 additions and 8103 deletions
+60 -38
View File
@@ -3,9 +3,9 @@
Uses the same collection + ``_filter_liquid_breadth_week_rich`` as
``run_backtest`` signal_eval. No parallel mask implementation.
Reconciles the harness +0.0575 vs prior dual-path 0.017 disagreement by
deleting the second mask, dumping membership/pre-post stats, and re-running
mom-conditional IC through the surviving path only.
Single-sourced liquid-breadth fip diagnostics through harness mask helpers.
Re-runs unconditional / tier / prod-subset / mom-conditional ICs and context
signals. Requires a complete research.sqlite completion manifest.
Research branch only. Example:
@@ -49,7 +49,12 @@ def _parse_args() -> argparse.Namespace:
p.add_argument("--min-price", type=float, default=5.0)
p.add_argument("--workers", type=int, default=max(1, (mp.cpu_count() or 4) - 1))
p.add_argument("--allow-spawn", action="store_true")
p.add_argument("--dump-weeks", type=int, default=5, help="How many weeks to dump membership for")
p.add_argument(
"--dump-weeks",
type=int,
default=0,
help="Weeks of liquid membership symbol lists to embed (default 0 — keep reports compact)",
)
p.add_argument("--out", default=None)
p.add_argument("--quiet", action="store_true")
return p.parse_args()
@@ -173,8 +178,23 @@ def main() -> None:
args = _parse_args()
research = Path(args.research_snapshot)
prod = Path(args.prod_snapshot)
if not research.exists():
raise SystemExit(f"Missing {research}")
# Refuse half-built research.sqlite (2026-07-18 21:14 race).
scripts_dir = Path(__file__).resolve().parent
if str(scripts_dir) not in sys.path:
sys.path.insert(0, str(scripts_dir))
from research_snapshot_manifest import ( # type: ignore[import-not-found]
assert_research_snapshot_complete,
)
manifest = assert_research_snapshot_complete(research)
if not args.quiet:
print(
f"Manifest ok: tickers={manifest.get('ticker_count')} "
f"ohlcv={manifest.get('ohlcv_row_count')} "
f"finished_at={manifest.get('finished_at')}",
flush=True,
)
# Force harness liquid-mode collection (same env as breadth run).
os.environ["BACKTEST_LIQUID_BREADTH"] = str(int(args.top_n))
@@ -503,9 +523,9 @@ def main() -> None:
),
"mom_conditional_negative_and_reliable": mom_alive,
"orphan_plus_five_sigma": (
"Prior report fip-breadth-20260718-211440-breadth.json listed "
"fip IC +0.0575 / t +5.12. This single-sourced recompute is the "
"authoritative number; if it disagrees, the +0.0575 row is orphaned."
"Orphaned 21:14 row (+0.0575 / t +5.12) raced a partial "
"research.sqlite and was removed from reports/ (Git history only). "
"Harness path and shared filter agree on complete data."
),
"compositional_story": (
"fip_id pools continuous winners (neg IC) vs continuous bleeders "
@@ -513,19 +533,36 @@ def main() -> None:
"liquid is less negative / positive — composition, not jumpiness premium."
),
"vol_tilt_warning": (
"High-vol names underperform on breadth relative to S&P-like books. "
"Re-validate production 80/20 high-vol tilt before any universe broaden."
"Authoritative liquid vol_6m IC ≈ 0.048 / t ≈ 1.36 — directional "
"hypothesis only, not significant. Do not cite the orphaned 0.16 / "
"t 6.1. Re-validate production 80/20 high-vol tilt before any "
"universe broaden; it is not a settled finding on this pool."
),
"breadth_momentum_thesis": (
"Residual mom on liquid-1500 is +0.029 / t +1.33 vs fingerprint "
"0.055 / t 1.98 on 505 names — more breadth did not strengthen the "
"momentum t-stat on this pool. Clean mom edge lives in the large-cap "
"universe already traded. A fip tilt presupposes a breadth mom book "
"worth tilting; that baseline must be proven first."
),
},
"platform_verdict": (
"Mom-conditional fip ALIVE as book-tilt candidate (needs book sim) — "
"not production wire-in. Unconditional fip not green."
"Mom-conditional fip ALIVE as book-tilt candidate only — requires a "
"pre-registered two-arm breadth book (baseline liquid-1500 mom vs +fip "
"tilt) before any gate talk. Unconditional fip not green. Production: none."
if mom_alive
else (
"fip CLOSED for production: mom-conditional does not clear iron rule "
"on single-sourced path. Display card is the resting place."
)
),
"research_snapshot_manifest": {
"finished_at": manifest.get("finished_at"),
"ticker_count": manifest.get("ticker_count"),
"ohlcv_row_count": manifest.get("ohlcv_row_count"),
"rank_only_count": manifest.get("rank_only_count"),
"complete": manifest.get("complete"),
},
}
stamp = datetime.now().strftime("%Y%m%d-%H%M%S")
@@ -533,8 +570,9 @@ def main() -> None:
out.parent.mkdir(parents=True, exist_ok=True)
out.write_text(json.dumps(results, indent=2, default=str), encoding="utf-8")
# Update research log
_update_md(Path("docs/research/fip-breadth-ic.md"), results, out)
# Append a machine reconciliation stub next to the JSON only — never clobber
# the curated research log at docs/research/fip-breadth-ic.md.
_update_md(out.with_suffix(".md"), results, out)
if not args.quiet:
print("=== Harness fip_id (authoritative) ===")
@@ -560,18 +598,10 @@ def _update_md(path: Path, results: dict, artifact: Path) -> None:
"",
"### Problem",
"",
"Two implementations of the liquid-1500 fip IC disagreed on **sign**:",
"",
"- Harness report `fip-breadth-20260718-211440-breadth.json`: **+0.0575 / t +5.12**",
"- Dual-path diagnostics (since deleted): **0.017 / t 1.9**",
"",
"A static read cannot decide which is right without single-sourcing the mask.",
"",
"### Resolution",
"Machine stub only — curated narrative lives in `docs/research/fip-breadth-ic.md`.",
"",
f"- **Single source:** {results.get('single_source')}",
f"- **avg_cross_section semantics:** {results.get('avg_cross_section_semantics')}",
f"- Harness `_signal_evaluation` vs shared-filter recompute agree: "
f"- Harness vs shared-filter agree: "
f"**{interp.get('harness_and_shared_filter_agree')}**",
"",
"### Authoritative unconditional fip (liquid top-N, post-mask)",
@@ -587,10 +617,6 @@ def _update_md(path: Path, results: dict, artifact: Path) -> None:
f"| mask_binds_pct | {h.get('mask_binds_pct')} |",
f"| reliable | {h.get('reliable')} |",
"",
"The **+0.0575 / +5.12** row is **orphaned** if the authoritative recompute "
"disagrees; do not cite it. Iron-rule unconditional green still requires "
"negative sign and |IC| ≳ 0.03 on this row.",
"",
"### Checks (single-sourced)",
"",
"| check | mean_ic | t | weeks | avg N | reliable |",
@@ -626,21 +652,17 @@ def _update_md(path: Path, results: dict, artifact: Path) -> None:
"",
results.get("platform_verdict", ""),
"",
"### Vol-tilt warning",
"### Vol-tilt / breadth-momentum notes",
"",
interp.get("vol_tilt_warning", ""),
"",
interp.get("breadth_momentum_thesis", ""),
"",
f"Artifact: `{artifact.as_posix()}`",
"",
])
existing = path.read_text(encoding="utf-8") if path.exists() else ""
marker = "## Reconciliation"
if marker in existing:
existing = existing.split(marker)[0].rstrip() + "\n"
# Also strip old dual-path diagnostics section if present after reconciliation
if "## Follow-up diagnostics" in existing and marker not in path.read_text(encoding="utf-8") if path.exists() else "":
pass
path.write_text(existing.rstrip() + "\n" + "\n".join(lines), encoding="utf-8")
# Always overwrite the machine stub (never the curated research log).
path.write_text("\n".join(lines).lstrip() + "\n", encoding="utf-8")
if __name__ == "__main__":