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
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)
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
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 == 0e 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}
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
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
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
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.
For further actions, you may consider blocking this person and/or reporting abuse
