Procházet zdrojové kódy

上传文件至 'modules'

Joey před 6 měsíci
rodič
revize
2139f19a16
1 změnil soubory, kde provedl 58 přidání a 19 odebrání
  1. 58 19
      modules/returnCalculator.dos

+ 58 - 19
modules/returnCalculator.dos

@@ -3,36 +3,31 @@ module fundit::returnCalculator
 use fundit::fundCalculator
 use fundit::dataPuller
 
+
 /*
  *  通用月收益计算
  * 
- *  @param entity_type <STRING>: MF, HF, EQ, CF, MI, TI, CI, FA, PF
  *  @param entity_info <TABLE>: COLUMN NEED entity_id, price_date, inception_date, ini_value
+ *  @param nav <TABLE>: COLUMN NEED entity_id, price_date, cumulative_nav
  * 
  */
-def cal_entity_monthly_returns(entity_type, entity_info) {
+def cal_monthly_returns_by_nav(entity_info, mutable nav) {
 
     tb_rets = null;
 
-    // 取涉及到的所有基金证券最早持仓日期
-    s_json = (SELECT entity_id AS sec_id, price_date.min() AS price_date FROM entity_info GROUP BY entity_id).toStdJson();
-
-	// 取涉及到的所有基金证券有用净值(前值+某日期后所有净值)
-    // TODO: need consider inception date nav
-    tb_nav = get_nav_for_return_calculation(entity_type, 'm', s_json);
+    if(nav.isVoid() || nav.size() == 0 || entity_info.isVoid() || entity_info.size() == 0 ) return null;
 
+    // 所有月末日期和净值
+    tb_monthly_nav = SELECT entity_id, price_date.month().last() AS end_date, price_date.last() AS price_date, cumulative_nav.last() AS cumulative_nav
+                     FROM nav.sortBy!(['entity_id', 'price_date'], [1, 1])
+                     GROUP BY entity_id, price_date.month();
 
-    // 填充好各基金有效期内所有月份的最后一天
-    tb_monthly_nav = SELECT sec_id, price_date.month().last() AS end_date, price_date.last() AS price_date, cumulative_nav.last() AS cumulative_nav
-                     FROM tb_nav.sortBy!(['sec_id', 'price_date'], [1, 1])
-                     GROUP BY sec_id, price_date.month();
-
-    // 删掉成立日之前的净值
-    tb_monthly_nav = SELECT sec_id AS entity_id, end_date, price_date, cumulative_nav 
-                     FROM tb_monthly_nav n // ej(tb_monthly_nav, entity_info, 'sec_id', 'entity_id')
-                     INNER JOIN entity_info ei ON n.sec_id = ei.entity_id
+    // 筛掉成立日之前的净值
+    tb_monthly_nav = SELECT n.entity_id, end_date, price_date, cumulative_nav 
+                     FROM tb_monthly_nav n
+                     INNER JOIN entity_info ei ON n.entity_id = ei.entity_id
                      WHERE n.price_date >= ei.inception_date
-                     ORDER BY n.sec_id, n.end_date, n.price_date;
+                     ORDER BY n.entity_id, n.end_date, n.price_date;
 
     if(tb_monthly_nav.isVoid() || tb_monthly_nav.size() == 0) { return tb_rets; }
 
@@ -41,12 +36,56 @@ def cal_entity_monthly_returns(entity_type, entity_info) {
               FROM tb_monthly_nav
               CONTEXT BY entity_id;
 
-
     // the records without return calculated but do have nav are still useful for some calculations (e.g. max drawdown)
     return ( SELECT * FROM tb_rets WHERE cumulative_nav > 0 );
 }
 
 /*
+ *  根据最新更新的净值计算收益,并与数据库历史收益合并为完整收益, 数据源是MySQL
+ *  
+ *  @param entity_type <STRING>:
+ *  @param entity_info <TABLE>: COLUMN NEED entity_id, inception_date, benchmark_id, ini_value, price_date
+ *  @param start_date <DATETIME>: 净值(datasource='nav')、收益起始日(datasource='perf')、净值更新的最新日期(datasource='mix')
+ *  @param data_source <STRING>: nav(nav table), perf(xxx_performance table), mix(new updated nav + old performance table ret_1m)
+ *  
+ * 
+ */
+def mix_monthly_returns(entity_type, entity_info) {
+
+    ret = null;
+
+    very_old_day = 1990.01.01;
+
+    end_day = today();
+
+    s_json = (SELECT entity_id AS sec_id, price_date FROM entity_info).toStdJson();
+            
+     // 取基金组合在包括各自的某净值日期的前值及之后的所有净值
+     tb_nav = get_nav_for_return_calculation(entity_type, 'm', s_json);
+     tb_nav.rename!('sec_id', 'entity_id');
+
+     // 计算某净值日期所在月份及之后的所有月收益
+     ret = cal_monthly_returns_by_nav(entity_info, tb_nav);
+
+    // 筛掉引入的前值,这些记录用来计算第一期收益后就不再有用
+    // 不知道为什么 delete ret from ej(ret, tb_entities, 'entity_id') where ret.price_date < tb_entities.price_date 会报错
+    ret = SELECT ret.* FROM ej(ret, entity_info, 'entity_id')
+                       WHERE ret.price_date >= entity_info.price_date;
+
+    // 取数据库中的所有历史收益
+    historical_rets = get_monthly_ret(entity_type, entity_info.entity_id, very_old_day, end_day, true);
+
+    // MIX 将新NAV计算的收益和数据库中的历史收益合并,相同月份时用新计算的收益代替
+    INSERT INTO ret
+        SELECT entity_id, end_date.temporalParse('yyyy-MM'), price_date, nav, ret
+        FROM historical_rets h
+        WHERE NOT EXISTS ( SELECT * FROM ret WHERE entity_id = historical_rets.entity_id AND end_date = historical_rets.end_date.temporalParse('yyyy-MM') );
+
+    return SELECT * FROM ret ORDER BY entity_id, end_date, price_date;
+}
+
+
+/*
  *  根据基金净值序列计算月收益序列(适合提供给指标运算)
  * 
  *  Create:  20240907                                                  Joey