<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>投资组合 on 格物堂</title><link>https://gewutang.cn/tags/%E6%8A%95%E8%B5%84%E7%BB%84%E5%90%88/</link><description>Recent content in 投资组合 on 格物堂</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Sat, 08 Mar 2014 00:00:00 +0000</lastBuildDate><atom:link href="https://gewutang.cn/tags/%E6%8A%95%E8%B5%84%E7%BB%84%E5%90%88/index.xml" rel="self" type="application/rss+xml"/><item><title>善用向量化提升R语言的计算性能</title><link>https://gewutang.cn/2014/03/08/use-vectorisation-to-improve-computational-performance/</link><pubDate>Sat, 08 Mar 2014 00:00:00 +0000</pubDate><guid>https://gewutang.cn/2014/03/08/use-vectorisation-to-improve-computational-performance/</guid><description>&lt;p&gt;用R 语言用的久了，渐渐发现 R 语言的世界里有很多黑魔法。其中一个黑魔法是向量化运算。向量化运算可以说是R语言区别于其它语言的特质之一，善用向量化运算不仅能有效降低代码的冗余度，还能显著提升代码的运算效率。遗憾的是，很多接触过其它编程语言的人未能掌握这一特性使得他们经常诟病R语言运算效率低下。这里给看官们举个例子来体会下。&lt;/p&gt;&#10;&lt;p&gt;想必大家都遇到过替换向量元素的情况。比如，我们有下面这样一个数据框，维度是1200000×2：&lt;/p&gt;&#10;&lt;pre&gt;&lt;code class="language-r"&gt;n = 1200000&#10;df = data.frame(values = rnorm(n), &#10; ID = rep(LETTERS[1:3], each = n/3),&#10; stringsAsFactors = FALSE)&#10;head(df)&#10;&lt;/code&gt;&lt;/pre&gt;&#10;&lt;pre&gt;&lt;code&gt;## values ID&#10;## 1 1.8442348 A&#10;## 2 -0.5089413 A&#10;## 3 -0.8743655 A&#10;## 4 0.7824285 A&#10;## 5 -1.2115204 A&#10;## 6 -1.4914611 A&#10;&lt;/code&gt;&lt;/pre&gt;&#10;&lt;pre&gt;&lt;code class="language-r"&gt;tail(df)&#10;&lt;/code&gt;&lt;/pre&gt;&#10;&lt;pre&gt;&lt;code&gt;## values ID&#10;## 1199995 1.753660616 C&#10;## 1199996 -0.007581797 C&#10;## 1199997 -0.635219385 C&#10;## 1199998 -1.437210958 C&#10;## 1199999 -0.101465502 C&#10;## 1200000 0.152135010 C&#10;&lt;/code&gt;&lt;/pre&gt;&#10;&lt;p&gt;假设要把&lt;em&gt;df$ID&lt;/em&gt;中的A替换为“A公司”，B和C类似。怎么做呢？一个常用的解决方案是利用if&amp;hellip;for写循环。代码是这样的：&lt;/p&gt;&#10;&lt;pre&gt;&lt;code class="language-r"&gt;translator_if_for = function(input_vector) {&#10; output_vector = input_vector&#10; for(index in seq_along(input_vector)) {&#10; if(input_vector[index] == 'A') {&#10; output_vector[index] = 'A公司'&#10; } else if(input_vector[index] == 'B') {&#10; output_vector[index] = 'B公司'&#10; } else if(input_vector[index] == 'C') {&#10; output_vector[index] = 'C公司'&#10; } &#10; } &#10; return(output_vector)&#10;}&#10;&lt;/code&gt;&lt;/pre&gt;&#10;&lt;pre&gt;&lt;code class="language-r"&gt;dum_if_for = translator_if_for(df$ID)&#10;head(dum_if_for)&#10;tail(dum_if_for)&#10;&lt;/code&gt;&lt;/pre&gt;&#10;&lt;p&gt;用&lt;code&gt;rbenchmark&lt;/code&gt;包测试下上述操作需要的时间：&lt;/p&gt;&#10;&lt;pre&gt;&lt;code class="language-r"&gt;library(rbenchmark)&#10;res = benchmark(if_for_solution = translator_if_for(df$ID),&#10; columns= c(&amp;quot;test&amp;quot;,&amp;quot;replications&amp;quot;,&amp;quot;elapsed&amp;quot;,&amp;quot;relative&amp;quot;,&amp;quot;user.self&amp;quot;,&amp;quot;sys.self&amp;quot;),&#10; replications = 10)&#10; res&#10;&lt;/code&gt;&lt;/pre&gt;&#10;&lt;pre&gt;&lt;code&gt; test replications elapsed relative user.self sys.self&#10;1 if_for_solution 10 69.63 1 69.48 0&#10;&lt;/code&gt;&lt;/pre&gt;&#10;&lt;p&gt;上面这段代码的缺点是时间久，而且看起来一点都不简洁。那么，是否有更好点的解决方案呢？Absolutely！而且不止一种。比较常用的替代方案是借助 &lt;code&gt;apply&lt;/code&gt;族函数（&lt;code&gt;apply&lt;/code&gt;族函数是R语言中避免编写显式循环的一个trick）。在用&lt;code&gt;apply&lt;/code&gt;族函数之前，得先构造一个辅助函数。构造函数之前不妨先想想我们的操作逻辑，我们想做的事情是这样的，对于&lt;code&gt;df$ID&lt;/code&gt;中的某个元素，如果元素是A其就变成A公司，B变成B公司，C变成C公司。这其实可以用&lt;code&gt;switch&lt;/code&gt;来实现。对所有元素进行依次进行判断就可以了，针对所有元素进行判断这个过程可以交给&lt;code&gt;sapply&lt;/code&gt;函数来完成，于是有下面的代码：&lt;/p&gt;&#10;&lt;pre&gt;&lt;code class="language-r"&gt; translator_function = function(element) {&#10; switch(element,&#10; A = 'A公司',&#10; B = 'B公司',&#10; C = 'C公司')&#10; }&#10; &#10; dum_switch_sapply = sapply(df$ID, translator_function)&#10; head(dum_switch_sapply)&#10; tail(dum_switch_sapply)&#10;&lt;/code&gt;&lt;/pre&gt;&#10;&lt;p&gt;代码看起来更简洁，看看操作结果是否一致：&lt;/p&gt;&#10;&lt;pre&gt;&lt;code class="language-r"&gt;all.equal(dum_if_for, dum_switch_sapply, check.attributes = FALSE)&#10;&lt;/code&gt;&lt;/pre&gt;&#10;&lt;p&gt;看看这段代码需要的时间：&lt;/p&gt;&#10;&lt;pre&gt;&lt;code class="language-r"&gt; res = benchmark(function_solution = sapply(df$ID, translator_function),&#10; columns= c(&amp;quot;test&amp;quot;,&amp;quot;replications&amp;quot;,&amp;quot;elapsed&amp;quot;,&amp;quot;relative&amp;quot;,&amp;quot;user.self&amp;quot;,&amp;quot;sys.self&amp;quot;),&#10; replications = 10)&#10; res&#10;&lt;/code&gt;&lt;/pre&gt;&#10;&lt;pre&gt;&lt;code&gt; test replications elapsed relative user.self sys.self&#10;1 function_solution 10 64.58 1 64.1 0.24&#10;&lt;/code&gt;&lt;/pre&gt;&#10;&lt;p&gt;可以看到时间也有所减少。But..这就是结束吗？操作效率是有提升，但提升的一点都不明显啊！？别急，我们还有杀手锏——向量化运算！先上代码，再慢慢解释逻辑。&lt;/p&gt;&#10;&lt;pre&gt;&lt;code class="language-r"&gt; translator_vector = c(A = 'A公司',&#10; B = 'B公司',&#10; C = 'C公司')&#10; dum_vectorized = translator_vector[df$ID]&#10; head(dum_vectorized)&#10; tail(dum_vectorized)&#10;&lt;/code&gt;&lt;/pre&gt;&#10;&lt;p&gt;看看结果是否一样：&lt;/p&gt;&#10;&lt;pre&gt;&lt;code class="language-r"&gt;all.equal(dum_vectorized, dum_switch_sapply, check.attributes = FALSE)&#10;&lt;/code&gt;&lt;/pre&gt;&#10;&lt;p&gt;完全一样。但是看起来无疑简洁了很多。那么，从耗时角度讲呢？我们看一看：&lt;/p&gt;&#10;&lt;pre&gt;&lt;code class="language-r"&gt; res = benchmark(vector_solution = translator_vector[df$ID],&#10; columns= c(&amp;quot;test&amp;quot;,&amp;quot;replications&amp;quot;,&amp;quot;elapsed&amp;quot;,&amp;quot;relative&amp;quot;,&amp;quot;user.self&amp;quot;,&amp;quot;sys.self&amp;quot;),&#10; replications = 10)&#10; res&#10;&lt;/code&gt;&lt;/pre&gt;&#10;&lt;pre&gt;&lt;code&gt; test replications elapsed relative user.self sys.self&#10;1 vector_solution 10 1.13 1 1.06 0.03&#10;&lt;/code&gt;&lt;/pre&gt;&#10;&lt;p&gt;时间大大减少了。把三种方案的耗时放到一起看一看：&lt;/p&gt;&#10;&lt;pre&gt;&lt;code class="language-r"&gt; res = benchmark(if_for_solution = translator_if_for(df$ID),&#10; function_solution = sapply(df$ID, translator_function),&#10; vector_solution = translator_vector[df$ID],&#10; columns= c(&amp;quot;test&amp;quot;,&amp;quot;replications&amp;quot;,&amp;quot;elapsed&amp;quot;,&amp;quot;relative&amp;quot;,&amp;quot;user.self&amp;quot;,&amp;quot;sys.self&amp;quot;),&#10; replications = 10)&#10; res&#10;&lt;/code&gt;&lt;/pre&gt;&#10;&lt;pre&gt;&lt;code&gt; test replications elapsed relative user.self sys.self&#10;2 function_solution 10 71.51 87.207 70.95 0.42&#10;1 if_for_solution 10 69.89 85.232 69.34 0.25&#10;3 vector_solution 10 0.82 1.000 0.75 0.08&#10;&lt;/code&gt;&lt;/pre&gt;&#10;&lt;p&gt;向量化运算无论在代码简洁度还是在效率上完爆前两种方案。缺点是操作逻辑十分巧妙，可能不是那么容易理解。这里简单解释一下：&lt;/p&gt;&#10;&lt;p&gt;上面的&lt;code&gt;translator_vector&lt;/code&gt;是个有名字的向量：&lt;/p&gt;&#10;&lt;pre&gt;&lt;code class="language-r"&gt; translator_vector = c(A = 'A公司',&#10; B = 'B公司',&#10; C = 'C公司')&#10; translator_vector&#10;&#10;names(translator_vector)&#10;&lt;/code&gt;&lt;/pre&gt;&#10;&lt;p&gt;因此，可以通过元素的名字对元素进行索引。例如：&lt;/p&gt;&#10;&lt;pre&gt;&lt;code class="language-r"&gt;translator_vector[&amp;quot;A&amp;quot;]&#10;translator_vector[&amp;quot;B&amp;quot;]&#10;translator_vector[c(&amp;quot;A&amp;quot;,&amp;quot;B&amp;quot;)]&#10;&lt;/code&gt;&lt;/pre&gt;&#10;&lt;p&gt;而&lt;code&gt;df$ID&lt;/code&gt;刚好是一个由&lt;code&gt;translator_vector&lt;/code&gt;向量的元素名字组成的向量。因此,代码：&lt;/p&gt;&#10;&lt;pre&gt;&lt;code class="language-r"&gt;translator_vector[df$ID]&#10;&lt;/code&gt;&lt;/pre&gt;&#10;&lt;p&gt;就相当于一个索引操作，其结果得到的恰好是&lt;code&gt;translator_vector&lt;/code&gt;中对应的元素，这也就相当于对向量元素进行了替换。&lt;/p&gt;&#10;</description></item><item><title>说说风险均摊投资组合的构建方法</title><link>https://gewutang.cn/2014/01/14/construct-pairty-risk-portfolio/</link><pubDate>Tue, 14 Jan 2014 00:00:00 +0000</pubDate><guid>https://gewutang.cn/2014/01/14/construct-pairty-risk-portfolio/</guid><description>&lt;h3 id="引子"&gt;&lt;a href="#%e5%bc%95%e5%ad%90" class="header-anchor"&gt;&lt;/a&gt;引子&#10;&lt;/h3&gt;&lt;p&gt;构建股票投资组合时，在筛选出备选股票之后，要解决的另外一个问题是确定备选股票在投资组合中的权重。我们可以用一个向量来表示：&lt;/p&gt;&#10;&lt;p&gt;$$&#10;w=\left(w_1,w_2,&amp;hellip;,w_n \right)&#10;$$&lt;/p&gt;&#10;&lt;p&gt;其中， &lt;code&gt;\(\sum_{i=1}^nw_{i}=1,i=1,2,...,n\)&lt;/code&gt; 。&lt;/p&gt;&#10;&lt;p&gt;显然，权重的可选集是个无限集合。如果想选取其中一个作为最优权重，那得先确定一个判断最优与否的方法。耳熟能详的方法自然是收益-风险法，即如果一个权重能够使投资组合的收益最大、风险最小，那么，它就是好的。如果用标准差来刻画风险的话，就有下面的评价函数:&lt;/p&gt;&#10;&lt;p&gt;$$&#10;U(R,\sigma)&#10;$$&lt;/p&gt;&#10;&lt;p&gt;其中， &lt;code&gt;\(\frac{\partial U\left(\cdot\right)}{\partial R}\gt 0\)&lt;/code&gt; ， &lt;code&gt;\(\frac{\partial U\left(\cdot\right)}{\partial\sigma}\lt 0\)&lt;/code&gt;。&lt;/p&gt;&#10;&lt;p&gt;&lt;code&gt;\(U\left(\cdot\right)\)&lt;/code&gt; 最简单的形式如下：&lt;/p&gt;&#10;&lt;p&gt;$$&#10;U_p = aR_{p} + b \sigma _{p}&#10;$$&lt;/p&gt;&#10;&lt;p&gt;其中， &lt;code&gt;\(a\gt 0\)&lt;/code&gt; ， &lt;code&gt;\(b\le 0\)&lt;/code&gt; ； &lt;code&gt;\(R_{p}\)&lt;/code&gt; 是组合的收益率， &lt;code&gt;\(\sigma_{p}\)&lt;/code&gt; 是组合的标准差。显然：&lt;/p&gt;&#10;&lt;p&gt;$$&#10;R_{p} = w&amp;rsquo;R&#10;$$&lt;/p&gt;&#10;&lt;p&gt;$$&#10;\sigma_p = w&amp;rsquo;Cw&#10;$$&lt;/p&gt;&#10;&lt;p&gt;其中， &lt;code&gt;\(R=(R_1,R_2,...,R_n)\)&lt;/code&gt; 是组合中各证券的收益率， &lt;code&gt;\(C\)&lt;/code&gt; 是组合中各证券的收益率的协方差矩阵。&lt;/p&gt;&#10;&lt;p&gt;把 &lt;code&gt;\(R_{p}\)&lt;/code&gt; 和 &lt;code&gt;\(\sigma_p\)&lt;/code&gt; 带入 &lt;code&gt;\(U_p\)&lt;/code&gt; ，解出令 &lt;code&gt;\(U_p\)&lt;/code&gt; 最小的 &lt;code&gt;\(w\)&lt;/code&gt; 即可。&lt;/p&gt;&#10;&lt;p&gt;当然，也有其它理念。比如下面要说的风险均摊。风险均摊是指选出来这样一个权重，使得其确定的投资组合中各个证券的风险占证券投资总的风险的比例是相同的。有些拗口，直接看下面的公式。&lt;/p&gt;&#10;&lt;h3 id="风险均摊的理论基础及公式"&gt;&lt;a href="#%e9%a3%8e%e9%99%a9%e5%9d%87%e6%91%8a%e7%9a%84%e7%90%86%e8%ae%ba%e5%9f%ba%e7%a1%80%e5%8f%8a%e5%85%ac%e5%bc%8f" class="header-anchor"&gt;&lt;/a&gt;风险均摊的理论基础及公式&#10;&lt;/h3&gt;&lt;p&gt;由前面的公式可知：&lt;/p&gt;&#10;&lt;p&gt;$$&#10;\sigma_p = wCw’=wC \times (w_1,w_2,&amp;hellip;,w_n)&amp;rsquo;=\sum_{i=1}^n wC_{\cdot i}w_i&#10;$$&lt;/p&gt;&#10;&lt;p&gt;上面可以看到证券组合的风险相当于是 &lt;code&gt;\(wC_{\cdot i}\)&lt;/code&gt; 的加权组合，权重是 &lt;code&gt;\(w=(w_1,w_2,...,w_n)\)&lt;/code&gt; 。所谓风险均摊，就是令 &lt;code&gt;\(wC_{\cdot i}w_i=wC_{\cdot j}w_j\)&lt;/code&gt; 。显然，确定一个权重 &lt;code&gt;\(w\)&lt;/code&gt; 使得证券组合的风险得以均摊等同于找到一个权重 &lt;code&gt;\(w\)&lt;/code&gt; 使得下式最小：&lt;/p&gt;&#10;&lt;p&gt;$$&#10;\sum_{j=1}^n \sum_{i=1}^n \left[ wC_{\cdot i}w_i-wC_{\cdot j}w_j \right]^2&#10;$$&lt;/p&gt;&#10;&lt;p&gt;这是一个比较简单的优化问题，在 R 软件里面很容易得到结果。&lt;/p&gt;&#10;&lt;h3 id="用-r-实现风险均摊优化器"&gt;&lt;a href="#%e7%94%a8-r-%e5%ae%9e%e7%8e%b0%e9%a3%8e%e9%99%a9%e5%9d%87%e6%91%8a%e4%bc%98%e5%8c%96%e5%99%a8" class="header-anchor"&gt;&lt;/a&gt;用 R 实现风险均摊优化器&#10;&lt;/h3&gt;&lt;pre&gt;&lt;code class="language-r"&gt;# 模拟获取数据&#10;require(quantmod);&#10;x=getSymbols(&amp;quot;000635.sz&amp;quot;,from=&amp;quot;2013-01-01&amp;quot;,auto.assign=FALSE)&#10;y=getSymbols(&amp;quot;000547.sz&amp;quot;,from=&amp;quot;2013-01-01&amp;quot;,auto.assign=FALSE)&#10;z=getSymbols(&amp;quot;600241.ss&amp;quot;,from=&amp;quot;2013-01-01&amp;quot;,auto.assign=FALSE)&#10;ret_mat &amp;lt;- cbind(dailyReturn(x),dailyReturn(y),dailyReturn(z))&#10;ret_mat &amp;lt;- na.omit(ret_mat)&#10;&#10;# 定义目标函数&#10;&#10;ff &amp;lt;- function(w,ret_mat){&#10; cov_mat &amp;lt;- cov(ret_mat);&#10; w=c(w,1-sum(w));&#10; len &amp;lt;- length(w);&#10; w_mat &amp;lt;- matrix(rep(w,time=len),nr=len,byrow=TRUE);&#10; diag_mat &amp;lt;- diag(w);&#10; res &amp;lt;- 2*len*as.vector(t(diag(w_mat%*%cov_mat%*%diag_mat))%*%diag(w_mat%*%cov_mat%*%diag_mat))-2*(sum (diag(w_mat%*%cov_mat%*%diag_mat)))^2;&#10; return(res);&#10; } &#10; &#10;# 优化目标函数&#10;&#10;w &amp;lt;- optim(c(0.3,0.3),ff,ret_mat=ret_mat)$par;&#10;&#10;# 查看权重&#10;&#10;w=c(w,1-sum(w));&#10;w = c(w,1-w[1]-w[2])&#10;&lt;/code&gt;&lt;/pre&gt;&#10;</description></item><item><title>使用R和DEoptim求解大规模投资组合</title><link>https://gewutang.cn/2013/08/20/large-scale-portfolio-optimization-with-r-and-deoptim/</link><pubDate>Tue, 20 Aug 2013 00:00:00 +0000</pubDate><guid>https://gewutang.cn/2013/08/20/large-scale-portfolio-optimization-with-r-and-deoptim/</guid><description>&lt;h3 id="1楔子"&gt;&lt;a href="#1%e6%a5%94%e5%ad%90" class="header-anchor"&gt;&lt;/a&gt;1.楔子&#10;&lt;/h3&gt;&lt;p&gt;第四届上海 R 语言会议时，在演讲中提到过一个 R 包：DEoptim 。这是一个用来做优化的包，它实现了几个差分进化算法（ Differential Evolution algorithms ）。差分进化算法是个什么东西呢？从“进化算法”这四个字可以看出它似乎跟生物学有点沾边，事实上，它算是遗传算法（ Genetic algorithms ）的一种。&lt;/p&gt;&#10;&lt;p&gt;诸位知道，遗传算法总的来说是借鉴了生物学中生物进化（其实，说优化可能更准确）过程，这种思路还是比较巧妙的。差分进化算法也类似，差分进化算法的主要过程是这样的：生成随机种子的群体、对各群体进行杂交、对群体进行变异、筛选优秀的群体作为后代。不断重复这一过程直到群体后代的优秀程度没法得到显著的提高为止。如果把目标函数（ objective function ）看作衡量群体优秀程度的标准、把目标函数的输入参数看作群体，显然，可以用差分遗传算法来对目标函数进行优化。&lt;/p&gt;&#10;&lt;p&gt;作为一种优化算法，差分进化算法可以应用到很多领域，金融领域的优化问题很多，求解投资组合就是其中一个应用。所以，这篇文章想看一看差分进化算法在求解投资组合方面的表现。选择投资组合的准则有很多，这里我们根据组合的 CVaR 对组合进行优化。具体来说，在各资产对组合贡献的 CVaR 的权重不超过 5% 的条件下，使得组合的 CVaR 的值最小。&lt;/p&gt;&#10;&lt;p&gt;具体的技术细节可参考 Portfolio Optimization with CVaR budgets 。&lt;/p&gt;&#10;&lt;p&gt;这里的组合优化对应的是一个非凸优化问题。用传统的局部最优方法，比如， optim 包和 nlminb 包中的 L-BFGS-B 方法和 Nelder-Mead 方法，在求解此类优化问题时，常常收敛到次优解，而非最优解。DEoptim 算法属于随机全局优化算法，相比较而言，它的优化效果会更好。&lt;/p&gt;&#10;&lt;p&gt;为了是文章更接近现实，下面使用的组合中的证券数目设定为 100 只股票。&lt;/p&gt;&#10;&lt;h3 id="2系统环境说明"&gt;&lt;a href="#2%e7%b3%bb%e7%bb%9f%e7%8e%af%e5%a2%83%e8%af%b4%e6%98%8e" class="header-anchor"&gt;&lt;/a&gt;2.系统环境说明&#10;&lt;/h3&gt;&lt;p&gt;文中所有的结果和代码都基于 R 3.0.1 。获取数据用的是 quantmod 包中的 getSymbols 函数。目标函数中的风险度量函数来自 PerformanceAnalytics 包。DEoptim 函数的初始群落则由 PortfolioAnalytics 包中的 random_portfolios 函数生成。&lt;/p&gt;&#10;&lt;p&gt;DEoptim 函数会反复计算目标函数的值，直到把群落逼近全局最小值。对提高 DEoptim 函数的性能感兴趣的同学，必须集中活力解决目标函数的进化速度。对于纯 R 代码而言，最好的途径是并行。当然，最根本的道路还是用 c或者Fortran 对该算法进行重写。&lt;/p&gt;&#10;&lt;h3 id="3获取数据"&gt;&lt;a href="#3%e8%8e%b7%e5%8f%96%e6%95%b0%e6%8d%ae" class="header-anchor"&gt;&lt;/a&gt;3.获取数据&#10;&lt;/h3&gt;&lt;p&gt;下面从 S&amp;amp;P 500中随意选取 100支股票作为组合中基础股票。然后，用 quantmod 包中的 getSymbols 函数来下载得到它们的10年间的月度数据，并计算指数收益率序列、均值序列及协方差矩阵。&lt;/p&gt;&#10;&lt;pre&gt;&lt;code class="language-r"&gt; tickers = c( &amp;quot;VNO&amp;quot; , &amp;quot;VMC&amp;quot; , &amp;quot;WMT&amp;quot; , &amp;quot;WAG&amp;quot; , &amp;quot;DIS&amp;quot; , &amp;quot;WPO&amp;quot; , &amp;quot;WFC&amp;quot; , &amp;quot;WDC&amp;quot; ,&#10; &amp;quot;WY&amp;quot; , &amp;quot;WHR&amp;quot; , &amp;quot;WMB&amp;quot; , &amp;quot;WEC&amp;quot; , &amp;quot;XEL&amp;quot; , &amp;quot;XRX&amp;quot; , &amp;quot;XLNX&amp;quot; ,&amp;quot;ZION&amp;quot; ,&amp;quot;MMM&amp;quot; ,&#10; &amp;quot;ABT&amp;quot;, &amp;quot;ADBE&amp;quot; , &amp;quot;AMD&amp;quot; , &amp;quot;AET&amp;quot; , &amp;quot;AFL&amp;quot; , &amp;quot;APD&amp;quot; , &amp;quot;ARG&amp;quot; ,&amp;quot;AA&amp;quot; , &amp;quot;AGN&amp;quot; ,&#10; &amp;quot;ALTR&amp;quot; , &amp;quot;MO&amp;quot; , &amp;quot;AEP&amp;quot; , &amp;quot;AXP&amp;quot; , &amp;quot;AIG&amp;quot; , &amp;quot;AMGN&amp;quot; , &amp;quot;APC&amp;quot; ,&amp;quot;ADI&amp;quot; , &amp;quot;AON&amp;quot; ,&#10; &amp;quot;APA&amp;quot;, &amp;quot;AAPL&amp;quot; , &amp;quot;AMAT&amp;quot; ,&amp;quot;ADM&amp;quot; , &amp;quot;T&amp;quot; , &amp;quot;ADSK&amp;quot; , &amp;quot;ADP&amp;quot; , &amp;quot;AZO&amp;quot; , &amp;quot;AVY&amp;quot; ,&#10; &amp;quot;AVP&amp;quot;, &amp;quot;BHI&amp;quot; , &amp;quot;BLL&amp;quot; , &amp;quot;BAC&amp;quot; , &amp;quot;BK&amp;quot; , &amp;quot;BCR&amp;quot; , &amp;quot;BAX&amp;quot; , &amp;quot;BBT&amp;quot; , &amp;quot;BDX&amp;quot; ,&#10; &amp;quot;BMS&amp;quot; , &amp;quot;BBY&amp;quot; , &amp;quot;BIG&amp;quot; , &amp;quot;HRB&amp;quot; , &amp;quot;BMC&amp;quot; , &amp;quot;BA&amp;quot; , &amp;quot;BMY&amp;quot; , &amp;quot;CA&amp;quot; , &amp;quot;COG&amp;quot; ,&#10; &amp;quot;CPB&amp;quot; , &amp;quot;CAH&amp;quot; , &amp;quot;CCL&amp;quot; , &amp;quot;CAT&amp;quot; , &amp;quot;CELG&amp;quot; , &amp;quot;CNP&amp;quot; , &amp;quot;CTL&amp;quot; , &amp;quot;YHOO&amp;quot;, &amp;quot;CERN&amp;quot; ,&#10; &amp;quot;SCHW&amp;quot; , &amp;quot;CVX&amp;quot; , &amp;quot;CB&amp;quot; , &amp;quot;CI&amp;quot; ,&amp;quot;CINF&amp;quot; ,&amp;quot;CTAS&amp;quot; , &amp;quot;CSCO&amp;quot; , &amp;quot;C&amp;quot; , &amp;quot;CLF&amp;quot; ,&#10; &amp;quot;CLX&amp;quot;, &amp;quot;CMS&amp;quot; , &amp;quot;KO&amp;quot; , &amp;quot;CCE&amp;quot; , &amp;quot;CL&amp;quot; , &amp;quot;CMCSA&amp;quot; ,&amp;quot;CMA&amp;quot; , &amp;quot;CSC&amp;quot; , &amp;quot;CAG&amp;quot; ,&#10; &amp;quot;COP&amp;quot; , &amp;quot;ED&amp;quot; , &amp;quot;QQQ&amp;quot; ,&amp;quot;GLW&amp;quot; , &amp;quot;COST&amp;quot; , &amp;quot;CVH&amp;quot; , &amp;quot;CSX&amp;quot; , &amp;quot;CMI&amp;quot; , &amp;quot;CVS&amp;quot; ,&#10; &amp;quot;DHR&amp;quot; , &amp;quot;DE&amp;quot;)&#10;&#10; library(quantmod);&#10; getSymbols(tickers, from = &amp;quot;2000-12-01&amp;quot;, to = &amp;quot;2010-12-31&amp;quot;)&#10; P &amp;lt;- NULL; seltickers &amp;lt;- NULL&#10; for(ticker in tickers) {&#10; tmp &amp;lt;- Cl(to.monthly(eval(parse(text=ticker))))&#10; if(is.null(P)){ timeP = time(tmp) }&#10; if( any( time(tmp)!=timeP )) next&#10; else P&amp;lt;-cbind(P,as.numeric(tmp))&#10; seltickers = c( seltickers , ticker )&#10; }&#10; P = xts(P,order.by=timeP)&#10; colnames(P) &amp;lt;- seltickers&#10; R &amp;lt;- diff(log(P))&#10; R &amp;lt;- R[-1,]&#10; dim(R)&#10; mu &amp;lt;- colMeans(R)&#10; sigma &amp;lt;- cov(R)&#10;&lt;/code&gt;&lt;/pre&gt;&#10;&lt;h3 id="4设定目标函数及约束条件"&gt;&lt;a href="#4%e8%ae%be%e5%ae%9a%e7%9b%ae%e6%a0%87%e5%87%bd%e6%95%b0%e5%8f%8a%e7%ba%a6%e6%9d%9f%e6%9d%a1%e4%bb%b6" class="header-anchor"&gt;&lt;/a&gt;4.设定目标函数及约束条件&#10;&lt;/h3&gt;&lt;p&gt;这里的投资组合的优化问题由两个部分组成：一是，选择令投资组合具有最小CVaR的的组合权重；二是，令每支股票对整个投资组合的CVaR值的贡献不超过 5%。同时，组合必须是正数，且组合必须进行满仓操作。&lt;/p&gt;&#10;&lt;p&gt;借助于 PerformanceAnalytics 包中的 ES 函数，我们可以方便地计算投资组合的 CVaR 水平以及各支股票对整个投资组合的CVaR贡献值。简单起见，在计算CVaR值时，假定序列服从正态分布。不过，对于非正态分布的收益率序列而言，也能用 ES 函数计算其相应的组合CVaR水平以及各支股票对整个投资组合CVaR的贡献值。&lt;/p&gt;&#10;&lt;p&gt;为了使得得到的组合中，所有股票对整个组合的 CVaR 的贡献不超过 5% ，我们可以向目标函数增加一个罚函数。本来，我们允许搜索算法去考虑那些非可行解。必须对那些无法为投资者接受的投资组合施加足够的惩罚，以使得他们在目标函数的最小化过程中被过滤掉。并且，其对约束的偏离越大，目标函数的值应该增加的越多。&lt;/p&gt;&#10;&lt;pre&gt;&lt;code class="language-r"&gt; library(&amp;quot;PerformanceAnalytics&amp;quot;)&#10; obj &amp;lt;- function(w) {&#10; if (sum(w) == 0) {&#10; w &amp;lt;- w + 1e-2&#10; }&#10; w &amp;lt;- w / sum(w)&#10; CVaR &amp;lt;- ES(weights = w,&#10; method = &amp;quot;gaussian&amp;quot;,&#10; portfolio_method = &amp;quot;component&amp;quot;,&#10; mu = mu,&#10; sigma = sigma)&#10; tmp1 &amp;lt;- CVaR$ES&#10; tmp2 &amp;lt;- max(CVaR$pct_contrib_ES - 0.05, 0)&#10; out &amp;lt;- tmp1 + 1e3 * tmp2&#10; return(out)&#10; }&#10;&lt;/code&gt;&lt;/pre&gt;&#10;&lt;p&gt;组合权重需要满足两个条件，一是，多头；二是，必须满仓操作。现在的 DEoptim 允许对组合权重设定约束范围。我们称其为下边界 lower 和上边界 upper。&lt;/p&gt;&#10;&lt;pre&gt;&lt;code class="language-r"&gt; N &amp;lt;- ncol(R)&#10; minw &amp;lt;- 0&#10; maxw &amp;lt;- 1&#10; lower &amp;lt;- rep(minw,N)&#10; upper &amp;lt;- rep(maxw,N)&#10;&lt;/code&gt;&lt;/pre&gt;&#10;&lt;p&gt;完整的投资约束包括两个方面：一是，将目标函数的所有权重进行标准化，使得所有权重之和为 1 。二是，使用 Portfolioanalytics 包中的 random_portfolios 函数来生成满足上述所有约束条件的随机组合。DEoptim 将采用这些随机组合作为初始群落。&lt;/p&gt;&#10;&lt;pre&gt;&lt;code class="language-r"&gt; source(&amp;quot;random_portfolios.R&amp;quot;)&#10; eps &amp;lt;- 0.025&#10; weight_seq&amp;lt;-generatesequence(min=minw,max=maxw,by=.001,rounding=3)&#10; rpconstraint&amp;lt;-constraint(&#10; assets=N, min_sum=(1-eps), max_sum=(1+eps),&#10; min=lower, max=upper, weight_seq=weight_seq)&#10; set.seed(1234)&#10; rp&amp;lt;- random_portfolios(rpconstraints=rpconstraint,permutations=N*10)&#10; rp &amp;lt;-rp/rowSums(rp)&#10;&lt;/code&gt;&lt;/pre&gt;&#10;&lt;h3 id="5尝试传统的梯度优化方法"&gt;&lt;a href="#5%e5%b0%9d%e8%af%95%e4%bc%a0%e7%bb%9f%e7%9a%84%e6%a2%af%e5%ba%a6%e4%bc%98%e5%8c%96%e6%96%b9%e6%b3%95" class="header-anchor"&gt;&lt;/a&gt;5.尝试传统的梯度优化方法&#10;&lt;/h3&gt;&lt;p&gt;前面介绍的目标函数中的罚函数是不可微，因此，标准的基于梯度的优化方法不再适用。比如，optim 包中的 L-BFGS-B 方法，还有 nlminb 包中的 Nelder-Mead 方法。&lt;/p&gt;&#10;&lt;pre&gt;&lt;code class="language-r"&gt; out &amp;lt;- optim(par = rep(1/N, N), fn = obj,&#10; method = &amp;quot;L-BFGS-B&amp;quot;, lower = lower, upper = upper)&#10; out$value&#10; out$message&#10; out &amp;lt;- nlminb(start =rep(1/N, N), objective = obj,&#10; lower = lower, upper = upper)&#10; out$objective&#10;&lt;/code&gt;&lt;/pre&gt;&#10;&lt;h3 id="6基于-deoptim-的组合优化"&gt;&lt;a href="#6%e5%9f%ba%e4%ba%8e-deoptim-%e7%9a%84%e7%bb%84%e5%90%88%e4%bc%98%e5%8c%96" class="header-anchor"&gt;&lt;/a&gt;6.基于 DEoptim 的组合优化&#10;&lt;/h3&gt;&lt;p&gt;与基于梯度的优化方法不同，DEoptim 函数会重复地查找优化问题的全局近似最小值。对于本文中这种复杂的组合，DEoptim 函数的表现极大地依赖于所选用的DE算法。&lt;/p&gt;&#10;&lt;p&gt;先看一下DEoptim的默认算法，即带固定参数的局部最优（local-to-best）策略。&lt;/p&gt;&#10;&lt;p&gt;如果目标函数在每150步迭代之后，两最优迭代之间的提升百分比低于 reltol=1e-6，我们就认为目标函数收敛。对于某些优化问题，DE算法要经过很多步迭代才收敛。这里，我们将最大迭代次数设定为5000。每完成250次迭代向屏幕输出一次结果。前面已经说过，用 rp 作为 DE 算法的初始群落。&lt;/p&gt;&#10;&lt;pre&gt;&lt;code class="language-r"&gt; controlDE &amp;lt;- list(reltol=.000001,steptol=150, itermax = 5000,trace = 250,&#10; NP=as.numeric(nrow(rp)),initialpop=rp)&#10; set.seed(1234)&#10; start &amp;lt;- Sys.time()&#10; out &amp;lt;- DEoptim(fn = obj, lower = lower, upper = upper, control = controlDE)&#10;&lt;/code&gt;&lt;/pre&gt;&#10;&lt;pre&gt;&lt;code&gt;Iteration: 250 bestvalit: 0.064652&#10;Iteration: 500 bestvalit: 0.057199&#10;Iteration: 750 bestvalit: 0.055774&#10;Iteration: 1000 bestvalit: 0.055013&#10;Iteration: 1250 bestvalit: 0.054581&#10;Iteration: 1500 bestvalit: 0.054269&#10;Iteration: 1750 bestvalit: 0.054146&#10;Iteration: 2000 bestvalit: 0.054049&#10;Iteration: 2250 bestvalit: 0.053706&#10;Iteration: 2500 bestvalit: 0.053695&#10;Iteration: 2750 bestvalit: 0.053351&#10;Iteration: 3000 bestvalit: 0.053273&#10;out$optim$iter&#10;[1] 3055&#10; out$optim$bestval&#10;[1] 0.05327273&#10;end &amp;lt;- Sys.time()&#10;end - start&#10;Time difference of 16.03645 mins&#10;&lt;/code&gt;&lt;/pre&gt;&#10;&lt;p&gt;在上面的5000次迭代中，带固定参数的局部最优策略得到的最优解优于前面提到的基于梯度的优化算法得到的结果。近来出现的JADE算法，对复杂优化问题具有更好地收敛特性。该算法糅合了局部最优策略和自适应参数控制策略。&lt;/p&gt;&#10;</description></item></channel></rss>