mergeron 2024.738953.1__tar.gz → 2024.738972.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of mergeron might be problematic. Click here for more details.
- {mergeron-2024.738953.1 → mergeron-2024.738972.0}/PKG-INFO +32 -17
- mergeron-2024.738972.0/README.rst +64 -0
- {mergeron-2024.738953.1 → mergeron-2024.738972.0}/pyproject.toml +1 -1
- {mergeron-2024.738953.1 → mergeron-2024.738972.0}/src/mergeron/License.txt +1 -1
- {mergeron-2024.738953.1 → mergeron-2024.738972.0}/src/mergeron/core/__init__.py +3 -3
- {mergeron-2024.738953.1 → mergeron-2024.738972.0}/src/mergeron/core/excel_helper.py +3 -1
- {mergeron-2024.738953.1 → mergeron-2024.738972.0}/src/mergeron/core/ftc_merger_investigations_data.py +13 -16
- {mergeron-2024.738953.1 → mergeron-2024.738972.0}/src/mergeron/core/guidelines_boundaries.py +48 -45
- {mergeron-2024.738953.1 → mergeron-2024.738972.0}/src/mergeron/core/guidelines_boundaries_specialized_functions.py +24 -21
- {mergeron-2024.738953.1 → mergeron-2024.738972.0}/src/mergeron/gen/__init__.py +72 -80
- mergeron-2024.738953.1/src/mergeron/gen/_data_generation_functions_nonpublic.py → mergeron-2024.738972.0/src/mergeron/gen/_data_generation_functions.py +103 -47
- {mergeron-2024.738953.1 → mergeron-2024.738972.0}/src/mergeron/gen/data_generation.py +42 -42
- {mergeron-2024.738953.1 → mergeron-2024.738972.0}/src/mergeron/gen/investigations_stats.py +1 -1
- mergeron-2024.738972.0/src/mergeron/gen/market_sample.py +79 -0
- {mergeron-2024.738953.1 → mergeron-2024.738972.0}/src/mergeron/gen/upp_tests.py +143 -98
- mergeron-2024.738953.1/README.rst +0 -49
- {mergeron-2024.738953.1 → mergeron-2024.738972.0}/src/mergeron/__init__.py +0 -0
- {mergeron-2024.738953.1 → mergeron-2024.738972.0}/src/mergeron/core/InCommon RSA Server CA cert chain.pem +0 -0
- {mergeron-2024.738953.1 → mergeron-2024.738972.0}/src/mergeron/core/damodaran_margin_data.py +0 -0
- {mergeron-2024.738953.1 → mergeron-2024.738972.0}/src/mergeron/core/ftc_invdata.msgpack +0 -0
- {mergeron-2024.738953.1 → mergeron-2024.738972.0}/src/mergeron/core/proportions_tests.py +0 -0
- {mergeron-2024.738953.1 → mergeron-2024.738972.0}/src/mergeron/core/pseudorandom_numbers.py +0 -0
- {mergeron-2024.738953.1 → mergeron-2024.738972.0}/src/mergeron/ext/__init__.py +0 -0
- {mergeron-2024.738953.1 → mergeron-2024.738972.0}/src/mergeron/ext/tol_colors.py +0 -0
- {mergeron-2024.738953.1 → mergeron-2024.738972.0}/src/mergeron/jinja_LaTex_templates/clrrate_cis_summary_table_template.tex.jinja2 +0 -0
- {mergeron-2024.738953.1 → mergeron-2024.738972.0}/src/mergeron/jinja_LaTex_templates/ftcinvdata_byhhianddelta_table_template.tex.jinja2 +0 -0
- {mergeron-2024.738953.1 → mergeron-2024.738972.0}/src/mergeron/jinja_LaTex_templates/ftcinvdata_summary_table_template.tex.jinja2 +0 -0
- {mergeron-2024.738953.1 → mergeron-2024.738972.0}/src/mergeron/jinja_LaTex_templates/ftcinvdata_summarypaired_table_template.tex.jinja2 +0 -0
- {mergeron-2024.738953.1 → mergeron-2024.738972.0}/src/mergeron/jinja_LaTex_templates/mergeron.cls +0 -0
- {mergeron-2024.738953.1 → mergeron-2024.738972.0}/src/mergeron/jinja_LaTex_templates/mergeron_table_collection_template.tex.jinja2 +0 -0
- {mergeron-2024.738953.1 → mergeron-2024.738972.0}/src/mergeron/jinja_LaTex_templates/setup_tikz_tables.tex.jinja2 +0 -0
- {mergeron-2024.738953.1 → mergeron-2024.738972.0}/src/mergeron/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: mergeron
|
|
3
|
-
Version: 2024.
|
|
3
|
+
Version: 2024.738972.0
|
|
4
4
|
Summary: Analysis of standards defined in Horizontal Merger Guidelines
|
|
5
5
|
License: MIT
|
|
6
6
|
Keywords: merger policy analysis,merger guidelines,merger screening,policy presumptions,concentration standards,upward pricing pressure,GUPPI
|
|
@@ -51,43 +51,58 @@ Intrinsic clearance and enforcement rates are distinguished from *observed* clea
|
|
|
51
51
|
Modules of primary interest
|
|
52
52
|
---------------------------
|
|
53
53
|
|
|
54
|
-
Routines for downloading and organizing FTC merger investigtions data published in 2004, 2007, 2008, and 2013. Includes a routine for constructing investigations data for non-overlapping periods, and other partitions on the data, subject to the constraints of the reported data.
|
|
55
54
|
|
|
56
|
-
mergeron.core.ftc_merger_investigations_data
|
|
57
55
|
|
|
58
|
-
|
|
56
|
+
Methods for plotting boundaries of (sets of mergers falling within) specified concentration and share-ratio boundaries, are in :code:`mergeron.core.guidelines_boundaries`, where share-ratio, :math:`\delta_{ij}` is defined as :math:`\delta_{ij} = d_{ij} / r_i` with :math:`d_{ij}, r_i` a diversion ratio and recapture rate. This module also includes functions for calibrating GUPPI thresholds to concentration (ΔHHI) thresholds, and vice-versa.
|
|
59
57
|
|
|
60
|
-
|
|
58
|
+
Methods for generating industry data under various distributions of shares, prices, and margins are included in, :code:`mergeron.gen.data_generation`. The user can specify whether rates are specified 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). Price-cost-margins may be specified as symmetric, i.i.d, or consistent with equilibrium conditions for (profit-mazimization in) Bertrand-Nash oligopoly with MNL demand. 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. Two alternative approaches for modeling statutory filing requirements (HSR filing thresholds) are implemented.
|
|
61
59
|
|
|
62
|
-
|
|
60
|
+
Methods for testing generated industry data against criteria on diversion ratio, gross upward pricing pressure ("GUPPI"), critical marginal cost reduction ("CMCR"), and indicative price rise ("IPR")/partial merger simulation are included in the module, :code:`mergeron.gen.guidelines_tests`. Test data are constructed in parallel and the user can specify number of `threads` and sub-sample size for each thread to manage CPU and memory utilization.
|
|
63
61
|
|
|
64
|
-
|
|
62
|
+
FTC investigations data and test data are printed to screen or rendered in LaTex to text files (for processing into publication-quality tables) using methods provided in :code:`mergeron.gen.investigations_stats`.
|
|
65
63
|
|
|
66
|
-
|
|
64
|
+
Programs demonstrating the analysis and reporting facilites provided by the sub-package, :code:`mergeron.examples`.
|
|
67
65
|
|
|
68
|
-
|
|
66
|
+
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
67
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
Programs demonstrating the analysis and reporting facilites provided by the package.
|
|
73
|
-
|
|
74
|
-
mergeron.examples
|
|
75
|
-
|
|
76
|
-
This package exposes routines 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:
|
|
68
|
+
.. code-block:: python
|
|
77
69
|
|
|
78
70
|
import mergeron.core.pseudorandom_numbers as prng
|
|
79
71
|
|
|
80
|
-
Also included are
|
|
72
|
+
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:
|
|
73
|
+
|
|
74
|
+
.. code-block:: python
|
|
81
75
|
|
|
82
76
|
import mergeron.core.proportions_tests as prci
|
|
83
77
|
|
|
84
78
|
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:`tol_colors` module is re-distributed as downloaded from, https://personal.sron.nl/~pault/data/tol_colors.py. The tol_colors.py module is distributed under the Standard 3-clause BSD license. To access the tol_colors module directly:
|
|
85
79
|
|
|
80
|
+
.. code-block:: python
|
|
81
|
+
|
|
86
82
|
import mergeron.ext.tol_colors
|
|
87
83
|
|
|
88
84
|
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:
|
|
89
85
|
|
|
86
|
+
.. code-block:: python
|
|
87
|
+
|
|
90
88
|
import mergeron.core.data_generation as dgl
|
|
91
89
|
|
|
92
90
|
help(dgl.gen_market_sample)
|
|
93
91
|
|
|
92
|
+
|
|
93
|
+
.. image:: https://img.shields.io/endpoint?url=https://python-poetry.org/badge/v0.json
|
|
94
|
+
:alt: Poetry
|
|
95
|
+
:target: https://python-poetry.org/
|
|
96
|
+
|
|
97
|
+
.. image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json
|
|
98
|
+
:alt: Ruff
|
|
99
|
+
:target: https://github.com/astral-sh/ruff
|
|
100
|
+
|
|
101
|
+
.. image:: https://www.mypy-lang.org/static/mypy_badge.svg
|
|
102
|
+
:alt: Checked with mypy
|
|
103
|
+
:target: https://mypy-lang.org/
|
|
104
|
+
|
|
105
|
+
.. image:: https://img.shields.io/badge/License-MIT-yellow.svg
|
|
106
|
+
:alt: License: MIT
|
|
107
|
+
:target: https://opensource.org/licenses/MIT
|
|
108
|
+
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
mergeron: Merger Policy Analysis with Python
|
|
2
|
+
============================================
|
|
3
|
+
|
|
4
|
+
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.
|
|
5
|
+
|
|
6
|
+
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.
|
|
7
|
+
|
|
8
|
+
Modules of primary interest
|
|
9
|
+
---------------------------
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
Methods for plotting boundaries of (sets of mergers falling within) specified concentration and share-ratio boundaries, are in :code:`mergeron.core.guidelines_boundaries`, where share-ratio, :math:`\delta_{ij}` is defined as :math:`\delta_{ij} = d_{ij} / r_i` with :math:`d_{ij}, r_i` a diversion ratio and recapture rate. This module also includes functions for calibrating GUPPI thresholds to concentration (ΔHHI) thresholds, and vice-versa.
|
|
14
|
+
|
|
15
|
+
Methods for generating industry data under various distributions of shares, prices, and margins are included in, :code:`mergeron.gen.data_generation`. The user can specify whether rates are specified 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). Price-cost-margins may be specified as symmetric, i.i.d, or consistent with equilibrium conditions for (profit-mazimization in) Bertrand-Nash oligopoly with MNL demand. 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. Two alternative approaches for modeling statutory filing requirements (HSR filing thresholds) are implemented.
|
|
16
|
+
|
|
17
|
+
Methods for testing generated industry data against criteria on diversion ratio, gross upward pricing pressure ("GUPPI"), critical marginal cost reduction ("CMCR"), and indicative price rise ("IPR")/partial merger simulation are included in the module, :code:`mergeron.gen.guidelines_tests`. Test data are constructed in parallel and the user can specify number of `threads` and sub-sample size for each thread to manage CPU and memory utilization.
|
|
18
|
+
|
|
19
|
+
FTC investigations data and test data are printed to screen or rendered in LaTex to text files (for processing into publication-quality tables) using methods provided in :code:`mergeron.gen.investigations_stats`.
|
|
20
|
+
|
|
21
|
+
Programs demonstrating the analysis and reporting facilites provided by the sub-package, :code:`mergeron.examples`.
|
|
22
|
+
|
|
23
|
+
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:
|
|
24
|
+
|
|
25
|
+
.. code-block:: python
|
|
26
|
+
|
|
27
|
+
import mergeron.core.pseudorandom_numbers as prng
|
|
28
|
+
|
|
29
|
+
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:
|
|
30
|
+
|
|
31
|
+
.. code-block:: python
|
|
32
|
+
|
|
33
|
+
import mergeron.core.proportions_tests as prci
|
|
34
|
+
|
|
35
|
+
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:`tol_colors` module is re-distributed as downloaded from, https://personal.sron.nl/~pault/data/tol_colors.py. The tol_colors.py module is distributed under the Standard 3-clause BSD license. To access the tol_colors module directly:
|
|
36
|
+
|
|
37
|
+
.. code-block:: python
|
|
38
|
+
|
|
39
|
+
import mergeron.ext.tol_colors
|
|
40
|
+
|
|
41
|
+
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:
|
|
42
|
+
|
|
43
|
+
.. code-block:: python
|
|
44
|
+
|
|
45
|
+
import mergeron.core.data_generation as dgl
|
|
46
|
+
|
|
47
|
+
help(dgl.gen_market_sample)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
.. image:: https://img.shields.io/endpoint?url=https://python-poetry.org/badge/v0.json
|
|
51
|
+
:alt: Poetry
|
|
52
|
+
:target: https://python-poetry.org/
|
|
53
|
+
|
|
54
|
+
.. image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json
|
|
55
|
+
:alt: Ruff
|
|
56
|
+
:target: https://github.com/astral-sh/ruff
|
|
57
|
+
|
|
58
|
+
.. image:: https://www.mypy-lang.org/static/mypy_badge.svg
|
|
59
|
+
:alt: Checked with mypy
|
|
60
|
+
:target: https://mypy-lang.org/
|
|
61
|
+
|
|
62
|
+
.. image:: https://img.shields.io/badge/License-MIT-yellow.svg
|
|
63
|
+
:alt: License: MIT
|
|
64
|
+
:target: https://opensource.org/licenses/MIT
|
|
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from importlib.metadata import version
|
|
4
4
|
|
|
5
|
-
from attrs import Attribute,
|
|
5
|
+
from attrs import Attribute, field, frozen, validators
|
|
6
6
|
|
|
7
7
|
from .. import _PKG_NAME, RECConstants, UPPAggrSelector # noqa: TID252
|
|
8
8
|
|
|
@@ -38,7 +38,7 @@ def _rec_spec_validator(
|
|
|
38
38
|
)
|
|
39
39
|
|
|
40
40
|
|
|
41
|
-
@
|
|
41
|
+
@frozen
|
|
42
42
|
class UPPBoundarySpec:
|
|
43
43
|
share_ratio: float = field(
|
|
44
44
|
kw_only=False,
|
|
@@ -54,7 +54,7 @@ class UPPBoundarySpec:
|
|
|
54
54
|
default=UPPAggrSelector.MAX,
|
|
55
55
|
validator=validators.instance_of(UPPAggrSelector),
|
|
56
56
|
)
|
|
57
|
-
|
|
57
|
+
recapture_form: RECConstants | None = field(
|
|
58
58
|
kw_only=True,
|
|
59
59
|
default=RECConstants.INOUT,
|
|
60
60
|
validator=(
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
2
|
+
Methods for writing data from Python to fresh Excel workbooks using
|
|
3
3
|
the third-party package, `xlsxwriter`.
|
|
4
4
|
|
|
5
5
|
Includes a flexible system of defining cell formats.
|
|
@@ -27,6 +27,8 @@ __version__ = version(_PKG_NAME)
|
|
|
27
27
|
class CFmtParent(dict[str, Any], enum.ReprEnum): # type: ignore
|
|
28
28
|
"""Unique mappings defining xlsxwirter Workbook formats"""
|
|
29
29
|
|
|
30
|
+
...
|
|
31
|
+
|
|
30
32
|
|
|
31
33
|
class CFmt(CFmtParent):
|
|
32
34
|
"""
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
2
|
+
Methods to parse FTC Merger Investigations Data, downloading source documents
|
|
3
3
|
as necessary
|
|
4
4
|
|
|
5
5
|
NOTES
|
|
@@ -16,7 +16,6 @@ from pathlib import Path
|
|
|
16
16
|
from types import MappingProxyType
|
|
17
17
|
from typing import Any, NamedTuple, TypeAlias
|
|
18
18
|
|
|
19
|
-
import fitz # type: ignore
|
|
20
19
|
import msgpack # type: ignore
|
|
21
20
|
import msgpack_numpy as m # type: ignore
|
|
22
21
|
import numpy as np
|
|
@@ -146,7 +145,7 @@ def construct_data(
|
|
|
146
145
|
)
|
|
147
146
|
return MappingProxyType(_invdata)
|
|
148
147
|
|
|
149
|
-
_invdata = dict(
|
|
148
|
+
_invdata = dict(_parse_invdata()) # Convert immutable to mutable
|
|
150
149
|
|
|
151
150
|
# Add some data periods (
|
|
152
151
|
# only periods ending in 2011, others have few observations and
|
|
@@ -385,21 +384,9 @@ def _invdata_build_aggregate_table(
|
|
|
385
384
|
)
|
|
386
385
|
|
|
387
386
|
|
|
388
|
-
def
|
|
389
|
-
_invdata_docnames: Sequence[str] = (
|
|
390
|
-
"040831horizmergersdata96-03.pdf",
|
|
391
|
-
"p035603horizmergerinvestigationdata1996-2005.pdf",
|
|
392
|
-
"081201hsrmergerdata.pdf",
|
|
393
|
-
"130104horizontalmergerreport.pdf",
|
|
394
|
-
),
|
|
395
|
-
) -> INVData:
|
|
387
|
+
def _parse_invdata() -> INVData:
|
|
396
388
|
"""Parse FTC merger investigations data reports to structured data.
|
|
397
389
|
|
|
398
|
-
Parameters
|
|
399
|
-
----------
|
|
400
|
-
_invdata_docnames
|
|
401
|
-
Names of PDF files reporting FTC merger investigations data.
|
|
402
|
-
|
|
403
390
|
Returns
|
|
404
391
|
-------
|
|
405
392
|
Immutable dictionary of merger investigations data, keyed to
|
|
@@ -408,6 +395,16 @@ def parse_invdata(
|
|
|
408
395
|
by range of HHI and ∆HHI.
|
|
409
396
|
|
|
410
397
|
"""
|
|
398
|
+
import fitz # type: ignore
|
|
399
|
+
# user must install pymupdf to make this function operable
|
|
400
|
+
|
|
401
|
+
_invdata_docnames: Sequence[str] = (
|
|
402
|
+
"040831horizmergersdata96-03.pdf",
|
|
403
|
+
"p035603horizmergerinvestigationdata1996-2005.pdf",
|
|
404
|
+
"081201hsrmergerdata.pdf",
|
|
405
|
+
"130104horizontalmergerreport.pdf",
|
|
406
|
+
)
|
|
407
|
+
|
|
411
408
|
_invdata: dict[str, dict[str, dict[str, INVTableData]]] = {}
|
|
412
409
|
|
|
413
410
|
for _invdata_docname in _invdata_docnames:
|
{mergeron-2024.738953.1 → mergeron-2024.738972.0}/src/mergeron/core/guidelines_boundaries.py
RENAMED
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
2
|
+
Methods for defining and analyzing boundaries for Guidelines standards,
|
|
3
3
|
with a canvas on which to draw boundaries for Guidelines standards.
|
|
4
4
|
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
import decimal
|
|
8
|
-
from collections.abc import Callable
|
|
9
8
|
from dataclasses import dataclass
|
|
10
9
|
from importlib.metadata import version
|
|
11
10
|
from typing import Any, Literal, TypeAlias
|
|
12
11
|
|
|
13
12
|
import numpy as np
|
|
14
|
-
from attrs import
|
|
13
|
+
from attrs import field, frozen
|
|
15
14
|
from mpmath import mp, mpf # type: ignore
|
|
16
15
|
from numpy.typing import NDArray
|
|
17
16
|
|
|
@@ -24,7 +23,7 @@ __version__ = version(_PKG_NAME)
|
|
|
24
23
|
mp.prec = 80
|
|
25
24
|
mp.trap_complex = True
|
|
26
25
|
|
|
27
|
-
HMGPubYear: TypeAlias = Literal[1992, 2010, 2023]
|
|
26
|
+
HMGPubYear: TypeAlias = Literal[1992, 2004, 2010, 2023]
|
|
28
27
|
|
|
29
28
|
|
|
30
29
|
@dataclass(slots=True, frozen=True)
|
|
@@ -43,25 +42,28 @@ class GuidelinesBoundary:
|
|
|
43
42
|
area: float
|
|
44
43
|
|
|
45
44
|
|
|
46
|
-
@
|
|
47
|
-
class GuidelinesBoundaryCallable:
|
|
48
|
-
boundary_function: Callable[[NDArray[np.float64]], NDArray[np.float64]]
|
|
49
|
-
area: float
|
|
50
|
-
s_naught: float = 0
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
@define(slots=True, frozen=True)
|
|
45
|
+
@frozen
|
|
54
46
|
class GuidelinesThresholds:
|
|
55
47
|
"""
|
|
56
48
|
Guidelines threholds by Guidelines publication year
|
|
57
49
|
|
|
58
50
|
ΔHHI, Recapture Rate, GUPPI, Diversion ratio, CMCR, and IPR thresholds
|
|
59
|
-
constructed from concentration standards
|
|
51
|
+
constructed from concentration standards in Guidelines published in
|
|
52
|
+
1992, 2004, 2010, and 2023.
|
|
53
|
+
|
|
54
|
+
The 2004 Guidelines refernced here are the EU Commission
|
|
55
|
+
guidelines on assessment of horizontal mergers. These
|
|
56
|
+
guidelines also define a presumption for mergers with
|
|
57
|
+
post-merger HHI in [1000, 2000) and ΔHHI >= 250 points,
|
|
58
|
+
whi is not modeled here.
|
|
59
|
+
|
|
60
|
+
All other Guidelines modeled here are U.S. merger guidelines.
|
|
61
|
+
|
|
60
62
|
"""
|
|
61
63
|
|
|
62
64
|
pub_year: HMGPubYear
|
|
63
65
|
"""
|
|
64
|
-
Year of publication of the
|
|
66
|
+
Year of publication of the Guidelines
|
|
65
67
|
"""
|
|
66
68
|
|
|
67
69
|
safeharbor: HMGThresholds = field(kw_only=True, default=None)
|
|
@@ -99,6 +101,7 @@ class GuidelinesThresholds:
|
|
|
99
101
|
_hhi_p, _dh_s, _dh_p = {
|
|
100
102
|
1992: (0.18, 0.005, 0.01),
|
|
101
103
|
2010: (0.25, 0.01, 0.02),
|
|
104
|
+
2004: (0.20, 0.015, 0.015),
|
|
102
105
|
2023: (0.18, 0.01, 0.01),
|
|
103
106
|
}[self.pub_year]
|
|
104
107
|
|
|
@@ -507,7 +510,7 @@ def boundary_plot(*, mktshares_plot_flag: bool = True) -> tuple[Any, ...]:
|
|
|
507
510
|
return plt, _fig, _ax_out, _set_axis_def
|
|
508
511
|
|
|
509
512
|
|
|
510
|
-
def dh_area(_dh_val: float = 0.01, /, *,
|
|
513
|
+
def dh_area(_dh_val: float = 0.01, /, *, prec: int = 9) -> float:
|
|
511
514
|
R"""
|
|
512
515
|
Area under the ΔHHI boundary.
|
|
513
516
|
|
|
@@ -530,7 +533,7 @@ def dh_area(_dh_val: float = 0.01, /, *, dh_dps: int = 9) -> float:
|
|
|
530
533
|
----------
|
|
531
534
|
_dh_val
|
|
532
535
|
Change in concentration.
|
|
533
|
-
|
|
536
|
+
prec
|
|
534
537
|
Specified precision in decimal places.
|
|
535
538
|
|
|
536
539
|
Returns
|
|
@@ -544,11 +547,11 @@ def dh_area(_dh_val: float = 0.01, /, *, dh_dps: int = 9) -> float:
|
|
|
544
547
|
|
|
545
548
|
return round(
|
|
546
549
|
float(_s_naught + (_dh_val / 2) * (mp.ln(1 - _s_naught) - mp.ln(_s_naught))),
|
|
547
|
-
|
|
550
|
+
prec,
|
|
548
551
|
)
|
|
549
552
|
|
|
550
553
|
|
|
551
|
-
def dh_area_quad(_dh_val: float = 0.01, /, *,
|
|
554
|
+
def dh_area_quad(_dh_val: float = 0.01, /, *, prec: int = 9) -> float:
|
|
552
555
|
"""
|
|
553
556
|
Area under the ΔHHI boundary.
|
|
554
557
|
|
|
@@ -560,7 +563,7 @@ def dh_area_quad(_dh_val: float = 0.01, /, *, dh_dps: int = 9) -> float:
|
|
|
560
563
|
----------
|
|
561
564
|
_dh_val
|
|
562
565
|
Merging-firms' ΔHHI bound.
|
|
563
|
-
|
|
566
|
+
prec
|
|
564
567
|
Specified precision in decimal places.
|
|
565
568
|
|
|
566
569
|
Returns
|
|
@@ -576,7 +579,7 @@ def dh_area_quad(_dh_val: float = 0.01, /, *, dh_dps: int = 9) -> float:
|
|
|
576
579
|
float(
|
|
577
580
|
_s_naught + mp.quad(lambda x: _dh_val / (2 * x), [_s_naught, 1 - _s_naught])
|
|
578
581
|
),
|
|
579
|
-
|
|
582
|
+
prec,
|
|
580
583
|
)
|
|
581
584
|
|
|
582
585
|
|
|
@@ -590,7 +593,7 @@ def delta_hhi_boundary(
|
|
|
590
593
|
----------
|
|
591
594
|
_dh_val:
|
|
592
595
|
Merging-firms' ΔHHI bound.
|
|
593
|
-
|
|
596
|
+
prec
|
|
594
597
|
Number of decimal places for rounding reported shares.
|
|
595
598
|
|
|
596
599
|
Returns
|
|
@@ -620,7 +623,7 @@ def delta_hhi_boundary(
|
|
|
620
623
|
np.array(_s_1_pts, np.float64),
|
|
621
624
|
np.array(_s_2_pts, np.float64),
|
|
622
625
|
)),
|
|
623
|
-
dh_area(_dh_val,
|
|
626
|
+
dh_area(_dh_val, prec=prec),
|
|
624
627
|
)
|
|
625
628
|
|
|
626
629
|
|
|
@@ -697,7 +700,7 @@ def shrratio_boundary(_bdry_spec: UPPBoundarySpec) -> GuidelinesBoundary:
|
|
|
697
700
|
return shrratio_boundary_xact_avg(
|
|
698
701
|
_bdry_spec.share_ratio,
|
|
699
702
|
_bdry_spec.rec,
|
|
700
|
-
|
|
703
|
+
recapture_form=_bdry_spec.recapture_form.value, # type: ignore
|
|
701
704
|
prec=_bdry_spec.precision,
|
|
702
705
|
)
|
|
703
706
|
case UPPAggrSelector.MAX:
|
|
@@ -708,7 +711,7 @@ def shrratio_boundary(_bdry_spec: UPPBoundarySpec) -> GuidelinesBoundary:
|
|
|
708
711
|
return shrratio_boundary_min(
|
|
709
712
|
_bdry_spec.share_ratio,
|
|
710
713
|
_bdry_spec.rec,
|
|
711
|
-
|
|
714
|
+
recapture_form=_bdry_spec.recapture_form.value, # type: ignore
|
|
712
715
|
prec=_bdry_spec.precision,
|
|
713
716
|
)
|
|
714
717
|
case UPPAggrSelector.DIS:
|
|
@@ -717,7 +720,7 @@ def shrratio_boundary(_bdry_spec: UPPBoundarySpec) -> GuidelinesBoundary:
|
|
|
717
720
|
_bdry_spec.rec,
|
|
718
721
|
agg_method="distance",
|
|
719
722
|
weighting=None,
|
|
720
|
-
|
|
723
|
+
recapture_form=_bdry_spec.recapture_form.value, # type: ignore
|
|
721
724
|
prec=_bdry_spec.precision,
|
|
722
725
|
)
|
|
723
726
|
case _:
|
|
@@ -738,7 +741,7 @@ def shrratio_boundary(_bdry_spec: UPPBoundarySpec) -> GuidelinesBoundary:
|
|
|
738
741
|
_bdry_spec.rec,
|
|
739
742
|
agg_method=_agg_method, # type: ignore
|
|
740
743
|
weighting=_weighting, # type: ignore
|
|
741
|
-
|
|
744
|
+
recapture_form=_bdry_spec.recapture_form.value, # type: ignore
|
|
742
745
|
prec=_bdry_spec.precision,
|
|
743
746
|
)
|
|
744
747
|
|
|
@@ -788,7 +791,7 @@ def shrratio_boundary_min(
|
|
|
788
791
|
_r_val: float = 0.80,
|
|
789
792
|
/,
|
|
790
793
|
*,
|
|
791
|
-
|
|
794
|
+
recapture_form: str = "inside-out",
|
|
792
795
|
prec: int = 10,
|
|
793
796
|
) -> GuidelinesBoundary:
|
|
794
797
|
"""
|
|
@@ -808,7 +811,7 @@ def shrratio_boundary_min(
|
|
|
808
811
|
Margin-adjusted benchmark share ratio.
|
|
809
812
|
_r_val
|
|
810
813
|
Recapture ratio.
|
|
811
|
-
|
|
814
|
+
recapture_form
|
|
812
815
|
Whether recapture-ratio is MNL-consistent ("inside-out") or has fixed
|
|
813
816
|
value for both merging firms ("proportional").
|
|
814
817
|
prec
|
|
@@ -824,7 +827,7 @@ def shrratio_boundary_min(
|
|
|
824
827
|
_s_intcpt = mpf("1.00")
|
|
825
828
|
_s_mid = _delta_star / (1 + _delta_star)
|
|
826
829
|
|
|
827
|
-
if
|
|
830
|
+
if recapture_form == "inside-out":
|
|
828
831
|
# ## Plot envelope of GUPPI boundaries with r_k = r_bar if s_k = min(_s_1, _s_2)
|
|
829
832
|
# ## See (s_i, s_j) in equation~(44), or thereabouts, in paper
|
|
830
833
|
_smin_nr = _delta_star * (1 - _r_val)
|
|
@@ -857,7 +860,7 @@ def shrratio_boundary_wtd_avg(
|
|
|
857
860
|
*,
|
|
858
861
|
agg_method: Literal["arithmetic", "geometric", "distance"] = "arithmetic",
|
|
859
862
|
weighting: Literal["own-share", "cross-product-share"] | None = "own-share",
|
|
860
|
-
|
|
863
|
+
recapture_form: Literal["inside-out", "proportional"] = "inside-out",
|
|
861
864
|
prec: int = 5,
|
|
862
865
|
) -> GuidelinesBoundary:
|
|
863
866
|
"""
|
|
@@ -874,7 +877,7 @@ def shrratio_boundary_wtd_avg(
|
|
|
874
877
|
Whether "arithmetic", "geometric", or "distance".
|
|
875
878
|
weighting
|
|
876
879
|
Whether "own-share" or "cross-product-share" (or None for simple, unweighted average).
|
|
877
|
-
|
|
880
|
+
recapture_form
|
|
878
881
|
Whether recapture-ratio is MNL-consistent ("inside-out") or has fixed
|
|
879
882
|
value for both merging firms ("proportional").
|
|
880
883
|
prec
|
|
@@ -895,7 +898,7 @@ def shrratio_boundary_wtd_avg(
|
|
|
895
898
|
g_val, r_val, m_val = 0.06, 0.80, 0.30
|
|
896
899
|
delta_star = g_val / (r_val * m_val)
|
|
897
900
|
|
|
898
|
-
#
|
|
901
|
+
# recapture_form == "inside-out"
|
|
899
902
|
oswag = solve(
|
|
900
903
|
s_1 * s_2 / (1 - s_1)
|
|
901
904
|
+ s_2 * s_1 / (1 - (r_val * s_2 + (1 - r_val) * s_1))
|
|
@@ -920,7 +923,7 @@ def shrratio_boundary_wtd_avg(
|
|
|
920
923
|
ylabel=s_2
|
|
921
924
|
)
|
|
922
925
|
|
|
923
|
-
#
|
|
926
|
+
# recapture_form == "proportional"
|
|
924
927
|
oswag = solve(
|
|
925
928
|
s_1 * s_2 / (1 - s_1)
|
|
926
929
|
+ s_2 * s_1 / (1 - s_2)
|
|
@@ -973,7 +976,7 @@ def shrratio_boundary_wtd_avg(
|
|
|
973
976
|
_de_1 = _s_2 / (1 - _s_1)
|
|
974
977
|
_de_2 = (
|
|
975
978
|
_s_1 / (1 - lerp(_s_1, _s_2, _r_val))
|
|
976
|
-
if
|
|
979
|
+
if recapture_form == "inside-out"
|
|
977
980
|
else _s_1 / (1 - _s_2)
|
|
978
981
|
)
|
|
979
982
|
|
|
@@ -1025,7 +1028,7 @@ def shrratio_boundary_wtd_avg(
|
|
|
1025
1028
|
_s_1_pre,
|
|
1026
1029
|
_delta_star,
|
|
1027
1030
|
_r_val,
|
|
1028
|
-
|
|
1031
|
+
recapture_form=recapture_form,
|
|
1029
1032
|
agg_method=agg_method,
|
|
1030
1033
|
weighting=weighting,
|
|
1031
1034
|
)
|
|
@@ -1063,7 +1066,7 @@ def shrratio_boundary_xact_avg(
|
|
|
1063
1066
|
_r_val: float = 0.80,
|
|
1064
1067
|
/,
|
|
1065
1068
|
*,
|
|
1066
|
-
|
|
1069
|
+
recapture_form: Literal["inside-out", "proportional"] = "inside-out",
|
|
1067
1070
|
prec: int = 5,
|
|
1068
1071
|
) -> GuidelinesBoundary:
|
|
1069
1072
|
"""
|
|
@@ -1082,7 +1085,7 @@ def shrratio_boundary_xact_avg(
|
|
|
1082
1085
|
g_val, r_val, m_val = 0.06, 0.80, 0.30
|
|
1083
1086
|
d_hat = g_val / (r_val * m_val)
|
|
1084
1087
|
|
|
1085
|
-
#
|
|
1088
|
+
# recapture_form = "inside-out"
|
|
1086
1089
|
sag = solve(
|
|
1087
1090
|
(s_2 / (1 - s_1))
|
|
1088
1091
|
+ (s_1 / (1 - (r_val * s_2 + (1 - r_val) * s_1)))
|
|
@@ -1095,7 +1098,7 @@ def shrratio_boundary_xact_avg(
|
|
|
1095
1098
|
ylabel=s_2
|
|
1096
1099
|
)
|
|
1097
1100
|
|
|
1098
|
-
#
|
|
1101
|
+
# recapture_form = "proportional"
|
|
1099
1102
|
sag = solve((s_2/(1 - s_1)) + (s_1/(1 - s_2)) - 2 * d_hat, s_2)[0]
|
|
1100
1103
|
symplot(
|
|
1101
1104
|
sag,
|
|
@@ -1109,7 +1112,7 @@ def shrratio_boundary_xact_avg(
|
|
|
1109
1112
|
Margin-adjusted benchmark share ratio.
|
|
1110
1113
|
_r_val
|
|
1111
1114
|
Recapture ratio
|
|
1112
|
-
|
|
1115
|
+
recapture_form
|
|
1113
1116
|
Whether recapture-ratio is MNL-consistent ("inside-out") or has fixed
|
|
1114
1117
|
value for both merging firms ("proportional").
|
|
1115
1118
|
prec
|
|
@@ -1127,14 +1130,14 @@ def shrratio_boundary_xact_avg(
|
|
|
1127
1130
|
|
|
1128
1131
|
_gbdry_points_start = np.array([(_s_mid, _s_mid)])
|
|
1129
1132
|
_s_1 = np.array(mp.arange(_s_mid - _gbd_step_sz, 0, -_gbd_step_sz), np.float64)
|
|
1130
|
-
if
|
|
1133
|
+
if recapture_form == "inside-out":
|
|
1131
1134
|
_s_intcpt = mp.fdiv(
|
|
1132
1135
|
mp.fsub(
|
|
1133
1136
|
2 * _delta_star * _r_val + 1, mp.fabs(2 * _delta_star * _r_val - 1)
|
|
1134
1137
|
),
|
|
1135
1138
|
2 * mpf(f"{_r_val}"),
|
|
1136
1139
|
)
|
|
1137
|
-
_nr_t1 = 1 + 2 * _delta_star * _r_val * (1 - _s_1) - _s_1 * (1 - _r_val)
|
|
1140
|
+
_nr_t1 = 1 + 2 * _delta_star * _r_val * (1 - _s_1) - _s_1 * (1 - _r_val)
|
|
1138
1141
|
|
|
1139
1142
|
_nr_sqrt_mdr = 4 * _delta_star * _r_val
|
|
1140
1143
|
_nr_sqrt_mdr2 = _nr_sqrt_mdr * _r_val
|
|
@@ -1159,7 +1162,7 @@ def shrratio_boundary_xact_avg(
|
|
|
1159
1162
|
|
|
1160
1163
|
_nr_t2_s1 = _nr_sqrt_s1sq + _nr_sqrt_s1 + _nr_sqrt_nos1
|
|
1161
1164
|
|
|
1162
|
-
if not np.isclose(
|
|
1165
|
+
if not np.isclose(
|
|
1163
1166
|
np.einsum("i->", _nr_t2_mdr.astype(np.float64)),
|
|
1164
1167
|
np.einsum("i->", _nr_t2_s1.astype(np.float64)),
|
|
1165
1168
|
rtol=0,
|
|
@@ -1167,7 +1170,7 @@ def shrratio_boundary_xact_avg(
|
|
|
1167
1170
|
):
|
|
1168
1171
|
raise RuntimeError(
|
|
1169
1172
|
"Calculation of sq. root term in exact average GUPPI"
|
|
1170
|
-
f"with recapture spec, {f'"{
|
|
1173
|
+
f"with recapture spec, {f'"{recapture_form}"'} is incorrect."
|
|
1171
1174
|
)
|
|
1172
1175
|
|
|
1173
1176
|
_s_2 = (_nr_t1 - np.sqrt(_nr_t2_s1)) / (2 * _r_val)
|
|
@@ -1223,7 +1226,7 @@ def _shrratio_boundary_intcpt(
|
|
|
1223
1226
|
_r_val: mpf,
|
|
1224
1227
|
/,
|
|
1225
1228
|
*,
|
|
1226
|
-
|
|
1229
|
+
recapture_form: Literal["inside-out", "proportional"],
|
|
1227
1230
|
agg_method: Literal["arithmetic", "geometric", "distance"],
|
|
1228
1231
|
weighting: Literal["cross-product-share", "own-share"] | None,
|
|
1229
1232
|
) -> float:
|
|
@@ -1234,7 +1237,7 @@ def _shrratio_boundary_intcpt(
|
|
|
1234
1237
|
_s_intcpt = mpf("1.0")
|
|
1235
1238
|
case None if agg_method == "distance":
|
|
1236
1239
|
_s_intcpt = _delta_star * mp.sqrt("2")
|
|
1237
|
-
case None if agg_method == "arithmetic" and
|
|
1240
|
+
case None if agg_method == "arithmetic" and recapture_form == "inside-out":
|
|
1238
1241
|
_s_intcpt = mp.fdiv(
|
|
1239
1242
|
mp.fsub(
|
|
1240
1243
|
2 * _delta_star * _r_val + 1, mp.fabs(2 * _delta_star * _r_val - 1)
|