102 lines
3.5 KiB
Python
102 lines
3.5 KiB
Python
"""推荐列表 API"""
|
||
|
||
from fastapi import APIRouter
|
||
|
||
from app.engine.recommender import (
|
||
refresh_recommendations,
|
||
get_latest_recommendations,
|
||
get_recommendation_history,
|
||
get_performance_stats,
|
||
)
|
||
from app.config import is_trading_hours
|
||
|
||
router = APIRouter(prefix="/api/recommendations", tags=["recommendations"])
|
||
|
||
|
||
@router.get("/latest")
|
||
async def get_latest():
|
||
"""获取最新推荐列表"""
|
||
result = await get_latest_recommendations()
|
||
|
||
mt = result.get("market_temp")
|
||
return {
|
||
"market_temperature": {
|
||
"trade_date": mt.trade_date if mt else "",
|
||
"temperature": mt.temperature if mt else 0,
|
||
"up_count": mt.up_count if mt else 0,
|
||
"down_count": mt.down_count if mt else 0,
|
||
"limit_up_count": mt.limit_up_count if mt else 0,
|
||
"limit_down_count": mt.limit_down_count if mt else 0,
|
||
"max_streak": mt.max_streak if mt else 0,
|
||
"broken_rate": mt.broken_rate if mt else 0,
|
||
"index_above_ma20": mt.index_above_ma20 if mt else False,
|
||
} if mt else None,
|
||
"recommendations": [
|
||
{
|
||
"ts_code": r.ts_code,
|
||
"name": r.name,
|
||
"sector": r.sector,
|
||
"score": r.score,
|
||
"level": r.level,
|
||
"signal": r.signal,
|
||
"market_temp_score": r.market_temp_score,
|
||
"sector_score": r.sector_score,
|
||
"capital_score": r.capital_score,
|
||
"technical_score": r.technical_score,
|
||
"position_score": r.position_score,
|
||
"valuation_score": r.valuation_score,
|
||
"entry_price": r.entry_price,
|
||
"target_price": r.target_price,
|
||
"stop_loss": r.stop_loss,
|
||
"reasons": r.reasons,
|
||
"risk_note": r.risk_note,
|
||
"llm_analysis": r.llm_analysis,
|
||
"entry_timing": r.entry_timing,
|
||
"llm_score": r.llm_score,
|
||
"strategy": r.strategy,
|
||
"entry_signal_type": r.entry_signal_type,
|
||
"scan_session": r.scan_session,
|
||
"created_at": r.created_at.isoformat() if r.created_at else None,
|
||
}
|
||
for r in result.get("recommendations", [])
|
||
],
|
||
"scan_mode": result.get("scan_mode", "unknown"),
|
||
}
|
||
|
||
|
||
@router.post("/refresh")
|
||
async def refresh(scan_session: str = "manual"):
|
||
"""手动触发一次全量筛选"""
|
||
result = await refresh_recommendations(scan_session=scan_session)
|
||
rec_count = len(result.get("recommendations", []))
|
||
mt = result.get("market_temp")
|
||
return {
|
||
"status": "ok",
|
||
"count": rec_count,
|
||
"temperature": mt.temperature if mt else 0,
|
||
"scan_mode": result.get("scan_mode", "unknown"),
|
||
"is_trading": is_trading_hours(),
|
||
}
|
||
|
||
|
||
@router.get("/status")
|
||
async def get_scan_status():
|
||
"""获取当前扫描状态信息"""
|
||
return {
|
||
"is_trading": is_trading_hours(),
|
||
"scan_mode": "intraday" if is_trading_hours() else "post_market",
|
||
"description": "盘中实时扫描(腾讯行情)" if is_trading_hours() else "盘后分析(Tushare日级数据)",
|
||
}
|
||
|
||
|
||
@router.get("/history")
|
||
async def get_history(days: int = 7):
|
||
"""获取历史推荐(按日期分组)"""
|
||
return await get_recommendation_history(days)
|
||
|
||
|
||
@router.get("/performance")
|
||
async def performance():
|
||
"""获取推荐胜率统计"""
|
||
return await get_performance_stats()
|