Files
OpenFin/backend/routers/profiles.py
OpenSquared d256b65d30 Initial commit — GeoOptions Intelligence Cockpit v2.0
Stack: FastAPI + React/TypeScript + SQLite + GPT-4o
Features: Radar géopolitique, Marchés, Régime Macro, Journal de Bord MTM,
Rapport IA, Super Contexte (base de raisonnement évolutive), Boucle feedback IA.
Deploy: Docker + docker-compose + nginx pour openfin.open-squared.tech

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 20:29:59 +02:00

89 lines
2.8 KiB
Python

from fastapi import APIRouter, HTTPException
from pydantic import BaseModel
from typing import Optional
from services.database import get_risk_profiles, upsert_risk_profile, delete_risk_profile, _compute_trade_score
router = APIRouter(prefix="/api/profiles", tags=["profiles"])
class RiskProfileRequest(BaseModel):
id: Optional[int] = None
name: str
min_score: int
min_gain_pct: float
color: Optional[str] = "#3b82f6"
enabled: Optional[bool] = True
sort_order: Optional[int] = 0
@router.get("")
def list_profiles():
"""List all risk profiles ordered by sort_order."""
profiles = get_risk_profiles()
# Annotate each profile with the EV breakeven info
result = []
for p in profiles:
# At the exact frontier: score = min_score, gain = min_gain_pct
_, ev_net, trade_score = _compute_trade_score(p["min_score"], p["min_gain_pct"])
result.append({
**p,
"ev_net_at_frontier": round(ev_net, 3),
"trade_score_at_frontier": trade_score,
})
return {"profiles": result}
@router.post("")
def create_profile(req: RiskProfileRequest):
"""Create a new risk profile."""
if not (0 <= req.min_score <= 100):
raise HTTPException(400, "min_score must be between 0 and 100")
if req.min_gain_pct < 0:
raise HTTPException(400, "min_gain_pct must be >= 0")
pid = upsert_risk_profile(req.model_dump())
profiles = get_risk_profiles()
return {"id": pid, "profiles": profiles}
@router.put("/{profile_id}")
def update_profile(profile_id: int, req: RiskProfileRequest):
"""Update an existing risk profile."""
if not (0 <= req.min_score <= 100):
raise HTTPException(400, "min_score must be between 0 and 100")
data = req.model_dump()
data["id"] = profile_id
upsert_risk_profile(data)
return {"profiles": get_risk_profiles()}
@router.delete("/{profile_id}")
def remove_profile(profile_id: int):
"""Delete a risk profile."""
profiles = get_risk_profiles()
if len([p for p in profiles if p["enabled"]]) <= 1:
# Allow deletion but warn
pass
delete_risk_profile(profile_id)
return {"profiles": get_risk_profiles()}
@router.get("/preview")
def preview_score(score: int = 50, gain_pct: float = 100.0):
"""
Preview the trade metrics for a given (score, gain_pct) pair.
Useful for the Config UI slider simulation.
"""
ev_gross, ev_net, trade_score = _compute_trade_score(score, gain_pct)
profiles = get_risk_profiles(enabled_only=True)
from services.database import _matches_profile
matched = _matches_profile(score, gain_pct, profiles)
return {
"score": score,
"gain_pct": gain_pct,
"ev_gross": ev_gross,
"ev_net": ev_net,
"trade_score": trade_score,
"matched_profile": matched,
"accepted": matched is not None,
}