---
name: price-threshold-detection
description: >
  Identifies critical price points where demand changes dramatically due to
  psychological thresholds. Trigger when asked to: find price thresholds or
  breakpoints, avoid crossing a price point that causes volume drops, decide
  between price increase vs pack size reduction, design safe price corridors,
  or detect regime changes in demand. Also trigger for: "umbral de precio",
  "precio psicologico", "precio critico", "donde cae el volumen", "CUSUM",
  "Markov switching", "regime switch", "just-below pricing", "precio redondo",
  "Hansen threshold". TomTom MCP activates when zone/city data is present.
  Always renders complete inline HTML dashboard with threshold map, risk
  assessment, and NBA by market, category, channel, and SKU.
---

# price-threshold-detection

Detects critical price thresholds using demand discontinuity analysis, CUSUM
change detection, common psychological price heuristics, and Hansen (2000)
regime-switching regression. Always outputs complete dashboard inline.

---

## Plain Language

```
"¿A partir de qué precio el volumen se desploma?"  → demand_discontinuity.py + CUSUM
"¿Cuántos de nuestros SKUs usan precios .99?"      → common_thresholds.py
"¿Es mejor subir precio o reducir pack size?"       → threshold_risk_assessor.py
"¿Hay dos regímenes de demanda diferentes?"         → regime_switch_model.py
"¿El umbral varía por zona en CDMX?"               → TomTom MCP geo mapping
```

**Core insight (EY PPA p.4):**
Price thresholds are purely psychological — consumers do not optimize rationally.
$9.99 is perceived fundamentally differently from $10.00. Crossing a round-number
threshold can cause -20 to -40% demand loss that a rational model would never predict.

---

## Core Equations

### CUSUM change-point detection
```python
# Cumulative sum of standardized residuals:
S_t = max(0, S_{t-1} + (x_t - mu_0 - k))

# Alert when S_t > h (control limit)
# k = slack parameter = 0.5 * expected_shift / sigma
# h = decision interval, typically 4-5 × sigma

# Optimal h and k via ARL (Average Run Length) calibration
```

### Hansen (2000) threshold regression
```python
# Two-regime model with unknown threshold gamma:
Q = alpha1 + beta1*P    if P <= gamma
Q = alpha2 + beta2*P    if P >  gamma

# Estimate gamma by grid search:
# gamma_hat = argmin_{gamma} RSS(gamma)
# Test: F = [RSS_linear - RSS_threshold] / RSS_threshold × (n-2k)/k
# Asymptotic p-value via bootstrap (Hansen 2000 simulation)
```

### Threshold risk assessment
```python
# Risk of crossing threshold tau:
volume_at_risk = Q(P_current) - Q(P_current + delta_P)

# If P_current + delta_P > tau:
# Expected volume loss = Q(tau) * abs(beta2 - beta1) * (P_new - tau) / tau
# Loss amplification factor = beta2 / beta1  (ratio of elasticities)
```

### Just-below pricing premium
```python
# Demand premium from .99 pricing:
premium_pct = (Q_just_below - Q_round) / Q_round * 100

# Industry benchmark: 5-15% for consumer goods
# Diminishing effect above $50 (Stiving & Winer, 1997)
```

---

## Workflow

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

### Step 2 — Demand discontinuity + CUSUM
```bash
python scripts/demand_discontinuity.py \
    --data /mnt/user-data/uploads/price_volume.csv \
    --price-col price --qty-col quantity \
    --output results/discontinuities.json
```

### Step 3 — Common thresholds audit
```bash
python scripts/common_thresholds.py \
    --skus /mnt/user-data/uploads/sku_data.xlsx \
    --price-col price_shelf \
    --output results/common_thresholds.json
```

### Step 4 — Regime-switch model
```bash
python scripts/regime_switch_model.py \
    --data /mnt/user-data/uploads/price_volume.csv \
    --n-regimes 2 \
    --output results/regime_switch.json
```

### Step 5 — Threshold risk assessment
```bash
python scripts/threshold_risk_assessor.py \
    --current-price 18.50 \
    --proposed-price 20.00 \
    --thresholds results/discontinuities.json \
    --elasticity -1.42 \
    --output results/risk_assessment.json
```

