|
@@ -64,40 +64,6 @@ defg maxDrawdown(navs) {
|
|
|
|
|
|
|
|
|
|
/*
|
|
/*
|
|
- * 取主基准和BFI的历史月收益率
|
|
|
|
- *
|
|
|
|
- * @param benchmarks <TABLE>: entity-benchmark 的对应关系表
|
|
|
|
- * @param end_day <DATE>: 收益的截止日期
|
|
|
|
- *
|
|
|
|
- * @return <TABLE>: benchmark_id, end_date, ret
|
|
|
|
- *
|
|
|
|
- */
|
|
|
|
-def get_benchmark_return(benchmarks, end_day) {
|
|
|
|
-
|
|
|
|
- s_index_ids = '';
|
|
|
|
- s_factor_ids = '';
|
|
|
|
-
|
|
|
|
- if(benchmarks.isVoid() || benchmarks.size() == 0) { return null; }
|
|
|
|
-
|
|
|
|
- // 前缀为 IN 的 benchmark id
|
|
|
|
- t_index_id = SELECT DISTINCT benchmark_id FROM benchmarks WHERE benchmark_id LIKE 'IN%';
|
|
|
|
- s_index_ids = iif(isVoid(t_index_id), "", "'" + t_index_id.benchmark_id.concat("','") + "'");
|
|
|
|
-
|
|
|
|
- // 前缀为 FA 的 benchmark id
|
|
|
|
- t_factor_id = SELECT DISTINCT benchmark_id FROM benchmarks WHERE benchmark_id LIKE 'FA%';
|
|
|
|
- s_factor_ids = iif(isVoid(t_factor_id), "", "'" + t_factor_id.benchmark_id.concat("','") + "'");
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- // 目前指数的月度业绩存在 fund_performance 表
|
|
|
|
- t_bmk = SELECT fund_id AS benchmark_id, temporalParse(end_date, 'yyyy-MM') AS end_date, ret FROM get_monthly_ret('IX', s_index_ids, 1990.01.01, end_day, true);
|
|
|
|
-
|
|
|
|
- // 而因子的月度业绩存在 cm_factor_performance 表
|
|
|
|
- INSERT INTO t_bmk SELECT factor_id, temporalParse(end_date, 'yyyy-MM') AS end_date, ret FROM get_monthly_ret('FA', s_factor_ids, 1990.01.01, end_day, true);
|
|
|
|
-
|
|
|
|
- return t_bmk;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*
|
|
|
|
* Trailing Monthly Return, Standard Deviation, Skewness, Kurtosis, Max Drawdown, VaR, CVaR, Calmar Ratio
|
|
* Trailing Monthly Return, Standard Deviation, Skewness, Kurtosis, Max Drawdown, VaR, CVaR, Calmar Ratio
|
|
*
|
|
*
|
|
* @param entity_info <TABLE>: xxx_information表,NEED COLUMNS entity_id, inception_date
|
|
* @param entity_info <TABLE>: xxx_information表,NEED COLUMNS entity_id, inception_date
|
|
@@ -1026,9 +992,75 @@ def cal_trailing_bfi_indicators(entity_info, benchmarks, end_day, tb_ret, bmk_re
|
|
|
|
|
|
|
|
|
|
/*
|
|
/*
|
|
|
|
+ * 通用月度指标计算
|
|
|
|
+ *
|
|
|
|
+ * @param entity_type <STRING>:
|
|
|
|
+ * @param indicator_type <STRING>: PBI, BFI
|
|
|
|
+ * @param monthly_returns <TABLE>: NEED COLUMN: entity_id, end_date, price_date, nav, ret
|
|
|
|
+ *
|
|
|
|
+ * @return <DICT TABLE>: ['PBI-INCEP', 'PBI-YTD', 'PBI-6M', 'PBI-1Y', 'PBI-2Y', 'PBI-3Y', 'PBI-4Y', 'PBI-5Y', 'PBI-10Y', 'MS-3Y', 'MS-5Y', 'MS-10Y']
|
|
|
|
+ *
|
|
|
|
+ */
|
|
|
|
+def cal_monthly_indicators(entity_type, indicator_type, monthly_returns) {
|
|
|
|
+
|
|
|
|
+ if(find(['MF', 'HF', 'PF'], entity_type) < 0) return null;
|
|
|
|
+
|
|
|
|
+ if(monthly_returns.isVoid() || monthly_returns.size() < 1) return null;
|
|
|
|
+
|
|
|
|
+ oldest_date = EXEC price_date.min() FROM monthly_returns;
|
|
|
|
+
|
|
|
|
+ v_entity_ids = (SELECT DISTINCT entity_id FROM monthly_returns).entity_id;
|
|
|
|
+
|
|
|
|
+ entity_info = get_entity_info(entity_type, v_entity_ids);
|
|
|
|
+
|
|
|
|
+ if(entity_info.isVoid() || entity_info.size() == 0) { return null };
|
|
|
|
+
|
|
|
|
+ end_day = today();
|
|
|
|
+
|
|
|
|
+ // 取基金和基准的对照表
|
|
|
|
+ if(indicator_type == 'BFI') {
|
|
|
|
+
|
|
|
|
+ benchmark = SELECT fund_id AS entity_id, end_date.temporalParse('yyyy-MM') AS end_date, factor_id AS benchmark_id
|
|
|
|
+ FROM get_fund_bfi_factors(v_entity_ids, oldest_date.temporalFormat('yyyy-MM'), end_day.temporalFormat('yyyy-MM'));
|
|
|
|
+
|
|
|
|
+ } else {
|
|
|
|
+ // 主基准, 对应 xxx_info 中的 primary_benchmark_id
|
|
|
|
+ benchmark = SELECT entity_id, end_date, iif(benchmark_id.isNull(), 'IN00000008', benchmark_id) AS benchmark_id
|
|
|
|
+ FROM get_entity_primary_benchmark(entity_type, v_entity_ids, oldest_date.temporalFormat('yyyy-MM'), end_day.temporalFormat('yyyy-MM')) ;
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 取所有出现的基准月收益
|
|
|
|
+ bmk_ret = get_benchmark_return(benchmark, end_day);
|
|
|
|
+
|
|
|
|
+ if(bmk_ret.isVoid() || bmk_ret.size() == 0) { return null; }
|
|
|
|
+
|
|
|
|
+ // TODO: risk free指数月收益存在fund_performance表,所以先将就用 fund_id 表示。之后统一改为更准确的名字
|
|
|
|
+ risk_free_rate = SELECT entity_id AS fund_id, temporalParse(end_date, 'yyyy-MM') AS end_date, ret FROM get_risk_free_rate(oldest_date, end_day);
|
|
|
|
+
|
|
|
|
+ if(risk_free_rate.isVoid() || risk_free_rate.size() == 0) { return null; }
|
|
|
|
+
|
|
|
|
+ // 指标计算
|
|
|
|
+ if(indicator_type == 'BFI') {
|
|
|
|
+
|
|
|
|
+ t0 = cal_trailing_bfi_indicators(entity_info, benchmark, end_day, monthly_returns, bmk_ret, risk_free_rate);
|
|
|
|
+
|
|
|
|
+ v_table_name = ['BFI-INCEP', 'BFI-YTD', 'BFI-6M', 'BFI-1Y', 'BFI-2Y', 'BFI-3Y', 'BFI-4Y', 'BFI-5Y', 'BFI-10Y'];
|
|
|
|
+
|
|
|
|
+ } else {
|
|
|
|
+
|
|
|
|
+ t0 = cal_trailing_indicators(entity_info, benchmark, end_day, monthly_returns, bmk_ret, risk_free_rate);
|
|
|
|
+
|
|
|
|
+ v_table_name = ['PBI-INCEP', 'PBI-YTD', 'PBI-6M', 'PBI-1Y', 'PBI-2Y', 'PBI-3Y', 'PBI-4Y', 'PBI-5Y', 'PBI-10Y'];
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return dict(v_table_name, t0);
|
|
|
|
+
|
|
|
|
+}
|
|
|
|
+/*
|
|
* Calculate historcial fund trailing indicators
|
|
* Calculate historcial fund trailing indicators
|
|
*
|
|
*
|
|
- * @param entity_type <STRING>: MF, HF
|
|
|
|
|
|
+ * @param fund_type <STRING>: MF, HF
|
|
* @param fund_ids <STRING>: 逗号和单引号分隔的fund_id
|
|
* @param fund_ids <STRING>: 逗号和单引号分隔的fund_id
|
|
* @param end_day <DATE>: 要计算的日期
|
|
* @param end_day <DATE>: 要计算的日期
|
|
* @param isFromNav <BOOL>: 用净值实时计算还是从表中取月收益
|
|
* @param isFromNav <BOOL>: 用净值实时计算还是从表中取月收益
|
|
@@ -1036,68 +1068,43 @@ def cal_trailing_bfi_indicators(entity_info, benchmarks, end_day, tb_ret, bmk_re
|
|
*
|
|
*
|
|
* @return <DICT TABLE>: ['PBI-INCEP', 'PBI-YTD', 'PBI-6M', 'PBI-1Y', 'PBI-2Y', 'PBI-3Y', 'PBI-4Y', 'PBI-5Y', 'PBI-10Y', 'MS-3Y', 'MS-5Y', 'MS-10Y']
|
|
* @return <DICT TABLE>: ['PBI-INCEP', 'PBI-YTD', 'PBI-6M', 'PBI-1Y', 'PBI-2Y', 'PBI-3Y', 'PBI-4Y', 'PBI-5Y', 'PBI-10Y', 'MS-3Y', 'MS-5Y', 'MS-10Y']
|
|
*
|
|
*
|
|
- * TODO: primary_benchmark_id seems not be used as benchmark, when it is FA00000VNB
|
|
|
|
*
|
|
*
|
|
* Example: cal_fund_indicators('HF', "'HF000004KN','HF000103EU','HF00018WXG'", 2024.06.28, true);
|
|
* Example: cal_fund_indicators('HF', "'HF000004KN','HF000103EU','HF00018WXG'", 2024.06.28, true);
|
|
*
|
|
*
|
|
*/
|
|
*/
|
|
-def cal_fund_indicators(entity_type, fund_ids, end_day, isFromNav) {
|
|
|
|
|
|
+def cal_fund_indicators(fund_type, fund_ids, end_day, isFromNav) {
|
|
|
|
|
|
very_old_date = 1990.01.01;
|
|
very_old_date = 1990.01.01;
|
|
|
|
|
|
- start_month = 1990.01M;
|
|
|
|
-
|
|
|
|
- fund_info = get_fund_info(fund_ids);
|
|
|
|
-
|
|
|
|
- if(fund_info.isVoid() || fund_info.size() == 0) { return null };
|
|
|
|
-
|
|
|
|
- fund_info.rename!('fund_id', 'entity_id');
|
|
|
|
-
|
|
|
|
if(isFromNav == true) {
|
|
if(isFromNav == true) {
|
|
|
|
+
|
|
// 从净值开始计算收益
|
|
// 从净值开始计算收益
|
|
- tb_ret = SELECT * FROM cal_fund_monthly_returns(entity_type, fund_ids, true) WHERE price_date <= end_day;
|
|
|
|
|
|
+ tb_ret = SELECT * FROM cal_fund_monthly_returns(fund_type, fund_ids, true) WHERE price_date <= end_day;
|
|
tb_ret.rename!(['fund_id', 'cumulative_nav'], ['entity_id', 'nav']);
|
|
tb_ret.rename!(['fund_id', 'cumulative_nav'], ['entity_id', 'nav']);
|
|
|
|
+
|
|
} else {
|
|
} else {
|
|
|
|
+
|
|
// 从fund_performance表里读月收益
|
|
// 从fund_performance表里读月收益
|
|
- tb_ret = get_monthly_ret('FD', fund_ids, very_old_date, end_day, true);
|
|
|
|
- tb_ret.rename!(['fund_id'], ['entity_id']);
|
|
|
|
|
|
+ tb_ret = get_monthly_ret(fund_type, fund_ids, very_old_date, end_day, true);
|
|
|
|
+
|
|
v_end_date = tb_ret.end_date.temporalParse('yyyy-MM');
|
|
v_end_date = tb_ret.end_date.temporalParse('yyyy-MM');
|
|
tb_ret.replaceColumn!('end_date', v_end_date);
|
|
tb_ret.replaceColumn!('end_date', v_end_date);
|
|
|
|
+
|
|
}
|
|
}
|
|
|
|
|
|
if(tb_ret.isVoid() || tb_ret.size() == 0) { return null; }
|
|
if(tb_ret.isVoid() || tb_ret.size() == 0) { return null; }
|
|
|
|
|
|
- // 取基金和基准的对照表
|
|
|
|
- primary_benchmark = SELECT fund_id AS entity_id, end_date, iif(benchmark_id.isNull(), 'IN00000008', benchmark_id) AS benchmark_id
|
|
|
|
- FROM get_fund_primary_benchmark(fund_ids, start_month.temporalFormat('yyyy-MM'), end_day.month().temporalFormat('yyyy-MM')) ;
|
|
|
|
-
|
|
|
|
- // 取所有出现的基准月收益
|
|
|
|
- bmk_ret = get_benchmark_return(primary_benchmark, end_day);
|
|
|
|
-
|
|
|
|
- if(bmk_ret.isVoid() || bmk_ret.size() == 0) { return null; }
|
|
|
|
-
|
|
|
|
- risk_free_rate = SELECT fund_id, temporalParse(end_date, 'yyyy-MM') AS end_date, ret FROM get_risk_free_rate(very_old_date, end_day);
|
|
|
|
-
|
|
|
|
- if(risk_free_rate.isVoid() || risk_free_rate.size() == 0) { return null; }
|
|
|
|
-
|
|
|
|
// 标准的指标
|
|
// 标准的指标
|
|
- t0 = cal_trailing_indicators(fund_info, primary_benchmark, end_day, tb_ret, bmk_ret, risk_free_rate);
|
|
|
|
|
|
+ d = cal_monthly_indicators(fund_type, 'PBI', tb_ret);
|
|
|
|
|
|
- // Morningstar 指标
|
|
|
|
- //t1 = cal_trailing_ms_indicators(fund_info, tb_ret, end_day, risk_free_rate);
|
|
|
|
|
|
+ return d;
|
|
|
|
|
|
- // PBI stands for "Primary Benchmark Index", MS stands for "MorningStar"
|
|
|
|
- v_table_name = ['PBI-INCEP', 'PBI-YTD', 'PBI-6M', 'PBI-1Y', 'PBI-2Y', 'PBI-3Y', 'PBI-4Y', 'PBI-5Y', 'PBI-10Y'/*, 'MS-3Y', 'MS-5Y', 'MS-10Y'*/];
|
|
|
|
-
|
|
|
|
- return dict(v_table_name, t0);
|
|
|
|
-// return dict(v_table_name, t0 <- t1[5] <- t1[7] <- t1[8]);
|
|
|
|
-
|
|
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
/*
|
|
* Calculate historcial fund trailing BFI indicators
|
|
* Calculate historcial fund trailing BFI indicators
|
|
*
|
|
*
|
|
- * @param entity_type <STRING>: MF, HF
|
|
|
|
|
|
+ * @param fund_type <STRING>: MF, HF
|
|
* @param fund_ids <STRING>: 逗号和单引号分隔的fund_id
|
|
* @param fund_ids <STRING>: 逗号和单引号分隔的fund_id
|
|
* @param end_day <DATE>: 要计算的日期
|
|
* @param end_day <DATE>: 要计算的日期
|
|
* @param isFromNav <BOOL>: 用净值实时计算还是从表中取月收益
|
|
* @param isFromNav <BOOL>: 用净值实时计算还是从表中取月收益
|
|
@@ -1105,56 +1112,37 @@ def cal_fund_indicators(entity_type, fund_ids, end_day, isFromNav) {
|
|
*
|
|
*
|
|
* @return <DICT TABLE>: ['BFI-INCEP', 'BFI-YTD', 'BFI-6M', 'BFI-1Y', 'BFI-2Y', 'BFI-3Y', 'BFI-4Y', 'BFI-5Y', 'BFI-10Y']
|
|
* @return <DICT TABLE>: ['BFI-INCEP', 'BFI-YTD', 'BFI-6M', 'BFI-1Y', 'BFI-2Y', 'BFI-3Y', 'BFI-4Y', 'BFI-5Y', 'BFI-10Y']
|
|
*
|
|
*
|
|
- * TODO: primary_benchmark_id seems not be used as benchmark, when it is FA00000VNB
|
|
|
|
- * TODO: intergrate with cal_fund_indicators
|
|
|
|
*
|
|
*
|
|
* Example: cal_fund_bfi_indicators('MF', "'MF00003PW2', 'MF00003PW1', 'MF00003PXO'", 2024.08.31, true);
|
|
* Example: cal_fund_bfi_indicators('MF', "'MF00003PW2', 'MF00003PW1', 'MF00003PXO'", 2024.08.31, true);
|
|
*
|
|
*
|
|
*/
|
|
*/
|
|
-def cal_fund_bfi_indicators(entity_type, fund_ids, end_day, isFromNav) {
|
|
|
|
|
|
+def cal_fund_bfi_indicators(fund_type, fund_ids, end_day, isFromNav) {
|
|
|
|
|
|
very_old_date = 1990.01.01;
|
|
very_old_date = 1990.01.01;
|
|
|
|
|
|
- start_month = 1990.01M;
|
|
|
|
-
|
|
|
|
- fund_info = get_fund_info(fund_ids);
|
|
|
|
-
|
|
|
|
- if(fund_info.isVoid() || fund_info.size() == 0) { return null };
|
|
|
|
-
|
|
|
|
- fund_info.rename!('fund_id', 'entity_id');
|
|
|
|
-
|
|
|
|
if(isFromNav == true) {
|
|
if(isFromNav == true) {
|
|
|
|
+
|
|
// 从净值开始计算收益
|
|
// 从净值开始计算收益
|
|
- tb_ret = SELECT * FROM cal_fund_monthly_returns(entity_type, fund_ids, true) WHERE price_date <= end_day;
|
|
|
|
|
|
+ tb_ret = SELECT * FROM cal_fund_monthly_returns(fund_type, fund_ids, true) WHERE price_date <= end_day;
|
|
tb_ret.rename!(['fund_id', 'cumulative_nav'], ['entity_id', 'nav']);
|
|
tb_ret.rename!(['fund_id', 'cumulative_nav'], ['entity_id', 'nav']);
|
|
|
|
+
|
|
} else {
|
|
} else {
|
|
|
|
+
|
|
// 从fund_performance表里读月收益
|
|
// 从fund_performance表里读月收益
|
|
- tb_ret = get_monthly_ret('FD', fund_ids, very_old_date, end_day, true);
|
|
|
|
|
|
+ tb_ret = get_monthly_ret(fund_type, fund_ids, very_old_date, end_day, true);
|
|
tb_ret.rename!(['fund_id'], ['entity_id']);
|
|
tb_ret.rename!(['fund_id'], ['entity_id']);
|
|
|
|
+
|
|
v_end_date = tb_ret.end_date.temporalParse('yyyy-MM');
|
|
v_end_date = tb_ret.end_date.temporalParse('yyyy-MM');
|
|
tb_ret.replaceColumn!('end_date', v_end_date);
|
|
tb_ret.replaceColumn!('end_date', v_end_date);
|
|
|
|
+
|
|
}
|
|
}
|
|
|
|
|
|
if(tb_ret.isVoid() || tb_ret.size() == 0) { return null; }
|
|
if(tb_ret.isVoid() || tb_ret.size() == 0) { return null; }
|
|
|
|
|
|
- // 取基金和基准的对照表
|
|
|
|
- bfi_benchmark = SELECT fund_id AS entity_id, end_date.temporalParse('yyyy-MM') AS end_date, factor_id AS benchmark_id
|
|
|
|
- FROM get_fund_bfi_factors(fund_ids, start_month.temporalFormat('yyyy-MM'), end_day.temporalFormat('yyyy-MM'));
|
|
|
|
-
|
|
|
|
- if(bfi_benchmark.isVoid() || bfi_benchmark.size() == 0) { return null; }
|
|
|
|
-
|
|
|
|
- bmk_ret = get_benchmark_return(bfi_benchmark, end_day);
|
|
|
|
-
|
|
|
|
- risk_free_rate = SELECT fund_id, temporalParse(end_date, 'yyyy-MM') AS end_date, ret FROM get_risk_free_rate(very_old_date, end_day);
|
|
|
|
-
|
|
|
|
- if(risk_free_rate.isVoid() || risk_free_rate.size() == 0) { return null; }
|
|
|
|
-
|
|
|
|
- t0 = cal_trailing_bfi_indicators(fund_info, bfi_benchmark, end_day, tb_ret, bmk_ret, risk_free_rate);
|
|
|
|
|
|
+ // BFI指标
|
|
|
|
+ d = cal_monthly_indicators(fund_type, 'BFI', tb_ret);
|
|
|
|
|
|
- // BFI stands for "Best Fit Index"
|
|
|
|
- v_table_name = ['BFI-INCEP', 'BFI-YTD', 'BFI-6M', 'BFI-1Y', 'BFI-2Y', 'BFI-3Y', 'BFI-4Y', 'BFI-5Y', 'BFI-10Y'];
|
|
|
|
-
|
|
|
|
- return dict(v_table_name, t0);
|
|
|
|
|
|
+ return d;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -1215,7 +1203,8 @@ def cal_portfolio_indicators(portfolio_ids, end_day, cal_method, isFromNav) {
|
|
|
|
|
|
if(bmk_ret.isVoid() || bmk_ret.size() == 0) { return null; }
|
|
if(bmk_ret.isVoid() || bmk_ret.size() == 0) { return null; }
|
|
|
|
|
|
- risk_free_rate = SELECT fund_id, temporalParse(end_date, 'yyyy-MM') AS end_date, ret FROM get_risk_free_rate(very_old_date, end_day);
|
|
|
|
|
|
+ // TODO: risk free指数月收益存在fund_performance表,所以先将就用 fund_id 表示。之后统一改为更准确的名字
|
|
|
|
+ risk_free_rate = SELECT entity_id AS fund_id, temporalParse(end_date, 'yyyy-MM') AS end_date, ret FROM get_risk_free_rate(very_old_date, end_day);
|
|
|
|
|
|
if(risk_free_rate.isVoid() || risk_free_rate.size() == 0) { return null; }
|
|
if(risk_free_rate.isVoid() || risk_free_rate.size() == 0) { return null; }
|
|
|
|
|
|
@@ -1286,7 +1275,8 @@ def cal_portfolio_bfi_indicators(portfolio_ids, end_day, cal_method, isFromNav)
|
|
|
|
|
|
if(bmk_ret.isVoid() || bmk_ret.size() == 0) { return null; }
|
|
if(bmk_ret.isVoid() || bmk_ret.size() == 0) { return null; }
|
|
|
|
|
|
- risk_free_rate = SELECT fund_id, temporalParse(end_date, 'yyyy-MM') AS end_date, ret FROM get_risk_free_rate(very_old_date, end_day);
|
|
|
|
|
|
+ // TODO: risk free指数月收益存在fund_performance表,所以先将就用 fund_id 表示。之后统一改为更准确的名字
|
|
|
|
+ risk_free_rate = SELECT entity_id AS fund_id, temporalParse(end_date, 'yyyy-MM') AS end_date, ret FROM get_risk_free_rate(very_old_date, end_day);
|
|
|
|
|
|
if(risk_free_rate.isVoid() || risk_free_rate.size() == 0) { return null; }
|
|
if(risk_free_rate.isVoid() || risk_free_rate.size() == 0) { return null; }
|
|
|
|
|