feat: Phase 2 — Pattern Reliability, Contre-thèses & Calibration probabiliste
Sprint 2.1 — Pattern Reliability Score - database.py: get_pattern_reliability() — win_rate × log(n+1) sur trades matures (≥35% horizon) - database.py: get_all_pattern_reliability_map() pour injection rapide dans les prompts - ai_analyzer.py: inject reliability_map dans suggest_patterns (patterns fiables mis en avant) - auto_cycle.py: charge reliability_map avant suggestion et le passe au suggéreur - routers/analytics.py: GET /api/analytics/reliability - PatternEditor.tsx: ReliabilityBadge sur chaque card + usePatternReliability hook - useApi.ts: usePatternReliability, useCalibration hooks Sprint 2.2 — Contre-thèses & Invalidation Triggers - database.py: migration ALTER TABLE — counter_thesis, invalidation_trigger, invalidation_probability - database.py: save_custom_pattern() persiste les 3 nouveaux champs - ai_analyzer.py: counter_thesis + invalidation_trigger + invalidation_probability dans le JSON schema - auto_cycle.py: détection automatique des triggers d'invalidation contre les news (keyword match) - routers/analytics.py: GET /api/analytics/invalidation-alerts - PatternEditor.tsx: affichage contre-thèse dans les cards + champs dans le formulaire - PatternEditor.tsx: affichage dans AiSuggestModal (suggestions IA) - routers/patterns.py: PatternRequest inclut les 3 nouveaux champs Sprint 2.3 — Calibration probabiliste & Demi-vie KB - database.py: migration — predicted_probability sur pattern_score_history - database.py: save_pattern_scores() stocke probability du pattern à chaque scoring run - database.py: get_calibration_data() — Brier score + buckets de calibration par décile - database.py: expires_at + confidence_decay_days sur knowledge_base - database.py: decay_kb_confidence() — decay automatique + archivage à 0 - auto_cycle.py: decay_kb_confidence() appelé au début de chaque cycle (non-bloquant) - routers/analytics.py: GET /api/analytics/calibration + POST /api/analytics/kb/decay - frontend/src/pages/Analytics.tsx: nouvelle page — tableau fiabilité + calibration Brier - App.tsx + Sidebar.tsx: route /analytics + entrée menu Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -87,6 +87,15 @@ def run_cycle_once(trigger: str = "auto") -> Dict[str, Any]:
|
||||
ai_score_news_batch, _chat, DEFAULT_ANALYSIS_TEMPLATE,
|
||||
)
|
||||
|
||||
# KB confidence decay (non-blocking)
|
||||
try:
|
||||
from services.database import decay_kb_confidence
|
||||
_decayed = decay_kb_confidence()
|
||||
if _decayed:
|
||||
logger.info(f"[Cycle {run_id[:16]}] KB decay: {_decayed} entrée(s) mise(s) à jour")
|
||||
except Exception as _e:
|
||||
logger.warning(f"[Cycle] KB decay failed (non-blocking): {_e}")
|
||||
|
||||
# Check AI key
|
||||
ai_key = get_config("openai_api_key") or ""
|
||||
if not ai_key:
|
||||
@@ -157,6 +166,37 @@ def run_cycle_once(trigger: str = "auto") -> Dict[str, Any]:
|
||||
geo_score_val = int(geo_score_obj.get("score") or 0)
|
||||
summary["geo_score"] = geo_score_val
|
||||
|
||||
# ── Invalidation trigger detection ────────────────────────────────────
|
||||
try:
|
||||
from services.database import get_custom_patterns as _gcp
|
||||
_active_patterns = _gcp()
|
||||
_news_headlines = " ".join(
|
||||
(n.get("title", "") + " " + (n.get("summary", "") or "")).lower()
|
||||
for n in news[:15]
|
||||
)
|
||||
_triggered = []
|
||||
for _pat in _active_patterns:
|
||||
_trigger = (_pat.get("invalidation_trigger") or "").lower().strip()
|
||||
if not _trigger:
|
||||
continue
|
||||
# Simple keyword matching: split trigger into words and check majority match
|
||||
_words = [w for w in _trigger.split() if len(w) > 3]
|
||||
if _words and sum(1 for w in _words if w in _news_headlines) >= max(1, len(_words) // 2):
|
||||
_triggered.append({
|
||||
"pattern_id": _pat["id"],
|
||||
"pattern_name": _pat["name"],
|
||||
"trigger": _pat["invalidation_trigger"],
|
||||
"probability": _pat.get("invalidation_probability"),
|
||||
})
|
||||
if _triggered:
|
||||
logger.warning(
|
||||
f"[Cycle {run_id[:16]}] ⚠ INVALIDATION TRIGGERS FIRED for "
|
||||
f"{len(_triggered)} pattern(s): {[t['pattern_name'] for t in _triggered]}"
|
||||
)
|
||||
summary["invalidation_alerts"] = _triggered
|
||||
except Exception as _ie:
|
||||
logger.debug(f"[Cycle] Invalidation check failed (non-blocking): {_ie}")
|
||||
|
||||
quotes = get_all_quotes()
|
||||
|
||||
gauges = get_macro_gauges()
|
||||
@@ -167,12 +207,21 @@ def run_cycle_once(trigger: str = "auto") -> Dict[str, Any]:
|
||||
|
||||
# ── Step 2: Suggest new patterns ──────────────────────────────────────
|
||||
logger.info(f"[Cycle {run_id[:16]}] Step 2: suggesting patterns")
|
||||
_reliability_map = {}
|
||||
try:
|
||||
from services.database import get_all_pattern_reliability_map
|
||||
_reliability_map = get_all_pattern_reliability_map()
|
||||
if _reliability_map:
|
||||
logger.info(f"[Cycle {run_id[:16]}] Reliability map: {len(_reliability_map)} patterns")
|
||||
except Exception as _re:
|
||||
logger.warning(f"[Cycle] Reliability map failed (non-blocking): {_re}")
|
||||
try:
|
||||
from services.data_fetcher import get_economic_calendar
|
||||
calendar = get_economic_calendar()
|
||||
suggestions = suggest_patterns_from_market_context(
|
||||
news, quotes, calendar, macro_regime=macro_regime, geo_score=geo_score_obj,
|
||||
portfolio_lessons=portfolio_lessons,
|
||||
reliability_map=_reliability_map or None,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning(f"[Cycle] Suggestion step failed: {e}")
|
||||
|
||||
Reference in New Issue
Block a user