"""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, live_recommendation=True, exclude_open_trade_tickers=True, exclude_reentry_gate_locked_tickers=True, ) 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 setup-outcome statistics (gate barrier diagnostic). Outcomes come from the nightly outcome_evaluator: win = gate target first, loss = stop first, expired = neither in the window. This is **not** the production ATR-trail book; it checks setup grading plumbing only. With qualified_only, overall/direction/action cover only gate-clearing setups; the confidence breakdown always covers all setups. """ 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, live_recommendation=True, include_reentry_gate_lock=True, ) 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)