Compare commits

..

3 Commits

Author SHA1 Message Date
0f8b9a7255 update 2026-08-14 17:34:26 +08:00
a9bf369a41 chore: 接入 Tushare skill 与 MCP Server
- .agents/skills/tushare + .claude/skills 符号链接:自然语言查A股/财报/资金流/板块等数据
- .mcp.json:tushareMcp(HTTP 型,token 鉴权),重启 Claude Code 后生效
- skills-lock.json:skill 版本锁定

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 15:03:40 +08:00
528357c3f5 feat: AI 自然语言选股(GLM)+ 全市场数据管道 + 远程 PostgreSQL
- 首页双入口(智能选股/策略回测):引入 vue-router,顶部导航
- 智能选股:自然语言 -> LLM 解析结构化条件(智谱 GLM,OpenAI 兼容,/v4 兼容)-> SQL 快照预筛 + pandas 指标过滤(复用 indicators 单一事实源)
- 条件模型:指标 vs 常数/指标(value_indicator,如 DIF>DEA、close<布林下轨)、lookback+match 表达连续N天/近N天任一天、市值/PE/PB/换手率快照条件、默认排除 ST/退市/北交所
- 全市场数据同步:按 trade_date 批量拉取未复权日线(与回测 candles qfq 隔离),交易日历/股票列表本地缓存,daily_basic 仅最新截面,Tushare 限频兜底(分钟级重试/小时级降级)
- 存储:DATABASE_URL 切远程 PostgreSQL(cirry.cn/stock),本地 SQLite 已移除
- .env 入库(私有仓库);smoke_test 扩展选股链路

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 14:49:53 +08:00
40 changed files with 4445 additions and 15 deletions

View File

@@ -0,0 +1,822 @@
---
name: tushare
description: 面向中文自然语言的 Tushare 数据研究技能。用于把“看看这只股票最近怎么样”“帮我查财报趋势”“最近哪个板块最强”“北向资金在买什么”“给我导出一份行情数据”这类请求,转成可执行的数据获取、清洗、对比、筛选、导出与简要分析流程。适用于 A 股、指数、ETF/基金、财务、估值、资金流、公告新闻、板块概念与宏观数据等研究场景。
author: tushare.pro
version: 1.1.12
credentials:
- name: TUSHARE_TOKEN
description: Tushare Token用于认证和授权访问Tushare数据服务。
how_to_get: "https://tushare.pro/register"
requirements:
python: 3.7+
packages:
- name: tushare
environment_variables:
- name: TUSHARE_TOKEN
required: false
sensitive: true
network_access: true
---
# tushare
把自然语言财经数据请求,转成可执行的 Tushare 数据工作流。
这是一个面向自然语言的金融数据研究 skill。
## What this skill is for
使用这个 skill 的典型场景:
- 看某只股票、指数、ETF 最近走势
- 查公司基本资料、估值、财务趋势
- 做多标的横向对比
- 看资金流、北向资金、龙虎榜、板块强弱
- 梳理公告、新闻、研报、政策线索
- 查看 CPI / PPI / PMI / 社融 / 利率等宏观数据
- 导出 CSV / parquet 供后续分析或回测使用
- 生成简洁研究摘要,而不是只吐原始字段表
先理解用户要解决什么问题,再去选接口、取数、整理、解释、交付。
***
## When to use
当用户表达以下意图时,优先使用本 skill
### 行情 / 趋势
- 看下 XX 最近怎么样
- XX 这段时间涨得怎么样
- 今年以来表现如何
- 最近有没有放量
- 这票最近强不强
### 财务 / 估值 / 公司质量
- 看下 XX 财报
- 最近几个季度利润趋势
- 财务质量怎么样
- 现金流好不好
- 现在估值算高吗
- 帮我看 PE / PB / ROE / 毛利率
### 对比 / 排行 / 筛选
- XX 和 YY 谁更强
- 帮我横向比较一下
- 哪些公司利润增长更快
- 帮我筛一下高 ROE 低负债
- 给我排个前十
### 板块 / 指数 / 主题
- 最近哪个板块最强
- 半导体最近怎么样
- 机器人为什么涨
- 指数成分股有哪些
- 哪些主题最热
### 资金流 / 情绪
- 最近资金在买什么
- 北向资金最近流向哪里
- 哪个板块最吸金
- 主力资金流入最多的是谁
- 龙虎榜上有什么看点
### 公告 / 新闻 / 研报 / 政策
- 最近有什么公告
- 帮我梳理下 XX 公告
- 最近有没有什么催化
- 最近新闻面怎么样
- 最近有什么重要政策
### 宏观 / 跨市场
- 最近宏观环境怎么样
- CPI / PMI 最近怎么看
- 当前市场风格偏什么
- 大盘环境偏多还是偏空
- 港股 / 美股 / 美债最近怎么样
### 数据导出 / 研究准备
- 给我导出一份行情数据
- 把近两年日线拉成 CSV
- 生成可回测的数据表
- 拉一个研究表供后续分析
***
## What this skill is NOT for
这个 skill 不适合:
- 直接给买卖建议或替代投资顾问
- 自动下单或执行交易
- 需要毫秒级实时交易决策的场景
- 复杂回测引擎、组合优化系统本身的实现(那是另一个工程)
- 在没有 Tushare 权限/积分支持的情况下强行伪造数据
如果数据权限不够、接口不可用或时间范围不合理,要明确说出限制,不要硬编。
***
## Natural-language trigger guide
即使用户完全不说 `tushare``financials``macro` 这些术语,只要意图符合以下含义,也应该触发本 skill。
### 常见口语触发
- 看看这个股票最近怎么样
- 给我快速研究一下 XX
- 上次说的那只票现在什么情况
- 帮我看下财报
- 最近哪个板块最强
- 北向最近在买什么
- 有什么催化消息
- 这个公司值不值得重点看
- 给我拉份数据
- 导出成 CSV
- 帮我筛一批票
- 把这几个公司对比一下
### 中文自然语言优先原则
用户说人话时,先理解任务,不要先回到接口名和字段名。
优先把:
- “最近” 解释成合理时间窗
- “财报” 解释成最近 8 个季度 / 最近年度
- “强不强” 解释成走势 + 相对强弱 + 活跃度
- “资金关注” 解释成净流入、活跃成交、龙虎榜/北向等可用口径
如果任务有多个合理解释,再做最小澄清。
***
## Environment check
在真正请求数据之前,先做前置校验:
1. 检查 Python 是否可用, 版本要求 3.7+
2. 检查 `tushare` 包是否已安装·
3. 检查 `TUSHARE_TOKEN` 是否存在.
4. 必要时做一次轻量接口冒烟测试(如交易日历 / 基础接口)
5. 如用户请求高权限接口,提前提示可能存在积分/权限限制
若缺失 token直接提示最短修复路径例如
```bash
export TUSHARE_TOKEN=your_token
```
不要等到主查询跑失败了才暴露环境问题。
***
## Intent taxonomy
先识别任务类型,再决定接口组合。
### 1. 行情 / 趋势
典型问题:
- 最近走势怎么样
- 今年涨了多少
- 最近波动大不大
- 最近有没有放量
常用接口:
- `daily`
- `pro_bar`
- `weekly`
- `monthly`
- `stk_mins`
- `rt_k` / `rt_min`(如确需实时口径且权限允许)
- `daily_basic`
### 2. 基本资料 / 标的识别
典型问题:
- 这是什么公司 / 什么指数 / 什么基金
- 是创业板吗 / 是 ST 吗 / 什么时候上市
常用接口:
- `stock_basic`
- `fund_basic`
- `index_basic`
- `stock_company`
- `stock_st` / `st`
### 3. 财务 / 公司质量
典型问题:
- 最近几个季度利润趋势
- 最近几个季度营收和净利润趋势
- 财务质量怎么样
- ROE / 毛利率 / 现金流如何
常用接口:
- `income`(营收 / 净利润趋势优先)
- `fina_indicator`ROE / 毛利率 / 净利率等质量指标补充)
- `balancesheet`
- `cashflow`
- `forecast`
- `express`
- `disclosure_date`
### 4. 估值 / 基本面指标
典型问题:
- 现在估值高不高
- 谁更便宜
- PE / PB / 股息率如何
常用接口:
- `daily_basic`
- `fina_indicator`
### 5. 资金流 / 市场行为
典型问题:
- 北向最近买什么
- 主力资金流向
- 龙虎榜情况
常用接口:
- `moneyflow`
- `moneyflow_hsgt`
- `hsgt_top10`
- `top_list`
- `top_inst`
- `moneyflow_ind_dc`
- `moneyflow_mkt_dc`
### 6. 板块 / 指数 / 主题
典型问题:
- 最近哪个板块最强
- 行业轮动如何
- 某板块有哪些成分股
常用接口:
- `index_basic`
- `index_daily`
- `index_classify`
- `index_member_all`
- `sw_daily`
- `ths_index`
- `ths_member`
- `dc_index`
- `dc_member`
### 7. 打板 / 情绪 / 活跃度
典型问题:
- 今天涨停梯队
- 连板结构
- 炸板率 / 情绪强弱
常用接口:
- `limit_list_d`
- `limit_step`
- `kpl_list`
- `dc_hot`
- `ths_hot`
### 8. 公告 / 新闻 / 研报 / 政策
典型问题:
- 最近有什么公告或催化
- 最近有什么研究报告
- 最近政策面发生了什么
常用接口:
- `anns_d`
- `news`
- `major_news`
- `research_report`
- `npr`
- `irm_qa_sh`
- `irm_qa_sz`
### 9. 宏观 / 跨市场
典型问题:
- CPI / PMI / 社融 / M2
- 利率与收益率曲线
- 港股 / 美股 / 美债数据
常用接口:
- `cn_cpi`
- `cn_ppi`
- `cn_pmi`
- `cn_gdp`
- `cn_m`
- `sf_month`
- `shibor`
- `shibor_lpr`
- `us_tycr`
- `us_daily`
- `hk_daily`
- `index_global`
### 10. 导出 / 研究准备
典型问题:
- 导出某标的一段时间行情
- 生成回测用数据表
- 输出 CSV / parquet
常用接口:
- 取决于上游任务,核心是统一输出规则与命名规范
***
## Entity resolution rules
### 标的解析
- 优先识别股票名、股票代码、指数名、ETF 名、基金名
- 对中文简称先尝试匹配标准对象
- 若重名或多解,列出候选并做最小澄清
- 证券代码内部统一为标准格式,如:`600519.SH``000001.SZ`
### 市场识别
- 默认先按 A 股理解,除非用户明确提到港股 / 美股 / 基金 / 债券 / 期货
- 指数、ETF、个股要分开判断不要混用接口
### 时间默认值
若用户没有明确给时间范围,使用合理默认:
- “最近走势” → 默认近 20 个交易日
- “这段时间 / 最近一段时间” → 默认近 3 个月
- “财报 / 业绩” → 默认最近 8 个季度 + 最近年度
- “资金流最近如何” → 默认近 520 个交易日,按任务粒度调整
- “宏观最近如何” → 默认看最近 612 期
### 板块口径默认值
若用户只说“板块 / 行业 / 概念”但未指定分类体系:
- 行业优先用申万 / 中信等较稳定口径
- 概念优先同花顺 / 东方财富等主题口径
- 若结论依赖具体口径差异,要明确说明使用了哪种分类
***
## Input normalization rules
在请求数据前先做规范化:
- 日期统一为 `YYYYMMDD`
- 检查 `start_date <= end_date`
- 用户输入未来日期时,自动裁剪到最近可用日期并提示
- 裸代码如 `000001` 不要盲猜,能补全则说明补全规则,不能补全则澄清
- 对冲突参数(如 `trade_date``start_date/end_date` 同时给)要先裁决,不要直接乱传
***
## Data retrieval rules
### 文档先行
在写请求代码前,先确认:
- 接口名是否正确
- 必填参数
- 可选参数
- 返回字段
- 积分 / 频率限制
不要仅凭记忆硬写字段名。
### 字段确认
`fields` 参数,优先使用已知字段白名单或接口文档确认。
若用户要求字段不存在,应明确说明,而不是盲查。
### 默认分段拉取
长区间数据不要一次性全拉。
建议:
- 日线 / 周线 / 月线:按年或季度切片
- 财报:按年份 / 报告期切片
- 分钟数据:按月 / 周切片
- 大批量多标的:按标的分批 + 日期分段
### 重试与限流
- 仅对瞬时错误网络抖动、超时、429进行有限重试
- 参数错误、权限不足、字段错误不要盲重试
- 批量拉取时加入节流,避免高频撞限
### 分段合并
分段拉取后:
- 合并
- 去重
- 按主键排序
- 记录失败分段
- 若部分成功,要明确告诉用户哪些段失败了
***
## Output contract
除非用户明确只要原始表,否则优先按这个结构输出:
1. **一句话结论**
2. **数据范围与口径**
3. **关键指标 / 关键表格**
4. **异常点 / 风险点 / 解释限制**
5. **如有本地输出,给出文件路径**
### 结果交付形态
按任务复杂度选择:
- 小结果Markdown 摘要 + 简短表格
- 中等数据表CSV
- 大规模 / 后续分析Parquet
- 需要可复用流程:附 Python 脚本
- 需要可视化时:输出图表 PNG 或说明可绘制图表
### 元信息
生成数据文件时,尽量同时记录:
- 接口名
- 请求参数
- 拉取时间
- 数据行数
- 字段列表
- 是否存在失败分段 / 缺失
***
## Workflow templates
下面这些模板,是本 skill 的核心。
不要直接从接口想起,而要从任务模板想起。
### 1. 单标的行情分析
适用:
- 看下 XX 最近怎么样
- 这票最近强不强
- 今年以来表现如何
默认流程:
1. 解析标的
2. 确定时间范围
3. 取行情 + 必要基础指标
4. 总结区间涨跌、成交活跃度、高低点、波动
5. 输出一句结论 + 关键数字
### 2. 多标的横向对比
适用:
- XX 和 YY 谁更强
- 把这几家公司对比一下
默认流程:
1. 锁定对象
2. 统一时间口径
3. 选 35 个关键指标
4. 输出对比表
5. 给出“谁在哪方面更强”的总结
### 3. 财务质量快照
适用:
- 看下 XX 财报
- 最近几个季度利润趋势
- 财务质量怎么样
默认流程:
1. 拉最近 8 个季度 + 最近年度财务核心数据
2. 区分营收、利润、毛利率、ROE、现金流
3. 标出改善 / 恶化 / 波动点
4. 说明累计值、单季值、同比口径
### 4. 估值分析 / 筛选
适用:
- 现在估值高不高
- 谁更便宜
- 筛低估值高股息
默认流程:
1. 明确标的池
2.`daily_basic` 等估值指标
3. 必要时联动财务质量
4. 输出排序、极值、口径说明
### 5. 资金流追踪
适用:
- 最近资金在买什么
- 北向最近流向哪里
- 主力资金流入最多的是谁
默认流程:
1. 明确资金口径(北向 / 主力 / 龙虎榜 / 板块资金)
2. 确定时间窗
3. 拉净流入 / 活跃成交 / 持续性
4. 和价格表现联动解释
5. 避免把单日噪声说成趋势
### 6. 板块 / 题材轮动分析
适用:
- 最近哪个板块最强
- 机器人最近强在哪
- 某概念板块里有哪些成分股
默认流程:
1. 确定分类口径
2. 拉板块区间表现
3. 必要时联动成分股、资金流、涨停梯队
4. 输出强势板块排行与代表标的
### 7. 公告 / 新闻 / 事件梳理
适用:
- 最近有什么公告
- 有没有什么催化
- 最近新闻面怎么样
默认流程:
1. 明确对象和时间窗
2. 拉公告 / 新闻 / 研报 / 政策数据
3. 去噪,提炼 35 条主线
4. 区分事实、公告、媒体解读
5. 必要时结合股价异动做弱因果解释
### 8. 数据导出与研究准备
适用:
- 拉一份 CSV
- 做回测数据表
- 导出某段时间的行情/财务数据
默认流程:
1. 明确数据范围、频率、字段
2. 采用分段策略取数
3. 清洗、去重、统一字段类型
4. 输出 CSV / parquet
5. 给出文件路径和元信息
### 9. 综合研究简报
适用:
- 给我快速研究一下 XX
- 做个投资者视角简报
- 先给个全景判断
默认流程:
1. 一句话结论
2. 行情表现
3. 财务趋势
4. 估值水平
5. 资金流情况
6. 公告 / 新闻催化
7. 风险点
8. 值得继续深挖的问题
***
## Data quality rules
拉取完成后,至少做这些检查:
- schema 校验
- 关键字段存在性检查
- 主键去重
- 固定排序
- 日期标准化
- 数值字段类型规范化
### 空结果处理
空表不一定是失败,要区分:
- 非交易日
- 区间无数据
- 股票未上市
- 参数错误
- 接口权限不足
不要把所有空结果都说成“接口坏了”。
***
## Cache and reuse rules
为了让 skill 可长期复用,应优先支持:
- 基础表缓存(如 `stock_basic`、交易日历、指数基础信息)
- 增量更新,而不是每次全量重拉
- 大任务断点续跑
- 结果文件规范命名
推荐命名格式:
- `daily_600519.SH_20230101_20231231_20260322.csv`
- `fina_indicator_300750.SZ_20260322.parquet`
缓存命中时,最好说明哪些来自缓存,哪些是新拉取的数据。
***
## Error handling
优先用“人话 + 调试细节分层”的方式输出错误。
### 用户可见层
- token 未配置
- 当前接口可能需要更高积分/权限
- 时间范围过大,已自动改为分段拉取
- 股票名称不唯一,请确认是哪一个
- 当前结果为空,可能因为该日期非交易日 / 标的未上市 / 无权限
### 调试层
必要时补:
- 接口名
- 参数
- 失败分段
- 异常原文
### 部分成功原则
如果部分分段失败,不要说“成功完成”。
应明确说:
- 哪些部分成功
- 哪些部分失败
- 是否已生成不完整结果
***
## Recommended minimal interface set
主 skill 正文不要塞几百个接口。
优先记住 80% 常用任务的核心接口集:
- `stock_basic`
- `trade_cal`
- `daily`
- `pro_bar`
- `daily_basic`
- `fina_indicator`
- `income`
- `balancesheet`
- `cashflow`
- `forecast`
- `express`
- `moneyflow`
- `moneyflow_hsgt`
- `hsgt_top10`
- `top_list`
- `index_basic`
- `index_daily`
- `index_classify`
- `sw_daily`
- `ths_index`
- `ths_member`
- `limit_list_d`
- `limit_step`
- `news`
- `major_news`
- `research_report`
- `anns_d`
- `cn_cpi`
- `cn_pmi`
- `us_tycr`
全部数据接口,请参考 `references/数据接口.md`
***
## Best practices
- 先理解任务,再选接口
- 能少取就少取,先核心数据,再扩展
- 先给结论,再给证据
- 默认说人话,不堆字段名
- 对“最近 / 财报 / 强不强 / 资金关注”这类模糊中文表达,要有合理默认口径
- 大任务先给执行计划,再开跑
- 导出任务尽量保留脚本、元信息、文件路径,方便复用
***
## Examples
### 单票行情
- 看下宁德时代最近三个月走势
- 茅台今年以来涨了多少
- 招行这两年最大回撤大概多少
### 财务 / 估值
- 看下比亚迪最近 8 个季度营收和净利润趋势
- 茅台现在估值算高吗
- 帮我找高 ROE 低负债的公司
### 对比
- 比一下茅台、五粮液、泸州老窖近一年的涨幅和估值
- 对比一下沪深300、中证500、创业板今年表现
### 资金流 / 板块
- 今天北向资金流入最多的股票有哪些
- 最近哪个板块最强
- 半导体板块最近一个月强不强
### 公告 / 事件
- 帮我梳理下寒武纪最近的重要公告
- 最近机器人板块有什么消息面催化
### 宏观
- 看一下最近 CPI、PPI、PMI 变化
- 当前市场风格偏成长还是价值
### 导出
- 把沪深300成分股近两年日线导成 CSV
- 下载宁德时代 2020 到现在的复权行情
- 把最近 3 年 ROE、PE、PB、营收增速拉成一个表
***
## Quick rule
当用户在说:
- 看走势
- 查财报
- 比较公司
- 看板块
- 看资金流
- 梳理公告新闻
- 看宏观
- 拉数据导出
就不要先想“有哪些接口”。
先想:
**这是什么任务?默认该走哪条数据工作流?结果应该怎样交付才真正有用?**

View File

