Skip to content

Repository files navigation

PairBook

DOI

Correlation, covariance, beta, volatility and true holdings overlap for 4,700+ US-listed stocks and ETFs, recomputed every trading day and published as a static site with a free JSON API.

Live site: https://www.pairbook.io CSV download: https://www.pairbook.io/data/ API documentation: https://www.pairbook.io/api/ OpenAPI 3.1 spec: https://www.pairbook.io/api/openapi.json

Download the data

The full dataset is two CSV files, rebuilt after every US close:

File Rows Contents
pairs.csv 52,358 Correlations over 1, 3 and 5 years, annualized covariance, holdings overlap
symbols.csv 4,755 Beta against SPY, annualized volatility, trailing returns, sector or fund group
import pandas as pd

pairs = pd.read_csv("https://www.pairbook.io/data/pairs.csv")

Licensed CC BY 4.0. An archived, citable snapshot carries a DOI: 10.5281/zenodo.22124847, also mirrored on Kaggle and Hugging Face.

What it does

Two funds can move together and also hold the same stocks. PairBook measures both sides of that question:

  • Pearson correlation of weekly returns over 1, 3 and 5 year windows, for 52,000+ tracked asset pairs, with a rolling one-year correlation series.
  • Annualized covariance and volatility, and beta against SPY.
  • True holdings overlap between ETFs, computed as the sum of minimum common weights from issuer portfolio disclosures.
  • A live in-browser calculator for any of the 11 million possible pairs, built on the published weekly return series.

API quickstart

No key, no signup, CORS enabled. Attribution is the only ask.

curl https://www.pairbook.io/api/v1/pairs/aapl-vs-msft.json
Endpoint Returns
GET /api/v1/index.json Universe: every symbol with name, kind and canonical slug
GET /api/v1/symbols/{slug}.json One asset: beta, volatility, returns, correlated partners, diversifiers
GET /api/v1/pairs/{a}-vs-{b}.json One pair: correlations, covariance, holdings overlap
GET /api/v1/series/{slug}.json Weekly return series (up to 156 weeks)
GET /api/v1/badge/{a}-vs-{b}.svg Embeddable SVG badge with the pair's 3-year correlation

Architecture

A Python pipeline renders roughly 57,000 static pages plus the API files. No server, no database at runtime.

pipeline/fetch_prices.py     5y of daily adjusted closes, cached per symbol
pipeline/fetch_holdings.py   ETF portfolio files from issuer disclosures
pipeline/compute.py          correlations, betas, overlaps, pair selection
pipeline/build.py            renders site/ (pages, API, sitemaps, robots)
pipeline/validate.py         post-build QA gate (structured data, titles, links)
pipeline/indexnow.py         notifies IndexNow-compatible search engines

Run everything locally:

python3 -m venv .venv
./.venv/bin/pip install numpy pandas jinja2 requests openpyxl pyarrow markupsafe curl_cffi
./update.sh

Automated daily refresh

.github/workflows/daily-refresh.yml runs the full pipeline after the US close on trading days and deploys the result. The price cache persists between runs; the core universe refreshes daily and the long tail rotates across the week to keep request volumes reasonable.

Deployments go through the CLI and CI only. Git auto-deploys are disabled on purpose (vercel.json at the repository root).

Data quality

  • Assets need roughly ten months of price history before they are published, so figures are never computed on a handful of observations.
  • Series with stale prices or sub-dollar median prices are excluded from partner lists and rankings to avoid phantom correlations.
  • Company names from exchange symbol directories are sanitized at load time.
  • A validation pass runs after every build and fails the deploy on structured data errors, malformed titles or missing canonicals.

Content integrity rules (do not break these)

  1. A date bump requires a real content change. lastmod is gated on a content hash (data/lastmod.json); never hand-edit dates and never bypass the gate.
  2. Hidden text is never a tactic. Nothing is served to crawlers that a reader does not see.
  3. Generated prose keeps its variant system (vary() in pipeline/build.py), seeded by ticker and never by date, so wording differs across pages without churning between builds.
  4. No hardcoded figures in templates; every number comes from the dataset.
  5. Sitemap exposure is phased (sitemap_expose in config.json): extended segments only enter the index once the front line indexes healthily.
  6. data/lastmod.json is CI-owned state. Local builds regenerate it against stale data; never commit it from a workstation. The nightly workflow is the only writer that persists it.

Data sources and terms

End-of-day market data comes from public quote endpoints; fund portfolios come from issuer disclosures; the universe comes from official exchange symbol directories. The site publishes derived metrics only and does not redistribute raw source tables. Everything on the site and the core API is free, with attribution. Nothing here is investment advice.

About

Correlation, covariance and true holdings overlap for 4,700+ US stocks and ETFs. Free JSON API, refreshed every trading day.

Topics

Resources

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages