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:
@@ -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__":
|
||||
|
||||
Reference in New Issue
Block a user