Stata:投资组合有效边界

发布时间:2021-04-11 阅读 8310

Stata连享会   主页 || 视频 || 推文 || 知乎

温馨提示: 定期 清理浏览器缓存,可以获得最佳浏览体验。

New! lianxh 命令发布了:
随时搜索推文、Stata 资源。安装命令如下:
. ssc install lianxh
详情参见帮助文件 (有惊喜):
. help lianxh

课程详情 https://gitee.com/lianxh/Course

课程主页 https://gitee.com/lianxh/Course

⛳ Stata 系列推文:

作者:史津宇 (中山大学)
E-Mail: shijy2000@outlook.com

指导老师: 连玉君(中山大学)
E-mail: arlionn@163.com


目录


1. 话题引入

有效前沿 (Efficient Frontier)是马科维茨投资组合理论的进一步延伸,它基于每类风险资产回报率、波动性、相关系数等历史数据,刻画不同资产配比下的多个投资组合散点图,进而回答资产配置的一个关键性问题——如何构建最优投资组合

1.1 何为最优投资组合?

根据 马科维茨投资组合理论:由多只证券构成的投资组合中一定存在一系列最优投资组合,它们既是相同风险水平下收益最大的组合,也是相同收益水平下风险最小的组合。

以下图为例,假设虚拟投资 A、B、C 是有价证券按照不同配置比例所构成的三个投资组合,它们按照 均值—方差 的不同特征,在平面坐标系上处于不同的位置。

那么,从投资的角度,同样的收益水平下,A 点风险更小;同样的风险水平下,C 点收益更高。但如果没更多的信息,我们将无法直接比较 A 和 C 。

均值-方差示意图
均值-方差示意图

而假若补充一个投资组合 D,那无论是预期收益还是波动率角度看, D 毫无疑问将是最优组合。

1.2 何谓有效前沿?

由于在无数投资组合中,只要给出既定风险水平,我们总是可以找到收益最高的一个组合。 所以,当投资者可承受的风险水平变化时,与该风险水平相对应的最优投资组合也一直存在,并且随着风险水平变化不断的移动。

这条由不同风险水平下最优投资组合点构成的曲线,称之为有效曲线,又叫有效前沿(Efficient frontier)。

推荐阅读

接下来,让我们了解如何利用 Stata 完成构造。

2. 股票数据获取与预处理

2.1 获取股票数据

为获取股票交易数据,我们首先安装外部命令 cntrade 便于下载个股和市场指数实时数据:

ssc install cntrade, replace 
ssc install openall, replace 

本文选取贵州茅台 (600519)、药明康德 (603259)、科大讯飞 (002230) 三只股票为例,利用 cntrade 命令下载并转化数据储存格式:

  local list "600519 603259 002230"
  cntrade `list'
  openall `list'
  gen year = year(date)
  keep if year == 2020 //保留2020年数据
  save "mstocks_long.dta", replace //保存合并后的数据

  use "mstocks_long.dta", clear  // Long format
    keep stkcd date clsprc //保留需要纵横变换的变量
    xtset date stkcd
    reshape wide clsprc, i(date) j(stkcd)
  save "mstocks_wide.dta", replace  // Wide format

2.2 观察股票价格走势

