モメンタム戦略をRでシミュレーションしてみよう

Finance

これまでにモメンタム戦略を使った株式の分析を幾つかの記事で紹介してきました。

ウェザーリポート戦略 (モメンタム戦略的なアプローチでポートフォリオを考える)
最終更新:2018年5月12日 モメンタム戦略 twitterで話題になっていた投資戦略に興味が湧いたのが本記事のきっかけになりました。モメンタム戦略というもので、「ウォール街のモメンタムウォーカー」で取り上げられている戦略です。リ...
モメンタム戦略をさらに検証していく
「ウェザーリポート戦略」と銘打ってオールウェザーポートフォリオというリスクを押さえたポートフォリオにモメンタム戦略を組み合わせた手法を先日の記事で紹介しました。 幾つかの戦略を紹介してみて、オールウェザーポートフォリオにモメン...

 

私が使った解析コードを公開するので、Rが使える方はぜひ戦略を練ったり、モメンタム戦略の穴を探したりするのに役立ててみてください。

 

パッケージ、設定

パッケージは3つのみ使いました。

####パッケージ読み込み####
library(Quandl)
library(quantmod)
library(PerformanceAnalytics)
Quandl.api_key("*******")

自分で関数を幾つか定義して使用しています。

#### Function読み込み ####
#### データセット編集に使用####
# return_list(asset_list, period)
# 株価/ポートフォリオのXTSデータのリストからまとめてリターンを計算する
# period = "monthly", "quarterly", "yearly"など
return_list <- function(asset_list, period){
data_list <- as.list(NA)
for (i in 1:length(asset_list)) {
temp <- periodReturn(asset_list[[i]], period = period, subset=paste(start, "::", sep = ""), type = 'arithmetic', indexAt = 'lastof')
colnames(temp) <- names(asset_list[i])
data_list[[i]] <- temp
}
data_list <- Reduce(cbind,lapply(data_list, merge.xts))
if(any(is.na(data_list)) == TRUE)
{
warning(paste("NAを0.0で埋めます"))
data_list <- na.fill(data_list, fill = 0)
}
return(data_list)
}
# return_period_adjust(x, period)
# return_listで作ったリターンのリスト(x)の時系列の単位を変更する
# デフォルトは四半期("quarters")、他にも月足("months")、年("years") etc...
return_period_adjust <- function(data_list, period = 'quarters'){
ep <- endpoints(data_list, period)
for (i in 1:ncol(data_list)){
if(i==1){
x <- period.apply(data_list[,i]+1, INDEX=ep, prod)-1
}else{
temp <- period.apply(data_list[,i]+1, INDEX=ep, prod)-1
x <- merge.xts(x, temp)
}
}
return(x)
}
# check_return(data_list)
# return_listで作ったリターンのリスト(data_list)の各セルを任意期間のリターンに変換する
# デフォルトはdays=365: 直近12ヶ月のリターンを計算する
check_return <- function(data_list, days=365){
temp <- data_list * 0
for(i in 1:nrow(data_list)){
dateto <- index(data_list[i])
datefrom <- as.character(as.Date(dateto)-days+1)
fromdateto <- c(paste(datefrom,"::",dateto,sep=""))
temp[i,1:ncol(temp)] <- apply(data_list[fromdateto]+1,2,prod)-1
}
return(temp)
}
# rebalance_cost(w, cost)
# ウェイトを調整してリバランス時の手数料を再現する
# x:ウェイト(XTS形式)、cost:リバランス手数料(%)
rebalance_cost <- function(w, cost=0.5){
temp <- w
temp <- apply(diff(temp) * sign(diff(temp)),1,sum)
temp <- na.fill(temp, 0)
w <- w * (1 - temp * cost * 0.01)
return(w)
}
#### モメンタムの戦略を再現する関数 ####
# momentumfundamental(CheckReeturn)
# CheckReturn:リバランスの指標にするリスト
# ・リバランスのタイミングで、
# ・過去12ヶ月に成績が良かったアセットクラス(ポートフォリオ)のみを100%ホールドする
# checkreturn:リターンのリスト
# ウェイト(XTS)が出力される
momentumfundamental <- function(CheckReturn){
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), 1, 0)}))
w <- t(apply(w, 1, function(x){return(x/sum(x))}))
w <- as.xts(w)
return(w)
}
# hold_or_sell(CheckReturn, w, benchmark)
# CheckReturn:リバランスの指標にするリスト, w:基本となるウェイト, benchmark:リターンの基準にするアセットアセットの名前(character)
# 設定したウェイトで開始
# リバランスのタイミングで、
# 昨年度の成績がbenchmarkより良ければホールド
# 悪ければbenchmarkに移動する
# ウェイト(xts)が出力
hold_or_sell <- function(CheckReturn, w, benchmark){
temp<- CheckReturn
for(i in 1:nrow(temp)){
for(j in 1:ncol(temp)){
temp[i,j] <- ifelse(CheckReturn[i,j] >= CheckReturn[i,benchmark], w[j], 0)
}
}
temp[,benchmark] <- 1-t(apply(temp, 1, sum)) #short-term billsのweightを計算
temp <- as.xts(temp)
return(temp)
}
# regulate_weight(CheckReturn, w, benchmark)
# CheckReturn:リバランスの指標にするリスト, w:基本となるウェイト, benchmark:リターンの基準にするアセットアセットの名前(character)
# 設定したポートフォリオで開始
# リバランスのタイミングで
# 昨年度の成績がベンチマークより良ければホールド、悪ければ0
# 残金はホールドする資産の割合で振り分ける(株、長期債なら(30%,40%)→(43%,57%)のポートフォリオになる)
# ホールドすべき資産が無ければベンチマークで持つ
# ウェイト(xts)が出力
regulate_weight <- function(CheckReturn, w, benchmark){
temp <- CheckReturn
for(i in 1:nrow(temp)){
for(j in 1:ncol(temp)){
temp[i,j] <- ifelse(CheckReturn[i,j] >= CheckReturn[i, benchmark], w[j], 0)
}
}
check_sum <- apply(temp, 1, sum)
for(i in 1:length(check_sum)){
if (check_sum[i] > 0) {
temp[i,] <- temp[i,] * 1/check_sum[i]
} else {
temp[i,benchmark] <- 1
}
}
temp <- as.xts(temp)
return(temp)
}

