Fundamental valuation software — predicts fair market capitalization from financial statements and identifies mispriced securities. Now live at mispriced.ch.
Mispriced answers the question: "Given these fundamentals, what should this company be worth?"
- Predicts fair market cap from balance sheet data (revenue, EBITDA, debt, cash flow, etc.)
- Identifies mispriced securities by comparing predicted vs. actual market caps
- Tracks mispricing across 12 global indices (S&P 500, NASDAQ 100, DAX, FTSE 100, etc.)
- Provides uncertainty estimates for every prediction
- Backtests signal quality across multiple time horizons
This is not a price forecaster. It's a financial audit tool that values companies cross-sectionally based on fundamentals.
What this system is:
- Fair value estimator from financial fundamentals
- Mispricing detector via cross-sectional comparison
- Index-level analyzer with uncertainty quantification
What this system is NOT:
- Time-series price predictor (no OHLCV modeling)
- Daily return forecaster (rebalances quarterly)
- Technical analysis tool (fundamentals only)
Key principles:
- Financial audit mindset — Predict what companies should be worth
- Uncertainty quantification — Every prediction has confidence intervals
- No data leakage — Out-of-fold predictions ensure fair evaluation
- Currency normalization — All values converted to USD
- Reproducibility — Model configs and data versions tracked
| Component | Choice |
|---|---|
| Algorithm | XGBoost (Gradient Boosted Trees) |
| Target | log(market_cap) for numerical stability |
| Validation | 10×5 Repeated K-Fold Cross-Validation |
| Predictions per stock | 50 (10 repeats × 5 folds) |
Each quarter is trained independently — the model only sees companies from that quarter. This is critical because:
- No future leakage — The model cannot learn from future quarters
- Market regime adaptation — Valuation multiples change over time (e.g., tech multiples were higher in 2021)
- Fair comparison — All companies are valued against their contemporaries, not against historical norms
The model uses repeated K-fold cross-validation to generate fair predictions with uncertainty:
For each of N repeats (N=10):
Split data into K folds (K=5)
For each fold:
Train XGBoost on K-1 folds
Predict on held-out fold
Final prediction for each ticker:
μ = mean of all out-of-fold predictions
σ = std of all out-of-fold predictions
This produces a distribution of predictions for each ticker, capturing model uncertainty without data leakage.
Relative error measures how much a security deviates from fair value:
Relative Error = (Predicted - Actual) / Actual
- Positive → Underpriced (model thinks it's worth more)
- Negative → Overpriced (model thinks it's worth less)
Raw mispricing exhibits a systematic size effect: smaller companies tend to show positive mispricing while larger companies show negative. The size-neutral mode corrects for this by fitting a curve to the mispricing vs. market cap relationship:
Size-Neutral Mispricing = Raw Mispricing - Size Premium(market_cap)
The model uses ~30 fundamental features:
| Category | Features |
|---|---|
| Income Statement | Revenue, Gross Profit, EBITDA, Operating Income, Net Income |
| Balance Sheet | Total Debt, Total Cash, Total Assets, Book Value, Working Capital |
| Cash Flow | Free Cash Flow, Operating Cash Flow, CapEx |
| Ratios | Profit Margins, ROA, ROE, Debt-to-Equity, Quick Ratio, Current Ratio |
| Other | Shares Outstanding, Float Shares, Insider/Institutional Holdings |
No price-derived features are used — only accounting fundamentals.
Given valuations for index constituents, compute aggregate index mispricing:
Index Mispricing = (Σ wᵢ × predictedᵢ - Σ wᵢ × actualᵢ) / Σ wᵢ × actualᵢ
Where weights (wᵢ) are market-cap based to match real index construction.
Assuming independent prediction errors:
σ_index = √(Σ wᵢ² × σᵢ²)
This provides a lower bound on index uncertainty.
| Region | Indices |
|---|---|
| US | S&P 500, NASDAQ 100, S&P 400 (Mid Cap), S&P 600 (Small Cap), Russell 1000 |
| Europe | DAX (Germany), FTSE 100 (UK), CAC 40 (France), SMI (Switzerland) |
| Asia | Nifty 50 (India), SSE 50 (China), Hang Seng (Hong Kong) |
All indices sourced from Wikipedia and mapped to yfinance ticker format.
# Install dependencies
pip install -r requirements.txt
# Run valuation pipeline (all quarters)
python scripts/run_quarterly_valuation.py
# Generate dashboard data
python scripts/generate_dashboard.py
# Build and serve web dashboard
cd web && npm install && npm run dev{
"ticker": "AAPL",
"predicted_mcap_mean": 2850000000000,
"predicted_mcap_std": 185000000000,
"actual_mcap": 2950000000000,
"relative_error": -0.034,
"model_version": "gbr_baseline_v3"
}Interpretation: Apple is overpriced by 3.4% relative to predicted fair value.
{
"index_id": "SP500",
"actual_mcap": 66787073988608,
"predicted_mcap": 63342789948551,
"index_mispricing": -0.052,
"n_tickers": 501
}Interpretation: S&P 500 is overpriced by 5.2% on aggregate.
The dashboard displays:
- Valuation Map: All stocks sized by market cap, colored by mispricing
- Sector/Index Charts: Aggregated mispricing by sector and index
- Time Series: Historical mispricing trends
- Signal Backtest: IC heatmaps and decay charts across horizons
- All Stocks Table: Searchable, sortable stock list with export
Built with Vite + TypeScript + Plotly.
mispriced/
├── src/
│ ├── ingestion/ # Fetch financial data from yfinance
│ ├── db/ # SQLite database models (SQLAlchemy)
│ ├── valuation/ # XGBoost prediction + feature engineering
│ └── evaluation/ # Repeated CV implementation
├── scripts/
│ ├── run_quarterly_valuation.py # Main valuation pipeline
│ └── generate_dashboard.py # Export data for web
├── web/ # Vite + TypeScript dashboard
│ ├── src/ # Frontend source
│ └── public/ # Static JSON data (quarters/*.json)
└── mispriced.db # SQLite database
yfinance API → ingestion → SQLite DB → valuation (XGBoost CV) → dashboard JSON → web
| Table | Purpose |
|---|---|
tickers |
Company metadata (sector, industry, currency) |
financial_snapshots |
Point-in-time balance sheet data (all USD) |
valuation_results |
Model predictions with uncertainty |
index_memberships |
Index constituent mappings |
- Financial Data: yfinance
- FX Rates: exchangerate.host
- Index Constituents: Wikipedia (automated scraping)
- Model: XGBoost
- Database: SQLite + SQLAlchemy
- Validation: scikit-learn cross-validation
- Visualization: Plotly.js
- Frontend: Vite + TypeScript
This project is licensed under the GNU General Public License v3.0 — see LICENSE for details.
