Multi-metric portfolio reporting and repairs

The APIs combine multiple financial measurements, their coverage, and one reporting schema. They diagnose the ambiguity a summary leaves, suggest local drills, and compare conditional evidence plans. These APIs are available in updatesupport-finance>=0.1.6 with updatesupport>=0.1.7.

Compile a common book without losing known evidence

import updatesupport as us
import updatesupport_finance as uf

evidence = uf.compile_portfolio_metrics(
    universe,
    observations=observations,
    taxonomy=taxonomy,
    policies={
        "negative_ocf": uf.PortfolioMetricPolicy("OCF", "currency"),
        "negative_cash_gap": uf.PortfolioMetricPolicy("OCF_minus_cash_capex", "currency"),
    },
)
# Alternatively, combine existing validated CompiledPortfolio objects:
# evidence = uf.JointPortfolioEvidence({"negative_ocf": ocf, "negative_cash_gap": gap})

Every metric must use the same supplied holdings, issuer mapping, currency, cutoff, and eligibility policy. Joins check per-position weights and descriptors, shared fact definitions, descriptor provenance, and issuer measurements across share classes. Original observations retain derivation inputs and fiscal dates; latest annual evidence does not imply a common calendar-year observation.

The movable book is the positive-value intersection covered by every metric. For each metric, metric_scope(name) retains its complete coverage ledger, known contributions outside that intersection, missing domains, and eligible scope evidence floor. For instance, missing capex for one company does not erase its known OCF from another metric.

Common-book endpoints are lifted as (common_value * endpoint + fixed_known_contribution + missing_domain_contribution) / eligible_value. Nonjoint holdings weights and known values are fixed. Missing domains range independently per metric; this does not assume that all joint endpoint outcomes can occur together. Unbounded missing domains make eligible conclusions inconclusive. Eligible/supplied denominators remain separate, and no independent holdings completeness or NAV reconciliation is inferred.

Inspect a frontier without fabricating a mandate

report = uf.joint_portfolio_report(
    evidence,
    public=("sector",),
    hidden=("sector", "industry", "issuer_id", "listing_age"),
    candidate_refinements=("industry", "listing_age"),
    q_presets=("saturated", us.q_tv_budget(0.01)),
    max_evaluations=64,
)
print(report.to_markdown())
tables = report.to_tables()

Without criteria this returns raw_frontier, no selected certificate. Optional ambiguity_limits={metric: width} and decisions={metric: DecisionRule} apply in the explicit scope="eligible" (default) or scope="common". Precision is in the metric’s units: a binary share width of 0.05 is five percentage points. Decision criteria require every admissible value to pass the declared rule. A Q mapping {metric: (preset, ...)} supports different scenarios per metric. Intervals envelope every declared Q, and per-Q results remain inspectable.

The finite search enumerates global descriptor subsets up to max_added_columns, plus individual supplied conditional proposals. The budget counts representations, each solved for every metric/Q. It raises before search if the full enumeration exceeds max_evaluations. Report-cell counts refer to the common book, not every supplied holding. Pareto choices retain ties; optimality is restricted to the declared candidate set. Optional disclosure_costs={column: effort} adds caller-supplied disclosure effort to frontier comparisons when every evaluated proposal can be priced. Partial cost information remains visible without treating missing estimates as zero. Evidence effort and disclosure effort remain separate dimensions.

For a chosen common-book threshold, use report.breaking_witness(metric, candidate_index=..., decision=..., q_index=...). This invokes the existing inverse machinery with respect_q=True. TV mass times common holdings value is a transferred-value equivalent, not a forecast loss or trade recommendation. Eligible-scope unknown evidence is not silently included in this witness.

Discover and replay local drills

suggestions = us.suggest_conditional_refinements(
    evidence.rows,
    public=chosen_public_columns,
    targets=tuple(evidence.metrics),
    candidate_columns=("industry", "listing_age"),
    weight="weight", max_candidates=64,
)
report = uf.joint_portfolio_report(
    evidence, public=("sector",), hidden=hidden,
    candidate_refinements=allowed_columns,
    conditional_refinements=suggestions.candidates,
)

ConditionalRefinement(name, column, predicate) is a data-only bucket split. Its as_dict()/from_dict() and transform(rows) preserve tagged categories and leave other buckets intact. A predicate includes every base public field; additional predicate fields become part of that proposal’s reporting schema. This lets a local drill of an already refined report compete against global schemas in one artifact. Each proposal is evaluated alone; arbitrary combinations of local drills are outside this search. All generated columns are appended to the declared hidden support; state-keyed Q matrices must use that full support.

Suggestions exclude identity and direct target columns by default, inspect mixed-target buckets, and enumerate separable descriptors. possible_count and exact disclose candidate truncation. Evaluation always solves the entire book under Q; local improvements are not added together. Suggestions are target-aware and in-sample, with no causal or out-of-sample classification claim.

Plan evidence acquisition alongside reporting

actions = [uf.EvidenceAcquisition(
    "company cash package", ("security_id",),
    residual_widths={"negative_ocf": 0, "negative_cash_gap": 0},
    availability="unknown",
    source_requirements=("annual OCF", "compatible cash capex"),
)]
plans = uf.plan_portfolio_repairs(
    report, actions,
    ambiguity_limits={"negative_ocf": .05, "negative_cash_gap": .05},
    max_evaluations=4096,
)

Each package declares attainable residual widths conditional on acquisition, not the eventual metric value or a successful API call. Width zero means exact measurement if research succeeds, without assuming its sign. Bounded nonbinary domains can be narrowed to explicit positive residual widths. Actions must affect missing nonjoint evidence. Unavailable packages are omitted from combinations; unknown availability stays visible. Optional effort costs enter the frontier only with complete estimates; absent estimates do not become zero. The planner retains insufficient plans and ties and labels all feasible plans as conditional. It performs no retrieval.

If newly covered companies enter the movable book, rebuild and rerun. The prospective width guarantee holds only when resolved nonjoint values and weights remain fixed. This API does not optimize outcome-dependent new support.

Save and monitor without historical calibration

snapshot = uf.capture_joint_portfolio_snapshot(report, capture_references=hashes)
restored = uf.JointPortfolioSnapshot.from_json(snapshot.to_json())
replayed = restored.replay()  # No provider calls.
restored.replay_matches()
restored.runtime_differences()
uf.compare_joint_portfolio_snapshots(snapshot, restored)

contract = uf.FrozenJointPortfolioContract.freeze(report, candidate_index=chosen_index)
future = contract.audit(new_evidence)
contract.audit(None)  # Inconclusive: no_snapshot.

Snapshots archive normalized facts, derivations, descriptor provenance, coverage, domains, schemas, predicates, Q, scope, results, capture references, and versions. They recompile supplied evidence and validate result scope/configuration. Hashes detect corruption; they do not authenticate source completeness. Snapshots do not include credentials, executable callbacks, or provider adapters.

The contract fixes schema and measurement policies; it does not redesign or recalibrate. Q is analyst-declared and applied to each fresh batch. Reference TV movement is diagnostic, not a historically calibrated coverage guarantee. New issuer support, new unresolved securities, lost reference public buckets, or changed measurement/scope definitions yield inconclusive results. A new share class of an existing issuer retains its facts and separate value. Hidden support may contract when reference public fibers survive. Changed classifications are detected as support changes, not silently rewritten.

Generic single-claim contracts use us.FrozenReportContract.freeze(claim, rows) with portable JSON round-trip and the same support diagnostics, independent of FrozenPublicReportPolicy’s historical calibration factory.

Run the provider-free example to exercise diagnosis, local repairs, conditional research, replay, and freezing.