# SEC corporate-finance methodology

## Scope and current status

Points `[37]–[39]` implement a corporate-finance foundation for Walmart Inc.
(`WMT`, CIK `0000104169`) using only the official SEC/EDGAR Company Facts
endpoint:

```text
https://data.sec.gov/api/xbrl/companyfacts/CIK0000104169.json
```

The implementation is covered by synthetic, offline tests and has now executed
against the official Walmart response. Exactly one non-forced request completed
on `2026-08-23`; the 3,726,043-byte cache has SHA-256
`8f3d9abe26f7f7842a070f4b639dbd39296144349270eeb0df5659e7510e9cec`.
Entity, CIK and schema checks passed. Points `[37]`, `[38]` and `[39]` are
`reviewer_approved`; downstream Excel points `[40]–[41]` are also
`reviewer_approved`. Point `[42]` is blocked pending review of its code-only
remediation V1 after a controlled attempt-1 failure, and `[43]` remains
`not_permitted`.

## Access contract

- The client requests exactly one company-specific Company Facts document; it
  never downloads the SEC bulk archive or another company.
- `SEC_USER_AGENT` must be provided by the operator. The value exists only in
  the in-memory HTTP header and is never written to logs, metadata, receipts,
  tests, or commits.
- The project limit is at most 8 requests per second. The current workflow
  normally needs one request; bounded retries apply only to 403, 429, and the
  configured 5xx statuses.
- `Retry-After` is honored up to 60 seconds. Otherwise exponential backoff is
  used, starting at one second.
- The request uses a 30-second timeout and asks for JSON with identity content
  encoding.
- The local cache is `data/raw/sec/walmart_companyfacts.json`. It is Git-ignored
  and installed atomically through a `.part` file.

The client rejects HTML, empty or truncated JSON, content-length mismatches,
unexpected schemas, a non-Walmart entity, or a noncanonical CIK before the
document can enter normalization. Response SHA-256 and byte size are recorded
without recording the user-agent value.

## Concept mapping and annual selection

`config/concept_map.yaml` is the declarative contract. Every normalized metric
defines its statement, preferred and fallback US-GAAP tags, allowed unit,
instant/duration behavior, sign rule, selection rule, missing-value policy,
and checks.

The real response confirmed the preferred executive-window tags documented in
the concept map. In particular, current receivables use
`ReceivablesNetCurrent`; total equity uses
`StockholdersEquityIncludingPortionAttributableToNoncontrollingInterest`;
liabilities-and-equity uses `LiabilitiesAndStockholdersEquity`; and short-term
borrowings use `ShortTermBorrowings`. No configured fallback was selected in
the five-year executive window. Conservative historical revenue fallbacks
remain declared because older annual facts use different taxonomy generations.

Annual selection is fail-closed:

1. only `10-K` and `10-K/A` are eligible;
2. `fp=FY` is required;
3. duration facts must span 330–371 inclusive days;
4. instant facts must not contain a start date;
5. `end_date` is the canonical period end; the project fiscal year is its
   calendar year, while the source `fy` is preserved only as `sec_reported_fy`;
6. only an allowed unit is eligible;
7. candidates compete by normalized metric plus canonical period end;
8. the lowest tag rank wins;
9. duplicates at that rank resolve to the latest filing date, then amendment,
   then lexicographically latest accession.

A later `10-K/A` for the same annual fact can therefore supersede a `10-K`,
but its form, filing date, accession, and amendment flag remain in lineage.
Facts are never summed merely because Company Facts contains duplicates.
Quarterly and YTD facts are never promoted to annual values.

For instant balance facts, `instant_date` is the canonical `end_date`. For
duration income and cash-flow facts, both `start_date` and `end_date` must be
present and satisfy the annual duration contract before the end date can
identify the period. The selected `total_assets` period ends define Walmart's
canonical annual-period universe; other metrics are reconciled to those exact
ends. This prevents an incidental balance date from creating a second project
year while retaining date-based identity. A comparative republished in a later
filing therefore competes with prior facts for the same economic period rather
than becoming a different year because the filing carries another `fy` label.
The selected filing's original numeric `fy` remains auditable in
`sec_reported_fy`; derived rows retain it only when every component agrees.

Missing concepts remain null with an explicit reason. Absence is never treated
as zero. Fallback tags are not combined with preferred tags; their equivalence
must be confirmed against the real filing presentation before publication.

## Derived metrics and sign conventions

