Carlos Eduardo Gatti Ferreira

Wealth Tracker — Roadmap Financeiro

Versão: 1.0
Data: Janeiro 2026
Status: Planejamento — Nenhuma implementação ainda


📋 Índice

  1. Fase 1: Modelo de Price Snapshot
  2. Fase 2: Estratégia de Integração de Preços
  3. Fase 3: Roadmap de Métricas
  4. Fase 4: Cashflow + Portfolio = Net Worth
  5. Considerações de Implementação

Fase 1: Modelo de Price Snapshot

Objetivo

Desenhar um modelo de dados para preços históricos que permita:

Proposta de Model Prisma (Design Only)

model AssetPrice {
  id          Int      @id @default(autoincrement())
  assetId     Int
  asset       Asset    @relation(fields: [assetId], references: [id], onDelete: Cascade)
  
  price       Decimal  @db.Decimal(20, 8)  // Suporta cripto e ações
  currency    String   @default("USD")
  
  source      PriceSource  @default(MANUAL)
  recordedAt  DateTime     @default(now())
  
  // Metadata opcional
  exchange    String?      // Ex: "NYSE", "NASDAQ", "BINANCE"
  volume      Decimal?     @db.Decimal(20, 8)
  marketCap   Decimal?     @db.Decimal(20, 2)
  
  createdAt   DateTime     @default(now())
  updatedAt   DateTime     @updatedAt
  
  @@index([assetId, recordedAt])
  @@index([recordedAt])
  @@map("asset_prices")
}

enum PriceSource {
  MANUAL      // Preço inserido manualmente pelo usuário
  YAHOO       // Yahoo Finance API
  POLYGON     // Polygon.io API
  ALPHA_VANTAGE // Alpha Vantage API
  CRYPTO_COMPARE // CryptoCompare API
}

Justificativa de Campos

assetId

price

currency

source

recordedAt

exchange, volume, marketCap

Índices Propostos

@@index([assetId, recordedAt])  // Busca rápida de histórico por ativo
@@index([recordedAt])            // Busca rápida de preços por data

Justificativa:

Conexão com PortfolioSummary (Futuro)

Estado Atual:

// Backend atual calcula marketValue baseado em lastPrice
marketValue = quantity * lastPrice

Estado Futuro:

// Backend buscará preço mais recente de AssetPrice
const latestPrice = await getLatestPrice(assetId);
marketValue = quantity * latestPrice.price;

Migração Incremental:

  1. Criar tabela AssetPrice (nova, não quebra nada)
  2. Manter lastPrice em Asset (backward compatibility)
  3. Gradualmente migrar: se existe AssetPrice, usar; senão, usar lastPrice
  4. Deprecar lastPrice apenas quando 100% dos ativos tiverem preços reais

Fase 2: Estratégia de Integração de Preços

Comparação de APIs

Yahoo Finance (yfinance)

Prós:

Contras:

Uso Recomendado: MVP / Prototipagem

Polygon.io

Prós:

Contras:

Uso Recomendado: Produção (quando houver budget)

Alpha Vantage

Prós:

Contras:

Uso Recomendado: Produção pequena escala

CryptoCompare / CoinGecko

Prós:

Contras:

Uso Recomendado: Complementar para crypto

Estratégia Recomendada (Híbrida)

┌─────────────────────────────────────────┐
│         Price Fetch Strategy            │
└─────────────────────────────────────────┘
                    │
        ┌───────────┴───────────┐
        │                       │
   ┌────▼────┐            ┌────▼────┐
   │  Stocks │            │  Crypto │
   │  ETFs   │            │  Tokens │
   └────┬────┘            └────┬────┘
        │                       │
   ┌────▼────┐            ┌────▼────┐
   │ Polygon │            │CoinGecko │
   │ (paid)  │            │  (free) │
   └────┬────┘            └────┬────┘
        │                       │
        └───────────┬───────────┘
                    │
            ┌───────▼───────┐
            │  Fallback:    │
            │  Yahoo Finance│
            └───────────────┘

Fluxo de Fetch de Preços

Opção 1: Cron Job (Recomendado)

┌─────────────────────────────────────────────┐
│  Cron Job (a cada hora / 4 horas)          │
└─────────────────────────────────────────────┘
                    │
        ┌───────────┴───────────┐
        │                       │
   ┌────▼────┐            ┌────▼────┐
   │ Fetch   │            │ Fetch   │
   │ Stocks  │            │ Crypto  │
   └────┬────┘            └────┬────┘
        │                       │
        └───────────┬───────────┘
                    │
        ┌───────────▼───────────┐
        │  Save to AssetPrice   │
        │  (batch insert)       │
        └───────────────────────┘

