|
@@ -5,15 +5,10 @@ use fundit::dataPuller
|
|
|
use fundit::dataSaver
|
|
|
|
|
|
|
|
|
-
|
|
|
-def cal_ranking() {
|
|
|
-
|
|
|
-}
|
|
|
-
|
|
|
/*
|
|
|
* 计算收益率排名
|
|
|
*
|
|
|
- *
|
|
|
+ * TODO: 整合入 gen_ranking_sql
|
|
|
*/
|
|
|
def cal_ret_ranking(entity_type, entity_info, end_date, isFromMySQL) {
|
|
|
|
|
@@ -184,6 +179,8 @@ defg perRank(x, is_ASC) {
|
|
|
* @param indicator_id <INT>:指标ID
|
|
|
* @param is_ASC <BOOL>: 是否排正序
|
|
|
* @param ranking_by <STRING>: 'strategy', 'substrategy', 'factor', 'catavg'
|
|
|
+ *
|
|
|
+ * TODO: bfi & category
|
|
|
*
|
|
|
*/
|
|
|
def gen_ranking_sql(data_table, indicator_name, indicator_id, is_ASC, ranking_by) {
|
|
@@ -344,20 +341,9 @@ def gen_ranking_sql(data_table, indicator_name, indicator_id, is_ASC, ranking_by
|
|
|
/*
|
|
|
* 运行排名SQL脚本
|
|
|
*
|
|
|
- *
|
|
|
+ * NOTE: 没有用 parseExpr 来生成动态脚本的原因是数据表无法传入
|
|
|
*/
|
|
|
def run_ranking_sql(data_table, indicator_name, indicator_id, is_ASC, mutable v_tables) {
|
|
|
-/*
|
|
|
- entity_type = 'MF'
|
|
|
- end_date = 2024.09M
|
|
|
- isFromMySQL = true;
|
|
|
- indicator_name = 'alpha'
|
|
|
- indicator_id = 11
|
|
|
- is_ASC = false;
|
|
|
- groupby_field = 'strategy';
|
|
|
- data_table = t;
|
|
|
- v_tables = v_ranking_tables
|
|
|
-*/
|
|
|
|
|
|
tb_strategy_ranking = gen_ranking_sql(data_table, indicator_name, indicator_id, is_ASC, 'strategy')[0];
|
|
|
v_tables[0].tableInsert(tb_strategy_ranking);
|
|
@@ -549,24 +535,292 @@ def cal_other_indicator_ranking(entity_type, entity_info, end_date, isFromMySQL)
|
|
|
return v_ranking_tables;
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+/*
|
|
|
+ * 将源指标表横表变竖表,以方便排名计算
|
|
|
+ *
|
|
|
+ *
|
|
|
+ */
|
|
|
+def transform_data_for_ranking(entity_type, data_table, ranking_by, indicator_info) {
|
|
|
+
|
|
|
+ v_trailing = ['6m', '1y', '2y', '3y', '5y', '10y', 'ytd'];
|
|
|
+
|
|
|
+ // 只有 portfolio_id 是整型,其它的都是字符串
|
|
|
+ is_id_integer = false;
|
|
|
+ if(entity_type == 'PF') is_id_integer = true;
|
|
|
+
|
|
|
+ if(ranking_by == 'strategy')
|
|
|
+ tb_ranking = create_entity_indicator_ranking(is_id_integer).rename!(ranking_by, 'category_id');
|
|
|
+ else if(ranking_by == 'substrategy')
|
|
|
+ tb_ranking = create_entity_indicator_substrategy_ranking(is_id_integer).rename!(ranking_by, 'category_id');
|
|
|
+ else if(ranking_by == 'factor_id')
|
|
|
+ tb_ranking = NULL;
|
|
|
+
|
|
|
+ for(indicator in indicator_info) {
|
|
|
+
|
|
|
+ t = sql(select = (sqlCol('entity_id'), sqlCol('end_date'), sqlCol('category_id'),
|
|
|
+ sqlCol(indicator.name + '_' + v_trailing,, 'indicator_' + v_trailing)
|
|
|
+ ),
|
|
|
+ from = data_table
|
|
|
+ ).eval();
|
|
|
+
|
|
|
+ t.join!(table(take(indicator.id, t.size()) AS indicator_id,
|
|
|
+ take(double(NULL), t.size()) AS indicator_1m,
|
|
|
+ take(int(NULL), t.size()) AS absrank_1m,
|
|
|
+ take(int(NULL), t.size()) AS perrank_1m,
|
|
|
+ take(double(NULL), t.size()) AS indicator_3m,
|
|
|
+ take(int(NULL), t.size()) AS absrank_3m,
|
|
|
+ take(int(NULL), t.size()) AS perrank_3m,
|
|
|
+ take(int(NULL), t.size()) AS absrank_6m,
|
|
|
+ take(int(NULL), t.size()) AS perrank_6m,
|
|
|
+ take(int(NULL), t.size()) AS absrank_1y,
|
|
|
+ take(int(NULL), t.size()) AS perrank_1y,
|
|
|
+ take(int(NULL), t.size()) AS absrank_2y,
|
|
|
+ take(int(NULL), t.size()) AS perrank_2y,
|
|
|
+ take(int(NULL), t.size()) AS absrank_3y,
|
|
|
+ take(int(NULL), t.size()) AS perrank_3y,
|
|
|
+ take(int(NULL), t.size()) AS absrank_5y,
|
|
|
+ take(int(NULL), t.size()) AS perrank_5y,
|
|
|
+ take(int(NULL), t.size()) AS absrank_10y,
|
|
|
+ take(int(NULL), t.size()) AS perrank_10y,
|
|
|
+ take(int(NULL), t.size()) AS absrank_ytd,
|
|
|
+ take(int(NULL), t.size()) AS perrank_ytd)
|
|
|
+ );
|
|
|
+
|
|
|
+ INSERT INTO tb_ranking
|
|
|
+ SELECT * FROM (sql(select = sqlCol(tb_ranking.colNames()),
|
|
|
+ from = t).eval());
|
|
|
+ }
|
|
|
+
|
|
|
+ return tb_ranking;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+/*
|
|
|
+ * 将源风险指标表横表变竖表,以方便排名计算
|
|
|
+ *
|
|
|
+ * TODO: 一直缺 portfolio bfi indicator 计算!mysql 里的 pf_fund_bfi_bm_indicator_ranking 是错的...
|
|
|
+ */
|
|
|
+def transform_risk_stats_for_ranking (entity_type, entity_info, end_date, ranking_by, isFromMySQL=true) {
|
|
|
+
|
|
|
+ table_desc = get_risk_stats_table_description(entity_type);
|
|
|
+
|
|
|
+ tb_data = get_monthly_indicator_data(table_desc.table_name[0], end_date, isFromMySQL);
|
|
|
+ sec_id_col = table_desc.sec_id_col[0];
|
|
|
+ tb_data.rename!(sec_id_col, 'entity_id');
|
|
|
+
|
|
|
+ data_table = SELECT * FROM entity_info en
|
|
|
+ INNER JOIN tb_data d ON en.entity_id = d.entity_id
|
|
|
+ WHERE en.strategy IS NOT NULL
|
|
|
+
|
|
|
+ data_table.dropColumns!('id');
|
|
|
+ data_table.rename!(ranking_by, 'category_id');
|
|
|
+
|
|
|
+
|
|
|
+ // 目前SQL排名的指标
|
|
|
+ v_indicator_name = ['maxdrawdown', 'kurtosis', 'skewness', 'stddev', 'alpha', 'beta', 'downsidedev', 'maxdrawdown_months', 'maxdrawdown_recoverymonths', 'winrate'];
|
|
|
+ v_indicator_id = [2, 6, 9, 10, 11, 12, 21, 50, 52, 59];
|
|
|
+ v_is_ASC = [true, true, false, true, false, false, true, true, true, false];
|
|
|
+ t_indicator = table(v_indicator_name AS name, v_indicator_id AS id, v_is_ASC AS is_ASC);
|
|
|
+
|
|
|
+ tb_ranking = transform_data_for_ranking(entity_type, data_table, ranking_by, t_indicator).rename!('category_id', ranking_by);
|
|
|
+
|
|
|
+ return tb_ranking;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+/*
|
|
|
+ * 将源风险调整指标表横表变竖表,以方便排名计算
|
|
|
+ *
|
|
|
+ *
|
|
|
+ */
|
|
|
+def transform_risk_adj_ret_stats_for_ranking (entity_type, entity_info, end_date, ranking_by, isFromMySQL=true) {
|
|
|
+
|
|
|
+ table_desc = get_riskadjret_stats_table_description(entity_type);
|
|
|
+
|
|
|
+ tb_data = get_monthly_indicator_data(table_desc.table_name[0], end_date, isFromMySQL);
|
|
|
+ sec_id_col = table_desc.sec_id_col[0];
|
|
|
+ tb_data.rename!(sec_id_col, 'entity_id');
|
|
|
+
|
|
|
+ data_table = SELECT * FROM entity_info en
|
|
|
+ INNER JOIN tb_data d ON en.entity_id = d.entity_id
|
|
|
+ WHERE en.strategy IS NOT NULL
|
|
|
+
|
|
|
+ data_table.dropColumns!('id');
|
|
|
+ data_table.rename!(ranking_by, 'category_id');
|
|
|
+
|
|
|
+ // 目前SQL排名的指标
|
|
|
+ v_indicator_name = ['kapparatio', 'treynorratio', 'jensen', 'omegaratio', 'sharperatio', 'sortinoratio_MAR', 'calmarratio', 'sortinoratio'];
|
|
|
+ v_indicator_id = [14, 15, 16, 17, 18, 19, 40, 58];
|
|
|
+ v_is_ASC = [false, false, false, false, false, false, false, false];
|
|
|
+ t_indicator = table(v_indicator_name AS name, v_indicator_id AS id, v_is_ASC AS is_ASC);
|
|
|
+
|
|
|
+ tb_ranking = transform_data_for_ranking(entity_type, data_table, ranking_by, t_indicator).rename!('category_id', ranking_by);
|
|
|
+
|
|
|
+ return tb_ranking;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+/*
|
|
|
+ * 将源杂项风险指标表横表变竖表,以方便排名计算
|
|
|
+ *
|
|
|
+ *
|
|
|
+ */
|
|
|
+def transform_other_indicator_for_ranking (entity_type, entity_info, end_date, ranking_by, isFromMySQL=true) {
|
|
|
+
|
|
|
+ table_desc = get_indicator_table_description(entity_type);
|
|
|
+
|
|
|
+ tb_data = get_monthly_indicator_data(table_desc.table_name[0], end_date, isFromMySQL);
|
|
|
+ sec_id_col = table_desc.sec_id_col[0];
|
|
|
+ tb_data.rename!(sec_id_col, 'entity_id');
|
|
|
+
|
|
|
+ data_table = SELECT * FROM entity_info en
|
|
|
+ INNER JOIN tb_data d ON en.entity_id = d.entity_id
|
|
|
+ WHERE en.strategy IS NOT NULL
|
|
|
+
|
|
|
+ data_table.dropColumns!('id');
|
|
|
+ data_table.rename!(ranking_by, 'category_id');
|
|
|
+
|
|
|
+ // 目前SQL排名的指标
|
|
|
+ v_indicator_name = ['per_con', 'info_ratio', 'var', 'cvar', 'smddvar', 'smddcvar', 'smdd_lpm1', 'smdd_lpm2', 'smdd_downside_dev', 'tracking_error', 'm2'];
|
|
|
+ v_indicator_id = [37, 38, 41, 42, 43, 44, 45, 46, 47, 48, 49];
|
|
|
+ v_is_ASC = [false, false, true, true, true, true, true, true, true, true, false];
|
|
|
+ t_indicator = table(v_indicator_name AS name, v_indicator_id AS id, v_is_ASC AS is_ASC);
|
|
|
+
|
|
|
+ tb_ranking = transform_data_for_ranking(entity_type, data_table, ranking_by, t_indicator).rename!('category_id', ranking_by);
|
|
|
+
|
|
|
+ return tb_ranking;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ *
|
|
|
+ * 参考某指定类排名,计算相对排名
|
|
|
+ *
|
|
|
+ * @param benchmark_ranking <TABLE>: 被参考的排名表,如公募混合基金
|
|
|
+ * @param entity_ranking <TABLE>: 被计算的指标表,排名被填充在原表中
|
|
|
+ * @param isFromMySQL <BOOL>
|
|
|
+ *
|
|
|
+ *
|
|
|
+ * Example: cal_relative_ranking(get_fund_indicator_ranking(NULL, 2024.09M, 102, true),
|
|
|
+ * transform_risk_stats_for_ranking('PF', get_entity_info('PF', NULL), 2024.09M, true),
|
|
|
+ * true);
|
|
|
+ */
|
|
|
+def cal_relative_ranking(benchmark_ranking, mutable entity_ranking, isFromMySQL=true) {
|
|
|
+
|
|
|
+ v_trailing = ['1m', '3m', '6m', '1y', '2y', '3y', '5y', '10y', 'ytd'];
|
|
|
+
|
|
|
+ for(tr in v_trailing) {
|
|
|
+
|
|
|
+ indicator_val_col = 'indicator_' + tr;
|
|
|
+
|
|
|
+ // 乘上100,000 是为了满足 window join 的字段必须是INT或DURATION
|
|
|
+ tb_tmp = sql(select = (sqlCol(['entity_id', 'end_date', 'category_id', 'indicator_id']),
|
|
|
+ sqlColAlias(makeCall(round, binaryExpr(sqlCol(indicator_val_col), 1000000, *), 0), indicator_val_col + '_int')),
|
|
|
+ from = entity_ranking,
|
|
|
+ where = < _$indicator_val_col is not null >,
|
|
|
+ orderBy = sqlCol(['end_date', 'category_id', 'indicator_id', indicator_val_col])
|
|
|
+ ).eval();
|
|
|
+
|
|
|
+ tb_tmp2 = sql(select = (sqlCol(['end_date', 'category_id', 'indicator_id']),
|
|
|
+ sqlColAlias(makeCall(round, binaryExpr(sqlCol(indicator_val_col), 1000000, *), 0), indicator_val_col + '_int'),
|
|
|
+ sqlCol('absrank_' + tr), sqlCol('perrank_' + tr)
|
|
|
+ ),
|
|
|
+ from = benchmark_ranking,
|
|
|
+ where = < _$indicator_val_col is not null >,
|
|
|
+ orderBy = sqlCol(['end_date', 'category_id', 'indicator_id', indicator_val_col])
|
|
|
+ ).eval();
|
|
|
+
|
|
|
+ absrank_col = 'absrank_' + tr;
|
|
|
+ perrank_col = 'perrank_' + tr;
|
|
|
+ // 用 pwj 来找最接近的排名
|
|
|
+ tb_tmp_ranking = sql(select = (sqlCol(['entity_id', 'end_date', 'category_id', 'indicator_id']),
|
|
|
+ sqlCol(indicator_val_col + '_int'),
|
|
|
+ sqlCol(['absrank_max', 'perrank_max'])),
|
|
|
+ from = pwj(tb_tmp, tb_tmp2,
|
|
|
+ window = 0:1,
|
|
|
+ aggs = [<max(_$absrank_col) as 'absrank_max'>, <max(_$perrank_col) as 'perrank_max'>],
|
|
|
+ matchingCols = ['end_date', 'category_id', 'indicator_id', indicator_val_col + '_int'])
|
|
|
+ ).eval();
|
|
|
+
|
|
|
+ // 计算的结果填入排名表
|
|
|
+ sqlUpdate(table = entity_ranking,
|
|
|
+ updates = [<absrank_max as _$absrank_col>, <perrank_max as _$perrank_col>],
|
|
|
+ from = <ej(entity_ranking, tb_tmp_ranking, ['entity_id', 'end_date', 'category_id','indicator_id'])>
|
|
|
+ ).eval();
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
/*
|
|
|
* 排名数据入库
|
|
|
*
|
|
|
* @param ranking_tables <VECTOR>: 包含4个数据表的向量,分别是一级策略排名,一级策略排名阈值,二级策略排名,二级策略排名阈值
|
|
|
*/
|
|
|
-def save_ranking_tables(ranking_tables) {
|
|
|
+def save_ranking_tables(entity_type, ranking_tables) {
|
|
|
|
|
|
if(ranking_tables.isVoid()) return;
|
|
|
|
|
|
- ranking_tables[0].rename!('entity_id', 'fund_id');
|
|
|
- save_and_sync(ranking_tables[0], 'raw_db.pf_fund_indicator_ranking', 'raw_db.pf_fund_indicator_ranking');
|
|
|
+ source_table = '';
|
|
|
+ target_table = '';
|
|
|
+
|
|
|
+ if(entity_type IN ['MF', 'HF']) {
|
|
|
+
|
|
|
+ entity_id_col = 'fund_id';
|
|
|
+ source_table = 'raw_db.pf_fund_indicator_ranking';
|
|
|
+ target_table = 'raw_db.pf_fund_indicator_ranking'
|
|
|
+
|
|
|
+ }
|
|
|
|
|
|
- save_and_sync(ranking_tables[1], 'raw_db.pf_fund_indicator_ranking_num', 'raw_db.pf_fund_indicator_ranking_num');
|
|
|
+ ranking_tables[0].rename!('entity_id', entity_id_col);
|
|
|
+ save_and_sync(ranking_tables[0], source_table, target_table);
|
|
|
+
|
|
|
+ save_and_sync(ranking_tables[1], source_table + '_num', target_table + '_num');
|
|
|
|
|
|
- ranking_tables[2].rename!('entity_id', 'fund_id');
|
|
|
- save_and_sync(ranking_tables[2], 'raw_db.pf_fund_indicator_substrategy_ranking', 'raw_db.pf_fund_indicator_substrategy_ranking');
|
|
|
+ ranking_tables[2].rename!('entity_id', entity_id_col);
|
|
|
+ save_and_sync(ranking_tables[2], source_table.replace!('_ranking', '_substrategy_ranking'), target_table.replace!('_ranking', '_substrategy_ranking'));
|
|
|
+
|
|
|
+ save_and_sync(ranking_tables[3], source_table + '_num', target_table + '_num');
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * 参考排名数据入库
|
|
|
+ *
|
|
|
+ * @param ranking_tables <TABLE>:
|
|
|
+ */
|
|
|
+def save_relative_ranking_table(entity_type, ranking_table, ranking_by) {
|
|
|
+
|
|
|
+ if(ranking_table.isVoid()) return;
|
|
|
+
|
|
|
+ source_table = '';
|
|
|
+ target_table = '';
|
|
|
+
|
|
|
+ if(entity_type == 'PF') {
|
|
|
+
|
|
|
+ entity_id_col = 'portfolio_id';
|
|
|
+
|
|
|
+ if(ranking_by == 'strategy') {
|
|
|
+ source_table = 'raw_db.pf_portfolio_indicator_ranking';
|
|
|
+ target_table = 'raw_db.pf_portfolio_indicator_ranking';
|
|
|
+ } else if(ranking_by == 'substrategy') {
|
|
|
+ source_table = 'raw_db.pf_portfolio_indicator_substrategy_ranking';
|
|
|
+ target_table = 'raw_db.pf_portfolio_indicator_substrategy_ranking';
|
|
|
+ } else if(ranking_by == 'factor_id') {
|
|
|
+ source_table = 'raw_db.pf_portfolio_bfi_bm_indicator_ranking';
|
|
|
+ target_table = 'raw_db.pf_portfolio_bfi_bm_indicator_ranking';
|
|
|
+ }
|
|
|
+
|
|
|
+ } else if(entity_type == 'CF') {
|
|
|
+
|
|
|
+ entity_id_col = 'fund_id';
|
|
|
+ source_table = 'raw_db.pf_cus_fund_indicator_ranking';
|
|
|
+ target_table = 'raw_db.pf_cus_fund_indicator_ranking'
|
|
|
+
|
|
|
+ }
|
|
|
|
|
|
- save_and_sync(ranking_tables[3], 'raw_db.pf_fund_indicator_substrategy_ranking_num', 'raw_db.pf_fund_indicator_substrategy_ranking_num');
|
|
|
+ save_and_sync(ranking_table, source_table, target_table);
|
|
|
|
|
|
}
|
|
|
|