アセットマネージャーのためのファイナンス機械学習:ノイズ除去 実験結果
最小分散ポートフォリオ推定における誤差から、ノイズ除去の効果を推定する。
S&P500のデトーニングされた相関行列を模式化したサンプル行列を作成する(スニペット2.7)。
ブロックサイズは50、ブロック内の相関係数は0.5、ブロック数は10としている。ランダムに列配列を入れ替えられた各特徴量(50x10)の分散は$${0.5}$$から$${0.2}$$の間から一様にランダムで与えられ、これにより共分散行列が作成される、平均値ベクトルは、分散と等しい平均と標準偏差を持つ正規分布からランダムに与えられる。
これが真の共分散行列となる。
from scipy.linalg import block_diag
def formBlockMatrix(nBlocks, bsize, bCorr):
block=np.ones((bSize,bSize))*bCorr
block[range(bSize),range(bSize)]=1
corr=block_diag(*([block]*nBlocks))
return corr
def formTrueMatrix(nBlocks,bSize, bCorr):
corr0=formBlockMatrix(nBlocks, bSize, bCorr)
corr0=pd.DataFrame(corr0)
cols=corr0.columns.tolist()
np.random.shuffle(cols)
corr0=corr0[cols].loc[cols].copy(deep=True)
std0=np.random.uniform(.05,.2,corr0.shape[0])
cov0=corr2cov(corr0,std0)
mu0=np.random.normal(std0,std0,cov0.shape[0]).reshape(-1,1)
return mu0,cov0
nBlocks,bSize,bCorr=10,50,.5
np.random.seed(0)
mu0,cov0=formTrueMatrix(nBlocks,bSize, bCorr)
これを元に、特徴量にあたる各列の平均値と分散に応じて発生させた$${T\times N}$$行列で、雑音の入った経験的共分散行列を作成する(スニペット2.8)。
ノイズ除去効果と比べるために、経験的共分散行列に含まれる不確かな共分散行列に対して行うシュリンケージ(Ledoit_Wolfシュリンケージ)をShrink=Trueで実行する。
def simCovMu(mu0,cov0,nObs,shrink=False):
x=np.random.multivariate_normal(mu0.flatten(),cov0,size=nObs)
mu1=x.mean(axis=0).reshape(-1,1)
if shrink:cov1=LedoitWolf().fit(x).covariance_
else:cov1=np.cov(x,rowvar=0)
return mu1,cov1
この共分散行列から最小分散ポートフォリを作成し、真の共分散行列から作った最小分散ポートフォリオとの平均二乗誤差を比較する1000回のモンテカルロシミュレーションを行う。
これは、スニペット2.(9-11)で実装されている。
def deNoiseCov(cov0,q,bWidth):
corr0=cov2corr(cov0)
eVal0, eVec0=getPCA(corr0)
eMax0, var0=findMaxEval(np.diag(eVal0),q,bWidth)
nFacts0=eVal0.shape[0]-np.diag(eVal0)[::-1].searchsorted(eMax0)
corr1=denoisedCorr(eVal0, eVec0,nFacts0)
cov1=corr2cov(corr1,np.diag(cov0)**.5)
return cov1
def optPort(cov, mu=None):
inv=np.linalg.inv(cov)
ones=np.ones(shape=(inv.shape[0],1))
if mu is None: mu=ones
w=np.dot(inv,mu)
w/=np.dot(ones.T, w)
return w
nObs, nTrials, bWidth, shrink, minVarPortf=1000, 1000, .01, False, True
w1=pd.DataFrame(columns=range(cov0.shape[0]),index=range(nTrials),dtype=float)
w1_d=w1.copy(deep=True)
np.random.seed(0)
for i in range(nTrials):
mu1, cov1=simCovMu(mu0,cov0, nObs, shrink=shrink)
if minVarPortf: mu1=None
cov1_d=deNoiseCov(cov1, nObs*1./cov1.shape[1],bWidth)
w1.loc[i]=optPort(cov1,mu1).flatten()
w1_d.loc[i]=optPort(cov1_d, mu1).flatten()
w0 = optPort(cov0, None if minVarPortf else mu0) # w0 true percentage asset allocation
w0 = np.repeat(w0.T, w1.shape[0], axis=0)
rmsd = np.mean((w1-w0).values.flatten()**2)**.5
rmsd_d = np.mean((w1_d-w0).values.flatten()**2)**.5 #RMSE denoised
print("RMSE not denoised:"+str( rmsd))
print("RMSE denoised:"+str( rmsd_d))
シュリンケージ無しでの最小分散ポートフォリオにおけるノイズ除去の有無の最小二乗誤差の結果
シュリンケージを行った場合の最小分散ポートフォリオにおけるノイズ除去の有無の最小二乗誤差の結果
最大シャープポートフォリオは、minValPortf=Falseで設定される。
シュリンケージ無しでの最大シャープレシオポートフォリオにおけるノイズ除去の有無の最小二乗誤差の結果
シュリンケージを行った場合の最大シャープレシオポートフォリオにおけるノイズ除去の有無の最小二乗誤差の結果。