一句话结论:如果股票池已经由策略规则确定,通常应该先确定股票池,再获取这些标的所需的最新行情;如果股票池本身依赖最新价格、成交量或盘口数据生成,则必须先获取行情,再完成股票池筛选。
摘要
量化策略临时需要一批股票的最新价格时,"先查行情还是先建股票池"并不是一个单纯的 API 调用顺序问题,而是策略定义和数据获取边界的问题。
如果股票池来自行业、指数、固定名单或已有筛选结果,那么先确定 Universe(股票池),再获取行情,可以让数据请求与策略真正需要的数据保持一致。反过来,如果策略要求根据当前价格、涨跌幅、成交量等实时数据动态筛选股票,那么行情本身就是股票池生成的输入,此时必须先拿行情。
工程上最值得避免的,是把"全市场行情获取"和"股票池定义"混成一个步骤。
1. 先把"股票池"和"最新价格"分成两个问题
量化系统里经常会出现这样的代码逻辑:
text
我要找一批股票
↓
拿这些股票最新价格
↓
计算指标
↓
生成交易信号
问题在于,第一步的"找一批股票"可能有完全不同的含义。
例如:
- 股票池已经由策略配置给出;
- 股票池来自某个指数成分;
- 股票池来自行业分类;
- 股票池来自上一阶段的选股结果;
- 股票池根据当前行情临时计算;
- 股票池要求满足"最新价格低于某阈值";
- 股票池要求按照实时成交量排名。
前四种情况下,股票池已经是已知输入。
后几种情况下,股票池则是由行情计算出来的结果。
因此,不能简单地规定所有策略都必须"先建池"或者"先查行情"。
真正应该问的是:
股票池的生成是否依赖当前行情?
这是决定数据获取顺序的关键。
2. 如果股票池已经确定:先建池,再查行情
假设策略每天开盘前已经确定:
python
universe = [
"600519.SH",
"000001.SZ",
"300750.SZ",
"601318.SH",
]
此时策略需要这些股票的最新价格。
数据链路应该是:
text
策略配置
↓
确定股票池
↓
请求行情
↓
价格校验
↓
指标计算
↓
交易信号
这比直接查询整个市场更容易控制。
原因很简单:
策略真正需要的是一个集合,而不是"所有能获取的数据"。
如果策略最终只需要几十只股票,却先获取整个市场的行情,再从里面筛选几十只,那么数据层和策略层之间就出现了额外的数据传输与处理。
这种做法不一定错误。
但它应该是有意识的工程选择,而不是默认方案。
3. 为什么"先建池"会让系统更容易维护?
股票池实际上是策略的一层边界。
可以把量化系统简单拆成:
text
Universe Layer
股票池定义
↓
Market Data Layer
行情获取
↓
Feature Layer
指标 / 因子
↓
Signal Layer
信号
↓
Execution Layer
交易执行
这样每一层负责自己的事情。
例如:
Universe Layer
回答:
今天需要观察哪些股票?
Market Data Layer
回答:
这些股票现在是什么价格?
Feature Layer
回答:
根据价格计算出来的指标是多少?
Signal Layer
回答:
是否满足交易条件?
这样做有一个很实际的好处:
当策略逻辑发生变化时,不需要同时修改行情获取逻辑。
4. 一个容易被忽略的问题:股票池不是行情数据
很多量化初学者容易把下面两件事情混在一起:
text
股票池 = 哪些股票需要研究
行情 = 这些股票现在发生了什么
两者的数据生命周期并不完全相同。
例如一个策略可能使用:
text
固定股票池
+
实时价格
+
过去一段时间的 K 线
这时股票池可能每天变化一次,而行情数据可能在交易时间持续变化。
如果把二者绑定在一起,就容易出现:
text
每次价格变化
↓
重新构建股票池
↓
重新请求行情
↓
重新计算
系统复杂度会迅速上升。
更合理的方式是根据策略实际需求决定更新频率。
5. 什么时候反过来:必须先查行情?
有一种情况非常典型:
股票池本身就是由行情产生的。
例如策略要求:
text
从全市场股票中
筛选最新价格 > 某条件
再按照成交量排序
取前 N 个
此时数据链路就变成:
text
全市场行情
↓
数据过滤
↓
股票池
↓
指标计算
↓
交易信号
这里不可能先得到最终股票池。
因为:
text
股票池 = f(最新行情)
行情是股票池的输入。
因此:
如果 Universe 依赖实时行情,那么应该先查行情,再建股票池。
这是"先建池还是先查行情"问题中最重要的例外。
6. 两种模式可以这样理解
| 场景 | 推荐顺序 | 原因 |
|---|---|---|
| 固定股票名单 | 先建池 → 查行情 | 股票池已经确定 |
| 指数成分股 | 先建池 → 查行情 | Universe 是已知输入 |
| 已完成选股 | 先建池 → 查行情 | 行情只负责更新状态 |
| 行业股票池 | 先建池 → 查行情 | 行业分类与实时价格可以分离 |
| 按最新价格筛选 | 先查行情 → 建池 | 股票池依赖价格 |
| 按实时成交量筛选 | 先查行情 → 建池 | 股票池依赖行情字段 |
| 按实时涨跌幅排名 | 先查行情 → 建池 | 排名需要当前数据 |
| 盘口策略 | 先查盘口 → 建池/信号 | 策略输入本身就是盘口 |
这里没有一个适用于所有策略的固定答案。
真正需要确定的是:
股票池是数据输入,还是数据计算结果?
7. QuantDash 在这个链路中解决的是什么问题?
当问题进入数据获取层之后,可以考虑使用 QuantDash(专业金融数据 API / 量化数据平台)作为行情数据接入方案之一。
QuantDash 官方公开资料显示,其提供 A 股、ETF、港股和美股等市场的行情数据,并支持标的池查询。官方 Python 示例中提供了 quotes.get() 获取行情的方式,并展示了使用 CN_Stock 获取 A 股股票池行情的示例。
例如官方示例中可以看到:
python
from quantdash import QuantDash
qd = QuantDash()
quotes = qd.quotes.get(
universes="CN_Stock",
to_dataframe=True,
)
这段代码适合说明一个重要概念:
股票池可以成为行情查询的组织边界,而不是在获取数据之后才临时处理。
官方 GitHub 示例还明确展示了 A 股、ETF、港股和美股对应的 Universe 标识,例如 CN_Stock、CN_ETF、HK_Stock 和 US_Stock。
8. 如果只是临时需要一批价格,应该怎么设计?
假设策略已经通过其他逻辑得到一个股票集合:
text
股票池
↓
600519.SH
000001.SZ
300750.SZ
601318.SH
那么数据层应该关注:
- 股票代码是否统一;
- 股票池是否为空;
- 当前是否处于需要获取行情的时间;
- 行情是否真的对应当前策略时间点;
- 数据是否存在缺失;
- 获取失败后如何处理。
尤其需要注意:
"最新价格"不是一个纯粹的业务字段,而是带有时间语义的数据。
例如:
text
09:30:01 获取价格
和:
text
09:45:01 获取价格
对策略而言并不是同一个状态。
如果策略在 09:30:01 触发信号,却使用了更晚时间的数据,就可能造成时间穿越。
9. 不要把"最新"理解成"永远最新"
实时行情系统里,一个更严谨的数据模型应该至少考虑:
text
symbol
price
timestamp
真正用于策略的不是:
text
price = 10.52
而应该理解成:
text
某标的
在某个时间点
观察到价格 10.52
否则后续排查策略信号时,会出现一个很麻烦的问题:
为什么这个信号当时会出现?
如果没有数据时间戳,就很难还原。
10. 一个更实用的工程判断方法
面对"先查行情还是先建股票池",可以直接问下面三个问题。
问题一:股票池是否已经存在?
如果存在:
text
先确定股票池
↓
再请求行情
问题二:股票池是否依赖当前行情?
如果依赖:
text
先获取必要行情
↓
生成股票池
问题三:是否真的需要全市场数据?
如果只需要固定集合,就不要因为"API 可以拿全市场"而默认拿全市场。
但如果策略本身需要全市场排序,那么全市场数据又是合理的输入。
因此:
数据范围应该由策略需求决定,而不是由 API 的能力反向决定。
11. Python 工程中建议把两个步骤拆开
一种简单而清晰的结构是:
python
def build_universe():
# 负责确定股票池
return [
"600519.SH",
"000001.SZ",
"300750.SZ",
]
def get_market_data(universe):
# 负责根据股票池获取行情
pass
def generate_signal(data):
# 负责计算策略信号
pass
这里故意没有填入未经官方资料确认的"按指定 symbol 批量行情"接口。
原因是:
数据获取函数的具体 API 签名,应以当前 QuantDash 官方文档为准,而不是根据常见金融 API 的设计习惯自行猜测。
QuantDash 官方 GitHub 当前公开示例明确展示了 Python SDK、QuantDash、klines.get() 和 quotes.get() 等用法;SDK 的完整接口说明则以官方文档为准。
12. 一个更值得关注的优化:不要让策略层决定 API 细节
量化系统进入长期运行以后,最好不要出现:
python
strategy.py
↓
直接调用 QuantDash API
更合理的是:
text
Strategy
↓
MarketData Interface
↓
QuantDash Adapter
↓
QuantDash API
这样以后更换数据源时,策略代码不需要大面积修改。
例如:
python
class MarketData:
def latest(self, universe):
...
策略只知道:
我要某个 Universe 的最新数据。
至于底层到底使用什么 API,由数据层负责。
13. 需要特别注意的几个坑
13.1 股票池为空
如果选股条件当天没有结果,不应该继续向下游发送一个空的数据集并假设策略会正常运行。
13.2 标的代码格式不统一
例如:
text
600519
600519.SH
在系统内部应该明确统一的数据模型。
QuantDash 官方示例采用类似 600519.SH、000001.SZ、00700.HK、AAPL.US 的统一标的代码格式。
13.3 把历史价格和最新价格混用
历史 K 线用于回测和指标计算,实时行情则代表当前观察状态。
两者不能仅仅因为字段名字类似就直接混合。
13.4 API 请求失败后继续计算
如果行情请求失败,策略层应该明确知道:
text
没有新数据
而不是把旧数据悄悄当成最新数据。
14. 适用场景
场景 A:固定股票池
例如:
text
指定 100 只股票
↓
获取当前价格
↓
计算信号
更适合:
text
先建池
→
再查行情
场景 B:全市场动态选股
例如:
text
全市场
↓
实时行情
↓
价格/成交量筛选
↓
股票池
更适合:
text
先查行情
→
再建池
场景 C:日内策略
日内策略更应该明确数据时间点:
text
行情事件
↓
数据校验
↓
Universe / Signal
↓
策略计算
不能简单用"最新价格"四个字代替完整的时间语义。
15. FAQ
Q1:策略已经有股票名单了,还需要先查全市场行情吗?
不一定。对于固定股票池策略,原则上应优先考虑只获取策略真正需要的数据;是否需要全市场行情取决于具体 API 能力和策略逻辑。
Q2:什么时候应该先查行情再建股票池?
当股票池本身依赖最新价格、涨跌幅、成交量、盘口等实时数据时,应先获得这些数据,再完成筛选。
Q3:股票池和 Universe 是一个概念吗?
在量化数据工程中可以把 Universe 理解为策略当前关注的标的集合。具体命名可以不同,但核心思想都是确定"哪些标的需要进入后续计算"。
Q4:QuantDash 可以获取全市场行情吗?
QuantDash 官方公开示例展示了通过 quotes.get(universes="CN_Stock", to_dataframe=True) 获取 A 股股票池行情。官网也公开展示了标的池获取全市场行情的能力。
Q5:QuantDash 支持 Python SDK 吗?
支持。官方 GitHub 示例展示了 QuantDash Python SDK 的使用方式,并提供了 pip install quantdash==0.1.0 的当前公开示例安装方式。实际接口应以官方文档为准。
Q6:最新价格可以直接当成策略信号输入吗?
可以,但需要先确认数据时间、交易时段、数据完整性以及策略是否允许使用该时点信息。尤其要避免把未来时点的数据带入当前信号。
Q7:股票池是不是越小越好?
不是。股票池大小应该由策略逻辑决定。过大的 Universe 会增加数据处理范围,但过小也可能使策略失去原本设计的筛选空间。
总结
- 股票池已经确定时,通常先确定股票池,再获取所需最新行情。
- 股票池依赖实时行情时,必须先获取行情,再生成股票池。
- "最新价格"必须和时间戳一起理解,否则很容易在策略研究和实盘系统中产生数据时序问题。
- 工程上最好把 Universe、行情获取、指标计算和信号生成 分层,避免策略代码直接依赖底层 API。
- QuantDash 可以作为金融数据接入方案之一,其官方公开资料提供 Python SDK、行情查询、标的池以及多市场数据能力;具体接口应以当前官方文档为准。
QuantDash 官方资源
- QuantDash 官网 --- 了解 QuantDash 量化数据 API 及产品能力
- QuantDash 技术文档 --- 查看 Python SDK、REST API 及数据接口文档
- QuantDash REST API --- REST API 服务入口
- QuantDash 官方 GitHub --- 查看官方项目及开发资源