Ch 04. 포트폴리오 분석 리포트
지금까지 단일 종목을 분석했습니다. 이제 여러 종목을 한 바구니에 담아 포트폴리오 관점에서 분석합니다. 좋은 포트폴리오는 단순히 수익률이 높은 종목들의 조합이 아닙니다. 서로 다르게 움직이는 종목을 섞어야 한 쪽이 떨어질 때 다른 쪽이 버텨줍니다. 이 챕터에서는 상관분석으로 종목 간 관계를 파악하고, 전체 분석을 하나의 리포트로 정리합니다.
분석 대상 종목 선정
빅테크 3종목과 ETF 1종목으로 포트폴리오를 구성합니다.
- AAPL: Apple (기술주)
- MSFT: Microsoft (기술주)
- GOOGL: Alphabet/Google (기술주)
- SPY: S&P 500 ETF (시장 벤치마크)
# 새 파일: analysis/portfolio_report.R
library(tidyquant)
library(tidyverse)
library(scales)
library(patchwork)
library(corrplot) # 상관계수 히트맵
# 데이터 수집
tickers <- c("AAPL", "MSFT", "GOOGL", "SPY")
portfolio_raw <- tq_get(
tickers,
get = "stock.prices",
from = "2022-01-01",
to = "2024-12-31"
)
# 일일 수익률 계산
portfolio_returns <- portfolio_raw |>
group_by(symbol) |>
tq_transmute(
select = adjusted,
mutate_fun = periodReturn,
period = "daily",
col_rename = "daily_return"
)
glimpse(portfolio_returns)
수익률 비교 시각화
# 누적 수익률 계산 및 비교
cum_returns <- portfolio_returns |>
group_by(symbol) |>
arrange(date) |>
mutate(cum_return = cumprod(1 + daily_return) - 1)
# 누적 수익률 라인 차트
p_cum <- cum_returns |>
ggplot(aes(x = date, y = cum_return, color = symbol)) +
geom_line(linewidth = 0.8) +
geom_hline(yintercept = 0, linetype = "dashed", color = "gray50") +
scale_y_continuous(labels = percent) +
scale_color_manual(
values = c(
AAPL = "#1976D2",
MSFT = "#388E3C",
GOOGL = "#F57C00",
SPY = "#7B1FA2"
)
) +
labs(
title = "포트폴리오 누적 수익률 비교",
subtitle = "2022년 1월 ~ 2024년 12월, SPY 대비 성과",
x = "날짜",
y = "누적 수익률",
color = "종목"
) +
theme_minimal(base_family = "AppleGothic")
p_cum
연도별 수익률 막대 그래프
# 월별 수익률 계산 후 연간 집계
annual_returns <- portfolio_raw |>
group_by(symbol) |>
tq_transmute(
select = adjusted,
mutate_fun = periodReturn,
period = "yearly",
col_rename = "annual_return"
) |>
mutate(year = year(date))
# 연도별 수익률 막대 그래프
p_bar <- annual_returns |>
ggplot(aes(x = factor(year), y = annual_return, fill = symbol)) +
geom_col(position = "dodge", alpha = 0.85) +
geom_hline(yintercept = 0, color = "gray40") +
scale_y_continuous(labels = percent) +
scale_fill_manual(
values = c(
AAPL = "#1976D2",
MSFT = "#388E3C",
GOOGL = "#F57C00",
SPY = "#7B1FA2"
)
) +
labs(
title = "연도별 수익률 비교",
x = "연도",
y = "연간 수익률",
fill = "종목"
) +
theme_minimal(base_family = "AppleGothic")
p_bar
상관분석
서로 다른 종목의 수익률이 얼마나 함께 움직이는지를 상관계수로 측정합니다. 상관계수가 1에 가까우면 같은 방향으로 움직이고, 0에 가까우면 독립적이며, -1에 가까우면 반대 방향으로 움직입니다.
# wide 형식으로 변환 (종목을 열로)
returns_wide <- portfolio_returns |>
pivot_wider(
names_from = symbol,
values_from = daily_return
) |>
drop_na()
# 상관계수 행렬 계산
cor_matrix <- returns_wide |>
select(-date) |>
cor()
round(cor_matrix, 3)
# corrplot으로 상관계수 히트맵
corrplot(
cor_matrix,
method = "color",
type = "upper",
order = "hclust",
addCoef.col = "black",
tl.col = "black",
tl.srt = 45,
col = colorRampPalette(c("#1976D2", "white", "#E53935"))(200),
title = "종목 간 수익률 상관계수",
mar = c(0, 0, 2, 0)
)
# ggplot2로 상관계수 히트맵 (corrplot 대안)
cor_df <- as.data.frame(cor_matrix) |>
rownames_to_column("symbol1") |>
pivot_longer(-symbol1, names_to = "symbol2", values_to = "correlation")
cor_df |>
ggplot(aes(x = symbol1, y = symbol2, fill = correlation)) +
geom_tile(color = "white", linewidth = 0.5) +
geom_text(aes(label = round(correlation, 2)), size = 4, fontface = "bold") +
scale_fill_gradient2(
low = "#1976D2",
mid = "white",
high = "#E53935",
midpoint = 0,
limits = c(-1, 1)
) +
coord_fixed() +
labs(
title = "종목 간 수익률 상관계수",
x = NULL,
y = NULL,
fill = "상관계수"
) +
theme_minimal(base_family = "AppleGothic") +
theme(axis.text = element_text(size = 11))
위험-수익률 산점도
# 종목별 위험-수익률 요약 통계
risk_return_summary <- portfolio_returns |>
group_by(symbol) |>
summarise(
ann_return = mean(daily_return) * 252 * 100,
ann_vol = sd(daily_return) * sqrt(252) * 100,
sharpe = (mean(daily_return) * 252) / (sd(daily_return) * sqrt(252))
) |>
mutate(across(where(is.numeric), \(x) round(x, 2)))
risk_return_summary
샤프 지수(Sharpe Ratio)는 단위 위험 대비 수익률입니다. 값이 클수록 같은 위험을 감수하고 더 많은 수익을 얻은 것입니다.
# 위험-수익률 산점도 + 샤프 지수 레이블
library(ggrepel)
risk_return_summary |>
ggplot(aes(x = ann_vol, y = ann_return)) +
geom_point(aes(size = sharpe), color = "#1976D2", alpha = 0.8) +
geom_text_repel(
aes(label = paste0(symbol, "\n샤프:", sharpe)),
size = 3.5,
fontface = "bold"
) +
geom_hline(yintercept = 0, linetype = "dashed", color = "gray50") +
scale_size_continuous(range = c(4, 12), guide = "none") +
labs(
title = "위험-수익률 포지셔닝",
subtitle = "점 크기 = 샤프 지수",
x = "연간화 변동성 (%)",
y = "연간화 수익률 (%)"
) +
theme_minimal(base_family = "AppleGothic")
최종 분석 리포트 작성
지금까지의 분석을 R Markdown 리포트로 정리합니다. 아래는 리포트 구조를 코드로 표현한 것입니다. 실제 .Rmd 파일로 만들면 HTML 문서로 렌더링됩니다.
# 새 파일: reports/portfolio_report.Rmd
# (아래는 Rmd 파일 전체 내용)
---
title: "포트폴리오 분석 리포트"
subtitle: "AAPL · MSFT · GOOGL · SPY | 2022-2024"
author: "R 데이터 분석 실습"
date: "`r Sys.Date()`"
output:
html_document:
toc: true
toc_float: true
theme: flatly
highlight: tango
code_folding: hide
---
## 1. 분석 개요
본 리포트는 빅테크 3종목(AAPL, MSFT, GOOGL)과 시장 벤치마크(SPY)의
2022~2024년 성과를 비교 분석합니다.
## 2. 수익률 비교
`r ''````{r cum-returns, fig.width=10, fig.height=5}
# 누적 수익률 차트 코드
`r ''````
## 3. 위험 분석
`r ''````{r volatility}
# 변동성 분석 코드
`r ''````
## 4. 상관분석
`r ''````{r correlation, fig.width=6, fig.height=6}
# 상관계수 히트맵 코드
`r ''````
## 5. 핵심 지표 요약
`r ''````{r summary-table}
knitr::kable(
risk_return_summary,
caption = "종목별 핵심 지표 요약",
col.names = c("종목", "연간수익률(%)", "연간변동성(%)", "샤프지수")
)
`r ''````
## 6. 결론
- MSFT는 세 빅테크 중 가장 안정적인 위험-수익률 균형을 보였습니다.
- GOOGL은 변동성이 가장 높았으나 2023년 이후 강한 반등을 기록했습니다.
- SPY 대비 세 종목 모두 초과 수익을 달성했습니다.
리포트를 렌더링하려면 RStudio에서 파일을 열고 "Knit" 버튼을 누르거나, 콘솔에서 rmarkdown::render("reports/portfolio_report.Rmd")를 실행합니다.
전체 요약 대시보드
patchwork로 핵심 차트를 한 화면에 배치합니다.
# 최종 대시보드 레이아웃
dashboard <- (p_cum | p_bar) / (cor_heatmap | risk_scatter) +
plot_annotation(
title = "포트폴리오 종합 분석 대시보드",
subtitle = "AAPL · MSFT · GOOGL · SPY | 2022-2024",
theme = theme(
plot.title = element_text(size = 16, face = "bold"),
plot.subtitle = element_text(size = 12, color = "gray40")
)
)
# 고해상도 저장
ggsave(
"reports/portfolio_dashboard.png",
plot = dashboard,
width = 16,
height = 10,
dpi = 150
)
이 챕터를 마치며
단일 종목 분석에서 시작해 포트폴리오 분석까지 이어지는 전체 흐름을 완성했습니다. 수익률 계산, 변동성 측정, 상관분석, 샤프 지수, R Markdown 리포트까지 실전 금융 분석에 필요한 핵심 도구들을 모두 사용했습니다.
PART 06은 여기서 끝납니다. 마지막 PART 07에서는 전체 교재를 돌아보고, 다음 단계로 나아갈 방향을 정리합니다.