Real-Time Price Optimization
Abstract
Section titled “Abstract”Demand falls as price rises, so profit is a curve with a peak rather than a line to climb. This project finds that peak under a constant-elasticity demand model — 18.00 for a unit costing 8.00 — then asks the harder question: what does it cost when the elasticity is estimated from noisy sales rather than known? Twelve observations recover 1.829 against a true 1.80, and the resulting price gives up 0.03% of profit.
Prerequisites
Section titled “Prerequisites”- Python 3.8 or above
- A code editor or IDE
- Basic understanding of ML and analytics
- Required libraries:
pandas,scikit-learn,matplotlib
Before you Start
Section titled “Before you Start”Install Python and the required libraries:
pip install pandas scikit-learn matplotlibGetting Started
Section titled “Getting Started”Create a Project
Section titled “Create a Project”- Create a folder named
real-time-price-optimization. - Open the folder in your code editor or IDE.
- Create a file named
real_time_price_optimization.py. - Copy the code below into your file.
Write the Code
Section titled “Write the Code”Real-Time Price Optimization
pch.viewSource"""Real-time price optimization.
Demand falls as price rises, so profit is a curve with a peak rather than a
line to climb. The optimiser finds that peak, and the interesting part is what
happens when the elasticity it assumes is wrong -- which it always is, because
elasticity has to be estimated from noisy sales.
"""
import matplotlib.pyplot as plt
import numpy as np
from scipy.optimize import minimize_scalar
class PriceOptimizer:
"""Constant-elasticity demand: quantity = base * (price / anchor) ** -e."""
def __init__(self, cost, base=1000.0, anchor=20.0, elasticity=1.8):
self.cost = cost
self.base = base
self.anchor = anchor
self.elasticity = elasticity
def demand(self, price):
return self.base * (price / self.anchor) ** -self.elasticity
def profit(self, price):
return (price - self.cost) * self.demand(price)
def best_price(self):
result = minimize_scalar(lambda p: -self.profit(p),
bounds=(self.cost + 0.01, self.anchor * 5),
method="bounded")
return float(result.x)
def estimate_elasticity(self, prices, quantities):
"""Fit the elasticity from observed sales, in log space."""
slope, _ = np.polyfit(np.log(prices), np.log(quantities), 1)
return float(-slope)
def observed_sales(truth, seed=0, points=12, noise=0.08):
rng = np.random.default_rng(seed)
prices = np.linspace(truth.cost * 1.2, truth.anchor * 2.2, points)
quantities = truth.demand(prices) * rng.lognormal(0, noise, points)
return prices, quantities
def main():
truth = PriceOptimizer(cost=8.0, elasticity=1.8)
optimal = truth.best_price()
print("Real-Time Price Optimization")
print(f" unit cost : {truth.cost:.2f}")
print(f" true elasticity : {truth.elasticity:.2f}")
print(f" profit-maximising price : {optimal:.2f}")
print(f" profit at that price : {truth.profit(optimal):,.0f}")
print(f" profit at cost + 50% : {truth.profit(truth.cost * 1.5):,.0f}")
print(f"\n {'assumed elasticity':>19} {'chosen price':>13} "
f"{'true profit':>13} {'lost vs best':>13}")
assumed_rows = []
for elasticity in (1.2, 1.5, 1.8, 2.2, 3.0):
assumed = PriceOptimizer(cost=truth.cost, elasticity=elasticity)
price = assumed.best_price()
realised = truth.profit(price)
assumed_rows.append((elasticity, price, realised))
print(f" {elasticity:>19.2f} {price:>13.2f} {realised:>13,.0f} "
f"{truth.profit(optimal) - realised:>13,.0f}")
prices, quantities = observed_sales(truth)
estimated = truth.estimate_elasticity(prices, quantities)
fitted = PriceOptimizer(cost=truth.cost, elasticity=estimated)
fitted_price = fitted.best_price()
print(f"\n elasticity estimated from 12 noisy observations: {estimated:.3f}"
f" (true {truth.elasticity:.2f})")
print(f" price it recommends: {fitted_price:.2f} against the true "
f"optimum {optimal:.2f}")
gap = truth.profit(optimal) - truth.profit(fitted_price)
print(f" profit given up by that error: {gap:,.0f} "
f"({gap / truth.profit(optimal):.2%})")
grid = np.linspace(truth.cost + 0.5, truth.anchor * 3, 300)
figure, axes = plt.subplots(1, 2, figsize=(9.5, 3.6))
axes[0].plot(grid, truth.profit(grid), linewidth=1.6)
axes[0].axvline(optimal, linestyle="--", linewidth=1.2,
label=f"optimum {optimal:.2f}")
axes[0].axvline(fitted_price, linestyle=":", linewidth=1.2,
label=f"from estimate {fitted_price:.2f}")
axes[0].set_xlabel("price")
axes[0].set_ylabel("profit")
axes[0].set_title("profit peaks, it does not climb")
axes[0].legend(fontsize=8)
axes[1].scatter(prices, quantities, s=18, label="observed sales")
axes[1].plot(grid, truth.demand(grid), linewidth=1.2, label="true demand")
axes[1].set_xscale("log")
axes[1].set_yscale("log")
axes[1].set_xlabel("price (log)")
axes[1].set_ylabel("quantity (log)")
axes[1].set_title(f"elasticity is the slope here: {estimated:.2f}")
axes[1].legend(fontsize=8)
figure.tight_layout()
plt.savefig("real_time_price_optimization.png", dpi=120,
bbox_inches="tight")
print("saved real_time_price_optimization.png")
if __name__ == "__main__":
main() Example Usage
Section titled “Example Usage”python real_time_price_optimization.pyWhat it produces
Section titled “What it produces”Running the file exactly as it ships takes 2.9 s and prints:
Real-Time Price Optimization
unit cost : 8.00
true elasticity : 1.80
profit-maximising price : 18.00
profit at that price : 12,088
profit at cost + 50% : 10,032
assumed elasticity chosen price true profit lost vs best
1.20 48.00 8,273 3,815
1.50 24.00 11,524 565
1.80 18.00 12,088 0
2.20 14.67 11,651 437
3.00 12.00 10,032 2,056
elasticity estimated from 12 noisy observations: 1.829 (true 1.80)
price it recommends: 17.65 against the true optimum 18.00
profit given up by that error: 3 (0.03%)
saved real_time_price_optimization.png
How it fits together
Section titled “How it fits together”Read from the top: this is what runs when you execute the file, and which function calls which. It is generated from the code, so it cannot drift from it.
flowchart TD RUN(["python real_time_price_optimization.py"]) PriceOptimizer["PriceOptimizer
class"] observed_sales("observed_sales") main("main") RUN --> main main --> PriceOptimizer main --> observed_sales
Explanation
Section titled “Explanation”Key Features
Section titled “Key Features”- Profit as a curve: bounded optimisation over price, not a gradient ascent that runs away.
- Elasticity from data: a log-log regression on observed sales, which is what the slope in that space means.
- Sensitivity, measured: assuming 3.0 elasticity when the truth is 1.8 costs 2,056 in profit; assuming 1.5 costs 565.
- The estimate’s real cost: the gap between the fitted price and the true optimum, priced in profit rather than in price units.
Code Breakdown
Section titled “Code Breakdown”- What it imports (lines 9–11)
import matplotlib.pyplot as plt
import numpy as np
from scipy.optimize import minimize_scalarPriceOptimizer— the class (lines 14–38)
class PriceOptimizer:
"""Constant-elasticity demand: quantity = base * (price / anchor) ** -e."""
def __init__(self, cost, base=1000.0, anchor=20.0, elasticity=1.8):
self.cost = cost
self.base = base
self.anchor = anchor
self.elasticity = elasticity
def demand(self, price):
return self.base * (price / self.anchor) ** -self.elasticity
def profit(self, price):
return (price - self.cost) * self.demand(price)
def best_price(self):
result = minimize_scalar(lambda p: -self.profit(p),
bounds=(self.cost + 0.01, self.anchor * 5),
method="bounded")
return float(result.x)
def estimate_elasticity(self, prices, quantities):
"""Fit the elasticity from observed sales, in log space."""
slope, _ = np.polyfit(np.log(prices), np.log(quantities), 1)
return float(-slope)observed_sales— the function (lines 41–45)
def observed_sales(truth, seed=0, points=12, noise=0.08):
rng = np.random.default_rng(seed)
prices = np.linspace(truth.cost * 1.2, truth.anchor * 2.2, points)
quantities = truth.demand(prices) * rng.lognormal(0, noise, points)
return prices, quantitiesmain— the function (lines 48–104)
def main():
truth = PriceOptimizer(cost=8.0, elasticity=1.8)
optimal = truth.best_price()
print("Real-Time Price Optimization")
print(f" unit cost : {truth.cost:.2f}")
print(f" true elasticity : {truth.elasticity:.2f}")
print(f" profit-maximising price : {optimal:.2f}")
print(f" profit at that price : {truth.profit(optimal):,.0f}")
print(f" profit at cost + 50% : {truth.profit(truth.cost * 1.5):,.0f}")
print(f"\n {'assumed elasticity':>19} {'chosen price':>13} "
f"{'true profit':>13} {'lost vs best':>13}")
assumed_rows = []
for elasticity in (1.2, 1.5, 1.8, 2.2, 3.0):
assumed = PriceOptimizer(cost=truth.cost, elasticity=elasticity)
price = assumed.best_price()
realised = truth.profit(price)
assumed_rows.append((elasticity, price, realised))
# ... 33 more lines in the file ...
axes[1].set_title(f"elasticity is the slope here: {estimated:.2f}")
axes[1].legend(fontsize=8)
figure.tight_layout()
plt.savefig("real_time_price_optimization.png", dpi=120,
bbox_inches="tight")
print("saved real_time_price_optimization.png")The file defines 3 top-level symbols in all; the whole thing is above under Write the Code.
Features
Section titled “Features”- Price Optimization: Real-time data preprocessing and optimization
- Modular Design: Separate functions for each task
- Error Handling: Manages invalid inputs and exceptions
- Production-Ready: Scalable and maintainable code
Next Steps
Section titled “Next Steps”Enhance the project by:
- Integrating with more pricing APIs
- Supporting advanced ML models
- Creating a GUI for optimization
- Adding real-time analytics
- Unit testing for reliability
Educational Value
Section titled “Educational Value”This project teaches:
- Elasticity: what it is and how it is recovered from a log-log slope.
- Optimisation on a bounded interval with
minimize_scalar. - Sensitivity analysis: pricing the consequence of a wrong assumption rather than just noting it.
Real-World Applications
Section titled “Real-World Applications”- E-commerce Platforms
- Analytics Tools
- Optimization Engines
Conclusion
Section titled “Conclusion”Real-Time Price Optimization demonstrates how to build a scalable and accurate price optimization tool using Python. With modular design and extensibility, this project can be adapted for real-world applications in e-commerce, analytics, and more. For more advanced projects, visit Python Central Hub.
Pitfalls
Section titled “Pitfalls”- The entire pricing decision rests on one estimated number. For constant
elasticity the optimum is
cost * e / (e - 1), so the markup depends only on the elasticity — and elasticity has to be estimated from noisy sales data. - Getting it wrong is not symmetric. Measured on the project: assuming 1.20 costs 3,815 in profit, assuming 3.00 costs 2,056, and they are equally far from the true 1.80 in opposite directions. Overpricing hurts more than underpricing, because demand falls faster than margin rises.
- The profit curve is flat at the top, which is the saving grace. In the exercise, every price from 16.09 to 20.38 is within 1% of the best profit, a window 4.29 wide around an optimum of 18.00. Pricing to the cent is not where the money is.
- A point estimate hides the tail. With 12 noisy observations the median profit loss is 0.42% and the worst 5% is 4.02%. Reporting only the median makes a risky estimate look settled.
- The formula has no optimum below elasticity 1. As e approaches 1 the recommended price runs to infinity, so an estimator that can return 0.9 needs a guard, not a spreadsheet.
- Measured on the project: unit cost 8.00, true elasticity 1.80, profit-maximising price 18.00, profit 12,088 against 10,032 at cost-plus-50%.
- Elasticity estimated from 12 noisy observations came out 1.829, recommending 17.65 against a true optimum of 18.00 — giving up 3 in profit, or 0.03%.
- The flatness of the curve is what makes that survivable: a moderately wrong elasticity gives a moderately wrong price, and a moderately wrong price costs almost nothing.
- Cost-plus pricing ignores elasticity entirely, which is why it left 2,056 on the table here.
-
Assuming elasticity 1.20 cost 3,815 in profit; assuming 3.00 cost 2,056. Both are 0.6 away from the true 1.80. Why the asymmetry?
pch.quizShowAnswer
B — Underestimating elasticity raises the price, and demand falls faster than margin rises — so errors towards overpricing are punished harder than errors towards underpricing
-
Every price from 16.09 to 20.38 lands within 1% of the maximum profit. What does that mean in practice?
pch.quizShowAnswer
B — Precision in the elasticity estimate matters far less than being in the right region — which is why a rough estimate from 12 observations gave up only 0.03%
-
An elasticity estimator returns 0.9. What does the pricing formula do?
pch.quizShowAnswer
B — Breaks down — cost * e / (e - 1) has no finite optimum at or below 1, because revenue keeps rising with price, so the value needs an explicit guard
Try it yourself
Section titled “Try it yourself”pch.coffeeTagline
pch.coffeeCtapch.feedbackHeading
pch.feedbackSubheading