dal-python 2026.8.11__cp312-cp312-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
dal/__init__.py ADDED
@@ -0,0 +1,11 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ # Import the C extension first to avoid circular imports when dal.py
4
+ # executes "from . import _dal" during package initialization.
5
+ from . import _dal # noqa: F401
6
+ from .dal import *
7
+ from .api import *
8
+
9
+ __author__ = 'The Derivatives Algorithms Group'
10
+ __email__ = 'wegamekinglc@hotmail.com'
11
+ __version__ = "2026.8.11"
Binary file
dal/api.py ADDED
@@ -0,0 +1,119 @@
1
+ from . import dal as _bindings
2
+
3
+
4
+ def Product_New(events_dates: list, events: list[str]):
5
+ wrapped = [d if isinstance(d, _bindings.Cell_) else _bindings.Cell_(d) for d in events_dates]
6
+ return _bindings.Product_New(wrapped, events)
7
+
8
+
9
+ # Settings whose target fields expect DAL value types — plain Python str
10
+ # must be wrapped before setattr so pybind11 can convert them correctly.
11
+ # DAL objects (String_, CollateralType_ etc.) pass through as-is.
12
+ _DAL_TYPE_CONVERTERS = {
13
+ 'curve_name': lambda v: _bindings.String_(v) if isinstance(v, str) else v,
14
+ 'target_collateral': lambda v: _bindings.CollateralType_(v) if isinstance(v, str) else v,
15
+ 'target_tenor': lambda v: _bindings.PeriodLength_(v) if isinstance(v, str) else v,
16
+ 'libor_basis': lambda v: _bindings.DayBasis_(v) if isinstance(v, str) else v,
17
+ }
18
+
19
+ _OPTIONAL_SETTING_ATTRS = {
20
+ 'curve_name': 'curveName_',
21
+ 'target_collateral': 'targetCollateral_',
22
+ 'target_tenor': 'targetTenor_',
23
+ 'calibrate_discount': 'calibrateDiscountCurve_',
24
+ 'libor_basis': 'liborBasis_',
25
+ 'smoothing_weight': 'smoothingWeight_',
26
+ 'tolerance': 'tolerance_',
27
+ 'fit_tolerance': 'fitTolerance_',
28
+ 'max_evaluations': 'maxEvaluations_',
29
+ 'max_restarts': 'maxRestarts_',
30
+ 'initial_guess': 'initialGuess_',
31
+ 'solve_mode': 'solveMode_',
32
+ 'parameterization': 'parameterization_',
33
+ 'log_df_scheme': 'logDfScheme_',
34
+ }
35
+
36
+
37
+ def _apply_optional_setting(spec, name, value):
38
+ """Apply a single optional setting to a spec builder if the value is not None."""
39
+ if name not in _OPTIONAL_SETTING_ATTRS:
40
+ valid = ', '.join(sorted(_OPTIONAL_SETTING_ATTRS))
41
+ raise ValueError(f"Unknown calibration setting {name!r}. Supported settings: {valid}")
42
+ if value is None:
43
+ return
44
+ attr = _OPTIONAL_SETTING_ATTRS[name]
45
+ convert = _DAL_TYPE_CONVERTERS.get(name)
46
+ setattr(spec, attr, convert(value) if convert else value)
47
+
48
+
49
+ def _build_calibration_spec(today, ccy, instruments, knot_dates, settings, base_curve=None):
50
+ """Build a CurveCalibrationSpec_ with sensible defaults and optional overrides."""
51
+ spec = _bindings.CurveCalibrationSpecBuilder_()
52
+ spec.today_ = today
53
+ spec.ccy_ = ccy if isinstance(ccy, _bindings.String_) else _bindings.String_(ccy)
54
+
55
+ spec.curveName_ = _bindings.String_("calibrated")
56
+ spec.calibrateDiscountCurve_ = True
57
+ spec.smoothingWeight_ = 1.0
58
+ spec.tolerance_ = 1e-8
59
+ spec.fitTolerance_ = 1e-6
60
+ spec.maxEvaluations_ = 200
61
+ spec.maxRestarts_ = 20
62
+ spec.initialGuess_ = 0.05
63
+
64
+ spec.instruments_ = instruments
65
+ spec.knotDates_ = knot_dates
66
+ if base_curve is not None:
67
+ spec.baseCurve_ = base_curve
68
+
69
+ if settings:
70
+ for key, value in settings.items():
71
+ _apply_optional_setting(spec, key, value)
72
+
73
+ return spec
74
+
75
+
76
+ def calibrate_curve(
77
+ today,
78
+ ccy,
79
+ instruments,
80
+ knot_dates,
81
+ settings=None,
82
+ jacobian_mode=None,
83
+ base_curve=None,
84
+ ):
85
+ """High-level single-curve calibration with sensible defaults.
86
+
87
+ Only discount-curve calibration (calibrate_discount=True) is supported here;
88
+ forward-curve calibration needs a preloaded discount curve, so build a
89
+ CurveCalibrationSpecBuilder_ directly (set discountCurves_) and call
90
+ dal.CalibrateSingleCurve.
91
+
92
+ Args:
93
+ today: Date_ for the calibration date
94
+ ccy: Currency string (e.g. "USD")
95
+ instruments: List of YCInstrument_ handles
96
+ knot_dates: List of Date_ knot points
97
+ settings: Optional dict of override settings. Supported keys:
98
+ curve_name, target_collateral, target_tenor, calibrate_discount
99
+ (must be True), libor_basis, smoothing_weight, tolerance,
100
+ fit_tolerance, max_evaluations, max_restarts, initial_guess,
101
+ solve_mode, parameterization, log_df_scheme
102
+ jacobian_mode: CurveJacobianMode enum (None = default without Jacobian)
103
+ base_curve: Optional discount curve multiplied into the calibrated curve.
104
+
105
+ Returns:
106
+ CalibrationResult_ with curve_ and diagnostics_
107
+ """
108
+ if settings and settings.get("calibrate_discount") is False:
109
+ raise ValueError(
110
+ "calibrate_curve() only supports discount-curve calibration "
111
+ "(calibrate_discount=True). For forward-curve calibration, build a "
112
+ "CurveCalibrationSpecBuilder_ with discountCurves_ and call "
113
+ "dal.CalibrateSingleCurve directly."
114
+ )
115
+ spec = _build_calibration_spec(today, ccy, instruments, knot_dates, settings, base_curve)
116
+ if jacobian_mode is not None:
117
+ return _bindings.CalibrateSingleCurve(spec.Build(), jacobian_mode)
118
+ else:
119
+ return _bindings.CalibrateSingleCurve(spec.Build())
dal/dal.py ADDED
@@ -0,0 +1,2 @@
1
+ # Auto-generated pybind11 shim -- re-exports all symbols from _dal
2
+ from ._dal import *
@@ -0,0 +1,629 @@
1
+ Metadata-Version: 2.4
2
+ Name: dal-python
3
+ Version: 2026.8.11
4
+ Summary: Python bindings for the DAL quantitative finance library
5
+ Author-Email: The Derivatives Algorithms Group <wegamekinglc@hotmail.com>
6
+ License-Expression: MIT
7
+ Classifier: Development Status :: 5 - Production/Stable
8
+ Classifier: Intended Audience :: Science/Research
9
+ Classifier: Operating System :: Microsoft :: Windows
10
+ Classifier: Operating System :: POSIX :: Linux
11
+ Classifier: Programming Language :: C++
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Topic :: Scientific/Engineering
18
+ Project-URL: Documentation, https://github.com/wegamekinglc/Derivatives-Algorithms-Lib/tree/master/dal-python
19
+ Project-URL: Repository, https://github.com/wegamekinglc/Derivatives-Algorithms-Lib
20
+ Requires-Python: <3.14,>=3.10
21
+ Provides-Extra: test
22
+ Requires-Dist: pytest>=7.0; extra == "test"
23
+ Requires-Dist: numpy>=1.24; extra == "test"
24
+ Description-Content-Type: text/markdown
25
+
26
+ # dal-python
27
+
28
+ Python bindings for the Derivatives Algorithms Library (DAL) — a high-performance C++17 quantitative finance library with Automatic Adjoint Differentiation (AAD) support.
29
+
30
+ ## Features
31
+
32
+ - **Black-Scholes and Dupire models** for equity derivatives pricing
33
+ - **Monte Carlo simulation** with pseudo-random and Sobol sequence generators
34
+ - **AAD Greeks** — compute pathwise sensitivities (delta, vega, rho, etc.) in a single simulation
35
+ - **Script engine** — define exotic payoffs using a domain-specific language
36
+ - **Curve calibration** — single-curve, multi-curve, staged XCCY, and joint domestic/foreign/basis calibration with resettable and MTM instruments plus AAD analytic Jacobians
37
+ - **Type-safe wrappers** for `Date_`, `Matrix_`, `Cell_`, and vector types
38
+
39
+ ## Prerequisites
40
+
41
+ - **CPython 3.10-3.13** with development headers
42
+ - **uv** — fast Python package manager ([install guide](https://docs.astral.sh/uv/getting-started/installation/))
43
+ - **pybind11 2.11.1** — installed automatically for isolated package builds;
44
+ repository builds fall back to the pinned `dal-cpp/externals/pybind11`
45
+ submodule, so run `git submodule update --init --recursive` on fresh clones
46
+ - **CMake 3.21+** and a C++17 compiler (GCC 13+, Clang 18+, or MSVC 2022)
47
+ - **DAL C++ staged install** — build core/public first; the canonical workflow is
48
+ in the [installation guide](https://github.com/wegamekinglc/Derivatives-Algorithms-Lib/blob/master/docs/installation.md#python-bindings)
49
+
50
+ ### Building the C++ Library
51
+
52
+ The Python bindings depend on a compiled DAL C++ staging prefix. Build it first:
53
+
54
+ ```bash
55
+ cd /path/to/Derivatives-Algorithms-Lib
56
+ ./build_linux.sh
57
+ ```
58
+
59
+ This produces `build/stage/Release-linux/`, containing the installed core/public
60
+ libraries, headers, and CMake package metadata.
61
+
62
+ ## Installation
63
+
64
+ ### Development Install (Recommended)
65
+
66
+ Clone the repository and install in editable mode:
67
+
68
+ ```bash
69
+ cd Derivatives-Algorithms-Lib/dal-python
70
+
71
+ # Create a virtual environment with uv
72
+ uv venv --python ">=3.10,<3.14"
73
+ source .venv/bin/activate # On Windows: .venv\Scripts\activate
74
+
75
+ # Install dependencies and build the extension
76
+ uv pip install -e ".[test]" "--config-settings=cmake.define.DAL_INSTALL_PREFIX=/absolute/path/to/Derivatives-Algorithms-Lib/build/stage/<platform-preset>"
77
+ ```
78
+
79
+ Use an absolute staged-prefix path and replace `<platform-preset>` with the
80
+ preset that built DAL, such as `Release-linux` or `Release-windows`. Standalone
81
+ `dal-python` reads the installed CMake packages and automatically applies their
82
+ configuration-aware MSVC runtime contract to `_dal`.
83
+
84
+ ### Workspace Build and Test
85
+
86
+ To provision Python test dependencies and run the bindings through the workspace
87
+ CTest integration:
88
+
89
+ ```bash
90
+ bash ../build_linux.sh --full
91
+ ```
92
+
93
+ The workspace script creates or reuses `dal-python/.venv`, builds the extension,
94
+ and runs the configured C++/public/Python tests.
95
+
96
+ ## Building Distribution Packages
97
+
98
+ For production deployment, you can build pre-compiled binary wheels or source distributions.
99
+ Official PyPI releases contain precompiled wheels only.
100
+
101
+ ### Building a Binary Wheel
102
+
103
+ Binary wheels contain the compiled C++ extension and can be installed without requiring compilation:
104
+
105
+ ```bash
106
+ DAL_INSTALL_PREFIX=/absolute/path/to/build/stage/Release-linux ./build_wheel.sh
107
+ DAL_INSTALL_PREFIX=/absolute/path/to/build/stage/Release-linux ./build_wheel.sh --clean
108
+ ```
109
+
110
+ The platform- and interpreter-tagged wheel is created under `dist/`.
111
+
112
+ Install the wheel:
113
+ ```bash
114
+ uv pip install dist/dal_python-*.whl
115
+ ```
116
+
117
+ **Note:** Binary wheels are platform-specific. DAL keeps native-CPU tuning off by
118
+ default so distributable builds use the compiler's portable baseline. Do not set
119
+ `DAL_ENABLE_NATIVE_ARCH=ON` for a wheel that must run on unknown machines.
120
+
121
+ ### Building a Source Distribution
122
+
123
+ Source distributions allow users to build from source on any platform:
124
+
125
+ ```bash
126
+ ./build_sdist.sh # Build source distribution
127
+ ./build_sdist.sh --clean # Clean build artifacts before building
128
+ ```
129
+
130
+ The source archive is created under `dist/`.
131
+
132
+ Install from source (requires C++ build tools):
133
+ ```bash
134
+ pip install dist/dal_python-2026.8.11.tar.gz \
135
+ "--config-settings=cmake.define.DAL_INSTALL_PREFIX=/absolute/path/to/Derivatives-Algorithms-Lib/build/stage/<platform-preset>"
136
+ # or
137
+ uv pip install dist/dal_python-2026.8.11.tar.gz \
138
+ "--config-settings=cmake.define.DAL_INSTALL_PREFIX=/absolute/path/to/Derivatives-Algorithms-Lib/build/stage/<platform-preset>"
139
+ ```
140
+
141
+ **Requirements for building from source:**
142
+ - C++17 compiler (GCC 13+, Clang 18+, or MSVC 2022)
143
+ - CMake 3.21+
144
+ - pybind11 2.11.1 (declared as an isolated build requirement and installed
145
+ automatically; repository builds may use the pinned vendored submodule)
146
+ - CPython 3.10-3.13 development headers
147
+ - DAL staged install containing the `dal-public`/`dal-cpp` CMake packages and
148
+ platform libraries
149
+
150
+ ## PyPI Binary Release
151
+
152
+ The repository release workflow builds and tests this wheel matrix:
153
+
154
+ | Operating system | Architecture | Wheel platform tag | CPython versions |
155
+ |------------------|--------------|-----------------------------|------------------|
156
+ | Linux | x86-64 | `manylinux_2_28_x86_64` | 3.10-3.13 |
157
+ | Windows | x86-64 | `win_amd64` | 3.10-3.13 |
158
+
159
+ The Linux tag requires glibc 2.28 or newer. macOS, Linux ARM, musllinux, PyPy,
160
+ free-threaded CPython, source distributions, and CPython 3.14 are not part of the
161
+ current PyPI release contract.
162
+
163
+ ### One-time PyPI setup
164
+
165
+ Configure a Trusted Publisher on the existing `dal-python` PyPI project with:
166
+
167
+ | Field | Value |
168
+ |----------------------|------------------------------------|
169
+ | PyPI project | `dal-python` |
170
+ | GitHub owner | `wegamekinglc` |
171
+ | GitHub repository | `Derivatives-Algorithms-Lib` |
172
+ | Workflow filename | `dal-python-release.yml` |
173
+ | GitHub environment | `pypi` |
174
+
175
+ Create the matching `pypi` environment in the GitHub repository and require a
176
+ manual deployment approval if the repository plan supports it. The workflow uses
177
+ OIDC short-lived credentials; do not add a long-lived PyPI API token.
178
+
179
+ ### Release procedure
180
+
181
+ 1. Choose a new PEP 440 version that does not exist on PyPI. Update both
182
+ `pyproject.toml` and `src/dal/__init__.py`.
183
+ 2. Build and test the workspace with `bash ./build_linux.sh --full` from the
184
+ repository root. Review and merge the version and release-note changes to
185
+ `master` only after the exact PR head is green.
186
+ 3. Run the `dal-python wheels and PyPI release` workflow manually from `master`.
187
+ This is a build-only rehearsal. Confirm that eight wheels and the SHA-256
188
+ release manifest are present.
189
+ 4. Tag that reviewed `master` commit and push only the tag:
190
+
191
+ ```bash
192
+ git tag -a dal-python-v<version> -m "Release dal-python <version>"
193
+ git push origin dal-python-v<version>
194
+ ```
195
+
196
+ 5. The tag run rebuilds and tests every wheel, validates the combined manifest,
197
+ checks that the version is unused on PyPI, then publishes the exact artifacts
198
+ from the build jobs through the `pypi` environment.
199
+ 6. Verify the PyPI file list contains all eight wheels. In fresh Windows and Linux
200
+ environments, install `dal-python==<version>`, import `dal`, and confirm
201
+ `dal.__version__` equals `<version>`.
202
+
203
+ PyPI versions and files are immutable. Never use a skip-existing option to repair
204
+ an incomplete release; correct the issue, increment the version, and run the full
205
+ process again. Local `build_wheel.*` scripts are for diagnostics and private
206
+ deployment only; their output is not a PyPI release artifact.
207
+
208
+ ## Usage
209
+
210
+ ### Basic Pricing Example
211
+
212
+ ```python
213
+ import dal
214
+
215
+ # Set evaluation date
216
+ dal.EvaluationDate_Set(dal.Date_(2022, 9, 25))
217
+
218
+ # Define model parameters
219
+ spot, vol, rate, div = 100.0, 0.2, 0.05, 0.02
220
+ model = dal.BSModelData_New(spot=spot, vol=vol, rate=rate, div=div)
221
+
222
+ # Define a European call option
223
+ strike = 100.0
224
+ maturity = dal.Date_(2023, 9, 25)
225
+ product = dal.Product_New(
226
+ ["STRIKE", dal.Cell_(maturity)],
227
+ [str(strike), "call pays MAX(spot() - STRIKE, 0.0)"]
228
+ )
229
+
230
+ # Price using Monte Carlo (65,536 paths, Sobol sequences)
231
+ result = dal.MonteCarlo_Value(product, model, 2**16, "sobol")
232
+ print(f"Call PV: {result['PV']:.4f}")
233
+ # Output: Call PV: 9.2259
234
+ ```
235
+
236
+ ### Computing AAD Greeks
237
+
238
+ Enable AAD to compute pathwise sensitivities in a single simulation:
239
+
240
+ ```python
241
+ result = dal.MonteCarlo_Value(
242
+ product, model,
243
+ 2**14, # num_paths
244
+ "sobol", # method
245
+ False, # use_bb
246
+ True # enable_aad
247
+ )
248
+
249
+ print(f"PV: {result['PV']:.6f}")
250
+ for key in sorted(result.keys()):
251
+ if key.startswith('d_'):
252
+ print(f" {key}: {result[key]:.6f}")
253
+ ```
254
+
255
+ Output:
256
+ ```
257
+ PV: 9.223019
258
+ d_STRIKE: -0.494542
259
+ d_div: -58.677195
260
+ d_rate: 49.454176
261
+ d_spot: 0.586772
262
+ d_vol: 37.873346
263
+ ```
264
+
265
+ ### Working with Dates
266
+
267
+ ```python
268
+ import dal
269
+
270
+ # Create dates
271
+ d = dal.Date_(2022, 9, 25)
272
+ print(d) # 2022-09-25
273
+
274
+ # Date arithmetic
275
+ d2 = d.AddDays(30)
276
+ print(f"Year: {dal.Year(d)}, Month: {dal.Month(d)}, Day: {dal.Day(d)}")
277
+
278
+ # Date comparisons
279
+ d3 = dal.Date_(2022, 10, 25)
280
+ print(d < d3) # True
281
+ ```
282
+
283
+ ### Random Number Generation
284
+
285
+ ```python
286
+ # Pseudo-random generator (MRG32k32a algorithm)
287
+ pseudo = dal.PseudoRSG_New(42, 3) # seed=42, ndim=3
288
+ uniform_samples = dal.PseudoRSG_Get_Uniform(pseudo, 1000) # Returns DoubleMatrix_
289
+ normal_samples = dal.PseudoRSG_Get_Normal(pseudo, 1000)
290
+
291
+ # Sobol quasi-random sequences (better convergence for MC)
292
+ sobol = dal.SobolRSG_New(0, 3) # i_path=0, ndim=3
293
+ sobol_samples = dal.SobolRSG_Get_Uniform(sobol, 1000)
294
+ precise_sobol = dal.SobolRSG_New(
295
+ 0, 3, precise=True, polish=True
296
+ ) # opt in to the precise-CDF Newton correction
297
+ ```
298
+
299
+ ### Dupire Local Volatility Model
300
+
301
+ ```python
302
+ # Define a local volatility surface with flat 20% vol
303
+ spots = [80.0, 90.0, 100.0, 110.0, 120.0]
304
+ times = [0.5, 1.0, 2.0]
305
+ vols = dal.DoubleMatrix_(len(spots), len(times), 0.2) # Fill with 20% vol
306
+
307
+ dupire_model = dal.DupireModelData_New(
308
+ spot=100.0,
309
+ rate=0.05,
310
+ repo=0.01,
311
+ spots=spots,
312
+ times=times,
313
+ vols=vols
314
+ )
315
+ ```
316
+
317
+ `DoubleMatrix_` also accepts rectangular nested sequences and supports mutable
318
+ `matrix[i, j]` access, so non-flat surfaces can be populated directly.
319
+
320
+ ## API Reference
321
+
322
+ ### Core Types
323
+
324
+ - `dal.Date_(year, month, day)` — Date object with arithmetic operations
325
+ - `dal.String_(value)` — String wrapper
326
+ - `dal.Cell_(value)` — Polymorphic value container (bool, double, Date, String)
327
+ - `dal.DoubleVector()` — Vector of doubles
328
+ - `dal.DoubleMatrix_(rows, cols, fill=0.0)` or `dal.DoubleMatrix_(nested_rows)` — mutable 2D matrix of doubles
329
+
330
+ ### Models
331
+
332
+ - `dal.BSModelData_New(spot, vol, rate, div)` — Black-Scholes model
333
+ - `dal.DupireModelData_New(spot, rate, repo, spots, times, vols)` — Dupire local vol model
334
+
335
+ ### Products
336
+
337
+ - `dal.Product_New(dates, events)` — Create a script product from event dates and payoff definitions
338
+ - `dal.Product_Debug(product)` — Print human-readable product structure
339
+
340
+ ### Valuation
341
+
342
+ - `dal.MonteCarlo_Value(product, modelData, num_path, method="sobol", use_bb=False, enable_aad=False, smooth=0.01, compiled=None)` — Monte Carlo pricing with optional AAD Greeks
343
+
344
+ **Parameters:**
345
+ - `product` — Script product (from `Product_New`)
346
+ - `modelData` — Model data (from `BSModelData_New` or `DupireModelData_New`)
347
+ - `num_path` — Positive number of simulation paths (powers of 2 are customary for Sobol)
348
+ - `method` — Random generator: `"sobol"` (default) or `"mrg32"`
349
+ - `use_bb` — Use Brownian bridge construction (default `False`)
350
+ - `enable_aad` — Enable AAD for pathwise Greeks (default `False`)
351
+ - `smooth` — Fuzzy logic smoothing parameter for discontinuous payoffs (default `0.01`)
352
+ - `compiled` — `True` selects the compiled evaluator; `None`/`False` uses tree-walk
353
+
354
+ **Returns:** Dictionary with keys:
355
+ - `"PV"` — Present value
356
+ - `"d_spot"`, `"d_vol"`, `"d_rate"`, `"d_div"`, `"d_STRIKE"` — AAD Greeks (only if `enable_aad=True`)
357
+
358
+ ### Random Generators
359
+
360
+ - `dal.PseudoRSG_New(seed, ndim=1)` — Pseudo-random generator (MRG32k32a)
361
+ - `dal.SobolRSG_New(i_path, ndim=1, precise=False, polish=False)` — Sobol
362
+ quasi-random generator; `polish` enables the Newton correction and `precise`
363
+ selects its CDF, so the precise-CDF correction requires both flags to be `True`
364
+ - `dal.PseudoRSG_Get_Uniform(rsg, num_paths)` — Uniform samples [0, 1]
365
+ - `dal.PseudoRSG_Get_Normal(rsg, num_paths)` — Standard normal samples
366
+ - `dal.SobolRSG_Get_Uniform(rsg, num_paths)` — Sobol uniform samples
367
+ - `dal.SobolRSG_Get_Normal(rsg, num_paths)` — Sobol normal samples
368
+
369
+ ### Global State
370
+
371
+ - `dal.EvaluationDate_Set(date)` — Set the process-wide evaluation date; waits
372
+ for an in-progress native valuation or scoped override
373
+ - `dal.EvaluationDate_Get()` — Read the stable process-wide evaluation date;
374
+ remains available while valuation runs
375
+
376
+ Both bindings release the GIL before entering native synchronization.
377
+
378
+ ## Testing
379
+
380
+ Build and run the full workspace suite:
381
+
382
+ ```bash
383
+ bash ../build_linux.sh --full
384
+ ```
385
+
386
+ After an editable install, run focused Python tests directly:
387
+
388
+ ```bash
389
+ python -m pytest tests -k "test_date" -v
390
+ ```
391
+
392
+ Tests are located in `tests/` and cover:
393
+
394
+ - Date arithmetic and comparisons
395
+ - Vector and matrix operations
396
+ - Model construction (BS, Dupire)
397
+ - Monte Carlo pricing accuracy vs Black-Scholes analytical formulas
398
+ - AAD Greek computation and validation
399
+ - Random number generator properties
400
+ - Curve construction plus single and staged multi-curve calibration
401
+ - Staged XCCY basis calibration, sensitivity matrices, axes, and availability metadata
402
+ - Resettable/MTM XCCY construction with immutable fixing snapshots
403
+ - Joint domestic/foreign/basis XCCY calibration, including matrix and named-range contracts
404
+
405
+ ## Project Structure
406
+
407
+ ```
408
+ dal-python/
409
+ ├── CMakeLists.txt # Build configuration
410
+ ├── pyproject.toml # Python package metadata (scikit-build-core)
411
+ ├── run_tests.sh # Standalone binding test helper
412
+ ├── src/
413
+ │ ├── bindings/
414
+ │ │ ├── module.cpp # pybind11 module definition
415
+ │ │ ├── bindings.h # shared binding helpers
416
+ │ │ ├── core.cpp # core types (Date_, String_, Cell_, vectors, DoubleMatrix_)
417
+ │ │ ├── global.cpp # Handle_<T> opaque types, EvaluationDate_Get/Set
418
+ │ │ ├── models.cpp # model types (BSModelData_, etc.)
419
+ │ │ ├── random.cpp # random number generators
420
+ │ │ ├── script.cpp # scripting engine bindings
421
+ │ │ ├── calendar.cpp # holiday calendars and business-day conventions
422
+ │ │ ├── curve.cpp # curve calibration, instruments, and interpolation
423
+ │ │ └── value.cpp # Monte Carlo valuation (MonteCarlo_Value)
424
+ │ └── dal/
425
+ │ ├── __init__.py # Package initialization
426
+ │ └── api.py # High-level Python API wrappers
427
+ ├── tests/
428
+ │ ├── conftest.py # Pytest fixtures
429
+ │ └── test_*.py # Test modules
430
+ ```
431
+
432
+ ## Architecture
433
+
434
+ The Python bindings are generated by pybind11 from domain-organized binding files. The build process:
435
+
436
+ 1. **CMake** configures the build and locates the DAL C++ libraries plus either
437
+ the isolated pybind11 build requirement or the pinned repository fallback
438
+ 2. **C++ compiler** builds `_dal.cpython-*.so` extension module from the domain-organized `src/bindings/*.cpp` files
439
+ 3. **scikit-build-core** packages everything into an installable wheel
440
+
441
+ When consuming an installed DAL package under MSVC, CMake applies the package's
442
+ `DAL_CPP_MSVC_RUNTIME_LIBRARY` value to `_dal` through
443
+ `dal_cpp_apply_msvc_runtime`. The helper is a no-op on other toolchains.
444
+
445
+ The hand-written Python code in `src/dal/` provides:
446
+ - `__init__.py` — Re-exports all pybind11-generated symbols
447
+ - `api.py` — Convenience wrappers (e.g., `Product_New` with automatic type conversion, `calibrate_curve(...)` for curve calibration)
448
+
449
+ ## Curve Calibration
450
+
451
+ The `curve` bindings (`dal-python/src/bindings/curve.cpp`) expose the supported Python
452
+ curve-construction and calibration workflows:
453
+
454
+ - **Instrument builders** — `Deposit_New`, `FRA_New`, `Future_New`, `Swap_New`, `OISSwap_New`, `BasisSwap_New`, `CrossCurrencySwap_New`
455
+ - **Curve factories** — `DiscountPWLF_New`, `DiscountZeroRate_New`
456
+ - **Calibration entry points** — `CalibrateSingleCurve`, `CalibrateMultiCurveBundle`, `CalibrateXccyMarket`, `CalibrateJointXccyMarket`
457
+ - **Enums** — `CurveParameterization` (`PIECEWISE_LINEAR_FWD`, `PIECEWISE_CONSTANT_FWD`, `ZERO_RATE`, `LOG_DISCOUNT`), `CurveSolveMode` (`EXACT`, `APPROXIMATE`), `CurveJacobianMode` (`ANALYTIC`, `BUMPED`), `LogDfScheme` (`LOG_LINEAR`, `LOG_CUBIC_NATURAL`, `MIXED`), `XccyNotionalMode` (`FIXED`, `RESETTABLE`, `MARK_TO_MARKET`)
458
+ - **Spec builders** — `CurveCalibrationSpecBuilder_`, `CrossCurrencyCalibrationSpecBuilder_`, and `JointXccyCalibrationSpecBuilder_`
459
+
460
+ The `dal.calibrate_curve(...)` helper in `api.py` wraps the common single-curve path with Python-friendly defaults. The underlying C++ methodology is documented in the [yield-curve guide](https://github.com/wegamekinglc/Derivatives-Algorithms-Lib/blob/master/docs/methodology/yield_curve.md) and [Jacobian guide](https://github.com/wegamekinglc/Derivatives-Algorithms-Lib/blob/master/docs/methodology/yield_curve_jacobian.md).
461
+
462
+ ### Continuously Compounded Zero-Rate Curves
463
+
464
+ Build a persistent zero-rate curve directly with future-only nodes:
465
+
466
+ ```python
467
+ today = dal.Date_(2026, 1, 2)
468
+ node_dates = [dal.Date_(2027, 1, 2), dal.Date_(2028, 1, 2)]
469
+
470
+ curve = dal.DiscountZeroRate_New(
471
+ "usd_zero",
472
+ "USD",
473
+ today,
474
+ node_dates,
475
+ [0.02, 0.025],
476
+ day_count=dal.DayBasis_("ACT_365F"),
477
+ log_df_scheme=dal.LogDfScheme.LOG_LINEAR,
478
+ )
479
+ ```
480
+
481
+ Each continuously compounded decimal rate $z_i$ is mapped to
482
+ `logDF_i = -z_i * YearFrac(today, node_date_i)`. The anchor log DF is fixed at zero
483
+ and has no zero-rate parameter. `LOG_LINEAR`, `LOG_CUBIC_NATURAL`, and `MIXED` all
484
+ interpolate the mapped log DFs. Before the anchor, `LOG_LINEAR` and `MIXED` clamp the
485
+ log DF to zero, while `LOG_CUBIC_NATURAL` extends its first cubic segment. Beyond the
486
+ last node, every scheme uses the last two mapped log-DF nodes as a secant. The returned
487
+ `DiscountZeroRate_` exposes read-only `anchor_date`, `node_dates`, `zero_rates`,
488
+ `day_count`, and `log_df_scheme` properties.
489
+
490
+ For calibration, select `CurveParameterization.ZERO_RATE` and supply strictly-future
491
+ knots. `initialGuess_` is a decimal continuously compounded zero rate copied to every
492
+ node. Both low-level `CalibrateSingleCurve` and the convenience helper use the analytic
493
+ AAD Jacobian when the normal single-discount-curve eligibility gates are met:
494
+
495
+ ```python
496
+ result = dal.calibrate_curve(
497
+ today,
498
+ "USD",
499
+ instruments,
500
+ node_dates,
501
+ settings={
502
+ "parameterization": dal.CurveParameterization.ZERO_RATE,
503
+ "log_df_scheme": dal.LogDfScheme.LOG_CUBIC_NATURAL,
504
+ "initial_guess": 0.02,
505
+ },
506
+ jacobian_mode=dal.CurveJacobianMode.ANALYTIC,
507
+ base_curve=base_curve, # optional: zero rates are spread coordinates over this base
508
+ )
509
+ ```
510
+
511
+ Python exposes single, staged multi-curve, staged XCCY basis, and simultaneous
512
+ joint XCCY calibration. A base curve is multiplied into the calibrated component;
513
+ it is not a replacement for the pricing discount curve required by a forward-curve stage.
514
+ Staged XCCY supports both the backward-compatible
515
+ `CalibrateXccyMarket(spec)` call and `CalibrateXccyMarket(spec, options)`.
516
+ `CrossCurrencyCalibrationOptions_` defaults to `ANALYTIC` with
517
+ `compute_forward_jacobian = True` and
518
+ `compute_eff_jacobian_inverse = True`; trailing-underscore property names are
519
+ available alongside the snake-case names.
520
+
521
+ The matrices remain on `result.diagnostics`. `diagnostics.jacobian` has
522
+ instrument rows and basis-parameter columns;
523
+ `diagnostics.eff_jacobian_inverse` has the reversed axes.
524
+ `instrument_names` follows input order and may contain duplicate labels.
525
+ `parameter_knot_dates` follows the spec's knot order and labels the
526
+ piecewise-constant basis curve's right-forward parameters. The diagnostics also
527
+ publish `residual_tolerance`, `jacobian_scaling == "unscaled"`,
528
+ `eff_jacobian_inverse_scaling == "solver_scaled"`, and independent
529
+ `jacobian_availability` / `eff_jacobian_inverse_availability` values:
530
+ `available`, `not_requested`, or `not_available_for_mode`.
531
+
532
+ For a raw decimal quote-bump vector `dq`, the solver-scaled effective inverse
533
+ `E` maps parameters as `dx = E * dq / residual_tolerance`. An unavailable
534
+ matrix is empty; inspect its availability property to distinguish an explicit
535
+ opt-out from a mode limitation.
536
+
537
+ ### Resettable and Joint XCCY Calibration
538
+
539
+ Use `CrossCurrencySwapConfigBuilder_` to set the currency pair, notionals, leg
540
+ conventions, `notional_mode`, `fx_reset`, and explicit `domestic_rate_fixing` /
541
+ `foreign_rate_fixing` identities. `MarketFixingSnapshot_New` takes a nested
542
+ dictionary whose keys are index names and whose values map `DateTime_` objects to
543
+ observations. One immutable snapshot can hold domestic rate, foreign rate, and FX
544
+ fixings for an already-started swap:
545
+
546
+ ```python
547
+ snapshot = dal.MarketFixingSnapshot_New({
548
+ "USD-JOINT-3M": {historical_fixing: 0.040},
549
+ "EUR-JOINT-3M": {historical_fixing: 0.030},
550
+ "FX[EUR/USD]": {historical_fixing: 1.20},
551
+ })
552
+ ```
553
+
554
+ `JointCurrencyCurveSpec_` holds the ordered domestic or foreign
555
+ `JointCurveDeclaration_` objects. `XccyBasisCurveDeclaration_` holds configured
556
+ XCCY instruments and basis knots. Assemble those groups with
557
+ `JointXccyCalibrationSpecBuilder_`, then call
558
+ `CalibrateJointXccyMarket(builder.build())`. The result exposes the domestic and
559
+ foreign curve blocks, `fx_forward_curve`, basis curve, retained snapshot, group
560
+ diagnostics, full market/model/residual vectors, analytic Jacobian, effective
561
+ inverse, and named `parameter_ranges` / `residual_ranges`. Pass
562
+ `JointXccyCalibrationOptions_` to select `ANALYTIC` or `BUMPED` and to disable
563
+ either diagnostic matrix. The `eff_jacobian_inverse` matrix has shape
564
+ `totalParameters x totalResiduals` and is the weighted inverse of the solver's
565
+ tolerance-scaled Jacobian. Transforming a raw decimal quote bump therefore
566
+ requires division by the spec's `tolerance_`; see the
567
+ [Jacobian methodology](https://github.com/wegamekinglc/Derivatives-Algorithms-Lib/blob/master/docs/methodology/yield_curve_jacobian.md#joint-xccy-jacobian-layout).
568
+
569
+ The runnable [joint XCCY calibration example](https://github.com/wegamekinglc/Derivatives-Algorithms-Lib/blob/master/dal-python/examples/007.xccy_joint_calibration.py)
570
+ uses an explicit fixing snapshot for a started MTM trade. It prints convergence,
571
+ the maximum absolute residual, Jacobian dimensions, named parameter and residual
572
+ half-open ranges, and every FX-forward date and value. With the `dal` package
573
+ installed in the active environment, run it from the repository root:
574
+
575
+ ```bash
576
+ python dal-python/examples/007.xccy_joint_calibration.py
577
+ ```
578
+
579
+ ## Troubleshooting
580
+
581
+ ### "Cannot find DAL::public" during build
582
+
583
+ Ensure `DAL_INSTALL_PREFIX` points to the correct staged DAL installation:
584
+
585
+ ```text
586
+ <stage>/lib/cmake/dal-public/dal-publicConfig.cmake
587
+ <stage>/lib/cmake/dal-cpp/dal-cppConfig.cmake
588
+ <stage>/include/dal/
589
+ ```
590
+
591
+ The library files beside the package metadata use the platform's native suffix,
592
+ such as `.a` on Linux or `.lib` on Windows; do not diagnose the prefix by
593
+ assuming one suffix.
594
+
595
+ ### "ImportError: No module named _dal"
596
+
597
+ The extension module failed to build. Check the build logs:
598
+
599
+ ```bash
600
+ uv pip install --reinstall -e . -v "--config-settings=cmake.define.DAL_INSTALL_PREFIX=/absolute/path/to/build/stage/<platform-preset>"
601
+ ```
602
+
603
+ Replace `<platform-preset>` with the stage produced by the active compiler and
604
+ configuration.
605
+
606
+ ### Tests fail with "ModuleNotFoundError"
607
+
608
+ Ensure you're using the virtual environment:
609
+
610
+ ```bash
611
+ uv run --no-sync python -c "import dal; print(dal.__version__)"
612
+ ```
613
+
614
+ ## License
615
+
616
+ MIT License. See the repository [LICENSE](https://github.com/wegamekinglc/Derivatives-Algorithms-Lib/blob/master/LICENSE).
617
+
618
+ ## Contributing
619
+
620
+ Follow the repository [contributor guide](https://github.com/wegamekinglc/Derivatives-Algorithms-Lib/blob/master/CONTRIBUTING.md). Binding changes
621
+ should include Python tests and updates to the
622
+ [public API guide](https://github.com/wegamekinglc/Derivatives-Algorithms-Lib/blob/master/docs/public-api.md) when the supported surface changes.
623
+
624
+ ## See Also
625
+
626
+ - [DAL C++ Library](https://github.com/wegamekinglc/Derivatives-Algorithms-Lib) — Workspace overview
627
+ - [Installation guide](https://github.com/wegamekinglc/Derivatives-Algorithms-Lib/blob/master/docs/installation.md) — Canonical setup commands
628
+ - [Public API guide](https://github.com/wegamekinglc/Derivatives-Algorithms-Lib/blob/master/docs/public-api.md) — C++, Python, and Excel entry points
629
+ - [pybind11 Documentation](https://pybind11.readthedocs.io/) — pybind11 binding syntax
@@ -0,0 +1,7 @@
1
+ dal/__init__.py,sha256=WahSHmuo12V66E-zTiUia6uILSHa8JjE68IfON0Avwo,342
2
+ dal/_dal.cp312-win_amd64.pyd,sha256=9PZMXp-9TN4Hmzki_wOv1QCjUR1RwJ7UCuNPW7xsPBE,6304256
3
+ dal/api.py,sha256=Q6Sg8q5_7zar8q__d5wEB35bLj3wJO-tPQl3jem4j0U,4706
4
+ dal/dal.py,sha256=b0-41lZPe9kLS9tlVBJJkYQEzp83e5PXxnrrJ8_23hc,89
5
+ dal_python-2026.8.11.dist-info/METADATA,sha256=pbGEa7XO_SyMSexEaXUrfG6-ARSV8dr_2ShJZ0IURjI,26256
6
+ dal_python-2026.8.11.dist-info/WHEEL,sha256=8VvGD5u36-DU6RfS42J0lKkRVHfll--6oSkFzWCpFTs,105
7
+ dal_python-2026.8.11.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: scikit-build-core 1.0.3
3
+ Root-Is-Purelib: false
4
+ Tag: cp312-cp312-win_amd64
5
+