定義した関数

return_list(asset_list, period) :

quantmodなどで読み込んだxtsファイルを格納したリストを入力して、periodで指定した単位でのリターンを求める関数です。
asset_list : priceを表すquanntmodなどで読み込んだxtsのリスト
period : “monthly”, “quarterly”, “yearly”などを指定
リターンを格納したリストを出力します。

return_period_adjust(x, period)

リターンを格納したリストの時系列の単位を変更します。日足のリターンから月足のリターン、四半期のリターンを計算したりできます。
x: リターンを格納したxts形式のリスト(return_listの出力と同じ)
period: デフォルトは四半期(“quarters”)、他にも月足(“months”)、年(“years”) etc..

check_return(data_list, days=365)

return_listで作ったリターンのリスト(data_list)の各セルを任意期間(days)のリターンに変換する
モメンタム戦略でポートフォリオのウェイトを変える時の参考にするための12ヶ月リターンを計算するのに使います。
例えば、ポートフォリオに組み込むアセットの四半期リターンのリストの各セルには3ヶ月分のリターンが格納されていますが、そこから各セルに12ヶ月分のリターンを返します。
data_list : return_listの出力で得られたリターンのリストを使用
days : デフォルトは365日(12ヶ月)、182日にすれば半年分のリターンが結果として返されます。

rebalance_cost(w, cost)

モメンタム戦略でウェイトを変えながら投資をする時に発生する売買に伴う手数料による減価を再現します。
w: ウェイトを表すxtsを入力
cost: リバランスの手数料(%)を入力します。デフォルトは片道0.5%としています。

モメンタム戦略を再現する関数

モメンタム戦略で重要な、ポートフォリオの中身(ウェイト)を変えるのに使います。
check_returnでもとめた、各チェックポイントでの12ヶ月リターンを示すxts(CheckReturn)を入力すると調整したウェイトを出力します。
売買コストを計算するときは出力されたxtsをrebalance_costで処理します

