摘要及声明
1:本文主要对基于均值方差最优化的资产配置方法进行拓展,从多目标最优化的角度看待资产配置并可视化展示;
2:本文主要为理念的讲解,模型也是笔者自建,文中假设与观点是基于笔者对模型及数据的一孔之见,若有不同见解欢迎随时留言交流;
3:笔者原则是只做干货的分享,后续将更新更多内容,但工作学习之余的闲暇时间有限,更新速度慢还请谅解;
4:本文主要数据通过数据爬虫获取,模型实现基于python3.8;
目录
1. 单目标优化
一个经典的均值方差最优化会形成最小方差前沿,以简单的两资产组合为例,组合回报率为:
组合标准差为:
通过随机权重,设计约束条件即可得到图一,最左侧的点连线即可得到最小方差前沿。
图一:一个均值方差最优化案例
对于均值方差最优化来说,标准差被看作是代表风险的指标。在不考虑客户效用曲线的前提下,其最优化目标即使得sharpe比率最大化,即是与前沿相切时的CAL(Capital Allocation Line)线,其切点处的投资组合夏普比率最大。
2. 多目标规划求解
标准差作为二阶矩,其实只能算是风险的其中一个方面,在上述最优化求解过程中,无法同时在多目标上进行优化。为了改进优化目标单一的缺点,笔者在均值方差最优化的基础上再引入其它维度,下面以组合分散度DR(Diversification Ratio)为例。
2.1 分散指标计算
首先这里的分散度并不是指投资组合里的证券数量,而是成分证券的相关性。
其中:为权重矩阵;
为成分证券的方差矩阵;
为组合协方差矩阵。
该式分母即是原始的投资组合方差计算公式,分子是不考虑协方差影响的组合方差。分子其实是投资组合处于最分散(理想状态)状态,即此时所有成分证券之间的相关性为0。当DR等于1时,意味着当前投资组合(分母)达到最分散的状态(分子)。
这个式子和有效前沿有类似的特征,设计优化目标[4]和约束条件[5]:
进行最优化后也可以得到类似均值方差最优化的一条有效前沿。
2.2 均值-方差分散度最优化
由于多加了一个维度,即是将原本的二维散点图延展到三维空间。因为三维空间没有斜率这个概念,原本的求解方案就无法使用单一的某个指标进行最优化了。不过三维空间还是可以通过偏导数求解出一个类似二维空间斜率的东西。
将Z轴设为投资回报率,那么最优组合一定是过无风险利率点,作与有效面相切的切线。由于无风险利率这个z轴上的点已经固定了,剩下只要求解x和y两个方向上的偏导数,使得过无风险利率点的两个方向上偏导达到最大即可完成目标。
另设x轴为标准差,y轴为分散度除以1(集中程度),CAL线方程为[6]式:
目标函数即是[7]式,线性约束条件依然满足[5]:
求解可以说非常的简单,不过这个式子其实并不是严格意义上的偏导数,因为z方向的偏导数是没有考虑进去的,笔者姑且管它叫“部分偏导”吧。
笔者选取4支私募基金进行最优化求解后得到三维散点分布及最优组合如图二。从图二不难看出,最优化组合所处位置其实并不在最顶端,也不在x和y方向投影的有效前沿上。这个组合除了进行均值方差最优还同时兼顾了一定的分散性,具备一定分散性的同时兼顾一定的风险回报最优,这就是多目标规划求解的优势。
图二:三维可视化
当然,通过这个思路还可以将分散度替换为其它指标,甚至加入更多指标,只是更高维的可视化就很难实现了。但是也需要注意指标也许不是越多越好,用绝大多数指标的不规则散点分布进行最优化得到的解很可能没有多大意义。
3. 代码实现
由于需要生成很多随机数和三维可视化,笔者借助R语言进行实现。笔者选取4支私募基金17年-20年的月度回报率数据,首先查看几个产品的相关性矩阵:
data <- data.frame(fund1$`涨跌幅(%)`, fund2$`涨跌幅(%)`, fund3$`涨跌幅(%)`, fund4$`涨跌幅(%)`)
names(data) <- c("基金1", "基金2", "基金3", "基金4")
cor(data)
col <- colorRampPalette(c("#BB4444", "#EE9988", "#FFFFFF", "#77AADD", "#4477AA"))
corrplot(cor(data), method = "shade", shade.col = NA,
tl.col = "black", tl.srt = 0, col = col(200), addCoef.col = "black",
cl.pos = NULL, order = "AOE", mar=c(1,1,1,2))
图三:组合相关性矩阵
下面进行最优化求解:
train_data <- data
train_data_nav <- cumprod(train_data)
r <- (train_data_nav[nrow(train_data_nav),] - train_data_nav[1,]) / train_data_nav[1,]
r <- (1 + r) ^ (12 / nrow(train_data_nav))-1 # 私募基金数据是月度的,要年化一下
r_matrix <- matrix(r, 1, 4) # 生成回报率矩阵
cov_matrix <- matrix(cov(train_data), 4, 4) # 生成协方差矩阵
std_cols <- apply(train_data, 2, sd) # 生成方差矩阵
# 存储随机投资组合数据
w_lst <- list()
r_lst <- c()
std_lst <- c()
DR_lst <- c()
for (i in 1:5000){ # 5000次随机
w <- runif(4,0,1) # 随机数生成
w <- matrix(w/sum(w), 1, 4) # 归一化,变成矩阵
std <- sqrt(w %*% cov_matrix %*% t(w))
r_portfolio <- sum(r * w)
dr <- std / sum(std_cols * w)
DR_lst <- c(DR_lst, dr)
w_lst[[i]] <- w
r_lst <- c(r_lst, r_portfolio)
std_lst <- c(std_lst, std)
}
mvo <- data.frame(DR_lst, std_lst, r_lst)# 下面计算出多维MVO
mvo$r_lst <- mvo$r_lst - 0.024 # 无风险利率取2.4%
mvo$sharp <- mvo$r_lst / mvo$std_lst
mvo$diverfication <- mvo$r_lst / mvo$DR_lst
mvo$max_target <- mvo$sharp + mvo$diverfication # 两个方向的偏导数相加
# 便历一下,查找最优化的权重配置
for (i in 1:nrow(mvo)){
if (mvo$max_target[i] >= max(mvo$max_target)){
print(i) # 第i个
w_max <- w_lst[i]
r_max <- r_lst[i]
std_min <- std_lst[i]
DR_min <- DR_lst[i]
}
}
cal_line <- data.frame(a = c(0, std_min*2), b = c(0, DR_min*2), c = c(0.024, r_max*2-0.024)) # 资产配置线的坐标
optimal_point <- data.frame(a = std_min, b = DR_min, c = r_max) # 最优组合的坐标
最后通过plotly可视化即可获得炫酷的交互式三维图。
library(plotly)
plot <- plot_ly() # 添加三维图
plot <- plot %>% add_trace(data = mvo, x = std_lst, y = DR_lst, z = r_lst, type = "scatter3d", size = 0.01, mode = "markers", name="投资组合")
plot <- plot %>% add_trace(data = cal_line, x = ~a, y = ~b, z = ~c, type = "scatter3d", mode = "lines", name = "资产配置线", showlegend = TRUE)
plot <- plot %>% add_trace(data = optimal_point, x = ~a, y = ~b, z = ~c, type = "scatter3d", size = 0.05, mode = "markers",
marker = list(color="red"), name = "最优组合", showlegend = TRUE)
plot <- plot %>% # 添加标签和修改格式
layout(scene = list(
yaxis = list(title = "集中度", zeroline = TRUE, zerolinecolor = "black", range = c(0.5,1)),
xaxis = list(title = "风险", zeroline = TRUE, zerolinecolor = "black", range = c(0,0.01)),
zaxis = list(title = "回报", zeroline = TRUE, zerolinecolor = "black", range = c(0.1,0.2))
),margin = list (1,1,1,1),
legend = list(x=0.1, y = 0.9))
print(plot)
4. 往期精选
往期精选 | ||
系列 | 文章传送门 | 实现方式 |
金融杂谈 | 基于均值方差最优化资产配置的模型特性 | Python |
基金市场的冷热传递什么信号? | Python | |
券商金股哪家强——信息比率 | Python | |
从指数构建原理看待A股的三千点魔咒 | Python | |
决策树学习基金持仓并识别公司风格类型 | R | |
垃圾公司对回报率计算的影响几何 | Python | |
市场预测美联储加息的有效性几何 | Python | |
市场风险分析 | Python |