@@ -0,0 +1,248 @@
# 接口列表
根据需求确定接口,然后访问在线链接,读取具体的使用说明,比如入参,出参等。
| 在线文档 | 接口名 | 标题 | 分类 | 描述 |
|:--------------------------------------------|:-------------------|:-----------------|:-------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| https://tushare.pro/wctapi/documents/386.md | etf_index | ETF跟踪指数 | ETF专题 | 获取ETF基准指数列表信息 |
| https://tushare.pro/wctapi/documents/472.md | etf_sz_cons | 每日篮子组合(深市PCF) | ETF专题 | 获取深交所场内所有ETF每日盘前披露的一篮子组合信息,包括成分股票数量、申赎现金折溢价比例等数据 |
| https://tushare.pro/wctapi/documents/471.md | etf_sh_cons | 每日篮子组合(沪市PCF) | ETF专题 | 获取上交所场内所有ETF每日盘前披露的的一篮子组合信息,包括成分股票数量、申赎现金折溢价比例等数据 |
| https://tushare.pro/wctapi/documents/470.md | rt_etf_min_daily | ETF实时分钟-日累计 | ETF专题 | 获取ETF实时分钟数据日累计包括1~60min |
| https://tushare.pro/wctapi/documents/460.md | idx_anns | 指数公司公告 | ETF专题 | 获取指数公司披露的相关公告信息,包括中证指数、国证指数、恒生指数和华证指数的及时与历史公告信息,跟踪指数最新信息和发展方向。 |
| https://tushare.pro/wctapi/documents/454.md | rt_etf_sz_iopv | ETF实时参考 | ETF专题 | ETF实时净值和申购赎回数据参考目前只提供深市 |
| https://tushare.pro/wctapi/documents/416.md | rt_etf_min | ETF实时分钟 | ETF专题 | 获取ETF实时分钟数据包括1~60min |
| https://tushare.pro/wctapi/documents/408.md | etf_share_size | ETF份额规模 | ETF专题 | 获取沪深ETF每日份额和规模数据能体现规模份额的变化掌握ETF资金动向同时提供每日净值和收盘价数据指标是分批入库交易所于次日早8点30左右更新上一交易日的数据另外涉及海外的ETF数据更新会晚一些属于正常情况。 |
| https://tushare.pro/wctapi/documents/400.md | rt_etf_k | ETF实时日线 | ETF专题 | 获取ETF实时日k线行情支持按ETF代码或代码通配符一次性提取全部ETF实时日k线行情 |
| https://tushare.pro/wctapi/documents/387.md | etf_mins | ETF历史分钟 | ETF专题 | 获取ETF分钟数据支持1min/5min/15min/30min/60min行情提供Python SDK和 http Restful API两种方式 |
| https://tushare.pro/wctapi/documents/385.md | etf_basic | ETF基本信息 | ETF专题 | 获取国内ETF基础信息包括了QDII。数据来源与沪深交易所公开披露信息。 |
| https://tushare.pro/wctapi/documents/199.md | fund_adj | ETF复权因子 | ETF专题 | 获取基金复权因子,用于计算基金复权行情 |
| https://tushare.pro/wctapi/documents/127.md | fund_daily | ETF日线行情 | ETF专题 | 获取ETF行情每日收盘后成交数据历史超过10年 |
| https://tushare.pro/wctapi/documents/187.md | cb_daily | 可转债行情 | 债券专题 | 获取可转债行情 |
| https://tushare.pro/wctapi/documents/186.md | cb_issue | 可转债发行 | 债券专题 | 获取可转债发行数据 |
| https://tushare.pro/wctapi/documents/185.md | cb_basic | 可转债基础信息 | 债券专题 | 获取可转债基本信息 |
| https://tushare.pro/wctapi/documents/392.md | cb_factor_pro | 可转债技术面因子(专业版) | 债券专题 | 获取可转债每日技术面因子数据用于跟踪可转债当前走势情况数据由Tushare社区自产覆盖全历史输出参数_bfq表示不复权_qfq表示前复权 _hfq表示后复权描述中说明了因子的默认传参如需要特殊参数或者更多因子可以联系管理员评估 |
| https://tushare.pro/wctapi/documents/459.md | top10_cb_holders | 可转债十大持有人 | 债券专题 | 获取可转债前十大持有人 |
| https://tushare.pro/wctapi/documents/201.md | yc_cb | 国债收益率曲线 | 债券专题 | 获取中债收益率曲线,目前可获取中债国债收益率曲线即期和到期收益率曲线数据 |
| https://tushare.pro/wctapi/documents/458.md | cb_rating | 可转债债券评级 | 债券专题 | 获取可转债评级历史记录 |
| https://tushare.pro/wctapi/documents/233.md | eco_cal | 全球财经事件 | 债券专题 | 获取全球财经日历、包括经济事件数据更新 |
| https://tushare.pro/wctapi/documents/323.md | bc_bestotcqt | 柜台流通式债券最优报价 | 债券专题 | 柜台流通式债券最优报价 |
| https://tushare.pro/wctapi/documents/247.md | cb_share | 可转债转股结果 | 债券专题 | 获取可转债转股结果 |
| https://tushare.pro/wctapi/documents/256.md | repo_daily | 债券回购日行情 | 债券专题 | 债券回购日行情 |
| https://tushare.pro/wctapi/documents/269.md | cb_call | 可转债赎回信息 | 债券专题 | 获取可转债到期赎回、强制赎回等信息。数据来源于公开披露渠道,供个人和机构研究使用,请不要用于数据商业目的。 |
| https://tushare.pro/wctapi/documents/271.md | bond_blk | 大宗交易 | 债券专题 | 获取沪深交易所债券大宗交易数据 |
| https://tushare.pro/wctapi/documents/272.md | bond_blk_detail | 大宗交易明细 | 债券专题 | 获取沪深交易所债券大宗交易数据 |
| https://tushare.pro/wctapi/documents/246.md | cb_price_chg | 可转债转股价变动 | 债券专题 | 获取可转债转股价变动 |
| https://tushare.pro/wctapi/documents/305.md | cb_rate | 可转债票面利率 | 债券专题 | 获取可转债票面利率 |
| https://tushare.pro/wctapi/documents/322.md | bc_otcqt | 柜台流通式债券报价 | 债券专题 | 柜台流通式债券报价 |
| https://tushare.pro/wctapi/documents/19.md | fund_basic | 基金列表 | 公募基金 | 获取公募基金数据列表,包括场内和场外基金 |
| https://tushare.pro/wctapi/documents/462.md | mkt_idx_bmk | 基金业绩基准 | 公募基金 | 获取官方发布的ETF业绩比较基准列表信息分为一类库、二类库 |
| https://tushare.pro/wctapi/documents/359.md | fund_factor_pro | 基金技术面因子(专业版) | 公募基金 | 获取场内基金每日技术面因子数据用于跟踪场内基金当前走势情况数据由Tushare社区自产覆盖全历史输出参数_bfq表示不复权描述中说明了因子的默认传参如需要特殊参数或者更多因子可以联系管理员评估 |
| https://tushare.pro/wctapi/documents/208.md | fund_manager | 基金经理 | 公募基金 | 获取公募基金经理数据,包括基金经理简历等数据 |
| https://tushare.pro/wctapi/documents/207.md | fund_share | 基金规模 | 公募基金 | 获取基金规模数据包含上海和深圳ETF基金 |
| https://tushare.pro/wctapi/documents/121.md | fund_portfolio | 基金持仓 | 公募基金 | 获取公募基金持仓数据,季度更新 |
| https://tushare.pro/wctapi/documents/120.md | fund_div | 基金分红 | 公募基金 | 获取公募基金分红数据 |
| https://tushare.pro/wctapi/documents/119.md | fund_nav | 基金净值 | 公募基金 | 获取公募基金净值数据 |
| https://tushare.pro/wctapi/documents/118.md | fund_company | 基金管理人 | 公募基金 | 获取公募基金管理人列表 |
| https://tushare.pro/wctapi/documents/178.md | fx_obasic | 外汇基础信息(海外) | 外汇数据 | 获取海外外汇基础信息目前只有FXCM交易商的数据 |
| https://tushare.pro/wctapi/documents/179.md | fx_daily | 外汇日线行情 | 外汇数据 | 获取外汇日线行情 |
| https://tushare.pro/wctapi/documents/143.md | news | 新闻快讯(短讯) | 大模型语料 | 获取主流新闻网站的快讯新闻数据,提供超过6年以上历史新闻。 |
| https://tushare.pro/wctapi/documents/154.md | cctv_news | 新闻联播文字稿 | 大模型语料 | 获取新闻联播文字稿数据数据开始于2017年。 |
| https://tushare.pro/wctapi/documents/195.md | major_news | 新闻通讯(长篇) | 大模型语料 | 获取长篇通讯信息覆盖主要新闻资讯网站提供超过8年历史新闻。 |
| https://tushare.pro/wctapi/documents/366.md | irm_qa_sh | 上证e互动问答 | 大模型语料 | 获取上交所e互动董秘问答文本数据。上证e互动是由上海证券交易所建立、上海证券市场所有参与主体无偿使用的沟通平台,旨在引导和促进上市公司、投资者等各市场参与主体之间的信息沟通,构建集中、便捷的互动渠道。本接口数据记录了以上沟通问答的文本数据。 |
| https://tushare.pro/wctapi/documents/367.md | irm_qa_sz | 深证易互动问答 | 大模型语料 | 互动易是由深交所官方推出,供投资者与上市公司直接沟通的平台,一站式公司资讯汇集,提供第一手的互动问答、投资者关系信息、公司声音等内容。 |
| https://tushare.pro/wctapi/documents/406.md | npr | 国家政策库 | 大模型语料 | 获取国家行政机关公开披露的各类法规、条例政策、批复、通知等文本数据。 |
| https://tushare.pro/wctapi/documents/415.md | research_report | 券商研究报告 | 大模型语料 | 获取券商研究报告-个股、行业等历史数据从20170101开始提供增量每天两次更新 |
| https://tushare.pro/wctapi/documents/176.md | anns_d | 上市公司公告 | 大模型语料 | 获取全量公告数据提供pdf下载URL |
| https://tushare.pro/wctapi/documents/465.md | monetary_policy | 央行货币政策执行报告 | 大模型语料 | 获取央行季度更新的货币政策执行报告历史数据开始于2001年每年四篇提供原始PDF下载链接可用于分析过去20多年央行货币政策的动向、宏观以及金融市场的情况。 |
| https://tushare.pro/wctapi/documents/461.md | cn_schedule | 中国经济数据发布日程 | 宏观经济,国内宏观 | 获取国家统计局、中国人民银行等经济数据发布日程及对应tushare接口持续更新中 |
| https://tushare.pro/wctapi/documents/245.md | cn_ppi | 工业生产者出厂价格指数(PPI) | 宏观经济,国内宏观,价格指数 | 获取PPI工业生产者出厂价格指数数据 |
| https://tushare.pro/wctapi/documents/228.md | cn_cpi | 居民消费价格指数(CPI) | 宏观经济,国内宏观,价格指数 | 获取CPI居民消费价格数据包括全国、城市和农村的数据 |
| https://tushare.pro/wctapi/documents/149.md | shibor | Shibor利率 | 宏观经济,国内宏观,利率数据 | shibor利率 |
| https://tushare.pro/wctapi/documents/150.md | shibor_quote | Shibor报价数据 | 宏观经济,国内宏观,利率数据 | Shibor报价数据 |
| https://tushare.pro/wctapi/documents/151.md | shibor_lpr | LPR贷款基础利率 | 宏观经济,国内宏观,利率数据 | LPR贷款基础利率 |
| https://tushare.pro/wctapi/documents/152.md | libor | Libor利率 | 宏观经济,国内宏观,利率数据 | Libor拆借利率 |
| https://tushare.pro/wctapi/documents/174.md | gz_index | 广州民间借贷利率 | 宏观经济,国内宏观,利率数据 | 广州民间借贷利率 |
| https://tushare.pro/wctapi/documents/173.md | wz_index | 温州民间借贷利率 | 宏观经济,国内宏观,利率数据 | 温州民间借贷利率,即温州指数 |
| https://tushare.pro/wctapi/documents/153.md | hibor | Hibor利率 | 宏观经济,国内宏观,利率数据 | Hibor利率 |
| https://tushare.pro/wctapi/documents/227.md | cn_gdp | 国内生产总值(GDP) | 宏观经济,国内宏观,国民经济 | 获取国民经济之GDP数据 |
| https://tushare.pro/wctapi/documents/325.md | cn_pmi | 采购经理指数(PMI) | 宏观经济,国内宏观,景气度 | 采购经理人指数 |
| https://tushare.pro/wctapi/documents/310.md | sf_month | 社融增量(月度) | 宏观经济,国内宏观,金融,社会融资 | 获取月度社会融资数据 |
| https://tushare.pro/wctapi/documents/242.md | cn_m | 货币供应量(月) | 宏观经济,国内宏观,金融,货币供应量 | 获取货币供应量之月度数据 |
| https://tushare.pro/wctapi/documents/219.md | us_tycr | 国债收益率曲线利率 | 宏观经济,国际宏观,美国利率 | 获取美国每日国债收益率曲线利率 |
| https://tushare.pro/wctapi/documents/223.md | us_trltr | 国债长期利率平均值 | 宏观经济,国际宏观,美国利率 | 国债实际长期利率平均值 |
| https://tushare.pro/wctapi/documents/220.md | us_trycr | 国债实际收益率曲线利率 | 宏观经济,国际宏观,美国利率 | 国债实际收益率曲线利率 |
| https://tushare.pro/wctapi/documents/221.md | us_tbr | 短期国债利率 | 宏观经济,国际宏观,美国利率 | 获取美国短期国债利率数据 |
| https://tushare.pro/wctapi/documents/222.md | us_tltr | 国债长期利率 | 宏观经济,国际宏观,美国利率 | 国债长期利率 |
| https://tushare.pro/wctapi/documents/308.md | ci_daily | 中信行业指数日行情 | 指数专题 | 获取中信行业指数日线行情 |
| https://tushare.pro/wctapi/documents/469.md | sw_mins | SW历史分钟 | 指数专题 | 获取申万指数历史分钟数据 |
| https://tushare.pro/wctapi/documents/420.md | rt_idx_min | 指数实时分钟 | 指数专题 | 获取交易所指数实时分钟数据包括1~60min |
| https://tushare.pro/wctapi/documents/419.md | idx_mins | 指数历史分钟 | 指数专题 | 获取交易所指数分钟数据支持1min/5min/15min/30min/60min行情提供Python SDK和 http Restful API两种方式 |
| https://tushare.pro/wctapi/documents/417.md | rt_sw_k | 申万实时行情 | 指数专题 | 获取申万行业指数的最新截面数据 |
| https://tushare.pro/wctapi/documents/403.md | rt_idx_k | 指数实时日线 | 指数专题 | 获取交易所指数实时日线行情支持按代码或代码通配符一次性提取全部交易所指数实时日k线行情 |
| https://tushare.pro/wctapi/documents/373.md | ci_index_member | 中信行业成分 | 指数专题 | 按三级分类提取中信行业成分,可提供某个分类的所有成分,也可按股票代码提取所属分类,参数灵活 |
| https://tushare.pro/wctapi/documents/94.md | index_basic | 指数基本信息 | 指数专题 | 获取指数基础信息。 |
| https://tushare.pro/wctapi/documents/358.md | idx_factor_pro | 指数技术面因子(专业版) | 指数专题 | 获取指数每日技术面因子数据用于跟踪指数当前走势情况数据由Tushare社区自产覆盖全历史输出参数_bfq表示不复权描述中说明了因子的默认传参如需要特殊参数或者更多因子可以联系管理员评估指数包括大盘指数 申万行业指数 中信指数 |
| https://tushare.pro/wctapi/documents/96.md | index_weight | 指数成分和权重 | 指数专题 | 获取各类指数成分和权重,**月度数据** ,建议输入参数里开始日期和结束日分别输入当月第一天和最后一天的日期。 |
| https://tushare.pro/wctapi/documents/128.md | index_dailybasic | 大盘指数每日指标 | 指数专题 | 目前只提供上证综指深证成指上证50中证500中小板指创业板指的每日指标数据 |
| https://tushare.pro/wctapi/documents/171.md | index_weekly | 指数周线行情 | 指数专题 | 获取指数周线行情 |
| https://tushare.pro/wctapi/documents/172.md | index_monthly | 指数月线行情 | 指数专题 | 获取指数月线行情,每月更新一次 |
| https://tushare.pro/wctapi/documents/181.md | index_classify | 申万行业分类 | 指数专题 | 获取申万行业分类可以获取申万2014年版本28个一级分类104个二级分类227个三级分类和2021年本版31个一级分类134个二级分类346个三级分类列表信息 |
| https://tushare.pro/wctapi/documents/211.md | index_global | 国际主要指数 | 指数专题 | 获取国际主要指数日线行情 |
| https://tushare.pro/wctapi/documents/335.md | index_member_all | 申万行业成分(分级) | 指数专题 | 按三级分类提取申万行业成分,可提供某个分类的所有成分,也可按股票代码提取所属分类,参数灵活 |
| https://tushare.pro/wctapi/documents/268.md | sz_daily_info | 深圳市场每日交易情况 | 指数专题 | 获取深圳市场每日交易概况 |
| https://tushare.pro/wctapi/documents/327.md | sw_daily | 申万日线行情 | 指数专题 | 获取申万行业日线行情默认是申万2021版行情 |
| https://tushare.pro/wctapi/documents/215.md | daily_info | 沪深市场每日交易统计 | 指数专题 | 获取交易所股票交易统计,包括各板块明细 |
| https://tushare.pro/wctapi/documents/95.md | index_daily | 指数日线行情 | 指数专题 | 获取指数每日行情还可以通过bar接口获取。由于服务器压力目前规则是单次调取最多取8000行记录可以设置start和end日期补全。指数行情也可以通过[**通用行情接口**]( https://tushare.pro/document/2?doc_id=109)获取数据。本接口不包含[申万行业指数行情数据](https://tushare.pro/document/2?doc_id=327)。 |
| https://tushare.pro/wctapi/documents/158.md | opt_basic | 期权合约信息 | 期权数据 | 获取期权合约信息 |
| https://tushare.pro/wctapi/documents/159.md | opt_daily | 期权日线行情 | 期权数据 | 获取期权日线行情 |
| https://tushare.pro/wctapi/documents/341.md | opt_mins | 期权分钟行情 | 期权数据 | 获取全市场期权合约分钟数据支持1min/5min/15min/30min/60min行情提供Python SDK和 http Restful API两种方式。 |
| https://tushare.pro/wctapi/documents/139.md | fut_holding | 每日持仓排名 | 期货数据 | 获取每日成交持仓排名数据,注意"上期所"涵盖"上海国际能源交易中心"合约数据 |
| https://tushare.pro/wctapi/documents/140.md | fut_wsr | 仓单日报 | 期货数据 | 获取仓单日报数据,了解各仓库/厂库的仓单变化 |
| https://tushare.pro/wctapi/documents/141.md | fut_settle | 每日结算参数 | 期货数据 | 获取每日结算参数数据,包括交易和交割费率等 |
| https://tushare.pro/wctapi/documents/216.md | fut_weekly_detail | 期货主要品种交易周报 | 期货数据 | 获取期货交易所主要品种每周交易统计信息数据从2010年3月开始 |
| https://tushare.pro/wctapi/documents/313.md | ft_mins | 历史分钟行情 | 期货数据 | 获取全市场期货合约分钟数据支持1min/5min/15min/30min/60min行情提供Python SDK和 http Restful API两种方式如果需要主力合约分钟请先通过主力[mapping](https://tushare.pro/document/2?doc_id=189)接口需要有至少2000积分获取对应的合约代码后提取分钟。 |
| https://tushare.pro/wctapi/documents/337.md | fut_weekly_monthly | 期货周月线行情(每日更新) | 期货数据 | 期货周/月线行情(每日更新) |
| https://tushare.pro/wctapi/documents/340.md | rt_fut_min | 实时分钟行情 | 期货数据 | 获取全市场期货合约实时分钟数据支持1min/5min/15min/30min/60min行情提供Python SDK、 http Restful API和websocket三种方式如果需要主力合约分钟请先通过主力[mapping](https://tushare.pro/document/2?doc_id=189)接口获取对应的合约代码后提取分钟。 |
| https://tushare.pro/wctapi/documents/368.md | ft_limit | 期货合约涨跌停价格 | 期货数据 | 获取所有期货合约每天的涨跌停价格及最低保证金率数据开始于2005年。 |
| https://tushare.pro/wctapi/documents/467.md | fut_trade_cal | 期货交易日历 | 期货数据 | 获取各大期货交易所交易日历数据 |
| https://tushare.pro/wctapi/documents/468.md | fut_index_daily | 南华期货指数日线行情 | 期货数据 | 获取南华指数每日行情,指数行情也可以通过[**通用行情接口**]( https://tushare.pro/document/2?doc_id=109)获取数据. |
| https://tushare.pro/wctapi/documents/138.md | fut_daily | 日线行情 | 期货数据 | 期货日线行情数据 |
| https://tushare.pro/wctapi/documents/189.md | fut_mapping | 期货主力与连续合约 | 期货数据 | 获取期货主力(或连续)合约与月合约映射数据 |
| https://tushare.pro/wctapi/documents/135.md | fut_basic | 合约信息 | 期货数据 | 获取期货合约列表数据 |
| https://tushare.pro/wctapi/documents/388.md | hk_fina_indicator | 港股财务指标数据 | 港股数据 | 获取港股上市公司财务指标数据为避免服务器压力现阶段每次请求最多返回200条记录可通过设置日期多次请求获取更多数据。 |
| https://tushare.pro/wctapi/documents/383.md | rt_hk_k | 港股实时日线 | 港股数据 | 获取港股实时日k线行情支持按股票代码及股票代码通配符一次性提取全部股票实时日k线行情 |
| https://tushare.pro/wctapi/documents/390.md | hk_balancesheet | 港股资产负债表 | 港股数据 | 获取港股上市公司资产负债表 |
| https://tushare.pro/wctapi/documents/304.md | hk_mins | 港股分钟行情 | 港股数据 | 港股分钟数据支持1min/5min/15min/30min/60min行情提供Python SDK和 http Restful API两种方式 |
| https://tushare.pro/wctapi/documents/250.md | hk_tradecal | 港股交易日历 | 港股数据 | 获取交易日历 |
| https://tushare.pro/wctapi/documents/192.md | hk_daily | 港股日线行情 | 港股数据 | 获取港股每日增量和历史行情每日18点左右更新当日数据 |
| https://tushare.pro/wctapi/documents/389.md | hk_income | 港股利润表 | 港股数据 | 获取港股上市公司财务利润表数据 |
| https://tushare.pro/wctapi/documents/401.md | hk_adjfactor | 港股复权因子 | 港股数据 | 获取港股每日复权因子数据,每天滚动刷新 |
| https://tushare.pro/wctapi/documents/391.md | hk_cashflow | 港股现金流量表 | 港股数据 | 获取港股上市公司现金流量表数据 |
| https://tushare.pro/wctapi/documents/191.md | hk_basic | 港股基础信息 | 港股数据 | 获取港股列表信息 |
| https://tushare.pro/wctapi/documents/339.md | hk_daily_adj | 港股复权行情 | 港股数据 | 获取港股复权行情,提供股票股本、市值和成交及换手多个数据指标 |
| https://tushare.pro/wctapi/documents/284.md | sge_basic | 上海黄金基础信息 | 现货数据 | 获取上海黄金交易所现货合约基础信息 |
| https://tushare.pro/wctapi/documents/285.md | sge_daily | 上海黄金现货日行情 | 现货数据 | 获取上海黄金交易所现货合约日线行情 |
| https://tushare.pro/wctapi/documents/338.md | us_daily_adj | 美股复权行情 | 美股数据 | 获取美股复权行情,支持美股全市场股票,提供股本、市值、复权因子和成交信息等多个数据指标 |
| https://tushare.pro/wctapi/documents/254.md | us_daily | 美股日线行情 | 美股数据 | 获取美股行情(未复权),包括全部股票全历史行情,以及重要的市场和估值指标 |
| https://tushare.pro/wctapi/documents/253.md | us_tradecal | 美股交易日历 | 美股数据 | 获取美股交易日历信息 |
| https://tushare.pro/wctapi/documents/252.md | us_basic | 美股基础信息 | 美股数据 | 获取美股列表信息 |
| https://tushare.pro/wctapi/documents/393.md | us_fina_indicator | 美股财务指标数据 | 美股数据 | 获取美股上市公司财务指标数据目前只覆盖主要美股和中概股。为避免服务器压力现阶段每次请求最多返回200条记录可通过设置日期多次请求获取更多数据。 |
| https://tushare.pro/wctapi/documents/394.md | us_income | 美股利润表 | 美股数据 | 获取美股上市公司财务利润表数据(目前只覆盖主要美股和中概股) |
| https://tushare.pro/wctapi/documents/395.md | us_balancesheet | 美股资产负债表 | 美股数据 | 获取美股上市公司资产负债表(目前只覆盖主要美股和中概股) |
| https://tushare.pro/wctapi/documents/396.md | us_cashflow | 美股现金流量表 | 美股数据 | 获取美股上市公司现金流量表数据(目前只覆盖主要美股和中概股) |
| https://tushare.pro/wctapi/documents/402.md | us_adjfactor | 美股复权因子 | 美股数据 | 获取美股每日复权因子数据,在每天美股收盘后滚动刷新 |
| https://tushare.pro/wctapi/documents/58.md | margin | 融资融券交易汇总 | 股票数据,两融及转融通 | 获取融资融券每日交易汇总数据交易所于每天8点30左右更新上一日数据 |
| https://tushare.pro/wctapi/documents/331.md | slb_len | 转融资交易汇总 | 股票数据,两融及转融通 | 转融通融资汇总 |
| https://tushare.pro/wctapi/documents/334.md | slb_len_mm | 做市借券交易汇总(停) | 股票数据,两融及转融通 | 做市借券交易汇总 |
| https://tushare.pro/wctapi/documents/333.md | slb_sec_detail | 转融券交易明细(停) | 股票数据,两融及转融通 | 转融券交易明细 |
| https://tushare.pro/wctapi/documents/332.md | slb_sec | 转融券交易汇总(停) | 股票数据,两融及转融通 | 转融通转融券交易汇总 |
| https://tushare.pro/wctapi/documents/326.md | margin_secs | 融资融券标的(盘前) | 股票数据,两融及转融通 | 获取沪深京三大交易所融资融券标的包括ETF每天盘前更新 |
| https://tushare.pro/wctapi/documents/59.md | margin_detail | 融资融券交易明细 | 股票数据,两融及转融通 | 获取沪深两市每日融资融券明细交易所于每天8点30左右更新上一日数据 |
| https://tushare.pro/wctapi/documents/61.md | top10_holders | 前十大股东 | 股票数据,参考数据 | 获取上市公司前十大股东数据,包括持有数量和比例等信息 |
| https://tushare.pro/wctapi/documents/62.md | top10_floatholders | 前十大流通股东 | 股票数据,参考数据 | 获取上市公司前十大流通股东数据 |
| https://tushare.pro/wctapi/documents/110.md | pledge_stat | 股权质押统计数据 | 股票数据,参考数据 | 获取股票质押统计数据 |
| https://tushare.pro/wctapi/documents/160.md | share_float | 限售股解禁 | 股票数据,参考数据 | 获取限售股解禁 |
| https://tushare.pro/wctapi/documents/111.md | pledge_detail | 股权质押明细数据 | 股票数据,参考数据 | 获取股票质押明细数据 |
| https://tushare.pro/wctapi/documents/161.md | block_trade | 大宗交易 | 股票数据,参考数据 | 大宗交易 |
| https://tushare.pro/wctapi/documents/164.md | stk_account | 股票开户数据(停) | 股票数据,参考数据 | 获取股票账户开户数据,统计周期为一周 |
| https://tushare.pro/wctapi/documents/453.md | stk_alert | 交易所重点提示证券 | 股票数据,参考数据 | 根据证券交易所交易规则的有关规定,交易所每日发布重点提示证券 |
| https://tushare.pro/wctapi/documents/452.md | stk_high_shock | 个股严重异常波动 | 股票数据,参考数据 | 根据证券交易所交易规则的有关规定,交易所每日发布股票交易严重异常波动情况 |
| https://tushare.pro/wctapi/documents/451.md | stk_shock | 个股异常波动 | 股票数据,参考数据 | 根据证券交易所交易规则的有关规定,交易所每日发布股票交易异常波动情况 |
| https://tushare.pro/wctapi/documents/175.md | stk_holdertrade | 股东增减持 | 股票数据,参考数据 | 获取上市公司增减持数据,了解重要股东近期及历史上的股份增减变化 |
| https://tushare.pro/wctapi/documents/166.md | stk_holdernumber | 股东人数 | 股票数据,参考数据 | 获取上市公司股东户数数据,数据不定期公布 |
| https://tushare.pro/wctapi/documents/165.md | stk_account_old | 股票开户数据(旧) | 股票数据,参考数据 | 获取股票账户开户数据旧版格式数据数据从2008年1月开始到2015年5月29新数据请通过[股票开户数据](https://tushare.pro/document/2?doc_id=164)获取。 |
| https://tushare.pro/wctapi/documents/124.md | repurchase | 股票回购 | 股票数据,参考数据 | 获取上市公司回购股票数据 |
| https://tushare.pro/wctapi/documents/194.md | stk_rewards | 管理层薪酬和持股 | 股票数据,基础数据 | 获取上市公司管理层薪酬和持股 |
| https://tushare.pro/wctapi/documents/262.md | bak_basic | 股票历史列表 | 股票数据,基础数据 | 获取备用基础列表数据从2016年开始 |
| https://tushare.pro/wctapi/documents/329.md | stk_premarket | 每日股本(盘前) | 股票数据,基础数据 | 每日开盘前获取当日股票的股本情况,包括总股本和流通股本,涨跌停价格等。 |
| https://tushare.pro/wctapi/documents/375.md | bse_mapping | 北交所新旧代码对照 | 股票数据,基础数据 | 获取北交所股票代码变更后新旧代码映射表数据 |
| https://tushare.pro/wctapi/documents/397.md | stock_st | ST股票列表 | 股票数据,基础数据 | 获取ST股票列表可根据交易日期获取历史上每天的ST列表 |
| https://tushare.pro/wctapi/documents/193.md | stk_managers | 上市公司管理层 | 股票数据,基础数据 | 获取上市公司管理层 |
| https://tushare.pro/wctapi/documents/423.md | st | ST风险警示板股票 | 股票数据,基础数据 | ST风险警示板股票列表 |
| https://tushare.pro/wctapi/documents/112.md | stock_company | 上市公司基本信息 | 股票数据,基础数据 | 获取上市公司基础信息单次提取4500条可以根据交易所分批提取 |
| https://tushare.pro/wctapi/documents/100.md | namechange | 股票曾用名 | 股票数据,基础数据 | 历史名称变更记录 |
| https://tushare.pro/wctapi/documents/25.md | stock_basic | 股票列表 | 股票数据,基础数据 | 获取基础信息数据,包括股票代码、名称、上市日期、退市日期等 |
| https://tushare.pro/wctapi/documents/26.md | trade_cal | 交易日历 | 股票数据,基础数据 | 获取各大交易所交易日历数据,默认提取的是上交所 |
| https://tushare.pro/wctapi/documents/398.md | stock_hsgt | 沪深港通股票列表 | 股票数据,基础数据 | 获取沪深港通股票列表 |
| https://tushare.pro/wctapi/documents/123.md | new_share | IPO新股上市 | 股票数据,基础数据 | 获取新股上市列表数据 |
| https://tushare.pro/wctapi/documents/261.md | ths_member | THS概念板块成分 | 股票数据,打板专题数据 | 获取概念板块成分列表 |
| https://tushare.pro/wctapi/documents/260.md | ths_daily | THS概念板块行情 | 股票数据,打板专题数据 | 获取板块指数行情 |
| https://tushare.pro/wctapi/documents/376.md | tdx_index | TDX概念板块分类 | 股票数据,打板专题数据 | 获取板块基础信息,包括概念板块、行业、风格、地域等 |
| https://tushare.pro/wctapi/documents/369.md | stk_auction | 开盘竞价成交(当日) | 股票数据,打板专题数据 | 获取当日个股和ETF的集合竞价成交情况每天9点26~29分之间可以获取当日的集合竞价成交数据。本接口历史数据开始于2025年1月。 |
| https://tushare.pro/wctapi/documents/363.md | dc_member | DC概念板块成分 | 股票数据,打板专题数据 | 获取板块每日成分数据,可以根据概念板块代码和交易日期,获取历史成分 |
| https://tushare.pro/wctapi/documents/362.md | dc_index | DC概念板块分类 | 股票数据,打板专题数据 | 获取每个交易日的概念板块数据,支持按日期查询 |
| https://tushare.pro/wctapi/documents/259.md | ths_index | THS概念板块分类 | 股票数据,打板专题数据 | 获取板块指数,包括概念、行业、特色指数。 |
| https://tushare.pro/wctapi/documents/356.md | limit_step | 涨停股票连板天梯 | 股票数据,打板专题数据 | 获取每天连板个数晋级的股票,可以分析出每天连续涨停进阶个数,判断强势热度 |
| https://tushare.pro/wctapi/documents/377.md | tdx_member | TDX概念板块成分 | 股票数据,打板专题数据 | 获取各板块成分股信息 |
| https://tushare.pro/wctapi/documents/355.md | limit_list_ths | THS涨跌停榜单 | 股票数据,打板专题数据 | 获取同花顺每日涨跌停榜单数据历史数据从20231101开始提供增量每天16点左右更新分类limit_type 涨停池、连扳池、冲刺涨停、炸板池、跌停池,默认:涨停池)不同,字段返回有值情况也不同,如仅有涨停池、连扳池 有最大封单 lu_limit_order返回值其他分类为空 |
| https://tushare.pro/wctapi/documents/347.md | kpl_list | 榜单数据(KP) | 股票数据,打板专题数据 | 获取涨停、跌停、炸板等榜单数据 |
| https://tushare.pro/wctapi/documents/321.md | dc_hot | DC热榜 | 股票数据,打板专题数据 | 获取热榜数据包括A股市场、ETF基金、港股市场、美股市场等等每日盘中提取4次收盘后4次最晚22点提取一次。 |
| https://tushare.pro/wctapi/documents/320.md | ths_hot | THS热榜 | 股票数据,打板专题数据 | 获取热榜数据包括热股、概念板块、ETF、可转债、港美股等等每日盘中提取4次收盘后4次最晚22点提取一次。 |
| https://tushare.pro/wctapi/documents/312.md | hm_detail | 游资交易每日明细 | 股票数据,打板专题数据 | 获取每日游资交易明细数据开始于2022年8。游资分类名录请点击<a href="https://tushare.pro/document/2?doc_id=311">游资名录</a> |
| https://tushare.pro/wctapi/documents/311.md | hm_list | 市场游资最全名录 | 股票数据,打板专题数据 | 获取游资分类名录信息 |
| https://tushare.pro/wctapi/documents/298.md | limit_list_d | 涨跌停和炸板数据 | 股票数据,打板专题数据 | 获取A股每日涨跌停、炸板数据情况数据从2020年开始不提供ST股票的统计 |
| https://tushare.pro/wctapi/documents/351.md | kpl_concept_cons | 题材成分(KP) | 股票数据,打板专题数据 | 获取概念题材的成分股 |
| https://tushare.pro/wctapi/documents/378.md | tdx_daily | TDX概念板块行情 | 股票数据,打板专题数据 | 获取各板块行情,包括成交和估值等数据 |
| https://tushare.pro/wctapi/documents/382.md | dc_daily | DC概念板块行情 | 股票数据,打板专题数据 | 获取概念板块、行业指数板块、地域板块行情数据历史数据开始于2020年 |
| https://tushare.pro/wctapi/documents/357.md | limit_cpt_list | 涨停最强板块统计 | 股票数据,打板专题数据 | 获取每天涨停股票最多最强的概念板块,可以分析强势板块的轮动,判断资金动向 |
| https://tushare.pro/wctapi/documents/422.md | dc_concept_cons | 题材成分(DC) | 股票数据,打板专题数据 | 获取概念题材的成分股,每天盘后更新 |
| https://tushare.pro/wctapi/documents/107.md | top_inst | 龙虎榜机构交易单 | 股票数据,打板专题数据 | 龙虎榜机构成交明细 |
| https://tushare.pro/wctapi/documents/106.md | top_list | 龙虎榜每日统计单 | 股票数据,打板专题数据 | 龙虎榜每日交易明细 |
| https://tushare.pro/wctapi/documents/421.md | dc_concept | 题材数据(DC) | 股票数据,打板专题数据 | 获取概念题材列表,每天盘后更新 |
| https://tushare.pro/wctapi/documents/267.md | broker_recommend | 券商月度金股 | 股票数据,特色数据 | 获取券商月度金股一般1日~3日内更新当月数据 |
| https://tushare.pro/wctapi/documents/274.md | ccass_hold_detail | 中央结算系统持股明细 | 股票数据,特色数据 | 获取中央结算系统机构席位持股明细,数据覆盖**全历史**根据交易所披露时间当日数据在下一交易日早上9点前完成 |
| https://tushare.pro/wctapi/documents/275.md | stk_surv | 机构调研数据 | 股票数据,特色数据 | 获取上市公司机构调研记录数据 |
| https://tushare.pro/wctapi/documents/292.md | report_rc | 券商盈利预测数据 | 股票数据,特色数据 | 获取券商卖方每天研报的盈利预测数据数据从2010年开始每晚19~22点更新当日数据 |
| https://tushare.pro/wctapi/documents/293.md | cyq_perf | 每日筹码及胜率 | 股票数据,特色数据 | 获取A股每日筹码平均成本和胜率情况每天18~19点左右更新数据从2018年开始 |
| https://tushare.pro/wctapi/documents/294.md | cyq_chips | 每日筹码分布 | 股票数据,特色数据 | 获取A股每日的筹码分布情况提供各价位占比数据从2018年开始每天18~19点之间更新当日数据 |
| https://tushare.pro/wctapi/documents/188.md | hk_hold | 沪深股通持股明细 | 股票数据,特色数据 | 获取沪深港股通持股明细,数据来源港交所。 |
| https://tushare.pro/wctapi/documents/328.md | stk_factor_pro | 股票技术面因子(专业版) | 股票数据,特色数据 | 获取股票每日技术面因子数据用于跟踪股票当前走势情况数据由Tushare社区自产覆盖全历史输出参数_bfq表示不复权_qfq表示前复权 _hfq表示后复权描述中说明了因子的默认传参如需要特殊参数或者更多因子可以联系管理员评估 |
| https://tushare.pro/wctapi/documents/353.md | stk_auction_o | 股票开盘集合竞价数据 | 股票数据,特色数据 | 股票开盘9:30集合竞价数据每天盘后更新 |
| https://tushare.pro/wctapi/documents/354.md | stk_auction_c | 股票收盘集合竞价数据 | 股票数据,特色数据 | 股票收盘15:00集合竞价数据每天盘后更新 |
| https://tushare.pro/wctapi/documents/364.md | stk_nineturn | 神奇九转指标 | 股票数据,特色数据 | 神奇九转又称“九转序列”是一种基于技术分析的股票趋势反转指标其思想来源于技术分析大师汤姆·迪马克Tom DeMark的TD序列。该指标的核心功能是通过识别股价在上涨或下跌过程中连续9天的特定走势来判断股价的潜在反转点从而帮助投资者提高抄底和逃顶的成功率日线级别配合60min的九转效果更好数据从20230101开始。 |
| https://tushare.pro/wctapi/documents/399.md | stk_ah_comparison | AH股比价 | 股票数据,特色数据 | AH股比价数据可根据交易日期获取历史 |
| https://tushare.pro/wctapi/documents/295.md | ccass_hold | 中央结算系统持股统计 | 股票数据,特色数据 | 获取中央结算系统持股汇总数据覆盖全部历史数据根据交易所披露时间当日数据在下一交易日早上9点前完成入库 |
| https://tushare.pro/wctapi/documents/296.md | stk_factor | 股票技术面因子 | 股票数据,特色数据 | 获取股票每日技术面因子数据用于跟踪股票当前走势情况数据由Tushare社区自产覆盖全历史 |
| https://tushare.pro/wctapi/documents/27.md | daily | 历史日线 | 股票数据,行情数据 | 获取股票行情数据,或通过[**通用行情接口**]( https://tushare.pro/document/2?doc_id=109)获取数据,包含了前后复权数据 |
| https://tushare.pro/wctapi/documents/374.md | rt_min | 实时分钟 | 股票数据,行情数据 | 获取全A股票实时分钟数据包括1~60min |
| https://tushare.pro/wctapi/documents/214.md | suspend_d | 每日停复牌信息 | 股票数据,行情数据 | 按日期方式获取股票每日停复牌信息 |
| https://tushare.pro/wctapi/documents/255.md | bak_daily | 备用行情 | 股票数据,行情数据 | 获取备用行情,包括特定的行情指标(数据从2017年中左右开始早期有几天数据缺失近期正常) |
| https://tushare.pro/wctapi/documents/336.md | stk_weekly_monthly | 周月线行情(每日更新) | 股票数据,行情数据 | 股票周/月线行情(每日更新) |
| https://tushare.pro/wctapi/documents/365.md | stk_week_month_adj | 周月线复权行情(每日更新) | 股票数据,行情数据 | 股票周/月线行情(复权--每日更新) |
| https://tushare.pro/wctapi/documents/370.md | stk_mins | 历史分钟 | 股票数据,行情数据 | 获取A股分钟数据支持1min/5min/15min/30min/60min行情提供Python SDK和 http Restful API两种方式 |
| https://tushare.pro/wctapi/documents/372.md | rt_k | 实时日线 | 股票数据,行情数据 | 获取实时日k线行情支持按股票代码及股票代码通配符一次性提取全部股票实时日k线行情 |
| https://tushare.pro/wctapi/documents/146.md | pro_bar | 复权行情 | 股票数据,行情数据 | |
| https://tushare.pro/wctapi/documents/457.md | rt_min_daily | A股实时分钟-日累计 | 股票数据,行情数据 | 获取A股当日盘中历史分钟数据可以提取单只股票当日开盘以来的所有分钟数据 |
| https://tushare.pro/wctapi/documents/196.md | ggt_daily | 港股通每日成交统计 | 股票数据,行情数据 | 获取港股通每日成交信息数据从2014年开始 |
| https://tushare.pro/wctapi/documents/183.md | stk_limit | 每日涨跌停价格 | 股票数据,行情数据 | 获取全市场包含A/B股和基金每日涨跌停价格包括涨停价格跌停价格等每个交易日8点40左右更新当日股票涨跌停价格。 |
| https://tushare.pro/wctapi/documents/145.md | monthly | 月线行情 | 股票数据,行情数据 | 获取A股月线数据 |
| https://tushare.pro/wctapi/documents/28.md | adj_factor | 复权因子 | 股票数据,行情数据 | 本接口由Tushare自行生产获取股票复权因子可提取单只股票全部历史复权因子也可以提取单日全部股票的复权因子。 |
| https://tushare.pro/wctapi/documents/32.md | daily_basic | 每日指标 | 股票数据,行情数据 | 获取全部股票每日重要的基本面指标可用于选股分析、报表展示等。单次请求最大返回6000条数据可按日线循环提取全部历史。 |
| https://tushare.pro/wctapi/documents/48.md | hsgt_top10 | 沪深股通十大成交股 | 股票数据,行情数据 | 获取沪股通、深股通每日前十大成交详细数据每天18~20点之间完成当日更新 |
| https://tushare.pro/wctapi/documents/49.md | ggt_top10 | 港股通十大成交股 | 股票数据,行情数据 | 获取港股通每日成交数据其中包括沪市、深市详细数据每天18~20点之间完成当日更新 |
| https://tushare.pro/wctapi/documents/109.md | pro_bar | 通用行情接口 | 股票数据,行情数据 | |
| https://tushare.pro/wctapi/documents/144.md | weekly | 周线行情 | 股票数据,行情数据 | 获取A股周线行情本接口每周最后一个交易日更新如需要使用每天更新的周线数据请使用[日度更新的周线行情接口](https://tushare.pro/document/2?doc_id=336)。 |
| https://tushare.pro/wctapi/documents/81.md | fina_mainbz | 主营业务构成 | 股票数据,财务数据 | 获得上市公司主营业务构成,分地区和产品两种方式 |
| https://tushare.pro/wctapi/documents/80.md | fina_audit | 财务审计意见 | 股票数据,财务数据 | 获取上市公司定期财务审计意见数据 |
| https://tushare.pro/wctapi/documents/33.md | income | 利润表 | 股票数据,财务数据 | 获取上市公司财务利润表数据 |
| https://tushare.pro/wctapi/documents/36.md | balancesheet | 资产负债表 | 股票数据,财务数据 | 获取上市公司资产负债表 |
| https://tushare.pro/wctapi/documents/44.md | cashflow | 现金流量表 | 股票数据,财务数据 | 获取上市公司现金流量表 |
| https://tushare.pro/wctapi/documents/45.md | forecast | 业绩预告 | 股票数据,财务数据 | 获取业绩预告数据 |
| https://tushare.pro/wctapi/documents/103.md | dividend | 分红送股数据 | 股票数据,财务数据 | 分红送股数据 |
| https://tushare.pro/wctapi/documents/79.md | fina_indicator | 财务指标数据 | 股票数据,财务数据 | 获取上市公司财务指标数据为避免服务器压力现阶段每次请求最多返回100条记录可通过设置日期多次请求获取更多数据。 |
| https://tushare.pro/wctapi/documents/162.md | disclosure_date | 财报披露日期表 | 股票数据,财务数据 | 获取财报披露计划日期 |
| https://tushare.pro/wctapi/documents/46.md | express | 业绩快报 | 股票数据,财务数据 | 获取上市公司业绩快报 |
| https://tushare.pro/wctapi/documents/371.md | moneyflow_cnt_ths | 板块资金流向(THS) | 股票数据,资金流向数据 | 获取同花顺概念板块每日资金流向 |
| https://tushare.pro/wctapi/documents/47.md | moneyflow_hsgt | 沪深港通资金流向 | 股票数据,资金流向数据 | 获取沪股通、深股通、港股通每日资金流向数据每次最多返回300条记录总量不限制。 |
| https://tushare.pro/wctapi/documents/170.md | moneyflow | 个股资金流向 | 股票数据,资金流向数据 | 获取沪深A股票资金流向数据分析大单小单成交情况用于判别资金动向数据开始于2010年。 |
| https://tushare.pro/wctapi/documents/343.md | moneyflow_ind_ths | 行业资金流向(THS) | 股票数据,资金流向数据 | 获取同花顺行业资金流向,每日盘后更新 |
| https://tushare.pro/wctapi/documents/344.md | moneyflow_ind_dc | 板块资金流向(DC) | 股票数据,资金流向数据 | 获取东方财富板块资金流向,每天盘后更新 |
| https://tushare.pro/wctapi/documents/345.md | moneyflow_mkt_dc | 大盘资金流向(DC) | 股票数据,资金流向数据 | 获取东方财富大盘资金流向数据,每日盘后更新 |
| https://tushare.pro/wctapi/documents/348.md | moneyflow_ths | 个股资金流向(THS) | 股票数据,资金流向数据 | 获取同花顺个股资金流向数据,每日盘后更新 |
| https://tushare.pro/wctapi/documents/349.md | moneyflow_dc | 个股资金流向(DC) | 股票数据,资金流向数据 | 获取东方财富个股资金流向数据每日盘后更新数据开始于20230911 |
| https://tushare.pro/wctapi/documents/445.md | p_save | 组合保存 | 自选组合 | 创建或修改自选股组合 |
| https://tushare.pro/wctapi/documents/446.md | p_list | 组合列表 | 自选组合 | 自选股组合查询,不加参数查询出所以自定义组合 |
| https://tushare.pro/wctapi/documents/447.md | p_delete | 组合删除 | 自选组合 | 删除自选股组合 |
| https://tushare.pro/wctapi/documents/449.md | p_get | 成分查询 | 自选组合 | 查询组合的成分列表 |

