Add activation thresholds: qualified-signal defaults and views
Deploy / lint (push) Successful in 7s
Deploy / test (push) Successful in 32s
Deploy / deploy (push) Successful in 24s

Admin-configurable thresholds (min R:R, default 2.0; min confidence,
default 70%) defining what counts as an actionable signal:

- Admin Settings: new Activation Thresholds panel
  (GET/PUT /admin/settings/activation)
- GET /trades/activation exposes values to all users with access
- Signals/Setups: filters initialize from activation values
- Track Record: "Qualified signals only" toggle (default on) via
  min_rr/min_confidence params on /trades/performance; the
  confidence breakdown always covers the full population so the
  thresholds can be validated against outcomes
- Dashboard: "Qualified" metric and qualified-first Top Setups
- Outcome evaluator unchanged: every setup is still evaluated

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-06-12 18:16:04 +02:00
parent d139dd0390
commit 6da65b8d8f
20 changed files with 440 additions and 29 deletions
+23
View File
@@ -9,6 +9,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
from app.dependencies import get_db, require_admin
from app.models.user import User
from app.schemas.admin import (
ActivationConfigUpdate,
CreateUserRequest,
DataCleanupRequest,
JobToggle,
@@ -148,6 +149,28 @@ async def update_recommendation_settings(
return APIEnvelope(status="success", data=updated)
@router.get("/admin/settings/activation", response_model=APIEnvelope)
async def get_activation_settings(
_admin: User = Depends(require_admin),
db: AsyncSession = Depends(get_db),
):
config = await admin_service.get_activation_config(db)
return APIEnvelope(status="success", data=config)
@router.put("/admin/settings/activation", response_model=APIEnvelope)
async def update_activation_settings(
body: ActivationConfigUpdate,
_admin: User = Depends(require_admin),
db: AsyncSession = Depends(get_db),
):
updated = await admin_service.update_activation_config(
db,
body.model_dump(exclude_unset=True, exclude_none=True),
)
return APIEnvelope(status="success", data=updated)
@router.get("/admin/settings/ticker-universe", response_model=APIEnvelope)
async def get_ticker_universe_setting(
_admin: User = Depends(require_admin),