In this document I shall demonstrate merger simulation with a synthetic example. On the supply side assuming a differentiated Bertrand model and on the demand side assuming a multinomial logit demand function.
The Bertrand system of equations has 4 components:
(1) Market shares.
(2) Prices.
(3) Derivatives of the demand function with respect to the prices.
(4) Marginal costs of the firms.
For the sake of demonstration the market shares chosen are 0.2, 0.25 and 0.3.
prices are 50, 75 and 80 correspondingly.
# 1. market shares
s <- c(0.2, 0.25, 0.3)
# outside option
(s_0 <- 1 - sum(s) )
## [1] 0.25
# 2. prices
p <- c(50, 75, 80)Calculation of derivatives
The logit derivative is a function of market shares and the consumer’s derivative with respect to the price - the model’s \(alpha\).
Again, for demonstration, \(alpha = 0.1\) is chosen.
In fact, this is a calibration of the system. One can estimate those parameters with a demand estimation (which is beyond the scope of this simple example).
# 3. derivatives
# choose alpha
alpha <- - 0.1
# derivatives are functions of market shares and alpha
# own derivative
(d_sj_d_pj <- alpha * (1-s) * s)
## [1] -0.0160 -0.0188 -0.0210
# cross derivative
(d_sk_d_pj <- - alpha * s %o% s)
## [,1] [,2] [,3]
## [1,] 0.004 0.00500 0.0060
## [2,] 0.005 0.00625 0.0075
## [3,] 0.006 0.00750 0.0090
# put own derivatives in diagonal
der <- d_sk_d_pj
diag(der) <- d_sj_d_pj
# final result
der
## [,1] [,2] [,3]
## [1,] -0.016 0.0050 0.0060
## [2,] 0.005 -0.0188 0.0075
## [3,] 0.006 0.0075 -0.0210
# cleanup
rm(d_sj_d_pj, d_sk_d_pj)solving the equation systme for the marginal costs
As stated above, First order condition has 4 components.
We have supplied the market shares, the prices and derivatives. next we solve the system for the marginal costs.
For the solution we create the “owner ship” matrix:
# 4. solving a system of equations for the marginal costs
# ownership matrix
(theta <- matrix( c(1,0,0,0,1,0,0,0,1), nrow = 3))## [,1] [,2] [,3]
## [1,] 1 0 0
## [2,] 0 1 0
## [3,] 0 0 1
# solve a system of linear equations
(mc <- solve(theta * der) %*% s + p )## [,1]
## [1,] 37.5
## [2,] 61.7
## [3,] 65.7
We can check the first order condition are valid.
# check that F.O.C's are met:
s + (theta * der) %*% (p - mc)## [,1]
## [1,] -0.0000000000000000278
## [2,] 0.0000000000000000555
## [3,] -0.0000000000000001110
a merger in the market
When a merger happens, the ownership of firms over the product set is changing. Here we assume that products 1 and 2 are now under the same ownership. For that, we update the ownership matrix. The 1st and 2nd elements 1st and 2nd rows of the matrix will equal to 1:
# new ownership matrix:
(theta_post <- matrix(c(1,1,0,1,1,0,0,0,1), nrow = 3))## [,1] [,2] [,3]
## [1,] 1 1 0
## [2,] 1 1 0
## [3,] 0 0 1
After changing the ownership matrix, the firm understands that when the price of product 1 goes up, some of the customers that choose not to by it anymore will land on product 2, hance for those customers, the firm does not loose its sails. With that knowledge, the firm can raise the price up. The first order condition is not valid anymore because the old price is not the optimal price.
# FOC don't hold
s + (theta_post * der) %*% p - (theta_post * der) %*% mc## [,1]
## [1,] 0.0667
## [2,] 0.0625
## [3,] 0.0000
s + (theta_post * der) %*% (p - mc)## [,1]
## [1,] 0.066666666666666624
## [2,] 0.062500000000000056
## [3,] -0.000000000000000111
Next we demonstrate a fixed point iteration step by step:
There is no analytic solution to this system. we need to use a method of search to find the new equilibrium that will prevail in the market after the merger. assuming convexity and continuity of the system, we can find a solution and know it is unique.
according to the model , the average utility from poduct j is: \[ \delta_j = x \beta_j - \alpha p + \xi _j \]
For each product we calculate the average utility.
A change in price will change the utility from the product. This in turn, will be reflected in the demand function, the demand decreases when the price goes up and vice versa.
# delta = x * b - alpha * p
(delta <- log(s / s_0)) ## [1] -0.223 0.000 0.182
exp(delta) / (1 + sum(exp(delta)))## [1] 0.20 0.25 0.30
fixed point first few iterations
For demonstration, I’ll show part of the convergence process stage by stage manually.
In every iteration 4 stages will take place:
(1) Solving the firms first order condition to the new prices in relation to the market shares of the previous stage.
(2) Compute the change in the price vector compared to the pre-merger price vector.
(3) Compute the change in consumers utilities.
(4) Get the new marked shares out from the demand function.
# the price update procedure:
# 1. solve FOC 1
(p1 <- as.vector(mc + (1/ - alpha) * (1 / (1 - theta_post %*% s))))## [1] 55.7 79.8 80.0
# 2. delta price
(d_p <- (p1 - p))## [1] 5.68 4.85 0.00
# 3. delta in utility
(d_delta <- d_p * alpha)## [1] -0.568 -0.485 0.000
# 4. solve demand for new utility
(s1 <- exp(delta + d_delta) / (1 + sum(exp(delta + d_delta))))## [1] 0.139 0.188 0.367
get the results and compere to the pre merger status
results <-rbind(c(s, p), c(s1, p1))
results## [,1] [,2] [,3] [,4] [,5] [,6]
## [1,] 0.200 0.250 0.300 50.0 75.0 80
## [2,] 0.139 0.188 0.367 55.7 79.8 80
Repead this process several times.
# solve FOC 2:
p2 <- as.vector(mc + (1/ -alpha) * (1/(1 - theta_post %*% s1)))
d_p <- (p2 - p)
d_delta <- d_p * alpha
# solve demand 2:
s2 <- exp(delta + d_delta) / (1 + sum(exp( delta + d_delta)))
results <- rbind(results, c(s2, p2))
# solve FOC 3:
p3 <- as.vector(mc + (1/ -alpha) * (1/(1 - theta_post %*% s2)))
d_p <- (p3 - p)
d_delta <- d_p * alpha
# solve demand 3:
s3 <- exp(delta + d_delta) / (1 + sum(exp( delta + d_delta)))
results <- rbind(results, c(s3, p3))
# solve FOC 4:
p4 <- as.vector(mc + (1/ -alpha) * (1/(1 - theta_post %*% s3)))
d_p <- (p4 - p)
d_delta <- d_p * alpha
# solve demand 3:
s4 <- exp(delta + d_delta) / (1 + sum(exp( delta + d_delta)))
results <- rbind(results, c(s4, p4))
# solve FOC 5:
p5 <- as.vector(mc + (1/ -alpha) * (1/(1 - theta_post %*% s4)))
d_p <- (p5 - p)
d_delta <- d_p * alpha
# solve demand 3:
s5 <- exp(delta + d_delta) / (1 + sum(exp( delta + d_delta)))
results <- rbind(results, c(s5, p5))
# solve FOC 6:
p6 <- as.vector(mc + (1/ -alpha) * (1/(1 - theta_post %*% s5)))
d_p <- (p6 - p)
d_delta <- d_p * alpha
# solve demand
s6 <- exp(delta + d_delta) / (1 + sum(exp( delta + d_delta)))
results <- rbind(results, c(s6, p6))
# solve FOC 7:
p7 <- as.vector(mc + (1/ -alpha) * (1/(1 - theta_post %*% s6)))
d_p <- (p7 - p)
d_delta <- d_p * alpha
# solve demand
s7 <- exp(delta + d_delta) / (1 + sum(exp( delta + d_delta)))
results <- rbind(results, c(s7, p7))
# solve FOC 8:
p8 <- as.vector(mc + (1/ -alpha) * (1/(1 - theta_post %*% s7)))
d_p <- (p8 - p)
d_delta <- d_p * alpha
# solve demand
s8 <- exp(delta + d_delta) / (1 + sum(exp( delta + d_delta)))In the next matrix, colums 1-3 are market shares and columns 4-6 are the prices.
every iteration the jumps in the values decreases.
(results <- rbind(results, c(s8, p8)))## [,1] [,2] [,3] [,4] [,5] [,6]
## [1,] 0.200 0.250 0.300 50.0 75.0 80.0
## [2,] 0.139 0.188 0.367 55.7 79.8 80.0
## [3,] 0.179 0.244 0.293 52.4 76.5 81.5
## [4,] 0.146 0.198 0.360 54.8 79.0 79.9
## [5,] 0.175 0.237 0.301 52.7 76.9 81.3
## [6,] 0.150 0.204 0.352 54.5 78.7 80.0
## [7,] 0.171 0.232 0.308 53.0 77.1 81.1
## [8,] 0.153 0.208 0.346 54.3 78.4 80.2
## [9,] 0.169 0.229 0.314 53.1 77.3 81.0
OK, we got the idea, lets calculate the new equilibrium.
max_iter <- 100
s_in <- s
i <- 0
s_delta_norm <- 1
convergence_matrix <- matrix(nrow = max_iter, ncol = length(s) * 2 + 2)
while(s_delta_norm > 1e-6 & i < max_iter){
i <- i + 1
# solve F.O.C
( p_new <- as.vector(mc + (1/ - alpha ) * ( 1 / (1 - theta_post %*% s_in) )))
# change in utility
d_delta <- (p_new - p) * alpha
# solve demand system
s_new <- exp(delta + d_delta) / (1 + sum(exp(delta + d_delta)))
# norm of change in market shares
(s_delta_norm <- sqrt(sum((s_in - s_new) ^ 2)))
# save resault for next iteration
s_in <- s_new
convergence_matrix[i, ] <- c(s_new, p_new, s_delta_norm, i)
}
colnames(convergence_matrix) <- c("s1", "s2", "s3", "p1", "p2", "p3", "norm", "iteration")
convergence_matrix## s1 s2 s3 p1 p2 p3 norm iteration
## [1,] 0.139 0.188 0.367 55.7 79.8 80.0 0.109827719 1
## [2,] 0.179 0.244 0.293 52.4 76.5 81.5 0.101188131 2
## [3,] 0.146 0.198 0.360 54.8 79.0 79.9 0.087867750 3
## [4,] 0.175 0.237 0.301 52.7 76.9 81.3 0.076132778 4
## [5,] 0.150 0.204 0.352 54.5 78.7 80.0 0.065389010 5
## [6,] 0.171 0.232 0.308 53.0 77.1 81.1 0.056301895 6
## [7,] 0.153 0.208 0.346 54.3 78.4 80.2 0.048324608 7
## [8,] 0.169 0.229 0.314 53.1 77.3 81.0 0.041574711 8
## [9,] 0.155 0.211 0.341 54.1 78.3 80.3 0.035693191 9
## [10,] 0.167 0.227 0.317 53.3 77.4 80.9 0.030698062 10
## [11,] 0.157 0.213 0.338 54.0 78.1 80.4 0.026361844 11
## [12,] 0.165 0.225 0.320 53.4 77.5 80.8 0.022667853 12
## [13,] 0.158 0.215 0.335 53.9 78.1 80.4 0.019469604 13
## [14,] 0.164 0.223 0.322 53.4 77.6 80.8 0.016738813 14
## [15,] 0.159 0.216 0.334 53.8 78.0 80.5 0.014379099 15
## [16,] 0.164 0.222 0.324 53.5 77.7 80.7 0.012360880 16
## [17,] 0.160 0.217 0.332 53.8 77.9 80.5 0.010619418 17
## [18,] 0.163 0.222 0.325 53.5 77.7 80.7 0.009128122 18
## [19,] 0.160 0.218 0.331 53.7 77.9 80.5 0.007842695 19
## [20,] 0.163 0.221 0.326 53.6 77.7 80.7 0.006740912 20
## [21,] 0.160 0.218 0.331 53.7 77.9 80.6 0.005791974 21
## [22,] 0.162 0.221 0.327 53.6 77.8 80.7 0.004978054 22
## [23,] 0.161 0.218 0.330 53.7 77.9 80.6 0.004277453 23
## [24,] 0.162 0.220 0.327 53.6 77.8 80.6 0.003676234 24
## [25,] 0.161 0.219 0.330 53.7 77.9 80.6 0.003158943 25
## [26,] 0.162 0.220 0.327 53.6 77.8 80.6 0.002714868 26
## [27,] 0.161 0.219 0.329 53.7 77.8 80.6 0.002332905 27
## [28,] 0.162 0.220 0.328 53.6 77.8 80.6 0.002004913 28
## [29,] 0.161 0.219 0.329 53.7 77.8 80.6 0.001722864 29
## [30,] 0.162 0.220 0.328 53.6 77.8 80.6 0.001480620 30
## [31,] 0.161 0.219 0.329 53.7 77.8 80.6 0.001272343 31
## [32,] 0.162 0.220 0.328 53.6 77.8 80.6 0.001093433 32
## [33,] 0.161 0.219 0.329 53.7 77.8 80.6 0.000939630 33
## [34,] 0.162 0.220 0.328 53.6 77.8 80.6 0.000807498 34
## [35,] 0.161 0.219 0.329 53.7 77.8 80.6 0.000693919 35
## [36,] 0.162 0.220 0.328 53.6 77.8 80.6 0.000596336 36
## [37,] 0.161 0.219 0.329 53.7 77.8 80.6 0.000512461 37
## [38,] 0.162 0.219 0.328 53.6 77.8 80.6 0.000440394 38
## [39,] 0.161 0.219 0.329 53.7 77.8 80.6 0.000378453 39
## [40,] 0.162 0.219 0.328 53.6 77.8 80.6 0.000325231 40
## [41,] 0.161 0.219 0.329 53.7 77.8 80.6 0.000279488 41
## [42,] 0.162 0.219 0.328 53.6 77.8 80.6 0.000240183 42
## [43,] 0.161 0.219 0.329 53.7 77.8 80.6 0.000206402 43
## [44,] 0.161 0.219 0.328 53.6 77.8 80.6 0.000177375 44
## [45,] 0.161 0.219 0.328 53.7 77.8 80.6 0.000152428 45
## [46,] 0.161 0.219 0.328 53.6 77.8 80.6 0.000130991 46
## [47,] 0.161 0.219 0.328 53.7 77.8 80.6 0.000112568 47
## [48,] 0.161 0.219 0.328 53.6 77.8 80.6 0.000096737 48
## [49,] 0.161 0.219 0.328 53.7 77.8 80.6 0.000083132 49
## [50,] 0.161 0.219 0.328 53.6 77.8 80.6 0.000071440 50
## [51,] 0.161 0.219 0.328 53.7 77.8 80.6 0.000061393 51
## [52,] 0.161 0.219 0.328 53.6 77.8 80.6 0.000052759 52
## [53,] 0.161 0.219 0.328 53.7 77.8 80.6 0.000045339 53
## [54,] 0.161 0.219 0.328 53.7 77.8 80.6 0.000038962 54
## [55,] 0.161 0.219 0.328 53.7 77.8 80.6 0.000033483 55
## [56,] 0.161 0.219 0.328 53.7 77.8 80.6 0.000028774 56
## [57,] 0.161 0.219 0.328 53.7 77.8 80.6 0.000024727 57
## [58,] 0.161 0.219 0.328 53.7 77.8 80.6 0.000021249 58
## [59,] 0.161 0.219 0.328 53.7 77.8 80.6 0.000018261 59
## [60,] 0.161 0.219 0.328 53.7 77.8 80.6 0.000015693 60
## [61,] 0.161 0.219 0.328 53.7 77.8 80.6 0.000013486 61
## [62,] 0.161 0.219 0.328 53.7 77.8 80.6 0.000011589 62
## [63,] 0.161 0.219 0.328 53.7 77.8 80.6 0.000009959 63
## [64,] 0.161 0.219 0.328 53.7 77.8 80.6 0.000008559 64
## [65,] 0.161 0.219 0.328 53.7 77.8 80.6 0.000007355 65
## [66,] 0.161 0.219 0.328 53.7 77.8 80.6 0.000006320 66
## [67,] 0.161 0.219 0.328 53.7 77.8 80.6 0.000005432 67
## [68,] 0.161 0.219 0.328 53.7 77.8 80.6 0.000004668 68
## [69,] 0.161 0.219 0.328 53.7 77.8 80.6 0.000004011 69
## [70,] 0.161 0.219 0.328 53.7 77.8 80.6 0.000003447 70
## [71,] 0.161 0.219 0.328 53.7 77.8 80.6 0.000002962 71
## [72,] 0.161 0.219 0.328 53.7 77.8 80.6 0.000002546 72
## [73,] 0.161 0.219 0.328 53.7 77.8 80.6 0.000002188 73
## [74,] 0.161 0.219 0.328 53.7 77.8 80.6 0.000001880 74
## [75,] 0.161 0.219 0.328 53.7 77.8 80.6 0.000001616 75
## [76,] 0.161 0.219 0.328 53.7 77.8 80.6 0.000001388 76
## [77,] 0.161 0.219 0.328 53.7 77.8 80.6 0.000001193 77
## [78,] 0.161 0.219 0.328 53.7 77.8 80.6 0.000001025 78
## [79,] 0.161 0.219 0.328 53.7 77.8 80.6 0.000000881 79
## [80,] NA NA NA NA NA NA NA NA
## [81,] NA NA NA NA NA NA NA NA
## [82,] NA NA NA NA NA NA NA NA
## [83,] NA NA NA NA NA NA NA NA
## [84,] NA NA NA NA NA NA NA NA
## [85,] NA NA NA NA NA NA NA NA
## [86,] NA NA NA NA NA NA NA NA
## [87,] NA NA NA NA NA NA NA NA
## [88,] NA NA NA NA NA NA NA NA
## [89,] NA NA NA NA NA NA NA NA
## [90,] NA NA NA NA NA NA NA NA
## [91,] NA NA NA NA NA NA NA NA
## [92,] NA NA NA NA NA NA NA NA
## [93,] NA NA NA NA NA NA NA NA
## [94,] NA NA NA NA NA NA NA NA
## [95,] NA NA NA NA NA NA NA NA
## [96,] NA NA NA NA NA NA NA NA
## [97,] NA NA NA NA NA NA NA NA
## [98,] NA NA NA NA NA NA NA NA
## [99,] NA NA NA NA NA NA NA NA
## [100,] NA NA NA NA NA NA NA NA
Compare the data before the merger with the prediction of the simulation about the merger effect on prices and market shares
# attach preconditions with last row without NA's in the matrix
final_resault <- convergence_matrix[tail(which(rowSums(!is.na(convergence_matrix)) > 0), 1),]
rbind(c(results[1,], NA,0),final_resault)## s1 s2 s3 p1 p2 p3 norm iteration
## 0.200 0.250 0.300 50.0 75.0 80.0 NA 0
## final_resault 0.161 0.219 0.328 53.7 77.8 80.6 0.000000881 79
The market share of the outside option has increased
c(before = s_0, after = 1 - sum(final_resault[1:3]))## before after
## 0.250 0.291
Present convergence process on a plot
p1 <- data.frame(convergence_matrix) %>% select(iteration, p1, p2, p3) %>% gather(k = "k", v = "prices", 2:4) %>%
ggplot(aes(x = iteration, y = prices, color = k)) + geom_line(size = 1.2)
p2 <- data.frame(convergence_matrix) %>% select(iteration, s1, s2, s3) %>% gather(k = "k", v = "shares", 2:4) %>%
ggplot(aes(x = iteration, y = shares, color = k)) + geom_line(size =1.2)
ggpubr::ggarrange(p1, p2, ncol = 1)First order conditions:
s_in + (theta_post*der) %*% (p_new - mc)## [,1]
## [1,] -0.01620
## [2,] -0.00271
## [3,] 0.01573
Compare our results with the Stata merger-sim package:
stata_sim <- read.csv("multinomial stata results.csv")
IA_kable(stata_sim)| q | p | m | firm | M_delta | M_costs | M_price2 | M_quantity2 | M_price_ch | M_marketsh | M_marketsh2 | M_marketsh_ch |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 20 | 50 | 100 | 1 | 4.78 | 37.5 | 53.7 | 16.1 | 0.073 | 0.267 | 0.228 | -0.039 |
| 25 | 75 | 100 | 2 | 7.50 | 61.7 | 77.8 | 21.9 | 0.038 | 0.333 | 0.309 | -0.024 |
| 30 | 80 | 100 | 3 | 8.18 | 65.7 | 80.6 | 32.8 | 0.008 | 0.400 | 0.463 | 0.063 |