multiarch 0.2.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.
- multiarch-0.2.0/LICENSE +21 -0
- multiarch-0.2.0/PKG-INFO +204 -0
- multiarch-0.2.0/README.md +177 -0
- multiarch-0.2.0/multiarch/__init__.py +18 -0
- multiarch-0.2.0/multiarch/_kernels.py +235 -0
- multiarch-0.2.0/multiarch/_numdiff.py +43 -0
- multiarch-0.2.0/multiarch/dcc.py +731 -0
- multiarch-0.2.0/multiarch/diagnostics.py +115 -0
- multiarch-0.2.0/multiarch/ewma.py +26 -0
- multiarch-0.2.0/multiarch/gogarch.py +220 -0
- multiarch-0.2.0/multiarch/rolling.py +69 -0
- multiarch-0.2.0/multiarch/simulate.py +43 -0
- multiarch-0.2.0/multiarch/univariate.py +213 -0
- multiarch-0.2.0/multiarch.egg-info/PKG-INFO +204 -0
- multiarch-0.2.0/multiarch.egg-info/SOURCES.txt +20 -0
- multiarch-0.2.0/multiarch.egg-info/dependency_links.txt +1 -0
- multiarch-0.2.0/multiarch.egg-info/requires.txt +8 -0
- multiarch-0.2.0/multiarch.egg-info/top_level.txt +1 -0
- multiarch-0.2.0/pyproject.toml +33 -0
- multiarch-0.2.0/setup.cfg +4 -0
- multiarch-0.2.0/tests/test_dcc.py +173 -0
- multiarch-0.2.0/tests/test_features.py +192 -0
multiarch-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Legard525
|
|
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.
|
multiarch-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: multiarch
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Fast multivariate GARCH for Python: CCC, DCC, aDCC, copula-DCC and GO-GARCH, validated against rmgarch
|
|
5
|
+
Author: Alex Nikolaenko
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/Legard525/multiarch
|
|
8
|
+
Project-URL: Issues, https://github.com/Legard525/multiarch/issues
|
|
9
|
+
Keywords: garch,dcc,multivariate garch,volatility,correlation,copula,go-garch,value-at-risk,econometrics,finance
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Science/Research
|
|
12
|
+
Classifier: Intended Audience :: Financial and Insurance Industry
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
15
|
+
Classifier: Topic :: Office/Business :: Financial :: Investment
|
|
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
|
+
Requires-Dist: pandas>=2.0
|
|
22
|
+
Requires-Dist: numba>=0.58
|
|
23
|
+
Provides-Extra: test
|
|
24
|
+
Requires-Dist: pytest; extra == "test"
|
|
25
|
+
Requires-Dist: arch; extra == "test"
|
|
26
|
+
Dynamic: license-file
|
|
27
|
+
|
|
28
|
+
# multiarch
|
|
29
|
+
|
|
30
|
+
[](https://github.com/Legard525/multiarch/actions/workflows/tests.yml)
|
|
31
|
+
|
|
32
|
+
**Multivariate GARCH for Python.** The [`arch`](https://github.com/bashtage/arch) package
|
|
33
|
+
only fits univariate models, so Python has had no maintained multivariate GARCH library.
|
|
34
|
+
Courses and research therefore send Python users to R (`rmgarch`, `tsmarch`).
|
|
35
|
+
multiarch fills that gap:
|
|
36
|
+
|
|
37
|
+
- **Models:** CCC, DCC and asymmetric DCC, optionally as Gaussian or Student-t copulas, plus GO-GARCH.
|
|
38
|
+
- **Margins:** GARCH(1,1) or GJR-GARCH(1,1) with normal or Student-t errors.
|
|
39
|
+
- **Inference:** two-step standard errors that correct for the first estimation stage.
|
|
40
|
+
- **Large cross-sections:** a composite-likelihood estimator for many assets.
|
|
41
|
+
- **Outputs:** forecasts, simulation, portfolio VaR and ES, rolling backtests and specification tests.
|
|
42
|
+
- **Validation:** it reproduces `rmgarch`'s likelihood exactly. It runs 2–9× faster than rmgarch and 7–30× faster than tsmarch.
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
pip install multiarch
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Requires Python ≥ 3.10, NumPy, SciPy, pandas and numba.
|
|
49
|
+
|
|
50
|
+
## Quick start
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
import multiarch as ma
|
|
54
|
+
|
|
55
|
+
res = ma.DCC(returns).fit() # returns: (T, N) DataFrame, any units
|
|
56
|
+
print(res.summary())
|
|
57
|
+
|
|
58
|
+
res.conditional_correlation # (T, N, N) R_t
|
|
59
|
+
res.conditional_covariance # (T, N, N) H_t = D_t R_t D_t
|
|
60
|
+
res.correlation("JPM", "INTC") # one rho_t path as a Series
|
|
61
|
+
res.forecast(10) # mean, variance, correlation, covariance
|
|
62
|
+
res.value_at_risk([0.5, 0.5], level=0.01, value=1e6) # {'VaR': ..., 'ES': ...}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
[`examples/course_workflow.py`](https://github.com/Legard525/multiarch/blob/main/examples/course_workflow.py) walks through the standard
|
|
66
|
+
course sequence: EWMA → CCC → DCC → constant-correlation test → portfolio VaR → rolling VaR backtest.
|
|
67
|
+
|
|
68
|
+
## What's in it
|
|
69
|
+
|
|
70
|
+
| | |
|
|
71
|
+
|---|---|
|
|
72
|
+
| `DCC(returns, model=...)` | `"dcc"` (Engle 2002), `"adcc"` (Cappiello, Engle & Sheppard 2006), `"ccc"` (Bollerslev 1990) |
|
|
73
|
+
| `vol=` | `"garch"` or `"gjr"` margins, each (1,1) |
|
|
74
|
+
| `mean=` | `"constant"` or `"zero"` |
|
|
75
|
+
| `dist=` | margin distribution, `"normal"` or `"t"` |
|
|
76
|
+
| `corr_dist=` | correlation-stage distribution, `"normal"` or multivariate `"t"` (defaults to `dist`) |
|
|
77
|
+
| `copula=` | `None`, `"normal"` or `"t"`: copula-DCC with parametric margins (as `rmgarch::cgarchfit`) |
|
|
78
|
+
| `estimator=` | `"full"` likelihood or `"composite"` pairwise likelihood (Engle, Shephard & Sheppard 2008) for large N |
|
|
79
|
+
| `convention=` | `"arch"` (default), `"rmgarch"` or `"tsmarch"`: start-up conventions that reproduce each package |
|
|
80
|
+
| `.fit(se=...)` | `"two-step"` (Engle & Sheppard 2001 corrected), `"naive"` or `None` |
|
|
81
|
+
| `.fix(params)` / `.apply(new_returns)` | evaluate fixed parameters, or filter new data with fitted ones |
|
|
82
|
+
| `.forecast(h)` | closed-form variances; Engle–Sheppard correlation forecast as in rmgarch |
|
|
83
|
+
| `.simulate(n, n_paths, start="end")` | Monte Carlo paths, including copula models |
|
|
84
|
+
| `.value_at_risk(w, level, horizon)` | analytic (normal or multivariate t) or simulated VaR and ES |
|
|
85
|
+
| `.value_at_risk_path(w, level)` | in-sample one-step VaR/ES path |
|
|
86
|
+
| `GOGARCH(returns)` | GO-GARCH (van der Weide 2002) with a built-in FastICA rotation |
|
|
87
|
+
| `rolling_forecast(returns, window, refit_every)` | rolling or expanding re-estimation with no look-ahead |
|
|
88
|
+
| `var_backtest(r, VaR, level)` | violation ratio, Kupiec and Christoffersen tests |
|
|
89
|
+
| `constant_correlation_test(returns)` | Engle & Sheppard (2001) test of CCC against DCC |
|
|
90
|
+
| `hosking_test(x, lags)` | multivariate portmanteau test |
|
|
91
|
+
| `ewma_covariance(returns, lam)` | RiskMetrics EWMA |
|
|
92
|
+
| `simulate_dcc(...)` | simulate from given parameters |
|
|
93
|
+
|
|
94
|
+
Estimation is scale-free: percent and fractional returns give the same parameters,
|
|
95
|
+
with ω and μ rescaled.
|
|
96
|
+
|
|
97
|
+
## Validation
|
|
98
|
+
|
|
99
|
+
All numbers come from [`validation/`](https://github.com/Legard525/multiarch/tree/main/validation) and are reproducible: `Rscript rmgarch_fit.R .`
|
|
100
|
+
then `python compare.py`, and `Rscript tsmarch_fit.R .` then `python compare_tsmarch.py`.
|
|
101
|
+
The data are daily returns of DJI stocks from `rmgarch`'s `dji30ret` (T = 5,521).
|
|
102
|
+
|
|
103
|
+
### Against rmgarch 1.3 (R 4.6)
|
|
104
|
+
|
|
105
|
+
`ll(ours @ rm)` is multiarch's log-likelihood evaluated at rmgarch's own estimates.
|
|
106
|
+
If both packages implement the same model, it equals rmgarch's reported value.
|
|
107
|
+
|
|
108
|
+
| case | ll (rmgarch) | ll (ours @ rm) | max \|ρ_t diff\| @ rm | ll (ours, own fit) | time rmgarch → ours |
|
|
109
|
+
|---|---|---|---|---|---|
|
|
110
|
+
| DCC, normal, N=5 | −53514.64292 | −53514.64292 | 0 | −53513.99601 | 2.8 s → 0.32 s |
|
|
111
|
+
| aDCC, normal | −53513.86843 | −53513.86843 | 0 | −53513.13231 | 3.4 s → 0.45 s |
|
|
112
|
+
| DCC, Student-t | −52235.91344 | −52235.91344 | 0 | −52237.53475 | 3.7 s → 0.60 s |
|
|
113
|
+
| DCC, GJR margins | −53481.97322 | −53481.97322 | 0 | −53481.59132 | 3.3 s → 0.39 s |
|
|
114
|
+
| DCC, zero mean | −53534.39786 | −53534.39786 | 0 | −53533.64643 | 2.1 s → 0.26 s |
|
|
115
|
+
| DCC, N=10 | −101267.48845 | −101267.48845 | 0 | −101266.80559 | 7.6 s → 1.2 s |
|
|
116
|
+
| DCC, raw returns, N=2 | 28163.98498 | 28163.98498 | 0 | 28163.97845 | 0.84 s → 0.09 s |
|
|
117
|
+
| t-copula DCC, t margins | −52079.16469 | −52079.16469 | 0 | −52080.47842 | 6.3 s → 3.1 s |
|
|
118
|
+
| Gaussian-copula DCC, t margins | −52196.46089 | −52196.46089 | 0 | −52197.96371 | 3.6 s → 1.1 s |
|
|
119
|
+
|
|
120
|
+
Timings for multiarch include the two-step standard errors.
|
|
121
|
+
|
|
122
|
+
- The likelihood functions are identical, and so are the correlation paths at the same parameters.
|
|
123
|
+
- The 10-step DCC covariance forecasts agree to 2e-14.
|
|
124
|
+
- Estimates differ only through the optimiser, and multiarch reaches higher margin likelihoods.
|
|
125
|
+
Because estimation is two-step, a better first stage can still leave the joint
|
|
126
|
+
log-likelihood slightly lower (the t and copula rows). That is a property of the two-step
|
|
127
|
+
estimator, not a different model.
|
|
128
|
+
- Standard errors for the DCC parameters *a* and *b* are within 5% of rmgarch's. For the shape ν and the aDCC asymmetry *g* they are 17–31% larger.
|
|
129
|
+
A 300-sample Monte Carlo (`mc_se.py`) shows that multiarch's two-step SEs are calibrated:
|
|
130
|
+
the median SE is 0.0059 against a sampling SD of 0.0059 for *a*, and 0.0124 against 0.0139 for *b*.
|
|
131
|
+
- **GO-GARCH:** given rmgarch's ICA rotation and factor parameters, the likelihood agrees to 0.06,
|
|
132
|
+
a difference that comes from how the constant mean is estimated. multiarch's own FastICA finds a rotation
|
|
133
|
+
with a likelihood 277 points higher on this data, in 0.04 s against 0.47 s.
|
|
134
|
+
|
|
135
|
+
### Against tsmarch (the package used in *Financial Risk Forecasting* notes)
|
|
136
|
+
|
|
137
|
+
| case | ll (tsmarch) | ll (ours @ ts) | max \|ρ_t diff\| @ ts | time tsmarch → ours |
|
|
138
|
+
|---|---|---|---|---|
|
|
139
|
+
| DCC, N=2 | 28163.94124 | 28163.94306 | 4.5e-4 | 0.73 s → 0.04 s |
|
|
140
|
+
| DCC, N=5 | 73611.21223 | 73611.23406 | 4.2e-4 | 2.7 s → 0.10 s |
|
|
141
|
+
| aDCC, N=5 | 73612.01170 | 73612.03276 | 4.2e-4 | 3.8 s → 0.11 s |
|
|
142
|
+
| DCC, multivariate t | 74873.58661 | 74873.58785 | 3.8e-4 | 3.9 s → 0.13 s |
|
|
143
|
+
|
|
144
|
+
The margins match tsgarch exactly (σ_t to 1e-14). A residual gap of under 0.03 log-likelihood
|
|
145
|
+
points remains in tsmarch's compiled DCC recursion; it fades to 1e-7 by the end of the sample.
|
|
146
|
+
|
|
147
|
+
### Notes on rmgarch's `DCCtest`
|
|
148
|
+
|
|
149
|
+
`rmgarch::DCCtest` departs from Engle & Sheppard (2001) in two ways. It calls
|
|
150
|
+
`qr.solve(regressand, regressors)`, which regresses the lags on the target instead of the
|
|
151
|
+
reverse, and it divides by √σ² instead of σ². `validation/compare.py` reproduces its statistic
|
|
152
|
+
exactly (65.34) from its own residuals. On the same residuals, the textbook statistic
|
|
153
|
+
returned by `constant_correlation_test` is 41.08.
|
|
154
|
+
|
|
155
|
+
### Scaling
|
|
156
|
+
|
|
157
|
+
From `validation/bench.py` (T = 2,500, no SEs):
|
|
158
|
+
|
|
159
|
+
| N | full likelihood | composite likelihood |
|
|
160
|
+
|---|---|---|
|
|
161
|
+
| 5 | 0.05 s | 0.02 s |
|
|
162
|
+
| 25 | 0.34 s | 0.09 s |
|
|
163
|
+
| 50 | 0.35 s | 0.19 s |
|
|
164
|
+
| 100 | n/a | 0.41 s |
|
|
165
|
+
|
|
166
|
+
The full-likelihood DCC estimate of *b* is biased downward as N grows: 0.930 against a true 0.95
|
|
167
|
+
at N = 50. The composite estimator does not have this bias.
|
|
168
|
+
|
|
169
|
+
## Conventions
|
|
170
|
+
|
|
171
|
+
The default (`"arch"`) follows the `arch` package for the margins (exponential backcast)
|
|
172
|
+
and uses Q̄ = Z′Z/T with Q₁ = Q̄. `convention="rmgarch"` reproduces rmgarch's start-up:
|
|
173
|
+
- σ²₁ is the sample mean of ε².
|
|
174
|
+
- Q̄ = cov(Z).
|
|
175
|
+
- The first Q step uses a pre-sample shock of ones in DCC and of zeros in copula-DCC.
|
|
176
|
+
- Copula uniforms are clipped at 0.99999.
|
|
177
|
+
|
|
178
|
+
`convention="tsmarch"` reproduces tsgarch's σ²₁ = ω + persistence · mean(ε²) and uses Q₁ = Q̄ = cov(Z).
|
|
179
|
+
These choices only affect the first few dozen observations.
|
|
180
|
+
|
|
181
|
+
## Limitations
|
|
182
|
+
|
|
183
|
+
- Margins are GARCH(1,1) or GJR(1,1) with a constant or zero mean. There are no ARMA means or higher orders.
|
|
184
|
+
- DCC is of order (1,1).
|
|
185
|
+
- Estimation is two-step. The two-step SEs treat Q̄ and N̄ as known, as rmgarch does.
|
|
186
|
+
- Composite-likelihood SEs are Hessian-only.
|
|
187
|
+
- GO-GARCH SEs ignore estimation error in the rotation.
|
|
188
|
+
- Copula models use parametric margins only. Empirical and semi-parametric margins are not implemented.
|
|
189
|
+
- BEKK and DCC-MIDAS are not implemented.
|
|
190
|
+
|
|
191
|
+
## References
|
|
192
|
+
|
|
193
|
+
- Bollerslev (1990), *Modelling the coherence in short-run nominal exchange rates*, REStat.
|
|
194
|
+
- Engle (2002), *Dynamic conditional correlation*, JBES.
|
|
195
|
+
- Engle & Sheppard (2001), *Theoretical and empirical properties of dynamic conditional correlation multivariate GARCH*, NBER WP 8554.
|
|
196
|
+
- Cappiello, Engle & Sheppard (2006), *Asymmetric dynamics in the correlations of global equity and bond returns*, J. Fin. Econometrics.
|
|
197
|
+
- Engle, Shephard & Sheppard (2008), *Fitting vast dimensional time-varying covariance models*, working paper.
|
|
198
|
+
- van der Weide (2002), *GO-GARCH: a multivariate generalized orthogonal GARCH model*, J. Applied Econometrics.
|
|
199
|
+
- Patton (2006), *Modelling asymmetric exchange rate dependence*, IER.
|
|
200
|
+
- Kupiec (1995); Christoffersen (1998); Hosking (1980).
|
|
201
|
+
|
|
202
|
+
## License
|
|
203
|
+
|
|
204
|
+
MIT
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# multiarch
|
|
2
|
+
|
|
3
|
+
[](https://github.com/Legard525/multiarch/actions/workflows/tests.yml)
|
|
4
|
+
|
|
5
|
+
**Multivariate GARCH for Python.** The [`arch`](https://github.com/bashtage/arch) package
|
|
6
|
+
only fits univariate models, so Python has had no maintained multivariate GARCH library.
|
|
7
|
+
Courses and research therefore send Python users to R (`rmgarch`, `tsmarch`).
|
|
8
|
+
multiarch fills that gap:
|
|
9
|
+
|
|
10
|
+
- **Models:** CCC, DCC and asymmetric DCC, optionally as Gaussian or Student-t copulas, plus GO-GARCH.
|
|
11
|
+
- **Margins:** GARCH(1,1) or GJR-GARCH(1,1) with normal or Student-t errors.
|
|
12
|
+
- **Inference:** two-step standard errors that correct for the first estimation stage.
|
|
13
|
+
- **Large cross-sections:** a composite-likelihood estimator for many assets.
|
|
14
|
+
- **Outputs:** forecasts, simulation, portfolio VaR and ES, rolling backtests and specification tests.
|
|
15
|
+
- **Validation:** it reproduces `rmgarch`'s likelihood exactly. It runs 2–9× faster than rmgarch and 7–30× faster than tsmarch.
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pip install multiarch
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Requires Python ≥ 3.10, NumPy, SciPy, pandas and numba.
|
|
22
|
+
|
|
23
|
+
## Quick start
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
import multiarch as ma
|
|
27
|
+
|
|
28
|
+
res = ma.DCC(returns).fit() # returns: (T, N) DataFrame, any units
|
|
29
|
+
print(res.summary())
|
|
30
|
+
|
|
31
|
+
res.conditional_correlation # (T, N, N) R_t
|
|
32
|
+
res.conditional_covariance # (T, N, N) H_t = D_t R_t D_t
|
|
33
|
+
res.correlation("JPM", "INTC") # one rho_t path as a Series
|
|
34
|
+
res.forecast(10) # mean, variance, correlation, covariance
|
|
35
|
+
res.value_at_risk([0.5, 0.5], level=0.01, value=1e6) # {'VaR': ..., 'ES': ...}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
[`examples/course_workflow.py`](https://github.com/Legard525/multiarch/blob/main/examples/course_workflow.py) walks through the standard
|
|
39
|
+
course sequence: EWMA → CCC → DCC → constant-correlation test → portfolio VaR → rolling VaR backtest.
|
|
40
|
+
|
|
41
|
+
## What's in it
|
|
42
|
+
|
|
43
|
+
| | |
|
|
44
|
+
|---|---|
|
|
45
|
+
| `DCC(returns, model=...)` | `"dcc"` (Engle 2002), `"adcc"` (Cappiello, Engle & Sheppard 2006), `"ccc"` (Bollerslev 1990) |
|
|
46
|
+
| `vol=` | `"garch"` or `"gjr"` margins, each (1,1) |
|
|
47
|
+
| `mean=` | `"constant"` or `"zero"` |
|
|
48
|
+
| `dist=` | margin distribution, `"normal"` or `"t"` |
|
|
49
|
+
| `corr_dist=` | correlation-stage distribution, `"normal"` or multivariate `"t"` (defaults to `dist`) |
|
|
50
|
+
| `copula=` | `None`, `"normal"` or `"t"`: copula-DCC with parametric margins (as `rmgarch::cgarchfit`) |
|
|
51
|
+
| `estimator=` | `"full"` likelihood or `"composite"` pairwise likelihood (Engle, Shephard & Sheppard 2008) for large N |
|
|
52
|
+
| `convention=` | `"arch"` (default), `"rmgarch"` or `"tsmarch"`: start-up conventions that reproduce each package |
|
|
53
|
+
| `.fit(se=...)` | `"two-step"` (Engle & Sheppard 2001 corrected), `"naive"` or `None` |
|
|
54
|
+
| `.fix(params)` / `.apply(new_returns)` | evaluate fixed parameters, or filter new data with fitted ones |
|
|
55
|
+
| `.forecast(h)` | closed-form variances; Engle–Sheppard correlation forecast as in rmgarch |
|
|
56
|
+
| `.simulate(n, n_paths, start="end")` | Monte Carlo paths, including copula models |
|
|
57
|
+
| `.value_at_risk(w, level, horizon)` | analytic (normal or multivariate t) or simulated VaR and ES |
|
|
58
|
+
| `.value_at_risk_path(w, level)` | in-sample one-step VaR/ES path |
|
|
59
|
+
| `GOGARCH(returns)` | GO-GARCH (van der Weide 2002) with a built-in FastICA rotation |
|
|
60
|
+
| `rolling_forecast(returns, window, refit_every)` | rolling or expanding re-estimation with no look-ahead |
|
|
61
|
+
| `var_backtest(r, VaR, level)` | violation ratio, Kupiec and Christoffersen tests |
|
|
62
|
+
| `constant_correlation_test(returns)` | Engle & Sheppard (2001) test of CCC against DCC |
|
|
63
|
+
| `hosking_test(x, lags)` | multivariate portmanteau test |
|
|
64
|
+
| `ewma_covariance(returns, lam)` | RiskMetrics EWMA |
|
|
65
|
+
| `simulate_dcc(...)` | simulate from given parameters |
|
|
66
|
+
|
|
67
|
+
Estimation is scale-free: percent and fractional returns give the same parameters,
|
|
68
|
+
with ω and μ rescaled.
|
|
69
|
+
|
|
70
|
+
## Validation
|
|
71
|
+
|
|
72
|
+
All numbers come from [`validation/`](https://github.com/Legard525/multiarch/tree/main/validation) and are reproducible: `Rscript rmgarch_fit.R .`
|
|
73
|
+
then `python compare.py`, and `Rscript tsmarch_fit.R .` then `python compare_tsmarch.py`.
|
|
74
|
+
The data are daily returns of DJI stocks from `rmgarch`'s `dji30ret` (T = 5,521).
|
|
75
|
+
|
|
76
|
+
### Against rmgarch 1.3 (R 4.6)
|
|
77
|
+
|
|
78
|
+
`ll(ours @ rm)` is multiarch's log-likelihood evaluated at rmgarch's own estimates.
|
|
79
|
+
If both packages implement the same model, it equals rmgarch's reported value.
|
|
80
|
+
|
|
81
|
+
| case | ll (rmgarch) | ll (ours @ rm) | max \|ρ_t diff\| @ rm | ll (ours, own fit) | time rmgarch → ours |
|
|
82
|
+
|---|---|---|---|---|---|
|
|
83
|
+
| DCC, normal, N=5 | −53514.64292 | −53514.64292 | 0 | −53513.99601 | 2.8 s → 0.32 s |
|
|
84
|
+
| aDCC, normal | −53513.86843 | −53513.86843 | 0 | −53513.13231 | 3.4 s → 0.45 s |
|
|
85
|
+
| DCC, Student-t | −52235.91344 | −52235.91344 | 0 | −52237.53475 | 3.7 s → 0.60 s |
|
|
86
|
+
| DCC, GJR margins | −53481.97322 | −53481.97322 | 0 | −53481.59132 | 3.3 s → 0.39 s |
|
|
87
|
+
| DCC, zero mean | −53534.39786 | −53534.39786 | 0 | −53533.64643 | 2.1 s → 0.26 s |
|
|
88
|
+
| DCC, N=10 | −101267.48845 | −101267.48845 | 0 | −101266.80559 | 7.6 s → 1.2 s |
|
|
89
|
+
| DCC, raw returns, N=2 | 28163.98498 | 28163.98498 | 0 | 28163.97845 | 0.84 s → 0.09 s |
|
|
90
|
+
| t-copula DCC, t margins | −52079.16469 | −52079.16469 | 0 | −52080.47842 | 6.3 s → 3.1 s |
|
|
91
|
+
| Gaussian-copula DCC, t margins | −52196.46089 | −52196.46089 | 0 | −52197.96371 | 3.6 s → 1.1 s |
|
|
92
|
+
|
|
93
|
+
Timings for multiarch include the two-step standard errors.
|
|
94
|
+
|
|
95
|
+
- The likelihood functions are identical, and so are the correlation paths at the same parameters.
|
|
96
|
+
- The 10-step DCC covariance forecasts agree to 2e-14.
|
|
97
|
+
- Estimates differ only through the optimiser, and multiarch reaches higher margin likelihoods.
|
|
98
|
+
Because estimation is two-step, a better first stage can still leave the joint
|
|
99
|
+
log-likelihood slightly lower (the t and copula rows). That is a property of the two-step
|
|
100
|
+
estimator, not a different model.
|
|
101
|
+
- Standard errors for the DCC parameters *a* and *b* are within 5% of rmgarch's. For the shape ν and the aDCC asymmetry *g* they are 17–31% larger.
|
|
102
|
+
A 300-sample Monte Carlo (`mc_se.py`) shows that multiarch's two-step SEs are calibrated:
|
|
103
|
+
the median SE is 0.0059 against a sampling SD of 0.0059 for *a*, and 0.0124 against 0.0139 for *b*.
|
|
104
|
+
- **GO-GARCH:** given rmgarch's ICA rotation and factor parameters, the likelihood agrees to 0.06,
|
|
105
|
+
a difference that comes from how the constant mean is estimated. multiarch's own FastICA finds a rotation
|
|
106
|
+
with a likelihood 277 points higher on this data, in 0.04 s against 0.47 s.
|
|
107
|
+
|
|
108
|
+
### Against tsmarch (the package used in *Financial Risk Forecasting* notes)
|
|
109
|
+
|
|
110
|
+
| case | ll (tsmarch) | ll (ours @ ts) | max \|ρ_t diff\| @ ts | time tsmarch → ours |
|
|
111
|
+
|---|---|---|---|---|
|
|
112
|
+
| DCC, N=2 | 28163.94124 | 28163.94306 | 4.5e-4 | 0.73 s → 0.04 s |
|
|
113
|
+
| DCC, N=5 | 73611.21223 | 73611.23406 | 4.2e-4 | 2.7 s → 0.10 s |
|
|
114
|
+
| aDCC, N=5 | 73612.01170 | 73612.03276 | 4.2e-4 | 3.8 s → 0.11 s |
|
|
115
|
+
| DCC, multivariate t | 74873.58661 | 74873.58785 | 3.8e-4 | 3.9 s → 0.13 s |
|
|
116
|
+
|
|
117
|
+
The margins match tsgarch exactly (σ_t to 1e-14). A residual gap of under 0.03 log-likelihood
|
|
118
|
+
points remains in tsmarch's compiled DCC recursion; it fades to 1e-7 by the end of the sample.
|
|
119
|
+
|
|
120
|
+
### Notes on rmgarch's `DCCtest`
|
|
121
|
+
|
|
122
|
+
`rmgarch::DCCtest` departs from Engle & Sheppard (2001) in two ways. It calls
|
|
123
|
+
`qr.solve(regressand, regressors)`, which regresses the lags on the target instead of the
|
|
124
|
+
reverse, and it divides by √σ² instead of σ². `validation/compare.py` reproduces its statistic
|
|
125
|
+
exactly (65.34) from its own residuals. On the same residuals, the textbook statistic
|
|
126
|
+
returned by `constant_correlation_test` is 41.08.
|
|
127
|
+
|
|
128
|
+
### Scaling
|
|
129
|
+
|
|
130
|
+
From `validation/bench.py` (T = 2,500, no SEs):
|
|
131
|
+
|
|
132
|
+
| N | full likelihood | composite likelihood |
|
|
133
|
+
|---|---|---|
|
|
134
|
+
| 5 | 0.05 s | 0.02 s |
|
|
135
|
+
| 25 | 0.34 s | 0.09 s |
|
|
136
|
+
| 50 | 0.35 s | 0.19 s |
|
|
137
|
+
| 100 | n/a | 0.41 s |
|
|
138
|
+
|
|
139
|
+
The full-likelihood DCC estimate of *b* is biased downward as N grows: 0.930 against a true 0.95
|
|
140
|
+
at N = 50. The composite estimator does not have this bias.
|
|
141
|
+
|
|
142
|
+
## Conventions
|
|
143
|
+
|
|
144
|
+
The default (`"arch"`) follows the `arch` package for the margins (exponential backcast)
|
|
145
|
+
and uses Q̄ = Z′Z/T with Q₁ = Q̄. `convention="rmgarch"` reproduces rmgarch's start-up:
|
|
146
|
+
- σ²₁ is the sample mean of ε².
|
|
147
|
+
- Q̄ = cov(Z).
|
|
148
|
+
- The first Q step uses a pre-sample shock of ones in DCC and of zeros in copula-DCC.
|
|
149
|
+
- Copula uniforms are clipped at 0.99999.
|
|
150
|
+
|
|
151
|
+
`convention="tsmarch"` reproduces tsgarch's σ²₁ = ω + persistence · mean(ε²) and uses Q₁ = Q̄ = cov(Z).
|
|
152
|
+
These choices only affect the first few dozen observations.
|
|
153
|
+
|
|
154
|
+
## Limitations
|
|
155
|
+
|
|
156
|
+
- Margins are GARCH(1,1) or GJR(1,1) with a constant or zero mean. There are no ARMA means or higher orders.
|
|
157
|
+
- DCC is of order (1,1).
|
|
158
|
+
- Estimation is two-step. The two-step SEs treat Q̄ and N̄ as known, as rmgarch does.
|
|
159
|
+
- Composite-likelihood SEs are Hessian-only.
|
|
160
|
+
- GO-GARCH SEs ignore estimation error in the rotation.
|
|
161
|
+
- Copula models use parametric margins only. Empirical and semi-parametric margins are not implemented.
|
|
162
|
+
- BEKK and DCC-MIDAS are not implemented.
|
|
163
|
+
|
|
164
|
+
## References
|
|
165
|
+
|
|
166
|
+
- Bollerslev (1990), *Modelling the coherence in short-run nominal exchange rates*, REStat.
|
|
167
|
+
- Engle (2002), *Dynamic conditional correlation*, JBES.
|
|
168
|
+
- Engle & Sheppard (2001), *Theoretical and empirical properties of dynamic conditional correlation multivariate GARCH*, NBER WP 8554.
|
|
169
|
+
- Cappiello, Engle & Sheppard (2006), *Asymmetric dynamics in the correlations of global equity and bond returns*, J. Fin. Econometrics.
|
|
170
|
+
- Engle, Shephard & Sheppard (2008), *Fitting vast dimensional time-varying covariance models*, working paper.
|
|
171
|
+
- van der Weide (2002), *GO-GARCH: a multivariate generalized orthogonal GARCH model*, J. Applied Econometrics.
|
|
172
|
+
- Patton (2006), *Modelling asymmetric exchange rate dependence*, IER.
|
|
173
|
+
- Kupiec (1995); Christoffersen (1998); Hosking (1980).
|
|
174
|
+
|
|
175
|
+
## License
|
|
176
|
+
|
|
177
|
+
MIT
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""multiarch: fast multivariate GARCH (CCC / DCC / aDCC / copula-DCC /
|
|
2
|
+
GO-GARCH) for Python."""
|
|
3
|
+
|
|
4
|
+
from .dcc import DCC, DCCResult
|
|
5
|
+
from .diagnostics import constant_correlation_test, hosking_test, var_backtest
|
|
6
|
+
from .ewma import ewma_covariance
|
|
7
|
+
from .gogarch import GOGARCH, GOGARCHResult
|
|
8
|
+
from .rolling import RollingForecast, rolling_forecast
|
|
9
|
+
from .simulate import simulate_dcc
|
|
10
|
+
from .univariate import UnivariateSpec, fit_univariate
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"DCC", "DCCResult", "GOGARCH", "GOGARCHResult", "RollingForecast",
|
|
14
|
+
"UnivariateSpec", "constant_correlation_test", "ewma_covariance",
|
|
15
|
+
"fit_univariate", "hosking_test", "rolling_forecast", "simulate_dcc",
|
|
16
|
+
"var_backtest",
|
|
17
|
+
]
|
|
18
|
+
__version__ = "0.2.0"
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
"""Numba kernels for the inner recursions. Everything here is O(T) loops that
|
|
2
|
+
would be slow in pure Python; the public API lives in the other modules."""
|
|
3
|
+
|
|
4
|
+
import math
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
from numba import njit
|
|
8
|
+
|
|
9
|
+
LOG_2PI = math.log(2.0 * math.pi)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# ---------------------------------------------------------------- univariate
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@njit(cache=True)
|
|
16
|
+
def garch_filter(omega, alpha, gamma, beta, resid, sigma2_0):
|
|
17
|
+
"""GJR-GARCH(1,1) variance recursion (gamma=0 gives GARCH), given the
|
|
18
|
+
first conditional variance sigma2_0."""
|
|
19
|
+
n = resid.shape[0]
|
|
20
|
+
sigma2 = np.empty(n)
|
|
21
|
+
sigma2[0] = sigma2_0
|
|
22
|
+
for t in range(1, n):
|
|
23
|
+
e = resid[t - 1]
|
|
24
|
+
e2 = e * e
|
|
25
|
+
s = omega + alpha * e2 + beta * sigma2[t - 1]
|
|
26
|
+
if e < 0.0:
|
|
27
|
+
s += gamma * e2
|
|
28
|
+
sigma2[t] = s
|
|
29
|
+
return sigma2
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@njit(cache=True)
|
|
33
|
+
def normal_loglik_obs(resid, sigma2):
|
|
34
|
+
return -0.5 * (LOG_2PI + np.log(sigma2) + resid * resid / sigma2)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@njit(cache=True)
|
|
38
|
+
def std_t_loglik_obs(resid, sigma2, nu):
|
|
39
|
+
"""Unit-variance Student-t, as in arch's StudentsT."""
|
|
40
|
+
c = (
|
|
41
|
+
math.lgamma(0.5 * (nu + 1.0))
|
|
42
|
+
- math.lgamma(0.5 * nu)
|
|
43
|
+
- 0.5 * math.log(math.pi * (nu - 2.0))
|
|
44
|
+
)
|
|
45
|
+
return c - 0.5 * np.log(sigma2) - 0.5 * (nu + 1.0) * np.log(
|
|
46
|
+
1.0 + resid * resid / (sigma2 * (nu - 2.0))
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# ---------------------------------------------------------------- correlation
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@njit(cache=True)
|
|
54
|
+
def _mv_logpdf(z_t, R, nu):
|
|
55
|
+
"""Log density of z_t ~ N(0, R) (nu <= 0) or unit-variance MV-t(nu)."""
|
|
56
|
+
n = z_t.shape[0]
|
|
57
|
+
L = np.linalg.cholesky(R)
|
|
58
|
+
logdet = 0.0
|
|
59
|
+
for i in range(n):
|
|
60
|
+
logdet += 2.0 * math.log(L[i, i])
|
|
61
|
+
# forward substitution: y = L^{-1} z, quad = y'y
|
|
62
|
+
y = np.empty(n)
|
|
63
|
+
quad = 0.0
|
|
64
|
+
for i in range(n):
|
|
65
|
+
s = z_t[i]
|
|
66
|
+
for k in range(i):
|
|
67
|
+
s -= L[i, k] * y[k]
|
|
68
|
+
y[i] = s / L[i, i]
|
|
69
|
+
quad += y[i] * y[i]
|
|
70
|
+
if nu <= 0.0:
|
|
71
|
+
return -0.5 * (n * LOG_2PI + logdet + quad)
|
|
72
|
+
return (
|
|
73
|
+
math.lgamma(0.5 * (nu + n))
|
|
74
|
+
- math.lgamma(0.5 * nu)
|
|
75
|
+
- 0.5 * n * math.log(math.pi * (nu - 2.0))
|
|
76
|
+
- 0.5 * logdet
|
|
77
|
+
- 0.5 * (nu + n) * math.log(1.0 + quad / (nu - 2.0))
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@njit(cache=True)
|
|
82
|
+
def _q_to_r(Q, R):
|
|
83
|
+
n = Q.shape[0]
|
|
84
|
+
for i in range(n):
|
|
85
|
+
for j in range(n):
|
|
86
|
+
R[i, j] = Q[i, j] / math.sqrt(Q[i, i] * Q[j, j])
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@njit(cache=True)
|
|
90
|
+
def dcc_loglik_obs(z, Qbar, Nbar, a, b, g, nu, Q0):
|
|
91
|
+
"""Per-observation log density of standardised residuals under (A)DCC.
|
|
92
|
+
|
|
93
|
+
Q_t = (1-a-b) Qbar - g Nbar + a z z' + g n n' + b Q_{t-1}, Q_0 = Qbar,
|
|
94
|
+
with n = z * 1{z<0}. g = 0 gives plain DCC; Q0 is the Q used for the
|
|
95
|
+
first observation (normally Qbar).
|
|
96
|
+
"""
|
|
97
|
+
T, n = z.shape
|
|
98
|
+
out = np.empty(T)
|
|
99
|
+
C = (1.0 - a - b) * Qbar - g * Nbar
|
|
100
|
+
Q = Q0.copy()
|
|
101
|
+
R = np.empty((n, n))
|
|
102
|
+
for t in range(T):
|
|
103
|
+
if t > 0:
|
|
104
|
+
zp = z[t - 1]
|
|
105
|
+
for i in range(n):
|
|
106
|
+
ni = zp[i] if zp[i] < 0.0 else 0.0
|
|
107
|
+
for j in range(i, n):
|
|
108
|
+
nj = zp[j] if zp[j] < 0.0 else 0.0
|
|
109
|
+
v = C[i, j] + a * zp[i] * zp[j] + g * ni * nj + b * Q[i, j]
|
|
110
|
+
Q[i, j] = v
|
|
111
|
+
Q[j, i] = v
|
|
112
|
+
_q_to_r(Q, R)
|
|
113
|
+
out[t] = _mv_logpdf(z[t], R, nu)
|
|
114
|
+
return out
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
@njit(cache=True)
|
|
118
|
+
def dcc_filter(z, Qbar, Nbar, a, b, g, Q0):
|
|
119
|
+
"""Q_t and R_t for t = 0..T (index T is the one-step-ahead forecast)."""
|
|
120
|
+
T, n = z.shape
|
|
121
|
+
Qs = np.empty((T + 1, n, n))
|
|
122
|
+
Rs = np.empty((T + 1, n, n))
|
|
123
|
+
C = (1.0 - a - b) * Qbar - g * Nbar
|
|
124
|
+
Q = Q0.copy()
|
|
125
|
+
for t in range(T + 1):
|
|
126
|
+
if t > 0:
|
|
127
|
+
zp = z[t - 1]
|
|
128
|
+
for i in range(n):
|
|
129
|
+
ni = zp[i] if zp[i] < 0.0 else 0.0
|
|
130
|
+
for j in range(i, n):
|
|
131
|
+
nj = zp[j] if zp[j] < 0.0 else 0.0
|
|
132
|
+
v = C[i, j] + a * zp[i] * zp[j] + g * ni * nj + b * Q[i, j]
|
|
133
|
+
Q[i, j] = v
|
|
134
|
+
Q[j, i] = v
|
|
135
|
+
Qs[t] = Q
|
|
136
|
+
_q_to_r(Q, Rs[t])
|
|
137
|
+
return Qs, Rs
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
@njit(cache=True)
|
|
141
|
+
def dcc_composite_loglik(z, Qbar, Nbar, pairs, a, b, g, nu, Q0):
|
|
142
|
+
"""Composite likelihood (Engle, Shephard & Sheppard 2008): the sum of
|
|
143
|
+
bivariate DCC log-likelihoods over the given pairs. O(T * #pairs) with
|
|
144
|
+
scalar 2x2 algebra, so it scales to hundreds of assets."""
|
|
145
|
+
T = z.shape[0]
|
|
146
|
+
total = 0.0
|
|
147
|
+
if nu > 0.0:
|
|
148
|
+
c = (
|
|
149
|
+
math.lgamma(0.5 * (nu + 2.0))
|
|
150
|
+
- math.lgamma(0.5 * nu)
|
|
151
|
+
- math.log(math.pi * (nu - 2.0))
|
|
152
|
+
)
|
|
153
|
+
else:
|
|
154
|
+
c = -LOG_2PI
|
|
155
|
+
for p in range(pairs.shape[0]):
|
|
156
|
+
i = pairs[p, 0]
|
|
157
|
+
j = pairs[p, 1]
|
|
158
|
+
cii = (1.0 - a - b) * Qbar[i, i] - g * Nbar[i, i]
|
|
159
|
+
cjj = (1.0 - a - b) * Qbar[j, j] - g * Nbar[j, j]
|
|
160
|
+
cij = (1.0 - a - b) * Qbar[i, j] - g * Nbar[i, j]
|
|
161
|
+
qii = Q0[i, i]
|
|
162
|
+
qjj = Q0[j, j]
|
|
163
|
+
qij = Q0[i, j]
|
|
164
|
+
for t in range(T):
|
|
165
|
+
if t > 0:
|
|
166
|
+
zi = z[t - 1, i]
|
|
167
|
+
zj = z[t - 1, j]
|
|
168
|
+
ni = zi if zi < 0.0 else 0.0
|
|
169
|
+
nj = zj if zj < 0.0 else 0.0
|
|
170
|
+
qii = cii + a * zi * zi + g * ni * ni + b * qii
|
|
171
|
+
qjj = cjj + a * zj * zj + g * nj * nj + b * qjj
|
|
172
|
+
qij = cij + a * zi * zj + g * ni * nj + b * qij
|
|
173
|
+
rho = qij / math.sqrt(qii * qjj)
|
|
174
|
+
det = 1.0 - rho * rho
|
|
175
|
+
if det <= 0.0:
|
|
176
|
+
return -np.inf
|
|
177
|
+
x = z[t, i]
|
|
178
|
+
y = z[t, j]
|
|
179
|
+
quad = (x * x - 2.0 * rho * x * y + y * y) / det
|
|
180
|
+
if nu > 0.0:
|
|
181
|
+
total += c - 0.5 * math.log(det) - 0.5 * (nu + 2.0) * math.log(
|
|
182
|
+
1.0 + quad / (nu - 2.0)
|
|
183
|
+
)
|
|
184
|
+
else:
|
|
185
|
+
total += c - 0.5 * math.log(det) - 0.5 * quad
|
|
186
|
+
return total
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
# ---------------------------------------------------------------- simulation
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
@njit(cache=True)
|
|
193
|
+
def simulate_path(mu, omega, alpha, gamma, beta, Qbar, Nbar, a, b, g, shocks, burn,
|
|
194
|
+
Q0, s2_0):
|
|
195
|
+
"""Simulate returns from GJR-GARCH margins + (A)DCC correlation.
|
|
196
|
+
|
|
197
|
+
`shocks` are iid unit-variance draws (T+burn, n) with identity
|
|
198
|
+
correlation; they are rotated by chol(R_t) each step. Q0 and s2_0 are
|
|
199
|
+
the state for the first simulated period (Qbar and the unconditional
|
|
200
|
+
variances for an unconditional start, Q_{T+1} and sigma2_{T+1} to
|
|
201
|
+
simulate forward from the end of a sample).
|
|
202
|
+
"""
|
|
203
|
+
Ttot, n = shocks.shape
|
|
204
|
+
eps = np.empty((Ttot, n))
|
|
205
|
+
sig2 = np.empty((Ttot, n))
|
|
206
|
+
zs = np.empty((Ttot, n))
|
|
207
|
+
C = (1.0 - a - b) * Qbar - g * Nbar
|
|
208
|
+
Q = Q0.copy()
|
|
209
|
+
R = np.empty((n, n))
|
|
210
|
+
for t in range(Ttot):
|
|
211
|
+
if t > 0:
|
|
212
|
+
zp = zs[t - 1]
|
|
213
|
+
for i in range(n):
|
|
214
|
+
ni = zp[i] if zp[i] < 0.0 else 0.0
|
|
215
|
+
for j in range(i, n):
|
|
216
|
+
nj = zp[j] if zp[j] < 0.0 else 0.0
|
|
217
|
+
v = C[i, j] + a * zp[i] * zp[j] + g * ni * nj + b * Q[i, j]
|
|
218
|
+
Q[i, j] = v
|
|
219
|
+
Q[j, i] = v
|
|
220
|
+
ep = eps[t - 1]
|
|
221
|
+
for i in range(n):
|
|
222
|
+
e2 = ep[i] * ep[i]
|
|
223
|
+
s = omega[i] + alpha[i] * e2 + beta[i] * sig2[t - 1, i]
|
|
224
|
+
if ep[i] < 0.0:
|
|
225
|
+
s += gamma[i] * e2
|
|
226
|
+
sig2[t, i] = s
|
|
227
|
+
else:
|
|
228
|
+
sig2[0] = s2_0
|
|
229
|
+
_q_to_r(Q, R)
|
|
230
|
+
L = np.linalg.cholesky(R)
|
|
231
|
+
zt = L @ shocks[t]
|
|
232
|
+
zs[t] = zt
|
|
233
|
+
for i in range(n):
|
|
234
|
+
eps[t, i] = math.sqrt(sig2[t, i]) * zt[i]
|
|
235
|
+
return eps[burn:] + mu, sig2[burn:], zs[burn:]
|