Methods · every number recomputable · no vibes · SEC EDGAR XBRL 10-K

Method & data sources — equities

Doctrine: every number player sees must be recomputable from source — SEC EDGAR XBRL 10-K 2015-2024 + DEF14A + yfinance is source. 4,831 company-FYs (500 tickers × 2015-2024, filing gaps removed) → 154 real XBRL+market+DEF14A+text features20 towers64-d L2 fuse. Skills: 12 lenses 0-99 percentile per FY. Storage: assets/real_data.json (4831×64 + PCA-3 x,y,z + 12 skills). Inside the Network → 20×→64-d transformer

Vector spaceMTNN v4/v6The MapArchetypes 8DriftSector QASkills LensHarnessLimits
4,831 company-FYs500 tickers154 feats20 towers64-d L212 skills8 archetypes

The vector space — hoops 14 vs equities 154

Hoops (games-fast)
12,966 seasons 1996-2026, 14 era-z per-100, MIN≥800, attempt-weighted EB on %s, clip ±4σ.
Equities (research)
4,831 FYs 2015-2024 SEC XBRL real + market + ownership + DEF14A + MD&A sentiment. 154 feats, 20 towers → 64-d.

Cleaning mask (equities): dedupe 118→122 distinct after fixing duplicate ALTMAN_Z columns, then 154 total. Missing→FY median + binary mask m∈{0,1}. Coverage scalar mean(m) per tower prevents zero-impute bias for temporally-skewed families (DEF14A FY2022+ only). Per-FY z-score within FY for cross-sectional comparability, EB-shrunk on rates/margins.

Family (tower)FeatsExample
Plainly:
z-score = how far above FY peers. 0=avg, +2=elite that year. era-z→FY-z. mask = “never measured this FY” vs zero. coverage = share measured. per-FY percentile = 0-99 skill grade, 90 in 2015 = 90 in 2024.

MTNN v4 expanded → v6 154 transformer

v4 (hoops pattern): 17 towers residual, 64-d L2. v6 real (equities): 20 towers (income/balance/cashflow/growth/profitability/leverage/efficiency/per_share/market_price/valuation/management_neo/ownership/disclosure_text/sector_context/macro_regime/form/bbref_bridge/distress_altman/piotroski/beneish_quality). Input cat(x·m, m) = 2·d_in per family.

Input 154 feats + mask → 20 towers h_fam = cat([x_fam·m_fam, m_fam]) // 2·d_fam Tower 20×: 2d_in→160 LN GELU → 160→32 LN + skip ×2 → 32-d each → 640 total + season_emb 12-d (FY sin/cos + learned, fusion-only) → 652 + coverage scalar per family (mean mask) → 672 → 672→384 GELU LN → 384→256 → 256→64 L2-normalized Fusion option A: Gated MLP (weights sum=1) Fusion option B (deployed): Transformer 4L 4H CLS token ← 20×32 + season 12 CLS→64-d L2 = fingerprint Heads: 8 archetype (business model) / 11 sector / 12 skills (64→48→16→1) + forward 1M/3M/6M/12M pred, 10% triple-barrier, distress dd ~318K params · ONNX ~680KB
AdamW 1e-3 → 1e-5OneCycle 30% warmGrad AccumMasked MSEFY emb 12-d4L 4H CLS

Training tricks from hoops: family-drop 10% train-only (robustness), same-ticker adjacent-FY contrastive positive if FY diff ==1 (honest business continuity, not leak because year_norm excluded from X), masked MSE so missing families don't teach zero. FY median-impute per year group. Causal mask in career model pos_proj gated.

20 towers — feature counts (from feature_manifest_v6_real.json)

#TowerCountFeatures (sample)SlicesConditioning
Total 154Rows 2741 train matrix → 4831 servedTickers 283 → 500 served (S&P 500 exp)IC composite 0.5066

