4、组合数据结构:投资组合表结构设计
组合风险归因的起点是数据。没有规范、完整的表结构,后续的流式计算、因子暴露计算和风险分解将寸步难行。本章定义四张核心表:持仓表、交易表、因子暴露表和基准权重表。这四张表构成组合风险归因的“数据底座”,所有 SQL 流式处理逻辑都围绕它们展开。
设计原则:每张表都包含
as_of_date(数据日期)和 portfolio_id(组合ID)两个分区键,便于流式场景下的增量更新与回溯。
4.1 持仓表(position)
持仓表记录每个组合在每日收盘后的实际持仓明细。它是计算组合收益、风险贡献和因子暴露的基础。
| 字段名 | 类型 | 说明 |
|---|---|---|
as_of_date |
DATE | 数据日期(分区键) |
portfolio_id |
STRING | 组合唯一标识(分区键) |
security_id |
STRING | 证券代码 |
quantity |
DECIMAL(18,4) | 持仓数量(股/份) |
market_price |
DECIMAL(18,6) | 收盘价 |
market_value |
DECIMAL(18,2) | 市值 = quantity × market_price |
weight |
DECIMAL(10,6) | 组合权重(市值 / 组合总市值) |
currency |
STRING | 币种 |
update_ts |
TIMESTAMP | 记录更新时间(流式事件时间) |
-- 持仓表 DDL(示例)
CREATE TABLE position (
as_of_date DATE,
portfolio_id STRING,
security_id STRING,
quantity DECIMAL(18,4),
market_price DECIMAL(18,6),
market_value DECIMAL(18,2),
weight DECIMAL(10,6),
currency STRING,
update_ts TIMESTAMP(3),
WATERMARK FOR update_ts AS update_ts - INTERVAL '5' SECOND,
PRIMARY KEY (as_of_date, portfolio_id, security_id) NOT ENFORCED
) PARTITIONED BY (as_of_date, portfolio_id);
4.2 交易表(trade)
交易表记录组合在交易日的所有买卖行为。流式场景下,交易表是驱动实时风险计算的“事件流”。
| 字段名 | 类型 | 说明 |
|---|---|---|
trade_id |
STRING | 交易唯一ID(主键) |
portfolio_id |
STRING | 组合ID |
security_id |
STRING | 证券代码 |
trade_dt |
DATE | 交易日期 |
trade_ts |
TIMESTAMP(3) | 交易时间戳(事件时间) |
side |
STRING | 买卖方向:BUY / SELL |
quantity |
DECIMAL(18,4) | 成交数量 |
price |
DECIMAL(18,6) | 成交价格 |
trade_amount |
DECIMAL(18,2) | 成交金额 = quantity × price |
fee |
DECIMAL(18,2) | 交易费用 |
流式关键点:交易表的
trade_ts 字段作为事件时间戳,配合 WATERMARK 策略,可实现“交易发生即计算”的实时风险归因。
4.3 因子暴露表(factor_exposure)
因子暴露表存储每个证券在特定日期对各个风险因子的暴露度。它是风险归因的核心输入,通常由外部模型或内部估值系统生成。
| 字段名 | 类型 | 说明 |
|---|---|---|
as_of_date |
DATE | 数据日期(分区键) |
security_id |
STRING | 证券代码 |
factor_id |
STRING | 因子ID(如:MKT_RF、SMB、HML、MOM) |
exposure_value |
DECIMAL(18,6) | 因子暴露值(标准化后) |
factor_type |
STRING | 因子类型:STYLE / INDUSTRY / CURRENCY |
update_ts |
TIMESTAMP(3) | 更新时间 |
-- 因子暴露表 DDL(示例)
CREATE TABLE factor_exposure (
as_of_date DATE,
security_id STRING,
factor_id STRING,
exposure_value DECIMAL(18,6),
factor_type STRING,
update_ts TIMESTAMP(3),
WATERMARK FOR update_ts AS update_ts - INTERVAL '10' SECOND,
PRIMARY KEY (as_of_date, security_id, factor_id) NOT ENFORCED
) PARTITIONED BY (as_of_date);
4.4 基准权重表(benchmark_weight)
基准权重表记录基准指数(如沪深300、标普500)在每日的成份股权重。用于计算主动权重(组合权重 - 基准权重),进而分解主动风险。
| 字段名 | 类型 | 说明 |
|---|---|---|
as_of_date |
DATE | 数据日期(分区键) |
benchmark_id |
STRING | 基准ID(如:HS300、SPX) |
security_id |
STRING | 证券代码 |
weight |
DECIMAL(10,6) | 基准权重(总和为1) |
update_ts |
TIMESTAMP(3) | 更新时间 |
4.5 四表关系与流式数据流
四张表通过 as_of_date、portfolio_id、security_id 和 factor_id 相互关联,形成组合风险归因的完整数据链路。
数据流说明:持仓表和交易表提供组合的实际持仓与交易行为;因子暴露表提供证券的因子特征;基准权重表提供对标基准。流式计算引擎以
as_of_date 和 portfolio_id 为关联键,将四张表进行 JOIN,实时计算组合的因子暴露、主动权重和风险归因结果。
4.6 表结构设计要点总结
- 分区策略:所有表均按
as_of_date分区,便于历史数据回溯和增量加载。持仓表额外按portfolio_id分区,提升多组合并发查询性能。 - 事件时间与水位线:交易表和因子暴露表设置
WATERMARK,保障流式计算的乱序处理能力。 - 主键设计:持仓表主键为
(as_of_date, portfolio_id, security_id),确保每日每组合每只证券只有一条记录;因子暴露表主键为(as_of_date, security_id, factor_id),避免重复暴露。 - 权重字段精度:权重字段使用
DECIMAL(10,6),满足万分位精度要求,避免浮点误差。 - 扩展性:因子暴露表的
factor_type字段支持风格因子、行业因子、货币因子等多维度扩展,无需修改表结构。
常见陷阱:不要将因子暴露值直接存储在持仓表中。因子暴露是证券的固有属性,与组合无关;分离存储可避免数据冗余,并支持因子模型的独立更新与版本管理。