TCI‑CRI‑H100$3.99TCI‑CRI‑H100‑US$3.99TCI‑CRI‑H100‑GLOBAL$3.93TCI‑CRI‑H100‑7D$3.76TCI‑CRI‑H100‑MKT— GAPTCI‑CRI‑COMPUTE122.8TCI‑CRI‑H100$3.99TCI‑CRI‑H100‑US$3.99TCI‑CRI‑H100‑GLOBAL$3.93TCI‑CRI‑H100‑7D$3.76TCI‑CRI‑H100‑MKT— GAPTCI‑CRI‑COMPUTE122.8
AS OF 2026-10-10 12:27:29 UTC
v0.11.0 iosco-aligned reproducible

The reference price, defined precisely.

TCI‑CRI‑H100 is the headline series: one NVIDIA H100 SXM GPU-hour, on-demand, per GPU, ex-VAT, from an EU/EEA data centre. Companion series cover market segments, additional GPU generations, and a chain-linked composite that follows the market across hardware cycles. Everything below is read from config/factors.yaml at build time, so no table on this page can drift from the calculation. Series identifiers quoted as code below are the stored keys, which the published files still use.

Version v0.11.0 Lock sha256:c0dcd45cb164bbfc… Print date 2026-10-10 Change procedure

Reference definition

GPU modelH100 SXM
TermOn-demand, no commitment
LocationEU/EEA data centre
Node size≥2 GPUs; sub-node offers excluded
UnitPer-GPU-hour, ex-VAT, excluding storage and metered egress
CurrencyUSD primary, EUR companion at the ECB reference rate (T−1)

Compute classes

A class prices its reference variant only. No assumed cross-variant normalisation factor enters the calculation path — a variant re-enters a class only once a factor is measured from same-venue, same-day, same-SKU pairs.

ClassReference variant
H100H100 SXM
H200H200 SXM
B200B200 SXM
B300B300 SXM
A100A100 SXM
H100PH100 PCIE

Market segments

The headline draws from marketplace, neocloud only. The constituent distribution is bimodal — the hyperscaler catalog sits 5.4 standard deviations away — so it is published as its own series rather than averaged in.

Hyperscaler

aws, azure, gcp, oci

Catalog list prices. Published as their own series.

Marketplace

runpod, vast.ai

Executable quotes with a demonstrated node size.

Neocloud

civo, coreweave, digitalocean, hyperstack, lambdalabs, nebius, ovhcloud, scaleway, seeweb, together, verda, voltagepark

Published list prices from specialist operators.

How the print is computed

Every offer is weighted by GPU count rather than counted once per provider: a median over six providers is a step function, a median over many weighted offers is locally smooth. Below the provider floor the value is published as a gap, never fabricated.

unit: offer # weighted over offers, not one price per provider
estimator: weighted_median
min_providers: 5 # below this: value=null, flag=insufficient_sources
min_offers: 5
trim: count-based (k=1 at n≥5, k=2 at n≥10, k=3 at n≥20)
smoothing_days: 7 # headline companion series only

TCI-CRI‑M · construction

Hash-locked. A change to any row requires a version bump, a CHANGELOG entry, and one publication's notice.

v0.11.0 · sha256:c0dcd45c…
  1. #StepParameter
  2. 01

    Reference unit

    One H100 SXM GPU-hour, on-demand, delivered from the EU/EEA, per GPU, ex-VAT, excluding storage and metered egress. A class prices its reference variant only.

    6 classes
  3. 02

    Unit filters

    Offers below 2 GPUs are excluded rather than normalised: the per-GPU discount saturates at 2 GPUs, while a 1-GPU offer carries a small-order premium. Sanity band $0.25–$25.00. Excluded tiers: interruptible, spot, community.

    ≥2 GPU
  4. 03

    Market segment

    The constituent distribution is bimodal, so series never average across the neocloud/hyperscaler gap. The headline draws from marketplace, neocloud.

    3 segments
  5. 04

    Staleness

    Manually verified static entries warn at 45 days and are excluded at 90.

    90 d
  6. 05

    Provider weight

    A provider with any executable offer is weighted 2× a list-only provider. Capacity does not enter at provider level: it is unobservable for every list source.

    ×2.0
  7. 06

    Concentration cap

    No provider may exceed this share of total weight; the excess is redistributed pro-rata to a fixed point. Mathematically inert at n=4; binds from n=5. Every print publishes whether it bound.

    25%
  8. 07

    Offer spread

    Each provider's share is spread across its own offers in proportion to observed capacity, capped at 64 GPUs, defaulting to 8 where capacity is unobservable.

    cap 64
  9. 08

    Trim

    Count-based: clamp the k highest and k lowest offer prices to the k-th order statistic. Percentile winsorising is inert at this panel size — at n=6 both p5/p95 and p10/p90 resolve to (min, max) and clamp nothing.

    n≥5→k=1 · n≥10→k=2 · n≥20→k=3
  10. 09

    Estimator

    Weighted median over offers: the first price at which cumulative weight reaches 50%. It always lands on a price someone actually quoted.

    over offers
  11. 10

    Publication gate

    Fewer than 5 qualifying providers or fewer than 5 qualifying offers publishes no value, flagged insufficient_sources / insufficient_offers. There is no fallback waterfall.

    ≥5 / ≥5
  12. 11

    EUR companion

    USD value divided by the most recent ECB reference rate dated on or before the print date, refused beyond 7 days old. The ECB publishes after the cut-off, so the EUR leg is T−1 by construction.

    ≤7 d
  13. 12

    Smoothing companion

    The headline's 7-day mean, requiring at least 4 non-null days. Published beside the headline, never as it.

    7 d
  14. 13

    Composite

    Chain-linked over class sub-indices from a base of 100, class shares floored at 5% and capped at 75%. A class that gaps drops out of that day's link.

    base 100
  15. 14

    Quality flag

    A constituent moving more than 30% day-over-day is flagged for review and is never silently excluded.

    30%
  16. 15

    Revisions

    Observations and prints are append-only, enforced by database triggers. An error is corrected as a new revision flagged correction; prior revisions stay queryable forever.

    append-only

