Tutorial 02 jul 2026 15 min de leitura por bolsai

Como criar um agente de IA para monitorar sua carteira de ações (2026)

Acompanhar uma carteira de ações à mão é repetitivo: abrir cada ativo, olhar preço, conferir se algum indicador mudou, checar se saiu dividendo. Este guia mostra como automatizar esse ritual com um agente de IA para monitorar carteira de ações escrito em Python. O agente lê os tickers da sua carteira, busca cotação, fundamentos e dividendos na API da bolsai, monta um resumo compacto e passa esse resumo para um LLM sinalizar o que mudou desde ontem. Tudo rodando sozinho no cron, depois do fechamento do pregão.

O que é um agente de monitoramento de carteira

Um agente de IA para monitorar carteira de ações é um programa que roda em intervalos fixos, coleta dados atualizados de cada ativo da carteira a partir de uma API financeira, compara o estado atual com o estado anterior e usa um modelo de linguagem para transformar essas diferenças em um resumo legível: variações de preço relevantes, mudanças em múltiplos, e dividendos com data-ex se aproximando. Ele não opera ordens nem prevê o futuro; ele reduz o esforço manual de "dar uma olhada na carteira" a uma mensagem diária.

Vale separar dois papéis logo de início, porque essa separação é o que mantém o agente confiável. O primeiro papel é a coleta de dados: determinística, previsível, feita por chamadas HTTP à API da bolsai. Dado um ticker, a API devolve sempre os mesmos campos no mesmo formato. O segundo papel é a interpretação: feita por um LLM, que recebe os números já coletados e os descreve em linguagem natural. O LLM nunca busca dado nenhum por conta própria e nunca inventa um valor; ele só comenta o que o agente entregou. Essa divisão é o que impede o problema mais comum de agentes financeiros, que é o modelo "chutar" um número plausível quando não tem a informação.

A segunda coisa a entender é a cadência dos dados. A bolsai serve dados de fechamento (end-of-day): preços e indicadores macro são atualizados uma vez por dia, às 20:30 no horário de Brasília, depois que o pregão da B3 fecha. Dados de empresas, balanços e dividendos são atualizados semanalmente. Isso define o desenho do agente: ele não é um painel de day trade em tempo real; é um observador diário. O melhor horário para rodá-lo é depois das 20:30 BRT, quando os dados do dia já entraram. Rodar antes disso significa reprocessar os números de ontem.

Se você quer o passo anterior, montar a carteira de forma programática a partir de critérios de dividendos, o post como montar uma carteira de dividendos com Python e a API cobre a seleção dos ativos. Aqui partimos do princípio de que a carteira já existe e o objetivo é vigiá-la.

Arquitetura do agente em quatro etapas

O agente tem um fluxo linear e fácil de depurar. São quatro etapas, executadas em sequência a cada rodada:

  1. Ler a carteira. Uma lista de tickers, guardada em um arquivo simples (JSON, CSV ou até uma constante no código). É a única entrada que você mantém manualmente.
  2. Coletar dados por ativo. Para cada ticker, o agente faz três chamadas à API bolsai: cotação de fechamento, indicadores fundamentalistas e histórico recente de dividendos.
  3. Resumir e comparar. O agente monta um resumo compacto em texto com os dados de hoje e, se houver um snapshot da execução anterior salvo em disco, calcula as variações (preço subiu/caiu, algum múltiplo mudou, dividendo novo apareceu).
  4. Passar para o LLM. Esse resumo textual vai para um modelo de linguagem com uma instrução clara: descreva, em português, o que mudou e o que merece atenção, sem inventar números e sem recomendar compra ou venda.

O resultado pode ser impresso no terminal, salvo em arquivo, enviado por e-mail ou empurrado para um canal de mensagens. A quinta etapa implícita é o agendamento: o sistema operacional dispara o script uma vez por dia via cron. A parte que exige atenção técnica é a etapa 2, porque é onde o agente toca a API real. As demais são cola em torno dela.