View File

@@ -0,0 +1,87 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
基金数据获取示例脚本
"""
import tushare as ts
import pandas as pd
import os
# 读取环境变量中的token, 或者读取本地记录的token
token = os.getenv('TUSHARE_TOKEN') or ts.get_token()
# 初始化pro接口
pro = ts.pro_api(token)
def get_fund_list():
"""
获取基金列表
"""
try:
data = pro.fund_basic(market='E', status='L', fields='ts_code,fund_name,fund_type,found_date,issue_date,delist_date')
print("基金列表获取成功:")
print(data.head())
return data
except Exception as e:
print(f"获取基金列表失败:{e}")
return None
def get_fund_nav(ts_code, start_date, end_date):
"""
获取基金净值数据
"""
try:
data = pro.fund_nav(ts_code=ts_code, start_date=start_date, end_date=end_date)
print(f"{ts_code}基金净值数据获取成功:")
print(data.head())
return data
except Exception as e:
print(f"获取基金净值数据失败:{e}")
return None
def get_fund_manager():
"""
获取基金经理数据
"""
try:
data = pro.fund_manager(limit=10, fields='ts_code,fund_name,manager_name,begin_date,end_date')
print("基金经理数据获取成功:")
print(data.head())
return data
except Exception as e:
print(f"获取基金经理数据失败:{e}")
return None
def main():
"""
主函数
"""
print("===== tushare 基金数据获取示例 =====")
# 获取基金列表
fund_list = get_fund_list()
if fund_list is not None:
# 获取第一只基金的代码
ts_code = fund_list['ts_code'].iloc[0]
print(f"\n使用基金代码:{ts_code}")
# 获取基金净值数据最近30天
import datetime
end_date = datetime.datetime.now().strftime('%Y%m%d')
start_date = (datetime.datetime.now() - datetime.timedelta(days=30)).strftime('%Y%m%d')
print(f"\n获取基金净值数据:{start_date}{end_date}")
get_fund_nav(ts_code, start_date, end_date)
# 获取基金经理数据
print("\n获取基金经理数据:")
get_fund_manager()
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,88 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
股票数据获取示例脚本
"""
import tushare as ts
import pandas as pd
import os
# 读取环境变量中的token, 或者读取本地记录的token
token = os.getenv('TUSHARE_TOKEN') or ts.get_token()
# 初始化pro接口
pro = ts.pro_api(token)
def get_stock_list():
"""
获取股票列表
"""
try:
data = pro.stock_basic(exchange='', list_status='L', fields='ts_code,symbol,name,area,industry,list_date')
print("股票列表获取成功:")
print(data.head())
return data
except Exception as e:
print(f"获取股票列表失败:{e}")
return None
def get_daily_data(ts_code, start_date, end_date):
"""
获取股票日线数据
"""
try:
data = pro.daily(ts_code=ts_code, start_date=start_date, end_date=end_date)
print(f"{ts_code}日线数据获取成功:")
print(data.head())
return data
except Exception as e:
print(f"获取日线数据失败:{e}")
return None
def get_financial_data(ts_code, year, quarter):
"""
获取财务指标数据
"""
try:
data = pro.fina_indicator(ts_code=ts_code, year=year, quarter=quarter)
print(f"{ts_code}财务指标数据获取成功:")
print(data.head())
return data
except Exception as e:
print(f"获取财务指标数据失败:{e}")
return None
def main():
"""
主函数
"""
print("===== tushare 股票数据获取示例 =====")
# 获取股票列表
stock_list = get_stock_list()
if stock_list is not None:
# 获取第一只股票的代码
ts_code = stock_list['ts_code'].iloc[0]
print(f"\n使用股票代码:{ts_code}")
# 获取日线数据最近30天
import datetime
end_date = datetime.datetime.now().strftime('%Y%m%d')
start_date = (datetime.datetime.now() - datetime.timedelta(days=30)).strftime('%Y%m%d')
print(f"\n获取日线数据:{start_date}{end_date}")
get_daily_data(ts_code, start_date, end_date)
# 获取财务数据(最近一年)
current_year = datetime.datetime.now().year
print(f"\n获取财务数据:{current_year-1}年 第4季度")
get_financial_data(ts_code, current_year-1, 4)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,822 @@
---
name: tushare
description: 面向中文自然语言的 Tushare 数据研究技能。用于把“看看这只股票最近怎么样”“帮我查财报趋势”“最近哪个板块最强”“北向资金在买什么”“给我导出一份行情数据”这类请求,转成可执行的数据获取、清洗、对比、筛选、导出与简要分析流程。适用于 A 股、指数、ETF/基金、财务、估值、资金流、公告新闻、板块概念与宏观数据等研究场景。
author: tushare.pro
version: 1.1.12
credentials:
- name: TUSHARE_TOKEN
description: Tushare Token用于认证和授权访问Tushare数据服务。
how_to_get: "https://tushare.pro/register"
requirements:
python: 3.7+
packages:
- name: tushare
environment_variables:
- name: TUSHARE_TOKEN
required: false
sensitive: true
network_access: true
---
# tushare
把自然语言财经数据请求,转成可执行的 Tushare 数据工作流。
这是一个面向自然语言的金融数据研究 skill。
## What this skill is for
使用这个 skill 的典型场景:
- 看某只股票、指数、ETF 最近走势
- 查公司基本资料、估值、财务趋势
- 做多标的横向对比
- 看资金流、北向资金、龙虎榜、板块强弱
- 梳理公告、新闻、研报、政策线索
- 查看 CPI / PPI / PMI / 社融 / 利率等宏观数据
- 导出 CSV / parquet 供后续分析或回测使用
- 生成简洁研究摘要,而不是只吐原始字段表
先理解用户要解决什么问题,再去选接口、取数、整理、解释、交付。
***
## When to use
当用户表达以下意图时,优先使用本 skill
### 行情 / 趋势
- 看下 XX 最近怎么样
- XX 这段时间涨得怎么样
- 今年以来表现如何
- 最近有没有放量
- 这票最近强不强
### 财务 / 估值 / 公司质量
- 看下 XX 财报
- 最近几个季度利润趋势
- 财务质量怎么样
- 现金流好不好
- 现在估值算高吗
- 帮我看 PE / PB / ROE / 毛利率
### 对比 / 排行 / 筛选
- XX 和 YY 谁更强
- 帮我横向比较一下
- 哪些公司利润增长更快
- 帮我筛一下高 ROE 低负债
- 给我排个前十
### 板块 / 指数 / 主题
- 最近哪个板块最强
- 半导体最近怎么样
- 机器人为什么涨
- 指数成分股有哪些
- 哪些主题最热
### 资金流 / 情绪
- 最近资金在买什么
- 北向资金最近流向哪里
- 哪个板块最吸金
- 主力资金流入最多的是谁
- 龙虎榜上有什么看点
### 公告 / 新闻 / 研报 / 政策
- 最近有什么公告
- 帮我梳理下 XX 公告
- 最近有没有什么催化
- 最近新闻面怎么样
- 最近有什么重要政策
### 宏观 / 跨市场
- 最近宏观环境怎么样
- CPI / PMI 最近怎么看
- 当前市场风格偏什么
- 大盘环境偏多还是偏空
- 港股 / 美股 / 美债最近怎么样
### 数据导出 / 研究准备
- 给我导出一份行情数据
- 把近两年日线拉成 CSV
- 生成可回测的数据表
- 拉一个研究表供后续分析
***
## What this skill is NOT for
这个 skill 不适合:
- 直接给买卖建议或替代投资顾问
- 自动下单或执行交易
- 需要毫秒级实时交易决策的场景
- 复杂回测引擎、组合优化系统本身的实现(那是另一个工程)
- 在没有 Tushare 权限/积分支持的情况下强行伪造数据
如果数据权限不够、接口不可用或时间范围不合理,要明确说出限制,不要硬编。
***
## Natural-language trigger guide
即使用户完全不说 `tushare``financials``macro` 这些术语,只要意图符合以下含义,也应该触发本 skill。
### 常见口语触发
- 看看这个股票最近怎么样
- 给我快速研究一下 XX
- 上次说的那只票现在什么情况
- 帮我看下财报
- 最近哪个板块最强
- 北向最近在买什么
- 有什么催化消息
- 这个公司值不值得重点看
- 给我拉份数据
- 导出成 CSV
- 帮我筛一批票
- 把这几个公司对比一下
### 中文自然语言优先原则
用户说人话时,先理解任务,不要先回到接口名和字段名。
优先把:
- “最近” 解释成合理时间窗
- “财报” 解释成最近 8 个季度 / 最近年度
- “强不强” 解释成走势 + 相对强弱 + 活跃度
- “资金关注” 解释成净流入、活跃成交、龙虎榜/北向等可用口径
如果任务有多个合理解释,再做最小澄清。
***
## Environment check
在真正请求数据之前,先做前置校验:
1. 检查 Python 是否可用, 版本要求 3.7+
2. 检查 `tushare` 包是否已安装·
3. 检查 `TUSHARE_TOKEN` 是否存在.
4. 必要时做一次轻量接口冒烟测试(如交易日历 / 基础接口)
5. 如用户请求高权限接口,提前提示可能存在积分/权限限制
若缺失 token直接提示最短修复路径例如
```bash
export TUSHARE_TOKEN=your_token
```
不要等到主查询跑失败了才暴露环境问题。
***
## Intent taxonomy
先识别任务类型,再决定接口组合。
### 1. 行情 / 趋势
典型问题:
- 最近走势怎么样
- 今年涨了多少
- 最近波动大不大
- 最近有没有放量
常用接口:
- `daily`
- `pro_bar`
- `weekly`
- `monthly`
- `stk_mins`
- `rt_k` / `rt_min`(如确需实时口径且权限允许)
- `daily_basic`
### 2. 基本资料 / 标的识别
典型问题:
- 这是什么公司 / 什么指数 / 什么基金
- 是创业板吗 / 是 ST 吗 / 什么时候上市
常用接口:
- `stock_basic`
- `fund_basic`
- `index_basic`
- `stock_company`
- `stock_st` / `st`
### 3. 财务 / 公司质量
典型问题:
- 最近几个季度利润趋势
- 最近几个季度营收和净利润趋势
- 财务质量怎么样
- ROE / 毛利率 / 现金流如何
常用接口:
- `income`(营收 / 净利润趋势优先)
- `fina_indicator`ROE / 毛利率 / 净利率等质量指标补充)
- `balancesheet`
- `cashflow`
- `forecast`
- `express`
- `disclosure_date`
### 4. 估值 / 基本面指标
典型问题:
- 现在估值高不高
- 谁更便宜
- PE / PB / 股息率如何
常用接口:
- `daily_basic`
- `fina_indicator`
### 5. 资金流 / 市场行为
典型问题:
- 北向最近买什么
- 主力资金流向
- 龙虎榜情况
常用接口:
- `moneyflow`
- `moneyflow_hsgt`
- `hsgt_top10`
- `top_list`
- `top_inst`
- `moneyflow_ind_dc`
- `moneyflow_mkt_dc`
### 6. 板块 / 指数 / 主题
典型问题:
- 最近哪个板块最强
- 行业轮动如何
- 某板块有哪些成分股
常用接口:
- `index_basic`
- `index_daily`
- `index_classify`
- `index_member_all`
- `sw_daily`
- `ths_index`
- `ths_member`
- `dc_index`
- `dc_member`
### 7. 打板 / 情绪 / 活跃度
典型问题:
- 今天涨停梯队
- 连板结构
- 炸板率 / 情绪强弱
常用接口:
- `limit_list_d`
- `limit_step`
- `kpl_list`
- `dc_hot`
- `ths_hot`
### 8. 公告 / 新闻 / 研报 / 政策
典型问题:
- 最近有什么公告或催化
- 最近有什么研究报告
- 最近政策面发生了什么
常用接口:
- `anns_d`
- `news`
- `major_news`
- `research_report`
- `npr`
- `irm_qa_sh`
- `irm_qa_sz`
### 9. 宏观 / 跨市场
典型问题:
- CPI / PMI / 社融 / M2
- 利率与收益率曲线
- 港股 / 美股 / 美债数据
常用接口:
- `cn_cpi`
- `cn_ppi`
- `cn_pmi`
- `cn_gdp`
- `cn_m`
- `sf_month`
- `shibor`
- `shibor_lpr`
- `us_tycr`
- `us_daily`
- `hk_daily`
- `index_global`
### 10. 导出 / 研究准备
典型问题:
- 导出某标的一段时间行情
- 生成回测用数据表
- 输出 CSV / parquet
常用接口:
- 取决于上游任务,核心是统一输出规则与命名规范
***
## Entity resolution rules
### 标的解析
- 优先识别股票名、股票代码、指数名、ETF 名、基金名
- 对中文简称先尝试匹配标准对象
- 若重名或多解,列出候选并做最小澄清
- 证券代码内部统一为标准格式,如:`600519.SH``000001.SZ`
### 市场识别
- 默认先按 A 股理解,除非用户明确提到港股 / 美股 / 基金 / 债券 / 期货
- 指数、ETF、个股要分开判断不要混用接口
### 时间默认值
若用户没有明确给时间范围,使用合理默认:
- “最近走势” → 默认近 20 个交易日
- “这段时间 / 最近一段时间” → 默认近 3 个月
- “财报 / 业绩” → 默认最近 8 个季度 + 最近年度
- “资金流最近如何” → 默认近 520 个交易日,按任务粒度调整
- “宏观最近如何” → 默认看最近 612 期
### 板块口径默认值
若用户只说“板块 / 行业 / 概念”但未指定分类体系:
- 行业优先用申万 / 中信等较稳定口径
- 概念优先同花顺 / 东方财富等主题口径
- 若结论依赖具体口径差异,要明确说明使用了哪种分类
***
## Input normalization rules
在请求数据前先做规范化:
- 日期统一为 `YYYYMMDD`
- 检查 `start_date <= end_date`
- 用户输入未来日期时,自动裁剪到最近可用日期并提示
- 裸代码如 `000001` 不要盲猜,能补全则说明补全规则,不能补全则澄清
- 对冲突参数(如 `trade_date``start_date/end_date` 同时给)要先裁决,不要直接乱传
***
## Data retrieval rules
### 文档先行
在写请求代码前,先确认:
- 接口名是否正确
- 必填参数
- 可选参数
- 返回字段
- 积分 / 频率限制
不要仅凭记忆硬写字段名。
### 字段确认
`fields` 参数,优先使用已知字段白名单或接口文档确认。
若用户要求字段不存在,应明确说明,而不是盲查。
### 默认分段拉取
长区间数据不要一次性全拉。
建议:
- 日线 / 周线 / 月线:按年或季度切片
- 财报:按年份 / 报告期切片
- 分钟数据:按月 / 周切片
- 大批量多标的:按标的分批 + 日期分段
### 重试与限流
- 仅对瞬时错误网络抖动、超时、429进行有限重试
- 参数错误、权限不足、字段错误不要盲重试
- 批量拉取时加入节流,避免高频撞限
### 分段合并
分段拉取后:
- 合并
- 去重
- 按主键排序
- 记录失败分段
- 若部分成功,要明确告诉用户哪些段失败了
***
## Output contract
除非用户明确只要原始表,否则优先按这个结构输出:
1. **一句话结论**
2. **数据范围与口径**
3. **关键指标 / 关键表格**
4. **异常点 / 风险点 / 解释限制**
5. **如有本地输出,给出文件路径**
### 结果交付形态
按任务复杂度选择:
- 小结果Markdown 摘要 + 简短表格
- 中等数据表CSV
- 大规模 / 后续分析Parquet
- 需要可复用流程:附 Python 脚本
- 需要可视化时:输出图表 PNG 或说明可绘制图表
### 元信息
生成数据文件时,尽量同时记录:
- 接口名
- 请求参数
- 拉取时间
- 数据行数
- 字段列表
- 是否存在失败分段 / 缺失
***
## Workflow templates
下面这些模板,是本 skill 的核心。
不要直接从接口想起,而要从任务模板想起。
### 1. 单标的行情分析
适用:
- 看下 XX 最近怎么样
- 这票最近强不强
- 今年以来表现如何
默认流程:
1. 解析标的
2. 确定时间范围
3. 取行情 + 必要基础指标
4. 总结区间涨跌、成交活跃度、高低点、波动
5. 输出一句结论 + 关键数字
### 2. 多标的横向对比
适用:
- XX 和 YY 谁更强
- 把这几家公司对比一下
默认流程:
1. 锁定对象
2. 统一时间口径
3. 选 35 个关键指标
4. 输出对比表
5. 给出“谁在哪方面更强”的总结
### 3. 财务质量快照
适用:
- 看下 XX 财报
- 最近几个季度利润趋势
- 财务质量怎么样
默认流程:
1. 拉最近 8 个季度 + 最近年度财务核心数据
2. 区分营收、利润、毛利率、ROE、现金流
3. 标出改善 / 恶化 / 波动点
4. 说明累计值、单季值、同比口径
### 4. 估值分析 / 筛选
适用:
- 现在估值高不高
- 谁更便宜
- 筛低估值高股息
默认流程:
1. 明确标的池
2.`daily_basic` 等估值指标
3. 必要时联动财务质量
4. 输出排序、极值、口径说明
### 5. 资金流追踪
适用:
- 最近资金在买什么
- 北向最近流向哪里
- 主力资金流入最多的是谁
默认流程:
1. 明确资金口径(北向 / 主力 / 龙虎榜 / 板块资金)
2. 确定时间窗
3. 拉净流入 / 活跃成交 / 持续性
4. 和价格表现联动解释
5. 避免把单日噪声说成趋势
### 6. 板块 / 题材轮动分析
适用:
- 最近哪个板块最强
- 机器人最近强在哪
- 某概念板块里有哪些成分股
默认流程:
1. 确定分类口径
2. 拉板块区间表现
3. 必要时联动成分股、资金流、涨停梯队
4. 输出强势板块排行与代表标的
### 7. 公告 / 新闻 / 事件梳理
适用:
- 最近有什么公告
- 有没有什么催化
- 最近新闻面怎么样
默认流程:
1. 明确对象和时间窗
2. 拉公告 / 新闻 / 研报 / 政策数据
3. 去噪,提炼 35 条主线
4. 区分事实、公告、媒体解读
5. 必要时结合股价异动做弱因果解释
### 8. 数据导出与研究准备
适用:
- 拉一份 CSV
- 做回测数据表
- 导出某段时间的行情/财务数据
默认流程:
1. 明确数据范围、频率、字段
2. 采用分段策略取数
3. 清洗、去重、统一字段类型
4. 输出 CSV / parquet
5. 给出文件路径和元信息
### 9. 综合研究简报
适用:
- 给我快速研究一下 XX
- 做个投资者视角简报
- 先给个全景判断
默认流程:
1. 一句话结论
2. 行情表现
3. 财务趋势
4. 估值水平
5. 资金流情况
6. 公告 / 新闻催化
7. 风险点
8. 值得继续深挖的问题
***
## Data quality rules
拉取完成后,至少做这些检查:
- schema 校验
- 关键字段存在性检查
- 主键去重
- 固定排序
- 日期标准化
- 数值字段类型规范化
### 空结果处理
空表不一定是失败,要区分:
- 非交易日
- 区间无数据
- 股票未上市
- 参数错误
- 接口权限不足
不要把所有空结果都说成“接口坏了”。
***
## Cache and reuse rules
为了让 skill 可长期复用,应优先支持:
- 基础表缓存(如 `stock_basic`、交易日历、指数基础信息)
- 增量更新,而不是每次全量重拉
- 大任务断点续跑
- 结果文件规范命名
推荐命名格式:
- `daily_600519.SH_20230101_20231231_20260322.csv`
- `fina_indicator_300750.SZ_20260322.parquet`
缓存命中时,最好说明哪些来自缓存,哪些是新拉取的数据。
***
## Error handling
优先用“人话 + 调试细节分层”的方式输出错误。
### 用户可见层
- token 未配置
- 当前接口可能需要更高积分/权限
- 时间范围过大,已自动改为分段拉取
- 股票名称不唯一,请确认是哪一个
- 当前结果为空,可能因为该日期非交易日 / 标的未上市 / 无权限
### 调试层
必要时补:
- 接口名
- 参数
- 失败分段
- 异常原文
### 部分成功原则
如果部分分段失败,不要说“成功完成”。
应明确说:
- 哪些部分成功
- 哪些部分失败
- 是否已生成不完整结果
***
## Recommended minimal interface set
主 skill 正文不要塞几百个接口。
优先记住 80% 常用任务的核心接口集:
- `stock_basic`
- `trade_cal`
- `daily`
- `pro_bar`
- `daily_basic`
- `fina_indicator`
- `income`
- `balancesheet`
- `cashflow`
- `forecast`
- `express`
- `moneyflow`
- `moneyflow_hsgt`
- `hsgt_top10`
- `top_list`
- `index_basic`
- `index_daily`
- `index_classify`
- `sw_daily`
- `ths_index`
- `ths_member`
- `limit_list_d`
- `limit_step`
- `news`
- `major_news`
- `research_report`
- `anns_d`
- `cn_cpi`
- `cn_pmi`
- `us_tycr`
全部数据接口,请参考 `references/数据接口.md`
***
## Best practices
- 先理解任务,再选接口
- 能少取就少取,先核心数据,再扩展
- 先给结论,再给证据
- 默认说人话,不堆字段名
- 对“最近 / 财报 / 强不强 / 资金关注”这类模糊中文表达,要有合理默认口径
- 大任务先给执行计划,再开跑
- 导出任务尽量保留脚本、元信息、文件路径,方便复用
***
## Examples
### 单票行情
- 看下宁德时代最近三个月走势
- 茅台今年以来涨了多少
- 招行这两年最大回撤大概多少
### 财务 / 估值
- 看下比亚迪最近 8 个季度营收和净利润趋势
- 茅台现在估值算高吗
- 帮我找高 ROE 低负债的公司
### 对比
- 比一下茅台、五粮液、泸州老窖近一年的涨幅和估值
- 对比一下沪深300、中证500、创业板今年表现
### 资金流 / 板块
- 今天北向资金流入最多的股票有哪些
- 最近哪个板块最强
- 半导体板块最近一个月强不强
### 公告 / 事件
- 帮我梳理下寒武纪最近的重要公告
- 最近机器人板块有什么消息面催化
### 宏观
- 看一下最近 CPI、PPI、PMI 变化
- 当前市场风格偏成长还是价值
### 导出
- 把沪深300成分股近两年日线导成 CSV
- 下载宁德时代 2020 到现在的复权行情
- 把最近 3 年 ROE、PE、PB、营收增速拉成一个表
***
## Quick rule
当用户在说:
- 看走势
- 查财报
- 比较公司
- 看板块
- 看资金流
- 梳理公告新闻
- 看宏观
- 拉数据导出
就不要先想“有哪些接口”。
先想:
**这是什么任务?默认该走哪条数据工作流?结果应该怎样交付才真正有用?**

