最近,我在研究投资组合优化的问题,主要针对的是股票持仓的组合优化,咱们会在这个分析过程当中发现一些有意思的现象,并一步一步优化、检验咱们的风控模型。本文将有四个部分分别阐述具体步骤。ios
在第一部分(原文)中,我将解释什么是杠铃策略,并初步创建风控模型,比较持仓策略和风险收益的关系。git
在第二部分(原文)中,我将解释什么是无风险利率假定,讨论多项式拟合的情形。github
在第三部分(原文)中,我将解释如何经过放松约束最优化求解过程以免非凹的情形,并作了实例演示。segmentfault
在第四部分(原文)中,我将对比大盘策略、等权策略以及以前的优化策略之间的优劣。微信
请注意,本文不该该被做为投资建议。本文数据是基于以前观察到的收益来模拟的,和历史上的数据并不太一致。这些技术能够帮助了解如何更好地分配一个投资组合。它不该该被用做惟一的投资决策,若是你正在寻找的建议应该找到一个合格的专业机构。app
当看到三个政府 ETF 债券(TLT、IEF、SHY)调整后的股息回报率,我注意到中间到期债券(IEF)风险收益状况比长期债券(TLT)更好。我以表格形式显示结果。在本文中,咱们将从新分析和图形化展现咱们的结果:函数
首先,用以下函数来获取ETF的回报序列测试
pacman::p_load( fImport, PerformanceAnalytics, stringb, tidyverse) # 将股票数据加载到一个时间序列对象的函数 importSeries = function(symbol,from,to) { # 从雅虎读取金融数据 input = yahooSeries(symbol, from = from, to = to) # 列名 adjClose = symbol %.% ".Adj.Close" inputReturn = symbol %.% ".Return" CReturn = symbol %.% ".CReturn" # 计算收益率并生成时间序列 input.Return = returns(input[,adjClose]) colnames(input.Return)[1] = inputReturn input = merge(input,input.Return) # 计算累积收益率并生成时间序列 input.first = input[,adjClose][1] input.CReturn = fapply(input[,adjClose], FUN = function(x) log(x) - log(input.first)) colnames(input.CReturn)[1] = CReturn input = merge(input,input.CReturn) # 删掉一些没用的东西来释放内存,若是你不知道就不用删除 rm(input.first, input.Return, input.CReturn, adjClose, inputReturn, CReturn) # 返回时间序列 return(input) }
计算年化收益、标准差和夏普率。优化
# 获取短中期和长期政府债券的收益率序列 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) %>% merge(ief) vars = c("tlt.Return", "shy.Return", "ief.Return") # 计算年回报率 (t = table.AnnualizedReturns(merged[,vars], Rf = mean(merged[,"shy.Return"], na.rm=TRUE)))
结果以下:ui
标的 | 年化收益率 | 年化波动率 | 年化夏普率 (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 |
若是你常常看娱乐投资电视台,你最终会听到"杠铃策略"这个术语。这是指一个极端的投资组合分配方案。全部的权重都是极端状况,你能够想象这是一个杠铃。在政府债券的投资组合,这将意味着购买期限长或短而不是持有中间。那么什么样的风险收益状况下你会采用这个策略?
首先,咱们将风险定义为投资组合的方差。有各类各样的理由不使用方差,但它是从最古老的50年代开始这种类型的分析都是全新的。咱们将定义收益为预期收益。在上面的表中,年回报率表示持有资产的预期收益为1年,标准差的平方表示风险。
假设投资组合只包括持有长期和短时间债券,咱们便须要计算投资组合的预期收益和风险。收益的计算是很容易的,这是两种持仓的加权平均收益,权重就是每一个资产的投入资本百分比。
$$
R_p=W_{TLT}R_{TLT}+W_{SHY}R_{SHY}
$$
$$
s.t. W_{TLT}+W_{SHY}=1
$$
显然这两种资产具备相关性(在马科维茨于1952年的博士论文发表以前,投资经理不了解相关性而且默认假设为1 -马科维茨所以得到了诺贝尔奖)。假设回报是正态分布的,那么投资组合方差将是:
$$
V_p=W_{TLT}^2σ_{TLT}^2+W_{SHY}^2σ_{SHY}^2+W_{TLT}W_{SHY}σ_{TLT}σ_{SHY}Corrleft(TLT,SHYright)
$$
$$
s.t. W_{TLT}+W_{SHY}=1
$$
咱们能够根据这两个部分的知识改变持仓权重并为咱们的杠铃策略创建风险收益模型。
# 检查相关性 corr = cor(merged[,vars], use = "complete.obs") c = corr["tlt.Return","shy.Return"] # 假设一个杠铃策略是持有长期和短时间资产 # 定义风险、收益 ws = NULL wt = NULL mu = NULL sigma = NULL # 50个观察 n=50 # 遍历杠铃策略的权重 rTLT = t["Annualized Return","tlt.Return"] rSHY = t["Annualized Return","shy.Return"] sTLT = t["Annualized Std Dev","tlt.Return"] sSHY = t["Annualized Std Dev","shy.Return"] for (i in 0:n){ wsi = i/n; wti = 1-wsi; mui = wsi * rSHY + wti * rTLT sigmai = wsi*wsi*sSHY*sSHY + wti*wti*sTLT*sTLT + wsi*wti*sSHY*sTLT*c ws = c(ws,wsi) wt = c(wt,wti) mu = c(mu,mui) sigma = c(sigma,sigmai) } # 风险收益的数据集 rrProfile = data.frame(ws=ws,wt=wt,mu=mu,sigma=sigma)
注意,上面的方程是二次的。咱们能够配合咱们刚刚建立的点画出抛物线。注意,而习惯上把风险放在X轴上,而把拟合方差(风险)做为因变量放在Y轴。
# 为模型拟合一个二次函数 fit = lm(rrProfile$sigma ~ rrProfile$mu + I(rrProfile$mu^2))
接下来,咱们图上添加拟合线。
# 获得回归系数 coe = fit$coefficients # 获得每一个回归预测的风险值 muf = NULL sfit = NULL for (i in seq(0,.08,by=.001)){ muf = c(muf,i) s = coe[1] + coe[2]*i + coe[3]*i^2 sfit = c(sfit,s) } # 绘图 plot(rrProfile$sigma, rrProfile$mu, xlim=c(0,.022), ylim=c(0,.08), ylab="Expected Yearly Return", xlab="Expected Yearly Variance", main="Efficient Frontier for Government Bond Portfolios") # 画出预测边值 lines(sfit,muf,col="red")
tseries
包中的portfolio.optim
也许是一个更好的选择。咱们只须要输入预期收益率,它会直接返回出来最优组合权重。咱们能够在最低预期回报率(好比 100% 持有 SHY)到最高预期回报率(好比 100% 持有 TLT)之间修改输入的收益。注意,portfolio.optim
使用日回报率作计算,所以代码将不得不作一些处理。咱们假设一年255个交易日。
# 如今让咱们添加第三个标的。 # 除非咱们想作一个格点搜索,不然咱们须要对每一个级别的回报减小风险来优化投资组合。 # portfolio.optim 在时间序列中不能有 NA 值。 m2 = removeNA(merged[,vars]) wSHY = NULL wIEF = NULL wTLT = NULL er = NULL eStd = NULL # 在回报水平之间不断循环搜索找到最优的投资组合,包括最小值(rSHY)和最大值(rTLT) # portfolio.optim 使用日回报数据,所以咱们不得不作出相应的调整 for (i in seq((rSHY+.001),(rTLT-.001),length.out=100)){ pm = 1+i pm = log(pm)/255 opt = tseries::portfolio.optim(m2,pm=pm) er = c(er,exp(pm*255)-1) eStd = c(eStd,opt$ps*sqrt(255)) wTLT = c(wTLT,opt$pw[1]) wSHY = c(wSHY,opt$pw[2]) wIEF = c(wIEF,opt$pw[3]) } # 绘图 plot(rrProfile$sigma, rrProfile$mu, xlim=c(0,.022), ylim=c(0,.08), ylab="Expected Yearly Return", xlab="Expected Yearly Variance", main="Efficient Frontier for Government Bond Portfolios") # 画出预测边值 lines(sfit,muf,col="red") # 画出三个标的的有效边界。 lines(eStd^2,er,col="blue") legend(.014,0.015,c("Barbell Strategy","All Assets"), col=c("red","blue"), lty=c(1,1)) solution = data.frame(wTLT,wSHY,wIEF,er,eStd)
以下图:
总资产组合中有效边界的蓝线表示其优于杠铃策略。对于每一个风险水平,预期回报都是更高的。从图表上看,这代表添加 IEF 到组合将优化组合。进一步,咱们看到杠铃策略回报的逼近最大值,用三个标的组合的组合策略比以前的风险少了一半。
在前面的文章中,咱们构建了一个投资组合的有效边界的债券,下一步,咱们要找到超级有效的(或市场)的投资组合。若是您有不熟悉的概念,第二部分能够在维基百科上参考一些资料。
若是你不肯意看维基百科,我也会解释相关概念的。若是你有一个保底回报率(无风险利率),那么资产位于图表的y轴。在边界的切点处画一条切线,切点表明着很是有效的投资组合。你能够混合持有必定权重的组合标的和无风险资产,实现比边界曲线更好的风险回报比。
明白了吗?很是棒!
因此咱们须要找到线和切点。首先,让咱们假定一个无风险利率。有些人会使用3个月的国债收益率。为了和数据匹配,咱们须要将它处理成一年期的。个人银行给我一个2%的年保底收益率,因此我将用2%。
咱们如何找到切点?当咱们有两个标的时,咱们知道咱们有一个二阶多项式。当咱们有三个标的时有一些存在缺陷的面(非凸时求极值较困难),在这种状况下咱们中止投资 SHY,转向投资 TLT。咱们能够拟合高阶多项式,但咱们不能确保咱们有一个凹面。或者我能够说,咱们不能保证咱们的切点老是高于边值。一样地,咱们也能够想象一下二次的情形或许有切点存在负值。
做为一个例子,这里虽然六阶多项式的拟合符合缺陷,但咱们的切线点不是有用的。
只有一个实根,其他的都是虚根,咱们须要另外一种方法。
咱们能够为第一部分里的边值拟合一个多项式;此时在持仓组合中只有 SHY 和 IEF。虽然这样也行得通,可是这不太通用。我想找到一个能够不论是什么边值形状都适用的通用解决方案。下个部分,咱们会继续讨论这个问题。
上一节,咱们讨论了用拟合曲线寻找有效边值来创建投资组合所存在的问题。因为边值存在的缺陷,咱们不能保证你和曲线在投资组合的解空间内是凹的。咱们须要其余方法来解决这个问题。
本文所用方法是在无风险利率和每一个边值之间都画一条线来计算这条线和边值的差值是多少。资本市场线应该是不超过全部边值的。
$$CML_i<=EF_i$$
咱们所找到的这个边值的尺度意味着咱们也许不能找到准确地市场投资组合。为避开这一点,我放松了上述约束:
$$Portfolio_j\ that\ max\ Count(CML_i < EF_i)$$
我整理如下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 in seq(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()函数的定义。
pacman::p_load( polynom, fImport, PerformanceAnalytics, tseries, 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))
日志的输出是:
xom | ibm | ief | er | eStd |
---|---|---|---|---|
0.09395 | 0.1378 | 0.7682 | 0.07762 | 0.05996 |
建立的图:
这个投资组合优化的给了咱们一个发现更低边界例子。这是一个不正常的现象。
这就是为何咱们结果子集只包括部分边界的顶点(min(StdDev))和最大的回报。由于咱们发现边界最小收益到最大的收益,咱们保证序列是有序的,因此只考虑了上部边界。
一个更精确的方法是找到的区域包含市场组合的边值而后用网格搜索寻找最优投资组合。上节咱们讨论了在一个范围中拟合曲线的方法。若是有需求,我也能够用上面的方法再作一次。出于演示目的,我想咱们应该足够了。
这节将对投资组合优化系列作一个总结,咱们将基于组合优化和测试结果对CAPM市场投资组合构建一个交易策略。
值得重申的是:
我所说不该该被当作投资建议。这些结果是基于以前观察到的收益而且是一些模拟值。这些技术能够帮助了解如何更好地分配一个投资组合。它不该该被看成是惟一的投资决策。若是你正在寻找的建议,仍是找一个合格的专家比较好。
在马科维茨的工做的基础上,特雷诺,夏普等人开发了资本资产订价模型(CAPM)。在1990年,他们由于这项工做与马科维茨共同得到了诺贝尔奖。CAPM是一个通常均衡模型。模型假定市场价格反映了全部可得到的信息而且反映一个标的的"公平"价值。在此基础上,市场投资组合能够证实是市值加权组合。市值(或市值)被定义为股价乘以流通股的数量,也就是公司的股本总额。公司的权重是该公司市值除以全部证券的总市值。
资本加权指标和指数基金已成为标准。标准普尔是大多数人考虑的标准"市场投资组合"。咱们将参考一个市值加权策略对咱们的投资组合优化策略进行测试。
如今的CAPM还存在诸多漏洞,有不少方法都能发现这些问题。一种方法是说如今的价格不是公允价值,而是将均值看成公允价值。在这种状况下,当价格高于公允价值,市值加权组合将对过订价太高的证券资产给予过大的权重。当它用均值取代后, 投资组合的表现将因为权重超标而变差。
这个理论是著名的罗伯特•阿诺特提出的。我强烈推荐这本书,《基本面指数:一种更好的投资方式》。他认为,任何打破用价格打破相关性的投资组合策略随着时间的推移都将跑赢资本化指数。他在书中提到他创造了一个新的指数,他简单地假定每一个标的都是等权重的(标准普尔发布了这个指数)。正由于如此,咱们还将在标的同等权重条件下测试咱们的策略。
这是咱们的投资组合优化策略:
每一个季度初,用上一季度收益计算市场投资组合。
对当前季度使用当前组合。
下个季度的开始,循环回到第一步
在咱们的投资组合中至少须要3个股票。
没有作空。
用2%做为无风险利率。
每次分析的第一个季度若是优化失败就使用同等权重的投资组合。
当价格走势是按季度选取的这种策略每每会跑赢大盘。好比若是上个季度的收益和波动性能够准确预测本季度的值的状况就是这样。此外,咱们也不考虑交易成本。并且,2%的无风险利率是静态的,严格的说,咱们应该在每一个季度开始时使用3个月国债的利率。这就是为何这只是一个例子,咱们假定了不少美好的假设。
首先,咱们须要修改咱们之前建立的 marketPortfolio()函数。你能够在这里找到它。新函数:
marketPortfolio <- function(merged, rf, returnNames, weightNames, graph=FALSE, points=500, maxWeight=.334, Debug=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 # 设置回报上下限 # 在.005%到50%之间 maxRet = min(.5,maxRet) minRet = max(0.005,minRet) # 若是都是预期回报均为负数, # 那么只返回收益最大的投资组合 if (maxRet < 0){ minRet = maxRet points = 1 } # 调试打印 if (Debug){ print("Max Return") print(maxRet) print("Min Return") print(minRet) } # portfolio.optim 不能处理含有 NA 值的时间序列,这里过滤掉 NA 值 m2 = removeNA(merged[,returnNames]) er = NULL eStd = NULL # 在回报水平之间循环,寻找最优的投资组合 # portfolio.optim 使用日收益,所以咱们必须做出相应调整 ok = FALSE for (i in seq(minRet,maxRet,length.out=points)){ pm = 1+i pm = log(pm)/255 # 调试 if (Debug){ print("Finding Optimum for") print("ER") print(pm) print(exp(pm*255)-1) } # 最优化 各组权重 <= 最大权重 # tryCatch 捕获异常值 opt = tryCatch(portfolio.optim(m2, pm = pm, reshigh = rep(maxWeight,length(weightNames)), shorts = FALSE), error = function(err) return(NULL)) # 打印最优结果 if (Debug) print(opt) # 检查是否存在可行解 if (!is.null(opt)){ 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) # 更新变量 ok = (ok | TRUE) } else { print("ERROR IN THIS TRY") # 更新变量 ok = (ok | FALSE) } } # 若是在边界没有可行解,则返回NULL if (!ok){ return (NULL) } solution = weights solution$er = er solution$eStd = eStd # 找到最小标准差和最大平均收益点的索引值 minIdx = which(solution$eStd == min(solution$eStd)) maxIdx = which(solution$er == max(solution$er)) if (Debug){ print(minIdx) print(maxIdx) } # 切分结果 subset = solution[minIdx:maxIdx,c("er","eStd")] subset$nAbove = NA # 对每一个分片的值,计算在划线下面的点的数量 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"] # 若是找到多个组合,返回第一个 if (length(er) > 1){ er = er[1] eStd = eStd[1] } # 市场投资组合的索引 idx = which(solution$er == er & solution$eStd == eStd) if (Debug){ print("solution") print(er) print(eStd) print(solution[idx,]) } # 若是须要能够加一条基准线 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) }
咱们的改进之处:
若是咱们须要能够添加一个调试选项打印输出
增长容错功能。有时直接获得一个可行解是不可能的,这个函数须要相应的检测而且处理错误。
资本的最大回报咱们限定在50%如下,这个值太大会致使其余奇怪的行为。
一样,把最小收益的下界定在.005%。
若是最大收益是< 0,那么简单地找到最小方差投资组合。
添加一个maxWeight选项,让咱们限制每一个证券标的的权重。
咱们考虑的股票池如今是28个道琼斯成分股(由于某些缘由雅虎金融值提供了28只而不是30只)。我预先下载并存储这些收益值为一个 .rda 文件。你能够在这里获得它。我计算每一个股票的初始市值权重并存储为 .csv 文件。你能够在这里获得它。
# 读取已存的收益值 download.file("http://www.pazzula.com/blog/returns.rda","returns.rda") load("returns.rda") returns = results rm(results) stocks = colnames(returns) # 获取大盘权重 stockWgt = read.table("http://www.pazzula.com/blog/stocks.csv", header=TRUE,sep=",")[,"Weight"] # 计算大盘权重投资组合的回报 results = as.matrix(returns) %*% stockWgt colnames(results) = c("CapWeight Portfolio") results = as.timeSeries(results) # 计算等权重投资组合的回报 ret = t(as.matrix(rep(1/length(stocks),length(stocks)))) resEqual = as.matrix(returns) %*% t(ret) colnames(resEqual) = "EqualWeight Portfolio" # 汇总结果到一个时间序列对象中 results = cbind(results,resEqual) # 获取收益值的日期序列 dates = time(returns) dates = dates@Data # 从日期计算季度 qtrs = quarters(dates) qtrs = cbind(dates,qtrs) keep = NULL lastQtr = "n" #遍历日期和季度序列,只保留每一个季度第一天的日期 for (i in seq(1,nrow(qtrs))){ if (qtrs[i,2] == lastQtr){ if (i == nrow(qtrs)){ keep = c(keep,1) } else { keep = c(keep,0) } }else { keep = c(keep,1) } lastQtr = qtrs[i,2] } qtrs = cbind(qtrs,keep) # 获取每一个季度第一天的下标 indx = which(qtrs[,3] == 1) # 对每一个周期的第一个季度,使用等权策略 res = as.matrix(returns[indx[1]:(indx[2]-1),]) %*% t(ret) #对每一个周期基于上一季度的数据进行循环计算大盘组合的表现 for (i in seq(2,length(indx)-1)){ print("Running ") print(i) # 获得上季度股票回报的子集 subset = returns[indx[i-1]:(indx[i]-1),] s = start(subset) e = end(subset) print("Fitting for:") print(s) print(e) # 计算大盘投资组合 mp = marketPortfolio(subset,.02,stocks,stocks, graph=TRUE, points=500, Debug=FALSE) #若是优化失败,使用等权策略 if (is.null(mp)){ ret = t(as.matrix(rep(1/length(stocks),length(stocks)))) } else { ret = as.matrix(mp[,stocks]) } # 本季度的子集 subRes = returns[indx[i]:(indx[i+1]-1),] s = start(subRes) e = end(subRes) print("Calculating Returns for:") print(s) print(e) # 计算当前季度的大盘策略的收益并追加在收益序列后面 subRes = as.matrix(subRes) %*% t(ret) res = rbind(res,subRes) } # 循环计算时,序列的最后一天不计算收益 subRes = returns[nrow(returns),] subRes = as.matrix(subRes) %*% t(ret) res = rbind(res,subRes) # 添加组合优化策略 colnames(res) = "Portfolio Optimization" res = as.timeSeries(res) results = cbind(results,res) #计算年化收益统计特征 table.AnnualizedReturns(results) #计算并绘制相关性 png("mpCorr.png") chart.Correlation(results,histogram=TRUE,pch="+") dev.off(); ##计算并绘制累积收益 png("mpReturns.png") chart.CumReturns(results, main="Total Returns CapWeight vs PortOpt", legend.loc="topleft") dev.off()
咱们的年回报率表:
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 |
咱们的投资组合优化策略优于大盘权重策略,但跑输了等权重策略。若是你支持阿诺特的话就以为这没什么奇怪的了,这只是由于咱们没有打破价格的相关性罢了。
这是相关性的图表:
咱们已经建立了一个和大盘权重策略很是相关的策略,可是仍是不如等权策略。等权策略和大盘权重策略的关联度是很是有趣的。
这是收益绘制的时间序列:
有趣的是,能够看到在图中绿色的部分显示咱们的投资组合在2009年3月份的市场底部开始有一个快速反弹。这大大跑赢了大盘权重组合。
做为分享主义者(sharism),本人全部互联网发布的图文均听从CC版权,转载请保留做者信息并注明做者 Harry Zhu 的 FinanceR专栏:https://segmentfault.com/blog...,若是涉及源代码请注明GitHub地址:https://github.com/harryprince。微信号: harryzhustudio商业使用请联系做者。