Os três endpoints da bolsai que o agente usa

Todo o dado numérico do agente vem de três endpoints da API da bolsai, todos no prefixo /api/v1/ e todos exigindo uma chave de API. A base é https://api.usebolsai.com/api/v1. A documentação completa de parâmetros e campos está em usebolsai.com/docs.

Cotação de fechamento

GET /stocks/{ticker}/quote devolve a última cotação disponível: abertura, máxima, mínima, fechamento, volume e a data do pregão (trade_date). É o preço que o agente usa para calcular variação em relação ao snapshot anterior. Lembre: é o fechamento do dia, não cotação intraday.

Indicadores fundamentalistas

GET /fundamentals/{ticker} devolve os indicadores fundamentalistas do ativo, com nomes de campo em snake_case: pl, pvp, ev_ebitda, roe, roic, net_margin, dividend_yield (TTM), net_debt_ebitda, entre outros, além de market_cap e reference_date. Como fundamentos mudam pouco de um dia para o outro, o valor deles no monitoramento é detectar saltos: uma variação brusca de pl ou de dividend_yield geralmente reflete um balanço novo ingerido ou uma variação forte de preço, e é exatamente o tipo de coisa que vale sinalizar.

Dividendos

GET /dividends/{ticker} devolve o histórico de proventos, com o parâmetro years controlando a janela (padrão 1 ano). Cada provento traz ex_date (data-ex), payment_date (data de pagamento), value e type (DIVIDEND, JCP ou BONIFICACAO). É a peça que permite ao agente avisar "a data-ex de tal ativo é daqui a poucos dias" ou "apareceu um provento novo que não estava no snapshot de ontem".

Crie sua chave gratuita para rodar os exemplos. Plano grátis com 200 requisições/dia, sem cartão.

Pegar chave grátis

Coletando os dados de um ativo

Não existe SDK oficial em Python para a bolsai; a API é REST pura e a biblioteca requests resolve tudo. O exemplo abaixo é executável: dado um ticker, ele bate nos três endpoints e devolve um dicionário compacto com os campos que interessam ao monitoramento. Guarde sua chave em uma variável de ambiente (BOLSAI_API_KEY) e nunca no código.

# Exemplo 1: coleta de dados de um ativo (executável)
# pip install requests
import os
import requests

BASE = "https://api.usebolsai.com/api/v1"
API_KEY = os.environ["BOLSAI_API_KEY"]  # chave em variável de ambiente


def coletar_ativo(ticker: str) -> dict:
    """Busca cotação, fundamentos e dividendos de um ticker na bolsai."""
    params = {"api_key": API_KEY}

    # 1. cotação de fechamento (end-of-day)
    quote = requests.get(f"{BASE}/stocks/{ticker}/quote", params=params).json()

    # 2. indicadores fundamentalistas
    fund = requests.get(f"{BASE}/fundamentals/{ticker}", params=params).json()

    # 3. dividendos dos últimos 12 meses
    div = requests.get(
        f"{BASE}/dividends/{ticker}",
        params={**params, "years": 1},
    ).json()

    return {
        "ticker": ticker,
        "close": quote.get("close"),
        "trade_date": quote.get("trade_date"),
        "pl": fund.get("pl"),
        "pvp": fund.get("pvp"),
        "roe": fund.get("roe"),
        "dividend_yield": fund.get("dividend_yield"),
        "proventos": div,  # lista com ex_date, value, type...
    }


if __name__ == "__main__":
    dados = coletar_ativo("PETR4")
    print(dados["ticker"], dados["close"], dados["trade_date"])
    print("P/L:", dados["pl"], "| DY:", dados["dividend_yield"])

A resposta de /stocks/PETR4/quote tem o formato abaixo (valores meramente ilustrativos, não são a cotação real de hoje). O ponto a reter é a estrutura: os nomes dos campos são fixos, e é sobre eles que o agente calcula variações.

