HRPその3(HRP)
今回やること
HRPによる資産配分戦略の表現
参考コード
参考コード対応部分
特に関数の意味するところについて学ぶ
今回はHRPの部分だけ知りたいので、get_all_portfolios
の中の関係ないところは無効にした〜
portfolios = get_all_portfolios(returns)
用いている関数
def get_all_portfolios(returns): cov, corr = returns.cov(), returns.corr() hrp = getHRP(cov, corr) ---------------------今回はここまで---------------------- ivp = getIVP(cov) ivp = pd.Series(ivp, index=cov.index) mvp = getMVP(cov) mvp = pd.Series(mvp, index=cov.index) portfolios = pd.DataFrame([mvp, ivp, hrp], index=['MVP', 'IVP', 'HRP']).T return portfolios def getHRP(cov, corr): # Construct a hierarchical portfolio dist = correlDist(corr) link = sch.linkage(dist, 'single') #dn = sch.dendrogram(link, labels=cov.index.values, label_rotation=90) #plt.show() sortIx = getQuasiDiag(link) sortIx = corr.index[sortIx].tolist() hrp = getRecBipart(cov, sortIx) return hrp.sort_index() def correlDist(corr): # A distance matrix based on correlation, where 0<=d[i,j]<=1 # This is a proper distance metric dist = ((1 - corr) / 2.)**.5 # distance matrix return dist def getQuasiDiag(link): # Sort clustered items by distance link = link.astype(int) sortIx = pd.Series([link[-1, 0], link[-1, 1]]) numItems = link[-1, 3] # number of original items while sortIx.max() >= numItems: sortIx.index = range(0, sortIx.shape[0] * 2, 2) # make space df0 = sortIx[sortIx >= numItems] # find clusters i = df0.index j = df0.values - numItems sortIx[i] = link[j, 0] # item 1 df0 = pd.Series(link[j, 1], index=i + 1) sortIx = sortIx.append(df0) # item 2 sortIx = sortIx.sort_index() # re-sort sortIx.index = range(sortIx.shape[0]) # re-index return sortIx.tolist() def getRecBipart(cov, sortIx): # Compute HRP alloc w = pd.Series(1, index=sortIx) cItems = [sortIx] # initialize all items in one cluster while len(cItems) > 0: cItems = [i[j:k] for i in cItems for j, k in ((0, len(i) // 2), (len(i) // 2, len(i))) if len(i) > 1] # bi-section for i in range(0, len(cItems), 2): # parse in pairs cItems0 = cItems[i] # cluster 1 cItems1 = cItems[i + 1] # cluster 2 cVar0 = getClusterVar(cov, cItems0) cVar1 = getClusterVar(cov, cItems1) alpha = 1 - cVar0 / (cVar0 + cVar1) w[cItems0] *= alpha # weight 1 w[cItems1] *= 1 - alpha # weight 2 return w
hrp
を出力した結果は、以下
Alphabet 0.164473 Amazon 0.162113 Apple 0.262221 Facebook 0.141686 Microsoft 0.269507 dtype: float64
何を出しているのかよくわからん、、、リターン?分散?シャープレシオ?
やったこと
関数getHRP
を上から1行ずつみていく
def getHRP(cov, corr): # Construct a hierarchical portfolio ----------------イマココ----------------- dist = correlDist(corr) ----------------イマココ----------------- link = sch.linkage(dist, 'single') #dn = sch.dendrogram(link, labels=cov.index.values, label_rotation=90) #plt.show() sortIx = getQuasiDiag(link) sortIx = corr.index[sortIx].tolist() hrp = getRecBipart(cov, sortIx) return hrp.sort_index()
correlDist
対応するコードは以下
def correlDist(corr): # A distance matrix based on correlation, where 0<=d[i,j]<=1 # This is a proper distance metric dist = ((1 - corr) / 2.)**.5 # distance matrix return dist
dist
の出力結果は以下
USDZAR USDJPY NZDUSD Nifty50 N225 AUDJPY USDZAR 0.000000 0.738111 0.881962 0.775393 0.743507 0.871771 USDJPY 0.738111 0.000000 0.704024 0.632215 0.602410 0.430278 NZDUSD 0.881962 0.704024 0.000000 0.623800 0.653769 0.438771 Nifty50 0.775393 0.632215 0.623800 0.000000 0.541428 0.573740 N225 0.743507 0.602410 0.653769 0.541428 0.000000 0.580813 AUDJPY 0.871771 0.430278 0.438771 0.573740 0.580813 0.000000
相関係数をもとに、各資産の距離?を出しているみたいだが、距離とは??
クラスタリングを行うにあたり、各資産間の距離?を出すのは必要だと思うけど、そもそも何を説明変数にするんだろう、、これをみると相関係数?まあ正直クラスタリングについてちゃんと理解はできていないんだけども、、なんか、もうわからん
クラスタリングするには、特徴量ベクトルを投入しないといけない
その特徴量ベクトル
わからなすぎて、、???わかめちゃんになったわね、、、、
わからないので、続きを見てみる
def getHRP(cov, corr): # Construct a hierarchical portfolio dist = correlDist(corr) ----------------イマココ----------------- link = sch.linkage(dist, 'single') ----------------イマココ----------------- #dn = sch.dendrogram(link, labels=cov.index.values, label_rotation=90) #plt.show() sortIx = getQuasiDiag(link) sortIx = corr.index[sortIx].tolist() hrp = getRecBipart(cov, sortIx) return hrp.sort_index()
sch.linkage
link
の出力結果は以下
[[1. 5. 0.67999173 2. ] [2. 6. 0.68403821 3. ] [3. 4. 0.76755537 2. ] [7. 8. 0.86267539 5. ] [0. 9. 1.14694683 6. ]]
sch
はインポートしたライブラリscipy.cluster.hierarchy
クラスタリングしているのか??とりあえずこのライブラリについてググると、階層的クラスタリングをしていた
まあ樹形図を見たいので、以下の通りコードを追加
def getHRP(cov, corr): # Construct a hierarchical portfolio dist = correlDist(corr) link = sch.linkage(dist, 'single') ----------------追加----------------- dendrogram(link, labels = cov.index.values) plt.show() ----------------まだ----------------- sortIx = getQuasiDiag(link) sortIx = corr.index[sortIx].tolist() hrp = getRecBipart(cov, sortIx) return hrp.sort_index()
表示されない、、、これを解決するのにめちゃくちゃ時間取られた
結論としては、最初に以下の一文を追加
%matplotlib inline
得られた樹形図がこれ
長かった、、
結果を見ると、株式指標のN225とNifty50が近く、また通貨ペアのNZD/USD,USD/JPY,AUD/JPYは近いもののUSD/ZARは遠いのが意外
さ、次
def getHRP(cov : pd.DataFrame, corr : pd.DataFrame) -> pd.DataFrame: # Construct a hierarchical portfolio dist = correlDist(corr) link = sch.linkage(dist, method = 'single', metric = 'euclidean') # fig = dendrogram(link, labels = cov.index.values) # plt.show() ----------------イマココ----------------- sortIx = getQuasiDiag(link) ----------------イマココ----------------- sortIx = corr.index[sortIx].tolist() hrp = getRecBipart(cov, sortIx) return hrp.sort_index()
getQuasiDiag
対応するコードはこれ
def getQuasiDiag(link): # Sort clustered items by distance link = link.astype(int) sortIx = pd.Series([link[-1, 0], link[-1, 1]]) numItems = link[-1, 3] # number of original items while sortIx.max() >= numItems: sortIx.index = range(0, sortIx.shape[0] * 2, 2) # make space df0 = sortIx[sortIx >= numItems] # find clusters i = df0.index j = df0.values - numItems sortIx[i] = link[j, 0] # item 1 df0 = pd.Series(link[j, 1], index=i + 1) sortIx = sortIx.append(df0) # item 2 sortIx = sortIx.sort_index() # re-sort sortIx.index = range(sortIx.shape[0]) # re-index return sortIx.tolist()
戻り値であるsortlx
の出力結果が以下。いや、何のこと、、
[0, 2, 1, 5, 3, 4]
1行ずつみていく
def getQuasiDiag(link): # Sort clustered items by distance link = link.astype(int) print (link) ----------------まだ----------------- sortIx = pd.Series([link[-1, 0], link[-1, 1]]) numItems = link[-1, 3] # number of original items while sortIx.max() >= numItems: sortIx.index = range(0, sortIx.shape[0] * 2, 2) # make space df0 = sortIx[sortIx >= numItems] # find clusters i = df0.index j = df0.values - numItems sortIx[i] = link[j, 0] # item 1 df0 = pd.Series(link[j, 1], index=i + 1) sortIx = sortIx.append(df0) # item 2 sortIx = sortIx.sort_index() # re-sort sortIx.index = range(sortIx.shape[0]) # re-index return sortIx.tolist()
結果
[[1 5 0 2] [2 6 0 3] [3 4 0 2] [7 8 0 5] [0 9 1 6]]
整数にデータの型を変換したことで、ユークリッド距離を表していた列が整数になった(小数点以下切り捨て)
なぜ切り捨てた?? → 今回はユークリッド距離は関係ないので、どうでもよかった
途中の値を色々見たら、何となくこの関数でやっている処理がわかった
link
内の1行目と2行目に格納されている元のデータフレームのindexを参照し、
元のデータフレームのindexの値のみを(今回は0から5)sortIx
に格納したい(@)
だから、list
4行目のグルーピングしたデータ数の項目(今回は6)を利用して、
list
の中に6より大きい値があったら(@)を満たさないので、
その要素を他の行の1行目と2行目の値に置き換える
これをsortIx
の中に6より大きい要素がなくなるまで繰り返すと、欲しい条件が満たされる
さ、次
def getHRP(cov : pd.DataFrame, corr : pd.DataFrame) -> pd.DataFrame: # Construct a hierarchical portfolio dist = correlDist(corr) link = sch.linkage(dist, method = 'single', metric = 'euclidean') # fig = dendrogram(link, labels = cov.index.values) # plt.show() sortIx = getQuasiDiag(link) ----------------イマココ----------------- sortIx = corr.index[sortIx].tolist() ----------------イマココ----------------- hrp = getRecBipart(cov, sortIx) return hrp.sort_index()
下記の通りprintするとわかった
def getHRP(cov : pd.DataFrame, corr : pd.DataFrame) -> pd.DataFrame: # Construct a hierarchical portfolio dist = correlDist(corr) link = sch.linkage(dist, method = 'single', metric = 'euclidean') # fig = dendrogram(link, labels = cov.index.values) # plt.show() sortIx = getQuasiDiag(link) ----------------イマココ----------------- print (sortIx) print (corr.index) print (corr.index[sortIx]) sortIx = corr.index[sortIx].tolist() ----------------イマココ----------------- hrp = getRecBipart(cov, sortIx) return hrp.sort_index()
結果
[0, 2, 1, 5, 3, 4] Index(['USDZAR', 'USDJPY', 'NZDUSD', 'Nifty50', 'N225', 'AUDJPY'], dtype='object') Index(['USDZAR', 'NZDUSD', 'USDJPY', 'AUDJPY', 'Nifty50', 'N225'], dtype='object')
さ、次
def getHRP(cov : pd.DataFrame, corr : pd.DataFrame) -> pd.DataFrame: # Construct a hierarchical portfolio dist = correlDist(corr) link = sch.linkage(dist, method = 'single', metric = 'euclidean') # fig = dendrogram(link, labels = cov.index.values) # plt.show() sortIx = getQuasiDiag(link) sortIx = corr.index[sortIx].tolist() ----------------イマココ----------------- hrp = getRecBipart(cov, sortIx) ----------------イマココ----------------- return hrp.sort_index()
getRecBipart
def getRecBipart(cov, sortIx): # Compute HRP alloc w = pd.Series(1, index=sortIx) cItems = [sortIx] # initialize all items in one cluster while len(cItems) > 0: cItems = [i[j:k] for i in cItems for j, k in ((0, len(i) // 2), (len(i) // 2, len(i))) if len(i) > 1] # bi-section for i in range(0, len(cItems), 2): # parse in pairs cItems0 = cItems[i] # cluster 1 cItems1 = cItems[i + 1] # cluster 2 cVar0 = getClusterVar(cov, cItems0) cVar1 = getClusterVar(cov, cItems1) alpha = 1 - cVar0 / (cVar0 + cVar1) w[cItems0] *= alpha # weight 1 w[cItems1] *= 1 - alpha # weight 2 return w
ラスボス感あるなあ、、、今23:30、、寝たい
ただ、これ見て気づいた!!こいつぁHRP用いた時の最適な資産配分比率を示している!!!