由于每只股票单价差距较大,为方便观察股价走势,我们分别将其指标准化(2020 年第一个交易日的股票价格定为 100):

 use mstocks_wide, clear
 foreach i of varlist clsprc*{
    if "`i'" != "date"{
        replace `i' = (`i' / `=`i'[1]') * 100
        format `i' %6.2f
      }
  }  //标准化股价

tw
line  clsprc*   date, ///
    xla(21915(45)22281, ang(20)) ///
    leg(order(1 "贵州茅台" 2 "药明康德" ///
        3 "科大讯飞" ) position(1) ring(0)) ///
	title("股价走势图(2020年)") ///
	xtitle("日期") ///
	ytitle("标准化股价")
    graph export "$out\price_trend.png", replace

导出图片呈现如下:

price_trend
price_trend

贵州茅台和药明康德的股价涨幅相对于科大讯飞更高;相对的,前两者也承担了更高的风险——这正是 CAPM 中重要的 风险和收益的权衡原则

2.3 计算收益率与协方差

接下来,我们计算每只股票的年均收益率、方差以及股票间的协方差。 此处我们采取对数收益率的计算方法,即根据公式:

rt=log(PtPt1)=log(Pt)log(Pt1)

了解更多关于收益率的 Stata 计算命令,欢迎参见 Stata 数据处理:ascol-mtoq-日收益转周-月-季-年度数据

use mstocks_wide, clear
gen dateid = _n
tsset dateid
  foreach i of varlist clsprc*{
    gen l`i' = l.`i'
    replace `i' = log(`i'/l`i')
    drop l`i'
   }
drop dateid
rename (clsprc2230 clsprc600519 clsprc603259)///
       (科大讯飞 贵州茅台 药明康德)
save return, replace

同样可以通过 CAPM 模型,计算股票的贝塔系数进而获得预期收益率。详情参见连享会推文 实时估计个股贝塔(beta)系数

得到对数收益率数据格式如下:

list in 1/5

     |       date     科大讯飞     贵州茅台     药明康德 |
     |---------------------------------------------------|
  1. | 2020-01-02            .            .            . |
  2. | 2020-01-03   -.01090398   -.04659081   -.02609613 |
  3. | 2020-01-06    .05773528   -.00052862   -.03025364 |
  4. | 2020-01-07    .00081666    .01522685    .01501375 |
  5. | 2020-01-08   -.01923136   -.00585523   -.00627605 |

根据获得的对数收益率,计算出年均收益率、协方差并分别储存在对应矩阵中:

use return, clear
 drop in 1 
 local j = 1
 foreach i of varlist 科大讯飞 贵州茅台 药明康德{
        qui sum `i'
        local r`j' = r(mean)
        local j = `j' + 1
 }
 mat rets = (`r1', `r2', `r3')// 年均收益率矩阵

 corr 科大讯飞 贵州茅台 药明康德, cov
 mat cov = r(C) // 协方差矩阵

存储得到的矩阵结果如下:

mat list rets

rets[1,3]
           c1         c2         c3
r1  .00063598  .00235508  .00159318

mat list cov

symmetric cov[3,3]
                  科大讯飞      贵州茅台      药明康德
    科大讯飞     .00068277
    贵州茅台     .00018373     .00032969
    药明康德     .00032823     .00023092     .00115149

3. 构造投资组合

3.1 两项资产的情况

完成之前的一系列准备工作后,我们首先用两个资产(贵州茅台和科大讯飞)进行尝试构造可能出现的投资组合。

假设,科大讯飞所占比重为 ω1,贵州茅台所占比重为 ω2,其中,ω1+ω2=1.

投资组合的预期收益:

预期的波动率:

观察不同比重组合下的收益率和方差:

clear
  set obs 11
  egen double w1=fill(0(0.1)1)
  format w1 %tg
  gen w2=1-w1
  format w2 %tg

  gen r_P = w1*rets[1,1] + w2*rets[1,2]
  gen varP= w1^2*cov[1,1] + w2^2*cov[2,2] ///
            + 2*w1*w2*cov[2,1]
  gen sdP=sqrt(varP)

