Разработка системы мониторинга продаж NFT-коллекций
Floor price коллекции упала на 30% за последние 6 часов, а вы узнали об этом из Twitter на следующий день. Или наоборот — whale купил 50 токенов подряд, спровоцировал pump, а ваши алерты молчали. Для трейдеров, фаундеров коллекций и аналитиков NFT-рынка нужна система, которая видит эти события в течение минут, а не часов.
Источники данных
Архитектурный выбор №1: брать данные из API маркетплейсов или напрямую из blockchain events. У каждого подхода свои trade-offs.
API маркетплейсов (OpenSea, Blur, Reservoir) — проще в реализации, но зависимость от uptime третьей стороны и задержки агрегации. Reservoir — наиболее удобный вариант: единый API, покрывает Blur, OpenSea, X2Y2, LooksRare и другие, отдаёт нормализованные данные.
On-chain события — исчерпывающие и не зависят от маркетплейсов, но требуют разбора каждого протокола отдельно. Каждый маркетплейс имеет свой event signature:
| Маркетплейс | Contract | Event |
|---|---|---|
| OpenSea Seaport | 0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC |
OrderFulfilled(bytes32,address,address,address,(uint8,address,uint256,uint256)[],(uint8,address,uint256,uint256,address)[]) |
| Blur | 0x000000000000Ad05Ccc4F10045630fb830B95127 |
OrdersMatched(bytes32,bytes32) |
| LooksRare v2 | 0x0000000000E655fAe4d56241588680F86E3b2377 |
TakerBid(...) / TakerAsk(...) |
Для надёжной системы мониторинга — комбинация: Reservoir API для быстрых данных + on-chain парсинг как резервный источник и для верификации.
Архитектура системы
Data pipeline
Blockchain events (WebSocket via Alchemy/QuickNode)
│
▼
Event Parser Service ◄── Reservoir API (polling / webhooks)
│
▼
Message Queue (Redis Streams / BullMQ)
│
┌────┴────┐
▼ ▼
Metrics DB Alert Engine
(TimescaleDB) (rules evaluation)
│ │
▼ ▼
Analytics API Notification Service
(Telegram, Discord, Email)
TimescaleDB — PostgreSQL расширение для time-series данных. Автоматическое партиционирование по времени, функции для агрегации по временным окнам (time_bucket), компрессия старых данных. Для NFT метрик это существенно лучше обычного PostgreSQL.
CREATE TABLE nft_sales (
time TIMESTAMPTZ NOT NULL,
collection VARCHAR(42) NOT NULL,
token_id TEXT,
price_eth DECIMAL(20, 8),
price_usd DECIMAL(20, 4),
marketplace VARCHAR(20),
buyer VARCHAR(42),
seller VARCHAR(42),
tx_hash VARCHAR(66)
);
SELECT create_hypertable('nft_sales', 'time');
-- Floor price за последние 24 часа по часам
SELECT time_bucket('1 hour', time) AS bucket,
MIN(price_eth) AS floor,
COUNT(*) AS volume,
SUM(price_eth) AS total_volume_eth
FROM nft_sales
WHERE collection = $1
AND time > NOW() - INTERVAL '24 hours'
GROUP BY bucket
ORDER BY bucket;
Realtime floor price tracking
Floor price — не просто минимальная цена последней продажи. Это минимальная цена активного листинга. Для корректного расчёта нужен отдельный трекер листингов:
class FloorPriceTracker {
private listings = new Map<string, { price: bigint; seller: string }>()
onListing(tokenId: string, price: bigint, seller: string) {
this.listings.set(tokenId, { price, seller })
this.updateFloor()
}
onDelisting(tokenId: string) {
this.listings.delete(tokenId)
this.updateFloor()
}
onSale(tokenId: string) {
this.listings.delete(tokenId) // sold = delisted
this.updateFloor()
}
getFloor(): bigint {
return [...this.listings.values()]
.reduce((min, l) => l.price < min ? l.price : min, BigInt(Infinity))
}
}
Состояние листингов инициализируется при старте из Reservoir API, затем поддерживается через event stream.
Система алертов
Типы алертов
Floor price алерты: падение/рост floor более чем на X% за Y минут. Важно считать процентное изменение относительно rolling baseline, а не предыдущего значения — иначе один washтrade с низкой ценой сгенерирует ложный алерт.
Whale активность: один адрес купил N+ токенов за M часов. Whale в контексте коллекции — относительное понятие, порог зависит от supply.
Volume spike: объём торгов за последний час превышает N-сигма от среднего значения (rolling mean + std deviation за 7 дней).
Large single sale: продажа токена по цене, превышающей floor в K раз.
Wash trading detection: одинаковые токены быстро перепродаются между связанными адресами по растущим ценам. Простая эвристика: if sale.buyer == previous sale.seller и интервал < 10 минут — подозрительно.
Конфигурация правил
Правила алертов хранятся в БД, редактируются через UI без деплоя:
{
"collection": "0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D",
"alert_type": "floor_change",
"conditions": {
"direction": "down",
"threshold_percent": 15,
"window_minutes": 30
},
"notifications": ["telegram:@bayc_holder", "discord:webhook_url"]
}
Метрики коллекции и дашборд
Ключевые метрики для дашборда:
- Floor price (current, 24h change, 7d change)
- Total volume (24h, 7d, all time)
- Number of sales (24h)
- Unique buyers/sellers (24h)
- Average sale price vs. floor (spread)
- Holder distribution: top 10 holders % of supply, unique holders count
- Listing depth: количество листингов в диапазонах +5%, +10%, +20% от floor
Holder distribution обновляется реже — раз в час достаточно. Требует либо on-chain трекинга Transfer events, либо запроса к Alchemy/Moralis NFT API.
Доставка уведомлений
Telegram Bot: наиболее востребован в NFT-сообществе. telegraf или grammy библиотека, групповые чаты для проектных комьюнити, личные уведомления для индивидуальных трейдеров.
Discord Webhooks: стандарт для NFT-проектов. Форматированный embed с иконкой коллекции, ценой, ссылкой на токен на маркетплейсе.
Email: через SendGrid/Resend для сводных дайджестов — ежечасно или раз в день.
Throttling: не более 1 алерта одного типа в N минут на коллекцию, иначе при volatile рынке система спамит. Очередь с дедупликацией в Redis.
Сроки разработки
День 1: настройка data pipeline, интеграция Reservoir API + Alchemy WebSocket, первичная загрузка истории продаж.
День 2: TimescaleDB схема, базовые метрики и агрегации, floor price tracker.
День 3: движок алертов с базовыми правилами, интеграция Telegram и Discord уведомлений, базовый дашборд.
Итого 2-3 рабочих дня для системы с realtime мониторингом, алертами и дашбордом. Добавление сложных детекторов (wash trading, multi-collection корреляции) — ещё 1-2 дня.







