vike
ConceptsData

cohort

The cohort store kind: its columns, partitioning, what venue and symbol mean for it, and the producers that write it.

cohort is one of the shapes the historical store holds. A stored series lives under a kind= path segment, and that segment is the layout contract its producers and its consumers have to agree on: the columns, how the leaf is partitioned, and what venue and symbol mean for this particular kind. All of it is below, rendered from the same table the runtime dispatches on.

One decoded row becomes a crate::cohort_log::CohortRow. The codec that owns the schema is CohortCodec; the store writes through append_cohort and reads through scan_cohort, and parts carry the Parquet metadata key vike.schema.cohort.

Identity

venue = the EXCHANGE whose positions were graded (hyperliquid), never the metrics service that served the grading; symbol = the ASSET (BTC), which is ALSO the asset row column. Every other dimension — axis, label, grading, label basis — is a COLUMN and not a path segment, because one decode context string cannot re-inject five

This is the field most often assumed and most often wrong, which is why it is stated per kind rather than once for the store.

Columns

Every Arrow column today's writer emits, in schema order, with the codec flavor that picks its Arrow type and nullability.

ColumnFlavorNote
tsi64—
assetstr—
axisstr—
cohortstr—
gradingstr—
label_basisstr—
long_usdf64—
total_usdf64—

Layout

PropertyValue
Partitioningsymbol
Grouped formno — one series per part
In the cross-kind tick reportno

Traps

⚠ A LONG ROW on purpose — the label taxonomy is DATA, not schema. crates/vike-backfill/src/vikedata/parse.rs's SIZE_COHORTS (12), PNL_COHORTS (12), PNL_UNRANKABLE (2) and TIER_COHORTS (10) admit 36 labels between them, each carrying the two wire numbers this row stores: 73 columns at one grading, and 129 once the pnl ladder is graded three ways (Grading's variants; the size and tier axes are ungraded), against a store whose widest kind — chain — has 16. crates/vike-data/tests/store_kind_gate.rs's every_row_matches_its_codec_field_list compares [StoreKind::columns] VERBATIM against the codec's field list, so a column-per-label schema would make every taxonomy revision — one new rung, one retired bucket — a SCHEMA MIGRATION. The long row keeps the label in the cohort column, so a taxonomy change writes rows rather than columns. ⚠ NOT grouped, and grouping is not the escape it looks like: crates/vike-data/src/series.rs's SeriesId makes symbol and group ALTERNATIVES (exactly one is meaningful, and symbol is EMPTY when grouped), so a grouped form RELOCATES the symbol dimension rather than adding one. Spending it on the cohort label would leave the ASSET homeless — one read of group=Whale would merge every asset's rows under that label. ⚠ grading and label_basis are per-FETCH facts, and they are columns for the reason the other five dimensions are: the three gradings produce SHAPE-IDENTICAL rows over the same (venue, asset) series at the same ts, so without them a re-graded fetch silently ALIASES a realized one — the same batch of hours, overwriting nothing and distinguishable by nothing. The commit key carries both for the same reason; crates/vike-data/src/cohort_rec.rs's commit_key is where that argument is made against the code that builds it. ⚠ NOT the same subject as perp_metrics below, despite both being called open interest in conversation: this kind is a GRADED POSITIONING panel from a metrics service (who is long, by cohort), while that one is the venue's own per-interval context for a perp. ⚠ ONE producer: CohortRecorder is the write-side entry point this series has, in the shape ChainRecorder and PropertiesRecorder already use, and crates/vike-backfill/src/vikedata/ingest.rs's backfill_cohort is what feeds it — the data.vike.io cohort-metrics collector, behind vike-backfill's vikedata feature. ⚠ Its idempotency is therefore BATCH-level on the WINDOW, like every other collector here: re-running one command is a no-op, two OVERLAPPING windows are two keys and their shared hours land twice.

Pinned rather than fixed: the contradiction is recorded with both sides named, so a reader meets it here instead of in production.

Known producers

Idempotency in this store is batch-level and keyed on a commit-key string, never on per-row value dedup. Two producers that collide on one template silently no-op each other's appends, and a producer that changes its template re-admits history it already wrote — both invisible unless the shapes are written side by side.

ProducerCommit-key template
crates/vike-data/src/cohort_rec.rscohort:{venue}:{asset}:{axis}:{grading}:{label_basis}:{start_ms}-{end_ms}

⚠ That list is observed, not exhaustive, and no gate can make it so: a commit key is an ordinary string argument, so proving the list complete would mean following every call in the workspace. What is gated per listed entry is that the producer exists and spells the template verbatim. Read it as "these producers write this kind", never as "only these do".

Where this comes from

vike_data::store_kind::STORE_KINDS, exported as rosters.json at every release. The store's own gate derives the live kind set from the code — the series directory literals, the compaction dispatch and the write verbs — and fails until a new kind has a row, so this page cannot describe a shape the store does not write.

On this page