robertsben333-cmyk

earnings-edge-panel

Stage E-P. The US earnings edge hunt run as stage E runs it, except that the Opus 5.5 hunter works as a SEARCHER and a blind panel of four different models (Opus 5, Opus 5.5, Sonnet 5.5, Fable 5.1) judges its evidence; panel_score.py reports each judge as a percentile of its own range and combines them into a ranking, a selection (the mean percentile in the top 20%), and a weight. Writes to its own edge-panel/ directory. Since 2026-10-19 it is the stage that trades the Alpaca paper account. It sells whatever is still held once its searchers finish, and buys the panel's book (panel_pct 80 or more, linear weights, 50% cap a name) after the panel. Use when asked to run stage E-P, the panel edge hunt, or the four-model judging panel.

robertsben333-cmyk 0 1 Actualitzat fa 3 d
GitHub

Instal·la

npx skillscat add robertsben333-cmyk/claude-research/earnings-edge-panel

Instal·la des del registre de SkillsCat.

SKILL.md

Stage E-P — the searcher and the four-model panel

Stage E-P replaced stage E-S on 2026-10-01, on the same Routine. It asks: does a panel
of four different models, judging the hunter's evidence blind, pick the names that move
better than the hunter's own number does?
The measurement behind it is in
research/analyses/judge-lab/ (README rounds 1 and 2, APPROACH.md sections 5 and 6):
on 187 resolved development names the hunter's most confident names moved LESS than the
average name, while names that three or four judges each put in their own top 20% hit
18/24 at +6.7% net.

This file is an overlay, not a copy: follow .claude/skills/earnings-edge-hunt/SKILL.md
step by step, and do differently only what the table and the new step 5p below say.

Read CLAUDE.md first, then the stage E skill in full, then come back here.

The overrides

Stage E does Stage E-P does instead Why
writes to <RUN>/edge/ writes to <RUN>/edge-panel/, every file, same layout, plus panel/ and edge-scores-panel.json Stage E fires the same afternoon into <RUN>/edge/.
launches unpriced-hunter launches unpriced-searcher (Opus 5.5) The same hunter with config/searcher-addendum.md: breadth over polish, because its sizes do not rank the day. Generated by scripts/sync_hunter_core.py; identical search, event check, source rule and output contract.
launches edge-sweep the same edge-sweep The sweep decides what is hunted; keep the universe comparable with stage E.
step 0b sells, step 6b buys step 3s sells, step 6b buys, both on the panel's own rules below. Since 2026-10-19 (operator's instruction) stage E-P is the ONLY stage connected to the paper account; stage E's Routine is stopped. Stage E's book selected on impact_scaled; this one selects on panel_pct.
runs the V2 shadow ledger (step 6c) and edge_grounded_score.py (step 5b) skips the shadow ledger; RUNS edge_grounded_score.py --run <RUN>/edge-panel before the first print The shadow ledger is one shared file and stage E feeds it; a second ingest would double-count. The grounded score only READS it and writes edge-scores-grounded.json into this run's own directory, as stage E-S did.
step 2b stamps provenance.py stamp --market US stamps --market US-P on <RUN>/edge-panel Records the searcher's prompt version; step 5p adds the judges.
step 5 scores and ranks step 5 and then step 5p, the panel The searcher's own edge-scores.json is kept as the comparison arm.
run-log headings Edge hunt — …, publish edge hunt: … Edge hunt (panel) — … and edge hunt panel: … Both stages append to one _run-log.md.

Every --run <RUN>/edge in the stage E skill becomes --run <RUN>/edge-panel. The only
alpaca_trade.py calls are the ones in steps 3s and 6b below; skip stage E's 0b and 6b. Seal your own baselines; never read
stage E's files except its edge-scores.json at the very end, for one comparison line.

If unpriced-searcher comes back "not found", use stage E's fallback (launch
general-purpose with .claude/agents/unpriced-searcher.md's body pasted in) and pass
model: opus. Record which form ran.

3s. Sell what is still held, as soon as the searchers have finished

Once every searcher has returned (or been shed), and BEFORE the panel packs are built:

python3 researcher_us/scripts/alpaca_trade.py mode
python3 researcher_us/scripts/alpaca_trade.py flatten --run <RUN>/edge-panel --submit

