Розробка 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, тощо
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 щопівхвилини (або після кожного оновлення ціни)
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', тощо
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.







