|
@@ -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
|