Versions and effective dates

Each print is computed under the version whose effective date is the latest on or before the print date. An announced version is in the repository from the day of its notice and applies only from its effective date; superseded versions are frozen and hash-locked.

VersionEffective fromNoticeStatus
v0.3.0-dev18 Jul 2026—Superseded
v0.4.015 Sep 20262026-N1Superseded
v0.5.022 Sep 20262026-N2Superseded
v0.6.01 Oct 20262026-N3Superseded
v0.7.02 Oct 20262026-N4Superseded
v0.8.03 Oct 20262026-N5Superseded
v0.9.04 Oct 20262026-N6Superseded
v0.10.05 Oct 20262026-N7Superseded
v0.11.06 Oct 20262026-N8In effect
v0.11.111 Oct 20262026-N9Announced

Audit and governance hooks

How a third party checks this print without asking us

# recompute every stored print under the version live on its
# date, and check every published digest against the database
python -m tci.run reproduce --published

# the full constituent set behind any print
python -m tci.run constituents --date 2026-10-10 --series EU-CRI-H100

# the weight review in effect on a date, recomputed from stored observations
python -m tci.run weights --date 2026-10-10

# regenerate this document and the lock from config
python -m tci.run docs
  1. A1

    Methodology lock

    A sha256 over the head parameters (factors.yaml, sovereign.yaml), the succession of versions, and the calculation code (index.py, normalise.py, weights.py), plus one hash per frozen version. CI fails whenever the working tree stops matching it, and the lock refuses any edit to a frozen version.sha256:c0dcd45cb164bbfcd2e2bfc09f4dce8d327b72002671a84172ab4a09f679297c

    head v0.11.1
  2. A2

    Machine-readable prints

    The full published history, latest revision per date and series; today's snapshot; and one file per print date with every series, its constituent set, and a digest that reproduce --published checks.

    CSV · JSON · print file
  3. A3

    Complaints

    Any print may be challenged. Acknowledged within 7 days; the outcome is published with the next print, whether it is a correction or a rationale for no change.

    P13

TCI Methodology — v0.11.0

TCI (The Compute Indices) is a daily reference price for renting AI compute delivered from data centres physically located in the EU/EEA. Headline series: TCI-CRI-H100, in USD per GPU-hour with a EUR companion at the ECB reference rate.

TCI is a price-transparency benchmark, not a settlement benchmark. Every print is reproducible by any third party from public sources using the published code. It is not transaction-based and must not be referenced in a financial contract. The conditions that would have to be met before settlement use is credible are published in GOVERNANCE.md. §9 lists every methodology version and the date it takes effect.

1. Unit definition

One TCI-CRI-H100 unit = one NVIDIA H100_SXM 80GB GPU-hour, on_demand (no term commitment), datacenter-hosted, delivered from the EU/EEA, per-GPU, ex-VAT, excluding storage and metered egress.

A class prices its reference variant only. Cross-variant normalisation by assumed factors was removed in v0.3.0: H100 SXM and H100 PCIe are different products, and H100→H200 normalisers disagree in sign depending on whether you divide by BF16 PFLOPs (+31%) or HBM bandwidth (−9%). A variant may enter a class only under a factor measured from same-venue, same-day, same-SKU pairs.

ClassReference variantVariants (factor)
H100H100_SXMH100_SXM (x1)
H200H200_SXMH200_SXM (x1)
B200B200_SXMB200_SXM (x1)
B300B300_SXMB300_SXM (x1)
A100A100_SXMA100_SXM (x1)
H100PH100_PCIEH100_PCIE (x1)

1.1 The panel

A row can enter a print only if its provider, the collector that observed it, and its class are all named here. Anything else is collected and stored, and appears in the audit set as not_in_panel, but moves no number. Admitting a new (provider, collector, class) is a constituent change under GOVERNANCE.md §1.

ProviderSegmentCollector: classes admitted
awshyperscalergpuhunt: H100
azurehyperscalerazure_retail: A100, H100, H100P, H200; gpuhunt: H100
civoneocloudcivo: H100
coreweaveneocloudcoreweave: H100
digitaloceanneoclouddigitalocean: H100
gcphyperscalergpuhunt: H100
hyperstackneocloudhyperstack: A100, B200, B300, H100, H100P, H200
lambdalabsneocloudgpuhunt: A100, B200, H100, H200
nebiusneocloudgpuhunt: B200, B300, H100, H200
ocihyperscalergpuhunt: A100, B200, H100, H200
ovhcloudneocloudovh: H100P, H200
runpodmarketplacerunpod: A100, H100
scalewayneocloudscaleway: B300, H100, H100P
seewebneocloudseeweb: H100
togetherneocloudtogether: H100
vast.aimarketplacevast_ai: A100, B200, B300, H100, H100P, H200
verdaneocloudgpuhunt: A100, B200, B300, H100, H200
voltageparkneocloudvoltagepark: H100

