Pipeline Serverless e Automatizado de Captura de Dados Financeiros Brasileiros
Recursos • Arquitetura • Funcionamento • Estrutura • Analistas • Desenvolvedor • Datasets
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.
- 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/YYYYouDD/MM/YYpara ISOYYYY-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
bizdayspara 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.pyreutilizável entre scrapers e scripts de pós-processamento.
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]
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).
# 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-holesScripts individuais aceitam
--quiet,--verbose,--no-colore--dry-runviascripts.utils.ux.add_common_args().
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-holesA 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— regeneradata/datasets.jsoncom os metadados atualizadosscripts/generate_market_latest.py— extrai os últimos valores de CDI, SELIC, IPCA, IGP-M, PTAX, IBOV e IMA-GERAL paradata/market_latest.json, alimentando o ticker em tempo real no dashboard
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
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.
=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.
Acesse o dashboard interativo, encontre o dataset desejado e clique em Download CSV. Pronto — o arquivo mais recente será baixado.
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).
-
Clone o repositório:
git clone https://github.com/PulseDataLabs/PulseFlat.git cd PulseFlat -
Instale as dependências:
pip install -r requirements.txt
-
Configure o arquivo de variáveis de ambiente:
cp .env.example .env
(Edite o
.envcom suas chaves caso precise utilizar APIs oficiais da ANBIMA ou B3).
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)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/ -vOs 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). |
Diária / Snapshot |
| IBGE | IBGE SIDRA API | Índices oficiais de inflação do Brasil (IPCA, IPCA-15, INPC). | Mensal |
| Misc | Yahoo Finance / ONU | Cotações históricas de índices globais e lista de empresas brasileiras participantes do Pacto Global da ONU. | Diária |
Contribuições são bem-vindas! Se você encontrou um bug, tem uma ideia para um novo scraper ou quer melhorar a documentação:
- Abra uma issue para discutir a mudança
- Faça um fork do repositório
- Crie um branch (
git checkout -b feature/minha-feature) - Commit suas mudanças (
git commit -m 'feat: adiciona scraper X') - Push para o branch (
git push origin feature/minha-feature) - Abra um Pull Request
Veja o guia do desenvolvedor para entender a arquitetura e como criar novos scrapers.
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.
