AI Agents

How to Build a Crypto Portfolio Tracker with AI Insights and Alerts

Build a personal crypto portfolio tracker that monitors your wallets and exchange balances, calculates P&L, and uses AI to generate weekly performance insights and rebalancing recommendations.

A
AI Agents Hubยท2026-03-06ยท5 min readยท833 words

Builder of AI agents, crypto trading bots, and open-source automation tools. Sharing practical guides on how to build, deploy, and profit from AI and DeFi technology.

Why Build Your Own Portfolio Tracker?

Apps like CoinStats and Delta are convenient, but they have limitations: they don't track DeFi positions well, they can't execute trades, and they definitely can't generate personalized AI analysis. Building your own gives you full control and a platform to add automation.

Architecture

Data Sources:
  โ”œโ”€โ”€ CEX APIs (Binance, Coinbase, Kraken)
  โ”œโ”€โ”€ On-chain wallets (Ethereum, Solana, BSC)
  โ””โ”€โ”€ DeFi protocols (Uniswap positions, Aave balances)
         โ†“
   Portfolio Aggregator
         โ†“
   P&L Calculator + Historical DB
         โ†“
   AI Analysis Engine (GPT-4o)
         โ†“
   Dashboard + Alerts (Telegram/Discord/Email)

Step 1: Aggregating Exchange Balances

import ccxt
from dataclasses import dataclass

@dataclass
class Position:
    asset: str
    quantity: float
    avg_buy_price: float
    current_price: float
    exchange: str
    
    @property
    def value_usd(self) -> float:
        return self.quantity * self.current_price
    
    @property
    def pnl_pct(self) -> float:
        return (self.current_price - self.avg_buy_price) / self.avg_buy_price * 100

def get_exchange_balances(api_configs: dict) -> list[Position]:
    positions = []
    
    for exchange_id, config in api_configs.items():
        exchange = getattr(ccxt, exchange_id)(config)
        
        try:
            balance = exchange.fetch_balance()
            tickers = exchange.fetch_tickers()
            
            for asset, data in balance['total'].items():
                if data > 0 and asset != 'USDT':
                    symbol = f"{asset}/USDT"
                    if symbol in tickers:
                        current_price = tickers[symbol]['last']
                        
                        # Get average buy price from trade history
                        avg_price = get_average_buy_price(exchange, symbol)
                        
                        positions.append(Position(
                            asset=asset,
                            quantity=data,
                            avg_buy_price=avg_price,
                            current_price=current_price,
                            exchange=exchange_id
                        ))
        except Exception as e:
            print(f"Error fetching {exchange_id}: {e}")
    
    return positions

Step 2: On-Chain Wallet Tracking

from web3 import Web3
from decimal import Decimal
import requests

# Track ERC-20 token balances
MORALIS_API_KEY = 'your_key'

def get_wallet_positions(wallet_address: str) -> list[Position]:
    """Get all token balances for an Ethereum wallet"""
    
    url = f"https://deep-index.moralis.io/api/v2.2/wallets/{wallet_address}/tokens"
    headers = {'X-API-Key': MORALIS_API_KEY}
    params = {'chain': 'eth', 'exclude_spam': 'true', 'exclude_unverified_contracts': 'true'}
    
    response = requests.get(url, headers=headers, params=params)
    tokens = response.json().get('result', [])
    
    positions = []
    for token in tokens:
        if float(token.get('usd_value', 0)) < 10:  # Skip dust
            continue
            
        positions.append(Position(
            asset=token['symbol'],
            quantity=float(token['balance_formatted']),
            avg_buy_price=0,  # Hard to track on-chain without full history
            current_price=float(token.get('usd_price', 0)),
            exchange=f"wallet:{wallet_address[:8]}..."
        ))
    
    return positions

Step 3: Portfolio Database

import sqlite3
from datetime import datetime

def init_db(db_path='portfolio.db'):
    conn = sqlite3.connect(db_path)
    conn.execute('''CREATE TABLE IF NOT EXISTS snapshots (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        timestamp TEXT NOT NULL,
        asset TEXT NOT NULL,
        quantity REAL,
        price_usd REAL,
        value_usd REAL,
        exchange TEXT
    )''')
    conn.execute('''CREATE TABLE IF NOT EXISTS daily_totals (
        date TEXT PRIMARY KEY,
        total_value_usd REAL,
        btc_value REAL,
        eth_value REAL
    )''')
    conn.commit()
    return conn

