feat: competitive intelligence & historical pattern matching layer

This commit is contained in:
Celes Renata
2026-04-14 19:42:48 +00:00
parent b478022ba3
commit f7a11d14ea
203 changed files with 20155 additions and 97 deletions
+115
View File
@@ -32,6 +32,8 @@ class SuppressionReason(str, Enum):
LOW_SOURCE_DIVERSITY = "low_source_diversity"
HIGH_EXTRACTION_FAILURE_RATE = "high_extraction_failure_rate"
INSUFFICIENT_VALID_DOCUMENTS = "insufficient_valid_documents"
MACRO_ONLY_SIGNAL = "macro_only_signal"
PATTERN_ONLY_SIGNAL = "pattern_only_signal"
@dataclass(frozen=True)
@@ -240,3 +242,116 @@ def evaluate_suppression(
data_quality_score=quality_score,
context=ctx,
)
# ---------------------------------------------------------------------------
# Macro-only suppression (Requirements: 10.3)
# ---------------------------------------------------------------------------
MACRO_ONLY_CAVEAT = (
"[Macro-only signal] This trend direction is driven solely by macro/geopolitical "
"signals with no supporting company-specific evidence. Recommendation is "
"informational only and should not be used for automated trading decisions."
)
def evaluate_macro_only_suppression(
summary: TrendSummary,
macro_signal_count: int,
company_signal_count: int,
) -> bool:
"""Evaluate whether a recommendation should be suppressed due to macro-only signals.
When macro signals are the sole basis for a trend direction change
(no supporting company-specific signals), the recommendation should
be forced to informational mode with a macro-only caveat.
Args:
summary: The trend summary to evaluate.
macro_signal_count: Number of macro signals contributing to the trend.
company_signal_count: Number of company-specific signals contributing.
Returns:
True if the recommendation should be suppressed (macro-only), False otherwise.
Requirements: 10.3
"""
# No macro signals means no macro-only suppression
if macro_signal_count <= 0:
return False
# If there are company-specific signals, no suppression needed
if company_signal_count > 0:
return False
# Macro signals are the sole basis — suppress
logger.info(
"Macro-only suppression triggered for %s/%s: "
"macro_signals=%d, company_signals=%d, direction=%s",
summary.entity_id,
summary.window.value,
macro_signal_count,
company_signal_count,
summary.trend_direction.value,
)
return True
# ---------------------------------------------------------------------------
# Pattern-only suppression (Requirements: 9.3)
# ---------------------------------------------------------------------------
PATTERN_ONLY_CAVEAT = (
"[Pattern-only signal] This trend direction is driven solely by historical "
"pattern and competitive signals with no supporting company-specific or macro "
"evidence. Recommendation is informational only."
)
def evaluate_pattern_only_suppression(
summary: TrendSummary,
pattern_signal_count: int,
company_signal_count: int,
macro_signal_count: int,
) -> bool:
"""Evaluate whether a recommendation should be suppressed due to pattern-only signals.
When pattern-based signals are the sole basis for a trend direction change
(no supporting company-specific or macro signals), the recommendation should
be forced to informational mode with a pattern-only caveat.
Args:
summary: The trend summary to evaluate.
pattern_signal_count: Number of pattern/competitive signals contributing.
company_signal_count: Number of company-specific signals contributing.
macro_signal_count: Number of macro signals contributing.
Returns:
True if the recommendation should be suppressed (pattern-only), False otherwise.
Requirements: 9.3
"""
# No pattern signals means no pattern-only suppression
if pattern_signal_count <= 0:
return False
# If there are company-specific signals, no suppression needed
if company_signal_count > 0:
return False
# If there are macro signals, no suppression needed
if macro_signal_count > 0:
return False
# Pattern signals are the sole basis — suppress
logger.info(
"Pattern-only suppression triggered for %s/%s: "
"pattern_signals=%d, company_signals=%d, macro_signals=%d, direction=%s",
summary.entity_id,
summary.window.value,
pattern_signal_count,
company_signal_count,
macro_signal_count,
summary.trend_direction.value,
)
return True