前回の記事で1つの証券のリスクとリターンを分析することができました。
次に2つの証券について、それぞれの相関係数を分析し、表示してみましょう。
まずおさらいですが、ファイナンス理論では証券のリターンとリスクを手がかりにポートフォリをを構築します。
ポートフォリオに個別証券をどれだけの割合で組み入れるかを判断するためには、以下のパラメータが必要です。
- リターン(期待収益率)
- リスク(標準偏差)
- 各銘柄についての相関係数
今回の主題は3.ですね。
それでは作っていきましょう。
入力画面
今回もphpはdisplay関数を呼ぶだけにとどめ、マークアップはtplファイルに任せます。
<?php
require_once('portfoliomaker/setup.php');
$smarty = new Smarty_PortfolioMaker;
$smarty->display('input.tpl');
?>
<!DOCTYPE html>
<html lang=“ja”>
<head>
<meta charset=“UFT-8”>
<title>さいきょうのポートフォリオを作ろう!</title>
</head>
<body>
<h1>ETFのリスクとリターンを検索する</h1>
<form action="quants.php" method="POST">
<p>シンボル<input type=“text” name="symbols[]"></p>
<p>シンボル<input type=“text” name="symbols[]"></p>
<p><button type=“submit” value=“送信“>送信</button></p>
</form>
</body>
</html>
複数の証券をinput
するにあたって、配列を使うほうが拡張性があるため、name
の値をsymbols[]
としました。
以下の記事を参考にしました。
Rスクリプト設計
ここからはRスクリプトの機能を設計していきます。
PHPからRへ配列データを連携する
入力画面から入力されたテキストデータは配列となり、出力画面であるquants.php
に渡されます。
POST
の値として配列を受け取ったquants.php
ははRの解析スクリプトにその値を渡します。
しかし、RはPHPの配列を理解することができません。
そこで、__まずPHPで配列をカンマ区切りの文字列に変換してRに渡し、Rが文字列をカンマで区切ってベクトルに変換すること__とします。
コードを抜粋して示しましょう。
# (前略)
if (isset($_POST['symbols'])) {
$symbols = $_POST['symbols'];
$enc_symbols = implode(',', $symbols);
if (file_exists('C:/xampp/php/pear/portfoliomaker/R/analyze_risk_and_return.R')) {
$cmd = "Rscript.exe --vanilla C:/xampp/php/pear/portfoliomaker/R/analyze_risk_and_return.R $enc_symbols";
exec($cmd, $response);
# (中略)
}
# (後略)
}
quants.php
はimplode()
関数でPOST
された配列をカンマ区切りの文字列に変換します。
そして、その文字列をRscript.exe
のコマンドライン引数に渡しています。
# (前略)
args <- commandArgs(trailingOnly = TRUE)
symbols <- strsplit(args[1], ",")[[1]]
start.date <- paste(as.POSIXlt(Sys.Date())$year + 1880, "-01-01", sep = "")
end.date <- paste(as.POSIXlt(Sys.Date())$year + 1899, "-12-31", sep = "")
# 最長過去20年間のヒストリカルデータを取得する
raw.data <- tq_get(symbols, from = start.date, to = end.date) %>%
group_by(symbol)
# (後略)
analyze_risk_and_return.R
はコマンドライン引数で渡された文字列をstrsplit()
関数でカンマで区切ったベクトルに変換しています。
このベクトルはそのままtq_get()
関数に渡して使用することができます。
相関行列の作成
相関行列の元となる日次リターンデータを、ヒストリカルデータから作成します。
# (前略)
# GetStartDate関数
# 各Groupの開始日のうち最も遅い日付を取得する
# 引数:data
# 戻り値:data内のGroupについて最小のdateを集計し、その中で最大の日付
GetStartDate <- function(data) {
data %>%
summarise(start.date = min(date)) %>%
pull(start.date) %>%
max()
}
# AlignStartDate関数
# dataの開始日を揃える
# 引数:data
# 戻り値:dataのうち、dateがstart.date以降のデータ
AlignStartDate <- function(data) {
start.date <- GetStartDate(data)
filter(data, date >= start.date)
}
# (中略)
# ポートフォリオ内商品のデータ開始日を揃え、調整済み株価の日次リターンに変換する
portfolio.data <- raw.data %>%
AlignStartDate() %>%
tq_transmute(adjusted, mutate_fun = dailyReturn, col_rename = "return") %>%
slice(-1)
# (後略)
日次リターンデータの作成はtq_transmute()
関数で行います。
adjusted
(配当調整済み株価)から日次リターンを計算し、return
という列名のデータフォーマットにraw.data
を変換します。
計算開始日の行は不要なので、slice(-1)
で削ります。
変換したデータはportfolio.data
に保存しておきます。
次に、portfolio.data
から相関行列を作成します。
# (前略)
# CreateCorrelationDataFrame関数
# 日次リターンデータから相関行列を作成し、データフレームに変換する
# 引数:data
# 戻り値:相関データフレーム
CreateCorrelationDataFrame <- function(data) {
# データを時系列データに変換する
correlation.matrix <- ConvertToXts(data, symbol, return) %>%
# 相関行列を作成する
cor()
# symbolsと同じ順番に並び替える
correlation.matrix[symbols, symbols] %>%
# データフレームに変換する
data.frame()
}
# (中略)
# portfolio.dataの相関行列を作成する
correlation.data <- CreateCorrelationDataFrame(portfolio.data)
# (後略)
まずはportfoli.data
を時系列データに変換します。
そして、cor()
関数で相関行列を作成します。
portfolio.data
はsymbol
の並びがフォームに入力された順番と異なっている場合があるので、揃えておきます。
最後にデータフレームに変換して戻します。
RからPHPへJSONデータを連携する
あとはPHPに戻したいデータをJSON形式に変換してcat()
関数で出力します。
# (前略)
# performance.summaryをJSONにエンコードして出力する
split(performance.summary, 1:nrow(performance.summary)) %>%
unname() %>%
toJSON() %>%
cat("\n")
# (中略)
# portfolio.dataの相関行列を作成する
correlation.data <- CreateCorrelationDataFrame(portfolio.data)
# 相関行列をJSONにエンコードして出力する
split(correlation.data, 1:nrow(correlation.data)) %>%
unname() %>%
toJSON() %>%
cat("\n")
出力する際に改行文字("\n"
)を足しておくことで、PHPに渡したときに異なる配列の添字にデータを格納して渡すことができます。
出力設計
それではPHPに戻りましょう。
以下のコードでPHPからRにデータを渡し、解析した後でRからPHPにデータを渡します。
# (前略)
if (isset($_POST['symbols'])) {
$symbols = $_POST['symbols'];
$enc_symbols = implode(',', $symbols);
if (file_exists('C:/xampp/php/pear/portfoliomaker/R/analyze_risk_and_return.R')) {
$cmd = "Rscript.exe --vanilla C:/xampp/php/pear/portfoliomaker/R/analyze_risk_and_return.R $enc_symbols";
exec($cmd, $response);
$performance_summary = json_decode($response[0]);
$correlation_data = json_decode($response[1]);
$smarty->assign('performance_summary', $performance_summary);
$smarty->assign('correlation_data', $correlation_data);
}
# (中略)
}
$smarty->display('quants.tpl');
?>
Rでcat()
するときに改行文字を追加したことで、証券のリスク・リターンデータを$performance_summary
に、相関行列を$correlation_data
に容易に格納することができました。
各変数をassign()
関数で割り当て、quants.tpl
でマークアップしていきます。
<!DOCTYPE html>
<html lang=“ja”>
<head>
<meta charset=“UFT-8”>
<title>さいきょうのポートフォリオを作ろう!</title>
</head>
<body>
<h1>ETFのリスクとリターン</h1>
<div>
{section name = item_count loop = $performance_summary}
<p>シンボル:{$performance_summary[item_count]->Symbol}</p>
<p>リスク:{math equation = $performance_summary[item_count]->StdDev * 100 format = "%.2f"}%</p>
<p>リターン:{math equation = $performance_summary[item_count]->Avg_Ret * 100 format = "%.2f"}%</p>
<p>累積リターン:{math equation = $performance_summary[item_count]->Cum_Ret * 100 format = "%.2f"}%</p>
<p>シャープレシオ:{$performance_summary[item_count]->Sharpe}</p>
<p>最大ドローダウン:{math equation = $performance_summary[item_count]->MaxDD * 100 format = "%.2f"}%</p>
{if count($correlation_data) > 1}
<p>相関係数</p>
{section name = correlation_coefficient_id loop = $correlation_data}
{if $smarty.section.correlation_coefficient_id.index != $smarty.section.item_count.index}
<p>
{$performance_summary[correlation_coefficient_id]->Symbol}:{math equation = $correlation_data[item_count]->{$performance_summary[correlation_coefficient_id]->Symbol} format = "%.2f"}
</p>
{/if}
{/section}
{/if}
{/section}
</div>
</body>
</html>
証券のリスク・リターンデータと相関行列はどちらも配列です。
配列の各要素には連想配列が格納されています。
配列の添字が意味する証券のシンボルは各配列で一致するように設計しています。
例えば、証券のリスク・リターンデータの0番目がVTのデータなら、相関行列の0番目もVTのデータを意味しています。
配列の各要素に対して反復処理を行う場合は、{section}
を使います。
まず最初の{section}
で個別証券のリスク・リターンなどの情報を表示します。
その後、ネストした{section}
の中で相関係数を表示します。
同じ証券同士の相関係数は1になるので、表示する必要はありませんから、{if}
で間引いています。
これで複数証券のリスク・リターンと相関行列を表示することができました。
次回はこれらを図示して効率的フロンティアを表示してみたいと思います。