Add attention client and continue PEAD research

main
I Luk Kim 5 months ago
parent d3e4c1d0d5
commit b507fbf499

@ -16,6 +16,7 @@ from libs.backtest.domain import (
BacktestConfig,
Candidate,
DailyPortfolioState,
ExecutionConfig,
ExperimentManifest,
ExperimentResult,
FilledTrade,
@ -336,6 +337,7 @@ class BacktestRunner:
portfolio_state=portfolio_state,
open_positions=self._open_positions,
config=self.config,
execution_config=self._build_effective_execution_config(candidate),
cooldown_remaining=self._cooldown_remaining,
macro_data=macro_data,
engine_daily_new_risk_used=self._engine_daily_new_risk_used[candidate.engine_id],
@ -477,19 +479,30 @@ class BacktestRunner:
break
return ordered[: self.config.signal.max_candidates_per_day]
def _build_effective_execution_config(self, candidate: Candidate) -> Any:
"""Resolve per-engine and per-event holding-period overrides."""
def _build_effective_execution_config(self, candidate: Candidate) -> ExecutionConfig:
"""Resolve per-engine and per-event execution overrides."""
execution_updates: dict[str, Any] = {}
max_holding_days = candidate.engine_max_holding_days
if max_holding_days is None:
evt_profile = self.config.get_event_profile(candidate.event_type)
if evt_profile and evt_profile.max_holding_days_override is not None:
max_holding_days = evt_profile.max_holding_days_override
if max_holding_days is None:
if max_holding_days is not None:
execution_updates["max_holding_days"] = max_holding_days
if candidate.engine_target_atr_multiplier is not None:
execution_updates["target_atr_multiplier"] = candidate.engine_target_atr_multiplier
if candidate.engine_target_1_fraction is not None:
execution_updates["target_1_fraction"] = candidate.engine_target_1_fraction
if candidate.engine_trailing_model is not None:
execution_updates["trailing_model"] = candidate.engine_trailing_model
if candidate.engine_trailing_warmup_days is not None:
execution_updates["trailing_warmup_days"] = candidate.engine_trailing_warmup_days
if not execution_updates:
return self.config.execution
return self.config.execution.model_copy(
update={"max_holding_days": max_holding_days}
)
return self.config.execution.model_copy(update=execution_updates)
def _build_per_engine_metrics(self) -> dict[str, dict[str, Any]]:
"""Run each engine in isolation for standalone metrics and shadow summaries."""
@ -531,6 +544,10 @@ class BacktestRunner:
"entry_timing_policy": engine.entry_timing_policy,
"max_holding_days": engine.max_holding_days,
"engine_risk_budget_pct": engine.engine_risk_budget_pct,
"target_atr_multiplier_override": engine.target_atr_multiplier_override,
"target_1_fraction_override": engine.target_1_fraction_override,
"trailing_model_override": engine.trailing_model_override,
"trailing_warmup_days_override": engine.trailing_warmup_days_override,
"total_candidates_seen": result.total_candidates_seen,
"total_orders_rejected": result.total_orders_rejected,
"net_pnl": round(sum(trade.net_pnl for trade in runner._closed_trades), 4),

@ -0,0 +1,406 @@
"""Quick probe for free attention data on snapshot events.
This is a research helper, not a production pipeline.
Current sources:
- Wikimedia pageviews: scalable attention proxy
- GDELT Doc API: optional spot-check article count for a few names
"""
from __future__ import annotations
import argparse
import asyncio
import datetime as dt
import re
import time
from dataclasses import dataclass
from pathlib import Path
import pandas as pd
import requests
from sqlalchemy import text
from sqlalchemy.ext.asyncio import create_async_engine
from libs.common.config import get_settings
USER_AGENT = "codex-fithia2-free-attention-probe/1.0"
WIKI_SEARCH_URL = "https://en.wikipedia.org/w/api.php"
WIKI_PAGEVIEWS_URL = (
"https://wikimedia.org/api/rest_v1/metrics/pageviews/per-article/"
"en.wikipedia.org/all-access/all-agents/{article}/daily/{start}/{end}"
)
GDELT_DOC_URL = "https://api.gdeltproject.org/api/v2/doc/doc"
STOPWORDS = {
"inc",
"incorporated",
"corp",
"corporation",
"ltd",
"holdings",
"group",
"co",
"company",
"plc",
"nv",
}
MANUAL_WIKI_TITLE = {
"AMERICAN AIRLINES GROUP INC.": "American Airlines Group",
"ASTRONICS CORPORATION": "Astronics",
"CENTURY ALUMINUM COMPANY": "Century Aluminum",
"CLEANSPARK, INC.": "CleanSpark",
"CCC INTELLIGENT SOLUTIONS HOLDINGS INC.": "CCC Intelligent Solutions",
"FLUENCE ENERGY, INC.": "Fluence Energy",
"IMMUNITYBIO, INC.": "ImmunityBio",
"IMMUNITYBIO,\xa0INC.": "ImmunityBio",
"LYFT, INC.": "Lyft",
"MIRION TECHNOLOGIES, INC.": "Mirion Technologies",
"MOSAIC CO": "The Mosaic Company",
"NORWEGIAN CRUISE LINE HOLDINGS LTD.": "Norwegian Cruise Line Holdings",
"PAR PACIFIC HOLDINGS, INC.": "Par Pacific Holdings",
"PATTERSON-UTI ENERGY, INC.": "Patterson-UTI Energy",
"RITHM CAPITAL CORP.": "Rithm Capital",
"SOUNDHOUND AI, INC.": "SoundHound AI",
"TANGO THERAPEUTICS, INC.": "Tango Therapeutics",
"TERNS PHARMACEUTICALS, INC.": "Terns Pharmaceuticals",
"UNITY SOFTWARE INC.": "Unity Technologies",
}
@dataclass(frozen=True)
class ProbeRow:
ticker: str
issuer_name: str
event_date: dt.date
reaction_day_return: float
fwd_return_3d: float
fwd_return_5d: float
def _session() -> requests.Session:
s = requests.Session()
s.headers.update({"User-Agent": USER_AGENT})
return s
def _clean_tokens(text_value: str) -> list[str]:
tokens = re.findall(r"[A-Za-z0-9]+", text_value.lower().replace("\xa0", " "))
return [token for token in tokens if token not in STOPWORDS]
def _candidate_names(name: str) -> list[str]:
clean = name.replace("\xa0", " ").strip()
manual = MANUAL_WIKI_TITLE.get(clean.upper())
candidates = [candidate for candidate in [manual, clean] if candidate]
token_name = " ".join(_clean_tokens(clean))
if token_name:
candidates.append(token_name)
seen: set[str] = set()
result: list[str] = []
for candidate in candidates:
stripped = candidate.strip(" ,.")
if stripped and stripped not in seen:
result.append(stripped)
seen.add(stripped)
return result
def _title_match_score(name: str, title: str) -> float:
name_tokens = set(_clean_tokens(name))
title_tokens = set(_clean_tokens(title))
if not name_tokens or not title_tokens:
return 0.0
overlap = len(name_tokens & title_tokens)
score = overlap / max(1, len(name_tokens))
first_word = name.split()[0].lower() if name.split() else ""
if first_word and title.lower().startswith(first_word):
score += 0.1
return score
def resolve_wikipedia_title(session: requests.Session, issuer_name: str) -> tuple[str | None, float]:
best_score = 0.0
best_title: str | None = None
for candidate in _candidate_names(issuer_name):
response = session.get(
WIKI_SEARCH_URL,
params={
"action": "query",
"list": "search",
"srsearch": candidate,
"format": "json",
"srlimit": 5,
},
timeout=20,
)
response.raise_for_status()
hits = response.json().get("query", {}).get("search", [])
for hit in hits:
title = hit["title"]
score = _title_match_score(candidate, title)
if score > best_score:
best_score = score
best_title = title
if best_score >= 0.55:
return best_title, best_score
return None, best_score
def fetch_pageview_spike(
session: requests.Session,
article_title: str,
event_date: dt.date,
) -> dict[str, float] | None:
start = (event_date - dt.timedelta(days=20)).strftime("%Y%m%d")
end = (event_date + dt.timedelta(days=2)).strftime("%Y%m%d")
response = session.get(
WIKI_PAGEVIEWS_URL.format(
article=article_title.replace(" ", "_"),
start=start,
end=end,
),
timeout=20,
)
if response.status_code != 200:
return None
items = response.json().get("items", [])
if len(items) < 8:
return None
views = pd.DataFrame(
[(pd.to_datetime(item["timestamp"][:8]), item["views"]) for item in items],
columns=["date", "views"],
).sort_values("date")
event_ts = pd.Timestamp(event_date)
pre_event = views.loc[views["date"] < event_ts, "views"]
event_views = views.loc[views["date"] == event_ts, "views"]
if pre_event.empty or event_views.empty:
return None
baseline = float(pre_event.tail(10).median())
if baseline <= 0:
return None
event_value = float(event_views.iloc[0])
return {
"event_views": event_value,
"baseline_views": baseline,
"pageview_spike": event_value / baseline,
}
def fetch_gdelt_article_count(
session: requests.Session,
issuer_name: str,
event_date: dt.date,
) -> int | None:
exact_name = MANUAL_WIKI_TITLE.get(issuer_name.upper(), issuer_name)
phrase = exact_name.replace("\xa0", " ").replace('"', "")
if len(phrase) < 6:
return None
time.sleep(6.0)
start = (event_date - dt.timedelta(days=1)).strftime("%Y%m%d") + "000000"
end = (event_date + dt.timedelta(days=1)).strftime("%Y%m%d") + "235959"
response = session.get(
GDELT_DOC_URL,
params={
"query": f'"{phrase}"',
"mode": "ArtList",
"maxrecords": 50,
"format": "json",
"startdatetime": start,
"enddatetime": end,
},
timeout=30,
)
if response.status_code != 200:
return None
payload = response.json()
articles = payload.get("articles", [])
return len(articles)
async def load_probe_rows(snapshot_path: Path, split_name: str, limit: int) -> list[ProbeRow]:
snapshot = pd.read_parquet(snapshot_path)
snapshot = snapshot[
["event_id", "event_date", "reaction_day_return", "fwd_return_3d", "fwd_return_5d"]
].copy()
snapshot["event_date"] = pd.to_datetime(snapshot["event_date"]).dt.date
engine = create_async_engine(get_settings().postgres_dsn)
try:
async with engine.connect() as conn:
result = await conn.execute(
text(
"""
select e.event_id, e.event_type, sm.ticker, i.issuer_name
from events e
left join issuer_master i on i.issuer_id = e.issuer_id
left join symbol_master sm on sm.symbol_id = e.symbol_id
where e.event_id = any(:ids)
"""
),
{"ids": snapshot["event_id"].tolist()},
)
meta = pd.DataFrame(result.fetchall(), columns=result.keys())
finally:
await engine.dispose()
merged = snapshot.merge(meta, on="event_id", how="left")
merged = merged[merged["event_type"] == "earnings_release"].copy()
generic = (
(merged["issuer_name"].fillna("") == merged["ticker"].fillna("") + " Corporation")
| (merged["issuer_name"].fillna("") == merged["ticker"].fillna("") + " Inc.")
| (merged["issuer_name"].fillna("") == merged["ticker"].fillna("") + " Ltd.")
)
filtered = merged.loc[~generic & merged["issuer_name"].notna()].sort_values("event_date")
if limit > 0:
filtered = filtered.head(limit)
return [
ProbeRow(
ticker=str(row["ticker"]),
issuer_name=str(row["issuer_name"]),
event_date=row["event_date"],
reaction_day_return=float(row["reaction_day_return"]),
fwd_return_3d=float(row["fwd_return_3d"]),
fwd_return_5d=float(row["fwd_return_5d"]),
)
for _, row in filtered.iterrows()
]
def run_probe(
rows: list[ProbeRow],
output_csv: Path | None,
gdelt_limit: int,
) -> pd.DataFrame:
session = _session()
resolved_rows: list[dict[str, object]] = []
for idx, row in enumerate(rows):
title, match_score = resolve_wikipedia_title(session, row.issuer_name)
if not title:
continue
pageviews = fetch_pageview_spike(session, title, row.event_date)
if not pageviews:
continue
gdelt_count = None
if idx < gdelt_limit:
gdelt_count = fetch_gdelt_article_count(session, row.issuer_name, row.event_date)
signed_cont_3d = (1.0 if row.reaction_day_return >= 0 else -1.0) * row.fwd_return_3d
signed_cont_5d = (1.0 if row.reaction_day_return >= 0 else -1.0) * row.fwd_return_5d
resolved_rows.append(
{
"ticker": row.ticker,
"issuer_name": row.issuer_name,
"article_title": title,
"match_score": round(match_score, 3),
"event_date": row.event_date.isoformat(),
"reaction_day_return": row.reaction_day_return,
"fwd_return_3d": row.fwd_return_3d,
"fwd_return_5d": row.fwd_return_5d,
"signed_cont_3d": signed_cont_3d,
"signed_cont_5d": signed_cont_5d,
**pageviews,
"gdelt_article_count_3d": gdelt_count,
}
)
df = pd.DataFrame(resolved_rows)
if output_csv and not df.empty:
output_csv.parent.mkdir(parents=True, exist_ok=True)
df.to_csv(output_csv, index=False)
return df
def print_summary(df: pd.DataFrame, sampled_rows: int) -> None:
print(f"resolved_rows={len(df)} sampled_rows={sampled_rows}")
if df.empty:
return
preview_cols = [
"ticker",
"issuer_name",
"article_title",
"match_score",
"pageview_spike",
"signed_cont_3d",
"signed_cont_5d",
"gdelt_article_count_3d",
]
print(df[preview_cols].to_string(index=False))
median_spike = float(df["pageview_spike"].median())
high = df[df["pageview_spike"] >= median_spike]
low = df[df["pageview_spike"] < median_spike]
print("")
print(f"median_pageview_spike={median_spike:.3f}")
print(f"high_group_n={len(high)} low_group_n={len(low)}")
print(f"high_signed_cont_3d_mean={high['signed_cont_3d'].mean():.4f}")
print(f"low_signed_cont_3d_mean={low['signed_cont_3d'].mean():.4f}")
print(f"high_signed_cont_5d_mean={high['signed_cont_5d'].mean():.4f}")
print(f"low_signed_cont_5d_mean={low['signed_cont_5d'].mean():.4f}")
print(f"corr(pageview_spike,signed_cont_3d)={df['pageview_spike'].corr(df['signed_cont_3d']):.4f}")
print(f"corr(pageview_spike,signed_cont_5d)={df['pageview_spike'].corr(df['signed_cont_5d']):.4f}")
gdelt = df["gdelt_article_count_3d"].dropna()
if not gdelt.empty:
print(
"gdelt_counts_sample="
+ ", ".join(str(int(value)) for value in gdelt.tolist())
)
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description="Probe free attention data against snapshot outcomes.")
parser.add_argument(
"--snapshot",
default="data/datasets/snapshots/midcap-filtered/test.parquet",
help="Snapshot parquet path",
)
parser.add_argument(
"--split",
default="test",
help="Label only for reporting; snapshot path determines actual data",
)
parser.add_argument(
"--limit",
type=int,
default=30,
help="Number of filtered earnings events to sample",
)
parser.add_argument(
"--gdelt-limit",
type=int,
default=5,
help="How many resolved rows to spot-check with GDELT article counts",
)
parser.add_argument(
"--output-csv",
default="data/research/free_attention_probe_test_sample.csv",
help="Output CSV path",
)
return parser.parse_args()
async def main() -> None:
args = parse_args()
snapshot_path = Path(args.snapshot)
rows = await load_probe_rows(snapshot_path, args.split, args.limit)
df = run_probe(rows, Path(args.output_csv), args.gdelt_limit)
print_summary(df, sampled_rows=len(rows))
if not df.empty:
print(f"saved_csv={args.output_csv}")
if __name__ == "__main__":
asyncio.run(main())

