Разработка Telegram-бота отслеживания цен
Типичная задача: пользователь хочет получать уведомление, когда BTC упадёт ниже $60 000 или ETH вырастет выше $4 000. Варианты: постоянный polling цен или подписка на алерты. Ключевой вопрос — частота обновления. Для долгосрочных алертов (достаточно обновления раз в минуту) архитектура простая. Для HFT-мониторинга с задержкой в секунды — нужен WebSocket к биржевому стриму.
Стек и структура
Telegram Bot API ← → Bot server (Node.js/Python)
↓
Price data source (CoinGecko / Binance WS)
↓
PostgreSQL (пользователи, алерты)
↓
Job scheduler (cron / BullMQ)
Реализация на Node.js + Grammy
grammy — современная TypeScript библиотека для Telegram Bot API, лучший выбор для новых проектов:
npm install grammy @grammyjs/conversations ioredis
import { Bot, Context, session } from 'grammy'
import { conversations, createConversation } from '@grammyjs/conversations'
const bot = new Bot(process.env.BOT_TOKEN!)
// Команда добавления алерта
bot.command('alert', async (ctx) => {
await ctx.reply(
'Введите алерт в формате:\n`BTC > 70000` или `ETH < 3000`',
{ parse_mode: 'Markdown' }
)
})
// Парсинг алерта
bot.on('message:text', async (ctx) => {
const match = ctx.message.text.match(/^(\w+)\s*([<>])\s*(\d+(?:\.\d+)?)$/)
if (!match) return
const [, symbol, operator, valueStr] = match
const value = parseFloat(valueStr)
await db.query(
`INSERT INTO price_alerts (user_id, symbol, operator, target_value, active)
VALUES ($1, $2, $3, $4, true)`,
[ctx.from!.id, symbol.toUpperCase(), operator, value]
)
await ctx.reply(
`Алерт установлен: когда ${symbol.toUpperCase()} ${operator} $${value.toLocaleString()}, ` +
`пришлю уведомление.`
)
})
Источник данных: CoinGecko vs Binance
CoinGecko Free API — агрегированные цены, без регистрации, лимит 30 запросов/мин:
async function getPrices(symbols: string[]): Promise<Record<string, number>> {
// CoinGecko использует свои ID, не тикеры
// bitcoin, ethereum, solana, etc.
const ids = symbols.map(s => COINGECKO_IDS[s]).join(',')
const res = await fetch(
`https://api.coingecko.com/api/v3/simple/price?ids=${ids}&vs_currencies=usd`
)
const data = await res.json()
return Object.fromEntries(
symbols.map(s => [s, data[COINGECKO_IDS[s]]?.usd ?? 0])
)
}
Binance WebSocket — для реального времени, без лимитов на подключение:
import WebSocket from 'ws'
// Подписка на стрим нескольких пар сразу
const streams = ['btcusdt', 'ethusdt', 'solusdt']
.map(s => `${s}@miniTicker`)
.join('/')
const ws = new WebSocket(`wss://stream.binance.com:9443/stream?streams=${streams}`)
ws.on('message', (data) => {
const msg = JSON.parse(data.toString())
const ticker = msg.data
// ticker.s = 'BTCUSDT', ticker.c = текущая цена (close)
priceCache.set(ticker.s.replace('USDT', ''), parseFloat(ticker.c))
})
ws.on('close', () => {
// Reconnect через 5 секунд
setTimeout(connectBinanceStream, 5000)
})
Binance WebSocket даёт обновления в реальном времени (каждую секунду), что делает его предпочтительным для алертов с высокой точностью срабатывания.
Проверка алертов
// Запускается через cron каждые 30 секунд (или после каждого обновления цены)
async function checkAlerts(): Promise<void> {
const alerts = await db.query<Alert>(
`SELECT * FROM price_alerts WHERE active = true`
)
for (const alert of alerts.rows) {
const currentPrice = priceCache.get(alert.symbol)
if (!currentPrice) continue
const triggered =
(alert.operator === '>' && currentPrice > alert.target_value) ||
(alert.operator === '<' && currentPrice < alert.target_value)
if (triggered) {
await bot.api.sendMessage(
alert.user_id,
`🔔 Алерт сработал!\n${alert.symbol} сейчас $${currentPrice.toLocaleString()} ` +
`(цель: ${alert.operator} $${alert.target_value.toLocaleString()})`
)
// Деактивировать после срабатывания (или оставить активным — зависит от требований)
await db.query(
`UPDATE price_alerts SET active = false, triggered_at = NOW() WHERE id = $1`,
[alert.id]
)
}
}
}
База данных
CREATE TABLE price_alerts (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL, -- Telegram user ID
symbol VARCHAR(10) NOT NULL, -- 'BTC', 'ETH', etc.
operator CHAR(1) NOT NULL, -- '>' или '<'
target_value NUMERIC(20, 8) NOT NULL,
active BOOLEAN DEFAULT true,
created_at TIMESTAMPTZ DEFAULT NOW(),
triggered_at TIMESTAMPTZ
);
CREATE INDEX ON price_alerts(active, symbol) WHERE active = true;
Команды бота
| Команда | Описание |
|---|---|
/price BTC |
Текущая цена |
/alert BTC > 70000 |
Установить алерт |
/alerts |
Список активных алертов |
/remove 5 |
Удалить алерт по ID |
/subscribe BTC 1h |
Периодические обновления раз в час |
Деплой: VPS с 512 MB RAM достаточно для бота с несколькими тысячами пользователей. Для масштабирования — Redis для кэша цен и горизонтальное масштабирование воркеров через BullMQ.







