浏览代码

feat:获取公募基金的基金经理变更历史的实现

mozuwen 1 年之前
父节点
当前提交
03c9019593

+ 23 - 0
src/main/java/com/smppw/analysis/application/dto/info/ManualFundManagerParams.java

@@ -0,0 +1,23 @@
+package com.smppw.analysis.application.dto.info;
+
+import lombok.Data;
+
+/**
+ * @author mozuwen
+ * @date 2023/8/9 17:00
+ * @description 公募基金的基金经理变更历史请求参数
+ */
+@Data
+public class ManualFundManagerParams {
+
+    /**
+     * 基金id
+     */
+    private String refId;
+
+    /**
+     * 基准
+     */
+    private String benchmarkId;
+
+}

+ 95 - 0
src/main/java/com/smppw/analysis/application/service/info/FundInfoService.java

@@ -3,6 +3,7 @@ package com.smppw.analysis.application.service.info;
 import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.collection.ListUtil;
+import cn.hutool.core.date.DateUnit;
 import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.map.MapUtil;
 import cn.hutool.core.text.CharSequenceUtil;
@@ -23,9 +24,15 @@ import com.smppw.common.pojo.IStrategy;
 import com.smppw.common.pojo.NewSubStrategy;
 import com.smppw.common.pojo.ValueLabelVO;
 import com.smppw.common.pojo.dto.DateValue;
+import com.smppw.common.pojo.dto.calc.IndicatorCalcIndexDataDto;
 import com.smppw.common.pojo.dto.calc.IndicatorCalcPropertyDto;
+import com.smppw.common.pojo.dto.calc.IndicatorCalcSecDataDto;
 import com.smppw.common.pojo.dto.calc.IndicatorCalcTimeRangeDto;
+import com.smppw.common.pojo.dto.indicator.CalcMultipleSecMultipleTimeRangeIndicatorReq;
+import com.smppw.common.pojo.dto.indicator.DateIntervalDto;
 import com.smppw.common.pojo.enums.*;
+import com.smppw.common.pojo.enums.strategy.Strategy;
+import com.smppw.constants.Consts;
 import com.smppw.constants.DateConst;
 import com.smppw.core.IndicatorService;
 import com.smppw.utils.BigDecimalUtils;
@@ -419,4 +426,92 @@ public class FundInfoService {
         return resultList;
     }
 
