You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

计算组合单股风险贡献时遇log方法NaN及矩阵不兼容错误

解决投资组合风险贡献计算中的R代码问题

咱们一步步来排查你遇到的两个问题:

1. 第一个警告:log(pr) : NaNs produced

这个问题的根源是你用错了Return.calculate函数。你已经通过percent_change2计算出了月度简单收益率(last(x)/first(x)-1),但又把这个收益率序列传给了Return.calculate——而这个函数的设计初衷是从价格序列计算收益率,不是对已有的收益率做二次处理。

当你用method="log"时,Return.calculate会执行log(1 + 简单收益率)来转换为对数收益率,但如果你的月度收益率里存在<= -1的值(比如价格出现负数,或者填充NA时出错导致收益异常),log(1 + r)就会遇到非正数,进而产生NaNs。

修复方案:

直接把你已经计算好的monthly_return处理成对数收益率就行,不用再调用Return.calculate:

# 直接将简单收益率转换为对数收益率,注意要确保1 + 收益率 > 0
portfolioComponentReturns <- na.omit(log(1 + monthly_return))

或者如果你本来就想用简单收益率计算风险贡献,那完全可以跳过对数转换,直接用monthly_return(记得先处理NA):

portfolioComponentReturns <- na.omit(monthly_return)

另外,建议检查你的monthly_return里是否有异常值(比如<= -1的收益率),这可能是填充NA时的逻辑问题导致的——你用前后两天的价格平均填充NA,但如果前后价格本身有问题,可能会算出不合理的月度收益。可以用summary(monthly_return)查看数据分布。

2. 第二个错误:non-conformable arguments

这个错误说明矩阵乘法的维度不匹配:t(w)的列数和covariance_matrix的行数不一致。通常有两个原因:

  • 你的权重向量w的长度,和portfolioComponentReturns中的资产数量(列数)不相等;
  • covariance_matrix的维度因为NA处理或数据错误发生了变化(比如某列全是NA被na.omit删掉了)。

修复方案:

  1. 先确认w的长度:
    运行ncol(portfolioComponentReturns)查看资产数量,确保length(w)和这个数字完全一致。比如如果你的monthly_return有18列(对应df的2:19列),那w必须是长度为18的向量,且元素和为1(投资组合权重的基本要求)。

  2. 检查covariance_matrix的维度:
    运行dim(covariance_matrix),看它是否是n×n的矩阵(n是资产数量)。如果不是,说明portfolioComponentReturns里有列被na.omit删掉了(因为整列都是NA),这时候需要重新检查数据填充的逻辑,确保每列都有有效数据。

  3. 确保w是列向量(或者在矩阵乘法时正确转换):
    有时候w如果是普通向量,t(w)会变成行向量,但如果covariance_matrix是n×n,t(w) %*% covariance_matrix需要行向量×n×n矩阵,结果是行向量,再乘以w(列向量)才能得到标量。可以显式把w转成矩阵:

    w_matrix <- matrix(w, ncol = 1)
    sd_portfolio <- sqrt(t(w_matrix) %*% covariance_matrix %*% w_matrix)
    

完整修正后的代码示例

# 先处理月度收益率转换为对数收益率(或直接用简单收益率)
portfolioComponentReturns <- na.omit(log(1 + monthly_return))
# 检查数据是否正常,避免出现-Inf值
stopifnot(all(apply(portfolioComponentReturns, 2, function(x) all(x != -Inf))))

# 计算协方差矩阵
covariance_matrix <- cov(portfolioComponentReturns)

# 确保权重向量维度匹配资产数量
w_matrix <- matrix(w, ncol = 1)
stopifnot(ncol(covariance_matrix) == length(w))

# 计算组合标准差
sd_portfolio <- sqrt(t(w_matrix) %*% covariance_matrix %*% w_matrix)

# 边际风险贡献
marginal_contribution <- (covariance_matrix %*% w_matrix) / sd_portfolio[1, 1]

# 组件风险贡献(权重×边际贡献,确保维度匹配)
component_contribution <- w_matrix * marginal_contribution

# 验证总和是否等于组合波动率
components_summed <- sum(component_contribution)

# 百分比风险贡献
component_percentages <- component_contribution / sd_portfolio[1, 1]

# 整理成表格
percentage_tibble_by_hand <- tibble(
  asset = colnames(portfolioComponentReturns),
  `portfolio weight` = as.vector(w_matrix),
  `risk contribution` = as.vector(component_percentages)
)

percentage_tibble_by_hand

另外,补充一点:你在填充NA的时候用的df[nas, -1] <- (df[nas-1,-1] + df[nas+1,-1])/2,如果nas是第一行或者最后一行,nas-1或nas+1会超出索引范围,导致错误。建议用更稳健的填充方法,比如zoo::na.approx(df[, -1])来线性插值填充NA,避免边界问题。

内容的提问来源于stack exchange,提问作者Revolucion for Monica

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 04:46:35