import numpy as np
import pandas as pd

np.random.seed(0)
returns = pd.Series(np.random.normal(0, 0.10, 100)).sort_values()
returns.values

"""
Explanation: Mengukur Downside Risk dengan VaR dan CVaR
Seperti kita bahas dalam studi sebelumnya, distribusi dari keuntungan biasanya bukanlah normal, sehingga pemakaian standar deviasi kurang tepat karena standar deviasi mengasumsikan distibusi yang simetris dan kurtosis yang tipis. Sementara dalam manajemen portfolio, investor biasanya lebih menguatirkan resiko untuk mengalami kerugian yang besar, atau probabilitas dari return negatif yang besar, daripada volatilitas dari laba (tentu kita tidak keberatan kalau suatu saat labanya melonjak tinggi). 
Atas pemikiran ini maka diciptakan perhitungan-perhitungan untuk lebih spesifik mengukur resiko atas kerugian (downside risk), yang akan kita bahas dalam studi ini.
Semi-Deviation
Semi-Deviation adalah ukuran volatilitas dari return yang di bawah rata-rata. Semi-deviation dihitung dengan menghitung standard deviasi dari return yang di bawah rata-ratanya:
$$ \sigma_{semi} = \sqrt{ \frac{1}{N} \sum_{R_t < \overline{R}} (R_t - \overline{R})^2 }$$
(catatan: N adalah jumlah sampel dari return yang di bawah rata-rata)
Atau praktisnya, untuk suatu data returns, kita hitung rata-ratanya, lalu buang sampel yang nilainya di atas atau sama-dengan rata-rata, lalu hitung standard deviasi dari sampel yang tersisa.
Semi-deviation mengukur volatilitas dari return yang di bawah rata-ratanya, namun tidak memberikan informasi resiko kerugian besar yang bisa dialami investor. Untuk itu kita bisa memakai perhitungan yang lain berikut ini.
Untuk mendemonstrasikan perhitungan semi-deviation, mari kita membuat data random sbb.
End of explanation
"""


semi_dev = returns[ returns < 0 ].std(ddof=0)
semi_dev

"""
Explanation: Berikut untuk menghitung semi-deviation. Catatan: kadang-kadang semi-deviation juga dihitung dari returns yang di bawah nol (bukan di bawah rata-rata). Perhitungan seperti ini yang kita pakai.
End of explanation
"""


np.percentile(returns, 5)

"""
Explanation: Value at Risk (VaR)
Value at Risk (VaR) mengukur maksimum kerugian yang bisa dialami dari suatu investasi setelah kasus kerugian ekstrim dihilangkan. Jadi 95% Value at Risk berarti maksimum kerugian yang bisa dialami setelah 5% kerugian yang paling ekstrim dihilangkan.
Angka 95% ini, disebut confidence level, dan bisa diganti dengan yang lain sesuai kebutuhan, misalnya 99% atau 99.9%.
VaR bisa dipakai oleh manajer investasi untuk menyisihkan cadangan modal yang dipakai untuk menutup kemungkinan kerugian terburuk yang bisa dialami.
Untuk menghitung VaR, pertama tentukan batas confidence level-nya, misalkan 95%. Karena batasnya 95%, maka buang 5% data return yang terendah. Lalu ambil return terendah dari data return yang tersisa, lalu  dijadikan positif. Itulah nilai VaR-nya.
Untuk menghitung 95% VaR dari data di atas, pertama kita hilangkan 5% data return terburuk, atau dalam hal ini berarti kita hilangkan lima sampel. Nilai VaR-nya adalah nilai terburuk yang tersisa (-0.16138978) lalu kita positifkan (menjadi 0.16138978).
Artinya, dalam 95% kemungkinan, kerugian terburuk investasi kita adalah minus 0.16138978 atau minus 16%.
Cara lain untuk menghitung VaR adalah dengan memanggil fungsi np.percentile() seperti di bawah ini.
End of explanation
"""


cvar = np.abs(returns[:5].mean())
cvar

"""
Explanation: Nilai di atas sedikit berbeda dengan perhitungan manual kita karena np.percentile() mengandung interpolasi. Dan jangan lupa bahwa nilai VaR adalah positif (jadi nilai di atas harus dipositifkan).
Conditional Value at Risk (CVaR)
Dalam kondisi lain, mungkin kita ingin melihat karakteristik dari kerugian terburuk di luar VaR. Seperti contoh di atas, kita sudah hitung bahwa VaR-nya adalah 16%. Bagaimana kalau kasus terburuk (yaitu kasus yg 5% itu) benar-benar terjadi? Berapa kerugiannya? Jangan-jangan kerugiannya sangat ekstrim yang membuat kita bangkrut!
Karakteristik inilah yang diukur oleh Conditional Value at Risk (CVaR).
Perhitungan CVaR sederhana saja. Cukup kita hitung nilai rata-rata dari return terburuk yang kita buang ketika menghitung VaR tadi, lalu kita positifkan.
Dengan contoh di atas, CVaR berarti nilai absolut dari rata-rata 5 kerugian terburuk, yang bisa kita hitung seperti ini:
End of explanation
"""


