欢迎大家订阅《教你用 Python 进阶量化交易》专栏!为了能够提供给大家更轻松的学习过程,笔者在专栏内容之外已陆续推出一些手记来辅助同学们学习本专栏内容,目前推出的扩展篇链接如下:
第一篇《管理概率==理性交易》
第二篇《线性回归拟合股价沉浮》
第三篇《最大回撤评价策略风险》
第四篇《寻找最优化策略参数》
第五篇《标记A股市场涨跌周期》
第六篇《Tushare Pro接口介绍》
第七篇《装饰器计算代码时间》
第八篇《矢量化计算KDJ指标》
第九篇《移植量化交易小工具》
第十篇《统计学预测随机漫步》
第十一篇《TA-Lib库扩展介绍》
第十二篇《股票分笔数据跨周期处理》
第十三篇《TA-Lib库量价指标分析》
第十四篇《ATR在仓位管理的应用》
第十五篇《扒一扒量化回测常见陷阱》
第十六篇《量化回测工具更新版1》
第十七篇《GUI控件在回测工具上的添加》
第十八篇《文本框显示Tushare股票信息》
第十九篇《建立基于TA-Lib的指标库》
第二十篇《爬虫抓取股票论坛帖子》
第二十一篇《欧奈尔RPS指标的计算》
第二十二篇《GUI显示股票RPS-TOP10》
股票投资中收益和风险始终存在着不确定性,对于理性投资者来说,他们倾向于在风险和收益之间达到均衡点。对于单只股票来说,在回测中使用夏普比率可以很好地去综合考虑收益与风险之间的关系。而对于多只股票组合投资时,会引入基金经理常常需要考虑的问题——仓位如何分配?
本节给大家分享一个经典的模型——Markowitz均值-方差投资组合理论模型,一起来了解下如何运用Markowitz模型在多种资产上确定最优的投资比例。
1952年,美国芝加哥大学的经济学家Markowitz应用了数学中的均值、方差概念来定义资产组合中收益和风险这两个关键因素,从而系统地阐述了资产组合和选择问题,标志着现代资产组合理论(Modern Portfolio Theory,简称MPT)的开端。
该理论认为,由于资产投资的风险在于收益的不确定性,因此可将收益率视为一个随机变量,投资组合的期望收益是该随机变量的期望值,收益的波动率用随机变量的方差/标准差来表示,以此衡量投资组合的风险。
在波动率为横坐标、收益率为纵坐标的二维平面中描绘各种优化投资组合,从而形成了一条曲线。这条曲线在最小方差点以上的部分就是著名的Markowitz投资组合“有效前沿”(Efficient Frontier,有效边界),对应的投资组合称为有效投资组合。
根据投资组合理论,我们可以对多只股票(资产)的组合配置进行以下的优化过程:
- 获取多只股票数据,分析股票的收益率和波动率
- 生成大量随机权重的收益/风险组合
- 找到风险最小时的投资组合
- 找到有效边界(有效前沿)
- 找到夏普率较高的随机组合,即收益-风险均衡点
首先我们选取002372伟星新材、000876新希望、000851高鸿股份、600797浙大网新、000651格力电器这5只股票进行分析,并且将代码名称以列表格式存储,如下所示:
# 构建一个股票代码列表
stock_set = ['002372.SZ','000876.SZ','000851.SZ','600797.SH','000651.SZ'] # 伟星新材、新希望、高鸿股份、浙大网新、格力电器
构建一个空的DataFrame表格,用于存储获取的股票数据,然后分别下载这5只股票的收盘价数据,并且合并至表格中,如下所示:
for code in stock_set:
df = get_stdat(code, start='20190101', end='20200101')
df.rename(columns={'close':code.split('.')[0]}, inplace = True) # df.columns = ['a','b','c']
if df_close.empty == True:
df_close = df
else:
df_close = df_close.join(df, how='outer')
"""
002372 000876 000851 600797 000651
trade_date
2019-01-02 14.63 7.40 5.03 7.30 35.80
2019-01-03 14.25 7.42 5.12 7.26 35.92
2019-01-04 14.49 7.52 5.49 7.51 36.65
2019-01-07 14.90 7.82 5.79 7.77 36.48
2019-01-08 15.18 7.60 5.79 7.67 36.34
2019-01-09 15.30 7.73 5.80 7.69 37.51
2019-01-10 15.37 7.60 5.84 7.61 37.76
... ... ... ... ... ...
2019-01-11 15.49 7.64 5.67 7.74 37.73
2019-01-14 15.45 7.77 5.74 7.68 37.68
2019-01-15 15.87 7.77 5.58 7.99 38.78
2019-01-16 16.07 7.64 5.46 7.86 39.31
"""
然后计算每个股票的收益率,此处计算的是对数收益率,具体方法可参考专栏介绍。如下所示:
df = np.log(df_close / df_close.shift(1)).dropna()
"""
002372 000876 000851 600797 000651
trade_date
2019-01-03 -0.026317 0.002699 0.017734 -0.005495 0.003346
2019-01-04 0.016702 0.013387 0.069774 0.033856 0.020119
2019-01-07 0.027902 0.039118 0.053204 0.034035 -0.004649
2019-01-08 0.018618 -0.028536 0.000000 -0.012954 -0.003845
2019-02-20 0.040290 0.023878 -0.009285 -0.012136 0.008258
... ... ... ... ... ...
2019-11-18 0.015429 0.047883 0.003650 0.032485 0.002206
2019-11-19 0.034841 0.029960 0.032261 0.010899 0.007936
2019-11-20 -0.018854 -0.018676 -0.012423 -0.038679 -0.010142
"""
我们可视化每只股票收益率的直方图,了解一下分布情况。可见每支股票的分布形状是近视正态分布,但仍然有“肥尾”现象。
在构建最优的投资组合过程中,计算投资组合的协方差矩阵是非常关键的一步。接下来我们可以运用Pandas内置的cov()方法计算协方差矩阵。如下所示:
# 计算协方差矩阵
cov_mat = df.cov()
cov_mat_annual = cov_mat * 252
"""
002372 000876 000851 600797 000651
002372 0.180281 0.040032 0.042272 0.022727 0.030380
000876 0.040032 0.293750 0.046080 0.085311 0.013274
000851 0.042272 0.046080 0.282092 0.161818 0.052123
600797 0.022727 0.085311 0.161818 0.245785 0.034793
000651 0.030380 0.013274 0.052123 0.034793 0.113990
"""
接下来我们可以用蒙特卡洛法随机生成多组权重,并根据权重计算出组合收益率和组合波动率。需要注意到此处的权重系数范围在0-1之间,并且权重系数总和为1。
annual_profit = df.mul(weights, axis=1).sum(axis=1).mean() * 252 # mean_profit * 252
# 年化标准差--Volatility
Random_Volatility = np.sqrt(np.dot(weights.T, np.dot(cov_mat_annual, weights)))
设定此过程重复5000次,即得到5000种组合收益率和波动率,当样本数越多就越能覆盖到各种权重组合,包括常用的等权重投资组合、市值加权投资组合。
然后将数据可视化为散点图。如下所示:
图中横坐标是代表风险的标准差,纵坐标是收益率,每一个点都代表着一种投资组合的情况。
我们作为理性的投资者来选择有效边界上的某一点作为投资组合。接下来介绍一个能够同时对收益与风险加以综合考虑的经典指标——夏普比率(Sharpe Ratio)。夏普比率计算的是每承受一单位的风险所产生的超额回报,它是基金绩效评价标准化指标。
接下来我们计算上述蒙特卡洛模拟的组合所对应的夏普比率,并将之作为第三个变量绘制在收益-风险的散点图中,此处采用颜色这一视觉线索来表征夏普比率。如下所示:
# 设置无风险回报率为3
RP = Random_Portfolios.Returns
RF = 3
QP = Random_Portfolios.Volatility
# 计算每项资产的夏普比率
Random_Portfolios['Sharpe'] = (RP - RF) / QP
# 绘制收益-标准差的散点图,并用颜色描绘夏普比率
plt.scatter(QP, RP, c=Random_Portfolios.Sharpe)
plt.ylabel(u'期望收益率')
plt.xlabel(u'标准差')
plt.colorbar(label=u'夏普比率')
plt.show()
接着开始找到夏普比率最大的组合,将其绘制在收益-风险的散点图中。如下所示:
# 找到夏普比率最大数据对应的索引值
max_index = Random_Portfolios.Sharpe.idxmax()
我们提取出最大夏普比率投资组合对应的权重,并转化为Numpy数组,如下所示:
[0.12617847 0.74273907 0.04905298 0.00580558 0.07622391]
由此处5只股票组成的例子可知。12.6%的权重买002372伟星新材,74.3%的权重买000876新希望,4.9%的权重买000851高鸿股份,0.5%的权重买600797浙大网新,7.6%的权重买000651格力电器。这样的组合,根据以往数据,可以分析得出组合年化收益率为72%,波动率为42.6%,夏普率为1.61。
Markowitz的投资组合理论揭示了“资产的期望收益由其自身的风险的大小来决定”这个结论。同时有效边界上也印证了风险与收益成正比,要想更高的收益率就需要承担更大的风险,但投资组合在有效边界上,则是性价比最高的组合。
关于完整代码可加入专栏交流群获取。更多的量化交易内容欢迎大家订阅专栏阅读!!