from datetime import date from typing import List, Optional from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy.orm import Session from app.core.database import get_db from app.api.deps import CurrentUser from app.models.screening import ScreeningSignal, AutoOrder from app.schemas.screening import ( ScreeningSignalResponse, AutoOrderResponse, WatchlistItem, ScreeningSummary, ) router = APIRouter(tags=["screening"]) @router.get("/api/screening/today", response_model=List[ScreeningSignalResponse]) async def get_today_screening( current_user: CurrentUser, db: Session = Depends(get_db), ): today = date.today() signals = ( db.query(ScreeningSignal) .filter(ScreeningSignal.screen_date == today) .order_by(ScreeningSignal.ticker) .all() ) return signals @router.get("/api/screening/history", response_model=List[ScreeningSignalResponse]) async def get_screening_history( current_user: CurrentUser, db: Session = Depends(get_db), start_date: Optional[date] = Query(None), end_date: Optional[date] = Query(None), ticker: Optional[str] = Query(None), status: Optional[str] = Query(None), limit: int = Query(100, ge=1, le=1000), ): query = db.query(ScreeningSignal) if start_date: query = query.filter(ScreeningSignal.screen_date >= start_date) if end_date: query = query.filter(ScreeningSignal.screen_date <= end_date) if ticker: query = query.filter(ScreeningSignal.ticker == ticker) if status: query = query.filter(ScreeningSignal.status == status) signals = ( query.order_by(ScreeningSignal.screen_date.desc(), ScreeningSignal.ticker) .limit(limit) .all() ) return signals @router.get("/api/screening/watchlist", response_model=List[WatchlistItem]) async def get_watchlist( current_user: CurrentUser, db: Session = Depends(get_db), ): signals = ( db.query(ScreeningSignal) .filter(ScreeningSignal.status.in_(["pending", "watching"])) .order_by(ScreeningSignal.screen_date.desc(), ScreeningSignal.ticker) .all() ) return signals @router.post("/api/screening/execute", response_model=dict) async def execute_screening( current_user: CurrentUser, db: Session = Depends(get_db), ): from app.core.config import get_settings from app.services.trading.kis_executor import KISTradeExecutor settings = get_settings() if not settings.kis_app_key or not settings.kis_app_secret: raise HTTPException(status_code=400, detail="KIS API credentials not configured") watching = ( db.query(ScreeningSignal) .filter(ScreeningSignal.status == "watching") .all() ) if not watching: return {"message": "No watching signals to execute", "orders": []} executor = KISTradeExecutor( app_key=settings.kis_app_key, app_secret=settings.kis_app_secret, account_no=settings.kis_account_no, paper_trade=settings.kis_paper_trade, ) results = [] for signal in watching: if not signal.entry_price: continue qty = 1 # placeholder - actual sizing handled by screening_job order = executor.place_buy_order( ticker=signal.ticker, qty=qty, price=int(signal.entry_price), ) auto_order = AutoOrder( order_date=date.today(), ticker=signal.ticker, order_type="buy", qty=qty, price=signal.entry_price, order_no=order.order_no, status="filled" if order.success else "rejected", screening_signal_id=signal.id, ) db.add(auto_order) if order.success: signal.status = "entered" results.append({ "ticker": signal.ticker, "success": order.success, "order_no": order.order_no, "message": order.message, }) db.commit() return {"message": f"Executed {len(results)} orders", "orders": results} @router.get("/api/trading/orders", response_model=List[AutoOrderResponse]) async def get_orders( current_user: CurrentUser, db: Session = Depends(get_db), limit: int = Query(100, ge=1, le=1000), ): orders = ( db.query(AutoOrder) .order_by(AutoOrder.order_date.desc()) .limit(limit) .all() ) return orders @router.get("/api/trading/positions", response_model=dict) async def get_trading_positions( current_user: CurrentUser, ): from app.core.config import get_settings from app.services.trading.kis_executor import KISTradeExecutor settings = get_settings() if not settings.kis_app_key or not settings.kis_app_secret: raise HTTPException(status_code=400, detail="KIS API credentials not configured") executor = KISTradeExecutor( app_key=settings.kis_app_key, app_secret=settings.kis_app_secret, account_no=settings.kis_account_no, paper_trade=settings.kis_paper_trade, ) positions = executor.get_positions() return {"positions": [ { "ticker": p.ticker, "name": p.name, "qty": p.qty, "avg_price": p.avg_price, "current_price": p.current_price, "pnl_amount": p.pnl_amount, "pnl_rate": p.pnl_rate, } for p in positions ]} @router.get("/api/trading/balance", response_model=dict) async def get_trading_balance( current_user: CurrentUser, ): from app.core.config import get_settings from app.services.trading.kis_executor import KISTradeExecutor settings = get_settings() if not settings.kis_app_key or not settings.kis_app_secret: raise HTTPException(status_code=400, detail="KIS API credentials not configured") executor = KISTradeExecutor( app_key=settings.kis_app_key, app_secret=settings.kis_app_secret, account_no=settings.kis_account_no, paper_trade=settings.kis_paper_trade, ) balance = executor.get_account_balance() return { "total_amount": balance.total_amount, "available_amount": balance.available_amount, "stock_amount": balance.stock_amount, "pnl_amount": balance.pnl_amount, }