Building a governed cross-SKU elasticity model on Snowflake that the pricing team, RGM team, and CFO will actually use to make decisions. Schema design, governance checklist, semantic layer integration, and a 30-day build timeline.
This guide is not about building a statistically sound elasticity model. Most analytics teams can do that. It is about building one that the pricing team, the RGM team, and the CFO will actually use to make decisions.
The pattern repeats across organizations: the analytics team builds a cross-SKU elasticity model. It runs on Databricks. The outputs are technically defensible. The commercial team runs their own Excel model. The pricing decision is made using a combination of both, with the spreadsheet getting the deciding vote because "at least we know where those numbers come from."
The architecture is additive. It layers on your existing Snowflake and dbt investment. No rip-and-replace. Each layer has a specific governance responsibility.
The most common elasticity model failure is baseline contamination — promotional lift volume bleeding into the baseline estimate. Your dbt model must enforce this separation at the transformation layer.
-- Staging model: enforces baseline/promotional separation -- All volume classified before entering the elasticity layer WITH raw_pos AS ( SELECT sku_id, retailer_id, channel, week_start_date, units_sold, net_revenue, avg_shelf_price, COALESCE(tc.is_promoted, FALSE) AS is_promotional_period, tc.promo_depth_pct FROM raw.pos_weekly_sales pos LEFT JOIN raw.trade_calendar tc ON pos.sku_id = tc.sku_id AND pos.week_start_date BETWEEN tc.promo_start AND tc.promo_end ), classified_volume AS ( SELECT *, CASE WHEN is_promotional_period THEN 0 ELSE units_sold END AS baseline_units, CASE WHEN is_promotional_period THEN units_sold ELSE 0 END AS promo_units FROM raw_pos ) SELECT * FROM classified_volume
class CrossSKUElasticityModel: """Governed model: separate everyday vs. promotional coefficients, cross-SKU substitution, market signal modulation, full lineage.""" def fit(self, df: pd.DataFrame): # CRITICAL: fit separately on baseline vs. promotional periods baseline_df = df[df['promotional_units'] == 0] promo_df = df[df['promotional_units'] > 0] for sku_id, group in baseline_df.groupby('sku_id'): X, y = self._build_features(group, mode='everyday') self.everyday_coef[sku_id] = self.model.fit(X, y).coef_[0] def scenario(self, sku_id, price_delta_pct, market_signals): """Returns predicted volume impact with full parameter lineage.""" e = self.everyday_coef.get(sku_id, -1.5) return { "own_elasticity": round(e, 3), "volume_impact_pct": round(e * (price_delta_pct / 100) * 100, 2), "market_signal_inputs": market_signals, # lineage "model_version": "governed_v1" }
Technical correctness is necessary but not sufficient. These requirements separate a model the commercial team uses from one they archive.
| Governance Requirement | Implementation | Priority |
|---|---|---|
| Unified SKU hierarchy (MDM) | AI-native entity resolution maps all source system item codes to a single unified product master | Critical |
| Baseline/promo separation | Trade calendar join in staging model; hard column separation in fact table | Critical |
| Semantic metric definitions | dbt metrics layer; single YAML definition per KPI used by all downstream tools | Critical |
| Data quality tests | dbt tests on all fact tables; alert on failure before model retrain | Required |
| Output lineage tracking | Every model output includes source table references, model version, run timestamp | Required |
| Market signal ingestion | Automated pipeline for fuel, commodity indices, consumer sentiment | Required |
| Commercial UI access | Governed BI layer or LISN interface for non-engineering users | High Value |
Realistic phasing for an analytics team deploying this architecture on an existing Snowflake and dbt stack. Assumes cloud data warehouse available. No migration required.
| Timeline | Phase | Key Activities |
|---|---|---|
| Days 1–5 | Data Audit & MDM Scoping | Map all source SKU hierarchies across ERP, POS, and syndicated data. Identify mismatches. Scope the AI-native entity resolution requirements. |
| Days 6–12 | Staging Models & Governance Layer | Build dbt staging models with baseline/promo separation. Implement data quality tests. Configure lineage tracking on all fact tables. |
| Days 13–18 | Market Signal Ingestion Pipeline | Build automated ingestion for external market signals. Configure appropriate lag windows. Integrate into the unified semantic layer. |
| Days 19–25 | Elasticity Model Training & Validation | Train cross-SKU elasticity model on governed fact table. Validate everyday vs. promotional coefficients. Run commercial team review on initial outputs. |
| Days 26–30 | Commercial Interface & Sign-Off | Connect model outputs to commercial BI layer or LISN interface. Run outputs against finance numbers. Obtain commercial team sign-off before declaring production-ready. |