Skip to content

PulseDataLabs/PulseFlat

Repository files navigation

PulseFlat Logo

PulseFlat

Pipeline Serverless e Automatizado de Captura de Dados Financeiros Brasileiros

Build Status Python Versions License Data Count Live Dashboard Last Commit Atualização Diária

RecursosArquiteturaFuncionamentoEstruturaAnalistasDesenvolvedorDatasets


PulseFlat é um pipeline de ETL (Extração, Transformação e Carga) serverless projetado para coletar, tratar e disponibilizar dados financeiros brasileiros históricos de fontes oficiais diariamente. Ele funciona 100% de forma automatizada via GitHub Actions, versionando o histórico diretamente no repositório em formato CSV plano, sem custos com banco de dados ou servidores.

PulseDataLabs nasceu da missão de democratizar o acesso a dados financeiros brasileiros de qualidade. Acreditamos que informações financeiras confiáveis não deveriam ser um privilégio de grandes instituições — por isso construímos o PulseFlat como um projeto 100% open-source.

💡 Quer apenas consumir os dados sem instalar nada? Acesse o dashboard online ou veja o guia rápido na seção Para Analistas.


🚀 Recursos e Diferenciais

  • OOP & Abstração Sólida: Scrapers estruturados sob uma classe base robusta (BaseScraper) que gerencia nativamente o ciclo de vida da execução, tratamento de exceções thread-safe e logs padronizados.
  • Descoberta Dinâmica (Reflection): O orquestrador detecta scrapers dinamicamente inspecionando a pasta scrapers/ no momento da execução, eliminando a necessidade de registros estáticos ou configurações hardcoded.
  • Sanitização Automática de Dados: Limpeza inteligente embutida que padroniza formatos de data brasileiros (DD/MM/YYYY ou DD/MM/YY para ISO YYYY-MM-DD) e normaliza representações decimais com vírgula para floats com ponto.
  • Execução Concorrente Multicondicional: Orquestrador inteligente capaz de paralelizar a execução de scripts independentes e enfileirar sequencialmente os scripts dependentes.
  • Monitoramento de Schema Drift: Proteção na persistência de arquivos contra mudanças repentinas nas estruturas originais de dados (emite logs detalhados sobre colunas adicionadas ou removidas).
  • Validação de Integridade Temporal: Verificação automática de "buracos" (dias úteis faltantes) em séries temporais, usando a biblioteca bizdays para garantir continuidade nas datas de referência entre a menor e a maior data de cada entidade.
  • Frontend Otimizado (Zero CLS/LCP Baixo): Dashboard interativo desenvolvido em vanilla HTML/CSS que consome os JSONs estáticos diretamente do repositório, com recursos avançados de preloading, busca instantânea debouncada e renderização adiada via CSS Containment (content-visibility).
  • Terminal UX colorido: Progresso com ícones Unicode, timing por etapa e cores ANSI via módulo compartilhado scripts/utils/ux.py reutilizável entre scrapers e scripts de pós-processamento.

📐 Arquitetura do Pipeline

A fluxo de processamento e disponibilização de dados funciona conforme a estrutura abaixo:

