You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

93 lines
3.5 KiB
Python

"""Event-level feature calculations from parser output."""
from __future__ import annotations
from typing import Any
from libs.schemas.types import ConfidenceOutput, GuidanceOutput, RiskFlagsOutput, SignalsOutput
def guidance_direction_score(guidance: GuidanceOutput) -> float:
"""raised=1.0, inline_or_maintained=0.5, lowered=0.0, else=0.25."""
mapping = {
"raised": 1.0,
"inline_or_maintained": 0.5,
"lowered": 0.0,
"withdrawn": 0.0,
"not_provided": 0.25,
"unclear": 0.25,
}
return mapping.get(guidance.status, 0.25)
def oneoff_penalty(risk_flags: RiskFlagsOutput) -> float:
"""Sum of active risk flags / total flags. Higher = more risk."""
flags = [
risk_flags.oneoff_item,
risk_flags.tax_benefit,
risk_flags.valuation_gain,
risk_flags.non_gaap_heavy,
risk_flags.financing_related,
risk_flags.legal_or_regulatory_overhang,
]
total = len(flags)
active = sum(flags)
return active / total if total > 0 else 0.0
def signal_strength_score(signals: SignalsOutput) -> float:
"""Composite score [0..1] of positive business signals."""
score = 0.0
weights = {
"demand_strength": {"strong": 1.0, "stable": 0.5, "weakening": 0.0, "unknown": 0.0},
"pricing_power": {"present": 1.0, "mixed": 0.5, "absent": 0.0, "unknown": 0.0},
"backlog_or_bookings": {"present": 1.0, "mixed": 0.5, "absent": 0.0, "unknown": 0.0},
"customer_expansion": {"present": 1.0, "mixed": 0.5, "absent": 0.0, "unknown": 0.0},
"margin_quality": {"improving": 1.0, "stable": 0.5, "deteriorating": 0.0, "unknown": 0.0},
}
total_weight = len(weights)
for field, mapping in weights.items():
val = getattr(signals, field)
score += mapping.get(val, 0.0)
return score / total_weight if total_weight > 0 else 0.0
def document_quality_score(confidence: ConfidenceOutput) -> float:
"""Weighted average of confidence dimensions."""
return (
confidence.overall * 0.4
+ confidence.event_type * 0.2
+ confidence.event_direction * 0.2
+ confidence.guidance * 0.1
+ confidence.risk_flags * 0.1
)
def compute_event_features(parser_output: dict[str, Any]) -> dict[str, Any]:
"""Compute all event features from raw parser output dict."""
from libs.schemas.types import (
ConfidenceOutput,
GuidanceOutput,
RiskFlagsOutput,
SignalsOutput,
)
guidance = GuidanceOutput.model_validate(parser_output["guidance"])
signals = SignalsOutput.model_validate(parser_output["signals"])
risk_flags = RiskFlagsOutput.model_validate(parser_output["risk_flags"])
confidence = ConfidenceOutput.model_validate(parser_output["confidence"])
return {
"guidance_direction_score": guidance_direction_score(guidance),
"guidance_status": guidance.status,
"oneoff_penalty": oneoff_penalty(risk_flags),
"signal_strength_score": signal_strength_score(signals),
"document_quality_score": document_quality_score(confidence),
"event_type": parser_output.get("event_type", "unknown"),
"event_direction": parser_output.get("event_direction", "unknown"),
"parse_confidence_overall": confidence.overall,
"parse_confidence_event_direction": confidence.event_direction,
"parse_confidence_guidance": confidence.guidance,
"filing_time_bucket": parser_output.get("filing_time_bucket", "unknown"),
"event_date": parser_output.get("event_date", ""),
}