Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Portfolio Performance Attribution

포트폴리오의 초과수익(Excess Return)을 벤치마크(S&P500 섹터 ETF) 대비 분해하여, 섹터 비중 조절(Allocation)과 종목선택(Selection)이 성과에 각각 얼마나 기여했는지 정량적으로 분석하는 프로젝트다.

파이프라인

데이터 수집 → 포트폴리오/벤치마크 구성 → 섹터별·전체 수익률 계산 → 누적수익률 비교 → 성과지표(CAGR/Sharpe/Alpha/Beta/IR/Tracking Error) → Brinson-Fachler 귀속분석 → 섹터/종목 기여도 시각화 → Attribution Dashboard → 자동 리포트 생성, 8단계 전체가 노트북 하나 (notebooks/01_performance_attribution.ipynb)에 순서대로 구현되어 있다.

핵심 아이디어: Brinson-Fachler 2항 분해

섹터 $i$, 한 기간에 대해 벤치마크 전체 수익률을 $R_b = \sum_i w_{b,i} r_{b,i}$라 하면:

$$\text{Allocation}_i = (w_{p,i} - w_{b,i}) \times (r_{b,i} - R_b)$$ $$\text{Selection}_i = w_{p,i} \times (r_{p,i} - r_{b,i})$$

Selection 효과에 벤치마크 비중이 아니라 포트폴리오 비중을 쓰는 것이 Brinson-Fachler의 핵심이다. 이렇게 하면 BHB(Brinson-Hood-Beebower) 모델처럼 별도의 Interaction 항을 두지 않고도, 섹터 합계가 정확히 총 초과수익과 일치한다:

$$\sum_i (\text{Allocation}_i + \text{Selection}_i) = R_p - R_b$$

이 재구성 항등식은 tests/test_attribution.py에서 합성 데이터로 직접 검증한다. 여러 달에 걸친 결과는 월별 효과를 산술적으로 합산하며, 이는 복리를 정확히 반영하는 다기간 연결 (Frongello/Menchero smoothing)의 근사치다.

대상 포트폴리오 및 벤치마크

  • 벤치마크: S&P500 GICS 11개 섹터의 SPDR 섹터 ETF (XLK/XLF/XLV/XLY/XLC/XLI/ XLP/XLE/XLU/XLRE/XLB, yfinance), 섹터 비중은 근사 스냅샷(src/config.py)
  • 포트폴리오: 성장주(Technology/Communication Services/Health Care) 오버웨이트, 가치· 경기방어주(Financials/Industrials/Consumer Staples/Energy/Utilities/Real Estate/Materials) 언더웨이트로 구성한 샘플 액티브 포트폴리오, 섹터별 실제 종목 1~3개 (AAPL/MSFT/NVDA/ GOOGL/META/UNH/LLY 등)
  • 분석 기간: 최근 3년 (src/config.pySTART_DATE/END_DATE), 수익률 빈도: 월간

폴더 구조

performance-attribution/
├── data/
│   ├── raw/                # yfinance 원본 가격 캐시 (git 미포함, 최초 실행 시 자동 생성)
│   └── processed/          # 월간 수익률 데이터
├── notebooks/
│   └── 01_performance_attribution.ipynb   # 1~8단계 전체를 순서대로 실행하는 노트북
├── src/
│   ├── config.py            # 섹터 ETF, 벤치마크/포트폴리오 비중, 분석 기간, 성과지표 파라미터
│   ├── data_loader.py       # 포트폴리오 보유종목 + 벤치마크 섹터 ETF 가격 수집 및 캐싱
│   ├── portfolio.py         # 보유종목 수익률 → 섹터별/전체 포트폴리오 수익률
│   ├── benchmark.py         # 섹터 ETF 수익률 → 섹터별/전체 벤치마크 수익률
│   ├── attribution.py       # Brinson-Fachler 귀속분석 (월별 계산 + 기간 합산)
│   ├── metrics.py           # CAGR, 변동성, Sharpe, Beta, Alpha, Tracking Error, Information Ratio
│   └── report.py            # 종목별 기여도 계산 + 자동 리포트(markdown) 생성
├── tests/
│   ├── test_attribution.py  # Brinson-Fachler 재구성 항등식, 효과별 sanity 테스트
│   └── test_metrics.py      # 성과지표 계산 sanity 테스트
├── requirements.txt
└── pytest.ini

실행 방법

pip install -r requirements.txt
jupyter notebook notebooks/01_performance_attribution.ipynb

단위 테스트 실행:

pytest

한계 및 향후 개선 방향

  • 벤치마크 섹터 비중과 포트폴리오 종목 비중은 분석 기간 내내 고정된 스냅샷이다. 실제로는 지수 구성비와 포트폴리오 비중 모두 매달 변하므로, 월별 실제 비중을 반영하면 귀속분석 정확도가 높아진다.
  • 다월 구간의 귀속분석 결과는 월별 효과를 산술적으로 합산한 것으로, 복리 효과를 정확히 반영하는 다기간 연결 방식은 아니다. 분석 기간이 길어질수록 산술합과 복리 기준 초과수익의 차이가 커질 수 있다.
  • 종목별 기여도도 같은 이유로 비중을 고정한 근사치이며, 매월 리밸런싱에 따른 비중 변화 (수익률에 따른 자연 표류)는 반영하지 않는다.
  • 거래비용, 슬리피지, 세금, 배당재투자 타이밍 차이는 반영되지 않았다.
  • 섹터 수익률을 개별 종목이 아닌 SPDR 섹터 ETF로 근사했기 때문에, 실제 GICS 지수 구성종목과 ETF 보유종목이 완전히 일치하지는 않는다.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages