Skip to main content

acquisitionuniverse (Python)

Utilities for search funds, independent sponsors and corporate development teams who work from screened company lists. Score records, rank them, measure evidence gaps and generate a clean thesis brief.

Pure Python, standard library only, offline.

pip install acquisitionuniverse

Where it fits

A full-web screen hands you a long ranked file. Before you call anyone you want to answer three questions: what exactly did we ask for, which companies come first, and how much is the file really telling us?

This package answers those with small functions that work on dictionaries and DataFrame rows alike. The method behind the screens is described on the search fund target lists page.

1. Write the brief

from acquisitionuniverse import ThesisBrief

brief = ThesisBrief(
    name="Calibration labs, Midwest",
    vertical="Calibration and metrology",
    regions=["Ohio", "Michigan", "Indiana"],
    employees_min=8,
    employees_max=60,
    must_have=["ISO/IEC 17025 accreditation"],
    exclusions=["group-owned", "single-person shops"],
)

assert brief.validate() == []
print(brief.to_text())

to_json() gives the same content as a string for a ticket or a shared drive.

2. Rank the file

from acquisitionuniverse import rank

companies = [
    {"id": "C-01", "mandate_fit": 92, "outreach_suitability": 75, "transition_context": 60},
    {"id": "C-02", "mandate_fit": 97, "outreach_suitability": 90, "transition_context": 80, "group_owned": True},
    {"id": "C-03", "mandate_fit": 84, "outreach_suitability": 88, "transition_context": 40},
]

for c in rank(companies):
    print(c["id"], c["composite"])

C-02 has the best fit but belongs to a group, so its outreach component is zero and it falls behind C-01. That ordering is the point: you should call the independent first.

Custom weights:

rank(companies, weights={"mandate_fit": 0.6, "outreach_suitability": 0.3, "transition_context": 0.1})

3. Count the evidence

from acquisitionuniverse import SIGNALS, signal_coverage

record = {SIGNALS[0]: "founder led", SIGNALS[3]: "service company", SIGNALS[10]: None}
print(signal_coverage(record))   # {'visible': 2, 'checked': 3, 'ratio': 0.666...}

A value of None or the string "not visible" counts as silent. You see at a glance whether a high score rests on much evidence or little.

With pandas

import pandas as pd
from acquisitionuniverse import composite

df = pd.read_csv("screen.csv")
df["composite"] = df.apply(
    lambda r: composite({
        "mandate_fit": r.mandate_fit,
        "outreach_suitability": r.outreach_suitability,
        "transition_context": r.transition_context,
        "group_owned": r.group_owned == "yes",
    }),
    axis=1,
)
df.sort_values("composite", ascending=False).head(25).to_csv("call_list.csv", index=False)

Reference

Name Kind Notes
ThesisBrief dataclass validate, to_dict, to_json, to_text
composite(company, weights=None) function 0 to 100, one decimal
rank(companies, weights=None) function returns copies with composite
signal_coverage(map) function visible, checked, ratio
SIGNALS tuple 15 names
DEFAULT_WEIGHTS dict 0.7, 0.2, 0.1
PILOT_URL str pilot request page

Alpha Quantum also publishes an audience segmentation API for advertisers and a CV parser for hiring teams. Neither is needed here.

Working with screened lists in Python

Python is where most deal analysts do their analysis, whether in a notebook or in a script that runs before a Monday meeting. This package gives you three small tools that fit that habit: a brief you can validate and send, a composite score you can recompute and explain, and a coverage measure that tells you how much of a record is evidence.

None of them touches the network. A screening engagement delivers a file, and what you do with it afterwards is your own business. The toolkit helps you do it consistently.

From a delivered file to a call list

Suppose you have received a ranked CSV with one row per company, three score columns and a flag for group ownership. A script turns it into a call list in a few lines, and the same script is easy to re-run when you change the weights.

import pandas as pd
from acquisitionuniverse import composite, rank

df = pd.read_csv("screen.csv")
records = df.to_dict("records")
for r in records:
    r["group_owned"] = str(r.get("group_owned", "")).lower() in ("yes", "true", "1")

ordered = pd.DataFrame(rank(records))
call_list = ordered[ordered["composite"] >= 75].head(40)
call_list.to_csv("call_list.csv", index=False)
print(f"{len(call_list)} companies at 75 or above")

A threshold such as 75 is a judgement. Look at the distribution first. If your list has a clear break between the top thirty and the rest, cut there. If it fades slowly, say so in the memo and call further down.

Explaining a ranking to a partner

Partners ask why. A good answer names the three scores and the rule about group ownership. A better answer shows the arithmetic for the company in question.

def explain(c, weights=None):
    w = weights or {"mandate_fit": 0.7, "outreach_suitability": 0.2, "transition_context": 0.1}
    outreach = 0 if c.get("group_owned") else c.get("outreach_suitability", 0)
    parts = [
        ("fit", c["mandate_fit"], w["mandate_fit"]),
        ("suitability", outreach, w["outreach_suitability"]),
        ("context", c["transition_context"], w["transition_context"]),
    ]
    lines = [f"{n}: {v} x {wt} = {v * wt:.1f}" for n, v, wt in parts]
    return "\n".join(lines + [f"composite: {composite(c, w)}"])

