You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

177 lines
5.1 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# -*- coding: utf-8 -*-
"""
===================================
股票数据接口
===================================
职责:
1. 提供 GET /api/v1/stocks/{code}/quote 实时行情接口
2. 提供 GET /api/v1/stocks/{code}/history 历史行情接口
"""
import logging
from fastapi import APIRouter, HTTPException, Query
from api.v1.schemas.stocks import (
StockQuote,
StockHistoryResponse,
KLineData,
)
from api.v1.schemas.common import ErrorResponse
from src.services.stock_service import StockService
logger = logging.getLogger(__name__)
router = APIRouter()
@router.get(
"/{stock_code}/quote",
response_model=StockQuote,
responses={
200: {"description": "行情数据"},
404: {"description": "股票不存在", "model": ErrorResponse},
500: {"description": "服务器错误", "model": ErrorResponse},
},
summary="获取股票实时行情",
description="获取指定股票的最新行情数据"
)
def get_stock_quote(stock_code: str) -> StockQuote:
"""
获取股票实时行情
获取指定股票的最新行情数据
Args:
stock_code: 股票代码(如 600519、00700、AAPL
Returns:
StockQuote: 实时行情数据
Raises:
HTTPException: 404 - 股票不存在
"""
try:
service = StockService()
# 使用 def 而非 async defFastAPI 自动在线程池中执行
result = service.get_realtime_quote(stock_code)
if result is None:
raise HTTPException(
status_code=404,
detail={
"error": "not_found",
"message": f"未找到股票 {stock_code} 的行情数据"
}
)
return StockQuote(
stock_code=result.get("stock_code", stock_code),
stock_name=result.get("stock_name"),
current_price=result.get("current_price", 0.0),
change=result.get("change"),
change_percent=result.get("change_percent"),
open=result.get("open"),
high=result.get("high"),
low=result.get("low"),
prev_close=result.get("prev_close"),
volume=result.get("volume"),
amount=result.get("amount"),
update_time=result.get("update_time")
)
except HTTPException:
raise
except Exception as e:
logger.error(f"获取实时行情失败: {e}", exc_info=True)
raise HTTPException(
status_code=500,
detail={
"error": "internal_error",
"message": f"获取实时行情失败: {str(e)}"
}
)
@router.get(
"/{stock_code}/history",
response_model=StockHistoryResponse,
responses={
200: {"description": "历史行情数据"},
422: {"description": "不支持的周期参数", "model": ErrorResponse},
500: {"description": "服务器错误", "model": ErrorResponse},
},
summary="获取股票历史行情",
description="获取指定股票的历史 K 线数据"
)
def get_stock_history(
stock_code: str,
period: str = Query("daily", description="K 线周期", pattern="^(daily|weekly|monthly)$"),
days: int = Query(30, ge=1, le=365, description="获取天数")
) -> StockHistoryResponse:
"""
获取股票历史行情
获取指定股票的历史 K 线数据
Args:
stock_code: 股票代码
period: K 线周期 (daily/weekly/monthly)
days: 获取天数
Returns:
StockHistoryResponse: 历史行情数据
"""
try:
service = StockService()
# 使用 def 而非 async defFastAPI 自动在线程池中执行
result = service.get_history_data(
stock_code=stock_code,
period=period,
days=days
)
# 转换为响应模型
data = [
KLineData(
date=item.get("date"),
open=item.get("open"),
high=item.get("high"),
low=item.get("low"),
close=item.get("close"),
volume=item.get("volume"),
amount=item.get("amount"),
change_percent=item.get("change_percent")
)
for item in result.get("data", [])
]
return StockHistoryResponse(
stock_code=stock_code,
stock_name=result.get("stock_name"),
period=period,
data=data
)
except ValueError as e:
# period 参数不支持的错误(如 weekly/monthly
raise HTTPException(
status_code=422,
detail={
"error": "unsupported_period",
"message": str(e)
}
)
except Exception as e:
logger.error(f"获取历史行情失败: {e}", exc_info=True)
raise HTTPException(
status_code=500,
detail={
"error": "internal_error",
"message": f"获取历史行情失败: {str(e)}"
}
)