![JAR search and dependency download from the Maven repository](/logo.png)
ta4jexamples.walkforward.WalkForward Maven / Gradle / Ivy
Show all versions of ta4j-examples Show documentation
/**
* 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");
}
}
}