mergeron 2025.739319.3__py3-none-any.whl → 2025.739341.8__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 +21 -23
- mergeron/core/__init__.py +21 -5
- mergeron/core/empirical_margin_distribution.py +213 -158
- mergeron/core/ftc_merger_investigations_data.py +31 -35
- mergeron/core/guidelines_boundaries.py +27 -20
- mergeron/core/guidelines_boundary_functions.py +22 -32
- mergeron/core/guidelines_boundary_functions_extra.py +15 -30
- mergeron/core/pseudorandom_numbers.py +21 -18
- mergeron/data/__init__.py +13 -11
- mergeron/data/damodaran_margin_data_serialized.zip +0 -0
- mergeron/gen/__init__.py +32 -41
- mergeron/gen/data_generation.py +19 -23
- mergeron/gen/data_generation_functions.py +27 -38
- mergeron/gen/enforcement_stats.py +144 -23
- mergeron/gen/upp_tests.py +4 -9
- mergeron-2025.739341.8.dist-info/METADATA +94 -0
- mergeron-2025.739341.8.dist-info/RECORD +20 -0
- {mergeron-2025.739319.3.dist-info → mergeron-2025.739341.8.dist-info}/WHEEL +1 -1
- mergeron/data/damodaran_margin_data.xls +0 -0
- mergeron/demo/__init__.py +0 -3
- mergeron/demo/visualize_empirical_margin_distribution.py +0 -94
- mergeron-2025.739319.3.dist-info/METADATA +0 -174
- mergeron-2025.739319.3.dist-info/RECORD +0 -22
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Non-public functions called in data_generation.py
|
|
3
|
-
"""
|
|
1
|
+
"""Non-public functions called in data_generation.py."""
|
|
4
2
|
|
|
5
3
|
from __future__ import annotations
|
|
6
4
|
|
|
@@ -28,13 +26,13 @@ from ..core.pseudorandom_numbers import ( # noqa: TID252
|
|
|
28
26
|
from . import (
|
|
29
27
|
DEFAULT_BETA_BND_DIST_PARMS,
|
|
30
28
|
DEFAULT_FCOUNT_WTS,
|
|
31
|
-
|
|
29
|
+
MarginSampleData,
|
|
32
30
|
PCMDistribution,
|
|
33
31
|
PCMRestriction,
|
|
34
32
|
PCMSpec,
|
|
35
|
-
|
|
33
|
+
PriceSampleData,
|
|
36
34
|
PriceSpec,
|
|
37
|
-
|
|
35
|
+
ShareSampleData,
|
|
38
36
|
ShareSpec,
|
|
39
37
|
SHRDistribution,
|
|
40
38
|
SSZConstant,
|
|
@@ -50,8 +48,8 @@ def gen_share_data(
|
|
|
50
48
|
_mktshr_rng_seed_seq: SeedSequence,
|
|
51
49
|
_nthreads: int,
|
|
52
50
|
/,
|
|
53
|
-
) ->
|
|
54
|
-
"""
|
|
51
|
+
) -> ShareSampleData:
|
|
52
|
+
"""Generate share data.
|
|
55
53
|
|
|
56
54
|
Parameters
|
|
57
55
|
----------
|
|
@@ -66,10 +64,9 @@ def gen_share_data(
|
|
|
66
64
|
|
|
67
65
|
Returns
|
|
68
66
|
-------
|
|
69
|
-
Arrays representing shares, diversion ratios, etc. structured as a :
|
|
67
|
+
Arrays representing shares, diversion ratios, etc. structured as a :ShareSampleData:
|
|
70
68
|
|
|
71
69
|
"""
|
|
72
|
-
|
|
73
70
|
dist_type_mktshr, firm_count_prob_wts, dist_parms_mktshr, recapture_form = (
|
|
74
71
|
getattr(_share_spec, _f)
|
|
75
72
|
for _f in ("dist_type", "firm_counts_weights", "dist_parms", "recapture_form")
|
|
@@ -101,7 +98,7 @@ def gen_share_data(
|
|
|
101
98
|
frmshr_array = mkt_share_sample.mktshr_array[:, :2]
|
|
102
99
|
r_bar_ = _share_spec.recapture_ratio or DEFAULT_REC_RATIO
|
|
103
100
|
if recapture_form == RECForm.INOUT:
|
|
104
|
-
mkt_share_sample =
|
|
101
|
+
mkt_share_sample = ShareSampleData(
|
|
105
102
|
mkt_share_sample.mktshr_array,
|
|
106
103
|
mkt_share_sample.fcounts,
|
|
107
104
|
mkt_share_sample.nth_firm_share,
|
|
@@ -117,7 +114,7 @@ def gen_market_shares_uniform(
|
|
|
117
114
|
_mktshr_rng_seed_seq: SeedSequence,
|
|
118
115
|
_nthreads: int,
|
|
119
116
|
/,
|
|
120
|
-
) ->
|
|
117
|
+
) -> ShareSampleData:
|
|
121
118
|
"""Generate merging-firm shares from Uniform distribution on the 3-D simplex.
|
|
122
119
|
|
|
123
120
|
Parameters
|
|
@@ -136,7 +133,6 @@ def gen_market_shares_uniform(
|
|
|
136
133
|
market shares and other market statistics for each draw (market)
|
|
137
134
|
|
|
138
135
|
"""
|
|
139
|
-
|
|
140
136
|
frmshr_array: ArrayDouble = np.empty((_s_size, 2), float)
|
|
141
137
|
|
|
142
138
|
MultithreadedRNG(
|
|
@@ -170,7 +166,7 @@ def gen_market_shares_uniform(
|
|
|
170
166
|
nth_firm_share_.fill(np.nan)
|
|
171
167
|
aggregate_purchase_prob_.fill(np.nan)
|
|
172
168
|
|
|
173
|
-
return
|
|
169
|
+
return ShareSampleData(
|
|
174
170
|
mktshr_array_, fcounts_, nth_firm_share_, aggregate_purchase_prob_
|
|
175
171
|
)
|
|
176
172
|
|
|
@@ -185,7 +181,7 @@ def _gen_market_shares_dirichlet_multimarket(
|
|
|
185
181
|
_mktshr_rng_seed_seq: SeedSequence,
|
|
186
182
|
_nthreads: int = NTHREADS,
|
|
187
183
|
/,
|
|
188
|
-
) ->
|
|
184
|
+
) -> ShareSampleData:
|
|
189
185
|
"""Dirichlet-distributed shares with multiple firm-counts.
|
|
190
186
|
|
|
191
187
|
Firm-counts may be specified as having Uniform distribution over the range
|
|
@@ -221,7 +217,6 @@ def _gen_market_shares_dirichlet_multimarket(
|
|
|
221
217
|
array of market shares and other market statistics
|
|
222
218
|
|
|
223
219
|
"""
|
|
224
|
-
|
|
225
220
|
_firm_count_wts = (
|
|
226
221
|
DEFAULT_FCOUNT_WTS
|
|
227
222
|
if _firm_count_wts is None or not len(_firm_count_wts)
|
|
@@ -310,7 +305,7 @@ def _gen_market_shares_dirichlet_multimarket(
|
|
|
310
305
|
)
|
|
311
306
|
)
|
|
312
307
|
|
|
313
|
-
return
|
|
308
|
+
return ShareSampleData(
|
|
314
309
|
mktshr_array_, fcounts_, nth_firm_share_, aggregate_purchase_prob_
|
|
315
310
|
)
|
|
316
311
|
|
|
@@ -322,7 +317,7 @@ def gen_market_shares_dirichlet(
|
|
|
322
317
|
_mktshr_rng_seed_seq: SeedSequence,
|
|
323
318
|
_nthreads: int,
|
|
324
319
|
/,
|
|
325
|
-
) ->
|
|
320
|
+
) -> ShareSampleData:
|
|
326
321
|
"""Dirichlet-distributed shares with fixed firm-count.
|
|
327
322
|
|
|
328
323
|
Parameters
|
|
@@ -349,7 +344,6 @@ def gen_market_shares_dirichlet(
|
|
|
349
344
|
array of market shares and other market statistics
|
|
350
345
|
|
|
351
346
|
"""
|
|
352
|
-
|
|
353
347
|
if not isinstance(_dir_alphas, np.ndarray):
|
|
354
348
|
_dir_alphas = np.array(_dir_alphas)
|
|
355
349
|
|
|
@@ -394,7 +388,7 @@ def gen_market_shares_dirichlet(
|
|
|
394
388
|
aggregate_purchase_prob_ = 1 - mktshr_array[:, [-1]] # type: ignore
|
|
395
389
|
mktshr_array = mktshr_array[:, :-1] / aggregate_purchase_prob_
|
|
396
390
|
|
|
397
|
-
return
|
|
391
|
+
return ShareSampleData(
|
|
398
392
|
mktshr_array,
|
|
399
393
|
(mktshr_array.shape[-1] * np.ones((_s_size, 1))).astype(np.int64),
|
|
400
394
|
mktshr_array[:, [-1]],
|
|
@@ -410,7 +404,7 @@ def gen_divr_array(
|
|
|
410
404
|
/,
|
|
411
405
|
) -> ArrayDouble:
|
|
412
406
|
"""
|
|
413
|
-
Given merging-firm shares and related parameters, return
|
|
407
|
+
Given merging-firm shares and related parameters, return diversion ratios.
|
|
414
408
|
|
|
415
409
|
If recapture is specified as :attr:`mergeron.RECForm.OUTIN`, then the
|
|
416
410
|
choice-probability for the outside good must be supplied.
|
|
@@ -442,7 +436,6 @@ def gen_divr_array(
|
|
|
442
436
|
Merging-firm diversion ratios for mergers in the sample.
|
|
443
437
|
|
|
444
438
|
"""
|
|
445
|
-
|
|
446
439
|
divr_array: ArrayDouble
|
|
447
440
|
if _recapture_form == RECForm.FIXED:
|
|
448
441
|
divr_array = _recapture_ratio * _frmshr_array[:, ::-1] / (1 - _frmshr_array) # type: ignore
|
|
@@ -468,7 +461,7 @@ def gen_divr_array(
|
|
|
468
461
|
return divr_array
|
|
469
462
|
|
|
470
463
|
|
|
471
|
-
def gen_margin_price_data(
|
|
464
|
+
def gen_margin_price_data(
|
|
472
465
|
_frmshr_array: ArrayDouble,
|
|
473
466
|
_nth_firm_share: ArrayDouble,
|
|
474
467
|
_aggregate_purchase_prob: ArrayDouble,
|
|
@@ -479,7 +472,7 @@ def gen_margin_price_data( # noqa: PLR0914
|
|
|
479
472
|
_pr_rng_seed_seq: SeedSequence | None,
|
|
480
473
|
_nthreads: int,
|
|
481
474
|
/,
|
|
482
|
-
) -> tuple[
|
|
475
|
+
) -> tuple[MarginSampleData, PriceSampleData]:
|
|
483
476
|
"""Generate margin and price data for mergers in the sample.
|
|
484
477
|
|
|
485
478
|
Parameters
|
|
@@ -519,8 +512,7 @@ def gen_margin_price_data( # noqa: PLR0914
|
|
|
519
512
|
-------
|
|
520
513
|
Simulated margin- and price-data arrays for mergers in the sample.
|
|
521
514
|
"""
|
|
522
|
-
|
|
523
|
-
margin_data = MarginDataSample(
|
|
515
|
+
margin_data = MarginSampleData(
|
|
524
516
|
np.empty_like(_frmshr_array), np.ones(len(_frmshr_array)) == 0
|
|
525
517
|
)
|
|
526
518
|
|
|
@@ -590,7 +582,7 @@ def gen_margin_price_data( # noqa: PLR0914
|
|
|
590
582
|
)
|
|
591
583
|
mnl_test_array = (pcm_array[:, [1]] >= 0) & (pcm_array[:, [1]] <= 1)
|
|
592
584
|
|
|
593
|
-
margin_data =
|
|
585
|
+
margin_data = MarginSampleData(pcm_array[:, :2], mnl_test_array)
|
|
594
586
|
del price_array_here
|
|
595
587
|
case _:
|
|
596
588
|
raise ValueError(
|
|
@@ -649,11 +641,11 @@ def gen_margin_price_data( # noqa: PLR0914
|
|
|
649
641
|
# Otherwise, all draws meet the filing test
|
|
650
642
|
hsr_filing_test = np.ones(len(_frmshr_array), dtype=bool)
|
|
651
643
|
|
|
652
|
-
# Assume that if minimum
|
|
644
|
+
# Assume that if minimum merging-firm share is 10%, merger filing required
|
|
653
645
|
# under value-of-transactions test or merger triggers post-consummation review
|
|
654
646
|
hsr_filing_test |= _frmshr_array.min(axis=1) >= test_rev_ratio_inv
|
|
655
647
|
|
|
656
|
-
return margin_data,
|
|
648
|
+
return margin_data, PriceSampleData(price_array, hsr_filing_test)
|
|
657
649
|
|
|
658
650
|
|
|
659
651
|
def _gen_margin_data(
|
|
@@ -664,7 +656,7 @@ def _gen_margin_data(
|
|
|
664
656
|
_pcm_rng_seed_seq: SeedSequence,
|
|
665
657
|
_nthreads: int,
|
|
666
658
|
/,
|
|
667
|
-
) ->
|
|
659
|
+
) -> MarginSampleData:
|
|
668
660
|
dist_type_pcm, dist_parms_pcm, pcm_restriction_ = (
|
|
669
661
|
getattr(_pcm_spec, _f) for _f in ("dist_type", "dist_parms", "pcm_restriction")
|
|
670
662
|
)
|
|
@@ -745,14 +737,14 @@ def _gen_margin_data(
|
|
|
745
737
|
else:
|
|
746
738
|
mnl_test_array = np.ones(len(pcm_array), dtype=bool)
|
|
747
739
|
|
|
748
|
-
return
|
|
740
|
+
return MarginSampleData(pcm_array, mnl_test_array)
|
|
749
741
|
|
|
750
742
|
|
|
751
743
|
def _beta_located(
|
|
752
744
|
_mu: float | ArrayDouble, _sigma: float | ArrayDouble, /
|
|
753
745
|
) -> ArrayDouble:
|
|
754
746
|
"""
|
|
755
|
-
Given mean and stddev, return shape parameters for corresponding Beta distribution
|
|
747
|
+
Given mean and stddev, return shape parameters for corresponding Beta distribution.
|
|
756
748
|
|
|
757
749
|
Solve the first two moments of the standard Beta to get the shape parameters.
|
|
758
750
|
|
|
@@ -768,20 +760,18 @@ def _beta_located(
|
|
|
768
760
|
shape parameters for Beta distribution
|
|
769
761
|
|
|
770
762
|
"""
|
|
771
|
-
|
|
772
763
|
mul = -1 + _mu * (1 - _mu) / _sigma**2
|
|
773
764
|
return np.array([_mu * mul, (1 - _mu) * mul], float)
|
|
774
765
|
|
|
775
766
|
|
|
776
767
|
def beta_located_bound(_dist_parms: ArrayDouble, /) -> ArrayDouble:
|
|
777
768
|
R"""
|
|
778
|
-
Return shape parameters for a non-standard beta, given
|
|
779
|
-
|
|
769
|
+
Return shape parameters for a non-standard beta, given mean, stddev, and range.
|
|
780
770
|
|
|
781
771
|
Recover the r.v.s as
|
|
782
772
|
:math:`\min + (\max - \min) \cdot \symup{Β}(a, b)`,
|
|
783
773
|
with `a` and `b` calculated from the specified mean (:math:`\mu`) and
|
|
784
|
-
variance (:math:`\sigma`). [
|
|
774
|
+
variance (:math:`\sigma`). [#]_
|
|
785
775
|
|
|
786
776
|
Parameters
|
|
787
777
|
----------
|
|
@@ -798,8 +788,7 @@ def beta_located_bound(_dist_parms: ArrayDouble, /) -> ArrayDouble:
|
|
|
798
788
|
|
|
799
789
|
References
|
|
800
790
|
----------
|
|
801
|
-
.. [
|
|
791
|
+
.. [#] NIST, Beta Distribution. https://www.itl.nist.gov/div898/handbook/eda/section3/eda366h.htm
|
|
802
792
|
""" # noqa: RUF002
|
|
803
|
-
|
|
804
793
|
bmu, bsigma, bmin, bmax = _dist_parms
|
|
805
794
|
return _beta_located((bmu - bmin) / (bmax - bmin), bsigma / (bmax - bmin))
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Methods to format and print summary statistics on merger enforcement patterns.
|
|
3
|
-
|
|
4
|
-
"""
|
|
1
|
+
"""Methods to format and print summary statistics on merger enforcement patterns."""
|
|
5
2
|
|
|
6
3
|
import enum
|
|
7
4
|
from collections.abc import Mapping
|
|
@@ -19,6 +16,8 @@ __version__ = VERSION
|
|
|
19
16
|
@this_yaml.register_class
|
|
20
17
|
@enum.unique
|
|
21
18
|
class IndustryGroup(str, Enameled):
|
|
19
|
+
"""Industry group of reported markets."""
|
|
20
|
+
|
|
22
21
|
ALL = "All Markets"
|
|
23
22
|
GRO = "Grocery Markets"
|
|
24
23
|
OIL = "Oil Markets"
|
|
@@ -34,21 +33,25 @@ class IndustryGroup(str, Enameled):
|
|
|
34
33
|
@this_yaml.register_class
|
|
35
34
|
@enum.unique
|
|
36
35
|
class OtherEvidence(str, Enameled):
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
36
|
+
"""Additional evidence available, if any, for reported markets."""
|
|
37
|
+
|
|
38
|
+
HOT = "Hot Documents Identified"
|
|
39
|
+
NHT = "No Hot Documents Identified"
|
|
40
|
+
HTU = "No Evidence on Hot Documents"
|
|
41
|
+
NCC = "No Strong Customer Complaints"
|
|
42
|
+
SCC = "Strong Customer Complaints"
|
|
43
|
+
CCU = "No Evidence on Customer Complaints"
|
|
44
|
+
END = "Entry Difficult"
|
|
45
|
+
EEY = "Entry Easy"
|
|
46
|
+
EEU = "No Entry Evidence"
|
|
47
|
+
UNR = "Unrestricted on additional evidence"
|
|
47
48
|
|
|
48
49
|
|
|
49
50
|
@this_yaml.register_class
|
|
50
51
|
@enum.unique
|
|
51
52
|
class StatsGrpSelector(str, Enameled):
|
|
53
|
+
"""Measure used to summarize investigations data."""
|
|
54
|
+
|
|
52
55
|
FC = "ByFirmCount"
|
|
53
56
|
HD = "ByHHIandDelta"
|
|
54
57
|
DL = "ByDelta"
|
|
@@ -58,6 +61,8 @@ class StatsGrpSelector(str, Enameled):
|
|
|
58
61
|
@this_yaml.register_class
|
|
59
62
|
@enum.unique
|
|
60
63
|
class StatsReturnSelector(str, Enameled):
|
|
64
|
+
"""Statistics to report on investigations data."""
|
|
65
|
+
|
|
61
66
|
CNT = "count"
|
|
62
67
|
RPT = "rate, point"
|
|
63
68
|
RIN = "rate, interval"
|
|
@@ -66,6 +71,8 @@ class StatsReturnSelector(str, Enameled):
|
|
|
66
71
|
@this_yaml.register_class
|
|
67
72
|
@enum.unique
|
|
68
73
|
class SortSelector(str, Enameled):
|
|
74
|
+
"""Sort order for reporting investigations data."""
|
|
75
|
+
|
|
69
76
|
UCH = "unchanged"
|
|
70
77
|
REV = "reversed"
|
|
71
78
|
|
|
@@ -128,7 +135,7 @@ ZONE_DETAIL_STRINGS_DELTA = {
|
|
|
128
135
|
|
|
129
136
|
|
|
130
137
|
def enf_cnts_obs_by_group(
|
|
131
|
-
_invdata_array_dict:
|
|
138
|
+
_invdata_array_dict: fid.INVData,
|
|
132
139
|
_study_period: str,
|
|
133
140
|
_table_ind_grp: IndustryGroup,
|
|
134
141
|
_table_evid_cond: OtherEvidence,
|
|
@@ -136,6 +143,28 @@ def enf_cnts_obs_by_group(
|
|
|
136
143
|
_enf_spec: INVResolution,
|
|
137
144
|
/,
|
|
138
145
|
) -> ArrayBIGINT:
|
|
146
|
+
"""Summarize investigations data by reporting group.
|
|
147
|
+
|
|
148
|
+
Parameters
|
|
149
|
+
----------
|
|
150
|
+
_invdata_array_dict
|
|
151
|
+
raw investigations data
|
|
152
|
+
_study_period
|
|
153
|
+
study period
|
|
154
|
+
_table_ind_grp
|
|
155
|
+
industry group
|
|
156
|
+
_table_evid_cond
|
|
157
|
+
additional evidence
|
|
158
|
+
_stats_group
|
|
159
|
+
grouping measure
|
|
160
|
+
_enf_spec
|
|
161
|
+
enforcement specification (see, :class:`mergeron.gen.INVResolution`)
|
|
162
|
+
|
|
163
|
+
Returns
|
|
164
|
+
-------
|
|
165
|
+
ArrayBIGINT
|
|
166
|
+
Counts of markets resolved as enforced, cleared, or both, respectively.
|
|
167
|
+
"""
|
|
139
168
|
if _stats_group == StatsGrpSelector.HD:
|
|
140
169
|
raise ValueError(
|
|
141
170
|
f"Clearance/enforcement statistics, '{_stats_group}' not valied here."
|
|
@@ -164,13 +193,34 @@ def enf_cnts_obs_by_group(
|
|
|
164
193
|
|
|
165
194
|
|
|
166
195
|
def enf_cnts_obs_byfirmcount(
|
|
167
|
-
_data_array_dict:
|
|
196
|
+
_data_array_dict: fid.INVData,
|
|
168
197
|
_data_period: str = "1996-2003",
|
|
169
198
|
_table_ind_group: IndustryGroup = IndustryGroup.ALL,
|
|
170
|
-
_table_evid_cond: OtherEvidence = OtherEvidence.
|
|
171
|
-
_enf_spec: INVResolution = INVResolution.
|
|
199
|
+
_table_evid_cond: OtherEvidence = OtherEvidence.UNR,
|
|
200
|
+
_enf_spec: INVResolution = INVResolution.ENFT,
|
|
172
201
|
/,
|
|
173
202
|
) -> ArrayBIGINT:
|
|
203
|
+
"""Summarize investigations data by firm count.
|
|
204
|
+
|
|
205
|
+
Parameters
|
|
206
|
+
----------
|
|
207
|
+
_data_array_dict
|
|
208
|
+
raw investigations data
|
|
209
|
+
_data_period
|
|
210
|
+
data period
|
|
211
|
+
_table_ind_group
|
|
212
|
+
industry group
|
|
213
|
+
_table_evid_cond
|
|
214
|
+
additional evidence
|
|
215
|
+
_enf_spec
|
|
216
|
+
enforcement specification (see, :class:`mergeron.gen.INVResolution`)
|
|
217
|
+
|
|
218
|
+
Returns
|
|
219
|
+
-------
|
|
220
|
+
ArrayBIGINT
|
|
221
|
+
Counts of markets resolved as enforced, cleared, or both, respectively,
|
|
222
|
+
reported by number of pre-merger firms.
|
|
223
|
+
"""
|
|
174
224
|
if _data_period not in _data_array_dict:
|
|
175
225
|
raise ValueError(
|
|
176
226
|
f"Invalid value of data period, {f'"{_data_period}"'}."
|
|
@@ -197,13 +247,34 @@ def enf_cnts_obs_byfirmcount(
|
|
|
197
247
|
|
|
198
248
|
|
|
199
249
|
def enf_cnts_obs_byhhianddelta(
|
|
200
|
-
_data_array_dict:
|
|
250
|
+
_data_array_dict: fid.INVData,
|
|
201
251
|
_data_period: str = "1996-2003",
|
|
202
252
|
_table_ind_group: IndustryGroup = IndustryGroup.ALL,
|
|
203
|
-
_table_evid_cond: OtherEvidence = OtherEvidence.
|
|
204
|
-
_enf_spec: INVResolution = INVResolution.
|
|
253
|
+
_table_evid_cond: OtherEvidence = OtherEvidence.UNR,
|
|
254
|
+
_enf_spec: INVResolution = INVResolution.ENFT,
|
|
205
255
|
/,
|
|
206
256
|
) -> ArrayBIGINT:
|
|
257
|
+
"""Summarize investigations data by HHI and ΔHHI.
|
|
258
|
+
|
|
259
|
+
Parameters
|
|
260
|
+
----------
|
|
261
|
+
_data_array_dict
|
|
262
|
+
raw investigations data
|
|
263
|
+
_data_period
|
|
264
|
+
data period
|
|
265
|
+
_table_ind_group
|
|
266
|
+
industry group
|
|
267
|
+
_table_evid_cond
|
|
268
|
+
additional evidence
|
|
269
|
+
_enf_spec
|
|
270
|
+
enforcement specification (see, :class:`mergeron.gen.INVResolution`)
|
|
271
|
+
|
|
272
|
+
Returns
|
|
273
|
+
-------
|
|
274
|
+
ArrayBIGINT
|
|
275
|
+
Counts of markets resolved as enforced, cleared, or both, respectively,
|
|
276
|
+
reported by HHI and ΔHHI.
|
|
277
|
+
"""
|
|
207
278
|
if _data_period not in _data_array_dict:
|
|
208
279
|
raise ValueError(
|
|
209
280
|
f"Invalid value of data period, {f'"{_data_period}"'}."
|
|
@@ -232,9 +303,19 @@ def enf_cnts_obs_byhhianddelta(
|
|
|
232
303
|
def table_no_lku(
|
|
233
304
|
_data_array_dict_sub: Mapping[str, fid.INVTableData],
|
|
234
305
|
_table_ind_group: IndustryGroup = IndustryGroup.ALL,
|
|
235
|
-
_table_evid_cond: OtherEvidence = OtherEvidence.
|
|
306
|
+
_table_evid_cond: OtherEvidence = OtherEvidence.UNR,
|
|
236
307
|
/,
|
|
237
308
|
) -> str:
|
|
309
|
+
"""Lookup table number based on industry group and additional evidence."""
|
|
310
|
+
if _table_evid_cond not in (
|
|
311
|
+
_egl := [
|
|
312
|
+
_data_array_dict_sub[_v].additional_evidence for _v in _data_array_dict_sub
|
|
313
|
+
]
|
|
314
|
+
):
|
|
315
|
+
raise ValueError(
|
|
316
|
+
f"Invalid value for additional evidence, {f'"{_table_evid_cond}"'}."
|
|
317
|
+
f"Must be one of {_egl!r}"
|
|
318
|
+
)
|
|
238
319
|
if _table_ind_group not in (
|
|
239
320
|
_igl := [_data_array_dict_sub[_v].industry_group for _v in _data_array_dict_sub]
|
|
240
321
|
):
|
|
@@ -256,6 +337,18 @@ def table_no_lku(
|
|
|
256
337
|
|
|
257
338
|
|
|
258
339
|
def enf_cnts_byfirmcount(_cnts_array: ArrayBIGINT, /) -> ArrayBIGINT:
|
|
340
|
+
"""Summarize investigations data by firm count.
|
|
341
|
+
|
|
342
|
+
Parameters
|
|
343
|
+
----------
|
|
344
|
+
_cnts_array
|
|
345
|
+
raw investigations data array
|
|
346
|
+
|
|
347
|
+
Returns
|
|
348
|
+
-------
|
|
349
|
+
ArrayBIGINT
|
|
350
|
+
Subtotals for columns other than the first, grouped by the first column.
|
|
351
|
+
"""
|
|
259
352
|
if not _cnts_array[:, 0].any():
|
|
260
353
|
return np.array([], int)
|
|
261
354
|
|
|
@@ -272,6 +365,20 @@ def enf_cnts_byfirmcount(_cnts_array: ArrayBIGINT, /) -> ArrayBIGINT:
|
|
|
272
365
|
|
|
273
366
|
|
|
274
367
|
def enf_cnts_bydelta(_cnts_array: ArrayBIGINT, /) -> ArrayBIGINT:
|
|
368
|
+
"""Summarize investigations data by ΔHHI.
|
|
369
|
+
|
|
370
|
+
Parameters
|
|
371
|
+
----------
|
|
372
|
+
_cnts_array
|
|
373
|
+
raw investigations data array
|
|
374
|
+
|
|
375
|
+
Returns
|
|
376
|
+
-------
|
|
377
|
+
ArrayBIGINT
|
|
378
|
+
Subtotals for columns higher than the second, grouped by the second column.
|
|
379
|
+
"""
|
|
380
|
+
if not _cnts_array[:, 1].any():
|
|
381
|
+
return np.array([], int)
|
|
275
382
|
ndim_in = 2
|
|
276
383
|
return np.vstack([
|
|
277
384
|
np.concatenate([
|
|
@@ -285,9 +392,23 @@ def enf_cnts_bydelta(_cnts_array: ArrayBIGINT, /) -> ArrayBIGINT:
|
|
|
285
392
|
|
|
286
393
|
|
|
287
394
|
def enf_cnts_byconczone(_cnts_array: ArrayBIGINT, /) -> ArrayBIGINT:
|
|
395
|
+
"""Summarize investigations data by concentration zone, as defined in the Guidelines.
|
|
396
|
+
|
|
397
|
+
Includes sub-total detail for "Moderately Concentrated" and "Unconcentrated" markets.
|
|
398
|
+
|
|
399
|
+
Parameters
|
|
400
|
+
----------
|
|
401
|
+
_cnts_array
|
|
402
|
+
raw investigations data array
|
|
403
|
+
|
|
404
|
+
Returns
|
|
405
|
+
-------
|
|
406
|
+
ArrayBIGINT
|
|
407
|
+
Subtotals range of HHI and ΔHHI, with detail
|
|
408
|
+
"""
|
|
288
409
|
if not _cnts_array[:, 0].any() or np.isnan(_cnts_array[:, 0]).all():
|
|
289
410
|
return np.array([], int)
|
|
290
|
-
# Step 1: Tag and agg. from HHI-post and
|
|
411
|
+
# Step 1: Tag and agg. from HHI-post and ΔHHI to zone triple
|
|
291
412
|
# NOTE: Although you could just map and not (partially) aggregate in this step,
|
|
292
413
|
# the mapped array is a copy, and is larger without partial aggregation, so
|
|
293
414
|
# aggregation reduces the footprint of this step in memory. Although this point
|
mergeron/gen/upp_tests.py
CHANGED
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Methods to compute intrinsic clearance rates and intrinsic enforcement rates
|
|
3
|
-
from generated market data.
|
|
4
|
-
|
|
5
|
-
"""
|
|
1
|
+
"""Methods to compute intrinsic enforcement/clearance rates from generated market data."""
|
|
6
2
|
|
|
7
3
|
from collections.abc import Sequence
|
|
8
4
|
from typing import TypedDict
|
|
@@ -28,20 +24,20 @@ __version__ = VERSION
|
|
|
28
24
|
|
|
29
25
|
|
|
30
26
|
class INVRESCntsArgs(TypedDict, total=False):
|
|
31
|
-
"Keyword arguments of function, :code:`sim_enf_cnts
|
|
27
|
+
"""Keyword arguments of function, :code:`sim_enf_cnts`."""
|
|
32
28
|
|
|
33
29
|
sample_size: int
|
|
34
30
|
seed_seq_list: Sequence[SeedSequence] | None
|
|
35
31
|
nthreads: int
|
|
36
32
|
|
|
37
33
|
|
|
38
|
-
def compute_upp_test_counts(
|
|
34
|
+
def compute_upp_test_counts(
|
|
39
35
|
_market_data_sample: MarketSampleData,
|
|
40
36
|
_upp_test_parms: gbl.HMGThresholds,
|
|
41
37
|
_upp_test_regime: UPPTestRegime,
|
|
42
38
|
/,
|
|
43
39
|
) -> UPPTestsCounts:
|
|
44
|
-
"""Estimate enforcement and clearance counts from market data sample
|
|
40
|
+
"""Estimate enforcement and clearance counts from market data sample.
|
|
45
41
|
|
|
46
42
|
Parameters
|
|
47
43
|
----------
|
|
@@ -64,7 +60,6 @@ def compute_upp_test_counts( # noqa: PLR0914
|
|
|
64
60
|
Enforced and cleared counts
|
|
65
61
|
|
|
66
62
|
"""
|
|
67
|
-
|
|
68
63
|
g_bar_, divr_bar_, cmcr_bar_, ipr_bar_ = (
|
|
69
64
|
getattr(_upp_test_parms, _f) for _f in ("guppi", "divr", "cmcr", "ipr")
|
|
70
65
|
)
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: mergeron
|
|
3
|
+
Version: 2025.739341.8
|
|
4
|
+
Summary: Python for analyzing merger enforcement policy
|
|
5
|
+
License: MIT
|
|
6
|
+
Keywords: merger policy analysis,merger guidelines,merger screening,policy presumptions,concentration standards,upward pricing pressure,GUPPI
|
|
7
|
+
Author: Murthy Kambhampaty
|
|
8
|
+
Author-email: smk@capeconomics.com
|
|
9
|
+
Requires-Python: >=3.12
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
21
|
+
Requires-Dist: aenum (>=3.1.15,<4.0.0)
|
|
22
|
+
Requires-Dist: attrs (>=23.2)
|
|
23
|
+
Requires-Dist: bs4 (>=0.0.1)
|
|
24
|
+
Requires-Dist: certifi (>=2023.11.17)
|
|
25
|
+
Requires-Dist: h5py (>=3.13.0,<4.0.0)
|
|
26
|
+
Requires-Dist: jinja2 (>=3.1)
|
|
27
|
+
Requires-Dist: joblib (>=1.3)
|
|
28
|
+
Requires-Dist: linuxdoc (>=20240924,<20240925)
|
|
29
|
+
Requires-Dist: lxml (>=5.3.1,<6.0.0)
|
|
30
|
+
Requires-Dist: matplotlib (>=3.8)
|
|
31
|
+
Requires-Dist: mpmath (>=1.3)
|
|
32
|
+
Requires-Dist: python-calamine (>=0.3.1,<0.4.0)
|
|
33
|
+
Requires-Dist: ruamel-yaml (>=0.18.10,<0.19.0)
|
|
34
|
+
Requires-Dist: scipy (>=1.12)
|
|
35
|
+
Requires-Dist: sympy (>=1.12)
|
|
36
|
+
Requires-Dist: types-beautifulsoup4 (>=4.11.2)
|
|
37
|
+
Requires-Dist: urllib3 (>=2.2.2,<3.0.0)
|
|
38
|
+
Project-URL: Documentation, https://capeconomics.github.io/mergeron/
|
|
39
|
+
Project-URL: Repository, https://github.com/capeconomics/mergeron.git
|
|
40
|
+
Description-Content-Type: text/x-rst
|
|
41
|
+
|
|
42
|
+
mergeron: Python for analyzing merger enforcement policy
|
|
43
|
+
========================================================
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
.. image:: https://img.shields.io/pypi/v/mergeron
|
|
47
|
+
:alt: PyPI - Package Version
|
|
48
|
+
:target: https://pypi.python.org/pypi/mergeron/
|
|
49
|
+
.. image:: https://img.shields.io/pypi/pyversions/mergeron
|
|
50
|
+
:alt: PyPI - Python Version
|
|
51
|
+
:target: https://pypi.python.org/pypi/mergeron/
|
|
52
|
+
.. image:: https://img.shields.io/pypi/status/mergeron
|
|
53
|
+
:alt: PyPI - Package status
|
|
54
|
+
:target: https://pypi.python.org/pypi/mergeron/
|
|
55
|
+
|
|
56
|
+
|
|
|
57
|
+
|
|
58
|
+
.. image:: https://img.shields.io/endpoint?url=https://python-poetry.org/badge/v0.json
|
|
59
|
+
:target: https://python-poetry.org/
|
|
60
|
+
.. image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json
|
|
61
|
+
:target: https://github.com/astral-sh/ruff
|
|
62
|
+
.. image:: https://www.mypy-lang.org/static/mypy_badge.svg
|
|
63
|
+
:target: https://mypy-lang.org/
|
|
64
|
+
.. image:: https://img.shields.io/badge/code%20style-black-000000
|
|
65
|
+
:target: https://github.com/psf/black
|
|
66
|
+
.. image:: https://img.shields.io/badge/License-MIT-yellow
|
|
67
|
+
:alt: License: MIT
|
|
68
|
+
:target: https://opensource.org/licenses/MIT
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
*Visualize* the sets of mergers falling within specified concentration and diversion-ratio thresholds. *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.
|
|
72
|
+
*Generate* data under specified distributions of firm counts, market shares, price-cost margins, and prices, optionally imposing equilibrium conditions for Bertrand oligopoly with MNL demand and or restrictions implied by statutory filing thresholds. *Compute* intrinsic enforcement rates or
|
|
73
|
+
intrinsic clearance rates using the generated data, given thresholds for
|
|
74
|
+
concentration;
|
|
75
|
+
diversion ratio;
|
|
76
|
+
gross upward pricing pressure (GUPPI);
|
|
77
|
+
critical marginal cost reduction (CMCR); and
|
|
78
|
+
illustrative price rise (IPR).
|
|
79
|
+
|
|
80
|
+
Installation
|
|
81
|
+
------------
|
|
82
|
+
|
|
83
|
+
To install the package, use the following shell command:
|
|
84
|
+
|
|
85
|
+
.. code:: bash
|
|
86
|
+
|
|
87
|
+
pip install mergeron
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
Documentation
|
|
91
|
+
-------------
|
|
92
|
+
|
|
93
|
+
Usage guide and API reference available `here <https://capeconomics.github.io/mergeron/>`_.
|
|
94
|
+
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
mergeron/__init__.py,sha256=teyW3ktycQTdjw8WVqhlwrk9xi9LOI05KgKWLuCHJ9E,5773
|
|
2
|
+
mergeron/core/__init__.py,sha256=NChJfOkahbGPa1Ll1fO3agY5-IoDlEra3xNkXPnKIVM,3259
|
|
3
|
+
mergeron/core/empirical_margin_distribution.py,sha256=qfCsalO3EnsGclYeFxCgRu_-shbXP5kYIMf328lLC80,11455
|
|
4
|
+
mergeron/core/ftc_merger_investigations_data.py,sha256=k4TDkP1rDBmN4uKOYF0SUvSRkYmyVhbsBvLUKDYJqOo,28537
|
|
5
|
+
mergeron/core/guidelines_boundaries.py,sha256=Ct6JiuFjSVuCz-m-yeUc50xTC7F4tLgsyqXitb_nObI,15551
|
|
6
|
+
mergeron/core/guidelines_boundary_functions.py,sha256=ycz0W5tV9oJ9vVH83IVaprOW4RmG68q8t1QROkQcwxU,29048
|
|
7
|
+
mergeron/core/guidelines_boundary_functions_extra.py,sha256=bFRpPGKPHhuhXI67GlEOKtQrNo7WVTK7yMd9NlHCc-M,22095
|
|
8
|
+
mergeron/core/pseudorandom_numbers.py,sha256=-mPveXjJJ446NrBMAmWIa2jI6j0Px0xcCJTGEEsn3bo,10149
|
|
9
|
+
mergeron/data/__init__.py,sha256=CbqheFSkXEe7NOfuAV-NLaaEiNzl9pVCndGjtUUOj9g,1846
|
|
10
|
+
mergeron/data/damodaran_margin_data_serialized.zip,sha256=Wc1v9buSrYTWWAravG8W9nPbgsU07zMtSAR2RvMQU5s,623482
|
|
11
|
+
mergeron/data/ftc_merger_investigations_data.zip,sha256=tiB2TLFyS9LMSFIv8DBA_oEEx12DU4MyjHni4NlsRMU,24002
|
|
12
|
+
mergeron/gen/__init__.py,sha256=vr4B3AynBeohLPYeb6kD_iPdEOtta-dUfA7_Bi-l-jI,23649
|
|
13
|
+
mergeron/gen/data_generation.py,sha256=M7RBJO8distfdFPFQcQM4Mpfi2Etxy9HEw5EoyjWDow,17347
|
|
14
|
+
mergeron/gen/data_generation_functions.py,sha256=f6aLBg6JNqOoBq57k3ovevgvtaLfIAfT-ATzZl11CEA,26076
|
|
15
|
+
mergeron/gen/enforcement_stats.py,sha256=-vxB6U0dyXRmPZzhJ-8LyPESMycbnG4Tf95ydneIgjQ,14246
|
|
16
|
+
mergeron/gen/upp_tests.py,sha256=gRJISQ2jGmIDmFOvaTIkvYooI4mK-QbgkfgL46RrRio,7445
|
|
17
|
+
mergeron/py.typed,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
|
|
18
|
+
mergeron-2025.739341.8.dist-info/METADATA,sha256=Toe2o1UJ7X-jQh2qAqL7AIqzLQa6lNKlbVmtYFja6rs,3906
|
|
19
|
+
mergeron-2025.739341.8.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
|
|
20
|
+
mergeron-2025.739341.8.dist-info/RECORD,,
|
|
Binary file
|