可変レバレッジド・ポートフォリオ検証の方法

ポートフォリオのシミュレーション

ROKOHOUSEさんの可変レバレッジド・ポートフォリオの検証記事はたくさんの方にお読み頂き、ありがとうございました。臨床統計とファイナンスの知識をなんとなく使ってシミュレーションしただけで、全く確証が無いものですので、もう少し知識がある方々からの指摘も参考にしたいのでソースを開示します。

www.rokohouse.net

 

・・・書ききって思いましたが後から読む人はあまりにも苦痛ですね。ソースコード以外の部分をお読みください。
長々とこの記事をアップした理由は、もう少し解析がうまい方にも検証をしていただきたかったからなのですが、ご意見あればtwitterなどにリプライいただけると幸いです。

パッケージ読み込み

今回はreadr, ggplot2, dplyr, tidyrの4つのパッケージを追加で使用します。Hadley Wickhamのパッケージ使ってる割に途中でfor構文の多用をして演算に時間がかかるので、漸化式的な計算をどうやったら早くできるのかのテクニックもぜひ教えていただきたいです。

####パッケージの読み込み####
install.packages("readr", dependencies = TRUE)
install.packages("readr", dependencies = TRUE)
install.packages("readr", dependencies = TRUE)
install.packages("readr", dependencies = TRUE)
require(readr)
require(ggplot2)
require(dplyr)
require(tidyr)

データの読み込みと抽出

今回はYahoo financeのHistorical DataからダウンロードしたCSVファイルと、S&P 500 Dividend Yield by yearから作ったCSVを読み込ませます。

####データの読み込み####
sp500 <- read_csv("SP500.csv")
vbmfx <- read_csv("VBMFX.csv")
spdiv <- read_csv("spdiv.csv")
spxl <- read_csv("SPXL.csv")
bnd <- read_csv("BND.csv")
tmf <- read_csv("TMF.csv")
#データの必要な部分のみ抽出
#日付とadj priceのみを残してリネームしておきます。
sp <- sp500[,c(1,6)]
colnames(sp) <- c("Date", "Price_sp")
bond <- vbmfx[,c(1,6)]
colnames(bond) <- c("Date", "Price_bond")
spxl <- spxl[,c(1,6)]
colnames(spxl) <- c("Date", "Price_spxl")
bnd <- bnd[,c(1,6)]
colnames(bnd) <- c("Date", "Price_bnd")
tmf <- tmf[,c(1,6)]
colnames(tmf) <- c("Date", "Price_tmf")
colnames(spdiv) <- c("Year", "Dividend")
#データを結合して日付と値段を格納したオブジェクトを作成
Price <- left_join(sp, bond, id="Date")
Price <- left_join(Price, spxl, by = "Date")
Price <- left_join(Price, tmf, by = "Date")
Price <- left_join(Price, bnd, by = "Date")
#日付操作がやりにくいので行毎にidを割り振る
Price$id <- c(1:nrow(Price))

現時点までのチャートをggplot2で描画してみます。

Price_gg <- data.frame(Date = Price$Date,
Price_sp = Price$Price_sp,
Price_bond = Price$Price_bond,
Price_spxl = Price$Price_spxl,
Price_bnd = Price$Price_bnd,
Price_tmf = Price$Price_tmf)
Price_gg <- Price_gg %>% gather(Index, Price, -Date)
g <- ggplot(Price_gg, aes(x=Date, y=Price,color=Index))+
geom_line()+
scale_y_log10()+
labs(color = "Index")+
scale_color_hue(labels = c(Price_sp = "S&P500", Price_bond = "Bond", Price_spxl = "SPXL", Price_bnd = "BND", Price_tmf = "TMF"))
print(g)

でてきたチャートがこんな感じ。
f:id:drkernel:20171210230706j:plain

債券インデックスをシミュレーションする

債券は変動が比較的穏やかで年率で安定したリターンを生むので、日々の変動率とそのバラつきが設定できればリアルワールドとそんなに変わらないでしょう、ということで乱数を発生させて仮想の債券インデックスを作ります。

