TL;DR: The Vote Engine, introduced in v3.85.0, is a non-parametric consensus engine spanning 8 Risk groups + 5 Quant groups + 2 meta-vote groups (11 total). It breaks every judgement into dozens of independent method ballots and aggregates them per horizon. Design rules: no normal VaR, no √t scaling, no single-method dominance — divergences are surfaced via the Rule-B cross-panel flag.
Concepts
Why a vote engine?
Traditional approach: pick one VaR / Beta / Sharpe formula, output a single number as the risk verdict.
The problem: every single formula has assumptions. Normal VaR assumes Gaussian returns (they aren't); √t assumes returns are i.i.d. (they have autocorrelation); GARCH assumes a specific conditional-variance form. One formula fails → the verdict fails.
The new approach: same horizon, 4 different methods compute VaR; 4 votes form a consensus. If one method's assumption breaks, three votes still anchor the result. If all four break, a model-credibility meta-vote catches it.
What the 11 groups cover
| Panel | Group | Measures |
|---|---|---|
| Risk | G1 Drawdown | MDD / CDaR / Avg drawdown / Recovery days |
| G2 Multi-period VaR | 4 methods × 6 horizons = 24 ballots/stock (Historical / Block Bootstrap / FHS / EVT POT) | |
| G3 Downside | Sortino / Downside dev / Omega | |
| G4 Systemic risk | Beta_60 / Beta_250 / Correlation / R² | |
| G5 Reward-risk | Sharpe / Calmar / M² | |
| G6 Stationarity | Hurst / ADF / KPSS | |
| G7 Tail dependence | Index left-tail 5% / 1% tail β / Asymmetric β | |
| G8 Vol regime | σ_20 z-score / vol-of-vol / clustering | |
| Quant | Q1 Trend | MA 5/20 / 20/60 / 60/200 / Donchian |
| Q2 Momentum | RSI / MACD / Stoch K | |
| Q3 Vol forward | EWMA / GARCH σ_{t+1} / Realized σ | |
| Q4 Seasonality | Same-month / Same-weekday / Month-start effect | |
| Q6 Expected return | FHS Monte Carlo 5d / 20d / 60d μ | |
| Meta | G9 Model credibility | Kupiec POF / Christoffersen / Basel (Sun nightly) |
| Q5 Forecast credibility | Rolling backtest hit-rate (Sun nightly) |
Four non-parametric VaR methods
G2 (multi-period matrix) is the engine's core. Per horizon (5/10/20/60/120/220d), four methods vote:
| Method | Assumptions | How |
|---|---|---|
| M-A Direct historical | 0 | Overlapping h-day cumulative returns → 5% quantile |
| M-B Block Bootstrap | 2 | Resample contiguous blocks to preserve vol clustering, n_sims=5000 |
| M-C FHS | 2 | GARCH(1,1) filter + standardized residual resampling |
| M-D EVT POT | 2 | Fit GPD on threshold exceedances, extrapolate deep tail |
Assumption weights: 0 assumptions ×1.0, 2 assumptions ×0.8, 3 assumptions ×0.6, 4+ (normal / √t) ×0 (excluded outright).
Horizon slicing
Each group's output slices into the four horizons:
- Ultra-Short: H1 = 5d
- Short: H2 = 10d, H3 = 20d
- Mid: H4 = 60d
- Long: H5 = 120d, H6 = 220d
Per-horizon aggregation feeds the "Risk" / "Quant" blocks inside each Reference Summary card.
Rule-B cross-panel divergence
Same horizon, simultaneously:
- Risk panel consensus = high (risk elevated)
- Quant panel consensus = bull (lean bullish)
→ backend cross_panel_divergence API flags it. Frontend lights up "⚠ Divergence" on the card header.
Design intent: a divergence shouldn't be averaged away — the user must know the signals conflict.
How to use it on-page
Open stock → Insights → Signal → below Reference Summary → expand ▶ Vote engine details — Risk × Quant 11 groups.
Each group expands to:
- overall consensus direction + confidence
- per_horizon consensus by 4 horizons (ultra/short/mid/long)
- Ballots table with method × horizon × direction × value × confidence
Reading hierarchy
Coarse → fine:
- The 4 verdict chips on the Reference Summary cards (Bull / Lean Bull / Neutral / Wait / Lean Bear / Bear)
- The Risk / Quant blocks inside each card (per-horizon consensus, 4-5 lines of key metrics)
- The bottom 11-group panel (when you want to know which exact method voted what)
Model credibility (G9 / Q5)
Run every Sunday late night on 250-day backtests:
- G9 Kupiec POF: does the VaR breach rate match nominal α (5%)?
- G9 Christoffersen: are breaches clustered? (GARCH missed vol clustering → red)
- G9 Basel Traffic Light: red / yellow / green
- Q5: ARIMA-MC forecast hit-rate
Red light → that method's weight is reduced to 40% for the following week (self-correction).
FAQ
Q1: Why ban normal VaR?
Normal distribution has thin tails. Empirically, equity returns are fat-tailed (kurtosis > 3) — normal VaR systematically underestimates extreme risk. Excluding it is a hard rule, not a tunable parameter.
Q2: Why ban √t scaling?
√t (1-day σ × √h → h-day VaR) requires i.i.d. returns. Markets exhibit autocorrelation (short-term momentum, long-term mean reversion). The engine estimates each horizon directly — no scaling shortcut.
Q3: Does it recompute daily?
Daily: short-window groups (G2 5-20d / G8 σ_20 / Q1-Q3 / Q6) — ~30 minutes/day. Weekly: long-window groups (G1 / G3-G7 / Q4) + meta-votes G9 / Q5 — ~5 hours/week, scheduled Sunday late-night.
This keeps daily key signals (short-term) fresh without burning CPU on slow-changing long-term data.
Q4: I see "no data" — what gives?
New listings (<260 days of history) are skipped. The meta-vote groups (G9/Q5) need the weekly cron to populate.