惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Y
Y Combinator Blog
博客园_首页
量子位
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
H
Help Net Security
The Cloudflare Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
A
About on SuperTechFans
腾讯CDC
H
Hackread – Cybersecurity News, Data Breaches, AI and More
D
DataBreaches.Net
L
LangChain Blog
Stack Overflow Blog
Stack Overflow Blog
Jina AI
Jina AI
月光博客
月光博客
云风的 BLOG
云风的 BLOG
博客园 - 聂微东
Hugging Face - Blog
Hugging Face - Blog
雷峰网
雷峰网
C
Check Point Blog
V
V2EX
T
Tailwind CSS Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Microsoft Security Blog
Microsoft Security Blog

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
Rebalanceando a carteira só com o próximo aporte (sem ven...
Diego · 2026-06-24 · via DEV Community

Diego

Parte de uma série sobre construir o Balance, um app de rebalanceamento de carteira
para investidores BR/US/Cripto, como dev solo. O código aqui é simplificado em relação ao real.

Todo investidor que faz aportes recorrentes esbarra na mesma parede:

"Tenho R$ 1.000 pra investir esse mês. O que eu compro pra minha carteira ficar perto da meta?"

A resposta de manual — "venda o que está acima, compre o que está abaixo" — tem custo: IR, corretagem e a disciplina emocional de vender o que está subindo. Existe um caminho mais gentil, que quase ninguém formaliza: rebalancear usando só o novo aporte.

É esse o algoritmo no coração do Balance. Vamos construir.

A ideia em um exemplo

Digamos que sua meta seja:

Categoria Meta
Ações 40%
Renda fixa 25%
FIIs 20%
Internacional 15%

Esse mês, depois de algumas oscilações de preço, você está em 46% de ações e 16% de FIIs. Ações acima, FIIs abaixo.

Em vez de vender ações (e gerar um fato gerador de IR), você direciona o aporte do mês inteiro para as categorias abaixo da meta. A carteira converge para o alvo sem uma única venda.

O algoritmo

A coisa toda é: descobrir o quanto cada categoria está longe de onde deveria estar depois do aporte, e distribuir o aporte proporcionalmente a esses gaps.

from collections import defaultdict
from decimal import Decimal

def calculate(self, deposit_amount: Decimal) -> dict:
    deposit = Decimal(deposit_amount).quantize(Decimal('0.01'))

    investments = self._category_investments()   # {category_id: [(kind, item), ...]}
    current_total = self.portfolio.total_value
    new_total = current_total + deposit

    # 1. Valor atual em cada categoria
    cat_value = defaultdict(lambda: Decimal('0'))
    for cat_id, rows in investments.items():
        for kind, item in rows:
            cat_value[cat_id] += item.current_value

    # 2. Gap = o quanto cada categoria está ABAIXO da meta pós-aporte
    gaps = {}
    for cat_id, rows in investments.items():
        category = rows[0][1].category
        target_value = new_total * category.target_percentage / Decimal('100')
        gaps[cat_id] = max(Decimal('0'), target_value - cat_value[cat_id])

    total_gap = sum(gaps.values())

    # 3. Divide o aporte entre as categorias, proporcional a cada gap
    cat_budget = {
        cat_id: (deposit * gaps[cat_id] / total_gap).quantize(Decimal('0.01'))
        for cat_id in investments
    }
    ...

Três passos:

  1. Valor por categoria — soma o que você tem hoje em cada uma.
  2. Gap por categoriamax(0, meta − atual). Categorias já na meta ou acima têm gap zero; não recebem nada.
  3. Orçamento por categoria — distribui o aporte proporcionalmente aos gaps.

O max(Decimal('0'), ...) é o pulo do gato: categorias acima da meta simplesmente não competem pelo aporte. O dinheiro flui para onde falta.

Caso de borda: se todas as categorias já estão na meta ou acima (total_gap == 0), não há déficit a preencher. Caímos num fallback que divide o aporte por peso de meta, mantendo a alocação atual em vez de dividir por zero.

Do orçamento para as ordens

Orçamento por categoria ainda não é uma ordem — você não compra 3,7 ações. Então o orçamento de cada categoria é dividido entre os ativos e vira quantidade inteira:

def _buy_suggestion(self, kind, item, budget: Decimal):
    if budget <= 0:
        return None

    if self.portfolio.market == 'CRYPTO':
        quantity = (budget / item.current_price).quantize(Decimal('0.00000001'))
    else:
        quantity = int(budget / item.current_price)   # só ações inteiras

    if quantity <= 0:
        return None
    cost = (quantity * item.current_price).quantize(Decimal('0.01'))
    return self._asset_suggestion_row(item, quantity, cost)

Repare no branch por mercado: ações compram unidades inteiras (int()), cripto compra frações de até 8 casas decimais. Esse único if é o que permite o mesmo motor servir uma carteira de ações e uma de Bitcoin. (Tem um artigo só sobre essa decisão mais pra frente na série.)

O problema do troco

Arredondar para ações inteiras deixa dinheiro na mesa. Compre int(500 / 140) = 3 ações a R$140 e você gastou R$420 — sobram R$80 do aporte sem alocação.

Por isso há uma segunda passada que gasta o restante, de forma gananciosa, na categoria que ainda tem o maior déficit:

while remaining > 0:
    # pega a categoria mais abaixo da meta que ainda dá pra comprar
    best_item = max(
        affordable_items,
        key=lambda it: target(it) - simulated_value[it.category_id],
        default=None,
    )
    if best_item is None:
        break
    # compra mais uma ação (ou, pra cripto, gasta todo o resto como fração)
    ...

Para ações isso compra uma ação extra por vez até não sobrar nada comprável. Para cripto, joga o restante no ativo mais abaixo da meta como fração. De qualquer jeito, o aporte é totalmente deployado em vez de deixar caixa parado.

Opcional: rebalancear com vendas (driblando o IR)

Às vezes o aporte não basta — uma categoria está tão acima que nenhum aporte realista resolve. Para esses casos, o serviço pode sugerir vendas também, com uma sacada específica da lei brasileira:

if avoid_ir_sells and _is_always_taxed(ticker):
    continue   # nunca sugere vender um ativo sempre tributado (ETF/FII)

No Brasil, vendas de ações até R$20k/mês são isentas, mas ETFs e FIIs (tickers terminados em 11) são sempre tributados. A flag avoid_ir_sells manda o motor rebalancear vendendo só o que é isento, deixando os ativos tributáveis intocados. O mesmo cálculo, agora consciente do IR.

Por que isso virou o produto

A matemática aqui não é sofisticada — é distribuição proporcional com alguns guardas. Mas embrulhada numa UI que busca cotação ao vivo, sabe suas metas e respeita as regras de IR, ela responde uma pergunta que investidores reais fazem todo mês e hoje resolvem com planilha e chute.

É a lição que eu fico reaprendendo construindo o Balance: a parte valiosa raramente é o algoritmo. É remover o atrito em volta dele.


O Balance é uma ferramenta de rebalanceamento para investidores BR/US/Cripto — diz o que comprar com o próximo aporte pra ficar na meta. Se você faz aportes recorrentes e quer testar, o link está no perfil. Dúvidas sobre a abordagem? Comenta aí.