### Step 6 — Geographic threshold variation (TomTom)
```
When data has region/zone/city columns:
→ tomtom-fuzzy-search: channel POIs per zone (MT/TT/convenience density)
→ Compute threshold separately per zone cluster
→ tomtom-dynamic-map: choropleth — threshold value by zone
→ Higher MT density zones → higher thresholds (richer shoppers)
→ Higher TT density zones → lower thresholds (price-sensitive)
```

### Step 7 — Dashboard
```
[show_widget] Full inline dashboard — all panels visible without scroll
```

**Output sequence:**
```
1. [bash_tool] All 4 scripts
2. [web_search] "psychological price thresholds [category] [market] [year]"
3. [TomTom MCP] If geographic data present
4. [show_widget] Complete dashboard
5. [text] NBA + market/category/SKU context
6. [text] Caveats
```

---

## Dashboard Panels (all visible inline, no scroll required)

1. **KPI bar** — thresholds found, critical threshold price, volume at risk (%),
   regime count, SKUs using just-below pricing (%)
2. **Demand discontinuity chart** — volume vs price scatter + CUSUM signal line
   with threshold markers
3. **Regime-switch visualization** — two-slope demand with regime boundary
4. **Threshold risk meter** — proposed price vs threshold distance + volume at risk
5. **Just-below pricing audit** — % SKUs by threshold type ($X.99 / $X.90 / round)
6. **Safe price corridor** — visual range: floor → current → threshold ceiling
7. **Geographic threshold map** — TomTom choropleth when zone data present

---

## Marketer Insights Layer (MANDATORY)

### Search before benchmarking
```
web_search: "psychological price thresholds consumer goods [category] [market] [year]"
web_search: "just-below pricing effectiveness CPG LATAM [year]"
```

### Translate to business language

| Technical | Business meaning |
|---|---|
| CUSUM alert at $19.90 | "Volume drops sharply above $19.90 — empirically confirmed, not assumed" |
| regime beta1=-0.8 → beta2=-2.3 | "Below $20: moderate sensitivity. Above $20: 3x more sensitive — don't cross" |
| volume_at_risk = 18% | "Proposed +$1.50 price increase would cost ~18% of volume" |
| 73% SKUs use .99 pricing | "Most of portfolio already uses just-below — diminishing differentiation" |
| threshold gap = $0.40 | "Only $0.40 from the danger zone — next price increase must stay below $19.90" |

### NBA
- **Hard ceiling:** "Do NOT price above $[tau] in [channel] — CUSUM confirms demand cliff.
  If revenue pressure, reduce pack size instead (model shrinkflation impact first)"
- **Safe corridor:** "Price can move from $[floor] to $[tau-0.10] without crossing
  a threshold — $[tau-0.10] is the safe maximum"
- **Just-below lever:** "Move $[X].00 → $[X-0.01].99 — expect +[N]% volume lift
  for no margin cost in [channel/segment]"
- **Pack size alternative:** "Crossing $20 → -18% volume. Instead: reduce pack
  from 500g to 470g at $18.90 = same revenue, no threshold crossing"
- **Zone differentiation:** "Zone [A] threshold = $[X] (MT-heavy), Zone [B] = $[Y]
  (TT-heavy) — regional pricing is justified and protects volume in sensitive zones"
- **Regime warning:** "Current price $[P] is in regime 2 (high elasticity zone).
  Return to regime 1 (below $[tau]) would restore elasticity to [beta1]"

---

## Key Caveats

- **Threshold drift:** Psychological thresholds shift with inflation — revalidate
  annually or after >8% cumulative category price inflation
- **Channel-specific:** $9.99 matters more in TT (price-sensitive) than in MT or
  e-commerce where shoppers are less anchored to round numbers
- **Sample size:** CUSUM and regime models require ≥52 weeks and ≥15 distinct price
  points for reliable detection
- **Confounding:** Promotions, out-of-stocks, and seasonality can create false
  discontinuities — always include these as controls
- **Not universal:** Some premium categories (wine, cosmetics) show REVERSE threshold
  effects — higher price signals quality and can INCREASE demand up to a point

---

## Integration with OS

| Skill | Handoff |
|---|---|
| `data-intake-normalizer` | Validate price/volume input |
| `price-elasticity-modeling` | Threshold tau feeds piecewise regression |
| `ppa-value-perception` | WTP ceiling validates threshold empirically |
| `price-demand-optimization` | Threshold = hard constraint on p_opt search |
| `trade-promotion-roi` | Threshold crossing = promo trigger condition |
| `ppa-portfolio-optimizer` | Threshold per channel feeds price-pack design |
