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>
74 lines
2.1 KiB
Python
74 lines
2.1 KiB
Python
from fastapi import APIRouter, HTTPException
|
|
from pydantic import BaseModel
|
|
from typing import Optional, List, Dict, Any
|
|
from services.database import (
|
|
save_custom_pattern, get_custom_patterns, delete_custom_pattern, toggle_pattern_active
|
|
)
|
|
|
|
router = APIRouter(prefix="/api/patterns", tags=["patterns"])
|
|
|
|
|
|
class PatternRequest(BaseModel):
|
|
id: Optional[str] = None
|
|
name: str
|
|
description: str
|
|
triggers: List[str]
|
|
keywords: List[str]
|
|
historical_instances: Optional[List[Dict[str, Any]]] = []
|
|
suggested_trades: Optional[List[Dict[str, Any]]] = []
|
|
asset_class: str
|
|
expected_move_pct: float
|
|
probability: float
|
|
horizon_days: int
|
|
ai_quality_score: Optional[int] = None
|
|
ai_evaluation: Optional[Dict[str, Any]] = None
|
|
source: Optional[str] = "custom"
|
|
counter_thesis: Optional[str] = None
|
|
invalidation_trigger: Optional[str] = None
|
|
invalidation_probability: Optional[float] = None
|
|
|
|
|
|
@router.get("/all")
|
|
def list_all():
|
|
"""Return all active patterns from DB (builtin + custom)."""
|
|
return get_custom_patterns()
|
|
|
|
|
|
@router.get("/builtin")
|
|
def list_builtin():
|
|
return [p for p in get_custom_patterns() if p.get("source") == "builtin"]
|
|
|
|
|
|
@router.get("/custom")
|
|
def list_custom():
|
|
return [p for p in get_custom_patterns() if p.get("source") != "builtin"]
|
|
|
|
|
|
@router.post("/custom")
|
|
def create_pattern(req: PatternRequest):
|
|
data = req.model_dump()
|
|
data["source"] = "custom"
|
|
pat_id = save_custom_pattern(data)
|
|
return {"id": pat_id, "status": "saved"}
|
|
|
|
|
|
@router.put("/custom/{pat_id}")
|
|
def update_pattern(pat_id: str, req: PatternRequest):
|
|
data = req.model_dump()
|
|
data["id"] = pat_id
|
|
save_custom_pattern(data)
|
|
return {"id": pat_id, "status": "updated"}
|
|
|
|
|
|
@router.delete("/custom/{pat_id}")
|
|
def delete_pattern(pat_id: str):
|
|
delete_custom_pattern(pat_id)
|
|
return {"status": "deleted"}
|
|
|
|
|
|
@router.put("/toggle/{pat_id}")
|
|
def toggle_pattern(pat_id: str):
|
|
"""Enable or disable a pattern (works for builtin and custom)."""
|
|
new_state = toggle_pattern_active(pat_id)
|
|
return {"id": pat_id, "is_active": new_state}
|