darm-vol 0.1.0__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.
- darm_vol-0.1.0/.github/workflows/publish.yml +62 -0
- darm_vol-0.1.0/.github/workflows/tests.yml +32 -0
- darm_vol-0.1.0/CHANGELOG.md +10 -0
- darm_vol-0.1.0/CITATION.cff +8 -0
- darm_vol-0.1.0/CONTRIBUTING.md +19 -0
- darm_vol-0.1.0/LICENSE +21 -0
- darm_vol-0.1.0/MANIFEST.in +11 -0
- darm_vol-0.1.0/PKG-INFO +208 -0
- darm_vol-0.1.0/README.md +173 -0
- darm_vol-0.1.0/RELEASE_BLOCKERS.md +12 -0
- darm_vol-0.1.0/VALIDATION_REPORT.md +71 -0
- darm_vol-0.1.0/benchmarks/benchmark_filter.py +30 -0
- darm_vol-0.1.0/docs/api_reference.md +66 -0
- darm_vol-0.1.0/docs/examples.md +19 -0
- darm_vol-0.1.0/docs/index.md +8 -0
- darm_vol-0.1.0/docs/installation.md +15 -0
- darm_vol-0.1.0/docs/limitations.md +33 -0
- darm_vol-0.1.0/docs/mathematical_background.md +36 -0
- darm_vol-0.1.0/docs/publishing.md +37 -0
- darm_vol-0.1.0/examples/asymmetric_darm.py +8 -0
- darm_vol-0.1.0/examples/basic_usage.py +11 -0
- darm_vol-0.1.0/examples/compare_kernels.py +9 -0
- darm_vol-0.1.0/examples/fit_equity_returns.py +12 -0
- darm_vol-0.1.0/examples/volatility_forecasting.py +10 -0
- darm_vol-0.1.0/pyproject.toml +48 -0
- darm_vol-0.1.0/setup.cfg +4 -0
- darm_vol-0.1.0/src/darm_vol/__init__.py +9 -0
- darm_vol-0.1.0/src/darm_vol/diagnostics.py +31 -0
- darm_vol-0.1.0/src/darm_vol/distributions.py +32 -0
- darm_vol-0.1.0/src/darm_vol/estimation.py +128 -0
- darm_vol-0.1.0/src/darm_vol/forecasting.py +50 -0
- darm_vol-0.1.0/src/darm_vol/kernels.py +62 -0
- darm_vol-0.1.0/src/darm_vol/likelihood.py +45 -0
- darm_vol-0.1.0/src/darm_vol/model.py +108 -0
- darm_vol-0.1.0/src/darm_vol/plotting.py +58 -0
- darm_vol-0.1.0/src/darm_vol/recursion.py +49 -0
- darm_vol-0.1.0/src/darm_vol/results.py +91 -0
- darm_vol-0.1.0/src/darm_vol/simulation.py +59 -0
- darm_vol-0.1.0/src/darm_vol/validation.py +51 -0
- darm_vol-0.1.0/src/darm_vol.egg-info/PKG-INFO +208 -0
- darm_vol-0.1.0/src/darm_vol.egg-info/SOURCES.txt +53 -0
- darm_vol-0.1.0/src/darm_vol.egg-info/dependency_links.txt +1 -0
- darm_vol-0.1.0/src/darm_vol.egg-info/requires.txt +19 -0
- darm_vol-0.1.0/src/darm_vol.egg-info/top_level.txt +1 -0
- darm_vol-0.1.0/tests/test_diagnostics.py +11 -0
- darm_vol-0.1.0/tests/test_estimation.py +31 -0
- darm_vol-0.1.0/tests/test_forecast_monte_carlo.py +27 -0
- darm_vol-0.1.0/tests/test_forecasting.py +43 -0
- darm_vol-0.1.0/tests/test_kernels.py +40 -0
- darm_vol-0.1.0/tests/test_likelihood.py +32 -0
- darm_vol-0.1.0/tests/test_model.py +49 -0
- darm_vol-0.1.0/tests/test_optional.py +25 -0
- darm_vol-0.1.0/tests/test_regression.py +15 -0
- darm_vol-0.1.0/tests/test_simulation.py +24 -0
- darm_vol-0.1.0/tests/test_student_asymmetric_fit.py +21 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
name: Controlled PyPI publishing
|
|
2
|
+
on:
|
|
3
|
+
release:
|
|
4
|
+
types: [published]
|
|
5
|
+
workflow_dispatch:
|
|
6
|
+
inputs:
|
|
7
|
+
destination:
|
|
8
|
+
description: 'Manual preview channel only'
|
|
9
|
+
required: true
|
|
10
|
+
type: choice
|
|
11
|
+
options: [testpypi]
|
|
12
|
+
|
|
13
|
+
permissions:
|
|
14
|
+
contents: read
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
build:
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v4
|
|
21
|
+
- uses: actions/setup-python@v5
|
|
22
|
+
with:
|
|
23
|
+
python-version: '3.11'
|
|
24
|
+
- run: |
|
|
25
|
+
test -f LICENSE || (echo 'Licence required before public release' && exit 1)
|
|
26
|
+
python -m pip install -e '.[dev]'
|
|
27
|
+
python -m pytest -q
|
|
28
|
+
python -m ruff check src tests examples
|
|
29
|
+
python -m build
|
|
30
|
+
python -m twine check dist/*
|
|
31
|
+
- uses: actions/upload-artifact@v4
|
|
32
|
+
with:
|
|
33
|
+
name: python-packages
|
|
34
|
+
path: dist/
|
|
35
|
+
testpypi:
|
|
36
|
+
if: github.event_name == 'workflow_dispatch' && inputs.destination == 'testpypi'
|
|
37
|
+
needs: build
|
|
38
|
+
runs-on: ubuntu-latest
|
|
39
|
+
environment: testpypi
|
|
40
|
+
permissions:
|
|
41
|
+
id-token: write
|
|
42
|
+
steps:
|
|
43
|
+
- uses: actions/download-artifact@v4
|
|
44
|
+
with:
|
|
45
|
+
name: python-packages
|
|
46
|
+
path: dist/
|
|
47
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
48
|
+
with:
|
|
49
|
+
repository-url: https://test.pypi.org/legacy/
|
|
50
|
+
pypi:
|
|
51
|
+
if: github.event_name == 'release' && github.event.action == 'published' && startsWith(github.ref, 'refs/tags/v')
|
|
52
|
+
needs: build
|
|
53
|
+
runs-on: ubuntu-latest
|
|
54
|
+
environment: pypi
|
|
55
|
+
permissions:
|
|
56
|
+
id-token: write
|
|
57
|
+
steps:
|
|
58
|
+
- uses: actions/download-artifact@v4
|
|
59
|
+
with:
|
|
60
|
+
name: python-packages
|
|
61
|
+
path: dist/
|
|
62
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
name: Test and build
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
branches: [main]
|
|
5
|
+
pull_request:
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
tests:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
strategy:
|
|
15
|
+
fail-fast: false
|
|
16
|
+
matrix:
|
|
17
|
+
python-version: ['3.10', '3.11', '3.12', '3.13']
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v4
|
|
20
|
+
- uses: actions/setup-python@v5
|
|
21
|
+
with:
|
|
22
|
+
python-version: ${{ matrix.python-version }}
|
|
23
|
+
- run: python -m pip install -e '.[dev]'
|
|
24
|
+
- run: python -m pytest -q
|
|
25
|
+
- run: python -m ruff check src tests examples
|
|
26
|
+
- run: python -m build
|
|
27
|
+
- run: python -m twine check dist/*
|
|
28
|
+
- name: Wheel smoke test in clean environment
|
|
29
|
+
run: |
|
|
30
|
+
python -m venv /tmp/darm-wheel-smoke
|
|
31
|
+
/tmp/darm-wheel-smoke/bin/python -m pip install dist/*.whl
|
|
32
|
+
(cd /tmp && /tmp/darm-wheel-smoke/bin/python -c 'from darm_vol import DARM; print(DARM(lags=5))')
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0 — pre-release candidate
|
|
4
|
+
|
|
5
|
+
- Finite-K normalized exponential, power and hyperbolic ARCH kernels.
|
|
6
|
+
- Symmetric and asymmetric news impact, Gaussian and unit-variance Student-t.
|
|
7
|
+
- Bounded multi-start conditional MLE, exact finite-memory forecasts, simulation.
|
|
8
|
+
- Diagnostics and optional Matplotlib visualisation.
|
|
9
|
+
|
|
10
|
+
Publication date is not assigned: this version has **not** been released on PyPI.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
cff-version: 1.2.0
|
|
2
|
+
message: "Please cite the software and its underlying research preprint."
|
|
3
|
+
title: "darm-vol: Decaying AutoRegressive Memory for Conditional Volatility"
|
|
4
|
+
type: software
|
|
5
|
+
authors:
|
|
6
|
+
- family-names: Connell
|
|
7
|
+
given-names: Callum
|
|
8
|
+
version: 0.1.0
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Please open a GitHub issue to discuss substantial numerical or public API changes.
|
|
4
|
+
Submit tests verifying mathematical identities independently from the implementation.
|
|
5
|
+
Do not alter kernel definitions or sample conditioning silently. Document version,
|
|
6
|
+
seed, data construction and optimization tolerances of all numeric comparisons.
|
|
7
|
+
|
|
8
|
+
Locally:
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
python -m pip install -e '.[dev]'
|
|
12
|
+
python -m pytest -q
|
|
13
|
+
ruff check src tests examples
|
|
14
|
+
python -m build
|
|
15
|
+
python -m twine check dist/*
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
**Licence selection is pending.** Obtain author approval before accepting
|
|
19
|
+
third-party contributions or publicly redistributing the source.
|
darm_vol-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Callum Connell
|
|
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.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
include README.md
|
|
2
|
+
include CHANGELOG.md
|
|
3
|
+
include CONTRIBUTING.md
|
|
4
|
+
include CITATION.cff
|
|
5
|
+
include RELEASE_BLOCKERS.md
|
|
6
|
+
recursive-include docs *.md
|
|
7
|
+
recursive-include examples *.py
|
|
8
|
+
recursive-include tests *.py
|
|
9
|
+
recursive-include .github *.yml
|
|
10
|
+
include VALIDATION_REPORT.md
|
|
11
|
+
recursive-include benchmarks *.py
|
darm_vol-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: darm-vol
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Decaying AutoRegressive Memory models for finite-memory conditional volatility
|
|
5
|
+
Author: Callum Connell
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Keywords: volatility,ARCH,conditional variance,financial econometrics
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Intended Audience :: Science/Research
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
16
|
+
Requires-Python: >=3.10
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Requires-Dist: numpy>=1.24
|
|
20
|
+
Requires-Dist: scipy>=1.10
|
|
21
|
+
Provides-Extra: pandas
|
|
22
|
+
Requires-Dist: pandas>=1.5; extra == "pandas"
|
|
23
|
+
Provides-Extra: plot
|
|
24
|
+
Requires-Dist: matplotlib>=3.6; extra == "plot"
|
|
25
|
+
Provides-Extra: performance
|
|
26
|
+
Requires-Dist: numba>=0.58; extra == "performance"
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: pytest>=7; extra == "dev"
|
|
29
|
+
Requires-Dist: ruff>=0.6; extra == "dev"
|
|
30
|
+
Requires-Dist: build>=1; extra == "dev"
|
|
31
|
+
Requires-Dist: twine>=5; extra == "dev"
|
|
32
|
+
Requires-Dist: pandas>=1.5; extra == "dev"
|
|
33
|
+
Requires-Dist: matplotlib>=3.6; extra == "dev"
|
|
34
|
+
Dynamic: license-file
|
|
35
|
+
|
|
36
|
+
# darm-vol
|
|
37
|
+
|
|
38
|
+
**Decaying AutoRegressive Memory (DARM) conditional-volatility models**
|
|
39
|
+
Author: **Callum Connell** · Version: **0.1.0 pre-release candidate**
|
|
40
|
+
|
|
41
|
+
A Python library for the finite-lag, normalized-kernel ARCH specifications developed in
|
|
42
|
+
*Decaying Autoregressive Memory Models for Conditional Volatility: Mathematical Foundations,
|
|
43
|
+
Finite-Sample Identification and Out-of-Sample Evidence* (preprint v1.0, 19 October 2026).
|
|
44
|
+
|
|
45
|
+
> **Not published.** `pip install darm-vol` will only work after a PyPI release.
|
|
46
|
+
> Source installation works now. The package has no selected software licence;
|
|
47
|
+
> author approval is required **before** any public distribution.
|
|
48
|
+
|
|
49
|
+
## Model
|
|
50
|
+
|
|
51
|
+
For zero conditional-mean innovations `epsilon_t = sqrt(h_t) z_t`, where independent,
|
|
52
|
+
standardized, symmetric `z_t` have mean zero and variance one, symmetric DARM is
|
|
53
|
+
|
|
54
|
+
```text
|
|
55
|
+
h_t = omega + rho * sum_{k=1}^K w_k(theta) * epsilon_(t-k)^2
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
with `omega > 0`, `0 <= rho < 1`, `w_k >= 0`, `sum w_k = 1`.
|
|
59
|
+
For asymmetric DARM, replace each squared innovation with
|
|
60
|
+
|
|
61
|
+
```text
|
|
62
|
+
u_t = epsilon_t^2 * [1 - kappa + 2*kappa*1{epsilon_t<0}]
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
where `0 <= kappa < 1`. Here `u_t` denotes the transformed shock; **`nu` in
|
|
66
|
+
parameter dictionaries denotes Student-t degrees of freedom, not `u_t`**.
|
|
67
|
+
|
|
68
|
+
Finite-lag kernels use `k=1..K`:
|
|
69
|
+
|
|
70
|
+
| Kernel | Unnormalized weight | Parameters |
|
|
71
|
+
|---|---|---|
|
|
72
|
+
| Exponential | `beta^(k-1)` | `0 < beta < 1` |
|
|
73
|
+
| Power | `(k-1+delta)^(-alpha)` | `alpha, delta > 0` |
|
|
74
|
+
| Hyperbolic | `(1+s*k)^(-p)` | `s, p > 0` |
|
|
75
|
+
|
|
76
|
+
Every family is normalized over exactly **K** lags. Hyperbolic coincides with
|
|
77
|
+
a subset of shifted power weights (`alpha=p`, `delta=1+1/s`) rather than an
|
|
78
|
+
independent stochastic-process class. The infinite-lag counterpart is a
|
|
79
|
+
**different specification**; finite exponential DARM is not exactly GARCH(1,1).
|
|
80
|
+
The infinite power kernel has a finite normalizing constant only when `alpha>1`.
|
|
81
|
+
|
|
82
|
+
DARM is a low-dimensional parametrization of ARCH(K), not a new stochastic
|
|
83
|
+
process outside ARCH theory. A finite-K DARM can show persistent squared returns
|
|
84
|
+
but is **not** strictly long-memory in the covariance non-summability sense under
|
|
85
|
+
the manuscript's finite-moment conditions.
|
|
86
|
+
|
|
87
|
+
## Installation
|
|
88
|
+
|
|
89
|
+
In a checked-out source directory:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
python -m pip install .
|
|
93
|
+
python -m pip install -e '.[dev]'
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Python >=3.10; runtime: NumPy and SciPy. pandas Series are accepted as inputs
|
|
97
|
+
without requiring pandas at runtime. Plotting and optional acceleration:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
python -m pip install '.[plot]'
|
|
101
|
+
python -m pip install '.[performance]'
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Quick start — fit, forecast, simulate
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
import numpy as np
|
|
108
|
+
from darm_vol import DARM
|
|
109
|
+
|
|
110
|
+
model = DARM(kernel="power", distribution="student-t", lags=30,
|
|
111
|
+
asymmetric=True)
|
|
112
|
+
true = {"omega": 0.00002, "rho": 0.70, "alpha": 1.8,
|
|
113
|
+
"delta": 5.0, "kappa": 0.4, "nu": 8.0}
|
|
114
|
+
|
|
115
|
+
sample = model.simulate(true, nobs=1200, burn=1000, random_state=42)
|
|
116
|
+
fit = model.fit(sample.returns, n_starts=2, random_state=42)
|
|
117
|
+
print(fit.summary())
|
|
118
|
+
print(fit.params, fit.loglikelihood, fit.aic, fit.bic)
|
|
119
|
+
|
|
120
|
+
fc = fit.forecast(horizon=5)
|
|
121
|
+
print(fc.daily_variance, fc.daily_volatility)
|
|
122
|
+
print(fc.integrated_variance)
|
|
123
|
+
|
|
124
|
+
new_data = fit.simulate(nobs=1000, burn=1000, random_state=99)
|
|
125
|
+
print(new_data.returns.shape, new_data.conditional_volatility.shape)
|
|
126
|
+
|
|
127
|
+
# Optional: pip install '.[plot]'
|
|
128
|
+
# fig, ax = fit.plot_volatility()
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
For live equity data see [`examples/fit_equity_returns.py`](examples/fit_equity_returns.py).
|
|
132
|
+
**Inputs are always in their original units**: e.g., a 1% decimal log return
|
|
133
|
+
is `0.01`, not `1.0`. No automatic scaling or demeaning occurs.
|
|
134
|
+
|
|
135
|
+
The **first K observations initialize the observed lag state** and are excluded
|
|
136
|
+
from the fitted conditional likelihood. Conditional variance arrays have `NaN`
|
|
137
|
+
in the first K positions. Therefore AIC/BIC use `T-K` observations and the
|
|
138
|
+
estimated number of free parameters. `omega` is internally reparameterized
|
|
139
|
+
by a sample-variance scale during optimization, then returned in the original
|
|
140
|
+
input units.
|
|
141
|
+
|
|
142
|
+
## Forecasting
|
|
143
|
+
|
|
144
|
+
For future times, unknown transformed shocks are replaced by their conditional
|
|
145
|
+
expectations. Both Gaussian and standardized Student-t innovations are
|
|
146
|
+
symmetric, giving `E[A_t]=1` with the stated asymmetric transformation.
|
|
147
|
+
`forecast(horizon=H).daily_variance` computes `E_T[h_(T+j)]` for `j=1,...,H`.
|
|
148
|
+
`integrated_variance` sums them. `daily_volatility` is `sqrt(E_T[h])`, **not**
|
|
149
|
+
`E_T[sqrt(h)]`.
|
|
150
|
+
|
|
151
|
+
With zero-mean martingale-difference returns, the sum of future conditional
|
|
152
|
+
variances also equals the conditional variance of their cumulative sum;
|
|
153
|
+
that equivalence should not be applied if separate serially dependent mean
|
|
154
|
+
dynamics are introduced. No interval coverage claim is implied.
|
|
155
|
+
|
|
156
|
+
## Simulation and diagnostics
|
|
157
|
+
|
|
158
|
+
Simulation uses independent standardized Gaussian or Student-t shocks and
|
|
159
|
+
exact finite-K transformed-shock recursion with K presample transformed shocks
|
|
160
|
+
fixed at `omega/(1-rho)` (valid due to the symmetric innovation laws). A burn-in
|
|
161
|
+
period reduces the effect of initialization. For infinite fourth-moment
|
|
162
|
+
Student-t laws with `2<nu<=4`, squared-return variance diagnostics do not
|
|
163
|
+
have the usual finite-moment theoretical justification.
|
|
164
|
+
|
|
165
|
+
`fit.diagnostics(lags=10)` returns residual mean, variance, excess kurtosis,
|
|
166
|
+
and Ljung-Box tests on residuals and residual squares (raw asymptotic p-values;
|
|
167
|
+
no automatic degrees-of-freedom correction for fitted parameters).
|
|
168
|
+
`fit.plot_volatility()`, `fit.plot_residuals()`, `fit.plot_kernel()`,
|
|
169
|
+
`fit.plot_forecast(10)` return Matplotlib `(Figure, Axes)` objects.
|
|
170
|
+
|
|
171
|
+
## Scientific limitations
|
|
172
|
+
|
|
173
|
+
- **Identification:** high kernel-shape uncertainty, flat likelihood ridges,
|
|
174
|
+
and boundary estimates can arise even when point forecasts are accurate.
|
|
175
|
+
No default Wald standard errors are provided; use careful profile inference.
|
|
176
|
+
- **Conditional MLE:** the first K returns are treated as conditioning
|
|
177
|
+
observations, rather than integrating out a stationary presample.
|
|
178
|
+
- **Finite K:** neither finite geometric kernel GARCH equivalence nor genuine
|
|
179
|
+
covariance non-summable long memory is claimed.
|
|
180
|
+
- **Forecast calibration:** prediction intervals for future squared returns
|
|
181
|
+
are not interchangeable with intervals for latent integrated variance.
|
|
182
|
+
- **Performance:** source supplied for this build includes the papers, but
|
|
183
|
+
**not original research implementation or raw experiment inputs**. Our
|
|
184
|
+
independent numerical unit tests do *not* establish full paper replication.
|
|
185
|
+
- **Inference:** fitted Student-t nu can be weakly identified; numerical boxes
|
|
186
|
+
are implementation limits, not the unrestricted theoretical parameter domain.
|
|
187
|
+
- **Empirical superiority:** the paper's primary 10-day comparison against
|
|
188
|
+
GJR-GARCH is inconclusive (HAC p=0.146291); no universal model superiority
|
|
189
|
+
or economic trading benefit is claimed.
|
|
190
|
+
|
|
191
|
+
## Research, attribution and citation
|
|
192
|
+
|
|
193
|
+
Callum Connell (2026), *Decaying Autoregressive Memory Models for Conditional
|
|
194
|
+
Volatility: Mathematical Foundations, Finite-Sample Identification and
|
|
195
|
+
Out-of-Sample Evidence*, preprint v1.0, 19 October 2026. See
|
|
196
|
+
[`research/`](research/) for the documents used to implement this package,
|
|
197
|
+
including the technical supplement and mathematical correction report.
|
|
198
|
+
See [`CITATION.cff`](CITATION.cff) for software citation metadata.
|
|
199
|
+
No DOI or public external paper URL has been supplied, so none is fabricated.
|
|
200
|
+
|
|
201
|
+
## Development status
|
|
202
|
+
|
|
203
|
+
This repository is a **0.1.0 pre-release source candidate**, not a published
|
|
204
|
+
PyPI release or independent replication of Experiments 13–18. See
|
|
205
|
+
[`docs/limitations.md`](docs/limitations.md) and
|
|
206
|
+
[`docs/publishing.md`](docs/publishing.md) before releasing.
|
|
207
|
+
|
|
208
|
+
Contributions: see [`CONTRIBUTING.md`](CONTRIBUTING.md).
|
darm_vol-0.1.0/README.md
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# darm-vol
|
|
2
|
+
|
|
3
|
+
**Decaying AutoRegressive Memory (DARM) conditional-volatility models**
|
|
4
|
+
Author: **Callum Connell** · Version: **0.1.0 pre-release candidate**
|
|
5
|
+
|
|
6
|
+
A Python library for the finite-lag, normalized-kernel ARCH specifications developed in
|
|
7
|
+
*Decaying Autoregressive Memory Models for Conditional Volatility: Mathematical Foundations,
|
|
8
|
+
Finite-Sample Identification and Out-of-Sample Evidence* (preprint v1.0, 19 October 2026).
|
|
9
|
+
|
|
10
|
+
> **Not published.** `pip install darm-vol` will only work after a PyPI release.
|
|
11
|
+
> Source installation works now. The package has no selected software licence;
|
|
12
|
+
> author approval is required **before** any public distribution.
|
|
13
|
+
|
|
14
|
+
## Model
|
|
15
|
+
|
|
16
|
+
For zero conditional-mean innovations `epsilon_t = sqrt(h_t) z_t`, where independent,
|
|
17
|
+
standardized, symmetric `z_t` have mean zero and variance one, symmetric DARM is
|
|
18
|
+
|
|
19
|
+
```text
|
|
20
|
+
h_t = omega + rho * sum_{k=1}^K w_k(theta) * epsilon_(t-k)^2
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
with `omega > 0`, `0 <= rho < 1`, `w_k >= 0`, `sum w_k = 1`.
|
|
24
|
+
For asymmetric DARM, replace each squared innovation with
|
|
25
|
+
|
|
26
|
+
```text
|
|
27
|
+
u_t = epsilon_t^2 * [1 - kappa + 2*kappa*1{epsilon_t<0}]
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
where `0 <= kappa < 1`. Here `u_t` denotes the transformed shock; **`nu` in
|
|
31
|
+
parameter dictionaries denotes Student-t degrees of freedom, not `u_t`**.
|
|
32
|
+
|
|
33
|
+
Finite-lag kernels use `k=1..K`:
|
|
34
|
+
|
|
35
|
+
| Kernel | Unnormalized weight | Parameters |
|
|
36
|
+
|---|---|---|
|
|
37
|
+
| Exponential | `beta^(k-1)` | `0 < beta < 1` |
|
|
38
|
+
| Power | `(k-1+delta)^(-alpha)` | `alpha, delta > 0` |
|
|
39
|
+
| Hyperbolic | `(1+s*k)^(-p)` | `s, p > 0` |
|
|
40
|
+
|
|
41
|
+
Every family is normalized over exactly **K** lags. Hyperbolic coincides with
|
|
42
|
+
a subset of shifted power weights (`alpha=p`, `delta=1+1/s`) rather than an
|
|
43
|
+
independent stochastic-process class. The infinite-lag counterpart is a
|
|
44
|
+
**different specification**; finite exponential DARM is not exactly GARCH(1,1).
|
|
45
|
+
The infinite power kernel has a finite normalizing constant only when `alpha>1`.
|
|
46
|
+
|
|
47
|
+
DARM is a low-dimensional parametrization of ARCH(K), not a new stochastic
|
|
48
|
+
process outside ARCH theory. A finite-K DARM can show persistent squared returns
|
|
49
|
+
but is **not** strictly long-memory in the covariance non-summability sense under
|
|
50
|
+
the manuscript's finite-moment conditions.
|
|
51
|
+
|
|
52
|
+
## Installation
|
|
53
|
+
|
|
54
|
+
In a checked-out source directory:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
python -m pip install .
|
|
58
|
+
python -m pip install -e '.[dev]'
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Python >=3.10; runtime: NumPy and SciPy. pandas Series are accepted as inputs
|
|
62
|
+
without requiring pandas at runtime. Plotting and optional acceleration:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
python -m pip install '.[plot]'
|
|
66
|
+
python -m pip install '.[performance]'
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Quick start — fit, forecast, simulate
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
import numpy as np
|
|
73
|
+
from darm_vol import DARM
|
|
74
|
+
|
|
75
|
+
model = DARM(kernel="power", distribution="student-t", lags=30,
|
|
76
|
+
asymmetric=True)
|
|
77
|
+
true = {"omega": 0.00002, "rho": 0.70, "alpha": 1.8,
|
|
78
|
+
"delta": 5.0, "kappa": 0.4, "nu": 8.0}
|
|
79
|
+
|
|
80
|
+
sample = model.simulate(true, nobs=1200, burn=1000, random_state=42)
|
|
81
|
+
fit = model.fit(sample.returns, n_starts=2, random_state=42)
|
|
82
|
+
print(fit.summary())
|
|
83
|
+
print(fit.params, fit.loglikelihood, fit.aic, fit.bic)
|
|
84
|
+
|
|
85
|
+
fc = fit.forecast(horizon=5)
|
|
86
|
+
print(fc.daily_variance, fc.daily_volatility)
|
|
87
|
+
print(fc.integrated_variance)
|
|
88
|
+
|
|
89
|
+
new_data = fit.simulate(nobs=1000, burn=1000, random_state=99)
|
|
90
|
+
print(new_data.returns.shape, new_data.conditional_volatility.shape)
|
|
91
|
+
|
|
92
|
+
# Optional: pip install '.[plot]'
|
|
93
|
+
# fig, ax = fit.plot_volatility()
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
For live equity data see [`examples/fit_equity_returns.py`](examples/fit_equity_returns.py).
|
|
97
|
+
**Inputs are always in their original units**: e.g., a 1% decimal log return
|
|
98
|
+
is `0.01`, not `1.0`. No automatic scaling or demeaning occurs.
|
|
99
|
+
|
|
100
|
+
The **first K observations initialize the observed lag state** and are excluded
|
|
101
|
+
from the fitted conditional likelihood. Conditional variance arrays have `NaN`
|
|
102
|
+
in the first K positions. Therefore AIC/BIC use `T-K` observations and the
|
|
103
|
+
estimated number of free parameters. `omega` is internally reparameterized
|
|
104
|
+
by a sample-variance scale during optimization, then returned in the original
|
|
105
|
+
input units.
|
|
106
|
+
|
|
107
|
+
## Forecasting
|
|
108
|
+
|
|
109
|
+
For future times, unknown transformed shocks are replaced by their conditional
|
|
110
|
+
expectations. Both Gaussian and standardized Student-t innovations are
|
|
111
|
+
symmetric, giving `E[A_t]=1` with the stated asymmetric transformation.
|
|
112
|
+
`forecast(horizon=H).daily_variance` computes `E_T[h_(T+j)]` for `j=1,...,H`.
|
|
113
|
+
`integrated_variance` sums them. `daily_volatility` is `sqrt(E_T[h])`, **not**
|
|
114
|
+
`E_T[sqrt(h)]`.
|
|
115
|
+
|
|
116
|
+
With zero-mean martingale-difference returns, the sum of future conditional
|
|
117
|
+
variances also equals the conditional variance of their cumulative sum;
|
|
118
|
+
that equivalence should not be applied if separate serially dependent mean
|
|
119
|
+
dynamics are introduced. No interval coverage claim is implied.
|
|
120
|
+
|
|
121
|
+
## Simulation and diagnostics
|
|
122
|
+
|
|
123
|
+
Simulation uses independent standardized Gaussian or Student-t shocks and
|
|
124
|
+
exact finite-K transformed-shock recursion with K presample transformed shocks
|
|
125
|
+
fixed at `omega/(1-rho)` (valid due to the symmetric innovation laws). A burn-in
|
|
126
|
+
period reduces the effect of initialization. For infinite fourth-moment
|
|
127
|
+
Student-t laws with `2<nu<=4`, squared-return variance diagnostics do not
|
|
128
|
+
have the usual finite-moment theoretical justification.
|
|
129
|
+
|
|
130
|
+
`fit.diagnostics(lags=10)` returns residual mean, variance, excess kurtosis,
|
|
131
|
+
and Ljung-Box tests on residuals and residual squares (raw asymptotic p-values;
|
|
132
|
+
no automatic degrees-of-freedom correction for fitted parameters).
|
|
133
|
+
`fit.plot_volatility()`, `fit.plot_residuals()`, `fit.plot_kernel()`,
|
|
134
|
+
`fit.plot_forecast(10)` return Matplotlib `(Figure, Axes)` objects.
|
|
135
|
+
|
|
136
|
+
## Scientific limitations
|
|
137
|
+
|
|
138
|
+
- **Identification:** high kernel-shape uncertainty, flat likelihood ridges,
|
|
139
|
+
and boundary estimates can arise even when point forecasts are accurate.
|
|
140
|
+
No default Wald standard errors are provided; use careful profile inference.
|
|
141
|
+
- **Conditional MLE:** the first K returns are treated as conditioning
|
|
142
|
+
observations, rather than integrating out a stationary presample.
|
|
143
|
+
- **Finite K:** neither finite geometric kernel GARCH equivalence nor genuine
|
|
144
|
+
covariance non-summable long memory is claimed.
|
|
145
|
+
- **Forecast calibration:** prediction intervals for future squared returns
|
|
146
|
+
are not interchangeable with intervals for latent integrated variance.
|
|
147
|
+
- **Performance:** source supplied for this build includes the papers, but
|
|
148
|
+
**not original research implementation or raw experiment inputs**. Our
|
|
149
|
+
independent numerical unit tests do *not* establish full paper replication.
|
|
150
|
+
- **Inference:** fitted Student-t nu can be weakly identified; numerical boxes
|
|
151
|
+
are implementation limits, not the unrestricted theoretical parameter domain.
|
|
152
|
+
- **Empirical superiority:** the paper's primary 10-day comparison against
|
|
153
|
+
GJR-GARCH is inconclusive (HAC p=0.146291); no universal model superiority
|
|
154
|
+
or economic trading benefit is claimed.
|
|
155
|
+
|
|
156
|
+
## Research, attribution and citation
|
|
157
|
+
|
|
158
|
+
Callum Connell (2026), *Decaying Autoregressive Memory Models for Conditional
|
|
159
|
+
Volatility: Mathematical Foundations, Finite-Sample Identification and
|
|
160
|
+
Out-of-Sample Evidence*, preprint v1.0, 19 October 2026. See
|
|
161
|
+
[`research/`](research/) for the documents used to implement this package,
|
|
162
|
+
including the technical supplement and mathematical correction report.
|
|
163
|
+
See [`CITATION.cff`](CITATION.cff) for software citation metadata.
|
|
164
|
+
No DOI or public external paper URL has been supplied, so none is fabricated.
|
|
165
|
+
|
|
166
|
+
## Development status
|
|
167
|
+
|
|
168
|
+
This repository is a **0.1.0 pre-release source candidate**, not a published
|
|
169
|
+
PyPI release or independent replication of Experiments 13–18. See
|
|
170
|
+
[`docs/limitations.md`](docs/limitations.md) and
|
|
171
|
+
[`docs/publishing.md`](docs/publishing.md) before releasing.
|
|
172
|
+
|
|
173
|
+
Contributions: see [`CONTRIBUTING.md`](CONTRIBUTING.md).
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Software licence decision required before publication
|
|
2
|
+
|
|
3
|
+
The author has not yet selected a software licence. No licence grant is
|
|
4
|
+
implied. Do **not** publish or redistribute the package publicly on PyPI
|
|
5
|
+
until Callum Connell has approved the licence and provided its full text in
|
|
6
|
+
a `LICENSE` file.
|
|
7
|
+
|
|
8
|
+
The most common permissive options are MIT, BSD-3-Clause and Apache-2.0,
|
|
9
|
+
but no option has been selected automatically. Confirm rights in any
|
|
10
|
+
incorporated third-party materials before choosing.
|
|
11
|
+
|
|
12
|
+
The release workflow intentionally fails while `LICENSE` is absent.
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# DARM v0.1.0 candidate — build & validation report
|
|
2
|
+
|
|
3
|
+
Date: 28 October 2026. Package has **not** been uploaded to PyPI or
|
|
4
|
+
published on GitHub. Software licence remains undecided and is a **release
|
|
5
|
+
blocker** requiring explicit author approval.
|
|
6
|
+
|
|
7
|
+
## Source inspection and mathematical design
|
|
8
|
+
|
|
9
|
+
- Supplied: preprint v1.0, technical supplement v1.0, mathematical
|
|
10
|
+
correction report v1.0, package implementation requirements.
|
|
11
|
+
- Not supplied: original research Python sources, Monte Carlo notebook
|
|
12
|
+
implementation archive, numerical model-fit regression fixtures,
|
|
13
|
+
original equity market data, exact run environment.
|
|
14
|
+
- Implemented kernel equations 10–12 from preprint, finite K normalization.
|
|
15
|
+
- Implemented finite-K symmetric/asymmetric transformed-shock recursion
|
|
16
|
+
and Gaussian / unit-variance Student-t conditional log-likelihood.
|
|
17
|
+
- Adopted the supplement's conditional likelihood convention: first K
|
|
18
|
+
observed returns initialize the state; effective n=T-K.
|
|
19
|
+
- Multi-step forecasts use future conditional expected transformed shocks,
|
|
20
|
+
not unavailable realized future returns, under symmetric innovation laws.
|
|
21
|
+
- Source code is newly implemented from papers; no claim that it recreates
|
|
22
|
+
original research Python code line-for-line.
|
|
23
|
+
|
|
24
|
+
## Checks executed (current candidate)
|
|
25
|
+
|
|
26
|
+
| Check | Command | Result |
|
|
27
|
+
| --- | --- | --- |
|
|
28
|
+
| Unit and Monte Carlo tests | `PYTHONPATH=src python -m pytest -q` | 26 passed |
|
|
29
|
+
| Bytecode syntax check | `python -m compileall -q src tests examples` | Passed |
|
|
30
|
+
| Wheel build | `python -m pip wheel --no-build-isolation --no-deps . -w dist` | Passed |
|
|
31
|
+
| Source distribution | `python -c 'import setuptools; setuptools.setup()' sdist --dist-dir dist` | Passed |
|
|
32
|
+
| Built-wheel smoke test | Install wheel with `pip --no-index --no-deps` into new venv; inject existing environment's NumPy/SciPy paths; import + ML fit + 5-day forecast from `/tmp` | Passed (dependencies NOT isolated) |
|
|
33
|
+
| Time benchmark | `PYTHONPATH=src python benchmarks/benchmark_filter.py` | Success; approximately 0.014 seconds for T=10,000 K=252 (best of 3) |
|
|
34
|
+
| `python -m build` | Attempted tool availability | Not run: `build` not installed and network unavailable |
|
|
35
|
+
| `twine check dist/*` | Attempted tool availability | Not run: `twine` not installed and network unavailable |
|
|
36
|
+
| `ruff check` | Attempted tool availability | Not run: `ruff` not installed and network unavailable |
|
|
37
|
+
| Base `pip check` | `python -m pip check` | Failed due unrelated existing moviepy/pillow version mismatch |
|
|
38
|
+
| Research code numerical parity | Compare original likelihood/forecasts | Not possible: original implementation absent |
|
|
39
|
+
| Multi-Python matrix | GitHub Actions configured for Python 3.10–3.13 | Not executed locally; Python 3.13 tested |
|
|
40
|
+
| PyPI TestPyPI publishing | Manual upload | NOT attempted; approval and licence required |
|
|
41
|
+
|
|
42
|
+
### Representative filter benchmarks
|
|
43
|
+
|
|
44
|
+
Environment: current Python 3.13 session, NumPy/SciPy, single-threaded
|
|
45
|
+
Python loop around NumPy dot operations. The figures are local wall times;
|
|
46
|
+
not a comparison against original DARM research software or GARCH models.
|
|
47
|
+
|
|
48
|
+
| T | K | best of 3 |
|
|
49
|
+
| ---: | ---: | ---: |
|
|
50
|
+
| 1,000 | 30 | 0.0013 s |
|
|
51
|
+
| 5,000 | 30 | 0.0062 s |
|
|
52
|
+
| 10,000 | 30 | 0.0129 s |
|
|
53
|
+
| 1,000 | 252 | 0.0011 s |
|
|
54
|
+
| 5,000 | 252 | 0.0070 s |
|
|
55
|
+
| 10,000 | 252 | 0.0138 s |
|
|
56
|
+
| 5,000 | 1,000 | 0.0075 s |
|
|
57
|
+
| 10,000 | 1,000 | 0.0162 s |
|
|
58
|
+
|
|
59
|
+
## Release blockers
|
|
60
|
+
|
|
61
|
+
1. Ask Callum Connell to choose and approve a software licence.
|
|
62
|
+
2. Have the author review public project metadata / legal data rights.
|
|
63
|
+
3. Run Ruff, `python -m build`, `twine check`, clean dependency-isolated
|
|
64
|
+
wheel install, and CI matrix in an online development environment.
|
|
65
|
+
4. Acquire old research implementation and numeric fixtures for
|
|
66
|
+
independent regression comparisons and original research parity checks.
|
|
67
|
+
5. Register GitHub Trusted Publishers with matching protected environments.
|
|
68
|
+
6. Authorize a deliberate TestPyPI dispatch and subsequent GitHub Release.
|
|
69
|
+
7. Confirm at publication time that the `darm-vol` distribution is available.
|
|
70
|
+
|
|
71
|
+
See `docs/publishing.md` for step-by-step instructions.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""Repeatable wall-clock microbenchmark of the finite-K variance recursion.
|
|
2
|
+
|
|
3
|
+
No claims are made about the original research implementations.
|
|
4
|
+
Run: python benchmarks/benchmark_filter.py
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import time
|
|
9
|
+
import numpy as np
|
|
10
|
+
from darm_vol import DARM
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def benchmark(n: int, k: int, repeats: int = 3) -> float:
|
|
14
|
+
rng = np.random.default_rng(20261028)
|
|
15
|
+
returns = rng.normal(0, .01, n)
|
|
16
|
+
p = {"omega": .00001, "rho": .8, "alpha": 2., "delta": 5.}
|
|
17
|
+
model = DARM(kernel="power", distribution="normal", lags=k)
|
|
18
|
+
times = []
|
|
19
|
+
for _ in range(repeats):
|
|
20
|
+
start = time.perf_counter()
|
|
21
|
+
model.conditional_variance(returns, p)
|
|
22
|
+
times.append(time.perf_counter()-start)
|
|
23
|
+
return min(times)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
if __name__ == "__main__":
|
|
27
|
+
for k in (30, 252, 1000):
|
|
28
|
+
for n in (1000, 5000, 10000):
|
|
29
|
+
if n > k:
|
|
30
|
+
print(f"T={n:5d} K={k:4d}: {benchmark(n,k):.6f} sec (best of three)")
|