# Formato ilustrativo da resposta de /stocks/PETR4/quote
{
  "ticker": "PETR4",
  "open": 00.00,
  "high": 00.00,
  "low": 00.00,
  "close": 00.00,
  "volume": 000000000,
  "trade_date": "2026-07-02"
}

Para entender cada indicador de /fundamentals e como ele é calculado (a metodologia de P/L, ROE, EV/EBITDA e afins), a referência de campos está detalhada em /docs. O agente não precisa recalcular nada: recebe os indicadores prontos.

Montando o resumo compacto para o LLM

O erro mais caro em agentes com LLM é jogar o JSON bruto de todos os ativos no prompt. Isso desperdiça tokens, dilui a atenção do modelo e aumenta a chance de ele misturar dados de tickers diferentes. A prática correta é o agente fazer o trabalho pesado de forma determinística em Python — calcular as variações, filtrar o que é relevante — e mandar para o LLM apenas um resumo enxuto e já organizado.

A comparação com a execução anterior é o coração do monitoramento. O agente salva, ao fim de cada rodada, um snapshot em disco (um JSON com preço e indicadores de cada ativo). Na rodada seguinte, ele carrega esse snapshot e calcula as diferenças. Sem snapshot anterior (primeira execução), ele apenas reporta o estado atual. O código abaixo monta esse resumo textual.

# Exemplo 2: montar resumo comparando com o snapshot anterior
import json
from datetime import date, datetime


def variacao_pct(atual, anterior):
    if atual is None or anterior in (None, 0):
        return None
    return round((atual - anterior) / anterior * 100, 2)


def dias_para(ex_date_str):
    """Dias até a data-ex (positivo = futuro)."""
    ex = datetime.strptime(ex_date_str, "%Y-%m-%d").date()
    return (ex - date.today()).days


def resumir(dados_hoje: list, snapshot_ontem: dict) -> str:
    linhas = []
    for a in dados_hoje:
        t = a["ticker"]
        ant = snapshot_ontem.get(t, {})
        var = variacao_pct(a["close"], ant.get("close"))
        parte = f"{t}: fech R${a['close']}"
        if var is not None:
            parte += f" ({var:+.2f}% vs ontem)"
        parte += f" | P/L {a['pl']} | DY {a['dividend_yield']}"

        # dividendos com data-ex nos próximos 7 dias
        for p in a["proventos"]:
            d = dias_para(p["ex_date"])
            if 0 <= d <= 7:
                parte += (
                    f" | PROVENTO {p['type']} R${p['value']}"
                    f" data-ex em {d}d ({p['ex_date']})"
                )
        linhas.append(parte)
    return "\n".join(linhas)


# snapshot da execução anterior (vazio na 1ª rodada)
try:
    with open("snapshot.json") as f:
        snapshot = json.load(f)
except FileNotFoundError:
    snapshot = {}

resumo = resumir(dados_hoje, snapshot)
print(resumo)

O resumo gerado é um bloco de texto curto, uma linha por ativo, já com as variações calculadas e os dividendos iminentes destacados. Esse é o formato ideal para o LLM: ele chega pronto para ser interpretado, não para ser processado. O modelo gasta seus tokens raciocinando sobre o que é relevante, não fazendo aritmética que o Python já fez melhor e de graça.

Passando o resumo para o LLM

Aqui o guia se mantém deliberadamente agnóstico ao modelo. O padrão vale para qualquer LLM que aceite um prompt e devolva texto: um serviço na nuvem, um modelo local, ou o que você já usa. O que importa é a forma da chamada, não o fornecedor. A função abaixo é um placeholder claro; substitua o corpo pela biblioteca do modelo que você escolher.

Duas coisas são inegociáveis no prompt, independentemente do modelo. Primeira: a instrução de que o LLM só pode comentar os números presentes no resumo, jamais inventar ou completar valores ausentes. Segunda: a proibição de recomendar compra ou venda; o agente descreve fatos, a decisão é do investidor. Essas duas regras são o que separa um monitor honesto de um gerador de opiniões sem lastro.

