Skip to content

Case study 1 of 6

eBay iPhone price engine

What a used iPhone sells for, learned from 30,000+ real eBay sold prices.

C$61.61

test MAE (10-13% of median, per family)

per-family MAE C$23-114

0.94

R² over the whole price range; read MAE per family

worst family (Ridge) at 0.885

81.5%

80% band coverage, held out

bands from out-of-fold residuals

Live model, runs in this browser

Price a phone with the exported model

The repo's exported LightGBM pipelines, run by ONNX Runtime in this browser; nothing leaves the page. Before the first prediction the widget replays 50 recorded parity vectors and stops if any output drifts more than 0.5% from the Python pipeline's.

Sold prices as labels, with an 80% band

Asking prices for used phones are unusable as labels: identical configs list anywhere from C$139 to C$1,420, and most listings never sell. Sold prices are market-clearing prices, so the scraper collects only completed sales from eBay.ca (31,875 raw listings, 26,027 after cleaning; every filter step logged as a funnel).

A point estimate alone overstates what any model knows about a marketplace. Each prediction carries an 80% band built from P10/P90 quantiles of out-of-fold residuals in log space, stored as two offsets in the model metadata; no second model. Held-out coverage lands at 81.5% against the 80% target.

Error against price median sold price per family, whiskers = ±MAE

Per family, MAE is 8 to 27% of the median, tightest where prices are highest. The X-generation families (median C$118-136, MAE C$21-32) are grouped in the repo's table and left off this chart.

The 56-fit sweep

Seven model families by four sampling strategies, per target, on one GPU. Pick a target and a cell; the winner is outlined. No sampling strategy beat unresampled training for price, and every tree ensemble beat the PyTorch MLP.

Sweep target
Test RMSE per model family and sampling strategy
sweepcells show test RMSE in C$; select one for the full row

Serving: FastAPI and a Go port

Serving runs twice. A FastAPI service is the simple path; a 250-line Go microservice runs the same models through ONNX Runtime for small, fast-scaling pods on Kubernetes. The Go server rebuilds the sklearn feature vector from constants exported off the fitted pipelines, replays recorded parity vectors at startup, and exits if predictions drift more than 0.5% from the Python pipeline's recorded outputs.

The demo at the top of this page is a third consumer of the same exported contract, held to the same parity rule. The depreciation scope on the home page is a fourth; every point on it came out of this model.