feat: math core v3 engine upgrade

This commit is contained in:
Celes Renata
2026-06-27 12:21:41 +00:00
parent 365bc5d4b7
commit b4bf0f2361
34 changed files with 11693 additions and 3 deletions
+129
View File
@@ -5,14 +5,20 @@ log-likelihood accumulation, Beta distribution parameters, and
Shannon entropy for mixed-signal detection.
Requirements: 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 9.1, 9.7
V3 posterior assembly: 5.1, 5.2, 5.3, 5.4, 5.5, 5.6, 5.7
"""
from __future__ import annotations
import math
from dataclasses import dataclass
from typing import TYPE_CHECKING
from services.aggregation.scoring import WeightedSignal
if TYPE_CHECKING:
from services.aggregation.regime import V3RegimeClassification
from services.aggregation.worker import EvidenceCluster
@dataclass(frozen=True)
class BayesianPosterior:
@@ -125,3 +131,126 @@ def compute_bayesian_posterior(
entropy=entropy,
signal_count=count,
)
# ---------------------------------------------------------------------------
# V3 Posterior Assembly — Calibrated Evidence Engine
# Requirements: 5.1, 5.2, 5.3, 5.4, 5.5, 5.6, 5.7
# ---------------------------------------------------------------------------
@dataclass(frozen=True)
class V3Posterior:
"""V3 posterior result from log-odds Bayesian assembly.
Attributes:
p_up: Posterior probability of upward move, (0, 1).
p_down: 1 - p_up.
log_odds: Raw log-odds (logit) of P_up.
strength: abs(2 × P_up - 1), signal conviction [0, 1].
direction: Classified direction string ('bullish', 'bearish', 'neutral').
n_eff_total: Total effective evidence count across all clusters.
regime: Market regime string used for this computation.
"""
p_up: float
p_down: float
log_odds: float
strength: float
direction: str
n_eff_total: float
regime: str
# Regime-specific direction thresholds: (bullish_threshold, bearish_threshold)
# P_up >= bullish → "bullish"; P_up <= bearish → "bearish"; else "neutral"
_V3_DIRECTION_THRESHOLDS: dict[str, tuple[float, float]] = {
"panic": (0.68, 0.32),
"trend_following": (0.60, 0.40),
"mean_reversion": (0.63, 0.37),
"uncertainty": (0.65, 0.35),
}
def _logit(p: float) -> float:
"""Compute logit = ln(p / (1-p)) with boundary guard."""
p = max(1e-10, min(1 - 1e-10, p))
return math.log(p / (1 - p))
def _sigmoid(x: float) -> float:
"""Compute sigmoid = 1 / (1 + exp(-x)) with overflow guard."""
if x > 500:
return 1.0
if x < -500:
return 0.0
return 1.0 / (1.0 + math.exp(-x))
def compute_v3_posterior(
clusters: list[EvidenceCluster],
regime: V3RegimeClassification,
p_prior: float = 0.50,
) -> V3Posterior:
"""Assemble v3 posterior via log-odds accumulation.
Computes:
logit(P_up) = logit(P_prior) + sum(gamma_regime × LLR_c)
P_up = sigmoid(log_odds), clamped to [1e-10, 1 - 1e-10]
strength = abs(2 × P_up - 1)
direction via regime-specific thresholds
Args:
clusters: List of EvidenceCluster objects with computed cluster_llr.
regime: V3RegimeClassification providing evidence_multiplier and regime.
p_prior: Calibrated prior probability, clamped to [0.40, 0.60].
Returns:
V3Posterior with computed posterior fields.
Requirements: 5.1, 5.2, 5.3, 5.4, 5.5, 5.6, 5.7
"""
# Clamp prior to [0.40, 0.60] (Req 5.6)
p_prior = max(0.40, min(0.60, p_prior))
# Gamma: regime-specific evidence multiplier (Req 5.2)
gamma = regime.evidence_multiplier
# Compute log-odds: logit(P_prior) + sum(gamma × LLR_c) (Req 5.1, 5.2)
log_odds = _logit(p_prior) + sum(gamma * c.cluster_llr for c in clusters)
# Compute P_up via sigmoid (Req 5.3)
p_up = _sigmoid(log_odds)
# Clamp to open interval (Req 5.3)
p_up = max(1e-10, min(1 - 1e-10, p_up))
p_down = 1.0 - p_up
# Strength = |2 × P_up - 1| (Req 5.4)
strength = abs(2.0 * p_up - 1.0)
# n_eff_total = sum of cluster n_eff (Req 5.5)
n_eff_total = sum(c.n_eff for c in clusters)
# Classify direction using regime-specific thresholds (Req 5.5)
regime_key = regime.regime.value # MarketRegime enum → string
bull_thresh, bear_thresh = _V3_DIRECTION_THRESHOLDS.get(
regime_key, (0.65, 0.35)
)
if p_up >= bull_thresh:
direction = "bullish"
elif p_up <= bear_thresh:
direction = "bearish"
else:
direction = "neutral"
return V3Posterior(
p_up=p_up,
p_down=p_down,
log_odds=log_odds,
strength=strength,
direction=direction,
n_eff_total=n_eff_total,
regime=regime_key,
)