Vantagens:

Desvantagens:

Opção 2: On-Demand (Manual Refresh)

┌─────────────────────────────────────────────┐
│  User clicks "Refresh Prices"              │
└─────────────────────────────────────────────┘
                    │
        ┌───────────▼───────────┐
        │  Get user's assets    │
        │  (from portfolios)    │
        └───────────┬───────────┘
                    │
        ┌───────────▼───────────┐
        │  Fetch prices         │
        │  (rate limited)       │
        └───────────┬───────────┘
                    │
        ┌───────────▼───────────┐
        │  Save to AssetPrice   │
        └───────────────────────┘

Vantagens:

Desvantagens:

Opção 3: Híbrida (Recomendada para Produção)

┌─────────────────────────────────────────────┐
│  Cron: Atualiza preços principais          │
│  (S&P 500, principais cryptos)             │
└─────────────────────────────────────────────┘
                    │
        ┌───────────▼───────────┐
        │  On-Demand: Ativos    │
        │  específicos do user   │
        └───────────────────────┘

Rate Limits e Fallback

Estratégia de Fallback

// Pseudocódigo (não implementar ainda)

async function fetchPrice(asset: Asset): Promise<Price> {
  // 1. Tentar fonte primária
  try {
    return await polygonApi.getPrice(asset.ticker);
  } catch (error) {
    // 2. Fallback para fonte secundária
    try {
      return await yahooFinance.getPrice(asset.ticker);
    } catch (error) {
      // 3. Se tudo falhar, usar último preço conhecido
      return await getLastKnownPrice(asset.id);
    }
  }
}

Rate Limit Handling

// Pseudocódigo (não implementar ainda)

class PriceFetcher {
  private rateLimiter: Map<string, number[]> = new Map();
  
  async fetchWithRateLimit(source: string, ticker: string) {
    const now = Date.now();
    const calls = this.rateLimiter.get(source) || [];
    
    // Remover calls antigas (última hora)
    const recentCalls = calls.filter(time => now - time < 3600000);
    
    if (recentCalls.length >= MAX_CALLS_PER_HOUR) {
      throw new RateLimitError('Rate limit exceeded');
    }
    
    // Registrar call
    recentCalls.push(now);
    this.rateLimiter.set(source, recentCalls);
    
    // Fazer fetch
    return await this.fetchPrice(source, ticker);
  }
}

Onde Isso Entra no Backend Atual

Estrutura Proposta (Sem Implementar)

backend/
├── src/
│   ├── assets/
│   │   ├── assets.service.ts      (existente)
│   │   ├── assets.resolver.ts     (existente)
│   │   └── assets.module.ts       (existente)
│   │
│   ├── pricing/                    (NOVO - não criar ainda)
│   │   ├── pricing.service.ts     // Lógica de fetch
│   │   ├── pricing.resolver.ts    // GraphQL queries
│   │   ├── pricing.module.ts
│   │   ├── providers/
│   │   │   ├── polygon.provider.ts
│   │   │   ├── yahoo.provider.ts
│   │   │   └── cryptocompare.provider.ts
│   │   └── jobs/
│   │       └── price-sync.job.ts  // Cron job
│   │
│   └── portfolios/
│       └── portfolios.service.ts  (modificar depois)

Evitando Recalcular Tudo a Cada Request

Estratégia de Cache

┌─────────────────────────────────────────────┐
│  Request: PortfolioSummary                  │
└─────────────────────────────────────────────┘
                    │
        ┌───────────▼───────────┐
        │  Check cache          │
        │  (Redis / Memory)     │
        └───────────┬───────────┘
                    │
        ┌───────────┴───────────┐
        │                       │
   ┌────▼────┐            ┌────▼────┐
   │ Cache   │            │ Cache   │
   │ Hit     │            │ Miss    │
   └────┬────┘            └────┬────┘
        │                       │
        │              ┌────────▼────────┐
        │              │ Fetch latest    │
        │              │ prices from DB  │
        │              └────────┬────────┘
        │                       │
        └───────────┬───────────┘
                    │
        ┌───────────▼───────────┐
        │  Calculate summary    │
        │  (with cached prices) │
        └───────────────────────┘

Cache Strategy:


Fase 3: Roadmap de Métricas

MVP Metrics (Implementar Primeiro)

1. Portfolio Value

Definição: Soma do valor de mercado de todas as posições

Cálculo:

Portfolio Value = Σ(quantity × currentPrice)

