FundDeletionTask.java 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. package com.simuwang.manage.task;
  2. import cn.hutool.core.date.DateUtil;
  3. import com.simuwang.base.common.enums.DeletionType;
  4. import com.simuwang.base.common.enums.DistributeType;
  5. import com.simuwang.base.common.enums.Frequency;
  6. import com.simuwang.base.common.util.DateUtils;
  7. import com.simuwang.base.common.util.StringUtil;
  8. import com.simuwang.base.mapper.*;
  9. import com.simuwang.base.pojo.dos.*;
  10. import org.springframework.beans.factory.annotation.Autowired;
  11. import org.springframework.scheduling.annotation.EnableScheduling;
  12. import org.springframework.scheduling.annotation.Scheduled;
  13. import org.springframework.stereotype.Component;
  14. import java.math.BigDecimal;
  15. import java.util.*;
  16. import java.util.stream.Collectors;
  17. /**
  18. * FileName: FundDeletionTask
  19. * Author: chenjianhua
  20. * Date: 2024/9/18 22:43
  21. * Description: ${DESCRIPTION}
  22. */
  23. @EnableScheduling
  24. @Component
  25. public class FundDeletionTask {
  26. @Autowired
  27. private NavMapper navMapper;
  28. @Autowired
  29. private AssetMapper assetMapper;
  30. @Autowired
  31. private FundInfoMapper fundInfoMapper;
  32. @Autowired
  33. private FundReportFrequencyMapper fundReportFrequencyMapper;
  34. @Autowired
  35. private TradeDateMapper tradeDateMapper;
  36. @Autowired
  37. private DeletionInfoMapper deletionInfoMapper;
  38. @Autowired
  39. private DistributionMapper distributionMapper;
  40. @Scheduled(cron = "0 0/5 * * * ?")
  41. public void computeDeletion(){
  42. List<String> fundIdList = navMapper.getAllFundId();
  43. for(String fundId : fundIdList){
  44. String inceptionDate = fundInfoMapper.getInceptionDateByFundId(fundId);
  45. if(inceptionDate == null){
  46. continue;
  47. }
  48. String today = DateUtils.getAroundToday(0);
  49. //获取基金对应的净值报送频率
  50. FundReportFrequencyDO fundReportFrequencyDO = fundReportFrequencyMapper.getFrequencyByFundId(fundId);
  51. if(StringUtil.isNull(fundReportFrequencyDO)){
  52. continue;
  53. }
  54. List<NavDO> navDOList = navMapper.selectNavByFundId(fundId);
  55. List<AssetDO> assetDOList = assetMapper.selectAssetByFundId(fundId);
  56. //查询成立日到今天为止的交易日集合
  57. List<TradeDateDO> tradeDateDOList = tradeDateMapper.selectTradeDate(inceptionDate,today);
  58. navDeletion(fundId,navDOList,tradeDateDOList,fundReportFrequencyDO);
  59. assetDeletion(fundId,assetDOList,tradeDateDOList,fundReportFrequencyDO);
  60. distributionDeletion(fundId,navDOList);
  61. }
  62. }
  63. private void distributionDeletion(String fundId, List<NavDO> navDOList) {
  64. if(navDOList.size() < 1){
  65. return;
  66. }
  67. //查询是否存在拆分
  68. List<DistributionDO> distributionDOS = distributionMapper.getDistributionByFundId(fundId, DistributeType.DIVIDENDS_SPLIT);
  69. if(distributionDOS.size() > 0){
  70. //存在拆分,不做分红缺失计算,同时把以往的数据添加备注
  71. deletionInfoMapper.updateRemark(fundId,DeletionType.ASSET_DELETION.getCode(),null,DeletionType.EXIST_SPLIT.getInfo());
  72. return;
  73. }
  74. BigDecimal threshold = new BigDecimal(0.0035);
  75. BigDecimal preDifference = new BigDecimal(0);
  76. for(int navIdx=0;navIdx < navDOList.size() ;navIdx++){
  77. NavDO navDO = navDOList.get(navIdx);
  78. //获取当前净值日期下的分红总和
  79. BigDecimal sumDistribute = distributionMapper.getSumDistributeByFundId(navDO.getFundId(),DateUtils.format(navDO.getPriceDate(),DateUtils.YYYY_MM_DD));
  80. BigDecimal nav = navDO.getNav();
  81. BigDecimal cumulativeNavWithdrawal = navDO.getCumulativeNavWithdrawal();
  82. if(sumDistribute == null){
  83. sumDistribute = new BigDecimal(0);
  84. }
  85. //不存在分红的时候,判断当前的差值是否符合要求
  86. BigDecimal difference = cumulativeNavWithdrawal.subtract(nav.add(sumDistribute));
  87. if(difference.compareTo(threshold) > 0){
  88. //存在缺失
  89. String tradeDate = null;
  90. if(navIdx == 0 || navDOList.size() ==1){
  91. //有且仅有一条数据
  92. tradeDate = DateUtils.format(navDO.getPriceDate(),DateUtils.YYYY_MM_DD)+"~"+DateUtils.format(navDO.getPriceDate(),DateUtils.YYYY_MM_DD);
  93. }else{
  94. //判断前一条的差值与当前是否一致,一致就不处理
  95. if(difference.compareTo(preDifference) == 0){
  96. continue;
  97. }
  98. tradeDate = DateUtils.format(navDOList.get(navIdx-1).getPriceDate(),DateUtils.YYYY_MM_DD)+"~"+DateUtils.format(navDO.getPriceDate(),DateUtils.YYYY_MM_DD);
  99. }
  100. saveDeletionInfoDO(fundId,tradeDate,DeletionType.DISTRIBUTION_DELETION.getCode());
  101. }
  102. preDifference = difference;
  103. }
  104. }
  105. private void assetDeletion(String fundId, List<AssetDO> assetDOList, List<TradeDateDO> tradeDateDOList, FundReportFrequencyDO fundReportFrequencyDO) {
  106. if(Frequency.DAY == Frequency.getFrequencyByCode(fundReportFrequencyDO.getAssetFrequency())){
  107. Map<String,List<AssetDO>> navListMap = assetDOList.stream().collect(Collectors.groupingBy(e -> DateUtils.format(e.getPriceDate(),DateUtils.YYYY_MM_DD)));
  108. Map<String,List<TradeDateDO>> tradeListMap = tradeDateDOList.stream().collect(Collectors.groupingBy(e -> DateUtils.format(e.getTradeDate(),DateUtils.YYYY_MM_DD)));
  109. for(String tradeDate : tradeListMap.keySet()){
  110. if(navListMap.containsKey(tradeDate)){
  111. deletionInfoMapper.updateRemark(fundId,DeletionType.ASSET_DELETION.getCode(),tradeDate,DeletionType.NO_DELETION.getInfo());
  112. continue;
  113. }
  114. //写入缺失信息表
  115. saveDeletionInfoDO(fundId,tradeDate,DeletionType.ASSET_DELETION.getCode());
  116. }
  117. }
  118. if(Frequency.WEEK == Frequency.getFrequencyByCode(fundReportFrequencyDO.getAssetFrequency())){
  119. Map<String,List<AssetDO>> navListMap = assetDOList.stream().collect(Collectors.groupingBy(e -> DateUtils.format(e.getPriceDate(),DateUtils.YYYY_MM_DD)));
  120. TreeMap<Integer,List<AssetDO>> weekNavListMap = new TreeMap<>();
  121. //按周数整合
  122. for(String priceDate : navListMap.keySet()){
  123. Integer weekOfYear = DateUtil.weekOfYear(DateUtils.parse(priceDate,DateUtils.YYYY_MM_DD));
  124. if(weekNavListMap.containsKey(weekOfYear)){
  125. List<AssetDO> assetDOS = weekNavListMap.get(weekOfYear);
  126. assetDOS.addAll(navListMap.get(priceDate));
  127. weekNavListMap.put(weekOfYear,assetDOS);
  128. }else{
  129. List<AssetDO> navDOS = new ArrayList<>();
  130. navDOS.addAll(navListMap.get(priceDate));
  131. weekNavListMap.put(weekOfYear,navDOS);
  132. }
  133. }
  134. Map<Integer,List<TradeDateDO>> tradeListMap = tradeDateDOList.stream().collect(Collectors.groupingBy(e -> e.getWeekOfYear()));
  135. for(Integer weekOfYear : tradeListMap.keySet()){
  136. if(weekNavListMap.containsKey(weekOfYear)){
  137. List<AssetDO> assetDOS = weekNavListMap.get(weekOfYear);
  138. String tradeDate = DateUtils.format(assetDOS.get(assetDOS.size()-1).getPriceDate(),DateUtils.YYYY_MM_DD);
  139. deletionInfoMapper.updateRemark(fundId,DeletionType.ASSET_DELETION.getCode(),tradeDate,DeletionType.NO_DELETION.getInfo());
  140. continue;
  141. }
  142. //不包含的话,默认取每周的最后一个交易日作为周净值日期
  143. List<TradeDateDO> tradeDateDOS = tradeListMap.get(weekOfYear);
  144. String tradeDate = null;
  145. if(tradeDateDOS.size() > 0){
  146. tradeDate = DateUtils.format(tradeDateDOS.get(tradeDateDOS.size()-1).getTradeDate(),DateUtils.YYYY_MM_DD);
  147. }
  148. //写入缺失信息表
  149. saveDeletionInfoDO(fundId,tradeDate,DeletionType.NAV_DELETION.getCode());
  150. }
  151. }
  152. if(Frequency.MONTH == Frequency.getFrequencyByCode(fundReportFrequencyDO.getAssetFrequency())){
  153. Map<String,List<AssetDO>> navListMap = assetDOList.stream().collect(Collectors.groupingBy(e -> DateUtils.format(e.getPriceDate(),DateUtils.YYYY_MM_DD)));
  154. TreeMap<String,List<AssetDO>> monthNavListMap = new TreeMap<>();
  155. //按周数整合
  156. for(String priceDate : navListMap.keySet()){
  157. String yearMonth = priceDate.substring(0,7);
  158. if(monthNavListMap.containsKey(yearMonth)){
  159. List<AssetDO> navDOS = monthNavListMap.get(yearMonth);
  160. navDOS.addAll(navListMap.get(priceDate));
  161. monthNavListMap.put(yearMonth,navDOS);
  162. }else{
  163. List<AssetDO> navDOS = new ArrayList<>();
  164. navDOS.addAll(navListMap.get(priceDate));
  165. monthNavListMap.put(yearMonth,navDOS);
  166. }
  167. }
  168. Map<String,List<TradeDateDO>> tradeListMap = tradeDateDOList.stream().collect(Collectors.groupingBy(e -> e.getYearmonth()));
  169. for(String yearMonth : tradeListMap.keySet()){
  170. if(monthNavListMap.containsKey(yearMonth)){
  171. List<AssetDO> assetDOS = monthNavListMap.get(yearMonth);
  172. String tradeDate = DateUtils.format(assetDOS.get(assetDOS.size()-1).getPriceDate(),DateUtils.YYYY_MM_DD);
  173. deletionInfoMapper.updateRemark(fundId,DeletionType.ASSET_DELETION.getCode(),tradeDate,DeletionType.NO_DELETION.getInfo());
  174. continue;
  175. }
  176. //不包含的话,默认取每周的最后一个交易日作为周净值日期
  177. List<TradeDateDO> tradeDateDOS = tradeListMap.get(yearMonth);
  178. String tradeDate = null;
  179. if(tradeDateDOS.size() > 0){
  180. tradeDate = DateUtils.format(tradeDateDOS.get(tradeDateDOS.size()-1).getTradeDate(),DateUtils.YYYY_MM_DD);
  181. }
  182. //写入缺失信息表
  183. saveDeletionInfoDO(fundId,tradeDate,DeletionType.ASSET_DELETION.getCode());
  184. }
  185. }
  186. }
  187. private void navDeletion(String fundId,List<NavDO> navDOList, List<TradeDateDO> tradeDateDOList,FundReportFrequencyDO fundReportFrequencyDO) {
  188. //只处理日月季频率
  189. if(Frequency.DAY == Frequency.getFrequencyByCode(fundReportFrequencyDO.getNavFrequency())){
  190. Map<String,List<NavDO>> navListMap = navDOList.stream().collect(Collectors.groupingBy(e -> DateUtils.format(e.getPriceDate(),DateUtils.YYYY_MM_DD)));
  191. Map<String,List<TradeDateDO>> tradeListMap = tradeDateDOList.stream().collect(Collectors.groupingBy(e -> DateUtils.format(e.getTradeDate(),DateUtils.YYYY_MM_DD)));
  192. for(String tradeDate : tradeListMap.keySet()){
  193. if(navListMap.containsKey(tradeDate)){
  194. deletionInfoMapper.updateRemark(fundId,DeletionType.NAV_DELETION.getCode(),tradeDate,DeletionType.NO_DELETION.getInfo());
  195. continue;
  196. }
  197. //写入缺失信息表
  198. saveDeletionInfoDO(fundId,tradeDate,DeletionType.NAV_DELETION.getCode());
  199. }
  200. }
  201. if(Frequency.WEEK == Frequency.getFrequencyByCode(fundReportFrequencyDO.getNavFrequency())){
  202. Map<String,List<NavDO>> navListMap = navDOList.stream().collect(Collectors.groupingBy(e -> DateUtils.format(e.getPriceDate(),DateUtils.YYYY_MM_DD)));
  203. TreeMap<Integer,List<NavDO>> weekNavListMap = new TreeMap<>();
  204. //按周数整合
  205. for(String priceDate : navListMap.keySet()){
  206. Integer weekOfYear = DateUtil.weekOfYear(DateUtils.parse(priceDate,DateUtils.YYYY_MM_DD));
  207. if(weekNavListMap.containsKey(weekOfYear)){
  208. List<NavDO> navDOS = weekNavListMap.get(weekOfYear);
  209. navDOS.addAll(navListMap.get(priceDate));
  210. weekNavListMap.put(weekOfYear,navDOS);
  211. }else{
  212. List<NavDO> navDOS = new ArrayList<>();
  213. navDOS.addAll(navListMap.get(priceDate));
  214. weekNavListMap.put(weekOfYear,navDOS);
  215. }
  216. }
  217. Map<Integer,List<TradeDateDO>> tradeListMap = tradeDateDOList.stream().collect(Collectors.groupingBy(e -> e.getWeekOfYear()));
  218. for(Integer weekOfYear : tradeListMap.keySet()){
  219. if(weekNavListMap.containsKey(weekOfYear)){
  220. List<NavDO> navDOS = weekNavListMap.get(weekOfYear);
  221. String tradeDate = DateUtils.format(navDOS.get(navDOS.size()-1).getPriceDate(),DateUtils.YYYY_MM_DD);
  222. deletionInfoMapper.updateRemark(fundId,DeletionType.NAV_DELETION.getCode(),tradeDate,DeletionType.NO_DELETION.getInfo());
  223. continue;
  224. }
  225. //不包含的话,默认取每周的最后一个交易日作为周净值日期
  226. List<TradeDateDO> tradeDateDOS = tradeListMap.get(weekOfYear);
  227. String tradeDate = null;
  228. if(tradeDateDOS.size() > 0){
  229. tradeDate = DateUtils.format(tradeDateDOS.get(tradeDateDOS.size()-1).getTradeDate(),DateUtils.YYYY_MM_DD);
  230. }
  231. //写入缺失信息表
  232. saveDeletionInfoDO(fundId,tradeDate,DeletionType.NAV_DELETION.getCode());
  233. }
  234. }
  235. if(Frequency.MONTH == Frequency.getFrequencyByCode(fundReportFrequencyDO.getNavFrequency())){
  236. Map<String,List<NavDO>> navListMap = navDOList.stream().collect(Collectors.groupingBy(e -> DateUtils.format(e.getPriceDate(),DateUtils.YYYY_MM_DD)));
  237. TreeMap<String,List<NavDO>> monthNavListMap = new TreeMap<>();
  238. //按周数整合
  239. for(String priceDate : navListMap.keySet()){
  240. String yearMonth = priceDate.substring(0,7);
  241. if(monthNavListMap.containsKey(yearMonth)){
  242. List<NavDO> navDOS = monthNavListMap.get(yearMonth);
  243. navDOS.addAll(navListMap.get(priceDate));
  244. monthNavListMap.put(yearMonth,navDOS);
  245. }else{
  246. List<NavDO> navDOS = new ArrayList<>();
  247. navDOS.addAll(navListMap.get(priceDate));
  248. monthNavListMap.put(yearMonth,navDOS);
  249. }
  250. }
  251. Map<String,List<TradeDateDO>> tradeListMap = tradeDateDOList.stream().collect(Collectors.groupingBy(e -> e.getYearmonth()));
  252. for(String yearMonth : tradeListMap.keySet()){
  253. if(monthNavListMap.containsKey(yearMonth)){
  254. List<NavDO> navDOS = monthNavListMap.get(yearMonth);
  255. String tradeDate = DateUtils.format(navDOS.get(navDOS.size()-1).getPriceDate(),DateUtils.YYYY_MM_DD);
  256. deletionInfoMapper.updateRemark(fundId,DeletionType.NAV_DELETION.getCode(),tradeDate,DeletionType.NO_DELETION.getInfo());
  257. continue;
  258. }
  259. //不包含的话,默认取每周的最后一个交易日作为周净值日期
  260. List<TradeDateDO> tradeDateDOS = tradeListMap.get(yearMonth);
  261. String tradeDate = null;
  262. if(tradeDateDOS.size() > 0){
  263. tradeDate = DateUtils.format(tradeDateDOS.get(tradeDateDOS.size()-1).getTradeDate(),DateUtils.YYYY_MM_DD);
  264. }
  265. //写入缺失信息表
  266. saveDeletionInfoDO(fundId,tradeDate,DeletionType.NAV_DELETION.getCode());
  267. }
  268. }
  269. }
  270. private void saveDeletionInfoDO(String fundId, String tradeDate, Integer code) {
  271. if(tradeDate == null){
  272. return;
  273. }
  274. DeletionInfoDO deletionInfoDO = new DeletionInfoDO();
  275. deletionInfoDO.setFundId(fundId);
  276. deletionInfoDO.setDeletionDate(tradeDate);
  277. deletionInfoDO.setDeletionType(code);
  278. DeletionInfoDO oldDeletionDO = deletionInfoMapper.getDeletionInfoDO(deletionInfoDO);
  279. if(StringUtil.isNull(oldDeletionDO)){
  280. deletionInfoDO.setIsvalid(1);
  281. deletionInfoDO.setUpdateTime(DateUtils.getNowDate());
  282. deletionInfoDO.setCreateTime(DateUtils.getNowDate());
  283. deletionInfoMapper.saveDeletionInfoDO(deletionInfoDO);
  284. }
  285. }
  286. }