How to Build a Dynamic Pricing Model

The following is known to be true in business: High Price, Low Volumes. High Volumes, Low Price. Or is it?

Photo by Benjamin Sharpe on Unsplash
|Photo by Benjamin Sharpe on Unsplash|

In this article, I share with you my experience in building a dynamic pricing system for a long-distance train company, and how we increased the number of seats sold without changing our timetables, nor lowering our average price per seat, by applying very basic principles of microeconomics.

This implementation also applies for any business in which the service it sells shares some characteristics with train seats, that is to say:

  • The cost of selling one additional unit, or marginal cost, is close to zero.
  • If a unit available is not sold by the time the service is rendered, (e.g. A train leaves the station), then it cannot be kept in stock, and its potential value is lost forever.

This would be the case of hotel rooms, airlines, long-haul bus tickets, cinema, theater, concerts, zoos, cruises, sporting events, etc.

In order to build the model, we first need to answer the following questions:

  • How much of a good or service do customers buy?
  • How much do customers pay?
  • When do customers buy?

How Much Customers Buy

Let’s start by taking a look at a typical demand function:

The demand function is the mathematical expression of the relationship between the price of a good or service, and the quantity of said good or service that you can sell at a given price. As stated, for normal goods, the higher the price, the lower the quantity of goods sold. This results in a downward sloping demand function. As you move to the right in the horizontal axis and you increase the quantity or volume of product you want to sell, then you need to lower the price you charge in order to achieve that quantity of goods sold.

So, how much of a good or service customers want to buy will depend on the price. Other things remaining equal, as you lower the price, more customers will be willing to pay for the service.

How Much Customers are Willing to Pay

The graph above can also tell us how much we can charge, given a determined number of units you want to sell.

To simplify, let’s assume that a customer can only buy one unit of the service (in this case a train ticket). Then if you price the ticket at €9, then there are 30 people willing to buy a ticket. If you price it at €3, then there are 90 people willing to buy a ticket (30 willing to buy at €9 plus the 60 additional customers willing to buy at €3).

Maximizing Revenue

If a firm can only charge one and the same price to all customers, and because revenue equals price times quantity, then if the firm charges 6 Euros per ticket, then it will sell 60 tickets, for a total of €360 in revenue.

Sometimes though, firms have the ability to charge different prices to different customers. Economists call this ability price discrimination, and what this enables the firm to do is to increase revenue by capturing more of the value under the demand curve function.

In the example above, if our train company can charge different prices for adult and children passengers, then they could potentially increase revenues from a maximum of €360 they could reach with a single price, to €440 (40 tickets at €8 per adult, plus 20 tickets at €6 per child).

The area under the demand curve represents the value that a particular good or service is delivering to the customer. So, if you could charge not only two different prices to two different types of customers, but several different prices to several different types of customers, then you could capture most of the area under the demand curve and substantially increase revenue, purely from your pricing strategy, without having to incur the cost of increasing departures, changing seating arrangements, increased advertising, or offering additional services on board.

As we have established that there is very little additional cost to selling a seat on a train that is going to depart with that seat empty anyway, and that the value of a seat unsold is lost forever, it will make sense to sell any empty seats at any price above zero, as long as you have empty seats available.

If only we knew a way to segment customers by their price sensitivity.

When do customers buy

We all procrastinate, and so do consumers, too. People will usually postpone purchase decisions for as much as they can. In the case of train tickets, we discovered that this behavior created a pattern with very sporadic purchases of tickets for train departures taking place still far into the future. Then, purchases become more frequent about one month prior to departure, accelerating one week prior to departure, and with a peak of tickets bought one day prior to departure.

If we plot the percentage of tickets remaining to be sold on the horizontal axis, and the number of days left before a train’s departure on the vertical, then we get a downward sloping function. As the time to departure gets closer, ticket sales accelerate, and so the number of tickets remaining to be sold decreases accordingly.

We also found out that the later a passenger bought a ticket, the less price-sensitive he or she was. Thus, we found the variable that allowed us to price discriminate: Time.

Putting it Together

Now, we need to find the particular price we need to charge at a particular time, given that we have a particular number of tickets left to sell. As luck would have it, we have two equations sharing two variables that can be solved simultaneously:

  • The Price Demand Function: P = g(Q)
    where Price (P) is a function (g) of Quantity (Q).

  • And the Number of Tickets left: Q = h(T)
    where Quantity (Q) is a function (h) of Time (T).

