Developing a Telegram Bot for Portfolio Monitoring
Standard problem: a person has assets on several networks, in several protocols, and to understand the current picture—need to log into 5 different interfaces. A Telegram bot with commands /portfolio and alerts on price movement or liquidation risk—more convenient than any mobile dashboard.
What to Track and Where to Get Data
Portfolio monitoring bots can be divided by complexity:
Basic (token balances)—token balances on specified addresses. Data sources:
- Moralis Web3 API / Alchemy Token API—balances for all ERC-20 in one request
- CoinGecko / CoinMarketCap API—current prices to calculate USD value
Extended (DeFi positions)—account for LP positions in Uniswap, collateral/debt in Aave/Compound, staking. Here you need either protocol-specific calls or aggregators:
- DeBank API—aggregates positions across 100+ protocols, but paid for serious volumes
- Zerion API—similarly, best EVM coverage
- Direct calls to Aave contracts (
getUserAccountData), Uniswap V3 (positions)
Alerts: Main Scenarios
Price alert: token X fell/rose by Y%
Liquidation: health factor in Aave < 1.2
Large tx: incoming/outgoing transaction > $N
Whale movement: wallet from watchlist made a transaction
New token: unknown token received to wallet
For price alerts—don't poll API every minute. Subscribe to WebSocket stream from exchange (Binance, Bybit have free WebSocket feeds) or use Chainlink price feed events.
For on-chain events—Alchemy/QuickNode webhooks on user addresses more reliable than self-polling.
Bot Architecture
Telegram Bot API (polling or webhook)
↓
Bot handler (Python/aiogram or Node.js/grammY)
↓
Portfolio aggregator service
├── Price service (CoinGecko/exchange WebSocket)
├── Balance fetcher (Moralis/Alchemy)
└── DeFi positions (DeBank/direct calls)
↓
Database (PostgreSQL / Redis)
├── user wallets
├── alert configs
└── cached portfolio snapshots
↓
Alert scheduler (cron / event-driven)
Redis is used for price and balance cache—no need to hit API on every /portfolio request. Cache TTL: prices 30 seconds, balances 2–5 minutes.
Implementation of Basic Commands
@router.message(Command("portfolio"))
async def portfolio_command(message: Message):
user = await get_user(message.from_user.id)
if not user.wallets:
await message.answer("Add wallet: /addwallet 0x...")
return
portfolio = await aggregator.get_portfolio(user.wallets)
text = format_portfolio(portfolio) # formatting with emoji and table
await message.answer(text, parse_mode="HTML")
For multi-chain users—parallel requests via asyncio.gather, otherwise response time will be unacceptable.
Security: What Matters
User sends public addresses—no private keys. This should be explicitly stated in onboarding. Store addresses in DB, never ask for seed phrase or private key—and add warning message if user tries to send something resembling a private key.
Rate limiting on commands—Telegram has a limit on message sending (30/sec per bot, 1/sec per user), exceeding returns 429 Too Many Requests.
What Gets Done in 2–3 Days
Telegram bot with commands to add/remove wallets, /portfolio command with balance aggregation and USD value, price alerts by specified thresholds, hosting setup (Railway, Fly.io or your VPS), connection to selected APIs for data. Network support—Ethereum + any EVM-compatible, Bitcoin/Solana—on request, require separate adapters.







