tushare-pro是在旧版本tushare的基础上改进的API接口,本文利用tushare-pro接口搜寻股票,并进行投资组合的优化。这里的优化主要基于马科维兹投资组合理论。
1952年,马科维茨在他的学术论文《资产选择:有效的多样化》中,首次应用资产组合报酬的均值和方差这两个数学概念,从数学上明确地定义了投资者偏好。第一次将边际分析原理运用于资产组合的分析研究。这一研究成果主要用来帮助家庭和公司如何合理运用、组合其资金,以在风险一定时取得最大收益。
证券及其它风险资产的投资首先需要解决的是两个核心问题:预期收益与风险。那么如何测定组合投资的风险与收益和如何平衡这两项指标进行资产分配是市场投资者迫切需要解决的问题。正是在这样的背景下,马科维茨的均值方差模型诞生了。虽然到今天资产配置领域已经发展出了各种各样的理论与模型,但是马科维茨的这套理论堪称资产配置届的“太祖”。
1、投资者在考虑每一次投资选择时,其依据是某一持仓时间内的证券收益的概率分布
2、投资者是根据证券的期望收益率估测证券组合的风险
3、投资者的决定仅仅是依据证券的风险和收益
4、在一定的风险水平上,投资者期望收益最大;相对应的是在一定的收益水平上,投资者希望风险最小
目标函数: m i n 1 2 σ 2 = 1 2 X T V X min\frac{1}{2}{ {\sigma }^{\text{2}}}=\frac{1}{2}{ {X}^{T}}VX min21σ2=21XTVX
限制条件 : u = r p T X ; 1 T X = 1 u={ {r}_{p}}^{T}X; { {1}^{T}}X=1 u=rpTX;1TX=1
1. X X X表示资产分配的权重,这里假设不准卖空,所以 X i X_i Xi都非负。
2. V V V表示的需要资产之间的协方差矩阵,是非退化的。
3. r p r_p rp表示需要投资的资产的预期收益率向量,常常用均值期望向量 ( E ( r 1 ) , E ( r 2 ) , E ( r 3 ) , E ( r 4 ) , ⋯ ) \left(E({ {r}_{1}}),E({ {r}_{2}}),E({ {r}_{3}}),E({ {r}_{4}}),\cdots \right) (E(r1),E(r2),E(r3),E(r4),⋯)来表示。
4.这里用方差表示投资风险,至于 σ 2 \sigma^2 σ2前面的1/2,目的是为了在进行拉格朗日乘数法的时候便于运算和推导。
Ok,现在进行实际数据的一个应用。
利用pro进行收集数据,这里假定需要进行分配的资产的股票代码为"002022.SZ",“002879.SZ”,“300207.SZ”,“300229.SZ”
代码如下所示:
import pandas as pd
import tushare as ts
import numpy as np
import matplotlib.pyplot as plt
from datetime import datetime
ts.set_token('这里填写自己的token')
pro = ts.pro_api()
#利用tusharepro 获取股票数据
#构建的投资组合需要的股票
stockpool=["002022.SZ","002879.SZ","300207.SZ","300229.SZ"]
indexes=pd.date_range('20180301', '20200331')#转换格式 利用datetime
indexes = indexes.map(lambda x: datetime.strftime(x, '%Y%m%d'))
data=pd.DataFrame()
for i in stockpool:
stock=pro.daily(ts_code=i, start_date="20180301", end_date="20200620")
stock.set_index("trade_date", inplace=True)
data[i] = stock["close"]
#将index倒序
data=data.reindex(index=data.index[::-1])
#整理数据
data=data.dropna()
#print(data)
#print(data.iloc[0])
#print(data.iloc[0,0])
#收盘价除以初始值
(data/data.iloc[0]*100).plot(figsize=(8,5))
plt.grid()
plt.show()
结果如下所示:
002022.SZ 002879.SZ 300207.SZ 300229.SZ
trade_date
20180301 13.32 31.64 10.83 13.86
20180302 13.15 30.57 10.65 13.51
20180305 13.20 31.05 10.81 13.80
20180306 13.31 31.89 10.90 13.57
20180307 13.14 33.37 10.71 13.56
... ... ... ... ...
20200615 17.82 15.57 15.65 11.93
20200616 17.84 15.77 16.19 12.01
20200617 18.84 15.61 15.97 11.83
20200618 17.76 15.86 17.57 11.78
20200619 17.40 15.87 18.38 11.95
[479 rows x 4 columns]
Process finished with exit code 0
#给资产随机分配权重
#假设不允许建立空头头寸
noa=4
weights=np.random.random(noa)
#print(np.sum(weights))
weights=weights/np.sum(weights)
计算资产组合的收益、方差、标准差
#计算收益率,此处用log计算,此处用到shift()代表着dataframe所有数据以行的方式向上平移
returns=np.log(data/data.shift(1))
#计算组合收益,组合方差,组合标准差
u=returns.mean()
u=np.mat(u)
ji=returns.cov()
ji=np.mat(ji)
weights=np.mat(weights)
ret=u*weights.T
risk=np.sqrt(weights*ji*weights.T)
蒙特卡洛随机模拟生成大量随机组合,并可视化,这里在均值和方差之外将夏普系数也表示进来。夏普系数即单位风险的超额收益,夏普系数越高,投资的可能性越高。
#利用蒙特卡洛随机模拟生成大量组合
stockreturns=[]
stockrisks=[]
for i in range(10000):
weights = np.random.random(4)
weights = weights / np.sum(weights)
weights = np.mat(weights)
ret=weights*u.T
risk = np.sqrt(weights * ji * weights.T)
stockreturns.append(ret)
stockrisks.append(risk)
stockreturns=np.array(stockreturns).reshape(10000)
stockrisks=np.array(stockrisks).reshape(10000)
#无风险利率设定为
riskfree=0.015
#这里scatter函数的参数c指定颜色
plt.scatter(stockrisks,stockreturns,c=(stockreturns-riskfree)/stockrisks,marker='o')
#设置刻度
plt.ylim(-0.0025,0.0025)
plt.xlim(0.02,0.045)
plt.xlabel(xlabel="stock_risk")
plt.ylabel(ylabel="stock_returns")
plt.colorbar(label='Sharpe ratio')
plt.show()
结果如下:
开始进行组合的优化,这里优化的目标主要有两方面,1是夏普系数尽量大;2是方差即风险尽量小。这里备注一下,由于进行的是规划问题,需要用到python的科学计算模块即scipy的优化器optimize,这里不作关于这个模块的介绍,会单独抽时间来介绍这方面的知识。
Sharp ratio 优化
#######################
#组合优化,目标sharp max
#定义一个函数目标是给一个组合权重,可以显示组合收益,组合风险即标准差,以及单位风险的收益
def statistics(weights):
weights = np.mat(weights)
ret=weights*u.T
risk=np.sqrt(weights*ji*weights.T)
return (ret[0,0],risk[0,0],(ret/risk)[0,0])
#print(statistics([0.1,0.3,0.2,0.4]))
#投资组合优化需要导入科学计算SCIPY模块
import scipy.optimize as sco
#目标是夏普指数越大越好,那么换句话说就是负的指数越小越好
#最小化夏普指数的负值
def min_sharp(weights):
return -statistics(weights)[2]
#定义约束
cons=({
'type':'eq','fun':lambda x:np.sum(x)-1})
#参数设置
bnds=tuple((0,1) for x in range(noa))
opts=sco.minimize(min_sharp,noa*[1./noa,],method='SLSQP',bounds=bnds,constraints=cons)
结果所示:
fun: -0.0339008343793833
jac: array([ 0.00016054, 0.08137522, -0.00010567, 0.03385163])
message: 'Optimization terminated successfully.'
nfev: 49
nit: 8
njev: 8
status: 0
success: True
x: array([3.92869775e-01, 0.00000000e+00, 6.07130225e-01, 1.69135539e-17])
***variance value***优化
#组合优化,目标风险最小即方差最小
#最小化标准差
def min_risk(weights):
return statistics(weights)[1]
optv=sco.minimize(min_risk,noa*[1./noa,],method='SLSQP',bounds=bnds,constraints=cons)
结果所示
fun: 0.02319989025217268
jac: array([0.02311588, 0.02259001, 0.0234072 , 0.02344037])
message: 'Optimization terminated successfully.'
nfev: 31
nit: 5
njev: 5
status: 0
success: True
x: array([0.5711425 , 0.05611909, 0.23356549, 0.13917292])
[0.571 0.056 0.234 0.139]
(0.00045354912047809487, 0.02319989025217268, 0.01954962353477598)
Process finished with exit code 0
构造投资组合的有效前沿,这里我们假设投资人是理性的,即厌恶风险,偏好收益。那么就要满足方差尽量小以及夏普系数尽量大。
#投资组合的有效前沿
#有效前沿是指给定目标收益率,使得方差最小的投资组合
#优化的约束有俩个,1、是指定收益率,2、权重为1
#最小化标准差
def min_risk(weights):
return statistics(weights)[1]
#给定不同的目标收益
target_returns=np.linspace(-0.0015,0.001,100)
target_variance=[]
for tar in target_returns:
cons=({
'type':'eq','fun':lambda x:statistics(x)[0]-tar},{
'type':'eq','fun':lambda x:np.sum(x)-1})
res = sco.minimize(min_risk, noa * [1. / noa, ], method='SLSQP', bounds=bnds, constraints=cons)
target_variance.append(res['fun'])
#print(target_variance)
target_variance=np.array(target_variance)
print(target_variance)
# 圆圈 蒙特卡罗随机模拟的
plt.scatter(stockrisks,stockreturns,c=stockreturns/stockrisks,marker='o')
print(stockrisks)
# 叉号,有效前沿
plt.scatter(target_variance,target_returns,c=target_returns/target_variance,marker='x')
#标记红星 为sharp最高的组合
plt.plot(statistics(opts['x'])[1],statistics(opts['x'])[0],'r*',markersize=15.0)
#标记黄星 为var最低的组合
plt.plot(statistics(optv['x'])[1],statistics(optv['x'])[0],'y*',markersize=15.0)
plt.grid(True)
plt.ylim(-0.0025,0.0025)
plt.xlim(0.02,0.045)
plt.xlabel(xlabel="stock_risk")
plt.ylabel(ylabel="stock_returns")
plt.colorbar(label='Sharpe ratio')
plt.show()
结果所示,
如图我们明显可以看出有效前沿的样子,计算机帮助我们验证了有效前沿。
资产分配计算结果如下:
fun: -0.0339008343793833
jac: array([ 0.00016054, 0.08137522, -0.00010567, 0.03385163])
message: 'Optimization terminated successfully.'
nfev: 49
nit: 8
njev: 8
status: 0
success: True
x: array([3.92869775e-01, 0.00000000e+00, 6.07130225e-01, 1.69135539e-17])
[0.393 0. 0.607 0. ]
(0.000891450860963759, 0.026295838355704083, 0.0339008343793833)
到这里,大致结束了这次简单的应用。
虽然这些理论现在看来可能比较平常,但是放到那个年代,是非常具有开创性的。那个年代的投资分析很少有定量方法,大多都是靠经验和感觉,而且偏收益分析而轻风险分析。马科维茨的理论提供了可靠的量化分析方法均值方差分析方法,将收益和风险共同分析,且给出了有效前沿的模型。用现在的话说,就是大开脑洞。这套理论为投资组合管理未来的发展奠定了基础。