Descrição do Projeto
Pipeline ETL para coleta de dados históricos de ações de bancos brasileiros através da API yfinance, com processamento e armazenamento em PostgreSQL e transformações orquestradas via dbt (Data Build Tool).
O projeto se divide em três partes: coleta de dados históricos de fechamento de ações (ITUB, BBDC4, BBAS3, SANB11), processamento e carga no banco relacional, e camada de transformação com dbt para preparar os dados para análises e visualizações.
Arquitetura do Pipeline
┌──────────────────────┐
│ yfinance API │
│ Dados de ações B3 │
│ · ITUB · BBDC4 │
│ · BBAS3 · SANB11 │
└──────────┬───────────┘
│ Python + yfinance
▼
┌──────────────────────┐
│ Processamento │
│ · Pandas DataFrame │
│ · Limpeza e formato │
│ · SQLAlchemy Engine │
└──────────┬───────────┘
│ INSERT / UPSERT
▼
┌──────────────────────┐
│ PostgreSQL │
│ Tabela raw: │
│ · tickers_historico │
└──────────┬───────────┘
│ dbt run
▼
┌──────────────────────┐
│ dbt │
│ · staging │
│ · analytics │
│ · testes e docs │
└──────────┬───────────┘
│
▼
┌──────────────────────┐
│ Análises e BI │
│ PostgreSQL queries │
└──────────────────────┘
Fluxo de Dados
- Coleta: Python + yfinance baixa dados históricos de fechamento dos tickers
- Processamento: Pandas organiza os dados em DataFrame com colunas padronizadas
- Carga: SQLAlchemy insere os dados na tabela raw do PostgreSQL
- Transformação: dbt aplica camadas staging e analytics com modelos SQL
Stack Tecnológica
Linguagem & Bibliotecas
- Python 3.8+
- yfinance — API de dados financeiros
- Pandas — manipulação de dados
- SQLAlchemy — ORM e conexão DB
Banco de Dados
- PostgreSQL
- Tabelas raw e analytics
- Schemas separados por camada
Transformação
- dbt (Data Build Tool)
- Modelos staging e analytics
- Testes de qualidade de dados
- Documentação automatizada
Tickers Monitorados
- ITUB — Itaú Unibanco
- BBDC4 — Bradesco
- BBAS3 — Banco do Brasil
- SANB11 — Santander
Exemplos de Código
Coleta de Dados com yfinance
import yfinance as yf import pandas as pd from sqlalchemy import create_engine from datetime import datetime # Tickers dos principais bancos brasileiros TICKERS = ['ITUB', 'BBDC4', 'BBAS3', 'SANB11'] # Adicionar sufixo .SA para B3 tickers_sa = [f'{t}.SA' for t in TICKERS] # Baixar dados históricos (últimos 2 anos) df = yf.download( tickers_sa, period='2y', interval='1d' ) # Processar e formatar para PostgreSQL df_clean = df.stack(level=0).reset_index() df_clean.columns = ['data', 'ticker', 'open', 'high', 'low', 'close', 'adj_close', 'volume'] # Conectar ao PostgreSQL e inserir engine = create_engine(os.getenv('DATABASE_URL')) df_clean.to_sql('raw_tickers', engine, if_exists='append', index=False)
Modelo dbt — staging
-- models/staging/stg_tickers.sql {{ config(materialized='view') }} SELECT data, ticker, CAST(open AS DECIMAL(10,2)) AS preco_abertura, CAST(high AS DECIMAL(10,2)) AS preco_maximo, CAST(low AS DECIMAL(10,2)) AS preco_minimo, CAST(close AS DECIMAL(10,2)) AS preco_fechamento, volume AS volume_negociado FROM {{ source('raw', 'raw_tickers') }} WHERE close > 0 AND data >= '2022-01-01'
Modelo dbt — analytics
-- models/analytics/metricas_diarias.sql {{ config(materialized='table') }} SELECT data, ticker, preco_fechamento, LAG(preco_fechamento) OVER ( PARTITION BY ticker ORDER BY data ) AS fechamento_anterior, ROUND( (preco_fechamento - LAG(preco_fechamento) OVER ( PARTITION BY ticker ORDER BY data )) / NULLIF(LAG(preco_fechamento) OVER ( PARTITION BY ticker ORDER BY data ), 0) * 100, 2 ) AS variacao_pct, volume_negociado FROM {{ ref('stg_tickers') }} ORDER BY data DESC, ticker
Desafios e Soluções
1. Dados da B3 com sufixo .SA
Desafio: Tickers brasileiros na yfinance exigem o sufixo .SA, gerando confusão no mapeamento.
Solução: Função de mapeamento automático que adiciona o sufixo e mantém o ticker original na tabela.
2. Volumetria de dados históricos
Desafio: 2 anos de dados diários para 4 tickers gera milhares de registros.
Solução: Lógica de upsert com controle de data para evitar duplicação e permitir execuções incrementais.
3. Qualidade e consistência
Desafio: Dados zerados ou nulos em feriados e fins de semana.
Solução: Filtros no modelo staging do dbt removem registros com close = 0 e datas inválidas.
Resultados
Tickers Monitorados
Histórico Coletado
Transformações SQL
Entregáveis
- Pipeline Python completo para coleta automatizada de dados da B3
- Modelo de dados relacional em PostgreSQL com tabelas raw e analytics
- Transformações dbt com staging, métricas diárias e variação percentual
- Base de dados pronta para dashboards financeiros e análises de mercado
Aprendizados
- Como consumir APIs financeiras com yfinance e tratar dados do mercado B3
- Estruturação de pipeline ETL com Python + SQLAlchemy para carga em PostgreSQL
- Uso do dbt para camadas de transformação com staging e analytics
- Implementação de window functions SQL para métricas de variação percentual