基於風險平價的資產配置策略
阿新 • • 發佈:2019-01-30
程式碼如下:
####### step1 import pandas as pd import numpy as np import matplotlib.pyplot as plt from sklearn.preprocessing import StandardScaler from scipy.optimize import minimize # 讀取收盤價資料 def read_data(file): s="C:/Users\Anita\Desktop" file_address=s+file df= pd.read_excel(file_address,sheetname="data") # 保證金比例 marginal_cost=[0.15,0.3,0.02,0.15,1,1] # 刪除前兩行說明行 df.drop(0,inplace=True) df.drop(1,inplace=True) # 刪除最後一行2018年的3月資料 df.drop(1497,inplace=True) # 把日期列設定為索引 df.set_index('Date',inplace=True) # 缺失值檢驗:沒有缺失值 df.isnull().any() # 把日資料轉換為月資料 period_type = 'M' __df= df.resample(period_type).last() monthly_df=pd.concat([df.iloc[0,0::].T,__df.T],axis=1).T return monthly_df # 計算對數收益率 def calculate_log_return(file): data=read_data(file) variable_list=['IF00.CFE','IC00.CFE','TF00.CFE','AU00.SHF','159920.OF','511880.SH'] for j in variable_list: data['return_'+str(j)]=0.0 for i in range(1,len(data)): data['return_'+str(j)][i]=np.log(float(data[j][i])/float(data[j][i-1])) return data.iloc[1::,0::] # 將收益率進行標準化處理,形成新的表格std_data def standardlize_process(file): data=calculate_log_return(file) scaler=StandardScaler() scaler.fit(data) column_list=['IF00.CFE','IC00.CFE','TF00.CFE','AU00.SHF','159920.OF','511880.SH','return_IF00.CFE','return_IC00.CFE','return_TF00.CFE','return_AU00.SHF','return_159920.OF','return_511880.SH'] std_data=pd.DataFrame(scaler.transform(data),columns=column_list,index=data.index) std_data=std_data.iloc[:,6::] return std_data #資產收益率序列相關性檢驗:滾動計算資產的協方差矩陣 def cov_asset(file): return standardlize_process(file).rolling(window=12).cov().iloc[66::,0::] # 風險度量期間為一年,故滾動視窗設定為12個月,從2012-12月末到2018-02月末共計 63個月,每個月6條記錄,共63*6=378條記錄 # 協方差矩陣 cov_matrix=np.matrix(cov_asset("\資料.xlsx")) ##### step2 # 計算每類資產對總資產組合的風險貢獻 def calculate_risk_contribution(weight,one_cov_matrix): weight=np.matrix(weight) sigma=np.sqrt(weight*one_cov_matrix*np.matrix(weight).T) # 邊際風險貢獻 Marginal Risk Contribution (MRC) MRC=one_cov_matrix*weight.T/sigma # 風險貢獻 Risk Contribution (RC) RC=np.multiply(MRC,weight.T) return RC # 定義優化問題的目標函式,即最小化資產之間的風險貢獻差 def risk_budget_objective(x_weight,parameters): # x_weight是帶求解的各個大類資產下面子標的的權重,parameters是下面函式中的args=[one_cov_matrix,RC_set_ratio]引數傳遞 # x_weight的初始值是函式 calculate_portfolio_weight中的 weight0 # 協方差矩陣,也即函式 calculate_portfolio_weight 中傳遞的 args中的第一個引數 one_cov_matrix one_cov_matrix=parameters[0] # 風險平價下的目標風險貢獻度向量,也即函式 calculate_portfolio_weight 中傳遞的 args中的第二個引數 RC_set_ratio RC_target_ratio=parameters[1] # RC_target為風險平價下的目標風險貢獻,一旦引數傳遞以後,RC_target就是一個常數,不隨迭代而改變 sigma_portfolio=np.sqrt(x_weight*one_cov_matrix*np.matrix(x_weight).T) RC_target=np.asmatrix(np.multiply(sigma_portfolio,RC_target_ratio)) # RC_real是 每次迭代以後最新的真實風險貢獻,隨迭代而改變 RC_real=calculate_risk_contribution(x_weight,one_cov_matrix) sum_squared_error= sum(np.square(RC_real-RC_target.T))[0,0] return sum_squared_error # 優化問題的第一個約束條件 def constraint1(x_weight): return np.sum(x_weight)-1.0 # 優化問題的第二個約束條件 def constraint2(x_weight): return x_weight # 根據資產預期目標風險貢獻度來計算各資產的權重 def calculate_portfolio_weight(RC_set_ratio,one_cov_matrix): weight0=[0.2, 0.2, 0.2, 0.1, 0.1, 0.2] cons=({'type': 'eq', 'fun': constraint1},{'type': 'ineq', 'fun': constraint2}) res= minimize(risk_budget_objective, weight0, args=[one_cov_matrix,RC_set_ratio], method='SLSQP',constraints=cons, options={'disp': True}) weight_final= np.asmatrix(res.x) return weight_final def calculate_monthly_weight(RC_set_ratio): result={} year=['2012','2013','2014','2015','2016','2017','2018'] month=['01','02','03','04','05','06','07','08','09','10','11','12'] for i in range(63): date=str(2012+int((i+11)/12))+'-'+ month[(i-1)%12] one_cov_matrix=cov_matrix[i*6:(i*6+6)] result[date]=pd.DataFrame(calculate_portfolio_weight(RC_set_ratio,one_cov_matrix)).iloc[0,0::] #xx=pd.DataFrame(result,index=date) return result ## 計算風險平價下每月的資產配置權重 # 假設1:四個資產的風險貢獻度相等 # 假設2:同類資產下每個標的的風險貢獻相同 df_monthly_weight=pd.DataFrame(calculate_monthly_weight([0.25/3, 0.25/3, 0.25, 0.25, 0.25/3, 0.25])).T df_monthly_weight.columns=['IF00.CFE','IC00.CFE','TF00.CFE','AU00.SHF','159920.OF','511880.SH'] df_monthly_weight #### step3:回測Backtest ## 基於過去一年的風險分配權重,計算各資產的月度收益率 def calculate_RC_monthly_return(file): std_df=standardlize_process(file) date_list=[] for i in range(63): date_list.append(str(2012+int((i+11)/12))+'-'+ month[(i-1)%12]) #date_list _std_df=std_df.iloc[11::,0::].T _std_df.columns=date_list __std__df=_std_df.T __std__df['key']=__std__df.index df_monthly_weight['key']=df_monthly_weight.index # 合併表,將月收益資料和權重表橫向合併 monthly_return=pd.merge(__std__df, df_monthly_weight,on='key') monthly_return.set_index('key',inplace=True) column_list=['return_IF00.CFE','return_IC00.CFE','return_TF00.CFE','return_AU00.SHF','return_159920.OF','return_511880.SH','IF00.CFE','IC00.CFE','TF00.CFE','AU00.SHF','159920.OF','511880.SH'] # 計算加權後的各資產收益率:權重乘以資產收益率 for i in range(6): monthly_return['weight'+'_'+column_list[i]]=monthly_return[column_list[i]]*monthly_return[column_list[i+6]] monthly__return=monthly_return.iloc[0::,-6::] monthly__return['all_portfolio_return']=monthly__return.apply(lambda x: x.sum(), axis=1) # 用債券收益率作為benchmark monthly__return['benchmark']=monthly_return['return_TF00.CFE'] return monthly__return ## 收益回測,模型評價 def backtest_model1(monthly_data): # 設定評價指標 total_return={} annual_return={} excess_return={} annual_volatility={} sharpe={} information_ratio={} win_prob={} drawdown={} tr={} # 單獨計算benchmark的相關指標 portfolio_list=['weight_return_IF00.CFE','weight_return_IC00.CFE','weight_return_TF00.CFE','weight_return_AU00.SHF','weight_return_159920.OF','weight_return_511880.SH','all_portfolio_return'] bench_total_return=(monthly_data['benchmark']+1).T.cumprod()[-1]-1 bench_annual_return=(float(bench_total_return)+1.0)**(1./(5+1/6))-1 # 每一種指標的具體構建方法 for i in portfolio_list: monthly=monthly_data[i] total_return[i]=(monthly+1).T.cumprod()[-1]-1 annual_return[i]=(float(total_return[i])+1.0)**(1./(5+1/6))-1 annual_volatility[i]=monthly.std() sharpe[i]=(annual_return[i]-bench_annual_return)/annual_volatility[i] drawdown[i]=monthly.min() win_excess=monthly-monthly_data['benchmark'] win_prob[i]=win_excess[win_excess>0].count()/float(len(win_excess)) # 將字典轉換為dataframe ar=pd.DataFrame(annual_return,index=monthly.index).drop_duplicates().T tr=pd.DataFrame(total_return,index=monthly.index).drop_duplicates().T av=pd.DataFrame(annual_volatility,index=monthly.index).drop_duplicates().T sp=pd.DataFrame(sharpe,index=monthly.index).drop_duplicates().T dd=pd.DataFrame(drawdown,index=monthly.index).drop_duplicates().T wp=pd.DataFrame(win_prob,index=monthly.index).drop_duplicates().T ar['key']=ar.index #年化收益 tr['key']=tr.index #累積收益 av['key']=av.index #年化波動 sp['key']=sp.index #夏普比率 dd['key']=dd.index #最大回撤 wp['key']=wp.index #勝率 backtest_df=pd.merge(ar,pd.merge(tr,pd.merge(av,pd.merge(sp,pd.merge(dd,wp,on='key'),on='key'),on='key'),on='key'),on='key') backtest_df.set_index('key',inplace=True) backtest_df.columns=['annual return','total return','annual volatility','sharpe ratio','drawdown','win prob'] return backtest_df hh=backtest_model1(calculate_RC_monthly_return("\資料.xlsx")) hh