/* * SPDX-License-Identifier: MIT */ package ta4jexamples.strategies; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.jfree.chart.JFreeChart; import org.ta4j.core.BarSeries; import org.ta4j.core.BaseStrategy; import org.ta4j.core.Rule; import org.ta4j.core.Strategy; import org.ta4j.core.TradingRecord; import org.ta4j.core.backtest.BarSeriesManager; import org.ta4j.core.criteria.pnl.GrossReturnCriterion; import org.ta4j.core.indicators.MACDIndicator; import org.ta4j.core.indicators.StochasticOscillatorKIndicator; import org.ta4j.core.indicators.averages.EMAIndicator; import org.ta4j.core.indicators.helpers.ClosePriceIndicator; import org.ta4j.core.rules.CrossedDownIndicatorRule; import org.ta4j.core.rules.CrossedUpIndicatorRule; import org.ta4j.core.rules.OverIndicatorRule; import org.ta4j.core.rules.UnderIndicatorRule; import ta4jexamples.charting.workflow.ChartWorkflow; import ta4jexamples.datasources.BitStampCsvTradesFileBarSeriesDataSource; /** * Moving momentum strategy. * * @see * http://stockcharts.com/help/doku.php?id=chart_school:trading_strategies:moving_momentum */ public class MovingMomentumStrategy { private static final Logger LOG = LogManager.getLogger(MovingMomentumStrategy.class); /** * @param series the bar series * @return the moving momentum strategy */ public static Strategy buildStrategy(BarSeries series) { if (series == null) { throw new IllegalArgumentException("Series cannot be null"); } ClosePriceIndicator closePrice = new ClosePriceIndicator(series); // The bias is bullish when the shorter-moving average moves above the longer // moving average. // The bias is bearish when the shorter-moving average moves below the longer // moving average. EMAIndicator shortEma = new EMAIndicator(closePrice, 9); EMAIndicator longEma = new EMAIndicator(closePrice, 26); StochasticOscillatorKIndicator stochasticOscillK = new StochasticOscillatorKIndicator(series, 14); MACDIndicator macd = new MACDIndicator(closePrice, 9, 26); EMAIndicator emaMacd = new EMAIndicator(macd, 18); // Entry rule Rule entryRule = new OverIndicatorRule(shortEma, longEma) // Trend .and(new CrossedDownIndicatorRule(stochasticOscillK, 20)) // Signal 1 .and(new OverIndicatorRule(macd, emaMacd)); // Signal 2 // Exit rule Rule exitRule = new UnderIndicatorRule(shortEma, longEma) // Trend .and(new CrossedUpIndicatorRule(stochasticOscillK, 80)) // Signal 1 .and(new UnderIndicatorRule(macd, emaMacd)); // Signal 2 String strategyName = "MovingMomentumStrategy"; return new BaseStrategy(strategyName, entryRule, exitRule); } public static void main(String[] args) { // Getting the bar series BarSeries series = BitStampCsvTradesFileBarSeriesDataSource.loadBitstampSeries(); // Building the trading strategy Strategy strategy = buildStrategy(series); // Running the strategy BarSeriesManager seriesManager = new BarSeriesManager(series); TradingRecord tradingRecord = seriesManager.run(strategy); LOG.debug(() -> strategy.toJson()); LOG.debug("{}'s number of positions: {}", strategy.getName(), tradingRecord.getPositionCount()); // Analysis var grossReturn = new GrossReturnCriterion().calculate(series, tradingRecord); LOG.debug("{}'s gross return: {}", strategy.getName(), grossReturn); ClosePriceIndicator closePrice = new ClosePriceIndicator(series); EMAIndicator shortEma = new EMAIndicator(closePrice, 9); EMAIndicator longEma = new EMAIndicator(closePrice, 26); MACDIndicator macd = new MACDIndicator(closePrice, 9, 26); EMAIndicator emaMacd = new EMAIndicator(macd, 18); StochasticOscillatorKIndicator stochasticOscillK = new StochasticOscillatorKIndicator(series, 14); // Charting ChartWorkflow chartWorkflow = new ChartWorkflow(); JFreeChart chart = chartWorkflow.builder() .withSeries(series) .withTradingRecordOverlay(tradingRecord) .withIndicatorOverlay(shortEma) .withIndicatorOverlay(longEma) .withSubChart(macd) .withIndicatorOverlay(emaMacd) .withSubChart(stochasticOscillK) .toChart(); chartWorkflow.displayChart(chart); chartWorkflow.saveChartImage(chart, series, "moving-momentum-strategy", "temp/charts"); } }