---
name: price-elasticity-modeling
description: >
  Estimates price elasticity of demand incorporating non-linearities, psychological
  price thresholds, and pack-size interactions. Trigger when asked to: predict volume
  impact of a price change, identify price thresholds where demand drops sharply,
  model shrinkflation or upsizing effects, estimate cross-elasticity between SKUs,
  or forecast demand with AI. Also trigger for: "elasticidad precio", "sensibilidad
  al precio", "impacto de precio en volumen", "precio umbral", "shrinkflation",
  "log-log regression", "piecewise elasticity", "demand forecast". When zone/city
  data is present activates TomTom MCP for geographic elasticity mapping. Always
  renders inline HTML dashboard + NBA by market, category, channel, and SKU.
---

# price-elasticity-modeling

Estimates price elasticity via log-log OLS, piecewise threshold regression, pack-size
interaction models, and AI demand forecasting (gradient boosting). Integrates TomTom
geo mapping when regional data is available. Always dashboard-first.

---

## Plain Language

```
"Si bajo 10% el precio, ¿cuánto sube el volumen?"  → linear_elasticity.py
"¿A qué precio el volumen cae de golpe?"            → threshold_elasticity.py
"¿El pack grande es más elástico que el chico?"     → elasticity_by_packsize.py
"Predice ventas próximas 12 semanas con promo"      → ai_demand_forecast.py
"Mapa de elasticidad por zona/canal en CDMX"        → TomTom MCP + heatmap
```

**Core rule (EY PPA p.4):** Elasticity is non-linear. A -10% price change does not
produce the same volume lift as a +10% price cut — psychological thresholds create
asymmetric responses. Always test for breakpoints before assuming linearity.

---

## Core Equations

### Log-log elasticity (constant elasticity model)
```python
# OLS on log-transformed data:
ln(Q) = α + β·ln(P) + γ·controls + ε

# β = price elasticity (typically -0.5 to -3.0 for FMCG)
# E = β  (constant across price range)

# Point elasticity at (P₀, Q₀):
E = (dQ/dP) × (P₀/Q₀) = β
```

### Piecewise / threshold elasticity
```python
# Segmented regression at breakpoint τ:
ln(Q) = α₁ + β₁·ln(P)            if P ≤ τ
ln(Q) = α₂ + β₂·ln(P)            if P > τ

# β₂ > β₁ in absolute value → demand more elastic above threshold
# Threshold detection: Chow test or Davies test for structural break

# Psychological price points (commonly tested):
# Round numbers: $5, $10, $15, $20, $25, $50, $100
# Just-below: $4.99, $9.99, $19.99
```

### Pack-size interaction elasticity
```python
# Interaction model for small (0) vs large (1) pack:
ln(Q) = α + β₁·ln(P) + β₂·size_dummy + β₃·(ln(P) × size_dummy) + ε

# Elasticity for small pack: β₁
# Elasticity for large pack: β₁ + β₃
# β₃ < 0 → large pack more elastic (more sensitive to price changes)
```

### Cross-price elasticity
```python
# Impact of competitor price change on own volume:
E_cross = (dQ_own/dP_comp) × (P_comp/Q_own) = β_comp in log-log model

# E_cross > 0 → substitutes (competitor raises price → own volume rises)
# E_cross < 0 → complements
```

---

## Workflow

### Step 1 — Data validation
```bash
# Always first — data-intake-normalizer
# Required: price, quantity, date. Optional: channel, region, promo_flag, pack_size
```

### Step 2 — Linear elasticity baseline
```bash
python scripts/linear_elasticity.py \
    --data /mnt/user-data/uploads/price_volume.csv \
    --price-col price --qty-col quantity \
    --controls "promo_flag,seasonality" \
    --output results/linear_elasticity.json
```

### Step 3 — Threshold detection
```bash
python scripts/threshold_elasticity.py \
    --data /mnt/user-data/uploads/price_volume.csv \
    --n-breakpoints 3 \
    --output results/threshold_elasticity.json
```

### Step 4 — Pack-size interaction
```bash
python scripts/elasticity_by_packsize.py \
    --data /mnt/user-data/uploads/price_volume.csv \
    --size-col pack_size_g \
    --output results/packsize_elasticity.json
```

### Step 5 — AI demand forecast (optional)
```bash
python scripts/ai_demand_forecast.py \
    --data /mnt/user-data/uploads/price_volume.csv \
    --forecast-weeks 12 \
    --output results/demand_forecast.json
```

