Docs / Schema

Canonical data model

Seven entities. One identity, three hourly snapshots on a shared key, one daily series entity, and two market-wide feeds that are not keyed to an asset at all.

Entity relationships

model overview
Asset (1) ──< MarketSnapshot      hourly,  assetId + observedAt
      (1) ──< SocialSnapshot      hourly,  assetId + observedAt
      (1) ──< SentimentSnapshot   hourly,  assetId + observedAt
      (1) ──< EcosystemSnapshot   daily,   assetId + asOf + series
      (1) ──< NewsItem            m:n,     confident mappings only

Narrative                          hourly,  not keyed to an asset

# The three hourly snapshots share a key. They are separate entities
# because they have separate nullability and separate meaning, not
# because they arrive separately.

Asset

The registry entry. Mutable — display fields and pointers change — but the id never does. Everything else in the model references it. 7,461 records.

idintegerCanonical Moonlytics identifier. Stable for the lifetime of the asset. The only join key.
symbolstringTicker as published. Display data — not unique, not a key.
namestringDisplay name as published. Changes on rebrand.
providerIdsmapProvider identifier per source, e.g. market aggregator id and metadata provider slug.
categoriesstring[]Sector tags inherited from the metadata provider. Multiple per asset.
channelsobjectPublic channel pointers plus their manual override flags.
createdAttimestampFirst appearance in the registry.
updatedAttimestampLast change to the record.

MarketSnapshot

One immutable row per asset per hour. Written once, never updated.

assetIdintegerReference to Asset.
observedAttimestampUTC, aligned to the top of the hour.
pricedecimalUSD.
marketCapdecimalUSD, circulating.
volume24hdecimalUSD, trailing 24 hours.
volatilitydecimalProvider volatility measure.
marketDominancedecimalPercent of total market capitalisation.
altRankinteger?Combined market and social rank. Nullable.

SocialSnapshot

Shares the key and the timestamp of the market snapshot, so market and social values for an asset at an hour are exactly aligned rather than approximately so.

assetIdintegerReference to Asset.
observedAttimestampUTC, shares the timestamp of the market snapshot for the same asset.
socialVolume24hdecimalPost count over the trailing 24 hours.
interactions24hdecimalEngagement on those posts.
socialDominancedecimalShare of all tracked crypto social activity, percent.

SentimentSnapshot

Carries the sentiment measure and the composite score computed at write time. The score is stored with its inputs so a historical value can be audited against what produced it.

assetIdintegerReference to Asset.
observedAttimestampUTC, aligned to the hourly grid.
sentimentinteger?0–100. Nullable when activity was too thin to classify.
scoredecimalComposite market-and-social score as computed at write time. Stored with its inputs.

EcosystemSnapshot

Daily grain, and deliberately a separate entity with a separate time field. An asOf date next to an observedAt timestamp is a visible reminder that these are different measurements on different clocks. 69 assets.

assetIdintegerReference to Asset.
asOfdateCollection date. Daily grain — deliberately not the hourly observedAt.
seriesenumchannel | community | repository | contributors | mentions
metricsmapSeries-specific fields. Documented per series on the ecosystem and developer activity pages.

NewsItem

Market-wide. Asset links are present only where entity mapping was unambiguous — an empty assetIds is a deliberate outcome, not a missing value.

idintegerMonotonic identifier, usable as a cursor.
summarystringGenerated, self-contained account of the event.
mediaUrlurl?Illustrative image.
createdDatetimestampUTC entry time into the feed.
assetIdsinteger[]Canonical assets confidently identified in the item. Empty when mapping was ambiguous.

Narrative

Not keyed to an asset at all. A narrative is an event cluster; attributing one to a specific asset is a downstream analysis with its own error rate.

idintegerTopic identifier within the snapshot.
snapshotAttimestampUTC. Shared by every topic in the snapshot.
topicstringShort headline for the cluster.
countintegerDistinct contributing sources. The ranking signal.
categorystringOpen string. The label set is dynamic.
summarystringParagraph-length account of the topic.

Modelling decisions worth knowing

  • Three hourly entities rather than one wide row. Market, social and sentiment have different nullability and different reasons to be absent. Splitting them keeps "no social data" distinguishable from "no observation".
  • Daily and hourly never share a timestamp field. The naming difference is the guard rail.
  • Scores are stored, not recomputed. A stored score is a historical fact about what the system said at the time.
  • Provider identifiers live on the asset, not on the observation. An observation references the canonical asset; provider mapping is a property of identity.
  • Nullability is part of the contract. Every ? in the tables above is real and occurs in production data.

What is not in the model

  • Network and contract address — planned, see asset metadata.
  • Venue-level market data — the layer receives aggregated figures.
  • On-chain state — not indexed by this layer.
  • Post-level or article-level content — aggregates and summaries only.
  • Order book, depth or execution data.

Field-level definitions and coverage per entity are on the individual data pages. Figures measured 2026-08-31.

Related