A runnable demo of a dynamic pricing platform for retail, built on a Databricks lakehouse. One engine — demand elasticity → constrained optimization → agentic explanation — serves two verticals that differ in their pricing science, not their plumbing:
| Vertical | Pricing approach | Dominant levers |
|---|---|---|
| 🛒 Grocery retail | elasticity & markdown pricing | demand elasticity · KVI price-image · perishable markdown ladders |
| 📺 Consumer electronics | competitive & lifecycle pricing | competitor price-matching · MAP/RRP compliance · product-lifecycle markdown · attach economics |
The story in one line: one lakehouse, two retail pricing strategies. The two verticals share the bronze/silver/ML pipeline and diverge only at the gold feature set and the optimizer's constraints.
Honest demo. Everything runs on a laptop in seconds against synthetic data (
src/pricing/common/synthetic.py) — plausible in direction and shape, not calibrated to any real retailer. The data-generating process embeds a known elasticity per SKU, so the notebooks verify the model recovers the ground truth. The architecture is the one you'd actually run; the data is synthetic so the whole thing is reproducible. It lifts to Databricks unchanged.
pip install -r requirements.txt
# run the two end-to-end walkthroughs
jupyter notebook notebooks/grocery_retail.ipynb
jupyter notebook notebooks/consumer_electronics.ipynb
# or run the engine tests
PYTHONPATH=src python tests/test_pipeline.pyEach notebook walks the full pipeline: generate a 156-week sales panel → estimate demand elasticities (and check them against ground truth) → optimize prices under real business constraints → markdown → explain.
Validated demo results (synthetic data, reproducible):
| Grocery | Electronics | |
|---|---|---|
| Elasticity recovery (mean abs error) | ~0.29 | ~0.21 |
| Optimizer status | optimal | optimal |
| Revenue uplift @ flat margin | +6.4% | +1.0% |
| Binding constraint(s) | margin floor, milk price ladder | MAP floor, lifecycle band |
Electronics' modest uplift is the realistic finding: MAP floors and tight ceilings leave little room — several SKUs want to drop but are pinned at the MAP floor, a genuine compliance signal.
generate ─→ estimate elasticity ─→ optimize (constrained) ─→ markdown ─→ explain
synthetic log-log demand model revenue max s.t. expiry / role-based
panel (LightGBM/GPBoost in cost floor · ceiling · lifecycle rationale
prod; OLS in the demo) ladder · margin floor · ladder (LLM in prod)
MAP · max-change
The optimizer is solver-agnostic: SciPy/SLSQP by default (no license, runs anywhere), with a Gurobi MIQP backend for the full cardinality constraint (max-N price changes) when a license is present — it falls back gracefully so the demo always runs.
The same logic runs on a medallion lakehouse — see databricks/:
Delta Live Tables for bronze→silver→gold, MLflow for the elasticity model, a
Workflow that ties ingest → train → optimize → publish, and Mosaic AI for the
agentic explain layer.
Bronze ── POS · competitor feed · cost/RRP/MAP · inventory · weather · ESL
↓ DLT
Silver ── conformed SKU × store/channel × week fact
↓ DLT
Gold ──── elasticity features · optimizer I/O · KPI marts
↓
ML → Optimize → Agentic → Execution (Pricing API · ERP/POS · ESL)
src/pricing/ shared, solver-agnostic engine
elasticity/model.py log-log demand estimation (DemandModel)
optimization/optimizer.py revenue-max optimizer (scipy | gurobi backends)
common/synthetic.py synthetic panels + catalogs (known ground-truth elasticities)
notebooks/ end-to-end walkthroughs (grocery, electronics)
docs/architecture/ overview (shared) + per-vertical design
databricks/ DLT pipelines · Workflow · optimize job
data/synthetic/ CLI to materialize panels as files (bronze inputs)
tests/ engine smoke + correctness tests
docs/architecture/overview.md— shared lakehouse architecture, elasticity model, optimization math, and the agentic layerdocs/architecture/grocery-retail.md— elasticity & markdown pricingdocs/architecture/consumer-electronics.md— competitive & lifecycle pricing
MIT.