HADI MOUMENI

MadRent

I look at Madrid rentals more than I’d like to admit, and I kept having the same thought: some of these listings are obviously mispriced, but “cheap” and “underpriced” aren’t the same thing. I struggled my entire first year trying to find a decent apartment. A €1,200 studio in the center isn’t a deal. A €1,200 three-bed two minutes from a metro in the periphery might be. I wanted to find the places that are mispriced relative to what they actually are, the specific combination of size, location, condition, and features, instead of just the cheapest ones. The core of it is an isolation forest I wrote from scratch: anomalies are few and different, so a tree that splits on random features isolates them in far fewer cuts than a normal flat.

Highlights

  • Isolation forest implemented from scratch: random split trees, path-length averaging, the c(n) normalisation, no scikit-learn. It scores how unusual each listing is overall.
  • It doesn’t stop at “anomalous”. A comparables model finds each flat’s nearest neighbours in a price-free feature space and reads off a fair price, so every flag has a direction and a size, like “39% below comparable flats, -2.3σ”.
  • Location done properly. Haversine distance to 243 real metro stations (pulled from Wikidata), parks, universities, a nightlife-noise proxy, and centrality. Not raw lat/long dumped into the model.
  • A hedonic regression surfaces the part I actually cared about: which features the market systematically misprices.

Tech Stack

  • Python, NumPy, pandas - the whole pipeline
  • Fotocasa - live listings (served as a JSON blob in the page)
  • Wikidata SPARQL - real metro station coordinates
  • Vercel - hosting the report

Notes

  • Favourite finding: the market overpays for how a flat is presented and underpays for what it has. “Modern”, A/C, and furnished carry big premiums; a garage shows up 61% more often among the bargains than in the market overall. The portal’s ranking rewards cosmetics.
  • Bargains cluster in the periphery: Fuencarral, Usera, Villaverde. Overpricing concentrates in the tourist core: Sol, Lavapiés. Obvious in hindsight, but nice to watch it fall out of the data on its own.
  • Idealista is the obvious source, but it’s behind DataDome and returns a 403 to anything that isn’t a real browser. Fotocasa embeds its listings as JSON, so I went there instead.
  • The scraping was the actual hard part. Fotocasa soft-blocks you after a burst of requests, and (this took me way too long to work out) retrying while you’re blocked just keeps the block alive. The fix was small bursts, then genuine silence. Felt more like lockpicking than data engineering.
  • These are asking prices, not signed leases, so I’m really measuring what landlords think they can get. Comparable sets get thin in the low-inventory districts, so I gate on how trustworthy the comps are before calling anything mispriced. Otherwise a rare penthouse just looks “overpriced” against flats it has nothing in common with.
  • It’s a snapshot, not a live feed. Re-running re-scrapes. I might wire up a weekly refresh like I did for P4P.

Check it out: MadRent