De combien puis-je augmenter mon prix avant de tuer mes ventes ?
Mon client veut augmenter le prix de sa Box Cadeau de 30€ à 35€. Bonne idée ou pas ? La réponse n'est pas dans sa tête — elle est dans son historique de ventes. Une régression linéaire de 4 lignes répond. Voici le code, le pourquoi, et la phrase à ne JAMAIS lui dire.
Cet article décortique le projetSaleCastLa question à 80 000 €
Mon client vend une Box Cadeau à 30 €. Il en vend 220 par mois. Il me demande :
"Si je passe à 35 €, je vais en perdre combien ?"
C'est une vraie question. À 30 € × 220 = 6 600 € de chiffre mensuel. À 35 € × 220 = 7 700 €. Mais si à 35 € il n'en vend plus que 150, ça fait 5 250 €. Il perd 1 350 €.
Une réponse à ±10 % près sur la quantité vaut ~80 000 € sur 1 an.
J'ai 4 ans d'historique. Je peux répondre. Voici comment.
La théorie qu'on a tous croisée sans la coder
Manuel de microéconomie, première année. L'élasticité-prix de la demande :
ε = (Δquantité / quantité) / (Δprix / prix)
Si ε = -1 : pour 10 % d'augmentation de prix, la quantité baisse de 10 %. Si ε = -2 : pour 10 % d'augmentation, la quantité baisse de 20 %. Si ε = -0.3 : pour 10 % d'augmentation, la quantité baisse de seulement 3 % (produit peu sensible).
La théorie dit aussi que dans le modèle log-log, la pente d'une régression
de log(quantité) sur log(prix) est l'élasticité.
log(Q) = α + β × log(P) + ε
↑
← c'est ε, l'élasticité directement
Le modèle est posé. Reste à le faire tourner sur les données du client.
L'implémentation dans SaleCast
Voici le code complet (un seul fichier, 142 lignes en tout) :
public static PriceElasticityResult? Estimate(
ProductSalesHistory history,
bool detrend = true)
{
// 1. On garde uniquement les jours où on a vendu ET on a un prix
var validPoints = new List<(double LogQ, double LogP, int DayIndex)>();
for (var i = 0; i < history.Points.Count; i++)
{
var p = history.Points[i];
if (p.Quantity > 0 && p.UnitPrice > 0)
validPoints.Add((Math.Log(p.Quantity), Math.Log(p.UnitPrice), i));
}
if (validPoints.Count < ForecastDefaults.ElasticityMinObservations)
return null;
var logQ = validPoints.Select(v => v.LogQ).ToArray();
var logP = validPoints.Select(v => v.LogP).ToArray();
// 2. Si le prix n'a quasiment jamais bougé, on ne peut rien estimer
var priceStd = ComputeStd(logP);
if (priceStd < 1e-6) return null;
// 3. ⚠ ÉTAPE CRUCIALE : retirer la tendance temporelle
if (detrend)
{
var dayIndices = validPoints.Select(v => (double)v.DayIndex).ToArray();
logQ = Detrend(logQ, dayIndices);
}
// 4. Régression linéaire ordinaire (OLS)
var (beta, alpha, rSquared) = OlsRegression(logP, logQ);
// 5. Significativité statistique (test de Student)
var n = logP.Length;
var residualSumSq = 0.0;
for (var i = 0; i < n; i++)
{
var predicted = alpha + beta * logP[i];
residualSumSq += Math.Pow(logQ[i] - predicted, 2);
}
var residualVariance = n > 2 ? residualSumSq / (n - 2) : 0;
var sumSqDevX = logP.Sum(x => Math.Pow(x - logP.Average(), 2));
var seBeta = sumSqDevX > 1e-10 ? Math.Sqrt(residualVariance / sumSqDevX) : double.NaN;
var tStat = beta / seBeta;
return new PriceElasticityResult {
Elasticity = beta, // ← la réponse
RSquared = rSquared,
TStatistic = tStat,
ObservationCount = n
};
}
C'est tout. Le code est court parce que le bon modèle est court. Si tu sors un papier qui propose un modèle d'élasticité en 4 pages d'équations, c'est probablement un papier académique sans valeur en production.
Les 3 pièges où j'ai planté la première fois
Avant d'arriver à ces 142 lignes, j'ai écrit 3 versions qui marchaient pas. Voici les pièges :
Piège 1 : Oublier le detrending
Le t-shirt Marin se vend de plus en plus chaque année — pas parce qu'il est moins
cher, mais parce que la boutique gagne en notoriété. Si je régresse log(Q) sur
log(P) sans soustraire la tendance, je vais voir une corrélation négative entre
les deux et conclure "le prix baisse les ventes". Faux. Les deux varient pour des
raisons indépendantes.
if (detrend) {
var dayIndices = validPoints.Select(v => (double)v.DayIndex).ToArray();
logQ = Detrend(logQ, dayIndices);
}
Detrend fait une régression linéaire de logQ sur le temps, et soustrait
la prédiction. Ce qui reste, c'est la variation non-temporelle. On peut alors
chercher l'effet du prix dessus.
C'est l'erreur que 80 % des articles "comment calculer l'élasticité en Python" font sans la mentionner.
Piège 2 : Régresser sans variance de prix
Si le prix n'a jamais varié sur la période, il n'y a pas d'élasticité à mesurer. Le modèle te rendra un nombre, mais il sera nul de sens.
var priceStd = ComputeStd(logP);
if (priceStd < 1e-6) return null;
J'ai laissé le code rendre null plutôt qu'un nombre faussement précis. Le client
voit alors "impossible à estimer — pas assez de variation de prix", ce qui est
honnête.
Piège 3 : Croire le résultat sans vérifier la significativité
Un coefficient β = -1.4 calé sur 6 points avec un R² de 0.12, c'est du bruit
qui ressemble à un signal. Le test de Student donne le t-stat. Sous |t| > 2, on
considère que le résultat est significatif.
return new PriceElasticityResult {
Elasticity = beta,
RSquared = rSquared,
TStatistic = tStat,
ObservationCount = n
};
Dans l'UI de SaleCast, on n'affiche jamais l'élasticité seule. Toujours avec sa significativité :
Box Cadeau 30€
Élasticité estimée : -1.83
R² : 0.41
Significativité (t) : -3.2 ← bon signal
Observations : 284 jours
→ Hausse de prix de 30 → 35€ (+16.7%)
→ Quantité prédite : -27% (= -16.7% × -1.83 = +30%)
→ CA prédit : 35 × 161 = 5 635€ (vs 6 600€ actuel)
→ ⚠ Mauvaise idée : -965€/mois
Le client voit immédiatement que la box est élastique (β < -1, donc une hausse de prix réduit le CA total). Il ne tente pas l'augmentation.
Le contre-exemple — celui qui m'a convaincu
Pour un autre client, sur une eau parfumée à 78 € :
Eau parfumée 78€
Élasticité estimée : -0.41
R² : 0.18
Significativité (t) : -2.8 ← significatif
Observations : 420 jours
→ Hausse de prix de 78 → 89€ (+14%)
→ Quantité prédite : -5.7%
→ CA prédit : 89 × 75 = 6 675€ (vs 78 × 80 = 6 240€)
→ ✓ Hausse rentable : +435€/mois
β = -0.41 veut dire produit inélastique. Augmenter le prix de 14 % ne fait
perdre que 5.7 % en quantité. Le CA total monte.
Le client a fait l'augmentation. +435 €/mois confirmés 90 jours plus tard. Marge encore meilleure parce que les coûts variables sont restés constants.
La phrase à NE JAMAIS dire au client
"Vous pouvez augmenter de 10 %, vous perdrez 5 % de ventes."
C'est ce qu'on a envie de dire. C'est ce qui sonne professionnel. C'est ce qui est faux.
Ce qu'il faut dire :
"Sur les 18 derniers mois, sur ce produit précisément, l'élasticité estimée est de -0.41 avec un R² de 0.18. Ça veut dire qu'on a un signal modéré, et que dans ce signal, une hausse de 10 % du prix correspond à une baisse de 4 % de la quantité. C'est valable tant que rien ne change : pas de nouveau concurrent, pas de changement de positionnement, pas de pénurie. Si tu fais l'essai, je suggère 30 jours puis on re-mesure."
Trois choses :
- Le chiffre est étayé — l'élasticité, le R², la période d'observation.
- L'incertitude est nommée — "modéré", "tant que rien ne change".
- Le plan B est posé — re-mesurer à 30 jours.
Aucun de ces 3 points n'est dans la phrase "vous perdrez 5 %". C'est cette différence que mes clients paient.
La leçon
Toute la science derrière ce post tient en une formule de bac+2 : régression linéaire de log(Q) sur log(P) avec detrending. Pas de neural network. Pas de transformer. Pas de cluster GPU.
Et pourtant ce module rapporte des dizaines de milliers d'euros à mes clients chaque année — en évitant des hausses de prix catastrophiques et en débloquant des hausses de prix qu'ils n'osaient pas tenter.
Les modèles ML les plus rentables ne sont pas les plus complexes. Ce sont ceux qui répondent à une question commerciale précise avec un niveau de confiance honnête.
C'est exactement ce qu'un développeur senior doit ressentir avant de pondre 500 lignes d'IA "moderne" pour un problème que 142 lignes d'OLS résolvent mieux.
Stack & code
- PriceElasticityEstimator.cs — 142 lignes, C# pur
- OLS regression — implémentée à la main (10 lignes), pas de Math.NET
- Detrending — régression linéaire temporelle simple
- Test de Student — calcul du standard error sur β + t-stat
- Tests unitaires : 12 cas, dont des cas de données dégénérées (prix constant, n trop petit)
- Pas de dépendance ML — fonctionne en pur C# .NET 10
Le code complet est utilisable sur n'importe quel produit dans le catalogue, instantanément. Pas de batch nocturne. La régression sur 400 points prend 0.2 ms.
C'est exactement le genre de chose dont chaque plateforme e-commerce devrait disposer. Aucune ne l'a, à ma connaissance, sortie de la boîte.
Florian Sola
Lead Technique · Haute performance temps réel · 9 ans d'expérience