task_monthlyPerformance.dos 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. module fundit::task_monthlyPerformance
  2. use fundit::sqlUtilities;
  3. use fundit::operationDataPuller;
  4. use fundit::performanceDataPuller;
  5. use fundit::indicatorCalculator;
  6. use fundit::dataSaver;
  7. use fundit::bfiMatcher;
  8. use fundit::rankingCalculator;
  9. use fundit::navCalculator;
  10. /*
  11. * [定时任务] 计算基金一、二级分类排名并存入数据库
  12. *
  13. * @param entity_type <STRING>: 'MF', 'HF' (MF=HF)
  14. * @param end_date <MONTH>:
  15. * @param isFromMySQL <BOOL>: false 时读取dolphin本地的收益及指标表,用于初始化数据
  16. *
  17. *
  18. * Example: CalEntityRankingTask('MF', 2024.09M, true);
  19. */
  20. def CalEntityRankingTask(entity_type, end_date, isFromMySQL=true) {
  21. if(!(entity_type in ['MF', 'HF'])) return NULL;
  22. entity_info = get_entity_info(entity_type, NULL);
  23. v_ranking_tables = cal_indicator_ranking('strategy', entity_type, entity_info, end_date, isFromMySQL);
  24. save_ranking_tables(entity_type, v_ranking_tables);
  25. }
  26. /*
  27. * [定时任务] 计算基金BFI排名并存入数据库
  28. *
  29. * @param entity_type <STRING>: 'MF', 'HF' (MF=HF)
  30. * @param end_date <MONTH>:
  31. * @param isFromMySQL <BOOL>: false 时读取dolphin本地的收益及指标表,用于初始化数据
  32. *
  33. *
  34. * Example: CalEntityBfiRankingTask('MF', 2024.09M, true);
  35. */
  36. def CalEntityBfiRankingTask(entity_type, end_date, isFromMySQL=true) {
  37. if(!(entity_type in ['MF', 'HF'])) return NULL;
  38. entity_info = get_entity_info(entity_type, NULL);
  39. v_ranking_tables = cal_indicator_ranking('bfi', entity_type, entity_info, end_date, isFromMySQL);
  40. save_ranking_tables(entity_type, v_ranking_tables);
  41. }
  42. /*
  43. * Private Method: 计算相对排名并存入数据库
  44. *
  45. *
  46. */
  47. def cal_and_save_relative_ranking(entity_type, benchmark_ranking, entity_ranking, ranking_by, isFromMySQL=true) {
  48. // benchmark_ranking= tb_fund_ranking
  49. t_entity_ranking = entity_ranking;
  50. cal_relative_ranking(benchmark_ranking, t_entity_ranking, isFromMySQL);
  51. t_entity_ranking.rename!('category_id', iif(ranking_by=='bfi', 'factor_id', ranking_by));
  52. save_relative_ranking_table(entity_type, t_entity_ranking, ranking_by);
  53. }
  54. /*
  55. *
  56. * [定时任务] 以公募基金为评级参考,计算组合、私有基金收益及指标排名
  57. *
  58. * @param entityType <STRING>: PF
  59. *
  60. * TODO: customer fund
  61. * TODO: 计算单个组合时总耗时1.5min, 大部分时间用来获取Mysql数据
  62. *
  63. * Example: CalRelativeRankingTask('PF', NULL, 2024.09M, true);
  64. * CalRelativeRankingTask('PF', 143109, 2024.09M, true);
  65. */
  66. def CalRelativeRankingTask(entity_type, entity_ids, end_date, isFromMySQL=true) {
  67. // entity_type = 'PF'
  68. // end_date = 2024.09M
  69. // isFromMySQL = true
  70. // ranking_by = 'bfi'
  71. // entity_ids = 143109
  72. entity_info = get_entity_info(entity_type, entity_ids);
  73. if(entity_type == 'PF')
  74. entity_info = SELECT * FROM entity_info WHERE portfolio_type IN (1, 2) // 1: 用户组合、2:客户真实组合,忽略客户推荐组合、总览综合等虚拟组合
  75. v_ranking_by = ['strategy', 'substrategy', 'bfi'];
  76. // 暂时以公募混合基金为排名参考
  77. for(ranking_by in v_ranking_by) {
  78. if(ranking_by == 'strategy') {
  79. v_category = EXEC DISTINCT strategy FROM entity_info WHERE strategy IS NOT NULL;
  80. tb_fund_ranking = get_fund_indicator_ranking(NULL, end_date, v_category, isFromMySQL);
  81. UPDATE tb_fund_ranking SET category_id = strategy$STRING;
  82. } else if(ranking_by == 'substrategy') {
  83. v_category = EXEC DISTINCT substrategy FROM entity_info WHERE substrategy IS NOT NULL;
  84. tb_fund_ranking = get_fund_indicator_substrategy_ranking(NULL, end_date, v_category, isFromMySQL);
  85. UPDATE tb_fund_ranking SET category_id = substrategy$STRING;
  86. } else if(ranking_by == 'bfi') {
  87. if(entity_ids != NULL || entity_ids != '')
  88. v_category = EXEC DISTINCT factor_id FROM get_entity_bfi_factors(entity_type, entity_ids, end_date, end_date);
  89. else
  90. v_category = NULL;
  91. tb_fund_ranking = get_fund_bfi_bm_indicator_ranking(NULL, end_date, v_category, isFromMySQL);
  92. UPDATE tb_fund_ranking SET category_id = factor_id;
  93. }
  94. if(tb_fund_ranking.isVoid() || tb_fund_ranking.size() == 0) return;
  95. entity_ranking = transform_data_for_ranking(entity_type, entity_info, end_date, ranking_by, isFromMySQL);
  96. cal_and_save_relative_ranking(entity_type, tb_fund_ranking, entity_ranking, ranking_by, isFromMySQL);
  97. }
  98. }
  99. /*
  100. * 计算并存储基金经理和公司月度净值
  101. *
  102. * @return <TABLE>: [COLUMNS] entity_id, curve_type, strategy, end_date, price_date, ret, nav
  103. *
  104. */
  105. def cal_and_save_mc_nav(entity_type, entity_date, is_save_local) {
  106. tb_monthly_ret = table(1000:0, ['entity_id', 'curve_type', 'strategy', 'end_date', 'price_date', 'ret', 'nav'],
  107. [SYMBOL, INT, INT, MONTH, DATE, DOUBLE, DOUBLE]);
  108. if(entity_type == 'PL') s_entity_type = 'manager';
  109. else if(entity_type == 'CO') s_entity_type = 'company';
  110. else return tb_monthly_ret;
  111. if(entity_date.isVoid() || entity_date.size() == 0) return tb_monthly_ret;
  112. // 准备类似MySQL结构的数据表
  113. tb_entity_nav = create_mc_fitted_curve();
  114. // 暂时与 MySQL 保持一致,只计算公募,私募,公私募综合三条时间序列。未来可细化至公、私募+主策略
  115. d_curve_type = dict(INT, INT);
  116. d_curve_type[1] = 1; // 私募
  117. d_curve_type[4] = 2; // 公募
  118. d_curve_type[7] = -99; // 公私募综合
  119. // 分批跑
  120. i = 0;
  121. batch_size = 1000;
  122. all_entity_id = entity_date.entity_id.distinct();
  123. do { // 14 sec
  124. tb_entity = SELECT * FROM entity_date
  125. WHERE entity_id IN all_entity_id[i : min(all_entity_id.size(), i+batch_size)];
  126. if(tb_entity.isVoid() || tb_entity.size() == 0) break;
  127. s_json = tb_entity.toStdJson();
  128. t_ret = get_mc_monthly_return(s_entity_type, s_json, 0, 1, true);
  129. for(cur in d_curve_type.keys()) {
  130. tmp = SELECT entity_id, cur AS curve_type, 0 AS strategy, end_date, price_date, ret, incl_cal_cnt
  131. FROM t_ret WHERE raise_type = d_curve_type[cur] AND strategy = -99; // 目前只需要全策略
  132. // 计算月收益
  133. tb_nav = cal_mc_nav_by_return(entity_type, tmp);
  134. INSERT INTO tb_entity_nav
  135. SELECT entity_id, curve_type, strategy, end_date, nav, incl_cal_cnt
  136. FROM ej(tb_nav, tmp, ['entity_id', 'curve_type', 'strategy', 'end_date']);
  137. INSERT INTO tb_monthly_ret
  138. SELECT entity_id, curve_type, strategy, end_date.temporalParse('yyyy-MM').month(), price_date, ret, nav
  139. FROM ej(tmp, tb_nav, ['entity_id', 'curve_type', 'strategy', 'end_date']);
  140. }
  141. i += batch_size;
  142. } while (i <= all_entity_id.size());
  143. if(! tb_entity_nav.isVoid() && tb_entity_nav.size() > 0) {
  144. // save data to MySQL (12 sec)
  145. try {
  146. tb_entity_nav.rename!('entity_id', iif(entity_type == 'PL', 'fund_manager_id', 'company_id'));
  147. save_and_sync(tb_entity_nav, iif(entity_type == 'PL', 'raw_db.fund_manager_fitted_curve', 'raw_db.company_fitted_curve'), );
  148. // 数据初始化时将指标存入本地
  149. if(is_save_local == true) {
  150. save_table(tb_entity_nav, iif(entity_type == 'PL', 'mfdb.fund_manager_fitted_curve', 'mfdb.company_fitted_curve'), false);
  151. }
  152. } catch(ex) {
  153. //TODO: Log errors
  154. }
  155. }
  156. return tb_monthly_ret;
  157. }
  158. /*
  159. * 计算并存储基金经理/公司的月度收益及指标
  160. *
  161. *
  162. */
  163. def cal_and_save_mc_indicator(entity_type, entity_date, monthly_returns, is_save_local) {
  164. rt = '';
  165. if(!(entity_type IN ['PL', 'CO'])) return rt;
  166. if(entity_date.isVoid() || entity_date.size() == 0) return rt;
  167. indicators = cal_mc_monthly_indicators(entity_type, 'PBI', monthly_returns);
  168. }
  169. /*
  170. * [定时任务]: 基金经理月净值计算
  171. *
  172. *
  173. */
  174. def CalManagerNavTask(updatetime) {
  175. //updatetime = 2024.11.05;
  176. is_save_local = iif(updatetime <= get_ini_data_const()['updatetime'], true, false);
  177. // 31 sec
  178. entity_date = get_manager_list_by_fund_updatetime(updatetime);
  179. entity_date.rename!('manager_id', 'entity_id');
  180. tb_monthly_ret = cal_and_save_mc_nav('PL', entity_date, is_save_local);
  181. cal_and_save_mc_indicator('PL', entity_date, tb_monthly_ret, is_save_local);
  182. entity_date = null;
  183. tb_monthly_ret = null;
  184. }
  185. /*
  186. * [定时任务]: 基金公司月净值计算
  187. *
  188. *
  189. */
  190. def CalCompanyNavTask(updatetime) {
  191. //updatetime = 2024.11.05;
  192. is_save_local = iif(updatetime <= get_ini_data_const()['updatetime'], true, false);
  193. // 31 sec
  194. entity_date = get_company_list_by_fund_updatetime(updatetime);
  195. entity_date.rename!('company_id', 'entity_id');
  196. cal_and_save_mc_nav('CO', entity_date, is_save_local);
  197. entity_date = null;
  198. }
  199. /*
  200. *
  201. * [定时任务]: 基金经理的BFI MATCHING
  202. *
  203. *
  204. */
  205. def MatchManagerBFITask(updatetime) {
  206. rt = '';
  207. is_save_local = iif(updatetime <= get_ini_data_const()['updatetime'], true, false);
  208. entity_type = 'PL';
  209. // 31 sec
  210. entity_date = get_mc_performance_by_updatetime(entity_type, updatetime);
  211. if(entity_date.isVoid() || entity_date.size() == 0) return rt;
  212. i = 0;
  213. batch_size = 1000;
  214. max_cnt = entity_date.size();
  215. do {
  216. t_entity_date = entity_date[i : min(max_cnt, i + batch_size)];
  217. // 22 min per 1000 records, way too slow
  218. t_bfi = match_mc_bfi(entity_type, t_entity_date);
  219. t_max_r2 = SELECT entity_id , curve_type, strategy, factor_id.first() AS factor_id, end_date,
  220. string(NULL) AS performance_flag, coe.first() AS coe, r2.first() AS r2, concat(factor_name, ",") AS rz_portrait
  221. FROM ej(t_bfi, get_bfi_index_list(), 'factor_id')
  222. GROUP BY entity_id, curve_type, strategy, end_date;
  223. try {
  224. // 高度怀疑 pf_manager_factor_bfi 表只是中间表,没有用,这里就不存了
  225. // 有效 factors 存到 xxx_factor_bfi_by_category_group 表
  226. chg_columns_for_mysql(t_bfi, 'manager_id');
  227. save_and_sync(t_bfi, 'raw_db.pf_manager_factor_bfi_by_category_group', );
  228. // 有效因子中 R2 最大的因子存 xxx_max_r2
  229. chg_columns_for_mysql(t_max_r2, 'manager_id');
  230. save_and_sync(t_max_r2, 'raw_db.pf_manager_factor_bfi_max_r2', );
  231. } catch (ex) {
  232. //TODO: Log errors
  233. rt += ex + '\n';
  234. }
  235. i += batch_size
  236. } while (i < max_cnt);
  237. return rt;
  238. }