cfad 0.2.2__tar.gz
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.
- cfad-0.2.2/LICENSE +21 -0
- cfad-0.2.2/PKG-INFO +241 -0
- cfad-0.2.2/README.md +193 -0
- cfad-0.2.2/cfad/__init__.py +65 -0
- cfad-0.2.2/cfad/_ext/__init__.py +0 -0
- cfad-0.2.2/cfad/_ext/cusum.c +13915 -0
- cfad-0.2.2/cfad/_ext/rolling_ecf.c +14182 -0
- cfad-0.2.2/cfad/api.py +137 -0
- cfad-0.2.2/cfad/backtest.py +355 -0
- cfad-0.2.2/cfad/bootstrap.py +289 -0
- cfad-0.2.2/cfad/cli.py +191 -0
- cfad-0.2.2/cfad/contour.py +158 -0
- cfad-0.2.2/cfad/detection.py +417 -0
- cfad-0.2.2/cfad/empirical_cf.py +91 -0
- cfad-0.2.2/cfad/gof.py +323 -0
- cfad-0.2.2/cfad/market.py +403 -0
- cfad-0.2.2/cfad/models/__init__.py +7 -0
- cfad-0.2.2/cfad/models/base.py +72 -0
- cfad-0.2.2/cfad/models/cgmy.py +128 -0
- cfad-0.2.2/cfad/models/gaussian.py +48 -0
- cfad-0.2.2/cfad/models/levy_stable.py +100 -0
- cfad-0.2.2/cfad/models/nig.py +78 -0
- cfad-0.2.2/cfad/multivariate.py +281 -0
- cfad-0.2.2/cfad/py.typed +0 -0
- cfad-0.2.2/cfad/residue_score.py +150 -0
- cfad-0.2.2/cfad/sensitivity.py +278 -0
- cfad-0.2.2/cfad/utils.py +237 -0
- cfad-0.2.2/cfad/viz.py +459 -0
- cfad-0.2.2/cfad.egg-info/PKG-INFO +241 -0
- cfad-0.2.2/cfad.egg-info/SOURCES.txt +54 -0
- cfad-0.2.2/cfad.egg-info/dependency_links.txt +1 -0
- cfad-0.2.2/cfad.egg-info/entry_points.txt +2 -0
- cfad-0.2.2/cfad.egg-info/requires.txt +25 -0
- cfad-0.2.2/cfad.egg-info/top_level.txt +1 -0
- cfad-0.2.2/pyproject.toml +94 -0
- cfad-0.2.2/setup.cfg +4 -0
- cfad-0.2.2/setup.py +47 -0
- cfad-0.2.2/tests/test_api_utils.py +139 -0
- cfad-0.2.2/tests/test_backtest.py +128 -0
- cfad-0.2.2/tests/test_benchmarks.py +0 -0
- cfad-0.2.2/tests/test_bootstrap.py +95 -0
- cfad-0.2.2/tests/test_contour.py +67 -0
- cfad-0.2.2/tests/test_detection.py +212 -0
- cfad-0.2.2/tests/test_ecf.py +46 -0
- cfad-0.2.2/tests/test_empirical_ecf_v3.py +49 -0
- cfad-0.2.2/tests/test_gof.py +60 -0
- cfad-0.2.2/tests/test_market.py +83 -0
- cfad-0.2.2/tests/test_models.py +125 -0
- cfad-0.2.2/tests/test_multivariate.py +75 -0
- cfad-0.2.2/tests/test_properties.py +93 -0
- cfad-0.2.2/tests/test_regression.py +74 -0
- cfad-0.2.2/tests/test_sensitivity.py +72 -0
- cfad-0.2.2/tests/test_sequential_calibration_v2.py +49 -0
- cfad-0.2.2/tests/test_utils.py +46 -0
- cfad-0.2.2/tests/test_validation_study_benchmark.py +39 -0
- cfad-0.2.2/tests/test_viz.py +81 -0
cfad-0.2.2/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Diogo Ribeiro
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
cfad-0.2.2/PKG-INFO
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cfad
|
|
3
|
+
Version: 0.2.2
|
|
4
|
+
Summary: ECF-based distributional-shape anomaly detection for financial time series
|
|
5
|
+
Author: Diogo Ribeiro
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/DiogoRibeiro7/cfad
|
|
8
|
+
Project-URL: Repository, https://github.com/DiogoRibeiro7/cfad
|
|
9
|
+
Project-URL: Documentation, https://cfad.readthedocs.io
|
|
10
|
+
Project-URL: Changelog, https://github.com/DiogoRibeiro7/cfad/blob/main/CHANGELOG.md
|
|
11
|
+
Project-URL: Bug Tracker, https://github.com/DiogoRibeiro7/cfad/issues
|
|
12
|
+
Keywords: anomaly detection,empirical characteristic function,financial time series,change-point detection,CUSUM
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Cython
|
|
21
|
+
Classifier: Typing :: Typed
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
Requires-Dist: numpy>=1.24
|
|
26
|
+
Requires-Dist: scipy>=1.11
|
|
27
|
+
Requires-Dist: pandas>=2.0
|
|
28
|
+
Requires-Dist: matplotlib>=3.7
|
|
29
|
+
Requires-Dist: statsmodels>=0.14
|
|
30
|
+
Requires-Dist: joblib>=1.3
|
|
31
|
+
Provides-Extra: dev
|
|
32
|
+
Requires-Dist: pytest>=7.4; extra == "dev"
|
|
33
|
+
Requires-Dist: pytest-cov>=4.1; extra == "dev"
|
|
34
|
+
Requires-Dist: black>=24.0; extra == "dev"
|
|
35
|
+
Requires-Dist: ruff>=0.6; extra == "dev"
|
|
36
|
+
Requires-Dist: mypy>=1.10; extra == "dev"
|
|
37
|
+
Requires-Dist: Cython>=3.0; extra == "dev"
|
|
38
|
+
Requires-Dist: hypothesis>=6.100; extra == "dev"
|
|
39
|
+
Provides-Extra: notebooks
|
|
40
|
+
Requires-Dist: jupyter; extra == "notebooks"
|
|
41
|
+
Requires-Dist: seaborn; extra == "notebooks"
|
|
42
|
+
Requires-Dist: yfinance; extra == "notebooks"
|
|
43
|
+
Provides-Extra: docs
|
|
44
|
+
Requires-Dist: sphinx; extra == "docs"
|
|
45
|
+
Requires-Dist: sphinx-rtd-theme; extra == "docs"
|
|
46
|
+
Requires-Dist: numpydoc; extra == "docs"
|
|
47
|
+
Dynamic: license-file
|
|
48
|
+
|
|
49
|
+
# cfad — Characteristic Function Anomaly Detector
|
|
50
|
+
|
|
51
|
+
[](LICENSE)
|
|
52
|
+
[](https://github.com/DiogoRibeiro7/cfad/actions/workflows/ci.yml)
|
|
53
|
+
|
|
54
|
+
**cfad** is a research-oriented Python package for detecting changes in the
|
|
55
|
+
**distributional shape** of financial returns with empirical characteristic
|
|
56
|
+
functions (ECFs) and sequential CUSUM monitoring.
|
|
57
|
+
|
|
58
|
+
For each rolling window, CFAD compares the empirical characteristic function
|
|
59
|
+
with the Gaussian characteristic function fitted to that window's sample mean
|
|
60
|
+
and variance. The normalized real-frequency discrepancy is the anomaly score:
|
|
61
|
+
|
|
62
|
+
$$
|
|
63
|
+
D_t =
|
|
64
|
+
\left[
|
|
65
|
+
\frac{1}{\xi_{\max}-\xi_{\min}}
|
|
66
|
+
\int_{\xi_{\min}}^{\xi_{\max}}
|
|
67
|
+
\left|
|
|
68
|
+
\widehat\varphi_t(\xi)
|
|
69
|
+
-
|
|
70
|
+
\varphi_{\mathcal N(\widehat\mu_t,\widehat\sigma_t^2)}(\xi)
|
|
71
|
+
\right|^2
|
|
72
|
+
\,d\xi
|
|
73
|
+
\right]^{1/2}.
|
|
74
|
+
$$
|
|
75
|
+
|
|
76
|
+
Because location and scale are fitted within each window, the score is aimed at
|
|
77
|
+
higher-order shape changes such as tail and skewness changes. A two-sided
|
|
78
|
+
Page-CUSUM then converts the score sequence into sequential alarms.
|
|
79
|
+
|
|
80
|
+
> **Scientific scope.** The finite-sample ECF
|
|
81
|
+
> $\widehat\varphi_n(z)=n^{-1}\sum_j e^{izx_j}$ is a finite sum of entire
|
|
82
|
+
> functions and is itself entire. Therefore its exact closed-contour integral is
|
|
83
|
+
> zero. CFAD does **not** infer population-CF branch cuts or poles from an
|
|
84
|
+
> empirical contour residue. Complex contour integration remains available as a
|
|
85
|
+
> diagnostic helper for parametric characteristic functions evaluated at complex
|
|
86
|
+
> arguments, but it is not the empirical anomaly statistic.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## Status
|
|
91
|
+
|
|
92
|
+
The repository is currently a **research/development project**. The codebase
|
|
93
|
+
contains release scaffolding, documentation, benchmarks, notebooks, and a draft
|
|
94
|
+
software paper. GitHub releases are archival research-software snapshots; no
|
|
95
|
+
PyPI distribution is claimed unless a separate PyPI publication is explicitly
|
|
96
|
+
performed and verified.
|
|
97
|
+
|
|
98
|
+
### Current validation boundary
|
|
99
|
+
|
|
100
|
+
Two frozen validation programmes have now tested the corrected method rather
|
|
101
|
+
than the retired contour-residue interpretation.
|
|
102
|
+
|
|
103
|
+
The v2 sequential benchmark showed that Monte Carlo calibration can control the
|
|
104
|
+
Gaussian-null false-alarm rate, but the Gaussian-reference score was not robust
|
|
105
|
+
to a stable Student-t in-control law and had weak first-alarm power once false
|
|
106
|
+
alarms were controlled. The v3 score-level ablation then removed CUSUM and
|
|
107
|
+
separated frequency scaling from reference-law choice. Standardizing each window
|
|
108
|
+
corrected the legacy score's sensitivity to pure variance changes, and a frozen
|
|
109
|
+
empirical in-control ECF produced strong null-law stability. However, the
|
|
110
|
+
empirical-reference score achieved AUC 0.646 for a Gaussian-to-Student-t shape
|
|
111
|
+
change and 0.783 for a Gaussian-to-skew change, for an average of 0.715 versus
|
|
112
|
+
0.738 for a simple kurtosis-distance comparator.
|
|
113
|
+
|
|
114
|
+
Accordingly, the current evidence does **not** establish that CFAD is a validated
|
|
115
|
+
sequential detector or that its ECF score outperforms simpler moment summaries.
|
|
116
|
+
The negative v2 and v3 results are retained as reproducible evidence in
|
|
117
|
+
`benchmarks/` and are treated as design constraints rather than tuned away.
|
|
118
|
+
|
|
119
|
+
## Installation
|
|
120
|
+
|
|
121
|
+
From the default branch:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
git clone https://github.com/DiogoRibeiro7/cfad
|
|
125
|
+
cd cfad
|
|
126
|
+
git switch main
|
|
127
|
+
python -m pip install -e ".[dev]" --no-build-isolation
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
The package includes optional Cython acceleration for rolling ECF evaluation and
|
|
131
|
+
CUSUM updates. The statistical score itself is implemented once in NumPy, so
|
|
132
|
+
installing the extensions changes performance rather than the definition of the
|
|
133
|
+
statistic.
|
|
134
|
+
|
|
135
|
+
## Quick start
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
import numpy as np
|
|
139
|
+
from cfad import detect
|
|
140
|
+
|
|
141
|
+
rng = np.random.default_rng(42)
|
|
142
|
+
returns = np.concatenate(
|
|
143
|
+
[
|
|
144
|
+
rng.normal(0.0, 0.01, 300),
|
|
145
|
+
rng.standard_t(df=3.0, size=150) * 0.01 / np.sqrt(3.0),
|
|
146
|
+
]
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
report = detect(
|
|
150
|
+
returns,
|
|
151
|
+
window=60,
|
|
152
|
+
xi_range=(-10.0, 10.0),
|
|
153
|
+
step=1,
|
|
154
|
+
calibration_frac=0.4,
|
|
155
|
+
k=0.5,
|
|
156
|
+
h=5.0,
|
|
157
|
+
)
|
|
158
|
+
print(report.summary())
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
For market data, fetch the series explicitly with the provider of your choice,
|
|
162
|
+
then pass returns to `detect`. Keeping data acquisition outside the core example
|
|
163
|
+
makes the detector reproducible without relying on network access.
|
|
164
|
+
|
|
165
|
+
## Detection pipeline
|
|
166
|
+
|
|
167
|
+
```text
|
|
168
|
+
returns
|
|
169
|
+
│
|
|
170
|
+
├─ rolling ECF on a real-frequency grid
|
|
171
|
+
│
|
|
172
|
+
├─ fitted Gaussian CF in each window
|
|
173
|
+
│
|
|
174
|
+
├─ normalized ECF L2 shape distance D_t
|
|
175
|
+
│
|
|
176
|
+
├─ calibration of score mean/std on an in-control prefix
|
|
177
|
+
│
|
|
178
|
+
└─ two-sided Page-CUSUM → alarms
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## Parametric CF models
|
|
182
|
+
|
|
183
|
+
CFAD also implements characteristic-function models for descriptive model
|
|
184
|
+
comparison and goodness-of-fit work:
|
|
185
|
+
|
|
186
|
+
| Model | Main use |
|
|
187
|
+
|---|---|
|
|
188
|
+
| `GaussianCF` | location/scale baseline |
|
|
189
|
+
| `NIGCF` | semi-heavy tails and skewness |
|
|
190
|
+
| `CGMYCF` | jump/tail-shape modelling |
|
|
191
|
+
| `LevyStableCF` | power-law tail modelling |
|
|
192
|
+
|
|
193
|
+
`compare_models()` compares fitted models by real-frequency ECF discrepancy and
|
|
194
|
+
AIC. A better-fitting non-Gaussian model is evidence of distributional fit, not
|
|
195
|
+
a direct empirical test for complex singularities.
|
|
196
|
+
|
|
197
|
+
## Utilities
|
|
198
|
+
|
|
199
|
+
The repository includes:
|
|
200
|
+
|
|
201
|
+
- `rolling_gof`, `cf_distance`, and `epps_pulley_test` for ECF goodness of fit;
|
|
202
|
+
- `WalkForwardBacktest` for temporal evaluation;
|
|
203
|
+
- bootstrap and score-stability diagnostics;
|
|
204
|
+
- `window_sensitivity`, `frequency_sensitivity`, and threshold sensitivity;
|
|
205
|
+
- multivariate and market-oriented helpers;
|
|
206
|
+
- a Streamlit dashboard under `apps/`;
|
|
207
|
+
- reproducible notebooks and benchmark scripts.
|
|
208
|
+
|
|
209
|
+
## Reproducibility
|
|
210
|
+
|
|
211
|
+
The main scientific validation target is not "does an alarm fire on one famous
|
|
212
|
+
market event?" but how the detector behaves under controlled null and
|
|
213
|
+
alternative data-generating processes. The benchmark layer reports false-positive
|
|
214
|
+
behaviour, power/discrimination under prespecified shape changes, specificity to
|
|
215
|
+
location/scale changes, and comparison against simpler baselines.
|
|
216
|
+
|
|
217
|
+
Frozen failed experiments are part of the evidence record. In particular,
|
|
218
|
+
`benchmarks/v2_failed_calibration_record.json` records the failed sequential
|
|
219
|
+
screen and `benchmarks/v3_failed_score_validation_record.json` records the failed
|
|
220
|
+
score-level screen. Those failures are not retroactively reclassified after
|
|
221
|
+
parameter or method changes.
|
|
222
|
+
|
|
223
|
+
## Documentation and paper
|
|
224
|
+
|
|
225
|
+
- Sphinx sources: [`docs/source/`](docs/source/)
|
|
226
|
+
- Draft software paper: [`paper/paper.md`](paper/paper.md)
|
|
227
|
+
- Reproducible notebooks: [`notebooks/`](notebooks/)
|
|
228
|
+
- Benchmarks: [`benchmarks/`](benchmarks/)
|
|
229
|
+
|
|
230
|
+
The manuscript is a draft companion to the software. Citation metadata should
|
|
231
|
+
only advertise a journal DOI after an actual accepted/published record exists.
|
|
232
|
+
|
|
233
|
+
## Author
|
|
234
|
+
|
|
235
|
+
**Diogo Ribeiro**
|
|
236
|
+
Faculty of Media Arts and Design, Technical University of Porto
|
|
237
|
+
ORCID: [0009-0001-2022-7072](https://orcid.org/0009-0001-2022-7072)
|
|
238
|
+
|
|
239
|
+
## License
|
|
240
|
+
|
|
241
|
+
MIT
|
cfad-0.2.2/README.md
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# cfad — Characteristic Function Anomaly Detector
|
|
2
|
+
|
|
3
|
+
[](LICENSE)
|
|
4
|
+
[](https://github.com/DiogoRibeiro7/cfad/actions/workflows/ci.yml)
|
|
5
|
+
|
|
6
|
+
**cfad** is a research-oriented Python package for detecting changes in the
|
|
7
|
+
**distributional shape** of financial returns with empirical characteristic
|
|
8
|
+
functions (ECFs) and sequential CUSUM monitoring.
|
|
9
|
+
|
|
10
|
+
For each rolling window, CFAD compares the empirical characteristic function
|
|
11
|
+
with the Gaussian characteristic function fitted to that window's sample mean
|
|
12
|
+
and variance. The normalized real-frequency discrepancy is the anomaly score:
|
|
13
|
+
|
|
14
|
+
$$
|
|
15
|
+
D_t =
|
|
16
|
+
\left[
|
|
17
|
+
\frac{1}{\xi_{\max}-\xi_{\min}}
|
|
18
|
+
\int_{\xi_{\min}}^{\xi_{\max}}
|
|
19
|
+
\left|
|
|
20
|
+
\widehat\varphi_t(\xi)
|
|
21
|
+
-
|
|
22
|
+
\varphi_{\mathcal N(\widehat\mu_t,\widehat\sigma_t^2)}(\xi)
|
|
23
|
+
\right|^2
|
|
24
|
+
\,d\xi
|
|
25
|
+
\right]^{1/2}.
|
|
26
|
+
$$
|
|
27
|
+
|
|
28
|
+
Because location and scale are fitted within each window, the score is aimed at
|
|
29
|
+
higher-order shape changes such as tail and skewness changes. A two-sided
|
|
30
|
+
Page-CUSUM then converts the score sequence into sequential alarms.
|
|
31
|
+
|
|
32
|
+
> **Scientific scope.** The finite-sample ECF
|
|
33
|
+
> $\widehat\varphi_n(z)=n^{-1}\sum_j e^{izx_j}$ is a finite sum of entire
|
|
34
|
+
> functions and is itself entire. Therefore its exact closed-contour integral is
|
|
35
|
+
> zero. CFAD does **not** infer population-CF branch cuts or poles from an
|
|
36
|
+
> empirical contour residue. Complex contour integration remains available as a
|
|
37
|
+
> diagnostic helper for parametric characteristic functions evaluated at complex
|
|
38
|
+
> arguments, but it is not the empirical anomaly statistic.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Status
|
|
43
|
+
|
|
44
|
+
The repository is currently a **research/development project**. The codebase
|
|
45
|
+
contains release scaffolding, documentation, benchmarks, notebooks, and a draft
|
|
46
|
+
software paper. GitHub releases are archival research-software snapshots; no
|
|
47
|
+
PyPI distribution is claimed unless a separate PyPI publication is explicitly
|
|
48
|
+
performed and verified.
|
|
49
|
+
|
|
50
|
+
### Current validation boundary
|
|
51
|
+
|
|
52
|
+
Two frozen validation programmes have now tested the corrected method rather
|
|
53
|
+
than the retired contour-residue interpretation.
|
|
54
|
+
|
|
55
|
+
The v2 sequential benchmark showed that Monte Carlo calibration can control the
|
|
56
|
+
Gaussian-null false-alarm rate, but the Gaussian-reference score was not robust
|
|
57
|
+
to a stable Student-t in-control law and had weak first-alarm power once false
|
|
58
|
+
alarms were controlled. The v3 score-level ablation then removed CUSUM and
|
|
59
|
+
separated frequency scaling from reference-law choice. Standardizing each window
|
|
60
|
+
corrected the legacy score's sensitivity to pure variance changes, and a frozen
|
|
61
|
+
empirical in-control ECF produced strong null-law stability. However, the
|
|
62
|
+
empirical-reference score achieved AUC 0.646 for a Gaussian-to-Student-t shape
|
|
63
|
+
change and 0.783 for a Gaussian-to-skew change, for an average of 0.715 versus
|
|
64
|
+
0.738 for a simple kurtosis-distance comparator.
|
|
65
|
+
|
|
66
|
+
Accordingly, the current evidence does **not** establish that CFAD is a validated
|
|
67
|
+
sequential detector or that its ECF score outperforms simpler moment summaries.
|
|
68
|
+
The negative v2 and v3 results are retained as reproducible evidence in
|
|
69
|
+
`benchmarks/` and are treated as design constraints rather than tuned away.
|
|
70
|
+
|
|
71
|
+
## Installation
|
|
72
|
+
|
|
73
|
+
From the default branch:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
git clone https://github.com/DiogoRibeiro7/cfad
|
|
77
|
+
cd cfad
|
|
78
|
+
git switch main
|
|
79
|
+
python -m pip install -e ".[dev]" --no-build-isolation
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
The package includes optional Cython acceleration for rolling ECF evaluation and
|
|
83
|
+
CUSUM updates. The statistical score itself is implemented once in NumPy, so
|
|
84
|
+
installing the extensions changes performance rather than the definition of the
|
|
85
|
+
statistic.
|
|
86
|
+
|
|
87
|
+
## Quick start
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
import numpy as np
|
|
91
|
+
from cfad import detect
|
|
92
|
+
|
|
93
|
+
rng = np.random.default_rng(42)
|
|
94
|
+
returns = np.concatenate(
|
|
95
|
+
[
|
|
96
|
+
rng.normal(0.0, 0.01, 300),
|
|
97
|
+
rng.standard_t(df=3.0, size=150) * 0.01 / np.sqrt(3.0),
|
|
98
|
+
]
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
report = detect(
|
|
102
|
+
returns,
|
|
103
|
+
window=60,
|
|
104
|
+
xi_range=(-10.0, 10.0),
|
|
105
|
+
step=1,
|
|
106
|
+
calibration_frac=0.4,
|
|
107
|
+
k=0.5,
|
|
108
|
+
h=5.0,
|
|
109
|
+
)
|
|
110
|
+
print(report.summary())
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
For market data, fetch the series explicitly with the provider of your choice,
|
|
114
|
+
then pass returns to `detect`. Keeping data acquisition outside the core example
|
|
115
|
+
makes the detector reproducible without relying on network access.
|
|
116
|
+
|
|
117
|
+
## Detection pipeline
|
|
118
|
+
|
|
119
|
+
```text
|
|
120
|
+
returns
|
|
121
|
+
│
|
|
122
|
+
├─ rolling ECF on a real-frequency grid
|
|
123
|
+
│
|
|
124
|
+
├─ fitted Gaussian CF in each window
|
|
125
|
+
│
|
|
126
|
+
├─ normalized ECF L2 shape distance D_t
|
|
127
|
+
│
|
|
128
|
+
├─ calibration of score mean/std on an in-control prefix
|
|
129
|
+
│
|
|
130
|
+
└─ two-sided Page-CUSUM → alarms
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Parametric CF models
|
|
134
|
+
|
|
135
|
+
CFAD also implements characteristic-function models for descriptive model
|
|
136
|
+
comparison and goodness-of-fit work:
|
|
137
|
+
|
|
138
|
+
| Model | Main use |
|
|
139
|
+
|---|---|
|
|
140
|
+
| `GaussianCF` | location/scale baseline |
|
|
141
|
+
| `NIGCF` | semi-heavy tails and skewness |
|
|
142
|
+
| `CGMYCF` | jump/tail-shape modelling |
|
|
143
|
+
| `LevyStableCF` | power-law tail modelling |
|
|
144
|
+
|
|
145
|
+
`compare_models()` compares fitted models by real-frequency ECF discrepancy and
|
|
146
|
+
AIC. A better-fitting non-Gaussian model is evidence of distributional fit, not
|
|
147
|
+
a direct empirical test for complex singularities.
|
|
148
|
+
|
|
149
|
+
## Utilities
|
|
150
|
+
|
|
151
|
+
The repository includes:
|
|
152
|
+
|
|
153
|
+
- `rolling_gof`, `cf_distance`, and `epps_pulley_test` for ECF goodness of fit;
|
|
154
|
+
- `WalkForwardBacktest` for temporal evaluation;
|
|
155
|
+
- bootstrap and score-stability diagnostics;
|
|
156
|
+
- `window_sensitivity`, `frequency_sensitivity`, and threshold sensitivity;
|
|
157
|
+
- multivariate and market-oriented helpers;
|
|
158
|
+
- a Streamlit dashboard under `apps/`;
|
|
159
|
+
- reproducible notebooks and benchmark scripts.
|
|
160
|
+
|
|
161
|
+
## Reproducibility
|
|
162
|
+
|
|
163
|
+
The main scientific validation target is not "does an alarm fire on one famous
|
|
164
|
+
market event?" but how the detector behaves under controlled null and
|
|
165
|
+
alternative data-generating processes. The benchmark layer reports false-positive
|
|
166
|
+
behaviour, power/discrimination under prespecified shape changes, specificity to
|
|
167
|
+
location/scale changes, and comparison against simpler baselines.
|
|
168
|
+
|
|
169
|
+
Frozen failed experiments are part of the evidence record. In particular,
|
|
170
|
+
`benchmarks/v2_failed_calibration_record.json` records the failed sequential
|
|
171
|
+
screen and `benchmarks/v3_failed_score_validation_record.json` records the failed
|
|
172
|
+
score-level screen. Those failures are not retroactively reclassified after
|
|
173
|
+
parameter or method changes.
|
|
174
|
+
|
|
175
|
+
## Documentation and paper
|
|
176
|
+
|
|
177
|
+
- Sphinx sources: [`docs/source/`](docs/source/)
|
|
178
|
+
- Draft software paper: [`paper/paper.md`](paper/paper.md)
|
|
179
|
+
- Reproducible notebooks: [`notebooks/`](notebooks/)
|
|
180
|
+
- Benchmarks: [`benchmarks/`](benchmarks/)
|
|
181
|
+
|
|
182
|
+
The manuscript is a draft companion to the software. Citation metadata should
|
|
183
|
+
only advertise a journal DOI after an actual accepted/published record exists.
|
|
184
|
+
|
|
185
|
+
## Author
|
|
186
|
+
|
|
187
|
+
**Diogo Ribeiro**
|
|
188
|
+
Faculty of Media Arts and Design, Technical University of Porto
|
|
189
|
+
ORCID: [0009-0001-2022-7072](https://orcid.org/0009-0001-2022-7072)
|
|
190
|
+
|
|
191
|
+
## License
|
|
192
|
+
|
|
193
|
+
MIT
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""cfad — Characteristic Function Anomaly Detector.
|
|
2
|
+
|
|
3
|
+
CFAD detects changes in financial-return distributional shape using rolling
|
|
4
|
+
empirical characteristic functions (ECFs). Each window is compared with the
|
|
5
|
+
Gaussian characteristic function fitted to the same mean and variance; the
|
|
6
|
+
resulting real-frequency discrepancy score is monitored with Page-CUSUM.
|
|
7
|
+
|
|
8
|
+
The package also provides parametric characteristic-function models,
|
|
9
|
+
goodness-of-fit tools, backtesting, bootstrap diagnostics, and market utilities.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from cfad.api import compare_models, detect
|
|
13
|
+
from cfad.backtest import BacktestResult, WalkForwardBacktest
|
|
14
|
+
from cfad.bootstrap import bootstrap_scores, score_stability
|
|
15
|
+
from cfad.contour import contour_integral, gaussian_ecf_distance_scores
|
|
16
|
+
from cfad.detection import AnomalyReport, RollingDetector, StreamDetector
|
|
17
|
+
from cfad.empirical_cf import ecf_at, rolling_ecf
|
|
18
|
+
from cfad.gof import aic_table, cf_distance, epps_pulley_test, rolling_gof
|
|
19
|
+
from cfad.models.cgmy import CGMYCF
|
|
20
|
+
from cfad.models.gaussian import GaussianCF
|
|
21
|
+
from cfad.models.levy_stable import LevyStableCF
|
|
22
|
+
from cfad.models.nig import NIGCF
|
|
23
|
+
from cfad.residue_score import normalise_scores, rolling_pvalue, threshold_by_fpr
|
|
24
|
+
from cfad.sensitivity import (
|
|
25
|
+
frequency_sensitivity,
|
|
26
|
+
recommend_params,
|
|
27
|
+
window_sensitivity,
|
|
28
|
+
)
|
|
29
|
+
from cfad.utils import load_spy_sample, plot_scores, simulate_levy_returns
|
|
30
|
+
|
|
31
|
+
__version__ = "0.2.2"
|
|
32
|
+
__author__ = "Diogo Ribeiro"
|
|
33
|
+
|
|
34
|
+
__all__ = [
|
|
35
|
+
"detect",
|
|
36
|
+
"compare_models",
|
|
37
|
+
"WalkForwardBacktest",
|
|
38
|
+
"BacktestResult",
|
|
39
|
+
"bootstrap_scores",
|
|
40
|
+
"score_stability",
|
|
41
|
+
"RollingDetector",
|
|
42
|
+
"StreamDetector",
|
|
43
|
+
"AnomalyReport",
|
|
44
|
+
"ecf_at",
|
|
45
|
+
"rolling_ecf",
|
|
46
|
+
"gaussian_ecf_distance_scores",
|
|
47
|
+
"contour_integral",
|
|
48
|
+
"cf_distance",
|
|
49
|
+
"aic_table",
|
|
50
|
+
"rolling_gof",
|
|
51
|
+
"epps_pulley_test",
|
|
52
|
+
"GaussianCF",
|
|
53
|
+
"NIGCF",
|
|
54
|
+
"CGMYCF",
|
|
55
|
+
"LevyStableCF",
|
|
56
|
+
"normalise_scores",
|
|
57
|
+
"rolling_pvalue",
|
|
58
|
+
"threshold_by_fpr",
|
|
59
|
+
"window_sensitivity",
|
|
60
|
+
"frequency_sensitivity",
|
|
61
|
+
"recommend_params",
|
|
62
|
+
"load_spy_sample",
|
|
63
|
+
"plot_scores",
|
|
64
|
+
"simulate_levy_returns",
|
|
65
|
+
]
|
|
File without changes
|