This gives us a new function, where Price is a function of Time.

  • P = f(T) = g(h(T))

Shown graphically in the top right quadrant in the figure above. This function tells us what price per ticket to charge at any particular time, given how many tickets we have left to sell.

Using this function, we can also calculate the expected price P at any given value of time T and quantity Q to populate a matrix, and show this function in tabular form.

When things Don’t Go as Planned

Sometimes things don’t happen as expected, if for example, contrary to what our functions predicted, we sell more or fewer tickets.

If at any given time (T) and price (P), the quantity of goods sold was higher than expected, then it means that more people were willing to buy a ticket at the prevailing time and price.

We can represent this variance as an upwards shift in the demand function.

If the demand function shifts upwards in the top left quadrant in the image above. Then, in order to have tickets left to sell for later, when we can charge higher prices, the price needs to go up already now. This results in a similar parallel upward shift in the curve representing Price as a function of Time in the top right quadrant. Both illustrated in red.

Similarly, if we sell fewer tickets than expected, then it means that at a given Time and Price, the quantity sold was lower than expected. This results in the downward shifted curves in green. In order to fill the train, the price needs to go down.

If as time progresses, we find further discrepancies between the number of tickets we expected to sell, versus the actual number of tickets sold, we can think of them as further shifts in the demand function.

Imagine that we consistently find that we are selling more tickets than expected. These can be viewed as consecutive upward shifts in the price demand function for tickets, and the matching parallel upward shifts in the price/time function, as can be seen in the upward shifting red curves in the image above.

Calculating all the values that these upward and downward shifted demand functions give us all the Prices P, given combinations of Times T and Quantities Q needed to fill the full Price Matrix for our hypothetical train departure, showing us the prices that we needed to charge at any given combination of Time to Departure and Seats left to Sell that would maximize revenue for that departure.


During implementation, we had to make some policy decisions to simplify the transition to the new system.

Given that pricing different travel segments in a train network was a rather complex exercise, we did not modify the existing pricing model too extensively, but ended up implementing the price matrix on top of the existing model, as a percentage of listed prices.

We now had two pricing classes (not to be confused with carriage classes): The new, dynamically priced discounted tickets, which were non-refundable; and the full-price, which remained available for purchase as a fully-refundable ticket at the same fixed full-price all the time.

One of the first decisions that needed to be done was to allocate seats per departure into these two ticket classes, which was initially done manually, but was later automated, using historical data as a reference.

To simplify marketing communications, we decided not to charge more than the listed full price ticket, effectively capping the maximum price we could charge, even if the train was going to be very full. In the same vein, we also decided never to charge less than 20% of the full price ticket.

We also had to cluster our different departures, and implement different matrices for the different clusters. An example of one of the matrices we implemented can be seen below.

Final Thoughts

The introduction of this pricing system allowed us to retire an older, much more complicated one that relied on many more variables, requiring lots of manual input, and an expensive software licensing fee. Replacing it with a simpler, more robust and cheaper one that required fewer, in fact, only two inputs: (1) Time to Departure and (2) Quantity of Seats Sold.

Economists usually say that all information is ultimately distilled into the price. And in this case, it worked out precisely like that. We actually made more accurate predictions with fewer inputs, so the model required lower maintenance. Simplicity is cheaper to maintain.

We also retired several complicated discounted ticket classes. This single, time-dependent pricing, allowed us to replace them while simultaneously capturing a broader price range, not only in lower price points, if you bought your tickets earlier, but also in the higher price points, by introducing more nuanced pricing that hugged the demand function closer as time to departure approached. Effectively, it significantly improved our ability to price-discriminate.

After we went live with these price matrices, we saw a 5% increase in the number of tickets sold. Not only from additional sales from the discounted tickets, but interestingly, also from increased full-priced tickets, because the system allowed us to more accurately predict the number of full-price tickets we could sell.

Given the near-zero marginal cost of the additional tickets sold, all the associated increase in revenue went directly to the bottom-line, as no additional variable costs had to be incurred after the matrices were implemented in our ticketing system, resulting in a pure increase of profit margin.

Original article published in Medium.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Create a website or blog at

Up ↑

%d bloggers like this: