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 相互关联,形成组合风险归因的完整数据链路。

组合风险归因四表关系与流式数据流 组合风险归因四表关系与流式数据流 持仓表 position 交易表 trade 因子暴露表 factor_exposure 基准权重表 benchmark_weight 流式计算引擎 Flink SQL / Kafka Streams 组合风险归因结果输出 as_of_date + portfolio_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 字段支持风格因子、行业因子、货币因子等多维度扩展,无需修改表结构。
常见陷阱:不要将因子暴露值直接存储在持仓表中。因子暴露是证券的固有属性,与组合无关;分离存储可避免数据冗余,并支持因子模型的独立更新与版本管理。