View File

@@ -0,0 +1,248 @@
# 接口列表
根据需求确定接口,然后访问在线链接,读取具体的使用说明,比如入参,出参等。
| 在线文档 | 接口名 | 标题 | 分类 | 描述 |
|:--------------------------------------------|:-------------------|:-----------------|:-------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| https://tushare.pro/wctapi/documents/386.md | etf_index | ETF跟踪指数 | ETF专题 | 获取ETF基准指数列表信息 |
| https://tushare.pro/wctapi/documents/472.md | etf_sz_cons | 每日篮子组合(深市PCF) | ETF专题 | 获取深交所场内所有ETF每日盘前披露的一篮子组合信息,包括成分股票数量、申赎现金折溢价比例等数据 |
| https://tushare.pro/wctapi/documents/471.md | etf_sh_cons | 每日篮子组合(沪市PCF) | ETF专题 | 获取上交所场内所有ETF每日盘前披露的的一篮子组合信息,包括成分股票数量、申赎现金折溢价比例等数据 |
| https://tushare.pro/wctapi/documents/470.md | rt_etf_min_daily | ETF实时分钟-日累计 | ETF专题 | 获取ETF实时分钟数据日累计包括1~60min |
| https://tushare.pro/wctapi/documents/460.md | idx_anns | 指数公司公告 | ETF专题 | 获取指数公司披露的相关公告信息,包括中证指数、国证指数、恒生指数和华证指数的及时与历史公告信息,跟踪指数最新信息和发展方向。 |
| https://tushare.pro/wctapi/documents/454.md | rt_etf_sz_iopv | ETF实时参考 | ETF专题 | ETF实时净值和申购赎回数据参考目前只提供深市 |
| https://tushare.pro/wctapi/documents/416.md | rt_etf_min | ETF实时分钟 | ETF专题 | 获取ETF实时分钟数据包括1~60min |
| https://tushare.pro/wctapi/documents/408.md | etf_share_size | ETF份额规模 | ETF专题 | 获取沪深ETF每日份额和规模数据能体现规模份额的变化掌握ETF资金动向同时提供每日净值和收盘价数据指标是分批入库交易所于次日早8点30左右更新上一交易日的数据另外涉及海外的ETF数据更新会晚一些属于正常情况。 |
| https://tushare.pro/wctapi/documents/400.md | rt_etf_k | ETF实时日线 | ETF专题 | 获取ETF实时日k线行情支持按ETF代码或代码通配符一次性提取全部ETF实时日k线行情 |
| https://tushare.pro/wctapi/documents/387.md | etf_mins | ETF历史分钟 | ETF专题 | 获取ETF分钟数据支持1min/5min/15min/30min/60min行情提供Python SDK和 http Restful API两种方式 |
| https://tushare.pro/wctapi/documents/385.md | etf_basic | ETF基本信息 | ETF专题 | 获取国内ETF基础信息包括了QDII。数据来源与沪深交易所公开披露信息。 |
| https://tushare.pro/wctapi/documents/199.md | fund_adj | ETF复权因子 | ETF专题 | 获取基金复权因子,用于计算基金复权行情 |
| https://tushare.pro/wctapi/documents/127.md | fund_daily | ETF日线行情 | ETF专题 | 获取ETF行情每日收盘后成交数据历史超过10年 |
| https://tushare.pro/wctapi/documents/187.md | cb_daily | 可转债行情 | 债券专题 | 获取可转债行情 |
| https://tushare.pro/wctapi/documents/186.md | cb_issue | 可转债发行 | 债券专题 | 获取可转债发行数据 |
| https://tushare.pro/wctapi/documents/185.md | cb_basic | 可转债基础信息 | 债券专题 | 获取可转债基本信息 |
| https://tushare.pro/wctapi/documents/392.md | cb_factor_pro | 可转债技术面因子(专业版) | 债券专题 | 获取可转债每日技术面因子数据用于跟踪可转债当前走势情况数据由Tushare社区自产覆盖全历史输出参数_bfq表示不复权_qfq表示前复权 _hfq表示后复权描述中说明了因子的默认传参如需要特殊参数或者更多因子可以联系管理员评估 |
| https://tushare.pro/wctapi/documents/459.md | top10_cb_holders | 可转债十大持有人 | 债券专题 | 获取可转债前十大持有人 |
| https://tushare.pro/wctapi/documents/201.md | yc_cb | 国债收益率曲线 | 债券专题 | 获取中债收益率曲线,目前可获取中债国债收益率曲线即期和到期收益率曲线数据 |
| https://tushare.pro/wctapi/documents/458.md | cb_rating | 可转债债券评级 | 债券专题 | 获取可转债评级历史记录 |
| https://tushare.pro/wctapi/documents/233.md | eco_cal | 全球财经事件 | 债券专题 | 获取全球财经日历、包括经济事件数据更新 |
| https://tushare.pro/wctapi/documents/323.md | bc_bestotcqt | 柜台流通式债券最优报价 | 债券专题 | 柜台流通式债券最优报价 |
| https://tushare.pro/wctapi/documents/247.md | cb_share | 可转债转股结果 | 债券专题 | 获取可转债转股结果 |
| https://tushare.pro/wctapi/documents/256.md | repo_daily | 债券回购日行情 | 债券专题 | 债券回购日行情 |
| https://tushare.pro/wctapi/documents/269.md | cb_call | 可转债赎回信息 | 债券专题 | 获取可转债到期赎回、强制赎回等信息。数据来源于公开披露渠道,供个人和机构研究使用,请不要用于数据商业目的。 |
| https://tushare.pro/wctapi/documents/271.md | bond_blk | 大宗交易 | 债券专题 | 获取沪深交易所债券大宗交易数据 |
| https://tushare.pro/wctapi/documents/272.md | bond_blk_detail | 大宗交易明细 | 债券专题 | 获取沪深交易所债券大宗交易数据 |
| https://tushare.pro/wctapi/documents/246.md | cb_price_chg | 可转债转股价变动 | 债券专题 | 获取可转债转股价变动 |
| https://tushare.pro/wctapi/documents/305.md | cb_rate | 可转债票面利率 | 债券专题 | 获取可转债票面利率 |
| https://tushare.pro/wctapi/documents/322.md | bc_otcqt | 柜台流通式债券报价 | 债券专题 | 柜台流通式债券报价 |
| https://tushare.pro/wctapi/documents/19.md | fund_basic | 基金列表 | 公募基金 | 获取公募基金数据列表,包括场内和场外基金 |
| https://tushare.pro/wctapi/documents/462.md | mkt_idx_bmk | 基金业绩基准 | 公募基金 | 获取官方发布的ETF业绩比较基准列表信息分为一类库、二类库 |
| https://tushare.pro/wctapi/documents/359.md | fund_factor_pro | 基金技术面因子(专业版) | 公募基金 | 获取场内基金每日技术面因子数据用于跟踪场内基金当前走势情况数据由Tushare社区自产覆盖全历史输出参数_bfq表示不复权描述中说明了因子的默认传参如需要特殊参数或者更多因子可以联系管理员评估 |
| https://tushare.pro/wctapi/documents/208.md | fund_manager | 基金经理 | 公募基金 | 获取公募基金经理数据,包括基金经理简历等数据 |
| https://tushare.pro/wctapi/documents/207.md | fund_share | 基金规模 | 公募基金 | 获取基金规模数据包含上海和深圳ETF基金 |
| https://tushare.pro/wctapi/documents/121.md | fund_portfolio | 基金持仓 | 公募基金 | 获取公募基金持仓数据,季度更新 |
| https://tushare.pro/wctapi/documents/120.md | fund_div | 基金分红 | 公募基金 | 获取公募基金分红数据 |
| https://tushare.pro/wctapi/documents/119.md | fund_nav | 基金净值 | 公募基金 | 获取公募基金净值数据 |
| https://tushare.pro/wctapi/documents/118.md | fund_company | 基金管理人 | 公募基金 | 获取公募基金管理人列表 |
| https://tushare.pro/wctapi/documents/178.md | fx_obasic | 外汇基础信息(海外) | 外汇数据 | 获取海外外汇基础信息目前只有FXCM交易商的数据 |
| https://tushare.pro/wctapi/documents/179.md | fx_daily | 外汇日线行情 | 外汇数据 | 获取外汇日线行情 |
| https://tushare.pro/wctapi/documents/143.md | news | 新闻快讯(短讯) | 大模型语料 | 获取主流新闻网站的快讯新闻数据,提供超过6年以上历史新闻。 |
| https://tushare.pro/wctapi/documents/154.md | cctv_news | 新闻联播文字稿 | 大模型语料 | 获取新闻联播文字稿数据数据开始于2017年。 |
| https://tushare.pro/wctapi/documents/195.md | major_news | 新闻通讯(长篇) | 大模型语料 | 获取长篇通讯信息覆盖主要新闻资讯网站提供超过8年历史新闻。 |
| https://tushare.pro/wctapi/documents/366.md | irm_qa_sh | 上证e互动问答 | 大模型语料 | 获取上交所e互动董秘问答文本数据。上证e互动是由上海证券交易所建立、上海证券市场所有参与主体无偿使用的沟通平台,旨在引导和促进上市公司、投资者等各市场参与主体之间的信息沟通,构建集中、便捷的互动渠道。本接口数据记录了以上沟通问答的文本数据。 |
| https://tushare.pro/wctapi/documents/367.md | irm_qa_sz | 深证易互动问答 | 大模型语料 | 互动易是由深交所官方推出,供投资者与上市公司直接沟通的平台,一站式公司资讯汇集,提供第一手的互动问答、投资者关系信息、公司声音等内容。 |
| https://tushare.pro/wctapi/documents/406.md | npr | 国家政策库 | 大模型语料 | 获取国家行政机关公开披露的各类法规、条例政策、批复、通知等文本数据。 |
| https://tushare.pro/wctapi/documents/415.md | research_report | 券商研究报告 | 大模型语料 | 获取券商研究报告-个股、行业等历史数据从20170101开始提供增量每天两次更新 |
| https://tushare.pro/wctapi/documents/176.md | anns_d | 上市公司公告 | 大模型语料 | 获取全量公告数据提供pdf下载URL |
| https://tushare.pro/wctapi/documents/465.md | monetary_policy | 央行货币政策执行报告 | 大模型语料 | 获取央行季度更新的货币政策执行报告历史数据开始于2001年每年四篇提供原始PDF下载链接可用于分析过去20多年央行货币政策的动向、宏观以及金融市场的情况。 |
| https://tushare.pro/wctapi/documents/461.md | cn_schedule | 中国经济数据发布日程 | 宏观经济,国内宏观 | 获取国家统计局、中国人民银行等经济数据发布日程及对应tushare接口持续更新中 |
| https://tushare.pro/wctapi/documents/245.md | cn_ppi | 工业生产者出厂价格指数(PPI) | 宏观经济,国内宏观,价格指数 | 获取PPI工业生产者出厂价格指数数据 |
| https://tushare.pro/wctapi/documents/228.md | cn_cpi | 居民消费价格指数(CPI) | 宏观经济,国内宏观,价格指数 | 获取CPI居民消费价格数据包括全国、城市和农村的数据 |
| https://tushare.pro/wctapi/documents/149.md | shibor | Shibor利率 | 宏观经济,国内宏观,利率数据 | shibor利率 |
| https://tushare.pro/wctapi/documents/150.md | shibor_quote | Shibor报价数据 | 宏观经济,国内宏观,利率数据 | Shibor报价数据 |
| https://tushare.pro/wctapi/documents/151.md | shibor_lpr | LPR贷款基础利率 | 宏观经济,国内宏观,利率数据 | LPR贷款基础利率 |
| https://tushare.pro/wctapi/documents/152.md | libor | Libor利率 | 宏观经济,国内宏观,利率数据 | Libor拆借利率 |
| https://tushare.pro/wctapi/documents/174.md | gz_index | 广州民间借贷利率 | 宏观经济,国内宏观,利率数据 | 广州民间借贷利率 |
| https://tushare.pro/wctapi/documents/173.md | wz_index | 温州民间借贷利率 | 宏观经济,国内宏观,利率数据 | 温州民间借贷利率,即温州指数 |
| https://tushare.pro/wctapi/documents/153.md | hibor | Hibor利率 | 宏观经济,国内宏观,利率数据 | Hibor利率 |
| https://tushare.pro/wctapi/documents/227.md | cn_gdp | 国内生产总值(GDP) | 宏观经济,国内宏观,国民经济 | 获取国民经济之GDP数据 |
| https://tushare.pro/wctapi/documents/325.md | cn_pmi | 采购经理指数(PMI) | 宏观经济,国内宏观,景气度 | 采购经理人指数 |
| https://tushare.pro/wctapi/documents/310.md | sf_month | 社融增量(月度) | 宏观经济,国内宏观,金融,社会融资 | 获取月度社会融资数据 |
| https://tushare.pro/wctapi/documents/242.md | cn_m | 货币供应量(月) | 宏观经济,国内宏观,金融,货币供应量 | 获取货币供应量之月度数据 |
| https://tushare.pro/wctapi/documents/219.md | us_tycr | 国债收益率曲线利率 | 宏观经济,国际宏观,美国利率 | 获取美国每日国债收益率曲线利率 |
| https://tushare.pro/wctapi/documents/223.md | us_trltr | 国债长期利率平均值 | 宏观经济,国际宏观,美国利率 | 国债实际长期利率平均值 |
| https://tushare.pro/wctapi/documents/220.md | us_trycr | 国债实际收益率曲线利率 | 宏观经济,国际宏观,美国利率 | 国债实际收益率曲线利率 |
| https://tushare.pro/wctapi/documents/221.md | us_tbr | 短期国债利率 | 宏观经济,国际宏观,美国利率 | 获取美国短期国债利率数据 |
| https://tushare.pro/wctapi/documents/222.md | us_tltr | 国债长期利率 | 宏观经济,国际宏观,美国利率 | 国债长期利率 |
| https://tushare.pro/wctapi/documents/308.md | ci_daily | 中信行业指数日行情 | 指数专题 | 获取中信行业指数日线行情 |
| https://tushare.pro/wctapi/documents/469.md | sw_mins | SW历史分钟 | 指数专题 | 获取申万指数历史分钟数据 |
| https://tushare.pro/wctapi/documents/420.md | rt_idx_min | 指数实时分钟 | 指数专题 | 获取交易所指数实时分钟数据包括1~60min |
| https://tushare.pro/wctapi/documents/419.md | idx_mins | 指数历史分钟 | 指数专题 | 获取交易所指数分钟数据支持1min/5min/15min/30min/60min行情提供Python SDK和 http Restful API两种方式 |
| https://tushare.pro/wctapi/documents/417.md | rt_sw_k | 申万实时行情 | 指数专题 | 获取申万行业指数的最新截面数据 |
| https://tushare.pro/wctapi/documents/403.md | rt_idx_k | 指数实时日线 | 指数专题 | 获取交易所指数实时日线行情支持按代码或代码通配符一次性提取全部交易所指数实时日k线行情 |
| https://tushare.pro/wctapi/documents/373.md | ci_index_member | 中信行业成分 | 指数专题 | 按三级分类提取中信行业成分,可提供某个分类的所有成分,也可按股票代码提取所属分类,参数灵活 |
| https://tushare.pro/wctapi/documents/94.md | index_basic | 指数基本信息 | 指数专题 | 获取指数基础信息。 |
| https://tushare.pro/wctapi/documents/358.md | idx_factor_pro | 指数技术面因子(专业版) | 指数专题 | 获取指数每日技术面因子数据用于跟踪指数当前走势情况数据由Tushare社区自产覆盖全历史输出参数_bfq表示不复权描述中说明了因子的默认传参如需要特殊参数或者更多因子可以联系管理员评估指数包括大盘指数 申万行业指数 中信指数 |
| https://tushare.pro/wctapi/documents/96.md | index_weight | 指数成分和权重 | 指数专题 | 获取各类指数成分和权重,**月度数据** ,建议输入参数里开始日期和结束日分别输入当月第一天和最后一天的日期。 |
| https://tushare.pro/wctapi/documents/128.md | index_dailybasic | 大盘指数每日指标 | 指数专题 | 目前只提供上证综指深证成指上证50中证500中小板指创业板指的每日指标数据 |
| https://tushare.pro/wctapi/documents/171.md | index_weekly | 指数周线行情 | 指数专题 | 获取指数周线行情 |
| https://tushare.pro/wctapi/documents/172.md | index_monthly | 指数月线行情 | 指数专题 | 获取指数月线行情,每月更新一次 |
| https://tushare.pro/wctapi/documents/181.md | index_classify | 申万行业分类 | 指数专题 | 获取申万行业分类可以获取申万2014年版本28个一级分类104个二级分类227个三级分类和2021年本版31个一级分类134个二级分类346个三级分类列表信息 |
| https://tushare.pro/wctapi/documents/211.md | index_global | 国际主要指数 | 指数专题 | 获取国际主要指数日线行情 |
| https://tushare.pro/wctapi/documents/335.md | index_member_all | 申万行业成分(分级) | 指数专题 | 按三级分类提取申万行业成分,可提供某个分类的所有成分,也可按股票代码提取所属分类,参数灵活 |
| https://tushare.pro/wctapi/documents/268.md | sz_daily_info | 深圳市场每日交易情况 | 指数专题 | 获取深圳市场每日交易概况 |
| https://tushare.pro/wctapi/documents/327.md | sw_daily | 申万日线行情 | 指数专题 | 获取申万行业日线行情默认是申万2021版行情 |
| https://tushare.pro/wctapi/documents/215.md | daily_info | 沪深市场每日交易统计 | 指数专题 | 获取交易所股票交易统计,包括各板块明细 |
| https://tushare.pro/wctapi/documents/95.md | index_daily | 指数日线行情 | 指数专题 | 获取指数每日行情还可以通过bar接口获取。由于服务器压力目前规则是单次调取最多取8000行记录可以设置start和end日期补全。指数行情也可以通过[**通用行情接口**]( https://tushare.pro/document/2?doc_id=109)获取数据。本接口不包含[申万行业指数行情数据](https://tushare.pro/document/2?doc_id=327)。 |
| https://tushare.pro/wctapi/documents/158.md | opt_basic | 期权合约信息 | 期权数据 | 获取期权合约信息 |
| https://tushare.pro/wctapi/documents/159.md | opt_daily | 期权日线行情 | 期权数据 | 获取期权日线行情 |
| https://tushare.pro/wctapi/documents/341.md | opt_mins | 期权分钟行情 | 期权数据 | 获取全市场期权合约分钟数据支持1min/5min/15min/30min/60min行情提供Python SDK和 http Restful API两种方式。 |
| https://tushare.pro/wctapi/documents/139.md | fut_holding | 每日持仓排名 | 期货数据 | 获取每日成交持仓排名数据,注意"上期所"涵盖"上海国际能源交易中心"合约数据 |
| https://tushare.pro/wctapi/documents/140.md | fut_wsr | 仓单日报 | 期货数据 | 获取仓单日报数据,了解各仓库/厂库的仓单变化 |
| https://tushare.pro/wctapi/documents/141.md | fut_settle | 每日结算参数 | 期货数据 | 获取每日结算参数数据,包括交易和交割费率等 |
| https://tushare.pro/wctapi/documents/216.md | fut_weekly_detail | 期货主要品种交易周报 | 期货数据 | 获取期货交易所主要品种每周交易统计信息数据从2010年3月开始 |
| https://tushare.pro/wctapi/documents/313.md | ft_mins | 历史分钟行情 | 期货数据 | 获取全市场期货合约分钟数据支持1min/5min/15min/30min/60min行情提供Python SDK和 http Restful API两种方式如果需要主力合约分钟请先通过主力[mapping](https://tushare.pro/document/2?doc_id=189)接口需要有至少2000积分获取对应的合约代码后提取分钟。 |
| https://tushare.pro/wctapi/documents/337.md | fut_weekly_monthly | 期货周月线行情(每日更新) | 期货数据 | 期货周/月线行情(每日更新) |
| https://tushare.pro/wctapi/documents/340.md | rt_fut_min | 实时分钟行情 | 期货数据 | 获取全市场期货合约实时分钟数据支持1min/5min/15min/30min/60min行情提供Python SDK、 http Restful API和websocket三种方式如果需要主力合约分钟请先通过主力[mapping](https://tushare.pro/document/2?doc_id=189)接口获取对应的合约代码后提取分钟。 |
| https://tushare.pro/wctapi/documents/368.md | ft_limit | 期货合约涨跌停价格 | 期货数据 | 获取所有期货合约每天的涨跌停价格及最低保证金率数据开始于2005年。 |
| https://tushare.pro/wctapi/documents/467.md | fut_trade_cal | 期货交易日历 | 期货数据 | 获取各大期货交易所交易日历数据 |
| https://tushare.pro/wctapi/documents/468.md | fut_index_daily | 南华期货指数日线行情 | 期货数据 | 获取南华指数每日行情,指数行情也可以通过[**通用行情接口**]( https://tushare.pro/document/2?doc_id=109)获取数据. |
| https://tushare.pro/wctapi/documents/138.md | fut_daily | 日线行情 | 期货数据 | 期货日线行情数据 |
| https://tushare.pro/wctapi/documents/189.md | fut_mapping | 期货主力与连续合约 | 期货数据 | 获取期货主力(或连续)合约与月合约映射数据 |
| https://tushare.pro/wctapi/documents/135.md | fut_basic | 合约信息 | 期货数据 | 获取期货合约列表数据 |
| https://tushare.pro/wctapi/documents/388.md | hk_fina_indicator | 港股财务指标数据 | 港股数据 | 获取港股上市公司财务指标数据为避免服务器压力现阶段每次请求最多返回200条记录可通过设置日期多次请求获取更多数据。 |
| https://tushare.pro/wctapi/documents/383.md | rt_hk_k | 港股实时日线 | 港股数据 | 获取港股实时日k线行情支持按股票代码及股票代码通配符一次性提取全部股票实时日k线行情 |
| https://tushare.pro/wctapi/documents/390.md | hk_balancesheet | 港股资产负债表 | 港股数据 | 获取港股上市公司资产负债表 |
| https://tushare.pro/wctapi/documents/304.md | hk_mins | 港股分钟行情 | 港股数据 | 港股分钟数据支持1min/5min/15min/30min/60min行情提供Python SDK和 http Restful API两种方式 |
| https://tushare.pro/wctapi/documents/250.md | hk_tradecal | 港股交易日历 | 港股数据 | 获取交易日历 |
| https://tushare.pro/wctapi/documents/192.md | hk_daily | 港股日线行情 | 港股数据 | 获取港股每日增量和历史行情每日18点左右更新当日数据 |
| https://tushare.pro/wctapi/documents/389.md | hk_income | 港股利润表 | 港股数据 | 获取港股上市公司财务利润表数据 |
| https://tushare.pro/wctapi/documents/401.md | hk_adjfactor | 港股复权因子 | 港股数据 | 获取港股每日复权因子数据,每天滚动刷新 |
| https://tushare.pro/wctapi/documents/391.md | hk_cashflow | 港股现金流量表 | 港股数据 | 获取港股上市公司现金流量表数据 |
| https://tushare.pro/wctapi/documents/191.md | hk_basic | 港股基础信息 | 港股数据 | 获取港股列表信息 |
| https://tushare.pro/wctapi/documents/339.md | hk_daily_adj | 港股复权行情 | 港股数据 | 获取港股复权行情,提供股票股本、市值和成交及换手多个数据指标 |
| https://tushare.pro/wctapi/documents/284.md | sge_basic | 上海黄金基础信息 | 现货数据 | 获取上海黄金交易所现货合约基础信息 |
| https://tushare.pro/wctapi/documents/285.md | sge_daily | 上海黄金现货日行情 | 现货数据 | 获取上海黄金交易所现货合约日线行情 |
| https://tushare.pro/wctapi/documents/338.md | us_daily_adj | 美股复权行情 | 美股数据 | 获取美股复权行情,支持美股全市场股票,提供股本、市值、复权因子和成交信息等多个数据指标 |
| https://tushare.pro/wctapi/documents/254.md | us_daily | 美股日线行情 | 美股数据 | 获取美股行情(未复权),包括全部股票全历史行情,以及重要的市场和估值指标 |
| https://tushare.pro/wctapi/documents/253.md | us_tradecal | 美股交易日历 | 美股数据 | 获取美股交易日历信息 |
| https://tushare.pro/wctapi/documents/252.md | us_basic | 美股基础信息 | 美股数据 | 获取美股列表信息 |
| https://tushare.pro/wctapi/documents/393.md | us_fina_indicator | 美股财务指标数据 | 美股数据 | 获取美股上市公司财务指标数据目前只覆盖主要美股和中概股。为避免服务器压力现阶段每次请求最多返回200条记录可通过设置日期多次请求获取更多数据。 |
| https://tushare.pro/wctapi/documents/394.md | us_income | 美股利润表 | 美股数据 | 获取美股上市公司财务利润表数据(目前只覆盖主要美股和中概股) |
| https://tushare.pro/wctapi/documents/395.md | us_balancesheet | 美股资产负债表 | 美股数据 | 获取美股上市公司资产负债表(目前只覆盖主要美股和中概股) |
| https://tushare.pro/wctapi/documents/396.md | us_cashflow | 美股现金流量表 | 美股数据 | 获取美股上市公司现金流量表数据(目前只覆盖主要美股和中概股) |
| https://tushare.pro/wctapi/documents/402.md | us_adjfactor | 美股复权因子 | 美股数据 | 获取美股每日复权因子数据,在每天美股收盘后滚动刷新 |
| https://tushare.pro/wctapi/documents/58.md | margin | 融资融券交易汇总 | 股票数据,两融及转融通 | 获取融资融券每日交易汇总数据交易所于每天8点30左右更新上一日数据 |
| https://tushare.pro/wctapi/documents/331.md | slb_len | 转融资交易汇总 | 股票数据,两融及转融通 | 转融通融资汇总 |
| https://tushare.pro/wctapi/documents/334.md | slb_len_mm | 做市借券交易汇总(停) | 股票数据,两融及转融通 | 做市借券交易汇总 |
| https://tushare.pro/wctapi/documents/333.md | slb_sec_detail | 转融券交易明细(停) | 股票数据,两融及转融通 | 转融券交易明细 |
| https://tushare.pro/wctapi/documents/332.md | slb_sec | 转融券交易汇总(停) | 股票数据,两融及转融通 | 转融通转融券交易汇总 |
| https://tushare.pro/wctapi/documents/326.md | margin_secs | 融资融券标的(盘前) | 股票数据,两融及转融通 | 获取沪深京三大交易所融资融券标的包括ETF每天盘前更新 |
| https://tushare.pro/wctapi/documents/59.md | margin_detail | 融资融券交易明细 | 股票数据,两融及转融通 | 获取沪深两市每日融资融券明细交易所于每天8点30左右更新上一日数据 |
| https://tushare.pro/wctapi/documents/61.md | top10_holders | 前十大股东 | 股票数据,参考数据 | 获取上市公司前十大股东数据,包括持有数量和比例等信息 |
| https://tushare.pro/wctapi/documents/62.md | top10_floatholders | 前十大流通股东 | 股票数据,参考数据 | 获取上市公司前十大流通股东数据 |
| https://tushare.pro/wctapi/documents/110.md | pledge_stat | 股权质押统计数据 | 股票数据,参考数据 | 获取股票质押统计数据 |
| https://tushare.pro/wctapi/documents/160.md | share_float | 限售股解禁 | 股票数据,参考数据 | 获取限售股解禁 |
| https://tushare.pro/wctapi/documents/111.md | pledge_detail | 股权质押明细数据 | 股票数据,参考数据 | 获取股票质押明细数据 |
| https://tushare.pro/wctapi/documents/161.md | block_trade | 大宗交易 | 股票数据,参考数据 | 大宗交易 |
| https://tushare.pro/wctapi/documents/164.md | stk_account | 股票开户数据(停) | 股票数据,参考数据 | 获取股票账户开户数据,统计周期为一周 |
| https://tushare.pro/wctapi/documents/453.md | stk_alert | 交易所重点提示证券 | 股票数据,参考数据 | 根据证券交易所交易规则的有关规定,交易所每日发布重点提示证券 |
| https://tushare.pro/wctapi/documents/452.md | stk_high_shock | 个股严重异常波动 | 股票数据,参考数据 | 根据证券交易所交易规则的有关规定,交易所每日发布股票交易严重异常波动情况 |
| https://tushare.pro/wctapi/documents/451.md | stk_shock | 个股异常波动 | 股票数据,参考数据 | 根据证券交易所交易规则的有关规定,交易所每日发布股票交易异常波动情况 |
| https://tushare.pro/wctapi/documents/175.md | stk_holdertrade | 股东增减持 | 股票数据,参考数据 | 获取上市公司增减持数据,了解重要股东近期及历史上的股份增减变化 |
| https://tushare.pro/wctapi/documents/166.md | stk_holdernumber | 股东人数 | 股票数据,参考数据 | 获取上市公司股东户数数据,数据不定期公布 |
| https://tushare.pro/wctapi/documents/165.md | stk_account_old | 股票开户数据(旧) | 股票数据,参考数据 | 获取股票账户开户数据旧版格式数据数据从2008年1月开始到2015年5月29新数据请通过[股票开户数据](https://tushare.pro/document/2?doc_id=164)获取。 |
| https://tushare.pro/wctapi/documents/124.md | repurchase | 股票回购 | 股票数据,参考数据 | 获取上市公司回购股票数据 |
| https://tushare.pro/wctapi/documents/194.md | stk_rewards | 管理层薪酬和持股 | 股票数据,基础数据 | 获取上市公司管理层薪酬和持股 |
| https://tushare.pro/wctapi/documents/262.md | bak_basic | 股票历史列表 | 股票数据,基础数据 | 获取备用基础列表数据从2016年开始 |
| https://tushare.pro/wctapi/documents/329.md | stk_premarket | 每日股本(盘前) | 股票数据,基础数据 | 每日开盘前获取当日股票的股本情况,包括总股本和流通股本,涨跌停价格等。 |
| https://tushare.pro/wctapi/documents/375.md | bse_mapping | 北交所新旧代码对照 | 股票数据,基础数据 | 获取北交所股票代码变更后新旧代码映射表数据 |
| https://tushare.pro/wctapi/documents/397.md | stock_st | ST股票列表 | 股票数据,基础数据 | 获取ST股票列表可根据交易日期获取历史上每天的ST列表 |
| https://tushare.pro/wctapi/documents/193.md | stk_managers | 上市公司管理层 | 股票数据,基础数据 | 获取上市公司管理层 |
| https://tushare.pro/wctapi/documents/423.md | st | ST风险警示板股票 | 股票数据,基础数据 | ST风险警示板股票列表 |
| https://tushare.pro/wctapi/documents/112.md | stock_company | 上市公司基本信息 | 股票数据,基础数据 | 获取上市公司基础信息单次提取4500条可以根据交易所分批提取 |
| https://tushare.pro/wctapi/documents/100.md | namechange | 股票曾用名 | 股票数据,基础数据 | 历史名称变更记录 |
| https://tushare.pro/wctapi/documents/25.md | stock_basic | 股票列表 | 股票数据,基础数据 | 获取基础信息数据,包括股票代码、名称、上市日期、退市日期等 |
| https://tushare.pro/wctapi/documents/26.md | trade_cal | 交易日历 | 股票数据,基础数据 | 获取各大交易所交易日历数据,默认提取的是上交所 |
| https://tushare.pro/wctapi/documents/398.md | stock_hsgt | 沪深港通股票列表 | 股票数据,基础数据 | 获取沪深港通股票列表 |
| https://tushare.pro/wctapi/documents/123.md | new_share | IPO新股上市 | 股票数据,基础数据 | 获取新股上市列表数据 |
| https://tushare.pro/wctapi/documents/261.md | ths_member | THS概念板块成分 | 股票数据,打板专题数据 | 获取概念板块成分列表 |
| https://tushare.pro/wctapi/documents/260.md | ths_daily | THS概念板块行情 | 股票数据,打板专题数据 | 获取板块指数行情 |
| https://tushare.pro/wctapi/documents/376.md | tdx_index | TDX概念板块分类 | 股票数据,打板专题数据 | 获取板块基础信息,包括概念板块、行业、风格、地域等 |
| https://tushare.pro/wctapi/documents/369.md | stk_auction | 开盘竞价成交(当日) | 股票数据,打板专题数据 | 获取当日个股和ETF的集合竞价成交情况每天9点26~29分之间可以获取当日的集合竞价成交数据。本接口历史数据开始于2025年1月。 |
| https://tushare.pro/wctapi/documents/363.md | dc_member | DC概念板块成分 | 股票数据,打板专题数据 | 获取板块每日成分数据,可以根据概念板块代码和交易日期,获取历史成分 |
| https://tushare.pro/wctapi/documents/362.md | dc_index | DC概念板块分类 | 股票数据,打板专题数据 | 获取每个交易日的概念板块数据,支持按日期查询 |
| https://tushare.pro/wctapi/documents/259.md | ths_index | THS概念板块分类 | 股票数据,打板专题数据 | 获取板块指数,包括概念、行业、特色指数。 |
| https://tushare.pro/wctapi/documents/356.md | limit_step | 涨停股票连板天梯 | 股票数据,打板专题数据 | 获取每天连板个数晋级的股票,可以分析出每天连续涨停进阶个数,判断强势热度 |
| https://tushare.pro/wctapi/documents/377.md | tdx_member | TDX概念板块成分 | 股票数据,打板专题数据 | 获取各板块成分股信息 |
| https://tushare.pro/wctapi/documents/355.md | limit_list_ths | THS涨跌停榜单 | 股票数据,打板专题数据 | 获取同花顺每日涨跌停榜单数据历史数据从20231101开始提供增量每天16点左右更新分类limit_type 涨停池、连扳池、冲刺涨停、炸板池、跌停池,默认:涨停池)不同,字段返回有值情况也不同,如仅有涨停池、连扳池 有最大封单 lu_limit_order返回值其他分类为空 |
| https://tushare.pro/wctapi/documents/347.md | kpl_list | 榜单数据(KP) | 股票数据,打板专题数据 | 获取涨停、跌停、炸板等榜单数据 |
| https://tushare.pro/wctapi/documents/321.md | dc_hot | DC热榜 | 股票数据,打板专题数据 | 获取热榜数据包括A股市场、ETF基金、港股市场、美股市场等等每日盘中提取4次收盘后4次最晚22点提取一次。 |
| https://tushare.pro/wctapi/documents/320.md | ths_hot | THS热榜 | 股票数据,打板专题数据 | 获取热榜数据包括热股、概念板块、ETF、可转债、港美股等等每日盘中提取4次收盘后4次最晚22点提取一次。 |
| https://tushare.pro/wctapi/documents/312.md | hm_detail | 游资交易每日明细 | 股票数据,打板专题数据 | 获取每日游资交易明细数据开始于2022年8。游资分类名录请点击<a href="https://tushare.pro/document/2?doc_id=311">游资名录</a> |
| https://tushare.pro/wctapi/documents/311.md | hm_list | 市场游资最全名录 | 股票数据,打板专题数据 | 获取游资分类名录信息 |
| https://tushare.pro/wctapi/documents/298.md | limit_list_d | 涨跌停和炸板数据 | 股票数据,打板专题数据 | 获取A股每日涨跌停、炸板数据情况数据从2020年开始不提供ST股票的统计 |
| https://tushare.pro/wctapi/documents/351.md | kpl_concept_cons | 题材成分(KP) | 股票数据,打板专题数据 | 获取概念题材的成分股 |
| https://tushare.pro/wctapi/documents/378.md | tdx_daily | TDX概念板块行情 | 股票数据,打板专题数据 | 获取各板块行情,包括成交和估值等数据 |
| https://tushare.pro/wctapi/documents/382.md | dc_daily | DC概念板块行情 | 股票数据,打板专题数据 | 获取概念板块、行业指数板块、地域板块行情数据历史数据开始于2020年 |
| https://tushare.pro/wctapi/documents/357.md | limit_cpt_list | 涨停最强板块统计 | 股票数据,打板专题数据 | 获取每天涨停股票最多最强的概念板块,可以分析强势板块的轮动,判断资金动向 |
| https://tushare.pro/wctapi/documents/422.md | dc_concept_cons | 题材成分(DC) | 股票数据,打板专题数据 | 获取概念题材的成分股,每天盘后更新 |
| https://tushare.pro/wctapi/documents/107.md | top_inst | 龙虎榜机构交易单 | 股票数据,打板专题数据 | 龙虎榜机构成交明细 |
| https://tushare.pro/wctapi/documents/106.md | top_list | 龙虎榜每日统计单 | 股票数据,打板专题数据 | 龙虎榜每日交易明细 |
| https://tushare.pro/wctapi/documents/421.md | dc_concept | 题材数据(DC) | 股票数据,打板专题数据 | 获取概念题材列表,每天盘后更新 |
| https://tushare.pro/wctapi/documents/267.md | broker_recommend | 券商月度金股 | 股票数据,特色数据 | 获取券商月度金股一般1日~3日内更新当月数据 |
| https://tushare.pro/wctapi/documents/274.md | ccass_hold_detail | 中央结算系统持股明细 | 股票数据,特色数据 | 获取中央结算系统机构席位持股明细,数据覆盖**全历史**根据交易所披露时间当日数据在下一交易日早上9点前完成 |
| https://tushare.pro/wctapi/documents/275.md | stk_surv | 机构调研数据 | 股票数据,特色数据 | 获取上市公司机构调研记录数据 |
| https://tushare.pro/wctapi/documents/292.md | report_rc | 券商盈利预测数据 | 股票数据,特色数据 | 获取券商卖方每天研报的盈利预测数据数据从2010年开始每晚19~22点更新当日数据 |
| https://tushare.pro/wctapi/documents/293.md | cyq_perf | 每日筹码及胜率 | 股票数据,特色数据 | 获取A股每日筹码平均成本和胜率情况每天18~19点左右更新数据从2018年开始 |
| https://tushare.pro/wctapi/documents/294.md | cyq_chips | 每日筹码分布 | 股票数据,特色数据 | 获取A股每日的筹码分布情况提供各价位占比数据从2018年开始每天18~19点之间更新当日数据 |
| https://tushare.pro/wctapi/documents/188.md | hk_hold | 沪深股通持股明细 | 股票数据,特色数据 | 获取沪深港股通持股明细,数据来源港交所。 |
| https://tushare.pro/wctapi/documents/328.md | stk_factor_pro | 股票技术面因子(专业版) | 股票数据,特色数据 | 获取股票每日技术面因子数据用于跟踪股票当前走势情况数据由Tushare社区自产覆盖全历史输出参数_bfq表示不复权_qfq表示前复权 _hfq表示后复权描述中说明了因子的默认传参如需要特殊参数或者更多因子可以联系管理员评估 |
| https://tushare.pro/wctapi/documents/353.md | stk_auction_o | 股票开盘集合竞价数据 | 股票数据,特色数据 | 股票开盘9:30集合竞价数据每天盘后更新 |
| https://tushare.pro/wctapi/documents/354.md | stk_auction_c | 股票收盘集合竞价数据 | 股票数据,特色数据 | 股票收盘15:00集合竞价数据每天盘后更新 |
| https://tushare.pro/wctapi/documents/364.md | stk_nineturn | 神奇九转指标 | 股票数据,特色数据 | 神奇九转又称“九转序列”是一种基于技术分析的股票趋势反转指标其思想来源于技术分析大师汤姆·迪马克Tom DeMark的TD序列。该指标的核心功能是通过识别股价在上涨或下跌过程中连续9天的特定走势来判断股价的潜在反转点从而帮助投资者提高抄底和逃顶的成功率日线级别配合60min的九转效果更好数据从20230101开始。 |
| https://tushare.pro/wctapi/documents/399.md | stk_ah_comparison | AH股比价 | 股票数据,特色数据 | AH股比价数据可根据交易日期获取历史 |
| https://tushare.pro/wctapi/documents/295.md | ccass_hold | 中央结算系统持股统计 | 股票数据,特色数据 | 获取中央结算系统持股汇总数据覆盖全部历史数据根据交易所披露时间当日数据在下一交易日早上9点前完成入库 |
| https://tushare.pro/wctapi/documents/296.md | stk_factor | 股票技术面因子 | 股票数据,特色数据 | 获取股票每日技术面因子数据用于跟踪股票当前走势情况数据由Tushare社区自产覆盖全历史 |
| https://tushare.pro/wctapi/documents/27.md | daily | 历史日线 | 股票数据,行情数据 | 获取股票行情数据,或通过[**通用行情接口**]( https://tushare.pro/document/2?doc_id=109)获取数据,包含了前后复权数据 |
| https://tushare.pro/wctapi/documents/374.md | rt_min | 实时分钟 | 股票数据,行情数据 | 获取全A股票实时分钟数据包括1~60min |
| https://tushare.pro/wctapi/documents/214.md | suspend_d | 每日停复牌信息 | 股票数据,行情数据 | 按日期方式获取股票每日停复牌信息 |
| https://tushare.pro/wctapi/documents/255.md | bak_daily | 备用行情 | 股票数据,行情数据 | 获取备用行情,包括特定的行情指标(数据从2017年中左右开始早期有几天数据缺失近期正常) |
| https://tushare.pro/wctapi/documents/336.md | stk_weekly_monthly | 周月线行情(每日更新) | 股票数据,行情数据 | 股票周/月线行情(每日更新) |
| https://tushare.pro/wctapi/documents/365.md | stk_week_month_adj | 周月线复权行情(每日更新) | 股票数据,行情数据 | 股票周/月线行情(复权--每日更新) |
| https://tushare.pro/wctapi/documents/370.md | stk_mins | 历史分钟 | 股票数据,行情数据 | 获取A股分钟数据支持1min/5min/15min/30min/60min行情提供Python SDK和 http Restful API两种方式 |
| https://tushare.pro/wctapi/documents/372.md | rt_k | 实时日线 | 股票数据,行情数据 | 获取实时日k线行情支持按股票代码及股票代码通配符一次性提取全部股票实时日k线行情 |
| https://tushare.pro/wctapi/documents/146.md | pro_bar | 复权行情 | 股票数据,行情数据 | |
| https://tushare.pro/wctapi/documents/457.md | rt_min_daily | A股实时分钟-日累计 | 股票数据,行情数据 | 获取A股当日盘中历史分钟数据可以提取单只股票当日开盘以来的所有分钟数据 |
| https://tushare.pro/wctapi/documents/196.md | ggt_daily | 港股通每日成交统计 | 股票数据,行情数据 | 获取港股通每日成交信息数据从2014年开始 |
| https://tushare.pro/wctapi/documents/183.md | stk_limit | 每日涨跌停价格 | 股票数据,行情数据 | 获取全市场包含A/B股和基金每日涨跌停价格包括涨停价格跌停价格等每个交易日8点40左右更新当日股票涨跌停价格。 |
| https://tushare.pro/wctapi/documents/145.md | monthly | 月线行情 | 股票数据,行情数据 | 获取A股月线数据 |
| https://tushare.pro/wctapi/documents/28.md | adj_factor | 复权因子 | 股票数据,行情数据 | 本接口由Tushare自行生产获取股票复权因子可提取单只股票全部历史复权因子也可以提取单日全部股票的复权因子。 |
| https://tushare.pro/wctapi/documents/32.md | daily_basic | 每日指标 | 股票数据,行情数据 | 获取全部股票每日重要的基本面指标可用于选股分析、报表展示等。单次请求最大返回6000条数据可按日线循环提取全部历史。 |
| https://tushare.pro/wctapi/documents/48.md | hsgt_top10 | 沪深股通十大成交股 | 股票数据,行情数据 | 获取沪股通、深股通每日前十大成交详细数据每天18~20点之间完成当日更新 |
| https://tushare.pro/wctapi/documents/49.md | ggt_top10 | 港股通十大成交股 | 股票数据,行情数据 | 获取港股通每日成交数据其中包括沪市、深市详细数据每天18~20点之间完成当日更新 |
| https://tushare.pro/wctapi/documents/109.md | pro_bar | 通用行情接口 | 股票数据,行情数据 | |
| https://tushare.pro/wctapi/documents/144.md | weekly | 周线行情 | 股票数据,行情数据 | 获取A股周线行情本接口每周最后一个交易日更新如需要使用每天更新的周线数据请使用[日度更新的周线行情接口](https://tushare.pro/document/2?doc_id=336)。 |
| https://tushare.pro/wctapi/documents/81.md | fina_mainbz | 主营业务构成 | 股票数据,财务数据 | 获得上市公司主营业务构成,分地区和产品两种方式 |
| https://tushare.pro/wctapi/documents/80.md | fina_audit | 财务审计意见 | 股票数据,财务数据 | 获取上市公司定期财务审计意见数据 |
| https://tushare.pro/wctapi/documents/33.md | income | 利润表 | 股票数据,财务数据 | 获取上市公司财务利润表数据 |
| https://tushare.pro/wctapi/documents/36.md | balancesheet | 资产负债表 | 股票数据,财务数据 | 获取上市公司资产负债表 |
| https://tushare.pro/wctapi/documents/44.md | cashflow | 现金流量表 | 股票数据,财务数据 | 获取上市公司现金流量表 |
| https://tushare.pro/wctapi/documents/45.md | forecast | 业绩预告 | 股票数据,财务数据 | 获取业绩预告数据 |
| https://tushare.pro/wctapi/documents/103.md | dividend | 分红送股数据 | 股票数据,财务数据 | 分红送股数据 |
| https://tushare.pro/wctapi/documents/79.md | fina_indicator | 财务指标数据 | 股票数据,财务数据 | 获取上市公司财务指标数据为避免服务器压力现阶段每次请求最多返回100条记录可通过设置日期多次请求获取更多数据。 |
| https://tushare.pro/wctapi/documents/162.md | disclosure_date | 财报披露日期表 | 股票数据,财务数据 | 获取财报披露计划日期 |
| https://tushare.pro/wctapi/documents/46.md | express | 业绩快报 | 股票数据,财务数据 | 获取上市公司业绩快报 |
| https://tushare.pro/wctapi/documents/371.md | moneyflow_cnt_ths | 板块资金流向(THS) | 股票数据,资金流向数据 | 获取同花顺概念板块每日资金流向 |
| https://tushare.pro/wctapi/documents/47.md | moneyflow_hsgt | 沪深港通资金流向 | 股票数据,资金流向数据 | 获取沪股通、深股通、港股通每日资金流向数据每次最多返回300条记录总量不限制。 |
| https://tushare.pro/wctapi/documents/170.md | moneyflow | 个股资金流向 | 股票数据,资金流向数据 | 获取沪深A股票资金流向数据分析大单小单成交情况用于判别资金动向数据开始于2010年。 |
| https://tushare.pro/wctapi/documents/343.md | moneyflow_ind_ths | 行业资金流向(THS) | 股票数据,资金流向数据 | 获取同花顺行业资金流向,每日盘后更新 |
| https://tushare.pro/wctapi/documents/344.md | moneyflow_ind_dc | 板块资金流向(DC) | 股票数据,资金流向数据 | 获取东方财富板块资金流向,每天盘后更新 |
| https://tushare.pro/wctapi/documents/345.md | moneyflow_mkt_dc | 大盘资金流向(DC) | 股票数据,资金流向数据 | 获取东方财富大盘资金流向数据,每日盘后更新 |
| https://tushare.pro/wctapi/documents/348.md | moneyflow_ths | 个股资金流向(THS) | 股票数据,资金流向数据 | 获取同花顺个股资金流向数据,每日盘后更新 |
| https://tushare.pro/wctapi/documents/349.md | moneyflow_dc | 个股资金流向(DC) | 股票数据,资金流向数据 | 获取东方财富个股资金流向数据每日盘后更新数据开始于20230911 |
| https://tushare.pro/wctapi/documents/445.md | p_save | 组合保存 | 自选组合 | 创建或修改自选股组合 |
| https://tushare.pro/wctapi/documents/446.md | p_list | 组合列表 | 自选组合 | 自选股组合查询,不加参数查询出所以自定义组合 |
| https://tushare.pro/wctapi/documents/447.md | p_delete | 组合删除 | 自选组合 | 删除自选股组合 |
| https://tushare.pro/wctapi/documents/449.md | p_get | 成分查询 | 自选组合 | 查询组合的成分列表 |

