mergeron 2024.739127.0__py3-none-any.whl → 2024.739139.0__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/__init__.py +8 -9
- mergeron/core/ftc_merger_investigations_data.py +2 -2
- mergeron/core/guidelines_boundaries.py +24 -24
- mergeron/demo/visualize_empirical_margin_distribution.py +2 -3
- mergeron/gen/__init__.py +14 -15
- mergeron/gen/data_generation.py +4 -4
- mergeron/gen/data_generation_functions.py +5 -5
- mergeron/gen/upp_tests.py +6 -4
- {mergeron-2024.739127.0.dist-info → mergeron-2024.739139.0.dist-info}/METADATA +8 -8
- {mergeron-2024.739127.0.dist-info → mergeron-2024.739139.0.dist-info}/RECORD +11 -11
- {mergeron-2024.739127.0.dist-info → mergeron-2024.739139.0.dist-info}/WHEEL +0 -0
mergeron/__init__.py
CHANGED
|
@@ -2,14 +2,13 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import enum
|
|
4
4
|
from pathlib import Path
|
|
5
|
-
from typing import TypeAlias
|
|
6
5
|
|
|
7
6
|
import numpy as np
|
|
8
7
|
from numpy.typing import NDArray
|
|
9
8
|
|
|
10
9
|
_PKG_NAME: str = Path(__file__).parent.stem
|
|
11
10
|
|
|
12
|
-
VERSION = "2024.
|
|
11
|
+
VERSION = "2024.739139.0"
|
|
13
12
|
|
|
14
13
|
__version__ = VERSION
|
|
15
14
|
|
|
@@ -25,21 +24,21 @@ if not DATA_DIR.is_dir():
|
|
|
25
24
|
np.set_printoptions(precision=18)
|
|
26
25
|
|
|
27
26
|
|
|
28
|
-
ArrayINT = NDArray[np.intp]
|
|
29
|
-
ArrayFloat = NDArray[np.half | np.single | np.double]
|
|
27
|
+
type ArrayINT = NDArray[np.intp]
|
|
28
|
+
type ArrayFloat = NDArray[np.half | np.single | np.double]
|
|
30
29
|
|
|
31
30
|
|
|
32
|
-
ArrayBoolean
|
|
31
|
+
type ArrayBoolean = NDArray[np.bool_]
|
|
33
32
|
|
|
34
|
-
ArrayDouble
|
|
35
|
-
ArrayBIGINT
|
|
33
|
+
type ArrayDouble = NDArray[np.double]
|
|
34
|
+
type ArrayBIGINT = NDArray[np.int64]
|
|
36
35
|
|
|
37
36
|
DEFAULT_REC_RATE = 0.85
|
|
38
37
|
|
|
39
38
|
|
|
40
39
|
@enum.unique
|
|
41
40
|
class RECForm(enum.StrEnum):
|
|
42
|
-
"""
|
|
41
|
+
"""For derivation of recapture ratio from market shares."""
|
|
43
42
|
|
|
44
43
|
INOUT = "inside-out"
|
|
45
44
|
OUTIN = "outside-in"
|
|
@@ -49,7 +48,7 @@ class RECForm(enum.StrEnum):
|
|
|
49
48
|
@enum.unique
|
|
50
49
|
class UPPAggrSelector(enum.StrEnum):
|
|
51
50
|
"""
|
|
52
|
-
Aggregator
|
|
51
|
+
Aggregator for GUPPI and diversion ratio estimates.
|
|
53
52
|
|
|
54
53
|
"""
|
|
55
54
|
|
|
@@ -14,7 +14,7 @@ from importlib import resources
|
|
|
14
14
|
from operator import itemgetter
|
|
15
15
|
from pathlib import Path
|
|
16
16
|
from types import MappingProxyType
|
|
17
|
-
from typing import Any, NamedTuple
|
|
17
|
+
from typing import Any, NamedTuple
|
|
18
18
|
|
|
19
19
|
import msgpack # type: ignore
|
|
20
20
|
import msgpack_numpy as m # type: ignore
|
|
@@ -95,7 +95,7 @@ class INVTableData(NamedTuple):
|
|
|
95
95
|
data_array: ArrayBIGINT
|
|
96
96
|
|
|
97
97
|
|
|
98
|
-
INVData
|
|
98
|
+
type INVData = Mapping[str, Mapping[str, Mapping[str, INVTableData]]]
|
|
99
99
|
|
|
100
100
|
|
|
101
101
|
def construct_data(
|
|
@@ -7,7 +7,7 @@ with a canvas on which to draw boundaries for Guidelines standards.
|
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
9
|
from dataclasses import dataclass
|
|
10
|
-
from typing import Literal
|
|
10
|
+
from typing import Literal
|
|
11
11
|
|
|
12
12
|
import numpy as np
|
|
13
13
|
from attrs import Attribute, field, frozen, validators
|
|
@@ -28,7 +28,7 @@ __version__ = VERSION
|
|
|
28
28
|
mp.prec = 80
|
|
29
29
|
mp.trap_complex = True
|
|
30
30
|
|
|
31
|
-
HMGPubYear
|
|
31
|
+
type HMGPubYear = Literal[1992, 2004, 2010, 2023]
|
|
32
32
|
|
|
33
33
|
|
|
34
34
|
@dataclass(frozen=True)
|
|
@@ -47,7 +47,7 @@ class GuidelinesThresholds:
|
|
|
47
47
|
"""
|
|
48
48
|
Guidelines threholds by Guidelines publication year
|
|
49
49
|
|
|
50
|
-
ΔHHI, Recapture
|
|
50
|
+
ΔHHI, Recapture Ratio, GUPPI, Diversion ratio, CMCR, and IPR thresholds
|
|
51
51
|
constructed from concentration standards in Guidelines published in
|
|
52
52
|
1992, 2004, 2010, and 2023.
|
|
53
53
|
|
|
@@ -70,7 +70,7 @@ class GuidelinesThresholds:
|
|
|
70
70
|
"""
|
|
71
71
|
Negative presumption quantified on various measures
|
|
72
72
|
|
|
73
|
-
ΔHHI safeharbor bound, default recapture
|
|
73
|
+
ΔHHI safeharbor bound, default recapture ratio, GUPPI bound,
|
|
74
74
|
diversion ratio limit, CMCR, and IPR
|
|
75
75
|
"""
|
|
76
76
|
|
|
@@ -78,7 +78,7 @@ class GuidelinesThresholds:
|
|
|
78
78
|
"""
|
|
79
79
|
Presumption of harm defined in HMG
|
|
80
80
|
|
|
81
|
-
ΔHHI bound and corresponding default recapture
|
|
81
|
+
ΔHHI bound and corresponding default recapture ratio, GUPPI bound,
|
|
82
82
|
diversion ratio limit, CMCR, and IPR
|
|
83
83
|
"""
|
|
84
84
|
|
|
@@ -87,7 +87,7 @@ class GuidelinesThresholds:
|
|
|
87
87
|
Presumption of harm imputed from guidelines
|
|
88
88
|
|
|
89
89
|
ΔHHI bound inferred from strict numbers-equivalent
|
|
90
|
-
of (post-merger) HHI presumption, and corresponding default recapture
|
|
90
|
+
of (post-merger) HHI presumption, and corresponding default recapture ratio,
|
|
91
91
|
GUPPI bound, diversion ratio limit, CMCR, and IPR
|
|
92
92
|
"""
|
|
93
93
|
|
|
@@ -233,11 +233,11 @@ def _rec_spec_validator(
|
|
|
233
233
|
_value: RECForm,
|
|
234
234
|
/,
|
|
235
235
|
) -> None:
|
|
236
|
-
if _value == RECForm.OUTIN and _instance.
|
|
236
|
+
if _value == RECForm.OUTIN and _instance.recapture_ratio:
|
|
237
237
|
raise ValueError(
|
|
238
238
|
f"Invalid recapture specification, {_value!r}. "
|
|
239
239
|
"You may consider specifying `mergeron.RECForm.INOUT` here, and "
|
|
240
|
-
'assigning the default recapture
|
|
240
|
+
'assigning the default recapture ratio as attribute, "recapture_ratio" of '
|
|
241
241
|
"this `DiversionRatioBoundarySpec` object."
|
|
242
242
|
)
|
|
243
243
|
if _value is None and _instance.agg_method != UPPAggrSelector.MAX:
|
|
@@ -251,7 +251,7 @@ class DiversionRatioBoundary:
|
|
|
251
251
|
"""
|
|
252
252
|
Diversion ratio specification, boundary coordinates, and area under boundary.
|
|
253
253
|
|
|
254
|
-
Along with the default diversion ratio and recapture
|
|
254
|
+
Along with the default diversion ratio and recapture ratio,
|
|
255
255
|
a diversion ratio boundary specification includes the recapture form --
|
|
256
256
|
whether fixed for both merging firms' products ("proportional") or
|
|
257
257
|
consistent with share-proportionality, i.e., "inside-out";
|
|
@@ -267,7 +267,7 @@ class DiversionRatioBoundary:
|
|
|
267
267
|
validator=(validators.instance_of(float), _divr_value_validator),
|
|
268
268
|
)
|
|
269
269
|
|
|
270
|
-
|
|
270
|
+
recapture_ratio: float = field(
|
|
271
271
|
kw_only=False, default=DEFAULT_REC_RATE, validator=validators.instance_of(float)
|
|
272
272
|
)
|
|
273
273
|
|
|
@@ -277,13 +277,13 @@ class DiversionRatioBoundary:
|
|
|
277
277
|
validator=(validators.instance_of((type(None), RECForm)), _rec_spec_validator),
|
|
278
278
|
)
|
|
279
279
|
"""
|
|
280
|
-
The form of the recapture
|
|
280
|
+
The form of the recapture ratio.
|
|
281
281
|
|
|
282
|
-
When :attr:`mergeron.RECForm.INOUT`, the recapture
|
|
282
|
+
When :attr:`mergeron.RECForm.INOUT`, the recapture ratio for
|
|
283
283
|
he product having the smaller market-share is assumed to equal the default,
|
|
284
|
-
and the recapture
|
|
285
|
-
computed assuming MNL demand. Fixed recapture
|
|
286
|
-
:attr:`mergeron.RECForm.FIXED`. (To specify that recapture
|
|
284
|
+
and the recapture ratio for the product with the larger market-share is
|
|
285
|
+
computed assuming MNL demand. Fixed recapture ratios are specified as
|
|
286
|
+
:attr:`mergeron.RECForm.FIXED`. (To specify that recapture ratios be
|
|
287
287
|
constructed from the generated purchase-probabilities for products in
|
|
288
288
|
the market and for the outside good, specify :attr:`mergeron.RECForm.OUTIN`.)
|
|
289
289
|
|
|
@@ -331,7 +331,7 @@ class DiversionRatioBoundary:
|
|
|
331
331
|
|
|
332
332
|
def __attrs_post_init__(self, /) -> None:
|
|
333
333
|
_share_ratio = critical_share_ratio(
|
|
334
|
-
self.diversion_ratio, r_bar=self.
|
|
334
|
+
self.diversion_ratio, r_bar=self.recapture_ratio
|
|
335
335
|
)
|
|
336
336
|
_upp_agg_kwargs: gbfn.ShareRatioBoundaryKeywords = {
|
|
337
337
|
"recapture_form": getattr(self.recapture_form, "value", "inside-out"),
|
|
@@ -370,7 +370,7 @@ class DiversionRatioBoundary:
|
|
|
370
370
|
|
|
371
371
|
_upp_agg_kwargs |= {"agg_method": _aggregator, "weighting": _wgt_type}
|
|
372
372
|
|
|
373
|
-
_boundary = _upp_agg_fn(_share_ratio, self.
|
|
373
|
+
_boundary = _upp_agg_fn(_share_ratio, self.recapture_ratio, **_upp_agg_kwargs)
|
|
374
374
|
object.__setattr__(self, "coordinates", _boundary.coordinates)
|
|
375
375
|
object.__setattr__(self, "area", _boundary.area)
|
|
376
376
|
|
|
@@ -392,11 +392,11 @@ def guppi_from_delta(
|
|
|
392
392
|
m_star
|
|
393
393
|
Parametric price-cost margin.
|
|
394
394
|
r_bar
|
|
395
|
-
Default recapture
|
|
395
|
+
Default recapture ratio.
|
|
396
396
|
|
|
397
397
|
Returns
|
|
398
398
|
-------
|
|
399
|
-
GUPPI bound corresponding to ∆HHI bound, at given margin and recapture
|
|
399
|
+
GUPPI bound corresponding to ∆HHI bound, at given margin and recapture ratio.
|
|
400
400
|
|
|
401
401
|
"""
|
|
402
402
|
return gbfn.round_cust(
|
|
@@ -424,12 +424,12 @@ def critical_share_ratio(
|
|
|
424
424
|
m_star
|
|
425
425
|
Parametric price-cost margin.
|
|
426
426
|
r_bar
|
|
427
|
-
Default recapture
|
|
427
|
+
Default recapture ratio.
|
|
428
428
|
|
|
429
429
|
Returns
|
|
430
430
|
-------
|
|
431
431
|
Critical share ratio (share ratio bound) corresponding to the GUPPI bound
|
|
432
|
-
for given margin and recapture
|
|
432
|
+
for given margin and recapture ratio.
|
|
433
433
|
|
|
434
434
|
"""
|
|
435
435
|
return gbfn.round_cust(
|
|
@@ -445,7 +445,7 @@ def share_from_guppi(
|
|
|
445
445
|
r_bar: float = DEFAULT_REC_RATE,
|
|
446
446
|
) -> float:
|
|
447
447
|
"""
|
|
448
|
-
Symmetric-firm share for given GUPPI, margin, and recapture
|
|
448
|
+
Symmetric-firm share for given GUPPI, margin, and recapture ratio.
|
|
449
449
|
|
|
450
450
|
Parameters
|
|
451
451
|
----------
|
|
@@ -454,13 +454,13 @@ def share_from_guppi(
|
|
|
454
454
|
m_star
|
|
455
455
|
Parametric price-cost margin.
|
|
456
456
|
r_bar
|
|
457
|
-
Default recapture
|
|
457
|
+
Default recapture ratio.
|
|
458
458
|
|
|
459
459
|
Returns
|
|
460
460
|
-------
|
|
461
461
|
float
|
|
462
462
|
Symmetric firm market share on GUPPI boundary, for given margin and
|
|
463
|
-
recapture
|
|
463
|
+
recapture ratio.
|
|
464
464
|
|
|
465
465
|
"""
|
|
466
466
|
|
|
@@ -48,10 +48,9 @@ with warnings.catch_warnings():
|
|
|
48
48
|
mgn_kde = stats.gaussian_kde(mgn_data_obs, weights=mgn_data_wts, bw_method="silverman")
|
|
49
49
|
mgn_kde.set_bandwidth(bw_method=mgn_kde.factor / 3.0)
|
|
50
50
|
|
|
51
|
-
mgn_xvec = np.linspace(0, BIN_COUNT, 10**5) / BIN_COUNT
|
|
52
51
|
mgn_ax.plot(
|
|
53
|
-
|
|
54
|
-
mgn_kde(
|
|
52
|
+
(_xv := np.linspace(0, BIN_COUNT, 10**5) / BIN_COUNT),
|
|
53
|
+
mgn_kde(_xv),
|
|
55
54
|
color="#004488",
|
|
56
55
|
rasterized=True,
|
|
57
56
|
label="Estimated Density",
|
mergeron/gen/__init__.py
CHANGED
|
@@ -105,15 +105,14 @@ class ShareSpec:
|
|
|
105
105
|
|
|
106
106
|
Notes
|
|
107
107
|
-----
|
|
108
|
-
If :attr:`mergeron.gen.ShareSpec.dist_type
|
|
108
|
+
If :attr:`mergeron.gen.ShareSpec.dist_type` == :attr:`mergeron.gen.SHRDistribution.UNI`,
|
|
109
109
|
then it is infeasible that
|
|
110
|
-
:attr:`mergeron.gen.ShareSpec.recapture_form
|
|
111
|
-
In other words, if firm-counts
|
|
112
|
-
estimated using outside
|
|
110
|
+
:attr:`mergeron.gen.ShareSpec.recapture_form` == :attr:`mergeron.RECForm.OUTIN`.
|
|
111
|
+
In other words, if the distribution of markets over firm-counts is unspecified,
|
|
112
|
+
recapture ratios cannot be estimated using outside-good choice probabilities.
|
|
113
113
|
|
|
114
|
-
For a sample with explicit firm counts, market shares must
|
|
115
|
-
|
|
116
|
-
(see :class:`mergeron.gen.SHRDistribution`).
|
|
114
|
+
For a sample with explicit firm counts, market shares must be specified as
|
|
115
|
+
having a supported Dirichlet distribution (see :class:`mergeron.gen.SHRDistribution`).
|
|
117
116
|
|
|
118
117
|
"""
|
|
119
118
|
|
|
@@ -161,19 +160,19 @@ class ShareSpec:
|
|
|
161
160
|
def _check_rf(_i: ShareSpec, _a: Attribute[RECForm], _v: RECForm) -> None:
|
|
162
161
|
if _v == RECForm.OUTIN and _i.dist_type == SHRDistribution.UNI:
|
|
163
162
|
raise ValueError(
|
|
164
|
-
"Market share specification requires estimation of recapture
|
|
165
|
-
"generated data. Either delete recapture
|
|
163
|
+
"Market share specification requires estimation of recapture ratio from "
|
|
164
|
+
"generated data. Either delete recapture ratio specification or set it to None."
|
|
166
165
|
)
|
|
167
166
|
|
|
168
|
-
|
|
167
|
+
recapture_ratio: float | None = field(default=DEFAULT_REC_RATE)
|
|
169
168
|
"""A value between 0 and 1.
|
|
170
169
|
|
|
171
170
|
:code:`None` if market share specification requires direct generation of
|
|
172
171
|
outside good choice probabilities (:attr:`mergeron.RECForm.OUTIN`).
|
|
173
172
|
|
|
174
|
-
The recapture
|
|
173
|
+
The recapture ratio is usually calibrated to the numbers-equivalent of the
|
|
175
174
|
HHI threshold for the presumtion of harm from unilateral competitive effects
|
|
176
|
-
in published merger guidelines. Accordingly, the recapture
|
|
175
|
+
in published merger guidelines. Accordingly, the recapture ratio rounded to
|
|
177
176
|
the nearest 5% is:
|
|
178
177
|
|
|
179
178
|
* 0.85, **7-to-6 merger from symmetry**; US Guidelines, 1992, 2023
|
|
@@ -188,14 +187,14 @@ class ShareSpec:
|
|
|
188
187
|
|
|
189
188
|
"""
|
|
190
189
|
|
|
191
|
-
@
|
|
190
|
+
@recapture_ratio.validator
|
|
192
191
|
def _check_rr(_i: ShareSpec, _a: Attribute[float], _v: float) -> None:
|
|
193
192
|
if _v and not (0 < _v <= 1):
|
|
194
|
-
raise ValueError("Recapture
|
|
193
|
+
raise ValueError("Recapture ratio must lie in the interval, [0, 1).")
|
|
195
194
|
elif _v is None and _i.recapture_form != RECForm.OUTIN:
|
|
196
195
|
raise ValueError(
|
|
197
196
|
f"Recapture specification, {_i.recapture_form!r} requires that "
|
|
198
|
-
"the market sample specification inclues a recapture
|
|
197
|
+
"the market sample specification inclues a recapture ratio in the "
|
|
199
198
|
"interval [0, 1)."
|
|
200
199
|
)
|
|
201
200
|
|
mergeron/gen/data_generation.py
CHANGED
|
@@ -134,7 +134,7 @@ class MarketSample:
|
|
|
134
134
|
"""
|
|
135
135
|
|
|
136
136
|
_recapture_form = self.share_spec.recapture_form
|
|
137
|
-
|
|
137
|
+
_recapture_ratio = self.share_spec.recapture_ratio
|
|
138
138
|
_dist_type_mktshr = self.share_spec.dist_type
|
|
139
139
|
_dist_firm2_pcm = self.pcm_spec.firm2_pcm_constraint
|
|
140
140
|
_hsr_filing_test_type = self.hsr_filing_test_type
|
|
@@ -208,7 +208,7 @@ class MarketSample:
|
|
|
208
208
|
# Calculate diversion ratios
|
|
209
209
|
_divr_array = gen_divr_array(
|
|
210
210
|
_recapture_form,
|
|
211
|
-
|
|
211
|
+
_recapture_ratio,
|
|
212
212
|
_mktshr_array[:, :2],
|
|
213
213
|
_aggregate_purchase_prob,
|
|
214
214
|
)
|
|
@@ -410,11 +410,11 @@ class MarketSample:
|
|
|
410
410
|
|
|
411
411
|
if (
|
|
412
412
|
self.share_spec.recapture_form != RECForm.OUTIN
|
|
413
|
-
and self.share_spec.
|
|
413
|
+
and self.share_spec.recapture_ratio != _enf_parm_vec.rec
|
|
414
414
|
):
|
|
415
415
|
raise ValueError(
|
|
416
416
|
"{} {} {}".format(
|
|
417
|
-
f"Recapture
|
|
417
|
+
f"Recapture ratio from market sample spec, {self.share_spec.recapture_ratio}",
|
|
418
418
|
f"must match the value, {_enf_parm_vec.rec}",
|
|
419
419
|
"the guidelines thresholds vector.",
|
|
420
420
|
)
|
|
@@ -106,7 +106,7 @@ def gen_share_data(
|
|
|
106
106
|
|
|
107
107
|
# If recapture_form == "inside-out", recalculate _aggregate_purchase_prob
|
|
108
108
|
_frmshr_array = _mkt_share_sample.mktshr_array[:, :2]
|
|
109
|
-
_r_bar = _share_spec.
|
|
109
|
+
_r_bar = _share_spec.recapture_ratio or DEFAULT_REC_RATE
|
|
110
110
|
if _recapture_form == RECForm.INOUT:
|
|
111
111
|
_mkt_share_sample = ShareDataSample(
|
|
112
112
|
_mkt_share_sample.mktshr_array,
|
|
@@ -409,7 +409,7 @@ def gen_market_shares_dirichlet(
|
|
|
409
409
|
|
|
410
410
|
def gen_divr_array(
|
|
411
411
|
_recapture_form: RECForm,
|
|
412
|
-
|
|
412
|
+
_recapture_ratio: float | None,
|
|
413
413
|
_frmshr_array: ArrayDouble,
|
|
414
414
|
_aggregate_purchase_prob: ArrayDouble = EMPTY_ARRAY_DEFAULT,
|
|
415
415
|
/,
|
|
@@ -425,8 +425,8 @@ def gen_divr_array(
|
|
|
425
425
|
_recapture_form
|
|
426
426
|
Enum specifying Fixed (proportional), Inside-out, or Outside-in
|
|
427
427
|
|
|
428
|
-
|
|
429
|
-
If recapture is proportional or inside-out, the recapture
|
|
428
|
+
_recapture_ratio
|
|
429
|
+
If recapture is proportional or inside-out, the recapture ratio
|
|
430
430
|
for the firm with the smaller share.
|
|
431
431
|
|
|
432
432
|
_frmshr_array
|
|
@@ -450,7 +450,7 @@ def gen_divr_array(
|
|
|
450
450
|
|
|
451
451
|
_divr_array: ArrayDouble
|
|
452
452
|
if _recapture_form == RECForm.FIXED:
|
|
453
|
-
_divr_array =
|
|
453
|
+
_divr_array = _recapture_ratio * _frmshr_array[:, ::-1] / (1 - _frmshr_array) # type: ignore
|
|
454
454
|
|
|
455
455
|
else:
|
|
456
456
|
_purchprob_array = _aggregate_purchase_prob * _frmshr_array
|
mergeron/gen/upp_tests.py
CHANGED
|
@@ -7,11 +7,12 @@ from generated market data.
|
|
|
7
7
|
from collections.abc import Sequence
|
|
8
8
|
from contextlib import suppress
|
|
9
9
|
from pathlib import Path
|
|
10
|
-
from typing import
|
|
10
|
+
from typing import Any, Literal, TypedDict
|
|
11
11
|
|
|
12
12
|
import numpy as np
|
|
13
13
|
import tables as ptb # type: ignore
|
|
14
14
|
from numpy.random import SeedSequence
|
|
15
|
+
from numpy.typing import NDArray
|
|
15
16
|
|
|
16
17
|
from .. import ( # noqa
|
|
17
18
|
VERSION,
|
|
@@ -40,7 +41,7 @@ __version__ = VERSION
|
|
|
40
41
|
ptb.parameters.MAX_NUMEXPR_THREADS = 8
|
|
41
42
|
ptb.parameters.MAX_BLOSC_THREADS = 4
|
|
42
43
|
|
|
43
|
-
SaveData
|
|
44
|
+
type SaveData = Literal[False] | tuple[Literal[True], ptb.File, ptb.Group]
|
|
44
45
|
|
|
45
46
|
|
|
46
47
|
class INVRESCntsArgs(TypedDict, total=False):
|
|
@@ -213,7 +214,8 @@ def compute_upp_test_arrays(
|
|
|
213
214
|
|
|
214
215
|
"""
|
|
215
216
|
_g_bar, _divr_bar, _cmcr_bar, _ipr_bar = (
|
|
216
|
-
getattr(_upp_test_parms, _f) for _f in ("guppi", "divr", "cmcr", "ipr")
|
|
217
|
+
getattr(_upp_test_parms, _f) for _f in ("guppi", "divr", "cmcr", "ipr")
|
|
218
|
+
)
|
|
217
219
|
|
|
218
220
|
_guppi_array, _ipr_array, _cmcr_array = (
|
|
219
221
|
np.empty_like(_market_data.price_array) for _ in range(3)
|
|
@@ -366,7 +368,7 @@ def save_data_to_hdf5(
|
|
|
366
368
|
|
|
367
369
|
|
|
368
370
|
def save_array_to_hdf5(
|
|
369
|
-
_array_obj:
|
|
371
|
+
_array_obj: NDArray[Any],
|
|
370
372
|
_array_name: str,
|
|
371
373
|
_h5_group: ptb.Group,
|
|
372
374
|
_h5_file: ptb.File,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: mergeron
|
|
3
|
-
Version: 2024.
|
|
3
|
+
Version: 2024.739139.0
|
|
4
4
|
Summary: Merger Policy Analysis using Python
|
|
5
5
|
License: MIT
|
|
6
6
|
Keywords: merger policy analysis,merger guidelines,merger screening,policy presumptions,concentration standards,upward pricing pressure,GUPPI
|
|
@@ -41,23 +41,23 @@ Description-Content-Type: text/x-rst
|
|
|
41
41
|
mergeron: Merger Policy Analysis using Python
|
|
42
42
|
=============================================
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
Visualize the sets of mergers conforming to concentration and diversion-ratio standards. Estimate intrinsic enforcement rates, and intrinsic clearance rates, under concentration, diversion ratio, GUPPI, CMCR, and IPR bounds using generated data with specified distributions of market shares, price-cost margins, firm counts, and prices, optionally imposing restrictions implied by statutory filing thresholds and/or Bertrand-Nash oligopoly with MNL demand. Download and analyze merger investigations data published by the U.S. Federal Trade Commission in various reports on extended merger investigations (Second Requests) during 1996 to 2011.
|
|
45
45
|
|
|
46
46
|
Here, enforcement rates derived with merger enforcement as being exogenous to firm conduct are defined as intrinsic enforcement rates, and similarly intrinsic clearance rates. Depending on the merger enforcement regime, or merger control regime, intrinsic enforcement rates may also not be the complement of intrinsic clearance rates, i.e, it is not necessarily true that the intrinsic clearance rate estimate for a given enforcement regime is 1 minus the intrinsic enforcement rate. In contrast, observed enforcement rates reflect the deterrent effects of merger enforcement on firm conduct as well as the effects of merger screening on the level of enforcement; and, by definition, the observed clearance rate is 1 minus the observed enforcement rate.
|
|
47
47
|
|
|
48
48
|
Introduction
|
|
49
49
|
------------
|
|
50
50
|
|
|
51
|
-
Module :code:`.core.guidelines_boundaries` includes classes for specifying concentration bounds (:code:`.core.guidelines_boundaries.ConcentrationBoundary`) and diversion-ratio bounds (:code:`.core.guidelines_boundaries.DiversionRatioBoundary`), with automatic generation of boundary
|
|
51
|
+
Module :code:`.core.guidelines_boundaries` includes classes for specifying concentration bounds (:code:`.core.guidelines_boundaries.ConcentrationBoundary`) and diversion-ratio bounds (:code:`.core.guidelines_boundaries.DiversionRatioBoundary`), with automatic generation of boundary, as an array of share-pairs, and area. This module also includes a function for generating plots of concentration and diversion-ratio boundaries, and functions for mapping GUPPI standards to concentration (ΔHHI) standards, and vice-versa.
|
|
52
52
|
|
|
53
|
-
Module :code:`.gen.data_generation` includes the :code:`.gen.data_generation.MarketSample` which provides for a rich specification of shares and diversion ratios (:code:`.gen.data_generation.MarketSample.share_spec`), margins (:code:`.gen.data_generation.MarketSample.pcm_spec`, prices (:code:`.gen.data_generation.MarketSample.price_spec`), and HSR filing requirements (:code:`.gen.data_generation.MarketSample.hsr_filing_test_type`), and with methods for, (i) generating sample data (:code:`.gen.data_generation.MarketSample.generate_sample`), and (ii)
|
|
53
|
+
Module :code:`.gen.data_generation` includes the :code:`.gen.data_generation.MarketSample` which provides for a rich specification of shares and diversion ratios (:code:`.gen.data_generation.MarketSample.share_spec`), margins (:code:`.gen.data_generation.MarketSample.pcm_spec`, prices (:code:`.gen.data_generation.MarketSample.price_spec`), and HSR filing requirements (:code:`.gen.data_generation.MarketSample.hsr_filing_test_type`), and with methods for, (i) generating sample data (:code:`.gen.data_generation.MarketSample.generate_sample`), and (ii) computing the intrinsic enforcement rate and intrinsic clearance rate for the generated sample, given a method (:code:`.UPPAggrSelector`) of aggregating diversion ratio or GUPPI estimates for the firms in a merger (:code:`.gen.data_generation.MarketSample.estimate_enf_counts`). While the latter populate the properties, :code:`.gen.data_generation.MarketSample.data`
|
|
54
54
|
and :code:`.gen.data_generation.MarketSample.enf_counts`, respectively, the underlying methods for generating standalone :code:`MarketDataSample` and :code:`UPPTestCounts` objects are included in the class definition, with helper functions defined in the modules, :code:`.gen.data_generation_functions` and :code:`.gen.upp_tests`. Notably, market shares are generated for a sample of markets with firm-count distributed as specified in :code:`.gen.data_generation.MarketSample.share_spec.firm_count_weights`, with defaults as discussed below (also see, :code:`.gen.ShareSpec.firm_count_weights`.
|
|
55
55
|
|
|
56
56
|
By default, merging-firm shares are drawn with uniform distribution over the space :math:`s_1 + s_2 \leqslant 1` for an unspecified number of firms. Alternatively, shares may be drawn from the Dirichlet distribution (see property `dist_type` of :code:`.gen.data_generation.MarketSample.share_spec`, of type, :code:`.gen.SHRDistribution`), with specified shape parameters (property `dist_parms` of :code:`.gen.data_generation.MarketSample.share_spec`. When drawing shares from the Dirichlet distribution, the user specifies the `firm_count_weights` property of :code:`.gen.data_generation.MarketSample.share_spec`, as a vector of weights specifying the frequency distribution over sequential firm counts, e.g., :code:`[133, 184, 134, 52, 32, 10, 12, 4, 3]` to specify shares drawn from Dirichlet distributions with 2 to 10 pre-merger firms distributed as in data for FTC merger investigations during 1996--2003 (See, for example, Table 4.1 of `FTC, Horizontal Merger Investigations Data, Fiscal Years 1996--2003 (Revised: August 31, 2004) <https://www.ftc.gov/sites/default/files/documents/reports/horizontal-merger-investigation-data-fiscal-years-1996-2003/040831horizmergersdata96-03.pdf>`_). If the property `firm_count_weights` is not explicitly assigned a value when defining :code:`.gen.data_generation.MarketSample.share_spec`, the default values is used, which results in a sample of markets with 2 to 7 firms with relative frequency in inverse proportion to firm-count, with 2-firm markets being 6 times as likely to be drawn as 7-firm markets.
|
|
57
57
|
|
|
58
|
-
Recapture
|
|
58
|
+
Recapture ratios can be specified as, "proportional", "inside-out", or "outside-in" (see :code:`.RECForm`). The "inside-out" specification (assigning :code:`.RECForm.INOUT` to the `recapture_form` property of :code:`.gen.data_generation.MarketSample.share_spec`) results in recapture ratios consistent with MNL demand, given merging-firms' in-market shares and a default recapture ratio. The "outside-in" specification (assigning :code:`.RECForm.INOUT` to the `recapture_form` property of :code:`.gen.data_generation.MarketSample.share_spec`) yields diversion ratios from purchase probabilities drawn at random for :math:`N+1` goods, with market shares and recapture ratios for the :math:`N` goods in the putative market (see, :code:`.gen.ShareSpec`) computed from the simulated choice probabilities. The "outside-in" specification requires specification of the distribution of markets over firm counts (the default being uniform distirbution over markets with 2 to 7 firms pre-merger), and Dirichlet-distributed shares, with optional parameters (the default being a "flat" Dirichlet distribution, i.e., one with all parameters being 1). The parameters of the Dirichlet distribution can, for example, be specified to increase (decrease) the probability of drawing mergers to monopoly relative to that probability associated with the Flat Dirichlet specification, by setting the first 2 specified parameters at higher (lower) values relative to the others. Lastly, the "proportional" form of recapture ratio (`recapture_form` = :code:`.RECForm.FIXED`) is often used in the literature, as an approximation to the "inside-out" calibration. See, for example, Coate (2011).
|
|
59
59
|
|
|
60
|
-
Price-cost-margins may be specified as having uniform distribution, Beta distribution (including a bounded Beta distribution with specified mean and variance), or
|
|
60
|
+
Price-cost-margins may be specified as having uniform distribution, Beta distribution (including a bounded Beta distribution with specified mean and variance), or a built-in empirical distribution (see, :code:`.gen.PCMSpec`). The in-built empirical margin distribution is based on resampling margin data published by Prof. Damodaran of NYU Stern School of Business (see Notes), using an estimated Gaussian KDE. The second merging firm's margin (per the property `firm2_pcm_constraint` of :code:`.gen.data_generation.MarketSample.pcm_spec`) may be specified as symmetric, i.i.d., or subject to equilibrium conditions for (profit-maximization in) Bertrand-Nash oligopoly with MNL demand (:code:`.gen.FM2Constraint`).
|
|
61
61
|
|
|
62
62
|
Prices may be specified as symmetric or asymmetric, and in the latter case, the direction of correlation between merging firm prices, if any, can also be specified (see, :code:`.gen.PriceSpec`). Prices may also be defined by imposing cost symmetry on firms in the sample, with fixed unit marginal costs normalized to 1 unit, such that prices equal :math:`1 / (1 - \pmb{m})`, where :math:`\pmb{m}` represents the array of margins for firms in the sample.
|
|
63
63
|
|
|
@@ -65,11 +65,11 @@ The market sample may be restricted to mergers meeting the HSR filing requiremen
|
|
|
65
65
|
|
|
66
66
|
The full specification of a market sample is given in a :code:`.gen.data_generation.MarketSample` object, including the above parameters. Data are drawn by invoking :code:`.gen.data_generation.MarketSample.generate_sample` which adds a :code:`data` property of class, :code:`.gen.MarketDataSample`. Enforcement or clearance counts are computed by invoking :code:`.gen.data_generation.MarketSample.estimate_enf_counts`, which adds an :code:`enf_counts` property of class :code:`.gen.UPPTestsCounts`. For fast, parallel generation of enforcement or clearance counts over large market data samples that ordinarily would exceed available limits on machine memory, the user can invoke the method :code:`.gen.data_generation.MarketSample.estimate_enf_counts` on a :code:`.gen.data_generation.MarketSample` object without first invoking :code:`.gen.data_generation.MarketSample.generate_sample`. Note, however, that this strategy does not retain the market sample in memory in the interests of conserving memory and maintaining high performance (the user can specify that the market sample and enforcement statistics be stored to permanent storage; when saving to current PCIe NVMe storage, the performance penalty is slight, but can be considerable if saving to SATA storage).
|
|
67
67
|
|
|
68
|
-
Enforcement statistics based on FTC investigations data and test data are
|
|
68
|
+
Enforcement statistics based on FTC investigations data and test data are tabulated using methods provided in :code:`.gen.enforcement_stats`.
|
|
69
69
|
|
|
70
70
|
Programs demonstrating the use of this package are included in the sub-package, :code:`.demo`.
|
|
71
71
|
|
|
72
|
-
This package includes a class, :code:`.core.pseudorandom_numbers.MultithreadedRNG` for generating random numbers with selected continuous distribution over specified parameters, and with CPU multithreading on machines with multiple virtual, logical, or physical
|
|
72
|
+
This package includes a class, :code:`.core.pseudorandom_numbers.MultithreadedRNG` for generating random numbers with selected continuous distribution over specified parameters, and with CPU multithreading on machines with multiple CPU cores, be they virtual, logical, or physical cores. This class is an adaptation from the documentation for the external :code:`numpy.random` subpackage, from the discussion on, "`Multithreaded generation <https://numpy.org/doc/stable/reference/random/multithreading.html>`_"; the version included here permits selection of the distribution with pre-tests to catch and inform on common errors. To access these directly:
|
|
73
73
|
|
|
74
74
|
.. code-block:: python
|
|
75
75
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
mergeron/License.txt,sha256=7iX-y0EyjkbVJKJLS4ZKzuuE1wd0lryfsD_IytLG8lQ,1246
|
|
2
|
-
mergeron/__init__.py,sha256=
|
|
2
|
+
mergeron/__init__.py,sha256=hiOhxFInENJRReecosK422vBUnZGrOEutNkWfEs7JOs,1460
|
|
3
3
|
mergeron/core/__init__.py,sha256=KtjBlZOl7jwBCAUhrTJB9PdrN39YLYytNiSUSM_gRmA,62
|
|
4
4
|
mergeron/core/damodaran_margin_data.py,sha256=rMrgN1Qtw572a0ftY97OOj4otq8ldlLrcOi-bcE-org,8554
|
|
5
|
-
mergeron/core/ftc_merger_investigations_data.py,sha256=
|
|
6
|
-
mergeron/core/guidelines_boundaries.py,sha256=
|
|
5
|
+
mergeron/core/ftc_merger_investigations_data.py,sha256=eldNU4hX9oKE4Rb08YE9_1LgolvNKZnhOXW6KyWSwnM,28622
|
|
6
|
+
mergeron/core/guidelines_boundaries.py,sha256=7C5hMa8vijDZbLdrSxO3XHpsXF5e7PZFpI-abDKqt90,15616
|
|
7
7
|
mergeron/core/guidelines_boundary_functions.py,sha256=GGn5mwBWmxkqcat4Ya0D-J6-7ujosgCCK3eJ9RFWASI,29749
|
|
8
8
|
mergeron/core/guidelines_boundary_functions_extra.py,sha256=HDwwKZDWlrj3Tw-I0gHm0TCSDcIyb9jDfwbuDvK55B8,11322
|
|
9
9
|
mergeron/core/pseudorandom_numbers.py,sha256=cJEWDTfy9CUTzR_di6Fm1Vl1Le6xWoU8wFHbYVMEuLI,9225
|
|
@@ -12,13 +12,13 @@ mergeron/data/damodaran_margin_data.xls,sha256=Qggl1p5nkOMJI8YUXhkwXQRz-OhRSqBTz
|
|
|
12
12
|
mergeron/data/damodaran_margin_data_dict.msgpack,sha256=sr6s4L69kposEpzGI7jpPb4ULz0UpY-bEYfeNi6UlRA,57621
|
|
13
13
|
mergeron/data/ftc_invdata.msgpack,sha256=WBFHgi7Ld4R-h2zL2Zc3TOIlKqVrbVFMH1LoI4-T-M0,264664
|
|
14
14
|
mergeron/demo/__init__.py,sha256=KtjBlZOl7jwBCAUhrTJB9PdrN39YLYytNiSUSM_gRmA,62
|
|
15
|
-
mergeron/demo/visualize_empirical_margin_distribution.py,sha256=
|
|
16
|
-
mergeron/gen/__init__.py,sha256=
|
|
17
|
-
mergeron/gen/data_generation.py,sha256=
|
|
18
|
-
mergeron/gen/data_generation_functions.py,sha256=
|
|
15
|
+
mergeron/demo/visualize_empirical_margin_distribution.py,sha256=R-sGC87kVovWBqcM5U6GiNC9oLsbNaMTJgljv8ts8w0,2347
|
|
16
|
+
mergeron/gen/__init__.py,sha256=DU6uFbRwe6ng-2sKCmpymD90CDNmyelWvUnmtNu8iHM,16593
|
|
17
|
+
mergeron/gen/data_generation.py,sha256=czTZcVZnT5kWViSFdSdbo0kupq1tUKGrKH5RThXbDwk,16803
|
|
18
|
+
mergeron/gen/data_generation_functions.py,sha256=Ag6u1yZ93ikGuKhj7PWbJ2lCZHxNGTemDfhK8p5xFq0,28576
|
|
19
19
|
mergeron/gen/enforcement_stats.py,sha256=ZjrV_VkFMF0D1myc-fj-W99M1EhJMA9-nCfyE5g9e54,10890
|
|
20
|
-
mergeron/gen/upp_tests.py,sha256=
|
|
20
|
+
mergeron/gen/upp_tests.py,sha256=uRF4RrBo3amwQQSu661Xa50xKGMUxtnM3zRtYy3nyB0,12581
|
|
21
21
|
mergeron/py.typed,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
|
|
22
|
-
mergeron-2024.
|
|
23
|
-
mergeron-2024.
|
|
24
|
-
mergeron-2024.
|
|
22
|
+
mergeron-2024.739139.0.dist-info/METADATA,sha256=NrSRBKtvNhW9KFKRB4gW_YK39BmhFKiA8DNtz-nZQ7g,14430
|
|
23
|
+
mergeron-2024.739139.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
24
|
+
mergeron-2024.739139.0.dist-info/RECORD,,
|
|
File without changes
|