1. Black-Litterman模型简介
在python实现资产配置(1)----Markowitz 投资组合模型中, 我们已经见过如何使用Markowitz求得最优资产配比. 这是一种在已知未来各资产的概率分布,然后再求解的方法.
Markowitz模型输入参数包括历史数据法和情景分析法两种方法,情景分析法的缺点是主观因素,随意性太强,因此使用历史数据法, 将资产的均值和协方差输入模型是比较常见的作法. 不过, 不足之处很明显: 未来的资产收益率分布不一定与过去相同. 此外, Markowitz 模型结果对输入参数过于敏感.
Black-Litterman模型就是基于此的改进. 其核心思想是将投资者对大类资产的观点 (主观观点) 与市场均衡收益率 (先验预期收益率)相结合,从而形成新的预期收益率(后验预期收益率). 这里的先验预期收益率的分布可以是贝叶斯推断中的先验概率密度函数的多元正态分布形式,投资者的主观观点就是贝叶斯推断中的似然函数(可以看作新的信息, 因为做出主观判断必然是从外界获取得到了这些资产的收益率变化信息), 而相应的, 后验预期收益率也可以从后验概率密度函数中得到. 具体的推导可以看我的这篇文章:从贝叶斯定理到贝叶斯推断.
BL模型的求解步骤包括下面几步:
(1) 使用历史数据估计预期收益率的协方差矩阵作为先验概率密度函数的协方差.
(2) 确定市场预期之收益率向量, 也就是先验预期收益之期望值. 作为先验概率密度函数的均值. 或者使用现有的期望值和方差来反推市场隐含的均衡收益率(Implied Equilibrium Return Vector), 不过在使用这种方法时, 需要知道无风险收益率的大小.
(3) 融合投资人的个人观点,即根据历史数据(看法变量的方差)和个人看法(看法向量的均值)
(4) 修正后验收益.
是均衡收益率协方差的调整系数,可以根据信心水平来判断. 是历史资产收益率的协方差矩阵, P是投资者的观点矩阵,是似然函数(即投资者观点函数)中的协方差矩阵,其值为的对角阵, 是先验收益率的期望值.
(5) 投资组合优化: 将修正后的期望值与协方差矩阵即重新代入Markowitz投资组合模型求解.
2. Black-litterman 模型的python实现
(1)定义求解函数,输入为投资者观点P,Q以及目前资产的市场收益率矩阵,输出为后验的市场收益率和协方差矩阵.
import numpy as np
import baostock as bs
import pandas as pd
from numpy import linalg
def blacklitterman(returns,tau,P,Q):
mu = returns.mean()
sigma = returns.cov()
pil = np.expand_dims(mu,axis = 0).T
ts = tau * sigma
ts_1 = linalg.inv(ts)
Omega = np.dot(np.dot(P,ts), P.T)* np.eye(Q.shape[0])
Omega_1 = linalg.inv(Omega)
er = np.dot(linalg.inv(ts_1 + np.dot(np.dot(P.T,Omega_1),P)),(np.dot(ts_1 ,pil)+np.dot(np.dot(P.T,Omega_1),Q)))
posterirorSigma = linalg.inv(ts_1 + np.dot(np.dot(P.T,Omega_1),P))
return [er, posterirorSigma]
(2) 实列分析
我们继续研究python实现资产配置(1)----Markowitz 投资组合模型中的五支股票: 白云机场, 福建高速, 华夏银行, 生益科技和浙能电力. 假设现在分析师的观点为:
- 白云机场, 华夏银行, 浙能电力, 生益科技四只股票的日均收益率均值为0.3%
- 白云机场和福建高速的日均收益率均值高于浙能电力0.1%
则投资者观点矩阵P为:
Q 为:
则获取后验收益率和协方差的代码为:
pick1 = np.array([1,0,1,1,1])
q1 = np.array([0.003*4])
pick2 = np.array([0.5,0.5,0,0,-1])
q2 = np.array([0.001])
P = np.array([pick1,pick2])
Q = np.array([q1,q2])
获取股票数据, 并且获得后验的均值和方差:
def get_stock_data(t1,t2,stock_name):
lg = bs.login()
print('login respond error_code:' + lg.error_code)
print('login respond error_msg:' + lg.error_msg)
#### 获取沪深A股历史K线数据 ####
# 详细指标参数,参见“历史行情指标参数”章节
rs = bs.query_history_k_data(stock_name,
"date,code,open,high,low,close,preclose,volume,amount,adjustflag,turn,tradestatus,pctChg,isST",
start_date=t1, end_date=t2,
frequency="d", adjustflag="3")
print('query_history_k_data respond error_code:' + rs.error_code)
print('query_history_k_data respond error_msg:' + rs.error_msg)
#### 打印结果集 ####
data_list = []
while (rs.error_code == '0') & rs.next():
# 获取一条记录,将记录合并在一起
data_list.append(rs.get_row_data())
result = pd.DataFrame(data_list, columns=rs.fields)
print(result)
#### 结果集输出到csv文件 ####
result.to_csv("D:\stockdata\history_A_stock_k_data.csv", index=False)
print(result)
#### 登出系统 ####
bs.logout()
result['date'] = pd.to_datetime(result['date'])
result.set_index("date", inplace=True)
return result
byjc = get_stock_data('2014-1-1','2015-1-1','sh.600004')
hxyh = get_stock_data('2014-1-1','2015-1-1','sh.600015')
zndl = get_stock_data('2014-1-1','2015-1-1','sh.600023')
fjgs = get_stock_data('2014-1-1','2015-1-1','sh.600033')
sykj = get_stock_data('2014-1-1','2015-1-1','sh.600183')
by = byjc['pctChg']
by.name = 'byjc'
by = pd.DataFrame(by,dtype=np.float)/100
hx = hxyh['pctChg']
hx.name = 'hxyh'
hx = pd.DataFrame(hx,dtype=np.float)/100
zn = zndl['pctChg']
zn.name = 'zndl'
zn = pd.DataFrame(zn,dtype=np.float)/100
fj = fjgs['pctChg']
fj.name = 'fjgs'
fj = pd.DataFrame(fj,dtype=np.float)/100
sy = sykj['pctChg']
sy.name = 'sykj'
sy = pd.DataFrame(sy,dtype=np.float)/100
sh_return = pd.concat([by,fj,hx,sy,zn],axis=1)
res = blacklitterman(sh_return,0.1,P,Q)
p_mean = pd.DataFrame(res[0],index = sh_return.columns, columns = ['posterior_mean'])
p_cov = res[1]
print(p_mean)
print(p_cov)
这时候,已经可以使用Markowitz模型进行资产的配置. 定义新的函数blminVar以求解资产配置权重. 该函数的输入变量为blacklitterman函数的输出结果, 以及投资人的目标收益率goalRet.假设目标收益率为年化70%,则goalRet = 0.7:
def blminVar(blres, goalRet):
covs = np.array(blres[1],dtype=float)
means = np.array(blres[0],dtype=float)
L1 = np.append(np.append(covs.swapaxes(0,1),[means.flatten()],axis=0),
[np.ones(len(means))],axis=0).swapaxes(0,1)
L2 = list(np.ones(len(means)))
L2.extend([0,0])
L3 = list(means)
L3.extend([0,0])
L4 = np.array([L2,L3],dtype=float)
L = np.append(L1,L4,axis=0)
results = linalg.solve(L,np.append(np.zeros(len(means)),[1,goalRet]))
return pd.DataFrame(results[:-2],columns = ['p_weight'])
blresult = blminVar(res,0.70/252)
print(blresult)
输出结果为:
0-5分别对应上面的五只股票.
参考文献
- 蔡立耑:量化投资——以python为工具. 电子工业出版社
- 华泰证券: 周期轮动下的BL资产配置策略
- 中信建投:基于Black-Litterman的多资产配置策略