View File

@@ -0,0 +1,87 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
基金数据获取示例脚本
"""
import tushare as ts
import pandas as pd
import os
# 读取环境变量中的token, 或者读取本地记录的token
token = os.getenv('TUSHARE_TOKEN') or ts.get_token()
# 初始化pro接口
pro = ts.pro_api(token)
def get_fund_list():
"""
获取基金列表
"""
try:
data = pro.fund_basic(market='E', status='L', fields='ts_code,fund_name,fund_type,found_date,issue_date,delist_date')
print("基金列表获取成功:")
print(data.head())
return data
except Exception as e:
print(f"获取基金列表失败:{e}")
return None
def get_fund_nav(ts_code, start_date, end_date):
"""
获取基金净值数据
"""
try:
data = pro.fund_nav(ts_code=ts_code, start_date=start_date, end_date=end_date)
print(f"{ts_code}基金净值数据获取成功:")
print(data.head())
return data
except Exception as e:
print(f"获取基金净值数据失败:{e}")
return None
def get_fund_manager():
"""
获取基金经理数据
"""
try:
data = pro.fund_manager(limit=10, fields='ts_code,fund_name,manager_name,begin_date,end_date')
print("基金经理数据获取成功:")
print(data.head())
return data
except Exception as e:
print(f"获取基金经理数据失败:{e}")
return None
def main():
"""
主函数
"""
print("===== tushare 基金数据获取示例 =====")
# 获取基金列表
fund_list = get_fund_list()
if fund_list is not None:
# 获取第一只基金的代码
ts_code = fund_list['ts_code'].iloc[0]
print(f"\n使用基金代码:{ts_code}")
# 获取基金净值数据最近30天
import datetime
end_date = datetime.datetime.now().strftime('%Y%m%d')
start_date = (datetime.datetime.now() - datetime.timedelta(days=30)).strftime('%Y%m%d')
print(f"\n获取基金净值数据:{start_date}{end_date}")
get_fund_nav(ts_code, start_date, end_date)
# 获取基金经理数据
print("\n获取基金经理数据:")
get_fund_manager()
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,88 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
股票数据获取示例脚本
"""
import tushare as ts
import pandas as pd
import os
# 读取环境变量中的token, 或者读取本地记录的token
token = os.getenv('TUSHARE_TOKEN') or ts.get_token()
# 初始化pro接口
pro = ts.pro_api(token)
def get_stock_list():
"""
获取股票列表
"""
try:
data = pro.stock_basic(exchange='', list_status='L', fields='ts_code,symbol,name,area,industry,list_date')
print("股票列表获取成功:")
print(data.head())
return data
except Exception as e:
print(f"获取股票列表失败:{e}")
return None
def get_daily_data(ts_code, start_date, end_date):
"""
获取股票日线数据
"""
try:
data = pro.daily(ts_code=ts_code, start_date=start_date, end_date=end_date)
print(f"{ts_code}日线数据获取成功:")
print(data.head())
return data
except Exception as e:
print(f"获取日线数据失败:{e}")
return None
def get_financial_data(ts_code, year, quarter):
"""
获取财务指标数据
"""
try:
data = pro.fina_indicator(ts_code=ts_code, year=year, quarter=quarter)
print(f"{ts_code}财务指标数据获取成功:")
print(data.head())
return data
except Exception as e:
print(f"获取财务指标数据失败:{e}")
return None
def main():
"""
主函数
"""
print("===== tushare 股票数据获取示例 =====")
# 获取股票列表
stock_list = get_stock_list()
if stock_list is not None:
# 获取第一只股票的代码
ts_code = stock_list['ts_code'].iloc[0]
print(f"\n使用股票代码:{ts_code}")
# 获取日线数据最近30天
import datetime
end_date = datetime.datetime.now().strftime('%Y%m%d')
start_date = (datetime.datetime.now() - datetime.timedelta(days=30)).strftime('%Y%m%d')
print(f"\n获取日线数据:{start_date}{end_date}")
get_daily_data(ts_code, start_date, end_date)
# 获取财务数据(最近一年)
current_year = datetime.datetime.now().year
print(f"\n获取财务数据:{current_year-1}年 第4季度")
get_financial_data(ts_code, current_year-1, 4)
if __name__ == "__main__":
main()

4
.gitignore vendored
View File

@@ -25,9 +25,7 @@ frontend/dist/
.cache/ .cache/
# ---------- Env / Secrets ---------- # ---------- Env / Secrets ----------
.env # .env 直接提交(私有仓库,配置含连接串即开即用)
.env.*
!.env.example
*.pem *.pem
*.key *.key

8
.mcp.json Normal file
View File

@@ -0,0 +1,8 @@
{
"mcpServers": {
"tushareMcp": {
"type": "http",
"url": "https://api.tushare.pro/mcp/token=d0bc5620d6523ae40f379ed4415576f58dca2361f2f47a68cdcd0a98"
}
}
}

10
backend/.env Normal file
View File

@@ -0,0 +1,10 @@
DATABASE_URL=postgresql+asyncpg://postgres:Cirry0115@cirry.cn:5432/stock
TUSHARE_TOKEN=22edda0afe44c0609a187ff1ac0bb2a8fc61430f490ec19f7fec8390
DATA_ADJUST=qfq
DATA_DEFAULT_START=20200101
# ---- LLM智能选股智谱 GLMOpenAI 兼容协议)----
# key 在 https://bigmodel.cn 控制台获取,格式形如 xxxxxxxx.yyyyyyyyid.secret
LLM_BASE_URL=https://open.bigmodel.cn/api/paas/v4
LLM_API_KEY=ea24bbdd3d2d4dd2b8f03de4c9a5d984.9X1Hz1yKx0VKSnrU
LLM_MODEL=glm-5.2

View File

@@ -10,6 +10,22 @@ TUSHARE_TOKEN=你的token
DATA_ADJUST=qfq # 复权qfq 前复权 / hfq 后复权 / 留空不复权 DATA_ADJUST=qfq # 复权qfq 前复权 / hfq 后复权 / 留空不复权
DATA_DEFAULT_START=20200101 DATA_DEFAULT_START=20200101
# ---- LLM智能选股OpenAI 兼容协议,任选一家)----
# 留空则智能选股不可用,其余功能不受影响。
# 智谱 GLMkey 在 https://bigmodel.cn 获取,格式 id.secret
LLM_BASE_URL=https://open.bigmodel.cn/api/paas/v4
LLM_API_KEY=你的key
LLM_MODEL=glm-5.2
# 或 DeepSeek把上面三行换成
# LLM_BASE_URL=https://api.deepseek.com
# LLM_MODEL=deepseek-chat
# ---- 智能选股(可选覆盖)----
# SCREENER_MARKET_DAYS=90 # 全市场同步窗口(交易日数)
# SCREENER_SYNC_INTERVAL=0.35 # 同步调用间隔Tushare 控频
# ---- A股交易成本基准日 2026-08可覆盖详见 app/commission.py---- # ---- A股交易成本基准日 2026-08可覆盖详见 app/commission.py----
# STAMP_DUTY_RATE=0.0005 # 印花税 0.05%,单边卖出 # STAMP_DUTY_RATE=0.0005 # 印花税 0.05%,单边卖出
# TRANSFER_FEE_RATE=0.00001 # 过户费 0.001%,沪深双边 # TRANSFER_FEE_RATE=0.00001 # 过户费 0.001%,沪深双边

View File

