Status date: 2026-07-16
Test whether topic state can become an additional input to the existing residual-rank ML pipeline without reopening the failed v4 confirmation. This stage implements and audits features only; it does not claim that topics improve forecasting or portfolio performance.
- Text source: FNSPID development archive.
- Text dates: 2018-01-01 through 2023-12-31.
- Backend: NMF with 10 topics.
- Vectorization: TF-IDF, unigrams and bigrams, maximum 5,000 terms.
- Refit: monthly.
- Training window: trailing 730 days, strictly before each scoring month.
- Minimum unique training documents: 200.
- Ticker names are masked and exact duplicate training texts are removed.
- The observed 2024 through 2025 Q1 confirmation is not loaded or rescored.
Configuration: configs/experiments/topic_features_v5_development.yaml.
TopicFeatureBuilder fits each topic model only on prior text. NMF components are aligned to the
previous refit using maximum cosine matching so the ten topic-share columns have a more stable
meaning through time. Every refit persists top words, representative headlines, topic diversity,
and alignment quality.
The raw ticker-date feature panel contains:
- ten aligned topic-share features;
- topic concentration;
- normalized topic entropy;
- semantic novelty versus current topic components;
- topic diversity;
- topic volume;
- topic sentiment dispersion.
Weekend text rolls to the next business day. All topic features then receive the same additional one-business-day feature-store lag as the existing sentiment variables.
The residual-rank integration cross-sectionally ranks topic values only among genuinely covered
ticker-dates. Uncovered stocks remain neutral zero and receive an explicit
nlp_topic_coverage flag. This prevents missing news from becoming an artificial negative signal.
Before inspecting any forecast or return result, the continuation gate was frozen at at least 50% of eligible ticker-date rows covered after the required lag and at least 200 eligible securities ever covered. The existing 5.7 GB FNSPID archive was already cached, so all 552 tickers in the available point-in-time membership file were imported without another network download.
| Item | Result |
|---|---|
| Raw FNSPID rows available | 235,993 |
| Point-in-time monthly refits completed | 71 |
| Raw topic feature columns | 16 |
| Topic-augmented residual-rank columns | 75 total, 17 topic-related |
| Ticker-date topic panel rows | 698,644 |
| Active raw ticker-date rows | 97,513 |
| Raw ticker identifiers covered | 449 |
| Eligible residual-rank rows with topic coverage | 52,588 |
| Eligible residual-rank coverage | 13.95% |
| Eligible tickers ever covered | 313 |
| Uncovered rows receiving nonzero topic signals | 0 |
| Median cross-refit topic alignment cosine | 0.905 |
| Minimum cross-refit topic alignment cosine | 0.578 |
| Median top-word diversity | 0.91 |
| 50% eligible-row coverage gate | Failed |
The latest refit produced recognizable themes around broad markets, sector updates, Buffett, Wall Street and monetary policy, growth stocks, Tesla/EVs, artificial intelligence, earnings, options, and large technology stocks.
NMF K=10 completed the full 71-refit monthly contract in 6.2 minutes. A matched count-based LDA adapter was implemented and passed the same point-in-time, normalization, and leakage tests. However, batch LDA with 10 passes and bounded online LDA with 5 passes both exceeded a fixed 15-minute full-run ceiling and produced no completed artifact. The code retains online LDA as a transparent optional baseline, but it is not operationally competitive with NMF for this monthly rolling corpus.
The remaining topic-count grid, BERTopic, topic/no-topic nested ML A/B, and timestamp-shuffled placebo were not run. This was a pre-result gate decision: exact-date coverage failed 50% even though the number-of-securities gate passed.
The engineering contract scales from 9 to hundreds of names, keeps uncovered signals neutral, and produces stable, interpretable NMF themes. Historical import scope caused the original 1.18% coverage result. Even after correcting it, however, 86.05% of eligible rows have no exact-date topic event. Running model selection or a return comparison at this coverage would primarily tune missing-news handling rather than test whether topic state improves the broad selector.
Therefore this stage passes as an expanded implementation baseline but is not eligible for an ML or portfolio-uplift claim.
- Add another point-in-time ticker-linked historical source or a defensible as-of text mapping; do not manufacture coverage by treating missing news as negative sentiment.
- Define event-driven and slow-state contracts separately. Exact timestamps are required for the tactical arm; trailing weekly state may use date-only text with an explicit age feature.
- Re-run the frozen 50% eligible-row and 200-name coverage gate before model selection.
- Only after it passes, evaluate BERTopic, topic/no-topic nested ML, and timestamp-shuffled controls.
- Report incremental rank IC and net information ratio; do not reuse v4 confirmation as untouched evidence.
python -m src.cli build-topic-features \
--experiment-config configs/experiments/topic_features_v5_development.yaml \
--posts-file data/raw/nlp_posts.parquet \
--source fnspid --start 2018-01-01 --end 2024-01-01 \
--output data/processed/topic_v5_development/topic_features.parquet \
--audit-output data/processed/topic_v5_development/topic_model_audit.json \
--standalone
python -m src.cli augment-residual-rank-topics \
--dataset data/processed/residual_rank_v2/features.parquet \
--topic-features data/processed/topic_v5_development/topic_features.parquet \
--output data/processed/topic_v5_development/residual_rank_features_with_topics.parquet