diff --git a/leopard-strategy/pom.xml b/leopard-strategy/pom.xml
index 25db35b..2ec3d9c 100644
--- a/leopard-strategy/pom.xml
+++ b/leopard-strategy/pom.xml
@@ -20,15 +20,27 @@
org.springframework.boot
spring-boot-starter
+
+ com.yomahub
+ liteflow-spring-boot-starter
+
io.github.ralfkonrad.quantlib_for_maven
quantlib
- 1.39.0
org.ta4j
ta4j-core
- 0.17
+
+
+ org.commonmark
+ commonmark
+ 0.26.0
+
+
+ org.commonmark
+ commonmark-ext-gfm-tables
+ 0.26.0
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 592354a..e52a196 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,28 +1,46 @@
package com.lanyuanxiaoyao.leopard.strategy;
-import cn.hutool.core.lang.Tuple;
-import com.lanyuanxiaoyao.leopard.core.entity.Daily;
+import cn.hutool.core.map.MapUtil;
+import cn.hutool.core.util.StrUtil;
+import com.fasterxml.jackson.databind.ObjectMapper;
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.repository.DailyRepository;
import com.lanyuanxiaoyao.leopard.core.repository.StockRepository;
import jakarta.annotation.Resource;
import jakarta.transaction.Transactional;
-import java.util.stream.Collectors;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.time.Duration;
+import java.time.ZoneId;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics;
+import org.commonmark.Extension;
+import org.commonmark.ext.gfm.tables.TablesExtension;
+import org.commonmark.parser.Parser;
+import org.commonmark.renderer.html.HtmlRenderer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
+import org.ta4j.core.BaseBar;
+import org.ta4j.core.BaseBarSeriesBuilder;
+import org.ta4j.core.indicators.SMAIndicator;
+import org.ta4j.core.indicators.helpers.ClosePriceIndicator;
@Slf4j
@SpringBootApplication(scanBasePackages = "com.lanyuanxiaoyao.leopard")
@EnableJpaAuditing
public class StrategyApplication {
+ private static final List extensions = List.of(TablesExtension.create());
+ private static final Parser parser = Parser.builder().extensions(extensions).build();
+ private static final HtmlRenderer render = HtmlRenderer.builder().extensions(extensions).build();
+ private static final ObjectMapper mapper = new ObjectMapper();
@Resource
private StockRepository stockRepository;
@Resource
@@ -32,34 +50,316 @@ public class StrategyApplication {
SpringApplication.run(StrategyApplication.class, args);
}
+ private static void render(Map data) throws IOException {
+ Files.writeString(
+ Path.of("result.html"),
+ StrUtil.format(
+ // language=HTML
+ """
+
+
+
+
+
+ Strategy
+
+
+
+
+
+
+
+
+
+
+
+ """,
+ mapper.writeValueAsString(data)
+ )
+ );
+ }
+
@Transactional(rollbackOn = Throwable.class)
@EventListener(ApplicationReadyEvent.class)
- public void test() {
- var dailies = dailyRepository.findAll(
+ public void test() throws IOException {
+ /*var dailies = dailyRepository.findAll(
QDaily.daily.tradeDate.year().eq(2025),
Sort.by(Daily_.TRADE_DATE)
);
- // log.info("OpenDate: {}, CloseDate: {}", dailies.getFirst().getTradeDate(), dailies.getLast().getTradeDate());
- // var closes = dailies.stream().map(Daily::getHfqClose).toList();
- // var statistics = new DescriptiveStatistics();
- // closes.forEach(statistics::addValue);
- // log.info("Ascending: {}", (closes.getLast() - closes.getFirst()) * 100.0 / closes.getFirst());
- // log.info("STD: {}", statistics.getStandardDeviation());
- dailies.stream()
+ var data = dailies.stream()
.collect(Collectors.groupingBy(Daily::getStock))
.entrySet()
- .stream()
+ .parallelStream()
.map(entry -> {
var stock = entry.getKey();
- var statistics = new DescriptiveStatistics();
- entry.getValue()
+ var dailyList = entry.getValue()
.stream()
- .map(Daily::getHfqClose)
- .forEach(statistics::addValue);
- var std = statistics.getStandardDeviation();
- return new Tuple(stock, std);
+ .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(t2.get(1), t1.get(1)))
- .forEachOrdered(tuple -> log.info("Stock: {}, STD: {}", ((Stock) tuple.get(0)).getCode(), tuple.get(1)));
+ .sorted((t1, t2) -> Double.compare(Double.parseDouble(t2.get("increase")), Double.parseDouble(t1.get("increase"))))
+ .toList();
+ render(Map.of("items", data));*/
+ var dailies = dailyRepository.findAll(
+ QDaily.daily.stock.code.eq("000002.SZ")
+ .and(QDaily.daily.tradeDate.year().goe(2023)),
+ Sort.by(Daily_.TRADE_DATE)
+ );
+ var barSeries = new BaseBarSeriesBuilder().build();
+ dailies.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()
+ )));
+ var sma10 = new SMAIndicator(new ClosePriceIndicator(barSeries), 10);
+ var sma60 = new SMAIndicator(new ClosePriceIndicator(barSeries), 60);
+ var sma120 = new SMAIndicator(new ClosePriceIndicator(barSeries), 120);
+
+ var xList = new ArrayList();
+ var yList = new ArrayList>();
+ var sma10List = new ArrayList();
+ var sma60List = new ArrayList();
+ var sma120List = new ArrayList();
+ for (int index = 0; index < dailies.size(); index++) {
+ var daily = dailies.get(index);
+ xList.add(daily.getTradeDate().toString());
+ yList.add(List.of(daily.getHfqOpen(), daily.getHfqClose(), daily.getHfqLow(), daily.getHfqHigh()));
+ sma10List.add(sma10.getValue(index).doubleValue());
+ sma60List.add(sma60.getValue(index).doubleValue());
+ sma120List.add(sma120.getValue(index).doubleValue());
+ }
+ render(
+ MapUtil.builder()
+ .put("xList", xList)
+ .put("yList", yList)
+ .put("sma10", sma10List)
+ .put("sma60", sma60List)
+ .put("sma120", sma120List)
+ .build()
+ );
}
}
diff --git a/leopard-strategy/src/main/resources/application.yml b/leopard-strategy/src/main/resources/application.yml
index 6fcbf15..2e177ab 100644
--- a/leopard-strategy/src/main/resources/application.yml
+++ b/leopard-strategy/src/main/resources/application.yml
@@ -2,7 +2,7 @@ spring:
application:
name: leopard-strategy
datasource:
- url: jdbc:postgresql://81.71.3.24:6785/leopard_dev
+ url: jdbc:postgresql://81.71.3.24:6785/leopard
username: leopard
password: '9NEzFzovnddf@PyEP?e*AYAWnCyd7UhYwQK$pJf>7?ccFiN^x4$eKEZ5~E<7<+~X'
driver-class-name: org.postgresql.Driver
@@ -12,3 +12,6 @@ spring:
banner-mode: off
fenix:
print-banner: false
+liteflow:
+ print-banner: false
+ check-node-exists: false
diff --git a/leopard-strategy/src/test/java/com/lanyuanxiaoyao/leopard/strategy/TestMarkdown.java b/leopard-strategy/src/test/java/com/lanyuanxiaoyao/leopard/strategy/TestMarkdown.java
new file mode 100644
index 0000000..ab821c2
--- /dev/null
+++ b/leopard-strategy/src/test/java/com/lanyuanxiaoyao/leopard/strategy/TestMarkdown.java
@@ -0,0 +1,44 @@
+package com.lanyuanxiaoyao.leopard.strategy;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+import org.commonmark.ext.gfm.tables.TablesExtension;
+import org.commonmark.parser.Parser;
+import org.commonmark.renderer.html.HtmlRenderer;
+
+/**
+ * Markdown Render
+ *
+ * @author lanyuanxiaoyao
+ * @version 20250918
+ */
+public class TestMarkdown {
+ public static void main(String[] args) throws IOException {
+ var extensions = List.of(TablesExtension.create());
+ var parser = Parser.builder()
+ .extensions(extensions)
+ .build();
+ var render = HtmlRenderer.builder()
+ .extensions(extensions)
+ .build();
+ var result = render.render(parser.parse(
+ // language=Markdown
+ """
+ ### Hello
+
+ ```echarts
+ System.out.println("go");
+ ```
+
+ > I am iron man
+
+ | | |
+ |------|----|
+ | Tony | 12 |
+ """
+ ));
+ Files.writeString(Path.of("result.html"), result);
+ }
+}