Skip to main content
CFDL

Python SDK

cfdl_sdk compiles and runs CFDL models from Python with pandas accessors over the results. The compiler and engine are embedded in-process as a Rust extension module — no separate binary or server, and results are byte-identical to the CLI's.

Install

See Install for Python. Short version: pip install cfdl-sdk.

Quickstart

import cfdl_sdk

results = cfdl_sdk.run(
    "examples/cre_developer",
    packs_dir="packs",
    pack="cre",
    config="examples/cre_developer/run.base.json",
)

results.cashflows()      # wide DataFrame: one column per stream, PeriodIndex
results.metrics()        # flat Series of core + domain metrics
results.metrics_frame()  # metric / value / currency / source(core|domain:<pack>)
results.scenarios()      # one row per scenario (when the run declares them)
results.monte_carlo()    # per-metric summary stats (mean/stdev/percentiles)
results.annual()         # annual rollup, when present

The package's surface, with the signatures as the package defines them:

cfdl_sdk.run(model_dir, *, packs_dir=None, config=None, rate=None, as_of=None, pack=None) -> Results
cfdl_sdk.compile(model_dir, *, packs_dir=None) -> Model
Model.run(*, config=None, rate=None, as_of=None, pack=None, packs_dir=None) -> Results
Model.ir                                  # property: the compiled IR, a dict
Results.from_json(results_json: str) -> Results

Results.cashflows(*, wide: bool = True) -> pandas.DataFrame
Results.metrics() -> pandas.Series
Results.metrics_frame() -> pandas.DataFrame
Results.annual() -> pandas.DataFrame
Results.scenarios() -> pandas.DataFrame
Results.monte_carlo() -> pandas.DataFrame
Results.scenario(name: str) -> Results
Results.compare(other=None, *, scenario=None, baseline=None) -> dict
Results.explain(series: str, period: int) -> dict
Results.explain_statement(statement: str, row: int, period: int, through: int | None = None) -> dict
Results.trace(kind=None, actor=None) -> pandas.DataFrame
Results.journal() -> pandas.DataFrame
Results.to_dict() -> dict
Results.status, Results.warnings, Results.model_hash, Results.ledger_hash, Results.run_config, Results.results_json, Results.scenario_names   # properties
cfdl_sdk.package(dir, *, root="model.cfdl", run="run.json", results="results.json", ir=None) -> dict
cfdl_sdk.verify(dir) -> dict
cfdl_sdk.reproduce(dir) -> dict
Results.plot.cashflows(series=None, *, ax=None)          # needs the [viz] extra
Results.plot.cumulative(series="model.net_cash_flow", *, ax=None)
Results.plot.mc_distribution(metric, *, ax=None)
  • explain and explain_statement say what made a number: the agreement, the source line, the folds, the cell's reason and its owner's lifecycle evaluation that period, and the acts. row and period are 0-based as published; through is the last column of a cell that spans several, such as a year of a monthly statement.
  • compare(scenario="downside") says what differs between the base run and a scenario, and why; baseline= names another scenario to compare from, and other another Results. scenario("downside") is a scenario's run as results of its own. The answer's decisions list the first period each actor decided differently, lifecycles first, before the cash.
  • trace() says why every period is what it is: one row per run of one reason, for each lifecycle (moved, held or set, the state, the guards tested), event, option, stream and field; kind and actor filter it. Its entry is a position in journal(), which lists every act with its position and parent.
  • status is the deterministic run's status, warnings what it noticed, model_hash which model produced it, ledger_hash what came out, and run_config the run configuration as the run used it.
  • package, verify and reproduce make and check a package folder (Packaging and verifying runs).
  • The review page, inputs.assumptions, has no accessor of its own: read it from to_dict().

config accepts a dict, a path to a run-config JSON file, or a raw JSON string. pack names the pack whose domain metrics the run computes; it is auto-detected from a pack file in the model directory when one exists, and examples/cre_developer has none, so the quickstart passes it. The four domain packs are built into the package, so a model that says use pack needs no packs_dir; pass one to read the packs from a directory instead. compile() and Model.run() are available separately when you want to reuse compiled IR.

Compile problems raise CompileError with structured .diagnostics (code, message, span); runtime problems raise RunError.

Notebooks

Four notebooks, one per domain, built on the benchmark models. Each is published here with the outputs and chart it produced, so you can read the SDK's DataFrame surface on a real model before installing anything:

Each page is produced by executing its notebook against the released packs and engine, so the outputs and charts are real. The notebooks read benchmark models that are not distributed, so they are published as rendered pages rather than as files to run.