马科维茨有效前沿实现(Stata版本)

马科维茨有效前沿实现(Stata版本)

这篇文章是受上一篇文章的启发,想用Stata也实现一遍,不过后面的最优化部分我实在不会用Stata做了。

准备数据集

Stata
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
cpd /
cd "Stata马科维茨有效前沿的实现"
* 使用上证50四根票进行组合,首先获取四只股票的交易数据
foreach i in "600000" "600016" "600019" "600028"{
cntrade2 `i', start(20180101) end(20180927)
save `i', replace
}

use 600000, clear
keep close date
ren close 浦发银行
save a600000, replace

use 600016, clear
keep close date
ren close 民生银行
save a600016, replace

use 600019, clear
keep close date
ren close 宝钢股份
save a600019, replace

use 600028, clear
keep close date
ren close 中国石化
save a600028, replace

use a600000, clear
foreach i in "600016" "600019" "600028"{
merge 1:1 date using a`i'
drop _m
}
save rawdata, replace

上面的处理之后会得到这样的一个数据集:

使用基准价格(把2018年1月2日设为基期)比较四只股票的价格的走势

Stata
1
2
3
4
5
6
7
8
9
10
11
12
13
14
use rawdata, clear
foreach i of varlist _all{
if "`i'" != "date"{
replace `i' = (`i' / `=`i'[1]') * 100
format `i' %6.2f
}
}
colorscheme 4, palette(Dark2)
tw ///
line 浦发银行 民生银行 宝钢股份 中国石化 date, ///
xla(21186(45)21454, ang(20)) leg(order(1 "浦发银行" 2 "民生银行" ///
3 "宝钢股份" 4 "中国石化") pos(1) ring(0)) ///
lp(solid solid solid solid) lc(`r(colors)')
gre 四只股票的价格走势

计算对数收益率

Stata
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
use rawdata, clear
* 由于股票交易日之间可能存在间隔,所以不能直接用日期作为时间变量
gen dateid = _n
tsset dateid
foreach i of varlist _all{
if "`i'" != "date" & "`i'" != "dateid"{
gen l`i' = l.`i'
replace `i' = log(`i'/l`i')
drop l`i'
format `i' %6.4f
}
}

* 绘制收益率的分布直方图
drop dateid
save returndata, replace
use returndata, clear
foreach i of varlist _all{
if "`i'" != "date"{
ren `i' returns`i'
}
}
reshape long returns, i(date) j(name) string
hist returns, by(name, r note("")) bin(50) ///
xti(日对数收益率) yti(频率) ///
xla(, format(%6.2f)) norm
gre 直方图

给股票随意分配权重

这部分用了大量的矩阵,Stata的矩阵功能还是蛮强大的,因为不常接触,也不是很熟悉。

Stata
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
. use returndata, clear

. local w1 = runiform()

. local w2 = runiform()

. local w3 = runiform()

. local w4 = runiform()

. mat weight = (`w1' \ `w2' \ `w3' \ `w4')

. mat list weight

weight[4,1]
c1
r1 .03407098
r2 .61925299
r3 .08955422
r4 .61012636

. mat weight = weight / (`w1'+`w2'+`w3'+`w4')

. mat list weight

weight[4,1]
c1
r1 .02518172
r2 .45768729
r3 .06618915
r4 .45094184

计算预期组合年化收益、组合方差和组合标准差

注意回忆一下这三个东西的计算公式:

Stata
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
* 组合年化收益
local j = 1
foreach i of varlist _all{
if "`i'" != "date"{
qui sum `i'
local r`j' = r(mean)
local j = `j' + 1
}
}

mat returns = (`r1', `r2', `r3', `r4')
mat list returns
mat a = returns * weight * 252
mat list a
local ret = a[1, 1]

* 组合年化方差
corr 浦发银行 民生银行 宝钢股份 中国石化, cov
ret list
mat variance = r(C)
mat list variance
mat b = weight' * variance * 252 * weight
mat list b
local var = b[1, 1]

* 组合年化标准差
local std = sqrt(b[1, 1])

蒙特卡洛模拟

Stata
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
cap prog drop front
prog def front, rclass
version 15.1
use returndata, clear
local w1 = runiform()
local w2 = runiform()
local w3 = runiform()
local w4 = runiform()
mat weight = (`w1' \ `w2' \ `w3' \ `w4')
mat list weight
mat weight = weight / (`w1'+`w2'+`w3'+`w4')
mat list weight
ret scalar w1 = weight[1, 1]
ret scalar w2 = weight[2, 1]
ret scalar w3 = weight[3, 1]
ret scalar w4 = weight[4, 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 returns = (`r1', `r2', `r3', `r4')
mat list returns
mat a = returns * weight * 252
mat list a
ret scalar ret = a[1, 1]
corr 浦发银行 民生银行 宝钢股份 中国石化, cov
ret list
mat variance = r(C)
mat list variance
mat b = weight' * variance * 252 * weight
mat list b
ret scalar var = b[1, 1]
ret scalar std = sqrt(b[1, 1])
end

simulate ret = r(ret) var = r(var) std = r(std) w1 = r(w1) w2 = r(w2) w3 = r(w3) w4 = r(w4), reps(100000): front
* 计算夏普比率
gen sharpe_ratio = (ret - 0.04)/std
save montecarlo, replace

montecarlo.dta数据集是这样的:

ret:组合的对数收益率;
var:组合的方差;
std:组合的标准差;
w*:四只股票的权重;
sharpe_ratio:夏普比率。

Stata
1
2
3
4
5
use montecarlo, clear
tw sc ret std, msize(*0.8) xti(波动率) yti(收益率) ///
xla(#6, format(%6.2f)) yla(, format(%6.1f)) ///
ti("图:投资组合收益率与波动率的关系")
gre 投资组合收益率与波动率的关系

由于我没能看懂Stata如何进行最优化(mata语言的,几乎一窍不通),所以我就简单的直接寻找最优点:

Stata
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
use montecarlo, clear
gen id = ""
gsort -sharpe_ratio
replace id = "夏普比率最大的组合" in 1
local std1 = std[1]
gsort std
replace id = "标准差最小的组合" in 1
local std2 = std[1]
gen varround = round(var, 0.001)
format varround %6.4f
order varround
gsort varround ret
by varround: replace id = "有效前沿上的组合" if _n == _N & inrange(std, `std2', `std1') & missing(id)
tw sc ret std, msize(*0.8) xti(波动率) yti(收益率) ///
xla(#6, format(%6.2f)) yla(, format(%6.1f)) ///
ti("图:投资组合收益率与波动率的关系") leg(off) || ///
sc ret std if id == "有效前沿上的组合", ///
m(A) mc(blue) msize(*2) msa(40)|| ///
sc ret std if id == "夏普比率最大的组合", ///
m(D) mc(red) msize(*2) || ///
sc ret std if id == "标准差最小的组合", ///
m(X) mc(yellow) msize(*2)
gre 有效前沿

上面还有两个蓝色的箭头是错误的。

# Stata

评论

程振兴

程振兴 @czxa.top
截止今天,我已经在本博客上写了607.9k个字了!

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×