|
@@ -30,17 +30,22 @@ def get_annulization_multiple(freq) {
|
|
* @param ret: 收益表,需要有 entity_id, price_dat, end_date, nav
|
|
* @param ret: 收益表,需要有 entity_id, price_dat, end_date, nav
|
|
* @param freq: 数据频率,d, w, m, q, s, a
|
|
* @param freq: 数据频率,d, w, m, q, s, a
|
|
*
|
|
*
|
|
|
|
+ * NOTE: standard deviation of Java version is noncompliant-GIPS annulized number
|
|
|
|
+ *
|
|
* Create: 20240904 Joey
|
|
* Create: 20240904 Joey
|
|
* TODO: var and cvar are silightly off compared with Java version
|
|
* TODO: var and cvar are silightly off compared with Java version
|
|
*
|
|
*
|
|
*/
|
|
*/
|
|
-def cal_basic_performance(ret) {
|
|
|
|
|
|
+def cal_basic_performance(ret, freq) {
|
|
|
|
|
|
t = SELECT entity_id, max(end_date) AS end_date, max(price_date) AS price_date, min(price_date) AS min_date,
|
|
t = SELECT entity_id, max(end_date) AS end_date, max(price_date) AS price_date, min(price_date) AS min_date,
|
|
- (nav.last() \ nav.first() - 1).round(6) AS trailing_ret,
|
|
|
|
|
|
+ //(nav.last() \ nav.first() - 1).round(6) AS trailing_ret,
|
|
|
|
+ ((1+ret).prod()-1).round(6) AS trailing_ret,
|
|
iif(price_date.max().month()-price_date.min().month()>12,
|
|
iif(price_date.max().month()-price_date.min().month()>12,
|
|
- (nav.last() \ nav.first()).pow(365 \(max(price_date) - min(price_date)))-1,
|
|
|
|
- (nav.last() \ nav.first() - 1)).round(6) AS trailing_ret_a,
|
|
|
|
|
|
+ //(nav.last() \ nav.first()).pow(365 \(max(price_date) - min(price_date)))-1,
|
|
|
|
+ //(nav.last() \ nav.first() - 1)).round(6) AS trailing_ret_a,
|
|
|
|
+ ((1+ret).prod()-1) * sqrt(get_annulization_multiple(freq)),
|
|
|
|
+ ((1+ret).prod()-1)).round(6) AS trailing_ret_a,
|
|
ret.std() AS std_dev,
|
|
ret.std() AS std_dev,
|
|
ret.skew(false) AS skewness,
|
|
ret.skew(false) AS skewness,
|
|
ret.kurtosis(false) - 3 AS kurtosis,
|
|
ret.kurtosis(false) - 3 AS kurtosis,
|
|
@@ -112,7 +117,7 @@ def cal_omega_sortino_kappa(ret, risk_free_rate) {
|
|
|
|
|
|
/*
|
|
/*
|
|
* Alpha & Beta
|
|
* Alpha & Beta
|
|
- *
|
|
|
|
|
|
+ * NOTE: alpha of Java version is noncompliant-GIPS annulized number
|
|
*/
|
|
*/
|
|
def cal_alpha_beta(ret, bmk_ret, risk_free) {
|
|
def cal_alpha_beta(ret, bmk_ret, risk_free) {
|
|
|
|
|
|
@@ -158,6 +163,7 @@ def cal_benchmark_tracking(ret, bmk_ret) {
|
|
|
|
|
|
/*
|
|
/*
|
|
* Sharpe Ratio
|
|
* Sharpe Ratio
|
|
|
|
+ * NOTE: Java version is noncompliant-GIPS annulized number
|
|
*/
|
|
*/
|
|
def cal_sharpe(ret, std_dev, risk_free_rate) {
|
|
def cal_sharpe(ret, std_dev, risk_free_rate) {
|
|
|
|
|
|
@@ -165,6 +171,7 @@ def cal_sharpe(ret, std_dev, risk_free_rate) {
|
|
FROM ret t
|
|
FROM ret t
|
|
INNER JOIN std_dev std ON t.entity_id = std.entity_id
|
|
INNER JOIN std_dev std ON t.entity_id = std.entity_id
|
|
INNER JOIN risk_free_rate rfr ON t.end_date = rfr.end_date
|
|
INNER JOIN risk_free_rate rfr ON t.end_date = rfr.end_date
|
|
|
|
+ WHERE std.std_dev[0] <> 0
|
|
GROUP BY t.entity_id;
|
|
GROUP BY t.entity_id;
|
|
|
|
|
|
return sharpe;
|
|
return sharpe;
|
|
@@ -222,6 +229,7 @@ def cal_calmar(ret_a){
|
|
/*
|
|
/*
|
|
* Modigliani Modigliani Measure (M2)
|
|
* Modigliani Modigliani Measure (M2)
|
|
* NOTE: M2 = sharpe * std(benchmark) + risk_free_rate
|
|
* NOTE: M2 = sharpe * std(benchmark) + risk_free_rate
|
|
|
|
+ * NOTE: Java version is noncompliant-GIPS annulized number
|
|
*/
|
|
*/
|
|
def cal_m2(ret, bmk_ret, risk_free_rate) {
|
|
def cal_m2(ret, bmk_ret, risk_free_rate) {
|
|
|
|
|
|
@@ -237,10 +245,11 @@ def cal_m2(ret, bmk_ret, risk_free_rate) {
|
|
|
|
|
|
/*
|
|
/*
|
|
* Monthly Since_inception_date Indicator Calculation
|
|
* Monthly Since_inception_date Indicator Calculation
|
|
- * @param: ret: historical return table
|
|
|
|
- * index_ret: historical benchmark return table
|
|
|
|
- * risk_free: historical risk free rate table
|
|
|
|
- *
|
|
|
|
|
|
+ * @param: ret <TABLE>: 收益表,NEED COLUMNS entity_id, price_dat, end_date, nav
|
|
|
|
+ * @param index_ret <TABLE>: historical benchmark return table, NEED COLUMNS fund_id, end_date, ret
|
|
|
|
+ * @param risk_free <TABLE>: historical risk free rate table, NEED COLUMNS fund_id, end_date, ret
|
|
|
|
+ * @param freq <CHAR>: 数据频率,d, w, m, q, s, a
|
|
|
|
+ *
|
|
* @return: indicators table
|
|
* @return: indicators table
|
|
*
|
|
*
|
|
*
|
|
*
|
|
@@ -256,7 +265,7 @@ def cal_indicators(mutable ret, index_ret, risk_free, freq) {
|
|
ret.sortBy!(['entity_id', 'price_date'], [1, 1]);
|
|
ret.sortBy!(['entity_id', 'price_date'], [1, 1]);
|
|
|
|
|
|
// 收益、标准差、偏度、峰度、最大回撤、VaR, CVaR
|
|
// 收益、标准差、偏度、峰度、最大回撤、VaR, CVaR
|
|
- rtn = cal_basic_performance(ret);
|
|
|
|
|
|
+ rtn = cal_basic_performance(ret, freq);
|
|
|
|
|
|
// alpha, beta
|
|
// alpha, beta
|
|
alpha_beta = cal_alpha_beta(ret, index_ret, risk_free);
|
|
alpha_beta = cal_alpha_beta(ret, index_ret, risk_free);
|
|
@@ -297,20 +306,71 @@ def cal_indicators(mutable ret, index_ret, risk_free, freq) {
|
|
plainAnnu = get_annulization_multiple(freq);
|
|
plainAnnu = get_annulization_multiple(freq);
|
|
sqrtAnnu = sqrt(get_annulization_multiple(freq));
|
|
sqrtAnnu = sqrt(get_annulization_multiple(freq));
|
|
|
|
|
|
- r.addColumn(['ds_dev_a', 'alpha_a', 'sharpe_a', 'sortino_a', 'jensen_a', 'track_error_a', 'info_a', 'm2_a'], [DOUBLE, DOUBLE, DOUBLE, DOUBLE, DOUBLE, DOUBLE, DOUBLE, DOUBLE]);
|
|
|
|
|
|
+ r.addColumn(['std_dev_a', 'ds_dev_a', 'alpha_a', 'sharpe_a', 'sortino_a', 'jensen_a', 'track_error_a', 'info_a', 'm2_a'],
|
|
|
|
+ [DOUBLE, DOUBLE, DOUBLE, DOUBLE, DOUBLE, DOUBLE, DOUBLE, DOUBLE, DOUBLE]);
|
|
|
|
|
|
UPDATE r
|
|
UPDATE r
|
|
- SET ds_dev_a = ds_dev * sqrtAnnu,
|
|
|
|
- alpha_a = alpha * plainAnnu,
|
|
|
|
- sharpe_a = sharpe * sqrtAnnu,
|
|
|
|
- sortino_a = sortino * sqrtAnnu,
|
|
|
|
- jensen_a = jensen * plainAnnu,
|
|
|
|
- track_error_a = track_error * sqrtAnnu,
|
|
|
|
- info_a = info * sqrtAnnu,
|
|
|
|
- m2_a = m2 * plainAnnu
|
|
|
|
- WHERE price_date.month() - min_date.month() >= 12;
|
|
|
|
|
|
+ SET std_dev_a = std_dev * iif(price_date.month() - min_date.month() >= 11, sqrtAnnu, 1),
|
|
|
|
+ ds_dev_a = ds_dev * iif(price_date.month() - min_date.month() >= 11, sqrtAnnu, 1),
|
|
|
|
+ alpha_a = alpha * iif(price_date.month() - min_date.month() >= 11, plainAnnu, 1),
|
|
|
|
+ sharpe_a = sharpe * iif(price_date.month() - min_date.month() >= 11, sqrtAnnu, 1),
|
|
|
|
+ sortino_a = sortino * iif(price_date.month() - min_date.month() >= 11, sqrtAnnu, 1),
|
|
|
|
+ jensen_a = jensen * iif(price_date.month() - min_date.month() >= 11, plainAnnu, 1),
|
|
|
|
+ track_error_a = track_error * iif(price_date.month() - min_date.month() >= 11, sqrtAnnu, 1),
|
|
|
|
+ info_a = info * iif(price_date.month() - min_date.month() >= 11, sqrtAnnu, 1),
|
|
|
|
+ m2_a = m2 * iif(price_date.month() - min_date.month() >= 11, plainAnnu, 1);
|
|
|
|
|
|
return r;
|
|
return r;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * Calculate trailing 6m, ytd, 1y, 2y, 3y, 4y, 5y, 10y and since inception indicators
|
|
|
|
+ *
|
|
|
|
+ * @param: ret <TABLE>: 收益表,NEED COLUMNS entity_id, price_dat, end_date, nav
|
|
|
|
+ * @param: end_day <DATE>: 计算截止日期
|
|
|
|
+ * @param index_ret <TABLE>: historical benchmark return table, NEED COLUMNS fund_id, end_date, ret
|
|
|
|
+ * @param risk_free <TABLE>: historical risk free rate table, NEED COLUMNS fund_id, end_date, ret
|
|
|
|
+ * @param freq <CHAR>: 数据频率,d, w, m, q, s, a
|
|
|
|
+
|
|
|
|
+ *
|
|
|
|
+ */
|
|
|
|
+def cal_all_trailing_indicators(mutable tb_ret, end_day, bmk_ret, risk_free_rate, freq) {
|
|
|
|
+
|
|
|
|
+ // since inception
|
|
|
|
+ r_incep = cal_indicators(tb_ret, bmk_ret, risk_free_rate, 'm');
|
|
|
|
+
|
|
|
|
+ // ytd
|
|
|
|
+ tb_ret_ytd = SELECT * FROM tb_ret WHERE end_date >= end_day.yearBegin().month();
|
|
|
|
+ r_ytd = cal_indicators(tb_ret_ytd, bmk_ret, risk_free_rate, 'm');
|
|
|
|
+
|
|
|
|
+ // trailing 6m
|
|
|
|
+ tb_ret_6m = SELECT * FROM tb_ret WHERE end_date > end_day.month()-6;
|
|
|
|
+ r_6m = cal_indicators(tb_ret_6m, bmk_ret, risk_free_rate, 'm');
|
|
|
|
+
|
|
|
|
+ // trailing 1y
|
|
|
|
+ tb_ret_1y = SELECT * FROM tb_ret WHERE end_date > end_day.month()-12;
|
|
|
|
+ r_1y = cal_indicators(tb_ret_1y, bmk_ret, risk_free_rate, 'm');
|
|
|
|
+
|
|
|
|
+ // trailing 2y
|
|
|
|
+ tb_ret_2y = SELECT * FROM tb_ret WHERE end_date > end_day.month()-24;
|
|
|
|
+ r_2y = cal_indicators(tb_ret_2y, bmk_ret, risk_free_rate, 'm');
|
|
|
|
+
|
|
|
|
+ // trailing 3y
|
|
|
|
+ tb_ret_3y = SELECT * FROM tb_ret WHERE end_date > end_day.month()-36;
|
|
|
|
+ r_3y = cal_indicators(tb_ret_3y, bmk_ret, risk_free_rate, 'm');
|
|
|
|
+
|
|
|
|
+ // trailing 4y
|
|
|
|
+ tb_ret_4y = SELECT * FROM tb_ret WHERE end_date > end_day.month()-48;
|
|
|
|
+ r_4y = cal_indicators(tb_ret_4y, bmk_ret, risk_free_rate, 'm');
|
|
|
|
+
|
|
|
|
+ // trailing 5y
|
|
|
|
+ tb_ret_5y = SELECT * FROM tb_ret WHERE end_date > end_day.month()-60;
|
|
|
|
+ r_5y = cal_indicators(tb_ret_5y, bmk_ret, risk_free_rate, 'm');
|
|
|
|
+
|
|
|
|
+ // trailing 10y
|
|
|
|
+ tb_ret_10y = SELECT * FROM tb_ret WHERE end_date > end_day.month()-120;
|
|
|
|
+ r_10y = cal_indicators(tb_ret_10y, bmk_ret, risk_free_rate, 'm');
|
|
|
|
+
|
|
|
|
+ return r_incep, r_ytd, r_6m, r_1y, r_2y, r_3y, r_4y, r_5y, r_10y;
|
|
|
|
+}
|
|
|
|
|