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

使用R的PerformanceAnalytics计算ETL时出现不可靠结果的求助

印度股市投资组合ETL计算错误排查与解决

问题背景

通过Python获取印度股市多只股票每日收盘价,使用pct_change()计算日简单收益率并构建等权重投资组合,导出CSV后在R中用PerformanceAnalytics包的ETL()函数按月度分组计算预期尾部风险(Expected Tail Risk)时,触发错误:

ES calculation produces unreliable result (risk over 100%) for column: 1 : 4.05194174741596

原始Python代码

import pandas as pd
import numpy as np
import datetime as dt
from datetime import timedelta, datetime
from pandas_datareader import data as pdr
from scipy.stats import norm, t
import matplotlib.pyplot as plt
import statistics

def getData(stocks, start, end):
    stockData = pdr.get_data_yahoo(stocks, start=start, end=end)
    stockData = stockData['Close']
    returns_pctchange = stockData.pct_change()
    return returns_pctchange

stockList = ['IOC', 'RELIANCE', 'BPCL', 'HINDPETRO', 'EXIDEIND']
stocks = [stock+'.NS' for stock in stockList]
endDate = dt.datetime.now()
startDate = endDate - dt.timedelta(days=8000)

returns = getData(stocks, start=startDate, end=endDate)
returns = returns.dropna()
weights = np.array([1/len(stocks) for n in stocks])
returns['portfolio'] = returns.dot(weights)
returns.reset_index(inplace=True)

数据示例

Date  IOC.NS RELIANCE.NS  BPCL.NS HINDPETRO.NS EXIDEIND.NS    portfolio
0 2002-07-02 0.001000 -0.016930 0.001264 0.009413    0.055980     0.010145
1 2002-07-03 -0.000500 0.003931 -0.003787 0.015961    0.077108     0.018543
... ... ... ... ... ... ... ...
5010 2022-08-26 0.006298 -0.003704 0.002881 0.003733 0.020563     0.005954

原始R代码

df = read.csv('C:/Users/VaR and CVaR/returns')

ss = data.frame(`Month/Year`=unique(format(as.Date(df$Date),"%b/%Y")),
                `Monthly Expected Shortfall`=(tapply(df$portfolio,sub("-..$","",df$Date),FUN=PerformanceAnalytics::ETL)),
                check.names=F,row.names=NULL)

错误原因

  1. 简单收益率的极端值问题:pct_change()计算的是算术简单收益率,当某只股票单日出现极端波动(比如因拆股、退市风险等导致的异常数据),会使组合收益率出现超出合理范围的值,触发PerformanceAnalytics对ETL计算可靠性的校验警告。
  2. ETL计算逻辑限制:ETL(预期尾部损失)基于尾部收益率的平均值,若样本中存在极端异常值,会拉低尾部均值的计算结果,导致风险值超过100%,系统判定结果不可靠。

解决方法

改用**对数收益率(连续复利收益率)**是最合理的方案,其特性避免了极端值问题,且更适合金融时间序列的风险计算。

1. 修改Python代码计算对数收益率

import pandas as pd
import numpy as np
import datetime as dt
from pandas_datareader import data as pdr

def getData(stocks, start, end):
    stockData = pdr.get_data_yahoo(stocks, start=start, end=end)
    stockData = stockData['Close']
    # 计算对数收益率:ln(今日收盘价/昨日收盘价)
    returns_log = np.log(stockData / stockData.shift(1))
    return returns_log

stockList = ['IOC', 'RELIANCE', 'BPCL', 'HINDPETRO', 'EXIDEIND']
stocks = [stock+'.NS' for stock in stockList]
endDate = dt.datetime.now()
startDate = endDate - dt.timedelta(days=8000)

returns = getData(stocks, start=startDate, end=endDate)
returns = returns.dropna()
weights = np.array([1/len(stocks) for n in stocks])
# 组合对数收益率为各股票对数收益率的加权平均
returns['portfolio'] = returns.dot(weights)
returns.reset_index(inplace=True)
# 导出CSV,确保日期格式正确
returns.to_csv('returns_log.csv', index=False)

2. 调整R代码适配对数收益率数据

library(PerformanceAnalytics)

# 读取对数收益率数据,转换日期格式
df = read.csv('returns_log.csv')
df$Date = as.Date(df$Date)

# 按月度分组计算ETL,明确指定置信水平(默认95%,可按需调整)
monthly_etl = tapply(df$portfolio,
                     format(df$Date, "%Y-%m"),
                     FUN = function(x) ETL(x, p = 0.95))

# 整理结果为易读的数据框
ss = data.frame(
  `Month/Year` = format(as.Date(paste0(names(monthly_etl), "-01")), "%b/%Y"),
  `Monthly Expected Shortfall` = unname(monthly_etl),
  check.names = FALSE,
  row.names = NULL
)

print(ss)

额外建议

  • 若必须使用简单收益率,需先过滤异常值(例如删除单日收益率绝对值超过50%的记录),但这种方式可能丢失真实的尾部风险信息,不推荐。
  • 检查原始数据中是否存在拆股、分红等事件导致的价格异常,可使用pandas_datareader的调整后收盘价(Adj Close)计算收益率,避免因除权除息导致的虚假波动。

内容的提问来源于stack exchange,提问作者Starlord22

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 05:48:19