research: single-source liquid mask; orphan +0.06 fip IC

Harness and diagnostics share _filter_liquid_breadth_week_rich. Recompute
shows unconditional liquid fip IC -0.017 (mask binds 97%); mom-conditional
-0.088/t-4.58 stands. Document +0.0575 as orphaned.
This commit is contained in:
2026-07-19 00:06:04 +02:00
parent ceaaadc49f
commit 7d60e54f5a
4 changed files with 7280 additions and 526 deletions
+96 -11
View File
@@ -942,6 +942,8 @@ def _accumulate_signal_series(
records: list,
collected: dict,
benchmark_closes: dict[date, float] | None = None,
*,
symbol: str | None = None,
) -> None:
"""For each weekly as-of bar, emit (signal, forward-return) pairs keyed by ISO
week into ``collected[name][week_key]``. Forward return is close-to-close over
@@ -974,6 +976,7 @@ def _accumulate_signal_series(
"fwd": fwd,
"close": closes[i],
"median_dvol_63": dvol,
"symbol": symbol,
})
else:
collected[name][week_key].append((val, fwd))
@@ -1041,12 +1044,28 @@ def _filter_liquid_breadth_week(
Ranking is relative (IEX volume undercount is OK for order stats). Membership
is recomputed every week from as-of bars — never frozen from today's liquidity.
"""
ranked: list[tuple[float, float, float]] = [] # (-dvol, val, fwd)
kept = _filter_liquid_breadth_week_rich(
recs, top_n=top_n, min_price=min_price
)
return [(float(r["val"]), float(r["fwd"])) for r in kept]
def _filter_liquid_breadth_week_rich(
recs: list,
*,
top_n: int,
min_price: float,
) -> list[dict]:
"""Same mask as ``_filter_liquid_breadth_week``, returning rich rows.
Single source for harness IC and research diagnostics. Eligible pool =
dict observations with close ≥ min_price and median_dvol_63 > 0; then
keep top_n by dollar volume (highest first). Non-dict legacy tuples are
not eligible for the liquid mask (they have no dvol).
"""
eligible: list[tuple[float, dict]] = [] # (-dvol, row)
for rec in recs:
if not isinstance(rec, dict):
pair = _obs_val_fwd(rec)
if pair is not None:
ranked.append((0.0, pair[0], pair[1]))
continue
close = rec.get("close")
dvol = rec.get("median_dvol_63")
@@ -1057,10 +1076,50 @@ def _filter_liquid_breadth_week(
pair = _obs_val_fwd(rec)
if pair is None:
continue
ranked.append((-float(dvol), pair[0], pair[1]))
ranked.sort(key=lambda row: row[0])
kept = ranked[:top_n]
return [(val, fwd) for _, val, fwd in kept]
row = {
"val": pair[0],
"fwd": pair[1],
"close": float(close),
"median_dvol_63": float(dvol),
"symbol": rec.get("symbol"),
}
# Preserve optional research fields for mom-conditional diagnostics.
for key in ("mom_12_1", "mom_12_1_resid", "vol_6m", "fip_id"):
if key in rec and rec[key] is not None:
row[key] = rec[key]
eligible.append((-float(dvol), row))
eligible.sort(key=lambda item: item[0])
return [row for _, row in eligible[:top_n]]
def _liquid_breadth_week_stats(
recs: list,
*,
top_n: int,
min_price: float,
) -> dict[str, int | bool]:
"""Pre/post mask counts for reconciling avg_cross_section semantics."""
raw = len(recs)
eligible = 0
for rec in recs:
if not isinstance(rec, dict):
continue
close = rec.get("close")
dvol = rec.get("median_dvol_63")
if close is None or float(close) < min_price:
continue
if dvol is None or float(dvol) <= 0:
continue
if _obs_val_fwd(rec) is None:
continue
eligible += 1
post = min(eligible, top_n) if top_n > 0 else eligible
return {
"raw_pool": raw,
"eligible_pre_mask": eligible,
"post_mask": post,
"mask_binds": bool(top_n > 0 and eligible > top_n),
}
def _quintile_spread(pairs: list[tuple[float, float]]) -> float | None:
@@ -1126,9 +1185,18 @@ def _signal_evaluation(collected: dict) -> list[dict]:
ics: list[float] = []
spreads: list[float] = []
sizes: list[int] = []
raw_sizes: list[int] = []
eligible_sizes: list[int] = []
bind_flags: list[bool] = []
for wk in kept:
recs = weeks_map[wk]
if top_n > 0:
stats = _liquid_breadth_week_stats(
recs, top_n=top_n, min_price=min_price
)
raw_sizes.append(int(stats["raw_pool"]))
eligible_sizes.append(int(stats["eligible_pre_mask"]))
bind_flags.append(bool(stats["mask_binds"]))
pairs = _filter_liquid_breadth_week(
recs, top_n=top_n, min_price=min_price
)
@@ -1146,6 +1214,7 @@ def _signal_evaluation(collected: dict) -> list[dict]:
spread = _quintile_spread(pairs)
if spread is not None:
spreads.append(spread)
# avg_cross_section is ALWAYS post-mask pair count (the IC sample).
sizes.append(len(pairs))
if not ics:
continue
@@ -1168,16 +1237,32 @@ def _signal_evaluation(collected: dict) -> list[dict]:
if top_n > 0:
row["liquid_breadth_top_n"] = top_n
row["liquid_min_price"] = min_price
# Explicit pre/post mask diagnostics (reconcile "did top-N bind?").
if raw_sizes:
row["avg_raw_pool"] = round(sum(raw_sizes) / len(raw_sizes), 1)
if eligible_sizes:
row["avg_eligible_pre_mask"] = round(
sum(eligible_sizes) / len(eligible_sizes), 1
)
if bind_flags:
row["mask_binds_pct"] = round(
sum(1 for b in bind_flags if b) / len(bind_flags) * 100, 1
)
rows.append(row)
rows.sort(key=lambda r: r["mean_ic"], reverse=True)
return rows
def _signal_series(records: list, benchmark_closes: dict[date, float] | None = None) -> dict:
def _signal_series(
records: list,
benchmark_closes: dict[date, float] | None = None,
*,
symbol: str | None = None,
) -> dict:
"""Per-ticker signal/forward-return series as a PLAIN (picklable) nested dict
— no defaultdict/lambda — so it can cross a process boundary."""
tmp: dict = defaultdict(lambda: defaultdict(list))
_accumulate_signal_series(records, tmp, benchmark_closes)
_accumulate_signal_series(records, tmp, benchmark_closes, symbol=symbol)
return {name: dict(weeks) for name, weeks in tmp.items()}
@@ -1218,7 +1303,7 @@ def _replay_and_signals(
)
return (
candidates,
_signal_series(bars, benchmark_closes),
_signal_series(bars, benchmark_closes, symbol=symbol),
)