[CONSEJO — Doc 09] Contrarian: Tres motores estadísticos coordinados en V1.0 es demasiado ambicioso para un equipo de 4-6 personas con 24 meses de build. La tentación de implementar todo en el primer lanzamiento es real — y peligrosa. Priorizar: Motor de elasticidad de precio (diferenciador más visible) en V1.0. Scoring de leads y demand multi-store pueden ir en V1.1. First Principles: El motor de elasticidad de precio es el feature que ningún competidor POS LATAM tiene. Es el diferenciador que justifica el ARPU de $449–$1,299. Si ese motor falla o da malas sugerencias en los primeros 30 días, el cliente lo desactiva y nunca más lo usa. Executor: El cold start del motor de elasticidad requiere ≥90 días de historial de ventas a diferentes precios. Para clientes que instalan SalesMind sin historial, el motor debe ser honesto: "No tenemos suficientes datos para estimar la elasticidad. El motor se activa cuando haya ≥90 días de ventas registradas." Security Auditor: Los datos de precios y ventas por SKU son comercialmente sensibles. El motor de elasticidad no puede usar datos de un tenant para estimar elasticidades de otro tenant. El modelo es per-tenant, no global.
1. Los tres motores de SalesMind
1.1 Motor 1 — Elasticidad de precio (V1.0)
Propósito: estimar la elasticidad-precio por SKU y sugerir el precio óptimo que maximiza revenue o margen, dado el stock disponible y el contexto de mercado.
Metodología: regresión log-log (elasticidad constante) con variables adicionales:
log(quantity_sold) = α + β × log(price) + γ × log(stock_level)
+ δ × season_index + ε × competitor_price_index
+ η × promo_flag + error
# β es la elasticidad-precio (típicamente negativa: -0.5 a -2.5 para bienes de consumo)
# Un β = -1.0 significa que un 1% de aumento de precio → 1% caída en demanda (elasticidad unitaria)
# Un β = -0.3 significa inelástico: 1% subida de precio → solo 0.3% caída en demanda
Precio óptimo (máximo de revenue):
Para maximizar revenue: P* = P_actual × (1 + 1/(1+β))
Para maximizar margen: P* ajustado por cost_per_unit
Input del motor:
- sl_sale_items: historial de ventas (fecha, precio, cantidad)
- sm_skus.stock_current: nivel de stock actual (afecta urgencia de venta)
- sl_pricing_history: historial de precios anteriores aplicados
Output: sl_pricing_suggestions con precio sugerido + banda de confianza + explicación HTML.
Cold start protocol: - Si <90 días de historial: no generar sugerencia. Status: "Activación en {N} días — acumulando datos." - Si historial sin variación de precio (precio constante): no se puede estimar elasticidad. Status: "Variación de precio necesaria — ajusta manualmente el precio 1-2 veces para calibrar el motor."
Guardrails de pricing: - G1: nunca sugerir precio <costo unitario (margen negativo) - G2: máximo variación permitida por sugerencia: ±20% del precio actual (configurable por tenant) - G3: si el cliente configura precio mínimo/máximo por SKU, la sugerencia respeta esos límites - G4: precio sugerido siempre requiere aprobación del gerente (nunca se aplica automáticamente)
1.2 Motor 2 — Scoring de leads (V1.1)
Propósito: estimar la probabilidad de cierre de cada oportunidad en el pipeline CRM.
Metodología: logistic regression + gradient boosting (scikit-learn), con features:
features = [
# Features del cliente
'customer_sector', # ferretería, distribuidora, etc.
'customer_purchase_count', # cuántas veces ha comprado antes
'customer_ltv', # LTV histórico
'days_since_last_purchase',
# Features de la oportunidad
'lead_value', # MRR estimado
'days_in_current_stage', # cuántos días lleva en esta etapa
'activity_count_30d', # número de actividades en los últimos 30 días
'has_demo_done', # booleano
'has_proposal_sent', # booleano
# Features del rep
'rep_close_rate_90d', # tasa de cierre histórica del rep
'rep_avg_cycle_days', # ciclo de venta promedio del rep
]
# Output: probability_close (0.0 a 1.0)
# Threshold para "hot lead": >0.70
# Threshold para "at risk": <0.30 en stage ≥ 'demo'
Cold start: si el tenant tiene <20 oportunidades históricas cerradas, usar modelo de benchmark de industria (pesos genéricos calibrados con datos del sector retail LATAM). Se activa el modelo tenant-específico cuando hay ≥20 oportunidades con resultado conocido.
1.3 Motor 3 — Demand multi-store (V1.1)
Propósito: para clientes con múltiples tiendas, proyectar la demanda por tienda × SKU × semana para optimizar el reabastecimiento entre tiendas.
Metodología: hierarchical forecasting con reconciliación MinT (Hyndman et al. 2011):
Nivel total → Nivel tienda → Nivel SKU×tienda
Reconciliación top-down + bottom-up con método MinT (Minimum Trace)
Reutiliza el motor statsforecast de StockMind con extensión jerárquica
Output: tabla de transferencias recomendadas entre tiendas para las próximas 4 semanas.
2. Arquitectura técnica de los motores
Celery Worker: pricing_worker (diario 4am)
│
├── Lee sl_sale_items + sl_pricing_history (últimos 180 días)
├── Ejecuta regresión log-log por SKU (scikit-learn)
├── Calcula precio óptimo + banda de confianza
├── Aplica guardrails G1-G4
├── Genera explicación HTML (Jinja2)
└── Escribe en sl_pricing_suggestions
Celery Worker: scoring_worker (cada 6h)
│
├── Lee sl_leads con stage en ['prospect', 'contacted', 'demo', 'proposal']
├── Extrae features por oportunidad
├── Aplica modelo logistic regression
└── Actualiza sl_leads.score
Celery Worker: demand_worker (diario 3am, V1.1)
│
├── Lee sl_sale_items por tienda × SKU × semana
├── Ejecuta statsforecast con reconciliación jerárquica
└── Genera recomendaciones de transferencia entre tiendas
3. Validación y calidad del motor
3.1 Tests del motor de elasticidad
def test_elasticity_negative_for_normal_goods():
"""La elasticidad-precio debe ser negativa para bienes normales."""
# Fixture: serie temporal donde mayor precio → menor cantidad
result = estimate_elasticity(PRICE_DOWN_DEMAND_UP_SERIES)
assert result.beta < 0
def test_optimal_price_never_below_cost():
"""El precio óptimo nunca puede ser menor al costo unitario."""
result = suggest_optimal_price(
elasticity=-0.8,
current_price=100,
unit_cost=80
)
assert result.suggested_price >= 80 # Guardrail G1
def test_cold_start_returns_no_suggestion():
"""Con <90 días de historial, el motor no genera sugerencia."""
result = run_pricing_motor(
sales_history=SERIES_WITH_60_DAYS,
sku_id='test-sku'
)
assert result.status == 'cold_start'
assert result.suggested_price is None
3.2 Métricas de calidad del motor de pricing
| Métrica | Meta | Alarma |
|---|---|---|
| % SKUs con elasticidad estimada | ≥60% del catálogo activo | <40% → datos insuficientes |
| Tasa de aprobación de sugerencias | ≥50% | <30% → sugerencias malas |
| % sugerencias dentro del ±20% del precio actual | ≥90% | <80% → guardrail inefectivo |
| Revenue lift post-aprobación vs. control | ≥2% | <0% → motor no aporta valor |
Ver también: Doc 07 (SRD Backend — workers que ejecutan estos motores) · Doc 08 (Modelo de Datos — tablas de input/output de cada motor) · Doc 06 (SRD Frontend — cómo se presentan las sugerencias al usuario) · wiki/05-motor-estadistico.md SalesMind (spec técnica extendida)