diff --git a/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/entity/Yearly.java b/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/entity/Yearly.java deleted file mode 100644 index a840016..0000000 --- a/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/entity/Yearly.java +++ /dev/null @@ -1,57 +0,0 @@ -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 + "yearly") -public class Yearly extends SimpleEntity { - @Column(name = "`year`", nullable = false) - @Comment("年份") - private Integer year; - @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/repository/YearlyRepository.java b/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/repository/YearlyRepository.java deleted file mode 100644 index bac44db..0000000 --- a/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/repository/YearlyRepository.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.lanyuanxiaoyao.leopard.core.repository; - -import com.lanyuanxiaoyao.leopard.core.entity.Yearly; -import com.lanyuanxiaoyao.service.template.repository.SimpleRepository; -import org.springframework.stereotype.Repository; - -@Repository -public interface YearlyRepository extends SimpleRepository { -} 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 f225830..8c68fd7 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 @@ -9,7 +9,6 @@ import com.lanyuanxiaoyao.leopard.core.task.TaskRunner; import com.lanyuanxiaoyao.leopard.core.task.UpdateDailyTask; import com.lanyuanxiaoyao.leopard.core.task.UpdateFinanceIndicatorTask; import com.lanyuanxiaoyao.leopard.core.task.UpdateStockTask; -import com.lanyuanxiaoyao.leopard.core.task.UpdateYearlyTask; import com.lanyuanxiaoyao.service.template.service.SimpleServiceSupport; import java.util.Map; import java.util.Set; @@ -35,7 +34,6 @@ public class TaskService extends SimpleServiceSupport { @Getter private final Set templates = Stream.of( 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) diff --git a/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/service/TuShareService.java b/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/service/TuShareService.java index c9d9079..cca3347 100644 --- a/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/service/TuShareService.java +++ b/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/service/TuShareService.java @@ -97,9 +97,19 @@ public class TuShareService { @SneakyThrows public TuShareResponse factorList(LocalDate tradeDate) { + return factorList(tradeDate, null); + } + + @SneakyThrows + public TuShareResponse factorList(LocalDate tradeDate, String stockCode) { + var paramsMap = new HashMap(); + paramsMap.put("trade_date", tradeDate.format(TRADE_FORMAT)); + if (StrUtil.isNotBlank(stockCode)) { + paramsMap.put("ts_code", stockCode); + } var response = HttpUtil.post(API_URL, buildRequest( "adj_factor", - Map.of("trade_date", tradeDate.format(TRADE_FORMAT)), + paramsMap, List.of("ts_code", "trade_date", "adj_factor") )); var tuShareResponse = mapper.readValue(response, TuShareResponse.class); 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 deleted file mode 100644 index 90857ae..0000000 --- a/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/task/UpdateYearlyTask.java +++ /dev/null @@ -1,80 +0,0 @@ -package com.lanyuanxiaoyao.leopard.core.task; - -import com.lanyuanxiaoyao.leopard.core.entity.Daily; -import com.lanyuanxiaoyao.leopard.core.entity.QDaily; -import com.lanyuanxiaoyao.leopard.core.entity.QYearly; -import com.lanyuanxiaoyao.leopard.core.entity.Yearly; -import com.lanyuanxiaoyao.leopard.core.repository.DailyRepository; -import com.lanyuanxiaoyao.leopard.core.repository.StockRepository; -import com.lanyuanxiaoyao.leopard.core.repository.YearlyRepository; -import java.util.Map; -import lombok.extern.slf4j.Slf4j; -import org.springframework.context.ApplicationContext; -import org.springframework.stereotype.Component; -import org.springframework.transaction.annotation.Transactional; - -/** - * 更新年线指标 - * - * @author lanyuanxiaoyao - * @version 20250924 - */ -@Slf4j -@Component -public class UpdateYearlyTask extends TaskRunner { - private final StockRepository stockRepository; - private final DailyRepository dailyRepository; - private final YearlyRepository yearlyRepository; - - protected UpdateYearlyTask(ApplicationContext context, StockRepository stockRepository, DailyRepository dailyRepository, YearlyRepository yearlyRepository) { - super(context); - this.stockRepository = stockRepository; - this.dailyRepository = dailyRepository; - this.yearlyRepository = yearlyRepository; - } - - @Transactional(rollbackFor = Throwable.class) - @Override - public String process(Map params, StepUpdater updater) { - var startYear = dailyRepository.findMinTradeDate().getYear(); - var endYear = dailyRepository.findMaxTradeDate().getYear(); - var stocks = stockRepository.findAll(); - for (int year = startYear, index = 0; year <= endYear; year++, index++) { - 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; - } -} diff --git a/leopard-server/src/test/resources/tushare.http b/leopard-server/src/test/resources/tushare.http index 54f66b0..2ddaab6 100644 --- a/leopard-server/src/test/resources/tushare.http +++ b/leopard-server/src/test/resources/tushare.http @@ -109,7 +109,8 @@ Content-Type: application/json "api_name": "adj_factor", "token": "{{api_key}}", "params": { - "trade_date": "20241231" + "ts_code": "600018.SH", + "trade_date": "20060331" }, "fields": [ "ts_code", @@ -126,12 +127,15 @@ Content-Type: application/json "api_name": "stk_factor_pro", "token": "{{api_key}}", "params": { - "ts_code": "000002.SZ", - "trade_date": "20250102" + "ts_code": "600018.SH", + "trade_date": "20060331" }, "fields": [ "ts_code", "trade_date", + "open", + "open_qfq", + "open_hfq", "close", "close_qfq", "close_hfq" diff --git a/leopard-strategy/src/main/java/com/lanyuanxiaoyao/leopard/strategy/StrategyApplication.java b/leopard-strategy/src/main/java/com/lanyuanxiaoyao/leopard/strategy/StrategyApplication.java index 0326652..2309b50 100644 --- a/leopard-strategy/src/main/java/com/lanyuanxiaoyao/leopard/strategy/StrategyApplication.java +++ b/leopard-strategy/src/main/java/com/lanyuanxiaoyao/leopard/strategy/StrategyApplication.java @@ -1,18 +1,17 @@ package com.lanyuanxiaoyao.leopard.strategy; -import cn.hutool.core.util.NumberUtil; import cn.hutool.core.util.StrUtil; import com.fasterxml.jackson.databind.ObjectMapper; +import com.lanyuanxiaoyao.leopard.core.entity.QDaily; +import com.lanyuanxiaoyao.leopard.core.repository.DailyRepository; import com.lanyuanxiaoyao.leopard.core.service.AssessmentService; +import com.lanyuanxiaoyao.leopard.core.service.TuShareService; import com.lanyuanxiaoyao.leopard.core.service.selector.PyramidStockSelector; -import com.lanyuanxiaoyao.leopard.core.service.selector.StockSelector; import jakarta.annotation.Resource; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; import java.util.Map; -import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @@ -30,6 +29,10 @@ public class StrategyApplication { private PyramidStockSelector pyramidStockSelector; @Resource private AssessmentService assessmentService; + @Resource + private TuShareService tuShareService; + @Resource + private DailyRepository dailyRepository; public static void main(String[] args) { SpringApplication.run(StrategyApplication.class, args); @@ -269,62 +272,11 @@ public class StrategyApplication { @Transactional(readOnly = true) @EventListener(ApplicationReadyEvent.class) public void test() { - /*var dailies = dailyRepository.findAll( - QDaily.daily.tradeDate.year().eq(2025), - Sort.by(Daily_.TRADE_DATE) - ); - var data = dailies.stream() - .collect(Collectors.groupingBy(Daily::getStock)) - .entrySet() - .parallelStream() - .map(entry -> { - var stock = entry.getKey(); - var dailyList = entry.getValue() - .stream() - .sorted(Comparator.comparing(Daily::getTradeDate)) - .toList(); - var statistics = new DescriptiveStatistics(); - dailyList.stream().map(Daily::getHfqClose).forEach(statistics::addValue); - var barSeries = new BaseBarSeriesBuilder().build(); - dailyList.forEach(daily -> barSeries.addBar(new BaseBar( - Duration.ofDays(1), - daily.getTradeDate().atStartOfDay().atZone(ZoneId.systemDefault()), - daily.getHfqOpen(), - daily.getHfqHigh(), - daily.getHfqLow(), - daily.getHfqClose(), - daily.getVolume() - ))); - return Map.of( - "code", stock.getCode(), - "name", stock.getName(), - "std", NumberUtil.roundStr(statistics.getStandardDeviation(), 2), - "increase", NumberUtil.roundStr((dailyList.getLast().getHfqClose() - dailyList.getFirst().getHfqClose()) * 100.0 / dailyList.getFirst().getHfqClose(), 2) - ); - }) - .sorted((t1, t2) -> Double.compare(Double.parseDouble(t2.get("increase")), Double.parseDouble(t1.get("increase")))) - .toList(); - render(Map.of("items", data));*/ - - var lines = new ArrayList(); - for (int year = 2024; year < 2025; year++) { - var candidates = pyramidStockSelector.select(new PyramidStockSelector.Request(year)); - for (StockSelector.Candidate candidate : candidates) { - log.info("{} {}", candidate.stock().getName(), candidate.score()); - } - var stocks = candidates.stream() - .map(StockSelector.Candidate::stock) - .collect(Collectors.toSet()); - var results = assessmentService.assess(stocks, year); - int up = results.stream() - .filter(result -> result.change() > 0) - .mapToInt(result -> 1) - .sum(); - lines.add(NumberUtil.roundStr(up * 100.0 / results.size(), 2)); - results.forEach(result -> log.info("{} {} {} {} {}", result.stock().getCode(), result.stock().getName(), result.change(), result.std(), result.industryTop())); - } - for (int index = 0, year = 2010; index < lines.size(); index++, year++) { - log.info("胜率: {} {}", year, lines.get(index)); + var dailies = dailyRepository.findAll(QDaily.daily.factor.isNull(), QDaily.daily.tradeDate.asc()); + for (var daily : dailies) { + log.info("{} {} {}", daily.getStock().getCode(), daily.getStock().getName(), daily.getTradeDate()); + // var response = tuShareService.factorList(daily.getTradeDate(), daily.getStock().getCode()); + // var factor = response.data().items().getFirst().get(2); } } }