A canonical, well-specified, cross-language (Python + TypeScript) reference implementation of the total-market-cap index. Own each company in proportion to its size and the weights look after themselves — at the cost of being exactly as concentrated as the market is. This module computes the index in exact decimal arithmetic with every input factor checked on its own, then measures the concentration the level never mentions.
📖 Full article (canonical): Total-Market-Cap Index — The Fintech Builder
This repository is the runnable, production-oriented companion to that article. The article teaches the concept; this repo is the code you install and build on.
🧭 Browse all algorithms: Awesome FinTech Algorithms — the full index of the library.
🗂️ This algorithm's domain: Index and Benchmark Engineering › Weighting and Capping
📥 Just want to call it? It also ships in the fintech-algorithms npm package — see Two ways to use this.
| Catalog topic | D03-F02-A02 |
| Domain | D03 — Index and Benchmark Engineering |
| Family | D03-F02 — Weighting and Capping |
| Difficulty | 2 / 5 |
| Languages | Python, TypeScript |
- Weight by what a company is worth
- The number the level hides
- What the reference got wrong
- Two ways to use this
- Install
- Quickstart
- Views: the analysis surface
- Input shape
- API reference
- Edge cases & limitations
- Testing
- Related algorithms
- License
market value_i = price_i x shares_i x fx_i
weight_i = market value_i / sum(market values)
level = sum(market values) / divisor
Prices move and the weights move with them, so a fund tracking this index never has to trade to stay aligned. That property is why cap weighting is the default design for a market benchmark.
1 MEGA value 3000000000 weight 0.695491 cumulative 0.695491
2 LARGE value 960000000 weight 0.222557 cumulative 0.918048
3 MID value 297000000 weight 0.068854 cumulative 0.986902
4 SMALL value 48000000 weight 0.011128 cumulative 0.99803
5 TINY value 8500000 weight 0.001971 cumulative 1
herfindahl 0.538108, effective constituents 1.858364 of 5
names needed for half the index: 1
Five members, and the index is really 1.86 of them. effectiveConstituents is 1 / HHI — the number of
equally weighted names that would be this concentrated — and it is the honest description of a
cap-weighted benchmark that a level cannot give you.
All confirmed by running it:
- Sign errors cancel. Only the product
price × shares × fxwas checked, so a negative price times negative shares — two data errors — passed as a valid constituent. Each factor is checked on its own here. - Coercion, NaN and infinity were all accepted.
- Ids were never read. The fixture carries them; the output does not. Here they are validated when supplied and default to positional labels, so the analysis surface can name members.
Arithmetic is exact: every JSON number is read as the decimal it spells, all arithmetic runs in exact fractions, and rounding happens once, at publication, to six decimals, half away from zero.
This repo is the production home: the full implementation, the analysis surface below, and 140 tests across two languages.
The fintech-algorithms npm package
ships the same topic as one import among several hundred.
fintech-algorithms/index-and-benchmark-engineering/weighting-and-capping/total-market-cap-index
Python
cd python
pip install -e ".[dev]"TypeScript
cd typescript
npm install
npm run buildfrom fintech_total_cap import calculate, concentration_report
basket = {"constituents": [
{"id": "A", "price": 20, "shares": 1000000, "fx": 1},
{"id": "B", "price": 40, "shares": 500000, "fx": 1},
{"id": "C", "price": 30, "shares": 800000, "fx": 1.1},
], "divisor": 50000}
calculate(basket)["indexLevel"] # 1328
concentration_report(basket)["effectiveConstituents"] # 2.945276TypeScript is the same call:
import { calculate, concentrationReport } from 'fintech-total-cap';
calculate(basket).indexLevel; // 1328Run the tour in either language:
cd python && python examples/quickstart.py
cd typescript && npm run exampleBoth print byte-identical output.
Members largest first (ties by id in code-point order), each with its exact weight and the cumulative weight above it. Where the cumulative column crosses 0.5 is how much of the index the top handful is.
Top-N share by name, the Herfindahl index, effectiveConstituents, namesForHalf, and
equalWeightHerfindahl for comparison. Cutoffs default to 1/3/5/10, skipping any larger than the basket.
market value 4313500000 -> 4013500000 (non-market change -300000000)
divisor 50000 -> 46522.545497, level 86270 -> 86270
without the adjustment the level would read 80270: a -0.069549090 return nobody earned
No price moved, so the divisor absorbs the change. levelWithoutAdjustment is the phantom return the
index would have printed had it not been adjusted, and every other member's weight rises.
The last one, theReportedValuesAndLevelAgree, re-derives the level from the reported market values,
so it catches a level that was computed on a different basket than the one published beside it. The
publication residual is reported as a fact, never as a failure.
Python — from fintech_total_cap import ...
| function | returns |
|---|---|
calculate(data) / total_market_cap_index(data) |
marketValues, weights, indexLevel |
weight_table(data) |
members ranked by size with cumulative weight and the residual |
concentration_report(data, cutoffs=None) |
top-N shares, HHI, effective constituents, names for half |
share_change_impact(data, changes) |
the rescaled divisor, weight shifts, and the phantom return |
verify_total_cap(data, result=None) |
four checks; pass result to audit a supplied answer |
validate_request · validate_constituent |
the validation steps, exposed |
to_fraction · render · number · trim · scaled |
exact-arithmetic helpers |
TypeScript — import { ... } from 'fintech-total-cap'
The same functions in camelCase (totalMarketCapIndex, concentrationReport, ...).
- Total shares, not free float. This design weights the whole company, including stock that will never trade; the free-float sibling below closes that gap.
share_change_impactholds prices fixed — it prices the non-market part of a change. If prices moved too, split them out first.- The output is positional, as the contract is;
weight_tableand the other views are ranked and carry ids. - Published weights are six decimals and need not sum to 1; the residual is reported.
- Large published numbers are doubles. Above ~9e9 a six-decimal value is finer than a double can hold; the number is the nearest double to the correctly rounded decimal.
cd python && pytest -q # 70 tests
cd typescript && npm test # 70 testsBoth suites reproduce the canonical fixture byte for byte.
The two implementations were compared directly across 1,500 scenarios and 7,500 calls — the index and all four surfaces, valid and malformed — and their canonical JSON output is byte-identical (2.6 MB). The examples are byte-identical too.
The Python port was differentially tested against the reference engine on 12,000 generated baskets
with zero unexplained divergences. An independent Decimal oracle confirmed all 9,404 results the
port returned. Every divergence is classified by name:
| divergence | cases | what happened |
|---|---|---|
| double resolution | 3,302 | both correct to the decimal; neighbouring doubles above ~9e9 |
| rounding rule | 861 | reference summed binary floats; port rounds the exact decimal half away from zero |
| coercion | 728 | reference accepted strings and booleans |
| ids validation | 472 | a constituent id was not a non-blank string, or ids repeated |
| non-finite | 319 | reference accepted NaN or infinity |
| sign cancellation | 286 | reference accepted negative price × negative shares |
Same family — D03-F02 Weighting and Capping
- Price-Weighted Index — the older design, where weights are an accident of share prices.
- Free-Float Market-Cap Index — weight by the part of each company that actually trades.
- Capped Free-Float Market-Cap Index — what to do about the concentration this design produces.
- Modified Market-Cap Index — bend these weights toward equal weighting with one exponent.
Related — D03-F01 Index Initialization and Continuity
- Divisor Continuity Adjustment — the general rule behind
share_change_impact.
MIT — see LICENSE.
{ "constituents": [{ "id": "A", // optional; non-blank and unique when supplied, else positional "price": 20, // > 0 "shares": 1000000, // > 0, total shares outstanding "fx": 1 // > 0, into the index currency }], "divisor": 50000 // > 0 }