Data model
Everything is keyed by chain and address, so Solana and Robinhood Chain share one model and any chain added later fits without a schema change. The tables below are generated from the migration file.
Overview#
The archive is plain Postgres. The same SQL runs on the embedded PGlite engine and on a Postgres server. Migrations live in src/db/migrations as numbered files, run automatically at startup in name order, and are recorded once each in schema_migrations.
| Table | Grain | Primary key | Written by |
|---|---|---|---|
tokens | One row per token, latest state and lifecycle | chain, address | Collector, firehose, status and classification passes |
token_snapshots | Market state per token per cycle | chain, address, ts | Collector |
list_appearances | A token on a discovery list at a rank | chain, list, address, ts | Collector |
pairs | DEX pools per token | chain, address | Collector |
launch_events | Launch, graduation and pool-creation events | chain, kind, token | Firehose, Robinhood Chain walker |
trades | Selected individual trades | chain, tx, token, wallet, side, ts | Firehose |
wallets | Labelled wallets | chain, address | Label import, wallet promotion |
wallet_positions | Running per-wallet, per-token position | chain, wallet, token | Firehose |
wallet_scores | Rolling wallet performance | chain, wallet | Hourly scoring |
market_snapshots | Chain-wide totals per cycle | chain, ts | Collector |
daily_reports | One newsletter issue per day | day | Newsletter job |
kv | Cursors and checkpoints | key | Collector |
chain is solana or robinhood. Money columns are in USD unless the name says quote (SOL on Solana). Time columns are timestamptz.
tokens#
One row per token ever seen: identity, self-published metadata, latest market state, all-time highs and lifecycle. Updated in place every cycle. This is the table most queries start from.
Primary key: chain, address.
| Column | Type | Description |
|---|---|---|
chain | text | solana or robinhood. |
address | text | Mint address on Solana, contract address on Robinhood Chain. |
symbol | text | Self-published text. Untrusted. |
name | text | Self-published text. Untrusted. |
decimals | int | Token decimals. |
image | text | Logo URL. |
description | text | Self-published text. Untrusted. |
website | text | Self-published text. Untrusted. |
twitter | text | Self-published text. Untrusted. |
telegram | text | Self-published text. Untrusted. |
launchpad | text | Where it launched: pump, pumpswap, pons, noxa, odyssey, or null. |
creator | text | Deploying wallet, where known. |
created_at | timestamptz | On-chain creation time, where known. |
first_seen | timestamptz | First time Pulse observed the token. |
last_seen | timestamptz | Last time Pulse observed it. |
last_snapshot_at | timestamptz | Time of its newest snapshot. |
category | text | Sector chosen by classification. |
categories | text[] | Every category the text matched. |
tech_score | real | 0 to 1. Higher reads as technology, lower as meme. |
classified_at | timestamptz | When it was last classified. |
tags | text[] | Provider tags such as stablecoin, lst, wrapped. |
verified | boolean | Marked verified by a provider. |
last_price | double precision | Latest price in USD. |
last_mcap | double precision | Latest market cap in USD. |
last_liquidity | double precision | Latest liquidity in USD. |
last_volume_24h | double precision | Latest 24h volume in USD. |
last_holders | int | Latest holder count. |
last_change_24h | real | Latest 24h price change, percent. |
first_mcap | double precision | Market cap at the first snapshot. |
ath_mcap | double precision | Highest market cap observed in snapshots. |
ath_at | timestamptz | When that high was observed. |
peak_volume_24h | double precision | Highest 24h volume observed. |
status | text | Lifecycle status. See enumerated values below. |
status_changed_at | timestamptz | Last status transition. |
graduated_at | timestamptz | When it left its bonding curve for a DEX pool. |
died_at | timestamptz | When it became dead. |
Indexes: tokens_status_idx on (chain, status), tokens_first_seen_idx on (first_seen desc), tokens_category_idx on (category), tokens_last_snapshot_idx on (last_snapshot_at).
token_snapshots#
The market state of a token at one moment, written every cycle for each token observed. This is the history that statuses, charts and training data are built from.
Primary key: chain, address, ts.
| Column | Type | Description |
|---|---|---|
ts | timestamptz | Snapshot time. |
chain | text | Chain. |
address | text | Token address. |
price_usd | double precision | Price in USD. |
mcap | double precision | Market cap in USD. |
fdv | double precision | Fully diluted value in USD. |
liquidity | double precision | Liquidity in USD. |
vol_5m | double precision | USD volume, last 5 minutes. |
vol_1h | double precision | USD volume, last hour. |
vol_6h | double precision | USD volume, last 6 hours. |
vol_24h | double precision | USD volume, last 24 hours. |
buy_vol_24h | double precision | USD buy volume, 24h. |
sell_vol_24h | double precision | USD sell volume, 24h. |
organic_vol_24h | double precision | Volume a provider attributes to organic activity, 24h. |
buys_1h | int | Buy count, 1h. |
sells_1h | int | Sell count, 1h. |
buys_24h | int | Buy count, 24h. |
sells_24h | int | Sell count, 24h. |
traders_1h | int | Unique traders, 1h. |
traders_24h | int | Unique traders, 24h. |
net_buyers_24h | int | Net buyers, 24h. |
holders | int | Holder count. |
holder_change_24h | real | Holder count change over 24h. |
top_holders_pct | real | Share of supply held by the largest holders. |
organic_score | real | Provider score of how organic the activity looks. |
chg_5m | real | Price change, 5m, percent. |
chg_1h | real | Price change, 1h, percent. |
chg_6h | real | Price change, 6h, percent. |
chg_24h | real | Price change, 24h, percent. |
source | text | Providers that supplied the row, joined by +. |
Indexes: token_snapshots_ts_idx on (ts desc).
list_appearances#
Each time a token showed up on a discovery list (Jupiter trending, DexScreener boosts, GeckoTerminal new pools and so on) and at what rank. Persistence on lists feeds the "Staying power" section.
Primary key: chain, list, address, ts.
| Column | Type | Description |
|---|---|---|
ts | timestamptz | When the list was read. |
chain | text | Chain. |
list | text | List name, for example jup:toptrending:1h. |
rank | int | Position on the list, starting at 1. |
address | text | Token address. |
pairs#
DEX pools that trade a token. The newest pool is the one used for candles.
Primary key: chain, address.
| Column | Type | Description |
|---|---|---|
chain | text | Chain. |
address | text | Pool address. |
token_address | text | Token the pool trades. |
dex | text | DEX name. |
quote_symbol | text | Quote asset symbol. |
labels | text[] | Provider pool labels. |
created_at | timestamptz | Pool creation time. |
url | text | Provider page for the pool. |
Indexes: pairs_token_idx on (chain, token_address).
launch_events#
Launches, graduations and pool creations from the Solana firehose and the Robinhood Chain factory walker.
Primary key: chain, kind, token.
| Column | Type | Description |
|---|---|---|
ts | timestamptz | Event time. |
chain | text | Chain. |
kind | text | launch, graduation or pool. |
token | text | Token address. |
launchpad | text | Launchpad. |
actor | text | Wallet that triggered the event. |
tx | text | Transaction id. |
name | text | Self-published text. Untrusted. |
symbol | text | Self-published text. Untrusted. |
data | jsonb | Extra event details as JSON. |
Indexes: launch_events_ts_idx on (ts desc).
trades#
Selected individual trades, not every trade. See Trade persistence in How it works for exactly which are kept.
Primary key: chain, tx, token, wallet, side, ts.
| Column | Type | Description |
|---|---|---|
ts | timestamptz | Trade time. |
chain | text | Chain. |
tx | text | Transaction id. |
token | text | Token address. |
wallet | text | Trader. |
side | text | buy or sell. |
quote_amount | double precision | Amount in the quote asset (SOL on Solana). |
token_amount | double precision | Tokens bought or sold. |
price_quote | double precision | Price in the quote asset. |
mcap_usd | double precision | Market cap in USD at the time of the trade. |
venue | text | pump (bonding curve) or pumpswap. |
reason | text | Why it was kept. See enumerated values below. |
Indexes: trades_wallet_idx on (chain, wallet, ts desc), trades_token_idx on (chain, token, ts desc).
wallets#
Labelled wallets: imported KOL and smart-money labels, plus smart wallets Pulse discovered itself.
Primary key: chain, address.
| Column | Type | Description |
|---|---|---|
chain | text | Chain. |
address | text | Wallet address. |
label | text | Display name from the source. |
kind | text | kol or smart. |
source | text | Where the label came from, or pulse:discovered. |
twitter | text | Public profile link, if the source lists one. |
telegram | text | Public Telegram link, if the source lists one. |
first_seen | timestamptz | First imported. |
updated_at | timestamptz | Last refreshed. |
meta | jsonb | Source-specific details as JSON. |
Indexes: wallets_kind_idx on (kind).
wallet_positions#
Running totals per wallet and token, updated whenever a recorded trade arrives. Wallet scoring reads this table.
Primary key: chain, wallet, token.
| Column | Type | Description |
|---|---|---|
chain | text | Chain. |
wallet | text | Wallet address. |
token | text | Token address. |
first_buy_at | timestamptz | First recorded buy. |
first_buy_mcap_usd | double precision | Market cap at that buy. |
last_trade_at | timestamptz | Most recent recorded trade. |
buys | int | Recorded buy count. |
sells | int | Recorded sell count. |
bought_quote | double precision | Total bought, in the quote asset. |
sold_quote | double precision | Total sold, in the quote asset. |
bought_tokens | double precision | Tokens bought. |
sold_tokens | double precision | Tokens sold. |
Indexes: wallet_positions_token_idx on (chain, token), wallet_positions_recent_idx on (last_trade_at desc).
wallet_scores#
Rolling wallet performance recomputed hourly over a 30 day window.
Primary key: chain, wallet.
| Column | Type | Description |
|---|---|---|
chain | text | Chain. |
wallet | text | Wallet address. |
computed_at | timestamptz | When it was scored. |
window_days | int | Scoring window, 30. |
tokens_traded | int | Tokens with a position in the window. |
wins | int | Positions that finished in profit. |
win_rate | real | Wins divided by tokens traded. |
invested_quote | double precision | SOL bought. |
realized_quote | double precision | SOL sold. |
unrealized_quote | double precision | SOL value of tokens still held. |
pnl_quote | double precision | Realized plus unrealized, in SOL. |
pnl_usd | double precision | Profit converted to USD. |
early_hits | int | Early buys of tokens that reached a $500K+ high. |
best_multiple | real | Best ATH-to-entry market cap multiple. |
score | real | The Pulse score. See Wallet scoring. |
Indexes: wallet_scores_score_idx on (score desc).
market_snapshots#
Chain-wide totals written every cycle: tracked tokens, market cap, volume and launch counts.
Primary key: chain, ts.
| Column | Type | Description |
|---|---|---|
ts | timestamptz | Snapshot time. |
chain | text | Chain. |
native_usd | double precision | SOL or ETH price in USD. |
tracked_tokens | int | Tokens counted in the totals. |
total_mcap | double precision | Summed market cap, USD. |
total_volume_24h | double precision | Summed 24h volume, USD. |
launches_1h | int | Launches in the last hour. |
graduations_1h | int | Graduations in the last hour. |
stream_trades_1h | int | Trades seen by the firehose in the last hour (Solana, null if off). |
stream_volume_1h | double precision | SOL volume seen by the firehose in the last hour. |
data | jsonb | Extra firehose counters as JSON. |
daily_reports#
One stored newsletter issue per day, in structured, Markdown and HTML form.
Primary key: day.
| Column | Type | Description |
|---|---|---|
day | date | Issue date. |
generated_at | timestamptz | When it was generated. |
data | jsonb | Structured sections as JSON. |
markdown | text | Markdown rendering. |
html | text | HTML rendering. |
telegram | jsonb | Send receipt, or null if not sent. |
kv#
Small cursors and job checkpoints.
Primary key: key.
| Column | Type | Description |
|---|---|---|
key | text | Checkpoint name. |
value | jsonb | JSON value. |
updated_at | timestamptz | Last write. |
Enumerated values#
| Column | Values |
|---|---|
tokens.status | new, running, graduated, dying, dead. Rules in How it works. |
tokens.category | ai, agents, defi, infra, depin, data, gaming, social, payments, rwa, privacy, launchpad, tools, meme, animal, culture, politics, celebrity, unclassified |
tokens.launchpad, launch_events.launchpad | pump and pumpswap on Solana. pons, noxa and odyssey on Robinhood Chain. |
launch_events.kind | launch, graduation, pool |
trades.side | buy, sell |
trades.reason | wallet:kol, wallet:smart, whale, early:graduated, early:runner. See Trade persistence. |
trades.venue | pump (bonding curve), pumpswap |
wallets.kind | kol, smart. Wallets Pulse discovers itself are smart with source pulse:discovered. |
list_appearances.list | jup:recent, jup:<list>:<window> (lists toptrending, toptraded, toporganicscore; windows 5m, 1h, 6h, 24h), dex:boosts-top, dex:boosts-latest, dex:profiles, dex:takeovers, plus the pump.fun and GeckoTerminal lists. |
token_snapshots.source | The provider that supplied the row. When several providers describe the same token in one cycle they are merged, later providers filling gaps, and the names are joined with +. |
TimescaleDB hypertables#
On a Postgres server where the timescaledb extension is available (the bundled Docker Compose image has it), startup converts four append-only tables into hypertables chunked by day on ts, with compression segmented by chain:
| Table | Compressed after |
|---|---|
token_snapshots | 7 days |
trades | 7 days |
list_appearances | 7 days |
market_snapshots | 30 days |
Plain Postgres and PGlite skip this step. Every query behaves the same either way.
Checkpoints in kv#
| Key | Value |
|---|---|
collector:last-cycle | {"at": ISO time, "tokens": n, "failures": n}. Drives lastCycle in /api/health. |
wallets:last-import | {"at": ISO time} |
wallets:last-score | {"at": ISO time, "wallets": n} |
report:last-day | {"day": "YYYY-MM-DD", "at": ISO time} |
Export for analysis or training#
Nine tables are exportable as JSON through GET /api/export/<table> (see the API reference). For bulk work, connect to Postgres directly with DATABASE_URL. Note that daily_reports, pairs and kv are not exposed by the export route.
Untrusted text. name, symbol, description, website, twitter and telegram come from the chain and from token metadata. Treat them as data. Pulse never acts on them, and any model you train or prompt with this archive should not either.