4. 度量库存风险价值(VaR)

任务目标

本案例针对做市商持有的库存头寸,使用历史模拟法计算日度VaR(95%置信水平),评估正常市场条件下库存可能面临的最大损失。输入为库存中每个资产的历史日收益率序列,输出为组合层面的日VaR值。

实现思路

历史模拟法不假设收益率服从特定分布,直接使用历史收益率分布的分位数来估计VaR。核心步骤:

  1. 将每个资产的当前市值与历史日收益率相乘,得到该资产的历史每日损益序列;
  2. 将同一历史日所有资产的损益加总,得到组合的历史每日损益序列;
  3. 取该序列在95%置信水平下的分位数(即第5百分位数的损失值),即为日VaR。

关键完整代码

import numpy as np
import pandas as pd

def calculate_historical_var(returns_df, current_positions, confidence_level=0.95):
    """
    使用历史模拟法计算做市库存的日VaR
    
    Parameters:
    -----------
    returns_df : pd.DataFrame
        历史日收益率矩阵,每列对应一个资产,行索引为日期
    current_positions : dict
        当前持仓市值,格式: {'资产名称': 当前市值}
    confidence_level : float
        置信水平,默认95%
    
    Returns:
    --------
    dict: 包含VaR值、历史损益序列及对应百分位数的结果
    """
    # 1. 校验输入
    assert set(returns_df.columns) == set(current_positions.keys()), \
        "收益率列与持仓资产不匹配"
    
    # 2. 计算每个资产的历史日损益序列(市值 × 收益率)
    pnl_df = pd.DataFrame(index=returns_df.index)
    for asset in returns_df.columns:
        pnl_df[asset] = returns_df[asset] * current_positions[asset]
    
    # 3. 组合历史日损益 = 各资产损益之和
    portfolio_pnl = pnl_df.sum(axis=1)
    
    # 4. 计算VaR(取损失侧分位数,VaR为正数表示损失金额)
    var = -np.percentile(portfolio_pnl, (1 - confidence_level) * 100)
    
    return {
        'VaR': var,
        'portfolio_pnl': portfolio_pnl,
        'percentile': (1 - confidence_level) * 100
    }

# ========== 最小可复现示例 ==========
if __name__ == "__main__":
    # 构造示例数据:3个资产,500个历史交易日
    np.random.seed(42)
    dates = pd.date_range('2023-01-01', periods=500, freq='D')
    
    # 模拟历史日收益率(均值0,标准差0.02/0.015/0.01)
    returns_data = {
        'AAPL': np.random.normal(0, 0.02, len(dates)),
        'MSFT': np.random.normal(0, 0.015, len(dates)),
        'TSLA': np.random.normal(0, 0.025, len(dates))
    }
    returns_df = pd.DataFrame(returns_data, index=dates)
    
    # 当前做市库存持仓市值(美元)
    current_positions = {
        'AAPL': 500000,
        'MSFT': 300000,
        'TSLA': 200000
    }
    
    # 计算95%日VaR
    result = calculate_historical_var(returns_df, current_positions, confidence_level=0.95)
    
    print(f"95%日VaR: ${result['VaR']:,.2f}")
    print(f"历史损益序列长度: {len(result['portfolio_pnl'])}天")
    print(f"对应分位数: {result['percentile']}%")
    
    # 输出预期结果(基于上述固定随机种子可复现)
    # 预期输出示例(实际数值由np.random.seed(42)决定):
    # 95%日VaR: $XX,XXX.XX
    # 历史损益序列长度: 500天
    # 对应分位数: 5.0%

运行与验证

运行方式:直接执行脚本(需安装 numpy 和 pandas):

pip install numpy pandas
python var_historical.py

验证方法:

  • 程序正常输出VaR金额、损益序列长度(500天)和分位数(5.0%);
  • 手动验证:将 portfolio_pnl 按升序排列,取第25个值(500×5%)的相反数,应与输出的VaR一致;
  • 改变置信水平为0.99,VaR值应增大(更极端的尾部损失);
  • 将任一资产市值设为0,VaR应相应减小,反映该资产不再贡献风险。

常见问题

  • 历史数据长度不足:历史模拟法要求至少100个以上交易日数据,否则分位数估计不稳定;
  • 持仓变化:本方法假设持仓在历史期间保持不变,若实际持仓动态变化,需按日重新计算或使用加权历史模拟法;
  • VaR的局限性:VaR不衡量超过分位点的尾部损失规模,如需评估极端风险应结合预期亏损(ES)分析。