本文由CDA作者库成员Harry Zhu翻译,Harry Zhu擅长用Python和R进行数据建模、定量研究,目前就职于量子金服(Quantum Financial Service)
请注意,本文不应该被作为投资建议。本文数据是基于之前观察到的收益来模拟的,和历史上的数据并不太一致。这些技术可以帮助了解如何更好地分配一个投资组合。它不应该被用作唯一的投资决策,如果你正在寻找的建议应该找到一个合格的专业机构。
第一部分
数字特征计算
当看到三个政府 ETF 债券(TLT、IEF、SHY)调整后的股息回报率,我注意到中间到期债券(IEF)风险收益情况比长期债券(TLT)更好。我以表格形式显示结果。在本文中,我们将重新分析和图形化展示我们的结果:}
计算年化收益、标准差和夏普率。
# 获取短中期和长期政府债券的收益率序列
from = “2001-01-01″
to = “2011-12-16″
tlt = importSeries(“tlt”,from,to)
shy = importSeries(“shy”,from,to)
ief = importSeries(“ief”,from,to)
merged = merge(tlt,shy)
merged = merge(merged,ief)
vars = c(“tlt.Return”,“shy.Return”,“ief.Return”)
# 计算年回报率
t =
table.AnnualizedReturns(merged[,vars],Rf=mean(merged[,“shy.Return”],na.rm=TRUE))
t
结果如下:
标的 | 年化收益率 | 年化波动率 | 年化夏普率 (Rf=2.81%) | |
---|---|---|---|---|
tlt.Return | 0.0772 | 0.0283 | 0.0645 | |
shy.Return | 0.1404 | 0.0173 | 0.0740 | |
ief.Return | 0.3378 | -0.0086 |
0.4729 |
结论:
总资产组合中有效边界的蓝线表示其优于杠铃策略。对于每个风险水平,预期回报都是更高的。从图表上看,这表明添加 IEF 到组合将优化组合。进一步,我们看到杠铃策略回报的逼近最大值,用三个标的组合的组合策略比之前的风险少了一半。
相关代码
第二部分
在前面的文章中,我们构建了一个投资组合的有效边界的债券,下一步,我们要找到超级有效的(或市场)的投资组合。如果您有不熟悉的概念,第二部分可以在维基百科上参考一些资料。
无风险利率假定
如果你不愿意看维基百科,我也会解释相关概念的。如果你有一个保底回报率(无风险利率),那么资产位于图表的y轴。在边界的切点处画一条切线,切点代表着非常有效的投资组合。你可以混合持有一定权重的组合标的和无风险资产,实现比边界曲线更好的风险回报比。
明白了吗?非常棒!
所以我们需要找到线和切点。首先,让我们假定一个无风险利率。有些人会使用3个月的国债收益率。为了和数据匹配,我们需要将它处理成一年期的。我的银行给我一个2%的年保底收益率,所以我将用2%。
多项式拟合
我们如何找到切点?当我们有两个标的时,我们知道我们有一个二阶多项式。当我们有三个标的时有一些存在缺陷的面(非凸时求极值较困难),在这种情况下我们停止投资 SHY,转向投资 TLT。我们可以拟合高阶多项式,但我们不能确保我们有一个凹面。或者我可以说,我们不能保证我们的切点总是高于边值。同样地,我们也可以想象一下二次的情形或许有切点存在负值。
作为一个例子,这里虽然六阶多项式的拟合符合缺陷,但我们的切线点不是有用的。
只有一个实根,其余的都是虚根,我们需要另一种方法。
我们可以为第一部分里的边值拟合一个多项式;此时在持仓组合中只有 SHY 和 IEF。虽然这样也行得通,但是这不太通用。我想找到一个可以不管是什么边值形状都适用的通用解决方案。下个部分,我们会继续讨论这个问题。
第三部分
上一节,我们讨论了用拟合曲线寻找有效边值来建立投资组合所存在的问题。由于边值存在的缺陷,我们不能保证你和曲线在投资组合的解空间内是凹的。我们需要其他方法来解决这个问题。
放松约束
本文所用方法是在无风险利率和每个边值之间都画一条线来计算这条线和边值的差值是多少。资本市场线应该是不超过所有边值的。
CMLi <= EFi
我们所找到的这个边值的尺度意味着我们也许不能找到准确地市场投资组合。为避开这一点,我放松了上述约束:
Portfolioj that max Count(CMLi < EFi)
我整理以下R函数。注意,我已经转向使用标准差作为风险度量尺度,这是更传统的选择。
marketPortfolio = function(merged,rf,returnNames, weightNames,graph=FALSE){
# 为投资组合创建空数据框以初始化
weights = data.frame(t(rep(NA,length(weightNames))))
colnames(weights) = weightNames
weights = weights[–1,]
# 计算年化收益
t = table.AnnualizedReturns(merged[,returnNames])
# 优化范围
maxRet = max(t[‘Annualized Return’,]) – .005
minRet = min(t[‘Annualized Return’,]) + .005
#portfolio.optim 没有 NA 值,进行过滤
m2 = removeNA(merged[,returnNames])
er = NULL
eStd = NULL
# 在每个收益水平上循环搜索最优组合
# portfolio.optim 是日收益,做出相应调整
for (i inseq(minRet,maxRet,length.out=500)){
pm = 1+i
pm = log(pm)/255
opt = portfolio.optim(m2,pm=pm)
er = c(er,exp(pm*255)–1)
eStd = c(eStd,opt$ps*sqrt(255))
w = t(opt$pw)
colnames(w) = weightNames
weights = rbind(weights,w)
}
solution = weights
solution$er = er
solution$eStd = eStd
#找到最小 Std 和最大 Er 的下标
minIdx = which(solution$eStd == min(solution$eStd))
maxIdx = which(solution$er == max(solution$er))
# 获取结果子集
subset = solution[minIdx:maxIdx,c(“er”,“eStd”)]
subset$nAbove = NA
#对于子集中的每一个值, 计算点的总数,在下面的点和RF资产之间画线
for (i in seq(1,maxIdx–minIdx+1)){
toFit = data.frame(er=rf,eStd=0)
toFit = rbind(toFit,subset[i,c(“er”,“eStd”)])
fit = lm(toFit$er ~ toFit$eStd)
poly = polynomial(coef = fit$coefficients)
toPred = subset
colnames(toPred) = c(“actEr”,“eStd”)
toPred$er = predict(poly,toPred[,“eStd”])
toPred$diff = toPred$er – toPred$actEr
subset[i,“nAbove”] = nrow(toPred[which(toPred$diff > 0),])
}
# 得到切点
# 线以下是最大化
max = max(subset$nAbove)
er = subset[which(subset$nAbove == max),“er”]
eStd = subset[which(subset$nAbove == max),“eStd”]
# 市场投资组合的下标
idx = which(solution$er == er & solution$eStd == eStd)
# 画线
if (graph){
maxStd = max(solution$eStd) + .02
maxRetg = max(solution$er) + .02
plot(solution$eStd,
solution$er,
xlim=c(0,maxStd),
ylim=c(0,maxRetg),
ylab=“Expected Yearly Return”,
xlab=“Expected Yearly Std Dev”,
main=“Efficient Frontier”,
col=“red”,
type=“l”,
lwd=2)
abline(v=c(0), col=“black”, lty=“dotted”)
abline(h=c(0), col =“black”, lty=“dotted”)
toFit = data.frame(er=rf,eStd=0)
toFit = rbind(toFit,solution[idx,c(“er”,“eStd”)])
fit = lm(toFit$er ~ toFit$eStd)
abline(coef=fit$coefficients,col=“blue”,lwd=2)
}
# 返回投资组合权重、eStd 和 eR
out = solution[idx,]
return (out)
}
例子
让我们使用埃克森美孚(XOM),IBM(IBM),中期政府债券ETF(IEF)这个组合做测试。这里假定你有importSeries()函数的定义。
require(polynom)
require(fImport)
require(PerformanceAnalytics)
require(tseries)
require(stats)
from = “2003-01-01″
to = “2011-12-16″
xom = importSeries(“xom”,from,to)
ibm = importSeries(“ibm”,from,to)
ief = importSeries(“ief”,from,to)
merged = merge(xom,ibm)
merged = merge(merged,ief)
vars = c(“xom.Return”,“ibm.Return”,“ief.Return”)
vars2 = c(“xom”,“ibm”,“ief”)
mp = marketPortfolio(merged,.02,vars,vars2,graph=TRUE)
mp
日志的输出是:
xom | ibm | ief | er | eStd |
---|---|---|---|---|
0.09395 | 0.1378 | 0.7682 | 0.07762 |
0.05996 |
第四部分
这节将对投资组合优化系列做一个总结,我们将基于组合优化和测试结果对CAPM市场投资组合构建一个交易策略。
值得重申的是:
我所说不应该被当做投资建议。这些结果是基于之前观察到的收益并且是一些模拟值。这些技术可以帮助了解如何更好地分配一个投资组合。它不应该被当作是唯一的投资决策。如果你正在寻找的建议,还是找一个合格的专家比较好。
在马科维茨的工作的基础上,特雷诺,夏普等人开发了资本资产定价模型(CAPM)。在1990年,他们因为这项工作与马科维茨共同获得了诺贝尔奖。CAPM是一个一般均衡模型。模型假定市场价格反映了所有可获得的信息并且反映一个标的的“公平”价值。在此基础上,市场投资组合可以证明是市值加权组合。市值(或市值)被定义为股价乘以流通股的数量,也就是公司的股本总额。公司的权重是该公司市值除以所有证券的总市值。
资本加权指标和指数基金已成为标准。标准普尔是大多数人考虑的标准“市场投资组合”。我们将参考一个市值加权策略对我们的投资组合优化策略进行测试。
现在的CAPM还存在诸多漏洞,有很多方法都能发现这些问题。一种方法是说现在的价格不是公允价值,而是将均值当作公允价值。在这种情况下,当价格高于公允价值,市值加权组合将对过定价过高的证券资产给予过大的权重。当它用均值取代后, 投资组合的表现将由于权重超标而变差。
这个理论是著名的罗伯特•阿诺特提出的。我强烈推荐这本书,《基本面指数:一种更好的投资方式》。他认为,任何打破用价格打破相关性的投资组合策略随着时间的推移都将跑赢资本化指数。他在书中提到他创造了一个新的指数,他简单地假定每个标的都是等权重的(标准普尔发布了这个指数)。正因为如此,我们还将在标的同等权重条件下测试我们的策略。
组合优化策略
这是我们的投资组合优化策略:
1.每个季度初,用上一季度收益计算市场投资组合。
2.对当前季度使用当前组合。
3.下个季度的开始,循环回到第一步
4.在我们的投资组合中至少需要3个股票。
5.没有做空。
6.用2%作为无风险利率。
7.每次分析的第一个季度如果优化失败就使用同等权重的投资组合。
当价格走势是按季度选取的这种策略往往会跑赢大盘。比如如果上个季度的收益和波动性可以准确预测本季度的值的情况就是这样。此外,我们也不考虑交易成本。而且,2%的无风险利率是静态的,严格的说,我们应该在每个季度开始时使用3个月国债的利率。这就是为什么这只是一个例子,我们假定了很多美好的假设。
首先,我们需要修改我们以前创建的marketPortfolio()函数。你可以在这里找到它。新函数:
marketPortfolio = function(merged, rf, returnNames, weightNames,graph=FALSE, points=500, maxWeight=.334, Debug=FALSE)
改进之处
我们的改进之处:
1.如果我们需要可以添加一个调试选项打印输出
2.增加容错功能。有时直接得到一个可行解是不可能的,这个函数需要相应的检测并且处理错误。
3.资本的最大回报我们限定在50%以下,这个值太大会导致其他奇怪的行为。
4.同样,把最小收益的下界定在.005%。
5.如果最大收益是< 0,那么简单地找到最小方差投资组合。
6.添加一个maxWeight选项,让我们限制每个证券标的的权重。
我们考虑的股票池现在是28个道琼斯成分股(因为某些原因雅虎金融值提供了28只而不是30只)。我预先下载并存储这些收益值为一个 .rda 文件。你可以在这里得到它。我计算每个股票的初始市值权重并存储为 .csv 文件。你可以在这里得到它。
我们的年回报率表:
Result | CapWeight Portfolio | EqualWeight Portfolio | Portfolio Optimization |
---|---|---|---|
Annualized Return | -0.0393 | 0.0128 | 0.0069 |
Annualized Std Dev | 0.2530 | 0.2242 | 0.1785 |
Annualized Sharpe (Rf=0%) | -0.1554 | 0.0570 |
0.0387 |
本文链接:https://segmentfault.com/a/1190000004430109?from=timeline&isappinstalled=1
数据分析咨询请扫描二维码