twoway (scatter r_P sdP, msize(medlarge)  ///
       mlabel(w1) mlabcolor(edkblue)),   ///
       ytitle(收益率)   ///
       ylabel(#5)      ///
       xtitle(波动性)  ///
       title(Frontier using ///
       2 stocks(科大讯飞 & 贵州茅台))

graph export "$out\Frontier_using_2_stocks.png" ///
, as(png) replace

横坐标是波动性,纵坐标是收益率,得到的图形如下:

3.2 添加第三项资产

在投资组合中添加第三项风险投资股票——药明康德,所占比重为 ω3,此时 ω1+ω2+ω3=1.

对于新的投资组合,我们的预期收益为:

预期的波动性为:

同上,观察不同比重组合下的收益率和方差:

clear
  set obs 101
  egen double w1=fill(0(0.01)1)
  format w1 %tg
  egen double w2=fill(0(0.01)1)
  gen w3=1-w1-w2
  format w2 %tg
  format w3 %tg

  gen r_P = w1*rets[1,1] + w2*rets[1,2] + w3*rets[1,3]

  gen varP= w1^2*cov[1,1] + w2^2*cov[2,2] ///
            + w3^2*cov[3,3]    ///
            + 2*w1*w3*cov[1,3] ///
            + 2*w2*w3*cov[2,3] ///
            + 2*w1*w2*cov[2,1]

  gen sdP=sqrt(varP)

  twoway (scatter r_P sdP, msize(tiny) ///
        mlabcolor(edkblue)), ///
        ytitle(收益率) ylabel(#5) xtitle(波动性) ///
        title("Frontier with 3 stocks")         ///
        subtitle("(科大讯飞 & 贵州茅台 & 药明康德)")

graph export "$out\Frontier_using_3_stocks.png"///
, as(png) replace

得到的投资组合收益率-波动性关系为:

Efficient Frontier using 3 stocks
Efficient Frontier using 3 stocks

将此类情形拓展到 n 种资产,则预期收益为:

预期的波动性为:

然而,这样的投资组合总是 有效 的吗?

答案是否定的。由于此时获得的收益率只是来自于不同权重的组合,而非限制波动性最小情况下的最优值,所以接下来需要我们计算有效前沿。

4. 计算并绘制有效边界

4.1 蒙特卡洛模拟初窥边界

当样本数据量足够大的时候,通常会非常接近正确答案——有效边界了。所以我们试图设置样本点为 10000 个,对于不同比重的资产组合计算均值与方差。

首先,基于以上三种股票的收益率、波动性和协方差,我们编写一个的蒙特卡洛模拟的 Stata 程序并封装此程序:

cap prog drop front
prog def front, rclass
    version 15.0
    use return, clear
    local w1 = runiform()
    local w2 = runiform()
    local w3 = runiform()
    mat weight = (`w1' \ `w2' \ `w3' )
    mat list weight
    mat weight = weight / (`w1'+`w2'+`w3')
    mat list weight
    ret scalar w1 = weight[1, 1]
    ret scalar w2 = weight[2, 1]
    ret scalar w3 = weight[3, 1]
    local j = 1
    foreach i of varlist _all{
        if "`i'" != "date"{
            qui sum `i'
            local r`j' = r(mean)
            local j = `j' + 1
        }
    }
    mat rets = (`r1', `r2', `r3')
    mat a =  rets * weight
    mat list a
    ret scalar ret = a[1, 1]
    corr 科大讯飞 贵州茅台 药明康德, cov
    ret list
    mat cov = r(C)
    mat b =  weight' * cov * weight
    mat list b
    ret scalar var = b[1, 1]
    ret scalar std = sqrt(b[1, 1])
end

了解更多关于蒙特卡洛模拟分析的 Stata 介绍,欢迎参见连享会专题文章 Stata:蒙特卡洛模拟分析 (Monte Carlo Simulation)

设置执行 100000 次蒙特卡洛模拟:

simulate ret = r(ret)   ///
         var = r(var)   ///
         std = r(std)   ///
         w1 = r(w1)     ///
         w2 = r(w2)     ///
         w3 = r(w3)     ///
         , reps(100000): front
save dataset, replace
graph export "$out\Portfolio.png", as(png) replace

得到图片,我们可以在后面对其边界进行验证:

Portfolio
Portfolio

4.2 最优化与有效边界计算

我们需要找到最小化方差点,该点以上的曲线部分是有效的,以下的部分则是无效的。即最小化方差 wVw ,对于这类最优化问题——我们可以借助 Mata 中的函数 optimize 来实现。由于权重和为 1,因此只需计算得到两种资产占比,即可得第三种资产权重。

输入 mata: 进入 Mata 环境,考虑到 optimize 默认计算最大值,加入负号即可计算最小值。

mata:
 mata clear
 void min_std(real scalar todo,///
      real vector w12, std, g, H)
  {
    real scalar w3
    w3 = 1 - sum(w12)
    real vector r_mean
	r_mean= st_matrix("rets")
   // 将储存于 Stata 的矩阵导入 Mata环境
    real vector w
    w = (w12, w3)
	real scalar r_P
    r_P = w * r_mean'
    real matrix var_P
    var_P = st_matrix("cov")
    var_P = w * var_P * w'
    std = -sqrt(var_P[1, 1]) 
    // 加入负号以达到求最小值的目的
  }

  S = optimize_init()
  optimize_init_evaluator(S, &min_std())
  optimize_init_params(S, J(1, 2, 0))
  wh = optimize(S)

  w = (wh, 1-sum(wh))
  r_Pm = (wh, 1-sum(wh)) * st_matrix("rets")'
  var_P = (w * st_matrix("cov") * w')
  std = sqrt(var_P[1, 1])

  w
  r_Pm
  std

end

最小方差点的均值-方差为 (0.0019607171 , 0.0171638423),科大讯飞、贵州茅台、药明康德在投资组合中的比重依次为:

     1             2             3
    +-------------------------------------------+
  1 |  .2085128365    .744359616   .0471275475  |
    +-------------------------------------------+

紧接着,需要计算投资组合并保证每个组合的收益率对应的最小方差。该步骤只需要在上一步的基础上嵌套关于预期收益率取值的循环:

mata:
 mata clear
 void min_std(real scalar todo,///
       real vector w123, std, g, H)
  {
    real vector rmean
    rmean = st_matrix("rets")
    real scalar rp
    real vector w
    w = w123
    rp = w * rmean'
    real matrix variancep
    variancep = st_matrix("cov")
    variancep = w * variancep * w'
    std = -sqrt(variancep[1, 1])
  }

  std_vector = J(1, 40, 0)

// 最小化方差对应的收益率为 0.00196,
// 此时收益率较低,为仅购入贵州茅台时的 0.00235
    
  j=1
  for (i = 0.00196; i < 0.00235; i = i + 0.00001)
  {
    S = optimize_init()
    optimize_init_evaluator(S, &min_std())
    optimize_init_technique(S, "nr")
    optimize_init_constraints(S, ///
    ((st_matrix("rets")\ J(1, 3, 1)), (i \ 1)))
    optimize_init_params(S, J(1, 3, 0))
    wh = optimize(S)
    variancep = (wh * st_matrix("cov") * wh')
    std = sqrt(variancep[1, 1])
    std_vector[1, j] = std
    j = j + 1
  }
  st_matrix("std_vector", std_vector)

end

画图,做出有效前沿:

clear
set obs 40
  gen ret = (_n + 195) / 100000
  gen std = .
  forval i = 1/40{
    replace std = std_vector[1, `i'] in `i'
  }
  gen front = 1
  drop if std == 0
save front, replace


tw ///
sc ret std if front == 1, ///
    msize(tiny) msymbol(D) ///
    mc("blue") leg(off) || ///
    scatteri .0019607171  ///
   .0171638423 "最小方差点", ///
    mc("red")  msymbol(o) mlabc("pink")
Efficient Frontier using 3 stocks
Efficient Frontier using 3 stocks

补充显示上一步通过蒙特卡洛模拟做出的数据集:

use dataset1, clear
  append using front

  twoway ///
     scatter ret std, msize(*0.01) ///
     xtitle(波动率) ytitle(收益率) ///
     title("Efficient Frontier using 3 stocks")///
     msymbol(o) ///
     xlabel(#6, format(%6.3f)) ylabel(, ///
     format(%6.4f))||    scatteri  ///
     .0019607171   .01716384 "最小方差点", ///
     mc("pink")  msymbol(o) mlabc("pink") || ///
     scatter ret std if front == 1, ///
     msize(tiny) msymbol(D) ///
     mc("blue") leg(off)

graph export ///
"$out\Investment_portfolio_using_3_stocks.png"///
, as(png) replace
Investment_portfolio_using_3_stocks
Investment_portfolio_using_3_stocks

5. 快捷命令

虽说花费如此多的篇幅去折腾这一条曲线,但在搜集信息的过程中,笔者发现了用 Stata 最快做出有效前沿的命令—— mvport.

这个由 Carlos Alberto Dorantes 提供的多个投资组合模型的估计程序,可以一步计算最小方差组合、最大夏普比率组合,同时这些组合也可以施加约束,包括是否允许卖空,是否有最小或最大权重的限制等等。

以本文中选取的三只股票为例,一条命令就可以计算得出最小方差组合的的权重:

use return.dta, clear
gmvport 科大讯飞 贵州茅台 药明康德, noshort

  Number of observations used to calculate///
  expected returns and var-cov matrix : 243
  The weight vector of the Global Minimum ///
  Variance Portfolio (NOT Allow Short Sales) is:

                Weights
    科大讯飞  .20851284
    贵州茅台  .74435962
    药明康德  .04712755
      
  The return of the Global Minimum Variance ///
  Portfolio is: .00196288

  The standard deviation (risk) of the Global ///
  Minimum Variance Portfolio is: .01716384

同样,借助命令 efrontier 画出有效前沿也丝毫不在话下:

use return.dta, clear
efrontier 科大讯飞 贵州茅台 药明康德
Efficient Frontier
Efficient Frontier

有关更多使用指南,读者可以通过以下指令自行探索:

findit mvport

6. 结束语

有效前沿可以帮助专业投资者,很好地评价自己过去的投资组合离最优投资组合的偏差,还可以在总结过往数据表现的基础上发现一些规律,较好地指导未来投资实践,如对当前投资组合做出调整和优化等。

当然,虽然有效前沿对资产组合配置有较强的指导意义,我们也必须意识到,由于上述分析完全基于已知的过去资产价格数据,未来的价格数据具有不确定性,因此有效前沿不能精准预测未来。

7. 参考资料

数据处理和计算相关

Stata 有效前沿的相关命令

  • findit mvport // 一组计算投资效率和投资组合的命令
  • help grsftest // Econometrica 57(5)
    • Gibbons, M.R., S. Ross, and J. Shanken, 1989. "A test of the efficiency of a given portfolio" Econometrica, 57(5), 1121-1152. - PDF -
  • search fetchcomponents // Stata Journal 13-3
    • Mehmet F. Dicle, 2013, Financial Portfolio Selection using the Multifactor Capital Asset Pricing Model and Imported Options Data, Stata Journal, 13(3): 603–617. - PDF -

8. 相关推文

Note:产生如下推文列表的 Stata 命令为:
lianxh 投资组合 收益 事件研究
安装最新版 lianxh 命令:
ssc install lianxh, replace

相关课程

免费公开课

最新课程-直播课

专题 嘉宾 直播/回看视频
最新专题 文本分析、机器学习、效率专题、生存分析等
研究设计 连玉君 我的特斯拉-实证研究设计-幻灯片-
面板模型 连玉君 动态面板模型-幻灯片-
面板模型 连玉君 直击面板数据模型 [免费公开课,2小时]
  • Note: 部分课程的资料,PPT 等可以前往 连享会-直播课 主页查看,下载。

课程主页

课程主页

关于我们

  • Stata连享会 由中山大学连玉君老师团队创办,定期分享实证分析经验。
  • 连享会-主页知乎专栏,400+ 推文,实证分析不再抓狂。直播间 有很多视频课程,可以随时观看。
  • 公众号关键词搜索/回复 功能已经上线。大家可以在公众号左下角点击键盘图标,输入简要关键词,以便快速呈现历史推文,获取工具软件和数据下载。常见关键词:课程, 直播, 视频, 客服, 模型设定, 研究设计, stata, plus, 绘图, 编程, 面板, 论文重现, 可视化, RDD, DID, PSM, 合成控制法

连享会小程序:扫一扫,看推文,看视频……

扫码加入连享会微信群,提问交流更方便

✏ 连享会-常见问题解答:
https://gitee.com/lianxh/Course/wikis

New! lianxh 命令发布了:
随时搜索连享会推文、Stata 资源,安装命令如下:
. ssc install lianxh
使用详情参见帮助文件 (有惊喜):
. help lianxh