# Exemplo 3: interpretar o resumo com um LLM (agnóstico)

INSTRUCOES = """
Você é um assistente que monitora uma carteira de ações da B3.
Recebe um resumo com dados de FECHAMENTO (end-of-day) de cada
ativo e as variações em relação ao dia anterior.

Sua tarefa: escrever, em português do Brasil, um boletim curto
apontando o que merece atenção hoje. Considere relevantes:
- variações de preço acima de 3% (para cima ou para baixo);
- mudanças perceptíveis em P/L ou Dividend Yield;
- dividendos com data-ex nos próximos dias.

REGRAS:
- Comente APENAS os números presentes no resumo. Nunca invente
  ou estime valores que não estejam ali.
- NÃO recomende compra nem venda. Apenas descreva os fatos.
- Se nada relevante mudou, diga isso em uma linha.
"""


def interpretar(resumo: str) -> str:
    # Substitua pelo cliente do LLM que você usar.
    # O contrato é sempre o mesmo: instruções + resumo -> texto.
    prompt = f"{INSTRUCOES}\n\n=== RESUMO DA CARTEIRA ===\n{resumo}"
    return chamar_llm(prompt)  # sua função de LLM aqui


boletim = interpretar(resumo)
print(boletim)

Repare que a fronteira entre dado e interpretação está limpa: a bolsai fornece os números, o Python calcula as variações, e o LLM só redige. Se amanhã você trocar de modelo, a única linha que muda é a implementação de chamar_llm. Toda a coleta de dados — a parte que precisa estar correta — permanece intacta. Para quem quer ir além do monitoramento e construir um agente que também analisa os ativos com raciocínio próprio, o post agente de IA para análise de ações brasileiras com Claude e MCP aprofunda o loop de tool use.

Juntando tudo: o agente completo

Com as três peças prontas — coleta, resumo e interpretação — o agente completo é só a orquestração delas, seguida da gravação do snapshot para a próxima rodada. Este é o script que o cron vai chamar.

# Exemplo 4: agente completo (agent.py)
import json

CARTEIRA = ["PETR4", "ITUB4", "VALE3", "BBAS3", "WEGE3"]


def main():
    # 1. coletar dados de cada ativo da carteira
    dados_hoje = [coletar_ativo(t) for t in CARTEIRA]

    # 2. carregar snapshot anterior (se existir)
    try:
        with open("snapshot.json") as f:
            snapshot = json.load(f)
    except FileNotFoundError:
        snapshot = {}

    # 3. montar resumo comparando hoje vs ontem
    resumo = resumir(dados_hoje, snapshot)

    # 4. interpretar com o LLM e emitir o boletim
    boletim = interpretar(resumo)
    print(boletim)
    # aqui você pode enviar por e-mail, Telegram, etc.

    # 5. salvar snapshot de hoje para a próxima execução
    novo = {
        a["ticker"]: {"close": a["close"], "pl": a["pl"],
                        "dividend_yield": a["dividend_yield"]}
        for a in dados_hoje
    }
    with open("snapshot.json", "w") as f:
        json.dump(novo, f)


if __name__ == "__main__":
    main()

Cada execução deixa um snapshot.json atualizado, que vira a base de comparação da próxima. Na primeira vez o boletim reporta só o estado atual; a partir da segunda, ele passa a falar em variações. É um agente com memória mínima, e essa memória cabe em um arquivo JSON.

Agendando com cron: depois das 20:30 BRT

A automação fecha o ciclo. Como os dados de preço da bolsai são atualizados às 20:30 no horário de Brasília, agendar a execução para logo depois garante que o agente sempre trabalhe com o fechamento do dia. Um horário confortável é 21:00 BRT, dando margem para a ingestão terminar. No crontab de um servidor configurado no fuso de Brasília, a linha fica assim:

