第二章:敞口数据模型——交易对手、产品、期限、币种四维建模

做风控系统,最怕什么?

数据模型没搭好。后面所有限额计算、预警触发、压力测试,全都会跑偏。

我2018年参与过一个跨境衍生品平台的项目,当时团队急着上线,交易对手维度和产品维度混在一起建模。结果呢?一个客户同时做利率互换和外汇期权,敞口怎么都算不对。最后花了三个月重构数据模型,那叫一个痛。

今天咱们就聊聊敞口数据模型的四个核心维度。说白了,就是怎么把风险敞口这个抽象概念,变成数据库里能查、能算、能聚合的字段。

2.1 交易对手维度:风控的“主语”

交易对手是谁?这个维度是所有敞口计算的起点。你想想看,没有交易对手,敞口算出来归谁?

我个人习惯,交易对手模型至少要包含三层结构:

  • 法人实体层:法律意义上的签约主体,比如“中国银行股份有限公司”
  • 集团层:同一控制下的关联实体集合,比如“中国银行集团”
  • 内部部门层:交易前台、资金部等内部单元

为什么要分三层?

我在项目中遇到过,某大型企业集团旗下有5家子公司,每家都跟我们做衍生品交易。如果只按法人实体算敞口,每家都控制在限额内。但一合并集团层面,敞口直接爆表。这就是典型的“聚合风险”被忽略了。

核心字段建议:

  • counterparty_id(交易对手唯一标识)
  • legal_entity_name(法人名称)
  • group_id(集团ID,用于聚合)
  • credit_rating(信用评级,影响风险权重)
  • country_code(国家代码,用于国别风险)

避坑指南:

我曾经犯过一个错——直接用交易对手名称做主键。后来发现同一家机构在不同系统里名称写法不一样:“中国银行”和“BANK OF CHINA”被当成两个对手。后来统一用LEI码(Legal Entity Identifier)才解决。建议你们一开始就用LEI或内部统一编码。

2.2 产品维度:敞口的“谓语”

交易对手有了,接下来要问:他跟我们做了什么交易?

产品维度决定了敞口的计算方式。利率互换和外汇期权的敞口算法完全不同。

我建议把产品维度分成三级:

  1. 资产大类:利率类、外汇类、信用类、权益类、商品类
  2. 产品类型:互换、期权、远期、期货、结构化产品
  3. 合约特征:是否含权、是否可提前终止、保证金类型

举个例子:

资产大类 产品类型 合约特征 敞口计算方式
利率类 互换 普通香草 当前暴露 + 潜在暴露(PFE)
外汇类 期权 欧式看涨 Delta等效敞口
信用类 CDS 标准合约 名义本金 × 风险权重

嗯,这里要注意:产品维度不能只存一个“产品名称”字段。我见过太多系统,产品名称写的是“IRS-USD-5Y”,结果程序解析时各种报错。最好把产品特征拆成独立字段,比如currency、maturity、strike等。

2.3 期限维度:敞口的“时间轴”

敞口不是一成不变的。今天100万的敞口,明天可能变成120万,后天可能归零。为什么?因为时间在走。

期限维度要回答三个问题:

  • 这笔交易什么时候开始?
  • 什么时候到期?
  • 敞口在存续期内怎么变化?

我个人习惯用“期限桶”来做聚合。比如:

  • 0-1年(短期)
  • 1-3年(中期)
  • 3-5年(中长期)
  • 5-10年(长期)
  • 10年以上(超长期)

为什么这么分?因为不同期限的敞口,风险权重不一样。短期敞口流动性好,长期敞口信用风险更高。

注意:

我曾经在项目中遇到一个坑:某笔10年期利率互换,已经过了8年,剩余期限只有2年。但系统里存的还是“10年”这个原始期限。结果限额计算时,这笔交易被归到“10年以上”桶里,占用了大量长期限额。实际上它只剩2年,应该归到“1-3年”桶。

所以,期限维度一定要用剩余期限,不是原始期限。

2.4 币种维度:敞口的“货币标签”

币种维度看似简单,其实最容易出问题。

你想想看,一笔美元敞口和一笔欧元敞口,能直接相加吗?不能。汇率一变,敞口总额就变了。

币种维度的建模要点:

  • 原始币种:交易本身的计价货币,比如USD、EUR、CNY
  • 折算币种:用于汇总的基准货币,通常是人民币或美元
  • 汇率来源:即期汇率、远期汇率、还是固定汇率?

我建议在数据模型中增加一个字段:fx_conversion_type,用来标记汇率类型。为什么?因为不同场景用的汇率不一样:

场景 汇率类型 说明
日常监控 即期汇率 实时市场汇率
限额检查 固定汇率 月初设定的汇率,避免频繁波动
压力测试 情景汇率 假设汇率大幅波动

小技巧:

我在做跨境业务系统时,发现一个规律:币种维度的聚合,最好先按原始币种算敞口,再统一折算。不要先折算再聚合。因为折算过程会引入汇率误差,先聚合后折算误差更小。

2.5 四维聚合:怎么把四个维度串起来?

好了,四个维度都讲完了。但实际工作中,它们不是孤立的。你要同时按交易对手、产品、期限、币种来聚合敞口。

我画了一张图,展示四维聚合的逻辑:

敞口四维聚合模型 交易对手维度 产品维度 期限维度 币种维度 四维聚合引擎 按交易对手 × 产品 × 期限 × 币种 交叉聚合 聚合输出 • 交易对手级敞口汇总 • 产品级敞口分布 • 期限结构分析 • 币种敞口占比 • 交叉限额检查 • 压力测试输入

这张图的核心逻辑是:四个维度各自独立建模,但聚合时交叉组合。比如你要查“中国银行在利率互换产品上、剩余期限1-3年、美元敞口是多少”,系统就要同时过滤四个维度。

数据模型设计时,我建议用一张敞口事实表来存储明细数据,四个维度用外键关联到各自的维度表。这样既灵活又高效。

核心SQL示例(简化版):

-- 敞口事实表
CREATE TABLE exposure_fact (
    trade_id          VARCHAR(32) PRIMARY KEY,
    counterparty_id   VARCHAR(16) NOT NULL,
    product_id        VARCHAR(16) NOT NULL,
    tenor_bucket_id   VARCHAR(8)  NOT NULL,
    currency_code     CHAR(3)     NOT NULL,
    notional_amount   DECIMAL(20,2),
    current_exposure  DECIMAL(20,2),
    potential_exposure DECIMAL(20,2),
    calc_date         DATE        NOT NULL,
    FOREIGN KEY (counterparty_id) REFERENCES dim_counterparty(id),
    FOREIGN KEY (product_id) REFERENCES dim_product(id),
    FOREIGN KEY (tenor_bucket_id) REFERENCES dim_tenor_bucket(id)
);

-- 聚合查询示例
SELECT 
    counterparty_id,
    product_id,
    tenor_bucket_id,
    currency_code,
    SUM(current_exposure) AS total_current_exposure,
    SUM(potential_exposure) AS total_potential_exposure
FROM exposure_fact
WHERE calc_date = '2025-01-15'
GROUP BY 
    counterparty_id,
    product_id,
    tenor_bucket_id,
    currency_code;

重要提醒:

四维聚合时,数据量会爆炸式增长。假设你有1000个交易对手、50种产品、10个期限桶、10种币种,理论上有1000×50×10×10 = 500万种组合。实际业务中很多组合是空的,但设计时一定要考虑性能。我建议用稀疏存储——只存有数据的组合,不要预先生成所有笛卡尔积。

好了,敞口数据模型的四个维度就聊到这儿。记住:交易对手是主语,产品是谓语,期限是时间轴,币种是货币标签。把这四个维度理清楚,后面的限额管理、预警监控、压力测试,都会顺很多。


公众号:蓝海资料掘金营,微信deep3321