Prioridade: 🔴 Alta
Complexidade: Baixa
Dependências: Price Snapshot (Fase 1)

2. Cost Basis

Definição: Valor total investido (já implementado)

Cálculo:

Cost Basis = Σ(quantity × averageCost)

Prioridade: 🟢 Já implementado
Status: ✅ Completo

3. Unrealized Gain/Loss

Definição: Diferença entre valor atual e custo

Cálculo:

Unrealized G/L = Portfolio Value - Cost Basis
Unrealized G/L % = (Unrealized G/L / Cost Basis) × 100

Prioridade: 🔴 Alta
Complexidade: Baixa
Dependências: Portfolio Value

4. Allocation %

Definição: Percentual de cada ativo/tipo no portfolio

Cálculo:

Allocation % = (Asset Value / Portfolio Value) × 100

Prioridade: 🟡 Média
Complexidade: Baixa
Status: ✅ Parcialmente implementado

Advanced Metrics (Implementar Depois)

1. Time-Weighted Return (TWR)

Definição: Retorno ajustado por tempo, ignorando depósitos/saques

Cálculo:

TWR = [(1 + r1) × (1 + r2) × ... × (1 + rn)] - 1

Prioridade: 🟡 Média
Complexidade: Alta
Uso: Comparar performance de diferentes períodos

2. Money-Weighted Return (MWR)

Definição: Retorno ajustado por valor investido (IRR)

Cálculo:

Resolve: NPV = 0
Onde: Σ(CFt / (1 + MWR)^t) = 0

Prioridade: 🟡 Média
Complexidade: Muito Alta
Uso: Performance real considerando timing de investimentos

3. Volatility (Desvio Padrão)

Definição: Medida de variação dos retornos

Cálculo:

Volatility = √(Σ(ri - r̄)² / n)

Prioridade: 🟢 Baixa
Complexidade: Média
Uso: Medir risco do portfolio

4. Max Drawdown

Definição: Maior queda desde o pico

Cálculo:

Max Drawdown = (Peak - Trough) / Peak

Prioridade: 🟢 Baixa
Complexidade: Baixa
Uso: Entender pior cenário histórico

UX Metrics (Visualização)

1. Monthly Performance

Definição: Retorno mensal do portfolio

Visualização:

Prioridade: 🟡 Média
Complexidade: Baixa

2. Annual Performance

Definição: Retorno anual (YTD, 1Y, 3Y, 5Y)

Visualização:

Prioridade: 🟡 Média
Complexidade: Média

3. Comparison vs Benchmark

Definição: Performance relativa ao S&P 500 ou outro índice

Visualização:

Prioridade: 🟢 Baixa
Complexidade: Alta (requer dados de benchmark)

Priorização de Métricas

Fase 1 (MVP - 2-4 semanas):
├── Portfolio Value ✅
├── Cost Basis ✅
├── Unrealized G/L ✅
└── Allocation % ✅

Fase 2 (Intermediário - 1-2 meses):
├── Monthly Performance
├── Annual Performance
└── Volatility

Fase 3 (Avançado - 3+ meses):
├── Time-Weighted Return
├── Money-Weighted Return
├── Max Drawdown
└── Benchmark Comparison

Fase 4: Cashflow + Portfolio = Net Worth

Definição de Net Worth

Net Worth = Total Assets - Total Liabilities

No contexto do Wealth Tracker:

Net Worth = 
  Cash Balance +
  Investment Portfolio Value +
  Other Assets -
  Recurring Expenses (projected) -
  Other Liabilities

Componentes do Net Worth

1. Cash Balance

Fonte: Cashflow module

Cálculo:

Cash Balance = 
  Initial Balance +
  Σ(Income entries) -
  Σ(Expense entries) -
  Σ(Recurring expenses projected)

Prioridade: 🔴 Alta
Status: Dados disponíveis no Cashflow

2. Investment Portfolio Value

Fonte: Portfolio module

Cálculo:

Portfolio Value = Σ(quantity × currentPrice)

Prioridade: 🔴 Alta
Status: Depende de Price Snapshot (Fase 1)

3. Recurring Expenses (Projected)

Fonte: Cashflow Recurring

Cálculo:

Projected Expenses = Σ(monthlyRecurringAmount)

Prioridade: 🟡 Média
Uso: Mostrar compromissos futuros

4. Other Assets / Liabilities

Fonte: Novo módulo (futuro)

Exemplos:

Prioridade: 🟢 Baixa (futuro)

Visualização no Dashboard

Layout Proposto