### Step 6 — Geographic elasticity (when region/zone data present)
```
→ TomTom MCP: tomtom-fuzzy-search for channel POIs per zone
→ Compute elasticity separately per zone cluster
→ tomtom-dynamic-map: choropleth of elasticity by zone
→ Integrate into dashboard as 7th panel
```

### Step 7 — Dashboard
```bash
python scripts/price_sensitivity_heatmap.py \
    --linear results/linear_elasticity.json \
    --threshold results/threshold_elasticity.json \
    --packsize results/packsize_elasticity.json \
    --output dashboard_data.json
```

**Output sequence:**
```
1. [bash_tool] All scripts
2. [web_search] Price elasticity benchmarks [category] [market] [year]
3. [TomTom MCP] If region data → geographic elasticity map
4. [show_widget] Dashboard
5. [text] NBA + market context
6. [text] Caveats
```

---

## Dashboard Panels

1. **KPI bar** — own-price elasticity, R², threshold price point, most elastic pack,
   demand forecast accuracy (MAPE), cross-elasticity vs main competitor
2. **Elasticity curve** — volume vs price with confidence band + threshold markers
3. **Piecewise segments** — elasticity above/below each breakpoint
4. **Pack-size comparison** — elasticity by pack size band (bar chart)
5. **Demand forecast** — actual vs predicted + 12-week forward projection
6. **Price sensitivity heatmap** — elasticity by segment × channel matrix
7. **TomTom geo map** — elasticity choropleth by zone (when geographic data present)

---

## Marketer Insights Layer (MANDATORY)

### Search before benchmarking
```
web_search: "price elasticity [category] [market] FMCG benchmark [year]"
web_search: "psychological price thresholds consumer goods [category] LATAM"
```

### Translate to business language

| Technical | Business meaning |
|---|---|
| E = -1.8 | "10% price cut → 18% volume increase. Elastic — promotions pay off" |
| E = -0.4 | "Even 25% cut barely moves volume. Inelastic — don't discount here" |
| threshold at $19.90 | "Above $20, demand drops sharply. Price ceiling: $19.90" |
| β₃ = -0.6 (large pack) | "Large pack 60% more sensitive to price than small — large pack shoppers hunt for deals" |
| MAPE = 8% | "AI forecast within 8% of actual — reliable enough for promo planning" |

### NBA
- **Threshold pricing:** "Do NOT exceed $[τ] — demand curve breaks above this. Keep at $[τ-0.10]"
- **Asymmetry:** "Price cuts work better than price increases: -10% adds [X]% volume but +10% loses [Y]% — asymmetric loss"
- **Pack leverage:** "Large pack (E=[X]) responds better to promotions than small (E=[Y]) — concentrate promo budget on large format"
- **Cross-elasticity:** "Competitor price +5% → own volume +[Z]% — time your price increases when competitor prices are also rising"
- **Geo opportunity:** "Zone [X] shows E=-0.9 (inelastic) vs Zone [Y] E=-2.1 (elastic) — charge premium in [X], run promos in [Y]"
- **Shrinkflation:** "Reducing pack from 500g to 450g at same price = implicit +11% price increase — model as E×0.11 volume risk"

---

## Key Caveats

- **Endogeneity:** Price and volume are jointly determined — use instrumental variables
  or control for promotions/seasonality to avoid bias
- **Data requirements:** Min 52 weeks of weekly data; min 20 distinct price points
- **Constant elasticity assumption:** Log-log model assumes E is constant — always
  test with threshold model first
- **Omitted variables:** Competitor prices, seasonality, distribution changes can
  confound elasticity estimates
- **Stated vs revealed:** Survey-based WTP (from ppa-value-perception) typically
  overstates price sensitivity vs. actual transaction elasticity

---

## Integration with OS

| Skill | Handoff |
|---|---|
| `data-intake-normalizer` | Validate price/quantity/date input |
| `ppa-assortment-diagnosis` | White space price points → validate with elasticity |
| `ppa-value-perception` | WTP upper bound feeds elasticity model price range |
| `price-demand-optimization` | Elasticity β feeds optimal price calculation (ε in p_opt formula) |
| `trade-promotion-roi` | Promotional elasticity → ROI model |
| `sell-out-forecaster` | Demand forecast output feeds sell-out projection |

---

## References
- `references/elasticity_methodology.md` — Log-log OLS, Chow test, Davies test
- `references/psychological_thresholds.md` — Price point benchmarks by category
- `references/elasticity_benchmarks.md` — FMCG elasticity ranges by category/market
