mergeron 2024.738953.0__py3-none-any.whl → 2024.738953.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of mergeron might be problematic. Click here for more details.
- {mergeron-2024.738953.0.dist-info → mergeron-2024.738953.1.dist-info}/METADATA +1 -1
- {mergeron-2024.738953.0.dist-info → mergeron-2024.738953.1.dist-info}/RECORD +3 -17
- mergeron/examples/__init__.py +0 -5
- mergeron/examples/concentration_as_diversion.py +0 -625
- mergeron/examples/enforcement_boundaries_for_mergers_with_asymmetric_shares.py +0 -493
- mergeron/examples/enforcement_boundaries_for_symmetric_firm_mergers.py +0 -206
- mergeron/examples/example_parameterizations.py +0 -143
- mergeron/examples/guidelines_enforcement_patterns.py +0 -74
- mergeron/examples/investigations_stats_obs_tables.py +0 -481
- mergeron/examples/investigations_stats_sim_tables.py +0 -425
- mergeron/examples/plotSafeHarbs_symbolically.py +0 -53
- mergeron/examples/sound_guppi_safeharbor.py +0 -181
- mergeron/examples/summarize_ftc_investigations_data.py +0 -44
- mergeron/examples/testIntrinsicClearanceRates.py +0 -135
- mergeron/examples/visualize_empirical_margin_distribution.py +0 -100
- mergeron/examples/visualize_guidelines_tests.py +0 -298
- {mergeron-2024.738953.0.dist-info → mergeron-2024.738953.1.dist-info}/WHEEL +0 -0
|
@@ -1,493 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
|
|
3
|
-
Draw boundaries for various standards from U.S. Horizontal Merger Guidelines.
|
|
4
|
-
"""
|
|
5
|
-
|
|
6
|
-
from itertools import product as iterprod
|
|
7
|
-
from pathlib import Path
|
|
8
|
-
from typing import Literal, TypedDict
|
|
9
|
-
|
|
10
|
-
import matplotlib.axis
|
|
11
|
-
from matplotlib import cm as colormgr
|
|
12
|
-
from matplotlib import colormaps
|
|
13
|
-
from matplotlib import colors as mcolors
|
|
14
|
-
from matplotlib.ticker import StrMethodFormatter
|
|
15
|
-
from numpy import arange, arctan, array, hsplit, insert, rad2deg, round, sqrt, vstack
|
|
16
|
-
|
|
17
|
-
import mergeron.core.guidelines_boundaries as gbl
|
|
18
|
-
from mergeron import DATA_DIR, UPPAggrSelector
|
|
19
|
-
from mergeron.core import UPPBoundarySpec
|
|
20
|
-
|
|
21
|
-
PROG_PATH = Path(__file__)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
class _CMAPArgs(TypedDict):
|
|
25
|
-
cmap: mcolors.Colormap
|
|
26
|
-
norm: mcolors.Normalize
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
_color_kwargs = _CMAPArgs(cmap=colormaps["cividis"], norm=mcolors.Normalize(0, 1.0))
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
def plot_delta_boundaries(
|
|
33
|
-
_guppi_bench_key: str,
|
|
34
|
-
_print_guppi_max_bndry_envs_flag: bool,
|
|
35
|
-
_recapture_spec: Literal["inside-out", "proportional"],
|
|
36
|
-
_color_kwargs: _CMAPArgs = _color_kwargs,
|
|
37
|
-
/,
|
|
38
|
-
) -> None:
|
|
39
|
-
_print_guppi_max_bndry_envs_flag = _print_guppi_max_bndry_envs_flag or False
|
|
40
|
-
|
|
41
|
-
print("ΔHHI safeharbor boundary")
|
|
42
|
-
_plt, _my_fig1, _ax1, _ = gbl.boundary_plot()
|
|
43
|
-
|
|
44
|
-
_hmg_thresholds = get_hmg_thresholds_by_key(_guppi_bench_key)
|
|
45
|
-
_dh_bar, _r_bar, _guppi_bench, _divr_bench = (
|
|
46
|
-
getattr(_hmg_thresholds, _f) for _f in ("delta", "rec", "guppi", "divr")
|
|
47
|
-
)
|
|
48
|
-
|
|
49
|
-
print("Contour map of selected ∆HHI boundaries")
|
|
50
|
-
|
|
51
|
-
for _dh_bound in 100, 200, 300, 500, 800, 1200, 2500:
|
|
52
|
-
if _dh_bound in (300, 500):
|
|
53
|
-
continue
|
|
54
|
-
|
|
55
|
-
_dh_boundary = gbl.delta_hhi_boundary(_dh_bound / 1e4)
|
|
56
|
-
_dh_dat_x, _dh_dat_y = (_z.T[0] for _z in hsplit(_dh_boundary.coordinates, 2))
|
|
57
|
-
if _dh_bound == 100:
|
|
58
|
-
_lwval, _lsval = 0.75, "-"
|
|
59
|
-
else:
|
|
60
|
-
_lwval, _lsval = 0.5, ":"
|
|
61
|
-
|
|
62
|
-
_ax1.plot(
|
|
63
|
-
_dh_dat_x,
|
|
64
|
-
_dh_dat_y,
|
|
65
|
-
linewidth=_lwval,
|
|
66
|
-
linestyle=_lsval,
|
|
67
|
-
color="black",
|
|
68
|
-
zorder=3,
|
|
69
|
-
)
|
|
70
|
-
|
|
71
|
-
if _print_guppi_max_bndry_envs_flag:
|
|
72
|
-
_symshr = gbl.round_cust(sqrt(_dh_bound / 2e4))
|
|
73
|
-
_dstar = gbl.round_cust(_symshr / (1 - _symshr))
|
|
74
|
-
_m_star = _guppi_bench / (_dstar * _r_bar)
|
|
75
|
-
print(_symshr, _dstar, _m_star, _r_bar, _guppi_bench, "...", end="")
|
|
76
|
-
|
|
77
|
-
_guppi_bdry_env_xs = (0, _symshr, 1)
|
|
78
|
-
if _recapture_spec == "inside-out":
|
|
79
|
-
# ## Plot envelope of GUPPI boundaries with
|
|
80
|
-
# r_k = r_bar if s_k = min(_s_1, _s_2)
|
|
81
|
-
# ## See (s_i, s_j) in equation~(44), or thereabouts, in paper
|
|
82
|
-
_smin_nr = _dstar * (1 - _r_bar)
|
|
83
|
-
_smax_nr = 1 - _dstar * _r_bar
|
|
84
|
-
_guppi_bdry_env_dr = _smin_nr + _smax_nr
|
|
85
|
-
_guppi_bdry_env_xs = ( # type: ignore
|
|
86
|
-
0,
|
|
87
|
-
_smin_nr / _guppi_bdry_env_dr,
|
|
88
|
-
_symshr,
|
|
89
|
-
_smax_nr / _guppi_bdry_env_dr,
|
|
90
|
-
1,
|
|
91
|
-
)
|
|
92
|
-
del _guppi_bdry_env_dr
|
|
93
|
-
print(_guppi_bdry_env_xs[1], _guppi_bdry_env_xs[-2], end="")
|
|
94
|
-
print()
|
|
95
|
-
|
|
96
|
-
_ax1.plot(
|
|
97
|
-
_guppi_bdry_env_xs,
|
|
98
|
-
_guppi_bdry_env_xs[::-1],
|
|
99
|
-
linewidth=0.5,
|
|
100
|
-
linestyle="--",
|
|
101
|
-
color=_color_kwargs["cmap"](_m_star),
|
|
102
|
-
zorder=3,
|
|
103
|
-
)
|
|
104
|
-
del _symshr, _dstar, _m_star
|
|
105
|
-
|
|
106
|
-
if _recapture_spec == "inside-out" and _dh_bound not in (200, 300, 500):
|
|
107
|
-
_ax1.annotate(
|
|
108
|
-
rf"$\Delta HHI$ = {_dh_bound:,d} pts.",
|
|
109
|
-
xy=(_dh_dat_x[1], _dh_dat_y[1]),
|
|
110
|
-
xytext=(_dh_dat_x[1], _dh_dat_y[1]),
|
|
111
|
-
textcoords="data",
|
|
112
|
-
ha="left",
|
|
113
|
-
va="center",
|
|
114
|
-
fontsize=5,
|
|
115
|
-
zorder=5.1,
|
|
116
|
-
)
|
|
117
|
-
del _guppi_bdry_env_xs
|
|
118
|
-
|
|
119
|
-
if _dh_bound == 100:
|
|
120
|
-
_ax1.fill_between(
|
|
121
|
-
_dh_dat_x,
|
|
122
|
-
_dh_dat_y,
|
|
123
|
-
0,
|
|
124
|
-
edgecolor=None,
|
|
125
|
-
facecolor="#64bb64",
|
|
126
|
-
alpha=0.7,
|
|
127
|
-
rasterized=True,
|
|
128
|
-
)
|
|
129
|
-
del (_dh_boundary, _dh_dat_x, _dh_dat_y) # , _dh_dat_x_pla, _dh_dat_y_pla
|
|
130
|
-
|
|
131
|
-
_my_fig1.savefig((DATA_DIR / f"{PROG_PATH.stem}_DH100_deltaHHI_only.pdf"), dpi=600)
|
|
132
|
-
|
|
133
|
-
del _plt, _my_fig1, _ax1
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
def plot_guppi_boundaries( # noqa PLR0915
|
|
137
|
-
_guppi_bench_key: str,
|
|
138
|
-
_pcm_same_flag: bool,
|
|
139
|
-
_recapture_spec: Literal["proportional", "inside-out"],
|
|
140
|
-
_color_kwargs: _CMAPArgs = _color_kwargs,
|
|
141
|
-
/,
|
|
142
|
-
) -> None:
|
|
143
|
-
if recapture_spec not in (_recspecs := ("inside-out", "proportional")):
|
|
144
|
-
raise ValueError(f"Recapture specification must be one of, {_recspecs!r}")
|
|
145
|
-
|
|
146
|
-
_hmg_thresholds = get_hmg_thresholds_by_key(_guppi_bench_key)
|
|
147
|
-
_dh_bar, _r_bar, _guppi_bench, _divr_bench = (
|
|
148
|
-
getattr(_hmg_thresholds, _f) for _f in ("delta", "rec", "guppi", "divr")
|
|
149
|
-
)
|
|
150
|
-
|
|
151
|
-
# First we get the data for the ΔHHI benchmark we want to plot
|
|
152
|
-
_dh_boundary = gbl.delta_hhi_boundary(_dh_bar)
|
|
153
|
-
_dh_dat_x, _dh_dat_y = (_z.T[0] for _z in hsplit(_dh_boundary.coordinates, 2))
|
|
154
|
-
|
|
155
|
-
_plt, _, _, _set_axis_def = gbl.boundary_plot()
|
|
156
|
-
|
|
157
|
-
_my_fig1 = _plt.figure(figsize=(5.5, 5.0))
|
|
158
|
-
|
|
159
|
-
_fig1_grid = _my_fig1.add_gridspec(
|
|
160
|
-
nrows=1,
|
|
161
|
-
ncols=2,
|
|
162
|
-
figure=_my_fig1,
|
|
163
|
-
width_ratios=[5, 0.5],
|
|
164
|
-
height_ratios=[1.0],
|
|
165
|
-
wspace=0.0,
|
|
166
|
-
)
|
|
167
|
-
_ax1 = _my_fig1.add_subplot(_fig1_grid[0, 0])
|
|
168
|
-
_ax1 = _set_axis_def(_ax1, mktshares_plot_flag=True, mktshares_axlbls_flag=True)
|
|
169
|
-
|
|
170
|
-
_ax1.plot(_dh_dat_x, _dh_dat_y, linewidth=0.75, color="black", zorder=3)
|
|
171
|
-
_ax1.fill_between(
|
|
172
|
-
_dh_dat_x,
|
|
173
|
-
_dh_dat_y,
|
|
174
|
-
0,
|
|
175
|
-
edgecolor=None,
|
|
176
|
-
facecolor="#64bb64",
|
|
177
|
-
alpha=0.7,
|
|
178
|
-
rasterized=True,
|
|
179
|
-
)
|
|
180
|
-
|
|
181
|
-
# GUPPI boundary-of-boundaries for symmetric-firm mergers
|
|
182
|
-
_step_size = 10**-5
|
|
183
|
-
_m_lim = _guppi_bench / _r_bar
|
|
184
|
-
_mst_vec = arange(_m_lim, 1.00 + _step_size, _step_size)
|
|
185
|
-
_sym_shr_vec = (_dst_vec := _m_lim / _mst_vec) / (1 + _dst_vec)
|
|
186
|
-
# _sym_shr_vec = np.arange(gbl.shr_from_gbd(g_bar), 0.5 + step_size, step_size)
|
|
187
|
-
# _mst_vec = (g_bar / r_bar) * (1 - sym_shr_vec) / sym_shr_vec
|
|
188
|
-
# https://stackoverflow.com/questions/39753282/
|
|
189
|
-
_ax1.scatter(
|
|
190
|
-
_sym_shr_vec,
|
|
191
|
-
_sym_shr_vec,
|
|
192
|
-
label=r"$\overline{g}$ = 6%; $\overline{d}$ = 20%",
|
|
193
|
-
marker=",",
|
|
194
|
-
s=(0.25 * 72.0 / _my_fig1.dpi) ** 2,
|
|
195
|
-
edgecolor=None,
|
|
196
|
-
c=_mst_vec,
|
|
197
|
-
**_color_kwargs,
|
|
198
|
-
rasterized=True,
|
|
199
|
-
zorder=3,
|
|
200
|
-
)
|
|
201
|
-
|
|
202
|
-
for _m_star in arange(1.00, _m_lim - 0.10, -0.10):
|
|
203
|
-
_m_star, _delta_star, _s_mid = (
|
|
204
|
-
round(_s, 4)
|
|
205
|
-
for _s in (_m_star, _m_lim / _m_star, _m_lim / (_m_star + _m_lim))
|
|
206
|
-
)
|
|
207
|
-
|
|
208
|
-
_s2_vals = (_delta_star, _s_mid, 0.0)
|
|
209
|
-
if _pcm_same_flag:
|
|
210
|
-
_s1_vals = _s2_vals[::-1]
|
|
211
|
-
_ax1.plot(
|
|
212
|
-
_s1_vals,
|
|
213
|
-
_s2_vals,
|
|
214
|
-
linestyle="--",
|
|
215
|
-
linewidth=0.75,
|
|
216
|
-
color=_color_kwargs["cmap"](_m_star),
|
|
217
|
-
zorder=3,
|
|
218
|
-
)
|
|
219
|
-
else:
|
|
220
|
-
_s1_vals = (0.0, _s_mid, 1.0)
|
|
221
|
-
if _recapture_spec == "inside-out":
|
|
222
|
-
_s_1_i2, _s_2_i2 = array([
|
|
223
|
-
(_x := _guppi_bench - _m_star),
|
|
224
|
-
(_y := _guppi_bench - _m_lim),
|
|
225
|
-
]) / (_x + _y)
|
|
226
|
-
del _x, _y
|
|
227
|
-
|
|
228
|
-
_s1_vals = insert(_s1_vals, [-1], [_s_1_i2])
|
|
229
|
-
_s2_vals = insert(_s2_vals, [-1], [_s_2_i2])
|
|
230
|
-
|
|
231
|
-
# Print Outer boundary, color-coded m_2
|
|
232
|
-
_ax1.plot(
|
|
233
|
-
_s1_vals,
|
|
234
|
-
_s2_vals,
|
|
235
|
-
linestyle="--",
|
|
236
|
-
linewidth=0.5,
|
|
237
|
-
color=colormaps["cividis"](_m_star),
|
|
238
|
-
zorder=3,
|
|
239
|
-
)
|
|
240
|
-
# Print inner boundary, color-coded m_1
|
|
241
|
-
_ax1.plot(
|
|
242
|
-
_s2_vals[::-1],
|
|
243
|
-
_s1_vals[::-1],
|
|
244
|
-
linestyle="--",
|
|
245
|
-
linewidth=0.5,
|
|
246
|
-
color=colormaps["cividis"](_m_star),
|
|
247
|
-
zorder=3,
|
|
248
|
-
)
|
|
249
|
-
|
|
250
|
-
# skip labeling a few
|
|
251
|
-
if _m_star >= 0.70:
|
|
252
|
-
continue
|
|
253
|
-
|
|
254
|
-
# Place boundary labels along each boundary-line segment
|
|
255
|
-
_bndry_grad = grad_est(_ax1, (0, _s_mid), (_delta_star, _s_mid))
|
|
256
|
-
_bndry_angle = rad2deg(arctan(_bndry_grad))
|
|
257
|
-
|
|
258
|
-
_x_shft_pts = 0.001
|
|
259
|
-
_y_shft_pts = 0.015
|
|
260
|
-
|
|
261
|
-
_m1_lbl_str, _m2_lbl_str = (
|
|
262
|
-
r"${0} = {1:3.{2}f}\%$".format(
|
|
263
|
-
("m^*" if _pcm_same_flag else f"m_{_k}"),
|
|
264
|
-
(_pcmv := _m_star * 100),
|
|
265
|
-
2 * int(_pcmv % 1 > 0),
|
|
266
|
-
)
|
|
267
|
-
for _k in (1, 2)
|
|
268
|
-
)
|
|
269
|
-
_ax1.annotate(
|
|
270
|
-
_m2_lbl_str,
|
|
271
|
-
xy=(0, _delta_star),
|
|
272
|
-
xytext=(0 + _x_shft_pts, _delta_star + _y_shft_pts),
|
|
273
|
-
textcoords="data",
|
|
274
|
-
rotation=_bndry_angle,
|
|
275
|
-
ha="left",
|
|
276
|
-
va="top",
|
|
277
|
-
fontsize=4,
|
|
278
|
-
zorder=5.1,
|
|
279
|
-
)
|
|
280
|
-
_ax1.annotate(
|
|
281
|
-
_m1_lbl_str,
|
|
282
|
-
xy=(_delta_star, 0),
|
|
283
|
-
xytext=(0 + _x_shft_pts, _delta_star + _y_shft_pts)[::-1],
|
|
284
|
-
textcoords="data",
|
|
285
|
-
rotation=-90 - _bndry_angle,
|
|
286
|
-
ha="right",
|
|
287
|
-
va="bottom",
|
|
288
|
-
fontsize=4,
|
|
289
|
-
zorder=5.1,
|
|
290
|
-
)
|
|
291
|
-
|
|
292
|
-
# Examples of hypothetical combinations
|
|
293
|
-
if not _pcm_same_flag:
|
|
294
|
-
if _guppi_bench_key == "DH50":
|
|
295
|
-
_xco, _yco, _lval = zip(
|
|
296
|
-
*([0.15, 0.10, "A"], [0.27, 0.15, "B"]), strict=True
|
|
297
|
-
)
|
|
298
|
-
else:
|
|
299
|
-
_xco, _yco, _lval = zip(
|
|
300
|
-
*([0.20, 0.135, "A"], [0.27, 0.15, "B"]), strict=True
|
|
301
|
-
)
|
|
302
|
-
|
|
303
|
-
_ax1.scatter(_xco, _yco, s=1, c="black", zorder=4.5)
|
|
304
|
-
for lid, lvl in enumerate(_lval):
|
|
305
|
-
_ax1.annotate(
|
|
306
|
-
f"${lvl}$",
|
|
307
|
-
xy=(_xco[lid], _yco[lid]),
|
|
308
|
-
xytext=(_xco[lid] - 0.025, _yco[lid] - 0.01),
|
|
309
|
-
fontsize=8,
|
|
310
|
-
zorder=4.6,
|
|
311
|
-
)
|
|
312
|
-
|
|
313
|
-
# print("Diversion ratio bound")
|
|
314
|
-
if _divr_bench < _r_bar:
|
|
315
|
-
_m_star_bench = _guppi_bench / _divr_bench
|
|
316
|
-
_s_mid_bench = gbl.shr_from_gbd(
|
|
317
|
-
_guppi_bench, m_star=_m_star_bench, r_bar=_r_bar
|
|
318
|
-
)
|
|
319
|
-
_delta_star = gbl.critical_shrratio(
|
|
320
|
-
_guppi_bench, m_star=_m_star_bench, r_bar=_r_bar
|
|
321
|
-
)
|
|
322
|
-
guppi_boundary = gbl.shrratio_boundary(
|
|
323
|
-
UPPBoundarySpec(_delta_star, _r_bar, agg_method=UPPAggrSelector.MAX)
|
|
324
|
-
)
|
|
325
|
-
_x_drt, _y_drt = zip(*guppi_boundary.coordinates, strict=True)
|
|
326
|
-
|
|
327
|
-
_ax1.plot(
|
|
328
|
-
_x_drt,
|
|
329
|
-
_y_drt,
|
|
330
|
-
linestyle="-",
|
|
331
|
-
linewidth=0.75,
|
|
332
|
-
color=_color_kwargs["cmap"](_m_star_bench),
|
|
333
|
-
zorder=4,
|
|
334
|
-
)
|
|
335
|
-
|
|
336
|
-
_drt_grad = grad_est(_ax1, _x_drt[:2], _y_drt[:2])
|
|
337
|
-
_drt_angle = rad2deg(arctan(_drt_grad))
|
|
338
|
-
|
|
339
|
-
_dl_xshift = 0.015 if _divr_bench < 0.20 else -0.01
|
|
340
|
-
_dl_yshift = 0.0075 if _divr_bench < 0.20 else 0.005
|
|
341
|
-
|
|
342
|
-
_dr_labelstr = R"$\symbf{{\overline{{d}} = {0:3.{1}f}\%}}$".format(
|
|
343
|
-
(_dbch := _divr_bench * 100), 1 * (_dbch % 1 > 0)
|
|
344
|
-
)
|
|
345
|
-
|
|
346
|
-
_ax1.annotate(
|
|
347
|
-
_dr_labelstr,
|
|
348
|
-
xy=(_s_mid_bench, _s_mid_bench),
|
|
349
|
-
xytext=(_s_mid_bench + _dl_xshift, _s_mid_bench + _dl_yshift),
|
|
350
|
-
rotation=_drt_angle,
|
|
351
|
-
ha="right",
|
|
352
|
-
va="bottom",
|
|
353
|
-
fontsize=5,
|
|
354
|
-
zorder=4.1,
|
|
355
|
-
)
|
|
356
|
-
_ax1.annotate(
|
|
357
|
-
_dr_labelstr,
|
|
358
|
-
xy=(_s_mid_bench, _s_mid_bench),
|
|
359
|
-
xytext=(_s_mid_bench + _dl_xshift, _s_mid_bench + _dl_yshift)[::-1],
|
|
360
|
-
textcoords="data",
|
|
361
|
-
rotation=-90 - _drt_angle,
|
|
362
|
-
ha="left",
|
|
363
|
-
va="top",
|
|
364
|
-
fontsize=5,
|
|
365
|
-
zorder=4.1,
|
|
366
|
-
)
|
|
367
|
-
|
|
368
|
-
del _x_drt, _y_drt, _drt_grad, _drt_angle
|
|
369
|
-
|
|
370
|
-
# Drop in a point for the Farrell/Shapiro example
|
|
371
|
-
# Generate the points and error bars
|
|
372
|
-
_ex_divratio = 1.0
|
|
373
|
-
_ex_shr_from_divratio = round(_ex_divratio / (1 + _ex_divratio), 3)
|
|
374
|
-
_ebar_array = vstack([_ex_shr_from_divratio, 0.0 * _ex_shr_from_divratio])
|
|
375
|
-
_ebar_plot = _ax1.errorbar(
|
|
376
|
-
_ex_shr_from_divratio,
|
|
377
|
-
_ex_shr_from_divratio,
|
|
378
|
-
xerr=_ebar_array,
|
|
379
|
-
yerr=_ebar_array,
|
|
380
|
-
fmt=".",
|
|
381
|
-
mfc=_color_kwargs["cmap"](_guppi_bench / _r_bar),
|
|
382
|
-
mec="None",
|
|
383
|
-
alpha=0.9,
|
|
384
|
-
zorder=5,
|
|
385
|
-
)
|
|
386
|
-
# Set linestyle for errorbars
|
|
387
|
-
# https://stackoverflow.com/questions/22995797/
|
|
388
|
-
for _ix in range(2):
|
|
389
|
-
_ebar_plot[-1][_ix].set(
|
|
390
|
-
color=_color_kwargs["cmap"](_guppi_bench / _r_bar),
|
|
391
|
-
linestyle="--",
|
|
392
|
-
linewidth=0.5,
|
|
393
|
-
alpha=0.9,
|
|
394
|
-
zorder=2,
|
|
395
|
-
)
|
|
396
|
-
|
|
397
|
-
# Annotate the point
|
|
398
|
-
_ax1.annotate(
|
|
399
|
-
rf"$d={_r_bar * 100:3.0f}\%$",
|
|
400
|
-
xy=(_ex_shr_from_divratio, _ex_shr_from_divratio),
|
|
401
|
-
xytext=(_ex_shr_from_divratio + 0.01, _ex_shr_from_divratio - 0.008),
|
|
402
|
-
fontsize=5,
|
|
403
|
-
zorder=5.1,
|
|
404
|
-
)
|
|
405
|
-
|
|
406
|
-
# Colorbar
|
|
407
|
-
ax1_cb = _my_fig1.add_subplot(_fig1_grid[0, 1], frameon=False)
|
|
408
|
-
ax1_cb.axis("off")
|
|
409
|
-
_cm_plot = _my_fig1.colorbar(
|
|
410
|
-
colormgr.ScalarMappable(**_color_kwargs),
|
|
411
|
-
use_gridspec=True,
|
|
412
|
-
ax=ax1_cb,
|
|
413
|
-
orientation="vertical",
|
|
414
|
-
fraction=0.75,
|
|
415
|
-
ticks=arange(0, 1.5, 0.1),
|
|
416
|
-
format=StrMethodFormatter("{x:>3.0%}"),
|
|
417
|
-
pad=0.0,
|
|
418
|
-
)
|
|
419
|
-
_cm_plot.set_label(label="Price-Cost Margin", fontsize=8)
|
|
420
|
-
_cm_plot.ax.tick_params(length=5, width=0.5, labelsize=6)
|
|
421
|
-
_cm_plot.ax.set_ylim(0, 1.0)
|
|
422
|
-
_cm_plot.outline.set_visible(False)
|
|
423
|
-
|
|
424
|
-
_fig1_savename = "_".join((
|
|
425
|
-
f"{PROG_PATH.stem}",
|
|
426
|
-
f"pcmSame{_pcm_same_flag}",
|
|
427
|
-
f"{_guppi_bench_key}",
|
|
428
|
-
f"rbar{_recapture_spec.upper()}",
|
|
429
|
-
))
|
|
430
|
-
_my_fig1.savefig((DATA_DIR / f"{_fig1_savename}.pdf"), dpi=600)
|
|
431
|
-
del _my_fig1, _ax1, _ebar_plot, _fig1_savename
|
|
432
|
-
del _dh_dat_x, _dh_dat_y
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
def grad_est(
|
|
436
|
-
_ax: matplotlib.axis.Axis, _pt_xs: tuple[float, ...], _pt_ys: tuple[float, ...]
|
|
437
|
-
) -> float:
|
|
438
|
-
if (_pt_len := max(len(_pt_xs), len(_pt_ys))) > 2:
|
|
439
|
-
raise ValueError(
|
|
440
|
-
"Expecting only 2 points for calculation of line-gradient; got {_pt_len}."
|
|
441
|
-
)
|
|
442
|
-
_pt1, _pt2 = (
|
|
443
|
-
_ax.transData.transform_point((_pt_xs[_i], _pt_ys[_i])) # type: ignore
|
|
444
|
-
for _i in range(2)
|
|
445
|
-
)
|
|
446
|
-
_grad: float = (_pt2[1] - _pt1[1]) / (_pt2[0] - _pt1[0])
|
|
447
|
-
return _grad
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
def get_hmg_thresholds_by_key(_guppi_bench_key: str, /) -> gbl.HMGThresholds:
|
|
451
|
-
match _guppi_bench_key:
|
|
452
|
-
case "DOJATR":
|
|
453
|
-
return gbl.HMGThresholds(
|
|
454
|
-
(_tmp := gbl.GuidelinesThresholds(2010).safeharbor).delta,
|
|
455
|
-
_tmp.rec,
|
|
456
|
-
0.05,
|
|
457
|
-
_tmp.divr,
|
|
458
|
-
_tmp.cmcr,
|
|
459
|
-
_tmp.ipr,
|
|
460
|
-
)
|
|
461
|
-
case "DH100":
|
|
462
|
-
return gbl.GuidelinesThresholds(2010).safeharbor
|
|
463
|
-
case "DH50":
|
|
464
|
-
return gbl.GuidelinesThresholds(1992).safeharbor
|
|
465
|
-
case _:
|
|
466
|
-
raise ValueError(
|
|
467
|
-
f"GUPPI benchmark key must be one of, {guppi_benchmark_keys!r}"
|
|
468
|
-
)
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
if __name__ == "__main__":
|
|
472
|
-
print("Define parameters for GUPPI safeharbor plots")
|
|
473
|
-
|
|
474
|
-
guppi_benchmark_keys = ("DH50", "DH100", "DOJATR")
|
|
475
|
-
|
|
476
|
-
print("Plot countour-maps of ∆HHI boundaries and GUPPI safeharbor boundaries")
|
|
477
|
-
|
|
478
|
-
pcm_same_flag: bool = True
|
|
479
|
-
recapture_spec: Literal["inside-out", "proportional"] = "inside-out"
|
|
480
|
-
plot_delta_boundaries("DH100", pcm_same_flag, recapture_spec, _color_kwargs)
|
|
481
|
-
|
|
482
|
-
print("GUPPI safeharbor boundaries, by precentage price-cost margin")
|
|
483
|
-
for pcm_same_flag, recapture_spec in iterprod(
|
|
484
|
-
(False, True), ("inside-out", "proportional")
|
|
485
|
-
):
|
|
486
|
-
if pcm_same_flag and recapture_spec == "inside-out":
|
|
487
|
-
continue
|
|
488
|
-
|
|
489
|
-
print(f"Symmetric margins: {pcm_same_flag!r}; recapture: '{recapture_spec}'")
|
|
490
|
-
for guppi_bench_key in guppi_benchmark_keys:
|
|
491
|
-
plot_guppi_boundaries(
|
|
492
|
-
guppi_bench_key, pcm_same_flag, recapture_spec, _color_kwargs
|
|
493
|
-
)
|
|
@@ -1,206 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Draw GUPPI safe harbor boundary for symmetric firm mergers
|
|
3
|
-
|
|
4
|
-
This boundary is limited to share-margin space, given restrictions from symmetry
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
from pathlib import Path
|
|
8
|
-
|
|
9
|
-
import matplotlib as mpl
|
|
10
|
-
import numpy as np
|
|
11
|
-
from matplotlib import cm as colormgr
|
|
12
|
-
from matplotlib import colors as mcolors
|
|
13
|
-
from matplotlib.ticker import AutoMinorLocator, MultipleLocator, StrMethodFormatter
|
|
14
|
-
|
|
15
|
-
import mergeron.core.guidelines_boundaries as gbl
|
|
16
|
-
from mergeron import DATA_DIR
|
|
17
|
-
|
|
18
|
-
PROG_PATH = Path(__file__)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
def _main() -> None:
|
|
22
|
-
plt, _, _, set_axis_def = gbl.boundary_plot()
|
|
23
|
-
# plt.delaxes(ax1)
|
|
24
|
-
# del my_fig1, ax1
|
|
25
|
-
# del ax1
|
|
26
|
-
|
|
27
|
-
pcm_colornorm = mcolors.Normalize(0, 1.0)
|
|
28
|
-
cmap_kwargs = {"cmap": "cividis", "norm": pcm_colornorm}
|
|
29
|
-
|
|
30
|
-
my_fig1 = plt.figure(figsize=(5.5, 5.0), dpi=600)
|
|
31
|
-
|
|
32
|
-
fig1_grid = my_fig1.add_gridspec(
|
|
33
|
-
nrows=1,
|
|
34
|
-
ncols=2,
|
|
35
|
-
figure=my_fig1,
|
|
36
|
-
width_ratios=[5, 0.5],
|
|
37
|
-
height_ratios=[1.0],
|
|
38
|
-
wspace=0.0,
|
|
39
|
-
)
|
|
40
|
-
ax1 = my_fig1.add_subplot(fig1_grid[0, 0])
|
|
41
|
-
ax1 = set_axis_def(ax1, mktshares_plot_flag=False)
|
|
42
|
-
|
|
43
|
-
print("Generate data for plots")
|
|
44
|
-
# Mgn coords, and div-ratio coords
|
|
45
|
-
g_bar, r_bar, dr_bar = 0.06, 0.80, 0.20
|
|
46
|
-
|
|
47
|
-
step_size = 10**-5
|
|
48
|
-
sym_shr_vec = np.arange(gbl.shr_from_gbd(g_bar), 0.5 + step_size, step_size)
|
|
49
|
-
mst_vec = (g_bar / r_bar) * (1 - sym_shr_vec) / sym_shr_vec
|
|
50
|
-
|
|
51
|
-
print("Setup basic figure and axes for plots of safe harbor boundaries.")
|
|
52
|
-
|
|
53
|
-
# Diversion-ratio boundary, in calibrated 6 percent safe-harbor
|
|
54
|
-
ax1.plot(
|
|
55
|
-
[np.sqrt(0.01 / 2) for _ in range(2)],
|
|
56
|
-
[0.0, 1.0],
|
|
57
|
-
label=r"$\Delta HHI = $100 points",
|
|
58
|
-
linestyle="-",
|
|
59
|
-
linewidth=0.5,
|
|
60
|
-
color="black",
|
|
61
|
-
zorder=5,
|
|
62
|
-
)
|
|
63
|
-
ax1.fill_between(
|
|
64
|
-
[0.0, np.sqrt(0.01 / 2)],
|
|
65
|
-
(1.0, 1.0),
|
|
66
|
-
0,
|
|
67
|
-
edgecolor=None,
|
|
68
|
-
facecolor="#64bb64",
|
|
69
|
-
alpha=0.7,
|
|
70
|
-
zorder=2,
|
|
71
|
-
)
|
|
72
|
-
|
|
73
|
-
# Proposed GUPPI safe harbor
|
|
74
|
-
ax1.scatter(
|
|
75
|
-
sym_shr_vec,
|
|
76
|
-
mst_vec,
|
|
77
|
-
label=r"$\overline{g} = $6%; $\overline{d} = $20%",
|
|
78
|
-
marker=",",
|
|
79
|
-
s=(0.25 * 72.0 / my_fig1.dpi) ** 2,
|
|
80
|
-
edgecolor=None,
|
|
81
|
-
c=mst_vec,
|
|
82
|
-
**cmap_kwargs,
|
|
83
|
-
rasterized=True,
|
|
84
|
-
zorder=9,
|
|
85
|
-
)
|
|
86
|
-
ax1.scatter(
|
|
87
|
-
sym_shr_vec,
|
|
88
|
-
0 * mst_vec,
|
|
89
|
-
label=r"Projection, $\overline{g} = $6%; $\overline{d} = $20%",
|
|
90
|
-
marker="o",
|
|
91
|
-
s=(2.0 * 72.0 / my_fig1.dpi) ** 2,
|
|
92
|
-
edgecolor=None,
|
|
93
|
-
c=mst_vec,
|
|
94
|
-
**cmap_kwargs,
|
|
95
|
-
rasterized=True,
|
|
96
|
-
zorder=11,
|
|
97
|
-
)
|
|
98
|
-
|
|
99
|
-
# Drop in a point for the Farrell/Shapiro example
|
|
100
|
-
# Generate the points and error bars
|
|
101
|
-
dr_bar_mgn = np.array([g_bar / d for d in (dr_bar, r_bar)])
|
|
102
|
-
dr_bar_shr = np.round((dst_vec := g_bar / (r_bar * dr_bar_mgn)) / (1 + dst_vec), 3)
|
|
103
|
-
ax1.scatter(
|
|
104
|
-
dr_bar_shr,
|
|
105
|
-
dr_bar_mgn,
|
|
106
|
-
s=(5.0 * 72.0 / my_fig1.dpi) ** 2,
|
|
107
|
-
marker=".",
|
|
108
|
-
edgecolor=None,
|
|
109
|
-
c=dr_bar_mgn,
|
|
110
|
-
**cmap_kwargs,
|
|
111
|
-
)
|
|
112
|
-
ax1.plot(
|
|
113
|
-
[dr_bar_shr[0] for _ in range(2)],
|
|
114
|
-
[0, dr_bar_mgn[0]],
|
|
115
|
-
label=r"$\overline{d} = $20%",
|
|
116
|
-
linestyle="-",
|
|
117
|
-
linewidth=0.75,
|
|
118
|
-
alpha=1,
|
|
119
|
-
color=mpl.colormaps["cividis"](dr_bar_mgn[0]),
|
|
120
|
-
zorder=5.5,
|
|
121
|
-
)
|
|
122
|
-
|
|
123
|
-
# Annotate diversion ratio limit
|
|
124
|
-
ax1.annotate(
|
|
125
|
-
r"$\overline{d}$ = 20%",
|
|
126
|
-
xy=(0.20, 0.30),
|
|
127
|
-
xytext=(0.20, 0.30 - 0.03), # (0.20 + 0.005, 0.30 + 0.005),
|
|
128
|
-
horizontalalignment="right",
|
|
129
|
-
fontsize=8,
|
|
130
|
-
zorder=5,
|
|
131
|
-
)
|
|
132
|
-
# Annotate diversion ratio limit
|
|
133
|
-
ax1.annotate(
|
|
134
|
-
r"$\overline{d}$ = 80%",
|
|
135
|
-
xy=(0.50, 0.075),
|
|
136
|
-
xytext=(0.5, 0.075 - 0.03),
|
|
137
|
-
fontsize=8,
|
|
138
|
-
horizontalalignment="right",
|
|
139
|
-
zorder=5,
|
|
140
|
-
)
|
|
141
|
-
# Annotate \Deltah{} boundary
|
|
142
|
-
ax1.annotate(
|
|
143
|
-
r"$\Delta HHI$ = 100 points",
|
|
144
|
-
xy=(0.07, 0.250),
|
|
145
|
-
xytext=(0.07 + 0.005, 0.40 + 0.01),
|
|
146
|
-
fontsize=8,
|
|
147
|
-
rotation=90,
|
|
148
|
-
zorder=5,
|
|
149
|
-
)
|
|
150
|
-
# Annotate GUPPI boundary
|
|
151
|
-
ax1.annotate(
|
|
152
|
-
r"$\overline{g}$ = 6%",
|
|
153
|
-
xy=(0.10, 0.70),
|
|
154
|
-
xytext=(0.10 + 0.004, 0.70 + 0.01),
|
|
155
|
-
fontsize=8,
|
|
156
|
-
zorder=5,
|
|
157
|
-
)
|
|
158
|
-
|
|
159
|
-
# Axis scale and labels
|
|
160
|
-
# x-axis
|
|
161
|
-
ax1.set_xlim(0.0, 0.5)
|
|
162
|
-
ax1.set_xlabel("Symmetric Firm Share, $s$", fontsize=10)
|
|
163
|
-
ax1.xaxis.set_label_coords(0.75, -0.1)
|
|
164
|
-
# y-axis
|
|
165
|
-
ax1.set_ylim(0.0, 1.0)
|
|
166
|
-
ax1.set_ylabel("Price-Cost Margin, $m^*$", fontsize=10)
|
|
167
|
-
ax1.yaxis.set_label_coords(-0.1, 0.75)
|
|
168
|
-
|
|
169
|
-
_minorLocator = AutoMinorLocator(5)
|
|
170
|
-
_majorLocator = MultipleLocator(0.05)
|
|
171
|
-
for _axs in ax1.xaxis, ax1.yaxis:
|
|
172
|
-
_majorticklabels_rot = 45 if _axs == ax1.xaxis else 0
|
|
173
|
-
# x-axis
|
|
174
|
-
_axs.set_major_locator(_majorLocator)
|
|
175
|
-
_axs.set_minor_locator(_minorLocator)
|
|
176
|
-
_axs.set_major_formatter(StrMethodFormatter("{x:>3.0%}"))
|
|
177
|
-
|
|
178
|
-
plt.setp(_axs.get_majorticklabels(), fontsize=6)
|
|
179
|
-
plt.setp(_axs.get_majorticklabels(), rotation=_majorticklabels_rot)
|
|
180
|
-
|
|
181
|
-
for axl in ax1.get_xticklabels(), ax1.get_yticklabels():
|
|
182
|
-
plt.setp(axl[::2], visible=False)
|
|
183
|
-
|
|
184
|
-
# Colorbar
|
|
185
|
-
ax1_cb = my_fig1.add_subplot(fig1_grid[0, 1], frameon=False)
|
|
186
|
-
ax1_cb.axis("off")
|
|
187
|
-
cm_plot = my_fig1.colorbar(
|
|
188
|
-
colormgr.ScalarMappable(**cmap_kwargs),
|
|
189
|
-
use_gridspec=True,
|
|
190
|
-
ax=ax1_cb,
|
|
191
|
-
orientation="vertical",
|
|
192
|
-
fraction=0.75,
|
|
193
|
-
ticks=np.arange(0, 1.5, 0.1),
|
|
194
|
-
format=StrMethodFormatter("{x:>3.0%}"),
|
|
195
|
-
pad=0.0,
|
|
196
|
-
)
|
|
197
|
-
cm_plot.set_label(label="Price-Cost Margin", fontsize=8)
|
|
198
|
-
cm_plot.ax.tick_params(length=5, width=0.5, labelsize=6)
|
|
199
|
-
cm_plot.ax.set_ylim(0, 1.0)
|
|
200
|
-
cm_plot.outline.set_visible(False)
|
|
201
|
-
|
|
202
|
-
plt.savefig(DATA_DIR / PROG_PATH.with_suffix(".pdf").name)
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
if __name__ == "__main__":
|
|
206
|
-
_main()
|