A static public website for the Reality Gap (RG) research project.
Reality Gap is a heuristic indicator measuring whether a company's market capitalisation is approximately covered by its estimated fundamental base (derived from smoothed earnings and tangible equity). This site presents the methodology, working paper, and an initial set of illustrative company data.
Live site: https://hstre.github.io/Reality-Gap/
| Component | Choice |
|---|---|
| Framework | Astro v4 (static output) |
| Styling | Tailwind CSS v3 |
| Language | TypeScript |
| Data | Local JSON files |
| Hosting | GitHub Pages |
| i18n | Manual (English + German) |
No backend. No database. No authentication. No live API calls.
- Node.js 18 or higher
- npm
git clone https://github.com/hstre/reality-gap.git
cd reality-gap
npm installnpm run devOpens at http://localhost:4321/reality-gap/
npm run buildOutput is written to ./dist/.
npm run previewThe repository includes a GitHub Actions workflow at .github/workflows/deploy.yml.
Setup steps:
- Push the repository to GitHub (repo name:
reality-gap, owner:hstre) - Go to Settings → Pages in the GitHub repository
- Under Source, select GitHub Actions
- Push a commit to
main— the workflow builds and deploys automatically
The site will be available at: https://hstre.github.io/reality-gap/
npm run build
# Then upload the ./dist/ folder to your hosting providersrc/
├── components/
│ ├── layout/
│ │ ├── BaseLayout.astro # HTML head, global layout
│ │ ├── Header.astro # Navigation + language switcher
│ │ └── Footer.astro # Footer
│ ├── ui/
│ │ ├── CompanyCard.astro # Card for company overview
│ │ ├── MetricBlock.astro # Single RG metric display block
│ │ └── TrendBadge.astro # Trend code badge (++, +, =, -, --)
│ └── RankingTable.astro # Sortable rankings table (with JS)
├── data/
│ ├── companies/
│ │ ├── tesla.json
│ │ ├── apple.json
│ │ ├── nvidia.json
│ │ ├── microsoft.json
│ │ └── samsung.json
│ ├── companies.index.json # List of company slugs
│ └── sectors.json # List of sectors (for filter)
├── i18n/
│ ├── en.ts # English translations
│ └── de.ts # German translations
├── lib/
│ ├── rg.ts # Core types + utility functions
│ ├── format.ts # Number/currency formatters
│ └── sort.ts # Sort helpers
├── pages/
│ ├── index.astro # Home (EN)
│ ├── methodology.astro # Methodology (EN)
│ ├── paper.astro # Paper (EN)
│ ├── companies/
│ │ ├── index.astro # Companies overview (EN)
│ │ └── [slug].astro # Company detail page (EN)
│ ├── rankings.astro # Rankings (EN)
│ ├── about.astro # About (EN)
│ └── de/ # German equivalents
│ ├── index.astro
│ ├── methodik.astro
│ ├── paper.astro
│ ├── unternehmen/
│ │ ├── index.astro
│ │ └── [slug].astro
│ ├── rankings.astro
│ └── ueber.astro
└── styles/
├── global.css # Tailwind imports + custom layers
└── tokens.css # CSS custom properties (colors, fonts)
public/
├── favicon.svg
└── papers/
└── reality-gap-working-paper.pdf ← place PDF here
Each company is stored in its own JSON file under src/data/companies/.
{
"company": "Tesla",
"ticker": "TSLA",
"slug": "tesla",
"sector": "Automotive / Technology",
"currency": "USD",
"description": "Illustrative approximation in the initial public RG dataset.",
"observations": [
{
"periodKey": "2026_q1",
"periodLabel": "Q1 2026",
"rg8": 12.4,
"rg10": 11.3,
"rg12": 10.5,
"trend": "-",
"marketCap": 1353,
"bookEquity": 82,
"netIncome": 3.8,
"fundamentalBaseApprox": 120,
"note": "Illustrative approximation, not fully adjusted."
}
]
}Field reference:
| Field | Type | Description |
|---|---|---|
company |
string | Display name |
ticker |
string | Exchange ticker symbol |
slug |
string | URL-safe identifier (lowercase, no spaces) |
sector |
string | Sector label (must match sectors.json) |
currency |
string | ISO 4217 currency code |
description |
string | Optional short description |
observations |
array | List of quarterly observations |
Observation fields:
| Field | Type | Description |
|---|---|---|
periodKey |
string | Internal key, e.g. "2026_q1" |
periodLabel |
string | Display label, e.g. "Q1 2026" |
rg8 |
number | RG value using 8-quarter smoothing |
rg10 |
number | RG value using 10-quarter smoothing |
rg12 |
number | RG value using 12-quarter smoothing |
trend |
string | One of: ++, +, =, -, -- |
marketCap |
number? | Market cap in billions |
bookEquity |
number? | Book equity in billions |
netIncome |
number? | Net income in billions (annualised) |
fundamentalBaseApprox |
number? | Estimated fundamental base in billions |
note |
string? | Data quality or disclaimer note |
dataType |
string? | "quarterly" (real) or "annual" (approximate) |
Internal period keys use the format YYYY_qN:
2026_q1 → Q1 2026
2026_q2 → Q2 2026
2025_q4 → Q4 2025
The site automatically determines the latest observation by sorting on periodKey. Do not hardcode which quarter is "current".
- Create
src/data/companies/<slug>.jsonfollowing the schema above - Add the slug to
src/data/companies.index.json - If the sector is new, add it to
src/data/sectors.json - Run
npm run buildto verify
Example — adding Alphabet:
// src/data/companies/alphabet.json
{
"company": "Alphabet",
"ticker": "GOOGL",
"slug": "alphabet",
"sector": "Technology",
"currency": "USD",
"description": "Illustrative approximation.",
"observations": [
{
"periodKey": "2026_q1",
"periodLabel": "Q1 2026",
"rg8": 22.1,
"rg10": 21.4,
"rg12": 20.8,
"trend": "+",
"note": "Illustrative approximation."
}
]
}Then add "alphabet" to companies.index.json:
["tesla", "apple", "nvidia", "microsoft", "samsung", "alphabet"]In the relevant company JSON file, prepend a new observation to the observations array:
"observations": [
{
"periodKey": "2026_q2",
"periodLabel": "Q2 2026",
"rg8": 11.9,
"rg10": 10.8,
"rg12": 10.1,
"trend": "=",
"marketCap": 1410,
"bookEquity": 85,
"netIncome": 3.9,
"fundamentalBaseApprox": 122,
"note": "Illustrative approximation."
},
{
"periodKey": "2026_q1",
...
}
]The site will automatically display the latest observation (determined by periodKey sort order).
Place the PDF file at:
public/papers/reality-gap-working-paper.pdf
The paper page links to this file automatically. Do not change the filename unless you also update the path in src/pages/paper.astro and src/pages/de/paper.astro.
The site supports English (default) and German.
| English URL | German URL |
|---|---|
/ |
/de/ |
/methodology |
/de/methodik |
/paper |
/de/paper |
/companies |
/de/unternehmen |
/companies/tesla |
/de/unternehmen/tesla |
/rankings |
/de/rankings |
/about |
/de/ueber |
Translations are in src/i18n/en.ts and src/i18n/de.ts. To add a new language, create a new translation file and add pages under a new language directory.
Key configuration file: astro.config.mjs
export default defineConfig({
integrations: [tailwind()],
output: 'static',
site: 'https://hstre.github.io',
base: '/Reality-Gap',
});site: Full site URL (used for canonical URLs and OG tags)base: Base path for GitHub Pages — must match the exact GitHub repository name (case-sensitive)
If you deploy to a custom domain (e.g. reality-gap.com), update both site and base (set base: '/').
- Light, white background — no dark mode
- Academic typography (Georgia for headings, system-ui for body)
- Single accent color:
#3b6e9e(muted blue) - No animations, no gradients, no decorative elements
- Generous whitespace
- Responsive at all screen sizes
Chart.js 4 (loaded via CDN at runtime) renders the historical line chart on each company detail page. It was chosen because:
- No npm dependency (static site stays lightweight)
- Mature, well-documented API
- Sufficient for simple multi-line time-series charts
- No flashy defaults — animations are disabled
The chart component (src/components/ui/RGChart.astro) enforces strict data-quality rules:
-
Only
dataType: "quarterly"observations are plotted.
These come from real yfinance quarterly income statements. -
dataType: "annual"observations are excluded from the chart.
Annual observations use synthetic quarterly equivalents (annual NI ÷ 4) and an approximated historical market cap (closing price × current shares). They remain in the JSON for data completeness but are never plotted. -
No values are fabricated, interpolated, or gap-filled.
If a quarter has no real data, it simply does not appear as a chart point. -
Minimum threshold: 2 real quarterly observations.
Fewer than 2 points cannot form a meaningful line.
| Real quarterly obs | Chart output |
|---|---|
| 0 or 1 | Empty state: "Historical RG chart not yet available for this company." |
| 2 – 39 | Chart rendered + note: "Historical series shorter than 10 years due to current data availability." |
| 40+ (≥ 10 years) | Chart rendered without any additional note |
yfinance returns at most 4–5 quarters of quarterly income data per company via the free API. Therefore:
- Most companies: 4 quarterly observations (~1 year), chart shown with "shorter than 10 years" note
- Some Japanese companies (semi-annual reporters): 1 quarterly observation, chart shows empty state
- No company yet has 10 years of quarterly data
As new quarters are appended each quarter, the chart will grow organically.
| Component | Data used | Purpose |
|---|---|---|
| Top metric blocks (RG8/10/12) | getLatestObservation() — single most recent observations[0] regardless of dataType |
Current snapshot |
| Historical chart | All dataType === "quarterly" observations, sorted oldest-first |
Trend over time |
The top section always works regardless of chart data availability.
To extend the chart backwards in time, add one observation per real reporting period directly to the company JSON. Each observation must have dataType: "quarterly" and use only values derived from actual reported data:
{
"periodKey": "2023_q2",
"periodLabel": "Q2 2023",
"rg8": 18.4,
"rg10": 17.1,
"rg12": 16.0,
"trend": "+",
"dataType": "quarterly",
"marketCap": 2800,
"bookEquity": 62,
"netIncome": 23.0,
"fundamentalBaseApprox": 292,
"note": "Computed from Q2 2023 yfinance data."
}Do not add entries with invented or interpolated RG values. The chart will show an honest shorter series rather than a falsely populated one.
The architecture is designed to support these additions without restructuring:
- More companies: add JSON files + update index
- More quarters: add observations to existing company files
- Longer historical charts: append real quarterly observations over time; the chart grows automatically
- Quarter selector: add a dropdown on detail pages to switch between observations
- Status badges: add
dataStatus,isApproximationetc. fields to observations - CSV/JSON export: generate export files during build from existing JSON data
- Semi-automated data updates: write a script to update JSON files from a data source
Research project. All data is illustrative. Not investment advice.