Excluded outright: variants not listed above, community/consumer hosts, interruptible/spot tiers (interruptible, spot, community), term-committed prices, offers below 2 GPUs, prices outside the sanity band [$0.25, $25.00] per GPU-hour.

Why the node floor is 2, not 8. Measured within one venue on one day, the per-GPU discount saturates at 2 GPUs (1×=1.000, 2×=0.951, 4×=0.916, 8×=0.916), so 2/4/8-GPU offers are mutually comparable within ~4% and need no adjustment, while the 1-GPU offer carries a ~9% small-order premium and is excluded rather than normalised away. The former 8-GPU floor admitted marketplace inventory on 1 collection day in 10 and discarded essentially all of the index's price discovery.

2. Data hierarchy and market segments (IOSCO P8)

Executable marketplace asks are preferred over list prices and carry a 2× weight multiplier. Overlay data (power prices) never enters the calculation. Competitor indices are never ingested.

The constituent distribution is bimodal — measured separation of 5.4 standard deviations between the neocloud/marketplace cluster and the hyperscaler catalog cluster. Averaging across that gap yields a number no one quotes, so series are segregated by market segment and never drawn across it:

SeriesPopulation
EU-CRI-H100 (headline)marketplace, neocloud
EU-CRI-H100-MKTmarketplace
EU-CRI-H100-HShyperscaler
EU-CRI-H100-SOVmarketplace, neocloud (further filtered by config/sovereign.yaml)
spothyperscaler, marketplace, neocloud

3. Aggregation (exact algorithm)

Per UTC day and series. The unit of aggregation is the offer, not the provider.

  1. Collect all observations for the day passing the unit filters above, restricted to the series' market-segment population.
  2. Every panel price is read by a collector on the day. No constituent is priced from a hand-maintained entry, so no staleness rule applies to the panel.
  3. Provider weight = 2 if the provider has any executable offer, else 1. Capacity does not enter here: it is unobservable for every list source, so a capacity term at provider level is fiction that made a rate card disclosing nothing outrank a marketplace disclosing a real 2-GPU offer.
  4. Concentration cap: no provider may exceed 25% of total weight; excess is redistributed pro-rata, iterated to a fixed point (the cap relaxes to 100/n when n providers cannot satisfy it). Note this cap is mathematically inert at n=4 — it forces exactly equal shares — and binds only for n≥5. Every print publishes whether it bound.
  5. Spread each provider's share across its own offers in proportion to offer capacity. Capacity is genuinely observable here, between offers from the same venue.
  6. Trim: clamp the k highest and k lowest offer prices to the k-th order statistic from each end, k by panel size (n≥5 → k=1; n≥10 → k=2; n≥20 → k=3). Count-based, because nearest-rank percentile winsorising is inert at this panel size — at n=6 both p5/p95 and p10/p90 resolve to (min, max) and clamp nothing.
  7. Weighted median over offers: sort by price ascending; the value is the first price at which cumulative weight reaches 50%. A median over ~6 providers has a delta of 1.0 to one constituent and 0.0 to every other; a median over many capacity-weighted offers is locally smooth and always lands on a price someone actually quoted.
  8. Publish gate: fewer than 5 qualifying providers or fewer than 5 qualifying offers → no value is published, flagged insufficient_sources / insufficient_offers. There is no fallback waterfall. A gap is credible; a fabricated print is fatal.
  9. EUR companion = USD value ÷ the most recent ECB EUR/USD reference rate dated strictly before the print date, so the EUR leg is T-1 on every day, whenever the run happens. Providers quoting natively in EUR are converted from their native amount at print time, at the same rate.
  10. Headline companion: 7-day mean of daily prints (requires ≥4 non-null days).

Constituent prices moving more than 30% day-over-day are flagged (jump) for manual review but are not excluded. A print with no executable input is flagged no_executable_input — a list-price-only print says so on its face.

3.1 Continuity series

The headline is the raw daily cross-section, not a chain-linked level. A Laspeyres link with a same-day divisor reset contributes exactly zero return on panel entry and exit, which makes in-panel up-moves permanent while down-moves taken via exit and re-entry are laundered out — a ratchet worth roughly +7.8% per cycle for a constituent at the 25% cap. A chained level is published beside the headline as a labelled companion, and the divergence between them is a published health metric with a 5% review trigger.

3.2 TCI-CRI-COMPUTE (chain-linked class composite)

The composite aggregates the class series into one level (base 100 at its first print), chain-linked so that reweighting never jumps the published level:

  • At each weight review, class basket shares = each class's share of total observed qualifying capacity over the window. With ≥2 eligible classes, shares are capped at 75% and floored at 5% (pro-rata redistribution, as in §3 step 6).
  • Daily: composite(t) = composite(t−1) × Σ share × (class(t) / class(t−1)), summed over classes with a published value on both endpoints, shares renormalised over those classes. A class that gaps drops out of that day's link; if no class links, the composite gaps (no_linkable_series).
  • As the observed market migrates across hardware generations (H100 → B200), the basket follows mechanically — no methodology change required.

4. Series

SeriesConstituents
EU-CRI-H100all qualifying H100-class providers (headline)
EU-CRI-H100-7D7-day mean of the headline
EU-CRI-H100-SOVEU/EEA-headquartered operators: genesis_cloud, hetzner, leaseweb, nebius, ovhcloud, scaleway, seeweb, verda
EU-CRI-H100-MKTmarketplace segment only
EU-CRI-H100-NCneocloud segment only
EU-CRI-H100-HShyperscaler catalog segment only
EU-CRI-H100-PCIEH100 PCIe, priced as its own class (no assumed SXM factor)
class series (EU-CRI-A100, EU-CRI-H200, EU-CRI-B300, …)one per observed class in §1; published once ≥5 providers exist (gapped, with audit trail, before that)
EU-CRI-COMPUTEchain-linked composite of class series (§3.2); a level, not a $/hr price
EU-CRI-H100-USthe headline's unit, estimator, weights and gate, US block (US)
EU-CRI-H100-GLOBALthe headline's unit, estimator, weights and gate, GLOBAL block (every ISO 3166-1 country, 249 codes listed in factors.yaml; also rows with no country from civo, together, which publish one price for every site)
EU-CRI-H100-SPOTthe headline's unit, estimator, weights and gate, EU/EEA block (AT, BE, BG, CY, CZ, DE, DK, EE, ES, FI, FR, GR, HR, HU, IE, IS, IT, LI, LT, LU, LV, MT, NL, NO, PL, PT, RO, SE, SI, SK), read from spot rows (population: every segment)
EU-CRI-H100-SPOT-USthe headline's unit, estimator, weights and gate, US block (US), read from spot rows (population: every segment)
EU-CRI-H100-SPOT-GLOBALthe headline's unit, estimator, weights and gate, GLOBAL block (every ISO 3166-1 country, 249 codes listed in factors.yaml; also rows with no country from civo, together, which publish one price for every site), read from spot rows (population: every segment)
EU-CRI-H100-BASIS-USEU-CRI-H100 minus EU-CRI-H100-US, USD per GPU-hour; a gap on any day either leg gaps
EU-CRI-H100-SPOTSPREADEU-CRI-H100 minus EU-CRI-H100-SPOT, USD per GPU-hour; a gap on any day either leg gaps
EU-CRI-H100-SPOTSPREAD-USEU-CRI-H100-US minus EU-CRI-H100-SPOT-US, USD per GPU-hour; a gap on any day either leg gaps
EU-CRI-H100-SPOTSPREAD-GLOBALEU-CRI-H100-GLOBAL minus EU-CRI-H100-SPOT-GLOBAL, USD per GPU-hour; a gap on any day either leg gaps

4.1 The US reference block and the EU-US basis

The US series prices one H100 SXM GPU-hour delivered from the United States with exactly the rules above: the same unit definition, node floor, weighted median over offers, trim, tier weights, concentration cap and publication gate. The basis series is the EU/EEA headline minus the US series on the same day. Holding the method constant is what makes the spread a regional basis rather than a comparison of two methods.

It is not the basis to the index on which the CME compute futures settle. That index's methodology is not public, and a spread against it would mix a regional difference with a methodological one that nobody outside can measure.

Region-flat prices are placed only where the seller itself says it sells: DigitalOcean's H100 is recorded once per region on its availability page, and RunPod's US row exists only on a day RunPod reports stock of that GPU type in a US datacentre.

EU-CRI-H100-CLOUD was retired in v0.3.0 and is not published. Its historical values remain in index_history.csv under the methodology version that produced them.

Segment size and the publication gate. A sub-population series can only print when its segment holds at least min_providers (5) qualifying providers. EU-CRI-H100-MKT and EU-CRI-H100-HS draw on segments smaller than that and therefore gap by construction rather than by circumstance. This is stated here because a permanent gap and a temporary one look identical on the dashboard, and the difference matters to a reader.

5. Revisions and corrections

Raw observations and published prints are append-only (enforced by database triggers). Errors are corrected in the next print as a new revision flagged correction; prior revisions remain queryable forever. The full constituent set for any print is available via python -m tci.run constituents --date YYYY-MM-DD, and every print is published with its constituents and a digest in site/data/prints/YYYY-MM-DD.json.

python -m tci.run reproduce --published recomputes every stored print from the stored observations under the version live on its date, compares value, counts, flags and the full constituent set with what was stored, then checks each published digest against the database. Exit 0 means every print matched.

6. Changes to this methodology

Any change to the parameters below or the calculation code requires a version bump, a CHANGELOG entry, and one publication's notice before taking effect, enforced mechanically via METHODOLOGY.lock in CI. See GOVERNANCE.md.

Effective dates are enforced in code. An announced version is committed on the day of its notice and is selected for a print only from its effective date (§9); earlier versions are frozen snapshots under config/methodology/<version>/, each with its own rendered copy of this document, and the lock refuses any change to a frozen snapshot.

7. Current parameters (config/factors.yaml, verbatim)

# EU-CRI methodology parameters. Every number that affects a published print lives here.
# Changing anything in this file (or sovereign.yaml, index.py, normalise.py, weights.py)
# requires a methodology_version bump + CHANGELOG entry - enforced by CI (see GOVERNANCE.md).
methodology_version: "0.11.0"      # effective 2026-10-06 (notice 2026-N8). See
                                    # config/methodology/succession.yaml for every version
                                    # and the date it takes effect.

