|
@@ -41,6 +41,8 @@ def get_benchmark_return(benchmarks, end_day) {
|
|
s_index_ids = '';
|
|
s_index_ids = '';
|
|
s_factor_ids = '';
|
|
s_factor_ids = '';
|
|
|
|
|
|
|
|
+ if(benchmarks.isVoid() || benchmarks.size() == 0) { return null; }
|
|
|
|
+
|
|
// 前缀为 IN 的 benchmark id
|
|
// 前缀为 IN 的 benchmark id
|
|
t_index_id = SELECT DISTINCT benchmark_id FROM benchmarks WHERE benchmark_id LIKE 'IN%';
|
|
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("','") + "'");
|
|
s_index_ids = iif(isVoid(t_index_id), "", "'" + t_index_id.benchmark_id.concat("','") + "'");
|
|
@@ -191,7 +193,7 @@ def cal_benchmark_tracking(ret, benchmarks, bmk_ret) {
|
|
t = SELECT entity_id, end_date.max() AS end_date, price_date.max() AS price_date, price_date.min() AS min_date, benchmark_id,
|
|
t = SELECT entity_id, end_date.max() AS end_date, price_date.max() AS price_date, price_date.min() AS min_date, benchmark_id,
|
|
exc_ret.bucketCount(0:999, 1) \ cnt[0] AS winrate,
|
|
exc_ret.bucketCount(0:999, 1) \ cnt[0] AS winrate,
|
|
exc_ret.std() AS track_error,
|
|
exc_ret.std() AS track_error,
|
|
- exc_ret.mean() / exc_ret.std() AS info
|
|
|
|
|
|
+ iif(exc_ret.std() == 0, null, exc_ret.mean() / exc_ret.std()) AS info
|
|
FROM t0 GROUP BY entity_id, benchmark_id;
|
|
FROM t0 GROUP BY entity_id, benchmark_id;
|
|
|
|
|
|
return t;
|
|
return t;
|
|
@@ -401,9 +403,8 @@ def cal_indicators_with_benchmark(mutable ret, benchmarks, index_ret, risk_free)
|
|
* Monthly standard indicator calculation
|
|
* Monthly standard indicator calculation
|
|
* @param: ret <TABLE>: 收益表,NEED COLUMNS entity_id, price_dat, end_date, nav
|
|
* @param: ret <TABLE>: 收益表,NEED COLUMNS entity_id, price_dat, end_date, nav
|
|
* @param benchmarks <TABLE>: entity-benchmark mapping table
|
|
* @param benchmarks <TABLE>: entity-benchmark mapping table
|
|
- * @param index_ret <TABLE>: historical benchmark return table, NEED COLUMNS fund_id, end_date, ret
|
|
|
|
|
|
+ * @param benchmark_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 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
|
|
*
|
|
*
|
|
@@ -457,13 +458,34 @@ def cal_indicators(mutable ret, benchmarks, benchmark_ret, risk_free) {
|
|
|
|
|
|
|
|
|
|
/*
|
|
/*
|
|
|
|
+ * Monthly BFI indicator calculation
|
|
|
|
+ * @param: ret <TABLE>: 收益表,NEED COLUMNS entity_id, price_dat, end_date, nav
|
|
|
|
+ * @param benchmarks <TABLE>: entity-benchmark mapping table
|
|
|
|
+ * @param benchmark_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
|
|
|
|
+ *
|
|
|
|
+ * @return: BFI indicators table
|
|
|
|
+ *
|
|
|
|
+ *
|
|
|
|
+ * Create 20240914 Joey
|
|
|
|
+ *
|
|
|
|
+ */
|
|
|
|
+def cal_bfi_indicators(mutable ret, benchmarks, benchmark_ret, risk_free) {
|
|
|
|
+
|
|
|
|
+ // 需要基准的指标们
|
|
|
|
+ r = cal_indicators_with_benchmark(ret, benchmarks, benchmark_ret, risk_free);
|
|
|
|
+
|
|
|
|
+ return r;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
* Calculate trailing 6m, ytd, 1y, 2y, 3y, 4y, 5y, 10y and since inception indicators
|
|
* Calculate trailing 6m, ytd, 1y, 2y, 3y, 4y, 5y, 10y and since inception indicators
|
|
*
|
|
*
|
|
* @param: entity_info <TABLE>: basic information of entity, NEED COLUMNS entity_id, inception_date
|
|
* @param: entity_info <TABLE>: basic information of entity, NEED COLUMNS entity_id, inception_date
|
|
* @param benchmarks <TABLE>: entity-benchmark mapping table
|
|
* @param benchmarks <TABLE>: entity-benchmark mapping table
|
|
* @param: ret <TABLE>: 收益表,NEED COLUMNS entity_id, price_dat, end_date, nav
|
|
* @param: ret <TABLE>: 收益表,NEED COLUMNS entity_id, price_dat, end_date, nav
|
|
* @param: end_day <DATE>: 计算截止日期
|
|
* @param: end_day <DATE>: 计算截止日期
|
|
- * @param index_ret <TABLE>: historical benchmark return table, NEED COLUMNS fund_id, end_date, ret
|
|
|
|
|
|
+ * @param bmk_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 risk_free <TABLE>: historical risk free rate table, NEED COLUMNS fund_id, end_date, ret
|
|
*
|
|
*
|
|
*/
|
|
*/
|
|
@@ -548,6 +570,96 @@ def cal_trailing_indicators(entity_info, benchmarks, mutable tb_ret, end_day, bm
|
|
return r_incep, r_ytd, r_6m, r_1y, r_2y, r_3y, r_4y, r_5y, r_10y, r_ms_3y, r_ms_5y, r_ms_10y;
|
|
return r_incep, r_ytd, r_6m, r_1y, r_2y, r_3y, r_4y, r_5y, r_10y, r_ms_3y, r_ms_5y, r_ms_10y;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Calculate trailing 6m, ytd, 1y, 2y, 3y, 4y, 5y, 10y and since inception BFI indicators
|
|
|
|
+ *
|
|
|
|
+ * @param: entity_info <TABLE>: basic information of entity, NEED COLUMNS entity_id, inception_date
|
|
|
|
+ * @param benchmarks <TABLE>: entity-benchmark mapping table
|
|
|
|
+ * @param: ret <TABLE>: 收益表,NEED COLUMNS entity_id, price_dat, end_date, nav
|
|
|
|
+ * @param: end_day <DATE>: 计算截止日期
|
|
|
|
+ * @param bmk_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
|
|
|
|
+ *
|
|
|
|
+ * TODO: integrated with cal_trailing_indicators by "function pointer"
|
|
|
|
+ *
|
|
|
|
+ */
|
|
|
|
+def cal_trailing_bfi_indicators(entity_info, benchmarks, mutable tb_ret, end_day, bmk_ret, risk_free_rate) {
|
|
|
|
+
|
|
|
|
+ r_incep = null;
|
|
|
|
+ r_ytd = null;
|
|
|
|
+ r_6m = null;
|
|
|
|
+ r_1y = null;
|
|
|
|
+ r_2y = null;
|
|
|
|
+ r_3y = null;
|
|
|
|
+ r_4y = null;
|
|
|
|
+ r_5y = null;
|
|
|
|
+ r_10y = null;
|
|
|
|
+
|
|
|
|
+ // since inception
|
|
|
|
+ if(tb_ret.size() > 0) {
|
|
|
|
+ r_incep = cal_bfi_indicators(tb_ret, benchmarks, bmk_ret, risk_free_rate);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // ytd
|
|
|
|
+ tb_ret_ytd = SELECT * FROM tb_ret WHERE end_date >= end_day.yearBegin().month();
|
|
|
|
+ if(tb_ret_ytd.size() > 0) {
|
|
|
|
+ r_ytd = cal_bfi_indicators(tb_ret_ytd, benchmarks, bmk_ret, risk_free_rate);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // trailing 6m
|
|
|
|
+ tb_ret_6m = SELECT * FROM tb_ret r INNER JOIN entity_info ei ON r.entity_id = ei.entity_id
|
|
|
|
+ WHERE r.end_date > end_day.month()-6 AND (end_day.month() - ei.inception_date.month()) >= 6;
|
|
|
|
+ if(tb_ret_6m.size() > 0) {
|
|
|
|
+ r_6m = cal_bfi_indicators(tb_ret_6m, benchmarks, bmk_ret, risk_free_rate);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // trailing 1y
|
|
|
|
+ tb_ret_1y = SELECT * FROM tb_ret r INNER JOIN entity_info ei ON r.entity_id = ei.entity_id
|
|
|
|
+ WHERE r.end_date > end_day.month()-12 AND (end_day.month() - ei.inception_date.month()) >= 12;
|
|
|
|
+ if(tb_ret_1y.size() > 0) {
|
|
|
|
+ r_1y = cal_bfi_indicators(tb_ret_1y, benchmarks, bmk_ret, risk_free_rate);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // trailing 2y
|
|
|
|
+ tb_ret_2y = SELECT * FROM tb_ret r INNER JOIN entity_info ei ON r.entity_id = ei.entity_id
|
|
|
|
+ WHERE r.end_date > end_day.month()-24 AND (end_day.month() - ei.inception_date.month()) >= 24;
|
|
|
|
+ if(tb_ret_2y.size() > 0) {
|
|
|
|
+ r_2y = cal_bfi_indicators(tb_ret_2y, benchmarks, bmk_ret, risk_free_rate);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // trailing 3y
|
|
|
|
+ tb_ret_3y = SELECT * FROM tb_ret r INNER JOIN entity_info ei ON r.entity_id = ei.entity_id
|
|
|
|
+ WHERE r.end_date > end_day.month()-36 AND (end_day.month() - ei.inception_date.month()) >= 36;
|
|
|
|
+ if(tb_ret_3y.size() > 0) {
|
|
|
|
+ r_3y = cal_bfi_indicators(tb_ret_3y, benchmarks, bmk_ret, risk_free_rate);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // trailing 4y
|
|
|
|
+ tb_ret_4y = SELECT * FROM tb_ret r INNER JOIN entity_info ei ON r.entity_id = ei.entity_id
|
|
|
|
+ WHERE r.end_date > end_day.month()-48 AND (end_day.month() - ei.inception_date.month()) >= 48;
|
|
|
|
+ if(tb_ret_4y.size() > 0) {
|
|
|
|
+ r_4y = cal_bfi_indicators(tb_ret_4y, benchmarks, bmk_ret, risk_free_rate);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // trailing 5y
|
|
|
|
+ tb_ret_5y = SELECT * FROM tb_ret r INNER JOIN entity_info ei ON r.entity_id = ei.entity_id
|
|
|
|
+ WHERE r.end_date > end_day.month()-60 AND (end_day.month() - ei.inception_date.month()) >= 60;
|
|
|
|
+ if(tb_ret_5y.size() > 0) {
|
|
|
|
+ r_5y = cal_bfi_indicators(tb_ret_5y, benchmarks, bmk_ret, risk_free_rate);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // trailing 10y
|
|
|
|
+ tb_ret_10y = SELECT * FROM tb_ret r INNER JOIN entity_info ei ON r.entity_id = ei.entity_id
|
|
|
|
+ WHERE r.end_date > end_day.month()-120 AND (end_day.month() - ei.inception_date.month()) >= 120;
|
|
|
|
+ if(tb_ret_10y.size() > 0) {
|
|
|
|
+ r_10y = cal_bfi_indicators(tb_ret_10y, benchmarks, bmk_ret, risk_free_rate);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return r_incep, r_ytd, r_6m, r_1y, r_2y, r_3y, r_4y, r_5y, r_10y;
|
|
|
|
+}
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* Calculate fund indicators for one date
|
|
* Calculate fund indicators for one date
|
|
*
|
|
*
|
|
@@ -567,6 +679,9 @@ def cal_fund_indicators(entity_type, fund_ids, end_day, isFromNav) {
|
|
very_old_date = 1990.01.01;
|
|
very_old_date = 1990.01.01;
|
|
|
|
|
|
fund_info = get_fund_info(fund_ids);
|
|
fund_info = get_fund_info(fund_ids);
|
|
|
|
+
|
|
|
|
+ if(fund_info.isVoid() || fund_info.size() == 0) { return null };
|
|
|
|
+
|
|
fund_info.rename!('fund_id', 'entity_id');
|
|
fund_info.rename!('fund_id', 'entity_id');
|
|
|
|
|
|
if(isFromNav == true) {
|
|
if(isFromNav == true) {
|
|
@@ -591,6 +706,53 @@ def cal_fund_indicators(entity_type, fund_ids, end_day, isFromNav) {
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
/*
|
|
|
|
+ * Calculate fund BFI indicators for one date
|
|
|
|
+ *
|
|
|
|
+ * @param entity_type <STRING>: MF, HF
|
|
|
|
+ * @param fund_ids <STRING>: 逗号和单引号分隔的fund_id
|
|
|
|
+ * @param end_day <DATE>: 要计算的日期
|
|
|
|
+ * @param isFromNav <BOOL>: 用净值实时计算还是从表中取月收益
|
|
|
|
+ * @param isFromSQL <BOOL>: TODO: 从MySQL还是本地DolphinDB取净值/收益数据
|
|
|
|
+ *
|
|
|
|
+ * 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);
|
|
|
|
+ *
|
|
|
|
+ */
|
|
|
|
+def cal_fund_bfi_indicators(entity_type, fund_ids, end_day, isFromNav) {
|
|
|
|
+
|
|
|
|
+ very_old_date = 1990.01.01;
|
|
|
|
+
|
|
|
|
+ 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) {
|
|
|
|
+ // 从净值开始计算收益
|
|
|
|
+ tb_ret = SELECT * FROM cal_fund_monthly_returns(entity_type, fund_ids, true) WHERE price_date <= end_day;
|
|
|
|
+ tb_ret.rename!(['fund_id', 'cumulative_nav'], ['entity_id', 'nav']);
|
|
|
|
+ } else {
|
|
|
|
+ // 从fund_performance表里读月收益
|
|
|
|
+ tb_ret = get_monthly_ret('FD', fund_ids, very_old_date, end_day, true);
|
|
|
|
+ tb_ret.rename!(['fund_id'], ['entity_id']);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 取基金和基准的对照表
|
|
|
|
+ bfi_benchmark = SELECT fund_id AS entity_id, factor_id AS benchmark_id FROM get_fund_bfi_factors(fund_ids, 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);
|
|
|
|
+
|
|
|
|
+ return cal_trailing_bfi_indicators(fund_info, bfi_benchmark, tb_ret, end_day, bmk_ret, risk_free_rate);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
* Calculate portfolio indicators for one date
|
|
* Calculate portfolio indicators for one date
|
|
*
|
|
*
|
|
* @param portfolio_ids <STRING>: comma-delimited portfolio ids
|
|
* @param portfolio_ids <STRING>: comma-delimited portfolio ids
|
|
@@ -606,6 +768,9 @@ def cal_portfolio_indicators(portfolio_ids, end_day, cal_method, isFromNav) {
|
|
very_old_date = 1990.01.01;
|
|
very_old_date = 1990.01.01;
|
|
|
|
|
|
portfolio_info = get_portfolio_info(portfolio_ids);
|
|
portfolio_info = get_portfolio_info(portfolio_ids);
|
|
|
|
+
|
|
|
|
+ if(portfolio_info.isVoid() || portfolio_info.size() == 0) { return null };
|
|
|
|
+
|
|
portfolio_info.rename!('portfolio_id', 'entity_id');
|
|
portfolio_info.rename!('portfolio_id', 'entity_id');
|
|
|
|
|
|
if(isFromNav == true) {
|
|
if(isFromNav == true) {
|
|
@@ -626,11 +791,65 @@ def cal_portfolio_indicators(portfolio_ids, end_day, cal_method, isFromNav) {
|
|
}
|
|
}
|
|
|
|
|
|
// 沪深300做基准,同SQL保持一致
|
|
// 沪深300做基准,同SQL保持一致
|
|
- bmk_ret = SELECT fund_id AS benchmark_id, 'PBI' AS benchmark_type, temporalParse(end_date, 'yyyy-MM') AS end_date, ret FROM get_monthly_ret('IX', "'IN00000008'", very_old_date, end_day, true);
|
|
|
|
|
|
+ primary_benchmark = SELECT entity_id, 'IN00000008' AS benchmark_id FROM portfolio_info;
|
|
|
|
+
|
|
|
|
+ // 取所有出现的基准月收益
|
|
|
|
+ bmk_ret = get_benchmark_return(primary_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);
|
|
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);
|
|
|
|
|
|
- return cal_all_trailing_indicators(portfolio_info, tb_ret, end_day, bmk_ret, risk_free_rate, 'm');
|
|
|
|
|
|
+ return cal_trailing_indicators(portfolio_info, primary_benchmark, tb_ret, end_day, bmk_ret, risk_free_rate);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * Calculate portfolio bfi indicators for one date
|
|
|
|
+ *
|
|
|
|
+ * @param portfolio_ids <STRING>: comma-delimited portfolio ids
|
|
|
|
+ * @param end_day <DATE>: the date
|
|
|
|
+ * @param cal_method <INT>: calculate based on cumulative nav (1) or nav (2)
|
|
|
|
+ * @param isFromNav <BOOL>: calculate returns from NAV on-the-fly (true) or get from monthly return table (false)
|
|
|
|
+ *
|
|
|
|
+ * TODO: intergrate with cal_portfolio_indicators
|
|
|
|
+ *
|
|
|
|
+ * Example: cal_portfolio_bfi_indicators('166002,166114', 2024.08.31, 1, true);
|
|
|
|
+ *
|
|
|
|
+ */
|
|
|
|
+def cal_portfolio_bfi_indicators(portfolio_ids, end_day, cal_method, isFromNav) {
|
|
|
|
+
|
|
|
|
+ very_old_date = 1990.01.01;
|
|
|
|
+
|
|
|
|
+ portfolio_info = get_portfolio_info(portfolio_ids);
|
|
|
|
+
|
|
|
|
+ if(portfolio_info.isVoid() || portfolio_info.size() == 0) { return null };
|
|
|
|
|
|
|
|
+ portfolio_info.rename!('portfolio_id', 'entity_id');
|
|
|
|
+
|
|
|
|
+ if(isFromNav == true) {
|
|
|
|
+ // 从净值开始计算收益
|
|
|
|
+ tb_raw_ret = SELECT * FROM cal_portfolio_nav(portfolio_ids, very_old_date, cal_method) WHERE price_date <= end_day;
|
|
|
|
+
|
|
|
|
+ // funky thing is you can't use "AS" for the grouping columns?
|
|
|
|
+ tb_ret = SELECT portfolio_id, price_date.month(), price_date.last() AS price_date, (1+ret).prod()-1 AS ret, nav.last() AS nav
|
|
|
|
+ FROM tb_raw_ret
|
|
|
|
+ WHERE price_date <= end_day
|
|
|
|
+ GROUP BY portfolio_id, price_date.month();
|
|
|
|
+ tb_ret.rename!(['portfolio_id', 'month_price_date'], ['entity_id', 'end_date']);
|
|
|
|
+
|
|
|
|
+ } else {
|
|
|
|
+ // 从pf_portfolio_performance表里读月收益
|
|
|
|
+ tb_ret = get_monthly_ret('PF', portfolio_ids, very_old_date, end_day, true);
|
|
|
|
+ tb_ret.rename!(['portfolio_id'], ['entity_id']);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 取组合和基准的对照表
|
|
|
|
+ bfi_benchmark = SELECT portfolio_id AS entity_id, factor_id AS benchmark_id FROM get_portfolio_bfi_factors(portfolio_ids, 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);
|
|
|
|
+
|
|
|
|
+ return cal_trailing_bfi_indicators(portfolio_info, bfi_benchmark, tb_ret, end_day, bmk_ret, risk_free_rate);
|
|
}
|
|
}
|