Data sources and methodology
In one paragraph. Each Trion market tracks a real-world resource price: GPU hours, memory, flash, wholesale power, Bitcoin hashprice, a DePIN token basket and (research only) LLM token pricing. Three different things carry those numbers, and they must not be confused: the historical daily indices in overdrive/exchange/data/index/*.csv are reconstructed from archived sources and served as clearly labelled history (each only from its RELIABLE start date — see "Historical indices" below; DRAM and NAND publish Trion's composite index, index points only); the observed-index oracle service (overdrive/exchange/oracle) polls live sources, blends them and hashes evidence, and is what the desk and https://api.trion.market/v1/oracle/* show as the live CMPT index; and the V2 risk feed (index I, mark M, expiry fixing X, hourly funding) is what TrionPriceVerifierV2 and FundingEpochsV2 accept on Robinhood Chain from a 2-of-3 signer committee. Only CMPT is deployed on 4663; the other markets' rows below describe collectors and design, not live derivatives.
Three things that carry a price
| Product | Where | What it is | What it is not |
|---|---|---|---|
| Historical indices | overdrive/exchange/data/index/*.csv, build-index.ts, METHODOLOGY.md | Daily OHLC reconstructed by chain-linking generations, anchored to a checked-in constant | Not live V2 data; served by the matcher only as source=index candles and by the oracle as /v1/daily/:symbol |
| Observed-index oracle | overdrive/exchange/oracle/src/server.ts (/v1/quote/:symbol, /v1/quotes, /v1/evidence/:hash), reachable publicly through the matcher proxy /v1/oracle/* | Collectors write quotes and evidence blobs; IndexEngine blends them into the published CMPT composite with per-venue components | Not by itself the on-chain accepted I or M; the verifier holds those |
| V2 risk feed | oracle/src/publisher.ts, signer.ts, publication-evidence.ts → TrionPriceVerifierV2, FundingEpochsV2, FixingCouncilV2 on 4663 | Threshold-signed PricePayload, FundingPayload, FixingPayload reproducible from independently held observations; three signers (overdrive/deployments/v2/reviewed/4663.options-launch.json signers) | Not an independently audited feed; while the CMPT perp is unlisted the mark carried in each snapshot is the index |
Markets, units and sources
| Market | Unit on chain (WAD USD per…) | Live source set (oracle README) | Cadence |
|---|---|---|---|
| CMPT | H100-hour | Vast.ai, RunPod, SF Compute, Lium, DataCrunch, Shadeform; trimmed weighted geometric mean, top and bottom venue dropped | 30 s (SF Compute 300 s) |
| DRAM | GB (design) / index points (current live feed) | DRAMeXchange sessions; DDR5 16Gb 45 %, DDR4 16Gb 35 %, DDR4 8Gb 20 % | 3 sessions/day |
| NAND | GB (design) / index points (current live feed) | DRAMeXchange 512Gb/256Gb/128Gb TLC wafers 50/30/20 % | 3 sessions/day |
| GRID | MWh | NYISO Zone J (PTID 61761) 5-minute real-time LBMP | 5 min |
| HASH | PH/s/day | mempool.space hashrate + block subsidy/fees × BTC price | 10 min |
| DPIN | index points | Equal-weight basket of DePIN tokens (Coinbase OHLC) | 5 s (design) |
| TOKN | Mtok | OpenRouter model-catalog basket, archival captures only | research; no derivatives |
The V2 design table (docs/spec/V2_ARCHITECTURE.md §2.2) sets per-market freshness gates: oldest source age (CMPT 1,800 s, DRAM/NAND 86,400 s, GRID 360 s, HASH 900 s, DPIN 120 s), maximum spread between source times, and minimum coverage (e.g. 3 of 5 for CMPT). These are encoded as sourceAge, sourceSpread, heartbeat, minSources, sourceTotal in the registry MarketConfig and are checked by TrionPriceVerifierV2 against the signed payload. The oracle re-reads them from the registry (v2-runtime.ts refreshMarket) rather than from a local constant, and refuses to run for a market whose registered source list does not map one-to-one onto its approved collector identities.
TOKN is disabled by construction, not by a flag someone might flip: DeployV2.s.sol requires zero exposure limits, zero source configuration and an uninitialized funding origin for TOKN, and SeedOptionsV2.s.sol rejects TOKN as an option underlying.
What a market id binds
marketId = keccak256(abi.encode(symbol, methodologyHash, unitHash, multiplierWad, fixingMethodHash))
methodologyHash today is a label hash (keccak256("CMPT_METHODOLOGY_V2")), not a hash of a published methodology document, source list and weights. The spec calls for immutable, versioned methodology artifacts; the code currently binds a name. Consequence: two deployments could share CMPT_METHODOLOGY_V2 while computing the index differently, and nothing on chain would distinguish them. The DRAM/NAND unit question (design says $/GB, the live oracle emits index points relative to a 2019 base) is an example the design record itself flags as unresolved (§1.2). Never infer economic equivalence from a symbol or a decimal conversion.
Observation time versus publication time
A PricePayload carries four timestamps:
| Field | Meaning |
|---|---|
minSourceAt / maxSourceAt | Oldest and newest source observation times behind this index value. |
sampleAt | When the committee sampled and signed. |
validUntil | When the snapshot stops being usable for risk. The V2 publisher signs sampleAt + 15 s (calculatePricePayload); the local seeding script uses 300 s. After it passes, the matcher API reports the price as unavailable (503 V2_PRICE_UNAVAILABLE) rather than serving the old value. |
The rule (spec §2.2) is that republishing an unchanged daily observation may refresh sampleAt but must keep the original minSourceAt/maxSourceAt; a stale price cannot be laundered by re-signing it. The matcher does not sign V2 snapshots at all: it only reads what the verifier holds (index-feed.ts pollV2Snapshot, canonical-prices.ts) and checks minSourceAt ≤ maxSourceAt ≤ sampleAt ≤ now ≤ validUntil before serving it.
Where the source clock comes from
ObservationValidator.derive (publication-evidence.ts) never invents a source time. Every approved source carries an explicit clock policy in the operator's oracle policy file (v2-runtime.ts SourceIdentity):
source-issued: the provider supplied an observation timestamp (source_atin the collector row). Report-style sources (NYISO, DRAMeXchange, SF Compute, daily aggregators) are required to use this clock;refreshMarketrefuses alive-responsepolicy for them so retrieval time can never masquerade as a market observation.live-response: a current-quote HTTPS endpoint that was reviewed for its cache semantics (liveEndpointApprovalwithmaxCacheAgeSec). The effective observation time ismin(HTTP Date, requestStart − HTTP Age), both recorded in the row'sresponse_provenance; a missingDate/Ageheader, a mismatched URL, a non-HTTPS transport, or an age above the approved cache ceiling makes the row unusable.
In both cases a row older than the registry sourceAge at the sample time is unavailability, not a stale value carried forward: derive throws, and the observed-index engine's "degraded" state is likewise rejected ("no last-good/reference fallback permitted"). minSourceAt, maxSourceAt, sourceMask and sourceCount are computed from the surviving rows and the registered source ids; venues trimmed away by the CMPT blend count as evidence but not toward quorum, and the implemented blend weights must match the registered weightsPpm within rounding.
How the V2 feed is produced
flowchart LR
C[Collectors\nSQLite quotes + evidence] --> P[Publisher\nno oracle key]
C2[Signer's own collectors] --> S1[Signer 1\none key]
C3[Signer's own collectors] --> S2[Signer 2\none key]
C4[Signer's own collectors] --> S3[Signer 3\none key]
B[Matcher /v2/mark-evidence\nauthenticated book] --> P
B --> S1
B --> S2
B --> S3
P -- "observation window + checkpoint" --> S1
P -- "observation window + checkpoint" --> S2
P -- "observation window + checkpoint" --> S3
S1 -- "EIP-712 signature" --> P
S2 -- "EIP-712 signature" --> P
S3 -- "EIP-712 signature" --> P
P -- "2 of 3, simulate, journal, broadcast" --> V[(TrionPriceVerifierV2 / FundingEpochsV2)]
Independent one-key services. The publisher (publisher.ts V2MarketDataPublisher) holds no oracle signing key; assertProcessKeySeparation refuses to start if any OD_SIGNER_*_KEY is present in its environment, and its relayer account must not be a committee member. Each signer (signer.ts) holds exactly one OD_SIGNER_KEY, must not hold the relayer key, must be listed in the on-chain committee, listens on loopback only, and reads observations from its own read-only quotes database (OD_SIGNER_QUOTES_DB), not from request bodies. The policy file must name three distinct signer addresses on three distinct hosts. Public Anvil accounts are rejected on any chain other than 31337.
Every source needs a license gate. loadOracleRuntime refuses a market unless every approved source has a rightsApproval with scope oracle-publication, a reference, an unexpired expiresAtSec, and an evidence file whose SHA-256 matches the policy. The code comments are explicit that public reachability is not a license.
Canonical chain anchors. Startup and every committee refresh run beginV2Verification/finishV2Verification (core/src/v2-bindings.ts): the RPC's chain id, genesis hash and the verified block recorded in the deployment manifest must all match, and the read block is re-checked after the reads. A fork or a different chain behind the same RPC is a hard error, not a warning.
Complete, independently held slot history. Both publisher and signers sample every 5 s. Each signer records its own independent_receipts row per 5-second slot (its own derived index, its own copy of the authenticated matcher book). When the publisher asks for a signature it sends the observation window and the last mark checkpoint; validateWindow then requires that the supplied slots are exactly the slots the signer itself holds for that range ("coordinator omitted/reordered canonical independently available slots"), and validate requires that each observation's index, source identities, book hash and timing match the signer's own receipt. A signer that lacks history for a slot cannot sign it.
Exact continuous EMA checkpoint and authentic genesis. The mark M is an EMA of the qualified book premium (matcher/src/mark-sampler.ts, replayed by replayMarks). A window either starts at the approved markHistoryStartSec genesis for the current configuration or continues from a checkpoint the signer already holds with the identical hash. Arbitrary seeds and suffixes are refused ("Arbitrary EMA seed/suffix forbidden"); conflicting checkpoints for the same slot are refused ("Conflicting canonical EMA checkpoint"). A price snapshot additionally requires an actual two-sided book at the registered depth D and the 150 s warm-up; I is never signed as M ("I-as-M forbidden"), and the signed mark must be within the registered markBps band of I.
Monotonic sequence and anti-equivocation. sequence is the observation's millisecond sample time; a signer refuses a sequence at or below the verifier's accepted one, a funding epoch other than the next unfinalized one, and a fixing that already exists. Before signing, reserveSignature records the EIP-712 digest for market:kind:sequence|epoch|expiry in the signer's journal; a later request for the same key with a different digest is refused ("Refusing equivocation"). The signer only signs a payload it can recompute byte-for-byte from the authenticated observations (evidenceHash(expected) === evidenceHash(payload)).
2-of-3 coordination without keys. ThresholdSignerService posts the same request to all three endpoints, recovers each returned signature, checks it belongs to that endpoint's committee address, sorts by signer address and requires at least two. The publisher then simulates pushSnapshot / finalizeEpoch / pushFixing, journals the broadcast intent, sends with its relayer key, waits two confirmations and records the outcome; an ambiguous or reverted prior publication blocks further publication until an operator reconciles the journal.
Funding and fixings use the same evidence. Hourly funding (calculateHistoricalPayload("FundingPayload")) replays a complete 3,600 s window from the governed funding origin through the boundary observation and finalizes the next epoch only. Expiry fixings (fixing-service.ts, FIXING_WINDOW_SECONDS = 86_400, FIXING_SAMPLE_SECONDS = 300) require every one of the 288 five-minute samples in the 24 h window ending at expiry (576 over 48 h for the single grace attempt), each derived from the node's own approved observations under the series' frozen recipe; X = floor(sum / N). Missing samples are reported, never filled, and the running estimate (estimateFixing) is served as an estimate only. Fixings are published only for expiries that exist in the deployed option series list, and remain eligible for an already-listed expiry even after the market is disabled at the trading cutoff.
Explicit approval before any signing or transaction. With --observe-v2 the publisher only records observations and checkpoints. OD_PUBLISH=1 additionally requires OD_ORACLE_PUBLICATION_ENABLED=1, and the signer requires OD_ORACLE_SIGNING_ENABLED=1; both exit with an error before loading keys, databases or the network when that approval is absent. The maintainers' local start-up check (September 2026) observed that exit for both CLIs; that is a start-up guard, not evidence of a running feed.
Evidence
Every oracle quote writes a canonical-JSON evidence blob to overdrive/exchange/data/evidence/<SYMBOL>/<ts>.json, and evidenceHash = keccak256(canonicalJson) is returned by GET /v1/quote/:symbol on the oracle service (port 8795) and retrievable by GET /v1/evidence/:hash. A verifier can re-fetch the listed evidenceUrls and recompute, but the blob records what the collector saw at that moment; source APIs are not archival, so "recompute" often means "compare against the collector's own record".
In V2 the evidenceRoot field in PricePayload is evidenceHash of the whole signed observation window (calculatePricePayload), and bookRoot is the ABI-encoded hash of the depth levels the matcher served over the authenticated /v2/mark-evidence endpoint (mark-evidence.ts). Funding and fixing payloads commit to their windows the same way; the fixing manifest records each five-minute sample's timestamp, index price, source mask and quote evidence root, and archive captures live in an unhashed sidecar keyed by evidence hash.
Historical indices
build-index.ts produces daily OHLC CSVs per market by chain-linking generations (e.g. V100 → A100 → H100 for CMPT; SDRAM → … → DDR5 for DRAM) with trimmed-mean overlap ratios, and anchors CMPT to a checked-in constant in data/index/anchors.json (the first on-chain marketplace-blend level, $2.6152/GPU-hr dated 2026-09-18, recorded 2026-09-20) so rebuilds are deterministic. Limitations documented in METHODOLOGY.md §7: spot prices lag OEM contract prices, pre-September-2025 memory captures are weekly, GRID can be negative, TOKN exists only on its capture dates. These candles are served by the matcher as source=index and are not live V2 oracle data; the design forbids relabeling them as such. DRAM and NAND history is Trion's composite index built from publicly listed DRAMeXchange spot-table observations (Wayback captures since 2019); underlying vendor tables are not redistributed.
Published history window (owner decision 2026-09-22). Each series is baked (/assets/index/<SYM>.json) and served (/v1/daily/<SYM>) only from where the 2026-09 data audit deemed it RELIABLE: CMPT 2023-01-01, DRAM/NAND 2019-01-01, GRID 2015-01-01, HASH 2016-01-01, DPIN 2024-01-01. TOKN, B200 and OCPI are live-quote/reference only — no published history. Earlier rows stay on disk for research. Every daily row carries a quality provenance tag (obs | carry | recon | splice) in its components JSON; carried-forward bars render as gaps or dashed segments (never as solid observations), splice days get a marker, and bun run data:audit re-runs the density/discontinuity audit on demand (the oracle daily-close writer runs it as a post-write warning log).
Current implementation limits
- Matcher funding view. The matcher reports
fundingStatus: "UNAVAILABLE"and zero funding rates on/v1/markets; it does not readFundingEpochsV2. No perpetual is listed, so no user pays or receives funding today. - Methodology artifacts. Each series'
recipeHash = keccak256(abi.encode(sourceIds, weightsPpm, methodHash))binds the source set and weights it settles on, and the launch recipe is hash-committed inoverdrive/deployments/v2/reviewed/4663.options-launch.json. ThemethodologyHashinside the market id is still a label hash, not a hash of a published methodology document. The human-readable recipe is the methodology factsheet. - Not independently audited. No third party has audited the methodology, the collectors or the signer services. Governance and all five council seats are Trion-held at launch. Numbers in this page are hypothetical or taken from repository files at the time of writing.
Source trail: overdrive/exchange/data/METHODOLOGY.md, data/index/anchors.json; overdrive/exchange/oracle/README.md, src/main.ts, src/server.ts, src/publisher.ts (loadPublisherConfig, V2MarketDataPublisher.observe/publishAll/submit), src/signer.ts (validateSigningRequest, main), src/threshold-signer.ts (ThresholdSignerService, payloadDomain), src/v2-runtime.ts (SourceIdentity, refreshMarket, loadOracleRuntime, assertProcessKeySeparation, requireRiskPublication), src/publication-evidence.ts (PublicationStore, ObservationValidator.derive/validate/validateWindow, replayMarks, calculatePricePayload, calculateHistoricalPayload), src/fixing-service.ts (FIXING_WINDOW_SECONDS, FIXING_SAMPLE_SECONDS, calculateFixing, estimateFixing), src/types.ts (SourceResponseProvenance); overdrive/exchange/matcher/src/mark-evidence.ts, mark-sampler.ts, index-feed.ts, server.ts (/v2/mark-evidence, /v1/oracle/*); overdrive/exchange/core/src/v2-bindings.ts (beginV2Verification, finishV2Verification); overdrive/perps/script/DeployV2.s.sol (TOKN requirements); overdrive/deployments/v2/reviewed/4663.options-launch.json; docs/spec/V2_ARCHITECTURE.md §§2.1–2.6; docs/spec/FIXING_ORACLE_V2.md §2.1.
Repository-owned documentation · September 2026 · Educational material, not investment advice and not an audit.
Documentation
V2 · Live on Robinhood Chain