All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ta4jexamples.walkforward.WalkForward Maven / Gradle / Ivy

The newest version!
/**
 * The MIT License (MIT)
 *
 * Copyright (c) 2014-2017 Marc de Verdelhan & respective authors (see AUTHORS)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
package ta4jexamples.walkforward;

import eu.verdelhan.ta4j.AnalysisCriterion;
import eu.verdelhan.ta4j.BaseTimeSeries;
import eu.verdelhan.ta4j.Strategy;
import eu.verdelhan.ta4j.TimeSeries;
import eu.verdelhan.ta4j.TimeSeriesManager;
import eu.verdelhan.ta4j.TradingRecord;
import eu.verdelhan.ta4j.analysis.criteria.TotalProfitCriterion;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.time.Duration;
import java.time.ZonedDateTime;

import ta4jexamples.loaders.CsvTradesLoader;
import ta4jexamples.strategies.CCICorrectionStrategy;
import ta4jexamples.strategies.GlobalExtremaStrategy;
import ta4jexamples.strategies.MovingMomentumStrategy;
import ta4jexamples.strategies.RSI2Strategy;

/**
 * Walk-forward optimization example.
 * 

* @see http://en.wikipedia.org/wiki/Walk_forward_optimization * @see http://www.futuresmag.com/2010/04/01/can-your-system-do-the-walk */ public class WalkForward { /** * Builds a list of split indexes from splitDuration. * @param series the time series to get split begin indexes of * @param splitDuration the duration between 2 splits * @return a list of begin indexes after split */ public static List getSplitBeginIndexes(TimeSeries series, Duration splitDuration) { ArrayList beginIndexes = new ArrayList<>(); int beginIndex = series.getBeginIndex(); int endIndex = series.getEndIndex(); // Adding the first begin index beginIndexes.add(beginIndex); // Building the first interval before next split ZonedDateTime beginInterval = series.getFirstTick().getEndTime(); ZonedDateTime endInterval = beginInterval.plus(splitDuration); for (int i = beginIndex; i <= endIndex; i++) { // For each tick... ZonedDateTime tickTime = series.getTick(i).getEndTime(); if (tickTime.isBefore(beginInterval) || !tickTime.isBefore(endInterval)) { // Tick out of the interval if (!endInterval.isAfter(tickTime)) { // Tick after the interval // --> Adding a new begin index beginIndexes.add(i); } // Building the new interval before next split beginInterval = endInterval.isBefore(tickTime) ? tickTime : endInterval; endInterval = beginInterval.plus(splitDuration); } } return beginIndexes; } /** * Returns a new time series which is a view of a subset of the current series. *

* The new series has begin and end indexes which correspond to the bounds of the sub-set into the full series.
* The tick of the series are shared between the original time series and the returned one (i.e. no copy). * @param series the time series to get a sub-series of * @param beginIndex the begin index (inclusive) of the time series * @param duration the duration of the time series * @return a constrained {@link TimeSeries time series} which is a sub-set of the current series */ public static TimeSeries subseries(TimeSeries series, int beginIndex, Duration duration) { // Calculating the sub-series interval ZonedDateTime beginInterval = series.getTick(beginIndex).getEndTime(); ZonedDateTime endInterval = beginInterval.plus(duration); // Checking ticks belonging to the sub-series (starting at the provided index) int subseriesNbTicks = 0; int endIndex = series.getEndIndex(); for (int i = beginIndex; i <= endIndex; i++) { // For each tick... ZonedDateTime tickTime = series.getTick(i).getEndTime(); if (tickTime.isBefore(beginInterval) || !tickTime.isBefore(endInterval)) { // Tick out of the interval break; } // Tick in the interval // --> Incrementing the number of ticks in the subseries subseriesNbTicks++; } return new BaseTimeSeries(series, beginIndex, beginIndex + subseriesNbTicks - 1); } /** * Splits the time series into sub-series lasting sliceDuration.
* The current time series is splitted every splitDuration.
* The last sub-series may last less than sliceDuration. * @param series the time series to split * @param splitDuration the duration between 2 splits * @param sliceDuration the duration of each sub-series * @return a list of sub-series */ public static List splitSeries(TimeSeries series, Duration splitDuration, Duration sliceDuration) { ArrayList subseries = new ArrayList<>(); if (splitDuration != null && !splitDuration.isZero() && sliceDuration != null && !sliceDuration.isZero()) { List beginIndexes = getSplitBeginIndexes(series, splitDuration); for (Integer subseriesBegin : beginIndexes) { subseries.add(subseries(series, subseriesBegin, sliceDuration)); } } return subseries; } /** * @param series the time series * @return a map (key: strategy, value: name) of trading strategies */ public static Map buildStrategiesMap(TimeSeries series) { HashMap strategies = new HashMap<>(); strategies.put(CCICorrectionStrategy.buildStrategy(series), "CCI Correction"); strategies.put(GlobalExtremaStrategy.buildStrategy(series), "Global Extrema"); strategies.put(MovingMomentumStrategy.buildStrategy(series), "Moving Momentum"); strategies.put(RSI2Strategy.buildStrategy(series), "RSI-2"); return strategies; } public static void main(String[] args) { // Splitting the series into slices TimeSeries series = CsvTradesLoader.loadBitstampSeries(); List subseries = splitSeries(series, Duration.ofHours(6), Duration.ofDays(7)); // Building the map of strategies Map strategies = buildStrategiesMap(series); // The analysis criterion AnalysisCriterion profitCriterion = new TotalProfitCriterion(); for (TimeSeries slice : subseries) { // For each sub-series... System.out.println("Sub-series: " + slice.getSeriesPeriodDescription()); TimeSeriesManager sliceManager = new TimeSeriesManager(slice); for (Map.Entry entry : strategies.entrySet()) { Strategy strategy = entry.getKey(); String name = entry.getValue(); // For each strategy... TradingRecord tradingRecord = sliceManager.run(strategy); double profit = profitCriterion.calculate(slice, tradingRecord); System.out.println("\tProfit for " + name + ": " + profit); } Strategy bestStrategy = profitCriterion.chooseBest(sliceManager, new ArrayList(strategies.keySet())); System.out.println("\t\t--> Best strategy: " + strategies.get(bestStrategy) + "\n"); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy