feat: 增加一点测试
This commit is contained in:
@@ -55,4 +55,20 @@ public class Daily extends SimpleEntity {
|
|||||||
@JoinColumn(nullable = false)
|
@JoinColumn(nullable = false)
|
||||||
@ToString.Exclude
|
@ToString.Exclude
|
||||||
private Stock stock;
|
private Stock stock;
|
||||||
|
|
||||||
|
public Double getHfqOpen() {
|
||||||
|
return open * factor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double getHfqClose() {
|
||||||
|
return close * factor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double getHfqHigh() {
|
||||||
|
return high * factor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double getHfqLow() {
|
||||||
|
return low * factor;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -126,16 +126,15 @@ Content-Type: application/json
|
|||||||
"api_name": "stk_factor_pro",
|
"api_name": "stk_factor_pro",
|
||||||
"token": "{{api_key}}",
|
"token": "{{api_key}}",
|
||||||
"params": {
|
"params": {
|
||||||
"ts_code": "000001.SZ",
|
"ts_code": "000002.SZ",
|
||||||
"trade_date": "20250225"
|
"trade_date": "20250102"
|
||||||
},
|
},
|
||||||
"fields": [
|
"fields": [
|
||||||
"ts_code",
|
"ts_code",
|
||||||
"trade_date",
|
"trade_date",
|
||||||
"close",
|
"close",
|
||||||
"close_qfq",
|
"close_qfq",
|
||||||
"close_hfq",
|
"close_hfq"
|
||||||
"ema_hfq_5"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
package com.lanyuanxiaoyao.leopard.strategy;
|
package com.lanyuanxiaoyao.leopard.strategy;
|
||||||
|
|
||||||
import cn.hutool.core.util.ArrayUtil;
|
import cn.hutool.core.lang.Tuple;
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
import com.lanyuanxiaoyao.leopard.core.entity.Daily;
|
||||||
import com.lanyuanxiaoyao.leopard.core.entity.FinanceIndicator;
|
import com.lanyuanxiaoyao.leopard.core.entity.Daily_;
|
||||||
|
import com.lanyuanxiaoyao.leopard.core.entity.QDaily;
|
||||||
import com.lanyuanxiaoyao.leopard.core.entity.Stock;
|
import com.lanyuanxiaoyao.leopard.core.entity.Stock;
|
||||||
import com.lanyuanxiaoyao.leopard.core.repository.DailyRepository;
|
import com.lanyuanxiaoyao.leopard.core.repository.DailyRepository;
|
||||||
import com.lanyuanxiaoyao.leopard.core.repository.StockRepository;
|
import com.lanyuanxiaoyao.leopard.core.repository.StockRepository;
|
||||||
@@ -15,6 +16,7 @@ import org.springframework.boot.SpringApplication;
|
|||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
import org.springframework.boot.context.event.ApplicationReadyEvent;
|
import org.springframework.boot.context.event.ApplicationReadyEvent;
|
||||||
import org.springframework.context.event.EventListener;
|
import org.springframework.context.event.EventListener;
|
||||||
|
import org.springframework.data.domain.Sort;
|
||||||
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
|
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@@ -33,185 +35,31 @@ public class StrategyApplication {
|
|||||||
@Transactional(rollbackOn = Throwable.class)
|
@Transactional(rollbackOn = Throwable.class)
|
||||||
@EventListener(ApplicationReadyEvent.class)
|
@EventListener(ApplicationReadyEvent.class)
|
||||||
public void test() {
|
public void test() {
|
||||||
var stocks = stockRepository.findAllByIndicatorsSizeGreaterThanEqual(5);
|
var dailies = dailyRepository.findAll(
|
||||||
var stocksMap = stocks.stream().collect(Collectors.toMap(Stock::getCode, stock -> stock));
|
QDaily.daily.tradeDate.year().eq(2025),
|
||||||
var scores = stocks.stream().map(Stock::getCode).collect(Collectors.toMap(code -> code, code -> 0));
|
Sort.by(Daily_.TRADE_DATE)
|
||||||
for (Stock stock : stocks) {
|
);
|
||||||
var recentIndicators = stock.getIndicators()
|
// log.info("OpenDate: {}, CloseDate: {}", dailies.getFirst().getTradeDate(), dailies.getLast().getTradeDate());
|
||||||
.stream()
|
// var closes = dailies.stream().map(Daily::getHfqClose).toList();
|
||||||
.sorted((a, b) -> b.getYear() - a.getYear())
|
// var statistics = new DescriptiveStatistics();
|
||||||
.limit(5)
|
// closes.forEach(statistics::addValue);
|
||||||
.toList();
|
// log.info("Ascending: {}", (closes.getLast() - closes.getFirst()) * 100.0 / closes.getFirst());
|
||||||
var latestIndicator = recentIndicators.getFirst();
|
// log.info("STD: {}", statistics.getStandardDeviation());
|
||||||
|
dailies.stream()
|
||||||
var roeScore = 0;
|
.collect(Collectors.groupingBy(Daily::getStock))
|
||||||
if (recentIndicators.stream().noneMatch(indicator -> indicator.getReturnOnEquity() == null || indicator.getReturnOnEquity() < 0)) {
|
.entrySet()
|
||||||
var averageRoe = recentIndicators.stream()
|
|
||||||
.map(FinanceIndicator::getReturnOnEquity)
|
|
||||||
.map(item -> ObjectUtil.defaultIfNull(item, 0.0))
|
|
||||||
.mapToDouble(Double::doubleValue)
|
|
||||||
.average()
|
|
||||||
.orElse(0.0);
|
|
||||||
if (averageRoe >= 35) {
|
|
||||||
roeScore = 550;
|
|
||||||
} else if (averageRoe >= 30) {
|
|
||||||
roeScore = 500;
|
|
||||||
} else if (averageRoe >= 25) {
|
|
||||||
roeScore = 450;
|
|
||||||
} else if (averageRoe >= 20) {
|
|
||||||
roeScore = 400;
|
|
||||||
} else if (averageRoe >= 15) {
|
|
||||||
roeScore = 350;
|
|
||||||
} else if (averageRoe >= 10) {
|
|
||||||
roeScore = 300;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
scores.put(stock.getCode(), scores.get(stock.getCode()) + roeScore);
|
|
||||||
|
|
||||||
var roaScore = 0;
|
|
||||||
if (recentIndicators.stream().noneMatch(indicator -> indicator.getReturnOnAssets() == null)) {
|
|
||||||
var averageRoa = recentIndicators.stream()
|
|
||||||
.map(FinanceIndicator::getReturnOnAssets)
|
|
||||||
.mapToDouble(Double::doubleValue)
|
|
||||||
.average()
|
|
||||||
.orElse(0.0);
|
|
||||||
if (averageRoa >= 15) {
|
|
||||||
roaScore = 100;
|
|
||||||
} else if (averageRoa >= 11) {
|
|
||||||
roaScore = 80;
|
|
||||||
} else if (averageRoa >= 7) {
|
|
||||||
roaScore = 50;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
scores.put(stock.getCode(), scores.get(stock.getCode()) + roaScore);
|
|
||||||
|
|
||||||
var netProfitScore = 0;
|
|
||||||
if (recentIndicators.stream().noneMatch(indicator -> indicator.getNetProfit() == null)) {
|
|
||||||
var averageNetProfit = recentIndicators.stream()
|
|
||||||
.map(FinanceIndicator::getNetProfit)
|
|
||||||
.mapToDouble(Double::doubleValue)
|
|
||||||
.average()
|
|
||||||
.orElse(0.0);
|
|
||||||
if (averageNetProfit >= 10000.0 * 10000000) {
|
|
||||||
netProfitScore = 150;
|
|
||||||
} else if (averageNetProfit >= 1000.0 * 10000000) {
|
|
||||||
netProfitScore = 100;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
scores.put(stock.getCode(), scores.get(stock.getCode()) + netProfitScore);
|
|
||||||
|
|
||||||
var cashScore = 0;
|
|
||||||
if (
|
|
||||||
ArrayUtil.isAllNotNull(latestIndicator.getTotalAssetsTurnover(), latestIndicator.getCashAndCashEquivalentsToTotalAssetsRatio())
|
|
||||||
&& (
|
|
||||||
latestIndicator.getTotalAssetsTurnover() > 0.8 && latestIndicator.getCashAndCashEquivalentsToTotalAssetsRatio() >= 0.1
|
|
||||||
|| latestIndicator.getTotalAssetsTurnover() <= 0.8 && latestIndicator.getCashAndCashEquivalentsToTotalAssetsRatio() >= 0.2
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
cashScore = 50;
|
|
||||||
}
|
|
||||||
scores.put(stock.getCode(), scores.get(stock.getCode()) + cashScore);
|
|
||||||
|
|
||||||
if (ObjectUtil.isNotNull(latestIndicator.getDaysAccountsReceivableTurnover()) && latestIndicator.getDaysAccountsReceivableTurnover() <= 30) {
|
|
||||||
scores.put(stock.getCode(), scores.get(stock.getCode()) + 20);
|
|
||||||
}
|
|
||||||
if (ObjectUtil.isNotNull(latestIndicator.getDaysInventoryTurnover()) && latestIndicator.getDaysInventoryTurnover() <= 30) {
|
|
||||||
scores.put(stock.getCode(), scores.get(stock.getCode()) + 20);
|
|
||||||
}
|
|
||||||
if (ArrayUtil.isAllNotNull(latestIndicator.getDaysAccountsReceivableTurnover(), latestIndicator.getDaysInventoryTurnover())) {
|
|
||||||
if (latestIndicator.getDaysAccountsReceivableTurnover() + latestIndicator.getDaysInventoryTurnover() <= 40) {
|
|
||||||
scores.put(stock.getCode(), scores.get(stock.getCode()) + 20);
|
|
||||||
} else if (latestIndicator.getDaysAccountsReceivableTurnover() + latestIndicator.getDaysInventoryTurnover() <= 60) {
|
|
||||||
scores.put(stock.getCode(), scores.get(stock.getCode()) + 10);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (recentIndicators.stream().noneMatch(indicator -> indicator.getOperatingGrossProfitMargin() == null)) {
|
|
||||||
var stat = new DescriptiveStatistics();
|
|
||||||
recentIndicators.stream()
|
|
||||||
.map(FinanceIndicator::getOperatingGrossProfitMargin)
|
|
||||||
.mapToDouble(Double::doubleValue)
|
|
||||||
.forEach(stat::addValue);
|
|
||||||
if (stat.getStandardDeviation() <= 0.3) {
|
|
||||||
scores.put(stock.getCode(), scores.get(stock.getCode()) + 50);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var operatingSafeMarginScore = 0;
|
|
||||||
if (ObjectUtil.isNotNull(latestIndicator.getOperatingSafetyMarginRatio())) {
|
|
||||||
if (latestIndicator.getOperatingSafetyMarginRatio() >= 70) {
|
|
||||||
operatingSafeMarginScore = 50;
|
|
||||||
} else if (latestIndicator.getOperatingSafetyMarginRatio() >= 50) {
|
|
||||||
operatingSafeMarginScore = 30;
|
|
||||||
} else if (latestIndicator.getOperatingSafetyMarginRatio() >= 30) {
|
|
||||||
operatingSafeMarginScore = 10;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
scores.put(stock.getCode(), scores.get(stock.getCode()) + operatingSafeMarginScore);
|
|
||||||
|
|
||||||
var netProfitAscendingScore = 0;
|
|
||||||
if (recentIndicators.stream().noneMatch(indicator -> indicator.getNetProfit() == null)) {
|
|
||||||
if (recentIndicators.get(0).getNetProfit() > recentIndicators.get(1).getNetProfit()) {
|
|
||||||
netProfitAscendingScore += 30;
|
|
||||||
} else {
|
|
||||||
netProfitAscendingScore -= 30;
|
|
||||||
}
|
|
||||||
if (recentIndicators.get(1).getNetProfit() > recentIndicators.get(2).getNetProfit()) {
|
|
||||||
netProfitAscendingScore += 25;
|
|
||||||
} else {
|
|
||||||
netProfitAscendingScore -= 25;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (recentIndicators.get(2).getNetProfit() > recentIndicators.get(3).getNetProfit()) {
|
|
||||||
netProfitAscendingScore += 20;
|
|
||||||
} else {
|
|
||||||
netProfitAscendingScore -= 20;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (recentIndicators.get(3).getNetProfit() > recentIndicators.get(4).getNetProfit()) {
|
|
||||||
netProfitAscendingScore += 15;
|
|
||||||
} else {
|
|
||||||
netProfitAscendingScore -= 15;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
scores.put(stock.getCode(), scores.get(stock.getCode()) + netProfitAscendingScore);
|
|
||||||
|
|
||||||
var cashAscendingScore = 0;
|
|
||||||
if (recentIndicators.stream().noneMatch(indicator -> indicator.getCashAndCashEquivalents() == null)) {
|
|
||||||
if (recentIndicators.get(0).getCashAndCashEquivalents() > recentIndicators.get(1).getCashAndCashEquivalents()) {
|
|
||||||
cashAscendingScore += 30;
|
|
||||||
} else {
|
|
||||||
cashAscendingScore -= 30;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (recentIndicators.get(1).getCashAndCashEquivalents() > recentIndicators.get(2).getCashAndCashEquivalents()) {
|
|
||||||
cashAscendingScore += 25;
|
|
||||||
} else {
|
|
||||||
cashAscendingScore -= 25;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (recentIndicators.get(2).getCashAndCashEquivalents() > recentIndicators.get(3).getCashAndCashEquivalents()) {
|
|
||||||
cashAscendingScore += 20;
|
|
||||||
} else {
|
|
||||||
cashAscendingScore -= 20;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (recentIndicators.get(3).getCashAndCashEquivalents() > recentIndicators.get(4).getCashAndCashEquivalents()) {
|
|
||||||
cashAscendingScore += 15;
|
|
||||||
} else {
|
|
||||||
cashAscendingScore -= 15;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
scores.put(stock.getCode(), scores.get(stock.getCode()) + cashAscendingScore);
|
|
||||||
}
|
|
||||||
scores.entrySet()
|
|
||||||
.stream()
|
.stream()
|
||||||
.sorted((e1, e2) -> e2.getValue() - e1.getValue())
|
.map(entry -> {
|
||||||
.map(entry -> new StockAndScore(stocksMap.get(entry.getKey()), entry.getValue()))
|
var stock = entry.getKey();
|
||||||
.limit(50)
|
var statistics = new DescriptiveStatistics();
|
||||||
.forEach(ss -> log.info("{} {}", ss.stock.getName(), ss.score));
|
entry.getValue()
|
||||||
}
|
.stream()
|
||||||
|
.map(Daily::getHfqClose)
|
||||||
public record StockAndScore(Stock stock, int score) {
|
.forEach(statistics::addValue);
|
||||||
|
var std = statistics.getStandardDeviation();
|
||||||
|
return new Tuple(stock, std);
|
||||||
|
})
|
||||||
|
.sorted((t1, t2) -> Double.compare(t2.get(1), t1.get(1)))
|
||||||
|
.forEachOrdered(tuple -> log.info("Stock: {}, STD: {}", ((Stock) tuple.get(0)).getCode(), tuple.get(1)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user