Recentemente, venho me aprofundando no mercado de ações, e o que mais tenho buscado é manter uma carteira equilibrada, por diversos fatores. Basicamente, defino uma porcentagem ideal para cada ticker e, todo mês, faço aportes seguindo essas proporções. Não sigo essas porcentagens cegamente, elas servem mais como um norte a ser seguido.
🧩 O Problema
Antes, eu mantinha todas as porcentagens em uma planilha no Excel. Todo mês eu acessava a planilha, atualizava os valores de cada ação e depois ia comprando uma por uma, para calcular o quanto precisava comprar de cada uma.
Apesar de não ser algo muito demorado, era um processo chato. E eu me perguntei: por que gastar 30 minutos todo mês se posso gastar alguns dias automatizando isso? haha
🛠 A Solução
Optei por usar Go, pois é uma linguagem que venho estudando ultimamente e também por ser uma ótima opção para CLIs (Command-Line Interfaces). Queria algo simples, local e prático, sem necessidade de subir um site ou aplicação web.
O projeto pode ser dividido em três partes:
- Leitura da carteira
- Atualização dos valores das ações
- Recomendação de compras para rebalancear
1. Leitura da carteira
Para facilitar, criei um arquivo JSON com os dados das ações, ao invés de integrar diretamente com a B3 ou APIs mais complexas. O formato é o seguinte:
{ "stocks": [ { "ticker": "BBAS3", "ideal_ratio": 0.5, "current_price": 27.78, "amount": 1, "updated_at": "2025-02-07T21:35:24.02599019-03:00" }, { "ticker": "BBDC4", "ideal_ratio": 0.5, "current_price": 11.99, "amount": 1, "updated_at": "2025-02-07T21:35:24.257580504-03:00" } ]}
As informações mais importantes são: ticker
, ideal_ratio
(porcentagem ideal) e amount
(quantidade). As demais são geradas automaticamente pelo sistema.
Futuramente, pretendo utilizar um banco de dados como SQLite, e já estruturei o projeto com repository pattern
, então será fácil fazer essa mudança.
2. Atualização dos valores
Utilizei a API da BRAPI (https://brapi.dev/) para atualizar os preços das ações. Ela possui um plano gratuito que atende bem às minhas necessidades.
O processo é simples:
- Leio os dados do JSON.
- Faço as requisições para atualizar os preços.
- Uso o campo
updated_at
para evitar excesso de chamadas à API, só atualizo após um intervalo de 2 horas.
Esse intervalo pode ser removido/modificado diretamente no código: stock.go#L26.
3. Algoritmo de Rebalanceamento
Essa foi a parte mais dificil. Refiz o algoritmo diversas vezes até chegar a algo funcional. O que eu precisava era:
- Manter as porcentagens o mais próximas possível das ideais.
- Evitar comprar ações que já ultrapassaram sua meta.
- Diversificar ao máximo, evitando gastar tudo em uma única ação.
- Gastar todo o dinheiro disponível, otimizando o aporte. Ou seja, eu precisava de um algoritmo guloso, que tentasse comprar o máximo de tickers diferentes, desde que possível.
Etapas do algoritmo:
- Cálculo do patrimônio total após o novo aporte (somando o valor atual da carteira com o novo investimento).
- Com esse novo valor, é possível calcular a porcentagem atual de cada ação na carteira.
- A partir disso, verificamos quanto falta para balancear cada ticker.
Mas isso não é suficiente. Por exemplo, se por exemplo,
PETR4
deveria representar 50% e atualmente tem 25%, o algoritmo não pode simplesmente alocar os 25% restantes nela, ignorando o restante do portfólio.
Por isso, implementei a lógica de “rodadas de compra”:
for remaining > 0 { // Continue comprando...}
Priorização das ações
A cada rodada, é criada uma lista de prioridades com base no “desvio” entre o valor ideal e o atual. O rank de cada ação é calculado assim:
newValue := currentValue + stock.CurrentPricedeviationBefore := math.Abs(idealValue - currentValue)deviationAfter := math.Abs(idealValue - newValue)
improvement = (deviationBefore - deviationAfter) / stock.CurrentPrice
improvement = improvement * (1.0 / (1.0 + float64(currentShares)))
- Se o desvio aumenta após a compra, o rank será negativo, ou seja, essa ação será despriorizada.
- Também penalizamos ações que já foram compradas várias vezes, para evitar compras repetidas.
Ordenando as ações
Depois de calcular os ranks, ordenamos a lista:
func (s *PortfolioService) sortPriorities(priorities []stockPriority) []stockPriority { sort.Slice(priorities, func(i, j int) bool { if priorities[i].rank == priorities[j].rank { return priorities[i].stock.CurrentPrice < priorities[j].stock.CurrentPrice } return priorities[i].rank > priorities[j].rank })
return priorities}
Se duas ações tiverem o mesmo rank, a prioridade será dada à mais barata.
💸Hora da compra
A parte final é a mais simples: comprar.
Basta percorrer a lista de prioridades e, caso o rank
seja positivo e ainda exista saldo, realizamos a compra.
for remaining > 0 { priorities := s.makePriorityList(stocks, buys, idealValues, remaining) priorities = s.sortPriorities(priorities)
// Hora de comprar bought := false for _, priority := range priorities { if priority.rank > 0 && priority.stock.CurrentPrice <= remaining { buys[priority.stock.Ticker]++ remaining -= priority.stock.CurrentPrice bought = true break } }
if !bought { break }}
A cada compra, atualizamos o saldo e seguimos até que não seja mais possível comprar nenhuma ação.
O projeto está aberto no github: https://github.com/xoesae/stock-balancer/tree/main. Ainda estou aprendendo Go, sugestões de melhoria e performance são sempre bem vindos! O que acharam? usariam meu CLI no seu dia-a-dia?