from scipy.stats import norm

def get_gaussian_var(returns, alpha):
    mean = returns.mean()
    std = returns.std(ddof=0)
    z = norm.ppf(alpha)
    var = -(mean + z * std)
    return var

get_gaussian_var(returns, 0.05)

"""
Explanation: Cara Perhitungan VaR yang Lain
Metoda penghitungan VaR yang kita pakai di atas memakai metoda historis. Keuntungan dari perhitungan secara historis adalah perhitungan ini tidak membutuhkan asumsi apapun, sedangkan kerugiannya adalah sensitivitas terhadap data (kalau datanya berubah, mungkin nilai VaR akan berubah secara signifikan).
Dengan kata lain, metoda itu mempunyai resiko akibat model (model risk) yang kecil, namun resiko akibat sampelnya (sample risk) besar.
Ada beberapa cara lain untuk menghitung VaR.
Metoda Gaussian
Dengan mengasumsikan bahwa distribusi return-nya adalah normal, maka kita dapat menghitung berapa nilai VaR untuk suatu probabilitas atau confidence level.
Misalkan kita mau menghitung 95% VaR, maka $ \alpha $ adalah 0.05 (=100% - 95%), seperti dalam gambar berikut.

Dari tabel z-table kita bisa tahu bahwa nilai $ z $ yang sesuai adalah -1.645. 
Maka nilai VaR dapat dihitung dengan:
$$ VaR = -(\mu - 1.645\ \sigma) $$
Formula perhitungan generiknya adalah:
$$ VaR_{\alpha} = -(\mu + z_{\alpha} \ \sigma) $$
dimana:
- $ VaR_{\alpha} $ : VaR untuk quantile $ \alpha $
- $ \mu $ : mean
- $ z_{\alpha} $ : z-value untuk quantile $ \alpha $ (lihat z-table)
- $ \sigma $ : standard deviasi
Implementasinya dalam Python adalah sbb.:
End of explanation
"""


from scipy.stats import skew, kurtosis

def get_cornish_fisher_var(returns, alpha):
    z = norm.ppf(alpha)
    s = skew(returns)
    k = kurtosis(returns, fisher=False)
    z = (z +
         (z**2 - 1)*s/6 +
         (z**3 -3*z)*(k-3)/24 -
         (2*z**3 - 5*z)*(s**2)/36)
    var = -(returns.mean() + z * returns.std(ddof=0))
    return var
         
get_cornish_fisher_var(returns, 0.05)

"""
Explanation: Seperti kita lihat, nilainya dekat dengan perhitungan-perhitungan sebelumnya. Namun harap diingat, bahwa untuk contoh ini, nilai returns-nya berasal dari angka random dengan distribusi normal. Kalau distribusi nilai returns nya bukan normal, maka hasil perhitungan bisa berbeda jauh. Dan memang inilah kelemahan dari perhitungan dengan metoda ini, yaitu mengasumsikan distribusi normal, padahal kita telah belajar sebelumnya bahwa kemungkinan besar bukan.
Dengan kata lain, metoda Gaussian mempunyai model risk yang besar, dan sample risk yang kecil.
Di sisi lain, perhitungan ini hanya membutuhkan nilai mean dan standard deviasi, jadi sangat simpel.
Secara umum, cara ini kurang tepat dipakai untuk menghitung VaR, karena VaR menghitung nilai pada ekor distribusi, sedangkan justru bagian ekor dari distribusi return yang karakteristiknya berbeda dengan distribusi normal (tebal vs tipis).
Metoda Cornish-Fisher
Metoda Cornish-Fisher juga mengasumsikan bahwa return mengikuti suatu distribusi, namun distribusinya bisa disesuaikan untuk nilai skew dan kurtosis tertentu. Penyesuaian ini disebut Cornish-Fisher expansion.

Cornish-Fisher pada dasarnya adalah nilai $ z_\alpha $ yang disesuaikan, dimana penyesuaiannya melibatkan skewness $ S $ dan kurtosis $ K $. Jika skewness nol dan kurtosis tiga, maka nilai $ \widetilde{z_\alpha} $ akan sama dengan nilai $ z_\alpha $.
Berikut adalah perhitungan VaR dengan koreksi Cornish-Fisher dalam Python.
End of explanation
"""


