PDEAlchemy is a CLI-first framework for pricing financial options with PDE solvers, starting from transparent TOML configurations.
Abbreviation: PDA (pronounced “pee-dee-ay”), with a playful nod to “Personal Digital Assistant”.
Initial project skeleton is in place, including:
uv-managed Python project configurationjusttask automation- CLI scaffold (
price,validate,explain) - Base logging and error handling modules
flowchart LR
User["User"]
Config["TOML pricing config"]
subgraph Interfaces
CLI["CLI<br/>price / validate / explain"]
Notebook["marimo notebook<br/>price explorer"]
end
subgraph Orchestration
Loader["Config loader + schema checks"]
Symbolic["Symbolic problem builder"]
Dispatcher["Pricing dispatcher"]
Validation["Validation runner<br/>(optional analytical checks)"]
Explain["Explain renderer<br/>(markdown/text)"]
end
subgraph Backends
QuantLib["QuantLib adapter<br/>FD vanilla + MC exotic"]
PyPDE["py_pde adapter<br/>1D vanilla PDE"]
end
Outputs["Price + metadata + diagnostics"]
UX["CLI output and notebook tables/plots"]
User --> Config
User --> CLI
User --> Notebook
CLI --> Loader
Notebook --> Loader
Config --> Loader
Loader --> Symbolic
Loader --> Dispatcher
Loader --> Validation
Dispatcher --> QuantLib
Dispatcher --> PyPDE
QuantLib --> Outputs
PyPDE --> Outputs
Validation --> Outputs
Symbolic --> Explain
Explain --> UX
Outputs --> UX
sequenceDiagram
participant U as User
participant CLI as pdealchemy CLI
participant L as Config loader
participant D as Pricing dispatcher
participant Q as QuantLib adapter
participant P as py_pde adapter
participant V as Validation runner
U->>CLI: pdealchemy price config.toml
CLI->>L: Parse and schema-validate config
L-->>CLI: Canonical pricing request
CLI->>D: dispatch(request)
alt backend = quantlib
D->>Q: price(request)
Q-->>D: price + diagnostics
else backend = py_pde
D->>P: price(request)
P-->>D: price + diagnostics
end
opt validation enabled
CLI->>V: Run optional analytical checks
V-->>CLI: Validation report
end
D-->>CLI: Result payload
CLI-->>U: Formatted price output
uv sync --all-extras --dev
just test
uv run pdealchemy --helpFor a step-by-step notebook-first Black-Scholes path, see QUICKSTART.md.
- Vanilla:
examples/vanilla_european_call.toml - Exotic (discrete Asian + barrier + dividends):
examples/exotic_discrete_asian_barrier_dividend.toml - Vanilla with market curve/surface inputs:
examples/vanilla_market_curve_surface.toml
uv run pdealchemy validate path/to/config.toml
uv run pdealchemy explain path/to/config.toml --format markdown
uv run pdealchemy validate path/to/config.toml --analytical --tolerance 0.75
uv run pdealchemy validate path/to/config.toml --equation-library library
uv run pdealchemy spec-to-runtime-toml path/to/spec.toml --output path/to/pricing.toml --overwrite
uv run pdealchemy price path/to/config.tomlConvenience just recipes for the main workflows:
just price
just validate
just explainQuantLib vanilla pricing routes can now consume optional market structures from TOML:
- Flat or node-based zero-rate curves for risk-free and dividend term structures
- Constant volatility, volatility term curves, or volatility surfaces
See examples/vanilla_market_curve_surface.toml for a complete configuration.
Current limitation:
- Exotic Monte Carlo routes currently require flat curves and constant volatility.
py_pdebackend currently supports 1D vanilla European routes (no exotic features).
An interactive marimo notebook example is available at examples/notebooks/price_explorer.py.
It now includes:
- side-by-side vanilla pricing comparison across QuantLib and
py_pde, - finite-difference Greeks estimates,
- Plotly charts for backend price comparison and spot-sweep sensitivity.
Launch it with either command:
just notebookuv run marimo edit examples/notebooks/price_explorer.pyRun it in app mode:
just notebook-runQuick notebook static check:
just notebook-checkjust notebook-run starts a server and keeps running until interrupted. Exiting with Ctrl-C returns code 130, which is expected behaviour.
PDEAlchemy also supports notebook-first specification authoring with semantic cell names:
- Template:
templates/spec_template.py - Example notebook:
examples/notebooks/spec_black_scholes.py - Equation library root:
library/
Convert a specification notebook into TOML:
uv run pdealchemy notebook-to-toml examples/notebooks/spec_black_scholes.py --output examples/notebooks/black_scholes_blueprint.toml --overwriteBridge spec TOML to executable runtime TOML:
uv run pdealchemy spec-to-runtime-toml examples/notebooks/black_scholes_blueprint.toml --output examples/notebooks/black_scholes_pricing.toml --overwriteOr use just recipes:
just notebook-to-toml
just spec-to-runtime-toml
just bs-e2e
just bs-results
just bs-spec-resultsResults capture notebook:
just notebook examples/notebooks/black_scholes_results.pyCombined notebook (specification content followed by outputs):
just notebook examples/notebooks/spec_black_scholes_with_results.pyPDEAlchemy now supports a side-effect-free payoff wrapper function in symbolic expressions:
PAY(max(S - K, 0))This exploratory mode is inspired by OpenSourceRisk conventions documented at opensourcerisk.org.
Current scope:
PAY(...)is interpreted as a pure identity wrapper around the contained payoff expression.- This enables exploration of ORE-style payoff authoring without changing core pricing behaviour.
LOGPAY(...)remains unsupported in core pricing and is intentionally treated as out of scope.
Ruff and ty are configured for progressive quality enforcement:
- Source code (
src/) is checked for docstrings and type annotations. - Tests are temporarily excluded from strict docstring/type-hint linting while coverage is improved incrementally.
Commands:
just lint
just typecheck
just test-cov
just checkPre-commit setup:
just precommit-install
just precommit-run- Philosophy:
VALIDATION_PHILOSOPHY.md - Practical strategy and trust boundaries:
docs/validation_strategy.md - Black-Scholes-first testing path:
docs/black_scholes_first_workflow.md - Output artefact content and format options:
docs/output_specification.md - Data source purpose and limitations:
docs/market_data_sources.md
The project now keeps a progressive engineering log under docs/blog/.
Start here:
docs/blog/README.md