Pricing Optimization: How to find the price that maximizes your profit

[This article was first published on R – insightR, and kindly contributed to R-bloggers]. (You can report issue about the content on this page here)
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

By Yuri Fonseca

Basic idea

In this post we will discuss briefly about pricing optimization. The main idea behind this problem is the following question: As manager of a company/store, how much should I charge in order to maximize my revenue or profit?

Obviously, the answer isn’t as high as possible. If you charge one hundred dollars for a candy, probably only one or two people will accept to buy it. Although the profit per product is very high, you probably won’t even your fixed costs. Charge a very small is also not the best call.

Before showing an example for this problem, let us build some simple formulas.

Imagine a monopolist selling a specific product with demand curve Q(p), where Q(p) is the quantity sold given a specific price p. To simplify things, let’s suppose that Q(p) is a linear function:

\displaystyle Q(p) = \alpha p + \beta

The total revenue will be given by:

\displaystyle R(p) = p Q(p) = \alpha p^2 + p\beta

And total profit will be given by:

\displaystyle L(p) = (p - c)Q(p) = \alpha p^2 - \alpha pc + \beta(p - c)

Where c is the unity cost of the product. Adding fixed costs in the profit equation does not change the price police, so we will suppose it’s zero.

Next, we differentiate the equations for R(p) and L(p) to find the first order conditions, which allow us to find the optimal police under the hypothesis of a linear demand curve. \alpha is expected to be negative (demand decrease when prices increase) R and L are concave functions of p. As consequence, the critical point will be a maximum point. Therefore, the optimal police for revenue is given by:

\displaystyle P_{\textrm{max rev}}= \frac{-\beta}{2\alpha}

And for profit:

\displaystyle P_{\textrm{max prof}} = \frac{-\beta + \alpha c}{2\alpha}

Note that sometimes people write the linear demand curve as Q(p) = -\alpha p + \beta, in this case \alpha should be positive, and the signs in equation 2 and equation 3 must change. Moreover, it is interesting to note that the price that maximizes profit is always bigger than the one that maximizes total revenue because c is always positive.

If taxes are calculated just on profit the price police remains the same. However, countries like Brazil usually charges a lot of taxes on total revenue. In this case, the price police for maximizing revenue doesn’t change, but the police for maximizing profit will change according to the following expression:

\displaystyle P_{\textrm{max prof}}^{(tax)} = \frac{-\beta(1-tax) + \alpha c}{2\alpha(1-tax)}

Example and implementations:

As an example of how to proceed with the estimation of the optimum price, let’s generate a linear demand curve with for daily selling of a product:

library(ggplot2)

# example of linear demand curve (first equation) 

demand = function(p, alpha = -40, beta = 500, sd = 10) {

  error = rnorm(length(p), sd = sd)
  q = p*alpha + beta + error

  return(q)
}

set.seed(100)

prices = seq(from = 5, to = 10, by = 0.1)
q = demand(prices)

data = data.frame('prices' = prices,'quantity' = q)

ggplot(data, aes(prices, quantity)) +
  geom_point(shape=1) +
  geom_smooth(method='lm') +
  ggtitle('Demand Curve')

plot of chunk demand

Imagine a company that has been selling the product which follows the demand curve above for a while (one year changing prices daily), testing some prices over time. The following time-series is what we should expect for the historical revenue, profit and cost of the company:

set.seed(10)

hist.prices = rnorm(252, mean = 6, sd = .5) # random prices defined by the company
hist.demand = demand(hist.prices) # demand curve defined in the chunck above
hist.revenue = hist.prices*hist.demand # From the revenue equation
unity.cost = 4 # production cost per unity
hist.cost = unity.cost*hist.demand
hist.profit = (hist.prices - unity.cost)*hist.demand # From the price equation

data = data.frame('Period' = seq(1,252),'Daily.Prices' = hist.prices,
                  'Daily.Demand' = hist.demand, 'Daily.Revenue' = hist.revenue,
                  'Daily.Cost' = hist.cost, 'Daily.Profit' = hist.profit)

ggplot(data, aes(Period, Daily.Prices)) +
  geom_line(color = 4) +
  ggtitle('Historical Prices used for explotation')

plot of chunk historical series