####債券インデックスがたどるべき1日の変動率とその偏差を求めて乱数を使って仮想インデックスを作成####
bond$diff_bond[2:nrow(bond)] <- diff(bond$Price_bond)/bond$Price_bond[1🙁nrow(bond)-1)] #債券の1日ごとの変動率を表すカラムを追加
mean(bond$diff_bond, na.rm=TRUE) #債券の日毎の変動の平均
bond_diffsd <- sd(bond$diff_bond, na.rm=TRUE) #債券の日毎の変動の偏差
#債券市場が年率5.5%で推移したデータがあるので、使ったインデックスの1951年時点での価格を求める
bond1951 <- bond[bond$Date == "2016-01-04", 2]/(2016-1951)^1.055
id1 <- Price[Price$Date == "1951-1-2",]$id #1951年の開始日のid
id2 <- Price[Price$Date == "1986-12-10",]$id #データが読み込めなかった最後の日のid
bond_meandiff <- (bond$Price_bond[1]/bond1951)^(1/(id2-id1)) #1951年の0.124から1986年の1.78に上昇するための毎日の平均上昇率
Price$diff_bond <- rnorm(nrow(Price), (bond_meandiff-1), bond_diffsd) #平均上昇率と変動(meanとSD)に正規分布する乱数を発生させて毎日の変動率とする。
#変動率から毎日の債券インデックスの価格を求めていく。
for(i in 1:id2){
Price$Price_bond[id2+1-i] <- Price$Price_bond[id2+2-i] / (1+Price$diff_bond[id2+2-i])
}
####債券インデックスにさらにバラつきを加えてTMF用のインデックスを作成####
#係数は適当(meanやsdが後で合うようにしてあるだけです。)
Price$diff_bond2 <- (Price$diff_bond * 1 + rnorm(nrow(Price), 0, sd(Price$diff_bond) * 2.0)) -Price$diff_bond * 0.3
Price$Price_bond2[nrow(Price)] <- Price$Price_bond[nrow(Price)]
for(i in 1:nrow(Price)){
Price$Price_bond2[nrow(Price)-i] <- Price$Price_bond2[nrow(Price)+1-i] / (1+Price$diff_bond2[nrow(Price)+1-i])
}
####シミュレーションしたインデックスのチャートを見てみます####
Price_gg <- data.frame(Date = Price$Date,
Price_sp = Price$Price_sp,
Price_bond = Price$Price_bond)
Price_gg <- Price_gg %>% gather(Index, Price, -Date)
g <- ggplot(Price_gg, aes(x=Date, y=Price,color=Index))+
geom_line()+
scale_y_log10()+
labs(color = "Index")+
scale_color_hue(labels = c(Price_sp = "S&P500", Price_bond = "Bond"))
print(g)

f:id:drkernel:20171211182339j:plain
注意したいのは、この時点で、bondの1986年より以前はそれっぽく作っただけのチャートです。

配当日と配当利回りを設定

次に、配当の再投資やリバランスを行う日付を3月6月9月12月の最終日に設定するので、その日にマークをしておきます。
配当利回りのデータから、配当日に幾ら支払われるのか(年間配当が2%なら3ヶ月に1回0.5%入る)計算もしておきます。

#adjustする日(adjday)がTRUEとなるようなカラムを作成
Price$Date <- as.POSIXlt(Price$Date)
Price$adjday <- append(0, (diff(Price$Date$mday) < 0))  & is.element(months(Price$Date), c("3月", "6月", "9月", "12月"))
#dividendのリストをつくる
Price$Year <- Price$Date$year
spdiv$Year <- as.numeric(spdiv$Year-1900)
Price$Year <- as.numeric(Price$Year)
Price$Date <- as.Date(Price$Date)
Price <- left_join(Price, spdiv, by = "Year")
Price$Dividend <- Price$Dividend/4

ETFの基準価格を求める

インデックスから、SPXLやTMF、BNDの過去データを求めてみます。
SPXLとTMFは年間1%(営業日あたり0.004%)手数料が取られます。1日の変動はS&P500、債券の3倍ずつです。
BNDの手数料は無視して、債券インデックスと同じ動きをするようにします。
TMFでsdとかmeanを求めているのはシミュレーションした部分が本来のTMFと同じ程度の変動、リターンを生むか検証して調整しているからです。
最後にもう一度チャートを描いてみます。

#SPXLの2008/11/5(ID 14572)以前のデータを算出
Price$diff_spxl[2:nrow(Price)] <- diff(Price$Price_sp)/Price$Price_sp[1:nrow(Price)-1] * 3
for(i in 1:14572){
Price$Price_spxl[14572-i] <- Price$Price_spxl[14572+1-i] / 0.99996 / (1+Price$diff_spxl[14572+1-i])
}
#TMFの2009/4/16(ID 14682)以前のデータを算出
Price$diff_tmf[2:nrow(Price)] <- diff(Price$Price_bond2)/Price$Price_bond2[1:nrow(Price)-1] * 3
for(i in 1:14682){
Price$Price_tmf[14682-i] <- Price$Price_tmf[14682+1-i] / 0.99996 / (1+Price$diff_tmf[14682+1-i])
}
sd(diff(Price$Price_tmf)/Price$Price_tmf[1:nrow(Price)-1],na.rm=TRUE)
mean(diff(Price$Price_tmf)/Price$Price_tmf[1:nrow(Price)-1],na.rm=TRUE)
sd(diff(tmf$Price_tmf)/tmf$Price_tmf[1🙁nrow(tmf)-1)], na.rm=TRUE)
mean(diff(tmf$Price_tmf)/tmf$Price_tmf[1🙁nrow(tmf)-1)], na.rm=TRUE)
Price$diff_tmf <- Price$diff_tmf - (mean(Price$diff_tmf, na.rm=TRUE) - mean(diff(tmf$Price_tmf)/tmf$Price_tmf[1🙁nrow(tmf)-1)], na.rm=TRUE))
#BNDの2007/4/10 (ID 14173)以前のデータを算出
Price$diff_bnd[2:nrow(Price)] <- diff(Price$Price_bond)/Price$Price_bond[1:nrow(Price)-1]
for(i in 1:14173){
Price$Price_bnd[14173-i] <- Price$Price_bnd[14173+1-i] / (1+Price$diff_bnd[14173+1-i])
}
#チャート描画
Price_gg <- data.frame(Date = Price$Date,
Price_sp = Price$Price_sp,
Price_bond = Price$Price_bond,
Price_spxl = Price$Price_spxl,
Price_tmf = Price$Price_tmf,
Price_bnd = Price$Price_bnd)
Price_gg <- Price_gg %>% gather(Index, Price, -Date)
g <- ggplot(Price_gg, aes(x=Date, y=Price,color=Index))+
geom_line()+
scale_y_log10()+
labs(color = "Index")+
scale_color_hue(labels = c(Price_sp = "S&P500", Price_bond = "Bond", Price_spxl = "SPXL", Price_tmf = "TMF", Price_bnd = "BND"))
print(g)

f:id:drkernel:20171211183417j:plain
シミュレーションからSPXL、TMF、BNDが1950年代から合ったらこんな動きをするのか、というのがなんとなく分かります。
これらのチャートを元にいよいよ投資を実践してみます。

投資の実行

投資は、読み込んだ1日目がたまたま1950年12月11日だったので、この日に100ドルを投資。
S&P500への投資は3月6月9月12月の最終日に配当を再投資します。このときは手数料、税金を無視して全額投資に回せると仮定しています。
ROKOHOUSE式の投資は初日に小リスクならSPXL 30 TMF 20 BND 50の割合で、中リスクならSPXL 35 TMF 25 BND 40の割合で、大リスクならSPXL 40 TMF 30 BND 30の割合で行います。そして3月6月9月12月の最終日にポートフォリオのリバランスを行います。売買に伴う手数料・税金は無視しています。

#S&P500インデックスに投資
Price$Invest_spx[1] <- 100
for(i in 1🙁nrow(Price)-1)){
ifelse(Price$adjday[i] == TRUE,  #その日が再投資実行日かどうか?
Price$Invest_spx[i+1] <- Price$Invest_spx[i] * (Price$Price_sp[i+1]/Price$Price_sp[i]) * (1+ Price$Dividend[i]), #再投資実行なら配当分が追加される
Price$Invest_spx[i+1] <- Price$Invest_spx[i] * (Price$Price_sp[i+1]/Price$Price_sp[i]) #再投資しなければ価格変動が起きるだけ
)}

ROKOHOUSE式でもリバランスを行う日かどうかで2通りの方法で投資します。

