ファイナンス機械学習:機械学習によるアセットアロケーション アウトオブサンプルのモンテカルロシミュレーション 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%も大きい分散を示している。

いいなと思ったら応援しよう!