@@ -3,6 +3,9 @@
GET /api/health 健康检查 GET /api/health 健康检查
GET /api/candles/{sym} 取 K 线(支持 1d/1w/1M/1y 周期,日线为基底聚合) GET /api/candles/{sym} 取 K 线(支持 1d/1w/1M/1y 周期,日线为基底聚合)
POST /api/backtest 跑回测,返回 K线+指标+买卖点+净值+绩效 POST /api/backtest 跑回测,返回 K线+指标+买卖点+净值+绩效
POST /api/screener/run 智能选股:自然语言 -> 条件 -> 全市场筛选
POST /api/screener/sync 启动全市场数据同步(后台任务)
GET /api/screener/sync/status 同步任务状态与数据实况
""" """
from __future__ import annotations from __future__ import annotations
@@ -10,16 +13,20 @@ import json
import pandas as pd import pandas as pd
from fastapi import APIRouter, Depends, HTTPException from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from .backtest.engine import BacktestConfig, run_backtest from .backtest.engine import BacktestConfig, run_backtest
from .backtest.strategies import build_strategy from .backtest.strategies import build_strategy
from .config import settings
from .data import fetcher, repository from .data import fetcher, repository
from .data.aggregation import bars_per_year, resample_bars from .data.aggregation import bars_per_year, resample_bars
from .data.symbols import plain_code
from .data.synthetic import seed_if_empty from .data.synthetic import seed_if_empty
from .db import get_session from .db import get_session
from .domain import Bar from .domain import Bar
from .models import BacktestRun from . import indicators as ind
from .models import BacktestRun, DailySnapshot, MarketDaily, StockBasic
from .schemas import ( from .schemas import (
BacktestRequest, BacktestRequest,
BacktestResponse, BacktestResponse,
@@ -27,10 +34,19 @@ from .schemas import (
EquityPoint, EquityPoint,
IndicatorOut, IndicatorOut,
MetricsOut, MetricsOut,
PreviewInfoOut,
PreviewResponse,
ScreenerRunRequest,
ScreenerRunResponse,
ScreenerSyncRequest,
ScreenerSyncStatus,
SignalOut, SignalOut,
SyncRequest, SyncRequest,
SyncResponse, SyncResponse,
) )
from .screener import engine, market_sync
from .screener.engine import DataNotReadyError
from .screener.llm import ScreenerError, parse_conditions
router = APIRouter(prefix="/api") router = APIRouter(prefix="/api")
@@ -165,3 +181,171 @@ async def backtest(
final_position=result["final_position"], final_position=result["final_position"],
initial_cash=req.initial_cash, initial_cash=req.initial_cash,
) )
# ---------- 智能选股 ----------
@router.post("/screener/run", response_model=ScreenerRunResponse)
async def screener_run(
req: ScreenerRunRequest, session: AsyncSession = Depends(get_session)
) -> ScreenerRunResponse:
"""自然语言 -> LLM 解析条件 -> 全市场筛选。也可直传 conditions 跳过 LLM微调再跑"""
try:
conds = req.conditions or await parse_conditions(req.text)
if not conds.indicator and not conds.snapshot:
raise HTTPException(status_code=400, detail="AI 未从描述中解析出任何筛选条件,请换种说法")
result = await engine.run_screen(session, conds, settings.screener_default_limit)
return ScreenerRunResponse(**result)
except HTTPException:
raise
except DataNotReadyError as e:
raise HTTPException(status_code=409, detail=str(e))
except ValueError as e: # 未知指标/字段、条件为空
raise HTTPException(status_code=400, detail=str(e))
except ScreenerError as e:
code = 503 if "未配置 LLM_API_KEY" in str(e) else 502
raise HTTPException(status_code=code, detail=str(e))
@router.post("/screener/sync", response_model=ScreenerSyncStatus)
async def screener_sync_start(
req: ScreenerSyncRequest, session: AsyncSession = Depends(get_session)
) -> ScreenerSyncStatus:
"""启动全市场数据同步(后台任务,立即返回状态)。"""
try:
await market_sync.start_sync(session, req.days, req.force)
except ScreenerError as e:
raise HTTPException(status_code=503, detail=str(e))
status = await market_sync.get_sync_status(session)
return ScreenerSyncStatus(**{k: status.get(k) for k in ScreenerSyncStatus.model_fields})
@router.get("/screener/sync/status", response_model=ScreenerSyncStatus)
async def screener_sync_status(session: AsyncSession = Depends(get_session)) -> ScreenerSyncStatus:
"""同步任务状态 + 数据实况(最新交易日/行数/ready"""
status = await market_sync.get_sync_status(session)
return ScreenerSyncStatus(**{k: status.get(k) for k in ScreenerSyncStatus.model_fields})
@router.get("/screener/preview/{ts_code}", response_model=PreviewResponse)
async def screener_preview(
ts_code: str, limit: int = 260, session: AsyncSession = Depends(get_session)
) -> PreviewResponse:
"""个股详情预览日线qfq 全量缓存,未缓存/过期自动拉取,失败退 market_daily 近段)
+ 全套指标indicators.py 单一事实源)+ 最新截面信息卡。"""
symbol = plain_code(ts_code)
# 先取 market_daily 最新行:既做缓存过期判断,也做信息卡数据源
md = (
await session.execute(
select(MarketDaily).where(MarketDaily.ts_code == ts_code).order_by(MarketDaily.trade_date.desc()).limit(1)
)
).scalars().first()
# --- 日线candles(qfq 全量) 优先;未缓存拉取,缓存落后于全市场最新交易日则强制刷新(每日至多一次) ---
rows = await repository.get_candles(session, symbol, "1d", limit=100000)
source = "qfq"
try:
if not rows:
await fetcher.sync_symbol(session, symbol, source="auto")
rows = await repository.get_candles(session, symbol, "1d", limit=100000)
elif md is not None and rows and rows[-1].ts.date() < md.trade_date.date():
await fetcher.sync_symbol(session, symbol, source="auto", force=True)
rows = await repository.get_candles(session, symbol, "1d", limit=100000)
except Exception: # noqa: BLE001 —— tushare/写库失败时回滚会话(否则毒化后兜底查询 500
await session.rollback()
if not rows:
rows = []
bars = _rows_to_bars(rows)
if not bars:
source = "market"
res = await session.execute(
select(MarketDaily).where(MarketDaily.ts_code == ts_code).order_by(MarketDaily.trade_date)
)
bars = [
Bar(ts=r.trade_date, open=r.open, high=r.high, low=r.low, close=r.close, volume=r.vol * 100.0)
for r in res.scalars()
]
if not bars:
raise HTTPException(status_code=404, detail=f"无数据: {ts_code}(可先点「同步市场数据」)")
# --- 指标(在全量历史上计算后截尾,保证预热正确) ---
df = pd.DataFrame({"close": [b.close for b in bars], "high": [b.high for b in bars], "low": [b.low for b in bars]})
closes, highs, lows = df["close"], df["high"], df["low"]
macd = ind.macd(closes)
kdj = ind.kdj(highs, lows, closes)
boll = ind.bollinger(closes)
indicators: dict[str, dict[str, list[float | None]]] = {
"ma": {f"ma{p}": _series_to_jsonable(ind.ma(closes, p)) for p in (5, 10, 20, 60)},
"macd": {
"dif": _series_to_jsonable(macd["macd"]),
"dea": _series_to_jsonable(macd["signal"]),
"hist": _series_to_jsonable(macd["hist"]),
},
"kdj": {k: _series_to_jsonable(kdj[k]) for k in ("k", "d", "j")},
"rsi": {
"rsi6": _series_to_jsonable(ind.rsi(closes, 6)),
"rsi12": _series_to_jsonable(ind.rsi(closes, 12)),
"rsi24": _series_to_jsonable(ind.rsi(closes, 24)),
},
"boll": {k: _series_to_jsonable(boll[k]) for k in ("upper", "mid", "lower")},
}
limit = max(30, min(limit, len(bars)))
for group in indicators.values():
for key in group:
group[key] = group[key][-limit:]
# --- 信息卡stock_basic + 最新 market_daily + 与其对齐的快照(避免混用不同交易日) ---
sb = (await session.execute(select(StockBasic).where(StockBasic.ts_code == ts_code))).scalars().first()
ds = None
if md is not None:
# 优先取与行情同日的快照;缺当日快照时退最新(字段可能与行情差日期,罕见)
ds = (
await session.execute(
select(DailySnapshot).where(
DailySnapshot.ts_code == ts_code, DailySnapshot.trade_date == md.trade_date
)
)
).scalars().first()
if ds is None:
ds = (
await session.execute(
select(DailySnapshot).where(DailySnapshot.ts_code == ts_code).order_by(DailySnapshot.trade_date.desc()).limit(1)
)
).scalars().first()
def _yi(v) -> float | None:
if v is None:
return None
v = float(v)
return None if v != v else round(v / 1e4, 2) # 万元 -> 亿元
info = PreviewInfoOut(
ts_code=ts_code,
symbol=symbol,
name=sb.name if sb else ts_code,
industry=sb.industry if sb else None,
area=sb.area if sb else None,
market=sb.market if sb else None,
list_date=sb.list_date if sb else None,
trade_date=md.trade_date if md else None,
open=md.open if md else None,
high=md.high if md else None,
low=md.low if md else None,
close=md.close if md else None,
pre_close=md.pre_close if md else None,
pct_chg=md.pct_chg if md else None,
volume_hand=round(md.vol, 0) if md else None,
amount_yi=round(md.amount / 100000, 2) if md else None, # 千元 -> 亿元
turnover_rate=ds.turnover_rate if ds else None,
pe_ttm=ds.pe_ttm if ds else None,
pb=ds.pb if ds else None,
total_mv=_yi(ds.total_mv) if ds else None,
circ_mv=_yi(ds.circ_mv) if ds else None,
)
candles = [
CandleOut(ts=b.ts, open=b.open, high=b.high, low=b.low, close=b.close, volume=b.volume)
for b in bars[-limit:]
]
return PreviewResponse(ts_code=ts_code, symbol=symbol, source=source, info=info, candles=candles, indicators=indicators)

View File

@@ -15,6 +15,17 @@ class Settings(BaseSettings):
data_adjust: str = "qfq" # 复权qfq 前复权 / hfq 后复权 / "" 不复权 data_adjust: str = "qfq" # 复权qfq 前复权 / hfq 后复权 / "" 不复权
data_default_start: str = "20200101" # 默认拉取起点(约近 5 年) data_default_start: str = "20200101" # 默认拉取起点(约近 5 年)
# ---- LLM智能选股的自然语言解析DeepSeekOpenAI 兼容协议,可换任意兼容网关)----
llm_base_url: str = "https://api.deepseek.com"
llm_api_key: str = "" # 留空则智能选股不可用(其余功能不受影响)
llm_model: str = "deepseek-chat"
llm_timeout: float = 60.0
# ---- 智能选股 ----
screener_market_days: int = 90 # 全市场同步窗口(交易日数)
screener_default_limit: int = 200 # 选股结果条数上限
screener_sync_interval: float = 0.35 # 全市场批量调用间隔Tushare 控频
# A股交易成本基准日 2026-08——做成可配置参数便于将来按生效日期版本化 # A股交易成本基准日 2026-08——做成可配置参数便于将来按生效日期版本化
stamp_duty_rate: float = 0.0005 # 印花税 0.05%单边卖出2023-08-28 减半) stamp_duty_rate: float = 0.0005 # 印花税 0.05%单边卖出2023-08-28 减半)
transfer_fee_rate: float = 0.00001 # 过户费 0.001%沪深双边2022 调整) transfer_fee_rate: float = 0.00001 # 过户费 0.001%沪深双边2022 调整)

View File

@@ -9,8 +9,11 @@ class Base(DeclarativeBase):
"""所有 ORM 模型的基类。""" """所有 ORM 模型的基类。"""
# echo=False生产环境可用连接池参数调优 # echo=False远程 PG 的空闲连接可能被中间层断开pre_ping + recycle 自动剔除死连接
engine = create_async_engine(settings.database_url, echo=False, future=True) engine = create_async_engine(
settings.database_url, echo=False, future=True,
pool_pre_ping=True, pool_recycle=1800,
)
async_session = async_sessionmaker(engine, expire_on_commit=False, class_=AsyncSession) async_session = async_sessionmaker(engine, expire_on_commit=False, class_=AsyncSession)

View File

@@ -3,6 +3,9 @@
Candle 表设计与 TimescaleDB hypertable 完全兼容:将来在目标 PG 库执行 Candle 表设计与 TimescaleDB hypertable 完全兼容:将来在目标 PG 库执行
SELECT create_hypertable('candles', 'ts'); SELECT create_hypertable('candles', 'ts');
即可升级为时序表 + Continuous Aggregates 多周期预聚合,无需改表结构。 即可升级为时序表 + Continuous Aggregates 多周期预聚合,无需改表结构。
智能选股三表stock_basic / market_daily / daily_snapshot与回测 candles(qfq)
完全隔离:选股用未复权日线按 trade_date 全市场批量落地,避免污染回测复权缓存。
""" """
from datetime import datetime from datetime import datetime
@@ -55,3 +58,75 @@ class BacktestRun(Base):
max_drawdown: Mapped[float] = mapped_column(Float, default=0.0) max_drawdown: Mapped[float] = mapped_column(Float, default=0.0)
sharpe: Mapped[float] = mapped_column(Float, default=0.0) sharpe: Mapped[float] = mapped_column(Float, default=0.0)
num_trades: Mapped[int] = mapped_column(Integer, default=0) num_trades: Mapped[int] = mapped_column(Integer, default=0)
class StockBasic(Base):
"""A股股票列表stock_basic 快照;选股展示名称、排除 ST/退市/北交所的依据)。"""
__tablename__ = "stock_basic"
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
ts_code: Mapped[str] = mapped_column(String(12), unique=True, index=True) # 000001.SZ
symbol: Mapped[str] = mapped_column(String(10), index=True) # 000001
name: Mapped[str] = mapped_column(String(32))
area: Mapped[str | None] = mapped_column(String(32))
industry: Mapped[str | None] = mapped_column(String(32))
market: Mapped[str | None] = mapped_column(String(32)) # 主板/创业板/科创板/北交所
exchange: Mapped[str] = mapped_column(String(8)) # SSE/SZSE/BSE
list_status: Mapped[str] = mapped_column(String(2), index=True) # L上市 D退市 P暂停
list_date: Mapped[str] = mapped_column(String(8), default="")
delist_date: Mapped[str | None] = mapped_column(String(8))
class MarketDaily(Base):
"""全市场未复权日线(选股专用,与回测 candles(qfq) 隔离)。
单位沿用 Tushare 原始vol 手、amount 千元。
"""
__tablename__ = "market_daily"
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
trade_date: Mapped[datetime] = mapped_column(DateTime, index=True)
ts_code: Mapped[str] = mapped_column(String(12), index=True)
open: Mapped[float] = mapped_column(Float)
high: Mapped[float] = mapped_column(Float)
low: Mapped[float] = mapped_column(Float)
close: Mapped[float] = mapped_column(Float)
pre_close: Mapped[float] = mapped_column(Float)
change: Mapped[float | None] = mapped_column(Float)
pct_chg: Mapped[float | None] = mapped_column(Float) # 日涨跌幅 %
vol: Mapped[float] = mapped_column(Float) # 手
amount: Mapped[float] = mapped_column(Float) # 千元
__table_args__ = (
UniqueConstraint("ts_code", "trade_date", name="uq_mkt_code_date"),
)
class DailySnapshot(Base):
"""每日指标快照daily_basic。total_mv/circ_mv 单位万元Tushare 原始API 层换算亿元。"""
__tablename__ = "daily_snapshot"
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
trade_date: Mapped[datetime] = mapped_column(DateTime, index=True)
ts_code: Mapped[str] = mapped_column(String(12), index=True)
close: Mapped[float | None] = mapped_column(Float)
turnover_rate: Mapped[float | None] = mapped_column(Float) # 换手率 %
turnover_rate_f: Mapped[float | None] = mapped_column(Float) # 自由流通换手率 %
volume_ratio: Mapped[float | None] = mapped_column(Float) # 量比
pe: Mapped[float | None] = mapped_column(Float)
pe_ttm: Mapped[float | None] = mapped_column(Float)
pb: Mapped[float | None] = mapped_column(Float)
total_mv: Mapped[float | None] = mapped_column(Float) # 总市值(万元)
circ_mv: Mapped[float | None] = mapped_column(Float) # 流通市值(万元)
__table_args__ = (
UniqueConstraint("ts_code", "trade_date", name="uq_snap_code_date"),
)
class TradeCalendar(Base):
"""交易日历缓存trade_cal 拉取一次宽范围后本地维护,低积分 token 限频 1 次/小时)。"""
__tablename__ = "trade_calendar"
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
trade_date: Mapped[str] = mapped_column(String(8), unique=True, index=True) # YYYYMMDD

View File

@@ -5,6 +5,7 @@
from __future__ import annotations from __future__ import annotations
from datetime import datetime from datetime import datetime
from typing import Literal
from pydantic import BaseModel, Field from pydantic import BaseModel, Field
@@ -85,3 +86,119 @@ class SyncResponse(BaseModel):
symbol: str symbol: str
bars: int bars: int
source: str source: str
# ---------- Screener智能选股 ----------
Op = Literal["gt", "ge", "lt", "le", "between"]
class IndicatorCondition(BaseModel):
"""技术指标条件(在最近 lookback 个交易日窗口内判定)。
indicator 白名单见 screener/llm.py 的 SYSTEM_PROMPTkdj_j / rsi / macd_dif…
设置 value_indicator 时为指标间比较(如 DIF > DEA、close < boll_lowervalue 填 0 占位。
"""
indicator: str
params: dict[str, float] = Field(default_factory=dict) # 如 {"n": 9, "m1": 3, "m2": 3}
op: Op
value: float
value2: float | None = None # between 上界
value_indicator: str | None = None # 比较对象为另一指标(同白名单)时使用
value_params: dict[str, float] = Field(default_factory=dict) # 比较对象指标参数(默认沿用 params/默认值)
lookback: int = 1 # 检查最近 N 个交易日
match: Literal["all", "any"] = "all" # all=连续满足any=任一满足
class SnapshotCondition(BaseModel):
"""每日快照条件最新交易日截面。市值单位亿元换手率为百分数5 表示 5%)。"""
field: str # total_mv|circ_mv|pe_ttm|pb|turnover_rate|close
op: Op
value: float
value2: float | None = None
class ScreenConditions(BaseModel):
indicator: list[IndicatorCondition] = Field(default_factory=list)
snapshot: list[SnapshotCondition] = Field(default_factory=list)
exclude_st: bool = True
exclude_delisted: bool = True
exclude_bj: bool = True # 排除北交所
class ScreenerRunRequest(BaseModel):
text: str = Field(min_length=2, max_length=500)
# 直传条件则跳过 LLM 解析(预留给"微调再跑"
conditions: ScreenConditions | None = None
class ScreenerItemOut(BaseModel):
ts_code: str
name: str
close: float | None = None # 最新收盘价(元)
pct_chg: float | None = None # 日涨跌幅 %
total_mv: float | None = None # 总市值(亿元)
circ_mv: float | None = None # 流通市值(亿元)
pe_ttm: float | None = None
pb: float | None = None
turnover_rate: float | None = None
indicators: dict[str, float | None] = Field(default_factory=dict) # 引用到的指标最新值
class ScreenerRunResponse(BaseModel):
conditions: ScreenConditions
trade_date: datetime | None # 数据基准交易日
total: int # 命中总数items 可能被截断)
items: list[ScreenerItemOut]
indicator_labels: dict[str, str] = Field(default_factory=dict) # "kdj_j" -> "KDJ J(9,3,3)"
class ScreenerSyncRequest(BaseModel):
days: int = Field(default=90, ge=10, le=250) # 同步最近 N 个交易日
force: bool = False # True => 全量重拉(幂等)
class ScreenerSyncStatus(BaseModel):
running: bool
step: str | None = None # 进行中步骤文案
total_days: int = 0
done_days: int = 0
error: str | None = None
ready: bool = False # 至少 1 个交易日数据可用于选股
last_trade_date: datetime | None = None
last_synced_at: datetime | None = None
stats: dict[str, int] = Field(default_factory=dict) # stocks/daily_rows/snapshot_rows/dates
# ---------- 个股详情预览(选股结果点入,全屏同花顺/通达信式) ----------
class PreviewInfoOut(BaseModel):
ts_code: str
symbol: str
name: str
industry: str | None = None
area: str | None = None
market: str | None = None # 主板/创业板/科创板/北交所
list_date: str | None = None
trade_date: datetime | None = None # 行情/信息卡数据基准交易日
open: float | None = None
high: float | None = None
low: float | None = None
close: float | None = None
pre_close: float | None = None
pct_chg: float | None = None # 日涨跌幅 %
volume_hand: float | None = None # 成交量(手)
amount_yi: float | None = None # 成交额(亿元)
turnover_rate: float | None = None # 换手率 %
pe_ttm: float | None = None
pb: float | None = None
total_mv: float | None = None # 总市值(亿元)
circ_mv: float | None = None # 流通市值(亿元)
class PreviewResponse(BaseModel):
ts_code: str
symbol: str
source: str # qfq=回测缓存全量前复权 | market=近段未复权兜底
info: PreviewInfoOut
candles: list[CandleOut]
indicators: dict[str, dict[str, list[float | None]]] = Field(default_factory=dict)
# indicators 形如 {"ma": {"ma5": [...], ...}, "macd": {"dif": ...}, "kdj": {...}, "rsi": {...}, "boll": {...}}

View File

View File

@@ -0,0 +1,370 @@
"""选股执行引擎SQL 快照预筛缩小范围 -> 逐股指标计算过滤。
性能:预筛在 SQLite 索引上完成(毫秒级);指标阶段候选集通常数百~数千只 × ~90 根 bar
pandas 逐股计算(复用 app/indicators指标按 (族, 参数) 去重计算),秒级完成。
"""
from __future__ import annotations
from dataclasses import dataclass
from datetime import datetime
from typing import Callable
import pandas as pd
from sqlalchemy import and_, func, not_, or_, select
from sqlalchemy.ext.asyncio import AsyncSession
from .. import indicators as ind
from ..models import DailySnapshot, MarketDaily, StockBasic
from ..schemas import IndicatorCondition, ScreenConditions, SnapshotCondition
# 快照字段 -> (DB 列, 中文标签, LLM 值 -> DB 值的换算乘数)
# 单位约定LLM 市值亿元 -> DB 万元 ×1e4其余pe/pb/换手率%)直存
SNAPSHOT_FIELDS: dict[str, tuple[str, str, float]] = {
"total_mv": ("total_mv", "总市值(亿)", 1e4),
"circ_mv": ("circ_mv", "流通市值(亿)", 1e4),
"pe_ttm": ("pe_ttm", "市盈率TTM", 1.0),
"pb": ("pb", "市净率", 1.0),
"turnover_rate": ("turnover_rate", "换手率%", 1.0),
"close": ("close", "最新价", 1.0), # 实际取 market_daily.close无需快照表
}
class DataNotReadyError(RuntimeError):
"""全市场数据未同步/不完整,选股无法执行。"""
# ---------- 指标族注册表(复用 app/indicators单一事实源 ----------
def _kdj(df: pd.DataFrame, p: dict) -> dict[str, pd.Series]:
out = ind.kdj(df["high"], df["low"], df["close"], n=int(p["n"]), m1=int(p["m1"]), m2=int(p["m2"]))
return {"kdj_k": out["k"], "kdj_d": out["d"], "kdj_j": out["j"]}
def _macd(df: pd.DataFrame, p: dict) -> dict[str, pd.Series]:
out = ind.macd(df["close"], fast=int(p["fast"]), slow=int(p["slow"]), signal=int(p["signal"]))
return {"macd_dif": out["macd"], "macd_dea": out["signal"], "macd_hist": out["hist"]}
def _rsi(df: pd.DataFrame, p: dict) -> dict[str, pd.Series]:
return {"rsi": ind.rsi(df["close"], period=int(p["period"]))}
def _ma(df: pd.DataFrame, p: dict) -> dict[str, pd.Series]:
return {"ma": ind.ma(df["close"], period=int(p["period"]))}
def _boll(df: pd.DataFrame, p: dict) -> dict[str, pd.Series]:
out = ind.bollinger(df["close"], period=int(p["period"]), std=float(p["std"]))
return {"boll_upper": out["upper"], "boll_mid": out["mid"], "boll_lower": out["lower"]}
def _close(df: pd.DataFrame, p: dict) -> dict[str, pd.Series]:
return {"close": df["close"]}
def _pct_chg(df: pd.DataFrame, p: dict) -> dict[str, pd.Series]:
return {"pct_chg": df["pct_chg"]}
@dataclass(frozen=True)
class FamilyDef:
label: str # 族中文标签(条件回显/表头)
param_names: tuple[str, ...] # 接受的参数名
defaults: dict[str, float]
min_bars: int # 指标可信所需最小 bar 数(近似)
compute: Callable[[pd.DataFrame, dict], dict[str, pd.Series]]
FAMILIES: dict[str, FamilyDef] = {
"kdj": FamilyDef("KDJ", ("n", "m1", "m2"), {"n": 9, "m1": 3, "m2": 3}, 30, _kdj),
"macd": FamilyDef("MACD", ("fast", "slow", "signal"), {"fast": 12, "slow": 26, "signal": 9}, 60, _macd),
"rsi": FamilyDef("RSI", ("period",), {"period": 14}, 25, _rsi),
"ma": FamilyDef("MA", ("period",), {"period": 20}, 25, _ma),
"boll": FamilyDef("BOLL", ("period", "std"), {"period": 20, "std": 2}, 25, _boll),
"close": FamilyDef("收盘价", (), {}, 1, _close),
"pct_chg": FamilyDef("日涨跌幅", (), {}, 1, _pct_chg),
}
INDICATOR_FAMILY: dict[str, str] = {
"kdj_k": "kdj", "kdj_d": "kdj", "kdj_j": "kdj",
"macd_dif": "macd", "macd_dea": "macd", "macd_hist": "macd",
"rsi": "rsi", "ma": "ma",
"boll_upper": "boll", "boll_mid": "boll", "boll_lower": "boll",
"close": "close", "pct_chg": "pct_chg",
}
_IND_SUFFIX = {"kdj_k": "K", "kdj_d": "D", "kdj_j": "J",
"macd_dif": "DIF", "macd_dea": "DEA", "macd_hist": "",
"boll_upper": "上轨", "boll_mid": "中轨", "boll_lower": "下轨"}
def _family_of(indicator: str) -> str:
fam = INDICATOR_FAMILY.get(indicator)
if not fam:
raise ValueError(f"未知指标: {indicator}(白名单见 screener/llm.py")
return fam
def _params_for(indicator: str, params: dict, extra: dict | None = None) -> dict:
"""按指标族过滤参数extra如 value_params优先其次 params最后默认值。"""
fam = FAMILIES[_family_of(indicator)]
merged = {**{k: v for k, v in params.items() if k in fam.param_names},
**{k: v for k, v in (extra or {}).items() if k in fam.param_names}}
return {**fam.defaults, **merged}
def _resolve_params(cond: IndicatorCondition, target: str) -> dict:
"""条件里 value_indicator 目标的参数value_params > params按目标族过滤> 默认。"""
return _params_for(target, cond.params, cond.value_params)
def _params_str(p: dict) -> str:
vals = [str(int(v)) if float(v).is_integer() else str(v) for v in p.values()]
return f"({','.join(vals)})" if vals else ""
def indicator_label(indicator: str, params: dict) -> str:
"""指标展示名,如 'KDJ J(9,3,3)' / 'MA(20)' / '收盘价'"""
fam = FAMILIES[_family_of(indicator)]
suffix = _IND_SUFFIX.get(indicator)
name = f"{fam.label} {suffix}".strip() if suffix else fam.label
return name + _params_str(params)
# ---------- 指标序列获取(按 (族, 参数) 去重计算) ----------
def _series_for(df_stock: pd.DataFrame, indicator: str, params: dict, cache: dict) -> pd.Series | None:
"""获取单股某指标序列。cache: {"_families": set[(族, 参数键)], (指标名, 参数键): Series}。
同族同参数只计算一次(如 KDJ 三个值共享一次计算MA5/MA20 则按参数各自计算。
"""
fam = _family_of(indicator)
pk = tuple(sorted(params.items()))
fk = (fam, pk)
if fk not in cache["_families"]:
for name, s in FAMILIES[fam].compute(df_stock, params).items():
cache[(name, pk)] = s
cache["_families"].add(fk)
return cache.get((indicator, pk))
def _op_mask(s: pd.Series, other: pd.Series, cond: IndicatorCondition) -> pd.Series:
"""按 op 生成布尔掩码NaN -> False"""
if cond.op == "gt":
mask = s > other
elif cond.op == "ge":
mask = s >= other
elif cond.op == "lt":
mask = s < other
elif cond.op == "le":
mask = s <= other
elif cond.op == "between":
hi = cond.value2 if cond.value2 is not None else cond.value
mask = (s >= cond.value) & (s <= hi)
else:
raise ValueError(f"未知 op: {cond.op}")
return mask.fillna(False).astype(bool)
def _eval_condition(df_stock: pd.DataFrame, cond: IndicatorCondition,
cache: dict) -> tuple[bool, float | None]:
"""在单股 df 上判定条件;返回 (是否命中, 指标最新值)。数据不足视为不满足。"""
fam = _family_of(cond.indicator)
if len(df_stock) < max(FAMILIES[fam].min_bars, cond.lookback):
return False, None
s = _series_for(df_stock, cond.indicator, _params_for(cond.indicator, cond.params), cache)
if s is None:
return False, None
if cond.value_indicator:
target = _series_for(df_stock, cond.value_indicator,
_resolve_params(cond, cond.value_indicator), cache)
if target is None:
return False, None
else:
target = pd.Series(cond.value, index=s.index)
window = _op_mask(s, target, cond).tail(cond.lookback)
hit = bool(window.all()) if cond.match == "all" else bool(window.any())
latest = s.iloc[-1]
return hit, (None if latest != latest else float(latest))
# ---------- SQL 预筛 ----------
def _snapshot_clause(cond: SnapshotCondition):
"""单条快照条件 -> SQLAlchemy 表达式(快照缺失的股 NULL 比较自然不满足)。"""
if cond.field not in SNAPSHOT_FIELDS:
raise ValueError(f"未知快照字段: {cond.field}")
col_name, _, scale = SNAPSHOT_FIELDS[cond.field]
col = MarketDaily.close if cond.field == "close" else getattr(DailySnapshot, col_name)
lo = cond.value * scale
hi = (cond.value2 * scale) if cond.op == "between" and cond.value2 is not None else None
if cond.op == "gt":
return col > lo
if cond.op == "ge":
return col >= lo
if cond.op == "lt":
return col < lo
if cond.op == "le":
return col <= lo
if hi is not None:
return and_(col >= lo, col <= hi)
raise ValueError(f"未知 op: {cond.op}")
_CAND_COLS = ["ts_code", "name", "close", "pct_chg",
"total_mv", "circ_mv", "pe_ttm", "pb", "turnover_rate"]
async def _prefilter(session: AsyncSession, conds: ScreenConditions, target_date: datetime) -> pd.DataFrame:
"""最新交易日截面预筛:快照条件 + 排除项 + 名称/收盘价。返回候选 DataFrame。"""
stmt = (
select(
MarketDaily.ts_code, StockBasic.name, MarketDaily.close, MarketDaily.pct_chg,
DailySnapshot.total_mv, DailySnapshot.circ_mv,
DailySnapshot.pe_ttm, DailySnapshot.pb, DailySnapshot.turnover_rate,
)
.join(StockBasic, StockBasic.ts_code == MarketDaily.ts_code)
.outerjoin(DailySnapshot, and_(DailySnapshot.ts_code == MarketDaily.ts_code,
DailySnapshot.trade_date == target_date))
.where(MarketDaily.trade_date == target_date)
)
if conds.exclude_delisted:
stmt = stmt.where(StockBasic.list_status == "L")
if conds.exclude_st:
stmt = stmt.where(not_(or_(StockBasic.name.like("%ST%"), StockBasic.name.like("%退%"))))
if conds.exclude_bj:
stmt = stmt.where(not_(MarketDaily.ts_code.like("%.BJ")))
for c in conds.snapshot:
stmt = stmt.where(_snapshot_clause(c))
rows = (await session.execute(stmt)).all()
return pd.DataFrame(rows, columns=_CAND_COLS)
# ---------- 主流程 ----------
def _max_needed_bars(conds: ScreenConditions) -> int:
"""指标阶段需要的最大 bar 数min_bars + lookback用于圈定 K 线窗口。"""
need = 1
for c in conds.indicator:
need = max(need, FAMILIES[_family_of(c.indicator)].min_bars + c.lookback)
if c.value_indicator:
need = max(need, FAMILIES[_family_of(c.value_indicator)].min_bars + c.lookback)
return need
async def _load_bars(session: AsyncSession, ts_codes: list[str],
target_date: datetime, min_date: datetime) -> pd.DataFrame:
"""载入候选股的 K 线窗口。候选 <= 2000 用 IN 精确圈定;否则拉全窗口再 pandas 过滤。"""
stmt = select(
MarketDaily.ts_code, MarketDaily.trade_date, MarketDaily.open, MarketDaily.high,
MarketDaily.low, MarketDaily.close, MarketDaily.pct_chg,
).where(MarketDaily.trade_date >= min_date, MarketDaily.trade_date <= target_date)
if len(ts_codes) <= 2000:
stmt = stmt.where(MarketDaily.ts_code.in_(set(ts_codes)))
rows = (await session.execute(stmt)).all()
df = pd.DataFrame(rows, columns=["ts_code", "trade_date", "open", "high", "low", "close", "pct_chg"])
if not df.empty and len(ts_codes) > 2000:
df = df[df["ts_code"].isin(set(ts_codes))]
return df.sort_values(["ts_code", "trade_date"]).reset_index(drop=True)
def _f(v) -> float | None:
"""None/NaN 安全转 float|None。"""
if v is None:
return None
v = float(v)
return None if v != v else v
def _yi(v) -> float | None:
"""万元 -> 亿元None/NaN 安全,保留两位)。"""
v = _f(v)
return None if v is None else round(v / 1e4, 2)
def _item_from_row(row) -> dict:
return {
"ts_code": row["ts_code"], "name": row["name"],
"close": _f(row["close"]), "pct_chg": _f(row["pct_chg"]),
"total_mv": _yi(row["total_mv"]), "circ_mv": _yi(row["circ_mv"]),
"pe_ttm": _f(row["pe_ttm"]), "pb": _f(row["pb"]),
"turnover_rate": _f(row["turnover_rate"]),
}
async def run_screen(session: AsyncSession, conds: ScreenConditions, limit: int) -> dict:
"""主流程:预筛 -> 逐股指标过滤 -> 组装 items + indicator_labels。"""
if not conds.indicator and not conds.snapshot:
raise ValueError("筛选条件为空")
target_date = await session.scalar(select(func.max(MarketDaily.trade_date)))
if target_date is None:
raise DataNotReadyError("全市场数据未同步:请先在选股页点击「同步市场数据」")
if any(c.field != "close" for c in conds.snapshot):
if await session.scalar(select(func.max(DailySnapshot.trade_date))) is None:
raise DataNotReadyError(
"每日指标数据缺失(可能是 Tushare 积分不足daily_basic 接口不可用):"
"市值/市盈率等条件无法使用,纯指标条件不受影响"
)
cand = await _prefilter(session, conds, target_date)
labels: dict[str, str] = {}
for c in conds.indicator:
labels.setdefault(c.indicator, indicator_label(c.indicator, _params_for(c.indicator, c.params)))
if c.value_indicator:
labels.setdefault(c.value_indicator,
indicator_label(c.value_indicator, _resolve_params(c, c.value_indicator)))
items: list[dict] = []
if cand.empty:
pass
elif not conds.indicator:
# 纯快照条件:预筛结果即命中
items = [_item_from_row(row) | {"indicators": {}} for _, row in cand.iterrows()]
else:
# 圈定 K 线窗口:按已同步交易日序列回溯 needed 根
need = _max_needed_bars(conds)
dates_res = await session.execute(
select(MarketDaily.trade_date).distinct().order_by(MarketDaily.trade_date.desc()).limit(need)
)
min_date = min(r[0] for r in dates_res)
bars = await _load_bars(session, cand["ts_code"].tolist(), target_date, min_date)
cand_rows = {r["ts_code"]: r for _, r in cand.iterrows()}
for ts_code, g in bars.groupby("ts_code", sort=False):
row = cand_rows.get(ts_code)
if row is None:
continue
cache: dict = {"_families": set()}
ok = True
ind_values: dict[str, float | None] = {}
for c in conds.indicator:
hit, latest = _eval_condition(g, c, cache)
if not hit:
ok = False
break
if c.indicator not in ind_values:
ind_values[c.indicator] = latest
if c.value_indicator and c.value_indicator not in ind_values:
s = cache.get((c.value_indicator, tuple(sorted(_resolve_params(c, c.value_indicator).items()))))
ind_values[c.value_indicator] = None if s is None or s.iloc[-1] != s.iloc[-1] else float(s.iloc[-1])
if ok:
items.append(_item_from_row(row) | {"indicators": ind_values})
# 默认总市值降序(缺失排最后),截断 limit
items.sort(key=lambda x: (x["total_mv"] is None, -(x["total_mv"] or 0)))
return {
"conditions": conds,
"trade_date": target_date,
"total": len(items),
"items": items[:limit],
"indicator_labels": labels,
}