#小リスクSPXL 30 TMF 20 BND 50
Price$Invest_lrisk <- 100
Price$Invest_lriskspxl[1] <- 30
Price$Invest_lrisktmf[1] <- 20
Price$Invest_lriskbnd[1] <- 50
for(i in 1🙁nrow(Price)-1)){
if(Price$adjday[i] == TRUE){
#リバランスの日は合計額からspx, tmf, bndそれぞれに割り振る。
Price$Invest_lriskspxl[i+1] <- Price$Invest_lriskspxl[i] * (Price$Price_spxl[i+1]/Price$Price_spxl[i])
Price$Invest_lrisktmf[i+1] <- Price$Invest_lrisktmf[i] * (Price$Price_tmf[i+1]/Price$Price_tmf[i])
Price$Invest_lriskbnd[i+1] <- Price$Invest_lriskbnd[i] * (Price$Price_bnd[i+1]/Price$Price_bnd[i])
Price$Invest_lrisk[i+1] <- Price$Invest_lriskspxl[i+1] + Price$Invest_lrisktmf[i+1] + Price$Invest_lriskbnd[i+1]
Price$Invest_lriskspxl[i+1] <- Price$Invest_lrisk[i+1] * 0.3
Price$Invest_lrisktmf[i+1] <- Price$Invest_lrisk[i+1] * 0.2
Price$Invest_lriskbnd[i+1] <- Price$Invest_lrisk[i+1] * 0.5
} else{
#リバランスしなければ価格変動が起きてその合計を求めるだけ
Price$Invest_lriskspxl[i+1] <- Price$Invest_lriskspxl[i] * (Price$Price_spxl[i+1]/Price$Price_spxl[i])
Price$Invest_lrisktmf[i+1] <- Price$Invest_lrisktmf[i] * (Price$Price_tmf[i+1]/Price$Price_tmf[i])
Price$Invest_lriskbnd[i+1] <- Price$Invest_lriskbnd[i] * (Price$Price_bnd[i+1]/Price$Price_bnd[i])
Price$Invest_lrisk[i+1] <- Price$Invest_lriskspxl[i+1] + Price$Invest_lrisktmf[i+1] + Price$Invest_lriskbnd[i+1]
}
}
#中リスクSPXL 35 TMF 25 BND 40
Price$Invest_mrisk <- 100
Price$Invest_mriskspxl[1] <- 35
Price$Invest_mrisktmf[1] <- 25
Price$Invest_mriskbnd[1] <- 40
for(i in 1🙁nrow(Price)-1)){
if(Price$adjday[i] == TRUE){
Price$Invest_mriskspxl[i+1] <- Price$Invest_mriskspxl[i] * (Price$Price_spxl[i+1]/Price$Price_spxl[i])
Price$Invest_mrisktmf[i+1] <- Price$Invest_mrisktmf[i] * (Price$Price_tmf[i+1]/Price$Price_tmf[i])
Price$Invest_mriskbnd[i+1] <- Price$Invest_mriskbnd[i] * (Price$Price_bnd[i+1]/Price$Price_bnd[i])
Price$Invest_mrisk[i+1] <- Price$Invest_mriskspxl[i+1] + Price$Invest_mrisktmf[i+1] + Price$Invest_mriskbnd[i+1]
Price$Invest_mriskspxl[i+1] <- Price$Invest_mrisk[i+1] * 0.35
Price$Invest_mrisktmf[i+1] <- Price$Invest_mrisk[i+1] * 0.25
Price$Invest_mriskbnd[i+1] <- Price$Invest_mrisk[i+1] * 0.4
} else{
Price$Invest_mriskspxl[i+1] <- Price$Invest_mriskspxl[i] * (Price$Price_spxl[i+1]/Price$Price_spxl[i])
Price$Invest_mrisktmf[i+1] <- Price$Invest_mrisktmf[i] * (Price$Price_tmf[i+1]/Price$Price_tmf[i])
Price$Invest_mriskbnd[i+1] <- Price$Invest_mriskbnd[i] * (Price$Price_bnd[i+1]/Price$Price_bnd[i])
Price$Invest_mrisk[i+1] <- Price$Invest_mriskspxl[i+1] + Price$Invest_mrisktmf[i+1] + Price$Invest_mriskbnd[i+1]
}
}
#大リスクSPXL 40 TMF 30 BND 30
Price$Invest_hrisk <- 100
Price$Invest_hriskspxl[1] <- 40
Price$Invest_hrisktmf[1] <- 30
Price$Invest_hriskbnd[1] <- 30
for(i in 1🙁nrow(Price)-1)){
if(Price$adjday[i] == TRUE){
Price$Invest_hriskspxl[i+1] <- Price$Invest_hriskspxl[i] * (Price$Price_spxl[i+1]/Price$Price_spxl[i])
Price$Invest_hrisktmf[i+1] <- Price$Invest_hrisktmf[i] * (Price$Price_tmf[i+1]/Price$Price_tmf[i])
Price$Invest_hriskbnd[i+1] <- Price$Invest_hriskbnd[i] * (Price$Price_bnd[i+1]/Price$Price_bnd[i])
Price$Invest_hrisk[i+1] <- Price$Invest_hriskspxl[i+1] + Price$Invest_hrisktmf[i+1] + Price$Invest_hriskbnd[i+1]
Price$Invest_hriskspxl[i+1] <- Price$Invest_hrisk[i+1] * 0.4
Price$Invest_hrisktmf[i+1] <- Price$Invest_hrisk[i+1] * 0.3
Price$Invest_hriskbnd[i+1] <- Price$Invest_hrisk[i+1] * 0.3
} else{
Price$Invest_hriskspxl[i+1] <- Price$Invest_hriskspxl[i] * (Price$Price_spxl[i+1]/Price$Price_spxl[i])
Price$Invest_hrisktmf[i+1] <- Price$Invest_hrisktmf[i] * (Price$Price_tmf[i+1]/Price$Price_tmf[i])
Price$Invest_hriskbnd[i+1] <- Price$Invest_hriskbnd[i] * (Price$Price_bnd[i+1]/Price$Price_bnd[i])
Price$Invest_hrisk[i+1] <- Price$Invest_hriskspxl[i+1] + Price$Invest_hrisktmf[i+1] + Price$Invest_hriskbnd[i+1]
}
}

