风险因子数据模型:市场数据、持仓数据与参考数据
做实时风控,最怕什么?
数据模型没设计好。我见过太多团队,算法写得漂亮,结果数据模型一塌糊涂。行情来了,系统直接崩了。嗯,今天我们就聊聊这个核心问题。
一、市场数据模型:行情、利率、汇率
市场数据是实时风控的血液。它变化最快,量也最大。我个人习惯把市场数据分成三类:
- 行情数据:股票、期货、期权的实时价格
- 利率数据:国债收益率、Libor、Shibor等
- 汇率数据:外汇即期、远期、掉期报价
这三类数据,更新频率完全不同。行情是毫秒级,利率是秒级,汇率介于两者之间。你想想看,如果用一个模型去套,肯定出问题。
1.1 行情数据模型
我在项目中遇到过最头疼的事:行情数据字段定义不统一。有的交易所用"last_price",有的用"close",还有的用"trade_price"。解析的时候,代码里全是if-else。
所以,我建议统一成这样的模型:
// 行情快照数据结构
struct MarketSnapshot {
string instrument_id; // 合约代码
int64 timestamp; // 纳秒时间戳
double bid_price; // 买一价
double ask_price; // 卖一价
int32 bid_volume; // 买一量
int32 ask_volume; // 卖一量
double last_price; // 最新成交价
double open_interest; // 持仓量
double settlement; // 结算价(期货特有)
};
核心原则:时间戳必须用纳秒级。别问我为什么,你试试用毫秒级做高频回测,结果全是错的。
1.2 利率与汇率模型
利率和汇率,说白了就是曲线。一条收益率曲线,包含多个期限点。我习惯用这样的结构:
// 利率曲线数据结构
struct YieldCurve {
string curve_id; // 曲线ID,如"CNY_SWAP"
int64 timestamp; // 更新时间
vector<CurvePoint> points; // 曲线点列表
};
struct CurvePoint {
string tenor; // 期限,如"1Y","5Y"
double rate; // 利率值
double spread; // 利差(可选)
};
汇率模型类似,但多了一个"报价方向"字段。是Bid还是Ask?这个搞错了,计算出来的风险敞口直接翻倍。
避坑指南:我曾经因为汇率数据缺少报价方向,导致VaR计算偏差了30%。后来加了个枚举字段,问题才解决。
二、持仓数据模型:你的风险敞口
持仓数据,是风控引擎的"家底"。没有持仓,市场数据再快也没用。
持仓数据分两种:
- 静态持仓:交易完成后就固定了,比如股票持仓
- 动态持仓:会随时间变化,比如期权持仓的希腊字母
我建议用这样的模型:
// 持仓数据结构
struct Position {
string portfolio_id; // 组合ID
string instrument_id; // 合约代码
int64 quantity; // 持仓数量(正为多,负为空)
double cost_price; // 成本价
double market_value; // 市值
double pnl; // 浮动盈亏
int64 update_time; // 更新时间
};
小技巧:持仓数量用int64,别用double。你想想看,股票持仓100股就是100股,用浮点数容易出精度问题。
对于衍生品持仓,还需要额外字段:
| 字段 | 类型 | 说明 |
|---|---|---|
| delta | double | 期权价格对标的物价格的敏感度 |
| gamma | double | delta的变化率 |
| vega | double | 对波动率的敏感度 |
| theta | double | 时间衰减 |
这些希腊字母,是实时风控的核心输入。每次行情更新,都要重新计算。所以,数据模型里必须预留这些字段。
三、参考数据模型:不变的"元数据"
参考数据,说白了就是"不会变"的数据。比如:
- 合约基本信息(代码、名称、交易所)
- 交易日历(开市、休市)
- 合约乘数、最小变动价位
- 保证金比例、涨跌停板
这些数据虽然变化少,但一旦错了,后果很严重。我记得有一次,某个合约的乘数配置错了,导致保证金计算少了10倍。还好是测试环境,不然就出大事了。
参考数据模型我建议这样设计:
// 合约参考数据
struct InstrumentRef {
string instrument_id; // 合约代码
string exchange; // 交易所
string product_type; // 产品类型(股票/期货/期权)
double multiplier; // 合约乘数
double tick_size; // 最小变动价位
double margin_rate; // 保证金比例
double upper_limit; // 涨停价
double lower_limit; // 跌停价
int32 delivery_month; // 交割月份
};
重要提醒:参考数据一定要做版本管理。合约乘数变了,旧数据怎么办?我习惯用"生效日期"字段来控制。
四、数据模型之间的关系
这三种数据不是孤立的。它们之间的关系,我用一张图来说明:
你看,市场数据驱动持仓数据的估值,参考数据为两者提供"骨架"。三者缺一不可。
五、实战中的设计要点
说了这么多理论,来点实际的。我在做实时风控引擎时,总结了几个要点:
- 数据模型要扁平化:别搞太多嵌套。实时计算讲究速度,嵌套结构解析慢。
- 字段类型要精确:时间戳用int64,价格用double,数量用int64。别混用。
- 预留扩展字段:市场在变,模型也要变。我习惯在每个结构体末尾加一个"ext"字段,存JSON。
- 数据版本化:参考数据尤其重要。合约乘数变了,历史数据怎么处理?加个"version"字段。
我的习惯:所有数据模型都用Protocol Buffers定义。一来跨语言兼容,二来序列化快。C++和Python都能用,省心。
嗯,数据模型就聊到这里。记住一句话:数据模型设计得好,实时风控就成功了一半。另一半,是计算引擎的优化。那个我们后面再聊。