From ca0d908129cae825ea1acd97e025bdab7aabcd26 Mon Sep 17 00:00:00 2001 From: Sakurasan <26715255+Sakurasan@users.noreply.github.com> Date: Thu, 17 Sep 2026 23:27:15 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E8=BF=9E=E7=BB=AD=E4=B8=8A=E6=A6=9C?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=E4=B8=A5=E6=A0=BCN=E6=97=A5=E6=AF=8F?= =?UTF-8?q?=E5=A4=A9=E4=B8=8A=E6=A6=9C=EF=BC=8C=E7=BC=BA=E4=B8=80=E6=97=A5?= =?UTF-8?q?=E9=83=BD=E4=B8=8D=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/routes/core_stocks.py | 55 +++++++++------------------ backend/services/ai_service.py | 4 +- backend/services/ai_tools.py | 68 +++++++++++++--------------------- 3 files changed, 44 insertions(+), 83 deletions(-) diff --git a/backend/routes/core_stocks.py b/backend/routes/core_stocks.py index e8f6bd7..db10873 100644 --- a/backend/routes/core_stocks.py +++ b/backend/routes/core_stocks.py @@ -90,73 +90,52 @@ async def active_core_stocks(days: int = Query(10, ge=1, le=30, description="交 conn.close() -@router.get("/consecutive", summary="连续上榜的核心股") -async def consecutive_core_stocks(min_days: int = Query(3, ge=2, le=10, description="最小连续天数")): - from datetime import date as date_cls +@router.get("/consecutive", summary="最近N日每天上榜的核心股") +async def consecutive_core_stocks(days: int = Query(3, ge=2, le=10, description="交易日天数")): conn = get_connection() try: - # 最近30个有数据的交易日 rows = conn.execute( - "SELECT DISTINCT trade_date FROM daily_core_stocks ORDER BY trade_date DESC LIMIT 30" + "SELECT DISTINCT trade_date FROM daily_core_stocks ORDER BY trade_date DESC LIMIT ?", + (days,) ).fetchall() - all_dates = [r["trade_date"] for r in reversed(rows)] + recent_dates = [r["trade_date"] for r in reversed(rows)] - if not all_dates: - return JSONResponse({"stocks": []}, headers=_NO_CACHE_HEADERS) + if len(recent_dates) < days: + return JSONResponse({"days": days, "stocks": []}, headers=_NO_CACHE_HEADERS) - # 查询所有上榜记录 - placeholders = ",".join("?" * len(all_dates)) + placeholders = ",".join("?" * len(recent_dates)) rows = conn.execute( f"""SELECT trade_date, stock_code, stock_name, f3, cover_count FROM daily_core_stocks - WHERE trade_date IN ({placeholders}) - ORDER BY stock_code, trade_date""", - all_dates, + WHERE trade_date IN ({placeholders})""", + recent_dates, ).fetchall() - # 按股票分组,记录上榜日期 stock_dates: dict[str, dict] = {} for r in rows: code = r["stock_code"] s = stock_dates.setdefault(code, { "stockCode": code, "stockName": r["stock_name"], - "dates": [], + "dates": set(), "dailyGains": {}, }) - s["dates"].append(r["trade_date"]) + s["dates"].add(r["trade_date"]) s["dailyGains"][r["trade_date"]] = r["f3"] - # 计算连续上榜天数(从最新交易日往回数) results = [] for code, s in stock_dates.items(): - dates_list = sorted(s["dates"]) - consecutive = 0 - last_n = [] - for i in range(len(dates_list) - 1, -1, -1): - if i == len(dates_list) - 1: - consecutive = 1 - last_n.append(dates_list[i]) - else: - prev_idx = all_dates.index(dates_list[i + 1]) if dates_list[i + 1] in all_dates else -1 - curr_idx = all_dates.index(dates_list[i]) if dates_list[i] in all_dates else -1 - if prev_idx - curr_idx <= 2: - consecutive += 1 - last_n.append(dates_list[i]) - else: - break - - if consecutive >= min_days: + if len(s["dates"]) >= days: results.append({ "stockCode": code, "stockName": s["stockName"], - "consecutiveDays": consecutive, - "recentDates": last_n, - "dailyGains": {d: s["dailyGains"].get(d) for d in last_n}, + "consecutiveDays": len(s["dates"]), + "recentDates": sorted(s["dates"]), + "dailyGains": s["dailyGains"], }) results.sort(key=lambda x: -x["consecutiveDays"]) - return JSONResponse({"stocks": results}, headers=_NO_CACHE_HEADERS) + return JSONResponse({"days": days, "recentDates": recent_dates, "stocks": results}, headers=_NO_CACHE_HEADERS) finally: conn.close() diff --git a/backend/services/ai_service.py b/backend/services/ai_service.py index 23c558a..88892b9 100644 --- a/backend/services/ai_service.py +++ b/backend/services/ai_service.py @@ -33,7 +33,7 @@ SYSTEM_PROMPT = """你是一位专业的A股市场分析师,擅长从数据中 - get_market_dashboard: 获取市场整体数据(指数/涨跌统计/市场温度/连板梯队/行业强度/板块资金流/两融/海外指数与国内期货/事件情报) - get_theme_history: 获取指定日期的题材涨幅排行 - get_active_core_stocks: 获取核心股追踪数据(10日涨幅矩阵+所属题材) -- get_consecutive_core_stocks: 获取连续上榜的核心股列表(近3日及以上连续上榜),用于识别持续活跃的热点股 +- get_consecutive_core_stocks: 获取最近N个交易日每天都上榜的核心股(缺一日都不行),用于识别持续活跃的热点股 - get_stock_quote: 获取个股实时行情 - get_fund_flow: 获取个股资金流向 - get_news: 获取财经快讯(新浪7x24,用于重要消息面) @@ -81,7 +81,7 @@ DAILY_ANALYSIS_PROMPT = """请对 {trade_date}(A股交易日) 的A股市场 1. get_market_dashboard() —— 指数、涨跌统计、市场温度、连板梯队 limitLadder、板块资金流 sectorFundFlow、两融、海外指数与国内期货 globalMarkets 2. get_theme_history(date="{trade_date}") —— 今日题材涨幅榜 3. get_active_core_stocks() —— 核心股 10 日涨幅矩阵与所属题材 -4. get_consecutive_core_stocks(min_days=3) —— 连续3日及以上上榜的核心股 +4. get_consecutive_core_stocks(days=3) —— 最近3个交易日每天都上榜的核心股(缺一日都不行) 5. get_news(limit=30) —— 今日财经快讯 只有在需要核实某只具体个股时,才额外调用 get_stock_quote / get_fund_flow,合计不超过 2 次。 diff --git a/backend/services/ai_tools.py b/backend/services/ai_tools.py index ffbff9d..0abc510 100644 --- a/backend/services/ai_tools.py +++ b/backend/services/ai_tools.py @@ -46,11 +46,11 @@ TOOLS = [ "type": "function", "function": { "name": "get_consecutive_core_stocks", - "description": "获取连续上榜的核心股列表(近3日及以上连续上榜),用于识别持续活跃的热点股", + "description": "获取最近N个交易日每天都上榜的核心股(缺一日都不行),用于识别持续活跃的热点股", "parameters": { "type": "object", "properties": { - "min_days": {"type": "integer", "description": "最小连续天数,默认3"} + "days": {"type": "integer", "description": "交易日天数,默认3"} }, "required": [] } @@ -113,7 +113,7 @@ async def execute_tool(tool_name: str, arguments: dict) -> str: elif tool_name == "get_active_core_stocks": return await _get_active_core_stocks() elif tool_name == "get_consecutive_core_stocks": - return await _get_consecutive_core_stocks(arguments.get("min_days", 3)) + return await _get_consecutive_core_stocks(arguments.get("days", 3)) elif tool_name == "get_stock_quote": return await _get_stock_quote(arguments.get("code", "")) elif tool_name == "get_fund_flow": @@ -235,28 +235,28 @@ async def _get_active_core_stocks() -> str: conn.close() -async def _get_consecutive_core_stocks(min_days: int = 3) -> str: - """获取连续上榜的核心股列表""" +async def _get_consecutive_core_stocks(days: int = 3) -> str: + """获取最近N个交易日**每个交易日都上榜**的核心股,缺一日都不行""" from database import get_connection conn = get_connection() try: - # 最近30个有数据的交易日 + # 最近N个有数据的交易日 rows = conn.execute( - "SELECT DISTINCT trade_date FROM daily_core_stocks ORDER BY trade_date DESC LIMIT 30" + "SELECT DISTINCT trade_date FROM daily_core_stocks ORDER BY trade_date DESC LIMIT ?", + (days,) ).fetchall() - all_dates = [r["trade_date"] for r in reversed(rows)] + recent_dates = [r["trade_date"] for r in reversed(rows)] - if not all_dates: - return json.dumps({"dates": [], "stocks": []}, ensure_ascii=False) + if len(recent_dates) < days: + return json.dumps({"days": days, "stocks": []}, ensure_ascii=False) - # 查询所有上榜记录 - placeholders = ",".join("?" * len(all_dates)) + # 查询这些日期的上榜记录 + placeholders = ",".join("?" * len(recent_dates)) rows = conn.execute( f"""SELECT trade_date, stock_code, stock_name, f3, cover_count FROM daily_core_stocks - WHERE trade_date IN ({placeholders}) - ORDER BY stock_code, trade_date""", - all_dates, + WHERE trade_date IN ({placeholders})""", + recent_dates, ).fetchall() # 按股票分组,记录上榜日期 @@ -266,48 +266,30 @@ async def _get_consecutive_core_stocks(min_days: int = 3) -> str: s = stock_dates.setdefault(code, { "stockCode": code, "stockName": r["stock_name"], - "dates": [], + "dates": set(), "dailyGains": {}, }) - s["dates"].append(r["trade_date"]) + s["dates"].add(r["trade_date"]) s["dailyGains"][r["trade_date"]] = r["f3"] - # 计算连续上榜天数(从最新交易日往回数) + # 只保留N个交易日全部上榜的股票 results = [] for code, s in stock_dates.items(): - dates_list = sorted(s["dates"]) - # 从最新的日期往回数连续天数 - consecutive = 0 - last_n = [] - for i in range(len(dates_list) - 1, -1, -1): - if i == len(dates_list) - 1: - consecutive = 1 - last_n.append(dates_list[i]) - else: - # 检查是否连续(允许间隔1天非交易日) - prev_idx = all_dates.index(dates_list[i + 1]) if dates_list[i + 1] in all_dates else -1 - curr_idx = all_dates.index(dates_list[i]) if dates_list[i] in all_dates else -1 - if prev_idx - curr_idx <= 2: # 允许间隔1天 - consecutive += 1 - last_n.append(dates_list[i]) - else: - break - - if consecutive >= min_days: + if len(s["dates"]) >= days: results.append({ "stockCode": code, "stockName": s["stockName"], - "consecutiveDays": consecutive, - "recentDates": last_n, - "dailyGains": {d: s["dailyGains"].get(d) for d in last_n}, + "consecutiveDays": len(s["dates"]), + "recentDates": sorted(s["dates"]), + "dailyGains": s["dailyGains"], }) - # 按连续天数降序排列 results.sort(key=lambda x: -x["consecutiveDays"]) return json.dumps({ - "minDays": min_days, - "stocks": results[:30], + "days": days, + "recentDates": recent_dates, + "stocks": results, }, ensure_ascii=False) finally: conn.close()