momentumfundamental(CheckReturn)

CheckReturn: check_returnの出力で得られた12ヶ月リターンのxts
・リバランスのタイミングで、
・過去12ヶ月に成績が良かったアセットクラス(ポートフォリオ)のみを100%ホールドする
ウェイト(XTS)が出力される

hold_or_sell(CheckReturn, w, benchmark)

CheckReturn: check_returnの出力で得られた12ヶ月リターンのxts
w:基本となるウェイト(オールウェザーポートフォリオをベースにするならオールウェザーを示すvector
benchmark:リターンの基準にするアセットアセットの名前(character)
・設定したウェイトで開始
・リバランスのタイミングで、
・昨年度の成績がbenchmarkより良ければホールド
・悪ければbenchmarkに移動する
ウェイト(xts)が出力

regulate_weight(CheckReturn, w, benchmark)

・CheckReturn: check_returnの出力で得られた12ヶ月リターンのxts
w:基本となるウェイト
benchmark:リターンの基準にするアセットアセットの名前(character)
・設定したポートフォリオで開始
・リバランスのタイミングで
・昨年度の成績がベンチマークより良ければホールド、悪ければ0
・残金はホールドする資産の割合で振り分ける(株、長期債なら(30%,40%)→(43%,57%)のポートフォリオになる)
・ホールドすべき資産が無ければベンチマークで持つ
ウェイト(xts)が出力

使い方

・quantmodなどで読み込んで、xtsのリストを作る
・return_listでxtsを処理してリターンをまとめて求める
・必要があればreturn_period_adjustで期間を調節(月足→四半期足とか)
・求めたリターンから、モメンタムの指標をcheck_returnで作る

・モメンタムの関数を使って時系列のウェイトを作る
・rebalance_costでコストを計算。
・performance analyticsでポートフォリオのリターンを計算する

です。実際にやってみましょう。

設定の読み込み

レバレッジなしのものを紹介します。レバ有りはソースだけ一番下に貼ります。

####期間の設定####
start = "1987-12-31"
end = "2018-03-31"
####データ読み込み####
sp500 <- getSymbols("^GSPC", src = 'yahoo', auto.assign = FALSE, warnings = FALSE, from=start, to=end)
vustx <-  getSymbols("VUSTX", src = 'yahoo', auto.assign = FALSE, warnings = FALSE, from=start, to=end)
vbmfx <- getSymbols("VBMFX", src = 'yahoo', auto.assign = FALSE, warnings = FALSE, from=start, to=end)
gold <-  Quandl("WGC/GOLD_DAILY_USD", type="xts", start_date=start, end_date=end)
commodity <-  Quandl("FRED/PPIACO", type="xts", start_date=start, end_date=end)
bills <- getSymbols("VSGBX", src = 'yahoo', auto.assign = FALSE, warnings = FALSE, from=start, to=end)
# mutual fundのリターンを設定
vbmfx_edit <- vbmfx[,6]
bills_edit <- bills[,6]
vustx_edit <- vustx[,6]
asset_list <- list(sp500 = sp500, vustx = vustx_edit, vbmfx = vbmfx_edit,
gold = gold, commodity = commodity, bills = bills_edit) #return_listに入力するxtsのリスト
####シミュレーションの前準備####
merged_monthly_returns <- return_list(asset_list, 'monthly') #月ごとのリターンのリスト
merged_quarterly_returns <- return_list(asset_list, 'quarterly') #四半期ごとのリターンのリスト
quarterly_return_check <- check_return(merged_quarterly_returns) #四半期ごとに見直す12ヶ月分のリターンのリスト

・月ごとのリターン(merged_monthly_returns)
・四半期ごとのリターン(merged_quarterly_returns)
・モメンタム戦略の指標にする3ヶ月おきに計算した12ヶ月のリターン(quarterly_return_check)
が得られました。

モメンタム戦略を検証する

ベンチマークにする検証用のポートフォリオを見てみます。

# オールウェザーポートフォリオ
w_all_weather <- c(.3, .4, .15, .075, .075, .0) #ウェイト
all_weather <- Return.portfolio(merged_monthly_returns, weights = w_all_weather, rebalance_on = "quarters", geometric = TRUE)
charts.PerformanceSummary(all_weather, main='All-weather portfolio', ylog = TRUE)
# S&P500バイ・アンド・ホールド
w_sp500 <- c(1.0, .0, .0, .0, .0, .0) #ウェイト
sp500only <- Return.portfolio(merged_monthly_returns, weights = w_sp500, rebalance_on = "quarters", geometric = TRUE)
charts.PerformanceSummary(sp500only, main='S&P Buy and Hold', ylog = TRUE)

紹介した戦略は以下のようにしてシミュレーションしました。

# 戦略1 Momentum fundamentalism
w1 <- momentumfundamental(quarterly_return_check) #リバランスで変更するウェイトを計算
w1 <- rebalance_cost(w1, cost = 0.0)
s1 <- Return.portfolio(merged_monthly_returns, weights=w1, rebalance_on = "quarters", geometric = TRUE)
strategy1 <- merge(s1, all_weather, sp500only)
names(strategy1) <- c("Momentum fundamentalism", "All-weather", "S&P500")
charts.PerformanceSummary(strategy1, main='Moment fundamentalism', ylog = TRUE)
# 戦略2 Weather report
w_all_weather <- c(.3, .4, .15, .075, .075, .0)
w2 <- hold_or_sell(quarterly_return_check, w_all_weather, "bills")
w2 <- rebalance_cost(w2, cost = 0.0)
s2 <- Return.portfolio(merged_monthly_returns, weights=w2, rebalance_on = "quarters", geometric = TRUE)
strategy2 <- merge(s2, all_weather, sp500only)
names(strategy2) <- c("Weather Report", "All-weather","S&P500")
charts.PerformanceSummary(strategy2, main='Weather Report')
# 戦略3 Weather report ver 2
w_all_weather <- c(.3, .4, .15, .075, .075, .0)
w3 <- regulate_weight(quarterly_return_check, w_all_weather, "bills")
w3 <- rebalance_cost(w3, cost = 0.0)
s3 <- Return.portfolio(merged_monthly_returns, weights=w3, rebalance_on = "quarters", geometric = TRUE)
strategy3 <- merge(s3, all_weather, sp500only)
names(strategy3) <- c("Weather Report ver2", "All-weather", "S&P500")
charts.PerformanceSummary(strategy3, main='Weather Report 2')
# 戦略4 hiroakit_normal
#それぞれのポートフォリオのリターンをまず求める→リストにして結合
w4_bull <- c(.6,.2,.2,.0,.0,.0)
w4_mid <- c(.3,.15,.15,.2,.2,.0)
w4_bear <- c(.3,.35,.35,.0,.0,.0)
compare1 <- Return.portfolio(merged_monthly_returns, weights=w4_bull, rebalance_on = "quarters", geometric = TRUE)
compare2 <- Return.portfolio(merged_monthly_returns, weights=w4_mid, rebalance_on = "quarters", geometric = TRUE)
compare3 <- Return.portfolio(merged_monthly_returns, weights=w4_bear, rebalance_on = "quarters", geometric = TRUE)
compare_portfolio <- merge(compare1,compare2, compare3)
#ポートフォリオ毎のリターンをリストにしてモメンタム戦略のウェイトを求める
w4_portfolio_return <- check_return(compare_portfolio)
w4 <- momentumfundamental(w4_portfolio_return)
w4 <- rebalance_cost(w4, cost = 0.0)
s4 <- Return.portfolio(compare_portfolio, weights=w4, rebalance_on = "quarters", geometric = TRUE)
strategy4 <- merge(s4, compare1, compare2, compare3)
names(strategy4) <- c("@hiroakit_roko", "Bull", "Mid", "Bear")
charts.PerformanceSummary(strategy4, main='@hiroakit_roko', ylog=TRUE)
#上の戦略をまとめてチャートにする
strategies <- merge(sp500only, all_weather, s1, s2, s3, s4)
names(strategies) <- c("S&P500", "All weather", "Momentum fundamentalism",
"Weather report", "Weather report ver2",
"@hiroakit_normal")
charts.PerformanceSummary(strategies, main='Momentum strategies', ylog=TRUE)

1〜3までは各戦略の関数を使っただけでほぼおなじです。4ではポートフォリオを設定してそれぞれのリターンを計算した上で、さらにそこからモメンタム戦略につかうリターンの比較を行っています。参考にしてみてください。

デュアルモメンタムをするときは、幾つかのETF+ベンチマークにするETFのリターンをmomentumfundamentalにかける→ポートフォリオが出来る。それで作ったポートフォリオをさらに組み合わせる、などでポートフォリオを設定できると思います。

 

以下、レバレッジ有りで書いたスクリプトも掲載しておきます。

####期間の設定####
start = "1987-12-31"
end = "2018-03-31"
####データ読み込み####
sp500 <- getSymbols("^GSPC", src = 'yahoo', auto.assign = FALSE, warnings = FALSE, from=start, to=end)
vustx <-  getSymbols("VUSTX", src = 'yahoo', auto.assign = FALSE, warnings = FALSE, from=start, to=end)
vbmfx <- getSymbols("VBMFX", src = 'yahoo', auto.assign = FALSE, warnings = FALSE, from=start, to=end)
gold <-  Quandl("WGC/GOLD_DAILY_USD", type="xts", start_date=start, end_date=end)
commodity <-  Quandl("FRED/PPIACO", type="xts", start_date=start, end_date=end)
bills <- getSymbols("VSGBX", src = 'yahoo', auto.assign = FALSE, warnings = FALSE, from=start, to=end)
# mutual fundのリターンを設定
vbmfx_edit <- vbmfx[,6]
bills_edit <- bills[,6]
vustx_edit <- vustx[,6]
asset_list <- list(sp500 = sp500, vustx = vustx_edit, vbmfx = vbmfx_edit,
gold = gold, commodity = commodity, bills = bills_edit)
merged_monthly_returns <- return_list(asset_list, 'monthly') #月ごとのリターンのリスト
#レバレッジETFのリターンをシミュレート
lev_monthly_returns <- merged_monthly_returns[,c(1,2)] * c(3.0, 3.4)
colnames(lev_monthly_returns) <- c("spxl", "tmf")
#レバレッジETFと合わせてリターンのリスト、リバランス用のリターンのリストを作成
merged_monthly_returns <- merge.xts(merged_monthly_returns, lev_monthly_returns)
merged_quarterly_returns <- return_period_adjust(merged_monthly_returns, period = "quarters")
quarterly_return_check <- check_return(merged_quarterly_returns) #四半期ごとに見直す12ヶ月分のリターンのリスト
####ポートフォリオ(比較用)####
# レバレッジドオールウェザーポートフォリオ
w_lev_all_weather <- c(.0, .0, .25, .15, .15, .0, .20, .25)
lev_all_weather <- Return.portfolio(merged_monthly_returns, weights = w_lev_all_weather, rebalance_on = "quarters", geometric = TRUE)
charts.PerformanceSummary(lev_all_weather, main='Leveraged All-weather portfolio', ylog = TRUE)
# レバレッジドPF リスク大
w_levpf <- c(.0, .0, .3, .0, .0, .0, .4, .3)
levpf_high <- Return.portfolio(merged_monthly_returns, weights = w_levpf, rebalance_on = "quarters", geometric = TRUE)
charts.PerformanceSummary(levpf_high, main='Leveraged PF high', ylog = TRUE)
####モメンタム戦略の検証####
# 戦略2-1 Leveraged weather report
w_lev_all_weather <- c(.0, .0, .25, .15, .15, .0, .2, .25) #レバレッジドオールウェザーポートフォリオのウェイト
w21 <- hold_or_sell(quarterly_return_check, w_lev_all_weather, "bills")
w21 <- rebalance_cost(w21, cost = 0.0)
s21 <- Return.portfolio(merged_monthly_returns, weights=w21, rebalance_on = "years", geometric = TRUE)
strategy21 <- merge(s21, lev_all_weather, sp500only)
names(strategy21) <- c("Leveraged Weather Report", "Leveraged All-weather","S&P500")
charts.PerformanceSummary(strategy21, main='Leveraged Weather Report', ylog = TRUE)
# 戦略2-2 Leveraged PF momentum
w_levpf <- c(.0, .0, .3, .0, .0, .0, .4, .3)
w22 <- hold_or_sell(quarterly_return_check, w_levpf, 'gold')
w22 <- rebalance_cost(w22, cost = 0.5)
s22 <- Return.portfolio(merged_monthly_returns, weights=w22, rebalance_on = "quarters", geometric = TRUE)
strategy22 <- merge(s22, lev_all_weather, levpf_high)
names(strategy22) <- c("Momentum leveraged PF", "Leveraged All-weather", "Leveraged PF High")
charts.PerformanceSummary(strategy22, main='Momentum Leveraged PF', ylog = TRUE)
# 戦略2-3 hiroakit_lev
w23_bull <- c(.0 , .0, .3, .0, .0 ,.0, .4, .3)
w23_mid <- c(.3, .15, .15, .2, .2, .0, .0, .0)
w23_bear <- c(.3, .35, .35, .0, .0, .0, .0, .0)
compare1 <- Return.portfolio(merged_monthly_returns, weights=w23_bull, rebalance_on = "quarters", geometric = TRUE)
colnames(compare1) <- "bull"
compare2 <- Return.portfolio(merged_monthly_returns, weights=w23_mid, rebalance_on = "quarters", geometric = TRUE)
colnames(compare2) <- "mid"
compare3 <- Return.portfolio(merged_monthly_returns, weights=w23_bear, rebalance_on = "quarters", geometric = TRUE)
colnames(compare3) <- "bear"
compare_portfolio <- merge(compare1,compare2, compare3)
w23_quarterly_return <- return_period_adjust(compare_portfolio, period='quarters') #月々のリターン→四半期のリターンに変換
w23_return_check <- check_return(w23_quarterly_return)
w23 <- momentumfundamental(w23_return_check)
w23 <- rebalance_cost(w23, cost = 0.0)
s23 <- Return.portfolio(compare_portfolio, weights=w23, rebalance_on = "quarters", geometric = TRUE)
strategy23 <- merge(s23, compare1, compare2, compare3)
names(strategy23) <- c("@hiroakit_roko_2", "Bull", "Mid", "Bear")
charts.PerformanceSummary(strategy23, main='Leveraged @hiroakit_roko', ylog=TRUE)
#戦略2-4 hiroakit_lev_2
w24_bull <- c(.0, .0, .25, .0, .0, .0, .45, .3)
w24_bear <- c(.3, .4, .15, .075, .075, .0, .0, .0)
compare1 <- Return.portfolio(merged_monthly_returns, weights=w24_bull, rebalance_on = "quarters", geometric = TRUE)
colnames(compare1) <- "bull"
compare2 <- Return.portfolio(merged_monthly_returns, weights=w24_bear, rebalance_on = "quarters", geometric = TRUE)
colnames(compare2) <- "bear"
compare_portfolio <- merge(compare1,compare2)
w24_quarterly_return <- return_period_adjust(compare_portfolio, period='quarters')
w24_return_check <- check_return(w24_quarterly_return)
w24 <- momentumfundamental(w24_return_check)
w24 <- rebalance_cost(w24, cost = 0.0)
s24 <- Return.portfolio(compare_portfolio, weights=w24, rebalance_on = "quarters", geometric = TRUE)
strategy24 <- merge(s24, compare1, compare2)
names(strategy24) <- c("@hiroakit_roko_3", "Bull", "Bear")
charts.PerformanceSummary(strategy24, main='Leveraged @hiroakit_roko', ylog=TRUE)
#上の戦略をまとめてチャートにする
strategies <- merge(lev_all_weather, levpf_high, s21, s22, s23, s24)
names(strategies) <- c("Leveraged all weather", "Leveraged PF High",
"Leveraged weather report", "Leveraged PF momentum",
"@hiroakit_lev1", "@hiroakit_lev2")
charts.PerformanceSummary(strategies, main='Leveraged momentum strategies', ylog=TRUE)

コメント