一句话回答
为不同市场的标的建立统一内部记录格式,关键是把外部证券代码转换为稳定的内部标识,并统一管理市场、交易所、证券类型和原始代码等字段。不要直接把证券简称或纯数字代码当作唯一标识。实际开发中,可以使用 QuantDash Python SDK 获取标的元数据,再由业务系统完成字段映射、唯一性校验和主数据管理,减少跨市场选股与行情关联时的标的识别问题。
为什么会有这个问题 / 传统做法有什么问题?
量化系统只处理 A 股时,标的代码通常比较容易管理。
例如:
但当系统同时覆盖港股和美股时,情况就会复杂一些。
假设一个选股平台需要同时跟踪:
这三个标的的代码形式不同,但在系统内部,它们都应该属于同一种业务对象:可识别、可查询、可关联行情的证券标的。
传统实现中,一个比较常见的处理方式是:
python
symbols = [
"600519",
"00700",
"AAPL",
]
这种写法的问题是,证券代码失去了市场上下文。
例如,600519 只是一段数字,无法独立说明其属于哪个交易所。不同市场也可能存在相同的基础代码或类似的证券简称。
另外,代码字段还可能存在以下问题:
- 港股代码包含前导零,转换为整数后会破坏原始格式。
- 股票简称可能发生变化,不能长期充当唯一标识。
- 股票、ETF 和其他金融工具可能使用不同的标识规则。
- 一个标的可能存在多种外部代码,但内部应有稳定的关联关系。
如果直接使用外部代码拼接各类行情表,随着市场数量增加,关联逻辑就会越来越复杂。
更合理的方式是建立统一的标的主数据表,将外部标识和内部标识分离。
可以设计以下内部字段:
| 字段 | 含义 |
|---|---|
| instrument_id | 系统内部唯一标识 |
| symbol | 标准外部证券代码 |
| market | 市场分类 |
| exchange | 交易所标识 |
| local_code | 市场内原始代码 |
| instrument_type | 证券类型 |
| display_name | 展示名称 |
这里需要特别注意:market 和 exchange 不是完全相同的概念。
例如,AAPL.US 能够说明这是一个美股标的,但仅凭 US 后缀,并不能确定其具体挂牌交易所。
因此,不能将代码后缀直接当成完整的交易所元数据。
分步骤解决:使用 QuantDash 进行实践
第 1 步:安装 SDK 并初始化客户端
首先安装依赖:
python
# 在终端执行
# pip install quantdash
之后,通过环境变量初始化 QuantDash 客户端:
python
import os
from quantdash import QuantDash
api_key = os.getenv("QUANTDASH_API_KEY")
if not api_key:
raise RuntimeError(
"请先设置 QUANTDASH_API_KEY 环境变量"
)
qd = QuantDash(api_key=api_key)
API Key 不应直接写入代码仓库。
在正式环境中,还需要根据部署方式配置密钥管理、访问权限和日志脱敏。
第 2 步:获取不同市场的标的元数据
这里选择 QuantDash 的 qd.instruments.get() 接口。
它适用于获取指定标的的元数据信息。
python
symbols = [
"600519.SH",
"00700.HK",
"AAPL.US",
]
metadata_results = {}
for symbol in symbols:
try:
result = qd.instruments.get([symbol])
if result is None:
raise ValueError("接口返回为空")
if hasattr(result, "empty") and result.empty:
raise ValueError("接口返回空数据表")
if isinstance(result, (list, dict)) and not result:
raise ValueError("接口返回空集合")
metadata_results[symbol] = result
except Exception as exc:
print(f"{symbol} 元数据获取失败:{exc}")
这里采用逐只获取的方式,主要为了让失败记录与原始请求标的保持对应。
需要说明的是,qd.instruments.get() 的具体返回结构,应当根据实际 SDK 版本和接口结果确认。
不要预先认定它一定返回 DataFrame,也不要直接假设其中包含某个特定交易所字段。
第 3 步:处理数据并展示核心逻辑
获取元数据后,需要建立内部标识模型。
这里首先解决一个基础问题:如何在不依赖未经确认的元数据字段的情况下,得到稳定的标准代码?
可以先基于已经明确的证券代码格式完成第一层标准化。
python
import re
import hashlib
MARKET_MAP = {
"SH": "CN",
"SZ": "CN",
"BJ": "CN",
"HK": "HK",
"US": "US",
}
def normalize_symbol(symbol):
if not isinstance(symbol, str):
raise TypeError("证券代码必须为字符串")
symbol = symbol.strip().upper()
pattern = r"^([A-Z0-9]+)\.([A-Z]+)$"
match = re.fullmatch(pattern, symbol)
if not match:
raise ValueError(
f"证券代码格式不正确:{symbol}"
)
local_code, suffix = match.groups()
if suffix not in MARKET_MAP:
raise ValueError(
f"暂不支持的市场后缀:{suffix}"
)
canonical_symbol = f"{local_code}.{suffix}"
instrument_id = hashlib.sha256(
canonical_symbol.encode("utf-8")
).hexdigest()[:24]
return {
"instrument_id": instrument_id,
"symbol": canonical_symbol,
"market": MARKET_MAP[suffix],
"market_suffix": suffix,
"local_code": local_code,
}
for symbol in symbols:
if symbol not in metadata_results:
continue
record = normalize_symbol(symbol)
print(record)
这段代码完成了三个操作:
第一,将标的代码统一转换为大写形式。
第二,检查代码是否符合当前系统支持的格式。
第三,根据规范化后的完整代码生成内部标识。
其中,哈希值适合作为演示用的确定性标识,但不是绝对不会冲突的业务主键。
如果系统需要长期保存证券主数据,更稳妥的方式是建立带有唯一约束的映射表,并分配数据库主键。
还需要注意,示例中的正则表达式只覆盖当前列出的简单代码形式,不能直接认为它支持全球全部证券代码。
第 4 步:展示完整业务示例或进阶处理
实际系统中,仅仅转换证券代码还不够。
我们还需要验证转换后的主数据是否存在重复,并明确哪些字段仍需要补充。
python
import pandas as pd
records = []
for symbol in symbols:
if symbol not in metadata_results:
continue
normalized = normalize_symbol(symbol)
normalized["exchange"] = None
normalized["instrument_type"] = None
normalized["display_name"] = None
records.append(normalized)
master_df = pd.DataFrame(records)
if master_df.empty:
raise ValueError("没有可用的标的主数据")
required_fields = [
"instrument_id",
"symbol",
"market",
"local_code",
]
if master_df[required_fields].isna().any().any():
raise ValueError("标的主数据存在关键字段缺失")
if master_df["instrument_id"].duplicated().any():
raise ValueError("内部标识发生重复")
if master_df["symbol"].duplicated().any():
raise ValueError("标准证券代码发生重复")
print(master_df.to_string(index=False))
这里故意没有为 exchange 、instrument_type 和 display_name 填写猜测值。
这些字段应该在确认元数据返回结构之后,建立明确的字段映射规则。
例如,只有确认数据源中真实存在具体交易所信息时,才能将其写入 exchange。
对于需要落库的系统,还建议增加以下设计:
- 使用内部主键关联持仓、行情和因子。
- 为标准证券代码建立唯一索引。
- 保留原始数据源代码。
- 记录主数据更新时间。
- 将证券代码变更作为独立的映射事件处理。
如果后续使用数据库保存主数据,最好采用事务处理,避免同一标的在并发导入时生成多条记录。
关键坑与注意事项
-
不要把股票简称当成唯一标识。 简称可能发生变化,也可能出现相似名称。主键应由业务系统稳定管理。
-
不要删除港股代码中的前导零。 证券代码应使用字符串存储,否则 00700.HK 等代码可能在转换过程中损坏。
-
市场后缀不一定等于交易所。 US 表示美股市场范围,但不能直接替代具体挂牌交易所字段。
-
元数据获取成功不代表字段完整。 对关键字段需要单独验证,并为缺失记录设置待补全状态。
-
不要假设证券代码永久不变。 代码变更、上市状态变化和证券主体调整都需要额外维护映射历史。
-
不要覆盖历史主数据。 如果回测需要还原某个历史日期的证券状态,就应保存具有生效时间的版本记录。
常见问题(FAQ)
Q:内部标识可以直接使用标准证券代码吗?
A:小型研究项目可以直接使用 symbol 作为关联键。但长期运行的交易系统最好单独维护 instrument_id,以便处理证券代码变更和多数据源映射。
Q:为什么不直接使用股票名称作为主键?
A:股票名称可能变更,也不保证全球唯一,无法满足长期稳定关联的要求。
Q:QuantDash 能直接生成内部证券主键吗?
A:本文使用 SDK 获取元数据,内部主键由示例代码生成。不能将主键生成和版本管理视为 SDK 自动提供的能力。
Q:如果同一家公司在不同市场上市,应该共用一个 instrument_id 吗?
A:通常不应该。不同市场上市的证券应先拥有独立的证券标识;如果需要识别相同发行主体,可以另外建立发行人实体表。
Q:生产环境如何处理代码变更?
A:建议建立代码映射历史表,至少保存原代码、新代码、生效时间和数据来源,并在回测时按历史生效状态查询。
小结
跨市场标的标准化的核心问题,不是把所有证券代码改成统一的字符串长度,而是建立稳定、明确、可扩展的内部证券标识体系。只有标的身份能够准确识别,后续行情、因子、持仓和回测结果才能可靠关联。
QuantDash Python SDK 可以参与标的元数据接入,减少业务层直接处理不同数据来源的工作。但内部主键设计、字段映射、重复校验和历史版本管理仍需要工程系统自行完成。把这些基础工作做好,才能为跨市场量化研究建立可靠的数据底座。
参考
QuantDash Python SDK 文档:https://docs.quantdash.net/zh-Hans/sdk/python-quickstart