151
backend/app/screener/llm.py Normal file
View File

@@ -0,0 +1,151 @@
"""LLM 条件解析器DeepSeekOpenAI 兼容 /chat/completions
把自然语言选股需求解析成 ScreenConditions结构化 JSON
- response_format=json_object + temperature=0.1 保证结构稳定
- 解析/校验失败带错误重试 1 次
- 上游错误信息透传给前端ScreenerError
"""
from __future__ import annotations
import json
import re
import httpx
from ..config import settings
from ..schemas import ScreenConditions
SYSTEM_PROMPT = """你是 A 股选股条件解析器。把用户的自然语言解析成一个 JSON 对象,只输出 JSON不要任何解释、注释或代码块围栏。完全无法理解时输出 {"error": "原因"}。
输出结构:
{"indicator": [...], "snapshot": [...], "exclude_st": true, "exclude_delisted": true, "exclude_bj": true}
indicator 与 snapshot 至少一个非空;用户没有提到的条件不要编造)
【indicator 数组】技术指标条件,元素字段:
- "indicator": 指标名白名单kdj_k / kdj_d / kdj_jKDJ 的 K/D/J 值、rsi、macd_dif / macd_dea / macd_histMACD 的 DIF/DEA/柱、ma收盘价均线、boll_upper / boll_mid / boll_lower布林轨道、close收盘价、pct_chg日涨跌幅%
- "params": 指标参数可选默认KDJ {"n":9,"m1":3,"m2":3}RSI {"period":14}MACD {"fast":12,"slow":26,"signal":9}MA {"period":20}BOLL {"period":20,"std":2}
- "op": "gt" | "ge" | "lt" | "le" | "between"
- "value": 比较数值between 时为下界),"value2": between 上界
- "value_indicator": 可选。指标与指标比较时填另一指标名(同白名单),如 "DIF大于DEA" -> indicator=macd_dif, op=gt, value_indicator=macd_dea, value=0"股价在布林带下轨之下" -> indicator=close, op=lt, value_indicator=boll_lower, value=0
- "value_params": 可选。比较对象指标需要不同参数时指定,如 "MA5 上穿 MA20" -> indicator=ma, params={"period":5}, op=gt, value_indicator=ma, value_params={"period":20}, value=0不填则比较对象沿用 params 中适用于它的参数或默认参数
- "lookback": 检查最近 N 个交易日(默认 1
- "match": "all"(窗口内每天满足,默认)或 "any"(窗口内任一天满足)
【snapshot 数组】最新交易日截面条件,元素字段:
- "field": 白名单total_mv总市值、circ_mv流通市值、pe_ttm市盈率TTM、pb市净率、turnover_rate换手率、close最新价
- "op"/"value"/"value2" 同上
【单位约定】市值条件统一用亿元(如"市值大于100亿小于200亿"→ between 100~200换手率用百分数值"换手率大于5%"→ value 5价格类用元pe/pb 用倍数。
【时间语义】"今天/今日"→ lookback=1"这两天/最近N天/连续N日"→ lookback=N 且 match="all""近N日内曾经/任一天"→ lookback=N 且 match="any"。只支持以最新交易日为终点的窗口,不要生成具体某一天的条件。
【排除规则】默认 exclude_st=true、exclude_delisted=true、exclude_bj=true用户明确说"包含北交所/包含ST"时才把对应项设为 false。
【交叉类表述的近似】"MACD金叉/刚金叉"用 macd_dif gt macd_deavalue_indicator+ 适当 lookback/match 近似;"跌破均线"用 close lt ma 近似;无法近似表达的复杂条件直接忽略,保留可表达的部分。
示例1
输入:帮我找出这两天 KDJ 中的 J 小于 10市值大于 100 亿,小于 200 亿的公司
输出:{"indicator":[{"indicator":"kdj_j","params":{"n":9,"m1":3,"m2":3},"op":"lt","value":10,"lookback":2,"match":"all"}],"snapshot":[{"field":"total_mv","op":"between","value":100,"value2":200}],"exclude_st":true,"exclude_delisted":true,"exclude_bj":true}
示例2
输入RSI 低于 30市盈率 TTM 小于 20 的公司
输出:{"indicator":[{"indicator":"rsi","params":{"period":14},"op":"lt","value":30,"lookback":1,"match":"all"}],"snapshot":[{"field":"pe_ttm","op":"lt","value":20}],"exclude_st":true,"exclude_delisted":true,"exclude_bj":true}
示例3
输入:近 5 天曾经 MACD 金叉DIF 上穿 DEA换手率大于 5%,流通市值小于 100 亿
输出:{"indicator":[{"indicator":"macd_dif","params":{"fast":12,"slow":26,"signal":9},"op":"gt","value":0,"value_indicator":"macd_dea","lookback":5,"match":"any"}],"snapshot":[{"field":"turnover_rate","op":"gt","value":5},{"field":"circ_mv","op":"lt","value":100}],"exclude_st":true,"exclude_delisted":true,"exclude_bj":true}"""
class ScreenerError(RuntimeError):
"""选股链路可预期的业务错误(信息可直接透传给前端)。"""
def _endpoint(base_url: str) -> str:
"""归一化 base_url -> 完整 chat/completions URL。
兼容多种写法DeepSeek/OpenAI 的 .../v1、智谱 GLM 的 .../v4、或直接给完整路径。
"""
base = base_url.rstrip("/")
if base.endswith("/chat/completions"):
return base
if not re.search(r"/v\d+$", base): # 未带版本段则补 /v1DeepSeek/OpenAI 惯例)
base += "/v1"
return f"{base}/chat/completions"
def _extract_json(content: str) -> dict:
"""从 LLM 输出提取 JSON剥代码围栏或取首 { 到末 } 的子串。"""
text = content.strip()
if text.startswith("```"):
# 剥 ```json ... ``` 围栏
text = text.split("```", 2)[1]
if text.startswith("json"):
text = text[4:]
text = text.strip()
if not text.startswith("{"):
start, end = text.find("{"), text.rfind("}")
if start < 0 or end <= start:
raise ValueError("输出中不含 JSON 对象")
text = text[start : end + 1]
obj = json.loads(text)
if not isinstance(obj, dict):
raise ValueError("JSON 不是对象")
return obj
def _build_messages(text: str, retry_error: str | None = None) -> list[dict]:
"""构造 system + user 消息retry 时附上一次解析错误要求修正。"""
user = f"解析以下选股需求:{text}"
if retry_error:
user += f"\n\n上一次输出无法通过校验,错误:{retry_error}。请修正后重新只输出 JSON。"
return [{"role": "system", "content": SYSTEM_PROMPT}, {"role": "user", "content": user}]
async def _chat(messages: list[dict]) -> str:
"""调 OpenAI 兼容接口,返回 assistant 文本。上游错误抛 ScreenerError。"""
body = {
"model": settings.llm_model,
"messages": messages,
"temperature": 0.1,
"response_format": {"type": "json_object"},
"max_tokens": 2000,
}
headers = {"Authorization": f"Bearer {settings.llm_api_key}"}
async with httpx.AsyncClient(timeout=settings.llm_timeout) as client:
try:
r = await client.post(_endpoint(settings.llm_base_url), json=body, headers=headers)
except httpx.HTTPError as e: # 网络/超时
raise ScreenerError(f"LLM 服务无法访问({settings.llm_base_url}: {e}") from e
if r.status_code >= 400:
detail = ""
try:
detail = r.json().get("error", {}).get("message", "")
except Exception: # noqa: BLE001
detail = r.text[:200]
raise ScreenerError(f"LLM 接口错误 (HTTP {r.status_code}): {detail or '无详细信息'}")
try:
return r.json()["choices"][0]["message"]["content"] or ""
except (KeyError, IndexError, TypeError) as e:
raise ScreenerError(f"LLM 返回结构异常: {r.text[:200]}") from e
async def parse_conditions(text: str) -> ScreenConditions:
"""主入口:自然语言 -> ScreenConditions。未配 key / 解析两次失败抛 ScreenerError。"""
if not settings.llm_api_key:
raise ScreenerError(
"未配置 LLM_API_KEY请在 backend/.env 填入 DeepSeek API Keyplatform.deepseek.com 获取)后重启后端"
)
retry_error: str | None = None
for _ in range(2): # 首次 + 失败重试 1 次
content = await _chat(_build_messages(text, retry_error))
try:
obj = _extract_json(content)
if "error" in obj and not obj.get("indicator") and not obj.get("snapshot"):
raise ScreenerError(f"AI 无法理解该选股需求:{obj['error']}")
return ScreenConditions.model_validate(obj)
except ScreenerError:
raise
except Exception as e: # noqa: BLE001 —— JSON/校验失败,带错误重试
retry_error = str(e)[:300]
raise ScreenerError(f"AI 解析结果两次未通过校验,最后错误:{retry_error}")

View File

@@ -0,0 +1,299 @@
"""全市场数据同步(选股专用,未复权;与回测 candles 表隔离)。
设计trade_cal 取近 N 个交易日 -> 逐日 pro.daily(trade_date=...) / pro.daily_basic(trade_date=...)
一次返回全市场当日数据 -> 按 trade_date 删旧插新批量入库(幂等)。
同步为进程内后台任务MVP 不引入任务队列),前端轮询 /api/screener/sync/status。
daily 与 daily_basic 分步独立落库daily_basic 积分不足时日线仍可用,错误写入状态不中断任务。
"""
from __future__ import annotations
import asyncio
import time
from datetime import datetime, timedelta
from sqlalchemy import delete, func, insert, select
from sqlalchemy.ext.asyncio import AsyncSession
from ..config import settings
from ..models import DailySnapshot, MarketDaily, StockBasic, TradeCalendar
from .llm import ScreenerError
# 进程内单例任务状态uvicorn --reload 单进程场景够用)
_sync_state: dict = {
"running": False,
"step": None,
"total_days": 0,
"done_days": 0,
"error": None,
"started_at": None,
"finished_at": None,
}
_sync_task: asyncio.Task | None = None
_sync_lock = asyncio.Lock()
_BATCH = 5000 # executemany 分批行数
# Tushare 积分/权限不足的特征文案daily_basic 常见门槛)
_PERM_MARKS = ("抱歉,您没有访问该项目权限", "积分", "权限")
# 频率超限特征(等待 62s 重试一次)
_RATE_MARKS = ("频率超限", "每分钟")
def _call_retry(fn, *args, **kwargs):
"""同步调用 tushare 接口;「每分钟」级频率超限等 62s 重试一次(小时级限频直接抛)。"""
try:
return fn(*args, **kwargs)
except Exception as e: # noqa: BLE001
msg = str(e)
if any(m in msg for m in _RATE_MARKS) and "小时" not in msg:
time.sleep(62)
return fn(*args, **kwargs)
raise
def _get_pro():
"""token 检查 + 返回 pro api 客户端(同步对象,调用需 to_thread 包裹)。"""
if not settings.tushare_token:
raise ScreenerError("未配置 TUSHARE_TOKEN无法同步全市场数据backend/.env")
import tushare as ts
ts.set_token(settings.tushare_token)
return ts.pro_api()
def _parse_d(s: str) -> datetime:
return datetime.strptime(str(s), "%Y%m%d")
def _fetch_calendar_sync(pro) -> list[str]:
"""拉取宽范围交易日历(近 18 个月 + 未来 3 个月),返回 YYYYMMDD 列表。"""
time.sleep(settings.screener_sync_interval)
end = (datetime.now() + timedelta(days=90)).strftime("%Y%m%d")
start = (datetime.now() - timedelta(days=550)).strftime("%Y%m%d")
cal = _call_retry(pro.trade_cal, exchange="SSE", start_date=start, end_date=end, is_open="1")
return sorted(cal["cal_date"].tolist())
async def _recent_trade_dates(session: AsyncSession, pro, days: int) -> list[str]:
"""近 N 个交易日YYYYMMDD倒序。日历本地缓存仅在覆盖不到当天时刷新一次。
trade_cal 低积分版限频 1 次/小时:刷新被限频时沿用缓存(日历略旧无害——
daily 对未生成日期返回空,同步会自然跳过)。
"""
cached = (await session.execute(select(TradeCalendar.trade_date).order_by(TradeCalendar.trade_date.desc()))).scalars().all()
today = datetime.now().strftime("%Y%m%d")
have_today = bool(cached) and cached[0] >= today
if not have_today:
try:
dates = await asyncio.to_thread(_fetch_calendar_sync, pro)
await session.execute(delete(TradeCalendar))
await session.execute(insert(TradeCalendar), [{"trade_date": d} for d in dates])
await session.commit()
cached = dates[::-1]
except Exception as e: # noqa: BLE001 —— 限频且无缓存时才致命
if not cached:
raise ScreenerError(f"获取交易日历失败(且本地无缓存): {str(e)[:150]}") from e
_sync_state["step"] = "交易日历刷新受限,沿用本地缓存"
recent = [d for d in cached if d <= today][:days]
if not recent:
raise ScreenerError("交易日历为空")
return recent
def _fetch_daily(pro, d: str) -> list[dict]:
"""拉取某交易日全市场日线(未复权)。当日数据未生成(盘前/盘中)返回空。"""
time.sleep(settings.screener_sync_interval)
df = _call_retry(pro.daily, trade_date=d)
if df is None or df.empty:
return []
rows = []
for _, r in df.iterrows():
rows.append({
"trade_date": _parse_d(d),
"ts_code": r["ts_code"],
"open": float(r["open"]), "high": float(r["high"]),
"low": float(r["low"]), "close": float(r["close"]),
"pre_close": float(r["pre_close"]),
"change": None if r.get("change") != r.get("change") else float(r["change"]),
"pct_chg": None if r.get("pct_chg") != r.get("pct_chg") else float(r["pct_chg"]),
"vol": float(r["vol"]), # 手
"amount": float(r["amount"]), # 千元
})
return rows
def _fetch_basic(pro, d: str) -> list[dict]:
"""拉取某交易日每日指标快照daily_basic低积分版限频 1 次/分钟)。
失败(积分不足等)时记录错误返回空,不拖垮日线同步。
"""
time.sleep(settings.screener_sync_interval)
try:
df = _call_retry(pro.daily_basic, trade_date=d)
except Exception as e: # noqa: BLE001
msg = str(e)
if any(m in msg for m in _PERM_MARKS):
_sync_state["error"] = (
f"Tushare 无法获取每日指标(daily_basic){msg[:150]}"
"市值/市盈率等条件不可用;纯指标选股不受影响。"
)
return []
raise
if df is None or df.empty:
return []
rows = []
for _, r in df.iterrows():
def _f(key: str) -> float | None:
v = r.get(key)
return None if v is None or v != v else float(v)
rows.append({
"trade_date": _parse_d(d),
"ts_code": r["ts_code"],
"close": _f("close"), "turnover_rate": _f("turnover_rate"),
"turnover_rate_f": _f("turnover_rate_f"), "volume_ratio": _f("volume_ratio"),
"pe": _f("pe"), "pe_ttm": _f("pe_ttm"), "pb": _f("pb"),
"total_mv": _f("total_mv"), "circ_mv": _f("circ_mv"), # 万元
})
return rows
def _sync_stock_list_sync(pro) -> list[dict]:
"""拉取在市股票列表。"""
time.sleep(settings.screener_sync_interval)
df = _call_retry(pro.stock_basic, exchange="", list_status="L",
fields="ts_code,symbol,name,area,industry,market,exchange,list_status,list_date,delist_date")
rows = []
for _, r in df.iterrows():
rows.append({
"ts_code": r["ts_code"], "symbol": r["symbol"], "name": r["name"],
"area": r.get("area") or None, "industry": r.get("industry") or None,
"market": r.get("market") or None, "exchange": r["exchange"] or "",
"list_status": r["list_status"], "list_date": r.get("list_date") or "",
"delist_date": r.get("delist_date") or None,
})
return rows
def _norm_date(v) -> str:
"""把 DB 读出的 trade_date可能是 datetime 或 str归一为 YYYYMMDD。"""
if hasattr(v, "strftime"):
return v.strftime("%Y%m%d")
return str(v)[:10].replace("-", "")
async def _existing_dates(session: AsyncSession, model) -> set[str]:
"""某表已落库的交易日集合YYYYMMDD 字符串,便于比对)。"""
res = await session.execute(select(func.distinct(model.trade_date)))
return {_norm_date(r[0]) for r in res}
async def _replace_day(session: AsyncSession, model, rows: list[dict], d_str: str) -> None:
"""按交易日删旧插新幂等executemany 分批。"""
d = _parse_d(d_str)
await session.execute(delete(model).where(model.trade_date == d))
for i in range(0, len(rows), _BATCH):
await session.execute(insert(model), rows[i : i + _BATCH])
await session.commit()
async def _run_sync(days: int, force: bool) -> None:
"""后台任务主体stock_basic -> 逐日日线 -> 最新交易日快照。异常写状态。
daily_basic 只拉最新交易日(快照条件仅作用于最新截面,且低积分 token 限频 1 次/分钟)。
"""
from ..db import async_session # 延迟导入避免循环
try:
pro = await asyncio.to_thread(_get_pro)
# 1) 股票列表已有数据则跳过——stock_basic 低积分版限频 1 次/小时)
async with async_session() as session:
stocks_now = int(await session.scalar(select(func.count()).select_from(StockBasic)) or 0)
if stocks_now == 0 or force:
_sync_state["step"] = "正在同步股票列表"
try:
rows = await asyncio.to_thread(_sync_stock_list_sync, pro)
async with async_session() as session:
await session.execute(delete(StockBasic))
for i in range(0, len(rows), _BATCH):
await session.execute(insert(StockBasic), rows[i : i + _BATCH])
await session.commit()
except Exception as e: # noqa: BLE001 —— 受限时沿用现有列表继续
if stocks_now > 0:
_sync_state["step"] = f"股票列表同步受限(沿用现有 {stocks_now} 只)"
else:
raise
# 2) 逐交易日日线(增量;当日未生成则跳过)
async with async_session() as session:
dates = await _recent_trade_dates(session, pro, days)
have_daily = set() if force else await _existing_dates(session, MarketDaily)
todo = [d for d in dates if d not in have_daily]
_sync_state["total_days"] = len(todo)
_sync_state["done_days"] = 0
for d in todo:
_sync_state["step"] = f"正在同步 {d} 日线({_sync_state['done_days'] + 1}/{len(todo)}"
daily_rows = await asyncio.to_thread(_fetch_daily, pro, d)
if daily_rows: # 盘前/盘中等未生成数据的日期直接跳过
async with async_session() as session:
await _replace_day(session, MarketDaily, daily_rows, d)
_sync_state["done_days"] += 1
# 3) 最新「有数据」交易日的快照daily_basic仅 1 次调用)
# 用 market_daily 实际最大交易日(今天的数据收盘后才生成,日历最新日会拉到空)
async with async_session() as session:
latest_dt = await session.scalar(select(func.max(MarketDaily.trade_date)))
latest = latest_dt.strftime("%Y%m%d") if latest_dt else None
if latest:
async with async_session() as session:
have_snap = force or latest not in await _existing_dates(session, DailySnapshot)
if have_snap:
_sync_state["step"] = f"正在同步 {latest} 每日指标"
basic_rows = await asyncio.to_thread(_fetch_basic, pro, latest)
if basic_rows:
async with async_session() as session:
await _replace_day(session, DailySnapshot, basic_rows, latest)
_sync_state["step"] = "同步完成"
except Exception as e: # noqa: BLE001
_sync_state["error"] = f"同步失败:{str(e)[:300]}"
_sync_state["step"] = "同步失败"
finally:
_sync_state["running"] = False
_sync_state["finished_at"] = datetime.now()
async def start_sync(session: AsyncSession, days: int, force: bool) -> dict:
"""幂等启动后台同步任务;已在跑则直接返回当前状态。"""
global _sync_task
async with _sync_lock:
if _sync_state["running"] and _sync_task and not _sync_task.done():
return dict(_sync_state)
_sync_state.update({
"running": True, "step": "准备同步", "total_days": days, "done_days": 0,
"error": None, "started_at": datetime.now(), "finished_at": None,
})
_sync_task = asyncio.create_task(_run_sync(days, force))
return dict(_sync_state)
async def get_sync_status(session: AsyncSession) -> dict:
"""合并任务状态 + DB 实况(最新交易日/行数/ready 标志),与 ScreenerSyncStatus DTO 对齐。"""
stocks = int(await session.scalar(select(func.count()).select_from(StockBasic)) or 0)
daily_rows = int(await session.scalar(select(func.count()).select_from(MarketDaily)) or 0)
snap_rows = int(await session.scalar(select(func.count()).select_from(DailySnapshot)) or 0)
last_daily = await session.scalar(select(func.max(MarketDaily.trade_date)))
n_dates = int(await session.scalar(select(func.count(func.distinct(MarketDaily.trade_date)))) or 0)
status = dict(_sync_state)
status.update({
"stats": {"stocks": stocks, "daily_rows": daily_rows, "snapshot_rows": snap_rows, "dates": n_dates},
"last_trade_date": last_daily,
"last_synced_at": _sync_state.get("finished_at") or _sync_state.get("started_at"),
"ready": daily_rows > 0,
})
return status

View File

@@ -14,6 +14,7 @@ dependencies = [
"numpy>=1.26", "numpy>=1.26",
"pandas>=2.2", "pandas>=2.2",
"tushare>=1.4", "tushare>=1.4",
"httpx>=0.28.1",
] ]
[tool.uv] [tool.uv]

View File

@@ -52,3 +52,49 @@ with TestClient(app) as c:
assert len(wd["candles"]) < len(d["candles"]) assert len(wd["candles"]) < len(d["candles"])
print("\n✅ 后端全链路自检通过(含周期聚合)") print("\n✅ 后端全链路自检通过(含周期聚合)")
# ---------- 智能选股 ----------
print("\n== 智能选股 ==")
# 1) JSON 提取容错(不联网):代码围栏 / 多余文本
from app.screener.llm import _extract_json
assert _extract_json('```json\n{"a": 1}\n```') == {"a": 1}
assert _extract_json('好的,结果如下:{"indicator": [], "snapshot": []} 谢谢')["indicator"] == []
print("_extract_json 围栏/噪音容错 ✅")
# 2) 未配置 LLM_API_KEY 时 /run 返回 503确定性不联网
from app.config import settings as _s
r = c.post("/api/screener/run", json={"text": "这两天 KDJ 的 J 小于 10"})
if not _s.llm_api_key:
assert r.status_code == 503, f"无 key 应 503实际 {r.status_code}"
print("无 LLM_API_KEY -> 503 ✅")
else:
print("已配置 LLM_API_KEY跳过 503 用例")
# 3) 直传条件选股(不依赖 LLM依赖已同步的全市场数据
from app.models import MarketDaily # noqa: F401
from sqlalchemy import select, func
from app.db import async_session
import asyncio
async def _has_data() -> bool:
async with async_session() as session:
return (await session.scalar(select(func.count()).select_from(MarketDaily))) or 0 > 0
if asyncio.run(_has_data()):
r = c.post("/api/screener/run", json={
"text": "测试直传",
"conditions": {
"indicator": [{"indicator": "kdj_j", "params": {"n": 9, "m1": 3, "m2": 3},
"op": "lt", "value": 0, "lookback": 1, "match": "all"}],
"snapshot": [],
},
})
assert r.status_code == 200, f"直传选股失败 {r.status_code}: {r.text}"
d = r.json()
assert d["total"] >= 0
print(f"KDJ J<0 选股 ✅ 命中 {d['total']} 只,基准日 {(d['trade_date'] or '')[:10]}")
else:
print("(未同步全市场数据,跳过直传选股用例;运行 POST /api/screener/sync 后再试)")
print("\n✅ 智能选股自检通过")

30
backend/uv.lock generated
View File

