API reference¶
Everything the package exports from suno_valuation. Nothing outside this list
is public: names that do not appear here may change without a version bump.
The whole surface is typed and the package ships py.typed, so a type checker
sees these signatures directly.
Running a valuation¶
run_valuation(inputs: ValuationInputs) -> ValuationResult¶
Runs the model and returns the result. Pure: no I/O, no globals, no clock. The same inputs always produce the same output.
Raises EvalDateBeforeCommissioningError, EvalDateAfterEndOfLifeError,
InvalidProjectDataError, EmptyMacroSeriesError, MacroYearMissingError,
EmptySeriesError, PpaYearMissingError, UnknownIndexationIndexError or
RiskScoreOutOfRangeError — all subclasses of SunoValuationError, which is
itself a ValueError.
load_inputs_snapshot(snapshot: Mapping[str, Any]) -> ValuationInputs¶
Rebuilds a published valuation's inputs from its snapshot document — the
inverse of build_inputs_snapshot, and the function that makes a published
valuation checkable from outside Suno.
It never guesses. A snapshot whose snapshot_schema it does not know is
refused by number rather than read on the assumption that the format did not
change, and a missing or unreadable key is refused by its path rather than
defaulted.
Raises:
UnsupportedSnapshotSchemaError—snapshot_schemais absent, is not an integer, or is not inSUPPORTED_SNAPSHOT_SCHEMAS.MalformedSnapshotError— a supported schema missing a key the format guarantees, or carrying an unreadable value for one.
run_sensitivities(inputs: ValuationInputs, base: ValuationResult) -> list[SensitivityRow]¶
Runs the fixed set of sensitivity levers against a base case and returns one row per lever. This is the tornado published alongside each valuation.
Serialising a valuation¶
build_inputs_snapshot(inputs, ctx, sensitivities=()) -> dict[str, Any]¶
Produces the snapshot document: everything the engine consumed, plus the
identifiers (SnapshotContext) that live only in Suno's systems. Those
identifiers are why an outside reader generally consumes snapshots rather than
building them.
serialize_yearly_table(result: ValuationResult) -> list[dict[str, Any]]¶
The complete year-by-year table as plain dictionaries, one per row, with every column the engine produced.
SNAPSHOT_SCHEMA: int¶
The schema version build_inputs_snapshot writes. Currently 15.
SUPPORTED_SNAPSHOT_SCHEMAS: tuple[int, ...]¶
The schemas load_inputs_snapshot reads: (14, 15). Schema 14 is read by
defaulting local_currency to "COP", which is what every valuation published
under it was denominated in.
Inputs¶
ValuationInputs¶
Frozen dataclass; everything one valuation consumes. Required fields first, then the optional ones with their defaults.
| Field | Type | Meaning |
|---|---|---|
kwp |
float |
Installed capacity. |
design_prod |
float |
Specific production, kWh per kWp per day — not annual energy. |
est_opex |
float |
Projected monthly O&M, local currency. |
eff_y0 |
float |
Panel efficiency at year zero (fraction). |
deg |
float |
Annual degradation (fraction). |
lifetime_years |
int |
Useful life from commissioning. |
commissioning |
date |
Commissioning date. |
eval_date |
date |
Valuation date. |
suno_fee |
float |
Fee as a fraction of total revenue. |
rec_price |
float |
REC price in local currency (legacy route). |
country_premium |
float |
Sovereign spread added to the discount rate. |
macro |
list[MacroYear] |
The macro series. |
ppa |
list[PpaPoint] |
The PPA price curve. |
risk |
list[RiskDimension] |
Scored risk dimensions. |
hist_prod_monthly |
list[float] |
Metered monthly production. Default []. |
hist_opex_monthly |
list[float] |
Invoiced monthly O&M, oldest first. Default []. |
ppa_indexation_index |
str |
"ppi", "inflation" or "plain". Default "ppi". |
ppa_base_year |
int \| None |
Year the PPA curve's money is stated in. Default: the first point's year. |
ppa_anchor_month |
int |
Month the 12-month price batches start in. Default 1. |
equipment_base_year |
int \| None |
Year replacement costs are stated in. Default: the evaluation year. |
additional_income_base_year |
int \| None |
Same, for additional income. |
specific_prod_override |
float \| None |
Pinned specific production, replacing the blend. |
opex_real_escalation |
float |
Annual real O&M growth over CPI. Default 0.0. |
rec_price_usd |
float \| None |
REC price in USD; takes precedence over rec_price. |
local_currency |
str |
Default "COP"; "USD" means no conversion. |
equipment_replacements |
list[EquipmentReplacement] |
Replacement events. Default []. |
additional_incomes |
list[AdditionalIncome] |
Additional income items. Default []. |
investment_amount_usd |
float \| None |
Capital invested, for the withholding calculation. |
capital_repayment_fractions |
dict[int, float] |
Repayment curve, keyed on years relative to the investment (1-based). |
investment_date |
date \| None |
Anchors that curve. None means the commissioning date. |
wht_rate |
float |
Withholding rate on distributions. Default 0.15. |
MacroYear¶
year, fx_rate (local currency per USD), inflation (local CPI), ppi,
fed_rate, usd_inflation (float | None; None holds the FX rate flat past
the forecast and raises a warning).
PpaPoint¶
year, present_value — a price in the curve's base-year money.
RiskDimension¶
dimension, max_bps, score. max_bps is basis points per point of
score, not a cap; the scale runs 0 to 2, so a dimension's effective ceiling is
twice max_bps. The field name is historical.
EquipmentReplacement¶
year_index (1-based row), amount (today's unit cost; negative means
salvage), label, quantity, currency ("LOCAL" or "USD"), usd_share,
real_delta_usd, real_delta_cop, uplift, tax, contingency,
probability, downtime. The defaults reproduce the legacy treatment: one
unit in local currency escalating at CPI, certain, with no downtime.
AdditionalIncome¶
year_index (1-based), amount (local currency), concept. Several items may
share a year.
Outputs¶
ValuationResult¶
npv_usd, npv_after_tax_usd, pwatt_nav_usd, currency, eval_date,
specific_prod_used, monthly_opex_used, yearly_table (list[YearRow]),
warnings (list[str]).
Each headline number is reconcilable from the table: npv_usd is the sum of
discounted_cashflow_usd, npv_after_tax_usd the sum of
discounted_net_investor_cashflow_usd, and pwatt_nav_usd the latter divided
by kwp × 1,000.
YearRow¶
One calendar row. Carries year, year_index, time_factor, cpi_rel,
cpi_index, ppi_index, discount_rate, discount_factor, discount_index,
fx_rate, efficiency, generation_kwh, recs, energy_price,
energy_sales_lc, rec_sales_lc, opex_lc, equipment_replacement_lc,
additional_income_lc, suno_fee_lc, net_cashflow_lc, net_cashflow_usd,
discounted_cashflow_usd, capital_repayment_usd, wht_usd,
net_investor_cashflow_usd, discounted_net_investor_cashflow_usd and
equipment_downtime.
Fields suffixed _lc are in the project's local currency; _usd ones are in
USD, converted at that row's fx_rate.
SensitivityRow¶
key, kind ("quantile", "parameter" or "scenario"), perturbation
(the exact magnitude applied), npv_usd, npv_after_tax_usd,
pwatt_nav_usd, delta_npv_pct, delta_nav_pct.
SnapshotContext¶
Metadata build_inputs_snapshot records alongside the inputs: project and
macro-set identifiers and names, country, OPEX source, the chosen capital
repayment curve, and the default evaluation date. None of it enters the
calculation.
Errors¶
Every error the package raises derives from SunoValuationError, which derives
from ValueError. Catching SunoValuationError catches all of them; catching
ValueError does too, for a caller that does not want to import the package's
names.
| Error | Raised when | Attributes |
|---|---|---|
SunoValuationError |
base class — never raised directly | — |
InvalidProjectDataError |
an input outside the range the model is defined on, physical or financial | field, value |
EvalDateBeforeCommissioningError |
the valuation date precedes commissioning | eval_date, commissioning |
EvalDateAfterEndOfLifeError |
the valuation date is past the end of useful life | — |
EmptyMacroSeriesError |
the macro series is empty | — |
MacroYearMissingError |
a year carrying cash flow has no macro coverage and precedes the series | year |
EmptySeriesError |
a trimmed mean was asked for over a series with no usable values | — |
PpaYearMissingError |
a year carrying cash flow precedes the first PPA point | year |
UnknownIndexationIndexError |
ppa_indexation_index is not one of the three known values |
kind |
RiskScoreOutOfRangeError |
a risk score outside the 0–2 scale | score |
UnsupportedSnapshotSchemaError |
the snapshot declares a schema the loader does not read | schema |
MalformedSnapshotError |
a supported schema is missing a key or carries a bad value | key, reason |
MalformedSnapshotError.key is a dotted path into the document
(assumptions.suno_fee, macro[2].fx_rate), so the offending entry can be
found in the JSON without reading any code.
Command line¶
suno-valuation is installed as a console script by the package.
suno-valuation run SNAPSHOT [--json]
suno-valuation verify SNAPSHOT [--expect-nav X] [--expect-npv Y] [--tolerance T]
main(argv: Sequence[str] | None = None, file: TextIO | None = None) -> int is
the same entry point as a Python function, returning the exit code rather than
raising. Exit codes: 0 reproduced, 1 mismatch, 2 cannot be evaluated —
see Reproduce a valuation.
Version¶
__version__ carries the installed package version. A published valuation
records both the version and the commit SHA of the engine that computed it;
pin the SHA, since a tag can move.