# ETH 1m-15m indicator lab, v2 summary ## Changes from v1 to v2 - **v1 holdout is burned.** The v1 sealed 30-day holdout was opened in v1. v2 treats it as development data: v2 development is all 160 cached days (2026-06-05 to 2026-11-12 00:00 UTC). No v1 file in `data/` or `ethlab/` was changed. - **New feature families** in `lab/features_v2.py` (and the exact TS port `src/composite/featuresV2.ts`): A sigma positions (`a_both`, `a_div`), B band breaches, first bar of each run (`b_price`, `b_rsi`, `b_both`, `b_noconf`, `b_rsi_only`), C RSI-band midline and width (`c_m7030`, `c_m50`, `c_sqz_end`, `c_span`, plus the width-percentile gate `width`). - `lab/composite.py` accepts the 11 new event names and an optional `width` gate. The default `width='none'` reproduces v1 exactly, and all 107 v1 parity tests still pass. - Search: 44 single-feature configs (11 features x 4 TFs) and 160 composite configs mixed with the v1 finalists, scored with the same `ethlab.scorer` (same costs, fills, stop-first 1m path, 50x liquidation, entry and exit metrics, BTC cross-check). The v1 lineage of 252 attempts is kept in `lab/runs_v2/leaderboard.json`. - Finalists were frozen in `configs/frozen-v2.json` before the final checks. Then the final checks ran once (`lab/runs_v2/final_checks.json`): (1) ETH bars newer than the v1 cache and (2) SOL-USDT-SWAP over the full 160 days. That data is in `lab/data_v2/` with a hash manifest. - Multiple testing: deflated Sharpe over 252 v1 trials plus 204 v2 trials, 456 in total. - In the TS module, each per-TF `Output` gains `v2` (zP, zR, width percentile, active A/B/C events with side, label and age in bars) and `subSignals` (vote, weight and points for each sub-signal). Parity with Python is covered by 25 new vitest cases. ## Verdict No v2 config shows an edge after costs that survives correction. The deflated Sharpe is 0.00 for every TF's finalist. Only the 15m finalist has a positive development selection score, and it lost money on SOL. The owner's hit-rate leans partly reappear, for example 15m `c_m7030` with 62%% hit (n=69) and 15m `a_div` with 56%%. But average R is negative after costs in almost every case, which matches the owner's note that failures are large. Family C as a gate (`width` mid / not_wide) improved the 15m v1 finalist's development score (0.0107 vs the not_wide variant at 0.0064). The narrow and wide gates left too few trades (<20) to rank. **ETH-new check is uninformative.** Only ~7.5 hours (450 1m bars) exist after the v1 cache end (2026-11-12 00:00 UTC). No finalist fired in that window, so n=0 on every TF. ## Best config per TF (frozen, reported even if negative) | TF | tag | dev mean fold net/day % | dev entries n | dev hit 1ATR | DSR | SOL entries n | SOL net/day % | SOL hit | SOL CI95 | ETH-new n | |---|---|---|---|---|---|---|---|---|---|---| | 1m | `v1+c_sqz_endx1/none` | -0.00381 | 20 | 0.75 | 0.0 | 11 | -0.00041 | 0.6364 | [-0.00538, 0.00563] | 0 | | 3m | `v1+b_rsix1/none` | -0.00773 | 21 | 0.4762 | 0.0 | 26 | -0.00813 | 0.5 | [-0.02114, 0.00296] | 0 | | 5m | `v2:a_both+b_price/any/align` | -0.0044375 | 27 | 0.3704 | 0.0 | 16 | -0.00673 | 0.5 | [-0.01637, 0.00254] | 0 | | 15m | `v1/mid` | 0.0106675 | 33 | 0.5938 | 0.0 | 46 | -0.01128 | 0.5652 | [-0.04535, 0.02268] | 0 | ## Each feature alone (entries: n / hit +1ATR before -1ATR / avg R after costs) | feature | 1m | 3m | 5m | 15m | |---|---|---|---|---| | `b_price` | 4564 / 0.4926 / -1.6161 | 1109 / 0.486 / -0.737 | 621 / 0.525 / -0.49 | 239 / 0.4477 / -0.3201 | | `c_m7030` | 887 / 0.4774 / -1.5685 | 278 / 0.4676 / -0.5437 | 157 / 0.3355 / -0.6307 | 69 / 0.6232 / 0.1207 | | `a_div` | 1876 / 0.5101 / -1.3651 | 598 / 0.4614 / -0.6905 | 898 / 0.5061 / -0.4812 | 331 / 0.5627 / -0.0997 | | `c_sqz_end` | 4756 / 0.492 / -1.903 | 1540 / 0.4805 / -0.8328 | 943 / 0.4783 / -0.6579 | 283 / 0.4539 / -0.4264 | | `b_rsi_only` | 4603 / 0.4884 / -1.9399 | 1540 / 0.4838 / -0.8524 | 1112 / 0.4851 / -0.6184 | 384 / 0.5286 / -0.2315 | | `b_rsi` | 8078 / 0.4891 / -1.897 | 2810 / 0.4833 / -0.8472 | 1714 / 0.4921 / -0.6097 | 606 / 0.5248 / -0.2311 | | `c_span` | 1699 / 0.4909 / -1.9787 | 553 / 0.4792 / -0.8582 | 298 / 0.5201 / -0.5083 | 114 / 0.4825 / -0.3135 | | `a_both` | 4684 / 0.4947 / -1.8332 | 1627 / 0.4831 / -0.8144 | 760 / 0.5092 / -0.5744 | 251 / 0.4661 / -0.3347 | | `b_both` | 2342 / 0.4936 / -1.6379 | 678 / 0.4867 / -0.7324 | 342 / 0.538 / -0.4727 | 123 / 0.4146 / -0.478 | | `b_noconf` | 2462 / 0.4898 / -1.5773 | 475 / 0.4968 / -0.7324 | 306 / 0.5033 / -0.4988 | 122 / 0.4754 / -0.1668 | | `c_m50` | 5831 / 0.497 / -1.9402 | 1976 / 0.5096 / -0.8365 | 1140 / 0.5022 / -0.6548 | 383 / 0.5459 / -0.2742 | ## Limits (read before using) - Exits and stops were scored only with the accepted scorer's fixed bracket (1 ATR stop, 1.5 ATR target, 30-bar time exit) and its signal-to-signal exit family. Variants such as ATR-stop width, other time stops and re-entry inside the band were **not** swept in this run. Family C was tested only as an entry gate, not for sizing or stop width. - The 15m-state filter for 1m/5m was tested only through the existing `mtf='align'` gate. - Selection is an in-sample mean over the four test folds (the v1 rule), not true walk-forward selection. - SOL has 1 missing 1m bar in 230,849. - The widget component itself was not changed. It receives the new optional `v2` and `subSignals` fields, but it does not render them yet.