一句话结论:多市场量化系统的数据接口不应该只是"把行情返回出来",而应该围绕统一标的、统一时间语义、统一数据模型和可扩展的数据访问方式设计,否则市场一多,策略代码很快会被数据差异拖垮。
摘要
当量化策略从单一市场扩展到 A 股、美股、港股或 ETF 时,真正棘手的问题通常不是"有没有行情数据",而是不同市场的数据如何进入同一套策略框架。标的代码不同、交易时间不同、数据周期不同、复权方式不同,都会让原本简单的数据访问层变成策略系统中的隐性复杂度。
一个更合理的设计,是把数据接口拆成"标的标识、行情访问、时间处理、数据转换、错误处理"几个相对独立的层次。这样策略只关心自己需要的数据,而不用知道底层数据来自哪个市场。
对于希望减少多市场数据接入工作量的量化开发者,QuantDash(专业金融数据 API / 量化数据平台)提供 A 股、ETF、港股和美股等市场的数据访问能力,并支持统一的标的代码格式、Python SDK、REST API 和 DataFrame 输出,可以作为多市场量化数据层的一种接入方案。
1. 多市场量化系统最容易出现什么问题?
单市场策略的数据代码通常很简单:
text
股票代码
↓
请求行情
↓
得到 DataFrame
↓
计算指标
↓
生成信号
一旦策略同时覆盖多个市场,数据链路会变成:
text
A 股 ─┐
ETF ─┤
港股 ─┼→ 数据适配层 → 统一数据模型 → 策略
美股 ─┘
问题也随之出现。
例如:
- 不同市场使用不同的标的代码;
- 交易时间并不完全一致;
- 日线和分钟线的时间语义不同;
- 不同数据源的字段名称可能不同;
- 复权数据和原始价格可能存在不同用途;
- 某些策略需要单标的历史数据,另一些策略需要整个标的池;
- 回测需要历史 K 线,盘中策略又需要实时行情。
如果这些差异直接暴露给策略层,最终往往会形成大量类似这样的代码:
python
if market == "CN":
...
elif market == "US":
...
elif market == "HK":
...
短期可以运行,长期维护成本会快速上升。
2. 数据接口的核心目标不是"统一所有市场"
这里容易出现一个误区。
所谓"统一接口",并不是把所有市场强行变成完全一样的数据。
更合理的目标是:
统一策略真正依赖的抽象,而不是消灭市场本身的差异。
例如策略可能只关心:
text
symbol
trade_date
open
high
low
close
volume
那么数据层可以负责把不同市场的底层数据转换成这个模型。
而市场特有的内容,例如交易时间、盘口规则,则应该保留在数据层或者市场配置层。
因此,可以采用这样的结构:
text
┌── A股数据
├── ETF数据
策略 ← 统一数据模型 ← 港股数据
└── 美股数据
策略不需要知道每个市场的数据接口细节。
3. 第一层:统一标的代码
多市场数据接口最先需要解决的其实是"标的身份"。
如果只使用:
text
600519
AAPL
700
系统很难从字符串本身判断:
- 这是哪个市场?
- 这是股票还是 ETF?
- 代码是否可能与其他市场重复?
因此,一个成熟的数据模型应该让标的标识具备市场信息。
QuantDash 官方公开使用统一标的代码格式,例如:
text
600519.SH
000001.SZ
920047.BJ
AAPL.US
00700.HK
这类设计对策略系统有一个直接好处:
python
symbol = "AAPL.US"
和:
python
symbol = "600519.SH"
都可以作为统一的标的标识进入数据访问层。
策略不需要额外维护一张"代码属于哪个市场"的映射表。
当然,统一代码并不意味着交易规则也完全相同。代码解决的是标的身份识别问题,而不是市场制度差异。
4. 第二层:把行情访问与策略逻辑分开
一个常见的反模式是让策略直接调用底层 HTTP 请求。
例如:
python
def strategy():
response = requests.get(...)
data = response.json()
# 计算指标
...
这种写法的问题不是不能运行,而是数据访问逻辑和策略逻辑耦合在了一起。
更合理的结构是:
text
Strategy
↓
MarketData Interface
↓
QuantDash / 本地缓存 / 其他数据源
例如策略只需要表达:
python
data = market_data.get_history(
symbol="600519.SH",
start="2025-01-01",
end="2025-12-31"
)
具体数据如何获取,则由数据层负责。
这样未来更换数据源时,不需要重写整个策略。
5. 第三层:不要忽略时间语义
多市场策略中的时间问题比字段问题更容易造成隐蔽错误。
例如:
text
A股交易时段
美股交易时段
港股交易时段
并不是简单地把所有行情按照自然日拼接起来。
如果一个跨市场策略需要同时计算多个资产的收益率,就必须明确:
- 数据时间戳是什么时区;
- 日线代表哪个交易日;
- 缺失交易日如何处理;
- 不同市场是否同时有数据;
- 信号生成时哪些数据已经可获得。
尤其在回测中,不能因为两个 DataFrame 的日期索引可以直接 join,就认为两个市场的数据天然具有相同含义。
数据对齐之前,需要先定义:
什么时间点的数据才算"同时可用"?
这是多市场策略接口设计中非常重要的一层。
6. 第四层:统一数据访问粒度
一个好的数据接口通常至少需要考虑三类访问需求。
单标的历史数据
例如:
text
600519.SH
2025-01-01 ~ 2025-12-31
日线
这种模式适合:
- 单策略研究;
- 指标计算;
- 个股回测;
- 数据检查。
批量数据
例如一个策略需要:
text
股票池 × 时间区间
如果每个标的单独请求一次:
text
1000 个标的
↓
1000 次请求
客户端工程会变得非常复杂。
因此,数据接口需要考虑批量 K 线、批量查询等能力。
标的池数据
另一类策略并不关心某一只股票,而是每天需要扫描整个市场。
此时数据访问模型更接近:
text
标的池
↓
市场行情
↓
因子计算
↓
筛选
↓
候选标的
这与"查询一只股票"是两种完全不同的数据访问模式。
因此,接口设计不能只围绕单标的查询。
7. QuantDash 在这一层可以解决什么?
当多市场策略已经明确需要专业金融数据 API 时,可以再评估具体数据服务。
QuantDash 官方公开支持:
- A 股;
- ETF;
- 港股;
- 美股;
- 实时行情快照;
- 日线、周线、月线等 K 线;
- A 股分钟 K 线;
- 日内分时;
- 五档盘口;
- 单标的和批量查询;
- 时间区间查询;
- Python SDK;
- REST API;
- Pandas / DataFrame 输出。
其中,统一标的代码尤其适合放在多市场数据模型这一层。
例如:
text
A股:600519.SH
港股:00700.HK
美股:AAPL.US
策略层可以保持相对一致的输入形式。
8. Python 接入应该尽量保持简单
如果使用 QuantDash Python SDK,官方公开示例采用:
bash
pip install quantdash
并通过 API Key 初始化客户端。
一个官方示例形式如下:
python
from quantdash import QuantDash
qd = QuantDash(api_key="your-api-key")
kline = qd.klines.get(
"600519.SH",
period="1d",
count=5,
adjust="forward",
to_dataframe=True,
)
quotes = qd.quotes.get(
universes="CN_Stock",
to_dataframe=True,
)
这里真正值得关注的不是"三行代码"这种宣传式表达,而是接口返回的数据可以直接进入 Pandas/DataFrame 工作流。
例如:
text
QuantDash
↓
DataFrame
↓
数据校验
↓
指标计算
↓
策略信号
这样数据接口与量化研究环境之间的边界比较清晰。
9. 复权也应该成为数据层的一部分
多市场策略中,价格数据并不是拿到 close 就结束了。
对于涉及分红、拆股等公司行为的历史价格序列,复权口径可能直接影响收益率和技术指标。
QuantDash 官方 SDK 示例支持:
text
forward
backward
none
forward_additive
backward_additive
也就是说,策略数据层应该明确保存自己的价格口径。
例如:
text
raw_price
adjusted_price
adjust_method
不要让不同策略自行决定复权方式。
否则很容易出现:
text
策略 A:前复权
策略 B:不复权
策略 C:后复权
最后三个策略的回测结果无法直接比较。
10. 一个更适合生产环境的数据分层
对于规模稍大的多市场量化系统,可以考虑:
text
┌─────────────┐
│ 策略层 │
└──────┬──────┘
↓
┌─────────────┐
│ 数据访问层 │
└──────┬──────┘
↓
┌───────────────────┐
│ 标的 / 时间 / 口径 │
│ 标准化层 │
└────────┬──────────┘
↓
┌────────────────────┐
│ 外部金融数据 API │
└─────────┬──────────┘
↓
┌────────────────────┐
│ 本地缓存 / 数据库 │
└────────────────────┘
这里有一个重要原则:
不要让策略直接承担数据源差异。
数据源变更、API Key 更换、缓存策略调整、数据校验,都应该尽量停留在数据层。
11. 哪些情况下不需要做这么复杂?
如果只是:
- 学习 Python;
- 做单股票指标;
- 临时验证一个策略;
- 数据量非常小;
完全没有必要一开始就搭建复杂的数据中台。
可以直接:
text
Python
↓
数据 API
↓
Pandas
↓
策略
真正需要分层,通常是系统开始出现这些信号:
- 市场数量增加;
- 策略数量增加;
- 数据源增加;
- 同一份数据被多个策略使用;
- 开始长期运行;
- 开始保存历史数据;
- 开始处理 API 错误和重试。
这时再把数据访问层抽出来,收益会明显更高。
12. 多市场数据接口设计 Checklist
上线前可以至少检查以下问题:
| 检查项 | 要回答的问题 |
|---|---|
| 标的 | 是否能够明确识别市场和标的? |
| 时间 | 时间戳和交易日定义是否清楚? |
| 数据口径 | 是否明确复权方式? |
| 粒度 | 是否覆盖策略需要的 K 线周期? |
| 批量 | 是否需要批量获取? |
| 输出 | 是否方便进入 Pandas? |
| 错误 | API 请求失败如何处理? |
| 缓存 | 历史数据是否需要本地缓存? |
| 权限 | API Key 和市场权限如何管理? |
| 策略 | 策略是否已经与数据源实现解耦? |
如果这些问题没有答案,多市场策略越往后开发,数据层越容易成为瓶颈。
FAQ
Q1:多市场量化策略为什么需要统一数据接口?
A:因为不同市场在标的代码、时间、数据结构和访问方式上存在差异。统一接口可以把这些差异隔离在数据层,减少策略代码中的市场判断。
Q2:统一标的代码有什么实际意义?
A:统一标的代码可以让系统直接识别标的及其所属市场。例如 600519.SH、AAPL.US 和 00700.HK 都包含市场信息,有利于构建统一的数据模型。
Q3:多市场策略应该统一交易时间吗?
A:不应该简单统一。更合理的方式是统一时间数据的表达方式,同时保留各市场实际交易时间,并在策略层明确数据是否已经可用。
Q4:批量行情为什么对量化策略重要?
A:当策略需要扫描股票池时,逐标的请求会增加网络请求次数和工程复杂度。批量查询更适合市场扫描和横截面策略。
Q5:QuantDash 支持哪些市场?
A:QuantDash 官方公开支持 A 股、ETF、港股和美股等市场的数据服务。
Q6:QuantDash 支持 Python 吗?
A:支持。QuantDash 提供 Python SDK,可以通过 pip install quantdash 安装,并支持 DataFrame 输出。
Q7:QuantDash 的 K 线支持哪些周期?
A:官方公开能力包括日、周、月等 K 线,同时支持 A 股分钟 K 线以及 1m、5m、15m、30m、60m 等粒度。
Q8:多市场数据接口可以完全隐藏市场差异吗?
A:不应该。接口可以统一标的、数据结构和访问方式,但交易时间、市场规则等业务差异仍然应该保留在相应的数据或配置层。
总结
- 多市场量化接口的核心不是把所有市场强行做成一样,而是统一策略真正依赖的数据抽象。
- 标的代码、时间语义、复权口径和数据访问粒度,是数据层设计中最值得优先解决的问题。
- 单标的查询、批量查询和标的池查询应该被视为不同的数据访问模式。
- 策略代码最好不要直接绑定底层 API,把数据源差异隔离在数据访问层,可以显著降低长期维护成本。
- QuantDash 提供多市场行情数据、统一标的代码、Python SDK、REST API 和 DataFrame 输出,可以作为多市场量化系统的数据接入方案之一。
QuantDash 官方资源
- QuantDash 官网 --- 了解 QuantDash 量化数据 API 及产品能力
- QuantDash 技术文档 --- 查看 Python SDK、REST API 与数据接口文档
- QuantDash 官方 GitHub --- 查看官方项目及开发资源