Como calcular o Preço Médio Ponderado de ativos com vendas parciais e taxas (lógica e implementação)
Ao desenvolver sistemas de portfólio financeiro ou ferramentas pessoais para controle de investimentos na B3, o primeiro desafio contábil real é implementar o algoritmo de Preço Médio (PM).
A Receita Federal do Brasil exige o método da média ponderada móvel. Em termos de código, o cálculo parece trivial até você lidar com compras sucessivas, taxas de corretagem/emolumentos e vendas parciais.
As regras fundamentais
- Compras somam custos: As taxas operacionais (emolumentos B3, corretagem, liquidação) devem ser incorporadas ao custo de aquisição. Isso aumenta o PM.
- Vendas não alteram o PM: A venda diminui a quantidade de ativos em custódia, mas o preço médio unitário permanece inalterado. As taxas da venda não alteram o PM das cotas remanescentes; elas reduzem o valor líquido da venda para apuração do lucro.
- Zerar a posição: Se a quantidade chegar a zero, o PM é resetado.
O modelo matemático passo a passo
Considere a seguinte sequência de eventos:
- Compra 1: 100 cotas a R$ 20,00. Custos operacionais = R$ 10,00.
- Custo total = (100 * 20,00) + 10,00 = R$ 2.010,00
- Quantidade acumulada = 100
- Preço Médio = R$ 2.010,00 / 100 = R$ 20,10
- Compra 2: 50 cotas a R$ 26,00. Custos operacionais = R$ 5,00.
- Custo desta operação = (50 * 26,00) + 5,00 = R$ 1.305,00
- Custo total acumulado = 2.010,00 + 1.305,00 = R$ 3.315,00
- Quantidade acumulada = 100 + 50 = 150
- Novo Preço Médio = R$ 3.315,00 / 150 = R$ 22,10
- Venda parcial: Venda de 70 cotas a R$ 30,00. Custos operacionais = R$ 8,00.
- A venda NÃO altera o PM unitário anterior. Ele continua sendo R$ 22,10.
- Quantidade restante em custódia = 150 - 70 = 80 cotas.
- Novo custo total em custódia = 80 * R$ 22,10 = R$ 1.768,00.
Para calcular o resultado tributável dessa venda:
- Receita líquida = (70 * R$ 30,00) - R$ 8,00 = R$ 2.092,00
- Custo de aquisição proporcional = 70 * R$ 22,10 = R$ 1.547,00
- Ganho líquido = R$ 2.092,00 - R$ 1.547,00 = R$ 545,00
Exemplo simples em TypeScript
type Operacao = {
tipo: 'COMPRA' | 'VENDA';
quantidade: number;
precoUnitario: number;
taxas: number;
};
type Posicao = {
quantidade: number;
precoMedio: number;
};
function processarOperacoes(operacoes: Operacao[]): Posicao {
let quantidade = 0;
let custoTotal = 0;
for (const op of operacoes) {
if (op.tipo === 'COMPRA') {
const custoOperacao = (op.quantidade * op.precoUnitario) + op.taxas;
custoTotal += custoOperacao;
quantidade += op.quantidade;
} else if (op.tipo === 'VENDA') {
if (op.quantidade > quantidade) {
throw new Error('Tentativa de vender mais do que a custódia disponível');
}
const pmAtual = custoTotal / quantidade;
quantidade -= op.quantidade;
custoTotal = quantidade * pmAtual;
}
if (quantidade === 0) {
custoTotal = 0;
}
}
return {
quantidade,
precoMedio: quantidade > 0 ? custoTotal / quantidade : 0
};
}
O maior problema em sistemas reais surge quando há operações de desdobramentos (splits), grupamentos e bonificações no meio do log de transações. Nesses casos, o ajuste de quantidade precisa ocorrer mantendo a paridade do custo acumulado.