前言
以下是使用历史模拟法计算 VaR(Value at Risk)的 Python 实现。历史模拟法是一种非参数方法,直接基于历史数据的分布来估计风险,无需假设收益率服从特定分布。
python3.13 环境配置
python3.13下载安装教程:https://blog.csdn.net/2501_91538706/article/details/147315428
核心代码实现
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy.stats import norm
def calculate_var(returns, confidence_level=0.95, horizon=1, method='historical',
portfolio_value=None, distribution=None, mu=None, sigma=None):
"""
计算投资组合的VaR(Value at Risk)
参数:
returns (pd.Series或pd.DataFrame): 资产或投资组合的历史收益率
confidence_level (float): 置信水平,默认为0.95
horizon (int): 预测的时间范围(天数),默认为1天
method (str): 计算方法,支持'historical'(历史模拟法)、'parametric'(参数法)
portfolio_value (float): 投资组合价值,若为None则返回百分比VaR
distribution (str): 参数法中使用的分布,支持'normal'(正态分布)
mu (float): 参数法中的均值
sigma (float): 参数法中的标准差
返回:
float: VaR值(若portfolio_value不为None,则返回绝对金额;否则返回百分比)
"""
# 历史模拟法
if method == 'historical':
# 计算时间范围内的累积收益率
if horizon > 1:
returns = returns.rolling(window=horizon).apply(lambda x: (1+x).prod() - 1).dropna()
# 计算VaR(负号表示损失)
var_percentile = np.percentile(returns, (1 - confidence_level) * 100)
# 转换为绝对金额(如果提供了投资组合价值)
if portfolio_value is not None:
return -portfolio_value * var_percentile
else:
return -var_percentile
# 参数法(正态分布假设)
elif method == 'parametric':
if distribution == 'normal':
if mu is None:
mu = returns.mean()
if sigma is None:
sigma = returns.std()
# 调整时间范围
mu_horizon = mu * horizon
sigma_horizon = sigma * np.sqrt(horizon)
# 计算VaR
z_score = norm.ppf(1 - confidence_level)
var_percentile = mu_horizon + z_score * sigma_horizon
# 转换为绝对金额(如果提供了投资组合价值)
if portfolio_value is not None:
return -portfolio_value * var_percentile
else:
return -var_percentile
else:
raise ValueError("不支持的计算方法,请选择'historical'或'parametric'")
def calculate_cvar(returns, confidence_level=0.95, horizon=1, portfolio_value=None):
"""
计算条件VaR(CVaR)或预期尾部损失(ES)
参数:
returns (pd.Series或pd.DataFrame): 资产或投资组合的历史收益率
confidence_level (float): 置信水平,默认为0.95
horizon (int): 预测的时间范围(天数),默认为1天
portfolio_value (float): 投资组合价值,若为None则返回百分比CVaR
返回:
float: CVaR值
"""
# 计算时间范围内的累积收益率
if horizon > 1:
returns = returns.rolling(window=horizon).apply(lambda x: (1+x).prod() - 1).dropna()
# 计算VaR阈值
var_threshold = np.percentile(returns, (1 - confidence_level) * 100)
# 计算CVaR(条件平均损失)
tail_returns = returns[returns <= var_threshold]
cvar_percentile = tail_returns.mean()
# 转换为绝对金额(如果提供了投资组合价值)
if portfolio_value is not None:
return -portfolio_value * cvar_percentile
else:
return -cvar_percentile
def plot_var(returns, var, confidence_level=0.95, title="收益率分布与VaR"):
"""可视化VaR"""
plt.figure(figsize=(10, 6))
plt.hist(returns, bins=50, density=True, alpha=0.7, color='skyblue')
# 绘制VaR线
var_percentile = np.percentile(returns, (1 - confidence_level) * 100)
plt.axvline(x=-var, color='r', linestyle='--', label=f'VaR({confidence_level*100}%) = {var:.4f}')
# 填充尾部区域
x = np.linspace(returns.min(), -var, 100)
plt.fill_between(x, norm.pdf(x, returns.mean(), returns.std()), color='red', alpha=0.3)
plt.title(title)
plt.xlabel('收益率')
plt.ylabel('密度')
plt.legend()
plt.grid(True)
plt.show()
使用示例
下面是一个使用真实股票数据计算 VaR 的完整示例:
# 导入必要的库
import yfinance as yf
# 获取股票数据
def get_stock_data(ticker, start_date, end_date):
"""获取股票历史数据"""
stock = yf.download(ticker, start=start_date, end=end_date)
return stock
# 计算投资组合收益率
def calculate_portfolio_returns(stock_data, weights=None):
"""计算投资组合收益率"""
# 计算每日收益率
returns = stock_data['Adj Close'].pct_change().dropna()
# 如果是单只股票,直接返回收益率
if isinstance(returns, pd.Series) or weights is None:
return returns
# 计算投资组合收益率
portfolio_returns = returns.dot(weights)
return portfolio_returns
# 主函数:演示VaR计算
def main():
# 获取股票数据
tickers = ['AAPL', 'MSFT', 'GOOGL', 'AMZN']
start_date = '2020-01-01'
end_date = '2023-01-01'
# 创建等权重投资组合
weights = np.array([0.25, 0.25, 0.25, 0.25])
# 存储各股票的收益率
all_returns = pd.DataFrame()
for ticker in tickers:
stock_data = get_stock_data(ticker, start_date, end_date)
returns = stock_data['Adj Close'].pct_change().dropna()
all_returns[ticker] = returns
# 计算投资组合收益率
portfolio_returns = calculate_portfolio_returns(all_returns, weights)
# 计算VaR(95%置信水平,1天)
var_95 = calculate_var(portfolio_returns, confidence_level=0.95)
var_99 = calculate_var(portfolio_returns, confidence_level=0.99)
# 计算CVaR
cvar_95 = calculate_cvar(portfolio_returns, confidence_level=0.95)
print(f"投资组合每日95% VaR: {var_95:.4%}")
print(f"投资组合每日99% VaR: {var_99:.4%}")
print(f"投资组合每日95% CVaR: {cvar_95:.4%}")
# 计算10天VaR(时间缩放)
var_95_10day = calculate_var(portfolio_returns, confidence_level=0.95, horizon=10)
print(f"投资组合10天95% VaR: {var_95_10day:.4%}")
# 使用参数法(正态分布)计算VaR
var_parametric = calculate_var(
portfolio_returns,
confidence_level=0.95,
method='parametric',
distribution='normal'
)
print(f"基于正态分布假设的95% VaR: {var_parametric:.4%}")
# 可视化VaR
plot_var(portfolio_returns, var_95, confidence_level=0.95, title="投资组合收益率分布与VaR")
# 假设投资组合价值为100万美元,计算绝对VaR
portfolio_value = 1000000
var_dollar = calculate_var(portfolio_returns, confidence_level=0.95, portfolio_value=portfolio_value)
print(f"投资组合价值 ${portfolio_value:,} 的95% VaR: ${var_dollar:,.2f}")
if __name__ == "__main__":
main()
代码解释
- 历史模拟法计算 VaR:直接基于历史收益率的分布,通过百分位数计算 VaR。例如,95% 置信水平下的 VaR 是收益率分布的第 5百分位数。
- 条件 VaR(CVaR)计算:计算超过 VaR 阈值的平均损失,提供了更保守的风险度量。
- 时间范围调整:通过滚动窗口计算多日累积收益率,实现时间范围的扩展。 参数法对比:支持基于正态分布假设的参数法 VaR计算,便于对比不同方法的结果。
- 可视化功能:绘制收益率分布直方图,并标记 VaR 阈值和尾部风险区域。