galaxis-po/backend/app/api/screening.py

205 lines
6.2 KiB
Python
Raw Normal View History

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,
}