spb/qc-election
Public
Python 66.6%
HTML 24.8%
CSS 4.9%
JavaScript 3.6%
1# QC Élection Forecast — Plateforme de prévision électorale du Québec 20262# Auteur : Simon-Pierre Boucher3# Contact : contact@spboucher.ai4# https://www.qc-election.com5"""Volatilité de campagne pilotée par les signaux web (v3 « 127 », §16).67Fusionne les signaux d'ATTENTION (jamais directionnels) en un seul8multiplicateur de variance de dérive, STRICTEMENT borné :910 m = 1 + a·max(0, z_wiki − 2) + b·max(0, z_social − 2) + c·confirmation11 1.00 ≤ m ≤ volatility_multiplier_max (défaut 1.5)1213Un pic d'attention (Wikipédia ou médias sociaux) signale un moment chaud de14campagne : l'issue est plus incertaine, dans un sens comme dans l'autre.15La moyenne du forecast n'est JAMAIS touchée. Débrayable par16`enable_social_volatility` (le repli conserve la seule turbulence Wikipédia17de la v2.1, elle-même débrayable)."""18from __future__ import annotations1920import numpy as np21from sqlalchemy.orm import Session2223from ...config import settings2425A_WIKI = 0.10 # pente par unité de z au-delà de 2 (Wikipédia)26B_SOCIAL = 0.06 # pente sociale (plus bruitée → plus faible)27C_CONFIRM = 0.05 # bonus si les DEUX familles de signaux confirment282930def compute(db: Session, wiki_signal: dict | None = None) -> dict:31 """Multiplicateur de variance borné + décomposition publiée."""32 cap = settings.volatility_multiplier_max33 wiki_z = float((wiki_signal or {}).get("z_max") or 0.0)34 social_z, social_avail = 0.0, False35 if settings.enable_social_volatility:36 try:37 from .social import pulse_index38 pulse = pulse_index(db)39 if pulse.get("available"):40 social_avail = True41 social_z = max((v["attention_z"]42 for v in pulse["parties"].values()), default=0.0)43 except Exception:44 pass45 w_part = A_WIKI * max(0.0, wiki_z - 2.0)46 s_part = B_SOCIAL * max(0.0, social_z - 2.0)47 confirm = C_CONFIRM if (w_part > 0 and s_part > 0) else 0.048 mult = float(np.clip(1.0 + w_part + s_part + confirm, 1.0, cap))49 return {"multiplier": round(mult, 3), "cap": cap,50 "wiki_z": round(wiki_z, 2),51 "social_z": round(social_z, 2) if social_avail else None,52 "components": {"wikipedia": round(w_part, 3),53 "social": round(s_part, 3),54 "confirmation": confirm},55 "note": ("Les signaux d'attention n'agissent que sur la VARIANCE "56 "de dérive (borne stricte ≤ %.2f) — jamais sur la moyenne."57 % cap)}58