research: timestamped Yahoo analyst snapshots and a forward-only EPS-revision overlay
collect archives Yahoo's earnings/revenue estimates, EPS trend, EPS revisions and price targets as immutable snapshots; a snapshot is usable only after its created_at, so the vendor's 30/90-day history never reaches earlier bars. evaluate replays overnight_recovery_top10 with an EPS-revision re-rank of the quant top 50 on the same ledger and costs and returns NEEDS EVIDENCE until 252 active sessions from 20 collection dates exist. The agent's candidate pack now carries each name's analyst context. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -4,7 +4,7 @@ Daily loop of the agent-reviewed strategy ``overnight_recovery_agent_top10``
|
||||
scores with a point-in-time stamp, and measure the scores against what
|
||||
happened.
|
||||
|
||||
uv run python agent_review.py candidates [--refresh] # pack for the next session
|
||||
uv run python agent_review.py candidates [--refresh] [--analyst-refresh]
|
||||
uv run python agent_review.py submit data/agent/us/draft_<as_of>.json
|
||||
uv run python agent_review.py evaluate # IC, vetoes, book vs control
|
||||
|
||||
@@ -75,6 +75,11 @@ def cmd_candidates(args) -> None:
|
||||
bars = trader.strategy_bars("us", NAME, tickers, close, open_)
|
||||
strat = trader.STRATEGY_REGISTRY[NAME]()
|
||||
cand = strat.candidates(bars)
|
||||
from research.lab.analyst_estimates import collect, candidate_context
|
||||
if args.analyst_refresh:
|
||||
collect(list(cand.index))
|
||||
generated_at = datetime.now(timezone.utc).isoformat()
|
||||
analyst = candidate_context(list(cand.index), generated_at)
|
||||
|
||||
c = close[cand.index].ffill()
|
||||
r = lambda k: (c.iloc[-1] / c.iloc[-1 - k] - 1).round(4) # noqa: E731
|
||||
@@ -109,6 +114,7 @@ def cmd_candidates(args) -> None:
|
||||
"overnight_mom_12m": float(row["overnight_mom_12m"]), "recovery_126d": float(row["recovery_126d"]),
|
||||
"held_weight": {k: v.get(t, 0.0) for k, v in held.items()},
|
||||
**links.get(t, {}),
|
||||
"analyst_estimates": analyst.get(t),
|
||||
"previous": ({"as_of": prev["as_of"], "score": p.get("score"), "veto": p.get("veto", False),
|
||||
"summary": p.get("summary", "")} if p else None),
|
||||
})
|
||||
@@ -120,7 +126,10 @@ def cmd_candidates(args) -> None:
|
||||
"deadline_utc": str(ar.session_deadline(nxt)),
|
||||
# 0: the next session rebalances on these scores; otherwise only vetoes act until then
|
||||
"sessions_until_rebalance": int(until),
|
||||
"generated_at": datetime.now(timezone.utc).isoformat(timespec="seconds"),
|
||||
"generated_at": generated_at,
|
||||
"analyst_estimates": {"covered": len(analyst),
|
||||
"eligible": sum(r["eligible"] for r in analyst.values()),
|
||||
"note": "Known only after snapshot collection; stale fiscal metadata is excluded from the research overlay."},
|
||||
"rule": {"agent_weight": ar.AGENT_WEIGHT, "score_range": list(ar.SCORE_RANGE),
|
||||
"veto_reasons": list(ar.VETO_REASONS), "top_n": strat.top_n, "pool": strat.pool,
|
||||
"rebal_freq": strat.rebal_freq},
|
||||
@@ -290,6 +299,7 @@ def main() -> None:
|
||||
sub = ap.add_subparsers(dest="cmd", required=True)
|
||||
p = sub.add_parser("candidates", help="write the candidate pack for the next session")
|
||||
p.add_argument("--refresh", action="store_true", help="download prices first (the monitor normally has)")
|
||||
p.add_argument("--analyst-refresh", action="store_true", help="archive current Yahoo analyst estimates before writing the pack")
|
||||
p = sub.add_parser("submit", help="validate and store an agent scores file")
|
||||
p.add_argument("file")
|
||||
sub.add_parser("evaluate", help="score IC, veto outcomes, and the book against its control")
|
||||
|
||||
Reference in New Issue
Block a user