SPB Git
4.9 KB

# Extension de l'univers : S&P 500 → Russell 3000 (2026-10-27)

Base de données EDGAR UQO (Manel Kammoun, Charli Tandja Mbianda, Simon-Pierre Boucher).

# Univers

Source Fichier Contenu
S&P 500 (Wikipédia, cache du 2026-05-25) data/metadata/sp500_constituents.json 503 lignes, 500 CIK
Russell 3000 = titres détenus par l'ETF iShares IWV (latest-holdings.csv, « Fund Holdings as of Oct 23, 2026 ») data/metadata/russell3000_constituents.json 2 587 lignes actions → 2 555 appariées à un CIK SEC (company_tickers.json) ; 21 non appariées (CVR, escrows, banques FDIC sans CIK…)
Univers fusionné data/metadata/universe.json 2 564 sociétés (491 dans les deux indices, 9 S&P seulement, 2 064 Russell seulement)

Reconstruire : python scripts/build_universe.py [--refresh-sp500] [--shards node:cores,… --master M3U96b]. Le Russell 3000 n'a pas de liste officielle gratuite ; l'ETF IWV (réplication quasi complète) est le proxy standard. Les ~400 titres manquants par rapport aux 3 000 nominaux sont les micro-caps non détenues par l'ETF, les classes d'actions doublonnées (même CIK) et les sociétés sans dépôt SEC (banques déposant à la FDIC).

# Pipeline (identique à l'original, rendu scalable)

  1. scripts/download_filings.py — téléchargeur concurrent (src/acquisition/bulk_downloader.py) : seau à jetons global (--rate req/s), 6 fils, reprise, repli 65 s sur HTTP 403/429, en-tête de la société (SIC, État, bourse) sauvé dans data/metadata/submissions/<cik>.json. Formulaires 10-K, 10-Q, 8-K depuis 2010-01-01 (document principal).
  2. scripts/build_database.py — sociétés (univers + en-têtes SEC, colonnes in_sp500, in_russell3000, sic_description, state_of_incorporation, exchange, fiscal_year_end, russell_weight_pct, shard_node) + dépôts depuis download_log.json et download_log.*.jsonl ; local_path recalculé depuis le nœud courant ; relançable (UPDATE + INSERT, compatible avec les clés étrangères DuckDB).
  3. scripts/parse_filings.py --max-workers N --batch-size 800 --no-json — parsing par lots (mémoire bornée), insertion par DataFrame ; les 10-K et 10-Q sont maintenant parsés (l'ancien corpus n'avait que les 8-K + 3 710 10-Q). src/parsing/chunker.py (perdu) a été recréé : fenêtres de 512 mots, chevauchement 64, minimum 50.
  4. scripts/build_embeddings.py — MiniLM-L6-v2 (384 d, MPS) en flux de 50 000 chunks, un fichier parquet float32 par lot.
  5. scripts/build_factors.py — scores d'ancrage (8 thèmes) ; agrégation par dépôt réécrite en SQL, centroïdes firme-année vectorisés (fonctionne avec >20 M de chunks).

# Exécution distribuée (cluster MacLustr)

scripts/cluster/orchestrate.py (sur la passerelle M1M32, copie du code dans ~/UQO_RESEARCH_DB-code, journal ~/edgar-orchestrate/) pilote 10 Macs + le serveur OVH BHS64 :

Étape Détail
sync code + métadonnées → ~/UQO_RESEARCH_DB de chaque nœud (venv Python 3.12 uv, torch MPS)
downloads la SEC limite à 10 req/s par IP : les 10 Macs (même IP publique) se partagent 9 req/s (0,9 chacun) ; BHS64 (2e IP) télécharge l'autre moitié de chaque shard à 9 req/s dans ~/edgar-dl/<nœud>/
ship chaque Mac rapatrie sa moitié BHS64 (rsync via l'agent SSH de M1M32)
pipeline scripts/cluster/run_shard.sh <nœud> pipeline : build_db → parse → embed → export (data/export/*.parquet)
pull + merge le maître M3U96b (qui détient le corpus S&P 500 d'origine, 88 Go, rafraîchi depuis mai 2026) rapatrie les shards, scripts/merge_shards.py fusionne dans data/duckdb/edgar_research.duckdb (sauvegarde *.pre-merge-*.duckdb), régénère les parquet mono-fichier, puis build_factors.py

Shards (data/metadata/shards.json, proportionnels aux cœurs) : M3U96a 370, M2U64 318, M5M36 238, M4M64a 212, M4M64c 212, M4BP48 212, M4BP36 185, m4mm 159, m4mn 158 nouvelles sociétés ; M3U96b = les 500 S&P. Les fichiers bruts restent sur le nœud qui les a traités (provenance) ; seuls les parquet/embeddings remontent au maître.

Reprise : chaque étape est idempotente (python3 scripts/cluster/orchestrate.py status | downloads | ship | pipeline | pull | merge).

# SPID (project-db)

  • v_sections lit maintenant toutes les sections parsées (10-K incluses) ; un 10-K déjà ingéré par spid ingest n'est pas relu.
  • Périmètre par défaut SPID_SECTION_SCOPE="10-K:business,risk_factors,properties,md_and_a,quantitative_disclosures" (les 10-Q et 8-K passent entièrement, comme pour le S&P 500) ; ex. restreindre : …;10-Q:md_and_a,risk_factors.
  • Registre extracted_sections : python -m spid backfill-extracted (une fois, avant la fusion, pour inscrire le run S&P 500), python -m spid pending (sections restantes + coût estimé gpt-4o-mini), run_full_extract.py --workers 24 [--max-sections N].
Files · 1a08d92ceaeff41d875ca573b250d01906056494
Menu

Theme