ファイナンス機械学習:機械学習によるアセットアロケーション アウトオブサンプルのモンテカルロシミュレーション HRP,IVP,CLA
インサンプルでパフォーマンスの高いものが、アウトオブサンプルで同じパフォーマンスを示すとは限らない。
モンテカルロ法を使い、CLAの最小分散ポートフォリオ、リスクパリティのIVP、そしてHRPポートフォリオのアウトオブサンプルにおけるパフォーマンスを比較する。
平均ゼロで10%の標準偏差の正規リターンを、$${520 \times 10}$$作成する。520の観測値個数は日次ベースで2年に相当する。
現実の価格はジャンプがあることから、生成データにランダムなショックと相関構造を追加する。
人工データを生成するコードはスニペット16.5で実装される。
def generateDataMC(nObs, sLength, size0, size1, mu0, sigma0, sigma1F):
#1) generate random uncorrelated data
x = np.random.normal(mu0, sigma0, size=(nObs, size0))
#2) create correlation between the variables
cols = [random.randint(0, size0 - 1) for i in range(size1)]
y = x[:, cols] + np.random.normal(0, sigma0 * sigma1F, size=(nObs, len(cols)))
x = np.append(x, y, axis=1)
#3) add common random shock
point = np.random.randint(sLength, nObs - 1, size=2)
x[np.ix_(point, [cols[0], size0])] = np.array([[-0.5, -0.5], [2, 2]])
#4) add specific random shock
point = np.random.randint(sLength, nObs - 1, size=2)
x[point, cols[-1]] = np.array([-0.5, 2])
return x, cols
このデータを使い、260の観測値(日次で一年分に相当する)を使い、HRP,CLA,IVPでポートフォリオを構築する。各ポートフォリオは月次(観測値22)で再推定され、リバランスされる。
インサンプルで計算された月次のウエイトに、アウトオブサンプルの残りの260の観測値を当てはめ、この3種のポートフォリオのリターンを計算する。
このインサンプルでの構築とアウトオブサンプルでのリターン計算を、10,000回繰り返し、アウトオブサンプルの分散を比較する。
モンテカルロの計算はスニペット16.5で実装されている。
def getHRP(cov, corr):
corr, cov = pd.DataFrame(corr), pd.DataFrame(cov)
dist = correlDist(corr)
link = linkage(dist, 'single')
sortIx = getQuasiDiag(link)
sortIx = corr.index[sortIx].tolist() # recover labels
hrp = getRecBipart(cov,sortIx)
return hrp.sort_index()
def getCLA(cov, **kargs):
import CLA
mean = np.arange(cov.shape[0]).reshape(-1,1)
lB=np.zeros(mean.shape)
uB=np.ones(mean.shape)
cla=CLA.CLA(mean,cov,lB,uB)
cla.solve()
return cla.w[-1].flatten()
def hrpMC(numIters= 1e4, nObs= 520, size0= 5, size1= 5, mu0= 0,sigma0= 1e-2, \
sigma1F= 0.25, sLength= 260, rebal= 22):
methods = [getIvp, getHRP,getCLA]
stats, numIter = {i.__name__: pd.Series() for i in methods}, 0
pointers = range(sLength, nObs, rebal)
while numIter < numIters:
print(numIter)
#1) Prepare data for one experiment
x, cols = generateDataMC(nObs, sLength, size0, size1, mu0, sigma0, sigma1F)
r = {i.__name__: pd.Series() for i in methods}
#2) Compute portfolios in-sample
for pointer in pointers:
x_ = x[pointer - sLength: pointer]
cov_, corr_ = np.cov(x_, rowvar=0), np.corrcoef(x_, rowvar=0)
#3) Compute performance out-of-sample
x_ = x[pointer: pointer + rebal]
for func in methods:
w_ = func(cov=cov_, corr=corr_) # callback
r_ = pd.Series(np.dot(x_, w_))
r[func.__name__] = pd.concat([r[func.__name__],r_.astype(r[func.__name__].dtypes)])
#r[func.__name__] = r[func.__name__]._append(r_)
#4) Evaluate and store results
for func in methods:
r_ = r[func.__name__].reset_index(drop=True)
p_ = (1 + r_).cumprod()
stats[func.__name__].loc[numIter] = p_.iloc[-1] - 1
numIter += 1
#5) Report results
stats = pd.DataFrame.from_dict(stats, orient='columns')
df0, df1 = stats.std(), stats.var()
print(pd.concat([df0, df1, df1 / df1['getHRP'] - 1], axis=1))
return
CLAの制限は、$${0\le \omega_i \le 1}$$である。
HRP、CLAとIVPのリターンの分散は以下の通りとなる。
HRPの分散が一番小さく、CLAがHRPに比べ74%も大きい分散を示している。