@@ -286,6 +286,19 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" },
] ]
[[package]]
name = "httpcore"
version = "1.0.9"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "certifi" },
{ name = "h11" },
]
sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" },
]
[[package]] [[package]]
name = "httptools" name = "httptools"
version = "0.8.0" version = "0.8.0"
@@ -322,6 +335,21 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/48/63/b906c01e53f50d432c0defe43ce52764a111dc1bdd028bafbeb54dcfd008/httptools-0.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:384c17174464c8e873398b7af24f0b1f44d992c820328413951a625323155d77", size = 108209, upload-time = "2026-05-25T22:17:39.473Z" }, { url = "https://files.pythonhosted.org/packages/48/63/b906c01e53f50d432c0defe43ce52764a111dc1bdd028bafbeb54dcfd008/httptools-0.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:384c17174464c8e873398b7af24f0b1f44d992c820328413951a625323155d77", size = 108209, upload-time = "2026-05-25T22:17:39.473Z" },
] ]
[[package]]
name = "httpx"
version = "0.28.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "anyio" },
{ name = "certifi" },
{ name = "httpcore" },
{ name = "idna" },
]
sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" },
]
[[package]] [[package]]
name = "idna" name = "idna"
version = "3.18" version = "3.18"
@@ -827,6 +855,7 @@ dependencies = [
{ name = "aiosqlite" }, { name = "aiosqlite" },
{ name = "asyncpg" }, { name = "asyncpg" },
{ name = "fastapi" }, { name = "fastapi" },
{ name = "httpx" },
{ name = "numpy" }, { name = "numpy" },
{ name = "pandas" }, { name = "pandas" },
{ name = "pydantic" }, { name = "pydantic" },
@@ -841,6 +870,7 @@ requires-dist = [
{ name = "aiosqlite", specifier = ">=0.20" }, { name = "aiosqlite", specifier = ">=0.20" },
{ name = "asyncpg", specifier = ">=0.29" }, { name = "asyncpg", specifier = ">=0.29" },
{ name = "fastapi", specifier = ">=0.115" }, { name = "fastapi", specifier = ">=0.115" },
{ name = "httpx", specifier = ">=0.28.1" },
{ name = "numpy", specifier = ">=1.26" }, { name = "numpy", specifier = ">=1.26" },
{ name = "pandas", specifier = ">=2.2" }, { name = "pandas", specifier = ">=2.2" },
{ name = "pydantic", specifier = ">=2.7" }, { name = "pydantic", specifier = ">=2.7" },

View File

@@ -16,7 +16,8 @@
"pinia": "^2.3.0", "pinia": "^2.3.0",
"primeicons": "^7.0.0", "primeicons": "^7.0.0",
"primevue": "^5.0.0", "primevue": "^5.0.0",
"vue": "^3.5.0" "vue": "^3.5.0",
"vue-router": "^4.6.4"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^22.0.0", "@types/node": "^22.0.0",

View File

@@ -29,6 +29,9 @@ importers:
vue: vue:
specifier: ^3.5.0 specifier: ^3.5.0
version: 3.5.41(typescript@5.6.2) version: 3.5.41(typescript@5.6.2)
vue-router:
specifier: ^4.6.4
version: 4.6.4(vue@3.5.41(typescript@5.6.2))
devDependencies: devDependencies:
'@types/node': '@types/node':
specifier: ^22.0.0 specifier: ^22.0.0
@@ -457,6 +460,9 @@ packages:
'@vue/devtools-api@6.6.3': '@vue/devtools-api@6.6.3':
resolution: {integrity: sha512-0MiMsFma/HqA6g3KLKn+AGpL1kgKhFWszC9U29NfpWK5LE7bjeXxySWJrOJ77hBz+TBrBQ7o4QJqbPbqbs8rJw==} resolution: {integrity: sha512-0MiMsFma/HqA6g3KLKn+AGpL1kgKhFWszC9U29NfpWK5LE7bjeXxySWJrOJ77hBz+TBrBQ7o4QJqbPbqbs8rJw==}
'@vue/devtools-api@6.6.4':
resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==}
'@vue/language-core@2.1.0': '@vue/language-core@2.1.0':
resolution: {integrity: sha512-S7uUdQXn4aA7QloQeAIhKDkP243nCEktQu1Kxur1fJFfedf+izBTb9bAR2tHe0V7xhFYhIwkNCtQl2AlKFaW3w==} resolution: {integrity: sha512-S7uUdQXn4aA7QloQeAIhKDkP243nCEktQu1Kxur1fJFfedf+izBTb9bAR2tHe0V7xhFYhIwkNCtQl2AlKFaW3w==}
peerDependencies: peerDependencies:
@@ -662,6 +668,11 @@ packages:
'@vue/composition-api': '@vue/composition-api':
optional: true optional: true
vue-router@4.6.4:
resolution: {integrity: sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg==}
peerDependencies:
vue: ^3.5.0
vue-tsc@2.1.0: vue-tsc@2.1.0:
resolution: {integrity: sha512-N6kelLgHPLiDzkeWfsfrfR79POitZ59M/2HaRWk4BW0ED40FlyQaVhGUvjg8VRPPJWic1M974cRTGIBR6zfgwQ==} resolution: {integrity: sha512-N6kelLgHPLiDzkeWfsfrfR79POitZ59M/2HaRWk4BW0ED40FlyQaVhGUvjg8VRPPJWic1M974cRTGIBR6zfgwQ==}
hasBin: true hasBin: true
@@ -967,6 +978,8 @@ snapshots:
'@vue/devtools-api@6.6.3': {} '@vue/devtools-api@6.6.3': {}
'@vue/devtools-api@6.6.4': {}
'@vue/language-core@2.1.0(typescript@5.6.2)': '@vue/language-core@2.1.0(typescript@5.6.2)':
dependencies: dependencies:
'@volar/language-core': 2.4.28 '@volar/language-core': 2.4.28
@@ -1182,6 +1195,11 @@ snapshots:
dependencies: dependencies:
vue: 3.5.41(typescript@5.6.2) vue: 3.5.41(typescript@5.6.2)
vue-router@4.6.4(vue@3.5.41(typescript@5.6.2)):
dependencies:
'@vue/devtools-api': 6.6.4
vue: 3.5.41(typescript@5.6.2)
vue-tsc@2.1.0(typescript@5.6.2): vue-tsc@2.1.0(typescript@5.6.2):
dependencies: dependencies:
'@volar/typescript': 2.4.28 '@volar/typescript': 2.4.28

View File

@@ -1,13 +1,18 @@
<script setup lang="ts"> <script setup lang="ts">
import BacktestView from '@/views/BacktestView.vue'; import { RouterLink, RouterView } from 'vue-router';
</script> </script>
<template> <template>
<div class="app-shell"> <div class="app-shell">
<header class="app-header"> <header class="app-header">
<h1>股市回测平台</h1> <h1>股市量化平台</h1>
<span class="sub">历史回测 · 回放式模拟 · A股红涨绿跌</span> <span class="sub">智能选股 · 历史回测 · A股红涨绿跌</span>
<nav class="app-nav">
<RouterLink to="/" class="nav-link">首页</RouterLink>
<RouterLink to="/screener" class="nav-link">智能选股</RouterLink>
<RouterLink to="/backtest" class="nav-link">策略回测</RouterLink>
</nav>
</header> </header>
<BacktestView /> <RouterView />
</div> </div>
</template> </template>

View File

@@ -1,4 +1,13 @@
import type { BacktestRequest, BacktestResponse, SyncRequest, SyncResponse } from './types'; import type {
BacktestRequest,
BacktestResponse,
ScreenerRunRequest,
ScreenerRunResponse,
ScreenerSyncRequest,
ScreenerSyncStatus,
SyncRequest,
SyncResponse,
} from './types';
// dev 用 Vite 代理(/api -> :8000生产构建设 VITE_API_BASE 指向后端地址。 // dev 用 Vite 代理(/api -> :8000生产构建设 VITE_API_BASE 指向后端地址。
const BASE = import.meta.env.VITE_API_BASE ?? ''; const BASE = import.meta.env.VITE_API_BASE ?? '';
@@ -27,3 +36,47 @@ export async function syncData(req: SyncRequest): Promise<SyncResponse> {
return (await res.json()) as SyncResponse; return (await res.json()) as SyncResponse;
} }
// ---------- 智能选股 ----------
export async function runScreener(req: ScreenerRunRequest): Promise<ScreenerRunResponse> {
const res = await fetch(`${BASE}/api/screener/run`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(req),
});
if (!res.ok) {
// 后端 detail 字段携带可读中文原因503 未配 key / 409 未同步 / 502 LLM 错)
let detail = '';
try {
detail = (await res.json())?.detail ?? '';
} catch {
detail = await res.text();
}
throw new Error(detail || `选股失败 (HTTP ${res.status})`);
}
return (await res.json()) as ScreenerRunResponse;
}
export async function startScreenerSync(req: ScreenerSyncRequest = {}): Promise<ScreenerSyncStatus> {
const res = await fetch(`${BASE}/api/screener/sync`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(req),
});
if (!res.ok) {
let detail = '';
try {
detail = (await res.json())?.detail ?? '';
} catch {
detail = await res.text();
}
throw new Error(detail || `启动同步失败 (HTTP ${res.status})`);
}
return (await res.json()) as ScreenerSyncStatus;
}
export async function getScreenerSyncStatus(): Promise<ScreenerSyncStatus> {
const res = await fetch(`${BASE}/api/screener/sync/status`);
if (!res.ok) throw new Error(`获取同步状态失败 (HTTP ${res.status})`);
return (await res.json()) as ScreenerSyncStatus;
}

View File

@@ -74,3 +74,76 @@ export interface SyncResponse {
bars: number; bars: number;
source: string; source: string;
} }
// ---------- 智能选股(镜像 app/schemas.py ----------
export type Op = 'gt' | 'ge' | 'lt' | 'le' | 'between';
export interface IndicatorCondition {
indicator: string;
params?: Record<string, number>;
op: Op;
value: number;
value2?: number | null;
value_indicator?: string | null;
value_params?: Record<string, number>;
lookback?: number;
match?: 'all' | 'any';
}
export interface SnapshotCondition {
field: string; // total_mv | circ_mv | pe_ttm | pb | turnover_rate | close
op: Op;
value: number;
value2?: number | null;
}
export interface ScreenConditions {
indicator: IndicatorCondition[];
snapshot: SnapshotCondition[];
exclude_st: boolean;
exclude_delisted: boolean;
exclude_bj: boolean;
}
export interface ScreenerRunRequest {
text: string;
conditions?: ScreenConditions | null; // 直传则跳过 LLM 解析
}
export interface ScreenerItemOut {
ts_code: string;
name: string;
close: number | null;
pct_chg: number | null;
total_mv: number | null; // 亿元
circ_mv: number | null;
pe_ttm: number | null;
pb: number | null;
turnover_rate: number | null;
indicators: Record<string, number | null>;
}
export interface ScreenerRunResponse {
conditions: ScreenConditions;
trade_date: string | null;
total: number;
items: ScreenerItemOut[];
indicator_labels: Record<string, string>;
}
export interface ScreenerSyncRequest {
days?: number;
force?: boolean;
}
export interface ScreenerSyncStatus {
running: boolean;
step?: string | null;
total_days: number;
done_days: number;
error?: string | null;
ready: boolean;
last_trade_date?: string | null;
last_synced_at?: string | null;
stats: { stocks: number; daily_rows: number; snapshot_rows: number; dates: number };
}

View File

@@ -0,0 +1,51 @@
<script setup lang="ts">
import type { ScreenConditions } from '@/api/types';
defineProps<{ conditions: ScreenConditions }>();
const OP_TEXT: Record<string, string> = { gt: '>', ge: '≥', lt: '<', le: '≤', between: '区间' };
const FIELD_TEXT: Record<string, string> = {
total_mv: '总市值', circ_mv: '流通市值', pe_ttm: '市盈率TTM',
pb: '市净率', turnover_rate: '换手率', close: '最新价',
};
function paramsStr(p?: Record<string, number>) {
if (!p || Object.keys(p).length === 0) return '';
const vals = Object.values(p).map((v) => (Number.isInteger(v) ? String(v) : String(v)));
return `(${vals.join(',')})`;
}
function lookbackText(c: { lookback?: number; match?: string }) {
const n = c.lookback ?? 1;
if (n <= 1) return '今日';
return c.match === 'any' ? `${n}日任一天` : `${n}日连续`;
}
</script>
<template>
<div class="cond-chips">
<span class="clabel">解析条件</span>
<span v-for="(c, i) in conditions.indicator" :key="'i' + i" class="cond-chip ci">
<i class="pi pi-bolt" style="font-size: 10px;"></i>
{{ c.indicator }}{{ paramsStr(c.params) }}
<span class="arrow">{{ OP_TEXT[c.op] }}</span>
<template v-if="c.value_indicator">{{ c.value_indicator }}{{ paramsStr(c.value_params) }}</template>
<template v-else-if="c.op === 'between' && c.value2">{{ c.value }} ~ {{ c.value2 }}</template>
<template v-else>{{ c.value }}</template>
<span style="color: var(--ink-3);"> · {{ lookbackText(c) }}</span>
</span>
<span v-for="(c, i) in conditions.snapshot" :key="'s' + i" class="cond-chip cs">
<i class="pi pi-table" style="font-size: 10px;"></i>
{{ FIELD_TEXT[c.field] ?? c.field }}
<span class="arrow">{{ OP_TEXT[c.op] }}</span>
<template v-if="c.op === 'between' && c.value2">{{ c.value }} ~ {{ c.value2 }}</template>
<template v-else>{{ c.value }}</template>
</span>
<span class="cond-chip cx">
排除ST · 退市<span v-if="conditions.exclude_bj"> · 北交所</span>
</span>
</div>
</template>

View File

@@ -0,0 +1,50 @@
<script setup lang="ts">
import { ref } from 'vue';
import Button from 'primevue/button';
import Textarea from 'primevue/textarea';
defineProps<{ loading: boolean }>();
const emit = defineEmits<{ (e: 'run', text: string): void }>();
const text = ref('');
// 示例(点击填入文本框)
const examples = [
'帮我找出这两天 KDJ 中的 J 小于 10市值大于 100 亿,小于 200 亿的公司',
'RSI 低于 30市盈率 TTM 小于 20 的公司',
'近 5 天曾经 MACD 金叉DIF 上穿 DEA换手率大于 5%,流通市值小于 100 亿',
'股价在布林带下轨之下,流通市值小于 50 亿',
];
function run() {
if (text.value.trim()) emit('run', text.value.trim());
}
</script>
<template>
<div class="toolbar" style="flex-direction: column; align-items: stretch; gap: 10px;">
<div class="field" style="gap: 7px;">
<label>用一句话描述你的选股条件</label>
<Textarea
v-model="text"
class="screener-input"
:auto-resize="true"
rows="2"
size="small"
placeholder="例如:这两天 KDJ 的 J 小于 10市值 100~200 亿的公司"
@keyup.ctrl.enter="run"
/>
</div>
<div class="quick">
<span class="qlabel">示例</span>
<span v-for="(ex, i) in examples" :key="i" class="qchip" @click="text = ex">{{ ex }}</span>
</div>
<div class="hint">
支持 KDJ / RSI / MACD / 布林 / 均线指标条件市值 / 市盈率 / 换手率等快照条件以及连续 N N 天任一天等时间窗口
<code>Ctrl+Enter</code> 快速筛选
</div>
<div>
<Button label="开始筛选" icon="pi pi-search" size="small" :loading="loading" :disabled="!text.trim()" @click="run" />
</div>
</div>
</template>

View File

@@ -0,0 +1,75 @@
<script setup lang="ts">
import { computed } from 'vue';
import DataTable from 'primevue/datatable';
import Column from 'primevue/column';
import type { ScreenerItemOut, ScreenerRunResponse } from '@/api/types';
const props = defineProps<{ result: ScreenerRunResponse }>();
const items = computed(() => props.result.items);
// 动态指标列:[{ key: 'kdj_j', label: 'KDJ J(9,3,3)' }]
const indCols = computed(() =>
Object.entries(props.result.indicator_labels).map(([key, label]) => ({ key, label })),
);
function pctClass(v: number | null) {
if (v == null) return '';
return v > 0 ? 'pos' : v < 0 ? 'neg' : '';
}
function fmt(v: number | null, digits = 2) {
return v == null ? '—' : v.toFixed(digits);
}
</script>
<template>
<div class="panel">
<div class="panel-title">
命中 {{ result.total }} {{ result.total > items.length ? `(仅显示前 ${items.length}` : '' }}
<span v-if="result.trade_date" style="color: var(--ink-3); margin-left: 8px;">· 数据基准 {{ result.trade_date.slice(0, 10) }}</span>
</div>
<DataTable :value="items" size="small" striped-rows :scrollable="true" scroll-height="560px" sort-mode="single">
<Column field="ts_code" header="代码" sortable style="min-width: 92px;">
<template #body="{ data }">
<span style="font-variant-numeric: tabular-nums; color: var(--ink);">{{ data.ts_code }}</span>
</template>
</Column>
<Column field="name" header="名称" sortable style="min-width: 96px;" />
<Column field="close" header="最新价" sortable style="min-width: 84px;">
<template #body="{ data }">{{ fmt(data.close) }}</template>
</Column>
<Column field="pct_chg" header="涨跌幅%" sortable style="min-width: 88px;">
<template #body="{ data }">
<span :class="pctClass(data.pct_chg)">{{ data.pct_chg == null ? '—' : (data.pct_chg > 0 ? '+' : '') + data.pct_chg.toFixed(2) }}</span>
</template>
</Column>
<Column field="total_mv" header="总市值(亿)" sortable style="min-width: 100px;">
<template #body="{ data }">{{ fmt(data.total_mv) }}</template>
</Column>
<Column field="circ_mv" header="流通市值(亿)" sortable style="min-width: 106px;">
<template #body="{ data }">{{ fmt(data.circ_mv) }}</template>
</Column>
<Column field="pe_ttm" header="PE-TTM" sortable style="min-width: 84px;">
<template #body="{ data }">{{ fmt(data.pe_ttm) }}</template>
</Column>
<Column field="pb" header="PB" sortable style="min-width: 70px;">
<template #body="{ data }">{{ fmt(data.pb) }}</template>
</Column>
<Column field="turnover_rate" header="换手率%" sortable style="min-width: 88px;">
<template #body="{ data }">{{ fmt(data.turnover_rate) }}</template>
</Column>
<Column
v-for="col in indCols"
:key="col.key"
:field="`indicators.${col.key}`"
:header="col.label"
sortable
style="min-width: 110px;"
>
<template #body="{ data }">
<span style="font-variant-numeric: tabular-nums;">{{ data.indicators?.[col.key] == null ? '—' : data.indicators[col.key].toFixed(2) }}</span>
</template>
</Column>
</DataTable>
</div>
</template>

View File

@@ -0,0 +1,56 @@
<script setup lang="ts">
import { computed } from 'vue';
import Button from 'primevue/button';
import type { ScreenerSyncStatus } from '@/api/types';
const props = defineProps<{ status: ScreenerSyncStatus | null }>();
const emit = defineEmits<{ (e: 'sync'): void }>();
function fmtDate(s?: string | null) {
if (!s) return '—';
return s.slice(0, 10);
}
// 数据是否可筛(有日线)。快照缺失则纯指标仍可用,给黄色提示。
const freshness = computed(() => {
const s = props.status;
if (!s) return { tone: 'none', text: '正在检查数据状态…' };
if (!s.ready) return { tone: 'warn', text: '全市场数据尚未同步,请先点击右侧「同步数据」' };
const snapMissing = s.stats.snapshot_rows === 0;
const d = s.stats.dates;
const base = `数据截至 ${fmtDate(s.last_trade_date)} · 共 ${d} 个交易日 · ${s.stats.stocks} 只股票`;
if (snapMissing) return { tone: 'warn', text: `${base}(每日指标暂不可用:市值/市盈率条件不可用,纯指标条件不受影响)` };
return { tone: 'ok', text: base };
});
</script>
<template>
<div class="sync-bar">
<span :class="freshness.tone === 'ok' ? 'ok' : freshness.tone === 'warn' ? 'warn' : ''">
<i class="pi" :class="freshness.tone === 'ok' ? 'pi-check-circle' : 'pi-info-circle'" style="margin-right: 5px;"></i>
{{ freshness.text }}
</span>
<template v-if="status && status.running">
<span class="sync-progress">
<i class="pi pi-spin pi-spinner"></i>
<span class="txt">{{ status.step || '同步中…' }}{{ status.done_days }}/{{ status.total_days }}</span>
</span>
</template>
<template v-else>
<span class="spacer"></span>
<Button
label="同步市场数据"
icon="pi pi-refresh"
size="small"
severity="secondary"
:disabled="status?.running"
@click="emit('sync')"
/>
</template>
<div v-if="status && status.error" class="warn" style="flex-basis: 100%; margin-top: 4px;">
<i class="pi pi-exclamation-triangle" style="margin-right: 5px;"></i>{{ status.error }}
</div>
</div>
</template>

View File

@@ -7,9 +7,11 @@ import 'primeicons/primeicons.css';
import './style.css'; import './style.css';
import App from './App.vue'; import App from './App.vue';
import router from './router';
const app = createApp(App); const app = createApp(App);
app.use(createPinia()); app.use(createPinia());
app.use(router);
app.use(PrimeVue, { app.use(PrimeVue, {
theme: { theme: {
preset: Aura, preset: Aura,

14
frontend/src/router.ts Normal file
View File

@@ -0,0 +1,14 @@
import { createRouter, createWebHistory } from 'vue-router';
import HomeView from '@/views/HomeView.vue';
const router = createRouter({
history: createWebHistory(),
routes: [
{ path: '/', name: 'home', component: HomeView },
{ path: '/screener', name: 'screener', component: () => import('@/views/ScreenerView.vue') },
{ path: '/backtest', name: 'backtest', component: () => import('@/views/BacktestView.vue') },
{ path: '/:pathMatch(.*)*', redirect: '/' },
],
});
export default router;

View File

@@ -0,0 +1,70 @@
import { defineStore } from 'pinia';
import { ref } from 'vue';
import { getScreenerSyncStatus, runScreener, startScreenerSync } from '@/api/client';
import type { ScreenerRunResponse, ScreenerSyncStatus } from '@/api/types';
export const useScreenerStore = defineStore('screener', () => {
const loading = ref(false);
const stage = ref<'idle' | 'parsing' | 'screening'>('idle');
const note = ref<string | null>(null);
const error = ref<string | null>(null);
const result = ref<ScreenerRunResponse | null>(null);
const syncStatus = ref<ScreenerSyncStatus | null>(null);
let pollTimer: ReturnType<typeof setInterval> | null = null;
async function run(text: string) {
loading.value = true;
error.value = null;
result.value = null;
note.value = 'AI 解析条件中…';
stage.value = 'parsing';
try {
// 条件解析与全市场筛选在后端一气呵成;切到筛选阶段给个过渡提示
setTimeout(() => {
if (loading.value && stage.value === 'parsing') {
stage.value = 'screening';
note.value = '全市场筛选中…';
}
}, 1200);
result.value = await runScreener({ text });
} catch (e) {
error.value = e instanceof Error ? e.message : '选股失败';
} finally {
loading.value = false;
stage.value = 'idle';
note.value = null;
}
}
async function fetchStatus() {
try {
syncStatus.value = await getScreenerSyncStatus();
} catch {
/* 静默:状态拉取失败不阻塞页面 */
}
}
function stopPolling() {
if (pollTimer) {
clearInterval(pollTimer);
pollTimer = null;
}
}
async function startSync(days = 90) {
error.value = null;
try {
await startScreenerSync({ days });
await fetchStatus();
stopPolling();
pollTimer = setInterval(async () => {
await fetchStatus();
if (syncStatus.value && !syncStatus.value.running) stopPolling();
}, 2000);
} catch (e) {
error.value = e instanceof Error ? e.message : '启动同步失败';
}
}
return { loading, stage, note, error, result, syncStatus, run, fetchStatus, startSync, stopPolling };
});

View File

@@ -121,3 +121,66 @@ body {
padding: 10px 14px; font-size: 13px; margin-top: 14px; padding: 10px 14px; font-size: 13px; margin-top: 14px;
} }
.placeholder { color: var(--ink-3); font-size: 13px; padding: 56px 0; text-align: center; } .placeholder { color: var(--ink-3); font-size: 13px; padding: 56px 0; text-align: center; }
/* ---------- 顶部导航 ---------- */
.app-nav { display: flex; gap: 4px; margin-left: 18px; }
.nav-link {
font-size: 13px; color: var(--ink-2); text-decoration: none;
padding: 5px 12px; border-radius: 8px; transition: 0.15s;
}
.nav-link:hover { color: var(--ink); background: var(--surface-2); }
.nav-link.router-link-active { color: #fff; background: var(--dif); }
/* ---------- 首页功能入口卡片 ---------- */
.home-grid {
display: grid; grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
gap: 16px; margin-top: 22px;
}
.feature-card {
display: block; text-decoration: none; color: var(--ink);
background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
padding: 26px 24px 20px; transition: 0.18s; position: relative;
}
.feature-card:hover { border-color: var(--border-2); transform: translateY(-2px); box-shadow: 0 14px 34px rgba(0,0,0,0.35); }
.fc-icon {
width: 44px; height: 44px; border-radius: 12px; display: flex; align-items: center; justify-content: center;
background: rgba(91, 143, 249, 0.12); color: var(--dif); font-size: 20px; margin-bottom: 14px;
}
.fc-title { font-size: 18px; font-weight: 600; margin-bottom: 8px; }
.fc-desc { font-size: 13px; color: var(--ink-2); line-height: 1.75; min-height: 66px; }
.fc-tags { display: flex; flex-wrap: wrap; gap: 6px; margin: 14px 0 16px; }
.fc-tag {
font-size: 11.5px; color: var(--ink-3); background: var(--surface-2);
border: 1px solid var(--border); border-radius: 999px; padding: 2px 9px;
}
.fc-enter { font-size: 13px; color: var(--dif); display: flex; align-items: center; gap: 6px; }
/* ---------- 智能选股 ---------- */
.screener-input { width: 100%; }
.cond-chips { display: flex; flex-wrap: wrap; gap: 7px; align-items: center; }
.cond-chips .clabel { font-size: 12px; color: var(--ink-3); margin-right: 3px; }
.cond-chip {
display: inline-flex; align-items: center; gap: 6px;
font-size: 12.5px; color: var(--ink); background: var(--surface-2);
border: 1px solid var(--border-2); border-radius: 999px; padding: 4px 11px;
font-variant-numeric: tabular-nums;
}
.cond-chip.ci { border-color: rgba(91, 143, 249, 0.45); color: #cdddfb; }
.cond-chip.cs { border-color: rgba(246, 189, 22, 0.4); color: #f2e3b3; }
.cond-chip.cx { color: var(--ink-3); }
.cond-chip .arrow { color: var(--ink-3); }
/* 数据状态条 */
.sync-bar {
display: flex; flex-wrap: wrap; align-items: center; gap: 12px;
background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
padding: 9px 14px; margin-top: 14px; font-size: 12.5px; color: var(--ink-2);
}
.sync-bar .ok { color: var(--down); }
.sync-bar .warn { color: var(--dea); }
.sync-bar .sync-progress { flex: 1 1 200px; min-width: 160px; display: flex; align-items: center; gap: 8px; }
.sync-bar .sync-progress .txt { white-space: nowrap; }
/* 结果表数字着色 */
.pos { color: var(--up); }
.neg { color: var(--down); }

View File

@@ -0,0 +1,35 @@
<script setup lang="ts">
import { RouterLink } from 'vue-router';
// 首页:两大功能入口(智能选股 / 策略回测)
const features = [
{
to: '/screener',
icon: 'pi pi-sparkles',
title: '智能选股',
desc: '用一句自然语言描述条件(如「这两天 KDJ 的 J 小于 10市值 100~200 亿」AI 解析后全市场筛选。',
tags: ['KDJ / RSI / MACD / 布林', '市值 · 市盈率 · 换手率', 'AI 条件解析'],
},
{
to: '/backtest',
icon: 'pi pi-chart-line',
title: '策略回测',
desc: '选标的、调参数跑历史回测K 线 + 指标 + 买卖点 + 净值曲线与绩效指标A股真实成本建模。',
tags: ['MACD / 双均线 / 单均线', 'T+1 · 印花税 · 佣金', '净值与绩效'],
},
];
</script>
<template>
<div class="home-grid">
<RouterLink v-for="f in features" :key="f.to" :to="f.to" class="feature-card">
<div class="fc-icon"><i :class="f.icon"></i></div>
<div class="fc-title">{{ f.title }}</div>
<div class="fc-desc">{{ f.desc }}</div>
<div class="fc-tags">
<span v-for="t in f.tags" :key="t" class="fc-tag">{{ t }}</span>
</div>
<div class="fc-enter">进入 <i class="pi pi-arrow-right"></i></div>
</RouterLink>
</div>
</template>

View File

@@ -0,0 +1,44 @@
<script setup lang="ts">
import { onBeforeUnmount, onMounted } from 'vue';
import { useScreenerStore } from '@/stores/screener';
import ScreenerForm from '@/components/ScreenerForm.vue';
import ConditionChips from '@/components/ConditionChips.vue';
import SyncStatusBar from '@/components/SyncStatusBar.vue';
import ScreenerTable from '@/components/ScreenerTable.vue';
const store = useScreenerStore();
onMounted(() => {
// 仅查状态;同步由用户点击「同步市场数据」显式触发(避免反复触发接口限频)
store.fetchStatus();
});
onBeforeUnmount(() => store.stopPolling());
</script>
<template>
<div>
<ScreenerForm :loading="store.loading" @run="store.run" />
<SyncStatusBar :status="store.syncStatus" @sync="store.startSync(90)" />
<div v-if="store.error" class="error-banner">
<i class="pi pi-exclamation-triangle" style="margin-right: 6px;"></i>{{ store.error }}
</div>
<div v-if="store.loading && store.note" class="placeholder">
<i class="pi pi-spin pi-spinner" style="margin-right: 8px;"></i>{{ store.note }}
</div>
<template v-else-if="store.result">
<div class="panel" style="padding: 12px 14px; margin-top: 14px;">
<ConditionChips :conditions="store.result.conditions" />
</div>
<ScreenerTable :result="store.result" />
</template>
<div v-else-if="!store.error" class="placeholder">
输入选股条件开始筛选即可全市场选股
</div>
</div>
</template>

11
skills-lock.json Normal file
View File

@@ -0,0 +1,11 @@
{
"version": 1,
"skills": {
"tushare": {
"source": "waditu-tushare/skills",
"sourceType": "github",
"skillPath": "tushare/SKILL.md",
"computedHash": "6e8c82c438a9494bb932c695769ce9ff1dede5d69cd90753f0f182b0dc0f3a7a"
}
}
}