WP46: registry/protocol configs, tests, runners, panel validity rules D-05/D-06
23 changed files +4,518 −2
added
configs/forecast_protocol.yaml
+31 −0
@@ -0,0 +1,31 @@ | ||
| 1 | +# Pre-registered forecasting protocol (frozen with docs/preregistered_protocol.md; git tag prereg-frozen). | |
| 2 | +units: {returns: "percent log return", variance: "percent^2"} | |
| 3 | +horizons: [1, 5, 22] # trading days; target = variance of the h-day cumulative return | |
| 4 | +estimation: | |
| 5 | + scheme: rolling # primary; 'expanding' and window 2000 are robustness variants | |
| 6 | + window: 1000 | |
| 7 | + refit_every: 5 # trading days; daily filtering with fixed parameters in between | |
| 8 | + max_restarts: 3 | |
| 9 | + seed: 46 | |
| 10 | + simulation_paths: 2000 # multi-step forecasts for models without an analytic recursion | |
| 11 | +periods: | |
| 12 | + validation: {start: "2012-01-03", end: "2015-12-31"} # used for design checks and failure accounting only | |
| 13 | + test: {start: "2016-01-04", end: "2026-10-30"} # untouched until the protocol is frozen | |
| 14 | +primary_loss: qlike | |
| 15 | +primary_proxy: rv5ss_on | |
| 16 | +secondary_losses: [mse, mae, mse_sd] | |
| 17 | +secondary_proxies: [rv5_on, rv1_on, bv5_on, medrv_on, r2, rv5ss_scaled] | |
| 18 | +tier_b_input: rv5ss_on | |
| 19 | +inference: | |
| 20 | + dm: {hac: "Newey-West Bartlett", bandwidth: "h - 1 + floor(4 (n/100)^(2/9))"} | |
| 21 | + mcs: {alpha: 0.10, bootstrap: stationary, block_length: 22, reps: 2000, statistic: "Tmax"} | |
| 22 | + spa: {reps: 2000, block_length: 22} | |
| 23 | + cross_asset: "block bootstrap over time with assets kept together (dependence across assets preserved)" | |
| 24 | +tail_risk: {levels: [0.01, 0.05], tests: [kupiec_uc, christoffersen_cc, engle_manganelli_dq, fz0_loss]} | |
| 25 | +regimes: | |
| 26 | + trailing_vol_terciles: "terciles of the 22-day trailing realized variance of the instrument, computed at the origin (no look-ahead), thresholds from the validation period" | |
| 27 | + calendar_episodes: | |
| 28 | + covid: {start: "2020-02-20", end: "2020-06-30"} | |
| 29 | + rate_shock_2022: {start: "2022-01-03", end: "2022-12-30"} | |
| 30 | + tariff_shock_2025: {start: "2025-04-02", end: "2025-05-30"} | |
| 31 | + return_sign: "sign of the previous day's return at the origin" | |
added
configs/model_registry.yaml
+55 −0
@@ -0,0 +1,55 @@ | ||
| 1 | +# WP46 model registry — the BINDING list of architectures and specifications (C1). One entry per architecture; | |
| 2 | +# specifications = architecture x innovation distribution x order (orders other than (1,1) only where listed). | |
| 3 | +# status: implemented | unsupported (no vetted implementation in the version-locked libraries; not scored) | extension. | |
| 4 | +# impl: arch (Sheppard, arch 8.0.0) | custom (src/wp46/models/custom.py, validated vs arch/rugarch) | ols | none. | |
| 5 | +# dists: innovation laws that are formally valid for the architecture AND available in the implementation. | |
| 6 | +# tier: A = daily returns only; B = daily returns + lagged realized measure; C = multivariate (configs/study.yaml). | |
| 7 | +version: 1 | |
| 8 | +frozen_with_protocol: true | |
| 9 | +families: | |
| 10 | + baseline: {label: "Baselines", color_key: 0} | |
| 11 | + classical: {label: "Classical ARCH/GARCH", color_key: 1} | |
| 12 | + asymmetric: {label: "Asymmetric", color_key: 2} | |
| 13 | + power: {label: "Power / absolute", color_key: 3} | |
| 14 | + fractional: {label: "Fractional persistence", color_key: 4} | |
| 15 | + component: {label: "Component", color_key: 5} | |
| 16 | + regime-switching: {label: "Regime switching", color_key: 6} | |
| 17 | + score-driven: {label: "Score-driven (non-GARCH)", color_key: 7} | |
| 18 | + realized: {label: "Realized-measure models (Tier B)", color_key: 8} | |
| 19 | +models: | |
| 20 | + constvar: {family: baseline, tier: A, impl: arch, status: implemented, dists: [normal], label: "Constant variance (window mean)", ref: null, nested_in: garch11} | |
| 21 | + histvar22: {family: baseline, tier: A, impl: ols, status: implemented, dists: [empirical], label: "Historical variance, 22 days", ref: null} | |
| 22 | + histvar66: {family: baseline, tier: A, impl: ols, status: implemented, dists: [empirical], label: "Historical variance, 66 days", ref: null} | |
| 23 | + histvar252: {family: baseline, tier: A, impl: ols, status: implemented, dists: [empirical], label: "Historical variance, 252 days", ref: null} | |
| 24 | + ewma094: {family: baseline, tier: A, impl: arch, status: implemented, dists: [normal], label: "EWMA (RiskMetrics 1994, lambda = 0.94)", ref: jpmorgan1996riskmetrics, nested_in: igarch11} | |
| 25 | + rm2006: {family: baseline, tier: A, impl: arch, status: implemented, dists: [normal], label: "RiskMetrics 2006", ref: zumbach2007riskmetrics} | |
| 26 | + arch5: {family: classical, tier: A, impl: arch, status: implemented, dists: [normal, t, ged, skewt], label: "ARCH(5)", ref: engle1982autoregressive} | |
| 27 | + garch11: {family: classical, tier: A, impl: arch, status: implemented, dists: [normal, t, ged, skewt], label: "GARCH(1,1)", ref: bollerslev1986generalized, benchmark: true} | |
| 28 | + garch21: {family: classical, tier: A, impl: arch, status: implemented, dists: [normal, t, ged, skewt], label: "GARCH(2,1)", ref: bollerslev1986generalized} | |
| 29 | + garch12: {family: classical, tier: A, impl: arch, status: implemented, dists: [normal, t, ged, skewt], label: "GARCH(1,2)", ref: bollerslev1986generalized} | |
| 30 | + igarch11: {family: classical, tier: A, impl: custom, status: implemented, dists: [normal, t, ged, skewt], label: "IGARCH(1,1)", ref: engle1986modelling} | |
| 31 | + garchm11: {family: classical, tier: A, impl: custom, status: implemented, dists: [normal, t, ged, skewt], label: "GARCH-M(1,1)", ref: engle1987estimating} | |
| 32 | + gjr11: {family: asymmetric, tier: A, impl: arch, status: implemented, dists: [normal, t, ged, skewt], label: "GJR-GARCH(1,1)", ref: glosten1993relation} | |
| 33 | + tarch11: {family: asymmetric, tier: A, impl: arch, status: implemented, dists: [normal, t, ged, skewt], label: "TGARCH(1,1) (Zakoian, threshold on sigma)", ref: zakoian1994threshold} | |
| 34 | + egarch11: {family: asymmetric, tier: A, impl: arch, status: implemented, dists: [normal, t, ged, skewt], label: "EGARCH(1,1)", ref: nelson1991conditional} | |
| 35 | + nagarch11: {family: asymmetric, tier: A, impl: custom, status: implemented, dists: [normal, t, ged, skewt], label: "NAGARCH(1,1) (Engle-Ng)", ref: engle1993measuring} | |
| 36 | + qgarch11: {family: asymmetric, tier: A, impl: custom, status: implemented, dists: [normal, t, ged, skewt], label: "QGARCH(1,1) (Sentana)", ref: sentana1995quadratic} | |
| 37 | + avgarch11: {family: power, tier: A, impl: arch, status: implemented, dists: [normal, t, ged, skewt], label: "AVGARCH(1,1) (Taylor-Schwert)", ref: taylor2008modelling} | |
| 38 | + aparch11: {family: power, tier: A, impl: arch, status: implemented, dists: [normal, t, ged, skewt], label: "APARCH(1,1)", ref: ding1993long} | |
| 39 | + figarch11: {family: fractional, tier: A, impl: arch, status: implemented, dists: [normal, t, ged, skewt], label: "FIGARCH(1,d,1)", ref: baillie1996fractionally, note: "arch truncates the ARCH(inf) representation at 1000 lags"} | |
| 40 | + fiegarch11: {family: fractional, tier: A, impl: none, status: unsupported, dists: [], label: "FIEGARCH", ref: bollerslev1996modeling, note: "no vetted implementation in arch 8.0.0 / rugarch 1.5-6; not scored"} | |
| 41 | + fiaparch11: {family: fractional, tier: A, impl: none, status: unsupported, dists: [], label: "FIAPARCH", ref: tse1998conditional, note: "no vetted implementation; not scored"} | |
| 42 | + hygarch11: {family: fractional, tier: A, impl: none, status: unsupported, dists: [], label: "HYGARCH", ref: davidson2004moment, note: "no vetted implementation; not scored"} | |
| 43 | + cgarch11: {family: component, tier: A, impl: custom, status: implemented, dists: [normal, t, ged, skewt], label: "Component GARCH (Engle-Lee)", ref: engle1999long} | |
| 44 | + msgarch2: {family: regime-switching, tier: A, impl: custom, status: implemented, dists: [normal, t], label: "MS-GARCH(2) (Haas-Mittnik-Paolella)", ref: haas2004new, note: "common nu across regimes; regimes ordered by unconditional variance"} | |
| 45 | + stgarch: {family: regime-switching, tier: A, impl: none, status: unsupported, dists: [], label: "Smooth-transition GARCH", ref: gonzalez1998smooth, note: "not implemented (no vetted library; deferred)"} | |
| 46 | + garch_midas: {family: component, tier: A, impl: none, status: unsupported, dists: [], label: "GARCH-MIDAS", ref: engle2013stock, note: "requires a low-frequency explanatory series outside the pre-registered information set; not scored"} | |
| 47 | + gas_t11: {family: score-driven, tier: A, impl: custom, status: implemented, dists: [t], label: "GAS-t(1,1) / Beta-t-GARCH (non-GARCH comparator)", ref: creal2013generalized} | |
| 48 | + sv: {family: score-driven, tier: A, impl: none, status: unsupported, dists: [], label: "Stochastic volatility", ref: kim1998stochastic, note: "simulation-based estimation not feasible at the rolling re-estimation cadence of this benchmark; not scored"} | |
| 49 | + rm_rw: {family: realized, tier: B, impl: ols, status: implemented, dists: [empirical], label: "Realized-measure random walk", ref: null} | |
| 50 | + har_rv: {family: realized, tier: B, impl: ols, status: implemented, dists: [empirical], label: "HAR-RV (direct, levels)", ref: corsi2009simple} | |
| 51 | + loghar_rv: {family: realized, tier: B, impl: ols, status: implemented, dists: [empirical], label: "log-HAR-RV (direct, smearing)", ref: corsi2009simple} | |
| 52 | + realgarch11: {family: realized, tier: B, impl: custom, status: implemented, dists: [normal, t], label: "Realized GARCH(1,1), log-linear", ref: hansen2012realized} | |
| 53 | + heavy11: {family: realized, tier: B, impl: custom, status: implemented, dists: [normal, t], label: "HEAVY(1,1)", ref: shephard2010realising} | |
| 54 | + garchx_rv11: {family: realized, tier: B, impl: custom, status: implemented, dists: [normal, t], label: "GARCH-X(1,1) with lagged realized measure", ref: engle2002new} | |
| 55 | + realegarch11: {family: realized, tier: B, impl: none, status: unsupported, dists: [], label: "Realized EGARCH", ref: hansen2016realized, note: "not implemented in this version (deferred)"} | |
added
configs/study.yaml
+11 −0
@@ -0,0 +1,11 @@ | ||
| 1 | +# Study-level settings (frozen with the protocol). | |
| 2 | +title: "Four Decades of Conditional Volatility Modeling: A Comprehensive Empirical Benchmark Across Assets, Frequencies, and Market Regimes" | |
| 3 | +wp_number: 46 | |
| 4 | +data_source: {name: "FinData OS (FirstRate Data)", base_url: "https://findata.spboucher.ai/api/v1", license: "frd-private"} | |
| 5 | +sample: {start: "2000-01-03", end: "2026-10-30"} | |
| 6 | +universe_file: configs/instruments.yaml | |
| 7 | +tier_c_baskets: | |
| 8 | + equity_etf: [SPY, QQQ, IWM, DIA] | |
| 9 | + cross_asset_etf: [SPY, TLT, GLD, USO, EEM] | |
| 10 | +tier_c_models: [rolling_cov252, ewma_cov094, ccc_garch11, dcc11, adcc11, scalar_bekk11] | |
| 11 | +tier_c_horizons: [1] | |
added
results/metrics/rugarch_crosscheck.json
+769 −0
@@ -0,0 +1,769 @@ | ||
| 1 | +{ | |
| 2 | + "rugarch": { | |
| 3 | + "rugarch_version": "1.5.6", | |
| 4 | + "garch11_t": { | |
| 5 | + "converged": true, | |
| 6 | + "loglik": -3379.50279602402, | |
| 7 | + "coef": { | |
| 8 | + "mu": 0.0272520855544271, | |
| 9 | + "omega": 0.0174152246241149, | |
| 10 | + "alpha1": 0.0766441622812045, | |
| 11 | + "beta1": 0.897925769946588, | |
| 12 | + "shape": 6.92334228660823 | |
| 13 | + }, | |
| 14 | + "sigma2_last5": [ | |
| 15 | + 0.331218279727791, | |
| 16 | + 0.332580388365619, | |
| 17 | + 0.386557106650745, | |
| 18 | + 0.41489230427875, | |
| 19 | + 0.395462742973217 | |
| 20 | + ], | |
| 21 | + "fc": [ | |
| 22 | + 0.373396880254564, | |
| 23 | + 0.381316596907874, | |
| 24 | + 0.389034914629954, | |
| 25 | + 0.396556955009274, | |
| 26 | + 0.403887709391963, | |
| 27 | + 0.411032042193878, | |
| 28 | + 0.417994694128454, | |
| 29 | + 0.424780285352459, | |
| 30 | + 0.431393318531763, | |
| 31 | + 0.437838181829138, | |
| 32 | + 0.444119151816078, | |
| 33 | + 0.450240396310574, | |
| 34 | + 0.456205977142725, | |
| 35 | + 0.462019852850014, | |
| 36 | + 0.467685881304048, | |
| 37 | + 0.473207822270496, | |
| 38 | + 0.478589339903933, | |
| 39 | + 0.483834005179235, | |
| 40 | + 0.488945298261143, | |
| 41 | + 0.493926610813575, | |
| 42 | + 0.498781248250204, | |
| 43 | + 0.503512431927809 | |
| 44 | + ] | |
| 45 | + }, | |
| 46 | + "gjr11_t": { | |
| 47 | + "converged": true, | |
| 48 | + "loglik": -3372.99049098085, | |
| 49 | + "coef": { | |
| 50 | + "mu": 0.0223490291478538, | |
| 51 | + "omega": 0.0174956405075909, | |
| 52 | + "alpha1": 0.0426372227127993, | |
| 53 | + "beta1": 0.900398468982572, | |
| 54 | + "gamma1": 0.0620237392157647, | |
| 55 | + "shape": 7.00993566707479 | |
| 56 | + }, | |
| 57 | + "sigma2_last5": [ | |
| 58 | + 0.332335215434199, | |
| 59 | + 0.340484527422025, | |
| 60 | + 0.419369166473932, | |
| 61 | + 0.463058113854574, | |
| 62 | + 0.441677271920104 | |
| 63 | + ], | |
| 64 | + "fc": [ | |
| 65 | + 0.416282527426611, | |
| 66 | + 0.422974621160647, | |
| 67 | + 0.429493038742298, | |
| 68 | + 0.435842287491262, | |
| 69 | + 0.442026757751298, | |
| 70 | + 0.448050725926038, | |
| 71 | + 0.453918357436013, | |
| 72 | + 0.45963370959893, | |
| 73 | + 0.465200734435209, | |
| 74 | + 0.470623281400702, | |
| 75 | + 0.475905100048492, | |
| 76 | + 0.481049842621618, | |
| 77 | + 0.486061066578505, | |
| 78 | + 0.490942237052855, | |
| 79 | + 0.495696729249701, | |
| 80 | + 0.500327830779275, | |
| 81 | + 0.504838743930304, | |
| 82 | + 0.509232587884315, | |
| 83 | + 0.513512400872467, | |
| 84 | + 0.51768114227641, | |
| 85 | + 0.521741694674624, | |
| 86 | + 0.525696865835649 | |
| 87 | + ] | |
| 88 | + }, | |
| 89 | + "egarch11_t": { | |
| 90 | + "converged": true, | |
| 91 | + "loglik": -3376.68334976327, | |
| 92 | + "coef": { | |
| 93 | + "mu": 0.0188171893640434, | |
| 94 | + "omega": -0.0170849884697369, | |
| 95 | + "alpha1": -0.0444147835573704, | |
| 96 | + "beta1": 0.968708686881274, | |
| 97 | + "gamma1": 0.156456196163122, | |
| 98 | + "shape": 6.99039734118426 | |
| 99 | + }, | |
| 100 | + "sigma2_last5": [ | |
| 101 | + 0.328885250801329, | |
| 102 | + 0.350822965161747, | |
| 103 | + 0.436863315480211, | |
| 104 | + 0.499447742972208, | |
| 105 | + 0.479693397387017 | |
| 106 | + ], | |
| 107 | + "fc": [ | |
| 108 | + 0.440981758288812, | |
| 109 | + 0.444761569483677, | |
| 110 | + 0.448453995963031, | |
| 111 | + 0.452060109871317, | |
| 112 | + 0.455581032094569, | |
| 113 | + 0.45901792729419, | |
| 114 | + 0.462371999182192, | |
| 115 | + 0.4656444860341, | |
| 116 | + 0.468836656435033, | |
| 117 | + 0.471949805253863, | |
| 118 | + 0.474985249839914, | |
| 119 | + 0.477944326436176, | |
| 120 | + 0.480828386802756, | |
| 121 | + 0.483638795043929, | |
| 122 | + 0.486376924632017, | |
| 123 | + 0.489044155621135, | |
| 124 | + 0.491641872043739, | |
| 125 | + 0.494171459482879, | |
| 126 | + 0.49663430281301, | |
| 127 | + 0.499031784102273, | |
| 128 | + 0.501365280669172, | |
| 129 | + 0.503636163286671 | |
| 130 | + ] | |
| 131 | + }, | |
| 132 | + "aparch11_t": { | |
| 133 | + "converged": true, | |
| 134 | + "loglik": -3372.84892807275, | |
| 135 | + "coef": { | |
| 136 | + "mu": 0.0219498032140538, | |
| 137 | + "omega": 0.0182869465555667, | |
| 138 | + "alpha1": 0.0734928958434684, | |
| 139 | + "beta1": 0.901720469927853, | |
| 140 | + "gamma1": 0.230203192227028, | |
| 141 | + "delta": 1.84060080476714, | |
| 142 | + "shape": 7.0279389234871 | |
| 143 | + }, | |
| 144 | + "sigma2_last5": [ | |
| 145 | + 0.332682598703389, | |
| 146 | + 0.342687583247142, | |
| 147 | + 0.423457812590537, | |
| 148 | + 0.47031035284744, | |
| 149 | + 0.448241954551476 | |
| 150 | + ], | |
| 151 | + "fc": [ | |
| 152 | + 0.420729778911146, | |
| 153 | + 0.426946162214478, | |
| 154 | + 0.433001778704925, | |
| 155 | + 0.438900519070086, | |
| 156 | + 0.444646200595584, | |
| 157 | + 0.450242566618973, | |
| 158 | + 0.455693286232804, | |
| 159 | + 0.461001954204718, | |
| 160 | + 0.46617209108646, | |
| 161 | + 0.471207143487254, | |
| 162 | + 0.476110484489945, | |
| 163 | + 0.48088541419098, | |
| 164 | + 0.485535160347515, | |
| 165 | + 0.490062879116947, | |
| 166 | + 0.494471655875874, | |
| 167 | + 0.498764506106986, | |
| 168 | + 0.502944376343734, | |
| 169 | + 0.507014145163756, | |
| 170 | + 0.510976624223076, | |
| 171 | + 0.514834559323999, | |
| 172 | + 0.518590631510407, | |
| 173 | + 0.522247458184874 | |
| 174 | + ] | |
| 175 | + }, | |
| 176 | + "figarch11_t": { | |
| 177 | + "converged": true, | |
| 178 | + "loglik": -3381.97980336414, | |
| 179 | + "coef": { | |
| 180 | + "mu": 0.028208711165193, | |
| 181 | + "omega": 0.0134114399389171, | |
| 182 | + "alpha1": 0.26282947219305, | |
| 183 | + "beta1": 0.780139313197165, | |
| 184 | + "delta": 0.631140189523463, | |
| 185 | + "shape": 6.41803885552558 | |
| 186 | + }, | |
| 187 | + "sigma2_last5": [ | |
| 188 | + 0.299696988917735, | |
| 189 | + 0.316330648317056, | |
| 190 | + 0.390432077409812, | |
| 191 | + 0.384885199689091, | |
| 192 | + 0.343022844513784 | |
| 193 | + ], | |
| 194 | + "fc": [ | |
| 195 | + 0.339059269752477, | |
| 196 | + 0.367590095570308, | |
| 197 | + 0.368843743107776, | |
| 198 | + 0.370160945435298, | |
| 199 | + 0.373457632868578, | |
| 200 | + 0.377938383945126, | |
| 201 | + 0.382963124329593, | |
| 202 | + 0.388198567929454, | |
| 203 | + 0.393484081477849, | |
| 204 | + 0.398744620082659, | |
| 205 | + 0.403945640040183, | |
| 206 | + 0.409071431842686, | |
| 207 | + 0.414115967772256, | |
| 208 | + 0.419078001693844, | |
| 209 | + 0.423958280305449, | |
| 210 | + 0.42875861007803, | |
| 211 | + 0.433481497208184, | |
| 212 | + 0.438129181396169, | |
| 213 | + 0.442704763673238, | |
| 214 | + 0.447212036971553, | |
| 215 | + 0.451653257516078, | |
| 216 | + 0.456031281967143 | |
| 217 | + ] | |
| 218 | + }, | |
| 219 | + "igarch11_t": { | |
| 220 | + "converged": true, | |
| 221 | + "loglik": -3385.51714991758, | |
| 222 | + "coef": { | |
| 223 | + "mu": 0.0273555996213812, | |
| 224 | + "omega": 0.00822664842815519, | |
| 225 | + "alpha1": 0.0911285633973949, | |
| 226 | + "beta1": 0.908871436602605, | |
| 227 | + "shape": 5.98132454596413 | |
| 228 | + }, | |
| 229 | + "sigma2_last5": [ | |
| 230 | + 0.301577365761645, | |
| 231 | + 0.303442040727677, | |
| 232 | + 0.367868964109828, | |
| 233 | + 0.402485492370637, | |
| 234 | + 0.380584653228751 | |
| 235 | + ], | |
| 236 | + "fc": [ | |
| 237 | + 0.355184003263419, | |
| 238 | + 0.363410651691574, | |
| 239 | + 0.371637300119729, | |
| 240 | + 0.379863948547884, | |
| 241 | + 0.388090596976039, | |
| 242 | + 0.396317245404194, | |
| 243 | + 0.40454389383235, | |
| 244 | + 0.412770542260505, | |
| 245 | + 0.42099719068866, | |
| 246 | + 0.429223839116815, | |
| 247 | + 0.43745048754497, | |
| 248 | + 0.445677135973126, | |
| 249 | + 0.453903784401281, | |
| 250 | + 0.462130432829436, | |
| 251 | + 0.470357081257591, | |
| 252 | + 0.478583729685746, | |
| 253 | + 0.486810378113901, | |
| 254 | + 0.495037026542057, | |
| 255 | + 0.503263674970212, | |
| 256 | + 0.511490323398367, | |
| 257 | + 0.519716971826522, | |
| 258 | + 0.527943620254678 | |
| 259 | + ] | |
| 260 | + }, | |
| 261 | + "cgarch11_t": { | |
| 262 | + "converged": true, | |
| 263 | + "loglik": -3379.76186721388, | |
| 264 | + "coef": { | |
| 265 | + "mu": 0.0273402099302754, | |
| 266 | + "omega": 0.0178411646698263, | |
| 267 | + "alpha1": 0.0472240690102781, | |
| 268 | + "beta1": 0.926336229155417, | |
| 269 | + "eta11": 0.973560849178106, | |
| 270 | + "eta21": 0.0299126275164861, | |
| 271 | + "shape": 6.89529147023611 | |
| 272 | + }, | |
| 273 | + "sigma2_last5": [ | |
| 274 | + 0.331806400268661, | |
| 275 | + 0.333156933397685, | |
| 276 | + 0.387466733404178, | |
| 277 | + 0.415888052702624, | |
| 278 | + 0.3961973946619 | |
| 279 | + ], | |
| 280 | + "fc": [ | |
| 281 | + 0.373894792219397, | |
| 282 | + 0.410734909756006, | |
| 283 | + 0.417716681277378, | |
| 284 | + 0.424513858333285, | |
| 285 | + 0.431131321506913, | |
| 286 | + 0.437573822341324, | |
| 287 | + 0.443845986751214, | |
| 288 | + 0.449952318344464, | |
| 289 | + 0.455897201655865, | |
| 290 | + 0.461684905295354, | |
| 291 | + 0.467319585013005, | |
| 292 | + 0.472805286682989, | |
| 293 | + 0.478145949208632, | |
| 294 | + 0.483345407350674, | |
| 295 | + 0.488407394480744, | |
| 296 | + 0.493335545262035, | |
| 297 | + 0.498133398259106, | |
| 298 | + 0.50280439847868, | |
| 299 | + 0.507351899843266, | |
| 300 | + 0.511779167599379, | |
| 301 | + 0.516089380662085, | |
| 302 | + 0.520285633897565 | |
| 303 | + ] | |
| 304 | + }, | |
| 305 | + "nagarch11_t": { | |
| 306 | + "converged": true, | |
| 307 | + "loglik": -3373.35050140171, | |
| 308 | + "coef": { | |
| 309 | + "mu": 0.0213271608385587, | |
| 310 | + "omega": 0.0169277508131017, | |
| 311 | + "alpha1": 0.0706508464116506, | |
| 312 | + "beta1": 0.895992701235594, | |
| 313 | + "eta21": 0.339825862746243, | |
| 314 | + "shape": 7.06871565857494 | |
| 315 | + }, | |
| 316 | + "sigma2_last5": [ | |
| 317 | + 0.320085472046233, | |
| 318 | + 0.335215163198902, | |
| 319 | + 0.410709126156738, | |
| 320 | + 0.458799430333668, | |
| 321 | + 0.445128729120879 | |
| 322 | + ], | |
| 323 | + "fc": [ | |
| 324 | + 0.423373945827428, | |
| 325 | + 0.429633698461851, | |
| 326 | + 0.435735720488707, | |
| 327 | + 0.441683986337342, | |
| 328 | + 0.447482370291107, | |
| 329 | + 0.453134649010792, | |
| 330 | + 0.458644503994484, | |
| 331 | + 0.464015523975432, | |
| 332 | + 0.469251207259498, | |
| 333 | + 0.474354964003706, | |
| 334 | + 0.479330118437384, | |
| 335 | + 0.484179911027331, | |
| 336 | + 0.488907500588426, | |
| 337 | + 0.493515966341068, | |
| 338 | + 0.498008309916754, | |
| 339 | + 0.502387457313138, | |
| 340 | + 0.50665626079982, | |
| 341 | + 0.510817500776111, | |
| 342 | + 0.514873887581995, | |
| 343 | + 0.518828063263449, | |
| 344 | + 0.522682603293291, | |
| 345 | + 0.526440018248661 | |
| 346 | + ] | |
| 347 | + }, | |
| 348 | + "tgarch11_t": { | |
| 349 | + "converged": true, | |
| 350 | + "loglik": -3378.26565819207, | |
| 351 | + "coef": { | |
| 352 | + "mu": 0.0185075851662991, | |
| 353 | + "omega": 0.0237242658452512, | |
| 354 | + "alpha1": 0.0806721422971265, | |
| 355 | + "beta1": 0.908415590277261, | |
| 356 | + "eta11": 0.278262852619424, | |
| 357 | + "shape": 7.02369203281082 | |
| 358 | + }, | |
| 359 | + "sigma2_last5": [ | |
| 360 | + 0.339303507806645, | |
| 361 | + 0.361929596403121, | |
| 362 | + 0.446541788728846, | |
| 363 | + 0.509029924970293, | |
| 364 | + 0.488014547239253 | |
| 365 | + ], | |
| 366 | + "fc": [ | |
| 367 | + 0.446902528383541, | |
| 368 | + 0.451531432310373, | |
| 369 | + 0.456042749975163, | |
| 370 | + 0.460438686643197, | |
| 371 | + 0.464721461954639, | |
| 372 | + 0.468893304636195, | |
| 373 | + 0.472956447662954, | |
| 374 | + 0.476913123839437, | |
| 375 | + 0.480765561770887, | |
| 376 | + 0.484515982197637, | |
| 377 | + 0.488166594667179, | |
| 378 | + 0.49171959452015, | |
| 379 | + 0.495177160168002, | |
| 380 | + 0.498541450641537, | |
| 381 | + 0.501814603390865, | |
| 382 | + 0.504998732318567, | |
| 383 | + 0.508095926029051, | |
| 384 | + 0.511108246278208, | |
| 385 | + 0.514037726608485, | |
| 386 | + 0.516886371155493, | |
| 387 | + 0.519656153613176, | |
| 388 | + 0.522349016345428 | |
| 389 | + ] | |
| 390 | + }, | |
| 391 | + "realgarch11_n": { | |
| 392 | + "converged": false | |
| 393 | + }, | |
| 394 | + "garch11_skewt": { | |
| 395 | + "converged": true, | |
| 396 | + "loglik": -3379.31479592696, | |
| 397 | + "coef": { | |
| 398 | + "mu": 0.0247602319116539, | |
| 399 | + "omega": 0.017295740459104, | |
| 400 | + "alpha1": 0.0762213395583014, | |
| 401 | + "beta1": 0.89844516796877, | |
| 402 | + "skew": 0.984514907132941, | |
| 403 | + "shape": 6.94374052154004 | |
| 404 | + }, | |
| 405 | + "sigma2_last5": [ | |
| 406 | + 0.330905238634026, | |
| 407 | + 0.332071373500861, | |
| 408 | + 0.385400191269274, | |
| 409 | + 0.413348758343739, | |
| 410 | + 0.394040260337795 | |
| 411 | + ], | |
| 412 | + "fc": [ | |
| 413 | + 0.372159534757372, | |
| 414 | + 0.380027174443972, | |
| 415 | + 0.387695499339791, | |
| 416 | + 0.395169558784582, | |
| 417 | + 0.402454274200687, | |
| 418 | + 0.40955444233363, | |
| 419 | + 0.41647473841062, | |
| 420 | + 0.423219719219034, | |
| 421 | + 0.429793826106908, | |
| 422 | + 0.436201387907421, | |
| 423 | + 0.442446623789292, | |
| 424 | + 0.448533646034957, | |
| 425 | + 0.454466462748379, | |
| 426 | + 0.460248980494249, | |
| 427 | + 0.465885006870329, | |
| 428 | + 0.471378253014633, | |
| 429 | + 0.476732336049089, | |
| 430 | + 0.481950781461291, | |
| 431 | + 0.487037025425924, | |
| 432 | + 0.491994417067363, | |
| 433 | + 0.496826220664968, | |
| 434 | + 0.501535617802502 | |
| 435 | + ] | |
| 436 | + } | |
| 437 | + }, | |
| 438 | + "ours": { | |
| 439 | + "garch11_t": { | |
| 440 | + "loglik": -3379.461796995996, | |
| 441 | + "params": { | |
| 442 | + "mu": 0.0272552165475274, | |
| 443 | + "omega": 0.017418392193745573, | |
| 444 | + "alpha[1]": 0.0766713206199158, | |
| 445 | + "beta[1]": 0.8979047644168446, | |
| 446 | + "nu": 6.915683563286161 | |
| 447 | + }, | |
| 448 | + "converged": true | |
| 449 | + }, | |
| 450 | + "gjr11_t": { | |
| 451 | + "loglik": -3372.953632113916, | |
| 452 | + "params": { | |
| 453 | + "mu": 0.02234711621886731, | |
| 454 | + "omega": 0.017500497241068884, | |
| 455 | + "alpha[1]": 0.04266800772043423, | |
| 456 | + "gamma[1]": 0.062034959552290915, | |
| 457 | + "beta[1]": 0.9003610818426231, | |
| 458 | + "nu": 7.002485146679242 | |
| 459 | + }, | |
| 460 | + "converged": true | |
| 461 | + }, | |
| 462 | + "egarch11_t": { | |
| 463 | + "loglik": -3376.6704308851477, | |
| 464 | + "params": { | |
| 465 | + "mu": 0.018828915964200128, | |
| 466 | + "omega": -0.011034952859929866, | |
| 467 | + "alpha[1]": 0.15651669565747273, | |
| 468 | + "gamma[1]": -0.044438618764715755, | |
| 469 | + "beta[1]": 0.9686789065580167, | |
| 470 | + "nu": 6.98363424230133 | |
| 471 | + }, | |
| 472 | + "converged": true | |
| 473 | + }, | |
| 474 | + "aparch11_t": { | |
| 475 | + "loglik": -3372.7593223523118, | |
| 476 | + "params": { | |
| 477 | + "mu": 0.021873566385943646, | |
| 478 | + "omega": 0.01845096728792146, | |
| 479 | + "alpha[1]": 0.0741123489743683, | |
| 480 | + "gamma[1]": 0.23185125233775555, | |
| 481 | + "beta[1]": 0.9019093525442575, | |
| 482 | + "delta": 1.810851742298129, | |
| 483 | + "nu": 7.017009293700917 | |
| 484 | + }, | |
| 485 | + "converged": true | |
| 486 | + }, | |
| 487 | + "figarch11_t": { | |
| 488 | + "loglik": -3383.4506039107964, | |
| 489 | + "params": { | |
| 490 | + "mu": 0.027341455003779366, | |
| 491 | + "omega": 0.018557454873431037, | |
| 492 | + "phi": 0.2272419820409831, | |
| 493 | + "d": 0.545516035918098, | |
| 494 | + "beta": 0.6927227001090212, | |
| 495 | + "nu": 6.4546568465574365 | |
| 496 | + }, | |
| 497 | + "converged": true | |
| 498 | + }, | |
| 499 | + "garch11_skewt": { | |
| 500 | + "loglik": -3379.275642878361, | |
| 501 | + "params": { | |
| 502 | + "mu": 0.0247698562671931, | |
| 503 | + "omega": 0.017301334377567416, | |
| 504 | + "alpha[1]": 0.07625585117671085, | |
| 505 | + "beta[1]": 0.8984117763911253, | |
| 506 | + "eta": 6.936304710964621, | |
| 507 | + "lambda": -0.01552668380206145 | |
| 508 | + }, | |
| 509 | + "converged": true | |
| 510 | + }, | |
| 511 | + "igarch11_t": { | |
| 512 | + "loglik": -3385.466983993589, | |
| 513 | + "params": { | |
| 514 | + "mu": 0.027354323480404286, | |
| 515 | + "omega": 0.008236861969721824, | |
| 516 | + "alpha": 0.09116543468488557, | |
| 517 | + "nu": 5.973513461861548 | |
| 518 | + }, | |
| 519 | + "converged": true | |
| 520 | + }, | |
| 521 | + "cgarch11_t": { | |
| 522 | + "loglik": -3379.461678802403, | |
| 523 | + "params": { | |
| 524 | + "mu": 0.027257000911121945, | |
| 525 | + "omega": 0.01736733788836256, | |
| 526 | + "rho": 0.9746416050735823, | |
| 527 | + "phi": 0.03393172704779641, | |
| 528 | + "alpha": 0.04272735073115051, | |
| 529 | + "beta": 0.9317694810875947, | |
| 530 | + "nu": 6.9169232979627395 | |
| 531 | + }, | |
| 532 | + "converged": true | |
| 533 | + }, | |
| 534 | + "nagarch11_t": { | |
| 535 | + "loglik": -3373.3123413949697, | |
| 536 | + "params": { | |
| 537 | + "mu": 0.02133256452316757, | |
| 538 | + "omega": 0.016934592629425642, | |
| 539 | + "alpha": 0.07068774289404209, | |
| 540 | + "theta": 0.3397290537185268, | |
| 541 | + "beta": 0.8959513510919638, | |
| 542 | + "nu": 7.06163608387285 | |
| 543 | + }, | |
| 544 | + "converged": true | |
| 545 | + }, | |
| 546 | + "realgarch11_n": { | |
| 547 | + "loglik": -4989.212643221531, | |
| 548 | + "params": { | |
| 549 | + "mu": 0.01677936657297113, | |
| 550 | + "omega": -0.0310098669169496, | |
| 551 | + "beta": 0.7043221645737289, | |
| 552 | + "gamma": 0.2567538036768552, | |
| 553 | + "xi": 0.04191198028547544, | |
| 554 | + "phi": 1.0024048779109547, | |
| 555 | + "tau1": 0.0010267719765093414, | |
| 556 | + "tau2": 0.013224579404035812, | |
| 557 | + "log_sigma_u": -0.9137863869883917 | |
| 558 | + }, | |
| 559 | + "converged": true | |
| 560 | + } | |
| 561 | + }, | |
| 562 | + "comparison": { | |
| 563 | + "garch11_t": { | |
| 564 | + "rugarch_loglik": -3379.50279602402, | |
| 565 | + "ours_loglik": -3379.461796995996, | |
| 566 | + "rugarch_coef": { | |
| 567 | + "mu": 0.0272520855544271, | |
| 568 | + "omega": 0.0174152246241149, | |
| 569 | + "alpha1": 0.0766441622812045, | |
| 570 | + "beta1": 0.897925769946588, | |
| 571 | + "shape": 6.92334228660823 | |
| 572 | + }, | |
| 573 | + "ours_params": { | |
| 574 | + "mu": 0.0272552165475274, | |
| 575 | + "omega": 0.017418392193745573, | |
| 576 | + "alpha[1]": 0.0766713206199158, | |
| 577 | + "beta[1]": 0.8979047644168446, | |
| 578 | + "nu": 6.915683563286161 | |
| 579 | + }, | |
| 580 | + "diff": 0.04099902802408906 | |
| 581 | + }, | |
| 582 | + "gjr11_t": { | |
| 583 | + "rugarch_loglik": -3372.99049098085, | |
| 584 | + "ours_loglik": -3372.953632113916, | |
| 585 | + "rugarch_coef": { | |
| 586 | + "mu": 0.0223490291478538, | |
| 587 | + "omega": 0.0174956405075909, | |
| 588 | + "alpha1": 0.0426372227127993, | |
| 589 | + "beta1": 0.900398468982572, | |
| 590 | + "gamma1": 0.0620237392157647, | |
| 591 | + "shape": 7.00993566707479 | |
| 592 | + }, | |
| 593 | + "ours_params": { | |
| 594 | + "mu": 0.02234711621886731, | |
| 595 | + "omega": 0.017500497241068884, | |
| 596 | + "alpha[1]": 0.04266800772043423, | |
| 597 | + "gamma[1]": 0.062034959552290915, | |
| 598 | + "beta[1]": 0.9003610818426231, | |
| 599 | + "nu": 7.002485146679242 | |
| 600 | + }, | |
| 601 | + "diff": 0.036858866934380785 | |
| 602 | + }, | |
| 603 | + "egarch11_t": { | |
| 604 | + "rugarch_loglik": -3376.68334976327, | |
| 605 | + "ours_loglik": -3376.6704308851477, | |
| 606 | + "rugarch_coef": { | |
| 607 | + "mu": 0.0188171893640434, | |
| 608 | + "omega": -0.0170849884697369, | |
| 609 | + "alpha1": -0.0444147835573704, | |
| 610 | + "beta1": 0.968708686881274, | |
| 611 | + "gamma1": 0.156456196163122, | |
| 612 | + "shape": 6.99039734118426 | |
| 613 | + }, | |
| 614 | + "ours_params": { | |
| 615 | + "mu": 0.018828915964200128, | |
| 616 | + "omega": -0.011034952859929866, | |
| 617 | + "alpha[1]": 0.15651669565747273, | |
| 618 | + "gamma[1]": -0.044438618764715755, | |
| 619 | + "beta[1]": 0.9686789065580167, | |
| 620 | + "nu": 6.98363424230133 | |
| 621 | + }, | |
| 622 | + "diff": 0.01291887812249115 | |
| 623 | + }, | |
| 624 | + "aparch11_t": { | |
| 625 | + "rugarch_loglik": -3372.84892807275, | |
| 626 | + "ours_loglik": -3372.7593223523118, | |
| 627 | + "rugarch_coef": { | |
| 628 | + "mu": 0.0219498032140538, | |
| 629 | + "omega": 0.0182869465555667, | |
| 630 | + "alpha1": 0.0734928958434684, | |
| 631 | + "beta1": 0.901720469927853, | |
| 632 | + "gamma1": 0.230203192227028, | |
| 633 | + "delta": 1.84060080476714, | |
| 634 | + "shape": 7.0279389234871 | |
| 635 | + }, | |
| 636 | + "ours_params": { | |
| 637 | + "mu": 0.021873566385943646, | |
| 638 | + "omega": 0.01845096728792146, | |
| 639 | + "alpha[1]": 0.0741123489743683, | |
| 640 | + "gamma[1]": 0.23185125233775555, | |
| 641 | + "beta[1]": 0.9019093525442575, | |
| 642 | + "delta": 1.810851742298129, | |
| 643 | + "nu": 7.017009293700917 | |
| 644 | + }, | |
| 645 | + "diff": 0.08960572043815773 | |
| 646 | + }, | |
| 647 | + "figarch11_t": { | |
| 648 | + "rugarch_loglik": -3381.97980336414, | |
| 649 | + "ours_loglik": -3383.4506039107964, | |
| 650 | + "rugarch_coef": { | |
| 651 | + "mu": 0.028208711165193, | |
| 652 | + "omega": 0.0134114399389171, | |
| 653 | + "alpha1": 0.26282947219305, | |
| 654 | + "beta1": 0.780139313197165, | |
| 655 | + "delta": 0.631140189523463, | |
| 656 | + "shape": 6.41803885552558 | |
| 657 | + }, | |
| 658 | + "ours_params": { | |
| 659 | + "mu": 0.027341455003779366, | |
| 660 | + "omega": 0.018557454873431037, | |
| 661 | + "phi": 0.2272419820409831, | |
| 662 | + "d": 0.545516035918098, | |
| 663 | + "beta": 0.6927227001090212, | |
| 664 | + "nu": 6.4546568465574365 | |
| 665 | + }, | |
| 666 | + "diff": -1.4708005466563918 | |
| 667 | + }, | |
| 668 | + "igarch11_t": { | |
| 669 | + "rugarch_loglik": -3385.51714991758, | |
| 670 | + "ours_loglik": -3385.466983993589, | |
| 671 | + "rugarch_coef": { | |
| 672 | + "mu": 0.0273555996213812, | |
| 673 | + "omega": 0.00822664842815519, | |
| 674 | + "alpha1": 0.0911285633973949, | |
| 675 | + "beta1": 0.908871436602605, | |
| 676 | + "shape": 5.98132454596413 | |
| 677 | + }, | |
| 678 | + "ours_params": { | |
| 679 | + "mu": 0.027354323480404286, | |
| 680 | + "omega": 0.008236861969721824, | |
| 681 | + "alpha": 0.09116543468488557, | |
| 682 | + "nu": 5.973513461861548 | |
| 683 | + }, | |
| 684 | + "diff": 0.05016592399078945 | |
| 685 | + }, | |
| 686 | + "cgarch11_t": { | |
| 687 | + "rugarch_loglik": -3379.76186721388, | |
| 688 | + "ours_loglik": -3379.461678802403, | |
| 689 | + "rugarch_coef": { | |
| 690 | + "mu": 0.0273402099302754, | |
| 691 | + "omega": 0.0178411646698263, | |
| 692 | + "alpha1": 0.0472240690102781, | |
| 693 | + "beta1": 0.926336229155417, | |
| 694 | + "eta11": 0.973560849178106, | |
| 695 | + "eta21": 0.0299126275164861, | |
| 696 | + "shape": 6.89529147023611 | |
| 697 | + }, | |
| 698 | + "ours_params": { | |
| 699 | + "mu": 0.027257000911121945, | |
| 700 | + "omega": 0.01736733788836256, | |
| 701 | + "rho": 0.9746416050735823, | |
| 702 | + "phi": 0.03393172704779641, | |
| 703 | + "alpha": 0.04272735073115051, | |
| 704 | + "beta": 0.9317694810875947, | |
| 705 | + "nu": 6.9169232979627395 | |
| 706 | + }, | |
| 707 | + "diff": 0.3001884114769382 | |
| 708 | + }, | |
| 709 | + "nagarch11_t": { | |
| 710 | + "rugarch_loglik": -3373.35050140171, | |
| 711 | + "ours_loglik": -3373.3123413949697, | |
| 712 | + "rugarch_coef": { | |
| 713 | + "mu": 0.0213271608385587, | |
| 714 | + "omega": 0.0169277508131017, | |
| 715 | + "alpha1": 0.0706508464116506, | |
| 716 | + "beta1": 0.895992701235594, | |
| 717 | + "eta21": 0.339825862746243, | |
| 718 | + "shape": 7.06871565857494 | |
| 719 | + }, | |
| 720 | + "ours_params": { | |
| 721 | + "mu": 0.02133256452316757, | |
| 722 | + "omega": 0.016934592629425642, | |
| 723 | + "alpha": 0.07068774289404209, | |
| 724 | + "theta": 0.3397290537185268, | |
| 725 | + "beta": 0.8959513510919638, | |
| 726 | + "nu": 7.06163608387285 | |
| 727 | + }, | |
| 728 | + "diff": 0.038160006740326935 | |
| 729 | + }, | |
| 730 | + "garch11_skewt": { | |
| 731 | + "rugarch_loglik": -3379.31479592696, | |
| 732 | + "ours_loglik": -3379.275642878361, | |
| 733 | + "rugarch_coef": { | |
| 734 | + "mu": 0.0247602319116539, | |
| 735 | + "omega": 0.017295740459104, | |
| 736 | + "alpha1": 0.0762213395583014, | |
| 737 | + "beta1": 0.89844516796877, | |
| 738 | + "skew": 0.984514907132941, | |
| 739 | + "shape": 6.94374052154004 | |
| 740 | + }, | |
| 741 | + "ours_params": { | |
| 742 | + "mu": 0.0247698562671931, | |
| 743 | + "omega": 0.017301334377567416, | |
| 744 | + "alpha[1]": 0.07625585117671085, | |
| 745 | + "beta[1]": 0.8984117763911253, | |
| 746 | + "eta": 6.936304710964621, | |
| 747 | + "lambda": -0.01552668380206145 | |
| 748 | + }, | |
| 749 | + "diff": 0.0391530485990188 | |
| 750 | + }, | |
| 751 | + "realgarch11_n": { | |
| 752 | + "rugarch_loglik": null, | |
| 753 | + "ours_loglik": -4989.212643221531, | |
| 754 | + "rugarch_coef": null, | |
| 755 | + "ours_params": { | |
| 756 | + "mu": 0.01677936657297113, | |
| 757 | + "omega": -0.0310098669169496, | |
| 758 | + "beta": 0.7043221645737289, | |
| 759 | + "gamma": 0.2567538036768552, | |
| 760 | + "xi": 0.04191198028547544, | |
| 761 | + "phi": 1.0024048779109547, | |
| 762 | + "tau1": 0.0010267719765093414, | |
| 763 | + "tau2": 0.013224579404035812, | |
| 764 | + "log_sigma_u": -0.9137863869883917 | |
| 765 | + }, | |
| 766 | + "diff": null | |
| 767 | + } | |
| 768 | + } | |
| 769 | +} | |
| \ No newline at end of file | ||
modified
scripts/audit_findata.py
+0 −0
modified
scripts/build_panels.py
+0 −0
modified
scripts/build_references.py
+0 −0
added
scripts/dispatch_nodes.py
+70 −0
@@ -0,0 +1,70 @@ | ||
| 1 | +#!/usr/bin/env python3 | |
| 2 | +"""Dispatch a job list over the MacLustr nodes (core-proportional shards) and collect results. | |
| 3 | +usage: dispatch_nodes.py launch --jobs results/manifests/jobs_main.json --name main [--nodes M3U96a:26,M3U96b:26,...] | |
| 4 | + dispatch_nodes.py status --name main | |
| 5 | + dispatch_nodes.py collect --name main # rsync <node>:~/wp46-cache/forecasts_<name>/ -> results/forecasts/<name>/ | |
| 6 | +Panels (daily returns + realized measures) must exist on every node under ~/wp46-cache/panels (synced here).""" | |
| 7 | +from __future__ import annotations | |
| 8 | + | |
| 9 | +import argparse | |
| 10 | +import json | |
| 11 | +import subprocess | |
| 12 | +from pathlib import Path | |
| 13 | + | |
| 14 | +ROOT = Path(__file__).resolve().parents[1] | |
| 15 | +DEFAULT_NODES = "M3U96a:24,M3U96b:26,M2U64:22,M5M36:16,M4M64a:14,M4M64c:14" | |
| 16 | + | |
| 17 | + | |
| 18 | +def sh(cmd: list[str], timeout=600) -> str: | |
| 19 | + return subprocess.run(cmd, capture_output=True, text=True, timeout=timeout).stdout | |
| 20 | + | |
| 21 | + | |
| 22 | +def main() -> None: | |
| 23 | + ap = argparse.ArgumentParser() | |
| 24 | + ap.add_argument("cmd", choices=["launch", "status", "collect", "sync-panels"]) | |
| 25 | + ap.add_argument("--jobs", default="") | |
| 26 | + ap.add_argument("--name", required=True) | |
| 27 | + ap.add_argument("--nodes", default=DEFAULT_NODES) | |
| 28 | + a = ap.parse_args() | |
| 29 | + nodes = [(n.split(":")[0], int(n.split(":")[1])) for n in a.nodes.split(",")] | |
| 30 | + if a.cmd == "sync-panels": | |
| 31 | + for n, _ in nodes: | |
| 32 | + print(n, sh(["rsync", "-aq", "--delete", str(Path.home() / "wp46-cache" / "panels") + "/", f"{n}:~/wp46-cache/panels/"])) | |
| 33 | + return | |
| 34 | + if a.cmd == "launch": | |
| 35 | + jobs = json.loads(Path(a.jobs).read_text()) | |
| 36 | + tot = sum(c for _, c in nodes) | |
| 37 | + # proportional contiguous shards over the (heavy-first) job order -> interleave: node k takes jobs with | |
| 38 | + # index % N in its share; simpler and balanced: assign job i to node by weighted round robin | |
| 39 | + assign = {n: [] for n, _ in nodes} | |
| 40 | + weights = [c / tot for _, c in nodes] | |
| 41 | + acc = [0.0] * len(nodes) | |
| 42 | + for i, j in enumerate(jobs): | |
| 43 | + k = max(range(len(nodes)), key=lambda q: weights[q] * (i + 1) - acc[q]) | |
| 44 | + acc[k] += 1 | |
| 45 | + assign[nodes[k][0]].append(j) | |
| 46 | + for n, cores in nodes: | |
| 47 | + jf = ROOT / "results" / "manifests" / f"jobs_{a.name}_{n}.json" | |
| 48 | + jf.write_text(json.dumps(assign[n])) | |
| 49 | + subprocess.run(["scp", "-q", str(jf), f"{n}:~/wp46_uqo/results/manifests/"], check=True) | |
| 50 | + print(n, cores, len(assign[n]), sh(["ssh", n, f"bash ~/wp46_uqo/scripts/node_run.sh results/manifests/jobs_{a.name}_{n}.json 0/1 {cores} {a.name}"]).strip()) | |
| 51 | + return | |
| 52 | + if a.cmd == "status": | |
| 53 | + for n, _ in nodes: | |
| 54 | + out = sh(["ssh", n, f"cd ~/wp46_uqo && (wc -l < ~/wp46-cache/forecasts_{a.name}/_runs.jsonl 2>/dev/null || echo 0); " | |
| 55 | + f"python3 -c \"import json;print(len(json.load(open('results/manifests/jobs_{a.name}_{n}.json'))))\" 2>/dev/null; " | |
| 56 | + f"grep -c error ~/wp46-cache/forecasts_{a.name}/_runs.jsonl 2>/dev/null; uptime | sed 's/.*load/load/'; " | |
| 57 | + f"pgrep -fc run_benchmark.py"]) | |
| 58 | + print(n, " | ".join(x.strip() for x in out.splitlines())) | |
| 59 | + return | |
| 60 | + if a.cmd == "collect": | |
| 61 | + dest = ROOT / "results" / "forecasts" / a.name | |
| 62 | + dest.mkdir(parents=True, exist_ok=True) | |
| 63 | + for n, _ in nodes: | |
| 64 | + print(n, sh(["rsync", "-aq", f"{n}:~/wp46-cache/forecasts_{a.name}/", str(dest) + "/"], timeout=3600)) | |
| 65 | + runs = [json.loads(l) for l in (dest / "_runs.jsonl").read_text().splitlines()] if (dest / "_runs.jsonl").exists() else [] | |
| 66 | + print("collected runs:", len(runs), "errors:", sum("error" in r for r in runs)) | |
| 67 | + | |
| 68 | + | |
| 69 | +if __name__ == "__main__": | |
| 70 | + main() | |
modified
scripts/ingest_bars.py
+0 −0
added
scripts/make_jobs.py
+61 −0
@@ -0,0 +1,61 @@ | ||
| 1 | +#!/usr/bin/env python3 | |
| 2 | +"""Expand the frozen configs into a job list (instrument x model x distribution x estimation variant). | |
| 3 | +usage: make_jobs.py --name main [--tier A,B] [--symbols ...] [--oos-start 2012-01-03] [--oos-end 2026-10-30] | |
| 4 | + [--window 1000] [--refit 5] [--expanding] [--models m1,m2]""" | |
| 5 | +from __future__ import annotations | |
| 6 | + | |
| 7 | +import argparse | |
| 8 | +import json | |
| 9 | +import sys | |
| 10 | +from pathlib import Path | |
| 11 | + | |
| 12 | +import yaml | |
| 13 | + | |
| 14 | +ROOT = Path(__file__).resolve().parents[1] | |
| 15 | +sys.path.insert(0, str(ROOT / "src")) | |
| 16 | + | |
| 17 | + | |
| 18 | +def main() -> None: | |
| 19 | + ap = argparse.ArgumentParser() | |
| 20 | + ap.add_argument("--name", required=True) | |
| 21 | + ap.add_argument("--tier", default="A,B") | |
| 22 | + ap.add_argument("--symbols", default="") | |
| 23 | + ap.add_argument("--models", default="") | |
| 24 | + ap.add_argument("--oos-start", default=None) | |
| 25 | + ap.add_argument("--oos-end", default=None) | |
| 26 | + ap.add_argument("--window", type=int, default=None) | |
| 27 | + ap.add_argument("--refit", type=int, default=None) | |
| 28 | + ap.add_argument("--expanding", action="store_true") | |
| 29 | + a = ap.parse_args() | |
| 30 | + reg = yaml.safe_load(open(ROOT / "configs" / "model_registry.yaml"))["models"] | |
| 31 | + inst = yaml.safe_load(open(ROOT / "configs" / "instruments.yaml")) | |
| 32 | + prot = yaml.safe_load(open(ROOT / "configs" / "forecast_protocol.yaml")) | |
| 33 | + uni = inst.get("universe") or inst["candidates"] | |
| 34 | + syms = [s for cls in uni for s in uni[cls]] | |
| 35 | + if a.symbols: | |
| 36 | + syms = [s for s in a.symbols.split(",") if s] | |
| 37 | + tiers = a.tier.split(",") | |
| 38 | + oos_start = a.oos_start or prot["periods"]["validation"]["start"] | |
| 39 | + oos_end = a.oos_end or prot["periods"]["test"]["end"] | |
| 40 | + window = a.window or prot["estimation"]["window"] | |
| 41 | + refit = a.refit or prot["estimation"]["refit_every"] | |
| 42 | + want = set(a.models.split(",")) if a.models else None | |
| 43 | + jobs = [] | |
| 44 | + for s in syms: | |
| 45 | + for mid, m in reg.items(): | |
| 46 | + if m["status"] != "implemented" or m["tier"] not in tiers or (want and mid not in want): | |
| 47 | + continue | |
| 48 | + for dist in m["dists"]: | |
| 49 | + jobs.append(dict(symbol=s, model_id=mid, dist=dist, tier=m["tier"], window=window, refit_every=refit, | |
| 50 | + expanding=a.expanding, rm_proxy=prot["tier_b_input"], oos_start=oos_start, oos_end=oos_end, | |
| 51 | + seed=prot["estimation"]["seed"], max_restarts=prot["estimation"]["max_restarts"])) | |
| 52 | + # heavy jobs first (better load balance): msgarch, figarch, realgarch, aparch/egarch (simulation forecasts) | |
| 53 | + rank = {"msgarch2": 0, "figarch11": 1, "realgarch11": 2, "aparch11": 3, "egarch11": 4, "tarch11": 5, "avgarch11": 6} | |
| 54 | + jobs.sort(key=lambda j: (rank.get(j["model_id"], 9), j["symbol"])) | |
| 55 | + out = ROOT / "results" / "manifests" / f"jobs_{a.name}.json" | |
| 56 | + out.write_text(json.dumps(jobs, indent=0)) | |
| 57 | + print(out, len(jobs), "jobs;", len(syms), "symbols;", sorted({j['model_id'] for j in jobs})) | |
| 58 | + | |
| 59 | + | |
| 60 | +if __name__ == "__main__": | |
| 61 | + main() | |
added
scripts/node_run.sh
+11 −0
@@ -0,0 +1,11 @@ | ||
| 1 | +#!/bin/bash | |
| 2 | +# Run one shard of a job list on a cluster node (called over ssh from scripts/dispatch_nodes.py). | |
| 3 | +# usage: node_run.sh <jobs_file_relative> <shard i/N> <workers> <run_name> | |
| 4 | +set -u | |
| 5 | +cd ~/wp46_uqo || exit 1 | |
| 6 | +git pull -q | |
| 7 | +mkdir -p results/logs | |
| 8 | +export WP46_CACHE=~/wp46-cache | |
| 9 | +nohup .venv/bin/python scripts/run_benchmark.py --jobs "$1" --shard "$2" --workers "$3" --out ~/wp46-cache/forecasts_"$4" \ | |
| 10 | + > results/logs/bench_"$4"_shard_"${2/\//_}".log 2>&1 & | |
| 11 | +echo "started shard $2 on $(hostname) pid $!" | |
added
scripts/run_benchmark.py
+96 −0
@@ -0,0 +1,96 @@ | ||
| 1 | +#!/usr/bin/env python3 | |
| 2 | +"""Run rolling-origin forecasting jobs (instrument x model x distribution) and write one Parquet per job. | |
| 3 | + | |
| 4 | +usage: run_benchmark.py --jobs results/manifests/jobs_<name>.json [--shard i/N] [--workers 8] [--out results/forecasts] | |
| 5 | +The job list is produced by scripts/make_jobs.py from the frozen configs. Each job writes | |
| 6 | + <out>/<symbol>/<spec_id>.parquet (forecasts, one row per origin) | |
| 7 | + <out>/<symbol>/<spec_id>.fits.parquet (refit attempts) | |
| 8 | +and a line in <out>/_runs.jsonl (git SHA, host, package versions, wall time). Existing outputs are skipped (resumable). | |
| 9 | +Nothing printed contains return values.""" | |
| 10 | +from __future__ import annotations | |
| 11 | + | |
| 12 | +import argparse | |
| 13 | +import json | |
| 14 | +import os | |
| 15 | +import platform | |
| 16 | +import resource | |
| 17 | +import socket | |
| 18 | +import subprocess | |
| 19 | +import sys | |
| 20 | +import time | |
| 21 | +from concurrent.futures import ProcessPoolExecutor, as_completed | |
| 22 | +from pathlib import Path | |
| 23 | + | |
| 24 | +ROOT = Path(__file__).resolve().parents[1] | |
| 25 | +sys.path.insert(0, str(ROOT / "src")) | |
| 26 | + | |
| 27 | + | |
| 28 | +def git_sha() -> str: | |
| 29 | + try: | |
| 30 | + return subprocess.check_output(["git", "-C", str(ROOT), "rev-parse", "--short", "HEAD"], text=True).strip() | |
| 31 | + except Exception: # noqa: BLE001 | |
| 32 | + return "unknown" | |
| 33 | + | |
| 34 | + | |
| 35 | +def run_one(job: dict, out: str) -> dict: | |
| 36 | + import numpy as np | |
| 37 | + import pandas as pd | |
| 38 | + from wp46.estimation import JobSpec, run_job | |
| 39 | + from wp46.preprocessing import load_panel | |
| 40 | + js = JobSpec(**{k: v for k, v in job.items() if k in JobSpec.__dataclass_fields__}) | |
| 41 | + d = Path(out) / js.symbol | |
| 42 | + d.mkdir(parents=True, exist_ok=True) | |
| 43 | + fp = d / f"{js.spec_id}.parquet" | |
| 44 | + if fp.exists(): | |
| 45 | + return {"spec_id": js.spec_id, "symbol": js.symbol, "skipped": True} | |
| 46 | + t0 = time.perf_counter() | |
| 47 | + panel = load_panel(js.symbol) | |
| 48 | + F, G = run_job(panel, js) | |
| 49 | + F.to_parquet(fp, index=False) | |
| 50 | + G.to_parquet(d / f"{js.spec_id}.fits.parquet", index=False) | |
| 51 | + ru = resource.getrusage(resource.RUSAGE_SELF) | |
| 52 | + return {"spec_id": js.spec_id, "symbol": js.symbol, "n_origins": int(len(F)), "n_fits": int(len(G)), | |
| 53 | + "status_counts": F["status"].value_counts().to_dict() if len(F) else {}, | |
| 54 | + "fit_status_counts": G["status"].value_counts().to_dict() if len(G) else {}, | |
| 55 | + "wall_s": time.perf_counter() - t0, "cpu_s": ru.ru_utime + ru.ru_stime, "maxrss_mb": ru.ru_maxrss / (1024 ** 2 if platform.system() == "Darwin" else 1024), | |
| 56 | + "fit_time_total_s": float(G["time_s"].sum()) if len(G) else 0.0, "fc_time_total_s": float(F["time_fc_s"].sum()) if "time_fc_s" in F else 0.0} | |
| 57 | + | |
| 58 | + | |
| 59 | +def main() -> None: | |
| 60 | + ap = argparse.ArgumentParser() | |
| 61 | + ap.add_argument("--jobs", required=True) | |
| 62 | + ap.add_argument("--shard", default="0/1") | |
| 63 | + ap.add_argument("--workers", type=int, default=max(1, os.cpu_count() - 2)) | |
| 64 | + ap.add_argument("--out", default=str(ROOT / "results" / "forecasts")) | |
| 65 | + a = ap.parse_args() | |
| 66 | + jobs = json.loads(Path(a.jobs).read_text()) | |
| 67 | + i, n = map(int, a.shard.split("/")) | |
| 68 | + jobs = [j for k, j in enumerate(jobs) if k % n == i] | |
| 69 | + import arch, numpy, scipy, pandas | |
| 70 | + meta = {"git_sha": git_sha(), "host": socket.gethostname(), "python": platform.python_version(), "arch": arch.__version__, | |
| 71 | + "numpy": numpy.__version__, "scipy": scipy.__version__, "pandas": pandas.__version__, "machine": platform.machine(), | |
| 72 | + "cpu_count": os.cpu_count(), "shard": a.shard, "n_jobs": len(jobs), "started": time.strftime("%Y-%m-%dT%H:%M:%S%z")} | |
| 73 | + Path(a.out).mkdir(parents=True, exist_ok=True) | |
| 74 | + log = Path(a.out) / "_runs.jsonl" | |
| 75 | + print(json.dumps(meta), flush=True) | |
| 76 | + done = 0 | |
| 77 | + t0 = time.time() | |
| 78 | + with ProcessPoolExecutor(a.workers) as ex: | |
| 79 | + futs = {ex.submit(run_one, j, a.out): j for j in jobs} | |
| 80 | + for f in as_completed(futs): | |
| 81 | + j = futs[f] | |
| 82 | + try: | |
| 83 | + rec = f.result() | |
| 84 | + except Exception as e: # noqa: BLE001 | |
| 85 | + rec = {"spec_id": j.get("model_id"), "symbol": j.get("symbol"), "error": f"{type(e).__name__}: {str(e)[:200]}"} | |
| 86 | + rec.update({"git_sha": meta["git_sha"], "host": meta["host"], "ts": time.strftime("%Y-%m-%dT%H:%M:%S%z")}) | |
| 87 | + with open(log, "a") as fh: | |
| 88 | + fh.write(json.dumps(rec) + "\n") | |
| 89 | + done += 1 | |
| 90 | + if done % 10 == 0 or "error" in rec: | |
| 91 | + print(f"[{done}/{len(jobs)} {time.time()-t0:7.0f}s] {rec.get('symbol')} {rec.get('spec_id')} {rec.get('error','') or rec.get('wall_s','')}", flush=True) | |
| 92 | + print("done", done, "jobs in", round(time.time() - t0), "s") | |
| 93 | + | |
| 94 | + | |
| 95 | +if __name__ == "__main__": | |
| 96 | + main() | |
modified
src/wp46/preprocessing/panel.py
+12 −2
@@ -28,7 +28,9 @@ from wp46.findata_client.ingest import DAILY_FOR, DATASET_FOR, cache_root | ||
| 28 | 28 | from wp46.realized_measures import MEASURE_COLUMNS, session_measures |
| 29 | 29 | |
| 30 | 30 | PANEL_COLUMNS = ["session", "close", "open", "r", "r_on", "r_oc", "valid_rm", "expected_bars"] + MEASURE_COLUMNS |
| 31 | −EXPECTED = {"futures": 1380, "fx": 1440, "crypto": 1440} | |
| 31 | +EXPECTED = {"futures": 1380, "fx": 1440, "crypto": 1440} # nominal; replaced by the instrument's modal bar count (D-05) | |
| 32 | +RM_VALID_FROM = {"crypto": "2020-01-01"} # D-06: crypto 1-minute bars before 2020 are contaminated by cross-venue bounce | |
| 33 | + # (audit: 1-min RV 10-100x the squared daily return in 2013-2019) -> no realized measure | |
| 32 | 34 | |
| 33 | 35 | |
| 34 | 36 | def _read_cache(dataset_id: str, symbol: str, cols: list[str]) -> pd.DataFrame: |
@@ -100,12 +102,20 @@ def build_daily_panel(asset_class: str, symbol: str) -> pd.DataFrame: | ||
| 100 | 102 | for c in MEASURE_COLUMNS + ["expected_bars", "valid_rm"]: |
| 101 | 103 | out[c] = ir[c] |
| 102 | 104 | # drop stub sessions (Sunday evenings, holiday fragments): they are not trading days for the daily model |
| 103 | − out = out[out["n_bars"] >= 0.25 * out["expected_bars"]] | |
| 105 | + modal = int(out["n_bars"].round(-1).mode().iloc[0]) | |
| 106 | + out = out[out["n_bars"] >= 0.25 * modal] | |
| 104 | 107 | out["r"] = 100.0 * np.log(out["close"] / out["close"].shift(1)) |
| 105 | 108 | out["r_on"] = 100.0 * np.log(out["open"] / out["close"].shift(1)) |
| 106 | 109 | out.index.name = "session" |
| 107 | 110 | out = out.iloc[1:] # first return undefined |
| 111 | + if asset_class not in ("etf", "stock"): | |
| 112 | + # D-05: expected bars = modal session length of THIS instrument (grains trade ~12.5 h, metals ~22 h, FX 24 h) | |
| 113 | + modal = int(out["n_bars"].round(-1).mode().iloc[0]) | |
| 114 | + out["expected_bars"] = modal | |
| 115 | + out["valid_rm"] = out["n_bars"] >= MIN_FRACTION * modal | |
| 108 | 116 | out["valid_rm"] = out["valid_rm"] & np.isfinite(out["rv_5m_ss"]) & (out["rv_5m_ss"] > 0) |
| 117 | + if asset_class in RM_VALID_FROM: | |
| 118 | + out.loc[out.index < pd.Timestamp(RM_VALID_FROM[asset_class]), "valid_rm"] = False | |
| 109 | 119 | return out[PANEL_COLUMNS[1:]].reset_index() |
| 110 | 120 | |
| 111 | 121 | |
added
tests/__init__.py
+0 −0
added
tests/conftest.py
+4 −0
@@ -0,0 +1,4 @@ | ||
| 1 | +import sys | |
| 2 | +from pathlib import Path | |
| 3 | + | |
| 4 | +sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src")) | |
added
tests/integration/test_rolling_driver.py
+38 −0
@@ -0,0 +1,38 @@ | ||
| 1 | +import numpy as np | |
| 2 | +import pandas as pd | |
| 3 | + | |
| 4 | +from wp46.estimation import JobSpec, run_job | |
| 5 | + | |
| 6 | + | |
| 7 | +def synthetic_panel(T=1400, seed=0): | |
| 8 | + rng = np.random.default_rng(seed) | |
| 9 | + s = np.empty(T); r = np.empty(T); s[0] = 1.0 | |
| 10 | + for t in range(T): | |
| 11 | + if t > 0: | |
| 12 | + s[t] = 0.05 + 0.08 * r[t - 1] ** 2 + 0.9 * s[t - 1] | |
| 13 | + r[t] = np.sqrt(s[t]) * rng.standard_normal() | |
| 14 | + rv = s * rng.lognormal(0, 0.3, T) | |
| 15 | + dates = pd.bdate_range("2010-01-04", periods=T) | |
| 16 | + return pd.DataFrame({"session": dates, "r": r, "r_on": np.zeros(T), "rv_5m_ss": rv, "rv_5m": rv, "rv_1m": rv, "bv_5m": rv, | |
| 17 | + "medrv_5m": rv, "valid_rm": True, "close": 100.0, "open": 100.0, "r_oc": r, "expected_bars": 391}) | |
| 18 | + | |
| 19 | + | |
| 20 | +def test_driver_refit_cadence_and_outputs(): | |
| 21 | + p = synthetic_panel() | |
| 22 | + F, G = run_job(p, JobSpec("SYN", "garch11", "normal", "A", window=1000, refit_every=5, oos_start="2013-01-01", oos_end="2016-12-31")) | |
| 23 | + assert (F.status == "ok").all() | |
| 24 | + assert len(G) == int(np.ceil(len(F) / 5)) | |
| 25 | + assert np.all(F.h5 > F.h1) and np.all(F.h22 > F.h5) | |
| 26 | + # origins strictly after the first `window` observations | |
| 27 | + assert F.origin.min() > p.session.iloc[999] | |
| 28 | + | |
| 29 | + | |
| 30 | +def test_driver_tier_b_and_failure_flags(): | |
| 31 | + p = synthetic_panel() | |
| 32 | + F, G = run_job(p, JobSpec("SYN", "har_rv", "empirical", "B", window=1000, oos_start="2013-01-01", oos_end="2014-12-31")) | |
| 33 | + assert (F.status == "ok").all() and np.isfinite(F.h1).all() | |
| 34 | + # a missing return inside the window is reported as missing_input, never silently filled | |
| 35 | + p2 = p.copy(); p2.loc[1100, "r"] = np.nan | |
| 36 | + F2, _ = run_job(p2, JobSpec("SYN", "garch11", "normal", "A", window=1000, oos_start="2013-01-01", oos_end="2014-12-31")) | |
| 37 | + affected = (F2.origin >= p.session.iloc[1100]) & (F2.origin <= p.session.iloc[2099]) | |
| 38 | + assert (F2.status == "missing_input").sum() == int(affected.sum()) and affected.sum() > 0 | |
added
tests/replication/fixture_returns.csv
+3001 −0
Large diff (3,001 lines) is not loaded by default to keep the page fast.Load diff
added
tests/replication/rugarch_crosscheck.R
+28 −0
Large diff (28 lines) is not loaded by default to keep the page fast.Load diff
added
tests/replication/test_rugarch.py
+69 −0
Large diff (69 lines) is not loaded by default to keep the page fast.Load diff
added
tests/simulation/test_recovery.py
+89 −0
Large diff (89 lines) is not loaded by default to keep the page fast.Load diff
added
tests/unit/test_custom_models.py
+89 −0
Large diff (89 lines) is not loaded by default to keep the page fast.Load diff
added
tests/unit/test_distributions.py
+31 −0
Large diff (31 lines) is not loaded by default to keep the page fast.Load diff
added
tests/unit/test_scoring_and_panel.py
+53 −0
Large diff (54 lines) is not loaded by default to keep the page fast.Load diff