+    public List<ManualFundManagerChangeVO> managerChangeList(ManualFundManagerParams params) {
+        String refId = params.getRefId();
+        String benchmarkId = params.getBenchmarkId();
+        List<ManualFundManagerChangeDo> dataList = this.baseInfoService.listFundManagerChangeHistory(refId);
+
+        dataList = dataList.stream().filter(e -> e.getStartDate() != null).collect(Collectors.toList());
+        // 按基金经理+开始任职时间分组每个经理只取一条数据
+        Map<String, ManualFundManagerChangeDo> managerInfoMap = MapUtil.newHashMap();
+        Map<String, List<ManualFundManagerChangeDo>> collect = dataList.stream().collect(Collectors.groupingBy(e -> e.getManagerId() + e.getStartDate(), Collectors.toList()));
+        collect.forEach((k ,v) -> {
+            ManualFundManagerChangeDo temp = v.stream().findFirst().orElse(null);
+            managerInfoMap.put(k, temp);
+        });
+
+        List<ManualFundManagerChangeVO> resultList = ListUtil.list(true);
+        List<Indicator> indicatorList = ListUtil.of(Indicator.IntervalReturn, Indicator.AnnualReturn, Indicator.MaxDrawdown,
+                Indicator.SortinoRatio, Indicator.CalmarRatio);
+        managerInfoMap.forEach((k, data) -> {
+            ManualFundManagerChangeVO vo = new ManualFundManagerChangeVO();
+            vo.setManagerId(data.getManagerId());
+            vo.setManagerName(data.getManagerName());
+            // 任职日期和天数计算
+            vo.setEmployDate(data.getStartDate() + "~" + (CharSequenceUtil.isBlank(data.getEndDate()) ? "至今" : data.getEndDate()));
+            Date date = new Date();
+            if (CharSequenceUtil.isNotBlank(data.getEndDate())) {
+                date = DateUtil.parseDate(data.getEndDate());
+            }
+            vo.setEmployDayNum(DateUtil.between(DateUtil.parseDate(data.getStartDate()), date, DateUnit.DAY) + "");
+            // 指标计算
+            Map<String, Map<String, String>> indicatorValue =
+                    this.getIndicatorValue(refId, benchmarkId, data.getStartDate(), data.getEndDate(), indicatorList);
+            if (indicatorValue.get(refId) != null) {
+                vo.setFundRet(indicatorValue.get(refId).get(Indicator.IntervalReturn.name()));
+                vo.setAnnualRet(indicatorValue.get(refId).get(Indicator.AnnualReturn.name()));
+                vo.setMaxDrawdown(indicatorValue.get(refId).get(Indicator.MaxDrawdown.name()));
+                vo.setCalmar(indicatorValue.get(refId).get(Indicator.CalmarRatio.name()));
+                vo.setSortino(indicatorValue.get(refId).get(Indicator.SortinoRatio.name()));
+            }
+            resultList.add(vo);
+        });
+        return resultList;
+    }
+
+
+    /**
+     * 计算公募基金和其基准区间内的指标
+     *
+     * @param refId         公募基金
+     * @param benchmarkId   基准
+     * @param startDate     开始日期
+     * @param endDate       结束日期
+     * @param indicatorList 计算指标
+     * @return /
+     */
+    private Map<String, Map<String, String>> getIndicatorValue(String refId, String benchmarkId, String startDate, String endDate, List<Indicator> indicatorList) {
+        DateIntervalDto dateIntervalDto = DateIntervalDto.builder()
+                .id(DateIntervalType.CustomInterval.name()).startDate(startDate).endDate(endDate)
+                .dateIntervalType(DateIntervalType.CustomInterval).frequency(Frequency.Default).build();
+        Map<String, List<DateIntervalDto>> dateIntervalMap = MapUtil.builder(refId, ListUtil.of(dateIntervalDto)).build();
+        Map<String, String> benchmarkIdMap = MapUtil.builder(refId, benchmarkId).build();
+        CalcMultipleSecMultipleTimeRangeIndicatorReq req = CalcMultipleSecMultipleTimeRangeIndicatorReq.builder()
+                .mainSecIdList(ListUtil.of(refId))
+                .secBenchmarkIdMap(benchmarkIdMap)
+                .indexIdList(ListUtil.of(benchmarkId))
+                .raiseType(RaiseType.Both)
+                .strategy(Strategy.All)
+                .visibility(Visibility.Both)
+                .dataFrequency(Frequency.Default)
+                .navType(NavType.CumulativeNav)
+                .secDateIntervalDtoListMap(dateIntervalMap)
+                .indicatorList(indicatorList)
+                .geoExtraindicatorList(ListUtil.empty())
+                .ifAnnualize(true)
+                .riskOfFreeId(Consts.RISK_OF_FREE)
+                .calcIndexRetIndicatorValue(true)
+                .ifConvertPerformanceConsistencyWord(true).build();
+        Map<String, List<IndicatorCalcPropertyDto>> indicatorValueMap = this.baseIndicatorServiceV2.calcMultipleSecMultipleTimeRangeIndicator(req);
+
+        // DateIntervalType.CustomInterval 类型下每个标的只会返回一个维度下的指标计算结果,所以只获取第一条记录的指标计算结果
+        Map<String, Map<String, String>> result = MapUtil.newHashMap();
+        Map<String, String> valueMap = Optional.ofNullable(indicatorValueMap).map(e -> e.get(refId)).orElse(ListUtil.empty()).stream().findFirst()
+                .map(IndicatorCalcPropertyDto::getSecData).map(IndicatorCalcSecDataDto::getIndicatorValueMap).orElse(MapUtil.empty());
+        Map<String, String> valueBenchmarkMap = Optional.ofNullable(indicatorValueMap).map(e -> e.get(refId)).orElse(ListUtil.empty()).stream().findFirst()
+                .map(IndicatorCalcPropertyDto::getIndexData).map(IndicatorCalcIndexDataDto::getIndexIndicatorValueMap).map(e ->e.get(benchmarkId)).orElse(MapUtil.empty());
+        result.put(refId, valueMap);
+        result.put(benchmarkId, valueBenchmarkMap);
+        return result;
+    }
 }

+ 11 - 0
src/main/java/com/smppw/analysis/client/FundApi.java

@@ -101,4 +101,15 @@ public class FundApi {
         return ResultVo.ok(this.service.getHFManagerInfo(refId));
     }
 
+    /**
+     * 公募基金的基金经理变更历史
+     *
+     * @param params 基金id
+     * @return 公募基金的基金经理变更历史
+     */
+    @GetMapping("manager-change")
+    public ResultVo<List<ManualFundManagerChangeVO>> managerChange(ManualFundManagerParams params) {
+        return ResultVo.ok(this.service.managerChangeList(params));
+    }
+
 }

+ 4 - 0
src/main/java/com/smppw/analysis/domain/dao/FundInformationDao.java

@@ -80,4 +80,8 @@ public class FundInformationDao {
     public FundFeeDo getFundFee(String fundId) {
         return this.fundInformationDoMapper.getFundFee(fundId);
     }
+
+    public List<ManualFundManagerChangeDo> listFundManagerChangeHistory(String fundId) {
+        return this.fundInformationDoMapper.listFundManagerChangeHistory(fundId);
+    }
 }

+ 37 - 0
src/main/java/com/smppw/analysis/domain/dataobject/ManualFundManagerChangeDo.java

