feat: 增加年线和周线的计算
This commit is contained in:
15
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
15
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
@@ -0,0 +1,15 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="NullableProblems" enabled="false" level="WARNING" enabled_by_default="false">
|
||||
<option name="REPORT_NULLABLE_METHOD_OVERRIDES_NOTNULL" value="true" />
|
||||
<option name="REPORT_NOT_ANNOTATED_METHOD_OVERRIDES_NOTNULL" value="true" />
|
||||
<option name="REPORT_NOTNULL_PARAMETER_OVERRIDES_NULLABLE" value="true" />
|
||||
<option name="REPORT_NOT_ANNOTATED_PARAMETER_OVERRIDES_NOTNULL" value="true" />
|
||||
<option name="REPORT_NOT_ANNOTATED_GETTER" value="true" />
|
||||
<option name="REPORT_NOT_ANNOTATED_SETTER_PARAMETER" value="true" />
|
||||
<option name="REPORT_ANNOTATION_NOT_PROPAGATED_TO_OVERRIDERS" value="true" />
|
||||
<option name="REPORT_NULLS_PASSED_TO_NON_ANNOTATED_METHOD" value="true" />
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,10 +61,6 @@ public class Stock extends SimpleEntity {
|
||||
@ToString.Exclude
|
||||
private Set<Daily> dailies;
|
||||
|
||||
@OneToMany(mappedBy = "stock", cascade = CascadeType.REMOVE)
|
||||
@ToString.Exclude
|
||||
private Set<Yearly> yearlies;
|
||||
|
||||
@OneToMany(mappedBy = "stock", cascade = CascadeType.REMOVE)
|
||||
@ToString.Exclude
|
||||
private Set<FinanceIndicator> indicators;
|
||||
|
||||
@@ -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
|
||||
) {
|
||||
}
|
||||
@@ -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
|
||||
) {
|
||||
}
|
||||
@@ -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
|
||||
) {
|
||||
}
|
||||
@@ -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<Daily> {
|
||||
|
||||
@EntityGraph(attributePaths = {"stock"})
|
||||
@Override
|
||||
List<Daily> findAll(Predicate predicate, Sort sort);
|
||||
List<Daily> findAll(Predicate predicate, OrderSpecifier<?>... orders);
|
||||
}
|
||||
|
||||
@@ -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<Stock> {
|
||||
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<Stock> {
|
||||
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<Stock> {
|
||||
public Optional<Daily> findDailyLatest(Long stockId) {
|
||||
return dailyRepository.findLatest(stockId);
|
||||
}
|
||||
|
||||
@Cacheable(value = "long-cache", sync = true)
|
||||
public List<Yearly> 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<Monthly> 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<Weekly> 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<Daily> dailies, Function<Daily, Double> function) {
|
||||
return dailies.stream()
|
||||
.map(function)
|
||||
.filter(ObjectUtil::isNotNull)
|
||||
.mapToDouble(Double::doubleValue)
|
||||
.max()
|
||||
.orElse(0);
|
||||
}
|
||||
|
||||
private Double minFromDaily(List<Daily> dailies, Function<Daily, Double> function) {
|
||||
return dailies.stream()
|
||||
.map(function)
|
||||
.filter(ObjectUtil::isNotNull)
|
||||
.mapToDouble(Double::doubleValue)
|
||||
.min()
|
||||
.orElse(0);
|
||||
}
|
||||
|
||||
private Double sumFromDaily(List<Daily> dailies, Function<Daily, Double> 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) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user