">
VICTOR SAAD
All buildsTodos os projetos
Python · Three.js · public city data · financial modelling
Python · Three.js · dados públicos da prefeitura · modelagem financeira

Home decision engine

Motor de decisão de moradia

Buying an apartment is the largest financial decision most people make, and it usually gets made on a listing photo and a feeling. I wanted it made on data: what the place really is, what is around it, what the market is doing, and what the money does over twenty years.

Comprar um apartamento é a maior decisão financeira que a maioria das pessoas toma, e ela costuma ser tomada com base numa foto de anúncio e numa sensação. Eu queria tomá-la com dados: o que o imóvel é de verdade, o que tem em volta, o que o mercado está fazendo e o que o dinheiro faz em vinte anos.

PythonThree.jsSweet Home 3DGeoSampaGoogle Placessystemd timersPostgreSQLAmortisation modellingData qualityPercentile analysis

What it became

No que ele virou

It started as a buy-versus-rent spreadsheet and grew into a small product that runs on the homelab. One place to open for every question about a home, with each module answering one of them.

Começou como uma planilha de comprar versus alugar e virou um pequeno produto que roda no homelab. Um lugar só para abrir diante de qualquer pergunta sobre um imóvel, com cada módulo respondendo a uma delas.

A 3D model of a São Paulo neighbourhood: towers and low-rise houses extruded from public footprint data, streets, trees and coloured markers for nearby places.
The neighbourhood in 3D, rebuilt in Three.js from the city's public building footprints and heights. Coloured pins are places nearby. Labels removed here on purpose.
O bairro em 3D, reconstruído em Three.js a partir das projeções e alturas públicas dos edifícios da cidade. Os pinos coloridos são lugares por perto. Rótulos removidos aqui de propósito.

A public, open-source cut of this model is live: Largo da Batata in 3D (how it was built).

Uma versão pública e com código aberto deste modelo está no ar: o Largo da Batata em 3D (como foi feito).

  • The place itself. The floor plan redrawn to scale from the official drawings and a tape-measure survey, with layout scenarios (open kitchen, rooms repurposed) and every furniture piece placed by its real dimensions.
  • In 3D. The same plan exported to a walkable 3D model, so a layout is judged standing inside it instead of from above.
  • The neighbourhood. Buildings, heights, terrain, zoning layers and recorded property sales (ITBI) from the city's open data, refreshed by scheduled jobs that check the sources before publishing.
  • Around it. Over six hundred places mapped by type and walking time: markets, pharmacies, transit, parks, schools, restaurants.
  • The market. Comparable units tracked over time, with price per square metre as a percentile rather than a bare number.
  • The money. The amortisation schedule month by month, an extra-payment simulator, and a calculator that sweeps every possible down payment to find the one that leaves the most net worth at the end of the horizon.
  • O imóvel em si. A planta redesenhada em escala a partir das plantas oficiais e de uma medição com trena, com cenários de layout (cozinha aberta, cômodos com novo uso) e cada móvel posicionado pelas medidas reais.
  • Em 3D. A mesma planta exportada para um modelo 3D em que dá para andar, para julgar um layout de pé lá dentro e não visto de cima.
  • O bairro. Edifícios, alturas, relevo, camadas de zoneamento e transações registradas (ITBI) dos dados abertos da prefeitura, atualizados por jobs agendados que checam as fontes antes de publicar.
  • Em volta. Mais de seiscentos lugares mapeados por tipo e tempo a pé: mercados, farmácias, transporte, parques, escolas, restaurantes.
  • O mercado. Unidades comparáveis acompanhadas ao longo do tempo, com o preço por metro quadrado como percentil e não como número solto.
  • O dinheiro. A tabela de amortização mês a mês, um simulador de amortização extra e uma calculadora que varre todas as entradas possíveis para achar a que deixa mais patrimônio no fim do horizonte.
A floor plan drawn to scale with room names and dimensions: three rooms, kitchen, living room, bathrooms and service area.
The plan, to scale. Furniture labels removed.
A planta, em escala. Rótulos dos móveis removidos.
The same floor plan as a 3D model seen from above, with furniture in every room.
The same plan in 3D, from a Sweet Home 3D export served on the homelab.
A mesma planta em 3D, a partir de um export do Sweet Home 3D servido no homelab.
The financing calculator: inputs for price, down payment, interest, term and amortisation system on the left, and the optimal scenario with net worth at the end of the horizon on the right.
The financing calculator, filled with example values.
A calculadora de financiamento, preenchida com valores de exemplo.

Everything shown here is anonymised: no address, no building, no real prices, no listing photos. Screens are cropped, labels removed and the calculator runs on made-up numbers.

Tudo o que aparece aqui está anonimizado: sem endereço, sem prédio, sem preços reais, sem fotos de anúncio. As telas estão recortadas, os rótulos removidos e a calculadora roda com números inventados.

The problem

O problema