- `total_liabilities = liabilities_and_equity - equity`, only when both source
  facts use compatible units and dates. This preserves the independent
  `Assets ≈ LiabilitiesAndStockholdersEquity` accounting check while avoiding
  an invented direct `Liabilities` tag that Walmart Company Facts does not
  provide.
- `total_debt = current_debt + long_term_debt + short_term_borrowings`, only
  when all three components use compatible units and dates. Here
  `current_debt` is the current portion of long-term debt.
- `free_cash_flow = operating_cash_flow - capex`.
- CAPEX is configured as a nonnegative cash-outflow magnitude. No sign is
  flipped silently; a negative fact under that contract is ineligible.
- Investing and financing cash flow preserve the reported sign.
- Income and equity preserve the reported sign; revenue, cost of sales,
  balance assets, debt, and CAPEX must be nonnegative under their configured
  concepts.

Every derived row names its formula and all component accessions.

## Ratios

Ratios are calculated only when required inputs exist, units are compatible,
and denominators are nonzero:

```text
gross_margin                    = (revenue - cost_of_sales) / revenue
operating_margin                = operating_income / revenue
net_margin                      = net_income / revenue
inventory_turnover              = cost_of_sales / average_inventory
inventory_days                  = average_inventory * 365 / cost_of_sales
operating_cash_flow_margin      = operating_cash_flow / revenue
capex_as_percent_of_revenue     = capex / revenue
free_cash_flow_margin           = free_cash_flow / revenue
debt_to_assets                  = total_debt / total_assets
average_inventory               = (beginning_inventory + ending_inventory) / 2
```

If beginning or ending inventory is unavailable, both inventory ratios are
`null` with `beginning_and_ending_inventory_required`. No ending-balance proxy
is substituted silently. Other missing inputs and zero denominators likewise
produce null plus a reason.

## Outputs and lineage

The successful real-source build created:

- `data/processed/sec/walmart_annual_financials.parquet`: all selected valid
  annual direct and derived facts, not only the executive five-year window;
- `provenance/sec_facts_lineage.csv`: one row per normalized fact with filing
  and accession lineage;
- `outputs/finance/corporate_financials.csv`: the dynamically selected latest
  five complete annual periods;
- `outputs/web/corporate_financials.json`: annual aggregates, ratios, checks,
  and limitations—never the raw Company Facts document;
- `outputs/validation/sec_finance_receipt.json`: hashes for the raw cache,
  concept map, implementation, SQL, and outputs, plus row/check counts.

The corrected build retained 18 continuous valid and complete annual periods,
`2009`–`2026`, and selected fiscal years `2022`–`2026` for the executive
outputs. The Parquet and lineage each represent 378 normalized rows. All 45
ratio cells across five years were
calculable. Balance-sheet identity, FCF reconciliation and debt reconciliation
each passed for all five executive years: 15 PASS, 0 FAIL and
0 NOT_CALCULABLE. The local receipt is 1,752 bytes with SHA-256
`dbb1c0400643f7289648ffc9933a15e35bed91fa9b0797e97d8f4282f4dd495d`.

All selected direct executive facts came from `10-K` filings in USD. The
selector still admits `10-K/A` and the synthetic suite demonstrates that a
later eligible amendment supersedes the prior fact while preserving its form
and accession. Quarterly and YTD facts were present in Company Facts but were
not eligible for the annual outputs.

`sql/08_financials.sql` independently pivots the normalized annual SEC table,
uses `LAG` for beginning inventory, derives ratios with `NULLIF`, and produces
balance-sheet, FCF, and debt checks. It contains no personal or raw-data path.

## Reproduction

Configuration and synthetic validation require no SEC identity or data:

```text
uv run python -m src.ingestion.sec --validate-config-only
uv run python -m src.finance.corporate_financials --validate-config-only
uv run pytest -q tests/test_sec_ingestion.py tests/test_sec_financials.py
```

Refreshing the real cache requires the operator to set `SEC_USER_AGENT`, then
run the ingestion module followed by the financial builder. The value must not
be passed as a CLI argument or committed. An unchanged valid cache is reused
without another request; `--force-refresh` requires an explicit reason.

## Separation from M5

Corporate SEC values are a separate contextual layer. They do not reconcile
the M5 sample, do not turn estimated retail revenue into Walmart corporate
revenue, and do not depend on validation or official-evaluation results. This
module preserves `delivery_profile=validation_only_portfolio`. The downstream
Excel model consumes only its aggregate output through Power Query; this SEC
module itself does not start Power BI, PVM, or any M5 lifecycle stage.