ggplot(data, aes(Period, Daily.Revenue, colour = 'Revenue')) +
  geom_line() +
  geom_line(aes(Period, Daily.Profit, colour = 'Profit')) +
  geom_line(aes(Period, Daily.Cost, colour = 'Cost')) +
  labs(title = 'Historical Performance', colour = '')

plot of chunk historical series

We can recover the demand curve using the historical data (that is how it is done in the real world).

library(stargazer)

model.fit = lm(hist.demand ~ hist.prices) # linear model for demand
stargazer(model.fit, type = 'html', header = FALSE) # output
Dependent variable:
hist.demand
hist.prices -38.822***
(1.429)
Constant 493.588***
(8.542)
Observations 252
R2 0.747
Adjusted R2 0.746
Residual Std. Error 10.731 (df = 250)
F Statistic 738.143*** (df = 1; 250)
Note: *p<0.1; **p<0.05; ***p<0.01

And now we need to apply equation 2 and equation 3.

# estimated parameters
beta = model.fit$coefficients[1]
alpha = model.fit$coefficients[2]  

p.revenue = -beta/(2*alpha) # estimated price for revenue
p.profit = (alpha*unity.cost - beta)/(2*alpha) # estimated price for profit

The final plot with the estimated prices:

true.revenue = function(p) p*(-40*p + 500) # Revenue with true parameters (chunck demand)
true.profit = function(p) (p - unity.cost)*(-40*p + 500) # price with true parameters

# estimated curves
estimated.revenue = function(p) p*(model.fit$coefficients[2]*p + model.fit$coefficients[1])
estimated.profit = function(p) (p - unity.cost)*(model.fit$coefficients[2]*p + model.fit$coefficients[1])

opt.revenue = true.revenue(p.revenue) # Revenue with estimated optimum price
opt.profit = true.profit(p.profit) # Profit with estimated optimum price

# plot
df = data.frame(x1 = p.revenue, x2 = p.profit,
                y1 = opt.revenue, y2 = opt.profit, y3 = 0)

ggplot(data = data.frame(Price = 0)) +
  stat_function(fun = true.revenue, mapping = aes(x = Price, color = 'True Revenue')) +
  stat_function(fun = true.profit, mapping = aes(x = Price, color = 'True Profit')) +
  stat_function(fun = estimated.revenue, mapping = aes(x = Price, color = 'Estimated Revenue')) +
  stat_function(fun = estimated.profit, mapping = aes(x = Price, color = 'Estimated Profit')) +
  scale_x_continuous(limits = c(4, 11)) +
  labs(title = 'True curves without noise') +
  ylab('Results') +
  scale_color_manual(name = "", values = c("True Revenue" = 2, "True Profit" = 3, "Estimated Revenue" = 4, "Estimated Profit" = 6)) +
  geom_segment(aes(x = x1, y = y1, xend = x1, yend = y3), data = df) +
  geom_segment(aes(x = x2, y = y2, xend = x2, yend = y3), data = df)

plot of chunk final plot

Final observations

As you can see, the estimated Revenue and estimated Profit curves are quite similar to the true ones without noise and the expected revenue for our estimated optimal policies looks very promising. Although the linear and monopolist assumption looks quite restrictive, this might not be the case, check Besbes and Zeevi (2015) and Cooper et al (2015).

If one expect a large variance for \alpha, it might be useful to simulate \alpha, \beta and then the optimal price using Jensen’s inequality.

References

Phillips, Robert Lewis. Pricing and revenue optimization. Stanford University Press, 2005.

Besbes, Omar, and Assaf Zeevi. “On the (surprising) sufficiency of linear models for dynamic pricing with demand learning.” Management Science 61.4 (2015): 723-739.

Cooper, William L., Tito Homem-de-Mello, and Anton J. Kleywegt. “Learning and pricing with models that do not explicitly incorporate competition.” Operations research 63.1 (2015): 86-103.

Talluri, Kalyan T., and Garrett J. Van Ryzin. The theory and practice of revenue management. Vol. 68. Springer Science & Business Media, 2006.


To leave a comment for the author, please follow the link and comment on their blog: R – insightR.

R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

Never miss an update!
Subscribe to R-bloggers to receive
e-mails with the latest R posts.
(You will not see this message again.)

Click here to close (This popup will not appear again)