graph TD
    A[GitHub Actions Cron / Trigger] --> B[run_all.py Orchestrator]
    B -->|Dynamic Discovery| C[scrapers/ folder]
    B -->|Executes Phase 1| D[Independent Scrapers]
    B -->|Executes Phase 2| E[Dependent Scrapers]
    D --> F[data/*.csv files]
    E --> F
    B -->|Calls generate_catalog.py| G[data/datasets.json]
    B -->|Calls generate_market_latest.py| H[data/market_latest.json]
    B -->|Calls verificar_buracos.py| K[Valida continuidade temporal]
    K -->|bizdays.Calendar| L[Relatório de buracos]
    F & G & H --> I[git push origin main]
    I --> J[GitHub Pages / index.html]
Loading

⚙️ Funcionamento do Orquestrador (run_all.py)

O orquestrador run_all.py varre a pasta scrapers/, importa as classes e lê os metadados diretamente de seus atributos de classe:

  • group (grupo a que pertence: anbima, bcb, b3, cvm, ibge, ratings, misc).
  • enabled (indica se deve rodar no pipeline diário principal).
  • phase (ordem de dependência: Phase 1 para independentes; Phase 2 para dependentes que dependem do resultado da Phase 1).

CLI Options e Exemplos de Uso

# Executa todos os scrapers ativos em paralelo (padrão: 4 workers)
python run_all.py

# Executa os scrapers sequencialmente (ideal para depuração de rede)
python run_all.py --sequential

# Executa em paralelo especificando um limite de threads
python run_all.py --parallel --max-workers 8

# Executa apenas scrapers pertencentes a um grupo específico
python run_all.py --group bcb
python run_all.py --group ratings

# Executa manualmente um scraper específico (mesmo se desabilitado por padrão)
python run_all.py --scraper anbima_indicadores

# Apenas regenera o catálogo data/datasets.json a partir dos metadados das classes
python run_all.py --generate-catalog

# Executa verificação de buracos ao final (dias úteis faltantes)
python run_all.py --check-holes
python run_all.py --check-holes --fail-on-holes

Scripts individuais aceitam --quiet, --verbose, --no-color e --dry-run via scripts.utils.ux.add_common_args().

Verificando Integridade Temporal

O script scripts/verificar_buracos.py valida se as séries temporais nos CSVs não possuem "buracos" (dias úteis faltantes entre a menor e a maior data de referência):

# Verifica todos os CSVs de série temporal configurados
python scripts/verificar_buracos.py

# Verifica apenas CSVs específicos
python scripts/verificar_buracos.py --csv anbima_ima_completo.csv anbima_idka.csv

# Falha com exit code 1 se encontrar buracos (útil para CI)
python scripts/verificar_buracos.py --fail-on-holes

# Ignora entidades com menos de N datas (padrão: 3)
python scripts/verificar_buracos.py --threshold 5

# Lista os CSVs habilitados para verificação e suas configurações
python scripts/verificar_buracos.py --list

# Modo detalhado: mostra cada entidade com buracos e as datas faltantes
python scripts/verificar_buracos.py --verbose

# Pode ser executado ao final do pipeline via run_all.py
python run_all.py --check-holes
python run_all.py --check-holes --fail-on-holes

A verificação usa a bizdays.Calendar (feriados brasileiros 2024–2028 já definidos em utils/parsers.py) e faz agrupamento por entidade — cada índice, ativo ou ticker tem sua sequência de datas verificada independentemente.

CSVs de snapshot (carteiras teóricas, classificações setoriais, cadastros) não são verificados, pois seus dados representam o estado em um ponto do tempo, não uma série temporal contínua.

Ao final de uma execução completa (todos os grupos), o orquestrador executa automaticamente:

  • scripts/generate_catalog.py — regenera data/datasets.json com os metadados atualizados
  • scripts/generate_market_latest.py — extrai os últimos valores de CDI, SELIC, IPCA, IGP-M, PTAX, IBOV e IMA-GERAL para data/market_latest.json, alimentando o ticker em tempo real no dashboard

📂 Estrutura do Projeto

PulseFlat/
├── .github/
│   └── workflows/
│       └── main.yml                 # Agendamento do pipeline no GitHub Actions
├── data/                            # Datasets, schemas e metadados de controle
│   ├── datasets.json                # Catálogo estruturado de metadados dos datasets
│   ├── market_latest.json / .js     # Últimos valores de indicadores para o ticker do dashboard
│   ├── schemas.json                 # Definição e mapeamento de campos e tipos
│   ├── pipeline_status.json / .js   # Logs de saúde e duração da última execução
│   ├── last_updates.json / .js      # Período de cobertura temporal mínima/máxima de cada CSV
│   └── *.csv                        # Séries temporais de dados financeiros
├── scripts/                         # Scripts de pós-processamento
│   ├── generate_catalog.py          # Gera datasets.json a partir dos metadados
│   ├── generate_market_latest.py    # Gera market_latest.json com últimos indicadores
│   ├── populate_last_updates.py     # Atualiza last_updates.json
│   ├── limpar_duplicatas.py         # Remove duplicatas de CSVs
│   ├── verificar_buracos.py         # Valida continuidade das datas em séries temporais
│   └── utils/ux.py                  # UX compartilhada (cores, ícones, CLI args)
├── scrapers/                        # Módulos de captura
│   ├── utils/
│   │   ├── __init__.py
│   │   └── base.py                  # Classe infraestrutural BaseScraper
│   ├── generic_scraper.py           # Scraper base genérico parametrizado por YAML
│   └── *.py                         # Scripts específicos de coleta por dataset
├── utils/                           # Utilitários compartilhados auxiliares
│   ├── __init__.py
│   ├── base.py                      # Funções genéricas e salvamento de CSVs
│   ├── parsers.py                   # Parsers robustos para ZIP, Excel, XLS, CSV, FWF, XML
│   └── b3_helpers.py                # Helpers específicos para seeds da B3
├── tests/                           # Suíte de testes automatizados
│   ├── test_base_scraper.py         # Testes de sanitização e ciclo da classe base
│   ├── test_parsers.py              # Testes para os parsers auxiliares (ZIP, FWF, etc.)
│   ├── test_run_all.py              # Testes da CLI e do mecanismo de descoberta dinâmica
│   ├── test_scrapers.py             # Testes mockados de scrapers individuais
│   └── test_utils.py                # Testes de persistência de arquivos e helpers
├── run_all.py                       # Orquestrador CLI central do projeto
├── requirements.txt                 # Dependências do Python
├── .env.example                     # Template de variáveis de ambiente
├── .gitignore
└── README.md

📊 Para Analistas: Consuma os Dados Sem Código

Você não precisa instalar nada para usar os dados do PulseFlat. Todas as coletas são feitas automaticamente e os CSVs ficam disponíveis em URLs públicas.

Importar no Excel ou Google Sheets

=IMPORTDATA("https://raw.githubusercontent.com/PulseDataLabs/PulseFlat/main/data/anbima_indicadores.csv")

Basta copiar a URL de qualquer dataset (lista completa no dashboard) e usar a função IMPORTDATA no Google Sheets ou Dados → De Texto/CSV no Excel.

Download Direto

Acesse o dashboard interativo, encontre o dataset desejado e clique em Download CSV. Pronto — o arquivo mais recente será baixado.

URL Raw para Ferramentas de BI

No Power BI, Tableau ou Metabase, aponte a fonte de dados para a URL raw do GitHub:

https://raw.githubusercontent.com/PulseDataLabs/PulseFlat/main/data/anbima_indicadores.csv

Os dados são atualizados automaticamente até 7 vezes por dia útil (06h–23h, seg–sex).


💻 Guia do Desenvolvedor

Instalação Local e Gerenciamento de Dependências

  1. Clone o repositório:

    git clone https://github.com/PulseDataLabs/PulseFlat.git
    cd PulseFlat
  2. Instale as dependências:

    Opção A (Recomendada — Usando uv):

    O uv é um gerenciador de pacotes extremamente rápido escrito em Rust.

    # Instale o uv (se já não tiver instalado)
    curl -LsSf https://astral.sh/uv/install.sh | sh
    
    # Crie o ambiente virtual com uv
    uv venv
    
    # Ative o ambiente
    source .venv/bin/activate
    
    # Instale as dependências de forma ultra-rápida
    uv pip install -r requirements.txt

    Opção B (Tradicional — Usando venv + pip):

    # Crie o ambiente virtual tradicional
    python3 -m venv .venv
    
    # Ative o ambiente
    source .venv/bin/activate
    
    # Instale as dependências tradicionalmente
    pip install -r requirements.txt
  3. Configure o arquivo de variáveis de ambiente:

    cp .env.example .env

    (Edite o .env com suas chaves de APIs e credenciais de banco de dados).

Persistência no Banco de Dados (Oracle Cloud Autonomous Database)

O projeto possui persistência automatizada opcional dos dados coletados em um banco Oracle Cloud Autonomous Database usando oracledb (Thin Mode) + SQLAlchemy.

Para habilitar a persistência, configure as seguintes variáveis de ambiente no arquivo .env:

  • ORACLE_DB_USER: Usuário do banco de dados
  • ORACLE_DB_PASSWORD: Senha do banco (tratada via urllib.parse.quote_plus para evitar falhas na URL de conexão)
  • ORACLE_DB_DSN: String DSN do banco (ou service name correspondente)
  • ORACLE_DB_WALLET_DIR (opcional): Diretório local contendo a carteira mTLS (ex: cwallet.sso, tnsnames.ora). Se configurado e não estiver vazio, usará conexão segura Mutual TLS (mTLS). Caso contrário, reverterá automaticamente para One-Way TLS.
  • ORACLE_DB_WALLET_PASSWORD (opcional): Senha associada à Wallet.

Desativação Local / Bypass (Dry-Run):

Para executar o pipeline sem tentar persistir dados no banco (por exemplo, em ambiente local ou de CI/CD sem banco):

  • Defina a variável de ambiente SKIP_ORACLE_DB=1 no arquivo .env ou no shell.
  • Ou adicione a flag --skip-db na execução do pipeline:
    python run_all.py --skip-db

Criando um Novo Scraper

Basta criar um novo arquivo Python dentro de scrapers/ herdando de BaseScraper. O orquestrador o detectará automaticamente:

from scrapers.utils.base import BaseScraper
import pandas as pd

class MeuNovoScraper(BaseScraper):
    # Propriedades de Orquestração
    name = "meu_novo_scraper"
    group = "misc"
    enabled = True
    phase = 1
    
    # Propriedades de Persistência
    accumulate = True
    chaves_dedup = ["data_captura", "ticker"]
    
    # Propriedades do Catálogo (Metadados do Dashboard)
    title = "Meu Novo Dataset"
    description = "Coleta dados de teste diariamente."
    icon = "📊"
    icon_class = "icon-misc"
    badge = "Diário"
    badge_class = "badge-daily"
    tags = ["teste", "novo"]
    source = "Minha Fonte"

    def fetch(self) -> pd.DataFrame:
        # A lógica do scraper deve ir aqui, retornando um Pandas DataFrame
        dados = [{"ticker": "ABCD3", "preco": "10,50", "data": "04/06/2026"}]
        return pd.DataFrame(dados)

Para loops com progresso colorido, use as funções do módulo scripts/utils/ux.py:

from scripts.utils.ux import print_done, print_warn

    def fetch(self) -> pd.DataFrame:
        import time
        for i, ticker in enumerate(tickers, 1):
            t0 = time.time()
            try:
                df = baixar(ticker)
                print_done(f"({i}/{len(tickers)}) {ticker}", elapsed=time.time() - t0)
            except Exception as e:
                print_warn(f"({i}/{len(tickers)}) {ticker}: {e}")
        return pd.concat(frames, ignore_index=True)

Executando Testes

A suíte de testes utiliza pytest com requests-mock para testar os parsers e APIs mockadas de forma rápida e offline:

python -m pytest tests/ -v

📊 Fontes e Datasets

Os scrapers estão classificados nos seguintes grupos de dados principais:

Grupo Fonte Primária Exemplos de Dados Disponibilizados Frequência
ANBIMA Portal ANBIMA / SND Debêntures Taxas indicativas, Projeções de Inflação (IPCA/IGPM), Títulos Públicos, Emissões e Mercado Secundário de Debêntures, Índices IMA/IDkA. Diária
BCB Banco Central do Brasil Cotações diárias do Dólar (PTAX), Séries SGS (SELIC, CDI, Inflação), Negociação de títulos públicos (DEMAB), Balancetes cadastrais de bancos. Diária
CVM Portal Brasileiro de Dados Abertos Cadastro geral de companhias abertas, informes diários e dados de cotas/classes de fundos. Diária
B3 B3 Market Data FIIs/ETFs listados, composição das carteiras teóricas de índices (IBOV, SMLL, ISEE, BDRX, IFNC), taxas DI Over, posição acionária / free float / capital social de companhias listadas (b3_companhias_financeiro), limites de aceitação de garantias (b3_limites_garantias). Diária / Snapshot / Mensal
IBGE IBGE SIDRA API Índices oficiais de inflação do Brasil (IPCA, IPCA-15, INPC). Mensal
Misc Yahoo Finance / Wikipedia / ONU Cotações históricas/diárias de ações brasileiras e internacionais, ETFs (B3 e Irlandeses UCITS), moedas, criptoativos, commodities e índices globais de forma 100% parametrizável e dinâmica (composição via Wikipedia e B3); e dados do Pacto Global da ONU. Diária

🤝 Contribuindo

Contribuições são bem-vindas! Se você encontrou um bug, tem uma ideia para um novo scraper ou quer melhorar a documentação:

  1. Abra uma issue para discutir a mudança
  2. Faça um fork do repositório
  3. Crie um branch (git checkout -b feature/minha-feature)
  4. Commit suas mudanças (git commit -m 'feat: adiciona scraper X')
  5. Push para o branch (git push origin feature/minha-feature)
  6. Abra um Pull Request

Veja o guia do desenvolvedor para entender a arquitetura e como criar novos scrapers.


📄 Licença

Este projeto é de código aberto e está licenciado sob os termos da licença MIT — você pode usar, modificar e distribuir livremente, mesmo para fins comerciais.

Dúvidas ou sugestões? Abra uma issue ou entre em contato pelo GitHub da PulseDataLabs.

About

Pipeline serverless de dados financeiros brasileiros. Captura diária automatizada de BCB, ANBIMA, B3, CVM e IBGE via GitHub Actions. Dados abertos em CSV, zero infraestrutura.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors