请 [注册] 或 [登录]  | 返回主站

量化交易吧 /  量化平台 帖子:3365793 新帖:0

投资组合理论代码实现

fx1118发表于:5 月 9 日 22:29回复(1)

本文通过代码实现投资组合理论,用到了scipy.optimize包,主要用于优化问题,对想学习python优化实现的小伙伴有帮助。

投资组合理论可以在不增加任何开销的情况下,提高收益或降低风险。在选股之后,简单的平均分配不是最佳选择,通过投资组合理论计算出的权重在投资效益上有明显提高。

代码实现了五种可选方法:平均分仓,等波动率,最小方差,风险平价,最大夏普比率。原理网上可以轻松找到,此处不再赘述。
聚宽平台提供了投资组合优化器,但只能在平台上调用,如果想搭建自己的量化平台或者数据量较大时都需要有自己的投资组合模块。

本文的代码基本实现了聚宽投资组合优化器功能,有部分功能未实现,但在此代码基础上添加也不难,有兴趣的小伙伴可以自己完善。

import pandas as pdimport numpy as npfrom jqdata import jyfrom jqdata import *import seaborn as snsimport matplotlib.pyplot as pltfrom cvxopt import solvers, matriximport datetimefrom scipy.optimize import minimizefrom jqlib.optimizer import *import pickleimport warningswarnings.filterwarnings('ignore')import matplotlibmatplotlib.rcParams['font.family']='serif'matplotlib.rcParams['axes.unicode_minus']=False # 处理负号
#获取所需的数据s_date,e_date = '2017-11-10','2018-02-10'moneyfund_price = get_price('511880.XSHG',start_date=s_date,end_date=e_date)
hy_df = get_industries(name='sw_l1')
#获取所需的数据s_date,e_date = '2012-11-10','2018-12-10'index_list = ['PrevClosePrice','OpenPrice','HighPrice','LowPrice','ClosePrice','TurnoverVolume',  'TurnoverValue','TurnoverDeals','ChangePCT','UpdateTime']moneyfund_price = get_price('511880.XSHG',start_date=s_date,end_date=e_date)#通过聚源数据获取申万行业指数行情数据(输入行业指数代码)def get_SW_index(SW_index = 801010,start_date = '2017-01-31',end_date = '2018-01-31'):jydf = jy.run_query(query(jy.SecuMain).filter(jy.SecuMain.SecuCode == str(SW_index)))link=jydf[jydf.SecuCode==str(SW_index)]rows=jydf[jydf.SecuCode==str(SW_index)].index.tolist()result=link['InnerCode'][rows]df = jy.run_query(query(jy.QT_SYWGIndexQuote).filter(jy.QT_SYWGIndexQuote.InnerCode==str(result[0]),\                                                   jy.QT_SYWGIndexQuote.TradingDay>=start_date,\                                                         jy.QT_SYWGIndexQuote.TradingDay<=end_date))df.index = df['TradingDay']df = df[index_list]return dfdef get_industry_data(start_date,end_date,industry_list,field='ClosePrice'):l = []for ind in industry_list:ind_price = get_SW_index(ind,start_date,end_date)price = ind_price[field]l.append(price)all_price_df = pd.concat(l,axis=1)all_price_df.columns = industry_listreturn all_price_dfind_df = get_industries(name='sw_l1')ind_list = ind_df.indexall_price_df = get_industry_data(s_date,e_date,ind_list)pct_daily = all_price_df.pct_change().dropna()
with open('invest_portfolio.pkl','wb') as pk_file:pickle.dump(pct_daily,pk_file)

以下模块可在本地运行¶

