"""标的池查询(New 页三层输入的后端): 预设指数池 + 标的搜索. 数据源 constituent_unified(与组合回测同库同表)。池语义 = 当前成分 (in_current=1)——"沪深300 池"给用户的就是当下的 300 只,历史并集(含被踢) 是回测口径,不是选池口径。跨行业 30 只样本是前端常量(与详情页一键直达 同源),不走本模块。 """ import sqlite3 # 池白名单:顺序即前端展示顺序(index_code, 中文名) POOLS: list[tuple[str, str]] = [ ("000300", "沪深300"), ("000905", "中证500"), ("000852", "中证1000"), ("932000", "中证2000"), ("000016", "上证50"), ("000985", "中证全A"), ] _POOL_NAMES = dict(POOLS) def _conn() -> sqlite3.Connection: """连行情库(constituent_unified 所在)。cfg 兜底同 analyzer(None→默认配置).""" from sanguo_data.config import load_config, find_config_path cfg = load_config(find_config_path()) vnpy_db = getattr(cfg, "data_paths", {}).get("vnpy_db") if not vnpy_db: raise RuntimeError("config 缺 data_paths.vnpy_db — 检查 data_platform.yaml 初始化") return sqlite3.connect(vnpy_db, timeout=30) def list_pools() -> list[dict]: """预设池清单(白名单顺序 + 当前成分数).""" out: list[dict] = [] conn = _conn() try: for code, name in POOLS: n = conn.execute( "SELECT COUNT(*) FROM constituent_unified " "WHERE index_code=? AND in_current=1", (code,), ).fetchone()[0] out.append({"key": code, "name": name, "count": int(n)}) finally: conn.close() return out def pool_stocks(key: str) -> list[dict]: """池成分(当前, 代码升序): [{code, name}]。白名单外的 key 返回 [](合法缺失).""" if key not in _POOL_NAMES: return [] conn = _conn() try: rows = conn.execute( "SELECT code, code_name FROM constituent_unified " "WHERE index_code=? AND in_current=1 ORDER BY code", (key,), ).fetchall() finally: conn.close() return [{"code": str(c), "name": str(n or c)} for c, n in rows] def search_stocks(q: str, limit: int = 10) -> list[dict]: """标的搜索: 代码前缀 OR 名称子串(同 code 多指数去重), LIMIT.""" v = q.strip() if not v: return [] conn = _conn() try: rows = conn.execute( "SELECT DISTINCT code, code_name FROM constituent_unified " "WHERE code LIKE ? OR code_name LIKE ? " "ORDER BY code LIMIT ?", (f"{v}%", f"%{v}%", limit), ).fetchall() finally: conn.close() return [{"code": str(c), "name": str(n or c)} for c, n in rows]