reference_unit:
  gpu_model: H100_SXM        # headline reference variant (class H100 below)
  term: on_demand            # no term commitment
  location: EU_EEA           # datacenter physically in the EU/EEA
  # v0.3.0 removed `node_gpus: 8`. The node-size floor now lives in filters.min_gpu_count
  # and is 2, not 8 - see the measurement recorded there.
  # Per-GPU-hour, ex-VAT, excluding metered egress. USD primary, EUR companion at the
  # ECB reference rate (T-1: the ECB publishes ~14:00 UTC, after our 11:00 cut-off, so
  # the EUR leg structurally cannot use same-day FX - see METHODOLOGY.md).

# Compute classes the index observes. A class prices its reference variant ONLY.
# v0.3.0: cross-variant normalisation factors are removed. H100 SXM and H100 PCIe are
# different products, and H100->H200 normalisers disagree in SIGN depending on whether
# you divide by BF16 PFLOPs (+31%) or HBM bandwidth (-9%). A variant may re-enter a class
# only with a factor MEASURED from same-venue, same-day, same-SKU pairs (see
# `measured_factors` below). No assumed factors are permitted in the calculation path.
model_classes:
  H100:   {reference_variant: H100_SXM,  variants: {H100_SXM: 1.0}}
  H200:   {reference_variant: H200_SXM,  variants: {H200_SXM: 1.0}}
  B200:   {reference_variant: B200_SXM,  variants: {B200_SXM: 1.0}}
  B300:   {reference_variant: B300_SXM,  variants: {B300_SXM: 1.0}}
  A100:   {reference_variant: A100_SXM,  variants: {A100_SXM: 1.0}}
  H100P:  {reference_variant: H100_PCIE, variants: {H100_PCIE: 1.0}}   # PCIe priced separately

# THE PANEL. Every provider that may contribute to a print, its market segment, and for
# each collector that observes it, the model classes that collector's rows may enter.
#
# Before v0.4.0 there was no enumerated panel: a provider was in the population if some
# collector produced a row for it, and an unlisted name defaulted to the neocloud segment.
# That made adding a collector a silent constituent change. Now a collector can store rows
# for months (the history a future constituent needs) without any of them reaching a print
# until a version admits the (provider, collector, class) triple here, after a notice.
#
# Segment is the tier-segregation control and it is methodology-affecting: the constituent
# population is bimodal (measured 2026-08-15, cluster separation 5.4 sd between the
# neocloud/marketplace cluster [2.16..3.85] and the hyperscaler cluster [5.44..7.36]).
# Averaging across that gap produces a number nobody quotes.
#
# v0.4.0 membership was identical to the 0.3.0-dev population. v0.5.0 (notice 2026-N2):
#   - verda, nebius and lambdalabs enter through dstack's gpuhunt catalogues, which carry a
#     location per row. Verda replaces the static `datacrunch` entry (the same company
#     under its new name, so the static entry leaves the panel the day the feed enters:
#     one company, one vote). Nebius's static entry is replaced by its catalogue feed.
#   - oci enters the hyperscaler segment.
#   - digitalocean enters in the H100 class: its H100 Droplets are "NVIDIA HGX H100"
#     (its own announcement) and are sold in Amsterdam (AMS3), New York and Toronto at
#     one price, recorded as one row per region.
#   - ovhcloud enters through its public order-catalogue API, in the H100P class only:
#     its H100 instances are PCIe ("80 GB HBM2e - PCIe 5.0"), never the SXM unit.
#   - vast.ai is admitted to the H100P, H200, B200 and B300 classes it already lists.
# v0.6.0 (notice 2026-N3): voltagepark, which sells only in the United States, enters for
# the US reference block below. It cannot reach an EU/EEA print: it has no EU/EEA rows.
# v0.7.0 (notice 2026-N4): seeweb is read daily by the `seeweb` collector from its own
# pricing page instead of from a hand-edited file, at the same EUR 1.89/GPU-hr on the same
# 8-GPU node. hetzner, genesis_cloud and leaseweb leave: each was a static_yaml line that
# never had a price on file, so none ever contributed. From this version no panel entry
# reads a hand-maintained price; tests/test_panel.py holds every later version to that.
# v0.8.0 (notice 2026-N5): hyperstack enters. Its price list is the same in every region,
# so its rows carry a country only on a day its own public stock feed shows that model
# deployable in a named datacentre (NORWAY-1, CANADA-1, US-1), at the largest node
# deployable there: the rule RunPod's US rows already follow under v0.6.0. An unplaced
# row has no country and reaches no series.
panel:
  vast.ai:       {segment: marketplace, sources: {vast_ai: [H100, A100, H100P, H200, B200, B300]}}
  runpod:        {segment: marketplace, sources: {runpod: [H100, A100]}}
  aws:           {segment: hyperscaler, sources: {gpuhunt: [H100]}}
  gcp:           {segment: hyperscaler, sources: {gpuhunt: [H100]}}
  azure:         {segment: hyperscaler, sources: {azure_retail: [H100, H100P, A100, H200],
                                                  gpuhunt: [H100]}}
  oci:           {segment: hyperscaler, sources: {gpuhunt: [H100, H200, B200, A100]}}
  scaleway:      {segment: neocloud, sources: {scaleway: [H100, H100P, B300]}}
  nebius:        {segment: neocloud, sources: {gpuhunt: [H100, H200, B200, B300]}}
  verda:         {segment: neocloud, sources: {gpuhunt: [H100, H200, B200, B300, A100]}}
  lambdalabs:    {segment: neocloud, sources: {gpuhunt: [H100, H200, B200, A100]}}
  ovhcloud:      {segment: neocloud, sources: {ovh: [H100P, H200]}}
  digitalocean:  {segment: neocloud, sources: {digitalocean: [H100]}}
  voltagepark:   {segment: neocloud, sources: {voltagepark: [H100]}}
  hyperstack:    {segment: neocloud, sources: {hyperstack: [H100, H100P, H200, B200, B300, A100]}}
  seeweb:        {segment: neocloud, sources: {seeweb: [H100]}}
  # v0.10.0 (notice 2026-N7): three more H100 sellers. CoreWeave's rows are placed by its
  # own North America and Europe tables (US and NO, see collectors/computable_sources.py);
  # Civo and Together publish one price with no region and count only in GLOBAL, through
  # `unplaced` below.
  # CoreWeave's "Europe" table covers the UK as well as the EEA and names no site, so the
  # NO placement is an approximation; it is priced in US and GLOBAL and never in EU/EEA.
  coreweave:     {segment: neocloud, sources: {coreweave: [H100]}, blocks: [US, GLOBAL]}
  civo:          {segment: neocloud, sources: {civo: [H100]}}
  together:      {segment: neocloud, sources: {together: [H100]}}