with open('invest_portfolio.pkl','rb') as pf:daily_profit = pickle.load(pf)columns = daily_profit.columnsdate_list = list(daily_profit.index)start_date = date_list[0]end_date = date_list[-1]
#获取日期列表def get_tradeday_list(start,end,frequency=None):'''    input:    start:str or datetime,起始时间,与count二选一    end:str or datetime,终止时间    frequency:        str: day,month,quarter,halfyear,默认为day        int:间隔天数    count:int,与start二选一,默认使用start    '''if isinstance(frequency,int):all_trade_days = get_trade_days(start,end)trade_days = all_trade_days[::frequency]days = [datetime.datetime.strftime(i,'%Y-%m-%d') for i in trade_days]return daysdf = daily_profit.iloc[:,:2]if frequency == None or frequency =='day':days = df.indexelse:df['year-month'] = [str(i)[0:7] for i in df.index]if frequency == 'month':days = df.drop_duplicates('year-month').indexelif frequency == 'quarter':df['month'] = [str(i)[5:7] for i in df.index]df = df[(df['month']=='01') | (df['month']=='04') | (df['month']=='07') | (df['month']=='10') ]days = df.drop_duplicates('year-month').indexelif frequency =='halfyear':df['month'] = [str(i)[5:7] for i in df.index]df = df[(df['month']=='01') | (df['month']=='06')]days = df.drop_duplicates('year-month').indexif isinstance(days[0],str):return list(days)else:trade_days = [datetime.datetime.strftime(i,'%Y-%m-%d') for i in days]return trade_daysdate_list_str = get_tradeday_list(start_date,end_date,frequency='day')daily_profit.index = date_list_strtrade_days = get_tradeday_list(start_date,end_date,frequency='quarter')trade_days_month = get_tradeday_list(start_date,end_date,frequency='month')daily_profit = daily_profit.loc[:,columns]
def portfolio_optimizer_function(daily_profit,date, target,count=250, bounds=(0.0, 1), \                                 default_port_weight_range=[0.99, 1.0], ftol=1e-9,rf=0.04):'''    input:    daily_profit:dataframe,index为时间,columns为投资组合的标的代码,values为每日收益率    date:优化发生的日期,也就是调仓时间    target: 优化目标函数,只能选择一个,共五个可选 'mean_weights','mean_vol','min_var','risk_parity','max_sharpe'    count:向前计算的数据长度,默认250,过去一年的数据    bounds: 边界函数,用以对组合中单标的权重进行限制,可设置一个或多个相同/不同类别的函数。如果不填,默认为 Bound(0., 1.);\    如果有多个 bound,则每只股票取对应的边界值    default_port_weight_range: 长度为2的列表,默认的组合权重之和的范围,默认值为 [0.0, 1.0]。如果限制函数(constraints) 中没有 WeightConstraint 或 WeightEqualConstraint 限制,则会添加 WeightConstraint(low=default_port_weight_range[0], high=default_port_weight_range[1]) 到 constraints列表中。    ftol: 默认为 1e-9,优化函数触发结束的函数值。当求解结果精度不够时可以适当降低 ftol 的值,当求解时间过长时可以适当提高 ftol 值    rf:计算夏普比率时用到的无风险利率,默认0.04    '''date_list = list(daily_profit.index)start_date = date_list[0]end_date = date_list[-1]date_list_str = get_tradeday_list(start_date,end_date,frequency='day')end_date_index = date_list_str.index(date)if end_date_index < 250:print('data not enouph to current date')return Noneelse:start_date_index = end_date_index - 250pct_temp = daily_profit.iloc[start_date_index:end_date_index]cov_mat = pct_temp.cov()omega = np.matrix(cov_mat.values)#初始值x0 = np.ones(omega.shape[0]) / omega.shape[0]  #每个变量的取值范围if isinstance(bounds,tuple):bnds = tuple(bounds for x in x0)elif isinstance(bounds,list):bnds = boundselse:print('error:please input correct bounds')return Nonecons = ({'type':'ineq', 'fun': lambda x: sum(x) - default_port_weight_range[0]},\           {'type':'ineq', 'fun': lambda x: -sum(x) + default_port_weight_range[1]})options={'disp':False, 'maxiter':1000, 'ftol':ftol}
        if target == 'mean_weights':weights = np.array([1.0/pct_temp.shape[1]]*pct_temp.shape[1])return weightselif target == 'mean_vol':wts = 1/pct_temp.std()weights = wts/wts.sum()return weights.valueselif target == 'min_var':#最小方差def min_var(x):risk =  np.dot(x.T,np.dot(cov_mat,x))return riskweights = minimize(min_var,x0,bounds=bnds,constraints=cons,method='SLSQP',options=options)['x']return weightselif target == 'risk_parity': #风险平价def risk_parity(x):tmp = (omega * np.matrix(x).T).A1z = np.sqrt(np.matrix(x) * omega * np.matrix(x).T).A1[0]risk = x * tmp/zdelta_risk = [sum((i - risk)**2) for i in risk]return sum(delta_risk)weights = minimize(risk_parity,x0,bounds=bnds,constraints=cons,method='SLSQP',options=options)['x']return weightselif target == 'max_sharpe':#最大夏普def max_sharpe(x):r_b = rfpct_mean = pct_temp.mean()p_r = (1+np.dot(x,pct_mean))**250-1p_sigma = np.sqrt(np.dot(x.T,np.dot(cov_mat,x))*250)p_sharpe = (p_r-r_b)/p_sigmareturn -p_sharpeweights = minimize(max_sharpe,x0,bounds=bnds,constraints=cons,method='SLSQP',options=options)['x']return weights
def cal_portfolio_profit(daily_profit,trade_days,target):'''    daily_profit:dataframe,index为时间,columns为投资组合的标的代码,values为每日收益率    target: 优化目标函数,只能选择一个,共五个可选 'mean_weights','mean_vol','min_var','risk_parity','max_sharpe'    trade_days:list,交易时间列表    '''l = []for i in range(len(trade_days)-1):weights = portfolio_optimizer_function(daily_profit,trade_days[i],target)profit_cut = (daily_profit.loc[trade_days[i]:trade_days[i+1]] * weights)profit_sum = profit_cut.cumsum(axis=1).iloc[:,-1]profit = (profit_sum + 1).cumprod().values[-1] -1l.append(profit)df = pd.DataFrame(l,columns=[target])df[target+'_cumprod'] = (df[target]+1).cumprod()return df
weights_name = [ 'mean_weights','mean_vol','min_var','risk_parity','max_sharpe']l = []for name in weights_name:profit = cal_portfolio_profit(daily_profit,trade_days_month[-30:],name)profit_cumprod = profit[name+'_cumprod']l.append(profit_cumprod)profit_df = pd.concat(l,axis=1)profit_df.plot(figsize=(15,7))
<matplotlib.axes._subplots.AxesSubplot at 0x7f42e81a8748>

全部回复

0/140

量化课程

    移动端课程