mergeron 2024.739091.2__py3-none-any.whl → 2024.739097.2__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 +1 -18
- mergeron/core/damodaran_margin_data.py +1 -1
- mergeron/core/ftc_merger_investigations_data.py +4 -5
- mergeron/ext/xlsxw_helper.py +8 -0
- mergeron/gen/__init__.py +24 -8
- mergeron/gen/_data_generation_functions.py +210 -49
- mergeron/gen/data_generation.py +51 -43
- mergeron/gen/market_sample.py +76 -8
- mergeron/gen/upp_tests.py +3 -8
- mergeron-2024.739097.2.dist-info/METADATA +130 -0
- {mergeron-2024.739091.2.dist-info → mergeron-2024.739097.2.dist-info}/RECORD +13 -13
- mergeron-2024.739091.2.dist-info/METADATA +0 -110
- /mergeron/{core → ext}/proportions_tests.py +0 -0
- {mergeron-2024.739091.2.dist-info → mergeron-2024.739097.2.dist-info}/WHEEL +0 -0
mergeron/__init__.py
CHANGED
|
@@ -2,16 +2,12 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import enum
|
|
4
4
|
from pathlib import Path
|
|
5
|
-
from typing import Any
|
|
6
5
|
|
|
7
6
|
import numpy as np
|
|
8
|
-
import pendulum # type: ignore
|
|
9
|
-
from icecream import argumentToString, ic, install # type: ignore
|
|
10
|
-
from numpy.typing import NDArray
|
|
11
7
|
|
|
12
8
|
_PKG_NAME: str = Path(__file__).parent.stem
|
|
13
9
|
|
|
14
|
-
VERSION = "2024.
|
|
10
|
+
VERSION = "2024.739097.2"
|
|
15
11
|
|
|
16
12
|
__version__ = VERSION
|
|
17
13
|
|
|
@@ -28,19 +24,6 @@ if not DATA_DIR.is_dir():
|
|
|
28
24
|
np.set_printoptions(precision=18)
|
|
29
25
|
|
|
30
26
|
|
|
31
|
-
def _timestamper() -> str:
|
|
32
|
-
return f"{pendulum.now().strftime("%F %T.%f")} |> "
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
@argumentToString.register(np.ndarray) # type: ignore
|
|
36
|
-
def _(_obj: NDArray[Any]) -> str:
|
|
37
|
-
return f"ndarray, shape={_obj.shape}, dtype={_obj.dtype}"
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
ic.configureOutput(prefix=_timestamper, includeContext=True)
|
|
41
|
-
install()
|
|
42
|
-
|
|
43
|
-
|
|
44
27
|
@enum.unique
|
|
45
28
|
class RECConstants(enum.StrEnum):
|
|
46
29
|
"""Recapture rate - derivation methods."""
|
|
@@ -22,7 +22,6 @@ import numpy as np
|
|
|
22
22
|
import re2 as re # type: ignore
|
|
23
23
|
import requests
|
|
24
24
|
from bs4 import BeautifulSoup
|
|
25
|
-
from icecream import ic # type: ignore
|
|
26
25
|
from numpy.testing import assert_array_equal
|
|
27
26
|
from numpy.typing import NDArray
|
|
28
27
|
|
|
@@ -345,7 +344,7 @@ def _construct_new_period_data(
|
|
|
345
344
|
# _invdata_array_bld_enfcls < 0, _invdata_array_bld_enfcls, 0
|
|
346
345
|
# )
|
|
347
346
|
# if np.einsum('ij->', invdata_array_bld_tbc):
|
|
348
|
-
#
|
|
347
|
+
# print(
|
|
349
348
|
# f"{_data_period}, {_table_no}, {_invdata_ind_group}:",
|
|
350
349
|
# abs(np.einsum('ij->', invdata_array_bld_tbc))
|
|
351
350
|
# )
|
|
@@ -551,7 +550,7 @@ def _parse_table_blocks(
|
|
|
551
550
|
_invdata_evid_cond = "Unrestricted on additional evidence"
|
|
552
551
|
|
|
553
552
|
else:
|
|
554
|
-
#
|
|
553
|
+
# print(_table_blocks)
|
|
555
554
|
_invdata_evid_cond = (
|
|
556
555
|
_table_blocks[1][-3].strip()
|
|
557
556
|
if _table_ser == 9
|
|
@@ -570,8 +569,8 @@ def _parse_table_blocks(
|
|
|
570
569
|
|
|
571
570
|
_table_array = process_table_func(_table_blocks)
|
|
572
571
|
if not isinstance(_table_array, np.ndarray) or _table_array.dtype != np.int64:
|
|
573
|
-
|
|
574
|
-
|
|
572
|
+
print(_table_num)
|
|
573
|
+
print(_table_blocks)
|
|
575
574
|
raise ValueError
|
|
576
575
|
|
|
577
576
|
_table_data = INVTableData(_invdata_ind_group, _invdata_evid_cond, _table_array)
|
mergeron/ext/xlsxw_helper.py
CHANGED
|
@@ -11,6 +11,14 @@ This module is designed for producing formatted summary output. For
|
|
|
11
11
|
writing bulk data to Excel, facilities provided in third-party packages
|
|
12
12
|
such as `polars <https://pola.rs/>`_ likely provide better performance.
|
|
13
13
|
|
|
14
|
+
License
|
|
15
|
+
========
|
|
16
|
+
|
|
17
|
+
Copyright 2017-2023 S. Murthy Kambhampaty
|
|
18
|
+
Licese: MIT
|
|
19
|
+
https://mit-license.org/
|
|
20
|
+
|
|
21
|
+
|
|
14
22
|
"""
|
|
15
23
|
|
|
16
24
|
from __future__ import annotations
|
mergeron/gen/__init__.py
CHANGED
|
@@ -21,7 +21,7 @@ __version__ = VERSION
|
|
|
21
21
|
|
|
22
22
|
EMPTY_ARRAY_DEFAULT = np.zeros(2)
|
|
23
23
|
FCOUNT_WTS_DEFAULT = np.divide(
|
|
24
|
-
(_nr := np.arange(1,
|
|
24
|
+
(_nr := np.arange(1, 7)[::-1]), _nr.sum(), dtype=np.float64
|
|
25
25
|
)
|
|
26
26
|
|
|
27
27
|
|
|
@@ -36,7 +36,7 @@ class PriceConstants(tuple[bool, str | None], enum.ReprEnum):
|
|
|
36
36
|
ZERO = (False, None)
|
|
37
37
|
NEG = (False, "negative share-correlation")
|
|
38
38
|
POS = (False, "positive share-correlation")
|
|
39
|
-
|
|
39
|
+
CSY = (False, "market-wide cost-symmetry")
|
|
40
40
|
|
|
41
41
|
|
|
42
42
|
@enum.unique
|
|
@@ -59,7 +59,11 @@ class SHRConstants(enum.StrEnum):
|
|
|
59
59
|
DIR_ASYM = "Asymmetric Dirichlet"
|
|
60
60
|
"""Share distribution for merging-firm shares has a higher peak share
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
By default, shape parameter for merging-firm-share is 2.5, and
|
|
63
|
+
1.0 for all others. Defining, :attr:`mergeron.ShareSpec.dist_parms`
|
|
64
|
+
as a vector of shape parameters with length matching
|
|
65
|
+
that of :attr:`mergeron.ShareSpec.dist_parms` allows flexible specification
|
|
66
|
+
of Dirichlet-distributed share-data generation.
|
|
63
67
|
"""
|
|
64
68
|
|
|
65
69
|
DIR_COND = "Conditional Dirichlet"
|
|
@@ -74,13 +78,24 @@ class SHRConstants(enum.StrEnum):
|
|
|
74
78
|
class ShareSpec:
|
|
75
79
|
"""Market share specification
|
|
76
80
|
|
|
81
|
+
A key feature of market-share specification in this package is that
|
|
82
|
+
the draws represent markets with multiple different firm-counts.
|
|
83
|
+
Firm-counts are unspecified if the share distribution is
|
|
84
|
+
:attr:`mergeron.SHRConstants.UNI`, for Dirichlet-distributed market-shares,
|
|
85
|
+
the default specification is that firm-counts vary between
|
|
86
|
+
2 and 7 firms with each value equally likely.
|
|
87
|
+
|
|
77
88
|
Notes
|
|
78
89
|
-----
|
|
79
|
-
If
|
|
80
|
-
|
|
90
|
+
If :attr:`mergeron.gen.ShareSpec.dist_type`:code:` == `:attr:`mergeron.gen.SHRConstants.UNI`,
|
|
91
|
+
then it is infeasible that
|
|
92
|
+
:attr:`mergeron.gen.ShareSpec.recapture_form`:code:` == `:attr:`mergeron.RECConstants.OUTIN`.
|
|
93
|
+
In other words, if firm-counts are unspecified, the recapture rate cannot be
|
|
94
|
+
estimated using outside good choice probabilities.
|
|
81
95
|
|
|
82
|
-
|
|
83
|
-
be specified as having a supported Dirichlet distribution
|
|
96
|
+
For a sample with explicit firm counts, market shares must
|
|
97
|
+
be specified as having a supported Dirichlet distribution
|
|
98
|
+
(see :class:`mergeron.gen.SHRConstants`).
|
|
84
99
|
|
|
85
100
|
"""
|
|
86
101
|
|
|
@@ -446,7 +461,8 @@ class UPPTestRegime:
|
|
|
446
461
|
default=UPPAggrSelector.MIN, validator=validators.instance_of(UPPAggrSelector)
|
|
447
462
|
)
|
|
448
463
|
divr_aggregator: UPPAggrSelector | None = field(
|
|
449
|
-
default=
|
|
464
|
+
default=UPPAggrSelector.MIN,
|
|
465
|
+
validator=validators.instance_of((UPPAggrSelector, type(None))),
|
|
450
466
|
)
|
|
451
467
|
|
|
452
468
|
|
|
@@ -7,6 +7,7 @@ from __future__ import annotations
|
|
|
7
7
|
from typing import Literal
|
|
8
8
|
|
|
9
9
|
import numpy as np
|
|
10
|
+
from attrs import evolve
|
|
10
11
|
from numpy.random import SeedSequence
|
|
11
12
|
from numpy.typing import NDArray
|
|
12
13
|
|
|
@@ -21,11 +22,12 @@ from . import (
|
|
|
21
22
|
FCOUNT_WTS_DEFAULT,
|
|
22
23
|
FM2Constants,
|
|
23
24
|
MarginDataSample,
|
|
24
|
-
MarketSpec,
|
|
25
25
|
PCMConstants,
|
|
26
|
+
PCMSpec,
|
|
26
27
|
PriceConstants,
|
|
27
28
|
PriceDataSample,
|
|
28
29
|
ShareDataSample,
|
|
30
|
+
ShareSpec,
|
|
29
31
|
SHRConstants,
|
|
30
32
|
SSZConstants,
|
|
31
33
|
)
|
|
@@ -35,7 +37,7 @@ __version__ = VERSION
|
|
|
35
37
|
|
|
36
38
|
def _gen_share_data(
|
|
37
39
|
_sample_size: int,
|
|
38
|
-
|
|
40
|
+
_share_spec: ShareSpec,
|
|
39
41
|
_fcount_rng_seed_seq: SeedSequence | None,
|
|
40
42
|
_mktshr_rng_seed_seq: SeedSequence,
|
|
41
43
|
_nthreads: int = 16,
|
|
@@ -45,8 +47,8 @@ def _gen_share_data(
|
|
|
45
47
|
|
|
46
48
|
Parameters
|
|
47
49
|
----------
|
|
48
|
-
|
|
49
|
-
Class specifying parameters for
|
|
50
|
+
_share_spec
|
|
51
|
+
Class specifying parameters for generating market share data
|
|
50
52
|
_fcount_rng_seed_seq
|
|
51
53
|
Seed sequence for assuring independent and, optionally, redundant streams
|
|
52
54
|
_mktshr_rng_seed_seq
|
|
@@ -60,44 +62,43 @@ def _gen_share_data(
|
|
|
60
62
|
|
|
61
63
|
"""
|
|
62
64
|
|
|
63
|
-
_recapture_form, _dist_type_mktshr, _dist_parms_mktshr,
|
|
64
|
-
getattr(
|
|
65
|
+
_recapture_form, _dist_type_mktshr, _dist_parms_mktshr, _firm_count_prob_wts = (
|
|
66
|
+
getattr(_share_spec, _f)
|
|
65
67
|
for _f in ("recapture_form", "dist_type", "dist_parms", "firm_counts_weights")
|
|
66
68
|
)
|
|
67
69
|
|
|
68
70
|
_ssz = _sample_size
|
|
69
71
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
)
|
|
72
|
+
if _dist_type_mktshr == SHRConstants.UNI:
|
|
73
|
+
_mkt_share_sample = _gen_market_shares_uniform(
|
|
74
|
+
_ssz, _dist_parms_mktshr, _mktshr_rng_seed_seq, _nthreads
|
|
75
|
+
)
|
|
75
76
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
77
|
+
elif _dist_type_mktshr.name.startswith("DIR_"):
|
|
78
|
+
_firm_count_prob_wts = (
|
|
79
|
+
None
|
|
80
|
+
if _firm_count_prob_wts is None
|
|
81
|
+
else np.array(_firm_count_prob_wts, dtype=np.float64)
|
|
82
|
+
)
|
|
83
|
+
_mkt_share_sample = _gen_market_shares_dirichlet_multisample(
|
|
84
|
+
_ssz,
|
|
85
|
+
_recapture_form,
|
|
86
|
+
_dist_type_mktshr,
|
|
87
|
+
_dist_parms_mktshr,
|
|
88
|
+
_firm_count_prob_wts,
|
|
89
|
+
_fcount_rng_seed_seq,
|
|
90
|
+
_mktshr_rng_seed_seq,
|
|
91
|
+
_nthreads,
|
|
92
|
+
)
|
|
92
93
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
94
|
+
else:
|
|
95
|
+
raise ValueError(
|
|
96
|
+
f'Unexpected type, "{_dist_type_mktshr}" for share distribution.'
|
|
97
|
+
)
|
|
97
98
|
|
|
98
99
|
# If recapture_form == "inside-out", recalculate _aggregate_purchase_prob
|
|
99
100
|
_frmshr_array = _mkt_share_sample.mktshr_array[:, :2]
|
|
100
|
-
_r_bar =
|
|
101
|
+
_r_bar = _share_spec.recapture_rate or 0.85
|
|
101
102
|
if _recapture_form == RECConstants.INOUT:
|
|
102
103
|
_mkt_share_sample = ShareDataSample(
|
|
103
104
|
_mkt_share_sample.mktshr_array,
|
|
@@ -392,15 +393,163 @@ def _gen_market_shares_dirichlet(
|
|
|
392
393
|
)
|
|
393
394
|
|
|
394
395
|
|
|
396
|
+
def _gen_margin_price_data(
|
|
397
|
+
_frmshr_array: NDArray[np.float64],
|
|
398
|
+
_nth_firm_share: NDArray[np.float64],
|
|
399
|
+
_aggregate_purchase_prob: NDArray[np.float64],
|
|
400
|
+
_pcm_spec: PCMSpec,
|
|
401
|
+
_price_spec: PriceConstants,
|
|
402
|
+
_hsr_filing_test_type: SSZConstants,
|
|
403
|
+
_pcm_rng_seed_seq: SeedSequence,
|
|
404
|
+
_pr_rng_seed_seq: SeedSequence | None = None,
|
|
405
|
+
_nthreads: int = 16,
|
|
406
|
+
/,
|
|
407
|
+
) -> tuple[MarginDataSample, PriceDataSample]:
|
|
408
|
+
_price_array, _price_ratio_array = (
|
|
409
|
+
np.ones_like(_frmshr_array, np.float64),
|
|
410
|
+
np.empty_like(_frmshr_array, np.float64),
|
|
411
|
+
)
|
|
412
|
+
|
|
413
|
+
_pr_max_ratio = 5.0
|
|
414
|
+
match _price_spec:
|
|
415
|
+
case PriceConstants.SYM:
|
|
416
|
+
_nth_firm_price = np.ones((len(_frmshr_array), 1))
|
|
417
|
+
case PriceConstants.POS:
|
|
418
|
+
_price_array, _nth_firm_price = (
|
|
419
|
+
np.ceil(_p * _pr_max_ratio) for _p in (_frmshr_array, _nth_firm_share)
|
|
420
|
+
)
|
|
421
|
+
case PriceConstants.NEG:
|
|
422
|
+
_price_array, _nth_firm_price = (
|
|
423
|
+
np.ceil((1 - _p) * _pr_max_ratio)
|
|
424
|
+
for _p in (_frmshr_array, _nth_firm_share)
|
|
425
|
+
)
|
|
426
|
+
case PriceConstants.ZERO:
|
|
427
|
+
_price_array_gen = prng(_pr_rng_seed_seq).choice(
|
|
428
|
+
1 + np.arange(_pr_max_ratio), size=(len(_frmshr_array), 3)
|
|
429
|
+
)
|
|
430
|
+
_price_array = _price_array_gen[:, :2]
|
|
431
|
+
_nth_firm_price = _price_array_gen[:, [2]]
|
|
432
|
+
# del _price_array_gen
|
|
433
|
+
case PriceConstants.CSY:
|
|
434
|
+
# TODO:
|
|
435
|
+
# evolve FM2Constraint (save running MNL test twice); evolve copy of _mkt_sample_spec=1q
|
|
436
|
+
# generate the margin data
|
|
437
|
+
# generate price and margin data
|
|
438
|
+
_frmshr_array_plus = np.hstack((_frmshr_array, _nth_firm_share))
|
|
439
|
+
_pcm_spec_here = evolve(_pcm_spec, firm2_pcm_constraint=FM2Constants.IID)
|
|
440
|
+
_margin_data = _gen_margin_data(
|
|
441
|
+
_frmshr_array_plus,
|
|
442
|
+
np.ones_like(_frmshr_array_plus, np.float64),
|
|
443
|
+
_aggregate_purchase_prob,
|
|
444
|
+
_pcm_spec_here,
|
|
445
|
+
_pcm_rng_seed_seq,
|
|
446
|
+
_nthreads,
|
|
447
|
+
)
|
|
448
|
+
|
|
449
|
+
_pcm_array, _mnl_test_array = (
|
|
450
|
+
getattr(_margin_data, _f) for _f in ("pcm_array", "mnl_test_array")
|
|
451
|
+
)
|
|
452
|
+
|
|
453
|
+
_price_array_here = 1 / (1 - _pcm_array)
|
|
454
|
+
_price_array = _price_array_here[:, :2]
|
|
455
|
+
_nth_firm_price = _price_array_here[:, [-1]]
|
|
456
|
+
if _pcm_spec.firm2_pcm_constraint == FM2Constants.MNL:
|
|
457
|
+
# Generate i.i.d. PCMs then take PCM0 and construct PCM1
|
|
458
|
+
# Regenerate MNL test
|
|
459
|
+
_purchase_prob_array = _aggregate_purchase_prob * _frmshr_array
|
|
460
|
+
_pcm_array[:, 1] = np.divide(
|
|
461
|
+
(
|
|
462
|
+
_m1_nr := np.divide(
|
|
463
|
+
np.einsum(
|
|
464
|
+
"ij,ij,ij->ij",
|
|
465
|
+
_price_array[:, [0]],
|
|
466
|
+
_pcm_array[:, [0]],
|
|
467
|
+
1 - _purchase_prob_array[:, [0]],
|
|
468
|
+
),
|
|
469
|
+
1 - _purchase_prob_array[:, [1]],
|
|
470
|
+
)
|
|
471
|
+
),
|
|
472
|
+
1 + _m1_nr,
|
|
473
|
+
)
|
|
474
|
+
_mnl_test_array = (_pcm_array[:, [1]] >= 0) & (_pcm_array[:, [1]] <= 1)
|
|
475
|
+
else:
|
|
476
|
+
# Generate i.i.d. PCMs
|
|
477
|
+
# Construct price_array = 1/ (1 - pcm_array)
|
|
478
|
+
# Rgenerate MNL test
|
|
479
|
+
pass
|
|
480
|
+
|
|
481
|
+
_margin_data = MarginDataSample(_pcm_array[:, :2], _mnl_test_array)
|
|
482
|
+
del _price_array_here
|
|
483
|
+
case _:
|
|
484
|
+
raise ValueError(
|
|
485
|
+
f"Specitication of price distribution"
|
|
486
|
+
f' "{_price_spec.value}" is invalid.'
|
|
487
|
+
)
|
|
488
|
+
if _price_spec != PriceConstants.CSY:
|
|
489
|
+
_margin_data = _gen_margin_data(
|
|
490
|
+
_frmshr_array,
|
|
491
|
+
_price_array,
|
|
492
|
+
_aggregate_purchase_prob,
|
|
493
|
+
_pcm_spec,
|
|
494
|
+
_pcm_rng_seed_seq,
|
|
495
|
+
_nthreads,
|
|
496
|
+
)
|
|
497
|
+
|
|
498
|
+
_price_array = _price_array.astype(np.float64)
|
|
499
|
+
_rev_array = _price_array * _frmshr_array
|
|
500
|
+
_nth_firm_rev = _nth_firm_price * _nth_firm_share
|
|
501
|
+
|
|
502
|
+
# Although `_test_rev_ratio_inv` is not fixed at 10%,
|
|
503
|
+
# the ratio has not changed since inception of the HSR filing test,
|
|
504
|
+
# so we treat it as a constant of merger enforcement policy.
|
|
505
|
+
_test_rev_ratio, _test_rev_ratio_inv = 10, 1 / 10
|
|
506
|
+
|
|
507
|
+
match _hsr_filing_test_type:
|
|
508
|
+
case SSZConstants.HSR_TEN:
|
|
509
|
+
# See, https://www.ftc.gov/enforcement/premerger-notification-program/
|
|
510
|
+
# -> Procedures For Submitting Post-Consummation Filings
|
|
511
|
+
# -> Key Elements to Determine Whether a Post Consummation Filing is Required
|
|
512
|
+
# under heading, "Historical Thresholds"
|
|
513
|
+
# Revenue ratio has been 10-to-1 since inception
|
|
514
|
+
# Thus, a simple form of the HSR filing test would impose a 10-to-1
|
|
515
|
+
# ratio restriction on the merging firms' revenues
|
|
516
|
+
_rev_ratio = (_rev_array.min(axis=1) / _rev_array.max(axis=1)).round(4)
|
|
517
|
+
_hsr_filing_test = _rev_ratio >= _test_rev_ratio_inv
|
|
518
|
+
# del _rev_array, _rev_ratio
|
|
519
|
+
case SSZConstants.HSR_NTH:
|
|
520
|
+
# To get around the 10-to-1 ratio restriction, specify that the nth firm test:
|
|
521
|
+
# if the smaller merging firm matches or exceeds the n-th firm in size, and
|
|
522
|
+
# the larger merging firm has at least 10 times the size of the nth firm,
|
|
523
|
+
# the size test is considered met.
|
|
524
|
+
# Alternatively, if the smaller merging firm has 10% or greater share,
|
|
525
|
+
# the value of transaction test is considered met.
|
|
526
|
+
_rev_ratio_to_nth = np.round(np.sort(_rev_array, axis=1) / _nth_firm_rev, 4)
|
|
527
|
+
_hsr_filing_test = (
|
|
528
|
+
np.einsum(
|
|
529
|
+
"ij->i",
|
|
530
|
+
1 * (_rev_ratio_to_nth > [1, _test_rev_ratio]),
|
|
531
|
+
dtype=np.int64,
|
|
532
|
+
)
|
|
533
|
+
== _rev_ratio_to_nth.shape[1]
|
|
534
|
+
) | (_frmshr_array.min(axis=1) >= _test_rev_ratio_inv)
|
|
535
|
+
|
|
536
|
+
# del _nth_firm_rev, _rev_ratio_to_nth
|
|
537
|
+
case _:
|
|
538
|
+
# Otherwise, all draws meet the filing test
|
|
539
|
+
_hsr_filing_test = np.ones(len(_frmshr_array), dtype=bool)
|
|
540
|
+
|
|
541
|
+
return _margin_data, PriceDataSample(_price_array, _hsr_filing_test)
|
|
542
|
+
|
|
543
|
+
|
|
544
|
+
# marked for deletion
|
|
395
545
|
def _gen_price_data(
|
|
396
546
|
_frmshr_array: NDArray[np.float64],
|
|
397
547
|
_nth_firm_share: NDArray[np.float64],
|
|
398
|
-
|
|
548
|
+
_price_spec: PriceConstants,
|
|
549
|
+
_hsr_filing_test_type: SSZConstants,
|
|
399
550
|
_seed_seq: SeedSequence | None = None,
|
|
400
551
|
/,
|
|
401
552
|
) -> PriceDataSample:
|
|
402
|
-
_hsr_filing_test_type = _mkt_sample_spec.hsr_filing_test_type
|
|
403
|
-
|
|
404
553
|
_price_array, _price_ratio_array, _hsr_filing_test = (
|
|
405
554
|
np.ones_like(_frmshr_array, np.float64),
|
|
406
555
|
np.empty_like(_frmshr_array, np.float64),
|
|
@@ -408,7 +557,7 @@ def _gen_price_data(
|
|
|
408
557
|
)
|
|
409
558
|
|
|
410
559
|
_pr_max_ratio = 5.0
|
|
411
|
-
match
|
|
560
|
+
match _price_spec:
|
|
412
561
|
case PriceConstants.SYM:
|
|
413
562
|
_nth_firm_price = np.ones((len(_frmshr_array), 1))
|
|
414
563
|
case PriceConstants.POS:
|
|
@@ -427,10 +576,12 @@ def _gen_price_data(
|
|
|
427
576
|
_price_array = _price_array_gen[:, :2]
|
|
428
577
|
_nth_firm_price = _price_array_gen[:, [2]]
|
|
429
578
|
# del _price_array_gen
|
|
579
|
+
case PriceConstants.CSY:
|
|
580
|
+
raise ValueError("Market-wide cost-symmetry is not yet implemented.")
|
|
430
581
|
case _:
|
|
431
582
|
raise ValueError(
|
|
432
|
-
f"
|
|
433
|
-
f' "{
|
|
583
|
+
f"Specitication of price distribution"
|
|
584
|
+
f' "{_price_spec.value}" is invalid.'
|
|
434
585
|
)
|
|
435
586
|
|
|
436
587
|
_price_array = _price_array.astype(np.float64)
|
|
@@ -471,7 +622,6 @@ def _gen_price_data(
|
|
|
471
622
|
== _rev_ratio_to_nth.shape[1]
|
|
472
623
|
) | (_frmshr_array.min(axis=1) >= _test_rev_ratio_inv)
|
|
473
624
|
|
|
474
|
-
# del _nth_firm_rev, _rev_ratio_to_nth
|
|
475
625
|
case _:
|
|
476
626
|
# Otherwise, all draws meet the filing test
|
|
477
627
|
_hsr_filing_test = np.ones(len(_frmshr_array), dtype=bool)
|
|
@@ -479,23 +629,26 @@ def _gen_price_data(
|
|
|
479
629
|
return PriceDataSample(_price_array, _hsr_filing_test)
|
|
480
630
|
|
|
481
631
|
|
|
482
|
-
def
|
|
632
|
+
def _gen_margin_data(
|
|
483
633
|
_frmshr_array: NDArray[np.float64],
|
|
484
634
|
_price_array: NDArray[np.float64],
|
|
485
635
|
_aggregate_purchase_prob: NDArray[np.float64],
|
|
486
|
-
|
|
636
|
+
_pcm_spec: PCMSpec,
|
|
487
637
|
_pcm_rng_seed_seq: SeedSequence,
|
|
488
638
|
_nthreads: int = 16,
|
|
489
639
|
/,
|
|
490
640
|
) -> MarginDataSample:
|
|
491
641
|
_dist_type_pcm, _dist_firm2_pcm, _dist_parms_pcm = (
|
|
492
|
-
getattr(
|
|
642
|
+
getattr(_pcm_spec, _f)
|
|
493
643
|
for _f in ("dist_type", "firm2_pcm_constraint", "dist_parms")
|
|
494
644
|
)
|
|
495
645
|
|
|
496
646
|
_dist_type: Literal["Beta", "Uniform"]
|
|
497
|
-
_pcm_array =
|
|
498
|
-
|
|
647
|
+
_pcm_array = (
|
|
648
|
+
np.empty((len(_frmshr_array), 1), dtype=np.float64)
|
|
649
|
+
if _pcm_spec.firm2_pcm_constraint == FM2Constants.SYM
|
|
650
|
+
else np.empty_like(_frmshr_array, dtype=np.float64)
|
|
651
|
+
)
|
|
499
652
|
|
|
500
653
|
_beta_min, _beta_max = [None] * 2 # placeholder
|
|
501
654
|
if _dist_type_pcm == PCMConstants.EMPR:
|
|
@@ -534,25 +687,33 @@ def _gen_pcm_data(
|
|
|
534
687
|
_pcm_array = (_beta_max - _beta_min) * _pcm_array + _beta_min
|
|
535
688
|
del _beta_min, _beta_max
|
|
536
689
|
|
|
690
|
+
if _dist_firm2_pcm == FM2Constants.SYM:
|
|
691
|
+
_pcm_array = np.column_stack((_pcm_array,) * _frmshr_array.shape[1])
|
|
537
692
|
if _dist_firm2_pcm == FM2Constants.MNL:
|
|
538
693
|
# Impose FOCs from profit-maximization with MNL demand
|
|
539
|
-
|
|
694
|
+
if _dist_type_pcm == PCMConstants.EMPR:
|
|
695
|
+
print(
|
|
696
|
+
"NOTE: Estimated Firm 2 parameters will not be consistent with "
|
|
697
|
+
"the empirical distribution of margins in the source data. For "
|
|
698
|
+
"consistency, respecify pcm_spec.firm2_pcm_constraint = FM2Constants.IID."
|
|
699
|
+
)
|
|
700
|
+
_purchase_prob_array = _aggregate_purchase_prob * _frmshr_array
|
|
540
701
|
|
|
541
702
|
_pcm_array[:, [1]] = np.divide(
|
|
542
703
|
np.einsum(
|
|
543
704
|
"ij,ij,ij->ij",
|
|
544
705
|
_price_array[:, [0]],
|
|
545
706
|
_pcm_array[:, [0]],
|
|
546
|
-
1 -
|
|
707
|
+
1 - _purchase_prob_array[:, [0]],
|
|
708
|
+
),
|
|
709
|
+
np.einsum(
|
|
710
|
+
"ij,ij->ij", _price_array[:, [1]], 1 - _purchase_prob_array[:, [1]]
|
|
547
711
|
),
|
|
548
|
-
np.einsum("ij,ij->ij", _price_array[:, [1]], 1 - _purchprob_array[:, [1]]),
|
|
549
712
|
)
|
|
550
713
|
|
|
551
714
|
_mnl_test_array = _pcm_array[:, 1].__ge__(0) & _pcm_array[:, 1].__le__(1)
|
|
552
715
|
else:
|
|
553
716
|
_mnl_test_array = np.ones(len(_pcm_array), dtype=bool)
|
|
554
|
-
if _dist_firm2_pcm == FM2Constants.SYM:
|
|
555
|
-
_pcm_array[:, [1]] = _pcm_array[:, [0]]
|
|
556
717
|
|
|
557
718
|
return MarginDataSample(_pcm_array, _mnl_test_array)
|
|
558
719
|
|
mergeron/gen/data_generation.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Methods to generate data
|
|
2
|
+
Methods to generate market data, including shares price, marginsm, and diversion ratios
|
|
3
|
+
for analyzing merger enforcement policy.
|
|
3
4
|
|
|
4
5
|
"""
|
|
5
6
|
|
|
@@ -21,7 +22,7 @@ from . import (
|
|
|
21
22
|
SHRConstants,
|
|
22
23
|
SSZConstants,
|
|
23
24
|
)
|
|
24
|
-
from ._data_generation_functions import
|
|
25
|
+
from ._data_generation_functions import _gen_margin_price_data, _gen_share_data
|
|
25
26
|
|
|
26
27
|
__version__ = VERSION
|
|
27
28
|
|
|
@@ -42,24 +43,13 @@ def gen_market_sample(
|
|
|
42
43
|
nthreads: int = 16,
|
|
43
44
|
) -> MarketDataSample:
|
|
44
45
|
"""
|
|
45
|
-
Generate share, diversion ratio, price, and margin data
|
|
46
|
+
Generate share, diversion ratio, price, and margin data for MarketSpec.
|
|
46
47
|
|
|
47
|
-
Diversion ratios generated assuming share-proportionality, unless
|
|
48
|
-
`recapture_form` = "proportional", in which case both firms' recapture rate
|
|
49
|
-
is set to `r_bar`.
|
|
50
|
-
|
|
51
|
-
The tuple of SeedSequences, if specified, is parsed in the following order
|
|
52
|
-
for generating the relevant random variates:
|
|
53
|
-
1.) quantity shares
|
|
54
|
-
2.) price-cost margins
|
|
55
|
-
3.) firm-counts, from :code:`[2, 2 + len(firm_counts_weights)]`,
|
|
56
|
-
weighted by :code:`firm_counts_weights`, where relevant
|
|
57
|
-
4.) prices, if :code:`price_spec == PriceConstants.ZERO`.
|
|
58
48
|
|
|
59
49
|
Parameters
|
|
60
50
|
----------
|
|
61
51
|
_mkt_sample_spec
|
|
62
|
-
class specifying parameters for data generation
|
|
52
|
+
class specifying parameters for data generation, see :class:`mergeron.gen.MarketSpec`
|
|
63
53
|
sample_size
|
|
64
54
|
number of draws to generate
|
|
65
55
|
seed_seq_list
|
|
@@ -100,7 +90,7 @@ def gen_market_sample(
|
|
|
100
90
|
# Generate share data
|
|
101
91
|
_mktshr_data = _gen_share_data(
|
|
102
92
|
_shr_sample_size,
|
|
103
|
-
_mkt_sample_spec,
|
|
93
|
+
_mkt_sample_spec.share_spec,
|
|
104
94
|
_fcount_rng_seed_seq,
|
|
105
95
|
_mktshr_rng_seed_seq,
|
|
106
96
|
nthreads,
|
|
@@ -116,40 +106,28 @@ def gen_market_sample(
|
|
|
116
106
|
)
|
|
117
107
|
)
|
|
118
108
|
|
|
119
|
-
# Generate merging-firm price data
|
|
120
|
-
_price_data =
|
|
121
|
-
_mktshr_array[:, :2],
|
|
109
|
+
# Generate merging-firm price and PCM data
|
|
110
|
+
_margin_data, _price_data = _gen_margin_price_data(
|
|
111
|
+
_mktshr_array[:, :2],
|
|
112
|
+
_nth_firm_share,
|
|
113
|
+
_aggregate_purchase_prob,
|
|
114
|
+
_mkt_sample_spec.pcm_spec,
|
|
115
|
+
_mkt_sample_spec.price_spec,
|
|
116
|
+
_mkt_sample_spec.hsr_filing_test_type,
|
|
117
|
+
_pcm_rng_seed_seq,
|
|
118
|
+
_pr_rng_seed_seq,
|
|
119
|
+
nthreads,
|
|
122
120
|
)
|
|
123
121
|
|
|
124
122
|
_price_array, _hsr_filing_test = (
|
|
125
123
|
getattr(_price_data, _f) for _f in ("price_array", "hsr_filing_test")
|
|
126
124
|
)
|
|
127
125
|
|
|
128
|
-
if _hsr_filing_test_type != SSZConstants.ONE:
|
|
129
|
-
_mktshr_array = _mktshr_array[_hsr_filing_test]
|
|
130
|
-
_fcounts = _fcounts[_hsr_filing_test]
|
|
131
|
-
_aggregate_purchase_prob = _aggregate_purchase_prob[_hsr_filing_test]
|
|
132
|
-
_nth_firm_share = _nth_firm_share[_hsr_filing_test]
|
|
133
|
-
_price_array = _price_array[_hsr_filing_test]
|
|
134
|
-
|
|
135
|
-
# Calculate diversion ratios
|
|
136
|
-
_divr_array = gen_divr_array(
|
|
137
|
-
_recapture_form, _recapture_rate, _mktshr_array[:, :2], _aggregate_purchase_prob
|
|
138
|
-
)
|
|
139
|
-
|
|
140
|
-
# Generate margin data
|
|
141
|
-
_pcm_data = _gen_pcm_data(
|
|
142
|
-
_mktshr_array[:, :2],
|
|
143
|
-
_price_array,
|
|
144
|
-
_aggregate_purchase_prob,
|
|
145
|
-
_mkt_sample_spec,
|
|
146
|
-
_pcm_rng_seed_seq,
|
|
147
|
-
nthreads,
|
|
148
|
-
)
|
|
149
126
|
_pcm_array, _mnl_test_rows = (
|
|
150
|
-
getattr(
|
|
127
|
+
getattr(_margin_data, _f) for _f in ("pcm_array", "mnl_test_array")
|
|
151
128
|
)
|
|
152
129
|
|
|
130
|
+
_mnl_test_rows = _mnl_test_rows * _hsr_filing_test
|
|
153
131
|
_s_size = sample_size # originally-specified sample size
|
|
154
132
|
if _dist_firm2_pcm == FM2Constants.MNL:
|
|
155
133
|
_mktshr_array = _mktshr_array[_mnl_test_rows][:_s_size]
|
|
@@ -158,7 +136,11 @@ def gen_market_sample(
|
|
|
158
136
|
_fcounts = _fcounts[_mnl_test_rows][:_s_size]
|
|
159
137
|
_aggregate_purchase_prob = _aggregate_purchase_prob[_mnl_test_rows][:_s_size]
|
|
160
138
|
_nth_firm_share = _nth_firm_share[_mnl_test_rows][:_s_size]
|
|
161
|
-
|
|
139
|
+
|
|
140
|
+
# Calculate diversion ratios
|
|
141
|
+
_divr_array = gen_divr_array(
|
|
142
|
+
_recapture_form, _recapture_rate, _mktshr_array[:, :2], _aggregate_purchase_prob
|
|
143
|
+
)
|
|
162
144
|
|
|
163
145
|
del _mnl_test_rows, _s_size
|
|
164
146
|
|
|
@@ -188,7 +170,33 @@ def parse_seed_seq_list(
|
|
|
188
170
|
_price_spec: PriceConstants,
|
|
189
171
|
/,
|
|
190
172
|
) -> SeedSequenceData:
|
|
191
|
-
"""Initialize RNG seed sequences to ensure independence of distinct random streams.
|
|
173
|
+
"""Initialize RNG seed sequences to ensure independence of distinct random streams.
|
|
174
|
+
|
|
175
|
+
The tuple of SeedSequences, is parsed in the following order
|
|
176
|
+
for generating the relevant random variates:
|
|
177
|
+
1.) quantity shares
|
|
178
|
+
2.) price-cost margins
|
|
179
|
+
3.) firm-counts, if :code:`MarketSpec.share_spec.dist_type` is a Dirichlet distribution
|
|
180
|
+
4.) prices, if :code:`MarketSpec.price_spec ==`:attr:`mergeron.gen.PriceConstants.ZERO`.
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
Parameters
|
|
185
|
+
----------
|
|
186
|
+
_sseq_list
|
|
187
|
+
List of RNG seed sequences
|
|
188
|
+
|
|
189
|
+
_mktshr_dist_type
|
|
190
|
+
Market share distribution type
|
|
191
|
+
|
|
192
|
+
_price_spec
|
|
193
|
+
Price specification
|
|
194
|
+
|
|
195
|
+
Returns
|
|
196
|
+
-------
|
|
197
|
+
Seed sequence data
|
|
198
|
+
|
|
199
|
+
"""
|
|
192
200
|
_fcount_rng_seed_seq: SeedSequence | None = None
|
|
193
201
|
_pr_rng_seed_seq: SeedSequence | None = None
|
|
194
202
|
|
mergeron/gen/market_sample.py
CHANGED
|
@@ -23,22 +23,51 @@ class MarketSample(MarketSpec):
|
|
|
23
23
|
self,
|
|
24
24
|
/,
|
|
25
25
|
*,
|
|
26
|
-
sample_size: int
|
|
26
|
+
sample_size: int,
|
|
27
27
|
seed_seq_list: list[SeedSequence] | None,
|
|
28
28
|
nthreads: int,
|
|
29
|
-
save_data_to_file: SaveData
|
|
29
|
+
save_data_to_file: SaveData,
|
|
30
30
|
) -> None:
|
|
31
|
+
"""Generate market data
|
|
32
|
+
|
|
33
|
+
Parameters
|
|
34
|
+
----------
|
|
35
|
+
sample_size
|
|
36
|
+
Size of the market sample drawn
|
|
37
|
+
|
|
38
|
+
seed_seq_list
|
|
39
|
+
List of :code:`numpy.random.SeedSequence` objects
|
|
40
|
+
|
|
41
|
+
nthreads
|
|
42
|
+
Number of threads to use
|
|
43
|
+
|
|
44
|
+
save_data_to_file
|
|
45
|
+
Save data to given HDF5 file, at specified group node
|
|
46
|
+
|
|
47
|
+
Returns
|
|
48
|
+
-------
|
|
49
|
+
None
|
|
50
|
+
|
|
51
|
+
Notes
|
|
52
|
+
-----
|
|
53
|
+
See documentation for :class:`mergeron.gen.data_generation.gen_market_sample`
|
|
54
|
+
for more information, and :func:`mergeron.gen.data_generation.parse_seed_seq_list`
|
|
55
|
+
on the specification of :code:`seed_seq_list`.
|
|
56
|
+
|
|
57
|
+
"""
|
|
31
58
|
self.data = gen_market_sample(
|
|
32
59
|
self,
|
|
33
60
|
sample_size=sample_size,
|
|
34
61
|
seed_seq_list=seed_seq_list,
|
|
35
62
|
nthreads=nthreads,
|
|
36
63
|
)
|
|
64
|
+
|
|
37
65
|
_invalid_array_names = (
|
|
38
66
|
("fcounts", "choice_prob_outgd", "nth_firm_share", "hhi_post")
|
|
39
67
|
if self.share_spec.dist_type == "Uniform"
|
|
40
68
|
else ()
|
|
41
69
|
)
|
|
70
|
+
|
|
42
71
|
if save_data_to_file:
|
|
43
72
|
save_data_to_hdf5(
|
|
44
73
|
self.data,
|
|
@@ -57,19 +86,58 @@ class MarketSample(MarketSpec):
|
|
|
57
86
|
nthreads: int,
|
|
58
87
|
save_data_to_file: SaveData = False,
|
|
59
88
|
) -> None:
|
|
89
|
+
"""Generate market data
|
|
90
|
+
|
|
91
|
+
Parameters
|
|
92
|
+
----------
|
|
93
|
+
_enf_parm_vec
|
|
94
|
+
Threshold values for various Guidelines criteria
|
|
95
|
+
|
|
96
|
+
_upp_test_regime
|
|
97
|
+
Specifies whether to analyze enforcement, clearance, or both
|
|
98
|
+
and the GUPPI and diversion ratio aggregators employed, with
|
|
99
|
+
default being to analyze enforcement based on the maximum
|
|
100
|
+
merging-firm GUPPI and maximum diversion ratio between the
|
|
101
|
+
merging firms
|
|
102
|
+
|
|
103
|
+
sample_size
|
|
104
|
+
Size of the market sample drawn
|
|
105
|
+
|
|
106
|
+
seed_seq_list
|
|
107
|
+
List of :code:`numpy.random.SeedSequence` objects
|
|
108
|
+
|
|
109
|
+
nthreads
|
|
110
|
+
Number of threads to use
|
|
111
|
+
|
|
112
|
+
save_data_to_file
|
|
113
|
+
Save data to given HDF5 file, at specified group node
|
|
114
|
+
|
|
115
|
+
Returns
|
|
116
|
+
-------
|
|
117
|
+
None
|
|
118
|
+
|
|
119
|
+
Notes
|
|
120
|
+
-----
|
|
121
|
+
See documentation for :class:`mergeron.gen.MarketSpec` for details on specifying
|
|
122
|
+
how shares, margins, prices, and diversion ratios are generated, and whether to restrict
|
|
123
|
+
the sample to draws representing mergers that meet the HSR filing requirements. See
|
|
124
|
+
:class:`mergeron.gen.MarketDataSample` on the sample data generated; see,
|
|
125
|
+
:func:`mergeron.gen.data_generation.parse_seed_seq_list` on
|
|
126
|
+
the specification of :code:`seed_seq_list`.
|
|
127
|
+
|
|
128
|
+
"""
|
|
129
|
+
|
|
60
130
|
if getattr(self, "market_data_sample", None) is None:
|
|
61
131
|
self.enf_counts = sim_enf_cnts_ll(
|
|
62
132
|
self,
|
|
63
133
|
_enf_parm_vec,
|
|
64
134
|
_upp_test_regime,
|
|
65
|
-
save_data_to_file=save_data_to_file,
|
|
66
135
|
sample_size=sample_size,
|
|
67
136
|
seed_seq_list=seed_seq_list,
|
|
68
137
|
nthreads=nthreads,
|
|
138
|
+
save_data_to_file=save_data_to_file,
|
|
69
139
|
)
|
|
70
140
|
else:
|
|
71
|
-
self.enf_counts = enf_cnts(
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
if save_data_to_file:
|
|
75
|
-
save_data_to_hdf5(self.enf_counts, save_data_to_file=save_data_to_file)
|
|
141
|
+
self.enf_counts = enf_cnts(self.data, _enf_parm_vec, _upp_test_regime)
|
|
142
|
+
if save_data_to_file:
|
|
143
|
+
save_data_to_hdf5(self.enf_counts, save_data_to_file=save_data_to_file)
|
mergeron/gen/upp_tests.py
CHANGED
|
@@ -11,7 +11,6 @@ from typing import Literal, TypeAlias, TypedDict
|
|
|
11
11
|
|
|
12
12
|
import numpy as np
|
|
13
13
|
import tables as ptb # type: ignore
|
|
14
|
-
from icecream import ic # type: ignore
|
|
15
14
|
from joblib import Parallel, cpu_count, delayed # type: ignore
|
|
16
15
|
from numpy.random import SeedSequence
|
|
17
16
|
from numpy.typing import NDArray
|
|
@@ -195,9 +194,7 @@ def sim_enf_cnts(
|
|
|
195
194
|
save_data_to_file=save_data_to_file,
|
|
196
195
|
)
|
|
197
196
|
|
|
198
|
-
_upp_test_arrays = enf_cnts(
|
|
199
|
-
_market_data_sample, _upp_test_parms, _sim_test_regime
|
|
200
|
-
)
|
|
197
|
+
_upp_test_arrays = enf_cnts(_market_data_sample, _upp_test_parms, _sim_test_regime)
|
|
201
198
|
|
|
202
199
|
save_data_to_hdf5(
|
|
203
200
|
_upp_test_arrays,
|
|
@@ -279,7 +276,7 @@ def enf_cnts(
|
|
|
279
276
|
try:
|
|
280
277
|
_hhi_zone_post_ranged = esl.hhi_zone_post_ranger(_hhi_post)
|
|
281
278
|
except ValueError as _err:
|
|
282
|
-
|
|
279
|
+
print(_hhi_post)
|
|
283
280
|
raise _err
|
|
284
281
|
|
|
285
282
|
_stats_byconczone_sim = -1 * np.ones(_stats_rowlen + 1, np.int64)
|
|
@@ -310,9 +307,7 @@ def enf_cnts(
|
|
|
310
307
|
]),
|
|
311
308
|
))
|
|
312
309
|
|
|
313
|
-
_enf_cnts_sim_byconczone_array = esl.enf_cnts_byconczone(
|
|
314
|
-
_stats_byconczone_sim[1:]
|
|
315
|
-
)
|
|
310
|
+
_enf_cnts_sim_byconczone_array = esl.enf_cnts_byconczone(_stats_byconczone_sim[1:])
|
|
316
311
|
del _stats_byconczone_sim
|
|
317
312
|
del _hhi_delta, _hhi_post, _fcounts
|
|
318
313
|
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: mergeron
|
|
3
|
+
Version: 2024.739097.2
|
|
4
|
+
Summary: Merger Policy Analysis using Python
|
|
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,<4.0
|
|
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.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
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: google-re2 (>=1.1)
|
|
26
|
+
Requires-Dist: jinja2 (>=3.1)
|
|
27
|
+
Requires-Dist: joblib (>=1.3)
|
|
28
|
+
Requires-Dist: lxml (>=5.0)
|
|
29
|
+
Requires-Dist: matplotlib (>=3.8)
|
|
30
|
+
Requires-Dist: mpmath (>=1.3)
|
|
31
|
+
Requires-Dist: msgpack (>=1.0)
|
|
32
|
+
Requires-Dist: msgpack-numpy (>=0.4)
|
|
33
|
+
Requires-Dist: numpy (>=1.26,<2.0)
|
|
34
|
+
Requires-Dist: openpyxl (>=3.1.2)
|
|
35
|
+
Requires-Dist: poetry-plugin-export (>=1.8.0,<2.0.0)
|
|
36
|
+
Requires-Dist: requests (>=2.31)
|
|
37
|
+
Requires-Dist: requests-toolbelt (>=1.0.0)
|
|
38
|
+
Requires-Dist: scipy (>=1.12)
|
|
39
|
+
Requires-Dist: sympy (>=1.12)
|
|
40
|
+
Requires-Dist: tables (>=3.8)
|
|
41
|
+
Requires-Dist: types-beautifulsoup4 (>=4.11.2)
|
|
42
|
+
Requires-Dist: types-requests (>=2.31.0)
|
|
43
|
+
Requires-Dist: xlrd (>=2.0.1,<3.0.0)
|
|
44
|
+
Requires-Dist: xlsxwriter (>=3.1)
|
|
45
|
+
Description-Content-Type: text/x-rst
|
|
46
|
+
|
|
47
|
+
mergeron: Merger Policy Analysis using Python
|
|
48
|
+
=============================================
|
|
49
|
+
|
|
50
|
+
Analyze the sets of mergers conforming to concentration and diversion ratio bounds. Analyze 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 impled 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.
|
|
51
|
+
|
|
52
|
+
Intrinsic enforcement rates and intrinsice clearance rates are distinguished from *observed* clearance and enforcement rates in that the former are derived from analyzing theorectical predictions regarding firm conduct against enforcement thresholds, treating enforcement policy as exogenous to firm conduct. 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, ny definition, the observed clearance rate is 1 minus the observed enforement rate.
|
|
53
|
+
|
|
54
|
+
Introduction
|
|
55
|
+
------------
|
|
56
|
+
|
|
57
|
+
Module :code:`mergeron.core.guidelines_boundaries` includes classes for specifying concentration bounds (:code:`mergeron.core.guidelines_boundaries.ConcentrationBoundary`) and diversion-ratio bounds (:code:`mergeron.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 concentation and diversion-ratio boundaries, and functions for mapping GUPPI standards to concentration (ΔHHI) standards, and vice-versa.
|
|
58
|
+
|
|
59
|
+
Module :code:`mergeron.gen.market_sample` includes the :code:`mergeron.gen.market_sample.MarketSample` with methods for, (i) generating sample data under a rich specification of shares, diversion ratios, margins, prices, and HSR filing requirements, and (ii) for estimating enforcement or clearance rates under specified enforcement regimes given a method of aggregating diversion ratio or GUPPI estimates for the firms in a merger. Notably. share are genarated not just for markets with a fixed number of firms, but for markets with multiple firm-count weights, which may be left unspecified or explicitly specified.
|
|
60
|
+
|
|
61
|
+
Unless otherwise specified, 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, with specified shape parameters (see :code:`mergeron.gen.ShareContants`. When drawing shares from the Dirichlet distribution, the user passes, using :code:`mergeron.gen.MarketSpec.ShareSpec.firm_count_weights`, 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 :code:`mergeron.gen.MarketSpec.ShareSpec.firm_count_weights` is not assigned a value when defining :code:`mergeron.gen.MarketSpec.ShareSpec` (which has type, :code:`mergeron.gen.ShareSpec`), the default values is used, with results in a sample of markets with 2 to 6 firms with equal relative frequency.
|
|
62
|
+
|
|
63
|
+
Recapture rates can be specifed as, "proportional", "inside-out", "outside-in" (see :code:`mergeron.RECConstants`. The "inside-out" specification results in recapture ratios consistent with merging-firms' in-market shares and a default recapture rate. The "outside-in" specification yields diversion ratios from purchase probabilities drawn at random for :math:`N+1` goods, from which are derived market shares and recapture rates for the :math:`N` goods in the putative market (see, :code:`mergeron.gen.DiversionRatioSpec`). The "outside-in" specification is invalid when the distribution of markets over firm-count is unspecified, i.e., when :code:`mergeron.gen.MarketSpec.ShareSpec.dist_type ==`:code:`mergeron.gen.ShareContants.UNI`.
|
|
64
|
+
|
|
65
|
+
Price-cost-margins may be specified as having uniform distribution, Beta distribution (including a bounded Beta distribution with specified mean and variance), or an empirical distribution. The 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 may be specified as symmetric, i.i.d., or subject to equilibrium conditions for (profit-mazimization in) Bertrand-Nash oligopoly with MNL demand (see, :code:`mergeron.gen.PCMSpec`).
|
|
66
|
+
|
|
67
|
+
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:`mergeron.gen.PriceSpec`).
|
|
68
|
+
|
|
69
|
+
The market sample may be restricted to mergers meeting the HSR filing requirement under two alternative approaches: in the one, the smaller of the two merging firms meets the HSR filing threshold for the smaller (acquired) firm. In the other, the :math:`n`-th firm's size matches the size requirement for the smaller merging firm (see, :code:`mergeron.gen.SSZConstants`). The second assumption avoids the unfortunate assumption in the first that, within the resulting sample, the larger merging firm be at least 10 times as large as the smaller merging firm, as a consequence of the full definition of the HSR filing requirement.
|
|
70
|
+
|
|
71
|
+
The full specification of a market sample is given in a :code:`mergeron.gen.market_sample.MarketSample` object, including the above parameters. Data are drawn by invoking :code:`mergeron.gen.market_sample.MarketSample.generate_sample` which adds a :code:`data` property of class, :code:`mergeron.gen.MarketDataSample`. Enforcement or clearance counts are computed by invoking :code:`mergeron.gen.market_sample.MarketSample.estimate_enf_counts`, which adds an :code:`enf_counts` property of class :code:`mergeron.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:`estimate_enf_counts` on a :code:`mergeron.gen.market_sample.MarketSample` object without first invoking :code:`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 perfomance penalty is slight, but can be considerable if saving to SATA storage).
|
|
72
|
+
|
|
73
|
+
Enforcement statistics based on FTC investigations data and test data are printed to screen or rendered to LaTex files (for processing into publication-quality tables) using methods provided in :code:`mergeron.gen.enforcement_stats`.
|
|
74
|
+
|
|
75
|
+
Programs demonstrating the use of this package are included in the sub-package, :code:`mergeron.demo`.
|
|
76
|
+
|
|
77
|
+
This package includes a class, :code:`mergeron.core.pseudorandom_numbers.MulithreadedRNG` for generating random numbers with selected continuous distribution over specified parameters, and with CPU multithreading on machines with multiple virtual, logical, or physical CPU cores. This class is an adaptation from the documentation of the :code:`numpy` package, from the discussion on `multithreaded random-number 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:
|
|
78
|
+
|
|
79
|
+
.. code-block:: python
|
|
80
|
+
|
|
81
|
+
import mergeron.core.pseudorandom_numbers as prng
|
|
82
|
+
|
|
83
|
+
Documentation for this package is in the form of the API Reference. Documentation for individual functions and classes is accessible within a python shell. For example:
|
|
84
|
+
|
|
85
|
+
.. code-block:: python
|
|
86
|
+
|
|
87
|
+
import mergeron.core.market_sample as market_sample
|
|
88
|
+
|
|
89
|
+
help(market_sample.MarketSample)
|
|
90
|
+
|
|
91
|
+
"Extras" Subpackage
|
|
92
|
+
---------------------
|
|
93
|
+
|
|
94
|
+
The "Extras" sub-package includes a small number of modules potentially useful to users, but which do not implement the principal functions of the package, and are hence considered "extras" or "external" modules. One of these modules is, in fact, repackaged here although published independently.
|
|
95
|
+
|
|
96
|
+
On of the external modules provides methods for estimating confidence intervals for proportions and for contrasts (differences) in proportions. This module improve is coded for conformance to the literature and to results from the corresponding modules in :code:`R`. Although written from scratch, the APIs implemented in the module included here are designed for consistency with the APIs in, :code:`statsmodels.stats.proportion` from the package, :code:`statsmodels` (https://pypi.org/project/statsmodels/). To access these directly:
|
|
97
|
+
|
|
98
|
+
.. code-block:: python
|
|
99
|
+
|
|
100
|
+
import mergeron.ext.proportions_tests as prci
|
|
101
|
+
|
|
102
|
+
Module :code:`mergeron.ext.xlsxw_helper` is useful for writing highly formatted output to spreadsheets with xlsx format. The class, :code:`mergeron.ext.xlsxw_helper.CFmt` and function, :code:`mergeron.ext.xlsxw_helper.array_to_sheet` are of particular interest, and can be accessed as :code:`xlh.CFmt` and :code:`xlh.array_to_sheet` with the following import:
|
|
103
|
+
|
|
104
|
+
.. code-block:: python
|
|
105
|
+
|
|
106
|
+
import mergeron.ext.xlsxw_helper as xlsxw_helper
|
|
107
|
+
|
|
108
|
+
A recent version of Paul Tol's python module, :code:`tol_colors.py`, which provides high-contrast color schemes for making displays with improved visibility for individuals with color-blindness, is redistributed within this package. Other than re-formatting and type annotation, the :code:`mergeron.ext.tol_colors` module is re-distributed as downloaded from, https://personal.sron.nl/~pault/data/tol_colors.py. The :code:`tol_colors.py` module is distributed under the Standard 3-clause BSD license. To access the :code:`mergeron.ext.tol_colors` module directly:
|
|
109
|
+
|
|
110
|
+
.. code-block:: python
|
|
111
|
+
|
|
112
|
+
import mergeron.ext.tol_colors as ptc
|
|
113
|
+
|
|
114
|
+
.. image:: https://img.shields.io/endpoint?url=https://python-poetry.org/badge/v0.json
|
|
115
|
+
:alt: Poetry
|
|
116
|
+
:target: https://python-poetry.org/
|
|
117
|
+
|
|
118
|
+
.. image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json
|
|
119
|
+
:alt: Ruff
|
|
120
|
+
:target: https://github.com/astral-sh/ruff
|
|
121
|
+
|
|
122
|
+
.. image:: https://www.mypy-lang.org/static/mypy_badge.svg
|
|
123
|
+
:alt: Checked with mypy
|
|
124
|
+
:target: https://mypy-lang.org/
|
|
125
|
+
|
|
126
|
+
.. image:: https://img.shields.io/badge/License-MIT-yellow.svg
|
|
127
|
+
:alt: License: MIT
|
|
128
|
+
:target: https://opensource.org/licenses/MIT
|
|
129
|
+
|
|
130
|
+
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
mergeron/License.txt,sha256=7iX-y0EyjkbVJKJLS4ZKzuuE1wd0lryfsD_IytLG8lQ,1246
|
|
2
|
-
mergeron/__init__.py,sha256=
|
|
2
|
+
mergeron/__init__.py,sha256=eXxEYw2DGgVE984IXzibIzZXEpfHfdwMtRIs8xqriOw,1180
|
|
3
3
|
mergeron/core/__init__.py,sha256=KtjBlZOl7jwBCAUhrTJB9PdrN39YLYytNiSUSM_gRmA,62
|
|
4
|
-
mergeron/core/damodaran_margin_data.py,sha256=
|
|
5
|
-
mergeron/core/ftc_merger_investigations_data.py,sha256=
|
|
4
|
+
mergeron/core/damodaran_margin_data.py,sha256=mzCRsva47lnrLuTqzAXCKEIgqwvcUN6C3_qiqf_yDYI,8589
|
|
5
|
+
mergeron/core/ftc_merger_investigations_data.py,sha256=9U0GPD6bl_xAsbFclt32Vnm9-rz9vnIWuiIi6jK1sc8,27903
|
|
6
6
|
mergeron/core/guidelines_boundaries.py,sha256=__OHme8aGtwOgRXKp56WdX7k4vssAVQ8Ub54XwpS7mg,15621
|
|
7
7
|
mergeron/core/guidelines_boundary_functions.py,sha256=rXjncqTn7NPgI2KY9Wuv3WNrsjmv74hpH9-mUI56NgQ,29714
|
|
8
8
|
mergeron/core/guidelines_boundary_functions_extra.py,sha256=TYq3M5onfAIAY-35Q_SaSVF0Upa9hCSKIQkY-KCGzwM,11393
|
|
9
|
-
mergeron/core/proportions_tests.py,sha256=akq0Xhdgtst4RAT42_E5cBD_kATq_V4bQeBznmzRSLg,15267
|
|
10
9
|
mergeron/core/pseudorandom_numbers.py,sha256=k3sDs_NJ2jXlkIWKQ6iiTB5n_QS0RoJ-sqzvFYkC7pY,9277
|
|
11
10
|
mergeron/data/__init__.py,sha256=KtjBlZOl7jwBCAUhrTJB9PdrN39YLYytNiSUSM_gRmA,62
|
|
12
11
|
mergeron/data/damodaran_margin_data.xls,sha256=Qggl1p5nkOMJI8YUXhkwXQRz-OhRSqBTzz57N0JQyYA,79360
|
|
@@ -22,15 +21,16 @@ mergeron/data/jinja2_LaTeX_templates/setup_tikz_tables.tex,sha256=1hw3RINDtBrh9Z
|
|
|
22
21
|
mergeron/demo/__init__.py,sha256=KtjBlZOl7jwBCAUhrTJB9PdrN39YLYytNiSUSM_gRmA,62
|
|
23
22
|
mergeron/demo/visualize_empirical_margin_distribution.py,sha256=v1xFJumBX2Ooye82kSSgly-_GpFVkYSDqBwM__rcmZY,2363
|
|
24
23
|
mergeron/ext/__init__.py,sha256=KtjBlZOl7jwBCAUhrTJB9PdrN39YLYytNiSUSM_gRmA,62
|
|
24
|
+
mergeron/ext/proportions_tests.py,sha256=akq0Xhdgtst4RAT42_E5cBD_kATq_V4bQeBznmzRSLg,15267
|
|
25
25
|
mergeron/ext/tol_colors.py,sha256=QBw8s-ZGpUpIIYOplHbLFZSXVoa6eDDJDtzSScP954E,22303
|
|
26
|
-
mergeron/ext/xlsxw_helper.py,sha256=
|
|
27
|
-
mergeron/gen/__init__.py,sha256=
|
|
28
|
-
mergeron/gen/_data_generation_functions.py,sha256=
|
|
29
|
-
mergeron/gen/data_generation.py,sha256=
|
|
26
|
+
mergeron/ext/xlsxw_helper.py,sha256=Kqlt89kcnoiE2aCtP3xbM4fypy_JNIg9ebjjURmyJ38,18050
|
|
27
|
+
mergeron/gen/__init__.py,sha256=Ky1p93L-epw42g8HDIcfiENGT1iiXdolnsv12ZUWGM4,17169
|
|
28
|
+
mergeron/gen/_data_generation_functions.py,sha256=boNQkDtarx_BqYHWjlsIXSQHqOBvgBcI_R53UfT66qg,27763
|
|
29
|
+
mergeron/gen/data_generation.py,sha256=n_rdi6Zk5-2Q0K_xXdWI14sj9XZyrF57hnyqvBf5v6Y,8531
|
|
30
30
|
mergeron/gen/enforcement_stats.py,sha256=Hr-w3LZ9SJD4A1RZS1KMhXwuKyGzPC7eeNQullWZRNU,24410
|
|
31
|
-
mergeron/gen/market_sample.py,sha256=
|
|
32
|
-
mergeron/gen/upp_tests.py,sha256=
|
|
31
|
+
mergeron/gen/market_sample.py,sha256=HkzRFTKBXYIs2HbAyVDUiUHo9nCtAciSn5sohR-34cM,4282
|
|
32
|
+
mergeron/gen/upp_tests.py,sha256=N-spIAYKPGJngtlckSjhQGcFBZv_sVZDvCHYzy0TxSc,17132
|
|
33
33
|
mergeron/py.typed,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
|
|
34
|
-
mergeron-2024.
|
|
35
|
-
mergeron-2024.
|
|
36
|
-
mergeron-2024.
|
|
34
|
+
mergeron-2024.739097.2.dist-info/METADATA,sha256=A3gMoFOyFeDY1eS80qv33K_O68hfNo1HGwS-cGEP7V0,13268
|
|
35
|
+
mergeron-2024.739097.2.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
36
|
+
mergeron-2024.739097.2.dist-info/RECORD,,
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: mergeron
|
|
3
|
-
Version: 2024.739091.2
|
|
4
|
-
Summary: Merger Policy Analysis using Python
|
|
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,<4.0
|
|
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.12
|
|
19
|
-
Classifier: Programming Language :: Python :: 3 :: Only
|
|
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: google-re2 (>=1.1)
|
|
26
|
-
Requires-Dist: icecream (>=2.1.0)
|
|
27
|
-
Requires-Dist: jinja2 (>=3.1)
|
|
28
|
-
Requires-Dist: joblib (>=1.3)
|
|
29
|
-
Requires-Dist: lxml (>=5.0)
|
|
30
|
-
Requires-Dist: matplotlib (>=3.8)
|
|
31
|
-
Requires-Dist: mpmath (>=1.3)
|
|
32
|
-
Requires-Dist: msgpack (>=1.0)
|
|
33
|
-
Requires-Dist: msgpack-numpy (>=0.4)
|
|
34
|
-
Requires-Dist: numpy (>=1.26,<2.0)
|
|
35
|
-
Requires-Dist: openpyxl (>=3.1.2)
|
|
36
|
-
Requires-Dist: pendulum (>=3.0.0)
|
|
37
|
-
Requires-Dist: poetry-plugin-export (>=1.8.0,<2.0.0)
|
|
38
|
-
Requires-Dist: requests (>=2.31)
|
|
39
|
-
Requires-Dist: requests-toolbelt (>=1.0.0)
|
|
40
|
-
Requires-Dist: scipy (>=1.12)
|
|
41
|
-
Requires-Dist: sympy (>=1.12)
|
|
42
|
-
Requires-Dist: tables (>=3.8)
|
|
43
|
-
Requires-Dist: types-beautifulsoup4 (>=4.11.2)
|
|
44
|
-
Requires-Dist: types-requests (>=2.31.0)
|
|
45
|
-
Requires-Dist: xlrd (>=2.0.1,<3.0.0)
|
|
46
|
-
Requires-Dist: xlsxwriter (>=3.1)
|
|
47
|
-
Description-Content-Type: text/x-rst
|
|
48
|
-
|
|
49
|
-
mergeron: Merger Policy Analysis using Python
|
|
50
|
-
=============================================
|
|
51
|
-
|
|
52
|
-
Download and analyze merger investigations data published by the U.S. Federal Trade Commission in various reports on extended merger investigations during 1996 to 2011. Model the sets of mergers conforming to various U.S. Horizontal Merger Guidelines standards. Analyze intrinsic clearance rates and intrinsic enforcement rates under Guidelines standards using generated data with specified distributions of market shares, price-cost margins, firm counts, and prices, optionally imposing restrictions impled by statutory filing thresholds and/or Bertrand-Nash oligopoly with MNL demand.
|
|
53
|
-
|
|
54
|
-
Intrinsic clearance and enforcement rates are distinguished from *observed* clearance and enforcement rates in that the former do not reflect the effects of screening and deterrence as do the latter.
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
Introduction
|
|
58
|
-
------------
|
|
59
|
-
|
|
60
|
-
Classes for specifying concentration standards (:code:`mergeron.core.guidelines_boundaries.ConcentrationBoundary`) and diversion-ratio standards (:code:`mergeron.core.guidelines_boundaries.DiversionRatioBoundary`), with automatic generation of boundary (as an array of share-pairs) and area, are provided in :code:`mergeron.core.guidelines_boundaries`. This module also includes a function for generating plots of concentation and diversion-ratio boundaries, and functions for mapping GUPPI standards to concentration (ΔHHI) standards, and vice-versa.
|
|
61
|
-
|
|
62
|
-
Methods for generating industry data under various distributions of shares, margins, and prices are included in, :code:`mergeron.gen.data_generation`. Shares are drawn with uniform distribution with :math:`s_1 + s_2 \leqslant 1` and an unspecified number of firms. Alternatively, shares may be drawn from the Dirichlet distribution. When drawing shares from the Dirichlet distribution, the user can specify a fixed number for firms or provide 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>`_). The user can specify recapture rates as, "proportional", "inside-out" --- i.e., consistent with merging-firms' in-market shares and a default recapture rate) --- or "outside-in" --- i.e., purchase probabilities are drawn at random for :math:`N+1` goods, from which are derived market shares and recapture rates for the :math:`N` goods in the putative market. Documentation on specifying the sampling strategy for market shares is at :code:`mergeron.gen.ShareSpec`. Price-cost-margins may be specified as symmetric, i.i.d., or subject to equilibrium conditions for (profit-mazimization in) Bertrand-Nash oligopoly with MNL demand (see, :code:`mergeron.gen.PCMSpec`). 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:`mergeron.gen.PriceSpec`). Two alternative approaches for modeling statutory filing requirements (HSR filing thresholds) are implemented (see, :code:`mergeron.gen.SSZConstants`). The full specification of a market sample is given in a :code:`mergeron.gen.market_sample.MarketSample` object. Data are drawn by invoking :code:`mergeron.gen.market_sample.MarketSample.generate_sample` which adds a :code:`data` property of class, :code:`mergeron.gen.MarketDataSample`. Enforcement or clearance counts are computed by invoking :code:`mergeron.gen.market_sample.MarketSample.estimate_invres_counts`, which adds an :code:`invres_counts` property of class :code:`mergeron.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:`estimate_invres_counts` on a :code:`mergeron.gen.market_sample.MarketSample` object without first invoking :code:`generate_sample`. Note, however, that this strategy discards the market sample in the interests of conserving memory and maintaining high performance.
|
|
63
|
-
|
|
64
|
-
Methods for printing enforcement statistics based on FTC investigations data and test data are printed to screen or rendered to LaTex files (for processing into publication-quality tables) using methods provided in :code:`mergeron.gen.enforcement_stats`.
|
|
65
|
-
|
|
66
|
-
Programs demonstrating the analysis and reporting facilites provided by the sub-package, :code:`mergeron.demo`.
|
|
67
|
-
|
|
68
|
-
This package exposes methods employed for generating random numbers with selected continuous distribution over specified parameters, and with CPU multithreading on machines with multiple virtual, logical, or physical CPU cores. To access these directly:
|
|
69
|
-
|
|
70
|
-
.. code-block:: python
|
|
71
|
-
|
|
72
|
-
import mergeron.core.pseudorandom_numbers as prng
|
|
73
|
-
|
|
74
|
-
Also included are methods for estimating confidence intervals for proportions and for contrasts (differences) in proportions. (Although coded from scratch using the source literature, the APIs implemented in the module included here are designed for consistency with the APIs in, :code:`statsmodels.stats.proportion` from the package, :code:`statsmodels` (https://pypi.org/project/statsmodels/).) To access these directly:
|
|
75
|
-
|
|
76
|
-
.. code-block:: python
|
|
77
|
-
|
|
78
|
-
import mergeron.core.proportions_tests as prci
|
|
79
|
-
|
|
80
|
-
A recent version of Paul Tol's python module, :code:`tol_colors.py` is redistributed within this package. Other than re-formatting and type annotation, the :code:`mergeron.ext.tol_colors` module is re-distributed as downloaded from, https://personal.sron.nl/~pault/data/tol_colors.py. The :code:`tol_colors.py` module is distributed under the Standard 3-clause BSD license. To access the :code:`mergeron.ext.tol_colors` module directly:
|
|
81
|
-
|
|
82
|
-
.. code-block:: python
|
|
83
|
-
|
|
84
|
-
import mergeron.ext.tol_colors as ptc
|
|
85
|
-
|
|
86
|
-
Documentation for this package is in the form of the API Reference. Documentation for individual functions and classes is accessible within a python shell. For example:
|
|
87
|
-
|
|
88
|
-
.. code-block:: python
|
|
89
|
-
|
|
90
|
-
import mergeron.core.market_sample as market_sample
|
|
91
|
-
|
|
92
|
-
help(market_sample.MarketSample)
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
.. image:: https://img.shields.io/endpoint?url=https://python-poetry.org/badge/v0.json
|
|
96
|
-
:alt: Poetry
|
|
97
|
-
:target: https://python-poetry.org/
|
|
98
|
-
|
|
99
|
-
.. image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json
|
|
100
|
-
:alt: Ruff
|
|
101
|
-
:target: https://github.com/astral-sh/ruff
|
|
102
|
-
|
|
103
|
-
.. image:: https://www.mypy-lang.org/static/mypy_badge.svg
|
|
104
|
-
:alt: Checked with mypy
|
|
105
|
-
:target: https://mypy-lang.org/
|
|
106
|
-
|
|
107
|
-
.. image:: https://img.shields.io/badge/License-MIT-yellow.svg
|
|
108
|
-
:alt: License: MIT
|
|
109
|
-
:target: https://opensource.org/licenses/MIT
|
|
110
|
-
|
|
File without changes
|
|
File without changes
|