多股票策略回测时常常遇到问题。
仓位如何分配?
其实,这个问题,好多好多年前马科维茨(Markowitz)我喜爱的小马哥就给出答案——投资组合理论。
根据这个理论,我们可以对多资产的组合配置进行三方面的优化。
1.找到有效前沿。在既定的收益率下使组合的方差最小。
2.找到sharpe最优的组合(收益-风险均衡点)
3.找到风险最小的组合
该理论基于用均值和方差来表述组合的优劣的前提。将选取几只股票,用蒙特卡洛模拟初步探究组合的有效前沿。
通过最大Sharpe和最小方差两种优化来找到最优的资产组合配置权重参数。
最后,刻画出可能的分布,两种最优以及组合的有效前沿。
1)选取A股所有股票2018年的EOD数据(数据事先已经下载好)
import pandas as pd
import numpy as np
data=pd.read_csv(r'C:\Users\yi\Desktop\Study\量化交易\stock2018.csv',sep=',',engine ='python')
查看DataFrame信息:
画一只股票的图形看看:
#切出需要的字段
EOD=data.iloc[:,[0,1,6]].sort_values('trade_date')
#按股票代码分组
stock=EOD.groupby(['ts_code'])
#取出'000063.SH'组的数据
zte=stock.get_group(['000063.SH'])
zte.plot('trade_date','close',figsize=(25,15))
stock_means=pd.DataFrame(columns=['ts_code', 'returnmean'])
j=0
for i in stock.nunique().index:
if j>10:
break
#取代码为i的股票的close数据
sclose=stock.get_group(i)['close']
#计算对数收益
returns = np.log(sclose / sclose.shift(1))
#计算年化对数收益
returnmean=returns.mean()*252
#将所有股票的年化对数收益写入列表
transit=pd.DataFrame({'ts_code':[0], 'returnmean':[0]})
transit['ts_code']=i
transit['returnmean']=returnmean
stock_means=stock_means.append(transit,ignore_index=True)
print('code=%s returnmean=%s'%(i,returnmean))
#returns.hist(bins=50, figsize=(12, 9))
j+=1
画出这12只股票的收益直方图,可以看出大体是正态分布.
去掉计数器j,对所有股票跑一边,得到stock_means数据:
stock_means.describe()
Out[69]:
returnmean
count 3556.000000
mean -0.373557
std 1.111227
min -26.589527
25% -0.573231
50% -0.381268
75% -0.195562
max 24.007412
我去,简直坑死人,2018年所有股票的平均指数收益率是-0.373557,-0.195562的成绩就已经跑赢了75%的股票~就问你坑不坑?
我们看看最坑的和最牛的5个都是谁:
stock_means.sort_values('returnmean').head(5)
Out[74]:
ts_code returnmean
536 002075.SZ -26.589527
244 000693.SZ -12.881314
2184 600086.SH -8.144002
1195 002739.SZ -7.548460
608 002147.SZ -7.318664
stock_means.sort_values('returnmean').tail(5)
Out[75]:
ts_code returnmean
2972 601162.SH 13.214315
2115 300751.SZ 15.765417
3001 601319.SH 23.970533
3226 603220.SH 23.990517
2043 300674.SZ 24.007412
年度最坑奖:沙钢股份
年度最牛奖:没有评的意义,基本全是新股,看来市场还是相当不理性的
*注:全部代码
import pandas as pd
import numpy as np
data=pd.read_csv(r'D:\Study\stock2018.csv',sep=',',engine ='python')
EOD=data.set_index(['trade_date'],drop=True).drop(['pre_close','change','pct_change','trade_date.1'],axis=1).sort_index()
p_close=EOD['close']
#EOD.pivot_table(index='ts_code',values=['open','high','low','close'],aggfunc=(np.log(p_close/p_close.shift(1)).mean))
EODG=EOD.groupby(['ts_code'])
j=0
lreturn_list=pd.DataFrame()
for i in EODG.groups.keys():
pclose=EODG.get_group(i)['close']
stock_code=i
log_return=np.log(pclose/pclose.shift(1))
lr=pd.DataFrame({'log_return':log_return})
log_return_mean=log_return.mean()*len(log_return)
transit=pd.DataFrame({'ts_code':[i],'log_return_mean':[log_return_mean]})
if j==0:
lreturn_mean=transit
else:
lreturn_mean=lreturn_mean.append(transit)
lreturn_list[i]=log_return
#print ('stock %s :log return is %s'%(i,log_return))
print ('%s/%s stock %s :mean log return is %s'%(len(EODG.groups.keys()),j+1, i,log_return_mean))
j=j+1
#if j>2000:
# break
cov_dual_stock=pd.DataFrame()
a=0
for st1 in lreturn_list.columns:
for st2 in lreturn_list.columns:
if st1!=st2:
cov2=lreturn_list.loc[:,[st1,st2]].cov()*len(log_return)
print(cov2)
cov_dual_stock=cov_dual_stock.append(cov2)
a+=1
if a>10:
break