Metadata-Version: 2.4
Name: crudecode-valuation
Version: 0.2.1
Summary: Oil & gas deal valuation toolkit for Crude Code inner agents — forecast, cashflow, NPV.
Project-URL: Homepage, https://github.com/petroleumPythoneer/crudecode
Author: Crude Code
License-Expression: MIT
License-File: LICENSE
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Requires-Python: >=3.11
Requires-Dist: crudecode-analyst>=0.5.0
Requires-Dist: numpy>=1.24
Requires-Dist: pandas>=2.0
Requires-Dist: scipy>=1.10
Description-Content-Type: text/markdown

# crudecode-valuation

Domain package for oil & gas deal valuation. Used by the `valuation_agent`
inner agent in the EI Plugins MCP server.

## What you get

Two subsystems:

- **`forecast`** — primitives + diagnostic helpers for building production
  forecasts from analogs, own history, or blends.
- **`econ`** — revenue → cashflow → NPV given a forecast tuple, interest
  type, and economic assumptions.

You compose them yourself. No fused `valuate()` entry point.

## Agent workflow — EXPLORE → DECIDE → EXECUTE

Every `/tmp/valuation.py` follows three phases marked by comment headers.

### EXPLORE

Load production, find analogs, fit them, and inspect with the three
diagnostic helpers:

```python
from crude_valuation import (
    load_production, find_analogs, fit_curve,
    cohort_summary, fit_quality, target_vs_cohort,
)

prod = load_production(WELL_APIS, run_sql)
analog_apis = find_analogs(ANALOG_FILTER, run_sql, exclude=WELL_APIS)
analog_curves = []
for a in analog_apis:
    try:
        analog_prod = load_production([a], run_sql)
        q = np.array(analog_prod["oil_bbl"])
        months = np.arange(len(q), dtype=float)
        meta = peek_well(a, run_sql)
        analog_curves.append(
            fit_curve(months, q, stream="oil", lateral_norm_ft=meta.lateral_ft, b_fixed=0.8)
        )
    except ValueError:
        continue   # skip thin / un-fittable analogs

print(cohort_summary(analog_curves))
cohort_median = percentile_curves(analog_curves, pct=0.5, norm_to_lateral_ft=10_000.0)

for api in WELL_APIS:
    meta = peek_well(api, run_sql)
    target_prod = load_production([api], run_sql)
    target_q = np.array(target_prod["oil_bbl"])
    target_months = np.arange(len(target_q), dtype=float)
    print(api, target_vs_cohort(cohort_median, target_months, target_q, meta.lateral_ft))
```

### DECIDE

A free-form comment block. Document the call per well in plain English:

```python
# === DECIDE ===
# Cohort: 15 analogs after dropping 3 bound-riders. b median 0.92, IQR 0.31.
# Target 42-329-12345 (Wolfcamp A, 9,400 ft):
#   36 months observed, qi_ratio 1.04 -> in line with cohort
#   Choice: pdp; fit own decline with b=0.9 (cohort median, dropped outliers)
# Target 42-329-12999 (PERMITTED, 10,200 ft):
#   no history; planned spud 2026-08
#   Choice: pure_analog with cohort curve at target lateral
```

### EXECUTE

Build Forecasts inline per-well — no router, no module constants:

```python
# === EXECUTE ===
forecasts_oil = []
forecasts_gas = []
for api, choice in CHOICES.items():
    if choice["strategy"] == "pdp":
        forecasts_oil.append(_build_pdp(api, b=choice["b"], stream="oil", ...))
    elif choice["strategy"] == "thin_blend":
        forecasts_oil.append(_build_thin(api, cohort_median_oil, ...))
    else:  # pure_analog
        forecasts_oil.append(_build_pure_analog(api, cohort_median_oil, ...))
    # same for gas

# Econ + persist
revenue = compute_gross_revenue(forecasts_oil, forecasts_gas, price_deck=...)
cashflow = compute_net_cashflow(revenue, interest_type=INTEREST_TYPE, ...)
npv_dict = npv(cashflow, discount_rates=[0.08, 0.10, 0.12, 0.15])

spec = build_minimal_briefing(headline=..., commentary=..., npv=npv_dict, ...)
persist(spec, persist_url="$persist_url")
```

Commentary must reference at least one concrete diagnostic you looked at.

## Playbooks

`crude_valuation/forecast/examples/` ships annotated playbooks showing the
EXPLORE → DECIDE → EXECUTE pattern end-to-end. Today only `forecast_mixed.py`
is in the new playbook form; the other two are still recipe-style.

## Diagnostic helpers in detail

- `cohort_summary(curves)` — distribution stats per fitted parameter (qi, di,
  b, terminal, lateral). Plus `notes` flagging fits that rode the b bound.
- `fit_quality(curve, months, q)` — R^2, RMSE, bound-riding flags, max
  residual %, early/late signed-% bias windows for a single fit.
- `target_vs_cohort(cohort, target_months, target_q, target_lateral_ft)` —
  qi_ratio of target to cohort, peak detection, per-month signed-% residual
  vs cohort placed at target lateral.

None of these decide anything. The agent reads them and chooses.
