Materi dari studi ini diambil dari minggu kedua kursus Introduction to Portfolio Construction and Analysis with Python.
Sumber-sumber lain:
Misalkan kita membangun portfolio yang berisi dua aset, yaitu aset A dan aset B. Return, volatilitas (dalam hal ini standard deviasi), dan alokasi (weight) masing-masing aset direpresentasikan dengan R, σ, dan w.
Maka return portfolio adalah weighted average dari masing-masing return:
$$ R(w_A, w_B) = w_A \times R_A + w_B \times R_B $$Sedangkan untuk volatilitas, nilainya tergantung dari korelasi antar aset. Kalau aset-asetnya berkorelasi sempurna, maka pada dasarnya aset-aset tersebut akan bergerak secara seragam, sehingga volatilitasnya adalah semacam rata-rata dari volatilitas aset-aset tersebut. Semakin rendah korelasinya, maka volatilitas dari portfolio akan semakin rendah juga, sampai pada suatu titik ketika korelasi antara A dan B cukup rendah sehingga bahkan volatilitas dari portfolio lebih rendah dari volatilitas baik A atau B!
Perhitungan untuk volatility adalah sbb:
$$ \sigma^2(w_A, w_B) = \sigma_A^2 w_A^2 + \sigma_B^2 w_B^2 + 2 w_A w_B \sigma_A \sigma_B \rho_{A,B} $$dimana $\rho_{A,B}$ adalah korelasi dari A dan B.
Men-generalisasi formula di bagian sebelumnya, kalau portfolio berisi lebih dari dua aset, maka return-nya adalah:
$$ R_p = \sum_{i=1}^{k} w_i R_o $$Sedangkan volatilitasnya adalah:
$$ \sigma_p^2 = \sum_{i=1}^{k} \sum_{j=1}^{k} w_i w_j \sigma_i \sigma_j \rho_{i, j} $$Volatilitas dari aset i dikali volatilitas dari aset j dikali korelasi dari aset i dan j adalah covariance dari aset i dan j, sehingga persamaan di atas bisa disederhanakan menjadi:
$$ \sigma_p^2 = \sum_{i=1}^{k} \sum_{j=1}^{k} w_i w_j \sigma_{i, j} $$Di mana $\sigma_{i,i}$ adalah covariance dari aset i dan j. Sebagai tambahan info, $\sigma_{i,i}$ adalah variance dari aset i (karena $\sigma_{i,i} = \sigma_i \sigma_i \rho_{i,i}$ dan $\rho_{i,i}$ tentunya adalah 1).
Efficient frontier yang ditemukan oleh pemenang Nobel Harry Markowitz di 1952, adalah kumpulan portfolio yang memberikan "performansi" yang paling optimal, yaitu memberikan return tertinggi untuk suatu resiko yang telah ditentukan, atau sebaliknya, resiko terendah untuk suatu return yang telah ditentukan.
Efficient frontier sering digambarkan sebagai kurva seperti di bawah ini.
Sumbu X adalah resiko, dan sumbu Y adalah return. Untuk suatu pilihan aset-aset, kombinasi yang melambangkan efficient frontier dilambangkan dengan kurva garis merah. Kombinasi lain dari aset-aset yang menyimpang dari efficient frontier bukanlah kombinasi yang bagus. Misalnya, ambil titik A, yang melambangkan suatu portfolio dengan kombinasi alokasi aset-aset tertentu. Kita tidak akan mau memilih portfolio A ini, karena ada portfolio lain (artinya dengan menggunakan kombinasi alokasi aset yang berbeda) dimana untuk resiko yang sama kita bisa mendapatkan return yang lebih tinggi (yaitu titik B), atau dengan return yang sama kita bisa mendapatkan resiko yang lebih rendah (titik C).
Kali ini kita akan mendemonstrasikan perhitungan return dan volatilitas dari portfolio. Data yang digunakan adalah data return bulanan beberapa industri/sektor mulai dari tahun 1926 sampai 2018. Data ini saya ambil dari kursus Introduction to Portfolio Construction and Analysis with Python oleh Vijay Vaidyanathan. Datanya sendiri bersumber dari dan hak cipta oleh Kenneth French
In [24]:
import pandas as pd
import numpy as np
%matplotlib inline
# Load data returns dari sektor2
ind = pd.read_csv("ind30_m_vw_rets.csv", header=0, index_col=0)/100
# Ubah index jadi perioda bulanan
ind.index = pd.to_datetime(ind.index, format="%Y%m").to_period('M')
# Hilangkan spasi pada kolom
ind.columns = ind.columns.str.strip()
# Batasi lingkup agar sama dengan di MOOC
ind = ind["1996":"2000"]
# Konversi returns menjadi tahunan. "er" adalah expected return
compounded_growth = (1+ind).prod()
er = compounded_growth ** (12/ind.shape[0]) -1
print('Expected returns:')
print(er)
Kita buat covariance matrix-nya.
In [2]:
# Covariance matrix
cov = ind.cov()
Agar lebih sederhana, kita pilih 4 industri saja dalam portfolio kita.
In [3]:
assets = ['Food', 'Beer', 'Smoke', 'Coal']
Expected returns dan covariance matrix untuk aset-aset ini:
In [4]:
er[assets]
Out[4]:
In [5]:
cov.loc[assets, assets]
Out[5]:
Mari membuat fungsi untuk menghitung expected return dan volatilitas dari portfolio dengan operasi matriks seperti dijelaskan di atas.
In [6]:
def portfolio_return(weights, returns):
return weights.T @ returns
def portfolio_vol(weights, covmat):
return (weights.T @ covmat @ weights)**0.5
Misalkan 4 aset tadi kita alokasikan secara seimbang:
In [7]:
weights = np.repeat(1/4, 4)
weights
Out[7]:
Kita bisa hitung expected return dari portfolionya dengan alokasi ini:
In [8]:
portfolio_return(weights, er[assets])
Out[8]:
Dan juga volatilitasnya:
In [9]:
portfolio_vol(weights, cov.loc[assets, assets])
Out[9]:
In [10]:
# Pilih 2 aset
assets = ['Games', 'Fin']
# Generate kombinasi alokasi untuk dua aset
N_POINTS = 20
weights = [np.array([w, 1-w]) for w in np.linspace(0, 1, N_POINTS)]
weights
Out[10]:
Sekarang mari kita hitung returns dari kombinasi alokasi di atas, dan kita masukkan dalam list.
In [11]:
rets = [portfolio_return(w, er[assets]) for w in weights]
rets
Out[11]:
Kita lakukan hal yang sama untuk volatility.
In [12]:
vols = [portfolio_vol(w, cov.loc[assets,assets]) for w in weights]
vols
Out[12]:
Mari kita gabungkan return dan volatilitas menjadi dataframe agar mudah diplot.
In [13]:
ef = pd.DataFrame(data={'Return': rets,
'Volatility': vols})
ef
Out[13]:
Sekarang mari kita plot volatility vs return-nya.
In [14]:
ef.plot.line(x='Volatility', y='Return',
title='Efficient Frontier dengan Dua Aset ({} dan {})'.format(assets[0], assets[1]),
figsize=(15,6), style='.-')
Out[14]:
Done! Kita telah menggambar efficient frontier untuk 2 aset.
Garis besar langkah-langkahnya kurang lebih adalah sebagai berikut:
n_points
). Contoh:n_points=20
), maka kita akan mempunyai list dengan isi 20 element, mulai dari return terendah yaitu 10% sampai tertinggi yaitu 90%: [10.0, 14.2, 18.4, ..., 85.8, 90.0]
minimize_vol()
di bawah. Fungsi ini memanggil fungsi minimize()
yang disediakan oleh pustaka scipy.optimize
.portfolio_vol()
, dan juga returnnya dengan memanggil portfolio_return()
.Kali ini langsung akan saya copy-paste kodenya dari lab MOOC Introduction to Portfolio Construction and Analysis with Python minggu kedua.
In [15]:
from scipy.optimize import minimize
def minimize_vol(target_return, er, cov):
"""
Returns the optimal weights that achieve the target return
given a set of expected returns and a covariance matrix
"""
n = er.shape[0]
init_guess = np.repeat(1/n, n)
bounds = ((0.0, 1.0),) * n # an N-tuple of 2-tuples!
# construct the constraints
weights_sum_to_1 = {'type': 'eq',
'fun': lambda weights: np.sum(weights) - 1
}
return_is_target = {'type': 'eq',
'args': (er,),
'fun': lambda weights, er: target_return - portfolio_return(weights,er)
}
weights = minimize(portfolio_vol, init_guess,
args=(cov,), method='SLSQP',
options={'disp': False},
constraints=(weights_sum_to_1,return_is_target),
bounds=bounds)
return weights.x
def optimal_weights(n_points, er, cov):
"""
"""
target_rs = np.linspace(er.min(), er.max(), n_points)
weights = [minimize_vol(target_return, er, cov) for target_return in target_rs]
return weights
def plot_ef(n_points, er, cov):
"""
Plots the multi-asset efficient frontier
"""
weights = optimal_weights(n_points, er, cov)
rets = [portfolio_return(w, er) for w in weights]
vols = [portfolio_vol(w, cov) for w in weights]
ef = pd.DataFrame({
"Returns": rets,
"Volatility": vols
})
ax = ef.plot.line(x="Volatility", y="Returns", style='.-',
label='Efficient Frontier', legend=True, figsize=(15,6))
ax.set_ylabel('Returns')
ax.set_xlim(left=0)
ax.set_ylim(bottom=0)
return ax
In [32]:
assets = ['Smoke', 'Fin', 'Games', 'Coal']
plot_ef(25, er[assets], cov.loc[assets, assets])
Out[32]:
Whoa! It works!
Capital Market Line (CML) adalah kurva yang menggambarkan grafik resiko vs return dari portfolio yang merupakan gabungan dari risk free asset dan risky asset. Garis CML menggambarkan return dari portfolio yang naik seiring dengan ditambahkannya risky asset dalam portfolio, seperti dalam grafik di bawah ini.
Pada alokasi yang optimal antara risk free dan risky asset, portfolio akan menghasilkan nilai Sharpe Ratio yang optimal. Titik ini adalah persinggungan antara kurva Capital Market Line dan Efficient Frontier, seperti terlihat dalam gambar di bawah.
Dan titik-titik dalam garis CML adalah kurva risk-return yang optimal untuk portolio yang merupakan perpaduan antara risk free asset dan risky asset.
In [45]:
# Credit: Vijay Vaidyanathan
# https://www.coursera.org/learn/introduction-portfolio-construction-python
def msr(riskfree_rate, er, cov):
"""
Returns the weights of the portfolio that gives you the maximum sharpe ratio
given the riskfree rate and expected returns and a covariance matrix
"""
n = er.shape[0]
init_guess = np.repeat(1/n, n)
bounds = ((0.0, 1.0),) * n # an N-tuple of 2-tuples!
# construct the constraints
weights_sum_to_1 = {'type': 'eq',
'fun': lambda weights: np.sum(weights) - 1
}
def neg_sharpe(weights, riskfree_rate, er, cov):
"""
Returns the negative of the sharpe ratio
of the given portfolio
"""
r = portfolio_return(weights, er)
vol = portfolio_vol(weights, cov)
return -(r - riskfree_rate)/vol
weights = minimize(neg_sharpe, init_guess,
args=(riskfree_rate, er, cov), method='SLSQP',
options={'disp': False},
constraints=(weights_sum_to_1,),
bounds=bounds)
return weights.x
In [43]:
def plot_cml(ax, riskfree_rate, w_msr, er, cov):
r = portfolio_return(w_msr, er)
vol = portfolio_vol(w_msr, cov)
x = [0, vol]
y = [riskfree_rate, r]
ax.plot(x, y, color='green', marker='o', label='CML',
linestyle='-', linewidth=2, markersize=10)
ax.legend()
RISKFREE_RATE = 0.10
ax = plot_ef(25, er[assets], cov.loc[assets, assets])
w_msr = msr(RISKFREE_RATE, er[assets], cov.loc[assets, assets])
plot_cml(ax, RISKFREE_RATE, w_msr, er[assets], cov.loc[assets, assets])
Walaupun pada awalnya Efficient frontier terlihat sangat menjanjikan, ternyata kemudian diketahui bahwa pendekatan ini mempunyai kelemahan yang cukup fatal, yang bahkan membuatnya kurang layak (feasible) untuk dipakai. Kelemahannya adalah, EF membutuhkan nilai expected return yang akurat, dan sedikit perbedaan pada nilai expected return akan menyebabkan perubahan yang drastis pada alokasi terhadap aset-asetnya.
Yuk kita demonstrasikan berikut. Agar sederhanan kita pakai dua aset saja.
In [35]:
assets = ['Food', 'Steel']
Expected return dari kedua asset di atas adalah:
In [36]:
er[assets]
Out[36]:
MSR untuk dua asset di atas adalah:
In [37]:
msr(RISKFREE_RATE, er[assets], cov.loc[assets, assets])
Out[37]:
Jadi alokasi yang optimal adalah Food 75% dan Steel 25%.
Sekarang mari kita lihat perubahan pada alokasi aset kalau expected return dari asetnya kita rubah sedikit.
In [38]:
msr(RISKFREE_RATE, np.array([0.11, 0.12]), cov.loc[assets, assets])
Out[38]:
Maka prosentase alokasinya sudah berubah jauh. Padahal perubahannya tidak sampai 1%.
Sekarang coba kita rubah expected return-nya lebih banyak.
In [39]:
msr(RISKFREE_RATE, np.array([0.10, 0.13]), cov.loc[assets, assets])
Out[39]:
Wow! Sekarang MSR mengalokasikan 100% ke aset Steel. Demikian juga kalau kita rubah sebaliknya:
In [40]:
msr(RISKFREE_RATE, np.array([0.13, 0.10]), cov.loc[assets, assets])
Out[40]:
Maka MSR mengalokasikan 100% ke Food. Padahal perubahannya tidak sampai 2%.
Dalam dunia nyata, kita harus ingat bahwa nilai expected return adalah nilai ramalan dari return suatu aset untuk periode ke depan. Nilai ini dibuat oleh analis. Kalaupun ada kesalahan 2%, maka di dunia nyata ini sudah merupakan ramalan yang sangat bagus. Namun ternyata hal ini menghasilkan perubahan alokasi yang sangat drastis jika kita memakai MSR.
Oleh karena itu orang kemudian memakai perhitungan alokasi lain, misalnya GMV di bawah ini.
GMV portfolio adalah portfolio dengan volatilitas terendah yang mungkin dicapai dengan mengombinasikan aset-aset yang ada. Dalam grafik EF, titik GMV adalah ujung "hidung" kurva EF, seperti terlihat di bawah.
Kelebihan GMV adalah perhitungannya hanya membutuhkan matriks covariance, dan tidak membutuhkan expected return, sehingga terbebas dari masalah kesalahan pada prediksi expected return di atas.
In [44]:
# Credit: Vijay Vaidyanathan
# https://www.coursera.org/learn/introduction-portfolio-construction-python
def gmv(cov):
"""
Returns the weights of the Global Minimum Volatility portfolio
given a covariance matrix
"""
n = cov.shape[0]
return msr(0, np.repeat(1, n), cov)
In [46]:
assets = ['Smoke', 'Fin', 'Games', 'Coal']
def plot_point(ax, weights, er, cov, label, color='C1'):
r = portfolio_return(weights, er)
vol = portfolio_vol(weights, cov)
x = [vol]
y = [r]
ax.plot([vol], [r], color=color, marker='o', label=label,
linestyle='-', linewidth=2, markersize=10)
ax.legend()
ax = plot_ef(25, er[assets], cov.loc[assets, assets])
w_gmv = gmv(cov.loc[assets, assets])
plot_point(ax, w_gmv, er[assets], cov.loc[assets, assets], 'GMV')
In [50]:
n_assets = len(assets)
ax = plot_ef(25, er[assets], cov.loc[assets, assets])
w_ew = np.repeat(1/n_assets, n_assets)
plot_point(ax, w_ew, er[assets], cov.loc[assets, assets], 'Equal weights', color='C4')
In [ ]: