Methodology & validation

data as of 2026-07-28

Factor Watch — Methodology

How every number is computed. This file is the spec; factors.py and analytics.py implement it. If the code changes, change this file in the same commit. Transparency is a product feature: Barra and the S&P factor indices are black boxes, this is not.

1. Universe

S&P 500 constituents, point-in-time. Membership at any past date is reconstructed from FMP's historical add/remove event feed by walking events backward from the current 503-name list. A stock acquired in January 2026 is therefore in the universe for rebalances before its removal date and out afterward — no survivorship bias from using today's list, to the extent the event feed is accurate.

Secondary share classes (GOOG, FOX, NWS, and — in the 2020–2022 stretch of the backfilled era — DISCK and UA) are dropped: FMP reports the full company market cap on both lines, so keeping both double-counts the company (Alphabet was 2x weighted before this fix). The primary line (GOOGL, FOXA, NWSA, DISCA, UAA) carries the company.

History depth. The series is backfilled to the 2019-12-31 first rebalance (returns from January 2020) so the era ruler in §5 covers all of "modern markets", COVID crash included. Ticker renames whose history FMP re-homes under the new symbol are mapped in ingest.RENAMES (FB→META); ingest.survivorship_audit checks every ever-member's price coverage against its actual membership window and reports holes to the run log and data/raw/meta.json, so a recycled ticker can never silently vanish from past portfolios.

Known limits:

2. Data

All from FMP (Premium tier), pulled by ingest.py:

DatasetEndpointNotes
Priceshistorical-price-eod/dividend-adjustedsplit and dividend adjusted close → returns are total returns. The default full endpoint is unadjusted — do not use it for returns.
Income statementincome-statement (quarterly, 44q)includes filingDate — basis for point-in-time visibility
Balance sheetbalance-sheet-statement (quarterly, 44q)includes filingDate
Cash flowcash-flow-statement (quarterly, 44q)operating cash flow for the experimental accruals metric (not in the quality composite — see §10)
Market cap, ROEkey-metrics (quarterly, 44q)no filing date; joined to income-statement quarters
Dividendsdividendsper-share cash dividends by ex-date
Membership eventshistorical-sp500-constituentsee §1

The FW 3000 universe builder (universe.py, §13) additionally uses company-screener, delisted-companies, historical-market-capitalization, and sp500-constituent.

Point-in-time rule: at a rebalance date t, a quarterly report is visible only if its filingDatet. No look-ahead into not-yet-filed quarters. TTM aggregates use the last 4 visible quarters.

Market cap at t: last reported quarter's market cap, drifted to t by the adjusted-price ratio. (Drifting with total-return prices slightly overstates cap for high-yield names between reports; immaterial for quintile ranking.)

Data-hygiene guards (2026-07). Vendor price errors in the broad universe's small end produced two published artifacts — an unadjusted ~160x print (ESPR, 2026-07-08) that moved five FW 3000 spread series by ±27–33% in one day, and a block of stale micro-cap tapes snapping back at once (2025-12-03, broad size spread +14%). Three guards now apply, all in factors.py:

3. Factor definitions

