Python bindings for the Derivatives Algorithms Library (DAL) — a high-performance C++17 quantitative finance library with Automatic Adjoint Differentiation (AAD) support.
- Black-Scholes and Dupire models for equity derivatives pricing
- Monte Carlo simulation with pseudo-random and Sobol sequence generators
- AAD Greeks — compute pathwise sensitivities (delta, vega, rho, etc.) in a single simulation
- Script engine — define exotic payoffs using a domain-specific language
- Type-safe wrappers for
Date_,Matrix_,Cell_, and vector types
- Python 3.10+ with development headers
- uv — fast Python package manager (install guide)
- pybind11 — vendored as a git submodule at
dal-cpp/externals/pybind11(v2.11.1); rungit submodule update --init --recursiveon fresh clones - CMake 3.21+ and a C++17 compiler (GCC 13+, Clang 18+, or MSVC 2022)
- DAL C++ library — must be built first (see Building the C++ Library)
The Python bindings depend on the compiled DAL C++ library. Build it first:
cd /path/to/Derivatives-Algorithms-Lib
./build_linux.shThis produces:
lib/libdal_public.a— public API librarylib/libdal_cpp.a— core libraryinclude/— public headers
Clone the repository and install in editable mode:
cd Derivatives-Algorithms-Lib/dal-python
# Create a virtual environment with uv
uv venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
# Install dependencies and build the extension
uv pip install -e ".[test]" --no-build-isolation \
--config-settings=cmake.define.DAL_DIR=/path/to/Derivatives-Algorithms-LibReplace /path/to/Derivatives-Algorithms-Lib with the absolute path to the repository root.
Use the provided test runner to automatically set up the environment and verify the installation:
bash run_tests.shThis script:
- Creates a uv virtual environment
- Installs all dependencies (scikit-build-core, pytest, numpy)
- Builds the Python extension in development mode
- Runs the full test suite (112 tests)
For production deployment, you can build pre-compiled binary wheels or source distributions.
Binary wheels contain the compiled C++ extension and can be installed without requiring compilation:
./build_wheel.sh # Build wheel for current platform
./build_wheel.sh --manylinux # Build manylinux-compatible wheel (Linux only)
./build_wheel.sh --clean # Clean build artifacts before buildingThe wheel will be created in dist/ directory:
dal_python-2025.12.7-cp313-cp313-linux_x86_64.whl(2.7 MB)
Install the wheel:
pip install dist/dal_python-2025.12.7-cp313-cp313-linux_x86_64.whl
# or
uv pip install dist/dal_python-2025.12.7-cp313-cp313-linux_x86_64.whlNote: Binary wheels are platform-specific. A wheel built on Linux x86_64 will only work on similar systems.
Source distributions allow users to build from source on any platform:
./build_sdist.sh # Build source distribution
./build_sdist.sh --clean # Clean build artifacts before buildingThe source distribution will be created in dist/ directory:
dal_python-2025.12.7.tar.gz(20 KB)
Install from source (requires C++ build tools):
pip install dist/dal_python-2025.12.7.tar.gz \
--config-settings=cmake.define.DAL_DIR=/path/to/Derivatives-Algorithms-Lib
# or
uv pip install dist/dal_python-2025.12.7.tar.gz \
--config-settings=cmake.define.DAL_DIR=/path/to/Derivatives-Algorithms-LibRequirements for building from source:
- C++17 compiler (GCC 13+, Clang 18+, or MSVC 2022)
- CMake 3.21+
- pybind11 (vendored as a git submodule at dal-cpp/externals/pybind11, v2.11.1)
- Python 3.10+ development headers
- DAL C++ library (libdal_public.a and libdal_cpp.a)
import dal
# Set evaluation date
dal.EvaluationDate_Set(dal.Date_(2022, 9, 25))
# Define model parameters
spot, vol, rate, div = 100.0, 0.2, 0.05, 0.02
model = dal.BSModelData_New(spot=spot, vol=vol, rate=rate, div=div)
# Define a European call option
strike = 100.0
maturity = dal.Date_(2023, 9, 25)
product = dal.Product_New(
["STRIKE", dal.Cell_(maturity)],
[str(strike), "call pays MAX(spot() - STRIKE, 0.0)"]
)
# Price using Monte Carlo (65,536 paths, Sobol sequences)
result = dal.MonteCarlo_Value(product, model, 2**16, "sobol")
print(f"Call PV: {result['PV']:.4f}")
# Output: Call PV: 9.2259Enable AAD to compute pathwise sensitivities in a single simulation:
result = dal.MonteCarlo_Value(
product, model,
2**14, # num_paths
"sobol", # method
False, # use_bb
True # enable_aad
)
print(f"PV: {result['PV']:.6f}")
for key in sorted(result.keys()):
if key.startswith('d_'):
print(f" {key}: {result[key]:.6f}")Output:
PV: 9.223019
d_STRIKE: -0.494542
d_div: -58.677195
d_rate: 49.454176
d_spot: 0.586772
d_vol: 37.873346
import dal
# Create dates
d = dal.Date_(2022, 9, 25)
print(d) # 2022-09-25
# Date arithmetic
d2 = d.AddDays(30)
print(f"Year: {dal.Year(d)}, Month: {dal.Month(d)}, Day: {dal.Day(d)}")
# Date comparisons
d3 = dal.Date_(2022, 10, 25)
print(d < d3) # True# Pseudo-random generator (MRG32k32a algorithm)
pseudo = dal.PseudoRSG_New(42, 3) # seed=42, ndim=3
uniform_samples = dal.PseudoRSG_Get_Uniform(pseudo, 1000) # Returns DoubleMatrix_
normal_samples = dal.PseudoRSG_Get_Normal(pseudo, 1000)
# Sobol quasi-random sequences (better convergence for MC)
sobol = dal.SobolRSG_New(0, 3) # i_path=0, ndim=3
sobol_samples = dal.SobolRSG_Get_Uniform(sobol, 1000)# Define a local volatility surface with flat 20% vol
spots = [80.0, 90.0, 100.0, 110.0, 120.0]
times = [0.5, 1.0, 2.0]
vols = dal.DoubleMatrix_(len(spots), len(times), 0.2) # Fill with 20% vol
dupire_model = dal.DupireModelData_New(
spot=100.0,
rate=0.05,
repo=0.01,
spots=spots,
times=times,
vols=vols
)Note: The current pybind11 binding only supports read access to matrix elements via matrix(i, j). Use the constructor's fill value parameter to initialize all elements.
dal.Date_(year, month, day)— Date object with arithmetic operationsdal.String_(value)— String wrapperdal.Cell_(value)— Polymorphic value container (bool, double, Date, String)dal.DoubleVector()— Vector of doublesdal.DoubleMatrix_(rows, cols)— 2D matrix of doubles
dal.BSModelData_New(spot, vol, rate, div)— Black-Scholes modeldal.DupireModelData_New(spot, rate, repo, spots, times, vols)— Dupire local vol model
dal.Product_New(dates, events)— Create a script product from event dates and payoff definitionsdal.Product_Debug(product)— Print human-readable product structure
dal.MonteCarlo_Value(product, model, num_paths, method="sobol", use_bb=False, enable_aad=False, smooth=0.01)— Monte Carlo pricing with optional AAD Greeks
Parameters:
product— Script product (fromProduct_New)model— Model data (fromBSModelData_NeworDupireModelData_New)num_paths— Number of simulation paths (use powers of 2 for Sobol)method— Random generator:"sobol"(default) or"mrg32"use_bb— Use Brownian bridge construction (defaultFalse)enable_aad— Enable AAD for pathwise Greeks (defaultFalse)smooth— Fuzzy logic smoothing parameter for discontinuous payoffs (default0.01)
Returns: Dictionary with keys:
"PV"— Present value"d_spot","d_vol","d_rate","d_div","d_STRIKE"— AAD Greeks (only ifenable_aad=True)
dal.PseudoRSG_New(seed, ndim=1)— Pseudo-random generator (MRG32k32a)dal.SobolRSG_New(i_path, ndim=1)— Sobol quasi-random generatordal.PseudoRSG_Get_Uniform(rsg, num_paths)— Uniform samples [0, 1]dal.PseudoRSG_Get_Normal(rsg, num_paths)— Standard normal samplesdal.SobolRSG_Get_Uniform(rsg, num_paths)— Sobol uniform samplesdal.SobolRSG_Get_Normal(rsg, num_paths)— Sobol normal samples
dal.EvaluationDate_Set(date)— Set global evaluation datedal.EvaluationDate_Get()— Get global evaluation date
Run the full test suite:
bash run_tests.shRun specific tests:
bash run_tests.sh -k "test_date" # Run date-related tests
bash run_tests.sh -v # Verbose outputTests are located in tests/ and cover:
- Date arithmetic and comparisons
- Vector and matrix operations
- Model construction (BS, Dupire)
- Monte Carlo pricing accuracy vs Black-Scholes analytical formulas
- AAD Greek computation and validation
- Random number generator properties
dal-python/
├── CMakeLists.txt # Build configuration
├── pyproject.toml # Python package metadata (scikit-build-core)
├── run_tests.sh # Development workflow script
├── src/
│ ├── bindings/
│ │ ├── module.cpp # pybind11 module definition
│ │ ├── bindings.h # shared binding helpers
│ │ ├── core.cpp # core types (Date_, String_, Cell_, vectors, DoubleMatrix_)
│ │ ├── global.cpp # Handle_<T> opaque types, EvaluationDate_Get/Set
│ │ ├── models.cpp # model types (BSModelData_, etc.)
│ │ ├── random.cpp # random number generators
│ │ ├── script.cpp # scripting engine bindings
│ │ └── value.cpp # value type conversions
│ └── dal/
│ ├── __init__.py # Package initialization
│ └── api.py # High-level Python API wrappers
├── tests/
│ ├── conftest.py # Pytest fixtures
│ └── test_*.py # Test modules
The Python bindings are generated by pybind11 from domain-organized binding files. The build process:
- CMake configures the build, locates DAL C++ libraries, and fetches pybind11
- C++ compiler builds
_dal.cpython-*.soextension module from the domain-organizedsrc/bindings/*.cppfiles - scikit-build-core packages everything into an installable wheel
The hand-written Python code in src/dal/ provides:
__init__.py— Re-exports all pybind11-generated symbolsapi.py— Convenience wrappers (e.g.,Product_Newwith automatic type conversion)
Ensure DAL_DIR points to the correct DAL installation:
ls $DAL_DIR/lib/libdal_public.a # Should exist
ls $DAL_DIR/include/dal # Should existThe extension module failed to build. Check the build logs:
# Rebuild with verbose output
rm -rf build/ *.egg-info
uv pip install -e . --no-build-isolation \
--config-settings=cmake.define.DAL_DIR=/path/to/dal \
-vEnsure you're using the virtual environment:
source .venv/bin/activate
python -c "import dal; print(dal.__version__)"BSD 3-Clause License. See the main DAL repository for details.
- Run tests before submitting changes:
bash run_tests.sh - Follow the existing code style in
src/dal/andtests/ - Add tests for new functionality in
tests/ - Update this README if you change the public API
- DAL C++ Library — Core library documentation
- CLAUDE.md — AI-assisted development context (coding conventions, build commands, test workflow for Claude Code)
- pybind11 Documentation — pybind11 binding syntax