# Which segments each published series draws from. The headline deliberately excludes the
# hyperscaler catalog tier - those are the two highest and least-moving prices in the panel
# and they belong in the series where they are the correct object of measurement.
series_populations:
  headline:    [marketplace, neocloud]
  marketplace: [marketplace]
  hyperscaler: [hyperscaler]
  sovereign:   [marketplace, neocloud]   # further filtered by config/sovereign.yaml
  # v0.11.0 (notice 2026-N8): the spot series draw from every segment. The hyperscalers are
  # most of the spot market (AWS, GCP, Azure); leaving them out, as the on-demand headline
  # does, would leave two sellers per region and a gap every day.
  spot:        [marketplace, neocloud, hyperscaler]

filters:
  min_gpu_count: 2           # v0.3.0: was 8. Measured within-venue same-day (vast.ai
                             # 2026-07-18) the per-GPU discount SATURATES at 2 GPUs:
                             # 1x=1.000, 2x=0.951, 4x=0.916, 8x=0.916. So 2/4/8-GPU offers
                             # are mutually comparable within ~4% and need no factor, while
                             # the 1-GPU offer carries a ~9% small-order premium and is
                             # excluded rather than normalised. The old 8-GPU floor passed
                             # 1 of 10 vast.ai days and threw away ALL marketplace price
                             # discovery (9 distinct EU H100 prices, +12.3% range).
  price_floor_usd: 0.25      # junk-listing guard: asks below this are excluded
  price_ceiling_usd: 25.0    # sanity ceiling
  datacenter_verified_only: true
  exclude_tiers: [interruptible, spot, community]

aggregation:
  unit: offer                # v0.3.0: aggregate over OFFERS, not one price per provider.
                             # A median over ~6 providers is a step function with delta 1.0
                             # to one name and 0.0 to the rest. A median over many
                             # GPU-count-weighted offers is the SOFR construction and is
                             # locally smooth. Density is what makes the median sound.
  estimator: weighted_median # over offers, weighted by GPU count (capacity)
  trim_k:                    # count-based trim: clamp the k highest and k lowest.
    - {min_n: 0,  k: 0}      # Percentile winsorising is INERT at this panel size: at n=6
    - {min_n: 5,  k: 1}      # nearest-rank p5/p95 AND p10/p90 both resolve to (min, max)
    - {min_n: 10, k: 2}      # and clamp nothing. The shipped [5,95] was a no-op for the
    - {min_n: 20, k: 3}      # entire history of the index. Count-based trimming binds at
                             # every n.
  min_providers: 5           # below this: value=null, flag='insufficient_sources'.
                             # A gap stays a gap - no fallback waterfall, no fabrication.
  min_offers: 5              # and at least this many surviving offers
  smoothing_days: 7          # headline companion series: 7-day mean (>=4 non-null required)

weights:
  capacity_cap: 64           # daily capacity term = min(observed qualifying GPUs, cap)
  default_capacity: 8        # ... where capacity is unobservable (all list-price rows)
  executable_multiplier: 2.0 # executable-quote sources weighted double list-price sources
  max_weight_share_pct: 25   # per-PROVIDER aggregate cap, applied after offers are summed
                             # within a provider. NOTE: this cap is mathematically inert at
                             # n=4 (max(25, 100/n) forces exactly equal shares - verified
                             # 0.0 deviation over 50k random weight vectors). It binds again
                             # only once the panel is wider; `cap_bound` is published per
                             # print so a reader can see whether it did anything.
  review:                    # scheduled, data-driven weight reviews (fixed rule, no discretion)
    anchor_weekday: 1        # ISO weekday a review takes effect (1 = Monday, i.e. weekly)
    window_days: 28          # trailing observation window ending the day before the review
    min_history_days: 5      # fewer collection days than this in the window -> bootstrap

composite:                   # EU-CRI-COMPUTE: chain-linked basket of class sub-indices
  base_value: 100.0          # level on the composite's first print
  min_class_share_pct: 5     # floor for an eligible class at review (applies with >=2 classes)
  max_class_share_pct: 75    # cap for a class at review (applies with >=2 classes)

