125 lines
3.3 KiB
Python
125 lines
3.3 KiB
Python
from __future__ import annotations
|
|
|
|
from datetime import date, timedelta
|
|
|
|
from fastapi import APIRouter, Depends
|
|
from pydantic import BaseModel
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.core.auth import token_manager
|
|
from app.core.database import get_db
|
|
from app.models.stock import Trade
|
|
from app.services.trading import trading_service
|
|
|
|
router = APIRouter(prefix="/api/trading", tags=["trading"])
|
|
|
|
|
|
class OrderRequest(BaseModel):
|
|
stock_code: str
|
|
side: str # buy / sell
|
|
qty: int
|
|
price: int = 0
|
|
order_type: str = "00"
|
|
|
|
|
|
class CancelRequest(BaseModel):
|
|
order_no: str
|
|
stock_code: str
|
|
qty: int
|
|
|
|
|
|
@router.post("/order")
|
|
async def place_order(data: OrderRequest, db: Session = Depends(get_db)) -> dict:
|
|
result = await trading_service.place_order(
|
|
stock_code=data.stock_code,
|
|
side=data.side,
|
|
qty=data.qty,
|
|
price=data.price,
|
|
order_type=data.order_type,
|
|
)
|
|
|
|
trade = Trade(
|
|
order_no=result.get("order_no", ""),
|
|
stock_code=data.stock_code,
|
|
stock_name="",
|
|
side=data.side,
|
|
qty=data.qty,
|
|
price=data.price,
|
|
order_type=data.order_type,
|
|
status="filled" if result.get("rt_cd") == "0" else "rejected",
|
|
)
|
|
db.add(trade)
|
|
db.commit()
|
|
|
|
return result
|
|
|
|
|
|
@router.post("/cancel")
|
|
async def cancel_order(data: CancelRequest) -> dict:
|
|
result = await trading_service.cancel_order(
|
|
order_no=data.order_no,
|
|
stock_code=data.stock_code,
|
|
qty=data.qty,
|
|
)
|
|
return result
|
|
|
|
|
|
@router.get("/history")
|
|
async def get_trade_history(
|
|
stock_code: str | None = None,
|
|
start_date: str | None = None,
|
|
end_date: str | None = None,
|
|
limit: int = 100,
|
|
db: Session = Depends(get_db),
|
|
) -> dict:
|
|
source = "local"
|
|
|
|
if token_manager.is_authenticated:
|
|
if not start_date:
|
|
start_date = (date.today() - timedelta(days=30)).strftime("%Y%m%d")
|
|
if not end_date:
|
|
end_date = date.today().strftime("%Y%m%d")
|
|
|
|
try:
|
|
kis_trades = await trading_service.get_daily_ccled(
|
|
start_date=start_date,
|
|
end_date=end_date,
|
|
stock_code=stock_code or "",
|
|
)
|
|
if kis_trades:
|
|
source = "kis"
|
|
return {"source": source, "trades": kis_trades[:limit]}
|
|
except Exception:
|
|
pass
|
|
|
|
query = db.query(Trade).order_by(Trade.created_at.desc())
|
|
if stock_code:
|
|
query = query.filter(Trade.stock_code == stock_code)
|
|
trades = query.limit(limit).all()
|
|
return {
|
|
"source": source,
|
|
"trades": [
|
|
{
|
|
"id": t.id,
|
|
"order_no": t.order_no,
|
|
"stock_code": t.stock_code,
|
|
"stock_name": t.stock_name,
|
|
"side": t.side,
|
|
"qty": t.qty,
|
|
"price": t.price,
|
|
"order_type": t.order_type,
|
|
"status": t.status,
|
|
"strategy_id": t.strategy_id,
|
|
"created_at": t.created_at.isoformat() if t.created_at else None,
|
|
"filled_at": t.filled_at.isoformat() if t.filled_at else None,
|
|
}
|
|
for t in trades
|
|
],
|
|
}
|
|
|
|
|
|
@router.get("/account")
|
|
async def get_account() -> dict:
|
|
from app.services.account import account_service
|
|
return await account_service.get_balance()
|