"""Trades router — R:R scanner trade setup endpoints.""" from fastapi import APIRouter, Depends, Query from sqlalchemy.ext.asyncio import AsyncSession from app.dependencies import get_db, require_access from app.schemas.common import APIEnvelope from app.schemas.trade_setup import RecommendationSummaryResponse, TradeSetupResponse from app.services import admin_service from app.services.outcome_service import get_performance_stats from app.services.rr_scanner_service import get_trade_setup_history, get_trade_setups router = APIRouter(tags=["trades"]) @router.get("/trades", response_model=APIEnvelope) async def list_trade_setups( direction: str | None = Query( None, description="Filter by direction: long or short" ), min_confidence: float | None = Query( None, ge=0, le=100, description="Minimum confidence score" ), recommended_action: str | None = Query( None, description="Filter by action: LONG_HIGH, LONG_MODERATE, SHORT_HIGH, SHORT_MODERATE, NEUTRAL", ), _user=Depends(require_access), db: AsyncSession = Depends(get_db), ) -> APIEnvelope: """Get latest trade setups with recommendation data.""" rows = await get_trade_setups( db, direction=direction, min_confidence=min_confidence, recommended_action=recommended_action, ) data = [] for row in rows: summary = RecommendationSummaryResponse( action=row.get("recommended_action") or "NEUTRAL", reasoning=row.get("reasoning"), risk_level=row.get("risk_level"), composite_score=row["composite_score"], ) payload = {**row, "recommendation_summary": summary} data.append(TradeSetupResponse(**payload).model_dump(mode="json")) return APIEnvelope(status="success", data=data) @router.get("/trades/activation", response_model=APIEnvelope) async def get_activation_thresholds( _user=Depends(require_access), db: AsyncSession = Depends(get_db), ) -> APIEnvelope: """Activation thresholds (min R:R, min confidence) for actionable signals. Readable by any user with access — drives Signals-page default filters and the Dashboard's qualified-setup metrics. Configured by admins via PUT /admin/settings/activation. """ config = await admin_service.get_activation_config(db) return APIEnvelope(status="success", data=config) @router.get("/trades/performance", response_model=APIEnvelope) async def get_trade_performance( qualified_only: bool = Query( False, description="Restrict overall/direction/action stats to setups that clear the activation gate" ), _user=Depends(require_access), db: AsyncSession = Depends(get_db), ) -> APIEnvelope: """Aggregate outcome statistics over evaluated trade setups. Outcomes are written by the nightly outcome_evaluator job (win = target hit first, loss = stop hit first, expired = neither within the window). With qualified_only, the overall/direction/action breakdowns cover only setups clearing the activation gate; the confidence breakdown always covers all setups so the gate can be validated against it. """ config = await admin_service.get_activation_config(db) if qualified_only else None stats = await get_performance_stats(db, config=config) return APIEnvelope(status="success", data=stats) @router.get("/trades/{symbol}", response_model=APIEnvelope) async def get_ticker_trade_setups( symbol: str, _user=Depends(require_access), db: AsyncSession = Depends(get_db), ) -> APIEnvelope: rows = await get_trade_setups(db, symbol=symbol) data = [] for row in rows: summary = RecommendationSummaryResponse( action=row.get("recommended_action") or "NEUTRAL", reasoning=row.get("reasoning"), risk_level=row.get("risk_level"), composite_score=row["composite_score"], ) payload = {**row, "recommendation_summary": summary} data.append(TradeSetupResponse(**payload).model_dump(mode="json")) return APIEnvelope(status="success", data=data) @router.get("/trades/{symbol}/history", response_model=APIEnvelope) async def get_ticker_trade_history( symbol: str, _user=Depends(require_access), db: AsyncSession = Depends(get_db), ) -> APIEnvelope: rows = await get_trade_setup_history(db, symbol=symbol) data = [] for row in rows: summary = RecommendationSummaryResponse( action=row.get("recommended_action") or "NEUTRAL", reasoning=row.get("reasoning"), risk_level=row.get("risk_level"), composite_score=row["composite_score"], ) payload = {**row, "recommendation_summary": summary} data.append(TradeSetupResponse(**payload).model_dump(mode="json")) return APIEnvelope(status="success", data=data)