From e387fc839f85bf00275866192dc8cc6111a298e1 Mon Sep 17 00:00:00 2001 From: lanyuanxiaoyao Date: Sat, 11 Oct 2025 18:41:18 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E5=91=A8=E7=BA=BF?= =?UTF-8?q?=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../leopard/core/entity/Weekly.java | 60 +++++++++++++++++++ .../leopard/core/service/TaskService.java | 10 ++-- .../leopard/core/task/UpdateYearlyTask.java | 58 ++++++++++-------- 3 files changed, 98 insertions(+), 30 deletions(-) create mode 100644 leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/entity/Weekly.java diff --git a/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/entity/Weekly.java b/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/entity/Weekly.java new file mode 100644 index 0000000..40afa09 --- /dev/null +++ b/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/entity/Weekly.java @@ -0,0 +1,60 @@ +package com.lanyuanxiaoyao.leopard.core.entity; + +import com.lanyuanxiaoyao.leopard.core.Constants; +import com.lanyuanxiaoyao.service.template.entity.SimpleEntity; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EntityListeners; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.FieldNameConstants; +import org.hibernate.annotations.Comment; +import org.hibernate.annotations.DynamicInsert; +import org.hibernate.annotations.DynamicUpdate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +/** + * 年线行情,后复权 + */ +@Setter +@Getter +@ToString(callSuper = true) +@FieldNameConstants +@Entity +@DynamicUpdate +@DynamicInsert +@EntityListeners(AuditingEntityListener.class) +@Table(name = Constants.DATABASE_PREFIX + "weekly") +public class Weekly extends SimpleEntity { + @Column(name = "`year`", nullable = false) + @Comment("年份") + private Integer year; + @Column(name = "`week`", nullable = false) + @Comment("周数") + private Integer week; + @Comment("开盘价") + private Double open; + @Comment("最高价") + private Double high; + @Comment("最低价") + private Double low; + @Comment("收盘价") + private Double close; + @Comment("涨跌额") + private Double priceChangeAmount; + @Comment("涨跌幅") + private Double priceFluctuationRange; + @Comment("成交量") + private Double volume; + @Comment("成交额") + private Double turnover; + + @ManyToOne + @JoinColumn(nullable = false) + @ToString.Exclude + private Stock stock; +} diff --git a/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/service/TaskService.java b/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/service/TaskService.java index 27794fe..f225830 100644 --- a/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/service/TaskService.java +++ b/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/service/TaskService.java @@ -34,11 +34,11 @@ public class TaskService extends SimpleServiceSupport { @Getter private final Set templates = Stream.of( - new TaskTemplate("更新股票信息", "更新股票信息", UpdateStockTask.class), - new TaskTemplate("更新年线指标", "更新年线指标", UpdateYearlyTask.class), - new TaskTemplate("更新日线数据", "更新日线数据", UpdateDailyTask.class), - new TaskTemplate("更新财务指标", "更新财务指标", UpdateFinanceIndicatorTask.class), - new TaskTemplate("金字塔选股", "金字塔选股", PyramidSelect.class) + new TaskTemplate("b29f76a5-b07d-4182-85f8-2641c2a975c1", "更新股票信息", "更新股票信息", UpdateStockTask.class), + new TaskTemplate("e42dde60-5584-4c27-b3f7-72e4a4ff662d", "更新年线数据", "更新年线数据", UpdateYearlyTask.class), + new TaskTemplate("b9df25ce-aa55-4f73-8265-d8a724614177", "更新日线数据", "更新日线数据", UpdateDailyTask.class), + new TaskTemplate("8ab30478-c81f-4bbf-94dd-7e05fa537b50", "更新财务指标", "更新财务指标", UpdateFinanceIndicatorTask.class), + new TaskTemplate("a6a7b569-a171-481b-9184-716925571639", "金字塔选股", "金字塔选股", PyramidSelect.class) ).collect(Collectors.toSet()); private final Map templateMap = templates.stream() .collect(Collectors.toMap(TaskTemplate::id, template -> template)); diff --git a/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/task/UpdateYearlyTask.java b/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/task/UpdateYearlyTask.java index 120ec47..90857ae 100644 --- a/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/task/UpdateYearlyTask.java +++ b/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/task/UpdateYearlyTask.java @@ -40,31 +40,39 @@ public class UpdateYearlyTask extends TaskRunner { var endYear = dailyRepository.findMaxTradeDate().getYear(); var stocks = stockRepository.findAll(); for (int year = startYear, index = 0; year <= endYear; year++, index++) { - for (var stock : stocks) { - log.info("Processing {} {}", stock.getCode(), year); - if (stock.getListedDate().getYear() > year) { - continue; - } - var dailies = dailyRepository.findAll( - QDaily.daily.tradeDate.year().eq(year) - .and(QDaily.daily.stock.eq(stock)) - ); - var yearly = yearlyRepository.findOne( - QYearly.yearly.stock.eq(stock) - .and(QYearly.yearly.year.eq(year)) - ).orElseGet(Yearly::new); - yearly.setStock(stock); - yearly.setYear(year); - yearly.setClose(dailies.getLast().getHfqClose()); - yearly.setOpen(dailies.getFirst().getHfqOpen()); - yearly.setHigh(dailies.stream().map(Daily::getHfqHigh).max(Double::compareTo).orElse(0.0)); - yearly.setLow(dailies.stream().map(Daily::getHfqLow).min(Double::compareTo).orElse(0.0)); - yearly.setVolume(dailies.stream().mapToDouble(Daily::getVolume).sum()); - yearly.setTurnover(dailies.stream().mapToDouble(Daily::getTurnover).sum()); - yearly.setPriceChangeAmount(yearly.getClose() - yearly.getOpen()); - yearly.setPriceFluctuationRange((yearly.getClose() - yearly.getOpen()) / yearly.getOpen()); - yearlyRepository.save(yearly); - } + int currentYear = year; + stocks.parallelStream() + .forEach(stock -> { + if (stock.getListedDate().getYear() > currentYear) { + return; + } + var yearlyOptional = yearlyRepository.findOne( + QYearly.yearly.stock.eq(stock) + .and(QYearly.yearly.year.eq(currentYear)) + ); + if (yearlyOptional.isPresent() && currentYear != endYear) { + return; + } + var dailies = dailyRepository.findAll( + QDaily.daily.tradeDate.year().eq(currentYear) + .and(QDaily.daily.stock.eq(stock)) + ); + if (dailies.isEmpty()) { + return; + } + var yearly = yearlyOptional.orElseGet(Yearly::new); + yearly.setStock(stock); + yearly.setYear(currentYear); + yearly.setClose(dailies.getLast().getHfqClose()); + yearly.setOpen(dailies.getFirst().getHfqOpen()); + yearly.setHigh(dailies.stream().map(Daily::getHfqHigh).max(Double::compareTo).orElse(0.0)); + yearly.setLow(dailies.stream().map(Daily::getHfqLow).min(Double::compareTo).orElse(0.0)); + yearly.setVolume(dailies.stream().mapToDouble(Daily::getVolume).sum()); + yearly.setTurnover(dailies.stream().mapToDouble(Daily::getTurnover).sum()); + yearly.setPriceChangeAmount(yearly.getClose() - yearly.getOpen()); + yearly.setPriceFluctuationRange((yearly.getClose() - yearly.getOpen()) / yearly.getOpen()); + yearlyRepository.save(yearly); + }); updater.update((year - startYear) * 1.0 / (endYear - startYear + 1)); } return null;