123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148 |
- module fundit::bfiMatcher
- use fundit::sqlUtilities;
- use fundit::operationDataPuller;
- use fundit::performanceDataPuller;
- /*
- * 返回预设的指标最小值
- *
- */
- def get_min_threshold(data_name) {
- ret = 0;
- if(data_name == 'correlation') { ret = 0.64; }
- else if(data_name == 'ret_count') { ret = 48; }
- return ret;
- }
- /*
- * 取BFI所需要的指数/因子ID
- *
- * NOTE: Java使用的逻辑如下(除了FA),暂时没有差别
- * SELECT a.fund_Id FROM mfdb.fund_performance AS a
- LEFT JOIN pfdb.cm_class_asset_index AS b ON a.fund_id = b.index_id
- WHERE a.end_date = 'YYYY-MM' AND a.fund_id LIKE 'IN%' AND b.isvalid = 1 ORDER BY a.fund_id ASC
- */
- def get_bfi_index_list() {
- return ['FA00000VML','FA00000VMM','FA00000VMN','FA00000VMO','FA00000WKG','FA00000WKH','IN00000008','IN0000000D','IN0000000M','IN0000000S',
- 'IN0000000T','IN0000000U','IN0000000V','IN0000000W','IN0000000X','IN0000000Y','IN0000000Z','IN00000010','IN00000011','IN00000012',
- 'IN00000013','IN00000014','IN00000015','IN00000016','IN00000017','IN00000077','IN00000078','IN00000079','IN0000007A','IN0000007B',
- 'IN0000007C','IN0000007D','IN0000007E','IN0000007F','IN0000007G','IN0000007M','IN0000007N','IN0000007O','IN00000080','IN00000088',
- 'IN0000008O','IN0000009M','IN0000028E','IN000002CM'];
- }
- defg regressionT(y, x) {
- r = SELECT beta, tstat FROM ols(y, x, true, 1) WHERE rowNo(beta) = 1;
- return r[0]['tstat'];
- }
- /*
- * 计算 bfi-matching 所需要的数据指标(月度)
- *
- *
- */
- def cal_monthly_closity(ret1, ret2, win) {
- t0 = SELECT end_date.month() AS end_date, end_date as price_date, ret1.ret AS ret1, ret2.ret AS ret2, tmoving(count, end_date, end_date, win) AS ret_count
- FROM ret1
- INNER JOIN ret2 ON ret1.end_date = ret2.end_date
- ORDER BY end_date;
- t = SELECT end_date, price_date,
- tmcorr(t0.end_date, ret1, ret2, win) AS corr,
- iif(tmstd(end_date, ret1-ret2, win) == 0, null, tmavg(end_date, ret1-ret2, win)\tmstd(end_date, ret1-ret2, win)) AS info,
- tmoving(regressionT, end_date, [ret1, ret2], win) AS t_value,
- tmbeta(end_date, ret1, ret2, win) AS beta // 用 ols() 算的值和这个一样
- FROM t0
- WHERE ret_count >= get_min_threshold('ret_count')
- ORDER BY end_date;
- UPDATE t SET corr = NULL WHERE corr < get_min_threshold('correlation');
- return SELECT end_date.month().last() AS end_date,
- corr.last() AS corr,
- info.last() * sqrt(get_annulization_multiple('w')) AS info, // annuulized info ratio
- t_value.last() AS t_value,
- beta.last() AS beta
- FROM t
- GROUP BY end_date.month();
- }
- /*
- * 计算目标和BFI所用指数因子的相关系数
- *
- * @param entity_info <TABLE>: NEED COLUMNS entity_id, inception_date, price_date
- *
- * TODO: correlation is OK; beta, info, t_value are way off!
- *
- * NOTE: 与Java把月末日期作为截止日期不同的是,这里用每月最后一个周五作为截止日,所以数值会与MySQL中存储的略为不同
- *
- */
- def cal_entity_index_coe(entity_type, entity_info) {
- // entity_info = get_fund_info(['MF00003PW1', 'MF00003PWC']).join(take(2024.10.31, 2) AS price_date).rename!('fund_id', 'entity_id');
- if(entity_info.isVoid() || entity_info.size() == 0) return null;
- // 简单起见,取数据集中最新日期作为截止日期
- end_day = entity_info.price_date.max();
- ret_entity = get_entity_weekly_rets(entity_type, entity_info);
- if(ret_entity.isVoid() || ret_entity.size() == 0) return null;
- // 取BFI用得到的指数/因子列表
- v_indexes = get_bfi_index_list();
- // 手搓一个带日期的数据表
- index_info = table(v_indexes AS entity_id, take(end_day, v_indexes.size()) AS price_date);
- ret_index = get_entity_weekly_rets('MI', index_info);
- if(ret_index.isVoid() || ret_index.size() == 0) return null;
- // 两次循环遍历所有entity和指数
- entity_coe = table(1000:0, ['entity_id', 'index_id', 'end_date', 'coe_1y', 'coe_3y', 'coe_5y', 'info_ratio_1y', 'info_ratio_3y', 'info_ratio_5y',
- 't_value_1y', 't_value_3y', 't_value_5y', 'beta_1y', 'beta_3y', 'beta_5y'],
- [SYMBOL, SYMBOL, MONTH, DOUBLE, DOUBLE, DOUBLE, DOUBLE, DOUBLE, DOUBLE,
- DOUBLE, DOUBLE, DOUBLE, DOUBLE, DOUBLE, DOUBLE]);
- for(entity in entity_info.entity_id) {
- ret1 = SELECT fund_id AS entity_id, price_date.weekEnd(4) AS end_date, price_date.weekEnd(4) AS price_date, ret_1w AS ret
- FROM ret_entity WHERE fund_id = entity AND price_date.weekEnd(4) <= end_day;
-
- for(index in v_indexes) {
- ret2 = SELECT index_id AS benchmark_id, price_date.weekEnd(4) AS end_date, price_date.weekEnd(4) AS price_date, ret_1w AS ret
- FROM ret_index WHERE index_id = index AND price_date.weekEnd(4) <= end_day;
- benchmarks = table(take(entity, ret1.size()) AS entity_id, take(index, ret1.size()) AS benchmark_id, ret1.price_date.weekEnd(4) AS end_date);
-
- closity_1y = cal_monthly_closity(ret1, ret2, 1y);
- closity_3y = cal_monthly_closity(ret1, ret2, 3y);
- closity_5y = cal_monthly_closity(ret1, ret2, 5y);
- INSERT INTO entity_coe
- SELECT entity, index, c1.end_date, c1.corr AS coe_1y, c3.corr AS coe_3y, c5.corr AS coe_5y,
- c1.info AS info_ratio_1y, c3.info AS info_ratio_3y, c5.info AS info_ratio_5y,
- c1.t_value AS t_value_1y, c3.t_value AS t_value_3y, c5.t_value AS t_value_5y,
- c1.beta AS beta_1y, c3.beta AS beta_3y, c5.beta AS beta_5y
- FROM closity_1y c1
- LEFT JOIN closity_3y c3 ON c1.end_date = c3.end_date
- LEFT JOIN closity_5y c5 ON c1.end_date = c5.end_date;
- }
- }
- return entity_coe;
- }
|