|
|
|
|
|
"""
|
|
|
|
|
|
股票数据路由
|
|
|
|
|
|
"""
|
|
|
|
|
|
from typing import List
|
|
|
|
|
|
from fastapi import APIRouter, Depends, Query
|
|
|
|
|
|
from sqlalchemy.orm import Session
|
|
|
|
|
|
|
|
|
|
|
|
from app.db.session import get_db
|
|
|
|
|
|
from app.schemas.base import ResponseModel
|
|
|
|
|
|
from app.schemas.kline import KlineRequest, BatchKlineRequest
|
|
|
|
|
|
from app.services.stock_service import StockService
|
|
|
|
|
|
from app.models.stock_basic import StockBasic
|
|
|
|
|
|
from app.core.security import get_current_user
|
|
|
|
|
|
from app.models.user import User
|
|
|
|
|
|
from app.utils.date_utils import parse_date
|
|
|
|
|
|
|
|
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/kline", response_model=ResponseModel)
|
|
|
|
|
|
async def get_stock_kline(
|
|
|
|
|
|
codes: str = Query(..., description="股票代码,多个用逗号分隔"),
|
|
|
|
|
|
start_date: str = Query(..., description="开始日期(YYYYMMDD)"),
|
|
|
|
|
|
end_date: str = Query(..., description="结束日期(YYYYMMDD)"),
|
|
|
|
|
|
period: str = Query("daily", description="周期: daily, min1, min5, min15, min30, min60"),
|
|
|
|
|
|
db: Session = Depends(get_db),
|
|
|
|
|
|
current_user: User = Depends(get_current_user)
|
|
|
|
|
|
):
|
|
|
|
|
|
"""获取股票K线数据(含基础信息)"""
|
|
|
|
|
|
service = StockService(db)
|
|
|
|
|
|
code_list = [c.strip() for c in codes.split(",")]
|
|
|
|
|
|
start = parse_date(start_date)
|
|
|
|
|
|
end = parse_date(end_date)
|
|
|
|
|
|
|
|
|
|
|
|
kline_data = service.get_kline(code_list, start, end, period)
|
|
|
|
|
|
|
|
|
|
|
|
result = {}
|
|
|
|
|
|
for code in code_list:
|
|
|
|
|
|
stock_basic = db.query(StockBasic).filter(StockBasic.code == code).first()
|
|
|
|
|
|
|
|
|
|
|
|
result[code] = {
|
|
|
|
|
|
"basic": {
|
|
|
|
|
|
"code": stock_basic.code if stock_basic else code,
|
|
|
|
|
|
"name": stock_basic.name if stock_basic else None,
|
|
|
|
|
|
"total_shares": stock_basic.total_shares if stock_basic else None,
|
|
|
|
|
|
"float_shares": stock_basic.float_shares if stock_basic else None,
|
|
|
|
|
|
"industry_index_name": stock_basic.industry_index_name if stock_basic else None,
|
|
|
|
|
|
"industry_index_code": stock_basic.industry_index_code if stock_basic else None,
|
|
|
|
|
|
"institution_hold_ratio": float(stock_basic.institution_hold_ratio) if stock_basic and stock_basic.institution_hold_ratio else None,
|
|
|
|
|
|
"industry_level3": stock_basic.industry_level3 if stock_basic else None,
|
|
|
|
|
|
"list_date": str(stock_basic.list_date) if stock_basic and stock_basic.list_date else None
|
|
|
|
|
|
},
|
|
|
|
|
|
"kline": kline_data.get(code, [])
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return ResponseModel(data=result)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.post("/kline/batch", response_model=ResponseModel)
|
|
|
|
|
|
async def batch_get_stock_kline(
|
|
|
|
|
|
request: BatchKlineRequest,
|
|
|
|
|
|
db: Session = Depends(get_db),
|
|
|
|
|
|
current_user: User = Depends(get_current_user)
|
|
|
|
|
|
):
|
|
|
|
|
|
"""批量获取股票K线数据(含基础信息)"""
|
|
|
|
|
|
service = StockService(db)
|
|
|
|
|
|
start = parse_date(request.start_date)
|
|
|
|
|
|
end = parse_date(request.end_date)
|
|
|
|
|
|
|
|
|
|
|
|
kline_data = service.get_kline(request.codes, start, end, request.period)
|
|
|
|
|
|
|
|
|
|
|
|
result = {}
|
|
|
|
|
|
for code in request.codes:
|
|
|
|
|
|
stock_basic = db.query(StockBasic).filter(StockBasic.code == code).first()
|
|
|
|
|
|
|
|
|
|
|
|
result[code] = {
|
|
|
|
|
|
"basic": {
|
|
|
|
|
|
"code": stock_basic.code if stock_basic else code,
|
|
|
|
|
|
"name": stock_basic.name if stock_basic else None,
|
|
|
|
|
|
"total_shares": stock_basic.total_shares if stock_basic else None,
|
|
|
|
|
|
"float_shares": stock_basic.float_shares if stock_basic else None,
|
|
|
|
|
|
"industry_index_name": stock_basic.industry_index_name if stock_basic else None,
|
|
|
|
|
|
"industry_index_code": stock_basic.industry_index_code if stock_basic else None,
|
|
|
|
|
|
"institution_hold_ratio": float(stock_basic.institution_hold_ratio) if stock_basic and stock_basic.institution_hold_ratio else None,
|
|
|
|
|
|
"industry_level3": stock_basic.industry_level3 if stock_basic else None,
|
|
|
|
|
|
"list_date": str(stock_basic.list_date) if stock_basic and stock_basic.list_date else None
|
|
|
|
|
|
},
|
|
|
|
|
|
"kline": kline_data.get(code, [])
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return ResponseModel(data=result)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/kline/{code}/chart", response_model=ResponseModel)
|
|
|
|
|
|
async def get_stock_kline_chart(
|
|
|
|
|
|
code: str,
|
|
|
|
|
|
start_date: str = Query(..., description="开始日期(YYYYMMDD)"),
|
|
|
|
|
|
end_date: str = Query(..., description="结束日期(YYYYMMDD)"),
|
|
|
|
|
|
period: str = Query("daily", description="周期: daily, min1, min5, min15, min30, min60"),
|
|
|
|
|
|
db: Session = Depends(get_db),
|
|
|
|
|
|
current_user: User = Depends(get_current_user)
|
|
|
|
|
|
):
|
|
|
|
|
|
"""获取股票K线图数据(ECharts格式,含基础信息)"""
|
|
|
|
|
|
service = StockService(db)
|
|
|
|
|
|
start = parse_date(start_date)
|
|
|
|
|
|
end = parse_date(end_date)
|
|
|
|
|
|
|
|
|
|
|
|
chart_data = service.get_kline_chart_data(code, start, end, period)
|
|
|
|
|
|
|
|
|
|
|
|
stock_basic = db.query(StockBasic).filter(StockBasic.code == code).first()
|
|
|
|
|
|
|
|
|
|
|
|
chart_data["basic"] = {
|
|
|
|
|
|
"code": stock_basic.code if stock_basic else code,
|
|
|
|
|
|
"name": stock_basic.name if stock_basic else None,
|
|
|
|
|
|
"total_shares": stock_basic.total_shares if stock_basic else None,
|
|
|
|
|
|
"float_shares": stock_basic.float_shares if stock_basic else None,
|
|
|
|
|
|
"industry_index_name": stock_basic.industry_index_name if stock_basic else None,
|
|
|
|
|
|
"industry_index_code": stock_basic.industry_index_code if stock_basic else None,
|
|
|
|
|
|
"institution_hold_ratio": float(stock_basic.institution_hold_ratio) if stock_basic and stock_basic.institution_hold_ratio else None,
|
|
|
|
|
|
"industry_level3": stock_basic.industry_level3 if stock_basic else None,
|
|
|
|
|
|
"list_date": str(stock_basic.list_date) if stock_basic and stock_basic.list_date else None
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return ResponseModel(data=chart_data)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/basic/{code}", response_model=ResponseModel)
|
|
|
|
|
|
async def get_stock_basic(
|
|
|
|
|
|
code: str,
|
|
|
|
|
|
db: Session = Depends(get_db),
|
|
|
|
|
|
current_user: User = Depends(get_current_user)
|
|
|
|
|
|
):
|
|
|
|
|
|
"""获取股票基础信息"""
|
|
|
|
|
|
stock_basic = db.query(StockBasic).filter(StockBasic.code == code).first()
|
|
|
|
|
|
|
|
|
|
|
|
if not stock_basic:
|
|
|
|
|
|
return ResponseModel(code=404, message="股票不存在")
|
|
|
|
|
|
|
|
|
|
|
|
return ResponseModel(data={
|
|
|
|
|
|
"code": stock_basic.code,
|
|
|
|
|
|
"name": stock_basic.name,
|
|
|
|
|
|
"total_shares": stock_basic.total_shares,
|
|
|
|
|
|
"float_shares": stock_basic.float_shares,
|
|
|
|
|
|
"industry_index_name": stock_basic.industry_index_name,
|
|
|
|
|
|
"industry_index_code": stock_basic.industry_index_code,
|
|
|
|
|
|
"institution_hold_ratio": float(stock_basic.institution_hold_ratio) if stock_basic.institution_hold_ratio else None,
|
|
|
|
|
|
"industry_level3": stock_basic.industry_level3,
|
|
|
|
|
|
"list_date": str(stock_basic.list_date) if stock_basic.list_date else None
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/snapshot", response_model=ResponseModel)
|
|
|
|
|
|
async def get_stock_snapshot(
|
|
|
|
|
|
codes: str = Query(..., description="股票代码,多个用逗号分隔"),
|
|
|
|
|
|
start_date: str = Query(..., description="开始日期(YYYYMMDD)"),
|
|
|
|
|
|
end_date: str = Query(..., description="结束日期(YYYYMMDD)"),
|
|
|
|
|
|
db: Session = Depends(get_db),
|
|
|
|
|
|
current_user: User = Depends(get_current_user)
|
|
|
|
|
|
):
|
|
|
|
|
|
"""获取股票历史快照数据"""
|
|
|
|
|
|
return ResponseModel(data={"message": "功能开发中"})
|