また実験をしてみます。レバレッジの検討をする際に債券利率のヒストリカルデータを得ることができました。1870年代からのものです。10年ものの金利なので正確ではないのですが、このデータを使って短期債のデータをシミュレーションすることができます。同様にS&P500のデータも1870年代ごろからあります。

モメンタムというアノマリー
本ブログで以前モメンタム投資について取り上げたことがありました。米国株、米国以外の株と債券を絶対モメンタム・相対モメンタムに従ってローテーションしていくといった投資手法でここ数十年では素晴らしい成績を収めていたといいます。
モメンタム自体は強いアノマリーと言われていて、長らく続いていると言われています。試しにPortfolio Visualizerというサイトに組み込まれた機能を使ってモメンタム投資のシミュレーションをしてみます。下はS&P500のETFとVSGBXというVanguardの短期債券の相対モメンタム(過去12ヶ月で成績が良かった方に投資する)による投資のシミュレーションを表しています。2000年代は株式市場がかなり冷え切っていた中、債券などにうまく逃げられると黄色の線で表されるインデックスを遥かに上回るリターンが得られていました。
同じようなコンセプトの投資をシミュレーションデータで再現してみるとこのようなデータが得られました。
まあ、大体同じようなグラフでしょうか。Portfolio VisualizerではCAGR 9%、StDev 9.1%, ドローダウン 17.5%と出ています。私のシミュレーションではCAGR 8%、StDev 8.4%、ドローダウン 13%となっています。モメンタムの判断については1ヶ月程度の差があったりはしますが大きくずれたところはありません。シミュレーションの商品を使っているという点を踏まえればまずまずの精度でしょうか。ちなみにリターンのデータは月次リターンのみを使っています。
グラフからは2000年代以降では相対モメンタムを使った投資方法はまずまずの成績をあげそうです。実際にはデュアルモメンタムをやると更にリターンは増えるのですが、今回はシミュレーションが困難なのでこちらは取り扱いません。
このデータ、シミュレーションした債券とS&P500のリターンを使ってヒストリカルな相対モメンタムのシミュレーションをしてみます。
ヒストリカルな相対モメンタムシミュレーション
まずは結果から示します。
データが取得できた1871年から2018年9月までのデータを示しています。緑がS&P500、赤が短期債券のリターンを示しています。グラフは上から累積リターン、月次リターン、ドローダウンです。
予想に反して相対モメンタムは超長期の歴史の中ではS&P500よりも明らかに優れているということはなさそうです。
SP500-Bond |
Bond |
SP500 | |
Annualized Return | 8.96 | 4.76 | 9.08 |
Annualized Standard Deviation | 9.67 | 4.24 | 14 |
Worst Drawdown | 49.1 | 14.8 | 81.5 |
Sharpe Ratio Annualized(Rf=0%) |
0.926 | 1.120 | 0.650 |
Sortino Ratio (MAR = 0%) | 0.429 | 0.619 | 0.316 |
指標を見てみても相対モメンタムによる投資方針はS&P500のバイ・アンド・ホールドにわずかに負けていました。S&P500は配当込みの計算なので、実際には配当分の課税がひかれてもう少しリターンが下がります。しかしモメンタムにしても配当分はひかれるうえに取引コストや売買に伴うキャピタルゲイン課税がかかるためリターンは更に押し下げられると考えられます。
リスクについては株式よりは減らすことができています。ドローダウンも大恐慌の頃でもせいぜい49%で済んだ点は参考にはなるかもしれません。
ただ、結果として相対モメンタム戦略は株式のバイ・アンド・ホールドには勝てない可能性があります。(※以前twitterで同様の画像を貼り付けましたがルックバックに関するコードの一部を修正したために以前よりもモメンタム戦略のリターンは低い見積りになりました。)
ウォール街のランダム・ウォーカーを読んで戦略を真似しようとする方の多くはデュアルモメンタム戦略をやるでしょうから、相対モメンタムは注目度は低いかも知れませんが、21世紀のデータだけをみて判断するのは木を見て森を見ないようなものだ、ということがわかりました。
以下、シミュレーションです。
シミュレーション
パッケージの用意と長期リターンのシミュレーション
#### Load Library #### | |
library(quantmod) | |
library(PerformanceAnalytics) | |
library(Quandl) | |
Quandl.api_key("<<自分のAPI KEY>>") | |
library(stringr) |
パッケージはfinance解析用のquantmod、PerformanceAnalytics。長期データ取得用にQuandlを読み込みます。QuandlのAPI KEYは自分用のやつを入力します。stringrは文字列編集のパッケージです、自分で作ったCSVの文字がいまいちだったのでその編集用ですね。
このデータを読み込んでリターンを計算しています。
#### シミュレートしたBondインデックスを読み込む #### | |
bondTable <- read.csv(file.path('data', 'simulatedBond.csv')) | |
bondTable$Date <- str_replace(bondTable$Date, '\\.', '\\-') | |
for(i in 1:(nrow(bondTable) %/% 12)){ | |
j <- i*12 -2 | |
bondTable$Date[j] <- str_c(bondTable$Date[j], "0") | |
} | |
bondTable$Date <- str_c(bondTable$Date, "-01") | |
bondIndexSim <- as.xts(read.zoo(bondTable[,c(1,3)])) | |
bond_return <- periodReturn(bondIndexSim, period = 'monthly') | |
#### S&P500インデックスのシミュレーション #### | |
end <- Sys.Date() | |
start <- "1871-01-01" | |
SP500 <- Quandl("MULTPL/SP500_REAL_PRICE_MONTH", type="xts", start_date=start, end_date=end) | |
SPYield <- Quandl("MULTPL/SP500_DIV_YIELD_MONTH", type="xts", start_date=start, end_date=end) | |
SP500_return <- periodReturn(SP500, period = 'monthly') | |
merged_sp500 <- merge.xts(SP500_return, (SPYield / (12 * 100))) | |
SP500_return <- apply.monthly(merged_sp500, sum, na.rm = TRUE) |
bond_returnとSP500_returnがそれぞれの月次リターンを表します。csvをもう少しキレイにしたらstringr使わなくても済むんですが、手間としてはどちらも同じくらいですね。
ルックバックと相対モメンタムによる割付
#### ルックバックと相対モメンタムによる割付 #### | |
#ルックバック用の関数 | |
lookBackReturn <- function(dataList, days=360){ | |
temp <- dataList * 0 | |
for(i in 2:nrow(dataList)){ | |
dateTo <- as.character(index(dataList[i]) - 1) #直前までを計算 | |
dateFrom <- as.character(as.Date(dateTo) - days + 1) # days遡って計算 | |
fromDateTo <- c(paste(dateFrom,"::",dateTo,sep="")) #期間の設定 | |
temp[i,1:ncol(temp)] <- apply(dataList[fromDateTo]+1,2,prod)-1 | |
} | |
temp[1, 1:ncol(temp)] <- as.vector(temp[2]) #便宜的に1行目のリターンは2行目と揃える | |
return(temp) | |
} | |
# 相対モメンタムで保有するアセットにウェイトを割り付ける関数 | |
relativeMomentum <- function(CheckReturn, n=1){ | |
temp <- as.data.frame(CheckReturn) | |
return_check_rank <- t(apply(temp, 1,rank)) | |
w <- t(apply(return_check_rank, 1, function(x){ifelse(x >= max(x) - n + 1, 1, 0)})) | |
w <- t(apply(w, 1, function(x){return(x/sum(x))})) | |
w <- as.xts(w) | |
return(w) | |
} | |
mergedReturns <- merge.xts(bond_return, SP500_return) | |
colnames(mergedReturns) <- c('Bond1yr', 'SP500') | |
returnWithinYear <- lookBackReturn(mergedReturns, days = 365) #1年分のリターンを計算 | |
w <- relativeMomentum(returnWithinYear) #相対モメンタムで保有するアセットを求める | |
colnames(mergedReturns) <- c('Bond1yr', 'SP500) |
求めたリターンの評価から、365日分のリターンをルックバックして求めています。lookBackReturnという関数を作りました。リターンから、相対モメンタムに応じてリターンが高いアセットを求めていつ何に投資すべきかを求めるrelativeMomentumという関数を作ってスイッチングのシミュレーションをしました。
左はルックバックしたリターンの表です。ここから、リターンが大きい方がどちらかを割り出してスイッチングの判断にしているんですね。
ポートフォリオのリターンを見る
# S&P500と1年債のモメンタムポートフォリオのリターンを求める | |
relMom_SP_Bond <- Return.portfolio(mergedReturns, weights=w, geometric = TRUE) | |
mergedReturns <- merge.xts(relMom_SP_Bond, mergedReturns) | |
colnames(mergedReturns)[1] <- c('SP500-Bond Relative Momentum') | |
# チャートと成績を比較 | |
charts.PerformanceSummary(mergedReturns, main='Relative Momentum Return (since 1871-01-01, rebalance cost = 0%)', wealth.index = TRUE, ylog = TRUE) | |
# ポートフォリオのCAGR, StdDev, SharpeRatioなどを出力 | |
portfolioMeasure <- function(pfReturns, save = TRUE, fileName = 'PF_Measure.csv'){ | |
ret <- Return.annualized(pfReturns) * 100 | |
sd <- StdDev.annualized(pfReturns) * 100 | |
maxD <- maxDrawdown(pfReturns) * 100 | |
sharpR <- SharpeRatio.annualized(pfReturns) | |
sortR <- SortinoRatio(pfReturns) | |
df <- rbind(ret, sd, maxD, sharpR, sortR) | |
df <- signif(df, digits = 3) | |
if(save) {write.table(df, file = fileName, sep = ',')} | |
return(df) | |
} | |
portfolioMeasure(mergedReturns, fileName = 'PM.csv') |
Return.Portfolioでモメンタム戦略を行った場合のリターンがわかります。これをつかって、S&P500・債券と共にチャートを一緒に描画しています。
ポートフォリオの指標としてはReturn.annualized, StdDev.annualized, maxDrawdown, SharpeRatio.annualizedなどを使って一括でcsvにして出力してしまいます。
こんな感じです。あまり汎用性は無いですが、参考にどうぞ。
コメント