┌─────────────────────────────────────────────┐
│         NET WORTH DASHBOARD                 │
├─────────────────────────────────────────────┤
│                                             │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐ │
│  │   Cash   │  │Portfolio │  │   Net    │ │
│  │ $10,000  │  │ $50,000  │  │ $60,000  │ │
│  └──────────┘  └──────────┘  └──────────┘ │
│                                             │
│  ┌───────────────────────────────────────┐ │
│  │     Net Worth Over Time (Chart)       │ │
│  │     [Line chart: last 12 months]      │ │
│  └───────────────────────────────────────┘ │
│                                             │
│  ┌───────────────────────────────────────┐ │
│  │     Breakdown by Category             │ │
│  │     [Pie chart: Cash vs Investments] │ │
│  └───────────────────────────────────────┘ │
│                                             │
│  ┌───────────────────────────────────────┐ │
│  │     Recurring Expenses (Projected)    │ │
│  │     - Netflix: $14.99/month          │ │
│  │     - Rent: $1,200/month              │ │
│  └───────────────────────────────────────┘ │
└─────────────────────────────────────────────┘

Exemplos de Gráficos

1. Net Worth Over Time

Tipo: Line Chart
Eixo X: Meses (últimos 12)
Eixo Y: Valor em USD
Séries:

2. Allocation Breakdown

Tipo: Pie/Donut Chart
Categorias:

3. Cashflow vs Investments

Tipo: Stacked Bar Chart
Eixo X: Meses
Eixo Y: Valor
Séries:

Integração com Módulos Existentes

Fluxo de Dados

┌─────────────────┐
│  Cashflow       │
│  - Entries      │
│  - Recurring    │
└────────┬────────┘
         │
         │ Cash Balance
         │ Recurring Expenses
         │
┌────────▼────────┐
│  Net Worth      │
│  Calculator     │
└────────┬────────┘
         │
         │ Portfolio Value
         │
┌────────▼────────┐
│  Portfolio      │
│  - Holdings     │
│  - Prices       │
└─────────────────┘

API GraphQL Proposta (Não Implementar Ainda)

type NetWorthSummary {
  totalNetWorth: Float!
  cashBalance: Float!
  portfolioValue: Float!
  recurringExpensesMonthly: Float!
  breakdown: NetWorthBreakdown!
  history: [NetWorthPoint!]!
}

type NetWorthBreakdown {
  cash: Float!
  stocks: Float!
  crypto: Float!
  other: Float!
}

type NetWorthPoint {
  date: DateTime!
  total: Float!
  cash: Float!
  portfolio: Float!
}

Considerações de Implementação

Ordem de Implementação

  1. Fase 1: Price Snapshot (base para tudo)
  2. Fase 2: Integração de preços (dados reais)
  3. Fase 3: Métricas básicas (Portfolio Value, G/L)
  4. Fase 4: Net Worth (unificação)

Dependências

Net Worth
  ├── Cash Balance (Cashflow) ✅ Existe
  ├── Portfolio Value (Portfolio + Prices) ⏳ Fase 1
  └── Recurring Expenses (Cashflow) ✅ Existe

Considerações de Implementação

Princípios de Evolução Incremental

  1. Additive Only: Sempre adicionar, nunca remover
  2. Backward Compatible: Código antigo continua funcionando
  3. Feature Flags: Permitir ativar/desativar features gradualmente
  4. Migration Strategy: Migrações opcionais, não obrigatórias

Estratégia de Rollout

Fase 1: Price Snapshot (2-3 semanas)

Fase 2: Integração de Preços (3-4 semanas)

Fase 3: Métricas (2-3 semanas)

Fase 4: Net Worth (2-3 semanas)

Riscos e Mitigações

Risco 1: Breaking Changes

Mitigação:

Risco 2: Performance

Mitigação:

Risco 3: Rate Limits de APIs

Mitigação:

Risco 4: Dados Inconsistentes

Mitigação:

Métricas de Sucesso

Técnicas

Produto


Conclusão

Este roadmap fornece uma base sólida para evoluir o Wealth Tracker de um sistema baseado em custo para um sistema completo de gestão de patrimônio com preços reais de mercado.

Próximos Passos:

  1. Revisar este documento com o time
  2. Priorizar fases baseado em necessidade de negócio
  3. Implementar Fase 1 (Price Snapshot) quando aprovado
  4. Iterar incrementalmente

Importante: Este documento é um plano. Nenhuma implementação deve ser feita sem aprovação explícita e sem seguir os princípios de evolução incremental descritos aqui.


Documento criado em: Janeiro 2026
Última atualização: Janeiro 2026
Status: Planejamento — Aguardando aprovação para implementação