Equity Fingerprint
Reads a Swiss stock's sector, size, volatility, beta and liquidity from its daily price and volume alone.
Overview
Equity Fingerprint asks how much of a company's identity is already written in its price and volume history. The universe is the SIX Swiss Exchange primary listings: 231 tickers, of which 207 had at least 252 prior trading rows and enter the experiments. Daily prices run from 2010-01-04 to 2026-10-07, with 28,016 month end observations from 2014-03. Five targets are read back from that history: the next month's volatility, the next year's beta against the SPI, the next month's traded value, the current market cap, and the sector.
How the data is split
Forward targets train to 2021-12-31, validate on 2022 to 2023 and test from 2024-01-01; for vol_1m that is 16,326, 4,326 and 6,473 observations. Stock targets (sector, log_mcap) use issuer grouped 5 fold cross validation with 10 repeats on 203 and 204 stocks.
Time split for the forward targets, folds for the stock targets. Counts from the committed results files.
What I built
- A SIX universe builder that keeps primary listings quoted in CHF and drops tickers with too little history.
- A feature set of 39 engineered features per stock and month end (returns, Parkinson and Garman Klass volatility, drawdown, beta and correlation to the SPI, Amihud illiquidity, traded value, return principal components), set against 4 raw ones.
- Ridge, logistic regression and LightGBM baselines, then kNN on market adjusted return correlations for the sector.
- A Transformer encoder pretrained on 4,314 non Swiss stocks (masked patch modelling and contrastive views, Kaggle GPU) and tested as a feature extractor.
- Ten rounds of add ons, each read against a named reference with stock bootstrap 95% intervals: MiniRocket kernels, Welch and Kalman betas, a longer Swiss history from 2011 and European reference stocks.
- A protocol where features only see data up to the feature date, share lines of one company stay in one fold, and 226 unit tests guard it. Every number in the README comes from a committed results file.
Results
Static targets get one row per stock and are scored with repeated issuer grouped 5 fold cross validation. Forward targets are scored on the 2024 test period and read against a persistence model that only knows the same quantity measured one period earlier. The table below holds the headline numbers.
| Target | Model | Metric | Value | 95% interval or reference |
|---|---|---|---|---|
| sector | knn_auto | macro F1 | 0.578 | [0.505, 0.631], 203 stocks |
| log_mcap | ridge_md_ext | R² | 0.922 | [0.896, 0.940], 204 stocks |
| vol_1m | lgbm_fefpfa | R² | 0.630 | persistence 0.426 |
| beta_1y | fpnet_a | R² | 0.630 | persistence 0.522 |
| beta_1y | avg_long2011 | R² | 0.650 | persistence 0.522; +0.016 [+0.006, +0.027] with history from 2011 |
| liquidity_1m | lgbm_fefpfa | R² | 0.957 | persistence 0.949 |
What can be read back
Size and liquidity are the easiest, volatility and beta are the ones where a model clearly beats yesterday's value, and the sector is the hardest of the five.
What can be read back
Sector macro F1 0.578 on 203 stocks, market cap R² 0.922 on 204 stocks. Forward R² on the 2024 test period: vol_1m 0.630 against 0.426 for persistence, beta_1y 0.630 (0.650 with history from 2011) against 0.522, liquidity_1m 0.957 against 0.949.
Mean over repeats with 95% stock bootstrap intervals for the stock targets; test R² for the forward targets. Source: assets/equity-fingerprint/evidence.json.
Where the sector signal lives
Stocks of one sector tend to move together once the market move is taken out. The map places the 203 stocks by that shared movement; pick a stock to see its nearest neighbours. The matrix below shows which sectors the model mixes up.
Stocks placed by how their returns move together
203 Swiss stocks laid out by the similarity of their daily returns and coloured by sector. Nearest neighbours tend to share a sector, which is why the kNN model reaches macro F1 0.578.
New computation for this page: t-SNE layout of Spearman correlations of daily returns after removing the first principal component (the matrix the sector model chose most often). Positions are a layout aid; only near neighbours carry meaning.
Where the sector model is wrong
Real Estate (82.2% of predictions right), Healthcare (79.7%) and Financial Services (77.3%) are read best. Consumer Cyclical (26.4%), Basic Materials (31.1%) and Communication Services (37.1%, 7 stocks) are read worst.
Rows are the true sector, columns the predicted one, from knn_auto summed over 10 repeats of issuer grouped 5 fold cross validation.
Size from price and volume
Traded value carries most of the size signal. Adding several past month ends per stock lifts R² from 0.891 to 0.922.
Market cap from price and volume
Predicted against true market cap for 204 stocks: R² 0.922 across repeats, 0.925 for the mean of the 10 out of fold predictions.
Each point averages the stock's 10 out of fold predictions (ridge_md_ext, issuer grouped folds); the headline R² 0.922 is the mean of the per repeat R².
What the features add
Engineered features beat the 4 raw ones for most targets, and a few add ons moved the score beyond noise. A pretrained encoder did not.
Raw prices against engineered features
With a linear model the 39 engineered features beat the 4 raw ones for sector (macro F1 0.104 to 0.313), vol_1m (0.490 to 0.609), beta_1y (0.429 to 0.632) and liquidity_1m (0.933 to 0.950), but not for log_mcap (0.808 to 0.762).
Single test split. The log_mcap drop is within seed noise: over five ticker splits the linear gap averages +0.017 (sd 0.037).
What the tree model leans on
Top feature by gain share: Volatility 63d for vol_1m (23.7%), Beta vs SPI 252d for beta_1y (50.4%), Log traded value 63d for liquidity_1m (46.7%), Amihud illiquidity 63d for log_mcap (56.5%), Log close for sector (11.3%).
LightGBM (lgbm_fe) gain share on the engineered features, v1 benchmark.
What helped and what did not
Of 15 add on results, 5 helped (interval above zero), 5 are within noise and 5 are single split v4 arms without an interval. None hurt.
Change in the metric against the named reference, with 95% intervals. Source files are listed per row.
The same kind of kNN scores macro F1 0.766 on 850 European stocks against 0.579 on the Swiss ones, which fits the small number of Swiss stocks limiting sector accuracy.
Limits
- The universe is today's listing only, so delisted companies are missing and the results carry survivorship.
- Sector and share count are single snapshots taken at download, not histories.
- yfinance is an unofficial source and the cleaning is simple.
- The same test rows were read across ten rounds, so later test numbers are a second look, not a fresh test.