dscc 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.
- dscc-0.1.0/.gitattributes +1 -0
- dscc-0.1.0/.github/workflows/tests.yml +23 -0
- dscc-0.1.0/.gitignore +15 -0
- dscc-0.1.0/CHANGELOG.md +11 -0
- dscc-0.1.0/CITATION.cff +34 -0
- dscc-0.1.0/LICENSE +21 -0
- dscc-0.1.0/PKG-INFO +448 -0
- dscc-0.1.0/README.md +416 -0
- dscc-0.1.0/docs/assets/style.css +101 -0
- dscc-0.1.0/docs/build_site.py +141 -0
- dscc-0.1.0/docs/index.template.html +256 -0
- dscc-0.1.0/docs/methods.md +66 -0
- dscc-0.1.0/examples/ff5_dscc_example.py +113 -0
- dscc-0.1.0/examples/quickstart.py +13 -0
- dscc-0.1.0/examples/quickstart_output.txt +36 -0
- dscc-0.1.0/pyproject.toml +40 -0
- dscc-0.1.0/src/dscc/__init__.py +45 -0
- dscc-0.1.0/src/dscc/_colors.py +95 -0
- dscc-0.1.0/src/dscc/_fast.py +152 -0
- dscc-0.1.0/src/dscc/_filters.py +221 -0
- dscc-0.1.0/src/dscc/_model.py +489 -0
- dscc-0.1.0/src/dscc/_plots.py +166 -0
- dscc-0.1.0/src/dscc/_representation.py +201 -0
- dscc-0.1.0/src/dscc/_tables.py +108 -0
- dscc-0.1.0/src/dscc/backtest.py +163 -0
- dscc-0.1.0/src/dscc/datasets.py +105 -0
- dscc-0.1.0/src/dscc/dcc.py +132 -0
- dscc-0.1.0/src/dscc/elliptope.py +245 -0
- dscc-0.1.0/src/dscc/portfolio.py +169 -0
- dscc-0.1.0/tests/test_core.py +195 -0
- dscc-0.1.0/tests/test_portfolio.py +105 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
* text=auto eol=lf
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
name: tests
|
|
2
|
+
on: [push, pull_request]
|
|
3
|
+
jobs:
|
|
4
|
+
test:
|
|
5
|
+
runs-on: ${{ matrix.os }}
|
|
6
|
+
strategy:
|
|
7
|
+
fail-fast: false
|
|
8
|
+
matrix:
|
|
9
|
+
os: [ubuntu-latest, windows-latest]
|
|
10
|
+
python-version: ["3.9", "3.11", "3.12"]
|
|
11
|
+
no_numba: ["0", "1"] # "1" = pure-NumPy code path
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
- uses: actions/setup-python@v5
|
|
15
|
+
with:
|
|
16
|
+
python-version: ${{ matrix.python-version }}
|
|
17
|
+
- run: python -m pip install --upgrade pip
|
|
18
|
+
- run: pip install -e ".[test]"
|
|
19
|
+
- if: matrix.no_numba == '0'
|
|
20
|
+
run: pip install numba
|
|
21
|
+
- run: pytest -q
|
|
22
|
+
env:
|
|
23
|
+
DSCC_NO_NUMBA: ${{ matrix.no_numba }}
|
dscc-0.1.0/.gitignore
ADDED
dscc-0.1.0/CHANGELOG.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0 — 2026-11-05
|
|
4
|
+
- Explicit minimal representation Σ = P^δ e^Υ Λ e^{−Υ} P^δ (Abadir 2023).
|
|
5
|
+
- DSCC with GARCH (2.5) and EGARCH (2.6)–(2.7) eigenvalue dynamics; ARMA(1,1) skew-symmetric dynamics (2.9).
|
|
6
|
+
- Maximum likelihood with greedy per-period δ_t, multi-start, best-point tracking and a Powell polish; profile-likelihood standard errors.
|
|
7
|
+
- Multistep forecasts (Section 4), in the paper's form and in the authors' replication form; the "-av" variant.
|
|
8
|
+
- Elliptope analytics: exact vol(E_k), slice coverage, exact slab-projection benchmark, DCC simulation (cDCC / Engle / iid).
|
|
9
|
+
- DCC(1,1) baseline; GMV portfolios; AV/SD/IR, TO/GL/PL, HAC pairwise tests, v/Δ/d/Δ′/κ; rolling backtest.
|
|
10
|
+
- Parula default colour scale; journal-style tables (text, LaTeX, Markdown) and figures.
|
|
11
|
+
- Optional numba kernels, with a pure-NumPy fallback that gives identical results.
|
dscc-0.1.0/CITATION.cff
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
cff-version: 1.2.0
|
|
2
|
+
message: "If you use this software, please cite both the package and the original articles."
|
|
3
|
+
title: "dscc: Dynamic Spectral Conditional Correlations in Python"
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
date-released: 2026-11-05
|
|
6
|
+
license: MIT
|
|
7
|
+
authors:
|
|
8
|
+
- family-names: Roudane
|
|
9
|
+
given-names: Merwan
|
|
10
|
+
email: merwanroudane920@gmail.com
|
|
11
|
+
repository-code: "https://github.com/merwanroudane/dscc"
|
|
12
|
+
keywords: [DSCC, DCC, multivariate volatility, correlation, elliptope, portfolio]
|
|
13
|
+
references:
|
|
14
|
+
- type: article
|
|
15
|
+
title: "Dynamic Spectral Conditional Correlations"
|
|
16
|
+
authors:
|
|
17
|
+
- family-names: Abadir
|
|
18
|
+
given-names: Karim M.
|
|
19
|
+
- family-names: Rockinger
|
|
20
|
+
given-names: Michael
|
|
21
|
+
journal: "Econometrics Journal"
|
|
22
|
+
year: 2026
|
|
23
|
+
doi: "10.1093/ectj/utag017"
|
|
24
|
+
- type: article
|
|
25
|
+
title: "Explicit minimal representation of variance matrices, and its implication for dynamic volatility models"
|
|
26
|
+
authors:
|
|
27
|
+
- family-names: Abadir
|
|
28
|
+
given-names: Karim M.
|
|
29
|
+
journal: "Econometrics Journal"
|
|
30
|
+
volume: 26
|
|
31
|
+
start: 88
|
|
32
|
+
end: 104
|
|
33
|
+
year: 2023
|
|
34
|
+
doi: "10.1093/ectj/utac023"
|
dscc-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Merwan Roudane
|
|
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.
|
dscc-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: dscc
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Dynamic Spectral Conditional Correlations (DSCC) and dynamic spectral heteroskedasticity (DSH) models: estimation, forecasting, elliptope coverage and minimum-variance portfolios
|
|
5
|
+
Project-URL: Homepage, https://merwanroudane.github.io/dscc/
|
|
6
|
+
Project-URL: Documentation, https://merwanroudane.github.io/dscc/
|
|
7
|
+
Project-URL: Repository, https://github.com/merwanroudane/dscc
|
|
8
|
+
Project-URL: Issues, https://github.com/merwanroudane/dscc/issues
|
|
9
|
+
Project-URL: Paper, https://doi.org/10.1093/ectj/utag017
|
|
10
|
+
Author-email: Dr Merwan Roudane <merwanroudane920@gmail.com>
|
|
11
|
+
License: MIT
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Keywords: DCC,DSCC,correlation,econometrics,elliptope,multivariate GARCH,portfolio,volatility
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Topic :: Office/Business :: Financial :: Investment
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
20
|
+
Requires-Python: >=3.9
|
|
21
|
+
Requires-Dist: matplotlib>=3.5
|
|
22
|
+
Requires-Dist: numpy>=1.22
|
|
23
|
+
Requires-Dist: pandas>=1.4
|
|
24
|
+
Requires-Dist: scipy>=1.9
|
|
25
|
+
Provides-Extra: fast
|
|
26
|
+
Requires-Dist: numba>=0.57; extra == 'fast'
|
|
27
|
+
Provides-Extra: interactive
|
|
28
|
+
Requires-Dist: plotly>=5; extra == 'interactive'
|
|
29
|
+
Provides-Extra: test
|
|
30
|
+
Requires-Dist: pytest>=7; extra == 'test'
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
|
|
33
|
+
# dscc
|
|
34
|
+
|
|
35
|
+
[](https://pypi.org/project/dscc/)
|
|
36
|
+
[](https://pypi.org/project/dscc/)
|
|
37
|
+
[](LICENSE)
|
|
38
|
+
[](https://merwanroudane.github.io/dscc/)
|
|
39
|
+
|
|
40
|
+
**dscc** implements the *Dynamic Spectral Conditional Correlation* (DSCC) model of
|
|
41
|
+
Abadir and Rockinger (2026, *Econometrics Journal*,
|
|
42
|
+
[doi:10.1093/ectj/utag017](https://doi.org/10.1093/ectj/utag017)), built on the explicit
|
|
43
|
+
minimal representation of variance matrices of Abadir (2023, *Econometrics Journal* 26,
|
|
44
|
+
88–104, [doi:10.1093/ectj/utac023](https://doi.org/10.1093/ectj/utac023)). You get
|
|
45
|
+
estimation, multistep forecasts, the elliptope-coverage benchmark, a DCC baseline and a
|
|
46
|
+
complete minimum-variance portfolio evaluation, with tables and figures ready for a
|
|
47
|
+
journal.
|
|
48
|
+
|
|
49
|
+
📖 **Documentation:** https://merwanroudane.github.io/dscc/
|
|
50
|
+
📦 **PyPI:** https://pypi.org/project/dscc/
|
|
51
|
+
🧑💻 **Author:** Dr Merwan Roudane · merwanroudane920@gmail.com · https://github.com/merwanroudane
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Table of contents
|
|
56
|
+
1. [What this package does](#1-what-this-package-does)
|
|
57
|
+
2. [Start from absolutely nothing](#2-start-from-absolutely-nothing)
|
|
58
|
+
3. [Installation](#3-installation)
|
|
59
|
+
4. [Your first analysis in 60 seconds](#4-your-first-analysis-in-60-seconds)
|
|
60
|
+
5. [Complete example with real data](#5-complete-example-with-real-data)
|
|
61
|
+
6. [Full syntax reference](#6-full-syntax-reference)
|
|
62
|
+
7. [Interpreting the output](#7-interpreting-the-output)
|
|
63
|
+
8. [Using your own Excel or CSV file](#8-using-your-own-excel-or-csv-file)
|
|
64
|
+
9. [Exporting tables and figures](#9-exporting-tables-and-figures)
|
|
65
|
+
10. [The method](#10-the-method)
|
|
66
|
+
11. [Fidelity to the paper and to the authors' code](#11-fidelity-to-the-paper-and-to-the-authors-code)
|
|
67
|
+
12. [Practical guidance and pitfalls](#12-practical-guidance-and-pitfalls)
|
|
68
|
+
13. [Limitations](#13-limitations)
|
|
69
|
+
14. [References](#14-references)
|
|
70
|
+
15. [Citation](#15-citation)
|
|
71
|
+
16. [Author and licence](#16-author-and-licence)
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## 1. What this package does
|
|
76
|
+
|
|
77
|
+
Correlation matrices must stay positive definite, and most multivariate volatility models
|
|
78
|
+
pay for that by restricting the matrices they can reach. DSCC writes every conditional
|
|
79
|
+
variance matrix as
|
|
80
|
+
|
|
81
|
+
$$\Sigma_t = P^{\delta_t} e^{\Upsilon_t}\,\Lambda_t\,e^{-\Upsilon_t} P^{\delta_t},$$
|
|
82
|
+
|
|
83
|
+
with an unrestricted skew-symmetric $\Upsilon_t$, positive eigenvalues $\Lambda_t$ and a
|
|
84
|
+
binary $\delta_t$. Every parameter is free (up to $k$ simple sign conditions), the
|
|
85
|
+
representation is minimal, and it spans the whole space of correlation matrices.
|
|
86
|
+
|
|
87
|
+
You give the package a $(T\times k)$ table of returns. It gives you back:
|
|
88
|
+
|
|
89
|
+
- **Estimation** of DSCC-λ<sub>garch</sub> (eq. 2.5) and DSCC-λ<sub>egarch</sub> (eqs 2.6–2.7) by maximum likelihood (2.13), with in-sample $\hat\delta_t$, multi-start optimisation and standard errors.
|
|
90
|
+
- **Conditional variances and correlations** $\Sigma_t$, $\Omega_t$ (2.14), standardised residuals, eigenvalue paths.
|
|
91
|
+
- **Multistep forecasts** (Section 4), including the "-av" variant, in the paper's form or exactly as in the authors' MATLAB code.
|
|
92
|
+
- **The span benchmark** of Section 3: exact volume of the elliptope $E_k$, slice-by-slice convex-hull coverage, DCC simulation, Table 1 and Figures 1–3.
|
|
93
|
+
- **A DCC(1,1) baseline** (Engle 2002).
|
|
94
|
+
- **Portfolio evaluation** of Section 5: global minimum-variance weights, AV/SD/IR, turnover and leverage, HAC pairwise tests, the v/Δ/d/Δ′/κ diagnostics, and a rolling monthly backtest (Tables 2–6, Figures 4–5).
|
|
95
|
+
- **Journal-quality output**: aligned text tables with stars, LaTeX (booktabs) and Markdown export, figures in vector PDF and 300-dpi PNG, MATLAB's Parula as the default colour scale.
|
|
96
|
+
|
|
97
|
+
## 2. Start from absolutely nothing
|
|
98
|
+
|
|
99
|
+
*If you have never used Python, do these four steps once. They take about fifteen minutes.*
|
|
100
|
+
|
|
101
|
+
### Step 1 — install Python
|
|
102
|
+
Download **Anaconda** from https://www.anaconda.com/download. It is the recommended option for researchers: one installer gives you Python, Jupyter and Spyder. Alternatively, install plain Python from https://www.python.org/downloads/; on the first screen of that installer, **tick "Add Python to PATH"**.
|
|
103
|
+
|
|
104
|
+
### Step 2 — open a terminal
|
|
105
|
+
- **Windows:** press `Win`, type `Anaconda Prompt` (or `cmd`), press Enter.
|
|
106
|
+
- **macOS:** press `Cmd+Space`, type `Terminal`, press Enter.
|
|
107
|
+
|
|
108
|
+
Check that Python works by typing this and pressing Enter:
|
|
109
|
+
```bash
|
|
110
|
+
python --version
|
|
111
|
+
```
|
|
112
|
+
You should see something like `Python 3.11.5`.
|
|
113
|
+
|
|
114
|
+
### Step 3 — install this package
|
|
115
|
+
```bash
|
|
116
|
+
pip install "dscc[fast]"
|
|
117
|
+
```
|
|
118
|
+
(`[fast]` also installs `numba`, which makes the estimation about four times faster. Plain `pip install dscc` works too.)
|
|
119
|
+
|
|
120
|
+
### Step 4 — run your first command
|
|
121
|
+
Type `python`, press Enter, then paste:
|
|
122
|
+
```python
|
|
123
|
+
import dscc
|
|
124
|
+
print(dscc.__version__)
|
|
125
|
+
```
|
|
126
|
+
Type `exit()` to leave.
|
|
127
|
+
|
|
128
|
+
### Where do I write my code?
|
|
129
|
+
| Tool | Best for | How to open |
|
|
130
|
+
|---|---|---|
|
|
131
|
+
| **Jupyter Notebook** | step-by-step work with the figures shown inline | type `jupyter notebook` in the terminal |
|
|
132
|
+
| **Spyder** | a window like MATLAB or RStudio | type `spyder`, or open it from Anaconda Navigator |
|
|
133
|
+
| **A `.py` file** | a reproducible script | save `analysis.py`, then run `python analysis.py` |
|
|
134
|
+
|
|
135
|
+
## 3. Installation
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
pip install dscc # core: numpy, scipy, pandas, matplotlib
|
|
139
|
+
pip install "dscc[fast]" # + numba (about 4x faster likelihood)
|
|
140
|
+
pip install "dscc[interactive]" # + plotly
|
|
141
|
+
```
|
|
142
|
+
Development version:
|
|
143
|
+
```bash
|
|
144
|
+
pip install git+https://github.com/merwanroudane/dscc.git
|
|
145
|
+
```
|
|
146
|
+
Requires Python ≥ 3.9.
|
|
147
|
+
|
|
148
|
+
## 4. Your first analysis in 60 seconds
|
|
149
|
+
|
|
150
|
+
Two Fama–French daily factors, the last 1,000 days of the paper's sample (the data are downloaded once from the Kenneth R. French Data Library, then cached):
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
import dscc
|
|
154
|
+
|
|
155
|
+
ff = dscc.datasets.load_ff5_daily(end="2022-03-31")
|
|
156
|
+
data = ff[["Mkt-RF", "HML"]].iloc[-1000:]
|
|
157
|
+
|
|
158
|
+
res = dscc.DSCC(data, dynamics="egarch").fit(n_starts=3, seed=1)
|
|
159
|
+
print(res.summary())
|
|
160
|
+
|
|
161
|
+
fc = res.forecast(H=21, init="average") # one trading month ahead
|
|
162
|
+
print(dscc.correlation_from_cov(fc.mean_sigma)[0, 1])
|
|
163
|
+
print(dscc.gmv_weights(fc.mean_sigma))
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Output:
|
|
167
|
+
|
|
168
|
+
```text
|
|
169
|
+
==============================================================================
|
|
170
|
+
Dynamic Spectral Conditional Correlation -- ML estimates
|
|
171
|
+
==============================================================================
|
|
172
|
+
Model: DSCC-egarch (2.6) Log-likelihood: -2508.149
|
|
173
|
+
Variables (k): 2 AIC: 5042.297
|
|
174
|
+
Observations (T): 1000 BIC: 5106.085
|
|
175
|
+
Q2t complement: householder Converged: yes
|
|
176
|
+
------------------------------------------------------------------------------
|
|
177
|
+
coef std err t P>|t| [0.025 0.975]
|
|
178
|
+
------------------------------------------------------------------------------
|
|
179
|
+
Eigenvalue dynamics, Lambda_t
|
|
180
|
+
D0[Mkt-RF] -0.2204 0.0562 -3.921 0.000 -0.331 -0.110 ***
|
|
181
|
+
D0[HML] -0.1563 0.0201 -7.793 0.000 -0.196 -0.117 ***
|
|
182
|
+
D11[Mkt-RF] 0.9609 0.0179 53.774 0.000 0.926 0.996 ***
|
|
183
|
+
D11[HML] 0.9786 0.0068 143.392 0.000 0.965 0.992 ***
|
|
184
|
+
D21[Mkt-RF] 0.2348 0.0512 4.586 0.000 0.134 0.335 ***
|
|
185
|
+
D21[HML] 0.1853 0.0237 7.813 0.000 0.139 0.232 ***
|
|
186
|
+
Skew-symmetric dynamics, Upsilon_t (2.9)
|
|
187
|
+
mu[Mkt-RF,HML] 1.6690 0.0134 124.989 0.000 1.643 1.695 ***
|
|
188
|
+
alpha[Mkt-RF,HML] 0.9293 0.0135 68.658 0.000 0.903 0.956 ***
|
|
189
|
+
beta[Mkt-RF,HML] -0.0201 0.0027 -7.441 0.000 -0.025 -0.015 ***
|
|
190
|
+
Asymmetry function g (2.7)
|
|
191
|
+
a[Mkt-RF] -0.2265 0.0959 -2.362 0.018 -0.414 -0.039 **
|
|
192
|
+
a[HML] 0.2479 0.1172 2.114 0.034 0.018 0.478 **
|
|
193
|
+
c[Mkt-RF] 0.0562 0.0321 1.751 0.080 -0.007 0.119 *
|
|
194
|
+
c[HML] 0.2140 0.1275 1.678 0.093 -0.036 0.464 *
|
|
195
|
+
------------------------------------------------------------------------------
|
|
196
|
+
Permutation component delta_t (Proposition 2.1, eq. 2.13)
|
|
197
|
+
share of t with delta_t = 1: 0.5095 psi = Pr(delta = 0): 0.4905
|
|
198
|
+
==============================================================================
|
|
199
|
+
Notes: *** p<0.01, ** p<0.05, * p<0.10. Standard errors from the numerical Hessian
|
|
200
|
+
of the profile log-likelihood with delta_t held at its MLE; '--' = not available
|
|
201
|
+
(parameter on a bound, or non-positive local curvature). Gaussian innovations.
|
|
202
|
+
|
|
203
|
+
Forecast correlation, average over the next 21 days: -0.471
|
|
204
|
+
Minimum-variance weights: [0.508 0.492]
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## 5. Complete example with real data
|
|
208
|
+
|
|
209
|
+
`examples/ff5_dscc_example.py` runs the whole paper on public data: the Fama–French five daily factors (Mkt-RF, SMB, HML, RMW, CMA), 2 January 1980 to 31 March 2022, in percent. These are exactly the five factors whose covariance matrix Abadir and Rockinger model with DSCC in Section 5.
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
python examples/ff5_dscc_example.py --months 60 --out results
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
The script follows the paper section by section.
|
|
216
|
+
|
|
217
|
+
**Step 1 — estimate on the first five-year window (1,260 days), as in the paper:**
|
|
218
|
+
```python
|
|
219
|
+
ff = dscc.datasets.load_ff5_daily(start="1980-01-02", end="2022-03-31")
|
|
220
|
+
factors = ff[["Mkt-RF", "SMB", "HML", "RMW", "CMA"]]
|
|
221
|
+
window = factors.iloc[:1260]
|
|
222
|
+
res = dscc.DSCC(window, dynamics="egarch").fit()
|
|
223
|
+
print(res.summary())
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
**Step 2 — correlations, eigenvalues and forecasts:**
|
|
227
|
+
```python
|
|
228
|
+
res.plot_correlations(); res.plot_eigenvalues()
|
|
229
|
+
fc = res.forecast(21, init="average") # DSCC-lambda_egarch-av
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
**Step 3 — the span benchmark (Section 3, Table 1):**
|
|
233
|
+
```python
|
|
234
|
+
from dscc import elliptope as el
|
|
235
|
+
el.elliptope_volume(3) # pi^2/2
|
|
236
|
+
tab = el.coverage_table(T=400_000, innovations="iid", slice_on=0)
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
**Step 4 — the monthly minimum-variance backtest (Section 5):**
|
|
240
|
+
```python
|
|
241
|
+
fcs = [dscc.EqualWeight(), dscc.SampleCovForecaster(), dscc.DCCForecaster(),
|
|
242
|
+
dscc.DSCCForecaster("garch", init="last"),
|
|
243
|
+
dscc.DSCCForecaster("egarch", init="average")]
|
|
244
|
+
bt = dscc.rolling_backtest(factors, fcs, n_months=60)
|
|
245
|
+
bt.performance_table() # Table 2
|
|
246
|
+
bt.pairwise() # Table 3
|
|
247
|
+
bt.turnover_table() # Table 4
|
|
248
|
+
bt.covariance_table() # Tables 5-6
|
|
249
|
+
dscc.plot_wealth(bt) # Figure 4
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
The script prints every table and writes them as `.tex`, `.md` and `.csv` files, together with
|
|
253
|
+
the figures (PDF and 300-dpi PNG), into the folder given by `--out`.
|
|
254
|
+
|
|
255
|
+
## 6. Full syntax reference
|
|
256
|
+
|
|
257
|
+
### `DSCC(data, dynamics="garch", demean=True, complement="householder", xi=None)`
|
|
258
|
+
The model.
|
|
259
|
+
|
|
260
|
+
| Argument | Type | Default | Meaning |
|
|
261
|
+
|---|---|---|---|
|
|
262
|
+
| `data` | DataFrame or (T, k) array | — | returns; column names are carried into every table |
|
|
263
|
+
| `dynamics` | `"garch"`, `"egarch"` | `"garch"` | eigenvalue dynamics: eq. (2.5) or eqs (2.6)–(2.7) |
|
|
264
|
+
| `demean` | bool | `True` | subtract the sample mean (Abadir 2023, §4.2) |
|
|
265
|
+
| `complement` | `"householder"`, `"eig"` | `"householder"` | how the orthogonal complement $Q_{2t}$ in (2.10) is built; irrelevant for k = 2 |
|
|
266
|
+
| `xi` | (T, l) array or None | `None` | precomputed $\xi_t$ of (2.11); pass the authors' MATLAB series to reproduce their likelihood exactly |
|
|
267
|
+
|
|
268
|
+
Methods: `.fit(...)` → `DSCCResults`; `.loglike(theta)` → float (eq. 2.13); `.filter(theta)` → paths.
|
|
269
|
+
|
|
270
|
+
### `DSCC.fit(start=None, n_starts=1, seed=0, method="SLSQP", maxiter=500, se=True, polish=True, polish_maxiter=20, verbose=False)`
|
|
271
|
+
|
|
272
|
+
| Argument | Default | Meaning |
|
|
273
|
+
|---|---|---|
|
|
274
|
+
| `start` | eq. (5.1) of Abadir (2023) | starting parameter vector |
|
|
275
|
+
| `n_starts` | `1` | number of starts; the extra ones are random draws in the admissible region (the paper's §5.5 uses 10) |
|
|
276
|
+
| `seed` | `0` | seed of the random starts |
|
|
277
|
+
| `method` | `"SLSQP"` | SciPy optimiser; SLSQP enforces $d_{1i}+d_{2i}<1$ |
|
|
278
|
+
| `se` | `True` | standard errors from the profile likelihood with $\delta_t$ fixed at its MLE |
|
|
279
|
+
| `polish` | `True` | derivative-free Powell polish of the best point found |
|
|
280
|
+
|
|
281
|
+
Parameter order (k variables, l = k(k−1)/2): `D0 (k), D11 (k), D21 (k), mu (l), alpha (l), beta (l)`, plus `a (k), c (k)` for EGARCH. This is the same order as the authors' code.
|
|
282
|
+
|
|
283
|
+
### `DSCCResults`
|
|
284
|
+
| Attribute / method | Returns | Meaning |
|
|
285
|
+
|---|---|---|
|
|
286
|
+
| `.params`, `.bse`, `.tvalues`, `.pvalues` | Series | estimates and Wald inference |
|
|
287
|
+
| `.loglik`, `.aic`, `.bic` | float | fit statistics |
|
|
288
|
+
| `.delta`, `.psi` | array, float | in-sample $\hat\delta_t$; $\hat\psi=\Pr(\delta=0)$ |
|
|
289
|
+
| `.lam`, `.ups` | arrays | $\Lambda_t$ and the free elements of $\Upsilon_t$ |
|
|
290
|
+
| `.sigma(t=None)`, `.correlation(t=None)` | (k,k) or (T,k,k) | $\Sigma_t$ (2.12), $\Omega_t$ (2.14) |
|
|
291
|
+
| `.std_resid()` | (T,k) | $\varepsilon_t=\Sigma_t^{-1/2}x_t$ |
|
|
292
|
+
| `.forecast(H=21, mode="paper", init="last")` | `DSCCForecast` | Section 4 forecasts; `.sigma`, `.correlation`, `.mean_sigma` |
|
|
293
|
+
| `.summary()`, `.to_frame()`, `.to_latex()`, `.to_markdown()` | str / DataFrame | tables |
|
|
294
|
+
| `.plot_correlations()`, `.plot_eigenvalues()` | Figure | figures |
|
|
295
|
+
|
|
296
|
+
`forecast` options: `mode="paper"` iterates $\Upsilon$ with (2.9) and weights the two δ-branches by $\hat\psi$ at every horizon. `mode="replication"` reproduces the authors' scripts. `init="average"` is the "-av" variant.
|
|
297
|
+
|
|
298
|
+
### `dscc.elliptope`
|
|
299
|
+
| Function | Meaning |
|
|
300
|
+
|---|---|
|
|
301
|
+
| `elliptope_volume(k)` | exact volume of $E_k$ ($\pi^2/2$ for k = 3, eq. 3.2) |
|
|
302
|
+
| `ellipse_slice_area(c)` | $\pi\sqrt{1-c^2}$, the section of $E_3$ at a fixed correlation |
|
|
303
|
+
| `slab_projection_area(lb, ub)` | exact maximal area of a slab (a sharper benchmark than the paper's) |
|
|
304
|
+
| `simulate_dcc(S, alpha, beta, T, innovations="cdcc")` | DCC paths; `"iid"` is the authors' simulator, `"engle"` Engle (2002) |
|
|
305
|
+
| `slice_coverage(points, n_slices=8, slice_on=2, benchmark="paper")` | Table 1 for one design |
|
|
306
|
+
| `coverage_table(...)` | the full Table 1 |
|
|
307
|
+
| `equicorrelation`, `toeplitz_corr`, `uniform_perturbation` | the designs of Table 1 |
|
|
308
|
+
|
|
309
|
+
### `dscc.portfolio`, `dscc.backtest`, `dscc.DCC`
|
|
310
|
+
| Function | Meaning |
|
|
311
|
+
|---|---|
|
|
312
|
+
| `gmv_weights(Sigma)` | $w=\Sigma^{-1}\iota/\iota'\Sigma^{-1}\iota$ |
|
|
313
|
+
| `performance(daily)` | AV, SD (annualised, percent) and IR |
|
|
314
|
+
| `turnover_leverage(W, R_list)` | TO, GL, PL |
|
|
315
|
+
| `hac_mean_test(d)`, `pairwise_tests(monthly)` | one-sided Newey–West tests (Table 3) |
|
|
316
|
+
| `covariance_summaries(Sigmas)` | $v_h$, $\Delta_h$ (5.2), $d_h$, $\Delta'_h$ (5.3), $\kappa_h$ |
|
|
317
|
+
| `rolling_backtest(returns, forecasters, window=1260, horizon=21, holding="buy_and_hold")` | monthly GMV backtest |
|
|
318
|
+
| `DSCCForecaster`, `DCCForecaster`, `SampleCovForecaster`, `EqualWeight` | the competing models |
|
|
319
|
+
| `DCC(data).fit()` | Engle (2002) DCC(1,1); `.forecast(H)` |
|
|
320
|
+
|
|
321
|
+
### Data and colours
|
|
322
|
+
`datasets.load_ff5_daily(path=None, start=None, end=None)` and `datasets.load_industry_daily(n=10)` download and cache the French library files (they are not redistributed with the package). `parula_colors(n)`, `matlab_jet_colors`, `turbo_colors`, `bluered_colors`, `sinha_colors`, `resolve_colorscale("Parula")` and `get_cmap("Parula")` handle colours.
|
|
323
|
+
|
|
324
|
+
## 7. Interpreting the output
|
|
325
|
+
|
|
326
|
+
**The summary table.**
|
|
327
|
+
- *D0, D11, D21* are the GARCH (or EGARCH) coefficients of each eigenvalue $\lambda_{it}$. *D11* is the persistence and *D21* the reaction to the newest rotated return $z_{t-1}$.
|
|
328
|
+
- *mu, alpha, beta* drive each free element $\upsilon_{ijt}$ of the rotation:
|
|
329
|
+
- *mu* is its long-run level, in radians.
|
|
330
|
+
- *alpha* is its persistence.
|
|
331
|
+
- *beta* is the loading on the data innovation $\xi_{ij,t-1}$.
|
|
332
|
+
|
|
333
|
+
A large |alpha| means the eigenvectors, and so the correlation structure, move slowly.
|
|
334
|
+
- *a, c* (EGARCH only) make the response asymmetric: $g_i(v)=|v_i-c_i|-a_i(v_i-c_i)$.
|
|
335
|
+
- The δ block reports the share of days on which the permuted branch $P\,e^{\Upsilon}$ fits better, and $\hat\psi=\Pr(\delta=0)$, which weights the two branches in forecasts.
|
|
336
|
+
- Stars use two-sided Wald p-values. "--" means the standard error is not available: the parameter sits on a bound, or the local Hessian is not positive there, which happens because $\delta_t$ makes the likelihood piecewise smooth.
|
|
337
|
+
|
|
338
|
+
**Table 1.** Each row is a slice of one correlation. "Max cover" is the largest area positive definiteness allows in that slice, and the other columns are the areas DCC actually reaches. DSCC reaches the maximum by construction.
|
|
339
|
+
|
|
340
|
+
**Tables 2–6.**
|
|
341
|
+
- AV, SD and IR are the annualised mean return, volatility and information ratio of daily out-of-sample returns.
|
|
342
|
+
- TO is turnover, GL gross leverage, and PL the share of short positions.
|
|
343
|
+
- In Table 3, a small p-value means Model 1 earns significantly more than Model 2.
|
|
344
|
+
- $\Delta'$ close to 0 means the forecast correlations are close to zero. A large κ means an ill-conditioned matrix.
|
|
345
|
+
|
|
346
|
+
## 8. Using your own Excel or CSV file
|
|
347
|
+
|
|
348
|
+
```python
|
|
349
|
+
import pandas as pd
|
|
350
|
+
import dscc
|
|
351
|
+
|
|
352
|
+
df = pd.read_excel("C:/Users/you/Documents/returns.xlsx", index_col=0, parse_dates=True)
|
|
353
|
+
# or: df = pd.read_csv("returns.csv", index_col=0, parse_dates=True)
|
|
354
|
+
df = df[["Asset1", "Asset2", "Asset3"]].dropna()
|
|
355
|
+
|
|
356
|
+
res = dscc.DSCC(df, dynamics="egarch").fit(n_starts=5)
|
|
357
|
+
print(res.summary())
|
|
358
|
+
```
|
|
359
|
+
On Windows, write paths with forward slashes (`C:/...`) or doubled backslashes (`C:\\...`). Use returns, not prices, and no missing values. Percent returns (as in the French library) keep the parameters on the scale used in the paper.
|
|
360
|
+
|
|
361
|
+
## 9. Exporting tables and figures
|
|
362
|
+
|
|
363
|
+
```python
|
|
364
|
+
print(res.to_latex()) # paste into the manuscript
|
|
365
|
+
res.to_frame().to_excel("dscc_estimates.xlsx")
|
|
366
|
+
fig = res.plot_correlations()
|
|
367
|
+
dscc.save_figure(fig, "figure_correlations") # writes .pdf (vector) and .png (300 dpi)
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
## 10. The method
|
|
371
|
+
|
|
372
|
+
For $x_t$ with conditional variance $\Sigma_t$:
|
|
373
|
+
|
|
374
|
+
1. **Representation** ([A23] Prop. 2.1; [AR26] 2.1): $\Sigma_t=P^{\delta_t}e^{\Upsilon_t}\Lambda_te^{-\Upsilon_t}P^{\delta_t}$, where $P$ swaps the first two variates.
|
|
375
|
+
2. **Eigenvalues** (2.4)–(2.5): with $z_t=e^{-\Upsilon_t}P^{\delta_t}x_t$, $\Lambda_t=D_0+D_{11}\Lambda_{t-1}+D_{21}\,\mathrm{dg}(z_{t-1}z_{t-1}')$; or the EGARCH (2.6) with $g$ of (2.7).
|
|
376
|
+
3. **Data innovations** (2.10)–(2.11): $x_tx_t'=Q_t\,\mathrm{diag}(x_t'x_t,0)Q_t'$, $|Q_t|=+1$, $\Xi_t=\log Q_t$ (principal value).
|
|
377
|
+
4. **Rotation dynamics** (2.9): $\upsilon_{ijt}=(1-\alpha_{ij})\mu_{ij}+\alpha_{ij}\upsilon_{ij,t-1}+\beta_{ij}\xi_{ij,t-1}$.
|
|
378
|
+
5. **Likelihood** (2.13): $\sum_t[\log f_\varepsilon(\Sigma_t^{-1/2}x_t)-\tfrac12\sum_i\log\lambda_{it}]$, with $\delta_t\in\{0,1\}$ chosen to maximise each summand.
|
|
379
|
+
6. **Forecasts** (Section 4, eq. 4.1): the two δ-branches are iterated forward and weighted by $\hat\psi$.
|
|
380
|
+
|
|
381
|
+
The full step → equation map is in [docs/methods.md](docs/methods.md).
|
|
382
|
+
|
|
383
|
+
## 11. Fidelity to the paper and to the authors' code
|
|
384
|
+
|
|
385
|
+
Every line was checked against the paper and against the authors' replication code (Zenodo, [doi:10.5281/zenodo.20817596](https://doi.org/10.5281/zenodo.20817596)). Where they disagree, the package follows the **paper** by default and offers an option that reproduces the **code**. The main points:
|
|
386
|
+
|
|
387
|
+
| Point | Paper | Authors' code | `dscc` |
|
|
388
|
+
|---|---|---|---|
|
|
389
|
+
| Complement $Q_{2t}$, k ≥ 3 | not specified | LAPACK's arbitrary null-space basis | deterministic Householder complement; `xi=` reproduces MATLAB |
|
|
390
|
+
| $\xi_t$, k = 2 | arcsin, eq. (4.5) of [A23] | principal log | principal log = atan2 (arcsin fails when $x_1<0$) |
|
|
391
|
+
| $\Upsilon$ over the forecast horizon | iterated with (2.9) | frozen at $\Upsilon_T$ | iterated; `mode="replication"` |
|
|
392
|
+
| DCC simulation, Table 1 | Aielli (2013) cDCC | iid innovations | cDCC; `innovations="iid"` reproduces Table 1 |
|
|
393
|
+
| Holding period | buy-and-hold | weights reset daily | buy-and-hold; `holding="constant"` |
|
|
394
|
+
|
|
395
|
+
Further checks:
|
|
396
|
+
- The general-k filter equals the bivariate closed form of [A23] to $10^{-13}$.
|
|
397
|
+
- $V_3=\pi^2/2$ is exact.
|
|
398
|
+
- The DCC column of Table 1 (ρ = 0.3) is reproduced to the second decimal.
|
|
399
|
+
|
|
400
|
+
The complete list, with measured effects, is in [docs/methods.md](docs/methods.md).
|
|
401
|
+
|
|
402
|
+
## 12. Practical guidance and pitfalls
|
|
403
|
+
|
|
404
|
+
- **Use several starts.** Because $\delta_t$ switches, the likelihood has many local optima. `n_starts=10` is the paper's choice. On the paper's first window, a single start stopped 10 points below the best value found.
|
|
405
|
+
- **Scale.** Use percent returns. The starting values (5.1) of [A23] are calibrated for them.
|
|
406
|
+
- **Dimension.** The number of parameters grows as $3k(k+1)/2$ (GARCH): k = 5 gives 45, k = 10 gives 165. A 1,260 × 5 fit takes one to three minutes with numba.
|
|
407
|
+
- **k ≥ 3.** Estimates depend on the convention for $Q_{2t}$. Keep the default for reproducible results across machines.
|
|
408
|
+
- **Standard errors** treat $\delta_t$ as known. Read them as approximate.
|
|
409
|
+
|
|
410
|
+
## 13. Limitations
|
|
411
|
+
|
|
412
|
+
- Only Gaussian innovations are implemented, as in the paper's applications.
|
|
413
|
+
- The NL-DCC (nonlinear-shrinkage DCC) block of De Nard, Ledoit and Wolf (2021), used for the 1,000-stock residuals, is not included. Neither is the CRSP stock data (restricted).
|
|
414
|
+
- The modified-likelihood estimation of the correlation-only parameters mentioned in Section 2.1 is left for future work, as in the paper.
|
|
415
|
+
|
|
416
|
+
## 14. References
|
|
417
|
+
|
|
418
|
+
- Abadir, K. M. and M. Rockinger (2026). Dynamic spectral conditional correlations. *Econometrics Journal*. https://doi.org/10.1093/ectj/utag017
|
|
419
|
+
- Abadir, K. M. (2023). Explicit minimal representation of variance matrices, and its implication for dynamic volatility models. *Econometrics Journal* 26, 88–104. https://doi.org/10.1093/ectj/utac023
|
|
420
|
+
- Abadir, K. M. and M. Rockinger (2026). Replication package. Zenodo. https://doi.org/10.5281/zenodo.20817596
|
|
421
|
+
- Aielli, G. P. (2013). Dynamic conditional correlation: on properties and estimation. *Journal of Business and Economic Statistics* 31, 282–299.
|
|
422
|
+
- De Nard, G., O. Ledoit and M. Wolf (2021). Factor models for portfolio selection in large dimensions: the good, the better and the ugly. *Journal of Financial Econometrics* 19, 236–257.
|
|
423
|
+
- Engle, R. F. (2002). Dynamic conditional correlation. *Journal of Business and Economic Statistics* 20, 339–350. https://doi.org/10.1198/073500102288618487
|
|
424
|
+
- Lewandowski, D., D. Kurowicka and H. Joe (2009). Generating random correlation matrices based on vines and extended onion method. *Journal of Multivariate Analysis* 100, 1989–2001. https://doi.org/10.1016/j.jmva.2009.04.008
|
|
425
|
+
- Newey, W. K. and K. D. West (1987). A simple, positive semi-definite, heteroskedasticity and autocorrelation consistent covariance matrix. *Econometrica* 55, 703–708. https://doi.org/10.2307/1913610
|
|
426
|
+
|
|
427
|
+
*(Every DOI above was verified on https://doi.org/.)*
|
|
428
|
+
|
|
429
|
+
## 15. Citation
|
|
430
|
+
|
|
431
|
+
```bibtex
|
|
432
|
+
@software{roudane_dscc,
|
|
433
|
+
author = {Roudane, Merwan},
|
|
434
|
+
title = {{dscc}: Dynamic Spectral Conditional Correlations in Python},
|
|
435
|
+
year = {2026},
|
|
436
|
+
url = {https://github.com/merwanroudane/dscc},
|
|
437
|
+
version = {0.1.0}
|
|
438
|
+
}
|
|
439
|
+
```
|
|
440
|
+
Please also cite Abadir and Rockinger (2026), doi:10.1093/ectj/utag017, and Abadir (2023), doi:10.1093/ectj/utac023.
|
|
441
|
+
|
|
442
|
+
## 16. Author and licence
|
|
443
|
+
|
|
444
|
+
**Dr Merwan Roudane**
|
|
445
|
+
📧 merwanroudane920@gmail.com
|
|
446
|
+
🔗 https://github.com/merwanroudane
|
|
447
|
+
|
|
448
|
+
Released under the MIT Licence — see [LICENSE](LICENSE).
|