Uris001/equity-research-platform
<div align="center">
<br>
<img src="https://readme-typing-svg.demolab.com?font=Fira+Code&weight=700&size=27&pause=1000&color=60A5FA¢er=true&vCenter=true&width=760&lines=%E2%9A%A1+AI-Powered+Equity+Research+Platform;Score+any+public+ticker+in+seconds;Live+Quant+%2B+OOS+Stat-Arb+%2B+Volatility+Cones" alt="Typing SVG" />
<br><br>
      [](LICENSE) [](https://huggingface.co/spaces/Uris001/equity-research-platform)
<br>
Type any ticker โ S&P 500, Nasdaq, or global โ and get a full quant deep-dive in seconds: a transparent 0โ100 scorecard, an AI-written analyst note, two peer-discovery engines, a look-ahead-free pairs stat-arb backtest, and a 21-day volatility cone โ all from live market data.
<br>
Built around one principle: every number on screen is either real, validated, or explicitly labelled as neither.
<br>
</div>
๐ฏ The Problem & The Solution
A junior equity analyst spends hours per stock pulling prices, computing technical indicators, reading fundamentals, scanning sentiment, writing up a note, and finding comparable companies. That workflow is repetitive, slow, and inconsistent across analysts.
This project automates the entire loop with a data-science pipeline that spans every stage of the course โ synthetic data generation, EDA, embedding-based recommendation, LLM text generation, a trained sentiment classifier, and a live application โ tied together into one coherent product:
What makes this version different: the platform is engineered to be defensible, not just impressive. The pairs backtest is look-ahead-free by construction and unit-tested for it; the return-prediction experiment that scored negative out-of-sample Rยฒ is disclosed on the card instead of dressed up as a forecast; and every tab carries a data-provenance badge (๐ก LIVE ยท ๐งช SYNTHETIC ยท ๐ค MODEL) so no synthetic number can masquerade as a real one.
๐ Table of Contents
๐บ End-to-End Workflow
flowchart TD
A["๐งช PART 1 โ Synthetic Data Generation<br/>Qwen2.5-1.5B-Instruct ยท 200 tickers ร 10 sectors<br/>ร event/technique mix ยท leakage-controlled = 10,000 notes"] --> B
B["๐ PART 2 โ Exploratory Data Analysis<br/>Quality audit ยท IQR outliers ยท 7 research questions<br/>feature engineering ยท text-vs-quant separability"] --> C
C["๐งน Quality Gate<br/>drop short / low-diversity / failed generations<br/>โ clean modeling corpus"] --> D
C --> T
D["๐ง PART 3 โ Embedding Benchmark<br/>MiniLM-L6-v2 ยท MPNet-base-v2 ยท BGE-small-en-v1.5<br/>(+ Tabular-PCA quant baseline) ยท intra-sim / silhouette / Davies-Bouldin"] --> E
E["๐ Winner: BAAI/bge-small-en-v1.5<br/>recorded in winner.json ยท FAISS IndexFlatIP (cosine)<br/>ticker & natural-language recommendation"] --> F
T["๐ข Quant feature matrix<br/>synthetic fundamentals โ winsorized z-scores<br/>SECTOR-NEUTRAL Value/Quality/Momentum factors"] --> H
F["โ๏ธ PART 4 โ Analyst-Note Generation<br/>Mistral-7B-Instruct-v0.2 ยท few-shot prompting<br/>ROUGE-L ยท BERTScore ยท Flesch ยท semantic sim ยท hallucination proxy"] --> H
P["๐ฎ PART 6 โ Return-Model Evaluation<br/>real yfinance features ยท temporal split<br/>VERDICT: OOS Rยฒ < 0 โ ship the VOLATILITY CONE instead"] --> H
Q["๐ Pairs Stat-Arb Engine<br/>formation/trading split ยท EngleโGranger screen<br/>causal rolling z ยท next-bar execution ยท bootstrap CI"] --> H
H["๐ PART 5 โ HuggingFace Space (Gradio 6)<br/>modular architecture ยท 26 unit tests ยท provenance badges<br/>6 tabs ยท zero runtime training ยท dark quant UI"]
style A fill:#e0e7ff,stroke:#4338ca,color:#1e1b4b
style B fill:#dcfce7,stroke:#16a34a,color:#052e16
style C fill:#fef3c7,stroke:#d97706,color:#451a03
style T fill:#fff7ed,stroke:#ea580c,color:#431407
style D fill:#e0f2fe,stroke:#0284c7,color:#0c4a6e
style E fill:#fce7f3,stroke:#db2777,color:#500724
style F fill:#f3e8ff,stroke:#9333ea,color:#2e1065
style P fill:#dbeafe,stroke:#2563eb,color:#0c1d57
style Q fill:#cffafe,stroke:#0891b2,color:#083344
style H fill:#e0f2fe,stroke:#0369a1,color:#0c4a6eTwo AI engines, one platform. A recommendation engine (embeddings + FAISS) finds similar stocks; a generation engine (Mistral-7B) writes the analyst note. Both are wrapped by a deterministic, finance-aware scorecard, extended by a look-ahead-free stat-arb backtester and an empirical volatility cone โ and everything that touches synthetic data says so on screen.
๐ How the Parts Connect
Nothing is an isolated exercise; every part is a dependency of the next.
- Part 1 โ Part 2. The synthetic generator produces ~10,000 notes across 200 tickers. Because the generator is a small instruction model, raw outputs need auditing โ so Part 2 (EDA) is the gate that measures quality and removes noise before modeling.
- Part 2 โ Part 3. The EDA defines the quality filters and engineers the tabular financial features that feed the factor model and the trained sentiment classifier.
- Part 3 โ Part 4 โ App. Part 3 benchmarks three text-embedding models (MiniLM, MPNet, BGE-small) against a quant-PCA baseline, crowns BGE-small-en-v1.5 the measured winner โ recorded in
winner.json, which the app reads at startup so the deployed encoder can never drift from the declared winner โ and builds the FAISS index the app reuses for narrative peer discovery. Part 4 validates the few-shot prompting strategy for Mistral-7B that the app reuses to write notes. Parts 3โ4 are the R&D; the app is the production deployment of their findings.
- Honesty as an architectural rule. Two return-prediction experiments were run โ one on synthetic fundamentals (Part 3 ยง11), one on real market data (Part 6). Both scored out-of-sample Rยฒ โ 0 or below, and both are shelved, with the negative results documented in the app itself. The forward-looking views the platform does ship are the ones that survive scrutiny: the factor ranking (transparent methodology), the OOS pairs backtest (look-ahead-free), and the volatility cone (measured uncertainty).
Part 1 (generate) โโถ Part 2 (clean + engineer features)
โ โ
โผ โผ
text notes (โ10k) tabular features
โ โ
โผ โโโโโโโโโโโโดโโโโโโโโโโโโโโโโ
Part 3 (BGE + FAISS) โผ โผ
โ Value/Quality/Momentum sentiment classifier
โ (sector-neutral z-scores) (the trained deliverable)
โ โ โ
โ โ ยง11 + Part 6 return predictors
โ โ โ BOTH SHELVED (OOS Rยฒ โค 0),
โ โ disclosed in-app
โโโโโโโโโโฌโโโโโโโโดโโโโโโโโโโโฌโโโโโโโโโโโโโโโโ
โผ โผ
Part 4 (Mistral few-shot) + pairs stat-arb engine + volatility cone
โผ
Part 5 โ Gradio App (this Space, 6 tabs, 26 unit tests)๐ฆ Upstream โ the Dataset (Parts 1 & 2)
This app is built on `Uris001/equity-research-dataset` โ ~10,000 analyst notes across 200 tickers and 10 sectors, generated with an instruction-tuned LLM under a leakage-controlled design, then audited and cleaned via a full EDA (quality, outliers, 7 research questions). The generation process and the EDA are documented in the [dataset card](https://huggingface.co/datasets/Uris001/equity-research-dataset).
One rule follows from this everywhere in the app: the corpus fundamentals are synthetic, so any panel built on them carries a ๐งช SYNTHETIC provenance badge, and no synthetic quantity is ever blended into a live-data number.
๐ง Part 3 โ Recommendation, Embeddings & the Quant Layer
๐ Notebook: 03_embeddings_recommendation.ipynbThe goal: turn each stock into a vector, so "find me similar stocks" becomes a geometry problem โ then extend the same feature work into a systematic factor model and a trained classifier.
3.1 โ Three-model embedding benchmark
Full-document coverage via chunk + mean-pool (naรฏve encode() silently truncates at ~250 words), and a composite quality metric (intra-sector cosine + silhouette + inverted Davies-Bouldin):
๐ `BAAI/bge-small-en-v1.5` wins the measured composite (intra-sector cosine + silhouette + inverted Davies-Bouldin) and is recorded in winner.json โ the app reads that file at startup. Winning vectors are L2-normalised and indexed with FAISS `IndexFlatIP` (inner product = cosine on unit vectors) for sub-millisecond search.3.1b โ Does the recommender actually recommend? (measured, not asserted)
The deployed index was validated two ways โ the sector label is never an input (the engine sees only note text), so recovering sector structure is genuine validation, not circularity:
Domain spot checks (pre-stated expectations, then inspected output):
Universe-wide metric โ querying every one of the 200 tickers and scoring its top-5 neighbours:
Sector precision@5 = 0.62 vs a โ 0.14 random-sector prior โ a same-sector peer is ~4.4ร more likely than chance. Strongest where the language is distinctive (Energy 0.90, Healthcare 0.72, Technology 0.72); weakest for Materials (0.07) and Communication Services (0.03) โ both have only 6 names in the universe (few same-sector candidates exist), and their notes read like adjacent sectors (GOOGL/META are labelled Communication Services but discussed as tech). That failure mode is the embedding faithfully reflecting the notes' language colliding with a rigid sector taxonomy โ disclosed here rather than hidden. KMeans + DBSCAN independently recover the sector taxonomy and flag the pre-profit Space/Quantum names as density outliers โ an unsupervised confirmation of the EDA.
3.2 โ Cross-Sectional Factor Model (the Universe Screener)
Every ticker is scored on three classic factors (Fama-French / AQR lineage) via winsorised (1/99%), sign-adjusted, cross-sectional z-scores:
New in this version โ sector-neutral scoring (default). Global z-scores make the Value factor a hidden sector bet (short tech, long energy). The screener now standardizes each input within its sector (with a global fallback for tiny groups), so "cheap" means cheap versus its own sector. The effect is unit-tested: on a synthetic universe with deliberately different sector valuation levels, sector-mean Value spread drops from >0.5ฯ to <0.15ฯ.
3.3 โ Return predictor on synthetic fundamentals (ยง11 โ shelved)
Pre-registered ship rule: deploy only if OOS Rยฒ โฅ 0.05. Verdict: shelved โ synthetic fundamentals carry no real forward signal (OOS Rยฒ โ 0 or below). In this version the shelving is complete: no synthetic-data return model is loaded anywhere in the app โ no hidden "ML estimate" panels.
3.4 โ Sentiment classifier (ยง12 โ the trained deliverable)
A multimodal bake-off (TF-IDF + SVD text channel โ engineered features โ standardized fundamentals) across four model families under one shared CV split, scored on macro-F1:
๐ Winner: Logistic Regression โ test macro-F1 = 0.618 (chance = 0.333), beating the tree ensembles and single-channel baselines. It runs live in the Document Research tab with its provenance disclosed on-screen: it was trained on this project's synthetic corpus, so agreement with FinBERT is a consistency check between models, not independent validation on real market text.
โ๏ธ Part 4 โ Analyst-Note Generation
๐ Notebook: 04_generation.ipynbBenchmarks the app's runtime generator โ `mistralai/Mistral-7B-Instruct-v0.2` โ across zero-shot / one-shot / few-shot prompting on a stratified test set, with a five-metric evaluation suite:
Few-shot wins the composite, and that exact strategy ships in the app. In production the model is served via the HF Inference API (no 7B weights in the Space) behind a two-tier fallback: Mistral-7B โ deterministic template (Mistral is the only LLM) โ the note box is never empty, even without an HF_TOKEN, on a rate limit, or during a cold start โ and every note is labelled with the tier that actually wrote it, so a template can never masquerade as an LLM.
๐ฏ The Heuristic Scorecard
Every ticker gets a transparent 0โ100 score. It is deliberately presented as what it is โ a documented rule-based rubric, not a fitted or validated predictive model โ and its bands are named accordingly (no implied trading calls):
Composite = 0.40 ยท Technical + 0.40 ยท Fundamental + 0.20 ยท Market Signal
Technical = 0.30ยทRSI + 0.30ยทMACD + 0.15ยทBB%B + 0.15ยทMomentum + 0.10ยทVolume
Fundamental โ profitable : 0.30ยทROE + 0.25ยทNetMargin + 0.25ยทRevGrowth + 0.20ยทP/E
โ pre-profit : 0.55ยทRevGrowth + 0.30ยทP/S + 0.15ยทMarketCap
Market Signal = analyst target-price premium + recommendation mean
(3-month momentum ONLY as a disclosed fallback when there is
no analyst coverage โ it is already inside Technical, so
blending it unconditionally would double-count it)Pre-profit awareness is the key design choice โ loss-making Space/Quantum names have structurally NaN profitability metrics, so pushing them through a P/E-based rubric would be nonsense. The platform routes them through a revenue-growth + price-to-sales + market-cap track instead. Both tracks are unit-tested to stay within [0, 100] even on all-NaN input.๐ฅ Part 5 โ The Live Application
๐ Entry point: app.py โ this Hugging Face Space (modular architecture, see Engineering & Tests)A six-tab Gradio app with a dark quant UI, reading the dataset and models directly from the Hub at runtime. Every tab carries a provenance badge.
Each tab ships one-click Quick Starters (e.g. AAPL, TSLA, IONQ).
๐ Pairs Trading โ a Look-Ahead-Free Backtest
The single most common flaw in amateur stat-arb backtests is look-ahead bias: computing the z-score from full-sample statistics, so the entry signal at time t "knows" the future mean it will revert to. This platform's backtest is engineered โ and unit-tested โ to be free of it:
โโโโโโโโโโโโโโโ FORMATION (60%) โโโโโโโโโโโโโโโคโโโโโโโโโโ TRADING (40%) โโโโโโโโโโค
pair selection (EngleโGranger ADF on spread) the ONLY window that is reported
hedge ratio ฮฒ (OLS on log prices) trailing 60-day z-score, shifted
OU half-life one bar โ strictly causal
positions execute NEXT bar
P&L net of costs (10 bps/side)- Selection without peeking โ the hedge leg is chosen among FAISS-suggested peers by formation-window correlation + cointegration only. A test proves that corrupting the trading window cannot change any formation metric.
- Causal signals โ
z_tuses the mean/std of the previous 60 bars (window shifted by one). A test proves that perturbing future prices cannot change past z-scores, and that a signal formed on the final bar earns exactly zero (next-bar execution). - Honest statistics โ OOS Sharpe ships with a bootstrap 90% confidence interval; pairs that fail the EngleโGranger 5% critical value (โ3.34) are labelled NO TRADE and their stats greyed out; low trade counts and weak correlations trigger explicit low-confidence banners.
- Validated math โ the pure-numpy ADF t-statistic matches
statsmodels.adfullerto 1e-6 in tests.
Example of the honesty working as designed: on a recent KO/PEP run the formation ADF was โ2.31 (fails the โ3.34 bar) โ the app shows NO TRADE, and the OOS Sharpe's CI of [โ0.68, +3.07] straddles zero โ which is exactly what an unvalidated pair should look like.
๐ฎ Part 6 โ From Return Model to Volatility Cone
๐ Notebook:price_predictor.ipynbยท Artifact:price_scenario_model.joblib
Part 6 asks: can a model predict the 21-day forward return from real market data? A 186-ticker, ~27k-row weekly panel of strictly-trailing technical features + fundamentals, a temporal split (train โค 12 months ago, test = last 12 months, no shuffle), three regressors, and a pre-registered ship rule (deploy if OOS Rยฒ โฅ 0.05):
โ๏ธ Verdict: no model clears the bar โ so no point forecast ships. Publishing bull/bear price targets from a negative-Rยฒ model would be decorative complexity. Instead, the ๐ฎ tab shows the defensible object:
The volatility cone. The ticker's own realized daily volatility (trailing 252d), scaled by โt under a zero-drift lognormal model:
upper_t = Pโ ยท exp(+1.5ยทฯยทโt) 21-day band โ ยฑ11% for a defensive name (KO)
lower_t = Pโ ยท exp(โ1.5ยทฯยทโt) โ ยฑ19% for a high-beta name (TSLA)The card states plainly why there is no expected-return line โ "a Ridge regression was evaluated for this horizon and scored OOS Rยฒ = โ0.03" โ turning a negative result into demonstrated methodology. Knowing what doesn't predict is part of the analysis.
๐งช Engineering & Tests
The app is a modular package (one concern per module) rather than a single-file script:
What the tests pin down (tests/test_logic.py):
Robustness & degraded modes: no HF_TOKEN โ deterministic template (note box never empty) ยท Yahoo blocked โ Finnhub โ history-derived cards ยท FAISS artifacts missing โ narrative peers disabled gracefully ยท long documents โ token-window chunking + a sentence cap with an on-screen notice ยท the ~40-fetch live peer index builds in a background thread at startup so the first click is fast. Secrets are read from the environment only and never logged. No GPU anywhere โ free CPU tier.
๐ How to Use the App
Live Space: **huggingface.co/spaces/Uris001/equity-research-platform**
Tab 1 โ ๐ Analyze Any Stock
Type a ticker (AAPL, NVDA, IONQ, โฆ) โ Analyze โ scorecard + band, technical/fundamental breakdown, price chart, AI note, and the two peer tables.
Tab 2 โ ๐ Universe & Factor Screener
Drag the Value / Quality / Momentum sliders (updates live), toggle sector-neutral, filter sectors, then ๐ฌ Deep-Dive #1 Pick to pull a full live dossier on the top name.
Tab 3 โ ๐ Analyst Terminal
Type a ticker โ Pull Full Dossier โ ~60 metrics + chart + live news on one screen.
Tab 4 โ ๐ง Document Research
Paste text or upload a .txt/.pdf โ Analyze Document โ dual-model sentiment with agreement read, summary, key figures & event signals, sentence-level evidence, AI research brief.
Tab 5 โ ๐ Pairs Trading
Enter a ticker (optionally a hedge leg) โ Run Stat-Arb โ cointegration screen, causal signal, and the out-of-sample backtest with its bootstrap CI. Try KO/PEP, GS/MS, XOM/CVX.
Tab 6 โ ๐ฎ Price Scenarios
Type a ticker โ Project Cone โ the 21-day โt volatility cone, terminal band levels, and the disclosed return-model evaluation.
Tip: generation needs an HF_TOKEN Space secret to call Mistral. Without it the app still runs end-to-end (deterministic template note, clearly labelled).๐ Tech Stack
โ ๏ธ Limitations & Disclaimer
- Not financial advice. This is an educational data-science project. Do not trade on its output.
- The scorecard is a heuristic. Its weights and thresholds are documented rules, not fitted parameters โ it is labelled on-screen as "not a validated predictive signal," and its bands deliberately avoid buy/sell language.
- The factor screener's inputs are synthetic (the project corpus), disclosed on the tab. The ranking methodology โ winsorized, sector-neutral cross-sectional z-scores โ is the deliverable; the deep-dive pulls real live data.
- The pairs backtest is honest but still simplified โ no slippage or borrow costs, IID bootstrap (ignores autocorrelation), single fixed formation split. All stated in the UI caveats.
- Near-term return prediction failed our ship rule twice (synthetic ยง11 and real-data Part 6, both OOS Rยฒ โค 0) โ which is why the app shows uncertainty cones, factor ranks and cointegration screens instead of price forecasts.
- ETFs & non-equities lack per-company fundamentals โ their fundamental sub-score falls back to neutral defaults and is indicative only.
๐ค Author
<table> <tr> <td> <b>Uri Sivan</b><br> Introduction to Data Science ยท Reichman University<br> Final Project โ Synthetic Data ยท EDA ยท Embeddings ยท Generation ยท Classifier ยท Stat-Arb ยท HuggingFace Spaces<br><br> <a href="https://huggingface.co/Uris001">๐ค HuggingFace</a> ยท <a href="https://huggingface.co/spaces/Uris001/equity-research-platform">๐ Live Space</a> ยท <a href="https://huggingface.co/datasets/Uris001/equity-research-dataset">๐ฆ Dataset</a> </td> </tr> </table>
<div align="center">
<br>
If you found this useful, consider leaving a โญ on the Space!
<br>
Built with โค๏ธ using
๐ค HuggingFace ยท ๐ yfinance ยท ๐ง BGE-small ยท ๐ค Mistral-7B ยท ๐ FAISS ยท ๐ฉ Gradio
<br>
<br>
</div>