“Buying beats renting” and “renting beats buying” are both slogans. The honest version is a comparison over a horizon, with the opportunity cost of the deposit taken seriously, and it depends entirely on numbers specific to the property and the month you're in.

“Comprar é melhor que alugar” e “alugar é melhor que comprar” são dois slogans. A versão honesta é uma comparação ao longo de um horizonte, levando a sério o custo de oportunidade da entrada, e ela depende inteiramente de números específicos do imóvel e do mês em que você está.

The model

O modelo

Two projected paths, same starting capital. Buy: deposit down, SAC-amortisation mortgage (declining instalments, which behaves quite differently from a fixed-payment loan late in the term), plus maintenance, association fees, and property tax, against the property's projected value. Rent: the same deposit invested, rent paid monthly, and the monthly difference between the two paths invested as well — because the version where you rent and spend the difference isn't the comparison, it's just a worse outcome.

Dois caminhos projetados, com o mesmo capital inicial. Comprar: entrada paga, financiamento pela tabela SAC (parcelas decrescentes, que se comportam bem diferente de um empréstimo de parcela fixa no fim do prazo), mais manutenção, condomínio e IPTU, contra o valor projetado do imóvel. Alugar: a mesma entrada investida, aluguel pago todo mês e a diferença mensal entre os dois caminhos investida também, porque a versão em que você aluga e gasta a diferença não é a comparação, é só um resultado pior.

Output is net worth at 5, 10, and 20 years. Not a verdict — a curve, with the crossover visible.

O resultado é o patrimônio em 5, 10 e 20 anos. Não um veredito: uma curva, com o ponto de virada à vista.

Sourcing the inputs

De onde vêm os dados

Candidate properties went into a database with their attributes, and the shortlist onto a self-hosted page: photos, price-per-square-metre as a percentile against comparable stock rather than as a bare number, and my own notes after each visit.

Os imóveis candidatos entraram num banco com seus atributos, e a lista final numa página self-hosted: fotos, preço por metro quadrado como percentil contra o estoque comparável e não como número solto, e minhas próprias anotações depois de cada visita.

Three analyses I got confidently wrong

Três análises que eu errei com toda a confiança

This is the useful part, and it's why the project is on this site.

Esta é a parte útil, e é por ela que o projeto está neste site.

One. I ranked by price per square metre without filtering for condition or location within the neighbourhood, and produced a list of “bargains” that were bargains because they were derelict or on the wrong side of a major road — properties already rejected in person.

Um. Ordenei por preço por metro quadrado sem filtrar estado de conservação nem localização dentro do bairro, e saiu uma lista de “pechinchas” que eram pechinchas porque estavam caindo aos pedaços ou do lado errado de uma avenida grande: imóveis que eu já tinha descartado pessoalmente.

Two. I compared a furnished unit's asking rent against unfurnished comparables. The furniture premium is real but size-dependent, and I'd applied it from the wrong band.

Dois. Comparei o aluguel pedido de uma unidade mobiliada com comparáveis sem mobília. O prêmio da mobília existe, mas depende do tamanho, e eu tinha aplicado o da faixa errada.

Three, and the worst. A listing's stated area was wrong by eighteen square metres. Every derived figure — price per square metre, the percentile, the yield, the entire recommendation — inherited that error and presented it with full confidence.

Três, e o pior. A área informada num anúncio estava errada em dezoito metros quadrados. Todo número derivado (preço por metro quadrado, o percentil, o yield, a recomendação inteira) herdou esse erro e o apresentou com total confiança.

What actually fixed it

O que resolveu de verdade

Not a better model. Validation rules:

Não foi um modelo melhor. Foram regras de validação:

Corroborate area against a second source before it enters any calculation — a duplicate listing of the same unit, tax records, the deed. Compute benchmarks within the exact band of the target, never a wider band that merely contains it. Filter comparability on both sides of any comparison, not just the side you're looking at. And treat the in-person visit as input data the database structurally cannot have.

Confirmar a área numa segunda fonte antes que ela entre em qualquer conta: outro anúncio da mesma unidade, o cadastro do IPTU, a escritura. Calcular benchmarks dentro da faixa exata do alvo, nunca numa faixa mais larga que só a contém. Filtrar a comparabilidade dos dois lados de qualquer comparação, não só do lado que você está olhando. E tratar a visita presencial como um dado de entrada que o banco, por estrutura, não tem como ter.

The general lesson has outlived the house search: a model is only ever as good as its least-validated field, and the field you didn't think to check is the one that's wrong.

A lição geral sobreviveu à busca pelo imóvel: um modelo é sempre tão bom quanto o seu campo menos validado, e o campo que você não pensou em checar é justamente o que está errado.

Deliberately absent: addresses, prices, and which property we bought. The method generalises; the specifics are nobody's business.
Ausentes de propósito: endereços, preços e qual imóvel compramos. O método vale para qualquer caso; os detalhes não são da conta de ninguém.

Want to talk about this? I'm happy to walk through how it was built.Quer conversar sobre isso? Mostro com prazer como foi construído.