Stress-test mathematical and financial logic in Python code — find the exact line where models break.
Project description
blackswan
A stress-testing engine for financial and mathematical Python code.
BlackSwan finds the exact source line where your model breaks under extreme conditions — before production does. It is a debugger for numerical fragility, not a linter, not a simulator.
Installation
pip install blackswan
Requires Python 3.11+ and NumPy 1.26+.
Quickstart
# Stress-test a function with the liquidity crash scenario
python -m blackswan test models/risk.py --scenario liquidity_crash
{
"status": "failures_detected",
"runtime_ms": 2840,
"iterations_completed": 5000,
"summary": {
"total_failures": 847,
"failure_rate": 0.1694,
"unique_failure_types": 1
},
"shatter_points": [
{
"line": 36,
"severity": "critical",
"failure_type": "non_psd_matrix",
"message": "Covariance matrix loses positive semi-definiteness when pairwise correlation exceeds 0.91. Smallest eigenvalue: -0.0034.",
"frequency": "847 / 5000 iterations (16.9%)",
"causal_chain": [
{ "line": 8, "variable": "correlation", "role": "root_input" },
{ "line": 31, "variable": "corr_matrix", "role": "intermediate" },
{ "line": 36, "variable": "cov_matrix", "role": "failure_site" }
],
"fix_hint": "Apply nearest-PSD correction (Higham 2002) after correlation perturbation, or clamp eigenvalues to epsilon."
}
]
}
CLI Reference
python -m blackswan test <file> [options]
Options:
--scenario Preset scenario name (required)
--function Target function name (auto-detected if omitted)
--iterations Number of Monte Carlo iterations (default: 5000)
--seed Random seed for reproducibility (default: 42)
--adversarial Use genetic algorithm to search for worst-case inputs
--population GA population size (default: 100, requires --adversarial)
Exit codes: 0 = no failures, 1 = failures detected, 2 = engine error.
Available Scenarios
| Name | Description |
|---|---|
liquidity_crash |
Spread widening, vol expansion, correlation stress, turnover collapse |
vol_spike |
2–4× volatility multiplier, mild correlation increase |
correlation_breakdown |
Pairwise correlation shift +0.20–+0.50 |
rate_shock |
+100–+300 bps interest rate shock with spread widening |
missing_data |
Random NaN injection and partial time series truncation |
Python API
from blackswan.engine.runner import StressRunner
from blackswan.scenarios.registry import load_scenario
scenario = load_scenario("liquidity_crash")
runner = StressRunner(scenario)
result = runner.run(
calculate_portfolio_var,
base_inputs={"weights": weights, "vol": vols, "correlation": 0.0},
)
print(f"Failures: {len(result.findings)}")
for finding in result.findings:
print(f" Line {finding.line} [{finding.severity}]: {finding.message}")
Adversarial API
from blackswan.engine.adversarial import EvolutionaryStressRunner
runner = EvolutionaryStressRunner(
scenario,
n_generations=20,
population_size=100,
elite_fraction=0.2,
)
result = runner.run(fn, base_inputs)
Custom Detectors
from blackswan.detectors.numerical import LogicalInvariantDetector
# Weights must always sum to 1 (+/-1e-6)
invariant = LogicalInvariantDetector(
assertion=lambda inputs, output: abs(output.sum() - 1.0) < 1e-6,
name="weights_sum_to_one",
)
runner = StressRunner(scenario, extra_detectors=[invariant])
Failure Detectors
| Detector | Catches | Always active? |
|---|---|---|
NaNInfDetector |
NaN or Inf in any output | Yes |
DivisionStabilityDetector |
Denominator approaching zero | Yes |
MatrixPSDDetector |
Covariance/correlation matrix non-PSD | Auto (on matrix code) |
ConditionNumberDetector |
Ill-conditioned matrices (cond > 1e12) | Auto (on linalg.inv) |
BoundsDetector |
Outputs outside configurable plausible ranges | Auto |
ExplodingGradientDetector |
Output growth > 100x input perturbation | Auto |
RegimeShiftDetector |
Structural breaks in output distribution | Auto |
LogicalInvariantDetector |
User-defined assertions | On demand |
Detectors are auto-tagged to relevant source lines via AST analysis.
Custom Scenarios
Create a YAML file and pass its path as --scenario:
name: my_scenario
description: "Custom stress test for credit portfolio"
perturbations:
- target: spread
type: multiplicative
distribution: lognormal
mu: 2.0
sigma: 0.4
constraints:
min: 1.0
max: 5.0
- target: correlation
type: additive
distribution: uniform
low: 0.15
high: 0.40
global_constraints:
- target: correlation
min_value: -1.0
max_value: 0.95
defaults:
iterations: 5000
seed: 42
python -m blackswan test models/credit.py --scenario path/to/my_scenario.yaml
What BlackSwan Supports
Works well on:
- Pure functions with NumPy / Pandas inputs and outputs
- Explicit variable assignments
- NumPy array operations,
linalg, random - Pandas DataFrame column operations
- Single-file scripts and focused modules
Out of scope for V1:
- Deeply nested class hierarchies with side effects
- Config-driven logic loading parameters from databases
- Multi-threaded or async computation pipelines
- C extensions or Cython modules
Links
License
MIT
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file blackswan-0.1.2.tar.gz.
File metadata
- Download URL: blackswan-0.1.2.tar.gz
- Upload date:
- Size: 70.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bef2d52fcfabf12d8150c1f6556e24ea887833e071d2e7b10715a02f65c7aefa
|
|
| MD5 |
be3348bf6ccd9034cd855a2898545e87
|
|
| BLAKE2b-256 |
8b2613c55788c0e7654f2422cc110ed895bdad69fc005227de88812324aab862
|
Provenance
The following attestation bundles were made for blackswan-0.1.2.tar.gz:
Publisher:
workflow.yml on Lushenwar/BlackSwan
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
blackswan-0.1.2.tar.gz -
Subject digest:
bef2d52fcfabf12d8150c1f6556e24ea887833e071d2e7b10715a02f65c7aefa - Sigstore transparency entry: 1269617945
- Sigstore integration time:
-
Permalink:
Lushenwar/BlackSwan@a2bac6ac71f8de7c263b8680bc634744ec5fcc00 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/Lushenwar
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
workflow.yml@a2bac6ac71f8de7c263b8680bc634744ec5fcc00 -
Trigger Event:
push
-
Statement type:
File details
Details for the file blackswan-0.1.2-py3-none-any.whl.
File metadata
- Download URL: blackswan-0.1.2-py3-none-any.whl
- Upload date:
- Size: 84.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a7ec2a2485c6cab1dbb676f8b8c5e26623d561a98a0460c7c0fa4631987bfc88
|
|
| MD5 |
b85f559845d08ab0021de2e941255b8f
|
|
| BLAKE2b-256 |
47d77e54d9358977154d545ba882f99a8df4e2f880af23ef8ce4c10b3f5aaef1
|
Provenance
The following attestation bundles were made for blackswan-0.1.2-py3-none-any.whl:
Publisher:
workflow.yml on Lushenwar/BlackSwan
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
blackswan-0.1.2-py3-none-any.whl -
Subject digest:
a7ec2a2485c6cab1dbb676f8b8c5e26623d561a98a0460c7c0fa4631987bfc88 - Sigstore transparency entry: 1269617992
- Sigstore integration time:
-
Permalink:
Lushenwar/BlackSwan@a2bac6ac71f8de7c263b8680bc634744ec5fcc00 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/Lushenwar
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
workflow.yml@a2bac6ac71f8de7c263b8680bc634744ec5fcc00 -
Trigger Event:
push
-
Statement type: