📸 Sistema de Snapshots e Preços
Visão Geral
O sistema de snapshots é uma camada de cache inteligente que armazena preços históricos de criptomoedas na base de dados local, reduzindo drasticamente as chamadas à API do CoinGecko e melhorando performance.
Por que Snapshots?
Problema sem cache:
Análise de portfólio com 100 datas × 5 ativos = 500 chamadas API
CoinGecko rate limit: ~30 chamadas/minuto
Tempo necessário: ~17 minutos 😱
Erro 429 (Too Many Requests): frequente ⚠️
Solução com snapshots:
1ª execução: 500 chamadas (salva tudo na BD)
2ª execução: 0 chamadas (lê tudo da BD) ⚡
Tempo: < 1 segundo 🚀
Arquitetura
Camadas de Cache
┌─────────────────────────────────────────┐
│ 1. Session Cache (Memória) │
│ Dicionário Python temporário │
│ Duração: 1 sessão Streamlit │
└─────────────────────────────────────────┘
↓ (se não encontrar)
┌─────────────────────────────────────────┐
│ 2. Database Cache (PostgreSQL) │
│ Tabela t_price_snapshots │
│ Duração: Permanente │
└─────────────────────────────────────────┘
↓ (se não encontrar)
┌─────────────────────────────────────────┐
│ 3. CoinGecko API (Externa) │
│ Endpoints: /simple/price, /history │
│ Rate limit: ~30/min grátis │
└─────────────────────────────────────────┘
Fluxo de Dados
Pedido: Preço de ADA em 2025-01-15
1. Verificar Session Cache
└─> _prices_session_cache[(ADA, 2025-01-15)]
└─> Se encontrar: RETURN ⚡
2. Verificar Database
└─> SELECT price FROM t_price_snapshots
WHERE asset_id=X AND snapshot_date='2025-01-15'
└─> Se encontrar:
- Guarda em Session Cache
- RETURN ⚡
3. Chamar CoinGecko API
└─> GET /coins/cardano/history?date=15-01-2025
└─> Parse response
- Guarda em Database
- Guarda em Session Cache
- time.sleep(2) # Rate limiting
- RETURN
Tabela t_price_snapshots
Schema
CREATE TABLE t_price_snapshots (
snapshot_id SERIAL PRIMARY KEY,
asset_id INTEGER NOT NULL REFERENCES t_assets(asset_id),
snapshot_date DATE NOT NULL,
price NUMERIC(20, 8) NOT NULL,
vs_currency VARCHAR(10) NOT NULL DEFAULT 'eur',
created_at TIMESTAMP DEFAULT NOW(),
-- Constraint: 1 preço por ativo/data/moeda
UNIQUE(asset_id, snapshot_date, vs_currency)
);
-- Índice para lookup rápido
CREATE INDEX idx_price_snapshots_lookup
ON t_price_snapshots(asset_id, snapshot_date, vs_currency);
Características
- ✅ Único: Não permite duplicatas (UNIQUE constraint)
- ✅ Indexado: Queries extremamente rápidas
- ✅ Preciso: NUMERIC(20,8) para 8 casas decimais
- ✅ Multi-moeda: Suporta EUR, USD, etc
- ✅ Auditável: Timestamp de quando foi criado
Integração CoinGecko
Cliente CoinGecko (services/coingecko.py)
1. Mapeamento Símbolo → ID
Problema: CoinGecko usa IDs internos, não símbolos
Símbolo → ID
ADA → cardano
BTC → bitcoin
ETH → ethereum
2. Preços Atuais
Endpoint: GET /simple/price
ids: bitcoin,ethereum,cardano
vs_currencies: eur
3. Preços Históricos
Endpoint: GET /coins/{id}/history
date: 31-10-2025 # DD-MM-YYYY
Performance
Benchmarks
Cenário: Portfolio com 50 datas, 3 ativos
| Método | Tempo | Chamadas API |
|---|---|---|
| Sem cache | ~300s | 150 |
| Cache BD apenas | ~5s | 0 (após 1ª vez) |
| Cache BD + Session | ~0.8s | 0 (após 1ª vez) |
| Prefetch + Bulk | ~0.5s | 0 (após 1ª vez) |
Boas Práticas
✅ DO
- Usar prefetch para múltiplas datas
- Implementar bulk queries quando possível
- Respeitar rate limits (sleep 2s)
- Logar cache hits/misses para monitorização
- Validar
coingecko_idantes de chamar API - Usar session cache para evitar duplicatas
❌ DON'T
- ❌ Chamar API em loop sem sleep
- ❌ Fazer queries 1-a-1 quando bulk é possível
- ❌ Assumir que preço sempre existe
- ❌ Ignorar erros de API (retry é crítico)
- ❌ Deixar cache crescer infinitamente sem limpeza