DEV Community

Cover image for Rebalanceando uma carteira sem vender nada: o algoritmo do aporte
Diego
Diego

Posted on

Rebalanceando uma carteira sem vender nada: o algoritmo do aporte

Como dividir um aporte mensal entre vários ativos pra aproximar a carteira das metas — sem precisar vender nada nem pagar imposto.

Todo investidor de longo prazo bate na mesma parede: você define uma alocação-alvo (digamos 40% ações, 25% renda fixa, 20% FIIs, 15% internacional), o mercado se mexe, e três meses depois a carteira tá torta. O conselho clássico é rebalancear vendendo o que subiu e comprando o que caiu.

O problema: vender gera imposto, custo de corretagem e, no Brasil, dor de cabeça com DARF. Existe um jeito muito mais barato de rebalancear se você aporta todo mês — direcionar o dinheiro novo pras categorias que estão atrasadas. Sem vender uma cota sequer.

Esse post mostra o algoritmo que usei pra isso. É mais sutil do que parece.

A tentação ingênua (que está errada)

A primeira ideia de todo mundo é: "tenho R$ 1.000 pra aportar, divido pelas metas". 40% vão pra ações, 25% pra renda fixa, e assim por diante.

# ERRADO
for categoria in categorias:
    orcamento[categoria] = aporte * categoria.meta_pct / 100
Enter fullscreen mode Exit fullscreen mode

Isso não rebalanceia nada — só perpetua a alocação atual. Se suas ações já estão em 50% (acima dos 40% de meta), jogar mais 40% do aporte nelas piora o desvio.

O que você quer é o contrário: dar mais dinheiro pra quem está mais atrás da meta.

O conceito central: o gap

Para cada categoria, calcule quanto ela deveria valer depois do aporte, e quanto falta pra chegar lá:

novo_total = total_atual + aporte

def gap(categoria):
    valor_alvo = novo_total * categoria.meta_pct / 100
    return max(0, valor_alvo - categoria.valor_atual)
Enter fullscreen mode Exit fullscreen mode

O max(0, ...) é importante: categorias que já passaram da meta têm gap zero — não recebem nada do aporte (e vão naturalmente "voltar" à meta conforme o resto cresce em volta delas).

Agora distribua o orçamento proporcionalmente aos gaps, não às metas:

total_gap = sum(gap(c) for c in categorias)

for categoria in categorias:
    orcamento[categoria] = aporte * gap(categoria) / total_gap
Enter fullscreen mode Exit fullscreen mode

Pronto — o dinheiro flui sozinho pras categorias subalocadas. Quanto mais atrás uma categoria está, maior a fatia que ela recebe. Quando todas estão na meta, os gaps se igualam e o aporte cai proporcionalmente como no caso ingênuo (que aí, sim, é o comportamento certo).

Caso de borda: se todas as categorias já estão na ou acima da meta, total_gap == 0 e você cairia numa divisão por zero. Nesse caso, faça fallback pra distribuição por meta — não há o que corrigir, só manter a proporção.

O problema chato: ações são indivisíveis

Até aqui é aritmética limpa. A realidade quebra ela: você não compra "R$ 213,47 de PETR4". Compra um número inteiro de ações.

def sugestao_compra(ativo, orcamento):
    quantidade = int(orcamento / ativo.preco)   # arredonda pra baixo
    if quantidade <= 0:
        return None
    custo = quantidade * ativo.preco
    return {'ticker': ativo.ticker, 'quantidade': quantidade, 'custo': custo}
Enter fullscreen mode Exit fullscreen mode

Esse int() joga fora as sobras de cada ativo. Some isso por uma carteira de 15 ativos e você facilmente deixa R$ 150 do aporte parados. Inaceitável — o usuário quer ver o dinheiro alocado.

Gastando o troco: a passada gulosa

A solução é uma segunda passada que pega o que sobrou e gasta de forma gulosa, sempre na categoria com maior déficit restante:

def gastar_restante(restante, ativos, simulado):
    while restante > 0:
        melhor = None
        maior_deficit = 0

        for ativo in ativos:
            if ativo.preco > restante:          # não cabe nem 1 unidade
                continue
            deficit = ativo.categoria.valor_alvo - simulado[ativo.categoria]
            if deficit > maior_deficit:
                maior_deficit = deficit
                melhor = ativo

        if melhor is None:                       # nada mais cabe no troco
            break

        restante -= melhor.preco                 # compra +1 unidade
        simulado[melhor.categoria] += melhor.preco
        registrar_compra(melhor, quantidade=1)

    return restante
Enter fullscreen mode Exit fullscreen mode

Cada volta do loop compra uma unidade do ativo cuja categoria está mais atrás, atualiza o déficit simulado, e repete. Para quando o troco não compra nem a ação mais barata. Isso espreme o aporte até o último real possível, sem nunca furar a lógica de meta.

(Renda fixa é mais fácil: como é divisível, dá pra alocar centavos exatos — sem o problema do inteiro.)

Vender só quando você quiser

Às vezes o desvio é grande demais pra corrigir só com aporte. Aí o usuário opta por rebalancear vendendo. O cálculo do excesso é o espelho do gap:

def sugestoes_venda(categoria):
    excesso = categoria.valor_atual - categoria.valor_alvo
    if excesso <= 0:
        return []
    # vende de cada ativo proporcional ao peso dele na categoria
    vendas = []
    for ativo in categoria.ativos:
        fatia = excesso * (ativo.valor_atual / categoria.valor_atual)
        qtd = int(fatia / ativo.preco)
        if qtd > 0:
            vendas.append({'ticker': ativo.ticker, 'quantidade': qtd})
    return vendas
Enter fullscreen mode Exit fullscreen mode

E como isso é Brasil, dá pra adicionar um flag evitar_vendas_ir=True que pula ETFs e FIIs (tickers terminados em 11, sempre tributados) das sugestões de venda — você rebalanceia mexendo só no que é isento.

Por que isso importa

O resultado é que o investidor abre o app, digita "vou aportar R$ 1.000", e recebe uma lista do tipo:

Comprar  12 BOVA11   × R$ 38,50   = R$ 462,00
Comprar   8 MXRF11   × R$ 10,30   = R$  82,40
Aportar     CDB Nubank          = R$ 455,60
Sobra: R$ 0,00
Enter fullscreen mode Exit fullscreen mode

Tudo alocado, carteira mais perto das metas, e nenhum imposto pago. É a diferença entre "rebalanceamento" como tarefa trimestral chata e como algo que acontece sozinho a cada aporte.


Construí isso no Balance, um app de código que ajuda investidores brasileiros a manter a carteira na meta calculando exatamente essas sugestões a cada aporte. O serviço completo lida ainda com cripto (frações de 8 casas), múltiplos mercados e cálculo de IR — mas o coração é o algoritmo acima.

Top comments (0)