# crontab -e
# roda o agente às 21:00 (BRT), de segunda a sexta
0 21 * * 1-5 cd /home/user/carteira && /usr/bin/python3 agent.py >> agent.log 2>&1

Rodar de segunda a sexta (1-5) evita gastar requisições nos fins de semana, quando não há pregão novo — embora a bolsai atualize dados cadastrais e dividendos aos sábados, o preço de fechamento não muda no fim de semana. Se o seu servidor está em UTC em vez de BRT, lembre que 21:00 BRT equivale a 00:00 UTC; ajuste a hora do cron de acordo com o fuso da máquina. Se você não tem um servidor sempre ligado, qualquer agendador de tarefas na nuvem que execute um script Python em horário fixo serve igualmente.

Requisições, limites e escolha de plano

O consumo de requisições é linear e fácil de prever: número de ativos × endpoints por ativo × execuções por dia. Com os três endpoints deste guia e uma execução diária, a conta por rodada é três chamadas por ativo. A tabela abaixo mostra o gasto diário em função do tamanho da carteira, assumindo uma execução por dia.

Tamanho da carteira Chamadas por execução Cabe no plano gratuito (200/dia)?
5 ativos 15 Sim, com folga
10 ativos 30 Sim
20 ativos 60 Sim
50 ativos 150 No limite; melhor ir para o Pro
50 ativos, 3x ao dia 450 Não; requer plano Pro

O plano gratuito da bolsai oferece 200 requisições por dia e cobre confortavelmente carteiras de até algumas dezenas de ativos com uma execução diária, que é o cenário natural de um monitor de fechamento. Se você acompanha muitos ativos, quer executar mais de uma vez ao dia, ou pretende adicionar mais endpoints por ativo (histórico de preços, estatísticas de 52 semanas), o plano Pro (R$49/mês, 10.000 requisições por dia) dá espaço de sobra e ainda libera histórico completo. Os limites de cota reiniciam à meia-noite UTC. Você pode acompanhar o consumo da sua chave e trocar de plano quando quiser no painel.

Uma otimização simples reduz o gasto: fundamentos e dividendos mudam pouco no dia a dia. Você pode buscar cotação todo dia (é o que varia) mas atualizar fundamentos e dividendos apenas uma vez por semana, guardando os valores no snapshot. Isso derruba o consumo diário para uma chamada por ativo na maioria dos dias, e mantém o agente igualmente útil.

Mantendo o agente confiável

Três cuidados fazem a diferença entre um script que quebra na primeira semana e um agente que roda por meses sem intervenção.

Trate falhas de rede e tickers inválidos

Uma chamada HTTP pode falhar por timeout, e um ticker digitado errado devolve erro. Envolva cada coleta em tratamento de exceção: se um ativo falhar, o agente registra o problema e segue para os demais, em vez de abortar a carteira inteira. O formato de ticker aceito segue o padrão da B3 (por exemplo PETR4, VALE3, ITUB4, HGLG11); a API é case-insensitive.

Nunca deixe o LLM inventar números

A regra de ouro merece repetição porque é onde agentes financeiros mais erram. Todo número que aparece no boletim tem de existir no resumo que o Python montou a partir das respostas da API. Uma verificação barata e eficaz: depois de gerar o boletim, cheque programaticamente se os valores citados batem com os do resumo. Um número no texto que não está nos dados coletados é sinal de alucinação, e o boletim daquele dia deve ser descartado ou regenerado.

Lembre que o dado é de fechamento

O agente é um observador diário, não um alerta de tempo real. Se o preço de um ativo despencar às 11h da manhã, o agente só verá isso no boletim da noite, depois da atualização das 20:30 BRT. Para monitoramento de fundamentos, dividendos e tendências de fechamento — que é o uso honesto dessa ferramenta — isso é perfeitamente adequado. Para reação intraday, nenhuma API de dados de fechamento serve, e a bolsai não promete isso.

