|
@@ -52,6 +52,66 @@ def convert_transaction_to_snapshot(portfolio_ids, end_day) {
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
+ * 根据持仓收益计算组合净值
|
|
|
+ *
|
|
|
+ * @param entity_cal_dates <TABLE>: 组合净值计算时间区间表,记录 [COLUMNS] entity_id, first_cal_date, latest_cal_date
|
|
|
+ * @parm holdings <TABLE>:带有各证券净值前值的截面持仓表 [COLUMNS] entity_id, price_date, sec_id, ret, weight
|
|
|
+ *
|
|
|
+ * @return <TABLE>: [COLUMNS] entity_id, price_date, ret
|
|
|
+ */
|
|
|
+def cal_nav_by_return(entity_type, entity_cal_dates, holdings) {
|
|
|
+// entity_type = 'FA'
|
|
|
+// entity_cal_dates=t_factor
|
|
|
+// holdings = t
|
|
|
+
|
|
|
+ // 组合收益计算: RET = ∑( weight_i * ret_i )
|
|
|
+ tb_portfolio_ret = SELECT entity_id, price_date, (weight * ret).sum() AS ret
|
|
|
+ FROM holdings
|
|
|
+ GROUP BY entity_id, price_date;
|
|
|
+
|
|
|
+ // 取组合净值前值
|
|
|
+ s_json = (SELECT entity_id, price_date.max() AS price_date
|
|
|
+ FROM ej(tb_portfolio_ret, entity_cal_dates, 'entity_id')
|
|
|
+ WHERE tb_portfolio_ret.price_date < entity_cal_dates.first_cal_date
|
|
|
+ GROUP BY entity_id).toStdJson();
|
|
|
+
|
|
|
+ tb_pre_nav = get_entity_nav_by_date(entity_type, s_json, true);
|
|
|
+
|
|
|
+ INSERT INTO tb_pre_nav
|
|
|
+ SELECT entity_id, first_cal_date, NULL
|
|
|
+ FROM entity_cal_dates
|
|
|
+ WHERE NOT exists( SELECT * FROM tb_pre_nav WHERE tb_pre_nav.entity_id = entity_cal_dates.entity_id);
|
|
|
+
|
|
|
+ tb_portfolio_ret.addColumn('nav', DOUBLE);
|
|
|
+
|
|
|
+ // start_cal_date 是最早净值日期
|
|
|
+ UPDATE tb_portfolio_ret
|
|
|
+ SET nav = 1, ret = 0
|
|
|
+ FROM ej(tb_portfolio_ret, ej(entity_cal_dates, tb_pre_nav, 'entity_id'), ['entity_id', 'price_date'], ['entity_id', 'first_cal_date'])
|
|
|
+ WHERE tb_pre_nav.cumulative_nav IS NULL;
|
|
|
+
|
|
|
+ // start_cal_date 是最早净值日期,用它作为初始净值日期
|
|
|
+ UPDATE tb_pre_nav
|
|
|
+ SET price_date = first_cal_date, cumulative_nav = 1
|
|
|
+ FROM ej(tb_pre_nav, entity_cal_dates, 'entity_id')
|
|
|
+ WHERE cumulative_nav IS NULL;
|
|
|
+
|
|
|
+ tb_portfolio_ret.sortBy!(['entity_id', 'price_date'], [1, 1]);
|
|
|
+
|
|
|
+ // 通过收益反算净值: nav_i = nav_0 * ∏(1 + ret_i)
|
|
|
+ UPDATE tb_portfolio_ret
|
|
|
+ SET nav = (tb_pre_nav.cumulative_nav * (1+ret).cumprod()).round(6)
|
|
|
+ FROM ej(tb_portfolio_ret, tb_pre_nav, 'entity_id')
|
|
|
+ CONTEXT BY entity_id;
|
|
|
+
|
|
|
+ // 返回有用的数据
|
|
|
+ return (SELECT DISTINCT tb_portfolio_ret.*
|
|
|
+ FROM ej(tb_portfolio_ret, entity_cal_dates, 'entity_id')
|
|
|
+ WHERE price_date >= first_cal_date AND price_date <= latest_cal_date
|
|
|
+ ORDER BY entity_id, price_date);
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
* 计算FOF类组合净值
|
|
|
* NOTE: 与MySQL逻辑一致,用户界面输入的交易净值会被暂时忽略,因为我们无法确保同一基金同一时间被输入的净值是相同的;
|
|
|
* 忽略手工净值会导致收益不精确或无法计算的问题,但可能错误的净值将导致错误的结果,两害取其轻。
|
|
@@ -143,7 +203,7 @@ def convert_transaction_to_snapshot(portfolio_ids, end_day) {
|
|
|
tb_holdings.addColumn(['ret', 'shares', 'market_value', 'total_mkt_value', 'weight'], [DOUBLE, DOUBLE, DOUBLE, DOUBLE, DOUBLE]);
|
|
|
|
|
|
// 计算各持仓证券收益
|
|
|
- UPDATE tb_holdings SET ret = (cumulative_nav.ratios()-1).round(7)
|
|
|
+ UPDATE tb_holdings SET ret = cumulative_nav.ratios()-1
|
|
|
CONTEXT BY portfolio_id, sec_id;
|
|
|
|
|
|
// 把交易日截面的份额数用于组合收益表
|
|
@@ -175,49 +235,15 @@ def convert_transaction_to_snapshot(portfolio_ids, end_day) {
|
|
|
SET total_mkt_value = market_value.sum()
|
|
|
CONTEXT BY portfolio_id, price_date;
|
|
|
|
|
|
-
|
|
|
// 计算各持仓的权重
|
|
|
UPDATE tb_holdings
|
|
|
SET weight = (market_value \ total_mkt_value).round(6)
|
|
|
WHERE total_mkt_value <> 0;
|
|
|
|
|
|
- // 组合收益计算: RET = ∑( weight_i * ret_i )
|
|
|
- tb_portfolio_ret = SELECT portfolio_id, price_date, (weight * ret).sum().round(7) AS ret
|
|
|
- FROM tb_holdings
|
|
|
- GROUP BY portfolio_id, price_date;
|
|
|
-
|
|
|
- // 取组合净值前值
|
|
|
- s_json = (SELECT portfolio_id, price_date.max() AS price_date
|
|
|
- FROM ej(tb_portfolio_ret, tb_port_first_cal_date, 'portfolio_id')
|
|
|
- WHERE tb_portfolio_ret.price_date < tb_port_first_cal_date.first_cal_date
|
|
|
- GROUP BY portfolio_id).toStdJson();
|
|
|
- tb_pre_nav = get_portfolio_nav_by_date(s_json, true);
|
|
|
-
|
|
|
- tb_portfolio_ret.addColumn('nav', DOUBLE);
|
|
|
-
|
|
|
- // start_cal_date 是最早净值日期
|
|
|
- UPDATE tb_portfolio_ret
|
|
|
- SET nav = 1, ret = 0
|
|
|
- FROM ej(tb_portfolio_ret, ej(tb_port_first_cal_date, tb_pre_nav, 'portfolio_id'), ['portfolio_id', 'price_date'], ['portfolio_id', 'first_cal_date'])
|
|
|
- WHERE tb_pre_nav.cumulative_nav IS NULL;
|
|
|
-
|
|
|
- // start_cal_date 是最早净值日期,用它作为初始净值日期
|
|
|
- UPDATE tb_pre_nav
|
|
|
- SET price_date = first_cal_date, cumulative_nav = 1
|
|
|
- FROM ej(tb_pre_nav, tb_port_first_cal_date, 'portfolio_id')
|
|
|
- WHERE cumulative_nav IS NULL;
|
|
|
+ // 通过持仓收益反算组合收益,再计算组合净值
|
|
|
+ tb_port_first_cal_date.rename!('portfolio_id', 'entity_id');
|
|
|
+ tb_holdings.rename!('portfolio_id', 'entity_id');
|
|
|
|
|
|
- tb_portfolio_ret.sortBy!(['portfolio_id', 'price_date'], [1, 1]);
|
|
|
+ return cal_nav_by_return('PF', tb_port_first_cal_date, tb_holdings);
|
|
|
|
|
|
- // 通过收益反算净值: nav_i = nav_0 * ∏(1 + ret_i)
|
|
|
- UPDATE tb_portfolio_ret
|
|
|
- SET nav = (tb_pre_nav.cumulative_nav * (1+ret).cumprod()).round(6)
|
|
|
- FROM ej(tb_portfolio_ret, tb_pre_nav, 'portfolio_id')
|
|
|
- CONTEXT BY portfolio_id;
|
|
|
-
|
|
|
- // 返回有用的数据
|
|
|
- return (SELECT DISTINCT tb_portfolio_ret.*
|
|
|
- FROM ej(tb_portfolio_ret, tb_port_first_cal_date, 'portfolio_id')
|
|
|
- WHERE price_date >= first_cal_date AND price_date <= latest_cal_date
|
|
|
- ORDER BY portfolio_id, price_date);
|
|
|
}
|