Files
sanguo_vnpy_v2/sanguo_factor/universe_pools.py
T
claude_dev 557479b848
CI/CD / test (push) Successful in 3s
CI/CD / nas-deploy (push) Successful in 6s
CI/CD / nas-verify (push) Successful in 9s
feat(factor): New/Result两页终端风重构落地(方案B设计稿v2用户拍板'开工') [nas]
后端3小端点(sanguo_factor/universe_pools.py+routes):/factor/universe/pools
池清单(白名单6指数+当前成分数,in_current=1池语义≠回测并集口径)/pool/{key}
成分(code+name读constituent_unified)/search?q=(代码前缀OR名称子句DISTINCT
LIMIT10);cfg=None兜底同analyzer;+6单测+2端点测(⚠️首版误覆盖batch_eval的
universe.py,git恢复后改名universe_pools,292测全绿)

前端:①New.vue终端风重写——FactorPicker(搜索+三色分组点选+已选托盘,
240因子弃下拉)+UniversePicker(三层输入:预设池一键选[跨行业30前端常量+
6指数池走后端]/搜索combobox防抖250ms带名称候选/chips池可删+复制+清空,
批量粘贴折叠兜底)+原生date input禁未来+跨度显示+校验前置(禁用+原因文案);
要素零变化(query.factor预填/hydrate回填/≥1因子+≥2标的/混合分隔)。
②Result.vue外壳——5格metric-strip(最优ICIR正红负绿/最优因子青)+IC明细
终端表格(右对齐tabular-nums,|t|≥2加粗,最优因子行青标+左青条)+tears区
tabs终端化(TearsPanel本体不动)。③factorSamples.ts常量:跨行业30只
(code+name)详情页一键直达与New页预设共用同源(LeaderboardDetail改引,
删本地重复清单)。npm run build绿(vue-tsc+rolldown)
2026-08-30 09:59:38 +08:00

83 lines
2.7 KiB
Python

"""标的池查询(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]