flatten cancels every open order and closes every position at market, then waits until
the account is flat and says if anything is still held. It sells everything, not only
stage E-P's own book: this stage is the only one connected to the account. It refuses
(and sells nothing) when execution.enabled is false or credentials are missing; record
the reason in the run log and carry on with the research. If it reports positions still
held, say so in the run log; step 6b nets them off the budget. Write one run-log line
with what was held and what was closed, then publish.

Budget. One sweep, up to twenty searchers (edge_panel.hunted_names; nineteen,
stage E's cap, until 2026-10-21, thirty on 2026-10-21 only) in stage E's waves, then four
judges in one wave. Shed names by budget.edge_degrade_order exactly as stage E does,
cutting at 20; never shed a judge to save a searcher. In the run log's wave line say how many were confirmed,
how many hunted and which were shed, and launch the amc names first so a long day cannot
push one past its release. Relaunch as slots free: the platform runs about 8 at once.
The buy at step 6b needs an open market. A 19-name day reached it about 45 minutes after
the fire. If the last searcher returns after 15:15 New York, write that in the run log, since
step 6b refuses after 16:00.

5p. The panel

Run it after step 5 (edge_score.py --run <RUN>/edge-panel) has written edge-scores.json,
so the packs hold exactly the names the stage ranks.

  1. Packs.

    python3 researcher_us/scripts/panel_packs.py --run <RUN>/edge-panel

    It writes <RUN>/edge-panel/panel/packs.json, the searchers' evidence with every size,
    verdict and final number removed, one field per line. If it reports 0 packs, there is
    nothing to judge: say so in the note and skip to 6.

    More than 20 packs (edge_panel.judge_pack_max; at the current cap this does not
    happen) and it also prints SPLIT
    and writes packs-p1.json, packs-p2.json: about
    20 KB a name, so a much larger day does not fit one judge's context. Then every judge runs once per part, on that part's file,
    writing <member>-p1.json, <member>-p2.json; panel_score.py merges them. Never
    hand a judge packs.json on a split day.

  2. Heartbeat, then publish, before spending anything:
    python3 scripts/run_log.py --heading "Edge hunt (panel) — panel STARTED" --line "<n> packs, four judges".

  3. Four judges, in ONE message, in parallel, one per model, each with this prompt and
    nothing else (fill in the two paths):

    Follow your definition. Your packs file is <RUN>/edge-panel/panel/packs.json. Write your JSON array to
    <RUN>/edge-panel/panel/<member>.json. Use only Read and Write.

    member agent fallback if the agent or its model is refused
    opus5 panel-judge-opus5 (claude-opus-5) none: no alias reaches Opus 5. Run without it and record it missing
    opus55 panel-judge-opus55 (claude-opus-5-5) general-purpose, model: opus, body of config/panel-judge.md pasted in
    sonnet55 panel-judge-sonnet55 (claude-sonnet-5-5) general-purpose, model: sonnet, same
    fable51 panel-judge-fable51 (claude-fable-5-1) general-purpose, model: fable, same

    On a split day launch all of them in the same message, four judges times the parts
    (eight for two parts), each with its part's packs file and its part's output path.

    Never give a judge anything beyond the packs file: not the hunts, not the
    searcher's scores, not another judge's output, not your own view. Their independence
    is the whole point of the panel.

  4. Check coverage. Every pack id must appear in every member's file (on a split day,
    in the member's part files together). Re-launch a
    member once for the ids it missed (a second file <member>-retry.json, merged by hand
    into <member>.json before scoring). A member that fails twice is missing for the day.

  5. Combine.

    python3 researcher_us/scripts/panel_score.py --run <RUN>/edge-panel

    It reads each member's sizes against that member's own live history from earlier runs
    (researcher_us/analysis/panel-history.json) as a signed PERCENTILE, averages the four
    into panel_pct, and selects a name when every member is present and |panel_pct| is
    80 or more
    (the mean in the top 20%; since 2026-10-14). The 3-of-4 verdict, the z
    and panel_score are still written. It writes <RUN>/edge-panel/edge-scores-panel.json
    and appends the day's sizes to the history. Fewer than four members is allowed: the
    mean is then over the members that ran, and the note must say which model was missing.

  6. Record the panel in <RUN>/edge-panel/provenance.json: add a panel block with,
    per member, the agent that ran (panel-judge-… or the fallback), the model pinned in
    its frontmatter, the model it reported if it reported one, and missing where it did
    not run. The searcher's block is already there from step 2b.

6. The note (stage E's note, with the panel first)

<RUN>/edge-panel/edge-note.md, answer first:

  1. Three lines at the top: this is stage E-P: Opus 5.5 searchers, a blind four-model
    panel, no orders
    ; which members ran; that one day is an anecdote.
  2. The panel table, every rankable name: rank, ticker, session, selected,
    panel_pct (0-100) and its side (long/short) in two columns, its spread, each member's
    own percentile 0-100 with L or S (* where in its own top 20%), then consensus_k and
    the old panel_score. Never print a negative percentile: the side is its own column,
    the searcher's own impact_sum, the expected return at that percentile
    (expected_return_pct with its 80% band and p_right), weight_equal and
    weight_precision_tilt. The percentile leads: every name at the same panel_pct has the
    same expected return, so present it as the translation of the percentile, never as a
    separate forecast. Say that it is gross, on the scorebord's window, and flat at about
    +0.3% below a percentile of 50. Report the judges in percentiles, never in raw sizes: a
    top-10% Opus 5 and a top-10% Opus 5.5 are the same statement at different sizes.
  3. One paragraph per selected name: the evidence items the members' notes name, and
    the strongest case against.
  4. Where the panel and the searcher disagree most, one line each.
  5. If stage E's edge-scores.json exists by then: the names both ranked and the Spearman
    ρ between stage E's impact_sum and panel_pct. If not, say so; do not wait.
  6. What the numbers are not: expected_return_pct (and expected_edge_pct, which on a
    selected name now carries the same expectation as a signed move) is what names at that
    percentile have earned, with a weak out-of-sample record (correlation +0.12 with the
    realised return over 148 names), not a forecast to size on; the weights are research, nothing trades them; certainty (p_up) is
    recorded and decides nothing because it did not predict on the development names.

Then stage E's standing note sections apply where they fit.

Run stage E's step 6d on this directory before the closing reply
(python3 researcher_us/scripts/edge_context.py --run <RUN>/edge-panel || true), so the
table's retail, search and volatility columns are filled. Context only, as in stage E: the panel
decides nothing on them.

The closing chat reply starts with the verbatim output of
python3 scripts/score_report.py --run <RUN>/edge-panel --label "Stage E-P (searcher)"
(the searcher's impact_sum, impact_scaled and V2), then the same command on <RUN>/edge
with --label "Stage E" if stage E's files exist, then the panel table from
panel_score.py's output. Finish with
python3 scripts/update_index.py and scripts/publish.sh "edge hunt panel: <what> for <YYYY-MM-DD>". It pushes to main.

6b. Buy the panel's book, after the note is published

python3 researcher_us/scripts/alpaca_trade.py plan --run <RUN>/edge-panel
python3 researcher_us/scripts/alpaca_trade.py open --run <RUN>/edge-panel --submit --no-flatten
python3 researcher_us/scripts/alpaca_trade.py status --run <RUN>/edge-panel

The rule is execution.panel in config/pipeline.yaml (operator's instruction,
2026-10-19), applied by select_panel() and size() in alpaca_trade.py:

  • Entry: panel_pct >= 80 with all four judges present; never at or below 60. Side
    from the panel. The same $200k turnover floor as stage E.
  • Weight: panel_pct - 60, linear (90 / 90 / 75 -> 40 / 40 / 20).
  • Cap: 50% of equity a name; what a capped name cannot take goes to the others in
    proportion to their weights, so one name is 50% and cash, two names are 50 / 50.
  • Cannot trade or short: dropped before the split, so its share goes to the rest.
  • Budget: 100% of equity minus anything still held after step 3s.

Market orders, so the session must still be open: open refuses a closed market rather
than queue an order, and a run that finishes after 16:00 ET buys nothing; say so. Never
pass --top, --min-pct or --allow-stale on a scheduled run; they exist for a
deliberate test. Write one run-log line with what was sent and filled, publish, and put
the plan table in the closing reply.

7. Resolve

edge_resolve.py --run <RUN>/edge-panel ranks the searcher's key and, beside it,
spearman_panel from edge-scores-panel.json. The question that matters for this stage
is not ρ over the whole day but whether the selected names moved and paid: the
judge-lab scripts (research/analyses/judge-lab/magnitude.py, consensus_top.py) are how
that was measured, and forward days should be added to them, not judged one at a time.

This is research, not investment advice.