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); + } +}