Para onde evoluir o agente

O esqueleto deste guia é deliberadamente enxuto para ser entendido de ponta a ponta, mas ele abre várias direções naturais. Você pode enviar o boletim por e-mail ou para um canal de mensagens em vez de imprimir no terminal. Pode enriquecer o resumo com indicadores macro — a API tem um endpoint /macro com séries como Selic, CDI e IPCA — para o LLM contextualizar a carteira frente ao cenário. Pode adicionar histórico de preços de 52 semanas para sinalizar quando um ativo se aproxima de máxima ou mínima anual. E, para FIIs, a bolsai tem endpoints dedicados (/fiis) que permitem estender o mesmo padrão a fundos imobiliários.

Um caminho mais ambicioso é dar autonomia real ao LLM via servidor MCP da bolsai, deixando o próprio modelo decidir quais ferramentas chamar em vez de receber um resumo pré-montado. Esse é o passo de "agente que monitora" para "agente que investiga", detalhado no post sobre MCP Server para B3. Para monitoramento diário, porém, o padrão pré-montado deste guia é mais barato, mais previsível e mais fácil de auditar — que é exatamente o que você quer de algo rodando sozinho todas as noites.

Perguntas frequentes

O agente monitora a carteira em tempo real?

Não. Os dados da bolsai são de fechamento (end-of-day), atualizados uma vez por dia às 20:30 no horário de Brasília, após o fechamento do pregão da B3. Um agente de monitoramento de carteira não é uma tela de day trade: ele foi desenhado para rodar uma vez por dia, depois das 20:30 BRT, comparar o estado de hoje com o de ontem e sinalizar o que mudou. Não há cotação intraday nem streaming de preços.

Quantas requisições o agente consome por dia?

Depende do número de tickers e de quantos endpoints você consulta por ativo. No exemplo deste guia, cada ticker faz três chamadas: cotação (/stocks/{ticker}/quote), fundamentos (/fundamentals/{ticker}) e dividendos (/dividends/{ticker}). Uma carteira de 10 ativos gasta cerca de 30 requisições por execução diária. O plano gratuito oferece 200 requisições por dia, suficiente para carteiras pequenas; carteiras maiores ou execuções mais frequentes justificam o plano Pro, com 10.000 por dia.

Preciso de uma chave de API para rodar o agente?

Sim. Todos os endpoints de dados da bolsai exigem uma chave. Você cria uma gratuita fazendo login com Google em usebolsai.com/dashboard. A chave pode ir como parâmetro api_key na URL ou no cabeçalho Authorization: Bearer. Nunca deixe a chave escrita no código; use uma variável de ambiente.

Qual LLM devo usar no agente?

O padrão descrito aqui é agnóstico ao modelo: o agente monta um resumo compacto em texto com os dados coletados da bolsai e envia esse resumo para qualquer LLM capaz de receber um prompt e devolver texto — na nuvem ou local. O ponto crítico não é qual modelo, e sim a regra de ouro: o LLM só comenta os números que o agente coletou da API, nunca inventa valores. A coleta é determinística e feita pela bolsai; o LLM apenas interpreta e resume.

O agente dá recomendações de compra ou venda?

Não, e não deve. O objetivo é monitoramento informativo: sinalizar variações de preço, mudanças em indicadores e eventos de dividendos com data-ex próxima. A interpretação e a decisão continuam sendo do investidor. Os dados da bolsai são informativos e analíticos, não constituem recomendação de investimento. Instrua o LLM explicitamente a descrever fatos, não a sugerir operações.

Leia também

O conteúdo deste artigo tem caráter educacional e técnico. Os dados da bolsai são de fechamento (end-of-day) e informativos; nada aqui constitui recomendação de compra ou venda de ativos financeiros. Valores usados em respostas de exemplo são ilustrativos e não representam cotações ou indicadores reais. As decisões de investimento são de responsabilidade exclusiva do investidor. Por bolsai, 2 de julho de 2026.