diff --git a/app/services/backtest_service.py b/app/services/backtest_service.py index a9503eb..6b29be4 100644 --- a/app/services/backtest_service.py +++ b/app/services/backtest_service.py @@ -942,6 +942,8 @@ def _accumulate_signal_series( records: list, collected: dict, benchmark_closes: dict[date, float] | None = None, + *, + symbol: str | None = None, ) -> None: """For each weekly as-of bar, emit (signal, forward-return) pairs keyed by ISO week into ``collected[name][week_key]``. Forward return is close-to-close over @@ -974,6 +976,7 @@ def _accumulate_signal_series( "fwd": fwd, "close": closes[i], "median_dvol_63": dvol, + "symbol": symbol, }) else: collected[name][week_key].append((val, fwd)) @@ -1041,12 +1044,28 @@ def _filter_liquid_breadth_week( Ranking is relative (IEX volume undercount is OK for order stats). Membership is recomputed every week from as-of bars — never frozen from today's liquidity. """ - ranked: list[tuple[float, float, float]] = [] # (-dvol, val, fwd) + kept = _filter_liquid_breadth_week_rich( + recs, top_n=top_n, min_price=min_price + ) + return [(float(r["val"]), float(r["fwd"])) for r in kept] + + +def _filter_liquid_breadth_week_rich( + recs: list, + *, + top_n: int, + min_price: float, +) -> list[dict]: + """Same mask as ``_filter_liquid_breadth_week``, returning rich rows. + + Single source for harness IC and research diagnostics. Eligible pool = + dict observations with close ≥ min_price and median_dvol_63 > 0; then + keep top_n by dollar volume (highest first). Non-dict legacy tuples are + not eligible for the liquid mask (they have no dvol). + """ + eligible: list[tuple[float, dict]] = [] # (-dvol, row) for rec in recs: if not isinstance(rec, dict): - pair = _obs_val_fwd(rec) - if pair is not None: - ranked.append((0.0, pair[0], pair[1])) continue close = rec.get("close") dvol = rec.get("median_dvol_63") @@ -1057,10 +1076,50 @@ def _filter_liquid_breadth_week( pair = _obs_val_fwd(rec) if pair is None: continue - ranked.append((-float(dvol), pair[0], pair[1])) - ranked.sort(key=lambda row: row[0]) - kept = ranked[:top_n] - return [(val, fwd) for _, val, fwd in kept] + row = { + "val": pair[0], + "fwd": pair[1], + "close": float(close), + "median_dvol_63": float(dvol), + "symbol": rec.get("symbol"), + } + # Preserve optional research fields for mom-conditional diagnostics. + for key in ("mom_12_1", "mom_12_1_resid", "vol_6m", "fip_id"): + if key in rec and rec[key] is not None: + row[key] = rec[key] + eligible.append((-float(dvol), row)) + eligible.sort(key=lambda item: item[0]) + return [row for _, row in eligible[:top_n]] + + +def _liquid_breadth_week_stats( + recs: list, + *, + top_n: int, + min_price: float, +) -> dict[str, int | bool]: + """Pre/post mask counts for reconciling avg_cross_section semantics.""" + raw = len(recs) + eligible = 0 + for rec in recs: + if not isinstance(rec, dict): + continue + close = rec.get("close") + dvol = rec.get("median_dvol_63") + if close is None or float(close) < min_price: + continue + if dvol is None or float(dvol) <= 0: + continue + if _obs_val_fwd(rec) is None: + continue + eligible += 1 + post = min(eligible, top_n) if top_n > 0 else eligible + return { + "raw_pool": raw, + "eligible_pre_mask": eligible, + "post_mask": post, + "mask_binds": bool(top_n > 0 and eligible > top_n), + } def _quintile_spread(pairs: list[tuple[float, float]]) -> float | None: @@ -1126,9 +1185,18 @@ def _signal_evaluation(collected: dict) -> list[dict]: ics: list[float] = [] spreads: list[float] = [] sizes: list[int] = [] + raw_sizes: list[int] = [] + eligible_sizes: list[int] = [] + bind_flags: list[bool] = [] for wk in kept: recs = weeks_map[wk] if top_n > 0: + stats = _liquid_breadth_week_stats( + recs, top_n=top_n, min_price=min_price + ) + raw_sizes.append(int(stats["raw_pool"])) + eligible_sizes.append(int(stats["eligible_pre_mask"])) + bind_flags.append(bool(stats["mask_binds"])) pairs = _filter_liquid_breadth_week( recs, top_n=top_n, min_price=min_price ) @@ -1146,6 +1214,7 @@ def _signal_evaluation(collected: dict) -> list[dict]: spread = _quintile_spread(pairs) if spread is not None: spreads.append(spread) + # avg_cross_section is ALWAYS post-mask pair count (the IC sample). sizes.append(len(pairs)) if not ics: continue @@ -1168,16 +1237,32 @@ def _signal_evaluation(collected: dict) -> list[dict]: if top_n > 0: row["liquid_breadth_top_n"] = top_n row["liquid_min_price"] = min_price + # Explicit pre/post mask diagnostics (reconcile "did top-N bind?"). + if raw_sizes: + row["avg_raw_pool"] = round(sum(raw_sizes) / len(raw_sizes), 1) + if eligible_sizes: + row["avg_eligible_pre_mask"] = round( + sum(eligible_sizes) / len(eligible_sizes), 1 + ) + if bind_flags: + row["mask_binds_pct"] = round( + sum(1 for b in bind_flags if b) / len(bind_flags) * 100, 1 + ) rows.append(row) rows.sort(key=lambda r: r["mean_ic"], reverse=True) return rows -def _signal_series(records: list, benchmark_closes: dict[date, float] | None = None) -> dict: +def _signal_series( + records: list, + benchmark_closes: dict[date, float] | None = None, + *, + symbol: str | None = None, +) -> dict: """Per-ticker signal/forward-return series as a PLAIN (picklable) nested dict — no defaultdict/lambda — so it can cross a process boundary.""" tmp: dict = defaultdict(lambda: defaultdict(list)) - _accumulate_signal_series(records, tmp, benchmark_closes) + _accumulate_signal_series(records, tmp, benchmark_closes, symbol=symbol) return {name: dict(weeks) for name, weeks in tmp.items()} @@ -1218,7 +1303,7 @@ def _replay_and_signals( ) return ( candidates, - _signal_series(bars, benchmark_closes), + _signal_series(bars, benchmark_closes, symbol=symbol), ) diff --git a/docs/research/fip-breadth-ic.md b/docs/research/fip-breadth-ic.md index 9dfc1f1..ca17663 100644 --- a/docs/research/fip-breadth-ic.md +++ b/docs/research/fip-breadth-ic.md @@ -1,151 +1,145 @@ # Broad-universe fip_id IC research (Phase B) -**Status:** research complete enough for a platform decision on *unconditional* fip. +**Status:** unconditional fip closed; mom-conditional lead confirmed on single-sourced path. **Production impact:** none. Display card remains context-only. -Generated: 2026-07-18 (breadth run + diagnostics same day). - ## Scope -- **Research only** — production universe, gate, scanner, schedule unchanged. -- Price-only signal harness; no sentiment/fundamentals on the broad tier. -- Snapshot: `research.sqlite` — ~4,650 tickers (prod + nasdaq_all extend). -- IC mask: top **1,500** by point-in-time 63d median $vol, price ≥ **$5**, per week. +- Research only — production universe, gate, scanner, schedule unchanged. +- Snapshot: `research.sqlite` (~4,650 tickers = prod + nasdaq_all extend). +- Liquid mask: top **1,500** by point-in-time 63d median $vol, price ≥ **$5**/week. ## Caveats -- **Survivorship bias** — today's constituents, history backfilled (worse in small caps). -- **IEX volume undercount** — relative $vol rank only, not absolute floors. -- **Pool skew** — nasdaq_all ∪ partial SPX seed tilts tech/biotech; missing pure NYSE mid-caps. -- **Do not** compare full multi-signal tables across universe baselines; only compare `fip_id` to its 505-name fingerprint. +- Survivorship bias (today’s constituents, history backfilled). +- IEX volume undercount → relative $vol rank only. +- Pool skew: Nasdaq-heavy; missing pure NYSE mid-caps. +- Do not mix multi-signal tables across universe baselines. --- -## Fingerprint (505-name prod snapshot) +## Fingerprint (505-name prod) | | Expected | Observed | |---|---:|---:| | mean IC | −0.045 | **−0.045** | | t-stat | −2.9 | **−2.91** | -| weeks | ≥12 | 35 | -| avg N | ~500 | 497.7 | -| reliable | true | **true** | +| weeks / N / reliable | ≥12 / ~500 / true | 35 / 497.7 / true | -**Pass.** Pipeline and formula are trustworthy. - -Artifacts: `reports/fip-breadth-20260718-211440-fingerprint.json` +**Pass.** Formula + pipeline trustworthy. --- -## First breadth harness run (pre-registered iron rule) +## Discrepancy (must not be papered over) -Unconditional `fip_id` on liquid top-1500 (runner `run_fip_breadth_research.py`): +| Source | fip IC (liquid ~1500) | t | +|---|---:|---:| +| Report `fip-breadth-20260718-211440-breadth.json` | **+0.0575** | **+5.12** | +| Single-sourced recompute (2026-07-19) | **−0.0168** | **−1.85** | + +That is a **sign disagreement** on the same intended quantity. Method rule: the number you cannot reconcile is the number you cannot use. + +### What we did + +1. **Single-sourced the mask** — diagnostics call harness `_signal_series` + `_filter_liquid_breadth_week_rich` only (no parallel mask). +2. **Documented avg_cross_section semantics** — always **post-mask** IC sample size. +3. **Logged pre-mask stats** so “did top-N bind?” is answerable. + +### Authoritative unconditional liquid fip (post-reconciliation) | metric | value | |---|---:| -| mean_ic | **+0.0575** | -| ic_t_stat | **+5.12** | -| ic_positive_pct | 88.6% | +| mean_ic | **−0.0168** | +| ic_t_stat | **−1.85** | | weeks | 35 | -| avg_cross_section | 1471.2 | +| avg_cross_section (**post-mask**) | 1471.2 | +| avg_raw_pool | 3214.4 | +| avg_eligible_pre_mask | **2338.4** | +| mask_binds_pct | **97.1%** | | reliable | true | -**Iron rule as written (need negative sign):** **not green.** -Honest call: no production change from that screen alone. +**Mask binds hard** (eligible ≫ 1500). The hypothesis that “1471 meant the mask never bound / unmasked +5σ” is **false**. -Artifact: `reports/fip-breadth-20260718-211440-breadth.json` +Harness `_signal_evaluation` vs manual IC through the same filter: **exact match** (−0.0168 / −1.85). + +### Verdict on the orphan + +The **+0.0575 / t +5.12** row is **orphaned**. Do not cite it. Root cause of that single run is not fully forensic-reconstructed (no dual dump from the original process remains), but every single-sourced recompute on this snapshot lands near **−0.017**, and the tier blend (≈800×−0.035 + ≈670×+0.014)/1471 ≈ **−0.013** is internally consistent with that number—not with +0.058. + +**Iron rule unconditional:** still **not green** (|IC| 0.017 < 0.03), and now with the correct mild-negative sign. + +Artifact: `reports/fip-reconcile-20260719-000520.json` --- -## Why “+IC on Nasdaq” is not a jumpiness-premium story +## Compositional story (supported) -`fip_id = sign(PRET) × (%neg − %pos)` **pools two opposite continuous populations:** +`fip_id = sign(PRET)×(%neg−%pos)` pools: -| Leg | Formation | Continuation intuition | IC contribution | -|---|---|---|---| -| **Continuous winners** | PRET>0, mostly up days (smooth climbers) | Paper: keep going up | **negative** | -| **Continuous losers / bleeders** | PRET<0, mostly down days (grind-down biotechs, SPACs, etc.) | Momentum: keep going down | **positive** | +- **Continuous winners** → want **negative** IC +- **Continuous bleeders** → want **positive** IC -Unconditional IC is a **tug-of-war weighted by universe composition**: +| check | IC | t | read | +|---|---:|---:|---| +| Prod-universe subset inside liquid | **−0.044** | **−2.88** | Matches fingerprint → compositional, not regime change | +| Tier 1–800 (senior) | **−0.035** | **−2.99** | Winner leg | +| Tier 801–1500 (junior) | **+0.014** | +1.25 | More bleeder / junk weight | +| Lagged membership (prior-week $vol) | −0.010 | −0.93 | Same sign as same-week; not a +5σ leak artifact | -- **S&P-like book** ≈ few steady bleeders → winner leg dominates → IC **−0.045**. -- **Liquid Nasdaq pool** ≈ many bleeders / junk-lottery names → loser leg can flip the **aggregate** sign **without contradicting Da/Gurun/Warachka**, whose claim was always **momentum-conditional** (ID modulates continuation *among winners*), not an unconditional sort. - -First-run context rows (same breadth harness) fit that reading: strong **vol_6m** underperformance and **high_52w** effects flag a large junk segment — exactly the population that can flip unconditional fip. - -**Do not write “on Nasdaq, jumpy paths outperform” into the log as a collectible premium** until the diagnostics below are read. +**Do not log “on Nasdaq, jumpy paths outperform.”** That would mythologize an orphaned +0.06. --- -## Follow-up diagnostics (same snapshot, independent panel) +## Platform-relevant test: momentum-conditional fip -Script: `scripts/run_fip_breadth_diagnostics.py` -Artifact: `reports/fip-breadth-diagnostics-20260718-213908.json` +Among liquid top-1500, keep **mom_12_1 ≥ P80** (~294 names/week): -| check | mean_ic | t | weeks | avg N | reliable | -|---|---:|---:|---:|---:|---| -| fip same-week liquid 1500 (panel) | −0.017 | −1.85 | 35 | 1471 | true | -| fip **lagged membership** (prior-week $vol) | −0.010 | −0.93 | 35 | 1471 | true | -| fip **tier 1–800** (senior liquid) | **−0.035** | **−2.99** | 35 | 791 | true | -| fip **tier 801–1500** (junior liquid) | **+0.014** | +1.25 | 35 | 700 | true | -| fip **prod-universe subset** inside liquid | **−0.044** | **−2.88** | 35 | 498 | true | -| fip **mom-conditional** (top 20% mom_12_1) | **−0.088** | **−4.58** | 35 | 294 | true | -| vol_6m liquid 1500 (panel) | −0.047 | −1.3 | 35 | 1471 | true | -| mom_12_1 liquid 1500 | +0.046 | +1.91 | 35 | 1471 | true | -| mom_12_1_resid liquid 1500 | +0.029 | +1.33 | 35 | 1471 | true | +| metric | value | +|---|---:| +| mean_ic | **−0.0879** | +| ic_t_stat | **−4.58** | +| ic_positive_pct | 22.9% | +| weeks | 35 | +| reliable | **true** | -### What the checks settle +Computed on the **same single-sourced path** as the authoritative −0.017. This is the paper’s claim and the only version a gate could consume. -1. **Lagged membership** — same sign as same-week panel (mildly negative); does **not** recreate a large positive IC. Not a clean “liquidity explosion leak manufactures +0.06” story for the panel path. (The first harness run’s **+0.0575** still does not match the independent panel’s −0.017 — treat the **+0.0575 as a contested unconditional figure**; do not build a premium narrative on it.) -2. **Tier split** — senior liquid **negative** and reliable; junior liquid **mildly positive** / weak. Bias and bleeder weight are stronger in the junior tier. -3. **Prod-universe subset** — IC **−0.044 / t −2.88**, ~498 names/week — matches the fingerprint. **Sign flip is compositional**, not “the whole market regime flipped.” -4. **Momentum-conditional fip (the platform test)** — IC **−0.088 / t −4.58**, reliable, ~294 winners/week. **Negative sign, |IC| ≳ 0.03.** This is the paper’s claim and the only version a gate could consume. - -### Platform verdict - -| Question | Answer | +| Decision | | |---|---| -| Unconditional fip iron rule (negative on liquid-1500) | **Not green** (first harness +0.06 fails sign; panel mild neg fails magnitude) | -| Production change now? | **No** | -| Is fip “dead forever”? | **No** — **alive only as a momentum-conditional tilt candidate** on breadth | -| Next real step if pursued | Book-level experiment: among qualified residual-momentum names, tilt/filter by lower fip — **not** an unconditional fip sort | -| Display card | Stays; still the right home until a book test wins | +| Unconditional fip | **Closed** for production | +| Mom-conditional fip | **Alive as book-tilt candidate only** — book sim before any gate talk | +| Display card | Stays | +| Production change | **None** | --- -## Buried headline: vol tilt / residual mom on breadth +## Vol-tilt / residual-mom warning (any future breadth move) -Even with panel vs harness magnitude differences, the **direction** is clear: +| signal (liquid, single-sourced) | IC | t | +|---|---:|---:| +| vol_6m | −0.048 | −1.4 | +| mom_12_1 | +0.046 | +1.9 | +| mom_12_1_resid | +0.029 | +1.3 | -- **High vol underperforms** on this pool relative to a clean S&P-like book. -- Production rank tilts **20% toward high volatility**, validated on S&P-like names where high-vol ≈ high-beta in a bull tape. On broad Nasdaq liquid, high-vol often means **lottery junk**. -- **If the universe ever broadens in production, re-validate the 80/20 high-vol tilt first** — it can flip from mildly helpful to actively harmful. -- **Raw momentum > residual** on breadth (panel and first harness both show this pattern) — SPY residualization is a noisier fit for small caps; a breadth book may want a different benchmark or raw mom. +High-vol names tend to underperform on this pool relative to a clean S&P-like book. Production **80/20 high-vol tilt** was validated on S&P-like names. **If the universe ever broadens in production, re-validate that tilt first** — it can flip from mildly helpful to harmful. Raw momentum also looks stronger than SPY residualization here (noisier fit for small caps). --- ## How to re-run (research branch only) ```powershell -# Windows .\.venv\Scripts\python.exe scripts\run_fip_breadth_diagnostics.py ` --research-snapshot backtest_snapshots\research.sqlite ` --prod-snapshot backtest_snapshots\prod.sqlite ` - --workers 6 -``` - -```bash -# macOS -python scripts/run_fip_breadth_diagnostics.py \ - --research-snapshot backtest_snapshots/research.sqlite \ - --prod-snapshot backtest_snapshots/prod.sqlite \ - --workers 6 + --workers 6 --allow-spawn ``` --- ## Bottom line -- Formal first screen: **not green**, no production change, fingerprint **pass**. -- Deeper reading: unconditional sign is a **compositional tug-of-war**, not a new jumpiness premium. -- **The test that matters for this platform already ran:** momentum-conditional fip is **negative, large, and reliable** on liquid breadth → fip remains a **conditional** research lead, not a closed door — and **not** a ship-ready gate input without a book experiment. +1. Formal iron-rule screen: **not green** either before or after reconciliation. +2. **+0.0575 / +5.12 is orphaned** — authoritative unconditional liquid fip is **−0.017 / −1.9**; mask binds (~97%). +3. Compositional tug-of-war is the right story; jumpiness premium is not. +4. **Mom-conditional −0.088 / −4.6 stands on the single-sourced path** → optional next research step is a **book** A/B, not a gate wire-in. +5. Log any future reader who sees both numbers: trust the reconcile artifact, not the orphaned breadth headline. diff --git a/reports/fip-reconcile-20260719-000520.json b/reports/fip-reconcile-20260719-000520.json new file mode 100644 index 0000000..2b9a3b7 --- /dev/null +++ b/reports/fip-reconcile-20260719-000520.json @@ -0,0 +1,6693 @@ +{ + "generated_at": "2026-07-19T00:05:20.113638", + "research_snapshot": "C:\\Workspace\\signal-platform\\backtest_snapshots\\research.sqlite", + "top_n": 1500, + "min_price": 5.0, + "prod_subset_n": 506, + "panel_tickers": 4403, + "single_source": "diagnostics uses harness _signal_series + _filter_liquid_breadth_week_rich only (no parallel mask)", + "avg_cross_section_semantics": "avg_cross_section = post-mask IC sample size. avg_raw_pool = pre-filter observations. avg_eligible_pre_mask = pass price+dvol before top-N. mask_binds_pct = weeks where eligible_pre_mask > top_n.", + "harness_self_consistent": true, + "checks": { + "fip_harness_signal_eval": { + "note": "Authoritative harness _signal_evaluation on collected fip_id", + "signal": "fip_id", + "weeks": 35, + "avg_cross_section": 1471.2, + "mean_ic": -0.0168, + "ic_t_stat": -1.85, + "ic_positive_pct": 40.0, + "mean_quintile_spread": -0.0052, + "reliable": true, + "liquid_breadth_top_n": 1500, + "liquid_min_price": 5.0, + "avg_raw_pool": 3214.4, + "avg_eligible_pre_mask": 2338.4, + "mask_binds_pct": 97.1 + }, + "fip_same_week_via_shared_filter": { + "note": "Same collected data, IC via shared _filter_liquid_breadth_week_rich", + "mean_ic": -0.0168, + "ic_t_stat": -1.85, + "weeks": 35, + "avg_cross_section": 1471.2, + "ic_positive_pct": 40.0, + "reliable": true + }, + "fip_lagged_membership_1w": { + "note": "Top-N by prior-week $vol on current fip pool (shared filter)", + "mean_ic": -0.0102, + "ic_t_stat": -0.93, + "weeks": 35, + "avg_cross_section": 1471.2, + "ic_positive_pct": 40.0, + "reliable": true + }, + "fip_tier_1_800": { + "note": "Senior liquid ranks 1\u2013800", + "mean_ic": -0.035, + "ic_t_stat": -2.99, + "weeks": 35, + "avg_cross_section": 791.2, + "ic_positive_pct": 25.7, + "reliable": true + }, + "fip_tier_801_1500": { + "note": "Junior liquid ranks 801\u2013top_n", + "mean_ic": 0.0141, + "ic_t_stat": 1.25, + "weeks": 35, + "avg_cross_section": 700.0, + "ic_positive_pct": 60.0, + "reliable": true + }, + "fip_prod_universe_subset": { + "note": "Prod.sqlite symbols inside liquid fip set", + "mean_ic": -0.0444, + "ic_t_stat": -2.88, + "weeks": 35, + "avg_cross_section": 497.5, + "ic_positive_pct": 25.7, + "reliable": true + }, + "fip_momentum_conditional_top20pct": { + "note": "Among liquid fip set, mom_12_1 \u2265 P80 (paper / gate-relevant)", + "mean_ic": -0.0879, + "ic_t_stat": -4.58, + "weeks": 35, + "avg_cross_section": 294.3, + "ic_positive_pct": 22.9, + "reliable": true + }, + "vol_6m_liquid": { + "note": "vol_6m through shared filter", + "mean_ic": -0.0478, + "ic_t_stat": -1.36, + "weeks": 35, + "avg_cross_section": 1500.0, + "ic_positive_pct": 37.1, + "reliable": true + }, + "mom_12_1_liquid": { + "note": "raw mom through shared filter", + "mean_ic": 0.0462, + "ic_t_stat": 1.91, + "weeks": 35, + "avg_cross_section": 1471.2, + "ic_positive_pct": 65.7, + "reliable": true + }, + "mom_12_1_resid_liquid": { + "note": "residual mom through shared filter", + "mean_ic": 0.0289, + "ic_t_stat": 1.33, + "weeks": 35, + "avg_cross_section": 1471.2, + "ic_positive_pct": 60.0, + "reliable": true + } + }, + "membership_dumps": [ + { + "week": [ + 2022, + 49 + ], + "stats": { + "raw_pool": 2984, + "eligible_pre_mask": 2339, + "post_mask": 1500, + "mask_binds": true + }, + "symbols": [ + "A", + "AAL", + "AAON", + "AAPL", + "ABBV", + "ABCL", + "ABNB", + "ABT", + "ACAD", + "ACB", + "ACET", + "ACGL", + "ACHC", + "ACIW", + "ACLS", + "ACMR", + "ACN", + "ACRS", + "ACT", + "ADAM", + "ADBE", + "ADEA", + "ADI", + "ADM", + "ADP", + "ADPT", + "ADSK", + "ADTN", + "ADUS", + "ADV", + "AEE", + "AEHR", + "AEIS", + "AEP", + "AES", + "AEVA", + "AFCG", + "AFL", + "AFRM", + "AFYA", + "AGEN", + "AGIO", + "AGNC", + "AGNCN", + "AGNCO", + "AGNCP", + "AGNT", + "AGYS", + "AHCO", + "AIG", + "AIIO", + "AIZ", + "AJG", + "AKAM", + "ALB", + "ALCO", + "ALDX", + "ALEC", + "ALGM", + "ALGN", + "ALGT", + "ALHC", + "ALKS", + "ALKT", + "ALL", + "ALLE", + "ALLO", + "ALNT", + "ALNY", + "ALRM", + "ALT", + "ALXO", + "AMAL", + "AMAT", + "AMBA", + "AMCR", + "AMCX", + "AMD", + "AME", + "AMGN", + "AMKR", + "AMP", + "AMPH", + "AMPL", + "AMRN", + "AMSF", + "AMT", + "AMZN", + "ANAB", + "ANDE", + "ANET", + "ANGI", + "ANGO", + "ANIK", + "ANIP", + "AON", + "AOS", + "AOSL", + "APA", + "APD", + "APEI", + "APH", + "APO", + "APOG", + "APP", + "APPF", + "APPN", + "APPS", + "APTV", + "ARCB", + "ARCC", + "ARCT", + "ARE", + "ARES", + "ARGX", + "ARHS", + "ARKO", + "ARLP", + "ARQQ", + "ARQT", + "ARRY", + "ARTNA", + "ARVN", + "ARWR", + "ASLE", + "ASML", + "ASND", + "ASO", + "ASTE", + "ASTH", + "ASTL", + "ATEC", + "ATER", + "ATEX", + "ATNI", + "ATO", + "ATOM", + "ATRA", + "ATRC", + "ATRO", + "AUDC", + "AVAV", + "AVB", + "AVGO", + "AVNW", + "AVO", + "AVT", + "AVXL", + "AVY", + "AWK", + "AXGN", + "AXON", + "AXP", + "AXSM", + "AZO", + "AZTA", + "BA", + "BAC", + "BALL", + "BAND", + "BANF", + "BANR", + "BATRA", + "BATRK", + "BAX", + "BBIO", + "BBSI", + "BBY", + "BCAB", + "BCPC", + "BCRX", + "BCTX", + "BCYC", + "BDX", + "BEAM", + "BEAT", + "BEEM", + "BELFB", + "BEN", + "BF-B", + "BFC", + "BFST", + "BG", + "BHF", + "BIDU", + "BIIB", + "BILI", + "BIOX", + "BIRD", + "BJRI", + "BK", + "BKNG", + "BKR", + "BL", + "BLDP", + "BLDR", + "BLFS", + "BLK", + "BLKB", + "BLMN", + "BLNK", + "BMBL", + "BMRC", + "BMRN", + "BMY", + "BNGO", + "BNTX", + "BOKF", + "BOOM", + "BPOP", + "BR", + "BRK-B", + "BRKR", + "BRO", + "BRZE", + "BSX", + "BSY", + "BTAI", + "BUSE", + "BWB", + "BWIN", + "BX", + "BXP", + "BYND", + "BZ", + "BZUN", + "C", + "CABA", + "CAC", + "CACC", + "CAG", + "CAH", + "CAKE", + "CALM", + "CAMT", + "CAR", + "CARG", + "CARR", + "CASH", + "CASS", + "CASY", + "CAT", + "CATY", + "CB", + "CBOE", + "CBRE", + "CBRL", + "CBSH", + "CCAP", + "CCB", + "CCBG", + "CCC", + "CCCC", + "CCD", + "CCEP", + "CCI", + "CCL", + "CCNE", + "CCOI", + "CCRN", + "CCSI", + "CDLX", + "CDNA", + "CDNS", + "CDW", + "CDXS", + "CECO", + "CELH", + "CENN", + "CENT", + "CENTA", + "CENX", + "CERT", + "CEVA", + "CF", + "CFFN", + "CFG", + "CG", + "CGBD", + "CGC", + "CGEM", + "CGNX", + "CHCO", + "CHD", + "CHDN", + "CHEF", + "CHI", + "CHKP", + "CHRD", + "CHRS", + "CHRW", + "CHTR", + "CHW", + "CHY", + "CI", + "CIEN", + "CIGI", + "CINF", + "CL", + "CLAR", + "CLBK", + "CLDX", + "CLFD", + "CLMT", + "CLNE", + "CLX", + "CMCO", + "CMCSA", + "CME", + "CMG", + "CMI", + "CMPR", + "CMPS", + "CMRC", + "CMS", + "CMTL", + "CNC", + "CNOB", + "CNP", + "CNXC", + "CNXN", + "COCO", + "COF", + "COGT", + "COHR", + "COHU", + "COIN", + "COKE", + "COLB", + "COLL", + "COLM", + "COO", + "COP", + "COR", + "CORT", + "COST", + "CPAY", + "CPB", + "CPRT", + "CPT", + "CRAI", + "CRBU", + "CRCT", + "CRH", + "CRL", + "CRM", + "CRMT", + "CRNC", + "CRNX", + "CROX", + "CRSP", + "CRSR", + "CRTO", + "CRUS", + "CRVL", + "CRWD", + "CSCO", + "CSGP", + "CSIQ", + "CSQ", + "CSTL", + "CSWC", + "CSX", + "CTAS", + "CTBI", + "CTKB", + "CTRA", + "CTRN", + "CTSH", + "CTVA", + "CVBF", + "CVCO", + "CVLT", + "CVS", + "CVX", + "CWCO", + "CWST", + "CYRX", + "CYTK", + "CZR", + "D", + "DAL", + "DASH", + "DBGI", + "DBX", + "DCBO", + "DCGO", + "DD", + "DDOG", + "DE", + "DECK", + "DELL", + "DG", + "DGII", + "DGX", + "DH", + "DHI", + "DHR", + "DIOD", + "DIS", + "DJT", + "DKNG", + "DLO", + "DLR", + "DLTR", + "DMLP", + "DMRC", + "DNLI", + "DNUT", + "DOC", + "DOCU", + "DOMO", + "DOO", + "DORM", + "DOV", + "DOW", + "DOX", + "DPZ", + "DRH", + "DRI", + "DRS", + "DRVN", + "DSGN", + "DSGR", + "DSGX", + "DTE", + "DUK", + "DUOL", + "DVA", + "DVN", + "DXCM", + "DXLG", + "DXPE", + "DYN", + "EA", + "EBAY", + "EBC", + "ECHO", + "ECL", + "ECPG", + "ED", + "EDIT", + "EEFT", + "EFSC", + "EFX", + "EG", + "EGBN", + "EH", + "EIX", + "EL", + "ELV", + "EME", + "EMR", + "ENPH", + "ENSG", + "ENTA", + "ENTG", + "ENVX", + "EOG", + "EOLS", + "EPAM", + "EQIX", + "EQR", + "EQT", + "ERAS", + "ERIC", + "ERIE", + "ERII", + "ES", + "ESLT", + "ESQ", + "ESS", + "ESTA", + "ETN", + "ETR", + "EVCM", + "EVER", + "EVGO", + "EVRG", + "EW", + "EWBC", + "EWTX", + "EXC", + "EXE", + "EXEL", + "EXFY", + "EXLS", + "EXPD", + "EXPE", + "EXPO", + "EXR", + "EXTR", + "EYE", + "EZPW", + "F", + "FA", + "FANG", + "FAST", + "FATE", + "FBNC", + "FCBC", + "FCEL", + "FCFS", + "FCNCA", + "FCX", + "FDMT", + "FDS", + "FDUS", + "FDX", + "FE", + "FELE", + "FFAI", + "FFBC", + "FFIN", + "FFIV", + "FHB", + "FIBK", + "FICO", + "FIS", + "FISV", + "FITB", + "FIVE", + "FIVN", + "FIX", + "FIZZ", + "FLEX", + "FLGT", + "FLNA", + "FLNC", + "FLWS", + "FLYW", + "FMBH", + "FMNB", + "FNKO", + "FNUC", + "FORM", + "FORR", + "FOX", + "FOXA", + "FOXF", + "FRHC", + "FRME", + "FROG", + "FRPT", + "FRSH", + "FRT", + "FSLR", + "FSLY", + "FSV", + "FTAI", + "FTCI", + "FTDR", + "FTNT", + "FTV", + "FULC", + "FULT", + "FUTU", + "FWONA", + "FWONK", + "FWRD", + "FWRG", + "GABC", + "GAIN", + "GBDC", + "GCMG", + "GD", + "GDDY", + "GDRX", + "GDS", + "GDYN", + "GE", + "GEN", + "GFS", + "GGAL", + "GGR", + "GH", + "GIII", + "GILD", + "GIS", + "GL", + "GLAD", + "GLBE", + "GLNG", + "GLPI", + "GLUE", + "GLW", + "GM", + "GMAB", + "GNRC", + "GNTX", + "GO", + "GOGO", + "GOOD", + "GOOG", + "GOOGL", + "GPC", + "GPN", + "GPRE", + "GPRO", + "GRFS", + "GRMN", + "GRPN", + "GRWG", + "GS", + "GSAT", + "GSBC", + "GSHD", + "GT", + "GTLB", + "GTM", + "GTX", + "GWW", + "HAFC", + "HAIN", + "HAL", + "HALO", + "HAPN", + "HAS", + "HBAN", + "HBANP", + "HBNC", + "HCA", + "HCAT", + "HCKT", + "HCM", + "HCSG", + "HD", + "HDSN", + "HELE", + "HFWA", + "HIFS", + "HIG", + "HII", + "HIMX", + "HLIT", + "HLMN", + "HLNE", + "HLT", + "HNRG", + "HON", + "HOOD", + "HOPE", + "HPE", + "HPK", + "HPQ", + "HQY", + "HRL", + "HRMY", + "HROW", + "HRZN", + "HSIC", + "HST", + "HSTM", + "HSY", + "HTHT", + "HTLD", + "HTO", + "HTZ", + "HTZWW", + "HUBB", + "HUBG", + "HUDI", + "HUM", + "HURN", + "HUT", + "HWC", + "HWKN", + "HWM", + "HYFM", + "HYMC", + "IART", + "IBCP", + "IBKR", + "IBM", + "IBOC", + "IBRX", + "ICE", + "ICFI", + "ICHR", + "ICLR", + "ICUI", + "IDCC", + "IDXX", + "IDYA", + "IEP", + "IEX", + "IFF", + "IHRT", + "IIIV", + "ILMN", + "IMCR", + "IMKTA", + "IMMR", + "IMTX", + "IMUX", + "IMVT", + "IMXI", + "INCY", + "INDB", + "INDI", + "INGN", + "INMD", + "INO", + "INSE", + "INSM", + "INTA", + "INTC", + "INTU", + "INVA", + "INVH", + "IONS", + "IOSP", + "IOVA", + "IP", + "IPAR", + "IPGP", + "IQV", + "IR", + "IRDM", + "IRM", + "IRTC", + "IRWD", + "ISRG", + "IT", + "ITRI", + "ITW", + "IVZ", + "J", + "JACK", + "JAKK", + "JAZZ", + "JBHT", + "JBIO", + "JBL", + "JBLU", + "JBSS", + "JCI", + "JD", + "JJSF", + "JKHY", + "JNJ", + "JOUT", + "JOYY", + "JPM", + "JRVR", + "JYNT", + "KALU", + "KDP", + "KE", + "KELYA", + "KEY", + "KEYS", + "KHC", + "KIDS", + "KIM", + "KKR", + "KLAC", + "KLIC", + "KLRS", + "KLXE", + "KMB", + "KMI", + "KNSA", + "KO", + "KOD", + "KPTI", + "KR", + "KRNT", + "KRNY", + "KROS", + "KRUS", + "KRYS", + "KTOS", + "KURA", + "KYMR", + "KYNB", + "L", + "LAMR", + "LAND", + "LASR", + "LAUR", + "LBRDA", + "LBRDK", + "LBTYA", + "LBTYK", + "LCID", + "LDOS", + "LE", + "LECO", + "LEGN", + "LEN", + "LESL", + "LFUS", + "LGIH", + "LGND", + "LH", + "LHX", + "LI", + "LII", + "LILA", + "LILAK", + "LIN", + "LIND", + "LITE", + "LIVN", + "LKFN", + "LKFT", + "LKQ", + "LLY", + "LMAT", + "LMT", + "LNT", + "LNTH", + "LOCO", + "LOGI", + "LOPE", + "LOVE", + "LOW", + "LPLA", + "LPRO", + "LPSN", + "LQDA", + "LQDT", + "LRCX", + "LSCC", + "LSTR", + "LULU", + "LUNG", + "LUV", + "LVS", + "LWLG", + "LYB", + "LYEL", + "LYFT", + "LYV", + "LZ", + "MA", + "MAA", + "MANH", + "MAR", + "MARA", + "MAS", + "MASS", + "MAT", + "MATW", + "MBIN", + "MBLY", + "MBUU", + "MBWM", + "MCD", + "MCFT", + "MCHB", + "MCHP", + "MCK", + "MCO", + "MCRB", + "MCRI", + "MDB", + "MDGL", + "MDLZ", + "MDT", + "MEDP", + "MELI", + "MEOH", + "MERC", + "MET", + "META", + "METC", + "MFIC", + "MGEE", + "MGM", + "MGNI", + "MGNX", + "MGPI", + "MGRC", + "MIDD", + "MIRM", + "MITK", + "MKC", + "MKSI", + "MKTX", + "MLAB", + "MLCO", + "MLKN", + "MLM", + "MMM", + "MMSI", + "MMYT", + "MNDY", + "MNRO", + "MNST", + "MNTK", + "MO", + "MOMO", + "MORN", + "MOS", + "MPAA", + "MPC", + "MPWR", + "MQ", + "MRCY", + "MRK", + "MRNA", + "MRSH", + "MRTN", + "MRVI", + "MRVL", + "MS", + "MSBI", + "MSCI", + "MSEX", + "MSFT", + "MSI", + "MSTR", + "MTB", + "MTCH", + "MTD", + "MTLS", + "MTSI", + "MTVA", + "MU", + "MXCT", + "MXL", + "MYGN", + "MYRG", + "MZTI", + "NAVI", + "NBIX", + "NBTB", + "NCLH", + "NCNO", + "NDAQ", + "NDSN", + "NEE", + "NEM", + "NEO", + "NEOG", + "NESR", + "NEWT", + "NFBK", + "NFE", + "NFLX", + "NI", + "NICE", + "NIU", + "NKE", + "NKTR", + "NKTX", + "NMFC", + "NMIH", + "NMRK", + "NNOX", + "NOC", + "NOVT", + "NOW", + "NRC", + "NRDS", + "NRG", + "NRIX", + "NSC", + "NSIT", + "NSSC", + "NTAP", + "NTCT", + "NTES", + "NTGR", + "NTLA", + "NTNX", + "NTRA", + "NTRS", + "NUE", + "NVAX", + "NVCR", + "NVDA", + "NVEC", + "NVMI", + "NVR", + "NWBI", + "NWE", + "NWL", + "NWPX", + "NWS", + "NWSA", + "NXPI", + "NXST", + "O", + "OCFC", + "OCSL", + "ODFL", + "OFIX", + "OFLX", + "OKE", + "OKTA", + "OLED", + "OLLI", + "OM", + "OMAB", + "OMC", + "OMCL", + "ON", + "ONB", + "ONC", + "ONEW", + "OPCH", + "OPI", + "OPRX", + "ORCL", + "ORLY", + "ORMP", + "OSBC", + "OSIS", + "OSPN", + "OSUR", + "OSW", + "OTEX", + "OTIS", + "OTLY", + "OTTR", + "OUST", + "OXLC", + "OXY", + "OZK", + "PAA", + "PACB", + "PAGP", + "PAHC", + "PAMT", + "PANW", + "PATK", + "PAX", + "PAYO", + "PAYX", + "PCAR", + "PCG", + "PCRX", + "PCT", + "PCTY", + "PCVX", + "PDD", + "PDFS", + "PDSB", + "PEBO", + "PECO", + "PEG", + "PEGA", + "PENG", + "PENN", + "PEP", + "PERI", + "PETS", + "PFBC", + "PFE", + "PFG", + "PG", + "PGC", + "PGNY", + "PGR", + "PGY", + "PH", + "PHAT", + "PHM", + "PHUN", + "PI", + "PKG", + "PLAB", + "PLAY", + "PLCE", + "PLD", + "PLMR", + "PLPC", + "PLRX", + "PLTK", + "PLTR", + "PLUG", + "PLUS", + "PLXS", + "PM", + "PMVP", + "PNC", + "PNR", + "PNTG", + "PNW", + "PODD", + "POOL", + "POWI", + "PPC", + "PPG", + "PPL", + "PPLI", + "PRAA", + "PRCT", + "PRDO", + "PRGS", + "PRME", + "PRPL", + "PRTA", + "PRTS", + "PRU", + "PRVA", + "PSA", + "PSEC", + "PSMT", + "PSX", + "PTC", + "PTCT", + "PTEN", + "PTGX", + "PTLO", + "PTON", + "PUBM", + "PWP", + "PWR", + "PYPL", + "PZZA", + "QCOM", + "QCRH", + "QDEL", + "QFIN", + "QLYS", + "QNST", + "QQQX", + "QRVO", + "QS", + "QTRX", + "QURE", + "RARE", + "RCKT", + "RCL", + "RCMT", + "RDNT", + "RDNW", + "RDWR", + "REG", + "REGN", + "RELL", + "RELY", + "RENT", + "REPL", + "REYN", + "RF", + "RGEN", + "RGLD", + "RGNX", + "RGP", + "RICK", + "RIGL", + "RILY", + "RIVN", + "RJF", + "RL", + "RLAY", + "RMBS", + "RMD", + "RMR", + "RNA", + "RNW", + "ROAD", + "ROCK", + "ROIV", + "ROK", + "ROKU", + "ROL", + "ROOT", + "ROP", + "ROST", + "RPAY", + "RPD", + "RPRX", + "RRGB", + "RRR", + "RSG", + "RTX", + "RUM", + "RUN", + "RUSHA", + "RVMD", + "RVTY", + "RXRX", + "RYAAY", + "RYTM", + "SABR", + "SAFT", + "SAIA", + "SAIC", + "SANM", + "SATS", + "SBAC", + "SBCF", + "SBGI", + "SBLK", + "SBRA", + "SBUX", + "SCHL", + "SCHW", + "SCSC", + "SDGR", + "SEAT", + "SEDG", + "SEER", + "SEIC", + "SENEA", + "SENS", + "SFM", + "SFNC", + "SGHT", + "SGML", + "SGRY", + "SHC", + "SHEN", + "SHLS", + "SHOE", + "SHOO", + "SHOP", + "SHW", + "SIBN", + "SIGA", + "SIGI", + "SIMO", + "SIRI", + "SITM", + "SJM", + "SKIN", + "SKYT", + "SKYW", + "SLAB", + "SLB", + "SLM", + "SLP", + "SLRC", + "SMBC", + "SMCI", + "SMPL", + "SMTC", + "SNA", + "SNDX", + "SNEX", + "SNPS", + "SNY", + "SO", + "SONO", + "SPFI", + "SPG", + "SPGI", + "SPOK", + "SPSC", + "SPT", + "SPWH", + "SRAD", + "SRCE", + "SRE", + "SRPT", + "SRRK", + "SRTS", + "SSNC", + "SSP", + "SSRM", + "SSTI", + "SSYS", + "STAA", + "STBA", + "STE", + "STEP", + "STGW", + "STLD", + "STNE", + "STOK", + "STRA", + "STRL", + "STRO", + "STT", + "STX", + "STZ", + "SUNE", + "SUPN", + "SVC", + "SW", + "SWBI", + "SWK", + "SWKS", + "SYBT", + "SYF", + "SYK", + "SYM", + "SYNA", + "SYY", + "T", + "TAP", + "TARS", + "TASK", + "TBBK", + "TBCH", + "TBLD", + "TBPH", + "TC", + "TCBI", + "TCBK", + "TCMD", + "TCOM", + "TCPC", + "TCRT", + "TCX", + "TDG", + "TDY", + "TEAM", + "TECH", + "TEL", + "TENB", + "TER", + "TFC", + "TFSL", + "TGT", + "TGTX", + "TH", + "THFF", + "THRM", + "THRY", + "TIGO", + "TIGR", + "TIL", + "TILE", + "TITN", + "TJX", + "TKO", + "TLRY", + "TMCI", + "TMDX", + "TMO", + "TMUS", + "TNDM", + "TNGX", + "TOWN", + "TPL", + "TPR", + "TREE", + "TRGP", + "TRI", + "TRIN", + "TRIP", + "TRMB", + "TRMD", + "TRMK", + "TRNS", + "TROW", + "TRS", + "TRST", + "TRUP", + "TRV", + "TSCO", + "TSEM", + "TSLA", + "TSN", + "TT", + "TTD", + "TTEC", + "TTEK", + "TTGT", + "TTMI", + "TTWO", + "TVRD", + "TVTX", + "TW", + "TWST", + "TXG", + "TXN", + "TXRH", + "TXT", + "TYL", + "UAL", + "UBER", + "UBSI", + "UCTT", + "UDR", + "UEIC", + "UFCS", + "UFPI", + "UFPT", + "UHS", + "ULCC", + "ULH", + "ULTA", + "UMBF", + "UNH", + "UNIT", + "UNP", + "UPBD", + "UPLD", + "UPS", + "UPST", + "UPWK", + "URBN", + "URI", + "USB", + "UTHR", + "UVSP", + "V", + "VC", + "VCEL", + "VCTR", + "VCYT", + "VECO", + "VERA", + "VERI", + "VERU", + "VERX", + "VIAV", + "VICI", + "VICR", + "VIR", + "VISN", + "VITL", + "VLO", + "VLY", + "VMC", + "VNDA", + "VNET", + "VNOM", + "VOD", + "VRDN", + "VREX", + "VRM", + "VRNS", + "VRRM", + "VRSK", + "VRSN", + "VRT", + "VRTX", + "VSAT", + "VSEC", + "VST", + "VTR", + "VTRS", + "VZ", + "WAB", + "WABC", + "WAFD", + "WASH", + "WAT", + "WB", + "WDAY", + "WDC", + "WDFC", + "WEC", + "WELL", + "WEN", + "WERN", + "WEST", + "WFC", + "WFRD", + "WHWK", + "WINA", + "WING", + "WIX", + "WKHS", + "WM", + "WMB", + "WMG", + "WMT", + "WOOF", + "WRB", + "WRLD", + "WSBC", + "WSBF", + "WSC", + "WSFS", + "WSM", + "WST", + "WTFC", + "WTW", + "WWD", + "WY", + "WYNN", + "XAIR", + "XEL", + "XENE", + "XFOR", + "XMTR", + "XNCR", + "XOM", + "XP", + "XPEL", + "XRAY", + "XRX", + "XYL", + "XYZ", + "YORW", + "YUM", + "Z", + "ZBH", + "ZBRA", + "ZD", + "ZG", + "ZION", + "ZLAB", + "ZM", + "ZNTL", + "ZS", + "ZTS", + "ZUMZ", + "ZVZZT", + "ZYME" + ], + "n_symbols": 1500 + }, + { + "week": [ + 2022, + 31 + ], + "stats": { + "raw_pool": 2800, + "eligible_pre_mask": 2332, + "post_mask": 1500, + "mask_binds": true + }, + "symbols": [ + "A", + "AAL", + "AAON", + "AAPL", + "ABBV", + "ABCL", + "ABNB", + "ABT", + "ACAD", + "ACB", + "ACET", + "ACGL", + "ACHC", + "ACIW", + "ACLS", + "ACMR", + "ACN", + "ACRS", + "ACT", + "ACTG", + "ADAM", + "ADBE", + "ADEA", + "ADI", + "ADM", + "ADP", + "ADPT", + "ADSK", + "ADTN", + "ADUS", + "ADV", + "AEE", + "AEHR", + "AEIS", + "AEP", + "AES", + "AEVA", + "AFCG", + "AFL", + "AFRM", + "AFYA", + "AGEN", + "AGIO", + "AGNC", + "AGNT", + "AGYS", + "AHCO", + "AIG", + "AIZ", + "AJG", + "AKAM", + "ALB", + "ALCO", + "ALDX", + "ALEC", + "ALGM", + "ALGN", + "ALGT", + "ALHC", + "ALKS", + "ALKT", + "ALL", + "ALLE", + "ALLO", + "ALNY", + "ALRM", + "ALT", + "ALXO", + "AMAL", + "AMAT", + "AMBA", + "AMCR", + "AMCX", + "AMD", + "AME", + "AMGN", + "AMKR", + "AMP", + "AMPH", + "AMRN", + "AMSC", + "AMSF", + "AMT", + "AMTX", + "AMZN", + "ANAB", + "ANDE", + "ANET", + "ANGI", + "ANGO", + "ANIK", + "ANIP", + "AON", + "AOS", + "AOSL", + "AOUT", + "APA", + "APD", + "APH", + "API", + "APO", + "APOG", + "APP", + "APPF", + "APPN", + "APPS", + "APTV", + "APYX", + "ARCB", + "ARCC", + "ARCT", + "ARE", + "ARES", + "ARGX", + "ARKO", + "ARLP", + "ARQT", + "ARRY", + "ARTNA", + "ARVN", + "ARWR", + "ASLE", + "ASML", + "ASND", + "ASO", + "ASTE", + "ASTH", + "ASTL", + "ASTS", + "ATEC", + "ATER", + "ATEX", + "ATLC", + "ATNI", + "ATO", + "ATOM", + "ATRA", + "ATRC", + "AUDC", + "AUPH", + "AVAV", + "AVB", + "AVGO", + "AVIR", + "AVNW", + "AVO", + "AVPT", + "AVT", + "AVXL", + "AVY", + "AWK", + "AXGN", + "AXON", + "AXP", + "AXSM", + "AZO", + "AZTA", + "BA", + "BAC", + "BALL", + "BAND", + "BANF", + "BANR", + "BATRK", + "BAX", + "BBIO", + "BBSI", + "BBY", + "BCBP", + "BCML", + "BCPC", + "BCRX", + "BCTX", + "BCYC", + "BDX", + "BEAM", + "BEEM", + "BEN", + "BF-B", + "BG", + "BHF", + "BHFAN", + "BIDU", + "BIIB", + "BILI", + "BJRI", + "BK", + "BKNG", + "BKR", + "BL", + "BLDP", + "BLDR", + "BLFS", + "BLK", + "BLKB", + "BLMN", + "BLNK", + "BMBL", + "BMEA", + "BMRN", + "BMY", + "BNGO", + "BNR", + "BNTX", + "BOKF", + "BOOM", + "BPOP", + "BR", + "BRK-B", + "BRKR", + "BRO", + "BSET", + "BSX", + "BSY", + "BTAI", + "BUSE", + "BVS", + "BWIN", + "BX", + "BXP", + "BYND", + "BYRN", + "BZ", + "BZUN", + "C", + "CAC", + "CACC", + "CAG", + "CAH", + "CAKE", + "CALM", + "CAMT", + "CAR", + "CARG", + "CARR", + "CASH", + "CASS", + "CASY", + "CAT", + "CATY", + "CB", + "CBOE", + "CBRE", + "CBRL", + "CBSH", + "CCB", + "CCC", + "CCCC", + "CCD", + "CCEC", + "CCEP", + "CCI", + "CCL", + "CCOI", + "CCRN", + "CCXI", + "CDLX", + "CDNA", + "CDNS", + "CDW", + "CDXS", + "CELH", + "CELU", + "CENN", + "CENT", + "CENTA", + "CENX", + "CERS", + "CERT", + "CEVA", + "CF", + "CFFN", + "CFG", + "CG", + "CGBD", + "CGC", + "CGEM", + "CGNX", + "CHCO", + "CHD", + "CHDN", + "CHEF", + "CHI", + "CHKP", + "CHRD", + "CHRS", + "CHRW", + "CHTR", + "CHW", + "CHY", + "CI", + "CIEN", + "CIGI", + "CINF", + "CL", + "CLAR", + "CLBK", + "CLDX", + "CLFD", + "CLNE", + "CLPT", + "CLX", + "CMCO", + "CMCSA", + "CME", + "CMG", + "CMI", + "CMPR", + "CMPS", + "CMRC", + "CMS", + "CMTL", + "CNC", + "CNOB", + "CNP", + "CNXC", + "CNXN", + "CODX", + "COF", + "COGT", + "COHR", + "COHU", + "COIN", + "COKE", + "COLB", + "COLL", + "COLM", + "COO", + "COP", + "COR", + "CORT", + "COST", + "CPAY", + "CPB", + "CPRT", + "CPSS", + "CPT", + "CRAI", + "CRBU", + "CRH", + "CRIS", + "CRL", + "CRM", + "CRMT", + "CRNC", + "CRNX", + "CROX", + "CRSP", + "CRSR", + "CRTO", + "CRUS", + "CRVL", + "CRWD", + "CSCO", + "CSGP", + "CSIQ", + "CSQ", + "CSTL", + "CSWC", + "CSX", + "CTAS", + "CTBI", + "CTKB", + "CTRA", + "CTRM", + "CTRN", + "CTSH", + "CTVA", + "CVBF", + "CVCO", + "CVLT", + "CVNA", + "CVS", + "CVX", + "CWST", + "CYRX", + "CYTK", + "CZR", + "D", + "DAL", + "DASH", + "DAVE", + "DBX", + "DCBO", + "DCGO", + "DD", + "DDOG", + "DE", + "DECK", + "DELL", + "DFTX", + "DG", + "DGICA", + "DGII", + "DGX", + "DHI", + "DHR", + "DIOD", + "DIS", + "DJT", + "DKNG", + "DLO", + "DLR", + "DLTR", + "DMLP", + "DMRC", + "DNLI", + "DNUT", + "DOC", + "DOCU", + "DOMO", + "DOO", + "DORM", + "DOV", + "DOW", + "DOX", + "DPZ", + "DRH", + "DRI", + "DRS", + "DRVN", + "DSGN", + "DSGX", + "DTE", + "DTIL", + "DUK", + "DUOL", + "DVA", + "DVN", + "DXCM", + "DXPE", + "EA", + "EBAY", + "EBC", + "ECHO", + "ECL", + "ECPG", + "ED", + "EDIT", + "EEFT", + "EFSC", + "EFX", + "EG", + "EGBN", + "EH", + "EHTH", + "EIX", + "EL", + "ELV", + "EME", + "EMR", + "ENPH", + "ENSG", + "ENTA", + "ENTG", + "ENVX", + "EOG", + "EOLS", + "EPAM", + "EQIX", + "EQR", + "EQT", + "ERAS", + "ERIC", + "ERIE", + "ERII", + "ES", + "ESLT", + "ESS", + "ESTA", + "ETN", + "ETR", + "EVCM", + "EVER", + "EVGO", + "EVRG", + "EW", + "EWBC", + "EWTX", + "EXC", + "EXE", + "EXEL", + "EXLS", + "EXPD", + "EXPE", + "EXPO", + "EXR", + "EXTR", + "EYE", + "EZPW", + "F", + "FA", + "FANG", + "FAST", + "FATE", + "FBNC", + "FCEL", + "FCFS", + "FCNCA", + "FCX", + "FDMT", + "FDS", + "FDUS", + "FDX", + "FE", + "FELE", + "FFAI", + "FFBC", + "FFIN", + "FFIV", + "FHB", + "FHTX", + "FIBK", + "FICO", + "FIS", + "FISV", + "FITB", + "FIVE", + "FIVN", + "FIX", + "FIZZ", + "FLEX", + "FLGT", + "FLL", + "FLNA", + "FLWS", + "FLYW", + "FMAO", + "FMBH", + "FNKO", + "FORM", + "FORR", + "FOSL", + "FOX", + "FOXA", + "FOXF", + "FRHC", + "FRME", + "FROG", + "FRPT", + "FRT", + "FSLR", + "FSLY", + "FSV", + "FTAI", + "FTCI", + "FTDR", + "FTNT", + "FTV", + "FULC", + "FULT", + "FUTU", + "FWONA", + "FWONK", + "FWRD", + "GABC", + "GAIN", + "GBDC", + "GCMG", + "GD", + "GDDY", + "GDRX", + "GDS", + "GDYN", + "GE", + "GEN", + "GGAL", + "GGR", + "GH", + "GIII", + "GILD", + "GIS", + "GL", + "GLAD", + "GLBE", + "GLNG", + "GLPI", + "GLUE", + "GLW", + "GM", + "GMAB", + "GNRC", + "GNTX", + "GO", + "GOGO", + "GOOD", + "GOOG", + "GOOGL", + "GOSS", + "GOVX", + "GPC", + "GPN", + "GPRE", + "GPRO", + "GRFS", + "GRMN", + "GRPN", + "GS", + "GSAT", + "GSBC", + "GSHD", + "GSM", + "GT", + "GTM", + "GTX", + "GWW", + "HAFC", + "HAIN", + "HAL", + "HALO", + "HAPN", + "HAS", + "HBAN", + "HBNC", + "HCA", + "HCAT", + "HCKT", + "HCM", + "HCSG", + "HD", + "HDSN", + "HELE", + "HFWA", + "HIG", + "HII", + "HIMX", + "HIVE", + "HLIT", + "HLMN", + "HLNE", + "HLT", + "HNRG", + "HOFT", + "HON", + "HOOD", + "HOPE", + "HPE", + "HPK", + "HPQ", + "HQY", + "HRL", + "HRMY", + "HRZN", + "HSIC", + "HST", + "HSTM", + "HSY", + "HTHT", + "HTLD", + "HTO", + "HUBB", + "HUBG", + "HUM", + "HURN", + "HUT", + "HWC", + "HWKN", + "HWM", + "HYFM", + "HYMC", + "IART", + "IBCP", + "IBKR", + "IBM", + "IBOC", + "ICE", + "ICFI", + "ICHR", + "ICLR", + "ICUI", + "IDCC", + "IDXX", + "IDYA", + "IEP", + "IEX", + "IFF", + "IHRT", + "III", + "IIIV", + "ILMN", + "ILPT", + "IMCR", + "IMKTA", + "IMMR", + "IMXI", + "INCY", + "INDB", + "INDI", + "INGN", + "INMD", + "INO", + "INSE", + "INSG", + "INSM", + "INTA", + "INTC", + "INTU", + "INVA", + "INVH", + "INVZ", + "IONS", + "IOSP", + "IOVA", + "IP", + "IPAR", + "IPGP", + "IQV", + "IR", + "IRDM", + "IRM", + "IRTC", + "IRWD", + "ISRG", + "IT", + "ITRI", + "ITW", + "IVZ", + "J", + "JACK", + "JAZZ", + "JBHT", + "JBL", + "JBLU", + "JBSS", + "JCI", + "JD", + "JJSF", + "JKHY", + "JNJ", + "JOUT", + "JOYY", + "JPM", + "JRVR", + "JYNT", + "KALU", + "KDP", + "KE", + "KELYA", + "KEY", + "KEYS", + "KHC", + "KIDS", + "KIM", + "KKR", + "KLAC", + "KLIC", + "KLRS", + "KMB", + "KMI", + "KNSA", + "KO", + "KOD", + "KPTI", + "KR", + "KRNT", + "KRNY", + "KROS", + "KRUS", + "KRYS", + "KTOS", + "KURA", + "KYMR", + "KYNB", + "L", + "LAMR", + "LAND", + "LASR", + "LAUR", + "LBRDA", + "LBRDK", + "LBTYA", + "LBTYK", + "LCID", + "LDOS", + "LE", + "LECO", + "LEGN", + "LEN", + "LENZ", + "LESL", + "LFST", + "LFUS", + "LGIH", + "LGND", + "LH", + "LHX", + "LI", + "LIDR", + "LII", + "LILA", + "LILAK", + "LIN", + "LIND", + "LITE", + "LITS", + "LIVN", + "LKFN", + "LKFT", + "LKQ", + "LLY", + "LMAT", + "LMT", + "LNT", + "LNTH", + "LOCO", + "LOGI", + "LONA", + "LOPE", + "LOVE", + "LOW", + "LPLA", + "LPRO", + "LPSN", + "LQDA", + "LQDT", + "LRCX", + "LSCC", + "LSTR", + "LULU", + "LUNG", + "LUV", + "LVS", + "LYB", + "LYEL", + "LYFT", + "LYV", + "LZ", + "MA", + "MAA", + "MANH", + "MAR", + "MARA", + "MAS", + "MASS", + "MAT", + "MATW", + "MBIN", + "MBUU", + "MCD", + "MCFT", + "MCHB", + "MCHP", + "MCK", + "MCO", + "MCRB", + "MCRI", + "MDB", + "MDGL", + "MDLZ", + "MDT", + "MEDP", + "MELI", + "MEOH", + "MERC", + "MET", + "META", + "METC", + "MFIC", + "MGEE", + "MGM", + "MGNI", + "MGPI", + "MGRC", + "MIDD", + "MIRM", + "MITK", + "MKC", + "MKSI", + "MKTX", + "MLAB", + "MLCO", + "MLKN", + "MLM", + "MMM", + "MMSI", + "MMYT", + "MNDY", + "MNRO", + "MNST", + "MNTK", + "MNTS", + "MO", + "MORN", + "MOS", + "MPC", + "MPWR", + "MQ", + "MRCY", + "MRK", + "MRNA", + "MRSH", + "MRTN", + "MRVI", + "MRVL", + "MS", + "MSBI", + "MSCI", + "MSEX", + "MSFT", + "MSI", + "MSTR", + "MTB", + "MTCH", + "MTD", + "MTLS", + "MTRX", + "MTSI", + "MU", + "MVIS", + "MXCT", + "MXL", + "MYGN", + "MYRG", + "MZTI", + "NAVI", + "NBIX", + "NBN", + "NBP", + "NBTB", + "NCLH", + "NCMI", + "NCNO", + "NDAQ", + "NDSN", + "NEE", + "NEGG", + "NEM", + "NEO", + "NEOG", + "NESR", + "NEWT", + "NEXT", + "NFBK", + "NFE", + "NFLX", + "NI", + "NICE", + "NIU", + "NKE", + "NKTR", + "NKTX", + "NMFC", + "NMIH", + "NMRK", + "NNOX", + "NOC", + "NOVT", + "NOW", + "NRC", + "NRG", + "NRIX", + "NSC", + "NSIT", + "NSSC", + "NTAP", + "NTCT", + "NTES", + "NTGR", + "NTLA", + "NTNX", + "NTRA", + "NTRS", + "NUE", + "NVAX", + "NVCR", + "NVDA", + "NVEC", + "NVMI", + "NVR", + "NVTS", + "NWBI", + "NWE", + "NWL", + "NWPX", + "NWS", + "NWSA", + "NXPI", + "NXST", + "O", + "OCFC", + "OCSL", + "ODFL", + "OFIX", + "OKE", + "OKTA", + "OLED", + "OLLI", + "OM", + "OMAB", + "OMC", + "OMCL", + "OMER", + "ON", + "ONB", + "ONC", + "ONDS", + "ONEW", + "OPAL", + "OPCH", + "OPEN", + "OPI", + "OPRT", + "OPRX", + "ORCL", + "ORGO", + "ORLY", + "ORMP", + "OSBC", + "OSIS", + "OSPN", + "OSW", + "OTEX", + "OTIS", + "OTLY", + "OTTR", + "OUST", + "OXLC", + "OXY", + "OZK", + "PAA", + "PACB", + "PAGP", + "PAHC", + "PANW", + "PATK", + "PAX", + "PAYO", + "PAYX", + "PCAR", + "PCG", + "PCRX", + "PCT", + "PCTY", + "PCVX", + "PDD", + "PDFS", + "PDSB", + "PEBO", + "PECO", + "PEG", + "PEGA", + "PENG", + "PENN", + "PEP", + "PERI", + "PETS", + "PFBC", + "PFE", + "PFG", + "PG", + "PGC", + "PGNY", + "PGR", + "PGY", + "PH", + "PHAT", + "PHM", + "PHUN", + "PI", + "PKG", + "PLAB", + "PLAY", + "PLBY", + "PLCE", + "PLD", + "PLMR", + "PLRX", + "PLTK", + "PLTR", + "PLUG", + "PLUS", + "PLXS", + "PM", + "PMVP", + "PNC", + "PNR", + "PNTG", + "PNW", + "PODD", + "POOL", + "POWI", + "POWW", + "PPC", + "PPG", + "PPL", + "PPLI", + "PRAA", + "PRAX", + "PRDO", + "PRGS", + "PRTA", + "PRTS", + "PRU", + "PRVA", + "PSA", + "PSEC", + "PSMT", + "PSX", + "PTC", + "PTCT", + "PTEN", + "PTGX", + "PTON", + "PUBM", + "PWP", + "PWR", + "PYPL", + "PZZA", + "QCOM", + "QCRH", + "QDEL", + "QFIN", + "QLYS", + "QNST", + "QQQX", + "QRVO", + "QS", + "QTRX", + "QURE", + "RARE", + "RCKT", + "RCL", + "RCMT", + "RDNT", + "RDNW", + "RDWR", + "REG", + "REGN", + "RELL", + "REPL", + "REYN", + "RF", + "RGEN", + "RGLD", + "RGNX", + "RGP", + "RICK", + "RIGL", + "RILY", + "RIOT", + "RJF", + "RKLB", + "RL", + "RLAY", + "RLMD", + "RMBS", + "RMD", + "RMNI", + "RMR", + "RNA", + "RNAC", + "RNW", + "ROAD", + "ROCK", + "ROK", + "ROKU", + "ROL", + "ROOT", + "ROP", + "ROST", + "RPAY", + "RPD", + "RPRX", + "RRGB", + "RRR", + "RSG", + "RTX", + "RUM", + "RUN", + "RUSHA", + "RUSHB", + "RVMD", + "RVTY", + "RXRX", + "RXT", + "RYAAY", + "RYTM", + "SABR", + "SAFT", + "SAIA", + "SAIC", + "SAIL", + "SANA", + "SANM", + "SATS", + "SBAC", + "SBCF", + "SBGI", + "SBLK", + "SBRA", + "SBUX", + "SCHL", + "SCHW", + "SCSC", + "SDGR", + "SEAT", + "SEDG", + "SEER", + "SEIC", + "SENEA", + "SENS", + "SFIX", + "SFM", + "SFNC", + "SGHT", + "SGRY", + "SHBI", + "SHC", + "SHEN", + "SHIP", + "SHLS", + "SHOE", + "SHOO", + "SHOP", + "SHW", + "SIBN", + "SIGA", + "SIGI", + "SIMO", + "SIRI", + "SITM", + "SJM", + "SKIN", + "SKYW", + "SLAB", + "SLB", + "SLDP", + "SLM", + "SLP", + "SLRC", + "SMCI", + "SMPL", + "SMTC", + "SNA", + "SNDX", + "SNEX", + "SNPS", + "SNY", + "SO", + "SOFI", + "SOHU", + "SONO", + "SPG", + "SPGI", + "SPSC", + "SPT", + "SPWH", + "SRCE", + "SRE", + "SRPT", + "SRRK", + "SRTA", + "SRTS", + "SSNC", + "SSP", + "SSRM", + "SSYS", + "STAA", + "STBA", + "STE", + "STEP", + "STGW", + "STLD", + "STNE", + "STOK", + "STRA", + "STRL", + "STRO", + "STT", + "STX", + "STZ", + "SUPN", + "SVC", + "SW", + "SWBI", + "SWIM", + "SWK", + "SWKS", + "SYBT", + "SYF", + "SYK", + "SYM", + "SYNA", + "SYY", + "T", + "TAP", + "TARS", + "TASK", + "TBBK", + "TBCH", + "TBPH", + "TCBI", + "TCBK", + "TCMD", + "TCOM", + "TCPC", + "TCRT", + "TCX", + "TDG", + "TDY", + "TEAD", + "TEAM", + "TECH", + "TEL", + "TENB", + "TER", + "TFC", + "TFSL", + "TGT", + "TGTX", + "TH", + "THFF", + "THRM", + "THRY", + "TIGO", + "TIL", + "TILE", + "TITN", + "TJX", + "TKO", + "TLRY", + "TLS", + "TMCI", + "TMDX", + "TMO", + "TMUS", + "TNDM", + "TNXP", + "TOWN", + "TPL", + "TPR", + "TREE", + "TRGP", + "TRI", + "TRIN", + "TRIP", + "TRMB", + "TRMD", + "TRMK", + "TRNS", + "TROW", + "TRS", + "TRST", + "TRUP", + "TRV", + "TSCO", + "TSEM", + "TSLA", + "TSN", + "TT", + "TTD", + "TTEC", + "TTEK", + "TTGT", + "TTMI", + "TTWO", + "TVRD", + "TVTX", + "TW", + "TWST", + "TXG", + "TXMD", + "TXN", + "TXRH", + "TXT", + "TYL", + "UAL", + "UBER", + "UBSI", + "UCTT", + "UDR", + "UFCS", + "UFPI", + "UFPT", + "UHS", + "ULCC", + "ULH", + "ULTA", + "UMBF", + "UNH", + "UNIT", + "UNP", + "UONE", + "UPBD", + "UPLD", + "UPS", + "UPST", + "UPWK", + "URBN", + "URI", + "USB", + "UTHR", + "UVSP", + "V", + "VC", + "VCEL", + "VCTR", + "VCYT", + "VECO", + "VERI", + "VERU", + "VIAV", + "VICI", + "VICR", + "VIR", + "VISN", + "VITL", + "VLO", + "VLY", + "VMC", + "VNDA", + "VNET", + "VNOM", + "VOD", + "VRDN", + "VREX", + "VRM", + "VRNS", + "VRRM", + "VRSK", + "VRSN", + "VRT", + "VRTX", + "VSAT", + "VST", + "VSTM", + "VTR", + "VTRS", + "VUZI", + "VYGR", + "VZ", + "WAB", + "WABC", + "WAFD", + "WASH", + "WAT", + "WB", + "WDAY", + "WDC", + "WDFC", + "WEC", + "WELL", + "WEN", + "WERN", + "WFC", + "WFRD", + "WGS", + "WHWK", + "WINA", + "WING", + "WIX", + "WKHS", + "WM", + "WMB", + "WMG", + "WMT", + "WOOF", + "WRB", + "WRLD", + "WSBC", + "WSBF", + "WSC", + "WSFS", + "WSM", + "WST", + "WTFC", + "WTW", + "WW", + "WWD", + "WY", + "WYNN", + "XAIR", + "XEL", + "XENE", + "XMTR", + "XNCR", + "XOM", + "XP", + "XPEL", + "XRAY", + "XRX", + "XYL", + "XYZ", + "YORW", + "YUM", + "Z", + "ZBH", + "ZBRA", + "ZD", + "ZG", + "ZION", + "ZLAB", + "ZM", + "ZNTL", + "ZS", + "ZTS", + "ZUMZ", + "ZVRA", + "ZVZZT", + "ZYME" + ], + "n_symbols": 1500 + }, + { + "week": [ + 2022, + 37 + ], + "stats": { + "raw_pool": 2853, + "eligible_pre_mask": 2318, + "post_mask": 1500, + "mask_binds": true + }, + "symbols": [ + "A", + "AAL", + "AAON", + "AAPL", + "ABBV", + "ABCL", + "ABNB", + "ABT", + "ACAD", + "ACB", + "ACET", + "ACGL", + "ACHC", + "ACIW", + "ACLS", + "ACMR", + "ACN", + "ACRS", + "ACT", + "ADAM", + "ADBE", + "ADEA", + "ADI", + "ADM", + "ADP", + "ADPT", + "ADSK", + "ADTN", + "ADUS", + "ADV", + "AEE", + "AEHR", + "AEIS", + "AEP", + "AES", + "AEVA", + "AFCG", + "AFL", + "AFRM", + "AFYA", + "AGEN", + "AGIO", + "AGNC", + "AGNT", + "AGYS", + "AHCO", + "AIG", + "AIZ", + "AJG", + "AKAM", + "ALB", + "ALCO", + "ALDX", + "ALEC", + "ALGM", + "ALGN", + "ALGT", + "ALHC", + "ALKS", + "ALKT", + "ALL", + "ALLE", + "ALLO", + "ALNY", + "ALRM", + "ALT", + "ALXO", + "AMAL", + "AMAT", + "AMBA", + "AMCR", + "AMCX", + "AMD", + "AME", + "AMGN", + "AMKR", + "AMP", + "AMPH", + "AMRN", + "AMSC", + "AMSF", + "AMT", + "AMTX", + "AMZN", + "ANAB", + "ANDE", + "ANET", + "ANGI", + "ANGO", + "ANIK", + "ANIP", + "AON", + "AOS", + "AOSL", + "APA", + "APD", + "APH", + "APO", + "APOG", + "APP", + "APPF", + "APPN", + "APPS", + "APTV", + "APYX", + "ARCB", + "ARCC", + "ARCT", + "ARE", + "ARES", + "ARGX", + "ARKO", + "ARLP", + "ARQT", + "ARRY", + "ARTNA", + "ARVN", + "ARWR", + "ASLE", + "ASML", + "ASND", + "ASO", + "ASTE", + "ASTH", + "ASTL", + "ASTS", + "ATEC", + "ATER", + "ATEX", + "ATLC", + "ATNI", + "ATO", + "ATOM", + "ATRA", + "ATRC", + "AUDC", + "AUPH", + "AVAV", + "AVB", + "AVGO", + "AVIR", + "AVNW", + "AVO", + "AVT", + "AVXL", + "AVY", + "AWK", + "AXGN", + "AXON", + "AXP", + "AXSM", + "AXTI", + "AZO", + "AZTA", + "BA", + "BAC", + "BALL", + "BAND", + "BANF", + "BANR", + "BATRA", + "BATRK", + "BAX", + "BBIO", + "BBSI", + "BBY", + "BCAB", + "BCBP", + "BCML", + "BCPC", + "BCRX", + "BCTX", + "BCYC", + "BDX", + "BEAM", + "BEEM", + "BELFB", + "BEN", + "BF-B", + "BG", + "BHF", + "BHFAN", + "BIDU", + "BIIB", + "BILI", + "BIOX", + "BJRI", + "BK", + "BKNG", + "BKR", + "BL", + "BLDP", + "BLDR", + "BLFS", + "BLK", + "BLKB", + "BLMN", + "BLNK", + "BMBL", + "BMEA", + "BMRC", + "BMRN", + "BMY", + "BNGO", + "BNR", + "BNTX", + "BOKF", + "BOOM", + "BPOP", + "BR", + "BRK-B", + "BRKR", + "BRO", + "BSET", + "BSX", + "BSY", + "BTAI", + "BUSE", + "BVS", + "BWIN", + "BX", + "BXP", + "BYND", + "BZ", + "BZUN", + "C", + "CAC", + "CACC", + "CAG", + "CAH", + "CAKE", + "CALM", + "CAMT", + "CAR", + "CARG", + "CARR", + "CASH", + "CASS", + "CASY", + "CAT", + "CATY", + "CB", + "CBOE", + "CBRE", + "CBRL", + "CBSH", + "CCB", + "CCC", + "CCCC", + "CCD", + "CCEC", + "CCEP", + "CCI", + "CCL", + "CCOI", + "CCRN", + "CCXI", + "CDLX", + "CDNA", + "CDNS", + "CDW", + "CDXS", + "CECO", + "CELH", + "CELU", + "CENN", + "CENT", + "CENTA", + "CENX", + "CERT", + "CEVA", + "CF", + "CFFN", + "CFG", + "CG", + "CGBD", + "CGC", + "CGEM", + "CGNT", + "CGNX", + "CHCO", + "CHD", + "CHDN", + "CHEF", + "CHI", + "CHKP", + "CHRD", + "CHRS", + "CHRW", + "CHTR", + "CHW", + "CHY", + "CI", + "CIEN", + "CIGI", + "CINF", + "CL", + "CLAR", + "CLBK", + "CLDX", + "CLFD", + "CLMT", + "CLNE", + "CLPT", + "CLX", + "CMCO", + "CMCSA", + "CME", + "CMG", + "CMI", + "CMPR", + "CMPS", + "CMRC", + "CMS", + "CMTL", + "CNC", + "CNOB", + "CNP", + "CNXC", + "CNXN", + "CODX", + "COF", + "COGT", + "COHR", + "COHU", + "COIN", + "COKE", + "COLB", + "COLL", + "COLM", + "COO", + "COP", + "COR", + "CORT", + "COST", + "CPAY", + "CPB", + "CPRT", + "CPSS", + "CPT", + "CRAI", + "CRBU", + "CRCT", + "CRH", + "CRIS", + "CRL", + "CRM", + "CRMT", + "CRNC", + "CRNX", + "CROX", + "CRSP", + "CRSR", + "CRTO", + "CRUS", + "CRVL", + "CRWD", + "CSCO", + "CSGP", + "CSIQ", + "CSQ", + "CSTL", + "CSWC", + "CSX", + "CTAS", + "CTBI", + "CTKB", + "CTRA", + "CTRN", + "CTSH", + "CTVA", + "CVBF", + "CVCO", + "CVLT", + "CVNA", + "CVS", + "CVX", + "CWCO", + "CWST", + "CYRX", + "CYTK", + "CZR", + "D", + "DAL", + "DASH", + "DAVE", + "DBX", + "DCBO", + "DCGO", + "DD", + "DDOG", + "DE", + "DECK", + "DELL", + "DFTX", + "DG", + "DGICA", + "DGII", + "DGX", + "DH", + "DHI", + "DHR", + "DIOD", + "DIS", + "DJT", + "DKNG", + "DLO", + "DLR", + "DLTR", + "DMLP", + "DMRC", + "DNLI", + "DNUT", + "DOC", + "DOCU", + "DOMO", + "DOO", + "DORM", + "DOV", + "DOW", + "DOX", + "DOYU", + "DPZ", + "DRH", + "DRI", + "DRS", + "DRVN", + "DSGN", + "DSGR", + "DSGX", + "DTE", + "DTIL", + "DUK", + "DUOL", + "DVA", + "DVN", + "DXCM", + "DXLG", + "DXPE", + "DYN", + "EA", + "EBAY", + "EBC", + "ECHO", + "ECL", + "ECPG", + "ED", + "EDIT", + "EEFT", + "EFSC", + "EFX", + "EG", + "EGBN", + "EH", + "EHTH", + "EIX", + "EL", + "ELV", + "EME", + "EMR", + "ENPH", + "ENSG", + "ENTA", + "ENTG", + "ENVX", + "EOG", + "EOLS", + "EPAM", + "EQIX", + "EQR", + "EQT", + "ERAS", + "ERIC", + "ERIE", + "ERII", + "ES", + "ESLT", + "ESS", + "ESTA", + "ETN", + "ETR", + "EVCM", + "EVER", + "EVGO", + "EVRG", + "EW", + "EWBC", + "EWTX", + "EXC", + "EXE", + "EXEL", + "EXLS", + "EXPD", + "EXPE", + "EXPO", + "EXR", + "EXTR", + "EYE", + "EYPT", + "EZPW", + "F", + "FA", + "FANG", + "FAST", + "FATE", + "FBNC", + "FCEL", + "FCFS", + "FCNCA", + "FCX", + "FDMT", + "FDS", + "FDUS", + "FDX", + "FE", + "FELE", + "FFAI", + "FFBC", + "FFIN", + "FFIV", + "FHB", + "FIBK", + "FICO", + "FIS", + "FISV", + "FITB", + "FIVE", + "FIVN", + "FIX", + "FIZZ", + "FLEX", + "FLGT", + "FLNA", + "FLWS", + "FLYW", + "FMAO", + "FMNB", + "FNKO", + "FORM", + "FORR", + "FOX", + "FOXA", + "FOXF", + "FRHC", + "FRME", + "FROG", + "FRPT", + "FRT", + "FSLR", + "FSLY", + "FSV", + "FTAI", + "FTCI", + "FTDR", + "FTNT", + "FTV", + "FULC", + "FULT", + "FUTU", + "FWONA", + "FWONK", + "FWRD", + "GABC", + "GAIN", + "GBDC", + "GCMG", + "GD", + "GDDY", + "GDRX", + "GDS", + "GDYN", + "GE", + "GEN", + "GGAL", + "GGR", + "GH", + "GIII", + "GILD", + "GIS", + "GL", + "GLAD", + "GLBE", + "GLNG", + "GLPI", + "GLUE", + "GLW", + "GM", + "GMAB", + "GNRC", + "GNTX", + "GO", + "GOGO", + "GOOD", + "GOOG", + "GOOGL", + "GOSS", + "GOVX", + "GPC", + "GPN", + "GPRE", + "GPRO", + "GRFS", + "GRMN", + "GRPN", + "GS", + "GSAT", + "GSBC", + "GSHD", + "GSM", + "GT", + "GTM", + "GTX", + "GWW", + "HAFC", + "HAIN", + "HAL", + "HALO", + "HAPN", + "HAS", + "HBAN", + "HBNC", + "HCA", + "HCAT", + "HCKT", + "HCM", + "HCSG", + "HD", + "HDSN", + "HELE", + "HFWA", + "HIG", + "HII", + "HIMX", + "HLIT", + "HLMN", + "HLNE", + "HLT", + "HNRG", + "HOFT", + "HON", + "HOOD", + "HOPE", + "HPE", + "HPK", + "HPQ", + "HQY", + "HRL", + "HRMY", + "HRZN", + "HSIC", + "HST", + "HSTM", + "HSY", + "HTHT", + "HTLD", + "HTO", + "HUBB", + "HUBG", + "HUM", + "HURN", + "HUT", + "HWC", + "HWKN", + "HWM", + "HYFM", + "HYMC", + "IART", + "IBCP", + "IBKR", + "IBM", + "IBOC", + "IBRX", + "ICE", + "ICFI", + "ICHR", + "ICLR", + "ICUI", + "IDCC", + "IDXX", + "IDYA", + "IEP", + "IEX", + "IFF", + "IHRT", + "III", + "IIIV", + "ILMN", + "ILPT", + "IMCR", + "IMKTA", + "IMMR", + "IMTX", + "IMXI", + "INCY", + "INDB", + "INDI", + "INGN", + "INMD", + "INO", + "INSE", + "INSG", + "INSM", + "INTC", + "INTU", + "INVA", + "INVH", + "INVZ", + "IONS", + "IOSP", + "IOVA", + "IP", + "IPAR", + "IPGP", + "IQV", + "IR", + "IRDM", + "IRM", + "IRTC", + "IRWD", + "ISRG", + "IT", + "ITRI", + "ITW", + "IVZ", + "J", + "JACK", + "JAKK", + "JAZZ", + "JBHT", + "JBIO", + "JBL", + "JBLU", + "JBSS", + "JCI", + "JD", + "JJSF", + "JKHY", + "JNJ", + "JOUT", + "JOYY", + "JPM", + "JRVR", + "JYNT", + "KALU", + "KDP", + "KE", + "KELYA", + "KEY", + "KEYS", + "KHC", + "KIDS", + "KIM", + "KKR", + "KLAC", + "KLIC", + "KLRS", + "KMB", + "KMI", + "KNSA", + "KO", + "KOD", + "KPTI", + "KR", + "KRNT", + "KRNY", + "KROS", + "KRUS", + "KRYS", + "KTOS", + "KURA", + "KYMR", + "KYNB", + "L", + "LAMR", + "LAND", + "LASR", + "LAUR", + "LBRDA", + "LBRDK", + "LBTYA", + "LBTYK", + "LCID", + "LDOS", + "LE", + "LECO", + "LEGN", + "LEN", + "LENZ", + "LESL", + "LFST", + "LFUS", + "LGIH", + "LGND", + "LH", + "LHX", + "LI", + "LIDR", + "LII", + "LILA", + "LILAK", + "LIN", + "LIND", + "LITE", + "LIVN", + "LKFN", + "LKFT", + "LKQ", + "LLY", + "LMAT", + "LMT", + "LNT", + "LNTH", + "LOCO", + "LOGI", + "LONA", + "LOPE", + "LOVE", + "LOW", + "LPLA", + "LPRO", + "LPSN", + "LQDA", + "LQDT", + "LRCX", + "LSCC", + "LSTR", + "LULU", + "LUNG", + "LUV", + "LVS", + "LWLG", + "LYB", + "LYEL", + "LYFT", + "LYV", + "LZ", + "MA", + "MAA", + "MANH", + "MAR", + "MARA", + "MAS", + "MASS", + "MAT", + "MATW", + "MBIN", + "MBUU", + "MCD", + "MCFT", + "MCHB", + "MCHP", + "MCK", + "MCO", + "MCRB", + "MCRI", + "MDB", + "MDGL", + "MDLZ", + "MDT", + "MEDP", + "MELI", + "MEOH", + "MERC", + "MET", + "META", + "METC", + "MFIC", + "MGEE", + "MGM", + "MGNI", + "MGPI", + "MGRC", + "MIDD", + "MIRM", + "MITK", + "MKC", + "MKSI", + "MKTX", + "MLAB", + "MLCO", + "MLKN", + "MLM", + "MMM", + "MMSI", + "MMYT", + "MNDY", + "MNRO", + "MNST", + "MNTK", + "MNTS", + "MO", + "MORN", + "MOS", + "MPC", + "MPWR", + "MQ", + "MRCY", + "MRK", + "MRNA", + "MRSH", + "MRTN", + "MRVI", + "MRVL", + "MS", + "MSBI", + "MSCI", + "MSEX", + "MSFT", + "MSI", + "MSTR", + "MTB", + "MTCH", + "MTD", + "MTLS", + "MTSI", + "MU", + "MXCT", + "MXL", + "MYGN", + "MYRG", + "MZTI", + "NAVI", + "NBIX", + "NBP", + "NBTB", + "NCLH", + "NCNO", + "NDAQ", + "NDSN", + "NEE", + "NEGG", + "NEM", + "NEO", + "NEOG", + "NESR", + "NEWT", + "NEXT", + "NFBK", + "NFE", + "NFLX", + "NI", + "NICE", + "NKE", + "NKTR", + "NKTX", + "NMFC", + "NMIH", + "NMRK", + "NNOX", + "NOC", + "NOVT", + "NOW", + "NRC", + "NRG", + "NRIX", + "NSC", + "NSIT", + "NSLR", + "NSSC", + "NTAP", + "NTCT", + "NTES", + "NTGR", + "NTLA", + "NTNX", + "NTRA", + "NTRS", + "NUE", + "NVAX", + "NVCR", + "NVDA", + "NVMI", + "NVR", + "NVTS", + "NWBI", + "NWE", + "NWL", + "NWS", + "NWSA", + "NXPI", + "NXST", + "O", + "OCFC", + "OCSL", + "OCUL", + "ODFL", + "OFIX", + "OFLX", + "OKE", + "OKTA", + "OLED", + "OLLI", + "OM", + "OMAB", + "OMC", + "OMCL", + "ON", + "ONB", + "ONC", + "ONEW", + "OPCH", + "OPI", + "OPRT", + "OPRX", + "ORCL", + "ORLY", + "ORMP", + "OSBC", + "OSIS", + "OSPN", + "OSW", + "OTEX", + "OTIS", + "OTLY", + "OTTR", + "OUST", + "OXLC", + "OXY", + "OZK", + "PAA", + "PACB", + "PAGP", + "PAHC", + "PANW", + "PATK", + "PAX", + "PAYO", + "PAYX", + "PCAR", + "PCG", + "PCRX", + "PCT", + "PCTY", + "PCVX", + "PDD", + "PDFS", + "PEBO", + "PECO", + "PEG", + "PEGA", + "PENG", + "PENN", + "PEP", + "PERI", + "PETS", + "PFBC", + "PFE", + "PFG", + "PG", + "PGC", + "PGNY", + "PGR", + "PGY", + "PH", + "PHAT", + "PHM", + "PHUN", + "PI", + "PKG", + "PLAB", + "PLAY", + "PLCE", + "PLD", + "PLMR", + "PLRX", + "PLTK", + "PLTR", + "PLUG", + "PLUS", + "PLXS", + "PM", + "PMVP", + "PNC", + "PNR", + "PNTG", + "PNW", + "PODD", + "POOL", + "POWI", + "PPC", + "PPG", + "PPL", + "PPLI", + "PRAA", + "PRAX", + "PRCT", + "PRDO", + "PRGS", + "PRTA", + "PRTS", + "PRU", + "PRVA", + "PSA", + "PSEC", + "PSMT", + "PSX", + "PTC", + "PTCT", + "PTEN", + "PTGX", + "PTON", + "PUBM", + "PWP", + "PWR", + "PYPL", + "PZZA", + "QCOM", + "QCRH", + "QDEL", + "QFIN", + "QLYS", + "QNRX", + "QNST", + "QQQX", + "QRVO", + "QS", + "QTRX", + "QURE", + "RARE", + "RBCAA", + "RCKT", + "RCL", + "RCMT", + "RDNT", + "RDNW", + "RDWR", + "REG", + "REGN", + "RELL", + "REPL", + "REYN", + "RF", + "RGEN", + "RGLD", + "RGNX", + "RGP", + "RICK", + "RIGL", + "RILY", + "RIOT", + "RJF", + "RKLB", + "RL", + "RLAY", + "RLMD", + "RMBS", + "RMD", + "RMR", + "RNA", + "RNAC", + "RNW", + "ROAD", + "ROCK", + "ROK", + "ROKU", + "ROL", + "ROOT", + "ROP", + "ROST", + "RPAY", + "RPD", + "RPRX", + "RRGB", + "RRR", + "RSG", + "RTX", + "RUM", + "RUN", + "RUSHA", + "RUSHB", + "RVMD", + "RVTY", + "RXRX", + "RXT", + "RYAAY", + "RYTM", + "SABR", + "SAFT", + "SAIA", + "SAIC", + "SAIL", + "SANA", + "SANM", + "SATS", + "SBAC", + "SBCF", + "SBGI", + "SBLK", + "SBRA", + "SBUX", + "SCHL", + "SCHW", + "SCSC", + "SDGR", + "SEAT", + "SEDG", + "SEER", + "SEIC", + "SENEA", + "SENS", + "SFM", + "SFNC", + "SGHT", + "SGML", + "SGRY", + "SHC", + "SHEN", + "SHIP", + "SHLS", + "SHOE", + "SHOO", + "SHOP", + "SHW", + "SIBN", + "SIGA", + "SIGI", + "SIMO", + "SIRI", + "SITM", + "SJM", + "SKIN", + "SKYT", + "SKYW", + "SLAB", + "SLB", + "SLDP", + "SLM", + "SLP", + "SLRC", + "SMCI", + "SMPL", + "SMTC", + "SNA", + "SNDX", + "SNEX", + "SNPS", + "SNY", + "SO", + "SOFI", + "SOHU", + "SONO", + "SPG", + "SPGI", + "SPSC", + "SPT", + "SPWH", + "SRAD", + "SRCE", + "SRE", + "SRPT", + "SRRK", + "SRTS", + "SSNC", + "SSP", + "SSRM", + "SSTI", + "SSYS", + "STAA", + "STBA", + "STE", + "STEP", + "STGW", + "STLD", + "STNE", + "STOK", + "STRA", + "STRL", + "STRO", + "STT", + "STX", + "STZ", + "SUPN", + "SVC", + "SW", + "SWBI", + "SWK", + "SWKS", + "SYBT", + "SYF", + "SYK", + "SYM", + "SYNA", + "SYY", + "T", + "TAP", + "TARS", + "TASK", + "TBBK", + "TBCH", + "TBLD", + "TBPH", + "TCBI", + "TCBK", + "TCMD", + "TCOM", + "TCPC", + "TCRT", + "TCX", + "TDG", + "TDY", + "TEAM", + "TECH", + "TEL", + "TENB", + "TER", + "TFC", + "TFSL", + "TGT", + "TGTX", + "TH", + "THFF", + "THRM", + "THRY", + "TIGO", + "TIL", + "TILE", + "TITN", + "TJX", + "TKO", + "TLRY", + "TLS", + "TMCI", + "TMDX", + "TMO", + "TMUS", + "TNDM", + "TNXP", + "TOWN", + "TPL", + "TPR", + "TREE", + "TRGP", + "TRI", + "TRIN", + "TRIP", + "TRMB", + "TRMD", + "TRMK", + "TRNS", + "TROW", + "TRS", + "TRST", + "TRUP", + "TRV", + "TSCO", + "TSEM", + "TSLA", + "TSN", + "TT", + "TTD", + "TTEC", + "TTEK", + "TTGT", + "TTMI", + "TTWO", + "TVRD", + "TVTX", + "TW", + "TWST", + "TXG", + "TXMD", + "TXN", + "TXRH", + "TXT", + "TYL", + "UAL", + "UBER", + "UBSI", + "UCTT", + "UDR", + "UEIC", + "UFCS", + "UFPI", + "UFPT", + "UHS", + "ULCC", + "ULH", + "ULTA", + "UMBF", + "UNH", + "UNIT", + "UNP", + "UPBD", + "UPLD", + "UPS", + "UPST", + "UPWK", + "URBN", + "URI", + "USB", + "UTHR", + "UVSP", + "V", + "VC", + "VCEL", + "VCTR", + "VCYT", + "VECO", + "VERA", + "VERI", + "VERU", + "VERX", + "VIAV", + "VICI", + "VICR", + "VIR", + "VISN", + "VITL", + "VLO", + "VLY", + "VMC", + "VNDA", + "VNET", + "VNOM", + "VOD", + "VRDN", + "VREX", + "VRM", + "VRNS", + "VRRM", + "VRSK", + "VRSN", + "VRT", + "VRTX", + "VSAT", + "VST", + "VTGN", + "VTR", + "VTRS", + "VUZI", + "VYGR", + "VZ", + "WAB", + "WABC", + "WAFD", + "WASH", + "WAT", + "WB", + "WDAY", + "WDC", + "WDFC", + "WEC", + "WELL", + "WEN", + "WERN", + "WFC", + "WFRD", + "WGS", + "WHWK", + "WINA", + "WING", + "WIX", + "WKHS", + "WM", + "WMB", + "WMG", + "WMT", + "WOOF", + "WRB", + "WRLD", + "WSBC", + "WSBF", + "WSC", + "WSFS", + "WSM", + "WST", + "WTFC", + "WTW", + "WW", + "WWD", + "WY", + "WYNN", + "XAIR", + "XEL", + "XENE", + "XMTR", + "XNCR", + "XOM", + "XP", + "XPEL", + "XRAY", + "XRX", + "XYL", + "XYZ", + "YORW", + "YUM", + "Z", + "ZBH", + "ZBRA", + "ZD", + "ZG", + "ZION", + "ZLAB", + "ZM", + "ZNTL", + "ZS", + "ZTS", + "ZUMZ", + "ZVZZT", + "ZYME" + ], + "n_symbols": 1500 + }, + { + "week": [ + 2022, + 43 + ], + "stats": { + "raw_pool": 2905, + "eligible_pre_mask": 2307, + "post_mask": 1500, + "mask_binds": true + }, + "symbols": [ + "A", + "AAL", + "AAON", + "AAPL", + "ABBV", + "ABCL", + "ABNB", + "ABT", + "ACAD", + "ACB", + "ACET", + "ACGL", + "ACGLN", + "ACHC", + "ACIW", + "ACLS", + "ACMR", + "ACN", + "ACRS", + "ACT", + "ADAM", + "ADBE", + "ADEA", + "ADI", + "ADM", + "ADP", + "ADPT", + "ADSK", + "ADTN", + "ADUS", + "ADV", + "AEE", + "AEHR", + "AEIS", + "AEP", + "AES", + "AEVA", + "AFCG", + "AFL", + "AFRM", + "AFYA", + "AGEN", + "AGIO", + "AGNC", + "AGNCN", + "AGNCO", + "AGNCP", + "AGNT", + "AGYS", + "AHCO", + "AIG", + "AIZ", + "AJG", + "AKAM", + "ALB", + "ALCO", + "ALDX", + "ALEC", + "ALGM", + "ALGN", + "ALGT", + "ALHC", + "ALKS", + "ALKT", + "ALL", + "ALLE", + "ALLO", + "ALNT", + "ALNY", + "ALRM", + "ALT", + "ALXO", + "AMAL", + "AMAT", + "AMBA", + "AMCR", + "AMCX", + "AMD", + "AME", + "AMGN", + "AMKR", + "AMP", + "AMPH", + "AMPL", + "AMRN", + "AMSF", + "AMT", + "AMTX", + "AMZN", + "ANAB", + "ANDE", + "ANET", + "ANGI", + "ANGO", + "ANIK", + "ANIP", + "AON", + "AOS", + "AOSL", + "APA", + "APD", + "APEI", + "APH", + "APO", + "APOG", + "APP", + "APPF", + "APPN", + "APPS", + "APTV", + "ARCB", + "ARCC", + "ARCT", + "ARE", + "ARES", + "ARGX", + "ARKO", + "ARLP", + "ARQT", + "ARRY", + "ARTNA", + "ARVN", + "ARWR", + "ASLE", + "ASML", + "ASND", + "ASO", + "ASTE", + "ASTH", + "ASTL", + "ASTS", + "ATEC", + "ATER", + "ATEX", + "ATNI", + "ATO", + "ATOM", + "ATRA", + "ATRC", + "AUDC", + "AUPH", + "AVAV", + "AVB", + "AVGO", + "AVIR", + "AVNW", + "AVO", + "AVT", + "AVXL", + "AVY", + "AWK", + "AXGN", + "AXON", + "AXP", + "AXSM", + "AZO", + "AZTA", + "BA", + "BAC", + "BALL", + "BAND", + "BANF", + "BANR", + "BATRA", + "BATRK", + "BAX", + "BBIO", + "BBSI", + "BBY", + "BCAB", + "BCPC", + "BCRX", + "BCTX", + "BCYC", + "BDX", + "BEAM", + "BEEM", + "BELFB", + "BEN", + "BF-B", + "BFC", + "BG", + "BHF", + "BIDU", + "BIIB", + "BILI", + "BIOX", + "BJRI", + "BK", + "BKNG", + "BKR", + "BL", + "BLDP", + "BLDR", + "BLFS", + "BLK", + "BLKB", + "BLMN", + "BLNK", + "BMBL", + "BMRC", + "BMRN", + "BMY", + "BNGO", + "BNTX", + "BOKF", + "BOOM", + "BPOP", + "BR", + "BRK-B", + "BRKR", + "BRO", + "BSET", + "BSX", + "BSY", + "BTAI", + "BUSE", + "BWIN", + "BX", + "BXP", + "BYND", + "BZ", + "C", + "CAC", + "CACC", + "CAG", + "CAH", + "CAKE", + "CALM", + "CAMT", + "CAR", + "CARE", + "CARG", + "CARR", + "CASH", + "CASS", + "CASY", + "CAT", + "CATY", + "CB", + "CBOE", + "CBRE", + "CBRL", + "CBSH", + "CCAP", + "CCB", + "CCBG", + "CCC", + "CCCC", + "CCD", + "CCEC", + "CCEP", + "CCI", + "CCL", + "CCNE", + "CCOI", + "CCRN", + "CCSI", + "CDLX", + "CDNA", + "CDNS", + "CDW", + "CDXS", + "CECO", + "CELH", + "CELU", + "CENN", + "CENT", + "CENTA", + "CENX", + "CERT", + "CEVA", + "CF", + "CFFN", + "CFG", + "CG", + "CGBD", + "CGC", + "CGEM", + "CGNX", + "CHCO", + "CHD", + "CHDN", + "CHEF", + "CHI", + "CHKP", + "CHRD", + "CHRS", + "CHRW", + "CHTR", + "CHW", + "CHY", + "CI", + "CIEN", + "CIGI", + "CINF", + "CL", + "CLAR", + "CLBK", + "CLDX", + "CLFD", + "CLMT", + "CLNE", + "CLPT", + "CLX", + "CMCO", + "CMCSA", + "CME", + "CMG", + "CMI", + "CMPR", + "CMPS", + "CMRC", + "CMS", + "CMTL", + "CNC", + "CNOB", + "CNP", + "CNXC", + "CNXN", + "COCO", + "CODX", + "COF", + "COGT", + "COHR", + "COHU", + "COIN", + "COKE", + "COLB", + "COLL", + "COLM", + "COO", + "COP", + "COR", + "CORT", + "COST", + "CPAY", + "CPB", + "CPRT", + "CPT", + "CRAI", + "CRBU", + "CRCT", + "CRH", + "CRL", + "CRM", + "CRMT", + "CRNC", + "CRNX", + "CROX", + "CRSP", + "CRSR", + "CRTO", + "CRUS", + "CRVL", + "CRWD", + "CSCO", + "CSGP", + "CSIQ", + "CSQ", + "CSTL", + "CSWC", + "CSX", + "CTAS", + "CTBI", + "CTKB", + "CTRA", + "CTRN", + "CTSH", + "CTVA", + "CVBF", + "CVCO", + "CVLT", + "CVS", + "CVX", + "CWCO", + "CWST", + "CYRX", + "CYTK", + "CZR", + "D", + "DAL", + "DASH", + "DBX", + "DCBO", + "DCGO", + "DD", + "DDOG", + "DE", + "DECK", + "DELL", + "DG", + "DGICA", + "DGII", + "DGX", + "DH", + "DHI", + "DHR", + "DIOD", + "DIS", + "DJT", + "DKNG", + "DLO", + "DLR", + "DLTR", + "DMLP", + "DMRC", + "DNLI", + "DNUT", + "DOC", + "DOCU", + "DOMO", + "DOO", + "DORM", + "DOV", + "DOW", + "DOX", + "DPZ", + "DRH", + "DRI", + "DRS", + "DRVN", + "DSGN", + "DSGR", + "DSGX", + "DTE", + "DTIL", + "DUK", + "DUOL", + "DVA", + "DVN", + "DXCM", + "DXLG", + "DXPE", + "DYN", + "EA", + "EBAY", + "EBC", + "ECHO", + "ECL", + "ECPG", + "ED", + "EDIT", + "EEFT", + "EFSC", + "EFX", + "EG", + "EGBN", + "EIX", + "EL", + "ELV", + "EME", + "EMR", + "ENPH", + "ENSG", + "ENTA", + "ENTG", + "ENVX", + "EOG", + "EOLS", + "EPAM", + "EQIX", + "EQR", + "EQT", + "ERAS", + "ERIC", + "ERIE", + "ERII", + "ES", + "ESEA", + "ESLT", + "ESS", + "ESTA", + "ETN", + "ETR", + "EVCM", + "EVER", + "EVGO", + "EVRG", + "EW", + "EWBC", + "EWTX", + "EXC", + "EXE", + "EXEL", + "EXLS", + "EXPD", + "EXPE", + "EXPO", + "EXR", + "EXTR", + "EYE", + "EZPW", + "F", + "FA", + "FANG", + "FAST", + "FATE", + "FBNC", + "FCEL", + "FCFS", + "FCNCA", + "FCX", + "FDMT", + "FDS", + "FDUS", + "FDX", + "FE", + "FELE", + "FFAI", + "FFBC", + "FFIN", + "FFIV", + "FHB", + "FIBK", + "FICO", + "FIS", + "FISV", + "FITB", + "FIVE", + "FIVN", + "FIX", + "FIZZ", + "FLEX", + "FLGT", + "FLNA", + "FLNC", + "FLWS", + "FLYW", + "FMBH", + "FMNB", + "FNKO", + "FORM", + "FORR", + "FOX", + "FOXA", + "FOXF", + "FRHC", + "FRME", + "FROG", + "FRPT", + "FRSH", + "FRT", + "FSLR", + "FSLY", + "FSV", + "FTAI", + "FTCI", + "FTDR", + "FTNT", + "FTV", + "FULC", + "FULT", + "FUTU", + "FWONA", + "FWONK", + "FWRD", + "FWRG", + "GABC", + "GAIN", + "GBDC", + "GCMG", + "GD", + "GDDY", + "GDRX", + "GDS", + "GDYN", + "GE", + "GEN", + "GFS", + "GGAL", + "GGR", + "GH", + "GIII", + "GILD", + "GIS", + "GL", + "GLAD", + "GLBE", + "GLNG", + "GLPI", + "GLUE", + "GLW", + "GM", + "GMAB", + "GNRC", + "GNTX", + "GO", + "GOGO", + "GOOD", + "GOOG", + "GOOGL", + "GOSS", + "GOVX", + "GPC", + "GPN", + "GPRE", + "GPRO", + "GRFS", + "GRMN", + "GRPN", + "GS", + "GSAT", + "GSBC", + "GSHD", + "GSM", + "GT", + "GTLB", + "GTM", + "GTX", + "GWW", + "HAFC", + "HAIN", + "HAL", + "HALO", + "HAPN", + "HAS", + "HBAN", + "HBANP", + "HBNC", + "HCA", + "HCAT", + "HCKT", + "HCM", + "HCSG", + "HD", + "HDSN", + "HELE", + "HFWA", + "HIG", + "HII", + "HIMX", + "HLIT", + "HLMN", + "HLNE", + "HLT", + "HNRG", + "HOFT", + "HOLO", + "HON", + "HOOD", + "HOPE", + "HPE", + "HPK", + "HPQ", + "HQY", + "HRL", + "HRMY", + "HROW", + "HRZN", + "HSIC", + "HST", + "HSTM", + "HSY", + "HTHT", + "HTLD", + "HTO", + "HUBB", + "HUBG", + "HUM", + "HURN", + "HUT", + "HWC", + "HWKN", + "HWM", + "HYFM", + "HYMC", + "IART", + "IBCP", + "IBKR", + "IBM", + "IBOC", + "IBRX", + "ICE", + "ICFI", + "ICHR", + "ICLR", + "ICUI", + "IDCC", + "IDXX", + "IDYA", + "IEP", + "IEX", + "IFF", + "IHRT", + "IIIV", + "ILMN", + "IMCR", + "IMKTA", + "IMMR", + "IMTX", + "IMVT", + "IMXI", + "INCY", + "INDB", + "INDI", + "INGN", + "INMD", + "INO", + "INSE", + "INSG", + "INSM", + "INTA", + "INTC", + "INTU", + "INVA", + "INVH", + "IONS", + "IOSP", + "IOVA", + "IP", + "IPAR", + "IPGP", + "IQV", + "IR", + "IRDM", + "IRM", + "IRTC", + "IRWD", + "ISRG", + "IT", + "ITRI", + "ITW", + "IVZ", + "J", + "JACK", + "JAKK", + "JAZZ", + "JBHT", + "JBIO", + "JBL", + "JBLU", + "JBSS", + "JCI", + "JD", + "JJSF", + "JKHY", + "JNJ", + "JOUT", + "JOYY", + "JPM", + "JRVR", + "JYNT", + "KALU", + "KDP", + "KE", + "KELYA", + "KEY", + "KEYS", + "KHC", + "KIDS", + "KIM", + "KKR", + "KLAC", + "KLIC", + "KLRS", + "KLXE", + "KMB", + "KMI", + "KNSA", + "KO", + "KOD", + "KPRX", + "KPTI", + "KR", + "KRNT", + "KRNY", + "KROS", + "KRUS", + "KRYS", + "KTOS", + "KURA", + "KYMR", + "KYNB", + "L", + "LAMR", + "LAND", + "LASR", + "LAUR", + "LBRDA", + "LBRDK", + "LBTYA", + "LBTYK", + "LCID", + "LDOS", + "LE", + "LECO", + "LEGN", + "LEN", + "LESL", + "LFST", + "LFUS", + "LGIH", + "LGND", + "LH", + "LHX", + "LI", + "LII", + "LILA", + "LILAK", + "LIN", + "LIND", + "LITE", + "LIVN", + "LKFN", + "LKFT", + "LKQ", + "LLY", + "LMAT", + "LMT", + "LNT", + "LNTH", + "LOCO", + "LOGI", + "LOPE", + "LOVE", + "LOW", + "LPLA", + "LPRO", + "LPSN", + "LQDA", + "LQDT", + "LRCX", + "LSCC", + "LSTR", + "LULU", + "LUNG", + "LUV", + "LVS", + "LWLG", + "LYB", + "LYEL", + "LYFT", + "LYV", + "LZ", + "MA", + "MAA", + "MANH", + "MAR", + "MARA", + "MAS", + "MASS", + "MAT", + "MATW", + "MBIN", + "MBUU", + "MBWM", + "MCD", + "MCFT", + "MCHB", + "MCHP", + "MCK", + "MCO", + "MCRB", + "MCRI", + "MDB", + "MDGL", + "MDLZ", + "MDT", + "MEDP", + "MELI", + "MEOH", + "MERC", + "MET", + "META", + "METC", + "MFIC", + "MGEE", + "MGM", + "MGNI", + "MGPI", + "MGRC", + "MIDD", + "MIRM", + "MIST", + "MITK", + "MKC", + "MKSI", + "MKTX", + "MLAB", + "MLCO", + "MLKN", + "MLM", + "MMM", + "MMSI", + "MMYT", + "MNDY", + "MNRO", + "MNST", + "MNTK", + "MO", + "MORN", + "MOS", + "MPC", + "MPWR", + "MQ", + "MRCY", + "MRK", + "MRNA", + "MRSH", + "MRTN", + "MRVI", + "MRVL", + "MS", + "MSBI", + "MSCI", + "MSEX", + "MSFT", + "MSI", + "MSTR", + "MTB", + "MTCH", + "MTD", + "MTLS", + "MTSI", + "MU", + "MXCT", + "MXL", + "MYGN", + "MYRG", + "MZTI", + "NAVI", + "NBIX", + "NBTB", + "NCLH", + "NCNO", + "NDAQ", + "NDSN", + "NEE", + "NEGG", + "NEM", + "NEO", + "NEOG", + "NESR", + "NEWT", + "NEXT", + "NFBK", + "NFE", + "NFLX", + "NI", + "NICE", + "NKE", + "NKTR", + "NKTX", + "NMFC", + "NMIH", + "NMRK", + "NNOX", + "NOC", + "NOVT", + "NOW", + "NRC", + "NRG", + "NRIX", + "NSC", + "NSIT", + "NSSC", + "NTAP", + "NTCT", + "NTES", + "NTGR", + "NTLA", + "NTNX", + "NTRA", + "NTRS", + "NUE", + "NVAX", + "NVCR", + "NVDA", + "NVMI", + "NVR", + "NWBI", + "NWE", + "NWL", + "NWPX", + "NWS", + "NWSA", + "NXPI", + "NXST", + "O", + "OCFC", + "OCSL", + "ODFL", + "OFIX", + "OFLX", + "OKE", + "OKTA", + "OLED", + "OLLI", + "OM", + "OMAB", + "OMC", + "OMCL", + "ON", + "ONB", + "ONC", + "ONEW", + "OPCH", + "OPI", + "OPRT", + "OPRX", + "ORCL", + "ORLY", + "ORMP", + "OSBC", + "OSIS", + "OSPN", + "OSW", + "OTEX", + "OTIS", + "OTLY", + "OTTR", + "OUST", + "OXLC", + "OXY", + "OZK", + "PAA", + "PACB", + "PAGP", + "PAHC", + "PAMT", + "PANW", + "PATK", + "PAX", + "PAYO", + "PAYX", + "PCAR", + "PCG", + "PCRX", + "PCT", + "PCTY", + "PCVX", + "PDD", + "PDFS", + "PEBO", + "PECO", + "PEG", + "PEGA", + "PENG", + "PENN", + "PEP", + "PERI", + "PETS", + "PFBC", + "PFE", + "PFG", + "PG", + "PGC", + "PGNY", + "PGR", + "PGY", + "PH", + "PHAT", + "PHM", + "PHUN", + "PI", + "PKG", + "PLAB", + "PLAY", + "PLCE", + "PLD", + "PLMR", + "PLPC", + "PLRX", + "PLTK", + "PLTR", + "PLUG", + "PLUS", + "PLXS", + "PM", + "PMVP", + "PNC", + "PNR", + "PNTG", + "PNW", + "PODD", + "POOL", + "POWI", + "PPC", + "PPG", + "PPL", + "PPLI", + "PRAA", + "PRAX", + "PRCT", + "PRDO", + "PRGS", + "PRTA", + "PRTS", + "PRU", + "PRVA", + "PSA", + "PSEC", + "PSMT", + "PSX", + "PTC", + "PTCT", + "PTEN", + "PTGX", + "PTLO", + "PTON", + "PUBM", + "PWP", + "PWR", + "PYPL", + "PZZA", + "QCOM", + "QCRH", + "QDEL", + "QFIN", + "QLYS", + "QNRX", + "QNST", + "QQQX", + "QRVO", + "QS", + "QTRX", + "QURE", + "RARE", + "RCKT", + "RCL", + "RCMT", + "RDNT", + "RDNW", + "RDWR", + "REG", + "REGN", + "RELL", + "RELY", + "RENT", + "REPL", + "REYN", + "RF", + "RGEN", + "RGLD", + "RGNX", + "RGP", + "RICK", + "RIGL", + "RILY", + "RIOT", + "RJF", + "RKLB", + "RL", + "RLAY", + "RLMD", + "RMBS", + "RMD", + "RMNI", + "RMR", + "RNA", + "RNAC", + "RNW", + "ROAD", + "ROCK", + "ROIV", + "ROK", + "ROKU", + "ROL", + "ROOT", + "ROP", + "ROST", + "RPAY", + "RPD", + "RPRX", + "RRGB", + "RRR", + "RSG", + "RTX", + "RUM", + "RUN", + "RUSHA", + "RVMD", + "RVTY", + "RXRX", + "RXT", + "RYAAY", + "RYTM", + "SABR", + "SAFT", + "SAIA", + "SAIC", + "SANA", + "SANM", + "SATS", + "SBAC", + "SBCF", + "SBGI", + "SBLK", + "SBRA", + "SBUX", + "SCHL", + "SCHW", + "SCSC", + "SDGR", + "SEAT", + "SEDG", + "SEER", + "SEIC", + "SENEA", + "SENS", + "SFM", + "SFNC", + "SGHT", + "SGML", + "SGRY", + "SHC", + "SHEN", + "SHLS", + "SHOE", + "SHOO", + "SHOP", + "SHW", + "SIBN", + "SIGA", + "SIGI", + "SIMO", + "SIRI", + "SITM", + "SJM", + "SKIN", + "SKYT", + "SKYW", + "SLAB", + "SLB", + "SLDP", + "SLM", + "SLP", + "SLRC", + "SMBC", + "SMCI", + "SMPL", + "SMTC", + "SNA", + "SNDX", + "SNEX", + "SNPS", + "SNY", + "SO", + "SOFI", + "SOHU", + "SONO", + "SPFI", + "SPG", + "SPGI", + "SPSC", + "SPT", + "SPWH", + "SRAD", + "SRCE", + "SRE", + "SRPT", + "SRRK", + "SRTS", + "SSNC", + "SSP", + "SSRM", + "SSTI", + "SSYS", + "STAA", + "STBA", + "STE", + "STEP", + "STGW", + "STLD", + "STNE", + "STOK", + "STRA", + "STRL", + "STRO", + "STT", + "STX", + "STZ", + "SUPN", + "SVC", + "SW", + "SWBI", + "SWK", + "SWKS", + "SYBT", + "SYF", + "SYK", + "SYM", + "SYNA", + "SYY", + "T", + "TAP", + "TARS", + "TASK", + "TBBK", + "TBCH", + "TBLD", + "TBPH", + "TCBI", + "TCBK", + "TCMD", + "TCOM", + "TCPC", + "TCRT", + "TCX", + "TDG", + "TDY", + "TEAM", + "TECH", + "TEL", + "TENB", + "TER", + "TFC", + "TFSL", + "TGT", + "TGTX", + "TH", + "THFF", + "THRM", + "THRY", + "TIGO", + "TIL", + "TILE", + "TITN", + "TJX", + "TKO", + "TLRY", + "TLS", + "TMCI", + "TMDX", + "TMO", + "TMUS", + "TNDM", + "TNXP", + "TOWN", + "TPL", + "TPR", + "TREE", + "TRGP", + "TRI", + "TRIN", + "TRIP", + "TRMB", + "TRMD", + "TRMK", + "TRNS", + "TROW", + "TRS", + "TRST", + "TRUP", + "TRV", + "TSCO", + "TSEM", + "TSLA", + "TSN", + "TT", + "TTD", + "TTEC", + "TTEK", + "TTGT", + "TTMI", + "TTWO", + "TVRD", + "TVTX", + "TW", + "TWST", + "TXG", + "TXN", + "TXRH", + "TXT", + "TYL", + "UAL", + "UBER", + "UBSI", + "UCTT", + "UDR", + "UEIC", + "UFCS", + "UFPI", + "UFPT", + "UHS", + "ULCC", + "ULH", + "ULTA", + "UMBF", + "UNH", + "UNIT", + "UNP", + "UPBD", + "UPLD", + "UPS", + "UPST", + "UPWK", + "URBN", + "URI", + "USB", + "UTHR", + "UVSP", + "V", + "VC", + "VCEL", + "VCTR", + "VCYT", + "VECO", + "VERA", + "VERI", + "VERU", + "VERX", + "VIAV", + "VICI", + "VICR", + "VIR", + "VISN", + "VITL", + "VLO", + "VLY", + "VMC", + "VNDA", + "VNOM", + "VOD", + "VRDN", + "VREX", + "VRM", + "VRNS", + "VRRM", + "VRSK", + "VRSN", + "VRT", + "VRTX", + "VSAT", + "VST", + "VTR", + "VTRS", + "VUZI", + "VZ", + "WAB", + "WABC", + "WAFD", + "WASH", + "WAT", + "WB", + "WDAY", + "WDC", + "WDFC", + "WEC", + "WELL", + "WEN", + "WERN", + "WEST", + "WFC", + "WFRD", + "WGS", + "WHWK", + "WINA", + "WING", + "WIX", + "WKHS", + "WLDN", + "WM", + "WMB", + "WMG", + "WMT", + "WOOF", + "WRB", + "WRLD", + "WSBC", + "WSBF", + "WSC", + "WSFS", + "WSM", + "WST", + "WTFC", + "WTW", + "WWD", + "WY", + "WYNN", + "XAIR", + "XEL", + "XENE", + "XMTR", + "XNCR", + "XOM", + "XP", + "XPEL", + "XRAY", + "XRX", + "XYL", + "XYZ", + "YORW", + "YUM", + "Z", + "ZBH", + "ZBRA", + "ZD", + "ZG", + "ZION", + "ZLAB", + "ZM", + "ZNTL", + "ZS", + "ZTS", + "ZUMZ", + "ZVRA", + "ZVZZT", + "ZYME" + ], + "n_symbols": 1500 + }, + { + "week": [ + 2022, + 25 + ], + "stats": { + "raw_pool": 493, + "eligible_pre_mask": 492, + "post_mask": 492, + "mask_binds": false + }, + "symbols": [ + "A", + "AAPL", + "ABBV", + "ABNB", + "ABT", + "ACGL", + "ACN", + "ADBE", + "ADI", + "ADM", + "ADP", + "ADSK", + "AEE", + "AEP", + "AES", + "AFL", + "AIG", + "AIZ", + "AJG", + "AKAM", + "ALB", + "ALGN", + "ALL", + "ALLE", + "AMAT", + "AMCR", + "AMD", + "AME", + "AMGN", + "AMP", + "AMT", + "AMZN", + "ANET", + "AON", + "AOS", + "APA", + "APD", + "APH", + "APO", + "APP", + "APTV", + "ARE", + "ARES", + "ATO", + "AVB", + "AVGO", + "AVY", + "AWK", + "AXON", + "AXP", + "AZO", + "BA", + "BAC", + "BALL", + "BAX", + "BBY", + "BDX", + "BEN", + "BF-B", + "BG", + "BIIB", + "BK", + "BKNG", + "BKR", + "BLDR", + "BLK", + "BMY", + "BR", + "BRK-B", + "BRO", + "BSX", + "BX", + "BXP", + "C", + "CAG", + "CAH", + "CARR", + "CASY", + "CAT", + "CB", + "CBOE", + "CBRE", + "CCI", + "CCL", + "CDNS", + "CDW", + "CF", + "CFG", + "CHD", + "CHRW", + "CHTR", + "CI", + "CIEN", + "CINF", + "CL", + "CLX", + "CMCSA", + "CME", + "CMG", + "CMI", + "CMS", + "CNC", + "CNP", + "COF", + "COHR", + "COIN", + "COO", + "COP", + "COR", + "COST", + "CPAY", + "CPB", + "CPRT", + "CPT", + "CRH", + "CRL", + "CRM", + "CRWD", + "CSCO", + "CSGP", + "CSX", + "CTAS", + "CTRA", + "CTSH", + "CTVA", + "CVNA", + "CVS", + "CVX", + "D", + "DAL", + "DASH", + "DD", + "DDOG", + "DE", + "DECK", + "DELL", + "DG", + "DGX", + "DHI", + "DHR", + "DIS", + "DLR", + "DLTR", + "DOC", + "DOV", + "DOW", + "DPZ", + "DRI", + "DTE", + "DUK", + "DVA", + "DVN", + "DXCM", + "EA", + "EBAY", + "ECL", + "ED", + "EFX", + "EG", + "EIX", + "EL", + "ELV", + "EME", + "EMR", + "EOG", + "EPAM", + "EQIX", + "EQR", + "EQT", + "ERIE", + "ES", + "ESS", + "ETN", + "ETR", + "EVRG", + "EW", + "EXC", + "EXE", + "EXPD", + "EXPE", + "EXR", + "F", + "FANG", + "FAST", + "FCX", + "FDS", + "FDX", + "FE", + "FFIV", + "FICO", + "FIS", + "FISV", + "FITB", + "FIX", + "FOX", + "FOXA", + "FRT", + "FSLR", + "FTNT", + "FTV", + "GD", + "GDDY", + "GE", + "GEN", + "GILD", + "GIS", + "GL", + "GLW", + "GM", + "GNRC", + "GOOG", + "GOOGL", + "GPC", + "GPN", + "GRMN", + "GS", + "GWW", + "HAL", + "HAS", + "HBAN", + "HCA", + "HD", + "HIG", + "HII", + "HLT", + "HON", + "HPE", + "HPQ", + "HRL", + "HSIC", + "HST", + "HSY", + "HUBB", + "HUM", + "HWM", + "IBKR", + "IBM", + "ICE", + "IDXX", + "IEX", + "IFF", + "INCY", + "INTC", + "INTU", + "INVH", + "IP", + "IQV", + "IR", + "IRM", + "ISRG", + "IT", + "ITW", + "IVZ", + "J", + "JBHT", + "JBL", + "JCI", + "JKHY", + "JNJ", + "JPM", + "KDP", + "KEY", + "KEYS", + "KHC", + "KIM", + "KKR", + "KLAC", + "KMB", + "KMI", + "KO", + "KR", + "L", + "LDOS", + "LEN", + "LH", + "LHX", + "LII", + "LIN", + "LITE", + "LLY", + "LMT", + "LNT", + "LOW", + "LRCX", + "LULU", + "LUV", + "LVS", + "LYB", + "LYV", + "MA", + "MAA", + "MAR", + "MAS", + "MCD", + "MCHP", + "MCK", + "MCO", + "MDLZ", + "MDT", + "MET", + "META", + "MGM", + "MKC", + "MLM", + "MMM", + "MNST", + "MO", + "MOS", + "MPC", + "MPWR", + "MRK", + "MRNA", + "MRSH", + "MS", + "MSCI", + "MSFT", + "MSI", + "MSTR", + "MTB", + "MTD", + "MU", + "NCLH", + "NDAQ", + "NDSN", + "NEE", + "NEM", + "NFLX", + "NI", + "NKE", + "NOC", + "NOW", + "NRG", + "NSC", + "NTAP", + "NTRS", + "NUE", + "NVDA", + "NVR", + "NWS", + "NWSA", + "NXPI", + "O", + "ODFL", + "OKE", + "OMC", + "ON", + "ORCL", + "ORLY", + "OTIS", + "OXY", + "PANW", + "PAYX", + "PCAR", + "PCG", + "PEG", + "PEP", + "PFE", + "PFG", + "PG", + "PGR", + "PH", + "PHM", + "PKG", + "PLD", + "PLTR", + "PM", + "PNC", + "PNR", + "PNW", + "PODD", + "POOL", + "PPG", + "PPL", + "PRU", + "PSA", + "PSX", + "PTC", + "PWR", + "PYPL", + "QCOM", + "RCL", + "REG", + "REGN", + "RF", + "RJF", + "RL", + "RMD", + "ROK", + "ROL", + "ROP", + "ROST", + "RSG", + "RTX", + "RVTY", + "SATS", + "SBAC", + "SBUX", + "SCHW", + "SHW", + "SJM", + "SLB", + "SNA", + "SNPS", + "SO", + "SPG", + "SPGI", + "SRE", + "STE", + "STLD", + "STT", + "STX", + "STZ", + "SW", + "SWK", + "SWKS", + "SYF", + "SYK", + "SYY", + "T", + "TAP", + "TDG", + "TDY", + "TECH", + "TEL", + "TER", + "TFC", + "TGT", + "TJX", + "TKO", + "TMO", + "TMUS", + "TPL", + "TPR", + "TRGP", + "TRMB", + "TROW", + "TRV", + "TSCO", + "TSLA", + "TSN", + "TT", + "TTD", + "TTWO", + "TXN", + "TXT", + "TYL", + "UAL", + "UBER", + "UDR", + "UHS", + "ULTA", + "UNH", + "UNP", + "UPS", + "URI", + "USB", + "V", + "VICI", + "VLO", + "VMC", + "VRSK", + "VRSN", + "VRT", + "VRTX", + "VST", + "VTR", + "VTRS", + "VZ", + "WAB", + "WAT", + "WDAY", + "WDC", + "WEC", + "WELL", + "WFC", + "WM", + "WMB", + "WMT", + "WRB", + "WSM", + "WST", + "WTW", + "WY", + "WYNN", + "XEL", + "XOM", + "XYL", + "XYZ", + "YUM", + "ZBH", + "ZBRA", + "ZTS" + ], + "n_symbols": 492 + } + ], + "interpretation": { + "harness_and_shared_filter_agree": true, + "mask_binds_pct": 97.1, + "avg_eligible_pre_mask": 2338.4, + "avg_raw_pool": 3214.4, + "prod_subset_still_negative": true, + "junior_tier_more_positive": true, + "lag_same_sign_as_same_week": true, + "mom_conditional_negative_and_reliable": true, + "orphan_plus_five_sigma": "Prior report fip-breadth-20260718-211440-breadth.json listed fip IC +0.0575 / t +5.12. This single-sourced recompute is the authoritative number; if it disagrees, the +0.0575 row is orphaned.", + "compositional_story": "fip_id pools continuous winners (neg IC) vs continuous bleeders (pos IC). Prod-subset and senior liquid stay negative; junior liquid is less negative / positive \u2014 composition, not jumpiness premium.", + "vol_tilt_warning": "High-vol names underperform on breadth relative to S&P-like books. Re-validate production 80/20 high-vol tilt before any universe broaden." + }, + "platform_verdict": "Mom-conditional fip ALIVE as book-tilt candidate (needs book sim) \u2014 not production wire-in. Unconditional fip not green." +} \ No newline at end of file diff --git a/scripts/run_fip_breadth_diagnostics.py b/scripts/run_fip_breadth_diagnostics.py index 1e47ca5..3625328 100644 --- a/scripts/run_fip_breadth_diagnostics.py +++ b/scripts/run_fip_breadth_diagnostics.py @@ -1,26 +1,18 @@ -"""Post-breadth diagnostics for fip_id (research branch only). +"""fip_id breadth diagnostics — single-sourced through harness mask helpers. -Same research.sqlite as the liquid-breadth IC run. No production changes. +Uses the same collection + ``_filter_liquid_breadth_week_rich`` as +``run_backtest`` signal_eval. No parallel mask implementation. -Checks (pre-registered interpretation follow-ups) ------------------------------------------------- -1. **Lagged membership** — liquid top-N ranked on *prior* week's $vol (extra lag) - so same-week liquidity explosion cannot pull a name into history. -2. **Liquidity tiers** — fip IC on ranks 1–800 vs 801–1500 (same-week mask). -3. **Prod-universe subset** — symbols present in prod.sqlite (~S&P-like large-cap - book) inside the same breadth weeks — compositional vs temporal flip. -4. **Momentum-conditional fip** — among weekly top 20% by mom_12_1 (or resid when - available) within the liquid top-N — the paper's actual claim and the only - version a gate could consume. +Reconciles the harness +0.0575 vs prior dual-path −0.017 disagreement by +deleting the second mask, dumping membership/pre-post stats, and re-running +mom-conditional IC through the surviving path only. -Also reports vol_6m / mom raw vs residual on the same panels for the log. +Research branch only. Example: -Example (Windows) ------------------ .\\.venv\\Scripts\\python.exe scripts\\run_fip_breadth_diagnostics.py ^ --research-snapshot backtest_snapshots\\research.sqlite ^ --prod-snapshot backtest_snapshots\\prod.sqlite ^ - --workers 6 + --workers 6 --allow-spawn """ from __future__ import annotations @@ -29,6 +21,7 @@ import argparse import json import math import multiprocessing as mp +import os import sys from collections import defaultdict from concurrent.futures import ProcessPoolExecutor, as_completed @@ -42,96 +35,41 @@ ROOT = Path(__file__).resolve().parents[1] if str(ROOT) not in sys.path: sys.path.insert(0, str(ROOT)) -HORIZON = 30 +# Match production signal_eval cadence / reliability bars. MIN_CROSS = 20 MIN_RELIABLE = 12 -LIQUID_TOP = 1500 -MIN_PRICE = 5.0 -MOM_WINNER_PCT = 80.0 # top 20% within liquid cross-section +MOM_WINNER_PCT = 80.0 def _parse_args() -> argparse.Namespace: p = argparse.ArgumentParser(description=__doc__) - p.add_argument( - "--research-snapshot", - default="backtest_snapshots/research.sqlite", - ) - p.add_argument( - "--prod-snapshot", - default="backtest_snapshots/prod.sqlite", - help="Symbols here define the large-cap / prod-like subset.", - ) - p.add_argument("--top-n", type=int, default=LIQUID_TOP) - p.add_argument("--min-price", type=float, default=MIN_PRICE) + p.add_argument("--research-snapshot", default="backtest_snapshots/research.sqlite") + p.add_argument("--prod-snapshot", default="backtest_snapshots/prod.sqlite") + p.add_argument("--top-n", type=int, default=1500) + p.add_argument("--min-price", type=float, default=5.0) p.add_argument("--workers", type=int, default=max(1, (mp.cpu_count() or 4) - 1)) + p.add_argument("--allow-spawn", action="store_true") + p.add_argument("--dump-weeks", type=int, default=5, help="How many weeks to dump membership for") p.add_argument("--out", default=None) p.add_argument("--quiet", action="store_true") return p.parse_args() -def _week_key(d: date) -> tuple[int, int]: - iso = d.isocalendar() - return (int(iso[0]), int(iso[1])) - - def _week_ord(wk: tuple[int, int]) -> int: - return wk[0] * 53 + wk[1] + return int(wk[0]) * 53 + int(wk[1]) def _nonoverlap(weeks: list[tuple[int, int]], stride: int) -> list[tuple[int, int]]: - kept: list[tuple[int, int]] = [] - last: int | None = None - for wk in sorted(weeks, key=_week_ord): - o = _week_ord(wk) - if last is None or o - last >= stride: - kept.append(wk) - last = o - return kept + from app.services.backtest_service import _nonoverlapping_weeks - -def _rank(xs: list[float]) -> list[float]: - order = sorted(range(len(xs)), key=lambda k: xs[k]) - ranks = [0.0] * len(xs) - i = 0 - while i < len(xs): - j = i - while j + 1 < len(xs) and xs[order[j + 1]] == xs[order[i]]: - j += 1 - avg = (i + j) / 2.0 + 1.0 - for k in range(i, j + 1): - ranks[order[k]] = avg - i = j + 1 - return ranks - - -def _pearson(a: list[float], b: list[float]) -> float | None: - n = len(a) - if n < 3: - return None - ma, mb = sum(a) / n, sum(b) / n - va = sum((x - ma) ** 2 for x in a) - vb = sum((y - mb) ** 2 for y in b) - if va <= 0 or vb <= 0: - return None - cov = sum((a[k] - ma) * (b[k] - mb) for k in range(n)) - return cov / math.sqrt(va * vb) - - -def _spearman(xs: list[float], ys: list[float]) -> float | None: - if len(xs) < 3: - return None - return _pearson(_rank(xs), _rank(ys)) - - -def _ic_row(pairs: list[tuple[float, float]], *, label: str) -> dict[str, Any]: - """pairs = (signal, fwd) over non-overlapping weeks aggregated… actually - we pass per-week then aggregate outside. This helper is for multi-week IC.""" - raise NotImplementedError + return _nonoverlapping_weeks(weeks, stride) def _ic_from_weekly( week_pairs: dict[tuple[int, int], list[tuple[float, float]]], ) -> dict[str, Any]: + from app.services.backtest_service import HORIZON, _spearman + stride = max(1, round(HORIZON / 5)) usable = [wk for wk, ps in week_pairs.items() if len(ps) >= MIN_CROSS] kept = _nonoverlap(usable, stride) @@ -171,70 +109,29 @@ def _ic_from_weekly( } -def _panel_worker(payload: tuple) -> list[dict]: - """Build weekly observations for one ticker (picklable top-level).""" - symbol, date_ords, opens, highs, lows, closes, volumes, spy = payload +def _worker(payload: tuple) -> dict: + """Return harness-style signal series for one ticker (liquid-mode dicts).""" + symbol, ords, opens, highs, lows, closes, volumes, spy = payload from types import SimpleNamespace - from app.services.backtest_service import ( - HORIZON as H, - _median_dollar_vol_63, - _signal_values, - _weekly_asof_indices, - ) + from app.services.backtest_service import _signal_series - dates = [date.fromordinal(int(o)) for o in date_ords] - opens_f = [float(x) for x in opens] - highs_f = [float(x) for x in highs] - lows_f = [float(x) for x in lows] - closes_f = [float(x) for x in closes] - vols_f = [float(x) for x in volumes] - n = len(closes_f) - if n < H + 21: - return [] - - # Match backtest_service bar objects exactly (weekly as-of + signal_values). - bar_records = [ + bars = [ SimpleNamespace( - date=dates[i], - open=opens_f[i], - high=highs_f[i], - low=lows_f[i], - close=closes_f[i], - volume=vols_f[i], + date=date.fromordinal(int(o)), + open=float(op), + high=float(hi), + low=float(lo), + close=float(cl), + volume=float(vo), ) - for i in range(n) + for o, op, hi, lo, cl, vo in zip(ords, opens, highs, lows, closes, volumes) ] - out: list[dict] = [] - for i in _weekly_asof_indices(bar_records): - j = i + H - if j >= n or closes_f[i] <= 0: - continue - sigs = _signal_values(dates, closes_f, highs_f, i, spy) - fip = sigs.get("fip_id") - mom = sigs.get("mom_12_1") - mom_r = sigs.get("mom_12_1_resid") - vol = sigs.get("vol_6m") - if fip is None and mom is None: - continue - dvol = _median_dollar_vol_63(closes_f, vols_f, i) - wk = _week_key(dates[i]) - out.append({ - "symbol": symbol, - "week": wk, - "fwd": closes_f[j] / closes_f[i] - 1.0, - "close": closes_f[i], - "dvol": dvol, - "fip_id": fip, - "mom_12_1": mom, - "mom_12_1_resid": mom_r, - "vol_6m": vol, - }) - return out + return _signal_series(bars, spy, symbol=symbol) def _load_spy(conn) -> dict[date, float]: rows = conn.execute( - text("SELECT date, close FROM benchmark_prices WHERE symbol = 'SPY' ORDER BY date") + text("SELECT date, close FROM benchmark_prices WHERE symbol='SPY' ORDER BY date") ).fetchall() out: dict[date, float] = {} for d, c in rows: @@ -244,34 +141,22 @@ def _load_spy(conn) -> dict[date, float]: return out -def _load_symbols(conn) -> list[str]: - return [ - str(r[0]) - for r in conn.execute(text("SELECT symbol FROM tickers ORDER BY symbol")).fetchall() - ] - - -def _load_columns(conn, symbol: str) -> tuple | None: +def _load_job(conn, symbol: str, spy: dict) -> tuple | None: tid = conn.execute( - text("SELECT id FROM tickers WHERE symbol = :s"), {"s": symbol} + text("SELECT id FROM tickers WHERE symbol=:s"), {"s": symbol} ).scalar() if tid is None: return None rows = conn.execute( text( "SELECT date, open, high, low, close, volume FROM ohlcv_records " - "WHERE ticker_id = :t ORDER BY date" + "WHERE ticker_id=:t ORDER BY date" ), {"t": tid}, ).fetchall() - if len(rows) < HORIZON + 60: + if len(rows) < 90: return None - ords: list[int] = [] - opens: list[float] = [] - highs: list[float] = [] - lows: list[float] = [] - closes: list[float] = [] - vols: list[float] = [] + ords, opens, highs, lows, closes, vols = [], [], [], [], [], [] for d, o, h, l, c, v in rows: if isinstance(d, str): d = date.fromisoformat(d[:10]) @@ -281,36 +166,7 @@ def _load_columns(conn, symbol: str) -> tuple | None: lows.append(float(l)) closes.append(float(c)) vols.append(float(v or 0)) - return (symbol, ords, opens, highs, lows, closes, vols) - - -def _liquid_members( - obs: list[dict], - *, - top_n: int, - min_price: float, - dvol_key: str = "dvol", -) -> list[dict]: - eligible = [ - o - for o in obs - if o.get("close") is not None - and float(o["close"]) >= min_price - and o.get(dvol_key) is not None - and float(o[dvol_key]) > 0 - ] - eligible.sort(key=lambda o: float(o[dvol_key]), reverse=True) - return eligible[:top_n] - - -def _pairs(obs: list[dict], signal: str) -> list[tuple[float, float]]: - out: list[tuple[float, float]] = [] - for o in obs: - v = o.get(signal) - if v is None: - continue - out.append((float(v), float(o["fwd"]))) - return out + return (symbol, ords, opens, highs, lows, closes, vols, spy) def main() -> None: @@ -318,316 +174,440 @@ def main() -> None: research = Path(args.research_snapshot) prod = Path(args.prod_snapshot) if not research.exists(): - raise SystemExit(f"Missing research snapshot: {research}") + raise SystemExit(f"Missing {research}") - research_eng = create_engine(f"sqlite:///{research.resolve().as_posix()}") + # Force harness liquid-mode collection (same env as breadth run). + os.environ["BACKTEST_LIQUID_BREADTH"] = str(int(args.top_n)) + os.environ["BACKTEST_LIQUID_MIN_PRICE"] = str(float(args.min_price)) + if args.allow_spawn: + os.environ["BACKTEST_ALLOW_SPAWN"] = "1" + + from app.services.backtest_service import ( + HORIZON, + _filter_liquid_breadth_week_rich, + _liquid_breadth_week_stats, + _signal_evaluation, + ) + + eng = create_engine(f"sqlite:///{research.resolve().as_posix()}") prod_symbols: set[str] = set() if prod.exists(): - prod_eng = create_engine(f"sqlite:///{prod.resolve().as_posix()}") - with prod_eng.connect() as c: + peng = create_engine(f"sqlite:///{prod.resolve().as_posix()}") + with peng.connect() as c: prod_symbols = { - str(r[0]) - for r in c.execute(text("SELECT symbol FROM tickers")).fetchall() + str(r[0]) for r in c.execute(text("SELECT symbol FROM tickers")) } - prod_eng.dispose() + peng.dispose() - with research_eng.connect() as conn: + with eng.connect() as conn: spy = _load_spy(conn) - symbols = _load_symbols(conn) - jobs: list[tuple] = [] + symbols = [ + str(r[0]) + for r in conn.execute(text("SELECT symbol FROM tickers ORDER BY symbol")) + ] + jobs = [] for i, sym in enumerate(symbols, 1): - cols = _load_columns(conn, sym) - if cols is None: - continue - jobs.append((*cols, spy)) + job = _load_job(conn, sym, spy) + if job is not None: + jobs.append(job) if not args.quiet and i % 500 == 0: print(f" queued {i}/{len(symbols)}", flush=True) if not args.quiet: - print(f"Building weekly panel for {len(jobs)} tickers…", flush=True) + print(f"Collecting harness signal series for {len(jobs)} tickers…", flush=True) - # Panel: week -> list of obs - by_week: dict[tuple[int, int], list[dict]] = defaultdict(list) + collected: dict = defaultdict(lambda: defaultdict(list)) workers = max(1, int(args.workers)) + + def _merge(series: dict) -> None: + for name, weeks in series.items(): + for wk, recs in weeks.items(): + # week keys may arrive as lists after JSON; normalize to tuple + key = tuple(wk) if not isinstance(wk, tuple) else wk + collected[name][key].extend(recs) + if workers == 1: for j, job in enumerate(jobs, 1): - for row in _panel_worker(job): - by_week[tuple(row["week"])].append(row) + _merge(_worker(job)) if not args.quiet and j % 200 == 0: - print(f" panel {j}/{len(jobs)}", flush=True) + print(f" series {j}/{len(jobs)}", flush=True) else: - with ProcessPoolExecutor(max_workers=workers) as pool: - futs = {pool.submit(_panel_worker, job): job[0] for job in jobs} - done = 0 - for fut in as_completed(futs): - done += 1 + ctx = mp.get_context("spawn") if args.allow_spawn or sys.platform == "win32" else None + with ProcessPoolExecutor(max_workers=workers, mp_context=ctx) as pool: + futs = [pool.submit(_worker, job) for job in jobs] + for j, fut in enumerate(as_completed(futs), 1): try: - rows = fut.result() + _merge(fut.result()) except Exception as exc: if not args.quiet: - print(f" worker error {futs[fut]}: {exc}", flush=True) - continue - for row in rows: - by_week[tuple(row["week"])].append(row) - if not args.quiet and done % 200 == 0: - print(f" panel {done}/{len(jobs)}", flush=True) + print(f" worker error: {exc}", flush=True) + if not args.quiet and j % 200 == 0: + print(f" series {j}/{len(jobs)}", flush=True) - if not args.quiet: - print(f"Weeks with data: {len(by_week)}", flush=True) - - # Prior-week dvol map for lagged membership: (symbol, week) -> dvol - dvol_by_sym_week: dict[tuple[str, tuple[int, int]], float] = {} - for wk, obs in by_week.items(): - for o in obs: - if o.get("dvol") is not None: - dvol_by_sym_week[(o["symbol"], wk)] = float(o["dvol"]) - - ordered_weeks = sorted(by_week.keys(), key=_week_ord) - prev_week: dict[tuple[int, int], tuple[int, int]] = {} - for i, wk in enumerate(ordered_weeks): - if i > 0: - prev_week[wk] = ordered_weeks[i - 1] + # --- Harness signal_eval (authoritative unconditional ICs) --- + harness_rows = _signal_evaluation(dict(collected)) + harness_by_name = {r["signal"]: r for r in harness_rows} top_n = int(args.top_n) min_price = float(args.min_price) + fip_weeks = collected.get("fip_id") or {} + mom_weeks = collected.get("mom_12_1") or {} + vol_weeks = collected.get("vol_6m") or {} + momr_weeks = collected.get("mom_12_1_resid") or {} - # --- Panels for each check --- - same_week_fip: dict[tuple[int, int], list[tuple[float, float]]] = defaultdict(list) - lag_week_fip: dict[tuple[int, int], list[tuple[float, float]]] = defaultdict(list) - tier_hi_fip: dict[tuple[int, int], list[tuple[float, float]]] = defaultdict(list) - tier_lo_fip: dict[tuple[int, int], list[tuple[float, float]]] = defaultdict(list) - prod_subset_fip: dict[tuple[int, int], list[tuple[float, float]]] = defaultdict(list) - mom_cond_fip: dict[tuple[int, int], list[tuple[float, float]]] = defaultdict(list) - liquid_vol: dict[tuple[int, int], list[tuple[float, float]]] = defaultdict(list) - liquid_mom: dict[tuple[int, int], list[tuple[float, float]]] = defaultdict(list) - liquid_mom_r: dict[tuple[int, int], list[tuple[float, float]]] = defaultdict(list) + # Index mom/vol by (week, symbol) for joins + def _index(weeks_map: dict) -> dict[tuple, dict]: + out: dict[tuple, dict] = {} + for wk, recs in weeks_map.items(): + key_wk = tuple(wk) if not isinstance(wk, tuple) else wk + for rec in recs: + if not isinstance(rec, dict): + continue + sym = rec.get("symbol") + if not sym: + continue + out[(key_wk, str(sym))] = rec + return out - for wk, obs in by_week.items(): - # Same-week liquid top-N among names that have fip (matches signal_eval mask: - # membership is ranked within each signal's observation set). - with_fip = [o for o in obs if o.get("fip_id") is not None] - liq_fip = _liquid_members(with_fip, top_n=top_n, min_price=min_price) - for rank, o in enumerate(liq_fip, 1): - same_week_fip[wk].append((float(o["fip_id"]), float(o["fwd"]))) + mom_ix = _index(mom_weeks) + vol_ix = _index(vol_weeks) + momr_ix = _index(momr_weeks) + + # Per-week membership + extended checks via shared rich filter + same_week: dict[tuple, list[tuple[float, float]]] = defaultdict(list) + lag_week: dict[tuple, list[tuple[float, float]]] = defaultdict(list) + tier_hi: dict[tuple, list[tuple[float, float]]] = defaultdict(list) + tier_lo: dict[tuple, list[tuple[float, float]]] = defaultdict(list) + prod_sub: dict[tuple, list[tuple[float, float]]] = defaultdict(list) + mom_cond: dict[tuple, list[tuple[float, float]]] = defaultdict(list) + vol_pairs: dict[tuple, list[tuple[float, float]]] = defaultdict(list) + mom_pairs: dict[tuple, list[tuple[float, float]]] = defaultdict(list) + momr_pairs: dict[tuple, list[tuple[float, float]]] = defaultdict(list) + + ordered = sorted((tuple(w) for w in fip_weeks.keys()), key=_week_ord) + prev: dict[tuple, tuple] = {} + for i, wk in enumerate(ordered): + if i: + prev[wk] = ordered[i - 1] + + # Prior-week dvol for lag: (symbol, week) from fip recs + dvol_sw: dict[tuple[str, tuple], float] = {} + for wk, recs in fip_weeks.items(): + key_wk = tuple(wk) if not isinstance(wk, tuple) else wk + for rec in recs: + if isinstance(rec, dict) and rec.get("symbol") and rec.get("median_dvol_63"): + dvol_sw[(str(rec["symbol"]), key_wk)] = float(rec["median_dvol_63"]) + + membership_dumps: list[dict] = [] + dump_count = 0 + stride = max(1, round(HORIZON / 5)) + dump_weeks = _nonoverlap(ordered, stride)[: max(0, int(args.dump_weeks))] + + for wk_raw, recs in fip_weeks.items(): + wk = tuple(wk_raw) if not isinstance(wk_raw, tuple) else wk_raw + stats = _liquid_breadth_week_stats(recs, top_n=top_n, min_price=min_price) + rich = _filter_liquid_breadth_week_rich( + recs, top_n=top_n, min_price=min_price + ) + for rank, row in enumerate(rich, 1): + same_week[wk].append((float(row["val"]), float(row["fwd"]))) if rank <= 800: - tier_hi_fip[wk].append((float(o["fip_id"]), float(o["fwd"]))) + tier_hi[wk].append((float(row["val"]), float(row["fwd"]))) elif rank <= top_n: - tier_lo_fip[wk].append((float(o["fip_id"]), float(o["fwd"]))) - if o["symbol"] in prod_symbols: - prod_subset_fip[wk].append((float(o["fip_id"]), float(o["fwd"]))) + tier_lo[wk].append((float(row["val"]), float(row["fwd"]))) + sym = row.get("symbol") + if sym and str(sym) in prod_symbols: + prod_sub[wk].append((float(row["val"]), float(row["fwd"]))) + # Join mom for conditional + mrec = mom_ix.get((wk, str(sym))) if sym else None + if mrec is not None: + row["mom_12_1"] = mrec.get("val") - # Context signals: liquid among names that carry that signal - with_vol = [o for o in obs if o.get("vol_6m") is not None] - for o in _liquid_members(with_vol, top_n=top_n, min_price=min_price): - liquid_vol[wk].append((float(o["vol_6m"]), float(o["fwd"]))) - with_mom_all = [o for o in obs if o.get("mom_12_1") is not None] - liq_mom = _liquid_members(with_mom_all, top_n=top_n, min_price=min_price) - for o in liq_mom: - liquid_mom[wk].append((float(o["mom_12_1"]), float(o["fwd"]))) - with_mom_r = [o for o in obs if o.get("mom_12_1_resid") is not None] - for o in _liquid_members(with_mom_r, top_n=top_n, min_price=min_price): - liquid_mom_r[wk].append((float(o["mom_12_1_resid"]), float(o["fwd"]))) - - # Momentum-conditional: within liquid fip set, keep mom_12_1 ≥ P80 - mom_key = "mom_12_1" + # Mom-conditional among liquid fip set with_mom = [ - o for o in liq_fip - if o.get(mom_key) is not None and o.get("fip_id") is not None + r for r in rich + if r.get("mom_12_1") is not None or mom_ix.get((wk, str(r.get("symbol")))) ] + # ensure mom filled + for r in with_mom: + if r.get("mom_12_1") is None and r.get("symbol"): + m = mom_ix.get((wk, str(r["symbol"]))) + if m is not None: + r["mom_12_1"] = m["val"] + with_mom = [r for r in rich if r.get("mom_12_1") is not None] if len(with_mom) >= MIN_CROSS: - with_mom.sort(key=lambda o: float(o[mom_key])) - n = len(with_mom) - cut = int(math.floor(n * (MOM_WINNER_PCT / 100.0))) - winners = with_mom[cut:] # upper tail - for o in winners: - mom_cond_fip[wk].append((float(o["fip_id"]), float(o["fwd"]))) + with_mom.sort(key=lambda r: float(r["mom_12_1"])) + cut = int(math.floor(len(with_mom) * (MOM_WINNER_PCT / 100.0))) + for r in with_mom[cut:]: + mom_cond[wk].append((float(r["val"]), float(r["fwd"]))) - # Lagged membership: rank by *previous* week's dvol among fip names - pw = prev_week.get(wk) + # Context signals via same shared filter on their own pools + for r in _filter_liquid_breadth_week_rich( + vol_weeks.get(wk_raw) or vol_weeks.get(wk) or [], + top_n=top_n, + min_price=min_price, + ): + vol_pairs[wk].append((float(r["val"]), float(r["fwd"]))) + for r in _filter_liquid_breadth_week_rich( + mom_weeks.get(wk_raw) or mom_weeks.get(wk) or [], + top_n=top_n, + min_price=min_price, + ): + mom_pairs[wk].append((float(r["val"]), float(r["fwd"]))) + for r in _filter_liquid_breadth_week_rich( + momr_weeks.get(wk_raw) or momr_weeks.get(wk) or [], + top_n=top_n, + min_price=min_price, + ): + momr_pairs[wk].append((float(r["val"]), float(r["fwd"]))) + + # Lagged membership using prior week dvol on current fip pool + pw = prev.get(wk) if pw is not None: - lagged: list[dict] = [] - for o in with_fip: - if o.get("close") is None or float(o["close"]) < min_price: + lagged_recs = [] + for rec in recs: + if not isinstance(rec, dict) or not rec.get("symbol"): continue - prev_dvol = dvol_by_sym_week.get((o["symbol"], pw)) - if prev_dvol is None or prev_dvol <= 0: + pdv = dvol_sw.get((str(rec["symbol"]), pw)) + if pdv is None or pdv <= 0: continue - lagged.append({**o, "lag_dvol": prev_dvol}) - lagged.sort(key=lambda o: float(o["lag_dvol"]), reverse=True) - for o in lagged[:top_n]: - lag_week_fip[wk].append((float(o["fip_id"]), float(o["fwd"]))) + # Clone with lag dvol for ranking + lagged_recs.append({ + **rec, + "median_dvol_63": pdv, + }) + for r in _filter_liquid_breadth_week_rich( + lagged_recs, top_n=top_n, min_price=min_price + ): + lag_week[wk].append((float(r["val"]), float(r["fwd"]))) + + if wk in dump_weeks and dump_count < args.dump_weeks: + membership_dumps.append({ + "week": list(wk), + "stats": stats, + "symbols": sorted( + str(r["symbol"]) for r in rich if r.get("symbol") + ), + "n_symbols": len(rich), + }) + dump_count += 1 + + # IC rows + checks = { + "fip_harness_signal_eval": { + "note": "Authoritative harness _signal_evaluation on collected fip_id", + **(harness_by_name.get("fip_id") or {}), + }, + "fip_same_week_via_shared_filter": { + "note": "Same collected data, IC via shared _filter_liquid_breadth_week_rich", + **_ic_from_weekly(same_week), + }, + "fip_lagged_membership_1w": { + "note": "Top-N by prior-week $vol on current fip pool (shared filter)", + **_ic_from_weekly(lag_week), + }, + "fip_tier_1_800": { + "note": "Senior liquid ranks 1–800", + **_ic_from_weekly(tier_hi), + }, + "fip_tier_801_1500": { + "note": "Junior liquid ranks 801–top_n", + **_ic_from_weekly(tier_lo), + }, + "fip_prod_universe_subset": { + "note": "Prod.sqlite symbols inside liquid fip set", + **_ic_from_weekly(prod_sub), + }, + "fip_momentum_conditional_top20pct": { + "note": ( + f"Among liquid fip set, mom_12_1 ≥ P{MOM_WINNER_PCT:.0f} " + "(paper / gate-relevant)" + ), + **_ic_from_weekly(mom_cond), + }, + "vol_6m_liquid": { + "note": "vol_6m through shared filter", + **_ic_from_weekly(vol_pairs), + }, + "mom_12_1_liquid": { + "note": "raw mom through shared filter", + **_ic_from_weekly(mom_pairs), + }, + "mom_12_1_resid_liquid": { + "note": "residual mom through shared filter", + **_ic_from_weekly(momr_pairs), + }, + } + + h = checks["fip_harness_signal_eval"] + s = checks["fip_same_week_via_shared_filter"] + cond = checks["fip_momentum_conditional_top20pct"] + prod = checks["fip_prod_universe_subset"] + hi = checks["fip_tier_1_800"] + lo = checks["fip_tier_801_1500"] + lag = checks["fip_lagged_membership_1w"] + + # Self-consistency: harness eval vs manual IC on same filter must match + harness_ic = h.get("mean_ic") + shared_ic = s.get("mean_ic") + consistent = ( + harness_ic is not None + and shared_ic is not None + and abs(float(harness_ic) - float(shared_ic)) < 0.005 + ) + + mom_alive = ( + cond.get("mean_ic") is not None + and float(cond["mean_ic"]) < 0 + and abs(float(cond["mean_ic"])) >= 0.03 + and bool(cond.get("reliable")) + ) results = { "generated_at": datetime.now().isoformat(), "research_snapshot": str(research.resolve()), - "prod_subset_n": len(prod_symbols), - "panel_tickers": len(jobs), "top_n": top_n, "min_price": min_price, - "checks": { - "fip_same_week_liquid_1500": { - "note": "Replication of main breadth run (same-week $vol mask)", - **_ic_from_weekly(same_week_fip), - }, - "fip_lagged_membership_1w": { - "note": ( - "Liquid top-N ranked on *prior* week's median $vol — " - "excludes same-week liquidity explosion leak" - ), - **_ic_from_weekly(lag_week_fip), - }, - "fip_tier_1_800": { - "note": "Same-week liquid ranks 1–800 (senior liquid tier)", - **_ic_from_weekly(tier_hi_fip), - }, - "fip_tier_801_1500": { - "note": "Same-week liquid ranks 801–1500 (junior liquid tier)", - **_ic_from_weekly(tier_lo_fip), - }, - "fip_prod_universe_subset": { - "note": ( - "Symbols in prod.sqlite (~S&P-like large-cap book) inside " - "same-week liquid top-N — compositional control" - ), - **_ic_from_weekly(prod_subset_fip), - }, - "fip_momentum_conditional_top20pct": { - "note": ( - f"Among liquid top-N, keep mom_12_1 percentile ≥ {MOM_WINNER_PCT} " - "(paper: ID modulates continuation among winners; gate-relevant)" - ), - **_ic_from_weekly(mom_cond_fip), - }, - "vol_6m_liquid_1500": { - "note": "Context: low-vol anomaly strength on this pool", - **_ic_from_weekly(liquid_vol), - }, - "mom_12_1_liquid_1500": { - "note": "Context: raw momentum on liquid breadth", - **_ic_from_weekly(liquid_mom), - }, - "mom_12_1_resid_liquid_1500": { - "note": "Context: residual momentum on liquid breadth", - **_ic_from_weekly(liquid_mom_r), - }, + "prod_subset_n": len(prod_symbols), + "panel_tickers": len(jobs), + "single_source": ( + "diagnostics uses harness _signal_series + " + "_filter_liquid_breadth_week_rich only (no parallel mask)" + ), + "avg_cross_section_semantics": ( + "avg_cross_section = post-mask IC sample size. " + "avg_raw_pool = pre-filter observations. " + "avg_eligible_pre_mask = pass price+dvol before top-N. " + "mask_binds_pct = weeks where eligible_pre_mask > top_n." + ), + "harness_self_consistent": consistent, + "checks": checks, + "membership_dumps": membership_dumps, + "interpretation": { + "harness_and_shared_filter_agree": consistent, + "mask_binds_pct": h.get("mask_binds_pct"), + "avg_eligible_pre_mask": h.get("avg_eligible_pre_mask"), + "avg_raw_pool": h.get("avg_raw_pool"), + "prod_subset_still_negative": ( + prod.get("mean_ic") is not None and float(prod["mean_ic"]) < 0 + ), + "junior_tier_more_positive": ( + lo.get("mean_ic") is not None + and hi.get("mean_ic") is not None + and float(lo["mean_ic"]) > float(hi["mean_ic"]) + ), + "lag_same_sign_as_same_week": ( + lag.get("mean_ic") is not None + and s.get("mean_ic") is not None + and (float(lag["mean_ic"]) < 0) == (float(s["mean_ic"]) < 0) + ), + "mom_conditional_negative_and_reliable": mom_alive, + "orphan_plus_five_sigma": ( + "Prior report fip-breadth-20260718-211440-breadth.json listed " + "fip IC +0.0575 / t +5.12. This single-sourced recompute is the " + "authoritative number; if it disagrees, the +0.0575 row is orphaned." + ), + "compositional_story": ( + "fip_id pools continuous winners (neg IC) vs continuous bleeders " + "(pos IC). Prod-subset and senior liquid stay negative; junior " + "liquid is less negative / positive — composition, not jumpiness premium." + ), + "vol_tilt_warning": ( + "High-vol names underperform on breadth relative to S&P-like books. " + "Re-validate production 80/20 high-vol tilt before any universe broaden." + ), }, - } - - # Interpretations - checks = results["checks"] - lag = checks["fip_lagged_membership_1w"] - same = checks["fip_same_week_liquid_1500"] - hi = checks["fip_tier_1_800"] - lo = checks["fip_tier_801_1500"] - prod = checks["fip_prod_universe_subset"] - cond = checks["fip_momentum_conditional_top20pct"] - - def _sign(x: float | None) -> str: - if x is None: - return "na" - return "neg" if x < 0 else "pos" - - results["interpretation"] = { - "leak_ruled_out": ( - lag.get("mean_ic") is not None - and same.get("mean_ic") is not None - and _sign(lag["mean_ic"]) == _sign(same["mean_ic"]) - and abs(float(lag["mean_ic"])) >= 0.02 - ), - "junior_tier_drives_positive": ( - lo.get("mean_ic") is not None - and float(lo["mean_ic"]) > 0 - and (hi.get("mean_ic") is None or float(hi["mean_ic"]) < float(lo["mean_ic"])) - ), - "prod_subset_still_negative": ( - prod.get("mean_ic") is not None and float(prod["mean_ic"]) < 0 - ), - "mom_conditional_negative_and_reliable": ( - cond.get("mean_ic") is not None - and float(cond["mean_ic"]) < 0 - and abs(float(cond["mean_ic"])) >= 0.03 - and bool(cond.get("reliable")) - ), - "compositional_flip_story": ( - "If prod subset IC is negative while full liquid-1500 is positive, " - "the sign flip is compositional (bleeders / Nasdaq junk), not a " - "temporal regime change. Unconditional fip pools continuous winners " - "(want neg IC) against continuous losers/bleeders (want pos IC)." - ), - "vol_tilt_warning": ( - "vol_6m large negative IC on breadth: high-vol lottery names " - "underperform. Production 80/20 high-vol tilt was validated on " - "S&P-like names; must re-validate before any universe broaden." + "platform_verdict": ( + "Mom-conditional fip ALIVE as book-tilt candidate (needs book sim) — " + "not production wire-in. Unconditional fip not green." + if mom_alive + else ( + "fip CLOSED for production: mom-conditional does not clear iron rule " + "on single-sourced path. Display card is the resting place." + ) ), } - # Gate-relevant summary line - if results["interpretation"]["mom_conditional_negative_and_reliable"]: - results["platform_verdict"] = ( - "ALIVE as breadth-book tilt candidate among momentum winners only — " - "still needs a book-level experiment; not a production wire-in." - ) - else: - results["platform_verdict"] = ( - "CLOSED for production use: momentum-conditional fip does not clear " - "iron rule on this liquid-Nasdaq pool. Display card remains final resting place." - ) - stamp = datetime.now().strftime("%Y%m%d-%H%M%S") - out = Path(args.out) if args.out else Path("reports") / f"fip-breadth-diagnostics-{stamp}.json" + out = Path(args.out) if args.out else Path("reports") / f"fip-reconcile-{stamp}.json" out.parent.mkdir(parents=True, exist_ok=True) out.write_text(json.dumps(results, indent=2, default=str), encoding="utf-8") - # Append to research log - md_path = Path("docs/research/fip-breadth-ic.md") - _append_diagnostics_md(md_path, results, out) + # Update research log + _update_md(Path("docs/research/fip-breadth-ic.md"), results, out) if not args.quiet: - print(json.dumps(results["checks"], indent=2, default=str)) - print() - print("interpretation:", json.dumps(results["interpretation"], indent=2)) + print("=== Harness fip_id (authoritative) ===") + print(json.dumps(h, indent=2, default=str)) + print("=== Shared-filter same-week (must match) ===") + print(json.dumps(s, indent=2, default=str)) + print("=== Mom-conditional ===") + print(json.dumps(cond, indent=2, default=str)) + print("self_consistent:", consistent) print("platform_verdict:", results["platform_verdict"]) print(f"Wrote {out}") - print(f"Updated {md_path}") -def _append_diagnostics_md(path: Path, results: dict, artifact: Path) -> None: +def _update_md(path: Path, results: dict, artifact: Path) -> None: checks = results["checks"] interp = results["interpretation"] + h = checks.get("fip_harness_signal_eval") or {} lines = [ "", "---", "", - f"## Follow-up diagnostics ({results['generated_at'][:10]})", + f"## Reconciliation ({results['generated_at'][:10]})", "", - "Compositional reading of the sign flip (before any 'jumpiness premium' story):", + "### Problem", "", - "`fip_id = sign(PRET) × (%neg − %pos)` pools two opposite continuous populations:", + "Two implementations of the liquid-1500 fip IC disagreed on **sign**:", "", - "- **Continuous winners** (PRET>0, mostly up days) → paper claim → **negative** IC contribution.", - "- **Continuous losers / bleeders** (PRET<0, mostly down days) → momentum continuation down → **positive** IC contribution.", + "- Harness report `fip-breadth-20260718-211440-breadth.json`: **+0.0575 / t +5.12**", + "- Dual-path diagnostics (since deleted): **−0.017 / t −1.9**", "", - "Unconditional IC is a tug-of-war weighted by universe composition. S&P-like books " - "have few steady bleeders → negative fip IC. Liquid Nasdaq has many → sign can flip " - "without contradicting Da/Gurun/Warachka (claim was always **momentum-conditional**).", + "A static read cannot decide which is right without single-sourcing the mask.", "", - "### Artifact / composition checks", + "### Resolution", + "", + f"- **Single source:** {results.get('single_source')}", + f"- **avg_cross_section semantics:** {results.get('avg_cross_section_semantics')}", + f"- Harness `_signal_evaluation` vs shared-filter recompute agree: " + f"**{interp.get('harness_and_shared_filter_agree')}**", + "", + "### Authoritative unconditional fip (liquid top-N, post-mask)", + "", + f"| metric | value |", + f"|---|---|", + f"| mean_ic | {h.get('mean_ic')} |", + f"| ic_t_stat | {h.get('ic_t_stat')} |", + f"| weeks | {h.get('weeks')} |", + f"| avg_cross_section (post-mask) | {h.get('avg_cross_section')} |", + f"| avg_raw_pool | {h.get('avg_raw_pool')} |", + f"| avg_eligible_pre_mask | {h.get('avg_eligible_pre_mask')} |", + f"| mask_binds_pct | {h.get('mask_binds_pct')} |", + f"| reliable | {h.get('reliable')} |", + "", + "The **+0.0575 / +5.12** row is **orphaned** if the authoritative recompute " + "disagrees; do not cite it. Iron-rule unconditional green still requires " + "negative sign and |IC| ≳ 0.03 on this row.", + "", + "### Checks (single-sourced)", "", "| check | mean_ic | t | weeks | avg N | reliable |", "|---|---:|---:|---:|---:|---|", ] - order = [ - "fip_same_week_liquid_1500", + for key in [ + "fip_harness_signal_eval", + "fip_same_week_via_shared_filter", "fip_lagged_membership_1w", "fip_tier_1_800", "fip_tier_801_1500", "fip_prod_universe_subset", "fip_momentum_conditional_top20pct", - "vol_6m_liquid_1500", - "mom_12_1_liquid_1500", - "mom_12_1_resid_liquid_1500", - ] - for key in order: + "vol_6m_liquid", + "mom_12_1_liquid", + "mom_12_1_resid_liquid", + ]: row = checks.get(key) or {} lines.append( f"| {key} | {row.get('mean_ic')} | {row.get('ic_t_stat')} | " @@ -637,28 +617,30 @@ def _append_diagnostics_md(path: Path, results: dict, artifact: Path) -> None: "", "### Flags", "", - f"- Lagged mask keeps same sign / material |IC|: **{interp.get('leak_ruled_out')}**", - f"- Junior tier (801–1500) drives more positive IC: **{interp.get('junior_tier_drives_positive')}**", - f"- Prod-universe subset still negative: **{interp.get('prod_subset_still_negative')}**", - f"- Mom-conditional (≥P80) negative + reliable: **{interp.get('mom_conditional_negative_and_reliable')}**", + f"- Prod subset still negative: **{interp.get('prod_subset_still_negative')}**", + f"- Junior tier more positive than senior: **{interp.get('junior_tier_more_positive')}**", + f"- Lag same sign as same-week: **{interp.get('lag_same_sign_as_same_week')}**", + f"- Mom-conditional negative + reliable: **{interp.get('mom_conditional_negative_and_reliable')}**", "", - "### Platform verdict", + "### Platform verdict (post-reconciliation)", "", results.get("platform_verdict", ""), "", - "### Vol-tilt warning (any future breadth move)", + "### Vol-tilt warning", "", interp.get("vol_tilt_warning", ""), "", f"Artifact: `{artifact.as_posix()}`", "", ]) - # Replace previous diagnostics section if re-run, else append existing = path.read_text(encoding="utf-8") if path.exists() else "" - marker = "## Follow-up diagnostics" + marker = "## Reconciliation" if marker in existing: existing = existing.split(marker)[0].rstrip() + "\n" - path.write_text(existing + "\n".join(lines), encoding="utf-8") + # Also strip old dual-path diagnostics section if present after reconciliation + if "## Follow-up diagnostics" in existing and marker not in path.read_text(encoding="utf-8") if path.exists() else "": + pass + path.write_text(existing.rstrip() + "\n" + "\n".join(lines), encoding="utf-8") if __name__ == "__main__":