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.

157 lines
4.6 KiB

"""
K 线数据 API 路由
"""
from datetime import datetime
from typing import Annotated, List
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session
from app.schemas import KlineRequest, KlineResponse, KlineDataItem, ResponseData
from app.services.kline_service import KlineService
from app.db.init_db import get_sqlite_db
router = APIRouter()
@router.get("/data", response_model=KlineResponse)
async def get_kline_data(
symbol: Annotated[str, Query(description="品种代码,如 IF2406")],
period: Annotated[str, Query(description="周期,如 1m, 5m, 1h, 1d")],
start: Annotated[datetime, Query(description="开始时间")],
end: Annotated[datetime, Query(description="结束时间")],
page: Annotated[int, Query(description="页码,默认 1")] = 1,
page_size: Annotated[int, Query(description="每页数量,默认 1000最大 5000")] = 1000,
):
"""
获取 K 线数据
- **symbol**: 品种代码 ( IF2406, SH0001)
- **period**: 周期 (1m, 5m, 15m, 30m, 1h, 4h, 1d, 1w)
- **start**: 开始时间 (ISO 8601 格式)
- **end**: 结束时间 (ISO 8601 格式)
- **page**: 页码 (默认 1)
- **page_size**: 每页数量 (默认 1000最大 5000)
"""
# 验证时间范围
if start >= end:
raise HTTPException(
status_code=400,
detail="开始时间必须早于结束时间"
)
# 验证开始时间不能晚于当前时间(允许 1 分钟误差)
from datetime import timedelta
if start > datetime.utcnow() + timedelta(minutes=1):
raise HTTPException(
status_code=400,
detail="开始时间不能晚于当前时间"
)
# 限制 page_size 最大值
if page_size > 5000:
page_size = 5000
if page_size < 1:
page_size = 1
if page < 1:
page = 1
try:
data = KlineService.get_kline_data(symbol, period, start, end, page, page_size)
return KlineResponse(
code=0,
message="success",
data=[KlineDataItem(**item) for item in data],
symbol=symbol,
period=period
)
except HTTPException:
raise
except Exception as e:
raise HTTPException(
status_code=500,
detail=f"Failed to fetch kline data: {str(e)}"
)
@router.get("/latest", response_model=ResponseData)
async def get_latest_kline(
symbol: Annotated[str, Query(description="品种代码")],
period: Annotated[str, Query(description="周期")],
):
"""获取最新一条 K 线数据"""
data = KlineService.get_latest_kline(symbol, period)
if not data:
return ResponseData(
code=404,
message="No data found",
data=None
)
return ResponseData(
code=0,
message="success",
data=data
)
@router.get("/symbols", response_model=ResponseData)
async def get_symbols():
"""获取所有品种代码列表"""
symbols = KlineService.get_symbols()
return ResponseData(
code=0,
message="success",
data={"symbols": symbols, "count": len(symbols)}
)
@router.get("/periods", response_model=ResponseData)
async def get_periods():
"""获取所有支持的周期"""
periods = KlineService.get_periods()
return ResponseData(
code=0,
message="success",
data={"periods": periods, "count": len(periods)}
)
@router.post("/data/batch", response_model=ResponseData)
async def batch_insert_kline(
symbol: Annotated[str, Query(description="品种代码")],
period: Annotated[str, Query(description="周期")],
kline_data: Annotated[List[KlineDataItem], Query(description="K 线数据列表")],
):
"""
批量插入 K 线数据 (管理接口)
注意此接口需要管理员权限
"""
try:
data_list = [
{
"time": item.time,
"open": item.open,
"high": item.high,
"low": item.low,
"close": item.close,
"volume": item.volume,
"amount": item.amount or 0,
"open_interest": item.open_interest or 0
}
for item in kline_data
]
count = KlineService.insert_kline_data(symbol, period, data_list)
return ResponseData(
code=0,
message="success",
data={"inserted": count}
)
except Exception as e:
raise HTTPException(
status_code=500,
detail=f"Failed to insert kline data: {str(e)}"
)