123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269 |
- package com.simuwang.manage.service.competition;
- import cn.hutool.core.collection.CollUtil;
- import cn.hutool.core.collection.ListUtil;
- import cn.hutool.core.date.DateUtil;
- import cn.hutool.core.exceptions.ExceptionUtil;
- import cn.hutool.core.map.MapUtil;
- import cn.hutool.core.util.StrUtil;
- import com.simuwang.base.common.conts.DateConst;
- import com.simuwang.base.event.CalcFundRankEventDO;
- import com.simuwang.base.mapper.*;
- import com.simuwang.base.pojo.FundInitAndEndNavDTO;
- import com.simuwang.base.pojo.dos.*;
- import com.simuwang.base.pojo.dto.FundScoreRankDTO;
- import com.smppw.common.pojo.dto.DateValue;
- import com.smppw.utils.BigDecimalUtils;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.springframework.stereotype.Service;
- import java.lang.reflect.Field;
- import java.math.BigDecimal;
- import java.util.*;
- import java.util.stream.Collectors;
- @Service
- public class CalcFundRankService {
- private final static Logger log = LoggerFactory.getLogger(CalcFundRankService.class);
- private final FundIndicatorMapper fundIndicatorMapper;
- private final RcCompetitionApplyMapper competitionApplyMapper;
- private final FundRatingRuleMapper fundRatingRuleMapper;
- private final FundInfoMapper fundInfoMapper;
- private final RcCompetitionResultMapper rcCompetitionResultMapper;
- public CalcFundRankService(FundIndicatorMapper fundIndicatorMapper, RcCompetitionApplyMapper competitionApplyMapper,
- FundRatingRuleMapper fundRatingRuleMapper, FundInfoMapper fundInfoMapper,
- RcCompetitionResultMapper rcCompetitionResultMapper) {
- this.fundIndicatorMapper = fundIndicatorMapper;
- this.competitionApplyMapper = competitionApplyMapper;
- this.fundRatingRuleMapper = fundRatingRuleMapper;
- this.fundInfoMapper = fundInfoMapper;
- this.rcCompetitionResultMapper = rcCompetitionResultMapper;
- }
- public void calFundRank(CalcFundRankEventDO calcFundRankEventDO) {
- Integer competitionId = calcFundRankEventDO.getCompetitionId();
- String period = calcFundRankEventDO.getPeriod();
- List<FundIndicatorDO> fundIndicatorList = fundIndicatorMapper.queryByPeriod(competitionId, period);
- if (CollUtil.isEmpty(fundIndicatorList)) {
- return;
- }
- // 基金策略
- List<RcCompetitionApplyDO> competitionApplyDOList = competitionApplyMapper.queryByCompetitionId(competitionId);
- Map<String, Integer> fundIdStrategyMap = competitionApplyDOList.stream().collect(Collectors.toMap(RcCompetitionApplyDO::getFundId, RcCompetitionApplyDO::getStrategy));
- // 排名规则
- List<FundRatingRuleDO> ratingRuleDOList = fundRatingRuleMapper.queryByCompetitionId(competitionId);
- Map<Integer, List<FundRatingRuleDO>> strategyRatingRuleMap = ratingRuleDOList.stream().collect(Collectors.groupingBy(FundRatingRuleDO::getStrategyId));
- // 基金按照策略进行分组
- Map<Integer, List<FundIndicatorDO>> strategyIndicatorMap = fundIndicatorList.stream().collect(Collectors.groupingBy(k -> fundIdStrategyMap.get(k.getFundId())));
- List<FundScoreRankDTO> fundScoreRankDTOList = CollUtil.newArrayList();
- for (Map.Entry<Integer, List<FundIndicatorDO>> strategyIndicatorEntry : strategyIndicatorMap.entrySet()) {
- try {
- List<FundIndicatorDO> indicatorDOList = strategyIndicatorEntry.getValue();
- Integer strategyId = strategyIndicatorEntry.getKey();
- List<FundRatingRuleDO> ruleDOList = strategyRatingRuleMap.get(strategyId);
- // 计算综合得分和排名
- List<FundScoreRankDTO> scoreRankDTOList = calculateScoreAndRank(indicatorDOList, ruleDOList);
- scoreRankDTOList.forEach(e -> e.setStrategyId(strategyId));
- fundScoreRankDTOList.addAll(scoreRankDTOList);
- } catch (Exception e) {
- log.error("计算综合得分和排名出错 -> 堆栈信息:{}", ExceptionUtil.stacktraceToString(e));
- }
- }
- if (CollUtil.isEmpty(fundScoreRankDTOList)) {
- log.info("基金综合得分和排名为空,无需绘制榜单结果表");
- return;
- }
- // 绘制榜单结果表
- try {
- saveCompetitionResult(competitionId, period, fundIndicatorList, fundScoreRankDTOList, competitionApplyDOList, calcFundRankEventDO.getFundIdNavMap());
- } catch (Exception e) {
- log.error("绘制榜单结果表出错 -> 堆栈信息:{}", ExceptionUtil.stacktraceToString(e));
- }
- }
- private void saveCompetitionResult(Integer competitionId, String period, List<FundIndicatorDO> fundIndicatorList, List<FundScoreRankDTO> fundScoreRankDTOList,
- List<RcCompetitionApplyDO> competitionApplyDOList, Map<String, List<DateValue>> fundIdNavMap) {
- // 基金信息和机构信息
- List<FundAndCompanyInfoDO> fundCompanyInfoDOList = fundInfoMapper.queryFundAndTrustByFundId(competitionId);
- Map<String, FundAndCompanyInfoDO> fundIdInfoMap = fundCompanyInfoDOList.stream().collect(Collectors.toMap(FundAndCompanyInfoDO::getFundId, v -> v));
- // 基金的期初期末净值信息
- Map<String, FundInitAndEndNavDTO> fundIdInitAndEndNavMap = getFundInitAndEndNav(fundIdNavMap);
- // 榜单数据
- Map<String, FundScoreRankDTO> fundIdScoreRankMap = fundScoreRankDTOList.stream().collect(Collectors.toMap(FundScoreRankDTO::getFundId, v -> v));
- Map<String, FundIndicatorDO> fundIdIndicatorMap = fundIndicatorList.stream().collect(Collectors.toMap(FundIndicatorDO::getFundId, v -> v));
- List<RcCompetitionResultDO> competitionResultDOList = convertToCompetitionResultDO(competitionId, period, fundIdIndicatorMap, fundIdInfoMap, fundIdInitAndEndNavMap, fundIdScoreRankMap);
- if (CollUtil.isNotEmpty(competitionResultDOList)) {
- List<List<RcCompetitionResultDO>> partition = ListUtil.partition(competitionResultDOList, 500);
- partition.forEach(rcCompetitionResultMapper::batchInsertOrUpdate);
- }
- }
- private List<FundScoreRankDTO> calculateScoreAndRank(List<FundIndicatorDO> indicatorDOList, List<FundRatingRuleDO> ruleDOList) {
- List<FundScoreRankDTO> scoreRankDTOList = CollUtil.newArrayList();
- if (CollUtil.isEmpty(indicatorDOList) || CollUtil.isEmpty(ruleDOList)) {
- return scoreRankDTOList;
- }
- // 1.按照规则计算综合得分
- Map<String, BigDecimal> fundIdScoreMap = MapUtil.newHashMap(indicatorDOList.size());
- List<Map<String, String>> indicatorMapList = indicatorDOList.stream().map(this::convertToMap).toList();
- for (FundRatingRuleDO ratingRuleDo : ruleDOList) {
- String indicatorId = ratingRuleDo.getIndicatorId();
- BigDecimal weight = ratingRuleDo.getWeight();
- Integer ratingType = ratingRuleDo.getRatingType();
- // 参与评分的指标总数
- int total = (int) indicatorMapList.stream().map(e -> e.get(indicatorId)).filter(StrUtil::isNotBlank).count();
- if (total == 0) {
- continue;
- }
- for (Map<String, String> indicatorMap : indicatorMapList) {
- String fundId = indicatorMap.get("fundId");
- String indicatorValue = indicatorMap.get(indicatorId);
- if (StrUtil.isBlank(indicatorValue)) {
- continue;
- }
- // 计算单个指标排名(从小到大 或 从大到小 排名)
- int rank;
- if (ratingType == 1) {
- rank = (int) indicatorMapList.stream().map(e -> e.get(indicatorId))
- .filter(StrUtil::isNotBlank).map(Double::valueOf).filter(e -> e.compareTo(Double.valueOf(indicatorValue)) > 0).count() + 1;
- } else {
- rank = (int) indicatorMapList.stream().map(e -> e.get(indicatorId))
- .filter(StrUtil::isNotBlank).map(Double::valueOf).filter(e -> e.compareTo(Double.valueOf(indicatorValue)) < 0).count() + 1;
- }
- // 排名得分 = [100-100*(绝对排名-1)/(总数-1)] * 权重
- double indicatorRank = total == 1 ? 100 : 100 - (100.0 * (rank - 1) / (total - 1));
- BigDecimal score = BigDecimalUtils.multiply(BigDecimal.valueOf(indicatorRank), weight);
- if (fundIdScoreMap.containsKey(fundId)) {
- score = BigDecimalUtils.add(fundIdScoreMap.get(fundId), score);
- }
- fundIdScoreMap.put(fundId, score);
- }
- }
- // 2.计算排名
- List<Map.Entry<String, BigDecimal>> list = new ArrayList<>(fundIdScoreMap.entrySet());
- // 按照BigDecimal值降序排序
- list.sort((o1, o2) -> o2.getValue().compareTo(o1.getValue()));
- int rank = 1;
- for (Map.Entry<String, BigDecimal> entry : list) {
- FundScoreRankDTO dto = new FundScoreRankDTO(entry.getKey(), null, entry.getValue(), rank++);
- scoreRankDTOList.add(dto);
- }
- return scoreRankDTOList;
- }
- private Map<String, FundInitAndEndNavDTO> getFundInitAndEndNav(Map<String, List<DateValue>> fundIdNavMap) {
- Map<String, FundInitAndEndNavDTO> fundIdInitAndEndNavMap = MapUtil.newHashMap();
- for (Map.Entry<String, List<DateValue>> fundIdNavEntry : fundIdNavMap.entrySet()) {
- String fundId = fundIdNavEntry.getKey();
- List<DateValue> dateValueList = fundIdNavEntry.getValue();
- if (CollUtil.isEmpty(dateValueList) || dateValueList.size() == 1) {
- continue;
- }
- FundInitAndEndNavDTO initAndEndNavDTO = new FundInitAndEndNavDTO();
- initAndEndNavDTO.setFundId(fundId);
- DateValue maxNav = dateValueList.stream().max(Comparator.comparing(DateValue::getDate)).orElse(null);
- DateValue minNav = dateValueList.stream().min(Comparator.comparing(DateValue::getDate)).orElse(null);
- if (minNav != null) {
- initAndEndNavDTO.setStartDate(minNav.getDate());
- initAndEndNavDTO.setStartCumulativeNavWithdrawal(minNav.getCumulativeNavWithdrawal());
- }
- if (maxNav != null) {
- initAndEndNavDTO.setEndDate(maxNav.getDate());
- initAndEndNavDTO.setEndCumulativeNavWithdrawal(maxNav.getCumulativeNavWithdrawal());
- }
- fundIdInitAndEndNavMap.put(fundId, initAndEndNavDTO);
- }
- return fundIdInitAndEndNavMap;
- }
- private List<RcCompetitionResultDO> convertToCompetitionResultDO(Integer competitionId, String period, Map<String, FundIndicatorDO> fundIdIndicatorMap, Map<String, FundAndCompanyInfoDO> fundIdInfoMap,
- Map<String, FundInitAndEndNavDTO> fundIdInitAndEndNavMap, Map<String, FundScoreRankDTO> fundIdScoreRankMap) {
- List<RcCompetitionResultDO> competitionResultDOList = CollUtil.newArrayList();
- for (Map.Entry<String, FundAndCompanyInfoDO> fundCompanyInfoEntry : fundIdInfoMap.entrySet()) {
- String fundId = fundCompanyInfoEntry.getKey();
- RcCompetitionResultDO resultDO = new RcCompetitionResultDO();
- resultDO.setCompetitionId(competitionId);
- resultDO.setPriod(period);
- FundAndCompanyInfoDO fundAndCompanyInfoDO = fundCompanyInfoEntry.getValue();
- resultDO.setFundId(fundId);
- resultDO.setStrategy(fundAndCompanyInfoDO.getStrategyId());
- resultDO.setFundRegisterNumber(fundAndCompanyInfoDO.getRegisterNumber());
- resultDO.setFundName(fundAndCompanyInfoDO.getFundName());
- resultDO.setFundShortName(fundAndCompanyInfoDO.getFundShortName());
- resultDO.setCompanyId(fundAndCompanyInfoDO.getCompanyId());
- resultDO.setCompanyName(fundAndCompanyInfoDO.getCompanyName());
- resultDO.setCompanyShortName(fundAndCompanyInfoDO.getCompanyShortName());
- resultDO.setCompanyRegisterNumber(fundAndCompanyInfoDO.getCompanyRegisterNumber());
- resultDO.setCompanyScale(fundAndCompanyInfoDO.getCompanyScale());
- resultDO.setIsOpenAccount(fundAndCompanyInfoDO.getIsOpenAccount());
- resultDO.setIsOpenAccount2(fundAndCompanyInfoDO.getIsOpenAccount2());
- FundScoreRankDTO fundScoreRankDTO = fundIdScoreRankMap.get(fundId);
- if (fundScoreRankDTO != null) {
- resultDO.setRank(fundScoreRankDTO.getRank());
- resultDO.setScore(fundScoreRankDTO.getScore());
- } else {
- resultDO.setRank(999999);
- resultDO.setScore(null);
- }
- FundInitAndEndNavDTO initAndEndNavDTO = fundIdInitAndEndNavMap.get(fundId);
- if (initAndEndNavDTO != null) {
- resultDO.setPrePriceDate(StrUtil.isNotBlank(initAndEndNavDTO.getStartDate()) ? DateUtil.parse(initAndEndNavDTO.getStartDate(), DateConst.YYYY_MM_DD) : null);
- resultDO.setPreCumulativeNavWithdrawal(initAndEndNavDTO.getStartCumulativeNavWithdrawal());
- resultDO.setPriceDate(StrUtil.isNotBlank(initAndEndNavDTO.getEndDate()) ? DateUtil.parse(initAndEndNavDTO.getEndDate(), DateConst.YYYY_MM_DD) : null);
- resultDO.setCumulativeNavWithdrawal(initAndEndNavDTO.getEndCumulativeNavWithdrawal());
- }
- FundIndicatorDO fundIndicatorDO = fundIdIndicatorMap.get(fundId);
- if (fundIndicatorDO != null) {
- resultDO.setRet(fundIndicatorDO.getRet());
- resultDO.setRetA(fundIndicatorDO.getRetA());
- resultDO.setMaxdrawdown(fundIndicatorDO.getMaxdrawdown());
- resultDO.setCalmarratio(fundIndicatorDO.getCalmarratio());
- resultDO.setSortinoratio(fundIndicatorDO.getSortinoratio());
- resultDO.setSharperatio(fundIndicatorDO.getSharperatio());
- resultDO.setAlpha(fundIndicatorDO.getAlpha());
- resultDO.setWinrate(fundIndicatorDO.getWinrate());
- resultDO.setInfoRatio(fundIndicatorDO.getInfoRatio());
- resultDO.setExcessWinrate(fundIndicatorDO.getExcessWinrate());
- resultDO.setExcessRet(fundIndicatorDO.getExcessGeometry());
- resultDO.setExcessMaxdown(fundIndicatorDO.getExcessMaxdrawdown());
- resultDO.setExcessCalmarratio(fundIndicatorDO.getExcessCalmarratio());
- resultDO.setExcessSharperatio(fundIndicatorDO.getExcessSharperatio());
- resultDO.setExcessStddev(fundIndicatorDO.getExcessStddev());
- resultDO.setProductScale(fundIndicatorDO.getProductScale());
- } else {
- resultDO.setRank(999999);
- resultDO.setScore(null);
- }
- resultDO.setIsvalid(1);
- resultDO.setCreateTime(new Date());
- resultDO.setCreatorId(999999);
- resultDO.setUpdaterId(999999);
- resultDO.setUpdateTime(new Date());
- competitionResultDOList.add(resultDO);
- }
- return competitionResultDOList;
- }
- public Map<String, String> convertToMap(FundIndicatorDO indicatorDO) {
- Map<String, String> indicatorMap = MapUtil.newHashMap();
- Field[] fields = FundIndicatorDO.class.getDeclaredFields();
- for (Field field : fields) {
- field.setAccessible(true);
- Object value = null;
- try {
- value = field.get(indicatorDO);
- } catch (Exception e) {
- indicatorMap.put(field.getName(), null);
- }
- indicatorMap.put(field.getName(), value != null ? value.toString() : null);
- }
- return indicatorMap;
- }
- }
|