1 intro

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.

2 Pre merger conditions

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

3 Merger simulate

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

3.1 Fixed point iteration

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

3.2 manual iteration

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

3.3 For loop

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

4 Stata comparison

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