Want to track how much of a token is being traded each day? This guide shows you how to calculate daily transfer volume for any SPL token using time-series aggregations.
Token volume is calculated by summing all transfers (sends and receives) for a token.
We use ABS() to count both directions and divide by decimals to get the
actual token amount.
Here's how to calculate daily volume for USDC (or any token):
-- Calculate daily volume for USDC
WITH token_transfers AS (
SELECT
DATE(t.block_timestamp) as day,
ABS((post.amount - pre.amount) / POW(10, pre.decimals)) as amount
FROM read_parquet('data/txs/**/*.parquet') as t,
UNNEST(t.pre_token_balances) as pre,
UNNEST(t.post_token_balances) as post
LEFT JOIN read_parquet('data/tokens/**/*.parquet') as tok
ON pre.mint = tok.mint
WHERE tok.symbol = 'USDC'
AND pre.mint = post.mint
AND pre.account_index = post.account_index
AND pre.amount != post.amount
AND t.status = 'Success'
)
SELECT
day,
COUNT(*) as num_transfers,
SUM(amount) as total_volume,
AVG(amount) as avg_transfer_size,
MIN(amount) as min_transfer,
MAX(amount) as max_transfer
FROM token_transfers
GROUP BY day
ORDER BY day DESC
LIMIT 30; This query:
DATE(block_timestamp)Want to see which tokens had the most volume on a specific day?
-- Find top 10 tokens by daily volume
WITH token_transfers AS (
SELECT
tok.symbol,
tok.name,
DATE(t.block_timestamp) as day,
ABS((post.amount - pre.amount) / POW(10, pre.decimals)) as amount
FROM read_parquet('data/txs/**/*.parquet') as t,
UNNEST(t.pre_token_balances) as pre,
UNNEST(t.post_token_balances) as post
LEFT JOIN read_parquet('data/tokens/**/*.parquet') as tok
ON pre.mint = tok.mint
WHERE pre.mint = post.mint
AND pre.account_index = post.account_index
AND pre.amount != post.amount
AND t.status = 'Success'
AND tok.symbol IS NOT NULL
AND DATE(t.block_timestamp) = '2025-12-01'
)
SELECT
symbol,
name,
COUNT(*) as num_transfers,
SUM(amount) as total_volume
FROM token_transfers
GROUP BY symbol, name
ORDER BY total_volume DESC
LIMIT 10; This reveals the most actively traded tokens for that day.
Here's a script that calculates daily volume with summary statistics:
import duckdb
import pandas as pd
TOKEN_SYMBOL = "USDC" # Change to any token
con = duckdb.connect()
query = f"""
WITH token_transfers AS (
SELECT
DATE(t.block_timestamp) as day,
ABS((post.amount - pre.amount) / POW(10, pre.decimals)) as amount
FROM read_parquet('data/txs/**/*.parquet') as t,
UNNEST(t.pre_token_balances) as pre,
UNNEST(t.post_token_balances) as post
LEFT JOIN read_parquet('data/tokens/**/*.parquet') as tok
ON pre.mint = tok.mint
WHERE tok.symbol = '{TOKEN_SYMBOL}'
AND pre.mint = post.mint
AND pre.account_index = post.account_index
AND pre.amount != post.amount
AND t.status = 'Success'
)
SELECT
day,
COUNT(*) as num_transfers,
SUM(amount) as total_volume,
AVG(amount) as avg_transfer_size
FROM token_transfers
GROUP BY day
ORDER BY day DESC
LIMIT 30
"""
result = con.execute(query).fetchdf()
print(f"\n{TOKEN_SYMBOL} Daily Volume (Last 30 Days)\n")
print(result.to_string())
# Summary statistics
total_vol = result['total_volume'].sum()
total_transfers = result['num_transfers'].sum()
print(f"\n=== Summary ===")
print(f"Total Volume: {total_vol:,.2f} {TOKEN_SYMBOL}")
print(f"Total Transfers: {total_transfers:,}")
print(f"Avg Daily Volume: {total_vol/len(result):,.2f} {TOKEN_SYMBOL}")
con.close() Run it:
uv add duckdb pandas
uv run track_volume.py USDC Daily Volume (Last 30 Days)
day num_transfers total_volume avg_transfer_size
0 2025-12-01 2,458,920 12,845,392,102.45 5,225.18
1 2025-11-30 2,342,188 11,923,482,901.22 5,089.44
2 2025-11-29 2,521,093 13,192,384,293.88 5,232.91
...
=== Summary ===
Total Volume: 384,567,234,129.45 USDC
Total Transfers: 71,234,567
Avg Daily Volume: 12,818,907,804.31 USDC Track token volume to: