Análise de dados financeiros · PWA · Visualização de dados
Cripto Terminal
Um terminal de criptoativos que mostra o risco da carteira inteira e explica cada métrica que calcula
Projeto privado — sem link público
- Cliente
- Produto pessoal
- Cargo
- Engenheiro de Software Full-stack — projeto independente
- Ano
- 2026
- Tecnologias
- Next.js 16
- React 19
- TypeScript
- Tailwind CSS 4
- TanStack Query
- Zustand
- Zod
- lightweight-charts
- Recharts
- Radix UI
- Vitest































Contexto
Aplicação web progressiva (PWA) mobile-first de análise de criptoativos, desenvolvida com Next.js 16, React 19 e TypeScript sobre dados reais de mercado das APIs públicas da CoinGecko e da Binance. Uso pessoal em círculo fechado, com acesso por código. A interface usa linguagem de ferramenta de análise: mede e descreve, sem recomendar compra ou venda.
Desafio
As ferramentas gratuitas em português que avaliei mostram preço e pesos, mas não o risco da carteira consolidada, e raramente informam fórmula, janela ou fonte dos indicadores. As APIs públicas de mercado têm limites de requisição restritos (HTTP 429), o que exige cache, controle de taxa e sinalização de dado defasado para a interface continuar utilizável.
Resultados
- testes automatizados (Vitest) cobrindo indicadores, risco, carteira e acesso
- 237
- métricas documentadas com fórmula, janela e armadilha
- 23
- ativos cobertos pelo screener (a tela Mercado exibe o top 100)
- 250
- chamada por minuto por moeda à CoinGecko para os 250 ativos, com cache compartilhado entre telas e usuários
- 1
Principais entregas
- 01Implementei em TypeScript puro, com testes Vitest, o painel de risco de 90 dias da carteira (volatilidade anualizada, Sharpe, Sortino, drawdown máximo, VaR 95%, beta em relação ao BTC, HHI e matriz de correlação), que reúne o risco da carteira consolidada em uma única tela.
- 02Desenvolvi gráficos de candles com lightweight-charts, de 15 minutos a 1 semana, com indicadores calculados em TypeScript (SMA 20, EMA 50, Bandas de Bollinger, RSI e MACD em painéis) e fallback sinalizado para candles diários da CoinGecko quando o ativo não tem par na Binance.
- 03Criei um screener sobre o top 250 por market cap, com filtros de market cap, variação de 24h, RSI horário e tendência, que calcula os indicadores a partir da sparkline horária da CoinGecko: uma única chamada por minuto por moeda cobre os 250 ativos.
- 04Projetei a biblioteca de componentes e o design system com tokens no @theme do Tailwind CSS 4, contraste WCAG AA e estados documentados (foco, erro, vazio, dado defasado, HTTP 429), reutilizados em todas as telas junto com popovers Radix UI e gráficos em Recharts e SVG.
- 05Arquitetei a camada de dados com Route Handlers do Next.js, validação com Zod, cache LRU com dedupe e stale-on-error, rate limit por IP e tratamento de HTTP 429 sem retentativas em cascata, exibindo a idade do dado na interface.
- 06Protegi o app com CSP com nonce por requisição e acesso por código com cookie HMAC-SHA256, limitado a 5 tentativas por IP a cada 15 minutos, e centralizei a metodologia das 23 métricas em uma fonte única que gera os popovers e a página de ajuda, com teste que falha após 12 meses sem revisão.
Telas e componentes
Mercado
A tela de mercado lista o top 100 por market cap e mostra no cabeçalho há quanto tempo o dado foi atualizado.

Gráfico com indicadores
O gráfico usa candles da Binance e sobrepõe SMA, EMA, Bollinger e MACD, calculados em TypeScript no próprio app.

Carteira e métricas no celular
A carteira mostra P&L, custo e alocação por ativo. No celular, o gráfico de candles mantém os indicadores técnicos, e cada métrica abre fórmula, janela e armadilha com um toque.
Risco da carteira
O painel mede o risco da carteira inteira, expõe a amostra em chips e avisa que a ferramenta não recomenda operações.

Métricas explicadas
A página de ajuda é gerada do mesmo objeto que alimenta os ícones ⓘ das telas.

Watchlist e screener
A watchlist acompanha os ativos com sparklines e cria alertas de preço, e o screener filtra 250 ativos e informa qual série alimenta o RSI.
Fundamentos
A prancha de fundamentos reúne os tokens do @theme do Tailwind CSS 4: cor, contraste, tipografia, raios e paleta de gráficos.

Biblioteca de componentes
A biblioteca de componentes documenta variantes e estados, inclusive dado defasado e limite de API (HTTP 429).

Visualização de dados
Os componentes de visualização de dados são reutilizados nas telas de moeda, carteira e screener.

Anatomia do StatTile
A anatomia do StatTile e do MetricInfo mostra medidas, estados e a fonte única da metodologia.

Arquitetura
O quadro segue uma atualização da tela Mercado de 1 a 12: o navegador nunca chama a CoinGecko nem a Binance, toda cotação passa pelas funções na região gru1, com limite por IP, cache com dedupe e o dado anterior na falha, e a tela mostra a idade do dado.

Funcionalidades e as dores que resolvem
Cada funcionalidade nasceu de um problema real da operação.
01
Risco da carteira
- Dor
- Quem investe em cripto acompanha a carteira entre o app da corretora e uma planilha, e as ferramentas gratuitas em português que avaliei mostram preço e peso de cada ativo, mas não o risco da carteira como um todo.
- Solução
- O painel busca a série diária de 90 dias de cada posição na CoinGecko (até 14 ativos, mais o BTC de referência) e calcula, em TypeScript puro e testado, volatilidade anualizada, Sharpe, Sortino, drawdown máximo, VaR 95%, beta em relação ao BTC, concentração HHI e a matriz de correlação. Chips expõem as premissas, e o chip da amostra fica amarelo abaixo de 60 retornos.
Impacto: 7 medidas sobre 90 dias de retornos diários, mais a concentração (HHI)
02
Posições e P&L
- Dor
- Sem preço médio e cotação no mesmo lugar, P&L, custo e alocação dependem de atualização manual, e numa versão anterior do app um ativo fora do top 250 valia 0 sem aviso.
- Solução
- Cada posição guarda quantidade, preço médio e a moeda em que foi digitada; a tela calcula patrimônio, P&L, variação de hoje e alocação em rosca. Ativos fora do top 250 são cotados à parte, e a posição sem cotação é marcada e sai do cálculo de risco.
Impacto: Sem cadastro: quantidades e preços médios ficam só no aparelho
03
Métricas explicadas (ⓘ e Ajuda)
- Dor
- As ferramentas de mercado raramente dizem com que janela e sobre quantos dados um número foi calculado, e a documentação, quando existe, fica fora do gráfico e em inglês.
- Solução
- Um único arquivo tipado descreve cada métrica (fórmula, janela real, como ler, armadilha, fonte, data de revisão) importando as constantes do próprio código, e gera os popovers Radix das telas e a página /ajuda. Testes falham com texto sem revisão há mais de 12 meses ou com verbos de recomendação.
Impacto: 23 métricas com fórmula, janela e armadilha
04
Gráfico de candles com indicadores
- Dor
- Os indicadores técnicos costumam aparecer sem a janela em que foram calculados, e hospedado na região padrão (EUA) o app nem receberia candles: a Binance responde HTTP 451 a servidores americanos.
- Solução
- Candles da Binance em lightweight-charts com SMA 20, EMA 50, Bollinger, RSI e MACD calculados em TypeScript, e a leitura técnica logo abaixo; as funções rodam na região de São Paulo (gru1). Ativo sem par na Binance recebe candles diários sintetizados da CoinGecko, marcados na tela.
Impacto: 5 intervalos, de 15 minutos a 1 semana
05
Screener
- Dor
- Filtrar por RSI e tendência no mesmo universo da carteira exigiria candles de centenas de ativos, o que estouraria o limite da API pública.
- Solução
- Filtros de market cap, variação de 24h, RSI 14 horário e tendência contra a SMA de 24h, com ordenação, calculados sobre a sparkline horária de 7 dias que já vem na listagem da CoinGecko. Uma nota informa de qual série o RSI sai.
Impacto: Top 250 filtrado sem nenhuma chamada extra
06
Mercado
- Dor
- O panorama do mercado fica no CoinGecko, e a carteira e os gráficos, em outras ferramentas.
- Solução
- A tela inicial reúne market cap global, volume de 24h, dominância do BTC, maiores altas e baixas de 24h, busca e a tabela do top 100 com variação de 1h, 24h e 7 dias e sparkline de 7 dias.
Impacto: Top 100 fatiado da mesma chamada do top 250
07
Alertas de preço
- Dor
- Acompanhar um preço-alvo exige olhar a cotação o tempo todo, e um alvo definido em dólar não pode mudar de valor quando a tela passa a mostrar reais.
- Solução
- O alerta (acima ou abaixo) guarda a moeda em que foi criado e é avaliado nela a cada atualização de 60 s enquanto a tela Mercado ou Watchlist está aberta e visível, convertendo pelo câmbio; sem câmbio, não avalia. Dispara uma vez com toast e notificação do navegador, se autorizada, e rearmar não apaga o histórico.
Impacto: Guarda os últimos 20 disparos por alerta; a tela mostra os 3 mais recentes
08
USD e BRL
- Dor
- Cotações de cripto vêm em USD e a carteira pode ter sido registrada em BRL; sem guardar a moeda de origem, trocar a moeda de exibição distorce custo e P&L.
- Solução
- Uma alternância USD/BRL, salva no aparelho, pede as cotações da CoinGecko já na moeda escolhida (os candles da Binance seguem em USDT) e converte preços médios pelo câmbio implícito (market cap global em BRL ÷ em USD), que a página de Ajuda documenta com fórmula e armadilha.
09
Idade do dado e limite de API
- Dor
- Sem chave, a API da CoinGecko responde HTTP 429 depois de poucas requisições; o app mostrava o erro cru, e uma única atualização, com a API já no limite, podia virar até 9 chamadas ao provedor.
- Solução
- Toda chamada passa pelo servidor, com cache LRU compartilhado (pedidos iguais em andamento viram um só, e o dado anterior é servido em caso de erro), limite por IP (120 de pico, 2 por segundo) e no máximo uma nova tentativa no 429. A tela Mercado mostra "atualizado há X min" e, se for o caso, o aviso de dado defasado.
Impacto: Com a API no limite, no máximo 2 chamadas ao provedor por requisição
10
Acesso e segurança
- Dor
- A ferramenta é de uso pessoal, sem contas e com orçamento zero: aberta na internet, qualquer script consumiria a cota de API, e um script injetado leria as posições salvas no navegador.
- Solução
- Um proxy exige o código antes de qualquer tela ou API (Ajuda e Privacidade seguem públicas), validado com Zod, comparado em tempo constante e trocado por um cookie HttpOnly assinado com HMAC-SHA256, válido por 30 dias. O mesmo proxy emite CSP com nonce por requisição e strict-dynamic, além de nosniff, X-Frame-Options e Referrer-Policy.
Impacto: 5 tentativas por IP a cada 15 min (limite por instância)




















