포트폴리오 이론에서의 기대수익률
포트폴리오 기대수익률은 개별 자산의 기대수익률의 가중치를 고려하여 만든 포트폴리오의 기대수익률이다
mean - variance space
자산 투자에 있어서 기대수익률을 위해서는 투자의 리스크를 감수해야 한다. 즉, 위험 한 단위가 증가할 때 기대수익률 또한 증가한다.
Indifference Curve
Risk Averse의 성향이 많은 개인 투자자는 위험에 대해 기피하는 경향이 있다. 따라서, 위험 한 단위가 증가할 때마다 기대수익률을 더 많이 받으려고 하는 경향을 보인다. 따라서 무차별 곡선은 우상향하며, 기울기는 체증한다. 반면, Risk lover의 Indifference Cuve의 기울기는 체감한다.
Portfolio Risk
포트폴리오의 리스크는 각 자산의 기대수익률간의 편차로 계산한다.
Example
2000년 1월부터 2020년 12월까지 기간동안 고려한 자산은 다음과 같다
- 스페인 국채 10년물
- KOSPI 주가지수
- S&P 주가지수
- 원유 선물
- 천연가스 선물
- 금 선물
- 구리 선물
import numpy as np import pandas as pd import matplotlib.pyplot as plt %matplotlib inline
csv data의 변수명은 다음과 같이 정의한다
- data : 수익률 데이터
- cycle : 경기순환 사이클 데이터
import한 데이터는 다음과 같이 세가지 형식으로 분류한다
df = pd.concat([data.iloc[:,1:],cycle.iloc[:,1:]], axis = 1) # 전체 데이터 df_up = df[df.cycle == 1] # 경기 확장기의 데이터 df_down = df[df.cycle == 0] # 경기 후퇴기의 데이터
noa = len(df.columns[:-1]) # 포트폴리오 구성자산의 갯수 port_df = [] port_std = []
함수 ret_std는 가중치 weight과 ret(기대수익률)을 인자로 받는 함수이다.
def ret_std(weight, ret): port_mean = np.sum(weight * ret.mean() * 252) port_var = np.dot(weight.T, np.dot(ret.cov()*252, weight)) port_std = np.sqrt(port_var) return port_mean, port_std
다음과 같이 포트폴리오의 개수 만큼 반복문을 돌린다. 예제에서는 5000개의 포트폴리오를 사용하였다.
for i in range(5000): weight = np.random.random(noa) weight /= np.sum(weight) mu, sig = ret_std(weight, df.iloc[:,:-1]) port_df.append(mu) port_std.append(sig)
Sharpe Ratio (sr)
포트폴리오 선택에 따른 초과수익률을 의미한다. 수익률을 위험으로 나누어 구한다.
sr = np.array(port_df)/np.array(port_std)
Optimization
scipy module의 optimize를 사용한다. 포트폴리오의 수익률은 개별수익률과 가중치의 내적의 합이다.
statistics 함수는 가중치 weights를 인자로 받는 함수로, 포트폴리오 전체의 수익률과 리스크를 반환한다
import scipy.optimize as opt def statistics(weights, rf=0): weights = np.array(weights) pret = np.sum(df.iloc[:,:-1].mean() * weights) * 252 - rf pvol = np.sqrt(np.dot(weights.T, np.dot(df.iloc[:,:-1].cov() * 252, weights))) return np.array([pret, pvol, pret / pvol])
min_func_port 함수는 portfolio의 최적점을 도출해내는 가중치의 값으로 계산한 리스크를 반환한다.
def min_func_port(weights): return statistics(weights)[1]
아래와 같이 mean variance frontier line을 구한다.
cons = ({'type': 'eq', 'fun': lambda x: statistics(x)[0] - tret}, {'type': 'eq', 'fun': lambda x: np.sum(x) - 1}) bnds = tuple([(0, 1) for x in range(noa)]) trets = np.linspace(0.02, 0.2, 50) tvols = [] for tret in trets: cons = ({'type': 'eq', 'fun': lambda x: statistics(x)[0] - tret}, {'type': 'eq', 'fun': lambda x: np.sum(x) - 1}) res = opt.minimize(min_func_port, noa * [1. / noa,], method='SLSQP' , bounds=bnds, constraints=cons) tvols.append(res['fun']) tvols = np.array(tvols)
시각화
plt.style.use('seaborn') plt.figure(figsize=(12, 8)) plt.scatter(port_std, port_df, c=np.array(port_df) / np.array(port_std), marker='o', cmap='PuBuGn') plt.plot(tvols, trets, label = 'mean-variance frontier') plt.legend() plt.grid(True) plt.ylabel('expected return$(μ)$') plt.xlabel('expected std $(σ)$'); plt.colorbar(label='Sharpe ratio')
위와 같은 방법으로 확장기와 후퇴기의 frontier 또한 도출하였다
댓글
댓글 쓰기