/* * SPDX-License-Identifier: MIT */ package ta4jexamples.backtesting; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.ta4j.core.*; import org.ta4j.core.backtest.BacktestExecutionResult; import org.ta4j.core.backtest.BacktestExecutor; import org.ta4j.core.backtest.TradingStatementExecutionResult.WeightedCriterion; import org.ta4j.core.criteria.drawdown.ReturnOverMaxDrawdownCriterion; import org.ta4j.core.criteria.pnl.NetProfitCriterion; import org.ta4j.core.indicators.averages.SMAIndicator; import org.ta4j.core.indicators.helpers.ClosePriceIndicator; import org.ta4j.core.num.DecimalNum; import org.ta4j.core.num.Num; import org.ta4j.core.reports.BasePerformanceReport; import org.ta4j.core.reports.PositionStatsReport; import org.ta4j.core.reports.TradingStatement; import org.ta4j.core.rules.OverIndicatorRule; import org.ta4j.core.rules.UnderIndicatorRule; import ta4jexamples.datasources.CsvFileBarSeriesDataSource; import java.util.ArrayList; import java.util.List; import java.util.Objects; /** * Example demonstrating how to use {@link BacktestExecutor} for parallel * strategy evaluation. * * This example: * */ public class SimpleMovingAverageRangeBacktest { private static final Logger LOG = LogManager.getLogger(SimpleMovingAverageRangeBacktest.class); private static final int DEFAULT_TOP_STRATEGIES = 3; public static void main(String[] args) { BarSeries series = CsvFileBarSeriesDataSource.loadSeriesFromFile(); int start = 3; int stop = 50; int step = 5; final List strategies = new ArrayList<>(); for (int i = start; i <= stop; i += step) { Strategy strategy = new BaseStrategy("Sma(" + i + ")", createEntryRule(series, i), createExitRule(series, i)); strategies.add(strategy); } BacktestExecutor backtestExecutor = new BacktestExecutor(series); BacktestExecutionResult result = backtestExecutor.executeWithRuntimeReport(strategies, DecimalNum.valueOf(50), Trade.TradeType.BUY); List tradingStatements = selectTopStrategies(result, DEFAULT_TOP_STRATEGIES); LOG.debug("Top {} weighted SMA strategies (7 parts net profit, 3 parts return over max drawdown)", tradingStatements.size()); LOG.debug(printReport(tradingStatements)); } /** * Selects the top strategies for this example using weighted, normalized * ranking. * * @param result full backtest result for the SMA parameter sweep * @param limit maximum number of strategies to keep * @return top strategies ordered by the example weighted criteria */ static List selectTopStrategies(BacktestExecutionResult result, int limit) { Objects.requireNonNull(result, "result cannot be null"); return result.getTopStrategiesWeighted(limit, WeightedCriterion.of(new NetProfitCriterion(), 7.0), WeightedCriterion.of(new ReturnOverMaxDrawdownCriterion(), 3.0)); } private static Rule createEntryRule(BarSeries series, int barCount) { Indicator closePrice = new ClosePriceIndicator(series); SMAIndicator sma = new SMAIndicator(closePrice, barCount); return new UnderIndicatorRule(sma, closePrice); } private static Rule createExitRule(BarSeries series, int barCount) { Indicator closePrice = new ClosePriceIndicator(series); SMAIndicator sma = new SMAIndicator(closePrice, barCount); return new OverIndicatorRule(sma, closePrice); } private static String printReport(List tradingStatements) { StringBuilder resultBuilder = new StringBuilder(); resultBuilder.append(System.lineSeparator()); for (TradingStatement statement : tradingStatements) { resultBuilder.append(printStatementReport(statement)); resultBuilder.append(System.lineSeparator()); } return resultBuilder.toString(); } private static StringBuilder printStatementReport(TradingStatement statement) { StringBuilder resultBuilder = new StringBuilder(); resultBuilder.append("######### ") .append(statement.getStrategy().getName()) .append(" #########") .append(System.lineSeparator()) .append(printPerformanceReport(statement.getPerformanceReport())) .append(System.lineSeparator()) .append(printPositionStats(statement.getPositionStatsReport())) .append(System.lineSeparator()) .append("###########################"); return resultBuilder; } private static StringBuilder printPerformanceReport(BasePerformanceReport report) { StringBuilder resultBuilder = new StringBuilder(); resultBuilder.append("--------- performance report ---------") .append(System.lineSeparator()) .append("total loss: ") .append(report.totalLoss) .append(System.lineSeparator()) .append("total profit: ") .append(report.totalProfit) .append(System.lineSeparator()) .append("total profit loss: ") .append(report.totalProfitLoss) .append(System.lineSeparator()) .append("total profit loss percentage: ") .append(report.totalProfitLossPercentage) .append(System.lineSeparator()) .append("---------------------------"); return resultBuilder; } private static StringBuilder printPositionStats(PositionStatsReport report) { StringBuilder resultBuilder = new StringBuilder(); resultBuilder.append("--------- trade statistics report ---------") .append(System.lineSeparator()) .append("loss trade count: ") .append(report.getLossCount()) .append(System.lineSeparator()) .append("profit trade count: ") .append(report.getProfitCount()) .append(System.lineSeparator()) .append("break even trade count: ") .append(report.getBreakEvenCount()) .append(System.lineSeparator()) .append("---------------------------"); return resultBuilder; } }