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.

Files changed (32) hide show
  1. {mergeron-2024.738953.1 → mergeron-2024.738972.0}/PKG-INFO +32 -17
  2. mergeron-2024.738972.0/README.rst +64 -0
  3. {mergeron-2024.738953.1 → mergeron-2024.738972.0}/pyproject.toml +1 -1
  4. {mergeron-2024.738953.1 → mergeron-2024.738972.0}/src/mergeron/License.txt +1 -1
  5. {mergeron-2024.738953.1 → mergeron-2024.738972.0}/src/mergeron/core/__init__.py +3 -3
  6. {mergeron-2024.738953.1 → mergeron-2024.738972.0}/src/mergeron/core/excel_helper.py +3 -1
  7. {mergeron-2024.738953.1 → mergeron-2024.738972.0}/src/mergeron/core/ftc_merger_investigations_data.py +13 -16
  8. {mergeron-2024.738953.1 → mergeron-2024.738972.0}/src/mergeron/core/guidelines_boundaries.py +48 -45
  9. {mergeron-2024.738953.1 → mergeron-2024.738972.0}/src/mergeron/core/guidelines_boundaries_specialized_functions.py +24 -21
  10. {mergeron-2024.738953.1 → mergeron-2024.738972.0}/src/mergeron/gen/__init__.py +72 -80
  11. 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
  12. {mergeron-2024.738953.1 → mergeron-2024.738972.0}/src/mergeron/gen/data_generation.py +42 -42
  13. {mergeron-2024.738953.1 → mergeron-2024.738972.0}/src/mergeron/gen/investigations_stats.py +1 -1
  14. mergeron-2024.738972.0/src/mergeron/gen/market_sample.py +79 -0
  15. {mergeron-2024.738953.1 → mergeron-2024.738972.0}/src/mergeron/gen/upp_tests.py +143 -98
  16. mergeron-2024.738953.1/README.rst +0 -49
  17. {mergeron-2024.738953.1 → mergeron-2024.738972.0}/src/mergeron/__init__.py +0 -0
  18. {mergeron-2024.738953.1 → mergeron-2024.738972.0}/src/mergeron/core/InCommon RSA Server CA cert chain.pem +0 -0
  19. {mergeron-2024.738953.1 → mergeron-2024.738972.0}/src/mergeron/core/damodaran_margin_data.py +0 -0
  20. {mergeron-2024.738953.1 → mergeron-2024.738972.0}/src/mergeron/core/ftc_invdata.msgpack +0 -0
  21. {mergeron-2024.738953.1 → mergeron-2024.738972.0}/src/mergeron/core/proportions_tests.py +0 -0
  22. {mergeron-2024.738953.1 → mergeron-2024.738972.0}/src/mergeron/core/pseudorandom_numbers.py +0 -0
  23. {mergeron-2024.738953.1 → mergeron-2024.738972.0}/src/mergeron/ext/__init__.py +0 -0
  24. {mergeron-2024.738953.1 → mergeron-2024.738972.0}/src/mergeron/ext/tol_colors.py +0 -0
  25. {mergeron-2024.738953.1 → mergeron-2024.738972.0}/src/mergeron/jinja_LaTex_templates/clrrate_cis_summary_table_template.tex.jinja2 +0 -0
  26. {mergeron-2024.738953.1 → mergeron-2024.738972.0}/src/mergeron/jinja_LaTex_templates/ftcinvdata_byhhianddelta_table_template.tex.jinja2 +0 -0
  27. {mergeron-2024.738953.1 → mergeron-2024.738972.0}/src/mergeron/jinja_LaTex_templates/ftcinvdata_summary_table_template.tex.jinja2 +0 -0
  28. {mergeron-2024.738953.1 → mergeron-2024.738972.0}/src/mergeron/jinja_LaTex_templates/ftcinvdata_summarypaired_table_template.tex.jinja2 +0 -0
  29. {mergeron-2024.738953.1 → mergeron-2024.738972.0}/src/mergeron/jinja_LaTex_templates/mergeron.cls +0 -0
  30. {mergeron-2024.738953.1 → mergeron-2024.738972.0}/src/mergeron/jinja_LaTex_templates/mergeron_table_collection_template.tex.jinja2 +0 -0
  31. {mergeron-2024.738953.1 → mergeron-2024.738972.0}/src/mergeron/jinja_LaTex_templates/setup_tikz_tables.tex.jinja2 +0 -0
  32. {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.738953.1
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
- Routines for plotting boundaries of (sets of mergers falling within) specified concentration and diversion ratio boundaries and for calibrating GUPPI thresholds to concentration (ΔHHI) thresholds, and vice-versa.
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
- mergeron.core.guidelines_boundaries
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
- Routines for generating industry data under various distributions of shares, prices, and margins. 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.
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
- mergeron.gen.data_generation
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
- Routines 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. 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.
64
+ Programs demonstrating the analysis and reporting facilites provided by the sub-package, :code:`mergeron.examples`.
67
65
 
68
- mergeron.gen.guidelines_tests
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
- FTC investigations data and test data are printed to screen or rendered in LaTex to text files (for processing into publication-quality tables) using routines provided in :code:`mergeron.gen.investigations_stats`.
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 routines 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:
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
@@ -1,7 +1,7 @@
1
1
  [tool.poetry]
2
2
  name = "mergeron"
3
3
  # See ./get_version_str.py
4
- version = "2024.738953.1"
4
+ version = "2024.738972.0"
5
5
  description = "Analysis of standards defined in Horizontal Merger Guidelines"
6
6
  keywords = [
7
7
  "merger policy analysis",
@@ -1,7 +1,7 @@
1
1
  mergeron
2
2
  ========
3
3
 
4
- Copyright 2017-2023 S. Murthy Kambhampaty
4
+ Copyright 2017-2024 S. Murthy Kambhampaty
5
5
 
6
6
  MIT License
7
7
  -----------
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  from importlib.metadata import version
4
4
 
5
- from attrs import Attribute, define, field, validators
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
- @define(slots=True, frozen=True)
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
- recapture_spec: RECConstants | None = field(
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
- Routines for writing data from Python to fresh Excel workbooks using
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
- Routines to parse FTC Merger Investigations Data, downloading source documents
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(parse_invdata()) # Convert immutable to mutable
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 parse_invdata(
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:
@@ -1,17 +1,16 @@
1
1
  """
2
- Routines for defining and analyzing boundaries for Guidelines standards,
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 define, field
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
- @dataclass(slots=True, frozen=True)
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 U.S. Horizontal Merger Guidelines (HMG)
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, /, *, dh_dps: int = 9) -> float:
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
- dh_dps
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
- dh_dps,
550
+ prec,
548
551
  )
549
552
 
550
553
 
551
- def dh_area_quad(_dh_val: float = 0.01, /, *, dh_dps: int = 9) -> float:
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
- dh_dps
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
- dh_dps,
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
- dh_dps
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, dh_dps=prec),
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
- recapture_spec=_bdry_spec.recapture_spec.value, # type: ignore
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
- recapture_spec=_bdry_spec.recapture_spec.value, # type: ignore
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
- recapture_spec=_bdry_spec.recapture_spec.value, # type: ignore
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
- recapture_spec=_bdry_spec.recapture_spec.value, # type: ignore
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
- recapture_spec: str = "inside-out",
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
- recapture_spec
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 recapture_spec == "inside-out":
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
- recapture_spec: Literal["inside-out", "proportional"] = "inside-out",
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
- recapture_spec
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
- # recapture_spec == "inside-out"
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
- # recapture_spec == "proportional"
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 recapture_spec == "inside-out"
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
- recapture_spec=recapture_spec,
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
- recapture_spec: Literal["inside-out", "proportional"] = "inside-out",
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
- # recapture_spec = "inside-out"
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
- # recapture_spec = "proportional"
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
- recapture_spec
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 recapture_spec == "inside-out":
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) # type: ignore
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( # type: ignore
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'"{recapture_spec}"'} is incorrect."
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
- recapture_spec: Literal["inside-out", "proportional"],
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 recapture_spec == "inside-out":
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)