From 9a31b8cae4e63b9c0b0fdd384164e1823b583c1c Mon Sep 17 00:00:00 2001 From: lanyuanxiaoyao Date: Tue, 11 Nov 2025 23:33:13 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=AE=80=E5=8C=96=E4=BA=A4=E6=98=93?= =?UTF-8?q?=E8=AE=A1=E7=AE=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- leopard-core/pom.xml | 6 ++++ .../leopard/core/strategy/TradeEngine.java | 34 ++++++++++++++++--- .../leopard/strategy/StrategyApplication.java | 4 ++- .../resources/templates/backtest_report.html | 5 +++ 4 files changed, 43 insertions(+), 6 deletions(-) diff --git a/leopard-core/pom.xml b/leopard-core/pom.xml index e657099..6e78fca 100644 --- a/leopard-core/pom.xml +++ b/leopard-core/pom.xml @@ -33,6 +33,12 @@ hutool-http + + org.icepear.echarts + echarts-java + 1.1.0 + + io.github.ralfkonrad.quantlib_for_maven quantlib diff --git a/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/strategy/TradeEngine.java b/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/strategy/TradeEngine.java index 78d66a1..be8d214 100644 --- a/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/strategy/TradeEngine.java +++ b/leopard-core/src/main/java/com/lanyuanxiaoyao/leopard/core/strategy/TradeEngine.java @@ -8,6 +8,7 @@ import com.lanyuanxiaoyao.leopard.core.repository.DailyRepository; import java.time.LocalDate; import java.util.ArrayList; import java.util.List; +import java.util.function.Predicate; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -50,9 +51,6 @@ public class TradeEngine { .filter(daily -> daily.getTradeDate().isBefore(currentDate)) .toList() ); - if (trade == 0) { - continue; - } var daily = dailies.stream() .filter(d -> ObjectUtil.equals(d.getTradeDate(), currentDate)) .findFirst() @@ -75,20 +73,46 @@ public class TradeEngine { } public int getVolume() { + return getVolume(trade -> true); + } + + public int getVolume(LocalDate date) { + return getVolume(trade -> trade.date().isBefore(date) || trade.date().isEqual(date)); + } + + private int getVolume(Predicate predicate) { return trades.stream() + .filter(predicate) .mapToInt(Trade::volume) .sum(); } public double getCash() { + return getCash(trade -> true); + } + + public double getCash(LocalDate date) { + return getCash(trade -> trade.date().isBefore(date) || trade.date().isEqual(date)); + } + + private double getCash(Predicate predicate) { return trades.stream() + .filter(predicate) .mapToDouble(trade -> -1 * trade.volume() * trade.price()) .sum(); } public double getPrice() { - int volume = getVolume(); - return volume == 0 ? 0 : getCash() / volume; + return getPrice(trade -> true); + } + + public double getPrice(LocalDate date) { + return getPrice(trade -> trade.date().isBefore(date) || trade.date().isEqual(date)); + } + + public double getPrice(Predicate predicate) { + int volume = getVolume(predicate); + return volume == 0 ? 0 : getCash(predicate) / volume; } public record Trade( 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 efdf1a0..89acd34 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 @@ -65,7 +65,7 @@ public class StrategyApplication { var startDate = LocalDate.of(2024, 12, 1); var endDate = LocalDate.of(2024, 12, 31); var charts = new ArrayList(); - List.of("000048.SZ", "000333.SZ", "000568.SZ", "000596.SZ"/*, "000651.SZ", "000848.SZ", "000858.SZ", "000933.SZ", "002027.SZ", "002032.SZ", "002142.SZ", "002192.SZ", "002415.SZ", "002432.SZ", "002475.SZ", "002517.SZ", "002555.SZ", "002648.SZ", "002756.SZ", "002847.SZ", "600036.SH", "600096.SH", "600132.SH", "600188.SH", "600309.SH", "600426.SH", "600436.SH", "600519.SH", "600546.SH", "600563.SH", "600702.SH", "600779.SH", "600803.SH", "600809.SH", "600961.SH", "601001.SH", "601100.SH", "601138.SH", "601225.SH", "601899.SH", "601919.SH", "603195.SH", "603198.SH", "603288.SH", "603369.SH", "603444.SH", "603565.SH", "603568.SH", "603605.SH", "603688.SH"*/) + List.of("000048.SZ", "000333.SZ", "000568.SZ", "000596.SZ", "000651.SZ", "000848.SZ", "000858.SZ", "000933.SZ", "002027.SZ", "002032.SZ", "002142.SZ", "002192.SZ", "002415.SZ", "002432.SZ", "002475.SZ", "002517.SZ", "002555.SZ", "002648.SZ", "002756.SZ", "002847.SZ", "600036.SH", "600096.SH"/*, "600132.SH", "600188.SH", "600309.SH", "600426.SH", "600436.SH", "600519.SH", "600546.SH", "600563.SH", "600702.SH", "600779.SH", "600803.SH", "600809.SH", "600961.SH", "601001.SH", "601100.SH", "601138.SH", "601225.SH", "601899.SH", "601919.SH", "603195.SH", "603198.SH", "603288.SH", "603369.SH", "603444.SH", "603565.SH", "603568.SH", "603605.SH", "603688.SH"*/) .parallelStream() .forEach(code -> { var stock = stockRepository.findOne(QStock.stock.code.eq(code)).orElseThrow(); @@ -112,6 +112,7 @@ public class StrategyApplication { charts.add( Dict.create() .set("title", code) + .set("type", "candle") .set( "data", Dict.create() @@ -125,6 +126,7 @@ public class StrategyApplication { "points", asset.getTrades() .stream() + .filter(trade -> trade.volume() != 0) .map(trade -> Dict.create() .set("value", trade.volume()) .set("itemStyle", Dict.create() diff --git a/leopard-strategy/src/main/resources/templates/backtest_report.html b/leopard-strategy/src/main/resources/templates/backtest_report.html index eb736e9..74b2715 100644 --- a/leopard-strategy/src/main/resources/templates/backtest_report.html +++ b/leopard-strategy/src/main/resources/templates/backtest_report.html @@ -397,6 +397,11 @@ type: 'tabs', tabsMode: 'vertical', tabs: data.map(item => { + let body = {} + if (item?.type === 'candle') { + body = Object.keys(item?.data ?? {}) + .map(key => candleChart(key, item.data[key])) + } return { title: item?.title, body: Object.keys(item?.data ?? {})