def save_snapshot(conn, positions: list[Position]):
    timestamp = datetime.now().isoformat()
    
    for pos in positions:
        conn.execute('''INSERT INTO snapshots 
            (timestamp, asset, quantity, price_usd, value_usd, exchange)
            VALUES (?, ?, ?, ?, ?, ?)''',
            (timestamp, pos.asset, pos.quantity, pos.current_price, 
             pos.value_usd, pos.exchange))
    
    conn.commit()

Step 4: AI Performance Analysis

from openai import OpenAI

openai = OpenAI()

def generate_ai_weekly_report(positions: list[Position], weekly_history: list) -> str:
    """Generate a personalized weekly portfolio analysis using GPT-4o"""
    
    total_value = sum(p.value_usd for p in positions)
    total_pnl = sum((p.current_price - p.avg_buy_price) * p.quantity for p in positions if p.avg_buy_price > 0)
    
    portfolio_summary = "\n".join([
        f"- {p.asset}: {p.quantity:.4f} @ ${p.current_price:.2f} = ${p.value_usd:.0f} ({p.pnl_pct:+.1f}%)"
        for p in sorted(positions, key=lambda x: x.value_usd, reverse=True)[:10]
    ])
    
    weekly_change = calculate_weekly_pnl(weekly_history)
    
    prompt = f"""
    Analyze this crypto portfolio and provide weekly insights:
    
    PORTFOLIO SUMMARY:
    Total Value: ${total_value:,.0f}
    Weekly P&L: ${weekly_change:+,.0f} ({weekly_change/total_value*100:+.1f}%)
    Unrealized P&L: ${total_pnl:+,.0f}
    
    TOP POSITIONS:
    {portfolio_summary}
    
    Please provide:
    1. Key performance drivers this week
    2. Risk assessment (concentration risk, correlated assets)
    3. Specific rebalancing suggestions if any allocation is > 30% of portfolio
    4. 3 actionable insights for next week
    
    Keep the response concise and specific.
    """
    
    response = openai.chat.completions.create(
        model="gpt-4o",
        messages=[{"role": "user", "content": prompt}]
    )
    
    return response.choices[0].message.content

Step 5: Automated Alerts

import telegram

bot = telegram.Bot(token=TELEGRAM_TOKEN)

async def check_and_alert(positions: list[Position]):
    alerts = []
    
    for pos in positions:
        # Large loss alert
        if pos.pnl_pct < -20:
            alerts.append(f"๐Ÿ”ด {pos.asset}: Down {pos.pnl_pct:.1f}% from avg buy")
        
        # Large gain โ€” consider taking profits
        if pos.pnl_pct > 100:
            alerts.append(f"๐ŸŸข {pos.asset}: Up {pos.pnl_pct:.1f}% โ€” consider taking some profits")
        
        # Concentration risk
        total = sum(p.value_usd for p in positions)
        if pos.value_usd / total > 0.40:
            alerts.append(f"โš ๏ธ {pos.asset}: {pos.value_usd/total:.0%} of portfolio โ€” consider diversifying")
    
    if alerts:
        message = "๐Ÿ“Š **Portfolio Alert**\n\n" + "\n".join(alerts)
        await bot.send_message(chat_id=TELEGRAM_CHAT_ID, text=message, parse_mode='Markdown')

# Schedule weekly reports
import schedule

schedule.every().monday.at("08:00").do(lambda: asyncio.run(send_weekly_report()))
schedule.every(30).minutes.do(lambda: asyncio.run(check_and_alert(get_all_positions())))

Sample Output

๐Ÿ“Š WEEKLY PORTFOLIO REPORT โ€” Week of March 6, 2026

Total Value: $47,832 (+$3,241 this week, +7.3%)

TOP PERFORMERS:
โœ… SOL: +18.2% this week (now 28% of portfolio)
โœ… ETH: +6.1%
โŒ BNB: -2.3%

AI INSIGHTS:
1. SOL now represents 28% of your portfolio โ€” above the 25% target threshold. 
   Consider trimming 10-15% of your SOL position and rotating into BTC for balance.
   
2. Your portfolio is heavily correlated โ€” 85% of value in ETH ecosystem assets.
   Adding BTC or non-correlated assets (LINK, ATOM) would reduce drawdown risk.
   
3. Your average buy prices suggest you DCA'd well during the Dec-Feb dip.
   Current unrealized gains of +$8,400 represent a 21% gain on invested capital.

RECOMMENDATION: Maintain current positions. Consider setting limit orders to 
take 15% profits on SOL above $280.

This tracker takes a weekend to build and provides insights that most paid apps can't match โ€” personalized, AI-generated, and fully automated.

Related Articles