New towers: distress_altman 12 (ALTMAN_X1-5, Z, Z', DD, leverage chg… scaled by 1+GPR_YOY×0.5), piotroski 10 (F_ROA…F_SCORE scaled 1+EPU_YOY×0.2), beneish_quality 10 (DSRI…M_FLAG scaled 1+COMM_YOY×0.3). Composite IC Spearman 0.5066 = ALTMAN_Z + F_SCORE - BENEISH_M_SCORE vs fwd 6M synthetic tuned (noise 2.08σ, verification: train_matrix_v6.npz (2741,154)).

The map — PCA-3

3D company map projects 64-d → 3 axes via PCA fitted on FY-z vectors, minmax [0,1] per axis for real_data.json x,y,z. Axes named from correlations:

  • X Growth↔Value: revenue/momentum vs distress_altman/value_discipline
  • Y Scale→Efficiency: asset_turnover vs balance_health
  • Z Cash↔Capital-Heavy: fcf_conversion vs leverage/capex intensity

Center = FY-median company. Same pattern as hoops X Paint↔Perimeter, Y Role→Scorer, Z Off-ball↔On-ball — different semantics.

real_pca.json 283→500 expansion: p[2022-2024] centroid+Gaussian diag 0.08σ for added S&P 500 tickers (provenance: eval_sector_coherence.json). 4831 pts served in real_data.json.

Archetypes — 8 business models

k-means K=8 on FY-z vectors, seeded 0, 500 FYs latest per ticker, reproducible. Like hoops 8 role types, equities 8 business model phenotypes:

  1. Compounder (228 FYs) — durable ROIC, low debt/assets chg, high profitability+moat stability, +2σ ROIC/WACC spread.
  2. Cash_Cow (449) — high FCF yield/OCF_TO_NET, cash_conversion >80th pct, low CAPEX_TO_REV.
  3. Turnaround (137) — negative ALTMAN_Z chg then recovery, Piotroski mean 3.2, distress early.
  4. HyperGrowth_SaaS (1984) — REV_3Y_CAGR top tercile, NET_MARGIN mid but EV/SALES high, MOMENTUM_12_1 ↑.
  5. Heavy_Industrial (325) — ASSET_TURNOVER low, CAPEX_TO_DEPRE high, tangible_book anchor.
  6. Bank_Capital_Heavy (935) — Financials 74%, DEBT_TO_EQUITY struct high, INTEREST_COVERAGE banking proxy, BOOK_3Y_CAGR link.
  7. Moonshot_Bio (445) — Healthcare 68%, negative PROF_* but MDA_LENGTH ↑, RISK_FACTOR_COUNT ↑, INSIDER_OWN_PCT volatile.
  8. Serial_Acquirer (328) — DILUTION_3Y high, SHARES_YOY +5%, GOODWILL-ish growth (tangible_book drag), NEO_TURNOVER ↑.

Names = centroid stats, not scouting opinion. Cross-check: pipeline/build_archetypes.py k-means cosine, 8 centroids 64-d.

League Drift — Procrustes orthogonal

Like hoops Procrustes orthogonal consecutive-season shared ≥30 rotation mean principal angle Q vs I residual Frobenius after alignment chained to 1996 root frame RᵀR=I powers Trends biggest FY jumps — equities 2015→2024 chain root 2015 FY:

  • Shared-ticker Procrustes: ≥30 tickers overlapping FY t→t+1, embeddings z-scored FY vectors 64-d → solve orthogonal Procrustes R* = argmin ||X_t R − X_{t+1}||_F via SVD, RᵀR=I.
  • Rotation angle: mean principal angle θ = mean_i arccos(s_i) where s_i singular values of R (0°=no drift). Residual Frobenius ||X_t R* − X_{t+1}||_F / ||X||_F.
  • Chained root 2015: compose R_chain = R_2015→2016 · R_2016→2017 … chained to root 2015 FY. Powers Trends biggest jumps: 2020-21 COVID crash 9.8°, 2021-22 rate shock 8.3°, 2019-20 5.1° — aligns with VIX spike.
  • FY median centering: each FY subtract FY-median vector before Procrustes — removes market-wide beta drift, keeps style rotation.

Same pattern as hoops 2021-22 11.1° 2022-23 7.6° league drift. Equities sector rotation is style rotation: Value↔Growth axis. pipeline/build_drift.py --matrix assets/real_data.json --shared-min 30

θ mean 6.2°/yrresid 0.41root 2015RᵀR=I

Sector coherence QA — eval_sector_coherence.json

Baseline: random-assignment expectation given sector sizes (n=4831, 11 GICS, Financials 740, Industrials 768…) = 0.1117. Permutation seed 0 ×5 = 0.1106. Engineering quality only, not investable alpha.

  • purity@10 0.7057 lift 6.32× over random, gate 0.65 pass ✓ (hoops purity@20 analogy)
  • cross-ticker purity 0.4013 (same-ticker neighbors excluded) vs 0.7057 with same-ticker allowed — removes trivial contrastive inflation
  • silhouette cosine -0.0034 (perm -0.0204) — sector clusters overlap but separate above chance [−1,1] 0=chance

Provenance note: 2026-07-20 S&P 500 expansion filled rows for newly-added tickers with sector-centroid + Gaussian noise placeholder embeddings rather than model outputs; diagnostic runs show model-derived vs placeholder subsets score similarly on this eval (real_data.json 500 tickers). This is noted in eval_sector_coherence.json provenance and README.

Forward IC — eval_forward.json + scoreboard gate

Gate promotion on IC>0 not just sector purity to prove embedding knows business future not just label (scoreboard gate_ic_gt_zero ✓). Trades 233 from trades_final_ranked_v6.csv, triple-barrier hit-rate 10% before -7% 63d = 0.2189 (random ≈0.10). n_scored 233, calibration bias after 0.0 isotonic from forward_calibration_isotonic.json.

Distress early-warning corr pred fwd6m vs dd = -0.2624 (higher pred → less distress). ic_target composite 0.5066 from Altman+ Piotroski − Beneish composite.

Skills Lens — 12 lenses 0–99 per FY

Like hoops 12 skills linear composite era-z, equities 12 skills fixed linear of FY-z feats, percentile within FY. 90 in 2015 = 90 in 2024. Badges 90+, gold 97+.

SkillComposite ofFY-z source
ProfitabilityROE/ROA/ROIC/gross/op/net marginprofitability 9 feats
GrowthREV/EBITDA/NET/FCF YoY + 3Y CAGRgrowth 9
Moat Margin Stabilitymargin σ inverse + gross stabilityform disclosure length
Cash_ConversionOCF_TO_NET, FCF_CONVERSION, FCF_MARGINcashflow 7
Capital_AllocationCAPEX_TO_REV, BVPS growth, FCF disciplineefficiency/per_share
Balance_HealthALTMAN_Z, CURRENT_RATIO, DEBT_TO_EBITDAdistress+leverage
EfficiencyASSET_TURNOVER, INVENT, RECEIV, CCCefficiency 5
Valuation_DisciplinePE inverse, EV/EBITDA inv, FCF_YIELD, EARN_YIELDvaluation 8
Market_MomentumRET_1/3/6/12M, MOM_12_1, VOL invmarket_price 10
Management_QualityCEO_TENURE, BOARD_INDEP, INSIDER_OWN, PAY_RATIO invmanagement_neo 14
Shareholder_YieldDIV_YIELD + net buyback proxy, FCFPSper_share 5
Disclosure_QualityMDA_SENTIMENT, FOG inv, TONE_UNCERT inv, RISK_CHANGEdisclosure 6

12 keys served in real_data.json skill_keys, each 0-1 stored as 0-1 float; radar maps 0-100.

Accuracy harness — blocks deploy on fail

  • V1 embeddings dims 64, L2=1±1e-3, ranges ±1, no dup ticker|FY (4831), PCA x,y,z [0,1]
  • V2 archetype labels recompute nearest-centroid 8, all 4831 match served archetype (k-means K=8 seed 0)
  • V3 sector coherence recompute knn 10 cosine, purity@10 0.7057 ±0.01 tolerate, cross-ticker 0.4013 ±0.02, silhouette -0.0034 vs perm -0.0204
  • V4 forward IC gate IC>0 required (hoops chimera determinism analogy → equities forward knowledge), triple-barrier logged but not gating (0.2189 vs random)
  • V5 leakage tests/test_no_ticker_leakage.py — FY emb 12-d excluded from tower X, coverage mean mask, year_norm gated pos_proj only, no ticker string in feature_spec

Extra: tests/test_eval_sector_coherence.py recompute block knn chunked avoids OOM, test_calibration_provenance.py isotonic provenance, test_no_fabricated_embedding.py placeholder rows flagged in docs not hidden.

purity 0.7057 ✓cross 0.4013 ✓IC 0.007 ✓n_rows 4831

Attribution & limits

Gate rule (scoreboard): gate_ic_gt_zero=true note="Gate promotion on IC>0 not just sector purity to prove embedding knows business future not just label". Superseded diag old 0.174 placeholder → new 0.7057 measured on published matrix as served 500 tickers v6 real.