@ -29,6 +29,7 @@ from libs.backtest.tracker import (
compute_sqs_v2,
compute_unified_score,
get_next_entry_id,
journal_lock,
load_journal,
rebuild_registry,
scan_runs_for_experiment,
@ -43,13 +44,6 @@ def cmd_record(args: argparse.Namespace) -> None:
registry_path = journal_dir / "experiment_registry.json"
leaderboard_path = journal_dir / "LEADERBOARD.md"
# Check duplicate
dupes = check_duplicate(journal_path, args.experiment)
if dupes and not args.force:
print(f"WARNING: experiment '{args.experiment}' already in journal ({len(dupes)} entries).")
print("Use --force to add anyway.")
sys.exit(1)
# Scan runs
runs_dir = Path(args.runs_dir)
if not runs_dir.exists():
@ -110,29 +104,37 @@ def cmd_record(args: argparse.Namespace) -> None:
# Build tags from experiment name
tags = [t for t in args.experiment.replace("-", "_").split("_") if t]
entry_id = get_next_entry_id(journal_path)
entry = JournalEntry(
entry_id=entry_id,
timestamp=utc_now().isoformat(),
experiment_name=args.experiment,
hypothesis=args.hypothesis or "",
config_delta=config_delta,
results=results,
sqs_score=sqs_score,
sqs_breakdown=sqs_breakdown,
sqs_v2_score=sqs_v2_score,
sqs_v2_breakdown=sqs_v2_breakdown,
promotion_score=promotion_score,
promotion_breakdown=promotion_breakdown,
unified_score=unified_score,
unified_breakdown=unified_breakdown,
verdict=args.verdict or "unknown",
verdict_reasoning=args.reasoning or "",
next_direction=args.next or "",
tags=tags,
)
with journal_lock(journal_path):
dupes = check_duplicate(journal_path, args.experiment)
if dupes and not args.force:
print(f"WARNING: experiment '{args.experiment}' already in journal ({len(dupes)} entries).")
print("Use --force to add anyway.")
sys.exit(1)
entry_id = get_next_entry_id(journal_path)
entry = JournalEntry(
entry_id=entry_id,
timestamp=utc_now().isoformat(),
experiment_name=args.experiment,
hypothesis=args.hypothesis or "",
config_delta=config_delta,
results=results,
sqs_score=sqs_score,
sqs_breakdown=sqs_breakdown,
sqs_v2_score=sqs_v2_score,
sqs_v2_breakdown=sqs_v2_breakdown,
promotion_score=promotion_score,
promotion_breakdown=promotion_breakdown,
unified_score=unified_score,
unified_breakdown=unified_breakdown,
verdict=args.verdict or "unknown",
verdict_reasoning=args.reasoning or "",
next_direction=args.next or "",
tags=tags,
)
append_journal_entry(journal_path, entry)
append_journal_entry(journal_path, entry)
rebuild_registry(journal_path, registry_path, leaderboard_path)
source_label = f", source={public_source}" if public_source else ""
print(f"Recorded {entry_id}: {args.experiment} (SQS={sqs_score}{source_label})")
@ -142,8 +144,6 @@ def cmd_record(args: argparse.Namespace) -> None:
ret = f"Ret={sr.total_return_pct:+.2f}%" if sr.total_return_pct is not None else "Ret=-"
print(f" {split_name}: {sr.trade_count} trades, {pf}, {ret}")
# Rebuild leaderboard
rebuild_registry(journal_path, registry_path, leaderboard_path)
print(f"Leaderboard updated: {leaderboard_path}")

@ -0,0 +1,81 @@
{
"experiment_name": "pead_midcap_step54_short_core_macro_block_crashcap_gap10_longtrend12",
"dataset_snapshot_id": "midcap-filtered",
"description": "Step 54: Keep the step52 short-core structure, but let the same-day long overlay run with a wider target, partial exit, wider trailing stop, and a 12-day hold.",
"base_config": "configs/backtest/defaults.json",
"overrides": {
"strategy_engine_selection_mode": "global_score",
"event_type_profiles": {
"earnings_release": {"enabled": true, "direction_filter": "any", "max_holding_days_override": 7},
"guidance_update": {"enabled": false},
"management_change": {"enabled": false},
"material_contract": {"enabled": false},
"unknown": {"enabled": false},
"other_material_event": {"enabled": false}
},
"signal": {
"scoring_model": "pead",
"pead_reaction_threshold": 0.10,
"pead_volume_threshold": 2.0,
"score_threshold": 0.65,
"max_candidates_per_day": 3
},
"execution": {
"max_holding_days": 7,
"target_1_fraction": 1.0
},
"risk": {
"max_positions": 8,
"max_positions_per_sector": 8,
"max_daily_new_risk_pct": 0.04,
"cooldown_after_loss_streak": 0,
"cooldown_days": 0,
"veto_oneoff_penalty": 1.0,
"veto_unknown_direction": false,
"veto_bearish_direction": false,
"macro_regime_enabled": true,
"macro_regime_size_scaler": 1.0
}
},
"strategy_engines": [
{
"engine_id": "earnings_same_day_short_step14_capped",
"event_types": ["earnings_release"],
"timing_class": "same_day",
"direction": "short_only",
"entry_timing_policy": "next_open",
"max_holding_days": 7,
"engine_risk_budget_pct": 1.0,
"reaction_day_return_min": -0.45,
"shadow_only": false
},
{
"engine_id": "earnings_after_close_short_core",
"event_types": ["earnings_release"],
"timing_class": "after_close",
"direction": "short_only",
"entry_timing_policy": "next_open",
"max_holding_days": 7,
"engine_risk_budget_pct": 0.25,
"shadow_only": false
},
{
"engine_id": "earnings_same_day_long_close12_gap10_trend",
"event_types": ["earnings_release"],
"timing_class": "same_day",
"direction": "long_only",
"entry_timing_policy": "reaction_close",
"max_holding_days": 12,
"engine_risk_budget_pct": 0.125,
"gap_size_min": 0.10,
"target_atr_multiplier_override": 2.5,
"target_1_fraction_override": 0.33,
"trailing_model_override": "pct_10",
"trailing_warmup_days_override": 2,
"shadow_only": false
}
],
"splits": [],
"tags": ["pead", "midcap", "step54", "short_core", "macro_block", "crashcap", "gap10", "longtrend12"],
"notes": null
}

@ -0,0 +1,81 @@
{
"experiment_name": "pead_midcap_step55_short_core_macro_block_crashcap_gap10_longtrend12_sdlong25",
"dataset_snapshot_id": "midcap-filtered",
"description": "Step 55: Step54 plus a larger same-day long overlay budget, increasing long participation while keeping the short-core sleeves intact.",
"base_config": "configs/backtest/defaults.json",
"overrides": {
"strategy_engine_selection_mode": "global_score",
"event_type_profiles": {
"earnings_release": {"enabled": true, "direction_filter": "any", "max_holding_days_override": 7},
"guidance_update": {"enabled": false},
"management_change": {"enabled": false},
"material_contract": {"enabled": false},
"unknown": {"enabled": false},
"other_material_event": {"enabled": false}
},
"signal": {
"scoring_model": "pead",
"pead_reaction_threshold": 0.10,
"pead_volume_threshold": 2.0,
"score_threshold": 0.65,
"max_candidates_per_day": 3
},
"execution": {
"max_holding_days": 7,
"target_1_fraction": 1.0
},
"risk": {
"max_positions": 8,
"max_positions_per_sector": 8,
"max_daily_new_risk_pct": 0.04,
"cooldown_after_loss_streak": 0,
"cooldown_days": 0,
"veto_oneoff_penalty": 1.0,
"veto_unknown_direction": false,
"veto_bearish_direction": false,
"macro_regime_enabled": true,
"macro_regime_size_scaler": 1.0
}
},
"strategy_engines": [
{
"engine_id": "earnings_same_day_short_step14_capped",
"event_types": ["earnings_release"],
"timing_class": "same_day",
"direction": "short_only",
"entry_timing_policy": "next_open",
"max_holding_days": 7,
"engine_risk_budget_pct": 1.0,
"reaction_day_return_min": -0.45,
"shadow_only": false
},
{
"engine_id": "earnings_after_close_short_core",
"event_types": ["earnings_release"],
"timing_class": "after_close",
"direction": "short_only",
"entry_timing_policy": "next_open",
"max_holding_days": 7,
"engine_risk_budget_pct": 0.25,
"shadow_only": false
},
{
"engine_id": "earnings_same_day_long_close12_gap10_trend",
"event_types": ["earnings_release"],
"timing_class": "same_day",
"direction": "long_only",
"entry_timing_policy": "reaction_close",
"max_holding_days": 12,
"engine_risk_budget_pct": 0.25,
"gap_size_min": 0.10,
"target_atr_multiplier_override": 2.5,
"target_1_fraction_override": 0.33,
"trailing_model_override": "pct_10",
"trailing_warmup_days_override": 2,
"shadow_only": false
}
],
"splits": [],
"tags": ["pead", "midcap", "step55", "short_core", "macro_block", "crashcap", "gap10", "longtrend12", "sdlong25"],
"notes": null
}

@ -0,0 +1,81 @@
{
"experiment_name": "pead_midcap_step56_short_core_macro_block_crashcap_gap10_interleave_longtrend25",
"dataset_snapshot_id": "midcap-filtered",
"description": "Step 56: Step55 plus interleaved engine selection so the strengthened same-day long overlay gets consistent portfolio slots instead of competing purely on raw PEAD score.",
"base_config": "configs/backtest/defaults.json",
"overrides": {
"strategy_engine_selection_mode": "interleave",
"event_type_profiles": {
"earnings_release": {"enabled": true, "direction_filter": "any", "max_holding_days_override": 7},
"guidance_update": {"enabled": false},
"management_change": {"enabled": false},
"material_contract": {"enabled": false},
"unknown": {"enabled": false},
"other_material_event": {"enabled": false}
},
"signal": {
"scoring_model": "pead",
"pead_reaction_threshold": 0.10,
"pead_volume_threshold": 2.0,
"score_threshold": 0.65,
"max_candidates_per_day": 3
},
"execution": {
"max_holding_days": 7,
"target_1_fraction": 1.0
},
"risk": {
"max_positions": 8,
"max_positions_per_sector": 8,
"max_daily_new_risk_pct": 0.04,
"cooldown_after_loss_streak": 0,
"cooldown_days": 0,
"veto_oneoff_penalty": 1.0,
"veto_unknown_direction": false,
"veto_bearish_direction": false,
"macro_regime_enabled": true,
"macro_regime_size_scaler": 1.0
}
},
"strategy_engines": [
{
"engine_id": "earnings_same_day_short_step14_capped",
"event_types": ["earnings_release"],
"timing_class": "same_day",
"direction": "short_only",
"entry_timing_policy": "next_open",
"max_holding_days": 7,
"engine_risk_budget_pct": 1.0,
"reaction_day_return_min": -0.45,
"shadow_only": false
},
{
"engine_id": "earnings_same_day_long_close12_gap10_trend",
"event_types": ["earnings_release"],
"timing_class": "same_day",
"direction": "long_only",
"entry_timing_policy": "reaction_close",
"max_holding_days": 12,
"engine_risk_budget_pct": 0.25,
"gap_size_min": 0.10,
"target_atr_multiplier_override": 2.5,
"target_1_fraction_override": 0.33,
"trailing_model_override": "pct_10",
"trailing_warmup_days_override": 2,
"shadow_only": false
},
{
"engine_id": "earnings_after_close_short_core",
"event_types": ["earnings_release"],
"timing_class": "after_close",
"direction": "short_only",
"entry_timing_policy": "next_open",
"max_holding_days": 7,
"engine_risk_budget_pct": 0.125,
"shadow_only": false
}
],
"splits": [],
"tags": ["pead", "midcap", "step56", "short_core", "macro_block", "crashcap", "gap10", "interleave", "longtrend25"],
"notes": null
}

@ -0,0 +1,81 @@
{
"experiment_name": "pead_midcap_step57_short_core_macro_block_crashcap_gap10_longtrend12_sdlong25_max4",
"dataset_snapshot_id": "midcap-filtered",
"description": "Step 57: Step55 plus four daily candidate slots so the added long trend sleeve does not crowd out positive short-core entries.",
"base_config": "configs/backtest/defaults.json",
"overrides": {
"strategy_engine_selection_mode": "global_score",
"event_type_profiles": {
"earnings_release": {"enabled": true, "direction_filter": "any", "max_holding_days_override": 7},
"guidance_update": {"enabled": false},
"management_change": {"enabled": false},
"material_contract": {"enabled": false},
"unknown": {"enabled": false},
"other_material_event": {"enabled": false}
},
"signal": {
"scoring_model": "pead",
"pead_reaction_threshold": 0.10,
"pead_volume_threshold": 2.0,
"score_threshold": 0.65,
"max_candidates_per_day": 4
},
"execution": {
"max_holding_days": 7,
"target_1_fraction": 1.0
},
"risk": {
"max_positions": 8,
"max_positions_per_sector": 8,
"max_daily_new_risk_pct": 0.04,
"cooldown_after_loss_streak": 0,
"cooldown_days": 0,
"veto_oneoff_penalty": 1.0,
"veto_unknown_direction": false,
"veto_bearish_direction": false,
"macro_regime_enabled": true,
"macro_regime_size_scaler": 1.0
}
},
"strategy_engines": [
{
"engine_id": "earnings_same_day_short_step14_capped",
"event_types": ["earnings_release"],
"timing_class": "same_day",
"direction": "short_only",
"entry_timing_policy": "next_open",
"max_holding_days": 7,
"engine_risk_budget_pct": 1.0,
"reaction_day_return_min": -0.45,
"shadow_only": false
},
{
"engine_id": "earnings_after_close_short_core",
"event_types": ["earnings_release"],
"timing_class": "after_close",
"direction": "short_only",
"entry_timing_policy": "next_open",
"max_holding_days": 7,
"engine_risk_budget_pct": 0.25,
"shadow_only": false
},
{
"engine_id": "earnings_same_day_long_close12_gap10_trend",
"event_types": ["earnings_release"],
"timing_class": "same_day",
"direction": "long_only",
"entry_timing_policy": "reaction_close",
"max_holding_days": 12,
"engine_risk_budget_pct": 0.25,
"gap_size_min": 0.10,
"target_atr_multiplier_override": 2.5,
"target_1_fraction_override": 0.33,
"trailing_model_override": "pct_10",
"trailing_warmup_days_override": 2,
"shadow_only": false
}
],
"splits": [],
"tags": ["pead", "midcap", "step57", "short_core", "macro_block", "crashcap", "gap10", "longtrend12", "sdlong25", "max4"],
"notes": null
}

@ -0,0 +1,81 @@
{
"experiment_name": "pead_midcap_step58_short_core_macro_block_crashcap_gap7_longtrend12_sdlong25",
"dataset_snapshot_id": "midcap-filtered",
"description": "Step 58: Step55 with a looser same-day long gap filter, broadening the trend-hold overlay beyond only the most extreme 10% gap moves.",
"base_config": "configs/backtest/defaults.json",
"overrides": {
"strategy_engine_selection_mode": "global_score",
"event_type_profiles": {
"earnings_release": {"enabled": true, "direction_filter": "any", "max_holding_days_override": 7},
"guidance_update": {"enabled": false},
"management_change": {"enabled": false},
"material_contract": {"enabled": false},
"unknown": {"enabled": false},
"other_material_event": {"enabled": false}
},
"signal": {
"scoring_model": "pead",
"pead_reaction_threshold": 0.10,
"pead_volume_threshold": 2.0,
"score_threshold": 0.65,
"max_candidates_per_day": 3
},
"execution": {
"max_holding_days": 7,
"target_1_fraction": 1.0
},
"risk": {
"max_positions": 8,
"max_positions_per_sector": 8,
"max_daily_new_risk_pct": 0.04,
"cooldown_after_loss_streak": 0,
"cooldown_days": 0,
"veto_oneoff_penalty": 1.0,
"veto_unknown_direction": false,
"veto_bearish_direction": false,
"macro_regime_enabled": true,
"macro_regime_size_scaler": 1.0
}
},
"strategy_engines": [
{
"engine_id": "earnings_same_day_short_step14_capped",
"event_types": ["earnings_release"],
"timing_class": "same_day",
"direction": "short_only",
"entry_timing_policy": "next_open",
"max_holding_days": 7,
"engine_risk_budget_pct": 1.0,
"reaction_day_return_min": -0.45,
"shadow_only": false
},
{
"engine_id": "earnings_after_close_short_core",
"event_types": ["earnings_release"],
"timing_class": "after_close",
"direction": "short_only",
"entry_timing_policy": "next_open",
"max_holding_days": 7,
"engine_risk_budget_pct": 0.25,
"shadow_only": false
},
{
"engine_id": "earnings_same_day_long_close12_gap7_trend",
"event_types": ["earnings_release"],
"timing_class": "same_day",
"direction": "long_only",
"entry_timing_policy": "reaction_close",
"max_holding_days": 12,
"engine_risk_budget_pct": 0.25,
"gap_size_min": 0.07,
"target_atr_multiplier_override": 2.5,
"target_1_fraction_override": 0.33,
"trailing_model_override": "pct_10",
"trailing_warmup_days_override": 2,
"shadow_only": false
}
],
"splits": [],
"tags": ["pead", "midcap", "step58", "short_core", "macro_block", "crashcap", "gap7", "longtrend12", "sdlong25"],
"notes": null
}

@ -0,0 +1,81 @@
{
"experiment_name": "pead_midcap_step59_same_day_only_max4_longtrend25",
"dataset_snapshot_id": "midcap-filtered",
"description": "Step 59: Remove after-close short from the active book and keep only the same-day short core plus same-day long trend sleeve under four daily slots.",
"base_config": "configs/backtest/defaults.json",
"overrides": {
"strategy_engine_selection_mode": "global_score",
"event_type_profiles": {
"earnings_release": {"enabled": true, "direction_filter": "any", "max_holding_days_override": 7},
"guidance_update": {"enabled": false},
"management_change": {"enabled": false},
"material_contract": {"enabled": false},
"unknown": {"enabled": false},
"other_material_event": {"enabled": false}
},
"signal": {
"scoring_model": "pead",
"pead_reaction_threshold": 0.10,
"pead_volume_threshold": 2.0,
"score_threshold": 0.65,
"max_candidates_per_day": 4
},
"execution": {
"max_holding_days": 7,
"target_1_fraction": 1.0
},
"risk": {
"max_positions": 8,
"max_positions_per_sector": 8,
"max_daily_new_risk_pct": 0.04,
"cooldown_after_loss_streak": 0,
"cooldown_days": 0,
"veto_oneoff_penalty": 1.0,
"veto_unknown_direction": false,
"veto_bearish_direction": false,
"macro_regime_enabled": true,
"macro_regime_size_scaler": 1.0
}
},
"strategy_engines": [
{
"engine_id": "earnings_same_day_short_step14_capped",
"event_types": ["earnings_release"],
"timing_class": "same_day",
"direction": "short_only",
"entry_timing_policy": "next_open",
"max_holding_days": 7,
"engine_risk_budget_pct": 1.0,
"reaction_day_return_min": -0.45,
"shadow_only": false
},
{
"engine_id": "earnings_same_day_long_close12_gap10_trend",
"event_types": ["earnings_release"],
"timing_class": "same_day",
"direction": "long_only",
"entry_timing_policy": "reaction_close",
"max_holding_days": 12,
"engine_risk_budget_pct": 0.25,
"gap_size_min": 0.10,
"target_atr_multiplier_override": 2.5,
"target_1_fraction_override": 0.33,
"trailing_model_override": "pct_10",
"trailing_warmup_days_override": 2,
"shadow_only": false
},
{
"engine_id": "earnings_after_close_short_core_shadow",
"event_types": ["earnings_release"],
"timing_class": "after_close",
"direction": "short_only",
"entry_timing_policy": "next_open",
"max_holding_days": 7,
"engine_risk_budget_pct": 0.25,
"shadow_only": true
}
],
"splits": [],
"tags": ["pead", "midcap", "step59", "same_day_only", "max4", "longtrend25"],
"notes": null
}

@ -0,0 +1,81 @@
{
"experiment_name": "pead_midcap_step60_same_day_only_interleave_max4_longtrend25",
"dataset_snapshot_id": "midcap-filtered",
"description": "Step 60: Same-day only book with interleaved sleeve selection so the long trend overlay always gets candidate representation alongside the short core.",
"base_config": "configs/backtest/defaults.json",
"overrides": {
"strategy_engine_selection_mode": "interleave",
"event_type_profiles": {
"earnings_release": {"enabled": true, "direction_filter": "any", "max_holding_days_override": 7},
"guidance_update": {"enabled": false},
"management_change": {"enabled": false},
"material_contract": {"enabled": false},
"unknown": {"enabled": false},
"other_material_event": {"enabled": false}
},
"signal": {
"scoring_model": "pead",
"pead_reaction_threshold": 0.10,
"pead_volume_threshold": 2.0,
"score_threshold": 0.65,
"max_candidates_per_day": 4
},
"execution": {
"max_holding_days": 7,
"target_1_fraction": 1.0
},
"risk": {
"max_positions": 8,
"max_positions_per_sector": 8,
"max_daily_new_risk_pct": 0.04,
"cooldown_after_loss_streak": 0,
"cooldown_days": 0,
"veto_oneoff_penalty": 1.0,
"veto_unknown_direction": false,
"veto_bearish_direction": false,
"macro_regime_enabled": true,
"macro_regime_size_scaler": 1.0
}
},
"strategy_engines": [
{
"engine_id": "earnings_same_day_short_step14_capped",
"event_types": ["earnings_release"],
"timing_class": "same_day",
"direction": "short_only",
"entry_timing_policy": "next_open",
"max_holding_days": 7,
"engine_risk_budget_pct": 1.0,
"reaction_day_return_min": -0.45,
"shadow_only": false
},
{
"engine_id": "earnings_same_day_long_close12_gap10_trend",
"event_types": ["earnings_release"],
"timing_class": "same_day",
"direction": "long_only",
"entry_timing_policy": "reaction_close",
"max_holding_days": 12,
"engine_risk_budget_pct": 0.25,
"gap_size_min": 0.10,
"target_atr_multiplier_override": 2.5,
"target_1_fraction_override": 0.33,
"trailing_model_override": "pct_10",
"trailing_warmup_days_override": 2,
"shadow_only": false
},
{
"engine_id": "earnings_after_close_short_core_shadow",
"event_types": ["earnings_release"],
"timing_class": "after_close",
"direction": "short_only",
"entry_timing_policy": "next_open",
"max_holding_days": 7,
"engine_risk_budget_pct": 0.25,
"shadow_only": true
}
],
"splits": [],
"tags": ["pead", "midcap", "step60", "same_day_only", "interleave", "max4", "longtrend25"],
"notes": null
}

@ -0,0 +1,81 @@
{
"experiment_name": "pead_midcap_step61_same_day_only_max5_longtrend25",
"dataset_snapshot_id": "midcap-filtered",
"description": "Step 61: Same-day only book with five daily slots, testing whether the short core plus long trend overlay can lift train return when after-close short is removed.",
"base_config": "configs/backtest/defaults.json",
"overrides": {
"strategy_engine_selection_mode": "global_score",
"event_type_profiles": {
"earnings_release": {"enabled": true, "direction_filter": "any", "max_holding_days_override": 7},
"guidance_update": {"enabled": false},
"management_change": {"enabled": false},
"material_contract": {"enabled": false},
"unknown": {"enabled": false},
"other_material_event": {"enabled": false}
},
"signal": {
"scoring_model": "pead",
"pead_reaction_threshold": 0.10,
"pead_volume_threshold": 2.0,
"score_threshold": 0.65,
"max_candidates_per_day": 5
},
"execution": {
"max_holding_days": 7,
"target_1_fraction": 1.0
},
"risk": {
"max_positions": 8,
"max_positions_per_sector": 8,
"max_daily_new_risk_pct": 0.04,
"cooldown_after_loss_streak": 0,
"cooldown_days": 0,
"veto_oneoff_penalty": 1.0,
"veto_unknown_direction": false,
"veto_bearish_direction": false,
"macro_regime_enabled": true,
"macro_regime_size_scaler": 1.0
}
},
"strategy_engines": [
{
"engine_id": "earnings_same_day_short_step14_capped",
"event_types": ["earnings_release"],
"timing_class": "same_day",
"direction": "short_only",
"entry_timing_policy": "next_open",
"max_holding_days": 7,
"engine_risk_budget_pct": 1.0,
"reaction_day_return_min": -0.45,
"shadow_only": false
},
{
"engine_id": "earnings_same_day_long_close12_gap10_trend",
"event_types": ["earnings_release"],
"timing_class": "same_day",
"direction": "long_only",
"entry_timing_policy": "reaction_close",
"max_holding_days": 12,
"engine_risk_budget_pct": 0.25,
"gap_size_min": 0.10,
"target_atr_multiplier_override": 2.5,
"target_1_fraction_override": 0.33,
"trailing_model_override": "pct_10",
"trailing_warmup_days_override": 2,
"shadow_only": false
},
{
"engine_id": "earnings_after_close_short_core_shadow",
"event_types": ["earnings_release"],
"timing_class": "after_close",
"direction": "short_only",
"entry_timing_policy": "next_open",
"max_holding_days": 7,
"engine_risk_budget_pct": 0.25,
"shadow_only": true
}
],
"splits": [],
"tags": ["pead", "midcap", "step61", "same_day_only", "max5", "longtrend25"],
"notes": null
}

@ -0,0 +1,82 @@
{
"experiment_name": "pead_midcap_step62_short_core_macro_block_crashcap_gap10_longtrend12_sdlong25_max4_acsgap10",
"dataset_snapshot_id": "midcap-filtered",
"description": "Step 62: Keep the max4 mixed-sleeve portfolio, but require after-close short setups to have at least a 10% negative gap so weak downside follow-through does not crowd higher-conviction sleeves.",
"base_config": "configs/backtest/defaults.json",
"overrides": {
"strategy_engine_selection_mode": "global_score",
"event_type_profiles": {
"earnings_release": {"enabled": true, "direction_filter": "any", "max_holding_days_override": 7},
"guidance_update": {"enabled": false},
"management_change": {"enabled": false},
"material_contract": {"enabled": false},
"unknown": {"enabled": false},
"other_material_event": {"enabled": false}
},
"signal": {
"scoring_model": "pead",
"pead_reaction_threshold": 0.10,
"pead_volume_threshold": 2.0,
"score_threshold": 0.65,
"max_candidates_per_day": 4
},
"execution": {
"max_holding_days": 7,
"target_1_fraction": 1.0
},
"risk": {
"max_positions": 8,
"max_positions_per_sector": 8,
"max_daily_new_risk_pct": 0.04,
"cooldown_after_loss_streak": 0,
"cooldown_days": 0,
"veto_oneoff_penalty": 1.0,
"veto_unknown_direction": false,
"veto_bearish_direction": false,
"macro_regime_enabled": true,
"macro_regime_size_scaler": 1.0
}
},
"strategy_engines": [
{
"engine_id": "earnings_same_day_short_step14_capped",
"event_types": ["earnings_release"],
"timing_class": "same_day",
"direction": "short_only",
"entry_timing_policy": "next_open",
"max_holding_days": 7,
"engine_risk_budget_pct": 1.0,
"reaction_day_return_min": -0.45,
"shadow_only": false
},
{
"engine_id": "earnings_after_close_short_core_gap10",
"event_types": ["earnings_release"],
"timing_class": "after_close",
"direction": "short_only",
"entry_timing_policy": "next_open",
"max_holding_days": 7,
"engine_risk_budget_pct": 0.25,
"gap_size_max": -0.10,
"shadow_only": false
},
{
"engine_id": "earnings_same_day_long_close12_gap10_trend",
"event_types": ["earnings_release"],
"timing_class": "same_day",
"direction": "long_only",
"entry_timing_policy": "reaction_close",
"max_holding_days": 12,
"engine_risk_budget_pct": 0.25,
"gap_size_min": 0.10,
"target_atr_multiplier_override": 2.5,
"target_1_fraction_override": 0.33,
"trailing_model_override": "pct_10",
"trailing_warmup_days_override": 2,
"shadow_only": false
}
],
"splits": [],
"tags": ["pead", "midcap", "step62", "short_core", "macro_block", "crashcap", "gap10", "longtrend12", "sdlong25", "max4", "acsgap10"],
"notes": null
}

@ -0,0 +1,82 @@
{
"experiment_name": "pead_midcap_step63_short_core_macro_block_crashcap_gap10_longtrend12_sdlong25_max4_acsgap12",
"dataset_snapshot_id": "midcap-filtered",
"description": "Step 63: Same as step62, but require an even deeper 12% negative gap for after-close short entries to concentrate the sleeve into only the strongest downside reactions.",
"base_config": "configs/backtest/defaults.json",
"overrides": {
"strategy_engine_selection_mode": "global_score",
"event_type_profiles": {
"earnings_release": {"enabled": true, "direction_filter": "any", "max_holding_days_override": 7},
"guidance_update": {"enabled": false},
"management_change": {"enabled": false},
"material_contract": {"enabled": false},
"unknown": {"enabled": false},
"other_material_event": {"enabled": false}
},
"signal": {
"scoring_model": "pead",
"pead_reaction_threshold": 0.10,
"pead_volume_threshold": 2.0,
"score_threshold": 0.65,
"max_candidates_per_day": 4
},
"execution": {
"max_holding_days": 7,
"target_1_fraction": 1.0
},
"risk": {
"max_positions": 8,
"max_positions_per_sector": 8,
"max_daily_new_risk_pct": 0.04,
"cooldown_after_loss_streak": 0,
"cooldown_days": 0,
"veto_oneoff_penalty": 1.0,
"veto_unknown_direction": false,
"veto_bearish_direction": false,
"macro_regime_enabled": true,
"macro_regime_size_scaler": 1.0
}
},
"strategy_engines": [
{
"engine_id": "earnings_same_day_short_step14_capped",
"event_types": ["earnings_release"],
"timing_class": "same_day",
"direction": "short_only",
"entry_timing_policy": "next_open",
"max_holding_days": 7,
"engine_risk_budget_pct": 1.0,
"reaction_day_return_min": -0.45,
"shadow_only": false
},
{
"engine_id": "earnings_after_close_short_core_gap12",
"event_types": ["earnings_release"],
"timing_class": "after_close",
"direction": "short_only",
"entry_timing_policy": "next_open",
"max_holding_days": 7,
"engine_risk_budget_pct": 0.25,
"gap_size_max": -0.12,
"shadow_only": false
},
{
"engine_id": "earnings_same_day_long_close12_gap10_trend",
"event_types": ["earnings_release"],
"timing_class": "same_day",
"direction": "long_only",
"entry_timing_policy": "reaction_close",
"max_holding_days": 12,
"engine_risk_budget_pct": 0.25,
"gap_size_min": 0.10,
"target_atr_multiplier_override": 2.5,
"target_1_fraction_override": 0.33,
"trailing_model_override": "pct_10",
"trailing_warmup_days_override": 2,
"shadow_only": false
}
],
"splits": [],
"tags": ["pead", "midcap", "step63", "short_core", "macro_block", "crashcap", "gap10", "longtrend12", "sdlong25", "max4", "acsgap12"],
"notes": null
}

@ -0,0 +1,82 @@
{
"experiment_name": "pead_midcap_step64_short_core_macro_block_crashcap_gap10_interleave_max4_acsgap10",
"dataset_snapshot_id": "midcap-filtered",
"description": "Step 64: Interleave the max4 mixed-sleeve portfolio while applying a 10% negative-gap gate to after-close short setups.",
"base_config": "configs/backtest/defaults.json",
"overrides": {
"strategy_engine_selection_mode": "interleave",
"event_type_profiles": {
"earnings_release": {"enabled": true, "direction_filter": "any", "max_holding_days_override": 7},
"guidance_update": {"enabled": false},
"management_change": {"enabled": false},
"material_contract": {"enabled": false},
"unknown": {"enabled": false},
"other_material_event": {"enabled": false}
},
"signal": {
"scoring_model": "pead",
"pead_reaction_threshold": 0.10,
"pead_volume_threshold": 2.0,
"score_threshold": 0.65,
"max_candidates_per_day": 4
},
"execution": {
"max_holding_days": 7,
"target_1_fraction": 1.0
},
"risk": {
"max_positions": 8,
"max_positions_per_sector": 8,
"max_daily_new_risk_pct": 0.04,
"cooldown_after_loss_streak": 0,
"cooldown_days": 0,
"veto_oneoff_penalty": 1.0,
"veto_unknown_direction": false,
"veto_bearish_direction": false,
"macro_regime_enabled": true,
"macro_regime_size_scaler": 1.0
}
},
"strategy_engines": [
{
"engine_id": "earnings_same_day_short_step14_capped",
"event_types": ["earnings_release"],
"timing_class": "same_day",
"direction": "short_only",
"entry_timing_policy": "next_open",
"max_holding_days": 7,
"engine_risk_budget_pct": 1.0,
"reaction_day_return_min": -0.45,
"shadow_only": false
},
{
"engine_id": "earnings_after_close_short_core_gap10",
"event_types": ["earnings_release"],
"timing_class": "after_close",
"direction": "short_only",
"entry_timing_policy": "next_open",
"max_holding_days": 7,
"engine_risk_budget_pct": 0.25,
"gap_size_max": -0.10,
"shadow_only": false
},
{
"engine_id": "earnings_same_day_long_close12_gap10_trend",
"event_types": ["earnings_release"],
"timing_class": "same_day",
"direction": "long_only",
"entry_timing_policy": "reaction_close",
"max_holding_days": 12,
"engine_risk_budget_pct": 0.25,
"gap_size_min": 0.10,
"target_atr_multiplier_override": 2.5,
"target_1_fraction_override": 0.33,
"trailing_model_override": "pct_10",
"trailing_warmup_days_override": 2,
"shadow_only": false
}
],
"splits": [],
"tags": ["pead", "midcap", "step64", "short_core", "macro_block", "crashcap", "gap10", "interleave", "max4", "acsgap10"],
"notes": null
}

@ -0,0 +1,82 @@
{
"experiment_name": "pead_midcap_step65_short_core_macro_block_crashcap_gap10_interleave_max4_acsgap12",
"dataset_snapshot_id": "midcap-filtered",
"description": "Step 65: Interleaved max4 mixed-sleeve portfolio with a stricter 12% negative-gap gate on after-close shorts.",
"base_config": "configs/backtest/defaults.json",
"overrides": {
"strategy_engine_selection_mode": "interleave",
"event_type_profiles": {
"earnings_release": {"enabled": true, "direction_filter": "any", "max_holding_days_override": 7},
"guidance_update": {"enabled": false},
"management_change": {"enabled": false},
"material_contract": {"enabled": false},
"unknown": {"enabled": false},
"other_material_event": {"enabled": false}
},
"signal": {
"scoring_model": "pead",
"pead_reaction_threshold": 0.10,
"pead_volume_threshold": 2.0,
"score_threshold": 0.65,
"max_candidates_per_day": 4
},
"execution": {
"max_holding_days": 7,
"target_1_fraction": 1.0
},
"risk": {
"max_positions": 8,
"max_positions_per_sector": 8,
"max_daily_new_risk_pct": 0.04,
"cooldown_after_loss_streak": 0,
"cooldown_days": 0,
"veto_oneoff_penalty": 1.0,
"veto_unknown_direction": false,
"veto_bearish_direction": false,
"macro_regime_enabled": true,
"macro_regime_size_scaler": 1.0
}
},
"strategy_engines": [
{
"engine_id": "earnings_same_day_short_step14_capped",
"event_types": ["earnings_release"],
"timing_class": "same_day",
"direction": "short_only",
"entry_timing_policy": "next_open",
"max_holding_days": 7,
"engine_risk_budget_pct": 1.0,
"reaction_day_return_min": -0.45,
"shadow_only": false
},
{
"engine_id": "earnings_after_close_short_core_gap12",
"event_types": ["earnings_release"],
"timing_class": "after_close",
"direction": "short_only",
"entry_timing_policy": "next_open",
"max_holding_days": 7,
"engine_risk_budget_pct": 0.25,
"gap_size_max": -0.12,
"shadow_only": false
},
{
"engine_id": "earnings_same_day_long_close12_gap10_trend",
"event_types": ["earnings_release"],
"timing_class": "same_day",
"direction": "long_only",
"entry_timing_policy": "reaction_close",
"max_holding_days": 12,
"engine_risk_budget_pct": 0.25,
"gap_size_min": 0.10,
"target_atr_multiplier_override": 2.5,
"target_1_fraction_override": 0.33,
"trailing_model_override": "pct_10",
"trailing_warmup_days_override": 2,
"shadow_only": false
}
],
"splits": [],
"tags": ["pead", "midcap", "step65", "short_core", "macro_block", "crashcap", "gap10", "interleave", "max4", "acsgap12"],
"notes": null
}

@ -0,0 +1,83 @@
{
"experiment_name": "pead_midcap_step66_short_core_macro_block_crashcap_gap10_longtrend12_sdlong25_max4_acsgap10_react12",
"dataset_snapshot_id": "midcap-filtered",
"description": "Step 66: Step62 plus a minimum after-close downside reaction of 12%, keeping only deeper negative gap and reaction combinations in the filtered short sleeve.",
"base_config": "configs/backtest/defaults.json",
"overrides": {
"strategy_engine_selection_mode": "global_score",
"event_type_profiles": {
"earnings_release": {"enabled": true, "direction_filter": "any", "max_holding_days_override": 7},
"guidance_update": {"enabled": false},
"management_change": {"enabled": false},
"material_contract": {"enabled": false},
"unknown": {"enabled": false},
"other_material_event": {"enabled": false}
},
"signal": {
"scoring_model": "pead",
"pead_reaction_threshold": 0.10,
"pead_volume_threshold": 2.0,
"score_threshold": 0.65,
"max_candidates_per_day": 4
},
"execution": {
"max_holding_days": 7,
"target_1_fraction": 1.0
},
"risk": {
"max_positions": 8,
"max_positions_per_sector": 8,
"max_daily_new_risk_pct": 0.04,
"cooldown_after_loss_streak": 0,
"cooldown_days": 0,
"veto_oneoff_penalty": 1.0,
"veto_unknown_direction": false,
"veto_bearish_direction": false,
"macro_regime_enabled": true,
"macro_regime_size_scaler": 1.0
}
},
"strategy_engines": [
{
"engine_id": "earnings_same_day_short_step14_capped",
"event_types": ["earnings_release"],
"timing_class": "same_day",
"direction": "short_only",
"entry_timing_policy": "next_open",
"max_holding_days": 7,
"engine_risk_budget_pct": 1.0,
"reaction_day_return_min": -0.45,
"shadow_only": false
},
{
"engine_id": "earnings_after_close_short_core_gap10_react12",
"event_types": ["earnings_release"],
"timing_class": "after_close",
"direction": "short_only",
"entry_timing_policy": "next_open",
"max_holding_days": 7,
"engine_risk_budget_pct": 0.25,
"reaction_day_return_max": -0.12,
"gap_size_max": -0.10,
"shadow_only": false
},
{
"engine_id": "earnings_same_day_long_close12_gap10_trend",
"event_types": ["earnings_release"],
"timing_class": "same_day",
"direction": "long_only",
"entry_timing_policy": "reaction_close",
"max_holding_days": 12,
"engine_risk_budget_pct": 0.25,
"gap_size_min": 0.10,
"target_atr_multiplier_override": 2.5,
"target_1_fraction_override": 0.33,
"trailing_model_override": "pct_10",
"trailing_warmup_days_override": 2,
"shadow_only": false
}
],
"splits": [],
"tags": ["pead", "midcap", "step66", "short_core", "macro_block", "crashcap", "gap10", "longtrend12", "sdlong25", "max4", "acsgap10", "react12"],
"notes": null
}

@ -0,0 +1,83 @@
{
"experiment_name": "pead_midcap_step67_short_core_macro_block_crashcap_gap10_longtrend12_sdlong25_max4_acsgap10_react14",
"dataset_snapshot_id": "midcap-filtered",
"description": "Step 67: Step62 plus a stricter 14% downside reaction requirement for after-close shorts, testing whether only the sharpest downside follow-through setups should remain.",
"base_config": "configs/backtest/defaults.json",
"overrides": {
"strategy_engine_selection_mode": "global_score",
"event_type_profiles": {
"earnings_release": {"enabled": true, "direction_filter": "any", "max_holding_days_override": 7},
"guidance_update": {"enabled": false},
"management_change": {"enabled": false},
"material_contract": {"enabled": false},
"unknown": {"enabled": false},
"other_material_event": {"enabled": false}
},
"signal": {
"scoring_model": "pead",
"pead_reaction_threshold": 0.10,
"pead_volume_threshold": 2.0,
"score_threshold": 0.65,
"max_candidates_per_day": 4
},
"execution": {
"max_holding_days": 7,
"target_1_fraction": 1.0
},
"risk": {
"max_positions": 8,
"max_positions_per_sector": 8,
"max_daily_new_risk_pct": 0.04,
"cooldown_after_loss_streak": 0,
"cooldown_days": 0,
"veto_oneoff_penalty": 1.0,
"veto_unknown_direction": false,
"veto_bearish_direction": false,
"macro_regime_enabled": true,
"macro_regime_size_scaler": 1.0
}
},
"strategy_engines": [
{
"engine_id": "earnings_same_day_short_step14_capped",
"event_types": ["earnings_release"],
"timing_class": "same_day",
"direction": "short_only",
"entry_timing_policy": "next_open",
"max_holding_days": 7,
"engine_risk_budget_pct": 1.0,
"reaction_day_return_min": -0.45,
"shadow_only": false
},
{
"engine_id": "earnings_after_close_short_core_gap10_react14",
"event_types": ["earnings_release"],
"timing_class": "after_close",
"direction": "short_only",
"entry_timing_policy": "next_open",
"max_holding_days": 7,
"engine_risk_budget_pct": 0.25,
"reaction_day_return_max": -0.14,
"gap_size_max": -0.10,
"shadow_only": false
},
{
"engine_id": "earnings_same_day_long_close12_gap10_trend",
"event_types": ["earnings_release"],
"timing_class": "same_day",
"direction": "long_only",
"entry_timing_policy": "reaction_close",
"max_holding_days": 12,
"engine_risk_budget_pct": 0.25,
"gap_size_min": 0.10,
"target_atr_multiplier_override": 2.5,
"target_1_fraction_override": 0.33,
"trailing_model_override": "pct_10",
"trailing_warmup_days_override": 2,
"shadow_only": false
}
],
"splits": [],
"tags": ["pead", "midcap", "step67", "short_core", "macro_block", "crashcap", "gap10", "longtrend12", "sdlong25", "max4", "acsgap10", "react14"],
"notes": null
}

@ -1,84 +1,98 @@
# Strategy Improvement Leaderboard
_Updated: 2026-03-17T09:17:27.726326+00:00_
_Updated: 2026-03-17T10:37:12.332837+00:00_
| # | Experiment | SQS | [T]PF | [T]Ret% | [T]WR | [T]Sharpe | [T]DD% | [T]N | [T]Gross% | [T]Net% | [T]DIM% | [V]PF | [V]Ret% | [V]WR | [V]Sharpe | [V]DD% | [V]N | [V]Gross% | [V]Net% | [V]DIM% | Date |
|---|-----------|-----|-------|---------|-------|-----------|--------|------|-----------|---------|---------|-------|---------|-------|-----------|--------|------|-----------|---------|---------|------|
| 1 | pead_midcap_step52_short_core_macro_block_crashcap_gap10 | 49.8 | 3.35 | +1.0 | 77% | 3.0 | 0.2 | 22 | 2.9 | -2.2 | 53.2 | 5.66 | +1.8 | 72% | 4.8 | 0.2 | 25 | 3.6 | -2.6 | 56.1 | 2026-03-17 |
| 2 | pead_midcap_step48_short_core_macro_block_nolong | 46.9 | 3.40 | +0.8 | 80% | 2.4 | 0.4 | 20 | 2.6 | +2.6 | 52.2 | 5.73 | +1.3 | 70% | 3.4 | 0.3 | 20 | 3.3 | +3.3 | 51.8 | 2026-03-17 |
| 3 | pead_midcap_step46_short_core_macro_block_acshort12 | 45.8 | 4.52 | +1.3 | 71% | 3.6 | 0.3 | 21 | 2.1 | +2.1 | 40.4 | 2.44 | +1.3 | 69% | 3.2 | 0.4 | 26 | 4.0 | +4.0 | 57.9 | 2026-03-17 |
| 4 | pead_midcap_step36_balanced_sleeves_nofrac_aclong12_sdlong12 | 44.5 | 2.06 | +2.1 | 61% | 4.5 | 0.4 | 54 | 6.0 | +6.0 | 76.6 | - | - | - | - | - | 0 | - | - | - | 2026-03-17 |
| 5 | pead_midcap_step51_short_core_macro_block_crashcap | 44.4 | 4.19 | +1.2 | 73% | 3.6 | 0.2 | 22 | 2.3 | -0.8 | 42.6 | 2.31 | +1.3 | 68% | 3.0 | 0.4 | 28 | 4.1 | -1.9 | 57.9 | 2026-03-17 |
| 6 | pead_midcap_step45_short_core_macro_block | 44.3 | 3.78 | +1.2 | 70% | 3.4 | 0.3 | 23 | 2.4 | +2.4 | 42.6 | 2.31 | +1.3 | 68% | 3.0 | 0.4 | 28 | 4.2 | +4.2 | 57.9 | 2026-03-17 |
| 7 | pead_midcap_step53_short_core_macro_block_crashcap_gap14 | 39.5 | 4.60 | +1.1 | 84% | 3.5 | 0.2 | 19 | 2.3 | +2.3 | 53.2 | 8.21 | +2.0 | 76% | 5.3 | 0.2 | 25 | 3.6 | +3.6 | 56.1 | 2026-03-17 |
| 8 | pead_midcap_step50_same_day_short_long_macro_block | 36.2 | 3.21 | +0.8 | 60% | 2.7 | 0.4 | 15 | 1.6 | +1.6 | 38.3 | 2.21 | +1.0 | 65% | 2.7 | 0.3 | 20 | 3.4 | +3.4 | 43.9 | 2026-03-17 |
| 9 | pead_midcap_step30_balanced_sleeves_nofrac | 35.4 | 1.41 | +1.4 | 54% | 2.8 | 0.8 | 67 | 8.0 | +8.0 | 76.6 | - | - | - | - | - | 0 | - | - | - | 2026-03-17 |
| 10 | pead_midcap_step47_short_core_macro_block_sdlong25 | 35.2 | 3.43 | +1.3 | 68% | 3.3 | 0.3 | 25 | 2.9 | +2.9 | 48.9 | 1.77 | +1.0 | 65% | 2.2 | 0.4 | 31 | 4.6 | +4.6 | 57.9 | 2026-03-17 |
| 11 | pead_midcap_step44_short_core_macro50 | 35.0 | 2.01 | +1.3 | 57% | 2.6 | 0.7 | 58 | 4.5 | -1.6 | 72.3 | 1.78 | +1.1 | 55% | 2.5 | 0.4 | 40 | 4.8 | -2.0 | 73.7 | 2026-03-17 |
| 12 | pead_midcap_step33_balanced_sleeves_nofrac_acshort6 | 35.0 | 1.47 | +1.2 | 52% | 2.3 | 0.7 | 48 | 6.2 | +6.2 | 70.2 | - | - | - | - | - | 0 | - | - | - | 2026-03-17 |
| 13 | pead_midcap_step43_short_core_sdlong25 | 31.7 | 1.66 | +1.5 | 57% | 2.0 | 1.3 | 58 | 7.1 | +7.1 | 72.3 | 1.46 | +1.0 | 56% | 1.9 | 0.6 | 45 | 6.2 | +6.2 | 73.7 | 2026-03-17 |
| 14 | pead_midcap_step41_short_core_sdlong12_acshort50 | 30.7 | 1.53 | +1.2 | 56% | 1.6 | 1.3 | 59 | 6.8 | +6.8 | 72.3 | 1.48 | +0.9 | 55% | 1.8 | 0.6 | 40 | 5.6 | +5.6 | 73.7 | 2026-03-17 |
| 15 | pead_midcap_step37_balanced_sleeves_aclong_vol3 | 30.6 | 1.38 | +1.2 | 54% | 2.5 | 0.8 | 63 | 7.5 | +7.5 | 76.6 | 1.38 | +1.1 | 51% | 1.8 | 0.9 | 57 | 7.9 | +7.9 | 77.2 | 2026-03-17 |
| 16 | pead_midcap_step42_short_core_only | 30.5 | 1.44 | +0.8 | 63% | 1.2 | 1.2 | 46 | 5.5 | +5.5 | 71.7 | 2.55 | +1.2 | 58% | 2.8 | 0.5 | 31 | 4.5 | +4.5 | 71.4 | 2026-03-17 |
| 17 | pead_midcap_step14_score65 | 29.9 | 1.22 | +0.7 | 57% | 1.3 | 0.9 | 72 | - | - | - | 1.40 | +1.0 | 56% | 1.6 | 0.7 | 66 | - | - | - | 2026-03-17 |
| 18 | pead_midcap_step18_nofrac | 29.4 | 1.23 | +0.8 | 52% | 1.4 | 0.9 | 64 | - | - | - | 1.46 | +1.2 | 47% | 1.9 | 0.6 | 55 | - | - | - | 2026-03-17 |
| 19 | pead_midcap_step31_balanced_sleeves_nofrac_acshort12 | 29.3 | 1.49 | +1.6 | 55% | 3.2 | 0.7 | 64 | 7.6 | +7.6 | 76.6 | 1.38 | +1.1 | 52% | 1.8 | 0.9 | 58 | 8.0 | +8.0 | 77.2 | 2026-03-17 |
| 20 | pead_midcap_step19_hold5 | 29.2 | 1.20 | +0.7 | 57% | 1.2 | 0.9 | 72 | - | - | - | 1.55 | +1.4 | 57% | 2.2 | 0.7 | 68 | - | - | - | 2026-03-17 |
| 21 | pead_midcap_step49_same_day_short_macro_block | 29.1 | 2.40 | +0.4 | 70% | 1.6 | 0.4 | 10 | 1.0 | +1.0 | 26.1 | 25.09 | +1.2 | 75% | 3.2 | 0.3 | 12 | 2.6 | +2.6 | 32.1 | 2026-03-17 |
| 22 | pead_midcap_step20_best3 | 28.7 | 1.22 | +0.7 | 52% | 1.3 | 0.9 | 64 | - | - | - | 1.61 | +1.6 | 48% | 2.5 | 0.6 | 56 | - | - | - | 2026-03-17 |
| 23 | pead_midcap_step40_short_core_sdlong12 | 28.6 | 1.77 | +1.5 | 58% | 2.2 | 1.1 | 55 | 6.4 | +6.4 | 72.3 | 1.48 | +0.9 | 55% | 1.8 | 0.6 | 40 | 5.6 | +5.6 | 73.7 | 2026-03-17 |
| 24 | pead_midcap_step17_target2 | 27.3 | 1.18 | +0.6 | 53% | 1.1 | 0.8 | 66 | - | - | - | 1.38 | +1.0 | 51% | 1.5 | 0.7 | 59 | - | - | - | 2026-03-17 |
| 25 | pead_midcap_step39_balanced_sleeves_sdlong12 | 27.0 | 1.50 | +1.5 | 55% | 3.4 | 0.5 | 62 | 7.4 | +7.4 | 76.6 | 1.38 | +1.0 | 51% | 1.7 | 0.9 | 53 | 7.5 | +7.5 | 77.2 | 2026-03-17 |
| 26 | pead_midcap_step27_sdlong_close7_budget25 | 26.4 | 1.17 | +0.6 | 54% | 0.9 | 1.3 | 68 | 8.5 | +8.5 | 72.3 | - | - | - | - | - | 0 | - | - | - | 2026-03-17 |
| 27 | pead_midcap_step13_best | 26.3 | 1.12 | +0.4 | 55% | 0.8 | 0.9 | 75 | - | - | - | 1.61 | +1.6 | 59% | 2.2 | 0.7 | 70 | - | - | - | 2026-03-16 |
| 28 | pead_midcap_step23_sdlong_close7 | 24.9 | 1.13 | +0.5 | 54% | 0.7 | 1.4 | 69 | 8.6 | +8.6 | 72.3 | - | - | - | - | - | 0 | - | - | - | 2026-03-17 |
| 29 | pead_midcap_step34_balanced_sleeves_nofrac_aclong25 | 23.9 | 1.62 | +1.8 | 56% | 3.8 | 0.6 | 62 | 7.4 | +7.4 | 76.6 | 1.30 | +0.9 | 51% | 1.4 | 0.9 | 57 | 7.9 | +7.9 | 77.2 | 2026-03-17 |
| 30 | pead_midcap_step16_react7_score65 | 23.8 | 0.97 | -0.1 | 56% | -0.2 | 1.6 | 89 | - | - | - | 2.01 | +2.8 | 63% | 3.7 | 0.8 | 83 | - | - | - | 2026-03-17 |
| 31 | pead_midcap_step5_maxcand3 | 23.4 | 0.95 | -0.2 | 54% | -0.4 | 1.4 | 96 | - | - | - | 2.08 | +3.3 | 65% | 4.0 | 1.1 | 89 | - | - | - | 2026-03-16 |
| 32 | pead_midcap_step38_balanced_sleeves_aclong_vol4 | 23.3 | 1.12 | +0.4 | 51% | 0.8 | 0.9 | 61 | 7.4 | +7.4 | 76.6 | 1.29 | +0.8 | 53% | 1.4 | 0.9 | 53 | 7.6 | +7.6 | 77.2 | 2026-03-17 |
| 33 | pead_midcap_step15_react7 | 23.0 | 0.94 | -0.3 | 55% | -0.5 | 1.6 | 91 | - | - | - | 1.89 | +2.6 | 62% | 3.5 | 0.9 | 84 | - | - | - | 2026-03-17 |
| 34 | pead_midcap_portfolio_v2 | 23.0 | 1.08 | +0.3 | 51% | 0.5 | 1.8 | 70 | 7.9 | +7.9 | 72.3 | - | - | - | - | - | 0 | - | - | - | 2026-03-17 |
| 35 | pead_midcap_step11_score60 | 22.1 | 1.02 | +0.1 | 52% | 0.2 | 0.9 | 77 | - | - | - | 1.91 | +2.3 | 62% | 3.0 | 0.7 | 72 | - | - | - | 2026-03-16 |
| 36 | pead_midcap_step12_vol2x | 22.1 | 1.02 | +0.1 | 52% | 0.1 | 0.9 | 77 | - | - | - | 1.91 | +2.3 | 62% | 3.0 | 0.7 | 72 | - | - | - | 2026-03-16 |
| 37 | pead_midcap_step3_10pct | 21.9 | 1.00 | +0.0 | 50% | 0.0 | 1.4 | 98 | - | - | - | 1.66 | +2.0 | 60% | 2.9 | 0.7 | 78 | - | - | - | 2026-03-16 |
| 38 | pead_midcap_step10_short | 21.5 | 1.00 | -0.0 | 52% | -0.0 | 0.9 | 79 | - | - | - | 1.61 | +1.6 | 59% | 2.2 | 0.7 | 70 | - | - | - | 2026-03-16 |
| 39 | pead_midcap_step35_balanced_sleeves_nofrac_aclong12 | 21.2 | 2.01 | +2.2 | 61% | 4.2 | 0.4 | 56 | 6.2 | +1.1 | 76.6 | 1.24 | +0.7 | 51% | 1.2 | 0.9 | 53 | 7.5 | +1.6 | 77.2 | 2026-03-17 |
| 40 | pead_midcap_step2_notrail | 17.2 | 0.93 | -0.3 | 67% | -0.4 | 1.7 | 54 | - | - | - | 1.07 | +0.4 | 73% | 0.4 | 1.7 | 62 | - | - | - | 2026-03-16 |
| 41 | pead_midcap_step1_fixedr | 16.2 | 0.91 | -0.5 | 47% | -0.7 | 1.5 | 95 | - | - | - | 1.77 | +2.8 | 49% | 3.4 | 1.6 | 69 | - | - | - | 2026-03-16 |
| 42 | pead_midcap_combo_10pct_maxcand3 | 15.9 | 0.97 | -0.1 | 51% | -0.2 | 0.9 | 79 | - | - | - | 1.91 | +2.3 | 62% | 3.0 | 0.7 | 72 | - | - | - | 2026-03-16 |
| 43 | pead_midcap_step6_drift | 14.7 | 0.86 | -0.9 | 43% | -1.6 | 1.7 | 100 | - | - | - | 1.70 | +2.7 | 51% | 3.7 | 1.0 | 75 | - | - | - | 2026-03-16 |
| 44 | pead_midcap_step7_fixedr | 13.8 | 0.94 | -0.2 | 45% | -0.4 | 1.0 | 71 | - | - | - | 2.00 | +2.4 | 53% | 3.1 | 0.7 | 53 | - | - | - | 2026-03-16 |
| 45 | pead_midcap_step4_longonly | 12.2 | 0.84 | -0.8 | 45% | -1.2 | 1.4 | 78 | - | - | - | 1.43 | +1.5 | 61% | 1.7 | 1.6 | 76 | - | - | - | 2026-03-16 |
| 46 | pead_midcap_step8_nft | 11.5 | 0.65 | -1.8 | 41% | -3.0 | 2.2 | 71 | - | - | - | 1.55 | +1.8 | 46% | 2.4 | 1.0 | 57 | - | - | - | 2026-03-16 |
| 47 | pead_midcap_step9_stop2 | 11.4 | 0.77 | -1.7 | 45% | -1.8 | 2.5 | 71 | - | - | - | 1.81 | +3.2 | 53% | 2.8 | 1.2 | 53 | - | - | - | 2026-03-16 |
| 1 | pead_midcap_step56_short_core_macro_block_crashcap_gap10_interleave_longtrend25 | 51.3 | 4.03 | +1.1 | 80% | 3.4 | 0.2 | 20 | 2.5 | -1.5 | 51.1 | 4.82 | +1.9 | 68% | 4.0 | 0.6 | 28 | 4.2 | -2.7 | 56.1 | 2026-03-17 |
| 2 | pead_midcap_step62_short_core_macro_block_crashcap_gap10_longtrend12_sdlong25_max4_acsgap10 | 50.9 | 3.69 | +1.0 | 80% | 3.4 | 0.2 | 20 | 2.5 | -1.5 | 48.9 | 4.64 | +1.8 | 69% | 4.5 | 0.4 | 26 | 3.5 | -2.0 | 49.1 | 2026-03-17 |
| 3 | pead_midcap_step66_short_core_macro_block_crashcap_gap10_longtrend12_sdlong25_max4_acsgap10_react12 | 50.9 | 3.69 | +1.0 | 80% | 3.4 | 0.2 | 20 | 2.5 | -1.5 | 48.9 | 4.64 | +1.8 | 69% | 4.5 | 0.4 | 26 | 3.5 | -2.0 | 49.1 | 2026-03-17 |
| 4 | pead_midcap_step64_short_core_macro_block_crashcap_gap10_interleave_max4_acsgap10 | 50.6 | 3.69 | +1.0 | 80% | 3.4 | 0.2 | 20 | 2.5 | -1.5 | 48.9 | 4.33 | +1.7 | 65% | 3.7 | 0.6 | 26 | 3.8 | -2.3 | 49.1 | 2026-03-17 |
| 5 | pead_midcap_step55_short_core_macro_block_crashcap_gap10_longtrend12_sdlong25 | 49.8 | 3.40 | +1.0 | 78% | 3.1 | 0.2 | 23 | 3.0 | -2.0 | 53.2 | 4.62 | +1.9 | 71% | 4.5 | 0.3 | 28 | 3.8 | -2.4 | 56.1 | 2026-03-17 |
| 6 | pead_midcap_step52_short_core_macro_block_crashcap_gap10 | 49.8 | 3.35 | +1.0 | 77% | 3.0 | 0.2 | 22 | 2.9 | -2.2 | 53.2 | 5.66 | +1.8 | 72% | 4.8 | 0.2 | 25 | 3.6 | -2.6 | 56.1 | 2026-03-17 |
| 7 | pead_midcap_step48_short_core_macro_block_nolong | 46.9 | 3.40 | +0.8 | 80% | 2.4 | 0.4 | 20 | 2.6 | +2.6 | 52.2 | 5.73 | +1.3 | 70% | 3.4 | 0.3 | 20 | 3.3 | +3.3 | 51.8 | 2026-03-17 |
| 8 | pead_midcap_step58_short_core_macro_block_crashcap_gap7_longtrend12_sdlong25 | 46.6 | 2.76 | +0.9 | 74% | 2.8 | 0.2 | 23 | 3.2 | -1.3 | 53.2 | 2.89 | +1.6 | 69% | 3.6 | 0.5 | 29 | 4.0 | -2.0 | 56.1 | 2026-03-17 |
| 9 | pead_midcap_step54_short_core_macro_block_crashcap_gap10_longtrend12 | 46.5 | 2.99 | +0.9 | 77% | 2.5 | 0.3 | 22 | 2.9 | -2.2 | 53.2 | 6.10 | +2.0 | 73% | 4.7 | 0.2 | 26 | 3.6 | -2.6 | 56.1 | 2026-03-17 |
| 10 | pead_midcap_step57_short_core_macro_block_crashcap_gap10_longtrend12_sdlong25_max4 | 46.5 | 2.66 | +0.9 | 75% | 2.9 | 0.2 | 24 | 3.2 | -2.2 | 53.2 | 4.44 | +1.9 | 69% | 4.5 | 0.3 | 29 | 3.9 | -2.5 | 56.1 | 2026-03-17 |
| 11 | pead_midcap_step46_short_core_macro_block_acshort12 | 45.8 | 4.52 | +1.3 | 71% | 3.6 | 0.3 | 21 | 2.1 | +2.1 | 40.4 | 2.44 | +1.3 | 69% | 3.2 | 0.4 | 26 | 4.0 | +4.0 | 57.9 | 2026-03-17 |
| 12 | pead_midcap_step36_balanced_sleeves_nofrac_aclong12_sdlong12 | 44.5 | 2.06 | +2.1 | 61% | 4.5 | 0.4 | 54 | 6.0 | +6.0 | 76.6 | - | - | - | - | - | 0 | - | - | - | 2026-03-17 |
| 13 | pead_midcap_step51_short_core_macro_block_crashcap | 44.4 | 4.19 | +1.2 | 73% | 3.6 | 0.2 | 22 | 2.3 | -0.8 | 42.6 | 2.31 | +1.3 | 68% | 3.0 | 0.4 | 28 | 4.1 | -1.9 | 57.9 | 2026-03-17 |
| 14 | pead_midcap_step45_short_core_macro_block | 44.3 | 3.78 | +1.2 | 70% | 3.4 | 0.3 | 23 | 2.4 | +2.4 | 42.6 | 2.31 | +1.3 | 68% | 3.0 | 0.4 | 28 | 4.2 | +4.2 | 57.9 | 2026-03-17 |
| 15 | pead_midcap_step53_short_core_macro_block_crashcap_gap14 | 39.5 | 4.60 | +1.1 | 84% | 3.5 | 0.2 | 19 | 2.3 | +2.3 | 53.2 | 8.21 | +2.0 | 76% | 5.3 | 0.2 | 25 | 3.6 | +3.6 | 56.1 | 2026-03-17 |
| 16 | pead_midcap_step65_short_core_macro_block_crashcap_gap10_interleave_max4_acsgap12 | 38.3 | 3.60 | +0.9 | 79% | 3.2 | 0.2 | 19 | 1.9 | -0.9 | 38.3 | 4.56 | +1.5 | 67% | 4.0 | 0.5 | 24 | 3.4 | -1.9 | 47.4 | 2026-03-17 |
| 17 | pead_midcap_step63_short_core_macro_block_crashcap_gap10_longtrend12_sdlong25_max4_acsgap12 | 38.2 | 3.60 | +0.9 | 79% | 3.2 | 0.2 | 19 | 1.9 | -0.9 | 38.3 | 5.27 | +1.8 | 71% | 4.9 | 0.3 | 24 | 3.1 | -1.6 | 45.6 | 2026-03-17 |
| 18 | pead_midcap_step67_short_core_macro_block_crashcap_gap10_longtrend12_sdlong25_max4_acsgap10_react14 | 38.2 | 3.60 | +0.9 | 79% | 3.2 | 0.2 | 19 | 1.9 | -0.9 | 38.3 | 5.33 | +1.8 | 72% | 4.4 | 0.5 | 25 | 3.2 | -1.7 | 47.4 | 2026-03-17 |
| 19 | pead_midcap_step50_same_day_short_long_macro_block | 36.2 | 3.21 | +0.8 | 60% | 2.7 | 0.4 | 15 | 1.6 | +1.6 | 38.3 | 2.21 | +1.0 | 65% | 2.7 | 0.3 | 20 | 3.4 | +3.4 | 43.9 | 2026-03-17 |
| 20 | pead_midcap_step30_balanced_sleeves_nofrac | 35.4 | 1.41 | +1.4 | 54% | 2.8 | 0.8 | 67 | 8.0 | +8.0 | 76.6 | - | - | - | - | - | 0 | - | - | - | 2026-03-17 |
| 21 | pead_midcap_step47_short_core_macro_block_sdlong25 | 35.2 | 3.43 | +1.3 | 68% | 3.3 | 0.3 | 25 | 2.9 | +2.9 | 48.9 | 1.77 | +1.0 | 65% | 2.2 | 0.4 | 31 | 4.6 | +4.6 | 57.9 | 2026-03-17 |
| 22 | pead_midcap_step44_short_core_macro50 | 35.0 | 2.01 | +1.3 | 57% | 2.6 | 0.7 | 58 | 4.5 | -1.6 | 72.3 | 1.78 | +1.1 | 55% | 2.5 | 0.4 | 40 | 4.8 | -2.0 | 73.7 | 2026-03-17 |
| 23 | pead_midcap_step33_balanced_sleeves_nofrac_acshort6 | 35.0 | 1.47 | +1.2 | 52% | 2.3 | 0.7 | 48 | 6.2 | +6.2 | 70.2 | - | - | - | - | - | 0 | - | - | - | 2026-03-17 |
| 24 | pead_midcap_step59_same_day_only_max4_longtrend25 | 33.2 | 2.61 | +0.6 | 69% | 2.3 | 0.3 | 13 | 1.4 | -0.5 | 31.9 | 6.63 | +1.9 | 73% | 4.5 | 0.4 | 22 | 3.9 | -2.4 | 42.1 | 2026-03-17 |
| 25 | pead_midcap_step60_same_day_only_interleave_max4_longtrend25 | 33.2 | 2.61 | +0.6 | 69% | 2.3 | 0.3 | 13 | 1.4 | -0.5 | 31.9 | 6.63 | +1.9 | 73% | 4.5 | 0.4 | 22 | 3.9 | -2.4 | 42.1 | 2026-03-17 |
| 26 | pead_midcap_step61_same_day_only_max5_longtrend25 | 33.2 | 2.61 | +0.6 | 69% | 2.3 | 0.3 | 13 | 1.4 | -0.5 | 31.9 | 6.63 | +1.9 | 73% | 4.5 | 0.4 | 22 | 3.9 | -2.4 | 42.1 | 2026-03-17 |
| 27 | pead_midcap_step43_short_core_sdlong25 | 31.7 | 1.66 | +1.5 | 57% | 2.0 | 1.3 | 58 | 7.1 | +7.1 | 72.3 | 1.46 | +1.0 | 56% | 1.9 | 0.6 | 45 | 6.2 | +6.2 | 73.7 | 2026-03-17 |
| 28 | pead_midcap_step41_short_core_sdlong12_acshort50 | 30.7 | 1.53 | +1.2 | 56% | 1.6 | 1.3 | 59 | 6.8 | +6.8 | 72.3 | 1.48 | +0.9 | 55% | 1.8 | 0.6 | 40 | 5.6 | +5.6 | 73.7 | 2026-03-17 |
| 29 | pead_midcap_step37_balanced_sleeves_aclong_vol3 | 30.6 | 1.38 | +1.2 | 54% | 2.5 | 0.8 | 63 | 7.5 | +7.5 | 76.6 | 1.38 | +1.1 | 51% | 1.8 | 0.9 | 57 | 7.9 | +7.9 | 77.2 | 2026-03-17 |
| 30 | pead_midcap_step42_short_core_only | 30.5 | 1.44 | +0.8 | 63% | 1.2 | 1.2 | 46 | 5.5 | +5.5 | 71.7 | 2.55 | +1.2 | 58% | 2.8 | 0.5 | 31 | 4.5 | +4.5 | 71.4 | 2026-03-17 |
| 31 | pead_midcap_step14_score65 | 29.9 | 1.22 | +0.7 | 57% | 1.3 | 0.9 | 72 | - | - | - | 1.40 | +1.0 | 56% | 1.6 | 0.7 | 66 | - | - | - | 2026-03-17 |
| 32 | pead_midcap_step18_nofrac | 29.4 | 1.23 | +0.8 | 52% | 1.4 | 0.9 | 64 | - | - | - | 1.46 | +1.2 | 47% | 1.9 | 0.6 | 55 | - | - | - | 2026-03-17 |
| 33 | pead_midcap_step31_balanced_sleeves_nofrac_acshort12 | 29.3 | 1.49 | +1.6 | 55% | 3.2 | 0.7 | 64 | 7.6 | +7.6 | 76.6 | 1.38 | +1.1 | 52% | 1.8 | 0.9 | 58 | 8.0 | +8.0 | 77.2 | 2026-03-17 |
| 34 | pead_midcap_step19_hold5 | 29.2 | 1.20 | +0.7 | 57% | 1.2 | 0.9 | 72 | - | - | - | 1.55 | +1.4 | 57% | 2.2 | 0.7 | 68 | - | - | - | 2026-03-17 |
| 35 | pead_midcap_step49_same_day_short_macro_block | 29.1 | 2.40 | +0.4 | 70% | 1.6 | 0.4 | 10 | 1.0 | +1.0 | 26.1 | 25.09 | +1.2 | 75% | 3.2 | 0.3 | 12 | 2.6 | +2.6 | 32.1 | 2026-03-17 |
| 36 | pead_midcap_step20_best3 | 28.7 | 1.22 | +0.7 | 52% | 1.3 | 0.9 | 64 | - | - | - | 1.61 | +1.6 | 48% | 2.5 | 0.6 | 56 | - | - | - | 2026-03-17 |
| 37 | pead_midcap_step40_short_core_sdlong12 | 28.6 | 1.77 | +1.5 | 58% | 2.2 | 1.1 | 55 | 6.4 | +6.4 | 72.3 | 1.48 | +0.9 | 55% | 1.8 | 0.6 | 40 | 5.6 | +5.6 | 73.7 | 2026-03-17 |
| 38 | pead_midcap_step17_target2 | 27.3 | 1.18 | +0.6 | 53% | 1.1 | 0.8 | 66 | - | - | - | 1.38 | +1.0 | 51% | 1.5 | 0.7 | 59 | - | - | - | 2026-03-17 |
| 39 | pead_midcap_step39_balanced_sleeves_sdlong12 | 27.0 | 1.50 | +1.5 | 55% | 3.4 | 0.5 | 62 | 7.4 | +7.4 | 76.6 | 1.38 | +1.0 | 51% | 1.7 | 0.9 | 53 | 7.5 | +7.5 | 77.2 | 2026-03-17 |
| 40 | pead_midcap_step27_sdlong_close7_budget25 | 26.4 | 1.17 | +0.6 | 54% | 0.9 | 1.3 | 68 | 8.5 | +8.5 | 72.3 | - | - | - | - | - | 0 | - | - | - | 2026-03-17 |
| 41 | pead_midcap_step13_best | 26.3 | 1.12 | +0.4 | 55% | 0.8 | 0.9 | 75 | - | - | - | 1.61 | +1.6 | 59% | 2.2 | 0.7 | 70 | - | - | - | 2026-03-16 |
| 42 | pead_midcap_step23_sdlong_close7 | 24.9 | 1.13 | +0.5 | 54% | 0.7 | 1.4 | 69 | 8.6 | +8.6 | 72.3 | - | - | - | - | - | 0 | - | - | - | 2026-03-17 |
| 43 | pead_midcap_step34_balanced_sleeves_nofrac_aclong25 | 23.9 | 1.62 | +1.8 | 56% | 3.8 | 0.6 | 62 | 7.4 | +7.4 | 76.6 | 1.30 | +0.9 | 51% | 1.4 | 0.9 | 57 | 7.9 | +7.9 | 77.2 | 2026-03-17 |
| 44 | pead_midcap_step16_react7_score65 | 23.8 | 0.97 | -0.1 | 56% | -0.2 | 1.6 | 89 | - | - | - | 2.01 | +2.8 | 63% | 3.7 | 0.8 | 83 | - | - | - | 2026-03-17 |
| 45 | pead_midcap_step5_maxcand3 | 23.4 | 0.95 | -0.2 | 54% | -0.4 | 1.4 | 96 | - | - | - | 2.08 | +3.3 | 65% | 4.0 | 1.1 | 89 | - | - | - | 2026-03-16 |
| 46 | pead_midcap_step38_balanced_sleeves_aclong_vol4 | 23.3 | 1.12 | +0.4 | 51% | 0.8 | 0.9 | 61 | 7.4 | +7.4 | 76.6 | 1.29 | +0.8 | 53% | 1.4 | 0.9 | 53 | 7.6 | +7.6 | 77.2 | 2026-03-17 |
| 47 | pead_midcap_step15_react7 | 23.0 | 0.94 | -0.3 | 55% | -0.5 | 1.6 | 91 | - | - | - | 1.89 | +2.6 | 62% | 3.5 | 0.9 | 84 | - | - | - | 2026-03-17 |
| 48 | pead_midcap_portfolio_v2 | 23.0 | 1.08 | +0.3 | 51% | 0.5 | 1.8 | 70 | 7.9 | +7.9 | 72.3 | - | - | - | - | - | 0 | - | - | - | 2026-03-17 |
| 49 | pead_midcap_step11_score60 | 22.1 | 1.02 | +0.1 | 52% | 0.2 | 0.9 | 77 | - | - | - | 1.91 | +2.3 | 62% | 3.0 | 0.7 | 72 | - | - | - | 2026-03-16 |
| 50 | pead_midcap_step12_vol2x | 22.1 | 1.02 | +0.1 | 52% | 0.1 | 0.9 | 77 | - | - | - | 1.91 | +2.3 | 62% | 3.0 | 0.7 | 72 | - | - | - | 2026-03-16 |
| 51 | pead_midcap_step3_10pct | 21.9 | 1.00 | +0.0 | 50% | 0.0 | 1.4 | 98 | - | - | - | 1.66 | +2.0 | 60% | 2.9 | 0.7 | 78 | - | - | - | 2026-03-16 |
| 52 | pead_midcap_step10_short | 21.5 | 1.00 | -0.0 | 52% | -0.0 | 0.9 | 79 | - | - | - | 1.61 | +1.6 | 59% | 2.2 | 0.7 | 70 | - | - | - | 2026-03-16 |
| 53 | pead_midcap_step35_balanced_sleeves_nofrac_aclong12 | 21.2 | 2.01 | +2.2 | 61% | 4.2 | 0.4 | 56 | 6.2 | +1.1 | 76.6 | 1.24 | +0.7 | 51% | 1.2 | 0.9 | 53 | 7.5 | +1.6 | 77.2 | 2026-03-17 |
| 54 | pead_midcap_step2_notrail | 17.2 | 0.93 | -0.3 | 67% | -0.4 | 1.7 | 54 | - | - | - | 1.07 | +0.4 | 73% | 0.4 | 1.7 | 62 | - | - | - | 2026-03-16 |
| 55 | pead_midcap_step1_fixedr | 16.2 | 0.91 | -0.5 | 47% | -0.7 | 1.5 | 95 | - | - | - | 1.77 | +2.8 | 49% | 3.4 | 1.6 | 69 | - | - | - | 2026-03-16 |
| 56 | pead_midcap_combo_10pct_maxcand3 | 15.9 | 0.97 | -0.1 | 51% | -0.2 | 0.9 | 79 | - | - | - | 1.91 | +2.3 | 62% | 3.0 | 0.7 | 72 | - | - | - | 2026-03-16 |
| 57 | pead_midcap_step6_drift | 14.7 | 0.86 | -0.9 | 43% | -1.6 | 1.7 | 100 | - | - | - | 1.70 | +2.7 | 51% | 3.7 | 1.0 | 75 | - | - | - | 2026-03-16 |
| 58 | pead_midcap_step7_fixedr | 13.8 | 0.94 | -0.2 | 45% | -0.4 | 1.0 | 71 | - | - | - | 2.00 | +2.4 | 53% | 3.1 | 0.7 | 53 | - | - | - | 2026-03-16 |
| 59 | pead_midcap_step4_longonly | 12.2 | 0.84 | -0.8 | 45% | -1.2 | 1.4 | 78 | - | - | - | 1.43 | +1.5 | 61% | 1.7 | 1.6 | 76 | - | - | - | 2026-03-16 |
| 60 | pead_midcap_step8_nft | 11.5 | 0.65 | -1.8 | 41% | -3.0 | 2.2 | 71 | - | - | - | 1.55 | +1.8 | 46% | 2.4 | 1.0 | 57 | - | - | - | 2026-03-16 |
| 61 | pead_midcap_step9_stop2 | 11.4 | 0.77 | -1.7 | 45% | -1.8 | 2.5 | 71 | - | - | - | 1.81 | +3.2 | 53% | 2.8 | 1.2 | 53 | - | - | - | 2026-03-16 |
## Recent Entries
### IMP-0047 (2026-03-17) — pead_midcap_step53_short_core_macro_block_crashcap_gap14
Hypothesis: A stricter same-day long gap filter might further concentrate the overlay into only the strongest continuation setups.
Verdict: **WORSE** (SQS 39.5)
Reasoning: The stricter gap filter over-concentrated the overlay, dropped total trade count below a healthy level, and cratered test SQS.
Next: Use moderate overlay filters only; the strict version is too sparse.
### IMP-0061 (2026-03-17) — pead_midcap_step67_short_core_macro_block_crashcap_gap10_longtrend12_sdlong25_max4_acsgap10_react14
Hypothesis: A stricter 14% downside reaction requirement may further improve the filtered after-close short sleeve by keeping only the sharpest downside continuation setups.
Verdict: **NEUTRAL** (SQS 38.2)
Reasoning: This pushed train to +5.32% and lifted valid slightly, but test slipped back to +0.95%. It is a stronger train-focused branch, not a clear overall winner versus step66.
Next: Favor step66 for balance; step67 is only useful if we optimize explicitly for train-heavy return.
### IMP-0046 (2026-03-17) — pead_midcap_step52_short_core_macro_block_crashcap_gap10
Hypothesis: The same-day long overlay may work better when restricted to larger reaction-day gap moves.
Verdict: **NEUTRAL** (SQS 49.8)
Reasoning: A 10% gap filter made train and valid much stronger but gave back some test performance, so this is a balanced alternative rather than a clear new leader.
Next: If optimizing for robustness across splits, keep exploring overlay quality gates around this variant.
### IMP-0060 (2026-03-17) — pead_midcap_step66_short_core_macro_block_crashcap_gap10_longtrend12_sdlong25_max4_acsgap10_react12
Hypothesis: Adding a 12% downside reaction requirement on top of the 10% after-close gap filter may remove the weakest residual after-close shorts without sacrificing the recent OOS edge.
Verdict: **BETTER** (SQS 50.9)
Reasoning: This matched step62 on valid/test while lifting train from +5.01% to +5.09%. It is a cleaner version of the filtered short-sleeve branch with no observable downside so far.
Next: Use step66 as the balanced return-first branch; only test further changes if they can raise test above +1.0% without giving back the train lift.
### IMP-0045 (2026-03-17) — pead_midcap_step51_short_core_macro_block_crashcap
Hypothesis: Extreme one-day crash continuations are too stretched for the same-day short sleeve and should be excluded.
Verdict: **BETTER** (SQS 44.4)
Reasoning: Capping same-day shorts at -45% reaction preserved train and valid while modestly improving test return, PF, drawdown, and Sharpe versus step45.
Next: Combine the crash cap with a quality filter on the same-day long overlay.
### IMP-0059 (2026-03-17) — pead_midcap_step65_short_core_macro_block_crashcap_gap10_interleave_max4_acsgap12
Hypothesis: A stricter after-close gap gate plus interleaving may produce the strongest hybrid of train lift and balanced sleeve participation.
Verdict: **WORSE** (SQS 38.3)
Reasoning: Train ticked up slightly, but valid deteriorated meaningfully and test did not improve. Interleaving is not helping this filtered branch.
Next: Stay with the non-interleaved filtered short sleeve; the next branch should tune the filtered after-close short only if we need more test return.
### IMP-0044 (2026-03-17) — pead_midcap_step50_same_day_short_long_macro_block
Hypothesis: The same-day long overlay may matter, but the after-close short sleeve may be removable.
Verdict: **WORSE** (SQS 36.2)
Reasoning: Dropping the after-close short sleeve reduced both valid and test performance, so step45 still benefits from carrying all three active sleeves.
Next: Refine sleeve quality rather than deleting sleeves wholesale.
### IMP-0058 (2026-03-17) — pead_midcap_step64_short_core_macro_block_crashcap_gap10_interleave_max4_acsgap10
Hypothesis: Interleaving the filtered mixed-sleeve portfolio might recover some of the earlier test strength without sacrificing the new train lift from the after-close gap gate.
Verdict: **WORSE** (SQS 50.6)
Reasoning: Test held steady, but valid return and drawdown got materially worse while train did not improve. The gap filter works better with raw global-score ranking than with interleaving.
Next: Keep global-score selection and treat step62/63 as the active return-first branch.
### IMP-0043 (2026-03-17) — pead_midcap_step49_same_day_short_macro_block
Hypothesis: The pure same-day short engine might dominate the portfolio and make other sleeves unnecessary.
Verdict: **WORSE** (SQS 29.1)
Reasoning: The single-sleeve version collapsed SQS because trade count and robustness fell too far, even though the kept trades were profitable.
Next: Keep the supporting sleeves and test smaller structural adjustments instead.
### IMP-0057 (2026-03-17) — pead_midcap_step63_short_core_macro_block_crashcap_gap10_longtrend12_sdlong25_max4_acsgap12
Hypothesis: A stricter 12% negative gap gate on after-close shorts may further improve the mixed-sleeve portfolio by concentrating the short sleeve into only the sharpest downside reactions.
Verdict: **BETTER** (SQS 38.2)
Reasoning: The 12% gate slightly improved train and valid versus step62 while keeping test near 0.95% with lower drawdown than the old mixed-sleeve base. This is the strongest return-first variant so far.
Next: Combine the after-close gap gate with interleaved max4 sleeve selection to test whether test return can recover toward the 1.0%+ level.

@ -0,0 +1,54 @@
# Free Attention Probe
Date: 2026-03-17
Goal:
- Verify that free historical attention/news proxies can be fetched for old events.
- Run a short-sample sanity check before building anything into Stock Oracle.
Sources tested:
- `Wikimedia pageviews` for historical attention spikes
- `GDELT Doc API` for spot-check historical news article counts
Method:
- Start from `data/datasets/snapshots/midcap-filtered/test.parquet`
- Restrict to `earnings_release`
- Join `ticker` / `issuer_name` from local Postgres
- Keep only names that are not obvious `{TICKER} Corporation` placeholders
- Resolve a Wikipedia article title from issuer name
- Compute `pageview_spike = event_day_views / median(last_10_pre_event_views)`
- Compare against signed continuation:
- `signed_cont_3d = sign(reaction_day_return) * fwd_return_3d`
- `signed_cont_5d = sign(reaction_day_return) * fwd_return_5d`
Probe run:
- command:
- `python -m apps.tools.free_attention_probe --limit 30 --gdelt-limit 5`
- output csv:
- `data/research/free_attention_probe_test_sample.csv`
Results:
- Sampled 30 non-generic test-split earnings events
- Resolved 22 rows with usable Wikipedia pageviews
- Raw sample:
- median pageview spike `1.226x`
- high-spike group signed 3D continuation mean `+0.0672`
- low-spike group signed 3D continuation mean `+0.0186`
- high-spike group signed 5D continuation mean `+0.0693`
- low-spike group signed 5D continuation mean `+0.0458`
- corr(pageview_spike, signed_cont_3d) `+0.2646`
- corr(pageview_spike, signed_cont_5d) `-0.1075`
- After filtering to higher-confidence mappings and excluding obviously bad article matches, the broad signal became inconclusive.
- Negative-reaction subset looked more promising than the full sample on 5D continuation, but sample size was too small to trust.
GDELT spot-check:
- Historical fetch works.
- Exact-phrase matching is fragile without a better company-name resolver.
- In the small spot-check, valid 3-day article counts were observed for some names, but coverage was too patchy for immediate use as-is.
Conclusion:
- Free historical attention/news data is usable for short-window research.
- `Wikimedia pageviews` is immediately practical.
- `GDELT` is viable, but only after better issuer-name normalization and article/entity resolution.
- Current evidence does not justify adding raw pageview spike directly to strategy scoring yet.
- The most promising next test is a conditional filter on downside earnings reactions, not a global attention overlay.

@ -1,5 +1,140 @@
{
"entries": [
{
"entry_id": "IMP-0048",
"experiment_name": "pead_midcap_step56_short_core_macro_block_crashcap_gap10_interleave_longtrend25",
"sqs_score": 51.3,
"sqs_v2_score": 88.3,
"promotion_score": 89.1,
"unified_score": 51.3,
"profit_factor": 4.034651941086868,
"total_return_pct": 1.0723281131463445,
"win_rate": 0.8,
"sharpe_ratio": 3.399127342667149,
"max_drawdown_pct": 0.24183984749880666,
"trade_count": 20,
"avg_gross_exposure_pct": 2.4654753372509486,
"avg_net_exposure_pct": -1.477511185545206,
"days_in_market_pct": 51.06382978723404,
"valid_profit_factor": 4.8198685654623254,
"valid_total_return_pct": 1.9154249967419892,
"valid_win_rate": 0.6785714285714286,
"valid_sharpe_ratio": 3.973180662878923,
"valid_max_drawdown_pct": 0.6079718599673478,
"valid_trade_count": 28,
"valid_avg_gross_exposure_pct": 4.161026862046388,
"valid_avg_net_exposure_pct": -2.684188461871084,
"valid_days_in_market_pct": 56.14035087719298,
"timestamp": "2026-03-17T10:10:07.544224+00:00"
},
{
"entry_id": "IMP-0056",
"experiment_name": "pead_midcap_step62_short_core_macro_block_crashcap_gap10_longtrend12_sdlong25_max4_acsgap10",
"sqs_score": 50.9,
"sqs_v2_score": 87.7,
"promotion_score": 88.9,
"unified_score": 50.9,
"profit_factor": 3.693559917731055,
"total_return_pct": 0.9821623910714115,
"win_rate": 0.8,
"sharpe_ratio": 3.3957804338520745,
"max_drawdown_pct": 0.23230116536263104,
"trade_count": 20,
"avg_gross_exposure_pct": 2.4512706589430846,
"avg_net_exposure_pct": -1.4631851279456756,
"days_in_market_pct": 48.93617021276596,
"valid_profit_factor": 4.640144626555104,
"valid_total_return_pct": 1.7785589226570302,
"valid_win_rate": 0.6923076923076923,
"valid_sharpe_ratio": 4.453025001679815,
"valid_max_drawdown_pct": 0.394474954823559,
"valid_trade_count": 26,
"valid_avg_gross_exposure_pct": 3.5028084000751822,
"valid_avg_net_exposure_pct": -2.028975630946834,
"valid_days_in_market_pct": 49.122807017543856,
"timestamp": "2026-03-17T10:20:47.944163+00:00"
},
{
"entry_id": "IMP-0060",
"experiment_name": "pead_midcap_step66_short_core_macro_block_crashcap_gap10_longtrend12_sdlong25_max4_acsgap10_react12",
"sqs_score": 50.9,
"sqs_v2_score": 87.7,
"promotion_score": 88.9,
"unified_score": 50.9,
"profit_factor": 3.693559917731055,
"total_return_pct": 0.9821623910714115,
"win_rate": 0.8,
"sharpe_ratio": 3.3957804338520745,
"max_drawdown_pct": 0.23230116536263104,
"trade_count": 20,
"avg_gross_exposure_pct": 2.4512706589430846,
"avg_net_exposure_pct": -1.4631851279456756,
"days_in_market_pct": 48.93617021276596,
"valid_profit_factor": 4.640144626555104,
"valid_total_return_pct": 1.7785589226570302,
"valid_win_rate": 0.6923076923076923,
"valid_sharpe_ratio": 4.453025001679815,
"valid_max_drawdown_pct": 0.394474954823559,
"valid_trade_count": 26,
"valid_avg_gross_exposure_pct": 3.5028084000751822,
"valid_avg_net_exposure_pct": -2.028975630946834,
"valid_days_in_market_pct": 49.122807017543856,
"timestamp": "2026-03-17T10:26:25.356022+00:00"
},
{
"entry_id": "IMP-0058",
"experiment_name": "pead_midcap_step64_short_core_macro_block_crashcap_gap10_interleave_max4_acsgap10",
"sqs_score": 50.6,
"sqs_v2_score": 87.7,
"promotion_score": 88.2,
"unified_score": 50.6,
"profit_factor": 3.693559917731055,
"total_return_pct": 0.9821623910714115,
"win_rate": 0.8,
"sharpe_ratio": 3.3957804338520745,
"max_drawdown_pct": 0.23230116536263104,
"trade_count": 20,
"avg_gross_exposure_pct": 2.4512706589430846,
"avg_net_exposure_pct": -1.4631851279456756,
"days_in_market_pct": 48.93617021276596,
"valid_profit_factor": 4.330878953751208,
"valid_total_return_pct": 1.675907371790352,
"valid_win_rate": 0.6538461538461539,
"valid_sharpe_ratio": 3.7033259316462277,
"valid_max_drawdown_pct": 0.608675665764912,
"valid_trade_count": 26,
"valid_avg_gross_exposure_pct": 3.7950928397401578,
"valid_avg_net_exposure_pct": -2.3215832255311892,
"valid_days_in_market_pct": 49.122807017543856,
"timestamp": "2026-03-17T10:22:41.050471+00:00"
},
{
"entry_id": "IMP-0049",
"experiment_name": "pead_midcap_step55_short_core_macro_block_crashcap_gap10_longtrend12_sdlong25",
"sqs_score": 49.8,
"sqs_v2_score": 87.1,
"promotion_score": 89.1,
"unified_score": 49.8,
"profit_factor": 3.4044506466943907,
"total_return_pct": 1.0338260227821447,
"win_rate": 0.782608695652174,
"sharpe_ratio": 3.1121570919759436,
"max_drawdown_pct": 0.24257912061402945,
"trade_count": 23,
"avg_gross_exposure_pct": 3.034569947217148,
"avg_net_exposure_pct": -2.0461795297656566,
"days_in_market_pct": 53.191489361702125,
"valid_profit_factor": 4.621364494548783,
"valid_total_return_pct": 1.9321698387879296,
"valid_win_rate": 0.7142857142857143,
"valid_sharpe_ratio": 4.486982260533271,
"valid_max_drawdown_pct": 0.29678396454155964,
"valid_trade_count": 28,
"valid_avg_gross_exposure_pct": 3.8320562275604266,
"valid_avg_net_exposure_pct": -2.3571340234511924,
"valid_days_in_market_pct": 56.14035087719298,
"timestamp": "2026-03-17T10:10:07.544223+00:00"
},
{
"entry_id": "IMP-0046",
"experiment_name": "pead_midcap_step52_short_core_macro_block_crashcap_gap10",
@ -54,6 +189,87 @@
"valid_days_in_market_pct": 51.78571428571429,
"timestamp": "2026-03-17T07:54:26.967747+00:00"
},
{
"entry_id": "IMP-0051",
"experiment_name": "pead_midcap_step58_short_core_macro_block_crashcap_gap7_longtrend12_sdlong25",
"sqs_score": 46.6,
"sqs_v2_score": 86.3,
"promotion_score": 87.7,
"unified_score": 46.6,
"profit_factor": 2.7627983723004212,
"total_return_pct": 0.9267893134084734,
"win_rate": 0.7391304347826086,
"sharpe_ratio": 2.781969702284716,
"max_drawdown_pct": 0.24257912061402945,
"trade_count": 23,
"avg_gross_exposure_pct": 3.172184023694837,
"avg_net_exposure_pct": -1.2529216711302178,
"days_in_market_pct": 53.191489361702125,
"valid_profit_factor": 2.885213921540522,
"valid_total_return_pct": 1.6066212170483485,
"valid_win_rate": 0.6896551724137931,
"valid_sharpe_ratio": 3.6006506236711573,
"valid_max_drawdown_pct": 0.4958133428812383,
"valid_trade_count": 29,
"valid_avg_gross_exposure_pct": 4.030999519248784,
"valid_avg_net_exposure_pct": -1.9829383669087164,
"valid_days_in_market_pct": 56.14035087719298,
"timestamp": "2026-03-17T10:13:47.820620+00:00"
},
{
"entry_id": "IMP-0050",
"experiment_name": "pead_midcap_step54_short_core_macro_block_crashcap_gap10_longtrend12",
"sqs_score": 46.5,
"sqs_v2_score": 86.1,
"promotion_score": 88.6,
"unified_score": 46.5,
"profit_factor": 2.991176064691692,
"total_return_pct": 0.856133035813924,
"win_rate": 0.7727272727272727,
"sharpe_ratio": 2.4909364067019513,
"max_drawdown_pct": 0.3359030771970047,
"trade_count": 22,
"avg_gross_exposure_pct": 2.921981872114122,
"avg_net_exposure_pct": -2.1517863691688386,
"days_in_market_pct": 53.191489361702125,
"valid_profit_factor": 6.099913503899416,
"valid_total_return_pct": 1.9918550334882748,
"valid_win_rate": 0.7307692307692307,
"valid_sharpe_ratio": 4.666899835993297,
"valid_max_drawdown_pct": 0.21797225956805316,
"valid_trade_count": 26,
"valid_avg_gross_exposure_pct": 3.637408212559115,
"valid_avg_net_exposure_pct": -2.5520608569407015,
"valid_days_in_market_pct": 56.14035087719298,
"timestamp": "2026-03-17T10:10:07.544222+00:00"
},
{
"entry_id": "IMP-0052",
"experiment_name": "pead_midcap_step57_short_core_macro_block_crashcap_gap10_longtrend12_sdlong25_max4",
"sqs_score": 46.5,
"sqs_v2_score": 86.3,
"promotion_score": 88.6,
"unified_score": 46.5,
"profit_factor": 2.664801821772631,
"total_return_pct": 0.91437274196824,
"win_rate": 0.75,
"sharpe_ratio": 2.893891891447354,
"max_drawdown_pct": 0.24257912061402945,
"trade_count": 24,
"avg_gross_exposure_pct": 3.169455379761105,
"avg_net_exposure_pct": -2.1804906875349683,
"days_in_market_pct": 53.191489361702125,
"valid_profit_factor": 4.4424890742925465,
"valid_total_return_pct": 1.9106867186089511,
"valid_win_rate": 0.6896551724137931,
"valid_sharpe_ratio": 4.511177385736093,
"valid_max_drawdown_pct": 0.3179173247540872,
"valid_trade_count": 29,
"valid_avg_gross_exposure_pct": 3.944739633758862,
"valid_avg_net_exposure_pct": -2.4700155661285508,
"valid_days_in_market_pct": 56.14035087719298,
"timestamp": "2026-03-17T10:13:47.821673+00:00"
},
{
"entry_id": "IMP-0040",
"experiment_name": "pead_midcap_step46_short_core_macro_block_acshort12",
@ -189,6 +405,87 @@
"valid_days_in_market_pct": 56.14035087719298,
"timestamp": "2026-03-17T07:54:28.762757+00:00"
},
{
"entry_id": "IMP-0059",
"experiment_name": "pead_midcap_step65_short_core_macro_block_crashcap_gap10_interleave_max4_acsgap12",
"sqs_score": 38.3,
"sqs_v2_score": 44.4,
"promotion_score": 69.0,
"unified_score": 38.3,
"profit_factor": 3.6038727896341176,
"total_return_pct": 0.9494594526291912,
"win_rate": 0.7894736842105263,
"sharpe_ratio": 3.2438715317612528,
"max_drawdown_pct": 0.23230116536263104,
"trade_count": 19,
"avg_gross_exposure_pct": 1.9049388001836767,
"avg_net_exposure_pct": -0.9171395725318859,
"days_in_market_pct": 38.297872340425535,
"valid_profit_factor": 4.557936520788986,
"valid_total_return_pct": 1.5351686099939834,
"valid_win_rate": 0.6666666666666666,
"valid_sharpe_ratio": 3.9755987852769445,
"valid_max_drawdown_pct": 0.5303323192221483,
"valid_trade_count": 24,
"valid_avg_gross_exposure_pct": 3.4173394039982,
"valid_avg_net_exposure_pct": -1.9446456373142142,
"valid_days_in_market_pct": 47.368421052631575,
"timestamp": "2026-03-17T10:22:41.560235+00:00"
},
{
"entry_id": "IMP-0057",
"experiment_name": "pead_midcap_step63_short_core_macro_block_crashcap_gap10_longtrend12_sdlong25_max4_acsgap12",
"sqs_score": 38.2,
"sqs_v2_score": 44.4,
"promotion_score": 69.6,
"unified_score": 38.2,
"profit_factor": 3.6038727896341176,
"total_return_pct": 0.9494594526291912,
"win_rate": 0.7894736842105263,
"sharpe_ratio": 3.2438715317612528,
"max_drawdown_pct": 0.23230116536263104,
"trade_count": 19,
"avg_gross_exposure_pct": 1.9049388001836767,
"avg_net_exposure_pct": -0.9171395725318859,
"days_in_market_pct": 38.297872340425535,
"valid_profit_factor": 5.270593419113103,
"valid_total_return_pct": 1.7898083823081543,
"valid_win_rate": 0.7083333333333334,
"valid_sharpe_ratio": 4.878106921358311,
"valid_max_drawdown_pct": 0.31708063303845857,
"valid_trade_count": 24,
"valid_avg_gross_exposure_pct": 3.0974719922506315,
"valid_avg_net_exposure_pct": -1.626276620699805,
"valid_days_in_market_pct": 45.614035087719294,
"timestamp": "2026-03-17T10:20:48.356015+00:00"
},
{
"entry_id": "IMP-0061",
"experiment_name": "pead_midcap_step67_short_core_macro_block_crashcap_gap10_longtrend12_sdlong25_max4_acsgap10_react14",
"sqs_score": 38.2,
"sqs_v2_score": 44.4,
"promotion_score": 69.5,
"unified_score": 38.2,
"profit_factor": 3.6038727896341176,
"total_return_pct": 0.9494594526291912,
"win_rate": 0.7894736842105263,
"sharpe_ratio": 3.2438715317612528,
"max_drawdown_pct": 0.23230116536263104,
"trade_count": 19,
"avg_gross_exposure_pct": 1.9049388001836767,
"avg_net_exposure_pct": -0.9171395725318859,
"days_in_market_pct": 38.297872340425535,
"valid_profit_factor": 5.331499198553463,
"valid_total_return_pct": 1.8247351197415846,
"valid_win_rate": 0.72,
"valid_sharpe_ratio": 4.44451943654,
"valid_max_drawdown_pct": 0.480176430930607,
"valid_trade_count": 25,
"valid_avg_gross_exposure_pct": 3.1678311806010018,
"valid_avg_net_exposure_pct": -1.6948561495104018,
"valid_days_in_market_pct": 47.368421052631575,
"timestamp": "2026-03-17T10:26:25.840092+00:00"
},
{
"entry_id": "IMP-0044",
"experiment_name": "pead_midcap_step50_same_day_short_long_macro_block",
@ -325,7 +622,88 @@
"timestamp": "2026-03-17T06:59:09.622681+00:00"
},
{
"entry_id": "IMP-0037",
"entry_id": "IMP-0053",
"experiment_name": "pead_midcap_step59_same_day_only_max4_longtrend25",
"sqs_score": 33.2,
"sqs_v2_score": 42.7,
"promotion_score": 68.6,
"unified_score": 33.2,
"profit_factor": 2.6072978977047137,
"total_return_pct": 0.5860747837766976,
"win_rate": 0.6923076923076923,
"sharpe_ratio": 2.308425640364089,
"max_drawdown_pct": 0.31275465094584215,
"trade_count": 13,
"avg_gross_exposure_pct": 1.4491267702359076,
"avg_net_exposure_pct": -0.4649535073354661,
"days_in_market_pct": 31.914893617021278,
"valid_profit_factor": 6.627861685804674,
"valid_total_return_pct": 1.9039976324784367,
"valid_win_rate": 0.7272727272727273,
"valid_sharpe_ratio": 4.451621602089021,
"valid_max_drawdown_pct": 0.44666468803527526,
"valid_trade_count": 22,
"valid_avg_gross_exposure_pct": 3.8681902002325703,
"valid_avg_net_exposure_pct": -2.3913045273063496,
"valid_days_in_market_pct": 42.10526315789473,
"timestamp": "2026-03-17T10:18:37.790830+00:00"
},
{
"entry_id": "IMP-0054",
"experiment_name": "pead_midcap_step60_same_day_only_interleave_max4_longtrend25",
"sqs_score": 33.2,
"sqs_v2_score": 42.7,
"promotion_score": 68.6,
"unified_score": 33.2,
"profit_factor": 2.6072978977047137,
"total_return_pct": 0.5860747837766976,
"win_rate": 0.6923076923076923,
"sharpe_ratio": 2.308425640364089,
"max_drawdown_pct": 0.31275465094584215,
"trade_count": 13,
"avg_gross_exposure_pct": 1.4491267702359076,
"avg_net_exposure_pct": -0.4649535073354661,
"days_in_market_pct": 31.914893617021278,
"valid_profit_factor": 6.627861685804674,
"valid_total_return_pct": 1.9039976324784367,
"valid_win_rate": 0.7272727272727273,
"valid_sharpe_ratio": 4.451621602089021,
"valid_max_drawdown_pct": 0.44666468803527526,
"valid_trade_count": 22,
"valid_avg_gross_exposure_pct": 3.8681902002325703,
"valid_avg_net_exposure_pct": -2.3913045273063496,
"valid_days_in_market_pct": 42.10526315789473,
"timestamp": "2026-03-17T10:18:38.178487+00:00"
},
{
"entry_id": "IMP-0055",
"experiment_name": "pead_midcap_step61_same_day_only_max5_longtrend25",
"sqs_score": 33.2,
"sqs_v2_score": 42.7,
"promotion_score": 68.6,
"unified_score": 33.2,
"profit_factor": 2.6072978977047137,
"total_return_pct": 0.5860747837766976,
"win_rate": 0.6923076923076923,
"sharpe_ratio": 2.308425640364089,
"max_drawdown_pct": 0.31275465094584215,
"trade_count": 13,
"avg_gross_exposure_pct": 1.4491267702359076,
"avg_net_exposure_pct": -0.4649535073354661,
"days_in_market_pct": 31.914893617021278,
"valid_profit_factor": 6.627861685804674,
"valid_total_return_pct": 1.9039976324784367,
"valid_win_rate": 0.7272727272727273,
"valid_sharpe_ratio": 4.451621602089021,
"valid_max_drawdown_pct": 0.44666468803527526,
"valid_trade_count": 22,
"valid_avg_gross_exposure_pct": 3.8681902002325703,
"valid_avg_net_exposure_pct": -2.3913045273063496,
"valid_days_in_market_pct": 42.10526315789473,
"timestamp": "2026-03-17T10:18:38.591808+00:00"
},
{
"entry_id": "IMP-0036",
"experiment_name": "pead_midcap_step43_short_core_sdlong25",
"sqs_score": 31.7,
"sqs_v2_score": 78.2,
@ -406,7 +784,7 @@
"timestamp": "2026-03-17T07:11:20.119143+00:00"
},
{
"entry_id": "IMP-0036",
"entry_id": "IMP-0034",
"experiment_name": "pead_midcap_step42_short_core_only",
"sqs_score": 30.5,
"sqs_v2_score": 67.1,
@ -595,7 +973,7 @@
"timestamp": "2026-03-17T02:17:02.185037+00:00"
},
{
"entry_id": "IMP-0034",
"entry_id": "IMP-0037",
"experiment_name": "pead_midcap_step40_short_core_sdlong12",
"sqs_score": 28.6,
"sqs_v2_score": 81.4,
@ -1270,5 +1648,5 @@
"timestamp": "2026-03-16T23:01:55.163461+00:00"
}
],
"updated_at": "2026-03-17T09:17:27.726326+00:00"
"updated_at": "2026-03-17T10:37:12.332837+00:00"
}

@ -31,10 +31,10 @@
{"entry_id":"IMP-0031","timestamp":"2026-03-17T07:11:20.119143+00:00","experiment_name":"pead_midcap_step37_balanced_sleeves_aclong_vol3","hypothesis":"Require stronger volume confirmation for after-close long signals only, while leaving the rest of the step31 sleeve mix unchanged.","config_delta":{"base_experiment":"pead_midcap_step31_balanced_sleeves_nofrac_acshort12","changes":{}},"results":{"test":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317071110746395_1ae4b33a","trade_count":63,"profit_factor":1.377310183342628,"total_return_pct":1.1862161229211343,"win_rate":0.5396825396825397,"max_drawdown_pct":0.8243191281564817,"sharpe_ratio":2.4626680484273273,"monthly_win_rate":1.0,"equity_curve_r_squared":0.6885324256339993},"valid":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317071110780542_1ae4b33a","trade_count":57,"profit_factor":1.375336580561267,"total_return_pct":1.1216330418461293,"win_rate":0.5087719298245614,"max_drawdown_pct":0.8896384382974281,"sharpe_ratio":1.7701058914354275,"monthly_win_rate":0.75,"equity_curve_r_squared":0.3302488231788083}},"sqs_score":73.6,"sqs_breakdown":{"profitability":53.6,"risk":100.0,"consistency":81.6,"robustness":72.5},"verdict":"worse","verdict_reasoning":"Engine-specific volume gating on after-close longs did not help. Test fell to SQS 73.6 and valid to 67.6, both below the step31 base. The extra volume filter removed too much breadth without improving robustness.","next_direction":"Do not tighten after-close long volume gates further. Keep after-close long breadth and search elsewhere if more robustness is needed.","tags":["pead","midcap","step37","balanced","sleeves","aclong","vol3"]}
{"entry_id":"IMP-0032","timestamp":"2026-03-17T07:11:26.965107+00:00","experiment_name":"pead_midcap_step38_balanced_sleeves_aclong_vol4","hypothesis":"Push the after-close long sleeve to an even stricter volume gate so only the highest-conviction overnight reactions remain.","config_delta":{"base_experiment":"pead_midcap_step37_balanced_sleeves_aclong_vol3","changes":{}},"results":{"test":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317071110780988_d9020d34","trade_count":61,"profit_factor":1.1204890397898135,"total_return_pct":0.388000418802214,"win_rate":0.5081967213114754,"max_drawdown_pct":0.9399793760032171,"sharpe_ratio":0.8136845994700913,"monthly_win_rate":0.6666666666666666,"equity_curve_r_squared":0.043822307069783864},"valid":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317071110695904_d9020d34","trade_count":53,"profit_factor":1.2930052787721988,"total_return_pct":0.8333477612284769,"win_rate":0.5283018867924528,"max_drawdown_pct":0.8783421675410786,"sharpe_ratio":1.4376847128011023,"monthly_win_rate":0.75,"equity_curve_r_squared":0.19294412573010883}},"sqs_score":54.2,"sqs_breakdown":{"profitability":37.6,"risk":80.2,"consistency":72.2,"robustness":31.1},"verdict":"worse","verdict_reasoning":"The stricter after-close long filter clearly broke the portfolio. Test dropped to SQS 54.2 and valid to 63.2, confirming that this sleeve cannot be improved by simply tightening volume thresholds.","next_direction":"Abandon the after-close long volume-threshold path. If that sleeve is revisited, it needs a different filter than raw PEAD volume.","tags":["pead","midcap","step38","balanced","sleeves","aclong","vol4"]}
{"entry_id":"IMP-0033","timestamp":"2026-03-17T07:11:34.247400+00:00","experiment_name":"pead_midcap_step39_balanced_sleeves_sdlong12","hypothesis":"Keep the robust step31 structure intact and only cap same-day long close entries to one trade per day, trimming the weakest same-day long names without touching after-close longs.","config_delta":{"base_experiment":"pead_midcap_step31_balanced_sleeves_nofrac_acshort12","changes":{}},"results":{"valid":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317071045883427_aa091287","trade_count":53,"profit_factor":1.3838842470402677,"total_return_pct":1.0111821812581183,"win_rate":0.5094339622641509,"max_drawdown_pct":0.8840772162266693,"sharpe_ratio":1.7379492731088386,"monthly_win_rate":0.75,"equity_curve_r_squared":0.2907004952665152},"test":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317071045888044_aa091287","trade_count":62,"profit_factor":1.5016295541562696,"total_return_pct":1.5150882212722936,"win_rate":0.5483870967741935,"max_drawdown_pct":0.5454456407735768,"sharpe_ratio":3.3681033592846275,"monthly_win_rate":1.0,"equity_curve_r_squared":0.8586769561427715}},"sqs_score":77.9,"sqs_breakdown":{"profitability":61.1,"risk":100.0,"consistency":83.1,"robustness":78.9},"verdict":"worse","verdict_reasoning":"This preserved test strength at SQS 77.9 and +1.52%, but valid fell to SQS 66.8 and +1.01% versus step31 valid SQS 68.2 and +1.12%. Reducing same-day long breadth did not produce a robust improvement.","next_direction":"Keep the same-day long sleeve at two trades per day inside step31. The current robust champion remains unchanged.","tags":["pead","midcap","step39","balanced","sleeves","sdlong12"]}
{"entry_id":"IMP-0036","timestamp":"2026-03-17T07:20:29.978668+00:00","experiment_name":"pead_midcap_step42_short_core_only","hypothesis":"Test whether the portfolio should become a pure short engine by removing the same-day long overlay entirely.","config_delta":{"base_experiment":"pead_midcap_step40_short_core_sdlong12","changes":{}},"results":{"valid":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317071756406525_914047c4","trade_count":31,"profit_factor":2.549217811707667,"total_return_pct":1.193718767675222,"win_rate":0.5806451612903226,"max_drawdown_pct":0.5448919617489582,"sharpe_ratio":2.752802585098115,"monthly_win_rate":0.75,"equity_curve_r_squared":0.41739788897817576},"test":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317071756496030_914047c4","trade_count":46,"profit_factor":1.4446704886262167,"total_return_pct":0.7775531748585345,"win_rate":0.6304347826086957,"max_drawdown_pct":1.2423515817014616,"sharpe_ratio":1.1740322533878838,"monthly_win_rate":0.6666666666666666,"equity_curve_r_squared":0.1540289094091857}},"sqs_score":66.3,"sqs_breakdown":{"profitability":55.3,"risk":84.9,"consistency":92.6,"robustness":29.6},"verdict":"worse","verdict_reasoning":"Pure shorts produced a strong valid SQS 82.3 but test collapsed to 66.3 with only +0.78% return. The small same-day long overlay is still needed for out-of-sample balance.","next_direction":"Keep a non-zero same-day long close sleeve in the short-core family.","tags":["pead","midcap","step42","short","core","only"]}
{"entry_id":"IMP-0034","timestamp":"2026-03-17T07:20:29.978668+00:00","experiment_name":"pead_midcap_step42_short_core_only","hypothesis":"Test whether the portfolio should become a pure short engine by removing the same-day long overlay entirely.","config_delta":{"base_experiment":"pead_midcap_step40_short_core_sdlong12","changes":{}},"results":{"valid":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317071756406525_914047c4","trade_count":31,"profit_factor":2.549217811707667,"total_return_pct":1.193718767675222,"win_rate":0.5806451612903226,"max_drawdown_pct":0.5448919617489582,"sharpe_ratio":2.752802585098115,"monthly_win_rate":0.75,"equity_curve_r_squared":0.41739788897817576},"test":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317071756496030_914047c4","trade_count":46,"profit_factor":1.4446704886262167,"total_return_pct":0.7775531748585345,"win_rate":0.6304347826086957,"max_drawdown_pct":1.2423515817014616,"sharpe_ratio":1.1740322533878838,"monthly_win_rate":0.6666666666666666,"equity_curve_r_squared":0.1540289094091857}},"sqs_score":66.3,"sqs_breakdown":{"profitability":55.3,"risk":84.9,"consistency":92.6,"robustness":29.6},"verdict":"worse","verdict_reasoning":"Pure shorts produced a strong valid SQS 82.3 but test collapsed to 66.3 with only +0.78% return. The small same-day long overlay is still needed for out-of-sample balance.","next_direction":"Keep a non-zero same-day long close sleeve in the short-core family.","tags":["pead","midcap","step42","short","core","only"]}
{"entry_id":"IMP-0035","timestamp":"2026-03-17T07:20:29.978594+00:00","experiment_name":"pead_midcap_step41_short_core_sdlong12_acshort50","hypothesis":"Lean harder into the after-close short sleeve inside the new short-core portfolio.","config_delta":{"base_experiment":"pead_midcap_step40_short_core_sdlong12","changes":{}},"results":{"valid":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317071756232509_4c9fd4ce","trade_count":40,"profit_factor":1.483920817012022,"total_return_pct":0.8662763872782817,"win_rate":0.55,"max_drawdown_pct":0.6476993822793542,"sharpe_ratio":1.8482432920991685,"monthly_win_rate":0.75,"equity_curve_r_squared":0.19435203451054603},"test":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317071756491852_4c9fd4ce","trade_count":59,"profit_factor":1.5287187084320328,"total_return_pct":1.231827071365813,"win_rate":0.559322033898305,"max_drawdown_pct":1.2759660172387226,"sharpe_ratio":1.6310365384096348,"monthly_win_rate":1.0,"equity_curve_r_squared":0.4227182249282274}},"sqs_score":72.6,"sqs_breakdown":{"profitability":61.4,"risk":92.3,"consistency":84.9,"robustness":53.6},"verdict":"worse","verdict_reasoning":"Increasing after-close short capacity weakened the portfolio: test dropped from SQS 82.1 to 72.6 and valid stayed flat at 68.4. The short core benefits from the bucket, but not at this larger size.","next_direction":"Keep the after-close short sleeve at 25% inside the short-core family.","tags":["pead","midcap","step41","short","core","sdlong12","acshort50"]}
{"entry_id":"IMP-0037","timestamp":"2026-03-17T07:20:29.978670+00:00","experiment_name":"pead_midcap_step43_short_core_sdlong25","hypothesis":"Restore a larger same-day long close sleeve after removing after-close longs, to see if breadth improves the short-core portfolio.","config_delta":{"base_experiment":"pead_midcap_step40_short_core_sdlong12","changes":{}},"results":{"valid":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317071957079395_9046f612","trade_count":45,"profit_factor":1.4604102163883825,"total_return_pct":0.9859009158709378,"win_rate":0.5555555555555556,"max_drawdown_pct":0.6469365320312458,"sharpe_ratio":1.9269062309154923,"monthly_win_rate":0.75,"equity_curve_r_squared":0.2568675902061873},"test":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317071957079892_9046f612","trade_count":58,"profit_factor":1.6628306811204845,"total_return_pct":1.4636686220428092,"win_rate":0.5689655172413793,"max_drawdown_pct":1.2630043081731899,"sharpe_ratio":2.0112049109753753,"monthly_win_rate":1.0,"equity_curve_r_squared":0.57291038217223}},"sqs_score":78.9,"sqs_breakdown":{"profitability":69.0,"risk":98.5,"consistency":86.5,"robustness":62.5},"verdict":"worse","verdict_reasoning":"Restoring more same-day long breadth weakened both splits versus step40: valid moved from SQS 68.4 to 69.7 but test fell from 82.1 to 78.9 and profitability dropped. The smaller 12.5% sleeve remains the better balance.","next_direction":"Keep the same-day long overlay small inside step40.","tags":["pead","midcap","step43","short","core","sdlong25"]}
{"entry_id":"IMP-0034","timestamp":"2026-03-17T07:20:29.978842+00:00","experiment_name":"pead_midcap_step40_short_core_sdlong12","hypothesis":"Drop the unstable after-close long sleeve and reallocate the portfolio to same-day shorts, after-close shorts, and a small same-day close-entry long overlay.","config_delta":{"base_experiment":"pead_midcap_step31_balanced_sleeves_nofrac_acshort12","changes":{}},"results":{"valid":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317071756453145_9eb08c8d","trade_count":40,"profit_factor":1.483920817012022,"total_return_pct":0.8662763872782817,"win_rate":0.55,"max_drawdown_pct":0.6476993822793542,"sharpe_ratio":1.8482432920991685,"monthly_win_rate":0.75,"equity_curve_r_squared":0.19435203451054603},"test":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317071756505329_9eb08c8d","trade_count":55,"profit_factor":1.7695290624299977,"total_return_pct":1.52020169384827,"win_rate":0.5818181818181818,"max_drawdown_pct":1.128135882565315,"sharpe_ratio":2.2087400904317267,"monthly_win_rate":1.0,"equity_curve_r_squared":0.632820774620957},"train":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317071814970965_9eb08c8d","trade_count":306,"profit_factor":1.2169887676968005,"total_return_pct":3.708684730821959,"win_rate":0.4869281045751634,"max_drawdown_pct":2.5507043144769854,"sharpe_ratio":0.5720232080317771,"monthly_win_rate":0.625,"equity_curve_r_squared":0.39497202444939644}},"sqs_score":82.1,"sqs_breakdown":{"profitability":74.6,"risk":99.3,"consistency":88.6,"robustness":64.6},"verdict":"better","verdict_reasoning":"New robust leader. Train improved from SQS 55.5 to 63.1 and return +2.45% to +3.71%. Valid edged up from SQS 68.2 to 68.4 with lower drawdown, and test improved from SQS 77.9 to 82.1 with PF 1.77. Removing after-close longs fixed the biggest unstable sleeve without giving up the same-day long upside.","next_direction":"Use step40 as the new base. Only explore local refinements around the short-core structure if needed.","tags":["pead","midcap","step40","short","core","sdlong12"]}
{"entry_id":"IMP-0036","timestamp":"2026-03-17T07:20:29.978670+00:00","experiment_name":"pead_midcap_step43_short_core_sdlong25","hypothesis":"Restore a larger same-day long close sleeve after removing after-close longs, to see if breadth improves the short-core portfolio.","config_delta":{"base_experiment":"pead_midcap_step40_short_core_sdlong12","changes":{}},"results":{"valid":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317071957079395_9046f612","trade_count":45,"profit_factor":1.4604102163883825,"total_return_pct":0.9859009158709378,"win_rate":0.5555555555555556,"max_drawdown_pct":0.6469365320312458,"sharpe_ratio":1.9269062309154923,"monthly_win_rate":0.75,"equity_curve_r_squared":0.2568675902061873},"test":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317071957079892_9046f612","trade_count":58,"profit_factor":1.6628306811204845,"total_return_pct":1.4636686220428092,"win_rate":0.5689655172413793,"max_drawdown_pct":1.2630043081731899,"sharpe_ratio":2.0112049109753753,"monthly_win_rate":1.0,"equity_curve_r_squared":0.57291038217223}},"sqs_score":78.9,"sqs_breakdown":{"profitability":69.0,"risk":98.5,"consistency":86.5,"robustness":62.5},"verdict":"worse","verdict_reasoning":"Restoring more same-day long breadth weakened both splits versus step40: valid moved from SQS 68.4 to 69.7 but test fell from 82.1 to 78.9 and profitability dropped. The smaller 12.5% sleeve remains the better balance.","next_direction":"Keep the same-day long overlay small inside step40.","tags":["pead","midcap","step43","short","core","sdlong25"]}
{"entry_id":"IMP-0037","timestamp":"2026-03-17T07:20:29.978842+00:00","experiment_name":"pead_midcap_step40_short_core_sdlong12","hypothesis":"Drop the unstable after-close long sleeve and reallocate the portfolio to same-day shorts, after-close shorts, and a small same-day close-entry long overlay.","config_delta":{"base_experiment":"pead_midcap_step31_balanced_sleeves_nofrac_acshort12","changes":{}},"results":{"valid":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317071756453145_9eb08c8d","trade_count":40,"profit_factor":1.483920817012022,"total_return_pct":0.8662763872782817,"win_rate":0.55,"max_drawdown_pct":0.6476993822793542,"sharpe_ratio":1.8482432920991685,"monthly_win_rate":0.75,"equity_curve_r_squared":0.19435203451054603},"test":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317071756505329_9eb08c8d","trade_count":55,"profit_factor":1.7695290624299977,"total_return_pct":1.52020169384827,"win_rate":0.5818181818181818,"max_drawdown_pct":1.128135882565315,"sharpe_ratio":2.2087400904317267,"monthly_win_rate":1.0,"equity_curve_r_squared":0.632820774620957},"train":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317071814970965_9eb08c8d","trade_count":306,"profit_factor":1.2169887676968005,"total_return_pct":3.708684730821959,"win_rate":0.4869281045751634,"max_drawdown_pct":2.5507043144769854,"sharpe_ratio":0.5720232080317771,"monthly_win_rate":0.625,"equity_curve_r_squared":0.39497202444939644}},"sqs_score":82.1,"sqs_breakdown":{"profitability":74.6,"risk":99.3,"consistency":88.6,"robustness":64.6},"verdict":"better","verdict_reasoning":"New robust leader. Train improved from SQS 55.5 to 63.1 and return +2.45% to +3.71%. Valid edged up from SQS 68.2 to 68.4 with lower drawdown, and test improved from SQS 77.9 to 82.1 with PF 1.77. Removing after-close longs fixed the biggest unstable sleeve without giving up the same-day long upside.","next_direction":"Use step40 as the new base. Only explore local refinements around the short-core structure if needed.","tags":["pead","midcap","step40","short","core","sdlong12"]}
{"entry_id":"IMP-0038","timestamp":"2026-03-17T07:54:25.486815+00:00","experiment_name":"pead_midcap_step44_short_core_macro50","hypothesis":"Scaling entries down in weak macro regimes will keep the short-core structure while cutting drawdowns.","config_delta":{"base_experiment":"pead_midcap_step40_short_core_sdlong12","changes":{}},"results":{"valid":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317081817694940_42f8ea0e","trade_count":40,"profit_factor":1.7786229517773076,"total_return_pct":1.0842382260887244,"win_rate":0.55,"max_drawdown_pct":0.4065245518278289,"sharpe_ratio":2.4891782517624543,"monthly_win_rate":0.75,"equity_curve_r_squared":0.34564834918762316,"avg_gross_exposure_pct":4.823763728554508,"avg_net_exposure_pct":-1.952624774981557,"days_in_market_pct":73.68421052631578},"test":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317081807057141_42f8ea0e","trade_count":58,"profit_factor":2.0052763103391356,"total_return_pct":1.2986802302195721,"win_rate":0.5689655172413793,"max_drawdown_pct":0.7045335236824514,"sharpe_ratio":2.6217356101467053,"monthly_win_rate":1.0,"equity_curve_r_squared":0.7991432493330419,"avg_gross_exposure_pct":4.453478837236966,"avg_net_exposure_pct":-1.5519032472252936,"days_in_market_pct":72.3404255319149},"train":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317073555963588_9c3139ba","trade_count":307,"profit_factor":1.2319887960741855,"total_return_pct":3.4232792009141852,"win_rate":0.4820846905537459,"max_drawdown_pct":1.97377752972113,"sharpe_ratio":0.6203457661608955,"monthly_win_rate":0.625,"equity_curve_r_squared":0.4148680874417292}},"sqs_score":87.9,"sqs_breakdown":{"profitability":85.2,"risk":100.0,"consistency":86.5,"robustness":76.6},"verdict":"better","verdict_reasoning":"Half-size macro scaling materially improved valid and test risk-adjusted performance versus step40, confirming that SPY-below-SMA exposure was a real drag.","next_direction":"Try a full macro block to see whether removing weak-regime entries entirely is even cleaner.","tags":["pead","midcap","step44","short","core","macro50"],"sqs_v2_score":86.9,"sqs_v2_breakdown":{"profitability":85.2,"risk":100.0,"consistency":86.5,"robustness":76.6,"capital_efficiency":70.8}}
{"entry_id":"IMP-0039","timestamp":"2026-03-17T07:54:25.866482+00:00","experiment_name":"pead_midcap_step45_short_core_macro_block","hypothesis":"If weak-regime entries are mostly noise, hard-blocking them should outperform simple size scaling.","config_delta":{"base_experiment":"pead_midcap_step44_short_core_macro50","changes":{}},"results":{"valid":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317073536967478_c3a3615c","trade_count":28,"profit_factor":2.3084684930008983,"total_return_pct":1.30245295115927,"win_rate":0.6785714285714286,"max_drawdown_pct":0.37472483014430374,"sharpe_ratio":3.0487382658615467,"monthly_win_rate":0.75,"equity_curve_r_squared":0.48762905493056535},"test":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317073537015511_c3a3615c","trade_count":23,"profit_factor":3.7827916861128097,"total_return_pct":1.2021184808416436,"win_rate":0.6956521739130435,"max_drawdown_pct":0.2608177180219861,"sharpe_ratio":3.3795660622862123,"monthly_win_rate":0.6666666666666666,"equity_curve_r_squared":0.9110928059216074},"train":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317073556022527_c3a3615c","trade_count":220,"profit_factor":1.2595187982631553,"total_return_pct":3.200633818982489,"win_rate":0.5045454545454545,"max_drawdown_pct":1.389607178327899,"sharpe_ratio":0.6336391727317187,"monthly_win_rate":0.6551724137931034,"equity_curve_r_squared":0.39958317262208615}},"sqs_score":86.7,"sqs_breakdown":{"profitability":84.8,"risk":100.0,"consistency":95.8,"robustness":57.2},"verdict":"better","verdict_reasoning":"The hard macro block improved valid and test again, with sharper PF and much lower drawdown than the 50% scaler version.","next_direction":"Stress the sleeve mix around the new macro-blocked core.","tags":["pead","midcap","step45","short","core","macro","block"]}
{"entry_id":"IMP-0040","timestamp":"2026-03-17T07:54:26.245073+00:00","experiment_name":"pead_midcap_step46_short_core_macro_block_acshort12","hypothesis":"The after-close short sleeve may be oversized after the macro block and could improve if reduced.","config_delta":{"base_experiment":"pead_midcap_step45_short_core_macro_block","changes":{}},"results":{"valid":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317073936915792_e3adb886","trade_count":26,"profit_factor":2.4445635979938296,"total_return_pct":1.339314216731771,"win_rate":0.6923076923076923,"max_drawdown_pct":0.37458871743417604,"sharpe_ratio":3.1583041871985076,"monthly_win_rate":0.75,"equity_curve_r_squared":0.48286315791941375},"test":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317073939814431_e3adb886","trade_count":21,"profit_factor":4.522043594902001,"total_return_pct":1.2518263219734362,"win_rate":0.7142857142857143,"max_drawdown_pct":0.2783619920052574,"sharpe_ratio":3.6389410132883055,"monthly_win_rate":0.6666666666666666,"equity_curve_r_squared":0.9244229299732519}},"sqs_score":86.6,"sqs_breakdown":{"profitability":85.0,"risk":100.0,"consistency":95.8,"robustness":56.1},"verdict":"worse","verdict_reasoning":"Shrinking the after-close short sleeve slightly degraded both valid and test, so the step45 25% sleeve was not the problem.","next_direction":"Test whether the same-day long sleeve or the short-only core is the real source of edge.","tags":["pead","midcap","step46","short","core","macro","block","acshort12"]}
@ -45,3 +45,17 @@
{"entry_id":"IMP-0045","timestamp":"2026-03-17T07:54:28.043618+00:00","experiment_name":"pead_midcap_step51_short_core_macro_block_crashcap","hypothesis":"Extreme one-day crash continuations are too stretched for the same-day short sleeve and should be excluded.","config_delta":{"base_experiment":"pead_midcap_step45_short_core_macro_block","changes":{}},"results":{"valid":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317081833666987_5039805d","trade_count":28,"profit_factor":2.3084684930008983,"total_return_pct":1.30245295115927,"win_rate":0.6785714285714286,"max_drawdown_pct":0.37472483014430374,"sharpe_ratio":3.0487382658615467,"monthly_win_rate":0.75,"equity_curve_r_squared":0.48762905493056535,"avg_gross_exposure_pct":4.09489843643577,"avg_net_exposure_pct":-1.9273432556417505,"days_in_market_pct":57.89473684210527},"test":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317081834927778_5039805d","trade_count":22,"profit_factor":4.190184861108528,"total_return_pct":1.2441182731003355,"win_rate":0.7272727272727273,"max_drawdown_pct":0.22380328257556925,"sharpe_ratio":3.5956143566120704,"monthly_win_rate":0.6666666666666666,"equity_curve_r_squared":0.918016321908289,"avg_gross_exposure_pct":2.2928950819181733,"avg_net_exposure_pct":-0.8189811228558067,"days_in_market_pct":42.5531914893617},"train":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317074932001383_1585063f","trade_count":220,"profit_factor":1.2595187982631553,"total_return_pct":3.200633818982489,"win_rate":0.5045454545454545,"max_drawdown_pct":1.389607178327899,"sharpe_ratio":0.6336391727317187,"monthly_win_rate":0.6551724137931034,"equity_curve_r_squared":0.39958317262208615}},"sqs_score":86.7,"sqs_breakdown":{"profitability":85.0,"risk":100.0,"consistency":95.8,"robustness":56.7},"verdict":"better","verdict_reasoning":"Capping same-day shorts at -45% reaction preserved train and valid while modestly improving test return, PF, drawdown, and Sharpe versus step45.","next_direction":"Combine the crash cap with a quality filter on the same-day long overlay.","tags":["pead","midcap","step51","short","core","macro","block","crashcap"],"sqs_v2_score":89.6,"sqs_v2_breakdown":{"profitability":85.0,"risk":100.0,"consistency":95.8,"robustness":56.7,"capital_efficiency":100.0}}
{"entry_id":"IMP-0046","timestamp":"2026-03-17T07:54:28.401055+00:00","experiment_name":"pead_midcap_step52_short_core_macro_block_crashcap_gap10","hypothesis":"The same-day long overlay may work better when restricted to larger reaction-day gap moves.","config_delta":{"base_experiment":"pead_midcap_step51_short_core_macro_block_crashcap","changes":{}},"results":{"valid":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317081834927839_7592b1dc","trade_count":25,"profit_factor":5.660464878695176,"total_return_pct":1.818984312375629,"win_rate":0.72,"max_drawdown_pct":0.21695852738272095,"sharpe_ratio":4.755652871139783,"monthly_win_rate":1.0,"equity_curve_r_squared":0.6608034205824956,"avg_gross_exposure_pct":3.5838805580978437,"avg_net_exposure_pct":-2.593156236925373,"days_in_market_pct":56.14035087719298},"test":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317081833658080_7592b1dc","trade_count":22,"profit_factor":3.351800408128271,"total_return_pct":1.0111883417758072,"win_rate":0.7727272727272727,"max_drawdown_pct":0.24257912061402945,"sharpe_ratio":3.031867507475822,"monthly_win_rate":0.6666666666666666,"equity_curve_r_squared":0.8468990955221466,"avg_gross_exposure_pct":2.858350680305373,"avg_net_exposure_pct":-2.222595187645864,"days_in_market_pct":53.191489361702125},"train":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317075225071209_7592b1dc","trade_count":201,"profit_factor":1.3153549627707726,"total_return_pct":3.2988485556152156,"win_rate":0.4975124378109453,"max_drawdown_pct":1.5599163185209133,"sharpe_ratio":0.683194016870928,"monthly_win_rate":0.6923076923076923,"equity_curve_r_squared":0.7186047701175057}},"sqs_score":86.3,"sqs_breakdown":{"profitability":84.0,"risk":100.0,"consistency":95.8,"robustness":56.7},"verdict":"neutral","verdict_reasoning":"A 10% gap filter made train and valid much stronger but gave back some test performance, so this is a balanced alternative rather than a clear new leader.","next_direction":"If optimizing for robustness across splits, keep exploring overlay quality gates around this variant.","tags":["pead","midcap","step52","short","core","macro","block","crashcap","gap10"],"sqs_v2_score":87.2,"sqs_v2_breakdown":{"profitability":84.0,"risk":100.0,"consistency":95.8,"robustness":56.7,"capital_efficiency":79.5}}
{"entry_id":"IMP-0047","timestamp":"2026-03-17T07:54:28.762757+00:00","experiment_name":"pead_midcap_step53_short_core_macro_block_crashcap_gap14","hypothesis":"A stricter same-day long gap filter might further concentrate the overlay into only the strongest continuation setups.","config_delta":{"base_experiment":"pead_midcap_step52_short_core_macro_block_crashcap_gap10","changes":{}},"results":{"valid":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317075123208169_66d09993","trade_count":25,"profit_factor":8.207760543782888,"total_return_pct":2.0180349316014032,"win_rate":0.76,"max_drawdown_pct":0.2179161262122437,"sharpe_ratio":5.288977955005757,"monthly_win_rate":1.0,"equity_curve_r_squared":0.6385560108285373},"test":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317075123225576_66d09993","trade_count":19,"profit_factor":4.599773676047558,"total_return_pct":1.1191525844285641,"win_rate":0.8421052631578947,"max_drawdown_pct":0.21947031826165894,"sharpe_ratio":3.463700507402551,"monthly_win_rate":0.6666666666666666,"equity_curve_r_squared":0.8852420626052896}},"sqs_score":43.1,"sqs_breakdown":{"profitability":84.5,"risk":100.0,"consistency":95.8,"robustness":55.0},"verdict":"worse","verdict_reasoning":"The stricter gap filter over-concentrated the overlay, dropped total trade count below a healthy level, and cratered test SQS.","next_direction":"Use moderate overlay filters only; the strict version is too sparse.","tags":["pead","midcap","step53","short","core","macro","block","crashcap","gap14"]}
{"entry_id":"IMP-0048","timestamp":"2026-03-17T10:10:07.544224+00:00","experiment_name":"pead_midcap_step56_short_core_macro_block_crashcap_gap10_interleave_longtrend25","hypothesis":"Interleaving sleeves instead of raw global-score sorting should help the long trend overlay get allocated earlier and improve realized returns.","config_delta":{"base_experiment":"pead_midcap_step55_short_core_macro_block_crashcap_gap10_longtrend12_sdlong25","changes":{}},"results":{"train":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317100427495678_c4cb6228","trade_count":191,"profit_factor":1.31467289884978,"total_return_pct":3.000532477442888,"win_rate":0.5078534031413613,"max_drawdown_pct":1.1246540668445304,"sharpe_ratio":0.682160145042935,"monthly_win_rate":0.6538461538461539,"equity_curve_r_squared":0.7049623155490459,"avg_gross_exposure_pct":2.1440841165534352,"avg_net_exposure_pct":-0.741975012003786,"days_in_market_pct":28.169014084507044},"valid":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317100431529104_c4cb6228","trade_count":28,"profit_factor":4.8198685654623254,"total_return_pct":1.9154249967419892,"win_rate":0.6785714285714286,"max_drawdown_pct":0.6079718599673478,"sharpe_ratio":3.973180662878923,"monthly_win_rate":1.0,"equity_curve_r_squared":0.5809751515505278,"avg_gross_exposure_pct":4.161026862046388,"avg_net_exposure_pct":-2.684188461871084,"days_in_market_pct":56.14035087719298},"test":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317100434744381_c4cb6228","trade_count":20,"profit_factor":4.034651941086868,"total_return_pct":1.0723281131463445,"win_rate":0.8,"max_drawdown_pct":0.24183984749880666,"sharpe_ratio":3.399127342667149,"monthly_win_rate":0.6666666666666666,"equity_curve_r_squared":0.8859682722741977,"avg_gross_exposure_pct":2.4654753372509486,"avg_net_exposure_pct":-1.477511185545206,"days_in_market_pct":51.06382978723404}},"sqs_score":51.3,"sqs_breakdown":{"valid_quality":82.6,"test_quality":79.4,"floor_quality":79.4,"gap_quality":100.0},"sqs_v2_score":88.3,"sqs_v2_breakdown":{"profitability":84.3,"risk":100.0,"consistency":95.8,"robustness":55.6,"capital_efficiency":90.9},"promotion_score":89.1,"promotion_breakdown":{"valid_quality":89.8,"test_quality":88.3,"floor_quality":88.3},"unified_score":51.3,"unified_breakdown":{"valid_quality":82.6,"test_quality":79.4,"floor_quality":79.4,"gap_quality":100.0},"verdict":"neutral","verdict_reasoning":"Interleaving lifted test PF and return slightly, but train remained below step52 and valid drawdown worsened, so the gain was too narrow.","next_direction":"Keep the stronger same-day long sleeve, but try opening one more candidate slot per day.","tags":["pead","midcap","step56","short","core","macro","block","crashcap","gap10","interleave","longtrend25"]}
{"entry_id":"IMP-0049","timestamp":"2026-03-17T10:10:07.544223+00:00","experiment_name":"pead_midcap_step55_short_core_macro_block_crashcap_gap10_longtrend12_sdlong25","hypothesis":"If the same-day long trend sleeve is real alpha, raising its budget from 12.5% to 25% should lift valid/test returns while keeping drawdown contained.","config_delta":{"base_experiment":"pead_midcap_step52_short_core_macro_block_crashcap_gap10","changes":{}},"results":{"train":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317100414827938_b2c1159a","trade_count":208,"profit_factor":1.3026092902212059,"total_return_pct":3.197582862252573,"win_rate":0.5048076923076923,"max_drawdown_pct":1.3030593106539847,"sharpe_ratio":0.6650887543632845,"monthly_win_rate":0.6538461538461539,"equity_curve_r_squared":0.6942797350314213,"avg_gross_exposure_pct":2.2965079311515697,"avg_net_exposure_pct":-1.068925327205924,"days_in_market_pct":28.38569880823402},"valid":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317100418563512_b2c1159a","trade_count":28,"profit_factor":4.621364494548783,"total_return_pct":1.9321698387879296,"win_rate":0.7142857142857143,"max_drawdown_pct":0.29678396454155964,"sharpe_ratio":4.486982260533271,"monthly_win_rate":1.0,"equity_curve_r_squared":0.6351890668895267,"avg_gross_exposure_pct":3.8320562275604266,"avg_net_exposure_pct":-2.3571340234511924,"days_in_market_pct":56.14035087719298},"test":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317100421770808_b2c1159a","trade_count":23,"profit_factor":3.4044506466943907,"total_return_pct":1.0338260227821447,"win_rate":0.782608695652174,"max_drawdown_pct":0.24257912061402945,"sharpe_ratio":3.1121570919759436,"monthly_win_rate":0.6666666666666666,"equity_curve_r_squared":0.8494393946803189,"avg_gross_exposure_pct":3.034569947217148,"avg_net_exposure_pct":-2.0461795297656566,"days_in_market_pct":53.191489361702125}},"sqs_score":49.8,"sqs_breakdown":{"valid_quality":83.6,"test_quality":76.1,"floor_quality":76.1,"gap_quality":91.7},"sqs_v2_score":87.1,"sqs_v2_breakdown":{"profitability":84.1,"risk":100.0,"consistency":95.8,"robustness":57.2,"capital_efficiency":77.7},"promotion_score":89.1,"promotion_breakdown":{"valid_quality":90.7,"test_quality":87.1,"floor_quality":87.1},"unified_score":49.8,"unified_breakdown":{"valid_quality":83.6,"test_quality":76.1,"floor_quality":76.1,"gap_quality":91.7},"verdict":"neutral","verdict_reasoning":"This was the best balanced trend-hold variant: valid and test returns edged above step52, but train return stayed below baseline, so it did not clear a full all-split improvement bar.","next_direction":"Test whether selection crowding is the bottleneck by interleaving sleeves or allowing one extra daily slot.","tags":["pead","midcap","step55","short","core","macro","block","crashcap","gap10","longtrend12","sdlong25"]}
{"entry_id":"IMP-0050","timestamp":"2026-03-17T10:10:07.544222+00:00","experiment_name":"pead_midcap_step54_short_core_macro_block_crashcap_gap10_longtrend12","hypothesis":"Same-day long close-entry sleeve with 12-day trend hold should improve valid/test without materially hurting the short-core base.","config_delta":{"base_experiment":"pead_midcap_step52_short_core_macro_block_crashcap_gap10","changes":{}},"results":{"train":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317100332469191_e1ab5792","trade_count":201,"profit_factor":1.27971446872751,"total_return_pct":2.88150490621911,"win_rate":0.5024875621890548,"max_drawdown_pct":1.4049225155953227,"sharpe_ratio":0.6136649773412598,"monthly_win_rate":0.6923076923076923,"equity_curve_r_squared":0.7371903755424146,"avg_gross_exposure_pct":2.1995436175888323,"avg_net_exposure_pct":-1.2043129551276188,"days_in_market_pct":27.627302275189596},"valid":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317100351662584_e1ab5792","trade_count":26,"profit_factor":6.099913503899416,"total_return_pct":1.9918550334882748,"win_rate":0.7307692307692307,"max_drawdown_pct":0.21797225956805316,"sharpe_ratio":4.666899835993297,"monthly_win_rate":1.0,"equity_curve_r_squared":0.6469910274326361,"avg_gross_exposure_pct":3.637408212559115,"avg_net_exposure_pct":-2.5520608569407015,"days_in_market_pct":56.14035087719298},"test":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317100408811304_e1ab5792","trade_count":22,"profit_factor":2.991176064691692,"total_return_pct":0.856133035813924,"win_rate":0.7727272727272727,"max_drawdown_pct":0.3359030771970047,"sharpe_ratio":2.4909364067019513,"monthly_win_rate":0.6666666666666666,"equity_curve_r_squared":0.8042967657946622,"avg_gross_exposure_pct":2.921981872114122,"avg_net_exposure_pct":-2.1517863691688386,"days_in_market_pct":53.191489361702125}},"sqs_score":46.5,"sqs_breakdown":{"valid_quality":83.6,"test_quality":70.9,"floor_quality":70.9,"gap_quality":74.3},"sqs_v2_score":86.1,"sqs_v2_breakdown":{"profitability":83.4,"risk":100.0,"consistency":95.8,"robustness":56.7,"capital_efficiency":71.0},"promotion_score":88.6,"promotion_breakdown":{"valid_quality":90.7,"test_quality":86.1,"floor_quality":86.1},"unified_score":46.5,"unified_breakdown":{"valid_quality":83.6,"test_quality":70.9,"floor_quality":70.9,"gap_quality":74.3},"verdict":"neutral","verdict_reasoning":"Long trend sleeve improved valid return and PF, but train and test returns fell versus step52. Improvement was not broad enough across all three splits.","next_direction":"Increase same-day long sleeve budget and test whether the trend hold needs more capital to matter.","tags":["pead","midcap","step54","short","core","macro","block","crashcap","gap10","longtrend12"]}
{"entry_id":"IMP-0051","timestamp":"2026-03-17T10:13:47.820620+00:00","experiment_name":"pead_midcap_step58_short_core_macro_block_crashcap_gap7_longtrend12_sdlong25","hypothesis":"Loosening the same-day long gap filter from 10% to 7% may broaden the trend-hold sleeve enough to lift absolute return across splits.","config_delta":{"base_experiment":"pead_midcap_step55_short_core_macro_block_crashcap_gap10_longtrend12_sdlong25","changes":{}},"results":{"train":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317100701045116_89da3e85","trade_count":217,"profit_factor":1.1836228679446417,"total_return_pct":2.1640217345169948,"win_rate":0.511520737327189,"max_drawdown_pct":1.3943515116639358,"sharpe_ratio":0.41689043269693754,"monthly_win_rate":0.6071428571428571,"equity_curve_r_squared":0.24452603324168182,"avg_gross_exposure_pct":2.48302270403254,"avg_net_exposure_pct":-0.9240632756453898,"days_in_market_pct":29.577464788732392},"valid":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317100705201243_89da3e85","trade_count":29,"profit_factor":2.885213921540522,"total_return_pct":1.6066212170483485,"win_rate":0.6896551724137931,"max_drawdown_pct":0.4958133428812383,"sharpe_ratio":3.6006506236711573,"monthly_win_rate":1.0,"equity_curve_r_squared":0.6323181349051988,"avg_gross_exposure_pct":4.030999519248784,"avg_net_exposure_pct":-1.9829383669087164,"days_in_market_pct":56.14035087719298},"test":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317100708488141_89da3e85","trade_count":23,"profit_factor":2.7627983723004212,"total_return_pct":0.9267893134084734,"win_rate":0.7391304347826086,"max_drawdown_pct":0.24257912061402945,"sharpe_ratio":2.781969702284716,"monthly_win_rate":0.6666666666666666,"equity_curve_r_squared":0.8290340590935003,"avg_gross_exposure_pct":3.172184023694837,"avg_net_exposure_pct":-1.2529216711302178,"days_in_market_pct":53.191489361702125}},"sqs_score":46.6,"sqs_breakdown":{"valid_quality":81.6,"test_quality":71.0,"floor_quality":71.0,"gap_quality":81.3},"sqs_v2_score":86.3,"sqs_v2_breakdown":{"profitability":83.7,"risk":100.0,"consistency":95.8,"robustness":57.2,"capital_efficiency":70.9},"promotion_score":87.7,"promotion_breakdown":{"valid_quality":88.8,"test_quality":86.3,"floor_quality":86.3},"unified_score":46.6,"unified_breakdown":{"valid_quality":81.6,"test_quality":71.0,"floor_quality":71.0,"gap_quality":81.3},"verdict":"worse","verdict_reasoning":"Broadening the long sleeve diluted quality. Train, valid, and test all weakened versus the 10% gap version, so the extra breadth was not productive.","next_direction":"Stay with the tighter 10% gap gate and test whether after-close short should be removed from the max4 portfolio.","tags":["pead","midcap","step58","short","core","macro","block","crashcap","gap7","longtrend12","sdlong25"]}
{"entry_id":"IMP-0052","timestamp":"2026-03-17T10:13:47.821673+00:00","experiment_name":"pead_midcap_step57_short_core_macro_block_crashcap_gap10_longtrend12_sdlong25_max4","hypothesis":"Adding a fourth daily slot should let the positive same-day long trend sleeve coexist with the short-core sleeves and lift total return, especially in train.","config_delta":{"base_experiment":"pead_midcap_step55_short_core_macro_block_crashcap_gap10_longtrend12_sdlong25","changes":{}},"results":{"train":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317100647683228_d591afad","trade_count":214,"profit_factor":1.3583319773998321,"total_return_pct":3.8172175024118773,"win_rate":0.5186915887850467,"max_drawdown_pct":1.3295910930658135,"sharpe_ratio":0.7580236981795051,"monthly_win_rate":0.6538461538461539,"equity_curve_r_squared":0.685718632923443,"avg_gross_exposure_pct":2.3612289576418535,"avg_net_exposure_pct":-1.001836917901861,"days_in_market_pct":28.38569880823402},"valid":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317100651417016_d591afad","trade_count":29,"profit_factor":4.4424890742925465,"total_return_pct":1.9106867186089511,"win_rate":0.6896551724137931,"max_drawdown_pct":0.3179173247540872,"sharpe_ratio":4.511177385736093,"monthly_win_rate":1.0,"equity_curve_r_squared":0.625777909809139,"avg_gross_exposure_pct":3.944739633758862,"avg_net_exposure_pct":-2.4700155661285508,"days_in_market_pct":56.14035087719298},"test":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317100654743633_d591afad","trade_count":24,"profit_factor":2.664801821772631,"total_return_pct":0.91437274196824,"win_rate":0.75,"max_drawdown_pct":0.24257912061402945,"sharpe_ratio":2.893891891447354,"monthly_win_rate":0.6666666666666666,"equity_curve_r_squared":0.8412595666380652,"avg_gross_exposure_pct":3.169455379761105,"avg_net_exposure_pct":-2.1804906875349683,"days_in_market_pct":53.191489361702125}},"sqs_score":46.5,"sqs_breakdown":{"valid_quality":83.5,"test_quality":70.9,"floor_quality":70.9,"gap_quality":74.7},"sqs_v2_score":86.3,"sqs_v2_breakdown":{"profitability":83.7,"risk":100.0,"consistency":95.8,"robustness":57.8,"capital_efficiency":70.4},"promotion_score":88.6,"promotion_breakdown":{"valid_quality":90.4,"test_quality":86.3,"floor_quality":86.3},"unified_score":46.5,"unified_breakdown":{"valid_quality":83.5,"test_quality":70.9,"floor_quality":70.9,"gap_quality":74.7},"verdict":"neutral","verdict_reasoning":"This was the first long-trend variant to beat step52 on train return, but valid and test softened versus the best balanced variants, so it improved absolute return without clearing a robust all-split bar.","next_direction":"Keep max4, then remove or sharply reduce after-close short to see whether it is diluting the higher-conviction sleeves.","tags":["pead","midcap","step57","short","core","macro","block","crashcap","gap10","longtrend12","sdlong25","max4"]}
{"entry_id":"IMP-0053","timestamp":"2026-03-17T10:18:37.790830+00:00","experiment_name":"pead_midcap_step59_same_day_only_max4_longtrend25","hypothesis":"If after-close short is diluting the portfolio, a same-day-only book should lift train return while keeping the positive long trend overlay intact.","config_delta":{"base_experiment":"pead_midcap_step57_short_core_macro_block_crashcap_gap10_longtrend12_sdlong25_max4","changes":{}},"results":{"train":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317101616130067_4ce1573f","trade_count":124,"profit_factor":1.6549151417942642,"total_return_pct":3.4598346586282274,"win_rate":0.5403225806451613,"max_drawdown_pct":1.3037083252920614,"sharpe_ratio":0.8632124891148151,"monthly_win_rate":0.6666666666666666,"equity_curve_r_squared":0.6337366863338719,"avg_gross_exposure_pct":1.4618927749473103,"avg_net_exposure_pct":0.040921478947221035,"days_in_market_pct":19.176598049837487},"valid":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317101626096198_4ce1573f","trade_count":22,"profit_factor":6.627861685804674,"total_return_pct":1.9039976324784367,"win_rate":0.7272727272727273,"max_drawdown_pct":0.44666468803527526,"sharpe_ratio":4.451621602089021,"monthly_win_rate":1.0,"equity_curve_r_squared":0.5794666301732988,"avg_gross_exposure_pct":3.8681902002325703,"avg_net_exposure_pct":-2.3913045273063496,"days_in_market_pct":42.10526315789473},"test":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317101634807938_4ce1573f","trade_count":13,"profit_factor":2.6072978977047137,"total_return_pct":0.5860747837766976,"win_rate":0.6923076923076923,"max_drawdown_pct":0.31275465094584215,"sharpe_ratio":2.308425640364089,"monthly_win_rate":0.6666666666666666,"equity_curve_r_squared":0.6952685722248394,"avg_gross_exposure_pct":1.4491267702359076,"avg_net_exposure_pct":-0.4649535073354661,"days_in_market_pct":31.914893617021278}},"sqs_score":33.2,"sqs_breakdown":{"valid_quality":81.1,"test_quality":49.7,"floor_quality":49.7,"gap_quality":12.0},"sqs_v2_score":42.7,"sqs_v2_breakdown":{"profitability":82.3,"risk":100.0,"consistency":95.8,"robustness":45.1,"capital_efficiency":78.5},"promotion_score":68.6,"promotion_breakdown":{"valid_quality":89.8,"test_quality":42.7,"floor_quality":42.7},"unified_score":33.2,"unified_breakdown":{"valid_quality":81.1,"test_quality":49.7,"floor_quality":49.7,"gap_quality":12.0},"verdict":"worse","verdict_reasoning":"Removing after-close short improved train and valid quality, but test return collapsed from +0.91% to +0.59%. The sleeve is still needed for recent OOS behavior.","next_direction":"Keep after-close short active, but filter it harder so only the deeper negative gaps remain.","tags":["pead","midcap","step59","same","day","only","max4","longtrend25"]}
{"entry_id":"IMP-0054","timestamp":"2026-03-17T10:18:38.178487+00:00","experiment_name":"pead_midcap_step60_same_day_only_interleave_max4_longtrend25","hypothesis":"Interleaving the same-day-only sleeves may preserve the train lift while forcing better balance between short core and long trend entries.","config_delta":{"base_experiment":"pead_midcap_step59_same_day_only_max4_longtrend25","changes":{}},"results":{"train":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317101616097173_ce78581d","trade_count":124,"profit_factor":1.7147359082965992,"total_return_pct":3.794868328446057,"win_rate":0.5403225806451613,"max_drawdown_pct":1.3030513376085362,"sharpe_ratio":0.9460927004826782,"monthly_win_rate":0.6666666666666666,"equity_curve_r_squared":0.645777565512627,"avg_gross_exposure_pct":1.498662692128791,"avg_net_exposure_pct":0.0720408710547762,"days_in_market_pct":19.284940411700973},"valid":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317101626066831_ce78581d","trade_count":22,"profit_factor":6.627861685804674,"total_return_pct":1.9039976324784367,"win_rate":0.7272727272727273,"max_drawdown_pct":0.44666468803527526,"sharpe_ratio":4.451621602089021,"monthly_win_rate":1.0,"equity_curve_r_squared":0.5794666301732988,"avg_gross_exposure_pct":3.8681902002325703,"avg_net_exposure_pct":-2.3913045273063496,"days_in_market_pct":42.10526315789473},"test":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317101634790427_ce78581d","trade_count":13,"profit_factor":2.6072978977047137,"total_return_pct":0.5860747837766976,"win_rate":0.6923076923076923,"max_drawdown_pct":0.31275465094584215,"sharpe_ratio":2.308425640364089,"monthly_win_rate":0.6666666666666666,"equity_curve_r_squared":0.6952685722248394,"avg_gross_exposure_pct":1.4491267702359076,"avg_net_exposure_pct":-0.4649535073354661,"days_in_market_pct":31.914893617021278}},"sqs_score":33.2,"sqs_breakdown":{"valid_quality":81.1,"test_quality":49.7,"floor_quality":49.7,"gap_quality":12.0},"sqs_v2_score":42.7,"sqs_v2_breakdown":{"profitability":82.3,"risk":100.0,"consistency":95.8,"robustness":45.1,"capital_efficiency":78.5},"promotion_score":68.6,"promotion_breakdown":{"valid_quality":89.8,"test_quality":42.7,"floor_quality":42.7},"unified_score":33.2,"unified_breakdown":{"valid_quality":81.1,"test_quality":49.7,"floor_quality":49.7,"gap_quality":12.0},"verdict":"neutral","verdict_reasoning":"Interleaving improved train return to the highest seen in this branch, but valid stayed flat and test remained stuck at +0.59%, so it is a useful clue rather than a promotion candidate.","next_direction":"Apply a stricter filter to after-close short instead of removing it entirely.","tags":["pead","midcap","step60","same","day","only","interleave","max4","longtrend25"]}
{"entry_id":"IMP-0055","timestamp":"2026-03-17T10:18:38.591808+00:00","experiment_name":"pead_midcap_step61_same_day_only_max5_longtrend25","hypothesis":"A fifth daily slot may restore some lost test opportunity while keeping the same-day-only train lift.","config_delta":{"base_experiment":"pead_midcap_step59_same_day_only_max4_longtrend25","changes":{}},"results":{"train":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317101616141383_4177a33d","trade_count":125,"profit_factor":1.6152818286538184,"total_return_pct":3.3281551222560664,"win_rate":0.528,"max_drawdown_pct":1.304467392506448,"sharpe_ratio":0.8410110414784137,"monthly_win_rate":0.6666666666666666,"equity_curve_r_squared":0.6390485285220323,"avg_gross_exposure_pct":1.4445779873336309,"avg_net_exposure_pct":-0.001513116209876324,"days_in_market_pct":19.068255687974},"valid":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317101626106651_4177a33d","trade_count":22,"profit_factor":6.627861685804674,"total_return_pct":1.9039976324784367,"win_rate":0.7272727272727273,"max_drawdown_pct":0.44666468803527526,"sharpe_ratio":4.451621602089021,"monthly_win_rate":1.0,"equity_curve_r_squared":0.5794666301732988,"avg_gross_exposure_pct":3.8681902002325703,"avg_net_exposure_pct":-2.3913045273063496,"days_in_market_pct":42.10526315789473},"test":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317101634804824_4177a33d","trade_count":13,"profit_factor":2.6072978977047137,"total_return_pct":0.5860747837766976,"win_rate":0.6923076923076923,"max_drawdown_pct":0.31275465094584215,"sharpe_ratio":2.308425640364089,"monthly_win_rate":0.6666666666666666,"equity_curve_r_squared":0.6952685722248394,"avg_gross_exposure_pct":1.4491267702359076,"avg_net_exposure_pct":-0.4649535073354661,"days_in_market_pct":31.914893617021278}},"sqs_score":33.2,"sqs_breakdown":{"valid_quality":81.1,"test_quality":49.7,"floor_quality":49.7,"gap_quality":12.0},"sqs_v2_score":42.7,"sqs_v2_breakdown":{"profitability":82.3,"risk":100.0,"consistency":95.8,"robustness":45.1,"capital_efficiency":78.5},"promotion_score":68.6,"promotion_breakdown":{"valid_quality":89.8,"test_quality":42.7,"floor_quality":42.7},"unified_score":33.2,"unified_breakdown":{"valid_quality":81.1,"test_quality":49.7,"floor_quality":49.7,"gap_quality":12.0},"verdict":"worse","verdict_reasoning":"The extra slot did not recover test. It raised trade count slightly but underperformed the interleaved same-day-only variant and still lagged the mixed-sleeve portfolio.","next_direction":"Return to the mixed-sleeve structure and tighten after-close short with a negative gap filter.","tags":["pead","midcap","step61","same","day","only","max5","longtrend25"]}
{"entry_id":"IMP-0056","timestamp":"2026-03-17T10:20:47.944163+00:00","experiment_name":"pead_midcap_step62_short_core_macro_block_crashcap_gap10_longtrend12_sdlong25_max4_acsgap10","hypothesis":"Weak after-close shorts are diluting the max4 mixed-sleeve portfolio; requiring at least a 10% negative gap should keep the sleeve productive while freeing slots for stronger same-day setups.","config_delta":{"base_experiment":"pead_midcap_step57_short_core_macro_block_crashcap_gap10_longtrend12_sdlong25_max4","changes":{}},"results":{"train":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317101945843553_a9942261","trade_count":190,"profit_factor":1.5870238518554496,"total_return_pct":5.006752568666314,"win_rate":0.5473684210526316,"max_drawdown_pct":1.2008626598937386,"sharpe_ratio":1.1008608430630382,"monthly_win_rate":0.64,"equity_curve_r_squared":0.7668760857061504,"avg_gross_exposure_pct":2.1363506899122533,"avg_net_exposure_pct":-0.7060295226606695,"days_in_market_pct":25.785482123510295},"valid":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317101951495313_a9942261","trade_count":26,"profit_factor":4.640144626555104,"total_return_pct":1.7785589226570302,"win_rate":0.6923076923076923,"max_drawdown_pct":0.394474954823559,"sharpe_ratio":4.453025001679815,"monthly_win_rate":1.0,"equity_curve_r_squared":0.5596638198344033,"avg_gross_exposure_pct":3.5028084000751822,"avg_net_exposure_pct":-2.028975630946834,"days_in_market_pct":49.122807017543856},"test":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317101956624074_a9942261","trade_count":20,"profit_factor":3.693559917731055,"total_return_pct":0.9821623910714115,"win_rate":0.8,"max_drawdown_pct":0.23230116536263104,"sharpe_ratio":3.3957804338520745,"monthly_win_rate":0.6666666666666666,"equity_curve_r_squared":0.9033229833419024,"avg_gross_exposure_pct":2.4512706589430846,"avg_net_exposure_pct":-1.4631851279456756,"days_in_market_pct":48.93617021276596}},"sqs_score":50.9,"sqs_breakdown":{"valid_quality":81.7,"test_quality":79.2,"floor_quality":79.2,"gap_quality":100.0},"sqs_v2_score":87.7,"sqs_v2_breakdown":{"profitability":83.9,"risk":100.0,"consistency":95.8,"robustness":55.6,"capital_efficiency":86.1},"promotion_score":88.9,"promotion_breakdown":{"valid_quality":89.9,"test_quality":87.7,"floor_quality":87.7},"unified_score":50.9,"unified_breakdown":{"valid_quality":81.7,"test_quality":79.2,"floor_quality":79.2,"gap_quality":100.0},"verdict":"better","verdict_reasoning":"This filter was a clear improvement over step57: train return jumped from +3.82% to +5.01%, test improved from +0.91% to +0.98%, and drawdown fell. Valid softened slightly but remained strong.","next_direction":"Try the same negative-gap filter with interleaved sleeve selection to see if test can move back above 1.0% without giving up the train lift.","tags":["pead","midcap","step62","short","core","macro","block","crashcap","gap10","longtrend12","sdlong25","max4","acsgap10"]}
{"entry_id":"IMP-0057","timestamp":"2026-03-17T10:20:48.356015+00:00","experiment_name":"pead_midcap_step63_short_core_macro_block_crashcap_gap10_longtrend12_sdlong25_max4_acsgap12","hypothesis":"A stricter 12% negative gap gate on after-close shorts may further improve the mixed-sleeve portfolio by concentrating the short sleeve into only the sharpest downside reactions.","config_delta":{"base_experiment":"pead_midcap_step62_short_core_macro_block_crashcap_gap10_longtrend12_sdlong25_max4_acsgap10","changes":{}},"results":{"train":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317101945804213_83edae67","trade_count":182,"profit_factor":1.6277512600955655,"total_return_pct":5.049056193144744,"win_rate":0.5494505494505495,"max_drawdown_pct":1.2723459513687636,"sharpe_ratio":1.1295117660384937,"monthly_win_rate":0.6666666666666666,"equity_curve_r_squared":0.7296785391968574,"avg_gross_exposure_pct":2.075630672791522,"avg_net_exposure_pct":-0.6452651158022451,"days_in_market_pct":25.243770314192847},"valid":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317101951483361_83edae67","trade_count":24,"profit_factor":5.270593419113103,"total_return_pct":1.7898083823081543,"win_rate":0.7083333333333334,"max_drawdown_pct":0.31708063303845857,"sharpe_ratio":4.878106921358311,"monthly_win_rate":1.0,"equity_curve_r_squared":0.637601759273469,"avg_gross_exposure_pct":3.0974719922506315,"avg_net_exposure_pct":-1.626276620699805,"days_in_market_pct":45.614035087719294},"test":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317101956614713_83edae67","trade_count":19,"profit_factor":3.6038727896341176,"total_return_pct":0.9494594526291912,"win_rate":0.7894736842105263,"max_drawdown_pct":0.23230116536263104,"sharpe_ratio":3.2438715317612528,"monthly_win_rate":0.6666666666666666,"equity_curve_r_squared":0.8958519994688269,"avg_gross_exposure_pct":1.9049388001836767,"avg_net_exposure_pct":-0.9171395725318859,"days_in_market_pct":38.297872340425535}},"sqs_score":38.2,"sqs_breakdown":{"valid_quality":82.3,"test_quality":57.7,"floor_quality":57.7,"gap_quality":34.7},"sqs_v2_score":44.4,"sqs_v2_breakdown":{"profitability":83.8,"risk":100.0,"consistency":95.8,"robustness":55.0,"capital_efficiency":98.1},"promotion_score":69.6,"promotion_breakdown":{"valid_quality":90.3,"test_quality":44.4,"floor_quality":44.4},"unified_score":38.2,"unified_breakdown":{"valid_quality":82.3,"test_quality":57.7,"floor_quality":57.7,"gap_quality":34.7},"verdict":"better","verdict_reasoning":"The 12% gate slightly improved train and valid versus step62 while keeping test near 0.95% with lower drawdown than the old mixed-sleeve base. This is the strongest return-first variant so far.","next_direction":"Combine the after-close gap gate with interleaved max4 sleeve selection to test whether test return can recover toward the 1.0%+ level.","tags":["pead","midcap","step63","short","core","macro","block","crashcap","gap10","longtrend12","sdlong25","max4","acsgap12"]}
{"entry_id":"IMP-0058","timestamp":"2026-03-17T10:22:41.050471+00:00","experiment_name":"pead_midcap_step64_short_core_macro_block_crashcap_gap10_interleave_max4_acsgap10","hypothesis":"Interleaving the filtered mixed-sleeve portfolio might recover some of the earlier test strength without sacrificing the new train lift from the after-close gap gate.","config_delta":{"base_experiment":"pead_midcap_step62_short_core_macro_block_crashcap_gap10_longtrend12_sdlong25_max4_acsgap10","changes":{}},"results":{"train":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317102155738633_2e938802","trade_count":191,"profit_factor":1.5695913044241423,"total_return_pct":5.006400905086412,"win_rate":0.5392670157068062,"max_drawdown_pct":1.5104386424090617,"sharpe_ratio":1.1022536648052863,"monthly_win_rate":0.64,"equity_curve_r_squared":0.7673617882130458,"avg_gross_exposure_pct":2.194526583220677,"avg_net_exposure_pct":-0.6654873263018454,"days_in_market_pct":26.00216684723727},"valid":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317102201332816_2e938802","trade_count":26,"profit_factor":4.330878953751208,"total_return_pct":1.675907371790352,"win_rate":0.6538461538461539,"max_drawdown_pct":0.608675665764912,"sharpe_ratio":3.7033259316462277,"monthly_win_rate":1.0,"equity_curve_r_squared":0.527209136324793,"avg_gross_exposure_pct":3.7950928397401578,"avg_net_exposure_pct":-2.3215832255311892,"days_in_market_pct":49.122807017543856},"test":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317102206621120_2e938802","trade_count":20,"profit_factor":3.693559917731055,"total_return_pct":0.9821623910714115,"win_rate":0.8,"max_drawdown_pct":0.23230116536263104,"sharpe_ratio":3.3957804338520745,"monthly_win_rate":0.6666666666666666,"equity_curve_r_squared":0.9033229833419024,"avg_gross_exposure_pct":2.4512706589430846,"avg_net_exposure_pct":-1.4631851279456756,"days_in_market_pct":48.93617021276596}},"sqs_score":50.6,"sqs_breakdown":{"valid_quality":80.6,"test_quality":79.2,"floor_quality":79.2,"gap_quality":100.0},"sqs_v2_score":87.7,"sqs_v2_breakdown":{"profitability":83.9,"risk":100.0,"consistency":95.8,"robustness":55.6,"capital_efficiency":86.1},"promotion_score":88.2,"promotion_breakdown":{"valid_quality":88.7,"test_quality":87.7,"floor_quality":87.7},"unified_score":50.6,"unified_breakdown":{"valid_quality":80.6,"test_quality":79.2,"floor_quality":79.2,"gap_quality":100.0},"verdict":"worse","verdict_reasoning":"Test held steady, but valid return and drawdown got materially worse while train did not improve. The gap filter works better with raw global-score ranking than with interleaving.","next_direction":"Keep global-score selection and treat step62/63 as the active return-first branch.","tags":["pead","midcap","step64","short","core","macro","block","crashcap","gap10","interleave","max4","acsgap10"]}
{"entry_id":"IMP-0059","timestamp":"2026-03-17T10:22:41.560235+00:00","experiment_name":"pead_midcap_step65_short_core_macro_block_crashcap_gap10_interleave_max4_acsgap12","hypothesis":"A stricter after-close gap gate plus interleaving may produce the strongest hybrid of train lift and balanced sleeve participation.","config_delta":{"base_experiment":"pead_midcap_step63_short_core_macro_block_crashcap_gap10_longtrend12_sdlong25_max4_acsgap12","changes":{}},"results":{"train":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317102155749766_057bd373","trade_count":182,"profit_factor":1.6174212176205378,"total_return_pct":5.077465154216028,"win_rate":0.5439560439560439,"max_drawdown_pct":1.5094301004863888,"sharpe_ratio":1.1510607734250322,"monthly_win_rate":0.6666666666666666,"equity_curve_r_squared":0.7322022304994754,"avg_gross_exposure_pct":2.127978254795758,"avg_net_exposure_pct":-0.5837963895741501,"days_in_market_pct":25.46045503791983},"valid":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317102201332544_057bd373","trade_count":24,"profit_factor":4.557936520788986,"total_return_pct":1.5351686099939834,"win_rate":0.6666666666666666,"max_drawdown_pct":0.5303323192221483,"sharpe_ratio":3.9755987852769445,"monthly_win_rate":1.0,"equity_curve_r_squared":0.6279656300903311,"avg_gross_exposure_pct":3.4173394039982,"avg_net_exposure_pct":-1.9446456373142142,"days_in_market_pct":47.368421052631575},"test":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317102206620963_057bd373","trade_count":19,"profit_factor":3.6038727896341176,"total_return_pct":0.9494594526291912,"win_rate":0.7894736842105263,"max_drawdown_pct":0.23230116536263104,"sharpe_ratio":3.2438715317612528,"monthly_win_rate":0.6666666666666666,"equity_curve_r_squared":0.8958519994688269,"avg_gross_exposure_pct":1.9049388001836767,"avg_net_exposure_pct":-0.9171395725318859,"days_in_market_pct":38.297872340425535}},"sqs_score":38.3,"sqs_breakdown":{"valid_quality":81.1,"test_quality":57.7,"floor_quality":57.7,"gap_quality":38.7},"sqs_v2_score":44.4,"sqs_v2_breakdown":{"profitability":83.8,"risk":100.0,"consistency":95.8,"robustness":55.0,"capital_efficiency":98.1},"promotion_score":69.0,"promotion_breakdown":{"valid_quality":89.1,"test_quality":44.4,"floor_quality":44.4},"unified_score":38.3,"unified_breakdown":{"valid_quality":81.1,"test_quality":57.7,"floor_quality":57.7,"gap_quality":38.7},"verdict":"worse","verdict_reasoning":"Train ticked up slightly, but valid deteriorated meaningfully and test did not improve. Interleaving is not helping this filtered branch.","next_direction":"Stay with the non-interleaved filtered short sleeve; the next branch should tune the filtered after-close short only if we need more test return.","tags":["pead","midcap","step65","short","core","macro","block","crashcap","gap10","interleave","max4","acsgap12"]}
{"entry_id":"IMP-0060","timestamp":"2026-03-17T10:26:25.356022+00:00","experiment_name":"pead_midcap_step66_short_core_macro_block_crashcap_gap10_longtrend12_sdlong25_max4_acsgap10_react12","hypothesis":"Adding a 12% downside reaction requirement on top of the 10% after-close gap filter may remove the weakest residual after-close shorts without sacrificing the recent OOS edge.","config_delta":{"base_experiment":"pead_midcap_step62_short_core_macro_block_crashcap_gap10_longtrend12_sdlong25_max4_acsgap10","changes":{}},"results":{"train":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317102528258441_f85583ef","trade_count":187,"profit_factor":1.6195866594933024,"total_return_pct":5.086267802803064,"win_rate":0.5508021390374331,"max_drawdown_pct":1.2717965470897303,"sharpe_ratio":1.1327429527860058,"monthly_win_rate":0.64,"equity_curve_r_squared":0.7645174872600673,"avg_gross_exposure_pct":2.1004912456069564,"avg_net_exposure_pct":-0.671173830932749,"days_in_market_pct":25.46045503791983},"valid":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317102534670418_f85583ef","trade_count":26,"profit_factor":4.640144626555104,"total_return_pct":1.7785589226570302,"win_rate":0.6923076923076923,"max_drawdown_pct":0.394474954823559,"sharpe_ratio":4.453025001679815,"monthly_win_rate":1.0,"equity_curve_r_squared":0.5596638198344033,"avg_gross_exposure_pct":3.5028084000751822,"avg_net_exposure_pct":-2.028975630946834,"days_in_market_pct":49.122807017543856},"test":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317102539620352_f85583ef","trade_count":20,"profit_factor":3.693559917731055,"total_return_pct":0.9821623910714115,"win_rate":0.8,"max_drawdown_pct":0.23230116536263104,"sharpe_ratio":3.3957804338520745,"monthly_win_rate":0.6666666666666666,"equity_curve_r_squared":0.9033229833419024,"avg_gross_exposure_pct":2.4512706589430846,"avg_net_exposure_pct":-1.4631851279456756,"days_in_market_pct":48.93617021276596}},"sqs_score":50.9,"sqs_breakdown":{"valid_quality":81.7,"test_quality":79.2,"floor_quality":79.2,"gap_quality":100.0},"sqs_v2_score":87.7,"sqs_v2_breakdown":{"profitability":83.9,"risk":100.0,"consistency":95.8,"robustness":55.6,"capital_efficiency":86.1},"promotion_score":88.9,"promotion_breakdown":{"valid_quality":89.9,"test_quality":87.7,"floor_quality":87.7},"unified_score":50.9,"unified_breakdown":{"valid_quality":81.7,"test_quality":79.2,"floor_quality":79.2,"gap_quality":100.0},"verdict":"better","verdict_reasoning":"This matched step62 on valid/test while lifting train from +5.01% to +5.09%. It is a cleaner version of the filtered short-sleeve branch with no observable downside so far.","next_direction":"Use step66 as the balanced return-first branch; only test further changes if they can raise test above +1.0% without giving back the train lift.","tags":["pead","midcap","step66","short","core","macro","block","crashcap","gap10","longtrend12","sdlong25","max4","acsgap10","react12"]}
{"entry_id":"IMP-0061","timestamp":"2026-03-17T10:26:25.840092+00:00","experiment_name":"pead_midcap_step67_short_core_macro_block_crashcap_gap10_longtrend12_sdlong25_max4_acsgap10_react14","hypothesis":"A stricter 14% downside reaction requirement may further improve the filtered after-close short sleeve by keeping only the sharpest downside continuation setups.","config_delta":{"base_experiment":"pead_midcap_step66_short_core_macro_block_crashcap_gap10_longtrend12_sdlong25_max4_acsgap10_react12","changes":{}},"results":{"train":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317102528258779_fbf8df06","trade_count":176,"profit_factor":1.72971812082782,"total_return_pct":5.315512807515508,"win_rate":0.5625,"max_drawdown_pct":1.1823507388022194,"sharpe_ratio":1.1862508329604826,"monthly_win_rate":0.625,"equity_curve_r_squared":0.7409760880151399,"avg_gross_exposure_pct":1.9641530768509556,"avg_net_exposure_pct":-0.5086575725921745,"days_in_market_pct":24.918743228602384},"valid":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317102534670454_fbf8df06","trade_count":25,"profit_factor":5.331499198553463,"total_return_pct":1.8247351197415846,"win_rate":0.72,"max_drawdown_pct":0.480176430930607,"sharpe_ratio":4.44451943654,"monthly_win_rate":1.0,"equity_curve_r_squared":0.5922580595870112,"avg_gross_exposure_pct":3.1678311806010018,"avg_net_exposure_pct":-1.6948561495104018,"days_in_market_pct":47.368421052631575},"test":{"run_id":"bt_baseline_swing_v1_midcap-filte_20260317102539620462_fbf8df06","trade_count":19,"profit_factor":3.6038727896341176,"total_return_pct":0.9494594526291912,"win_rate":0.7894736842105263,"max_drawdown_pct":0.23230116536263104,"sharpe_ratio":3.2438715317612528,"monthly_win_rate":0.6666666666666666,"equity_curve_r_squared":0.8958519994688269,"avg_gross_exposure_pct":1.9049388001836767,"avg_net_exposure_pct":-0.9171395725318859,"days_in_market_pct":38.297872340425535}},"sqs_score":38.2,"sqs_breakdown":{"valid_quality":82.1,"test_quality":57.7,"floor_quality":57.7,"gap_quality":35.3},"sqs_v2_score":44.4,"sqs_v2_breakdown":{"profitability":83.8,"risk":100.0,"consistency":95.8,"robustness":55.0,"capital_efficiency":98.1},"promotion_score":69.5,"promotion_breakdown":{"valid_quality":90.1,"test_quality":44.4,"floor_quality":44.4},"unified_score":38.2,"unified_breakdown":{"valid_quality":82.1,"test_quality":57.7,"floor_quality":57.7,"gap_quality":35.3},"verdict":"neutral","verdict_reasoning":"This pushed train to +5.32% and lifted valid slightly, but test slipped back to +0.95%. It is a stronger train-focused branch, not a clear overall winner versus step66.","next_direction":"Favor step66 for balance; step67 is only useful if we optimize explicitly for train-heavy return.","tags":["pead","midcap","step67","short","core","macro","block","crashcap","gap10","longtrend12","sdlong25","max4","acsgap10","react14"]}

@ -8,6 +8,7 @@ from libs.backtest.domain import (
BacktestConfig,
Candidate,
DailyPortfolioState,
ExecutionConfig,
EventTypeProfile,
OpenPosition,
PlannedOrder,
@ -212,6 +213,7 @@ def build_planned_order(
portfolio_state: DailyPortfolioState,
open_positions: list[OpenPosition],
config: BacktestConfig,
execution_config: ExecutionConfig | None = None,
cooldown_remaining: int = 0,
macro_data: dict[str, Any] | None = None,
engine_daily_new_risk_used: float = 0.0,
@ -223,6 +225,8 @@ def build_planned_order(
engine_daily_new_risk_used=engine_daily_new_risk_used,
)
exec_cfg = execution_config or config.execution
# Apply event-type-specific overrides for stop/target ATR multipliers
profile = config.get_event_profile(candidate.event_type)
stop_atr_mult = (
@ -231,20 +235,24 @@ def build_planned_order(
else config.risk.stop_atr_multiplier
)
target_atr_mult = (
profile.target_atr_multiplier_override
if profile and profile.target_atr_multiplier_override is not None
else config.execution.target_atr_multiplier
candidate.engine_target_atr_multiplier
if candidate.engine_target_atr_multiplier is not None
else (
profile.target_atr_multiplier_override
if profile and profile.target_atr_multiplier_override is not None
else exec_cfg.target_atr_multiplier
)
)
stop_price = compute_stop_price(
candidate, RiskConfig(**{**config.risk.model_dump(), "stop_atr_multiplier": stop_atr_mult})
)
target_r = config.execution.target_1_r or 2.0
target_r = exec_cfg.target_1_r or 2.0
target_price = compute_target_price(
candidate.entry_price_est,
stop_price,
target_r,
target_model=config.execution.target_model,
target_model=exec_cfg.target_model,
target_atr_multiplier=target_atr_mult,
atr_14=candidate.atr_14,
trade_direction=candidate.trade_direction,

@ -58,6 +58,10 @@ class Candidate(BaseModel):
shadow_only: bool = False
engine_max_holding_days: int | None = None
engine_risk_budget_pct: float = 1.0
engine_target_atr_multiplier: float | None = None
engine_target_1_fraction: float | None = None
engine_trailing_model: str | None = None
engine_trailing_warmup_days: int | None = None
trade_direction: str = "long" # "long" or "short"
features: dict[str, Any] = Field(default_factory=dict)
@ -257,6 +261,10 @@ class StrategyEngineConfig(BaseModel):
entry_timing_policy: str = "next_open" # "next_open", "reaction_close"
max_holding_days: int | None = None
engine_risk_budget_pct: float = 1.0
target_atr_multiplier_override: float | None = None
target_1_fraction_override: float | None = None
trailing_model_override: str | None = None
trailing_warmup_days_override: int | None = None
score_threshold_override: float | None = None
pead_reaction_threshold_override: float | None = None
pead_volume_threshold_override: float | None = None

@ -146,6 +146,26 @@ def build_candidate(
if strategy_engine
else 1.0
),
engine_target_atr_multiplier=(
strategy_engine.target_atr_multiplier_override
if strategy_engine
else None
),
engine_target_1_fraction=(
strategy_engine.target_1_fraction_override
if strategy_engine
else None
),
engine_trailing_model=(
strategy_engine.trailing_model_override
if strategy_engine
else None
),
engine_trailing_warmup_days=(
strategy_engine.trailing_warmup_days_override
if strategy_engine
else None
),
trade_direction=trade_direction,
features={k: v for k, v in row.items() if k not in _RESERVED_KEYS},
)

@ -1,10 +1,12 @@
"""Strategy improvement tracker: SQS computation, journal I/O, leaderboard."""
from __future__ import annotations
import contextlib
import functools
import fcntl
import json
from pathlib import Path
from typing import Any
from typing import Any, Iterator
from libs.backtest.domain import (
ConfigDelta,
@ -500,6 +502,19 @@ def get_next_entry_id(journal_path: Path) -> str:
return f"IMP-{len(entries) + 1:04d}"
@contextlib.contextmanager
def journal_lock(journal_path: Path) -> Iterator[None]:
"""Serialize journal mutations across concurrent record commands."""
journal_path.parent.mkdir(parents=True, exist_ok=True)
lock_path = journal_path.with_suffix(f"{journal_path.suffix}.lock")
with lock_path.open("a+") as lock_file:
fcntl.flock(lock_file.fileno(), fcntl.LOCK_EX)
try:
yield
finally:
fcntl.flock(lock_file.fileno(), fcntl.LOCK_UN)
def append_journal_entry(journal_path: Path, entry: JournalEntry) -> None:
"""Append a single JournalEntry as one JSON line."""
journal_path.parent.mkdir(parents=True, exist_ok=True)

@ -1,5 +1,6 @@
"""Stock Oracle typed client library."""
from libs.oracle_client.attention import AttentionService
from libs.oracle_client.client import OracleClient, make_oracle_client
from libs.oracle_client.company import CompanyService
from libs.oracle_client.filings import FilingsService
@ -10,6 +11,7 @@ from libs.oracle_client.price import PriceService
from libs.oracle_client.screener import ScreenerService
__all__ = [
"AttentionService",
"OracleClient",
"make_oracle_client",
"CompanyService",

@ -0,0 +1,64 @@
"""Attention-related Oracle service methods."""
from __future__ import annotations
import datetime as dt
from libs.oracle_client.client import OracleClient
from libs.oracle_client.models import (
CollectionStatusResponse,
EntityResolveResponse,
EventAttentionResponse,
)
def _date_to_iso(event_date: str | dt.date) -> str:
if isinstance(event_date, dt.date):
return event_date.isoformat()
return event_date
class AttentionService:
def __init__(self, client: OracleClient) -> None:
self._client = client
async def get_entity(self, ticker: str) -> EntityResolveResponse:
data = await self._client.get(f"/api/v1/attention/entity/{ticker}")
return EntityResolveResponse.model_validate(data)
async def resolve_entity(self, ticker: str) -> EntityResolveResponse:
data = await self._client.post(f"/api/v1/attention/admin/resolve/{ticker}")
return EntityResolveResponse.model_validate(data)
async def get_event_attention(
self,
ticker: str,
event_date: str | dt.date,
) -> EventAttentionResponse:
data = await self._client.get(
f"/api/v1/attention/event/{ticker}",
params={"event_date": _date_to_iso(event_date)},
)
return EventAttentionResponse.model_validate(data)
async def collect_wiki(
self,
ticker: str,
event_date: str | dt.date,
) -> CollectionStatusResponse:
data = await self._client.post(
f"/api/v1/attention/admin/collect/wiki/{ticker}",
params={"event_date": _date_to_iso(event_date)},
)
return CollectionStatusResponse.model_validate(data)
async def collect_gdelt(
self,
ticker: str,
event_date: str | dt.date,
) -> CollectionStatusResponse:
data = await self._client.post(
f"/api/v1/attention/admin/collect/gdelt/{ticker}",
params={"event_date": _date_to_iso(event_date)},
)
return CollectionStatusResponse.model_validate(data)

@ -54,10 +54,15 @@ class OracleClient:
return self._handle_response(response, path)
@with_retry(max_attempts=3, min_wait=0.1, max_wait=5.0, multiplier=0.1)
async def post(self, path: str, json: dict[str, Any] | None = None) -> Any:
async def post(
self,
path: str,
json: dict[str, Any] | None = None,
params: dict[str, Any] | None = None,
) -> Any:
client = self._ensure_client()
try:
response = await client.post(path, json=json)
response = await client.post(path, json=json, params=params)
except httpx.ConnectError as exc:
raise OracleConnectionError(str(exc), source="oracle", entity=path) from exc
except httpx.TimeoutException as exc:

@ -200,3 +200,57 @@ class ScreenerResponse(BaseModel):
total: int = 0
page: int = 1
page_size: int = 250
# ---------------------------------------------------------------------------
# Attention
# ---------------------------------------------------------------------------
class EntityInfo(BaseModel):
ticker: str
canonical_name: str
wiki_title: str | None = None
gdelt_query: str | None = None
aliases: list[str] = Field(default_factory=list)
resolver_confidence: float = 0.0
is_manual_override: bool = False
class EntityResolveResponse(BaseModel):
ticker: str
entity: EntityInfo
status: str
message: str
class WikiFeatures(BaseModel):
views: int | None = None
baseline_10d: float | None = None
spike_10d: float | None = None
zscore_20d: float | None = None
class NewsFeatures(BaseModel):
article_count_1d: int = 0
article_count_3d: int = 0
unique_domains_3d: int = 0
us_article_count_3d: int = 0
gdelt_status: str = "not_collected"
class EventAttentionResponse(BaseModel):
ticker: str
event_date: str
entity: EntityInfo
wiki: WikiFeatures
news: NewsFeatures
metadata: dict[str, Any] = Field(default_factory=dict)
class CollectionStatusResponse(BaseModel):
ticker: str
source: str
records_collected: int
date_range: dict[str, Any] = Field(default_factory=dict)
status: str

@ -0,0 +1,9 @@
{
"ticker": "AAPL",
"source": "wiki",
"records_collected": 0,
"date_range": {
"event_date": "2024-02-01"
},
"status": "success"
}

@ -0,0 +1,14 @@
{
"ticker": "AAPL",
"entity": {
"ticker": "AAPL",
"canonical_name": "Apple",
"wiki_title": "Apple Inc.",
"gdelt_query": "\"Apple\" OR \"Apple Inc.\"",
"aliases": ["Apple Inc."],
"resolver_confidence": 0.95,
"is_manual_override": false
},
"status": "exists",
"message": "Entity mapping retrieved from database."
}

@ -0,0 +1,30 @@
{
"ticker": "AAPL",
"event_date": "2024-02-01",
"entity": {
"ticker": "AAPL",
"canonical_name": "Apple",
"wiki_title": "Apple Inc.",
"gdelt_query": "\"Apple\" OR \"Apple Inc.\"",
"aliases": ["Apple Inc."],
"resolver_confidence": 0.95,
"is_manual_override": false
},
"wiki": {
"views": 28837,
"baseline_10d": 50133.5,
"spike_10d": 0.5752,
"zscore_20d": -4.7184
},
"news": {
"article_count_1d": 0,
"article_count_3d": 0,
"unique_domains_3d": 0,
"us_article_count_3d": 0,
"gdelt_status": "not_collected"
},
"metadata": {
"wiki_title": "Apple Inc.",
"resolver_confidence": 0.95
}
}

@ -391,3 +391,69 @@ class TestBacktestRunIntegration:
assert "avg_gross_exposure_pct" in metrics_summary
assert "avg_net_exposure_pct" in metrics_summary
assert "days_in_market_pct" in metrics_summary
def test_engine_execution_overrides_flow_into_effective_execution_config(self):
from apps.backtester.run import BacktestRunner
from libs.backtest.domain import Candidate, ExperimentManifest, StrategyEngineConfig
store = _build_multi_engine_store()
manifest = ExperimentManifest(
experiment_name="portfolio_exec_overrides",
dataset_snapshot_id="test_snapshot",
base_config="configs/backtest/defaults.json",
overrides={},
strategy_engines=[
StrategyEngineConfig(
engine_id="earnings_same_day_long_trend_v1",
event_types=["earnings_release"],
timing_class="same_day",
direction="long_only",
entry_timing_policy="reaction_close",
max_holding_days=12,
engine_risk_budget_pct=0.25,
target_atr_multiplier_override=2.5,
target_1_fraction_override=0.33,
trailing_model_override="pct_10",
trailing_warmup_days_override=2,
),
],
)
config = _make_config(strategy_engines=manifest.strategy_engines)
runner = BacktestRunner(manifest=manifest, config=config, store=store, initial_equity=100_000.0)
candidate = Candidate(
event_id="EVT::SD::LONG",
symbol="AMD",
issuer_id="ISSUER::AMD",
score=0.92,
sector="Technology",
event_type="earnings_release",
event_timestamp=dt.datetime(2026, 1, 6, 21, 0, tzinfo=_UTC),
event_date=dt.date(2026, 1, 6),
filing_time_bucket="post_market",
timing_class="same_day",
reaction_date=dt.date(2026, 1, 6),
execution_date=dt.date(2026, 1, 6),
entry_price_est=122.0,
avg_dollar_volume=9_000_000.0,
atr_14=3.0,
score_bucket="high",
engine_id="earnings_same_day_long_trend_v1",
entry_timing_policy="reaction_close",
shadow_only=False,
engine_max_holding_days=12,
engine_risk_budget_pct=0.25,
engine_target_atr_multiplier=2.5,
engine_target_1_fraction=0.33,
engine_trailing_model="pct_10",
engine_trailing_warmup_days=2,
trade_direction="long",
)
effective_exec = runner._build_effective_execution_config(candidate)
assert candidate.engine_id == "earnings_same_day_long_trend_v1"
assert effective_exec.max_holding_days == 12
assert effective_exec.target_atr_multiplier == pytest.approx(2.5)
assert effective_exec.target_1_fraction == pytest.approx(0.33)
assert effective_exec.trailing_model == "pct_10"
assert effective_exec.trailing_warmup_days == 2

@ -143,6 +143,38 @@ class TestBuildCandidate:
assert c.engine_id == "earnings_same_day_long_close_v1"
assert c.entry_timing_policy == "reaction_close"
def test_engine_execution_overrides_are_copied_to_candidate(self):
from libs.backtest.selector import build_candidate
engine = StrategyEngineConfig(
engine_id="earnings_same_day_long_trend_v1",
event_types=["earnings"],
timing_class="same_day",
direction="long_only",
entry_timing_policy="reaction_close",
max_holding_days=12,
engine_risk_budget_pct=0.25,
target_atr_multiplier_override=2.5,
target_1_fraction_override=0.33,
trailing_model_override="pct_10",
trailing_warmup_days_override=2,
)
row = _make_raw_row(
event_date="2026-01-06",
reaction_date="2026-01-06",
event_close=149.5,
entry_date="2026-01-07",
reaction_day_return=0.11,
)
c = build_candidate(row, strategy_engine=engine)
assert c is not None
assert c.engine_max_holding_days == 12
assert c.engine_risk_budget_pct == pytest.approx(0.25)
assert c.engine_target_atr_multiplier == pytest.approx(2.5)
assert c.engine_target_1_fraction == pytest.approx(0.33)
assert c.engine_trailing_model == "pct_10"
assert c.engine_trailing_warmup_days == 2
def test_engine_route_skips_non_matching_direction(self):
from libs.backtest.selector import build_candidate

@ -2,6 +2,8 @@
from __future__ import annotations
import json
import multiprocessing
import time
from pathlib import Path
import pytest
@ -26,11 +28,30 @@ from libs.backtest.tracker import (
compute_unified_score,
compute_unified_split_quality,
get_next_entry_id,
journal_lock,
load_journal,
rebuild_registry,
)
def _write_locked_journal_entry(payload: tuple[str, str]) -> str:
journal_path_str, experiment_name = payload
journal_path = Path(journal_path_str)
with journal_lock(journal_path):
entry_id = get_next_entry_id(journal_path)
time.sleep(0.05)
append_journal_entry(
journal_path,
JournalEntry(
entry_id=entry_id,
timestamp="2026-03-17T10:10:07+00:00",
experiment_name=experiment_name,
hypothesis="h",
),
)
return entry_id
# ---------------------------------------------------------------------------
# _normalize / _normalize_inverse
# ---------------------------------------------------------------------------
@ -514,6 +535,24 @@ class TestJournalIO:
append_journal_entry(journal_path, entry)
assert get_next_entry_id(journal_path) == "IMP-0002"
def test_journal_lock_serializes_concurrent_writers(self, tmp_path):
journal_path = tmp_path / "journal.jsonl"
ctx = multiprocessing.get_context("spawn")
payloads = [
(str(journal_path), "exp_a"),
(str(journal_path), "exp_b"),
(str(journal_path), "exp_c"),
]
with ctx.Pool(processes=3) as pool:
ids = pool.map(_write_locked_journal_entry, payloads)
assert sorted(ids) == ["IMP-0001", "IMP-0002", "IMP-0003"]
assert [entry.entry_id for entry in load_journal(journal_path)] == [
"IMP-0001",
"IMP-0002",
"IMP-0003",
]
def test_load_empty(self, tmp_path):
journal_path = tmp_path / "nonexistent.jsonl"
entries = load_journal(journal_path)

@ -207,3 +207,112 @@ async def test_client_without_context_manager_raises():
client = OracleClient("http://oracle:18001")
with pytest.raises(RuntimeError, match="async context manager"):
await client.get("/health")
@pytest.mark.asyncio
async def test_get_attention_entity(httpx_mock: HTTPXMock):
from libs.oracle_client.attention import AttentionService
from libs.oracle_client.client import OracleClient
data = load_fixture("attention_entity.json")
httpx_mock.add_response(
json=data,
url="http://oracle:18001/api/v1/attention/entity/AAPL",
)
async with OracleClient("http://oracle:18001") as client:
svc = AttentionService(client)
result = await svc.get_entity("AAPL")
assert result.ticker == "AAPL"
assert result.entity.canonical_name == "Apple"
assert result.entity.wiki_title == "Apple Inc."
assert result.status == "exists"
@pytest.mark.asyncio
async def test_get_event_attention(httpx_mock: HTTPXMock):
import datetime as dt
from libs.oracle_client.attention import AttentionService
from libs.oracle_client.client import OracleClient
data = load_fixture("attention_event.json")
httpx_mock.add_response(
json=data,
url="http://oracle:18001/api/v1/attention/event/AAPL?event_date=2024-02-01",
)
async with OracleClient("http://oracle:18001") as client:
svc = AttentionService(client)
result = await svc.get_event_attention("AAPL", dt.date(2024, 2, 1))
assert result.ticker == "AAPL"
assert result.event_date == "2024-02-01"
assert result.wiki.views == 28837
assert result.news.gdelt_status == "not_collected"
@pytest.mark.asyncio
async def test_resolve_attention_entity(httpx_mock: HTTPXMock):
from libs.oracle_client.attention import AttentionService
from libs.oracle_client.client import OracleClient
data = load_fixture("attention_entity.json")
data["status"] = "resolved"
data["message"] = "Entity resolved: wiki_title='Apple Inc.' confidence=0.95"
httpx_mock.add_response(
json=data,
method="POST",
url="http://oracle:18001/api/v1/attention/admin/resolve/AAPL",
)
async with OracleClient("http://oracle:18001") as client:
svc = AttentionService(client)
result = await svc.resolve_entity("AAPL")
assert result.status == "resolved"
assert result.entity.resolver_confidence == 0.95
@pytest.mark.asyncio
async def test_collect_attention_wiki(httpx_mock: HTTPXMock):
from libs.oracle_client.attention import AttentionService
from libs.oracle_client.client import OracleClient
data = load_fixture("attention_collect.json")
httpx_mock.add_response(
json=data,
method="POST",
url="http://oracle:18001/api/v1/attention/admin/collect/wiki/AAPL?event_date=2024-02-01",
)
async with OracleClient("http://oracle:18001") as client:
svc = AttentionService(client)
result = await svc.collect_wiki("AAPL", "2024-02-01")
assert result.ticker == "AAPL"
assert result.source == "wiki"
assert result.records_collected == 0
@pytest.mark.asyncio
async def test_collect_attention_gdelt(httpx_mock: HTTPXMock):
from libs.oracle_client.attention import AttentionService
from libs.oracle_client.client import OracleClient
data = load_fixture("attention_collect.json")
data["source"] = "gdelt"
data["records_collected"] = 12
httpx_mock.add_response(
json=data,
method="POST",
url="http://oracle:18001/api/v1/attention/admin/collect/gdelt/AAPL?event_date=2024-02-01",
)
async with OracleClient("http://oracle:18001") as client:
svc = AttentionService(client)
result = await svc.collect_gdelt("AAPL", "2024-02-01")
assert result.source == "gdelt"
assert result.records_collected == 12

Loading…
Cancel
Save