Printing that for a company that surprised somebody ends most debates. If it does not, the debate is about the weights, which is the debate you wanted.

Coverage as a triage tool

Evidence quality varies across a list. Use coverage to triage.

Coverage What it suggests
12 to 15 of 15 ready for outreach with quoted evidence
8 to 11 of 15 likely fine, check the signals that matter most to your thesis
below 8 a small or quiet site, research before you spend a call

Those bands are a starting point, not a rule. A quiet site is not a bad company, because many excellent family businesses publish little. It is simply a company where the screen has less to say, and where a human review pays off.

Using the brief with your team

A brief is also a team tool. Keep it in version control next to your analysis, so that a change in the thesis shows up as a diff. Run validate() in a test, so a brief cannot be committed with no exclusions. Print to_text() into the email that requests a pilot, so the wording a screening team receives matches the wording your own analysts use.

brief = ThesisBrief(
    name="Water treatment services, Mid-Atlantic",
    vertical="Water treatment",
    subverticals=["service and maintenance", "chemical programs"],
    regions=["Pennsylvania", "Maryland", "Virginia"],
    employees_min=10, employees_max=75,
    must_have=["service contracts"],
    exclusions=["group-owned", "residential retail"],
)
problems = brief.validate()
assert not problems, problems

Reading about the method

The search fund target lists page shows how a screened universe is turned into a list a searcher can actually call. The standards page lists the categories the service declines to serve, such as veterinary and dental practices, because the buyer's strategy there harms captive consumers.

If you work on both sides of a market, the sister services may help. The audience segmentation API returns demographic and interest attributes for any URL without cookies, and the CV parser returns 114 fields for any resume.

A note on vertical signals

Screenings add signals for the trade you are screening, such as ASME code stamps for boiler services or ISO/IEC 17025 accreditation for calibration labs. Keep them in a separate dictionary from the fifteen common signals, because signal_coverage counts only the fifteen it knows.

Further reading and practical notes

The Python documentation covers dataclasses, json and the other standard library pieces used above. For buyers who finance acquisitions with bank debt, the US Small Business Administration explains its loan programmes, which many searchers and independent sponsors use when they buy a small company.

Three notes from practice. First, keep inputs and outputs of every ranking run. A ranked list that cannot be reproduced is an argument waiting to happen. Save the input file, the weights, the package version and the date, and a month later you can explain the order that was on the screen. Second, version your briefs. A brief is a living document, and the change from one version to the next is the history of what you learned. Store each version, with a note about why it changed. Third, separate research from outreach. The list that analysts maintain is not the list the callers use. Give the callers a short, stable extract with the quotes beside each name, and keep the full file for analysts.

If you run searches in several verticals at once, keep a separate brief and a separate weights file for each. A single set of weights rarely suits every vertical, because the signals that matter in water treatment, such as chemical programmes and service contracts, differ from those that matter in precision machining, such as certifications and end markets.

Testing and typing

The module has type hints and a small test file. Add your own tests for the weights you use, with a handful of fixtures you can verify by hand. Property tests help too: for any inputs between 0 and 100, the composite must stay between 0 and 100, and marking a company group owned must never raise its score.

Compatibility

Python 3.7 or later. No third party dependencies. The package is MIT licensed.

Questions

Is a database included? No. Bring your own screened records.

Why dictionaries? They match JSON, CSV rows and pandas without a conversion step.

Python versions? 3.7 and up. MIT license. info@alpha-quantum.com

Metadata

Release files for acquisitionuniverse 1.0.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for acquisitionuniverse 1.0.1
File Size Uploaded Provenance Actions
acquisitionuniverse-1.0.1.tar.gz 10.5 kB Without provenance Details for acquisitionuniverse-1.0.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for acquisitionuniverse 1.0.1
File Interpreter ABI Platform Actions
acquisitionuniverse-1.0.1-py3-none-any.whl Python 3 none any Details for acquisitionuniverse-1.0.1-py3-none-any.whl

Total release size: 19.7 kB

Release files / acquisitionuniverse-1.0.1.tar.gz

Download URL acquisitionuniverse-1.0.1.tar.gz
Size 10.5 kB
Tags Source
SHA-256 checksum
How to use checksums
3972b0bb9dd6e967be2d9bf9c72e920707daa3649c9c9d2368d3bb682e7ff444
BLAKE2b-256 checksum
How to use checksums
33095720ab02ccda6b0a63eb54ef5a70af29978384579baa291e063f4de71db5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.8.10

Release files / acquisitionuniverse-1.0.1-py3-none-any.whl

Download URL acquisitionuniverse-1.0.1-py3-none-any.whl
Size 9.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a2de256a0bf4ff92c4f620b0e0406ae95ebe6cd9b3efcda4d929576fea443381
BLAKE2b-256 checksum
How to use checksums
8be39cd4db4b4c169526bdcff1a6df5da27de7029941a74a9cbb489dcbbd9c9a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.8.10

Security for acquisitionuniverse 1.0.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

1.0.1 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page