# Continuity series. v0.3.0 DEMOTES chain-linking from the headline to a labelled companion.
# A Laspeyres link with a same-day divisor reset contributes exactly zero return on panel
# entry/exit, which makes in-panel up-moves permanent while down-moves taken via exit and
# re-entry are laundered out - a ratchet worth ~+7.8% per cycle for a 25% constituent.
# The headline is therefore the raw cross-section (reproducible, no drift, no ratchet); the
# chained level is published beside it and the divergence between them is a health metric.
continuity:
  publish_chained: true
  base_value: 100.0
  divergence_review_pct: 5   # chained vs raw divergence above this triggers a documented review

fx:
  max_age_days: 7            # refuse an FX rate older than this (flag the print instead)
  strictly_before: true      # v0.5.0: the ECB rate dated strictly BEFORE the print date,
                             # so the EUR leg is T-1 whenever the run happens. Under the
                             # old on-or-before rule a run after the ECB publication took
                             # the same-day rate, and a print's FX depended on its run time.
  # HARD RULE enforced in code: fx_date <= print date. Two 2026-07 prints were computed
  # with an FX rate published on 2026-07-21, i.e. AFTER the print date - a look-ahead
  # introduced by backfill. Covered by a regression test.

staleness:                   # static (manually verified) provider entries
  warn_days: 45              # loader warns
  exclude_days: 90           # excluded from the index with exclusion_reason='stale'

quality:
  jump_flag_pct: 30          # day-over-day provider move that flags (never excludes)

# Factors measured from same-venue, same-day, same-SKU observations. Published for
# transparency; NONE of these currently enter the calculation path. A factor may only be
# promoted into `model_classes` after it is supported by >=3 independent venue-days.
measured_factors:
  node_size_per_gpu:         # vast.ai, EU H100_SXM, same-day ratios vs the 1-GPU offer
    "2": 0.9514              # n=3 venue-days
    "4": 0.9159              # n=1  - provisional
    "8": 0.9158              # n=1  - provisional
  fabric_infiniband_premium: 1.107   # Azure westeurope ND96isr vs ND96is_noIB, n=1 pair.
                                     # PROVISIONAL - measures Azure's internal price ladder,
                                     # not the market's. Not in the calculation path.

# Every code is QUOTED. YAML 1.1 reads the bare token NO as boolean false, so until v0.4.0
# this list held `False` where Norway should have been, and every Norwegian observation
# was dropped by the country filter (126 rows by 2026-09-08). tests/test_sources.py now
# asserts every entry is a two-letter string.
eu_eea_countries: ["AT", "BE", "BG", "HR", "CY", "CZ", "DK", "EE", "FI", "FR", "DE", "GR",
                   "HU", "IE", "IT", "LV", "LT", "LU", "MT", "NL", "PL", "PT", "RO", "SK",
                   "SI", "ES", "SE", "IS", "LI", "NO"]
# NOTE: CH (Switzerland) is deliberately absent - Switzerland is in EFTA but NOT the EEA.

# ---------------------------------------------------------------------------------------
# v0.6.0: THE US REFERENCE BLOCK AND THE EU-US BASIS (notice 2026-N3)
#
# CME Group plans to list compute futures on 5 October 2026 that settle in cash on Silicon
# Data's H100 and B200 rental indices. A European buyer who hedges with them carries a basis: the difference between what the
# European population of sellers charges and what the reference population charges. This
# section prices the United States with exactly the headline's unit definition, estimator,
# trim, weights and publication gate, so that the spread between the two is a basis and
# not a comparison of two methods. It is not the basis to the Silicon Data index itself,
# whose methodology is not public; METHODOLOGY.md says so beside the series.
blocks:
  US: ["US"]
  # v0.9.0 (notice 2026-N6): every country. The list is ISO 3166-1 alpha-2 in full, 249
  # codes, copied from the IANA time zone database's iso3166.tab rather than typed, so a
  # row from a country nobody thought of is still placed. A row with no country at all
  # is not: it cannot be placed anywhere, the same rule every other block applies.
  GLOBAL: ["AD", "AE", "AF", "AG", "AI", "AL", "AM", "AO", "AQ", "AR", "AS", "AT", "AU", "AW",
           "AX", "AZ", "BA", "BB", "BD", "BE", "BF", "BG", "BH", "BI", "BJ", "BL", "BM", "BN",
           "BO", "BQ", "BR", "BS", "BT", "BV", "BW", "BY", "BZ", "CA", "CC", "CD", "CF", "CG",
           "CH", "CI", "CK", "CL", "CM", "CN", "CO", "CR", "CU", "CV", "CW", "CX", "CY", "CZ",
           "DE", "DJ", "DK", "DM", "DO", "DZ", "EC", "EE", "EG", "EH", "ER", "ES", "ET", "FI",
           "FJ", "FK", "FM", "FO", "FR", "GA", "GB", "GD", "GE", "GF", "GG", "GH", "GI", "GL",
           "GM", "GN", "GP", "GQ", "GR", "GS", "GT", "GU", "GW", "GY", "HK", "HM", "HN", "HR",
           "HT", "HU", "ID", "IE", "IL", "IM", "IN", "IO", "IQ", "IR", "IS", "IT", "JE", "JM",
           "JO", "JP", "KE", "KG", "KH", "KI", "KM", "KN", "KP", "KR", "KW", "KY", "KZ", "LA",
           "LB", "LC", "LI", "LK", "LR", "LS", "LT", "LU", "LV", "LY", "MA", "MC", "MD", "ME",
           "MF", "MG", "MH", "MK", "ML", "MM", "MN", "MO", "MP", "MQ", "MR", "MS", "MT", "MU",
           "MV", "MW", "MX", "MY", "MZ", "NA", "NC", "NE", "NF", "NG", "NI", "NL", "NO", "NP",
           "NR", "NU", "NZ", "OM", "PA", "PE", "PF", "PG", "PH", "PK", "PL", "PM", "PN", "PR",
           "PS", "PT", "PW", "PY", "QA", "RE", "RO", "RS", "RU", "RW", "SA", "SB", "SC", "SD",
           "SE", "SG", "SH", "SI", "SJ", "SK", "SL", "SM", "SN", "SO", "SR", "SS", "ST", "SV",
           "SX", "SY", "SZ", "TC", "TD", "TF", "TG", "TH", "TJ", "TK", "TL", "TM", "TN", "TO",
           "TR", "TT", "TV", "TW", "TZ", "UA", "UG", "UM", "US", "UY", "UZ", "VA", "VC", "VE",
           "VG", "VI", "VN", "VU", "WF", "WS", "YE", "YT", "ZA", "ZM", "ZW"]