@@ -0,0 +1,37 @@
+package com.smppw.analysis.domain.dataobject;
+
+import lombok.Data;
+
+/**
+ * @author mozuwen
+ * @date 2023/8/9 14:00
+ * @description 公募基金的基金经理变更历史
+ */
+@Data
+public class ManualFundManagerChangeDo {
+
+    /**
+     * 基金id
+     */
+    private String fundId;
+
+    /**
+     * 基金经理id
+     */
+    private String managerId;
+
+    /**
+     * 经理名称
+     */
+    private String managerName;
+
+    /**
+     * 任职开始时间
+     */
+    private String startDate;
+    /**
+     * 任职结束时间
+     */
+    private String endDate;
+
+}

+ 58 - 0
src/main/java/com/smppw/analysis/domain/dto/info/ManualFundManagerChangeVO.java

@@ -0,0 +1,58 @@
+package com.smppw.analysis.domain.dto.info;
+
+import lombok.Data;
+
+/**
+ * @author mozuwen
+ * @date 2023/8/9 17:00
+ * @description 公募基金的基金经理变更历史
+ */
+@Data
+public class ManualFundManagerChangeVO {
+
+    /**
+     * 任内年化收益
+     */
+    private String annualRet;
+
+    /**
+     * 任内卡玛比率
+     */
+    private String calmar;
+
+    /**
+     * 任职日期
+     */
+    private String employDate;
+
+    /**
+     * 任职天数
+     */
+    private String employDayNum;
+
+    /**
+     * 任职期间收益
+     */
+    private String fundRet;
+
+    /**
+     * 基金经理id
+     */
+    private String managerId;
+
+    /**
+     * 基金经理名字
+     */
+    private String managerName;
+
+    /**
+     * 任内最大回撤
+     */
+    private String maxDrawdown;
+
+    /**
+     * 任内索提诺比率
+     */
+    private String sortino;
+
+}

+ 9 - 0
src/main/java/com/smppw/analysis/domain/mapper/core/FundInformationDoMapper.java

@@ -3,6 +3,7 @@ package com.smppw.analysis.domain.mapper.core;
 import com.smppw.analysis.domain.dataobject.FundFeeDo;
 import com.smppw.analysis.domain.dataobject.FundInformationDo;
 import com.smppw.analysis.domain.dataobject.FundSimilarDo;
+import com.smppw.analysis.domain.dataobject.ManualFundManagerChangeDo;
 import org.apache.ibatis.annotations.Param;
 import org.springframework.stereotype.Repository;
 
@@ -60,4 +61,12 @@ public interface FundInformationDoMapper {
      */
     FundFeeDo getFundFee(String fundId);
 
+    /**
+     * 获取公募基金的基金经理变更历史
+     *
+     * @param fundId 基金id
+     * @return 公募基金的基金经理变更历史
+     */
+    List<ManualFundManagerChangeDo> listFundManagerChangeHistory(@Param("fundId") String fundId);
+
 }

+ 8 - 0
src/main/java/com/smppw/analysis/domain/service/BaseInfoService.java

@@ -146,4 +146,12 @@ public interface BaseInfoService {
      * @return 人员任职经历
      */
     List<PersonnelWorkExperienceDo> listPersonnelWorkExperience(List<String> personnelIdList);
+
+    /**
+     * 获取公募基金的基金经理变更历史
+     *
+     * @param fundId 基金id
+     * @return 公募基金的基金经理变更历史
+     */
+    List<ManualFundManagerChangeDo> listFundManagerChangeHistory(String fundId);
 }

+ 5 - 0
src/main/java/com/smppw/analysis/domain/service/impl/BaseInfoServiceImpl.java

@@ -302,6 +302,11 @@ public class BaseInfoServiceImpl implements BaseInfoService, ApplicationContextA
         return personnelInformationMapper.listPersonnelWorkExperience(personnelIdList);
     }
 
+    @Override
+    public List<ManualFundManagerChangeDo> listFundManagerChangeHistory(String fundId) {
+        return fundInformationDao.listFundManagerChangeHistory(fundId);
+    }
+
     /**
      * 把指定类型的标的的名称映射查询出来
      *

+ 17 - 0
src/main/resources/mapping/core/FundInformationDoMapper.xml

@@ -338,4 +338,21 @@
         where t1.fund_id = #{fundId}
     </select>
 
+    <select id="listFundManagerChangeHistory"
+            resultType="com.smppw.analysis.domain.dataobject.ManualFundManagerChangeDo">
+        select t1.fund_id               as fundId,
+               t2.fund_manager_id       as managerId,
+               t3.personnel_name        as managerName,
+               t2.management_start_date as startDate,
+               t2.management_end_date   as endDate
+        from rz_hfdb_core.fund_information t1
+                 join rz_hfdb_core.fund_manager_mapping t2
+                      on t1.fund_id = t2.fund_id and t2.isvalid = 1
+                 join rz_hfdb_core.personnel_information t3
+                      on t2.fund_manager_id = t3.personnel_id and t3.isvalid = 1
+        where t1.isvalid = 1
+          and t1.fund_id = #{fundId}
+        order by startDate desc
+    </select>
+
 </mapper>