Skip to main content
CFDL

Scenarios and run configurations

A compiled model (IR) is evaluated under a run configuration — so the same model answers base case, stress case, and sensitivity questions without editing the model file.

Base + stress in one run

run.json:

{
  "deterministic": {
    "annual_discount_rate": 0.10,
    "parameters": { "inputs.market.new.ti_psf": 80.0 }
  },
  "scenarios": {
    "stress": {
      "annual_discount_rate": 0.12,
      "parameters": { "inputs.market.new.ti_psf": 40.0, "inputs.market.renewal.rent_share": 1.0 }
    }
  }
}
cfdl run my-deal/ir.json --config run.json --out results.json

Results carry the deterministic block plus one summary per scenario. Each summary carries the scenario's metrics, directly comparable with the base run's, and its own series, annual rollup, journal, transitions, trace, slices and statements, and its own ledger_hash, so two scenarios are told apart by what came out. A declared metric the scenario could not evaluate is listed under omitted with the reason, not published as a number. The results also publish the run configuration as run, each inputs file's content in place of its path, so a reader of the results sees what each scenario was asked.

Other run configuration keys

  • annual_discount_rate, or annual_discount_curve naming a series-shaped assumption the model declares, never both. With neither, the run publishes no model.npv.
  • as_of — the date discounting measures from.
  • valuation_grain — omitted, each cash flow discounts at the annual rate raised to its fractional year; "annual" first groups a finer model's cash by calendar year.
  • arithmetic — omitted, expressions evaluate in decimal; "excel_compat" evaluates in float64 to reconcile against a workbook. A scenario cannot set it.

Override keys

  • inputs.<name> — an assumption, by its full name; a set's field is inputs.<set>.<field>. A value is replaced and a distribution is fixed. An assumption derived from an overridden one reads the override, because the run's values land before any assumption is evaluated.
  • Nothing else. A stream's amount moves through an assumption it reads (amount = inputs.rent). The stream.<name>:amount key is not accepted and is refused with a hint.

The run is refused before any period exists when a key goes wrong:

  • E5033 — a key that matches nothing the model declares or reads, or a malformed setting; the message names the nearest name.
  • E5041 — a supplied value outside the assumption type's domain or its within.
  • E5045 — a bodiless assumption the run did not supply.

The inputs file

A bodiless assumption (assume sofr : cre.base_rate) is the run's to supply. inputs in the deterministic case or a scenario names a JSON file, relative to the run configuration, keyed by full assumption name; each entry states one shape — a value, a series with points, or a quantile — and may cite its own source, which the review page then shows. A series or a quantile entry replaces the model's body; a value entry is an ordinary override. A scenario's file is a different snapshot and nothing else.

Sensitivity sweeps

Generate configurations programmatically (they are plain JSON) or drive sweeps from the Python SDK, where results.scenarios() lands each scenario as a DataFrame row.

Monte Carlo

The monte_carlo section (trial_count, seed, and distributions, which override distributions the model declares, by full name) turns the same IR into a distribution — see Stochastic modeling and the run-config reference.