diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000..786a0ea
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/entity/Daily.java b/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/entity/Daily.java
index 9a74566..0b87536 100644
--- a/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/entity/Daily.java
+++ b/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/entity/Daily.java
@@ -1,5 +1,6 @@
package com.lanyuanxiaoyao.leopard.core.entity;
+import cn.hutool.core.util.ObjectUtil;
import com.lanyuanxiaoyao.leopard.core.Constants;
import com.lanyuanxiaoyao.service.template.entity.SimpleEntity;
import jakarta.persistence.Column;
@@ -58,18 +59,18 @@ public class Daily extends SimpleEntity {
private Stock stock;
public Double getHfqOpen() {
- return open * factor;
+ return open * ObjectUtil.defaultIfNull(factor, 1.0);
}
public Double getHfqClose() {
- return close * factor;
+ return close * ObjectUtil.defaultIfNull(factor, 1.0);
}
public Double getHfqHigh() {
- return high * factor;
+ return high * ObjectUtil.defaultIfNull(factor, 1.0);
}
public Double getHfqLow() {
- return low * factor;
+ return low * ObjectUtil.defaultIfNull(factor, 1.0);
}
}
diff --git a/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/entity/Stock.java b/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/entity/Stock.java
index 076b6e2..50a4707 100644
--- a/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/entity/Stock.java
+++ b/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/entity/Stock.java
@@ -61,10 +61,6 @@ public class Stock extends SimpleEntity {
@ToString.Exclude
private Set dailies;
- @OneToMany(mappedBy = "stock", cascade = CascadeType.REMOVE)
- @ToString.Exclude
- private Set yearlies;
-
@OneToMany(mappedBy = "stock", cascade = CascadeType.REMOVE)
@ToString.Exclude
private Set indicators;
diff --git a/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/entity/dto/Monthly.java b/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/entity/dto/Monthly.java
new file mode 100644
index 0000000..1569ae6
--- /dev/null
+++ b/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/entity/dto/Monthly.java
@@ -0,0 +1,18 @@
+package com.lanyuanxiaoyao.leopard.core.entity.dto;
+
+import java.time.LocalDate;
+
+public record Monthly(
+ LocalDate tradeDate,
+ int year,
+ int month,
+ Double open,
+ Double high,
+ Double low,
+ Double close,
+ Double priceChangeAmount,
+ Double priceFluctuationRange,
+ Double volume,
+ Double turnover
+) {
+}
diff --git a/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/entity/dto/Weekly.java b/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/entity/dto/Weekly.java
new file mode 100644
index 0000000..ae8ee0c
--- /dev/null
+++ b/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/entity/dto/Weekly.java
@@ -0,0 +1,18 @@
+package com.lanyuanxiaoyao.leopard.core.entity.dto;
+
+import java.time.LocalDate;
+
+public record Weekly(
+ LocalDate tradeDate,
+ int year,
+ int week,
+ Double open,
+ Double high,
+ Double low,
+ Double close,
+ Double priceChangeAmount,
+ Double priceFluctuationRange,
+ Double volume,
+ Double turnover
+) {
+}
diff --git a/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/entity/dto/Yearly.java b/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/entity/dto/Yearly.java
new file mode 100644
index 0000000..3458458
--- /dev/null
+++ b/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/entity/dto/Yearly.java
@@ -0,0 +1,17 @@
+package com.lanyuanxiaoyao.leopard.core.entity.dto;
+
+import java.time.LocalDate;
+
+public record Yearly(
+ LocalDate tradeDate,
+ int year,
+ Double open,
+ Double high,
+ Double low,
+ Double close,
+ Double priceChangeAmount,
+ Double priceFluctuationRange,
+ Double volume,
+ Double turnover
+) {
+}
diff --git a/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/repository/DailyRepository.java b/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/repository/DailyRepository.java
index b1fd0fa..d61a52f 100644
--- a/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/repository/DailyRepository.java
+++ b/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/repository/DailyRepository.java
@@ -2,12 +2,12 @@ package com.lanyuanxiaoyao.leopard.core.repository;
import com.lanyuanxiaoyao.leopard.core.entity.Daily;
import com.lanyuanxiaoyao.service.template.repository.SimpleRepository;
+import com.querydsl.core.types.OrderSpecifier;
import com.querydsl.core.types.Predicate;
import java.time.LocalDate;
import java.util.List;
import java.util.Optional;
import java.util.Set;
-import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
@@ -32,5 +32,5 @@ public interface DailyRepository extends SimpleRepository {
@EntityGraph(attributePaths = {"stock"})
@Override
- List findAll(Predicate predicate, Sort sort);
+ List findAll(Predicate predicate, OrderSpecifier>... orders);
}
diff --git a/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/service/StockService.java b/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/service/StockService.java
index cbc72dd..712c575 100644
--- a/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/service/StockService.java
+++ b/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/service/StockService.java
@@ -1,22 +1,28 @@
package com.lanyuanxiaoyao.leopard.core.service;
+import cn.hutool.core.util.ObjectUtil;
import com.lanyuanxiaoyao.leopard.core.entity.Daily;
-import com.lanyuanxiaoyao.leopard.core.entity.Daily_;
import com.lanyuanxiaoyao.leopard.core.entity.FinanceIndicator;
-import com.lanyuanxiaoyao.leopard.core.entity.FinanceIndicator_;
import com.lanyuanxiaoyao.leopard.core.entity.QDaily;
import com.lanyuanxiaoyao.leopard.core.entity.QFinanceIndicator;
import com.lanyuanxiaoyao.leopard.core.entity.Stock;
+import com.lanyuanxiaoyao.leopard.core.entity.dto.Monthly;
+import com.lanyuanxiaoyao.leopard.core.entity.dto.Weekly;
+import com.lanyuanxiaoyao.leopard.core.entity.dto.Yearly;
import com.lanyuanxiaoyao.leopard.core.repository.DailyRepository;
import com.lanyuanxiaoyao.leopard.core.repository.FinanceIndicatorRepository;
import com.lanyuanxiaoyao.leopard.core.repository.StockRepository;
import com.lanyuanxiaoyao.service.template.service.SimpleServiceSupport;
import java.time.LocalDate;
+import java.time.temporal.ChronoField;
+import java.time.temporal.WeekFields;
+import java.util.Comparator;
import java.util.List;
import java.util.Optional;
+import java.util.function.Function;
+import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.Cacheable;
-import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
/**
@@ -49,7 +55,7 @@ public class StockService extends SimpleServiceSupport {
return financeIndicatorRepository.findAll(
QFinanceIndicator.financeIndicator.stock.id.eq(stockId)
.and(QFinanceIndicator.financeIndicator.year.between(current.minusYears(years).getYear(), current.getYear())),
- Sort.by(Sort.Direction.ASC, FinanceIndicator_.YEAR)
+ QDaily.daily.tradeDate.asc()
);
}
@@ -59,7 +65,7 @@ public class StockService extends SimpleServiceSupport {
return dailyRepository.findAll(
QDaily.daily.stock.id.eq(stockId)
.and(QDaily.daily.tradeDate.between(current.minusDays(days), current)),
- Sort.by(Sort.Direction.ASC, Daily_.TRADE_DATE)
+ QDaily.daily.tradeDate.asc()
);
}
@@ -67,4 +73,174 @@ public class StockService extends SimpleServiceSupport {
public Optional findDailyLatest(Long stockId) {
return dailyRepository.findLatest(stockId);
}
+
+ @Cacheable(value = "long-cache", sync = true)
+ public List findYearlyRecent(Long stockId, int years) {
+ var current = LocalDate.now().withMonth(1).withDayOfMonth(1);
+ var start = current.minusYears(years).getYear();
+ var end = current.getYear();
+ return dailyRepository
+ .findAll(
+ QDaily.daily.stock.id.eq(stockId)
+ .and(QDaily.daily.tradeDate.year().gt(start))
+ .and(QDaily.daily.tradeDate.year().loe(end)),
+ QDaily.daily.tradeDate.asc()
+ )
+ .stream()
+ .collect(Collectors.groupingBy(daily -> daily.getTradeDate().getYear()))
+ .entrySet()
+ .stream()
+ .map(entry -> {
+ var year = entry.getKey();
+ var dailies = entry.getValue();
+ var open = dailies.getFirst().getHfqOpen();
+ var close = dailies.getLast().getHfqClose();
+ return new Yearly(
+ LocalDate.of(year, 1, 1),
+ year,
+ open,
+ maxFromDaily(dailies, Daily::getHfqHigh),
+ minFromDaily(dailies, Daily::getHfqLow),
+ close,
+ close - open,
+ (close - open) / open * 100,
+ sumFromDaily(dailies, Daily::getVolume),
+ sumFromDaily(dailies, Daily::getTurnover)
+ );
+ })
+ .sorted(Comparator.comparingInt(Yearly::year))
+ .toList();
+ }
+
+ @Cacheable(value = "long-cache", sync = true)
+ public List findMonthlyRecent(Long stockId, int months) {
+ var end = LocalDate.now().withDayOfMonth(1);
+ var start = end.minusMonths(months);
+ return dailyRepository
+ .findAll(
+ QDaily.daily.stock.id.eq(stockId)
+ .and(
+ QDaily.daily.tradeDate.year().gt(start.getYear())
+ .or(
+ QDaily.daily.tradeDate.year().eq(start.getYear())
+ .and(QDaily.daily.tradeDate.month().gt(start.getMonthValue()))
+ )
+ )
+ .and(
+ QDaily.daily.tradeDate.year().lt(end.getYear())
+ .or(
+ QDaily.daily.tradeDate.year().eq(end.getYear())
+ .and(QDaily.daily.tradeDate.month().loe(end.getMonthValue()))
+ )
+ ),
+ QDaily.daily.tradeDate.asc()
+ )
+ .stream()
+ .collect(Collectors.groupingBy(daily -> new YearAndMonth(daily.getTradeDate().getYear(), daily.getTradeDate().getMonthValue())))
+ .entrySet()
+ .stream()
+ .map(entry -> {
+ var yearAndMonth = entry.getKey();
+ var dailies = entry.getValue();
+ var open = dailies.getFirst().getHfqOpen();
+ var close = dailies.getLast().getHfqClose();
+ return new Monthly(
+ LocalDate.of(yearAndMonth.year, yearAndMonth.month, 1),
+ yearAndMonth.year,
+ yearAndMonth.month,
+ open,
+ maxFromDaily(dailies, Daily::getHfqHigh),
+ minFromDaily(dailies, Daily::getHfqLow),
+ close,
+ close - open,
+ (close - open) / open * 100,
+ sumFromDaily(dailies, Daily::getVolume),
+ sumFromDaily(dailies, Daily::getTurnover)
+ );
+ })
+ .sorted(Comparator.comparingInt(monthly -> monthly.year() * 100 + monthly.month()))
+ .toList();
+ }
+
+ @Cacheable(value = "long-cache", sync = true)
+ public List findWeeklyRecent(Long stockId, int weeks) {
+ var end = LocalDate.now().with(ChronoField.DAY_OF_WEEK, 1);
+ var start = end.minusWeeks(weeks);
+ return dailyRepository
+ .findAll(
+ QDaily.daily.stock.id.eq(stockId)
+ .and(
+ QDaily.daily.tradeDate.year().gt(start.getYear())
+ .or(
+ QDaily.daily.tradeDate.year().eq(start.getYear())
+ .and(QDaily.daily.tradeDate.week().gt(start.get(WeekFields.ISO.weekOfYear())))
+ )
+ )
+ .and(
+ QDaily.daily.tradeDate.year().lt(end.getYear())
+ .or(
+ QDaily.daily.tradeDate.year().eq(end.getYear())
+ .and(QDaily.daily.tradeDate.month().loe(end.get(WeekFields.ISO.weekOfYear())))
+ )
+ ),
+ QDaily.daily.tradeDate.asc()
+ )
+ .stream()
+ .collect(Collectors.groupingBy(daily -> new YearAndWeek(daily.getTradeDate().getYear(), daily.getTradeDate().get(WeekFields.ISO.weekOfYear()))))
+ .entrySet()
+ .stream()
+ .map(entry -> {
+ var yearAndWeek = entry.getKey();
+ var dailies = entry.getValue();
+ var open = dailies.getFirst().getHfqOpen();
+ var close = dailies.getLast().getHfqClose();
+ return new Weekly(
+ LocalDate.of(yearAndWeek.year, 1, 1).with(WeekFields.ISO.weekOfYear(), yearAndWeek.week),
+ yearAndWeek.year,
+ yearAndWeek.week,
+ open,
+ maxFromDaily(dailies, Daily::getHfqHigh),
+ minFromDaily(dailies, Daily::getHfqLow),
+ close,
+ close - open,
+ (close - open) / open * 100,
+ sumFromDaily(dailies, Daily::getVolume),
+ sumFromDaily(dailies, Daily::getTurnover)
+ );
+ })
+ .sorted(Comparator.comparingInt(weekly -> weekly.year() * 100 + weekly.week()))
+ .toList();
+ }
+
+ private Double maxFromDaily(List dailies, Function function) {
+ return dailies.stream()
+ .map(function)
+ .filter(ObjectUtil::isNotNull)
+ .mapToDouble(Double::doubleValue)
+ .max()
+ .orElse(0);
+ }
+
+ private Double minFromDaily(List dailies, Function function) {
+ return dailies.stream()
+ .map(function)
+ .filter(ObjectUtil::isNotNull)
+ .mapToDouble(Double::doubleValue)
+ .min()
+ .orElse(0);
+ }
+
+ private Double sumFromDaily(List dailies, Function function) {
+ return dailies.stream()
+ .map(function)
+ .filter(ObjectUtil::isNotNull)
+ .mapToDouble(Double::doubleValue)
+ .sum();
+ }
+
+ private record YearAndMonth(int year, int month) {
+ }
+
+ private record YearAndWeek(int year, int week) {
+ }
}
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 2309b50..b362679 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
@@ -2,9 +2,11 @@ package com.lanyuanxiaoyao.leopard.strategy;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.lanyuanxiaoyao.leopard.core.entity.QDaily;
+import com.lanyuanxiaoyao.leopard.core.entity.QStock;
import com.lanyuanxiaoyao.leopard.core.repository.DailyRepository;
+import com.lanyuanxiaoyao.leopard.core.repository.StockRepository;
import com.lanyuanxiaoyao.leopard.core.service.AssessmentService;
+import com.lanyuanxiaoyao.leopard.core.service.StockService;
import com.lanyuanxiaoyao.leopard.core.service.TuShareService;
import com.lanyuanxiaoyao.leopard.core.service.selector.PyramidStockSelector;
import jakarta.annotation.Resource;
@@ -33,6 +35,10 @@ public class StrategyApplication {
private TuShareService tuShareService;
@Resource
private DailyRepository dailyRepository;
+ @Resource
+ private StockRepository stockRepository;
+ @Resource
+ private StockService stockService;
public static void main(String[] args) {
SpringApplication.run(StrategyApplication.class, args);
@@ -272,11 +278,10 @@ public class StrategyApplication {
@Transactional(readOnly = true)
@EventListener(ApplicationReadyEvent.class)
public void test() {
- 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);
+ var stock = stockRepository.findOne(QStock.stock.code.eq("000001.SZ")).orElseThrow();
+ var weeklies = stockService.findWeeklyRecent(stock.getId(), 2);
+ for (var weekly : weeklies) {
+ log.info("{}", weekly);
}
}
}