Scores are computed cross-sectionally over the point-in-time universe at each rebalance. Raw metrics are winsorized at 2.5%/97.5%, then z-scored. Composites average the available component z-scores (a name missing one component is scored on the rest; missing all → excluded from that factor's portfolios).

FactorDefinition (higher score = stronger membership)
Momentum12-1 month total return: P(t-21d) / P(t-252d) - 1 (skips the most recent month, standard reversal exclusion)
Valuemean z of: E/P (TTM net income / mktcap), B/P (common equity / mktcap, equity>0 only), S/P (TTM revenue / mktcap)
Qualitymean z of: ROE (TTM net income / avg of current & year-ago equity), −D/E (total debt / equity), −earnings variability (std of YoY quarterly diluted-EPS growth, last 12 obs, min 8)
Size−log(market cap) — "small within the S&P 500"
Low volatility−std of daily total returns, trailing 252d (min 200 obs)
High betaOLS beta vs SPY, trailing 252d daily returns
Dividend yieldTTM dividends per share (by ex-date) / price
EPS revisionsnot yet computed — requires ≥ ~1 month of daily consensus snapshots from collect_revisions.py; see §8

4. Portfolio construction

At each month-end rebalance (last trading day):

1. Rank scored names; split into quintiles (~100 names each in the S&P 500, ~570 in the FW 3000). 2. Long-only factor series (<factor>_long): top quintile, cap-weighted with a 5% single-name cap (excess redistributed pro-rata, S&P-style). Without the cap, the quality and momentum longs degenerate into a mega-tech beta bet (6 names ≈ 60% of weight) and stop measuring the factor — this showed up directly in validation. This is the comparable to the S&P factor indices / factor ETFs and feeds the quilt. 3. Spread series (<factor>_spread): top quintile minus bottom quintile, both equal-weighted (academic convention; cap-weighted spreads are dominated by megacaps). 4. Benchmark (bench): full universe, cap-weighted. 5. Sectors (sector_*): cap-weighted within each GICS sector (current-constituent mapping; sector history not reconstructed).

Between rebalances portfolios are buy-and-hold: weights drift with prices; no daily re-ranking (daily re-ranking is the classic way to fake a factor series and leak turnover-free alpha). A name whose prices stop mid-month (delisting/acquisition) is frozen at its last price — economically a cash-out reinvested pro-rata at next rebalance.

Rebalances start at the first month-end on/after 2019-12-31, so daily series begin January 2020 — the era backfill of 2026-07 (previously 2024-06-30). The start needs a 12m momentum lookback, so price history is pulled from 2018-12-01. Note the backfill also deepened the quarterly fundamentals pull (20→44 quarters), which lets more names clear the earnings-variability minimum at recent rebalances too — recent quality quintiles can differ marginally from the pre-backfill series.

4a. Published constituent books (factor_books.json, Factors page)

The books are published, not just their returns. At every rebalance the membership captured during series construction (never recomputed after the fact, so the lists are exactly the portfolios behind the series) feeds data/derived/factor_books.json:

5. Spread monitor (z-scores)

For each factor, two relative series:

For horizons 1d / 5d / 20d / 60d: current h-day compounded return compared to the trailing 252 overlapping h-day returns of the same series (current excluded). Report z-score and percentile (returns are fat-tailed; the percentile keeps the z honest). |z| ≥ 2 is the headline flag.

Era context (the modern-markets ruler). Once a series has ≥750 overlapping observations (~3 years), each horizon also reports how the current move ranks against the series' entire history — the S&P series is backfilled to January 2020, COVID crash included, so this answers "how big is this move by modern-markets standards" on a near-fixed baseline. The block carries the empirical rank (nth_worst / nth_best, with the dates of the nearest prior worse/better move), the empirical percentile, and a fixed-ruler z. The empirical rank leads in all copy — with fat tails the statement "2nd-worst week since 2020" is honest where "−5σ" merely gestures. The trailing-year z remains the flagging engine: it answers "unusual lately", adapts to volatility regimes within a year, and is deliberately not replaced by the era ruler.

Caveat by design: with overlapping windows the baseline observations are autocorrelated — fine for "is this unusual?", not a t-test. A second caveat, discovered live in the June–July 2026 momentum unwind: after a volatility-regime shift, the trailing-year σ is calibrated on the calmer prior regime, so repeated 3σ+ prints can describe ONE persistent episode. The era rank and the percentile are the antidotes; read them together.

6. Rotation detector

On trailing-20d returns of the long-only series:

7. Performance quilt

Calendar-month total returns of the long-only factor series + benchmark, ranked best→worst per month, trailing 13 months. Monthly granularity only — a daily quilt reshuffles too much to read; daily action lives in the spread monitor.

8. EPS revisions (V2, collection running now)

collect_revisions.py snapshots consensus EPS/revenue (avg/high/low, analyst counts) for all constituents daily. **This series cannot be backfilled — non-Enterprise vendors don't sell daily revision history — which is exactly why it compounds into a moat.** Snapshots are committed to git because they are irreplaceable. Planned outputs once ≥1 month accumulates: net up/down revision breadth across the index; revision leaders-vs-laggards spread (the Counterpoint-style edge).

9. Factor seasonality

Our own series is too short for seasonality — 2 years gives n=2 per calendar month, which is astrology. So seasonality.py uses the Ken French library monthly factor returns (momentum to 1927, HML/SMB/RMW to 1963), free: mean/median return and hit rate per calendar month, full history and trailing 30y. The total US market (Mkt-RF + RF; CRSP all-US, labeled "US market", not strictly the S&P 500) is included as a reference row. The dashboard renders the full factor × month grid as a mosaic; the digest uses the current month's baselines. The snapshot carries the current month's baseline next to the live monitor ("June is historically momentum's strongest month, 70% hit rate — and it's currently -2.4σ"). Definition mismatch (French universe ≠ S&P 500 quintiles) is acceptable for a seasonal-baseline view and disclosed in the payload. Mapping: momentum→UMD, value→HML, size→SMB, quality→RMW(profitability); low vol / dividend yield / high beta have no French analogue and are omitted.

10. Validation

validate.py checks the computed series against two independent references (report: data/derived/validation.md): 1. Published S&P 500 factor index monthly returns (Invesco dashboard quilt, Bloomberg-sourced, May 2025–Apr 2026): per-factor correlation, sign agreement, mean abs difference; per-month quilt rank correlation. 2. Factor ETFs (SPMO, RPV, SPHQ, RSP, SPLV, SPHD, SPHB vs SPY): daily return correlation, absolute and benchmark-relative.

Exact agreement is not expected (S&P indices are ~100-name score-weighted baskets; RPV is style-weighted "pure value"). What must hold: high correlation, consistent sign, same ordering most months. If a change to factors.py moves these checks materially, that's a regression until explained.

Current status (2026-06): monthly corr vs published 0.90-1.00 across all factors, MAD 0.6-2.0pp; daily corr vs ETFs 0.87-1.00. Weakest link is quality's benchmark-relative correlation vs SPHQ (~0.5).

Tested and rejected (2026-06): S&P-style accruals in quality. Swapping earnings variability for NI−OCF/assets accruals regressed every quality check (monthly corr 0.91→0.76, relative corr vs SPHQ 0.51→0.02); a 4-component mix was also worse (0.79). Probable cause: NI−OCF accruals are meaningless for financials (~15% of the universe) and FMP quarterly OCF is noisy. The accruals metric is still computed in factors.py for future experiments (e.g. excluding financials) but is not in the composite. Methodology changes must beat the validation harness to ship.

11. Breadth

factors.py also writes data/derived/breadth_daily.csv: each day, the share of point-in-time index members trading above their own 50-day and 200-day moving averages (equal count, secondary share classes excluded, names without enough history for the MA dropped from that day's denominator). Used as a participation check on factor moves — a factor rally with collapsing breadth is a different animal from a broad one.

Validation spot checks: validate.py compares every factor against the published indices (monthly) and its ETF analogue (daily) on each close. The references differ in construction by design, so they are spot checks, not anchors: a monthly correlation below 0.75 or daily ETF correlation below 0.80 — comfortably under the current 0.90–1.00 / 0.87–1.00 levels — is flagged prominently at the top of this page's validation report and in the run log, but never blocks the daily run.

Known measurement compromises (audited 2026-07): dividend yield's numerator is the split-adjusted TTM dividend while its denominator is the dividend-AND-split-adjusted price, so historical yields are inflated in proportion to dividends paid after the measurement date (decaying to zero at the latest session); market caps drifted with an adjusted-price ratio overstate caps by the yield accrued over the (≤1 quarter) drift window. Both distortions are ~monotone in true yield, so rankings shift only at quintile margins. Chart series carry cumulative levels flat across any missing sessions rather than interpolating; validate.py's internal consistency guards flag any such gap. A quilt month built from fewer than 15 sessions is marked with *.

12. Thematic baskets

Definitions live in baskets/*.json, one file per basket: thesis, members (each with an added date and a written rationale, plus a removed date when dropped), and a changelog. Membership is curated by the maintaining agent; **every add/drop is dated and justified in the changelog — the audit trail is part of the product.**

Construction (baskets.py): equal-weighted across active members, buy-and-hold between rebalances — the same discipline as the factor portfolios (no daily re-ranking, frozen prices on delisting). Rebalances fall at month-ends and on any dated membership change, so adds and drops take effect on their changelog date with an equal-weight reset rather than waiting for the next month-end. Members without price data are excluded from the series and surfaced in the dashboard payload for review; names that listed mid-history simply enter the series when their price tape begins. Series before a basket's creation date are a **backtest of the membership as of creation**; live tracking starts at creation. Benchmark-relative figures compound the basket and the computed cap-weighted benchmark over the same window.

Membership aims for a **wide cross-section of each theme inside the S&P 500** rather than a concentrated best-ideas list: every leg of the theme with a listed index constituent should be represented. Some themes are structurally thin in a large-cap universe (cybersecurity's pure plays mostly trade outside the index); their changelogs say so rather than padding the basket with weak fits. Members that leave the S&P 500 are flagged by the pipeline (ex_index in the payload, surfaced on the dashboard) and removed with a dated changelog entry.

Two universes. Each basket is computed over both the S&P 500 and the FW 3000 (§13). Members tagged "universe": "broad" in the basket definition exist only in the FW 3000 version — off-index pure plays like ZS or OKTA in cybersecurity, mid-cap banks in regionals — while untagged members count in both (every S&P 500 name is an FW 3000 name by construction). One file, one changelog, one thesis per theme; names that leave the S&P 500 but remain in the FW 3000 move to a broad entry.

Reference-ETF cross-check. Where a listed sector/industry ETF — or a simple blend, e.g. 50/50 XLP+XLU for the defensives basket — approximates a basket's theme, the basket definition pins it as a reference, and baskets.py reports the daily return correlation against it: absolute, and benchmark-relative (basket minus our computed benchmark vs ETF minus SPY) so agreement is not just shared market beta. The numbers render on each basket's dashboard section in both universes; the S&P versions also appear in the validation report. References are sanity checks, not tracking targets: they are mostly cap-weighted, hold names outside the index, and follow different rules, so correlations should be high but not 1.0. A persistent drop is a prompt to review membership — deliberately not an automated failure, unlike the factor guardrails in §10.

13. FW 3000: the broad-universe mirror

The computation chain — ingest, factor construction, baskets, and the analytics snapshot — runs twice: once over the S&P 500 (the default views) and once over the FW 3000, our self-built Russell 3000-style broad universe, selected with the universe switch on the Overview and Baskets pages. Same factor definitions, same quintile construction, same z-scoring — only the universe changes. The revisions collection (§8), seasonality baselines (§9), the published-index factor validation (§10), and the daily digest run once, on the S&P side; the FW 3000 carries its own universe-level guardrail (below).

Construction (universe.py): the largest 3,000 actively trading US common stocks by market capitalization (NYSE/NASDAQ/AMEX, price > $1, one share class per company with known secondary classes deprioritized explicitly; preferred shares, warrants, rights, units, funds, and exchange notes excluded), always including every current S&P 500 member (some index members have foreign headquarters or fresh ticker renames that screeners mishandle). Reconstitutions are at calendar quarter ends, plus a provisional segment maintained by the weekly rebuild: the membership cut since the last quarter end is re-cut each Monday and only becomes final at the next quarter end. Known limit: history candidates are today's largest ~4,500 actives plus names delisted since mid-2024, so a still-listed stock that fell far out of today's top ~4,500 can be missing from historical quarters it belonged to — a mild winner bias the survivorship stats below do not capture. FMP carries no licensed Russell membership, so we build our own the way Bloomberg builds its B3000 — this is **not the Russell 3000**, and we never label it as such; it is a Russell 3000-style universe under the published rules in this section. Membership history back to mid-2024 is reconstructed point-in-time from historical market caps, including delisted names.

Survivorship is measured, not assumed away. At the first build, 98.4% of the 1,779 names delisted since mid-2024 had enough market-cap history to be ranked, 675 of them entered the reconstructed membership, and 17.7% of the June-2024 membership is no longer trading — those names stay in the series for the quarters they belonged. The numbers live in data/universe/fw3000_meta.json and refresh with each weekly universe rebuild.

Validation: the FW 3000 cap-weighted benchmark is checked against IWV (iShares Russell 3000 ETF) on every close run; a daily correlation below 0.97 raises a prominent spot-check warning (it does not block the run — one transient bad print once cost a full close). First build: correlation 0.997 over the full series, ~1.3% annualized tracking error, cumulative return within ~0.6pp of IWV over the backtest window. Seasonal baselines come from the Ken French library (§9), whose all-exchange universe is much closer to the FW 3000 than to the S&P 500.

Cadence: the FW 3000 refreshes on the same schedule as the S&P 500 — hourly through the session (prices only, off the cached fundamentals) and again at the close. Fundamentals refresh on close runs (Mondays and the month-end window, so rebalance inputs are fresh); the universe membership rebuilds on Mondays. Both universes therefore carry the same "as of" date through the day, and the intraday/close marker on each page distinguishes a partial session from a finalized close. The broad intraday refresh is best-effort: if it is ever unavailable, the FW 3000 pages fall back to the last committed close rather than block the S&P update.

14. Estimate revisions

Factor Watch snapshots the analyst FY consensus (EPS and revenue: mean, high, low, analyst count) once per market day (collect_revisions.py): every S&P 500 constituent since 2026-06-10, widened to the full FW 3000 membership on 2026-07-04. Small caps without analyst coverage store no rows — that absence is itself the coverage truth. The S&P series is era-aware: broad-era snapshots are intersected with the committed S&P membership so its trend never silently changes universe; the FW 3000 series publishes once it has six broad snapshots. Published history of when consensus moved cannot be bought later — the archive only exists because it is collected daily, which is why the site treats it as its most defensible dataset.

Net revision breadth (site section "Estimate revisions", sp500): for each constituent, compare today's FY1 EPS consensus to the reading 5 sessions earlier, matched on the same fiscal year (a fiscal-year roll is never counted as a revision). A name counts as raised/lowered when the consensus moved by at least 0.1% relative; names covered by fewer than 3 analysts are excluded. Net breadth = (% raised − % lowered).

Factor-quintile cut: the same breadth measured inside each factor's top and bottom quintile, using the committed membership from the latest monthly rebalance (latest_portfolios.json). A positive top-minus- bottom spread reads as fundamental confirmation of the factor; a negative one as analysts leaning against it.

Measurement notes: consensus levels come from the vendor's estimate aggregation and can jump when analysts are added/dropped, not only when targets change; the 0.1% threshold and the analyst-count floor damp but don't eliminate this. The series' trailing depth is bounded by the collection start date above.

Differences vs the big-shop dashboards (deliberate)

Disclaimer

Factor Watch is provided for informational and educational purposes only. Nothing on this site, in the email brief, or in any other output of this project is investment advice, a recommendation, or an offer or solicitation to buy or sell any security.

The pipeline is automated and depends on third-party data sources. Everything here is provided "as is", without warranty of any kind: no guarantee that any figure is accurate, complete, or timely, or that the site and pipeline are free of errors or bugs. Data may be delayed, revised, or simply wrong; runs can fail or publish incorrect output. Verify independently before relying on anything shown here.

We accept no liability for any loss or damage arising from the use of this site or its data. Consult a qualified financial adviser before making investment decisions.


Factor Watch validation report

Computed series: 2020-01-02 to 2026-07-27. References: S&P 500 factor index monthly returns (Invesco dashboard quilt, as of 2026-04-30) and factor ETF total returns (FMP, dividend-adjusted).

momentum (ETF ref: SPMO)

MonthOursETFPublished index
2025-05+5.2%+11.4%+11.4%
2025-06+2.2%+7.0%+6.9%
2025-07+0.0%+2.9%+2.9%
2025-08+0.4%+0.7%+0.6%
2025-09+6.2%+4.1%+4.2%
2025-10+1.0%+0.5%+0.6%
2025-11-1.5%-1.3%-1.3%
2025-12+0.2%-0.4%-0.4%
2026-01+4.8%+0.5%+0.4%
2026-02+1.6%-0.3%-0.3%
2026-03-6.0%-5.9%-5.8%
2026-04+18.8%+19.3%+19.3%

Monthly corr vs published: 0.90 | sign agreement: 83% | mean abs diff: 2.0pp

value (ETF ref: RPV)

MonthOursETFPublished index
2025-05+4.2%+2.4%+2.6%
2025-06+5.3%+4.1%+4.0%
2025-07-1.3%-1.8%-1.8%
2025-08+7.6%+6.6%+6.4%
2025-09+1.9%+2.1%+2.1%
2025-10-1.5%-0.3%-0.3%
2025-11+3.7%+3.5%+3.6%
2025-12+1.8%+1.3%+1.3%
2026-01+1.9%+3.8%+3.9%
2026-02+3.6%+4.7%+4.7%
2026-03-3.0%-3.8%-3.9%
2026-04+6.2%+3.6%+3.7%

Monthly corr vs published: 0.92 | sign agreement: 100% | mean abs diff: 1.1pp

quality (ETF ref: SPHQ)

MonthOursETFPublished index
2025-05+5.3%+6.3%+6.2%
2025-06+3.7%+1.6%+1.7%
2025-07-0.1%+0.2%+0.1%
2025-08+2.5%+1.4%+1.3%
2025-09+2.1%+1.6%+1.6%
2025-10+1.0%+1.0%+1.1%
2025-11+1.9%+0.9%+0.9%
2025-12-0.1%+0.7%+0.7%
2026-01+1.3%+3.1%+3.1%
2026-02+1.1%+4.6%+4.7%
2026-03-6.5%-6.8%-6.8%
2026-04+9.2%+7.8%+7.8%

Monthly corr vs published: 0.91 | sign agreement: 83% | mean abs diff: 1.2pp

size (ETF ref: RSP)

MonthOursETFPublished index
2025-05+4.1%+4.3%+4.3%
2025-06+1.7%+3.4%+3.4%
2025-07+0.9%+1.0%+1.0%
2025-08+4.2%+2.7%+2.7%
2025-09-1.1%+1.0%+1.1%
2025-10-2.5%-0.9%-0.9%
2025-11+3.5%+1.9%+1.9%
2025-12+0.3%+0.4%+0.4%
2026-01+3.6%+3.4%+3.4%
2026-02+3.1%+3.5%+3.5%
2026-03-6.9%-6.0%-6.0%
2026-04+4.0%+6.0%+6.0%

Monthly corr vs published: 0.93 | sign agreement: 92% | mean abs diff: 1.0pp

lowvol (ETF ref: SPLV)

MonthOursETFPublished index
2025-05+1.3%+1.0%+1.1%
2025-06-1.2%-0.7%-0.8%
2025-07-0.5%-0.3%-0.3%
2025-08+3.1%+1.6%+1.6%
2025-09-0.6%+0.2%+0.2%
2025-10-3.4%-3.7%-3.7%
2025-11+3.2%+3.8%+3.9%
2025-12-1.4%-2.2%-2.2%
2026-01+3.7%+3.3%+3.3%
2026-02+5.8%+5.3%+5.4%
2026-03-5.8%-5.3%-5.3%
2026-04+1.5%+2.0%+2.0%

Monthly corr vs published: 0.98 | sign agreement: 92% | mean abs diff: 0.6pp

divyield (ETF ref: SPHD)

MonthOursETFPublished index
2025-05+1.4%+0.4%+0.4%
2025-06+2.0%+0.4%+0.4%
2025-07+0.5%+0.6%+0.6%
2025-08+4.9%+4.1%+4.2%
2025-09-1.0%+0.3%+0.4%
2025-10-2.3%-4.0%-4.0%
2025-11+3.8%+3.2%+3.2%
2025-12+0.4%-0.9%-0.9%
2026-01+7.1%+5.2%+5.2%
2026-02+5.3%+4.7%+4.7%
2026-03-3.2%-5.0%-5.0%
2026-04+3.4%+2.0%+2.1%

Monthly corr vs published: 0.96 | sign agreement: 83% | mean abs diff: 1.2pp

bench (ETF ref: SPY)

MonthOursETFPublished index
2025-05+6.1%+6.3%+6.3%
2025-06+5.0%+5.1%+5.1%
2025-07+2.3%+2.3%+2.2%
2025-08+2.0%+2.1%+2.0%
2025-09+3.7%+3.6%+3.6%
2025-10+2.2%+2.4%+2.3%
2025-11+0.3%+0.2%+0.2%
2025-12+0.0%+0.1%+0.1%
2026-01+1.4%+1.5%+1.5%
2026-02-0.9%-0.9%-0.8%
2026-03-5.0%-4.9%-5.0%
2026-04+10.5%+10.5%+10.5%

Monthly corr vs published: 1.00 | sign agreement: 100% | mean abs diff: 0.1pp

Daily return correlation vs ETF analogue

FactorETFCorr (1y)Corr (rel, 1y)Corr (full history)Sessions
momentumSPMO0.9570.8980.9381649
valueRPV0.8770.8900.9721649
qualitySPHQ0.8790.5930.9631649
sizeRSP0.8600.9110.9551649
lowvolSPLV0.9430.9750.9641649
divyieldSPHD0.9330.9640.9701649
highbetaSPHB0.9540.8790.9621649
benchSPY0.9960.9981649

Sector series vs SPDR sector ETFs (daily corr, last 252 trading days)

SectorETFCorr (daily)Corr (relative-to-SPY daily)
Basic MaterialsXLB0.9720.961
Communication ServicesXLC0.8410.645
Consumer CyclicalXLY0.9790.945
Consumer DefensiveXLP0.9580.977
EnergyXLE0.9970.998
Financial ServicesXLF0.9970.995
HealthcareXLV0.9990.998
IndustrialsXLI0.9910.979
Real EstateXLRE0.9980.996
TechnologyXLK0.9780.941
UtilitiesXLU0.8920.916

Quilt rank agreement (Spearman, ours vs published, per month)

MonthRank corr
2025-050.82
2025-060.61
2025-070.79
2025-080.89
2025-090.82
2025-100.86
2025-110.75
2025-120.39
2026-010.29
2026-020.78
2026-030.95
2026-040.96

Thematic baskets vs reference ETFs

Each basket that has a listed sector/industry analogue declares it as a reference in its definition; baskets.py reports the daily return correlation, absolute and benchmark-relative (basket − our benchmark vs ETF − SPY). References differ in universe and weighting, so high — not perfect — agreement is the expectation. Informational, no guardrail.

BasketReferenceDaily corrRelative corrDays
AI Infrastructure LeadersSMH0.950.871649
AI Software & PlatformsIGV0.960.901649
CybersecurityCIBR0.920.821649
Optics & MemorySMH0.810.621649
RetailXRT0.830.601649
Travel & ExperiencesPEJ0.890.771649
Power & Grid BuildoutGRID0.850.531649
US Energy ComplexXLE0.980.961649
Capital Markets CycleKCE0.960.861649
Payments & FintechIPAY0.950.851649
Regional BanksKRE0.960.941649
Managed Care & Health InsurersIHF0.930.921649
Defense & AerospaceITA0.950.881649
Reshoring & Industrial CapexXLI0.940.781649
Defensives50/50 XLP+XLU0.940.941649
Housing ChainXHB0.980.951649
Magnificent SevenMAGS0.990.96825

FW 3000 benchmark vs IWV (iShares Russell 3000 ETF)

Daily correlation 0.9945 over 1636 sessions; annualized tracking error 2.18%; cumulative +128.4% (FW 3000) vs +141.9% (IWV). Spot-check floor: 0.97.

Internal consistency (gap and corrupted-book guards)

All factor series continuous, no outlier months. Clear.