式だけだとわけわかりませんがこれで投資シミュレーションができました。
プロットしてみます。

Invest_gg <- data.frame(Date = Price$Date,
spx = Price$Invest_spx,
lrisk = Price$Invest_lrisk,
mrisk = Price$Invest_mrisk,
hrisk = Price$Invest_hrisk)
Invest_gg <- Invest_gg %>% gather(Index, Investment, -Date)
g <- ggplot(Invest_gg, aes(x=Date, y=Investment, color=Index))+
geom_line()+
scale_y_log10()+
labs(color = "Investment")+
scale_color_hue(labels = c(spx = "S&P500", lrisk = "Low_risk", mrisk = "Mid_risk", hrisk = "High_risk"))
print(g)

f:id:drkernel:20171211184559j:plain
投資シミュレーションの結果は何度やってもROKOHOUSEが勝ちます。

5年間のリターン

1951年からの結果はわかりましたが、株価がボックス相場になった1965〜1980とか、レバレッジETFが壊滅的な打撃を受けるブラックマンデーやITバブル崩壊リーマンショックの影響をみてみるために、ある特定の時点から開始した場合に5年後(1250営業日)に何%上昇しているのかを求めます。
知りたいのはS&P500との比較なので、ROKOHOUSEのリターンからS&P500のリターンを引いたチャートにしてみました。

#ある時点で投資した時の1250営業日後(約5年)のリターン(%)
Price$Return_spx <- NA
Price$Return_lrisk <- NA
Price$Return_mrisk <- NA
Price$Return_hrisk <- NA
for(i in 1🙁nrow(Price)-1250)){
Price$Return_spx[i] <- (Price$Invest_spx[i+1250]-Price$Invest_spx[i])/Price$Invest_spx[i] * 100
Price$Return_lrisk[i] <- (Price$Invest_lrisk[i+1250]-Price$Invest_lrisk[i])/Price$Invest_lrisk[i] * 100
Price$Return_mrisk[i] <- (Price$Invest_mrisk[i+1250]-Price$Invest_mrisk[i])/Price$Invest_mrisk[i] * 100
Price$Return_hrisk[i] <- (Price$Invest_hrisk[i+1250]-Price$Invest_hrisk[i])/Price$Invest_hrisk[i] * 100
}
#S&P500のリターンとの差を求める
Price$Return_lrisk <- Price$Return_lrisk - Price$Return_spx
Price$Return_mrisk <- Price$Return_mrisk - Price$Return_spx
Price$Return_hrisk <- Price$Return_hrisk - Price$Return_spx
#プロット
Return_gg <- data.frame(Date = Price$Date,
lrisk = Price$Return_lrisk,
mrisk = Price$Return_mrisk,
hrisk = Price$Return_hrisk)
Return_gg <- Return_gg %>% gather(Index, Return, -Date)
g <- ggplot(Return_gg, aes(x=Date, y=Return, color=Index))+
geom_line()+
labs(color = "Return")+
scale_color_hue(labels = c(spx = "S&P500", lrisk = "Low_risk", mrisk = "Mid_risk", hrisk = "High_risk"))
print(g)

f:id:drkernel:20171211185354j:plain
Return=0より下の時から始めるとROKOHOUSE式が負けます。つまり、
1955年〜1962年、1980年前後と、2012年ごろ。
これはむしろ株価が非常に力強く上がるときは保有コストとリバランスがかかるレバレッジはかえってリターンが下がるということでしょうか?
詳しくないので分かりませんが。
S&P500に負けても5年で25%程度でした。

 

・昨日アップした検証記事
drkernel.hatenablog.com

コメント