Kaynağa Gözat

支持基金经理/公司周净值计算

Joey 1 ay önce
ebeveyn
işleme
a4aba3e518

+ 9 - 0
modules/dataSaver.dos

@@ -93,6 +93,15 @@ def create_entity_nav(is_id_integer=false) {
                 [iif(is_id_integer, INT, SYMBOL), DATE, DOUBLE]);
 }
 
+/*
+ *   建公司/经理周净值表 XXXX_nav
+ */
+def create_mc_nav() {
+
+    return table(1000:0, 
+                ['entity_id', 'curve_type', 'strategy', 'year_week', 'price_date', 'cumulative_nav', 'fund_num'],
+                [SYMBOL, INT, INT, STRING, DATE, DOUBLE, INT]);
+}
 
 /*
  *   建公司/经理净值表 XXXX_fitted_curve

+ 9 - 8
modules/navCalculator.dos

@@ -305,27 +305,28 @@ def cal_nav_by_return(entity_type, entity_cal_dates, holdings) {
  *   通用净值计算,由收益反推 
  *    
  *   @param entity_type <STRING>: PL, CO
- *   @param entity_ret <TABLE>: [COLUMNS] entity_id, curve_type, strategy, end_date, ret
- * 
- *   NOTE: 如果没有成立日,则无法计算
+ *   @param entity_ret <TABLE>: [COLUMNS] entity_id, curve_type, strategy, effective_date <STRING>, ret
+ *   
+ *   NOTE: 1) 如果没有成立日,则无法计算
+ *         2) monthly 时 effective_date 对应MySQL里的 end_date; weekly时对应 year_week
  */