regional_series:
  EU-CRI-H100-US: {block: US, class: H100, population: headline}
  # v0.9.0 (notice 2026-N6): the same unit, estimator, weights and gate over every country.
  # A comparison for the EU and US series: where the same panel prices one H100 SXM
  # GPU-hour anywhere it sells, rather than in one region.
  EU-CRI-H100-GLOBAL: {block: GLOBAL, class: H100, population: headline}
  # v0.11.0 (notice 2026-N8): the same three regions priced from spot rows. `tiers` names
  # the stored tier a series reads; every other series reads `list` and `executable`, the
  # on-demand prices. `spot` is a price the seller publishes for interruptible capacity.
  # Two stored tiers stay out on purpose: Azure's `interruptible` rows are its retired Low
  # Priority product, and vast.ai's are `min_bid`, the lowest bid a host accepts, which is
  # a floor nobody has necessarily paid. The unit is otherwise the headline's.
  EU-CRI-H100-SPOT: {block: EU_EEA, class: H100, population: spot, tiers: [spot]}
  EU-CRI-H100-SPOT-US: {block: US, class: H100, population: spot, tiers: [spot]}
  EU-CRI-H100-SPOT-GLOBAL: {block: GLOBAL, class: H100, population: spot, tiers: [spot]}

# v0.10.0 (notice 2026-N7): rows with no country. Every block is a list of countries a
# row is checked against, and a row with no country fails all of them. GLOBAL asks only
# that the hour is sold somewhere, which a seller's single worldwide price satisfies, so
# for the providers listed here a row with no country counts in GLOBAL and in no other
# block. Named one by one: a provider that places some of its rows (Hyperstack, from its
# stock feed) must never be listed, or its unplaced row would sit beside the placed ones
# and count the same hour twice.
unplaced:
  GLOBAL: [civo, together]

# value = lead - reference, USD per GPU-hour, published only on a day both legs publish.
basis_series:
  EU-CRI-H100-BASIS-US: {lead: EU-CRI-H100, reference: EU-CRI-H100-US}
  # v0.11.0 (notice 2026-N8): on-demand minus spot in each region, the price of not being
  # interrupted. A gap on any day either leg gaps.
  EU-CRI-H100-SPOTSPREAD: {lead: EU-CRI-H100, reference: EU-CRI-H100-SPOT}
  EU-CRI-H100-SPOTSPREAD-US: {lead: EU-CRI-H100-US, reference: EU-CRI-H100-SPOT-US}
  EU-CRI-H100-SPOTSPREAD-GLOBAL: {lead: EU-CRI-H100-GLOBAL, reference: EU-CRI-H100-SPOT-GLOBAL}

8. IOSCO Principles mapping

IOSCO principleImplementation
Benchmark design & data sufficiency (P6–P7)Observable, executable marketplace quotes anchored; ≥5-source rule; weights set by observed data on a fixed schedule; 25% concentration cap; no expert judgement in the calculation path
Hierarchy of data inputs (P8)Executable quotes weighted 2.0x over list prices inside the weight review; overlay data never enters the calculation
Transparency of methodology (P9, P11)This document is auto-generated from config, semver-versioned; every parameter is a visible config value; every weight review is stored and queryable
Changes to methodology (P12)CHANGELOG + one-publication notice before any parameter change takes effect; hash-locked in CI. Scheduled weight reviews follow the fixed published formula and are data updates, not methodology changes
Quality of the administrator (P4–P5)Named author; stated conflicts (the author may trade venues the index observes — disclosed); corrections never silent
Complaints & audit trail (P13, P16)Raw observations and weight reviews immutable in SQLite; any reader can request the constituent set for a given day

9. Versions and effective dates

A print is computed under the last version whose effective date is on or before the print date (config/methodology/succession.yaml).

VersionEffective fromNotice
0.3.0-dev2026-07-18—
0.4.02026-09-152026-N1
0.5.02026-09-222026-N2
0.6.02026-10-012026-N3
0.7.02026-10-022026-N4
0.8.02026-10-032026-N5
0.9.02026-10-042026-N6
0.10.02026-10-052026-N7
0.11.02026-10-062026-N8
0.11.12026-10-112026-N9

TCI is a research publication. It is not investment advice and is not administered as a benchmark under EU Regulation 2016/1011; it may not be used as a reference price in financial instruments.

Author and administrator: Mark Rusch — contact via the contact page. Conflicts: the author may hold positions on venues whose prices the index observes; see GOVERNANCE.md.