-def cal_mc_nav_by_return(entity_type, entity_ret) {
+def cal_mc_nav_by_return(entity_type, entity_ret, freq='m') {
 
-	t_nav = table(1000:0, ['entity_id', 'curve_type', 'strategy', 'end_date', 'ret', 'nav'], [SYMBOL, INT, INT, STRING, DOUBLE, DOUBLE]);
+	t_nav = table(1000:0, ['entity_id', 'curve_type', 'strategy', 'effective_date', 'ret', 'nav'], [SYMBOL, INT, INT, STRING, DOUBLE, DOUBLE]);
 
 	if(!(entity_type IN ['PL', 'CO'])) return t_nav;
 	if(entity_ret.isVoid() || entity_ret.size() == 0) return t_nav;
 
-	s_json = (SELECT entity_id AS entity_id, curve_type, strategy, end_date.min() AS end_date 
+	s_json = (SELECT entity_id AS entity_id, curve_type, strategy, effective_date.min() AS effective_date 
 	          FROM entity_ret
 	          GROUP BY entity_id, curve_type, strategy).toStdJson();
 
 	// 取净值前值
-	t_pre_nav = get_mc_nav_for_return_calculation(entity_type, s_json, 1);
+	t_pre_nav = get_mc_nav_for_return_calculation(entity_type, s_json, 1, freq);
 
 	// 没有前值时候, 做一个假记录,把净值1和日期填入
 	INSERT INTO t_pre_nav
-		SELECT entity_id, curve_type, strategy, end_date.min() AS end_date, 1 AS cumulative_nav
+		SELECT entity_id, curve_type, strategy, effective_date.min() AS effective_date, 1 AS cumulative_nav
         FROM entity_ret
         WHERE NOT exists (SELECT * FROM t_pre_nav 
 	                      WHERE entity_ret.entity_id = t_pre_nav.entity_id

+ 65 - 38
modules/performanceDataPuller.dos

@@ -494,16 +494,17 @@ def get_nav_for_return_calculation(entity_type, freq, json_query, pre_nav_incld=
  *  取基金经理/公司从某日期后的所有净值及前值
  *  
  *  @param entity_type <STRING>: PL, CO
- *  @param pre_nav_incld <INT>: 0- no pre_nav; 1- pre_nav only; 2- pre_nav + afters
  *  @param json_query <JSON>: [{entity_id:xxx, curve_type:1, strategy:0, end_date: yyyy-mm}]
+ *  @param pre_nav_incld <INT>: 0- no pre_nav; 1- pre_nav only; 2- pre_nav + afters
+ *  @param freq <STRING>: m- monthly, w-weekly
  * 
- *  Example: get_mc_nav_for_return_calculation('PL', '[{"entity_id":"PL000000NS", "curve_type":"4", "strategy":"0", "end_date":"2024-07"}]', 2);
- *           get_mc_nav_for_return_calculation('CO', '[{"entity_id":"CO00000017", "curve_type":"4", "strategy":"0", "end_date":"2024-07"}]', 1);
+ *  Example: get_mc_nav_for_return_calculation('PL', '[{"entity_id":"PL000000NS", "curve_type":"4", "strategy":"0", "effective_date":"2024-07"}]', 2);
+ *           get_mc_nav_for_return_calculation('CO', '[{"entity_id":"CO00000017", "curve_type":"4", "strategy":"0", "effective_date":"202406"}]', 1, 'w');
  *  
  */
-def get_mc_nav_for_return_calculation(entity_type, json_query, pre_nav_incld=2) {
+def get_mc_nav_for_return_calculation(entity_type, json_query, pre_nav_incld=2, freq='m') {
 
-    s_query = "CALL pfdb.sp_get_mc_nav_for_return_cal('" + entity_type + "', " + pre_nav_incld + ", '" + json_query + "')";
+    s_query = "CALL pfdb.sp_get_mc_nav_for_return_cal('" + entity_type + "', '" + freq + "', " + pre_nav_incld + ", '" + json_query + "')";
 
     conn = connect_mysql();
 
@@ -1077,16 +1078,16 @@ def get_category_avg_weekly_return(category_type, begin_day, trim_pct=5, min_cnt
  *  取基金经理或公司月收益
  *  
  * 
- *  Example: get_mc_monthly_return('manager', '[{"entity_id": "PL000000NS","end_date": "2024-07"},{"entity_id": "PL00000ICF","end_date": "2023-12"}]', 0, 1, true);
- *           get_mc_monthly_return('company', '[{"entity_id": "CO00000017","end_date": "2024-07"},{"entity_id": "CO0001003W","end_date": "2023-06"}]', 0, 1, true);
+ *  Example: get_mc_average_return('PL', 'm', '[{"entity_id": "PL000000NS","effective_date": "2024-07"},{"entity_id": "PL00000ICF","effective_date": "2023-12"}]', 0, 1, true);
+ *           get_mc_average_return('CO', 'w', '[{"entity_id": "CO00000017","effective_date": "202407"},{"entity_id": "CO0001003W","effective_date": "202306"}]', 0, 1, true);
  */
-def get_mc_monthly_return(entity_type, json_query, trim_pct=0, min_cnt=1, isFromMySQL=true) {
+def get_mc_average_return(entity_type, freq, json_query, trim_pct=0, min_cnt=1, isFromMySQL=true) {
 
     t = null;
 
     if(isFromMySQL == true) {
 
-        s_query = "CALL pfdb.sp_get_mc_avg_monthly_return('" + entity_type + "', '" + json_query + "', " + trim_pct + ", " + min_cnt + ")";
+        s_query = "CALL pfdb.sp_get_mc_avg_return('" + entity_type + "', '" + freq + "', '" + json_query + "', " + trim_pct + ", " + min_cnt + ")";
 
 	    conn = connect_mysql();
 	
@@ -1105,28 +1106,39 @@ def get_mc_monthly_return(entity_type, json_query, trim_pct=0, min_cnt=1, isFrom
  *   
  *   NOTE: 月收益表 isvalid = 0 的记录也会被返回
  * 
- *   Example: get_company_list_by_fund_updatetime(2024.11.14 11:10:00, true);
- * 
+ *   Example: get_company_list_by_fund_updatetime(2024.11.14 11:10:00, 'm', true);
+ *            get_company_list_by_fund_updatetime(2024.12.24 11:10:00, 'w', true);
  */
-def get_company_list_by_fund_updatetime(updatetime, isFromMySQL=true) {
+def get_company_list_by_fund_updatetime(updatetime, freq='m', isFromMySQL=true) {
 
 
     if(isFromMySQL == true) {
 
-        s_query = "SELECT ci.company_id, MIN(fp.end_date) AS end_date
-                   FROM mfdb.company_information ci
-                   INNER JOIN mfdb.fund_information fi ON ci.company_id = fi.advisor_id
-                   INNER JOIN mfdb.fund_performance fp ON fi.fund_id = fp.fund_id
-                   WHERE ci.isvalid = 1
-                     AND fi.isvalid = 1
-                     AND fp.updatetime >= '" + updatetime + "'
-                   GROUP BY ci.company_id;";
-     
-        conn = connect_mysql()
+		if(freq == 'm') {
+	        s_query = "SELECT ci.company_id, MIN(fp.end_date) AS effective_date
+	                   FROM mfdb.company_information ci
+	                   INNER JOIN mfdb.fund_information fi ON ci.company_id = fi.advisor_id
+	                   INNER JOIN mfdb.fund_performance fp ON fi.fund_id = fp.fund_id
+	                   WHERE ci.isvalid = 1
+	                     AND fi.isvalid = 1
+	                     AND fp.updatetime >= '" + updatetime + "'
+	                   GROUP BY ci.company_id;";
+		} else {
+	        s_query = "SELECT ci.company_id, MIN(fp.year_week) AS effective_date
+	                   FROM mfdb.company_information ci
+	                   INNER JOIN mfdb.fund_information fi ON ci.company_id = fi.advisor_id
+	                   INNER JOIN mfdb.fund_performance_weekly fp ON fi.fund_id = fp.fund_id
+	                   WHERE ci.isvalid = 1
+	                     AND fi.isvalid = 1
+	                     AND fp.updatetime >= '" + updatetime + "'
+	                   GROUP BY ci.company_id;";
+		}
+		
+        conn = connect_mysql();
      
-        t = odbc::query(conn, s_query)
+        t = odbc::query(conn, s_query);
      
-        conn.close()
+        conn.close();
 
     } else {
         
@@ -1142,26 +1154,41 @@ def get_company_list_by_fund_updatetime(updatetime, isFromMySQL=true) {
  *   
  *   NOTE: 月收益表 isvalid = 0 的记录也会被返回
  * 
- *   Example: get_manager_list_by_fund_updatetime(2024.11.01, true);
+ *   Example: get_manager_list_by_fund_updatetime(2024.11.01, 'm', true);
+ *            get_manager_list_by_fund_updatetime(2024.12.12, 'w', true);
  * 
  */
-def get_manager_list_by_fund_updatetime(updatetime, isFromMySQL=true) {
+def get_manager_list_by_fund_updatetime(updatetime, freq='m', isFromMySQL=true) {
 
     if(isFromMySQL == true) {
 
-        s_query = "SELECT mi.personnel_id AS manager_id, MIN(fp.end_date) AS end_date
-                   FROM mfdb.personnel_information mi
-                   INNER JOIN mfdb.fund_manager_mapping fmp ON mi.personnel_id = fmp.fund_manager_id
-                   INNER JOIN mfdb.fund_information fi ON fmp.fund_id = fi.fund_id
-                   INNER JOIN mfdb.fund_performance fp ON fi.fund_id = fp.fund_id
-                   WHERE mi.isvalid = 1
-                     AND fmp.isvalid = 1
-                     AND fi.isvalid = 1
-                     AND fp.updatetime >= '" + updatetime + "'
-                     AND fp.end_date <= DATE_FORMAT(IFNULL(fmp.management_end_date, CURRENT_DATE), '%y-%m')
-                   GROUP BY mi.personnel_id;";
+		if(freq == 'm') {
+	        s_query = "SELECT mi.personnel_id AS manager_id, MIN(fp.end_date) AS effective_date
+	                   FROM mfdb.personnel_information mi
+	                   INNER JOIN mfdb.fund_manager_mapping fmp ON mi.personnel_id = fmp.fund_manager_id
+	                   INNER JOIN mfdb.fund_information fi ON fmp.fund_id = fi.fund_id
+	                   INNER JOIN mfdb.fund_performance fp ON fi.fund_id = fp.fund_id
+	                   WHERE mi.isvalid = 1
+	                     AND fmp.isvalid = 1
+	                     AND fi.isvalid = 1
+	                     AND fp.updatetime >= '" + updatetime + "'
+	                     AND fp.price_date <= IFNULL(fmp.management_end_date, CURRENT_DATE)
+	                   GROUP BY mi.personnel_id;";
+		} else {
+	        s_query = "SELECT mi.personnel_id AS manager_id, MIN(fp.year_week) AS effective_date
+	                   FROM mfdb.personnel_information mi
+	                   INNER JOIN mfdb.fund_manager_mapping fmp ON mi.personnel_id = fmp.fund_manager_id
+	                   INNER JOIN mfdb.fund_information fi ON fmp.fund_id = fi.fund_id
+	                   INNER JOIN mfdb.fund_performance_weekly fp ON fi.fund_id = fp.fund_id
+	                   WHERE mi.isvalid = 1
+	                     AND fmp.isvalid = 1
+	                     AND fi.isvalid = 1
+	                     AND fp.updatetime >= '" + updatetime + "'
+	                     AND fp.price_date <= IFNULL(fmp.management_end_date, CURRENT_DATE)
+	                   GROUP BY mi.personnel_id;";
+		}
      
-        conn = connect_mysql();
+        conn = connect_mysql();;
      
         t = odbc::query(conn, s_query);
      

+ 13 - 15
modules/task_monthlyPerformance.dos

@@ -145,16 +145,14 @@ def CalRelativeRankingTask(entity_type, entity_ids, end_date, isFromMySQL=true)
 /*
  *  计算并存储基金经理和公司月度净值
  * 
- *  @return <TABLE>: [COLUMNS] entity_id, curve_type, strategy, end_date, price_date, ret, nav
+ *  @return <TABLE>: [COLUMNS] entity_id, curve_type, strategy, effective_date, price_date, ret, nav
  * 
  */
-def cal_and_save_mc_nav(entity_type, entity_date, is_save_local) {
+def cal_and_save_mc_monthly_nav(entity_type, entity_date, is_save_local) {
 
 	rt = '';
 
-	if(entity_type == 'PL') s_entity_type = 'manager';
-	else if(entity_type == 'CO') s_entity_type = 'company';
-	else return rt;
+	if( !(entity_type in ['PL', 'CO']) ) return rt;
 
 	if(entity_date.isVoid() || entity_date.size() == 0) return rt;
 
@@ -175,25 +173,25 @@ def cal_and_save_mc_nav(entity_type, entity_date, is_save_local) {
 
     do { // 14 sec
 
-        tb_entity = SELECT * FROM entity_date
+        tb_entity = SELECT entity_id, effective_date FROM entity_date
                     WHERE entity_id IN all_entity_id[i : min(all_entity_id.size(), i+batch_size)];
 
         if(tb_entity.isVoid() || tb_entity.size() == 0) break;
 
 		s_json = tb_entity.toStdJson();
-		t_ret = get_mc_monthly_return(s_entity_type, s_json, 0, 1, true);
+		t_ret = get_mc_average_return(entity_type, 'm', s_json, 0, 1, true);
         
         for(cur in d_curve_type.keys()) {
 
-			tmp = SELECT entity_id, cur AS curve_type, 0 AS strategy, end_date, price_date, ret, incl_cal_cnt 
+			tmp = SELECT entity_id, cur AS curve_type, 0 AS strategy, effective_date, price_date, ret, incl_cal_cnt 
 			      FROM t_ret WHERE raise_type = d_curve_type[cur] AND strategy = -99; // 目前只需要全策略
 
 			// 计算月收益
-        	tb_nav = cal_mc_nav_by_return(entity_type, tmp);
+        	tb_nav = cal_mc_nav_by_return(entity_type, tmp, 'm');
 
 	        INSERT INTO tb_entity_nav 
-	        	SELECT entity_id, curve_type, strategy, end_date, nav, incl_cal_cnt
-	            FROM ej(tb_nav, tmp, ['entity_id', 'curve_type', 'strategy', 'end_date']);
+	        	SELECT entity_id, curve_type, strategy, effective_date AS end_date, nav, incl_cal_cnt
+	            FROM ej(tb_nav, tmp, ['entity_id', 'curve_type', 'strategy', 'effective_date']);
 
         }
 
@@ -350,16 +348,16 @@ def cal_and_save_mc_indicator(entity_type, entity_date, monthly_returns, indicat
 /*
  *  [定时任务]: 基金经理/公司月净值计算
  * 
- *   Example: CalMCNavTask('CO', 2024.11.04);
+ *   Example: CalMCMonthlyNavTask('CO', 2024.12.04);
  */
-def CalMCNavTask(entity_type, updatetime) {
+def CalMCMonthlyNavTask(entity_type, updatetime) {
 //updatetime = 2024.11.05;
 //entity_type = 'CO';
 	if(!(entity_type IN ['PL', 'CO'])) return;
 
 	is_save_local = iif(updatetime <= get_ini_data_const()['updatetime'], true, false);
 
-	// 31 sec  简化起见,不区分curve_type, strategy
+	// 31 sec  简化起见,不区分curve_type, strategy; TODO: 性能能否优化?
 	if(entity_type == 'PL') {
 		entity_date = get_manager_list_by_fund_updatetime(updatetime);
 		entity_date.rename!('manager_id', 'entity_id');
@@ -370,7 +368,7 @@ def CalMCNavTask(entity_type, updatetime) {
 	}
 	
 	// 15 sec
-	cal_and_save_mc_nav(entity_type, entity_date, is_save_local);
+	cal_and_save_mc_monthly_nav(entity_type, entity_date, is_save_local);
 
 	entity_date = null;
 

+ 116 - 0
modules/task_weeklyPerformnce.dos

@@ -5,6 +5,7 @@ use fundit::operationDataPuller;
 use fundit::performanceDataPuller;
 use fundit::indicatorCalculator;
 use fundit::rbsaCalculator;
+use fundit::navCalculator;
 use fundit::bfiMatcher;
 use fundit::dataSaver;
 
@@ -296,3 +297,118 @@ def calEntityBfiIndicatorTask(entityType, date) {
     return rt;
 	
 }
+
+
+/*
+ *  计算并存储基金经理和公司月度净值
+ * 
+ *  @return <TABLE>: [COLUMNS] entity_id, curve_type, strategy, end_date, price_date, ret, nav
+ *  
+ *  NOTE: 基本上和 cal_and_save_mc_monthly_nav 一样
+ * 
+ */
+def cal_and_save_mc_weekly_nav(entity_type, entity_date, is_save_local) {
+
+	rt = '';
+
+	if( !(entity_type in ['PL', 'CO']) ) return rt;
+
+	if(entity_date.isVoid() || entity_date.size() == 0) return rt;
+
+    // 准备类似MySQL结构的数据表
+    tb_entity_nav = create_mc_nav();
+
+    // 暂时与 MySQL 保持一致,只计算公募,私募,公私募综合三条时间序列。未来可细化至公、私募+主策略
+    d_curve_type = dict(INT, INT);
+    d_curve_type[1] = 1; // 私募
+    d_curve_type[4] = 2; // 公募
+    d_curve_type[7] = -99; // 公私募综合
+
+    // 分批跑
+    i = 0;
+    batch_size = 1000;
+
+    all_entity_id = entity_date.entity_id.distinct();
+
+    do { // 14 sec
+
+        tb_entity = SELECT entity_id, effective_date FROM entity_date
+                    WHERE entity_id IN all_entity_id[i : min(all_entity_id.size(), i+batch_size)];
+
+        if(tb_entity.isVoid() || tb_entity.size() == 0) break;
+
+		s_json = tb_entity.toStdJson();
+		t_ret = get_mc_average_return(entity_type, 'w', s_json, 0, 1, true);
+        
+        for(cur in d_curve_type.keys()) {
+
+			tmp = SELECT entity_id, cur AS curve_type, 0 AS strategy, effective_date, price_date, ret, incl_cal_cnt 
+			      FROM t_ret WHERE raise_type = d_curve_type[cur] AND strategy = -99; // 目前只需要全策略
+
+			// 计算周收益
+        	tb_nav = cal_mc_nav_by_return(entity_type, tmp, 'w');
+
+	        INSERT INTO tb_entity_nav 
+	        	SELECT entity_id, curve_type, strategy, effective_date AS year_week, price_date, nav, incl_cal_cnt
+	            FROM ej(tb_nav, tmp, ['entity_id', 'curve_type', 'strategy', 'effective_date']);
+
+        }
+
+        i += batch_size;
+
+    } while (i <= all_entity_id.size());
+
+
+    if(! tb_entity_nav.isVoid() && tb_entity_nav.size() > 0) {
+
+        // save data to MySQL  (12 sec)
+        try {
+
+            tb_entity_nav.rename!('entity_id', iif(entity_type == 'PL', 'manager_id', 'company_id'));
+            save_and_sync(tb_entity_nav, iif(entity_type == 'PL', 'raw_db.manager_nav', 'raw_db.company_nav'), );
+
+            // 数据初始化时将指标存入本地
+            if(is_save_local == true) {
+            	save_table(tb_entity_nav, iif(entity_type == 'PL', 'mfdb.manager_nav', 'mfdb.company_nav'), false);
+            }
+
+        } catch(ex) {
+
+            //TODO: Log errors
+            rt += ex;
+        }
+    }
+
+    return rt;
+	
+}
+
+
+/*
+ *  [定时任务]: 基金经理/公司周净值计算
+ * 
+ *   Example: CalMCWeeklyNavTask('CO', 2024.11.04);
+ */
+def CalMCWeeklyNavTask(entity_type, updatetime) {
+//updatetime = 2024.12.05;
+//entity_type = 'CO';
+	if(!(entity_type IN ['PL', 'CO'])) return;
+
+	is_save_local = iif(updatetime <= get_ini_data_const()['updatetime'], true, false);
+
+	// 60 sec  简化起见,不区分curve_type, strategy; TODO: 性能能否优化?
+	if(entity_type == 'PL') {
+		entity_date = get_manager_list_by_fund_updatetime(updatetime, 'w');
+		entity_date.rename!('manager_id', 'entity_id');
+	}
+	else {
+		entity_date = get_company_list_by_fund_updatetime(updatetime, 'w');
+		entity_date.rename!('company_id', 'entity_id');
+	}
+	
+	// 15 sec
+	cal_and_save_mc_weekly_nav(entity_type, entity_date, is_save_local);
+
+	entity_date = null;
+
+}