/** * 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 eu.verdelhan.ta4j; import eu.verdelhan.ta4j.Order.OrderType; import eu.verdelhan.ta4j.mocks.MockTick; import eu.verdelhan.ta4j.mocks.MockTimeSeries; import eu.verdelhan.ta4j.trading.rules.FixedRule; import java.util.LinkedList; import java.util.List; import org.joda.time.DateTime; import org.joda.time.Period; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; import static org.junit.Assert.*; import org.junit.Before; import org.junit.Test; public class TimeSeriesTest { private TimeSeries defaultSeries; private TimeSeries subSeries; private TimeSeries emptySeries; private TimeSeries seriesForRun; private Strategy strategy; private List<Tick> ticks; private String defaultName; private DateTime date; @Before public void setUp() { date = new DateTime(0); ticks = new LinkedList<Tick>(); ticks.add(new MockTick(date.withDate(2014, 6, 13), 1d)); ticks.add(new MockTick(date.withDate(2014, 6, 14), 2d)); ticks.add(new MockTick(date.withDate(2014, 6, 15), 3d)); ticks.add(new MockTick(date.withDate(2014, 6, 20), 4d)); ticks.add(new MockTick(date.withDate(2014, 6, 25), 5d)); ticks.add(new MockTick(date.withDate(2014, 6, 30), 6d)); defaultName = "Series Name"; defaultSeries = new TimeSeries(defaultName, ticks); subSeries = defaultSeries.subseries(2, 4); emptySeries = new TimeSeries(); final DateTimeFormatter dtf = DateTimeFormat.forPattern("yyyy-MM-dd"); seriesForRun = new MockTimeSeries( new double[] { 1d, 2d, 3d, 4d, 5d, 6d, 7d, 8d, 9d }, new DateTime[] { dtf.parseDateTime("2013-01-01"), dtf.parseDateTime("2013-08-01"), dtf.parseDateTime("2013-10-01"), dtf.parseDateTime("2013-12-01"), dtf.parseDateTime("2014-02-01"), dtf.parseDateTime("2015-01-01"), dtf.parseDateTime("2015-08-01"), dtf.parseDateTime("2015-10-01"), dtf.parseDateTime("2015-12-01") }); strategy = new Strategy(new FixedRule(0, 2, 3, 6), new FixedRule(1, 4, 7, 8)); strategy.setUnstablePeriod(2); // Strategy would need a real test class } @Test public void getEndGetBeginGetTickCount() { // Original series assertEquals(0, defaultSeries.getBegin()); assertEquals(ticks.size() - 1, defaultSeries.getEnd()); assertEquals(ticks.size(), defaultSeries.getTickCount()); // Sub-series assertEquals(2, subSeries.getBegin()); assertEquals(4, subSeries.getEnd()); assertEquals(3, subSeries.getTickCount()); // Empty series assertEquals(-1, emptySeries.getBegin()); assertEquals(-1, emptySeries.getEnd()); assertEquals(0, emptySeries.getTickCount()); } @Test public void getSeriesPeriodDescription() { // Original series assertTrue(defaultSeries.getSeriesPeriodDescription().endsWith(ticks.get(defaultSeries.getEnd()).getEndTime().toString("hh:mm dd/MM/yyyy"))); assertTrue(defaultSeries.getSeriesPeriodDescription().startsWith(ticks.get(defaultSeries.getBegin()).getEndTime().toString("hh:mm dd/MM/yyyy"))); // Sub-series assertTrue(subSeries.getSeriesPeriodDescription().endsWith(ticks.get(subSeries.getEnd()).getEndTime().toString("hh:mm dd/MM/yyyy"))); assertTrue(subSeries.getSeriesPeriodDescription().startsWith(ticks.get(subSeries.getBegin()).getEndTime().toString("hh:mm dd/MM/yyyy"))); // Empty series assertEquals("", emptySeries.getSeriesPeriodDescription()); } @Test public void getName() { assertEquals(defaultName, defaultSeries.getName()); assertEquals(defaultName, subSeries.getName()); } @Test public void getTickWithRemovedIndexOnMovingSeriesShouldReturnFirstRemainingTick() { Tick tick = defaultSeries.getTick(4); defaultSeries.setMaximumTickCount(2); assertSame(tick, defaultSeries.getTick(0)); assertSame(tick, defaultSeries.getTick(1)); assertSame(tick, defaultSeries.getTick(2)); assertSame(tick, defaultSeries.getTick(3)); assertSame(tick, defaultSeries.getTick(4)); assertNotSame(tick, defaultSeries.getTick(5)); } @Test(expected = IndexOutOfBoundsException.class) public void getTickOnMovingAndEmptySeriesShouldThrowException() { defaultSeries.setMaximumTickCount(2); ticks.clear(); // Should not be used like this defaultSeries.getTick(1); } @Test(expected = IndexOutOfBoundsException.class) public void getTickWithNegativeIndexShouldThrowException() { defaultSeries.getTick(-1); } @Test(expected = IndexOutOfBoundsException.class) public void getTickWithIndexGreaterThanTickCountShouldThrowException() { defaultSeries.getTick(10); } @Test public void getTickOnMovingSeries() { Tick tick = defaultSeries.getTick(4); defaultSeries.setMaximumTickCount(2); assertEquals(tick, defaultSeries.getTick(4)); } @Test(expected = IllegalStateException.class) public void maximumTickCountOnSubserieShouldThrowException() { subSeries.setMaximumTickCount(10); } @Test(expected = IllegalArgumentException.class) public void negativeMaximumTickCountShouldThrowException() { defaultSeries.setMaximumTickCount(-1); } @Test public void setMaximumTickCount() { // Before assertEquals(0, defaultSeries.getBegin()); assertEquals(ticks.size() - 1, defaultSeries.getEnd()); assertEquals(ticks.size(), defaultSeries.getTickCount()); defaultSeries.setMaximumTickCount(3); // After assertEquals(0, defaultSeries.getBegin()); assertEquals(5, defaultSeries.getEnd()); assertEquals(3, defaultSeries.getTickCount()); } @Test(expected = IllegalArgumentException.class) public void addNullTickShouldThrowException() { defaultSeries.addTick(null); } @Test(expected = IllegalArgumentException.class) public void addTickWithEndTimePriorToSeriesEndTimeShouldThrowException() { defaultSeries.addTick(new MockTick(date.withDate(2000, 1, 1), 99d)); } @Test public void addTick() { defaultSeries = new TimeSeries(); Tick firstTick = new MockTick(date.withDate(2014, 6, 13), 1d); Tick secondTick = new MockTick(date.withDate(2014, 6, 14), 2d); assertEquals(0, defaultSeries.getTickCount()); assertEquals(-1, defaultSeries.getBegin()); assertEquals(-1, defaultSeries.getEnd()); defaultSeries.addTick(firstTick); assertEquals(1, defaultSeries.getTickCount()); assertEquals(0, defaultSeries.getBegin()); assertEquals(0, defaultSeries.getEnd()); defaultSeries.addTick(secondTick); assertEquals(2, defaultSeries.getTickCount()); assertEquals(0, defaultSeries.getBegin()); assertEquals(1, defaultSeries.getEnd()); } @Test public void subseriesWithIndexes() { TimeSeries subSeries2 = defaultSeries.subseries(2, 5); assertEquals(defaultSeries.getName(), subSeries2.getName()); assertEquals(2, subSeries2.getBegin()); assertNotEquals(defaultSeries.getBegin(), subSeries2.getBegin()); assertEquals(5, subSeries2.getEnd()); assertEquals(defaultSeries.getEnd(), subSeries2.getEnd()); assertEquals(4, subSeries2.getTickCount()); } @Test(expected = IllegalStateException.class) public void subseriesOnSeriesWithMaximumTickCountShouldThrowException() { defaultSeries.setMaximumTickCount(3); defaultSeries.subseries(0, 1); } @Test(expected = IllegalArgumentException.class) public void subseriesWithInvalidIndexesShouldThrowException() { defaultSeries.subseries(4, 2); } @Test public void subseriesWithDuration() { TimeSeries subSeries2 = defaultSeries.subseries(1, Period.weeks(2)); assertEquals(defaultSeries.getName(), subSeries2.getName()); assertEquals(1, subSeries2.getBegin()); assertNotEquals(defaultSeries.getBegin(), subSeries2.getBegin()); assertEquals(4, subSeries2.getEnd()); assertNotEquals(defaultSeries.getEnd(), subSeries2.getEnd()); assertEquals(4, subSeries2.getTickCount()); } @Test public void splitEvery3Ticks() { TimeSeries series = new MockTimeSeries( date.withYear(2010), date.withYear(2011), date.withYear(2012), date.withYear(2015), date.withYear(2016), date.withYear(2017), date.withYear(2018), date.withYear(2019)); List<TimeSeries> subseries = series.split(3); assertEquals(3, subseries.size()); assertEquals(0, subseries.get(0).getBegin()); assertEquals(2, subseries.get(0).getEnd()); assertEquals(3, subseries.get(1).getBegin()); assertEquals(5, subseries.get(1).getEnd()); assertEquals(6, subseries.get(2).getBegin()); assertEquals(7, subseries.get(2).getEnd()); } @Test public void splitByYearForTwoYearsSubseries() { TimeSeries series = new MockTimeSeries( date.withYear(2010), date.withYear(2011), date.withYear(2012), date.withYear(2015), date.withYear(2016)); List<TimeSeries> subseries = series.split(Period.years(1), Period.years(2)); assertEquals(5, subseries.size()); assertEquals(0, subseries.get(0).getBegin()); assertEquals(1, subseries.get(0).getEnd()); assertEquals(1, subseries.get(1).getBegin()); assertEquals(2, subseries.get(1).getEnd()); assertEquals(2, subseries.get(2).getBegin()); assertEquals(2, subseries.get(2).getEnd()); assertEquals(4, subseries.get(4).getBegin()); assertEquals(4, subseries.get(4).getEnd()); } @Test public void splitByMonthForOneWeekSubseries() { TimeSeries series = new MockTimeSeries( date.withMonthOfYear(04), date.withMonthOfYear(05), date.withMonthOfYear(07)); List<TimeSeries> subseries = series.split(Period.months(1), Period.weeks(1)); assertEquals(3, subseries.size()); assertEquals(0, subseries.get(0).getBegin()); assertEquals(0, subseries.get(0).getEnd()); assertEquals(1, subseries.get(1).getBegin()); assertEquals(1, subseries.get(1).getEnd()); assertEquals(2, subseries.get(2).getBegin()); assertEquals(2, subseries.get(2).getEnd()); } @Test public void splitByHour() { DateTime time = new DateTime(0).withTime(10, 0, 0, 0); TimeSeries series = new MockTimeSeries( time, time.plusMinutes(1), time.plusMinutes(2), time.plusMinutes(10), time.plusMinutes(15), time.plusMinutes(25), time.plusHours(1), time.plusHours(5), time.plusHours(10).plusMinutes(10), time.plusHours(10).plusMinutes(20), time.plusHours(10).plusMinutes(30)); List<TimeSeries> subseries = series.split(Period.hours(1)); assertEquals(4, subseries.size()); assertEquals(0, subseries.get(0).getBegin()); assertEquals(5, subseries.get(0).getEnd()); assertEquals(6, subseries.get(1).getBegin()); assertEquals(6, subseries.get(1).getEnd()); assertEquals(7, subseries.get(2).getBegin()); assertEquals(7, subseries.get(2).getEnd()); assertEquals(8, subseries.get(3).getBegin()); assertEquals(10, subseries.get(3).getEnd()); } @Test public void runOnWholeSeries() { TimeSeries series = new MockTimeSeries(20d, 40d, 60d, 10d, 30d, 50d, 0d, 20d, 40d); List<Trade> allTrades = series.run(strategy).getTrades(); assertEquals(2, allTrades.size()); } @Test public void runOnWholeSeriesWithAmount() { TimeSeries series = new MockTimeSeries(20d, 40d, 60d, 10d, 30d, 50d, 0d, 20d, 40d); List<Trade> allTrades = series.run(strategy,OrderType.BUY, Decimal.HUNDRED).getTrades(); assertEquals(2, allTrades.size()); assertEquals(Decimal.HUNDRED, allTrades.get(0).getEntry().getAmount()); assertEquals(Decimal.HUNDRED, allTrades.get(1).getEntry().getAmount()); } @Test public void runOnSlice() { List<TimeSeries> subseries = seriesForRun.split(Period.years(2000)); TimeSeries slice = subseries.get(0); List<Trade> trades = slice.run(strategy).getTrades(); assertEquals(2, trades.size()); assertEquals(Order.buyAt(2, slice.getTick(2).getClosePrice(), Decimal.NaN), trades.get(0).getEntry()); assertEquals(Order.sellAt(4, slice.getTick(4).getClosePrice(), Decimal.NaN), trades.get(0).getExit()); assertEquals(Order.buyAt(6, slice.getTick(6).getClosePrice(), Decimal.NaN), trades.get(1).getEntry()); assertEquals(Order.sellAt(7, slice.getTick(7).getClosePrice(), Decimal.NaN), trades.get(1).getExit()); } @Test public void runWithOpenEntryBuyLeft() { List<TimeSeries> subseries = seriesForRun.split(Period.years(1)); TimeSeries slice = subseries.get(0); Strategy aStrategy = new Strategy(new FixedRule(1), new FixedRule(3)); List<Trade> trades = slice.run(aStrategy).getTrades(); assertEquals(1, trades.size()); assertEquals(Order.buyAt(1, slice.getTick(1).getClosePrice(), Decimal.NaN), trades.get(0).getEntry()); assertEquals(Order.sellAt(3, slice.getTick(3).getClosePrice(), Decimal.NaN), trades.get(0).getExit()); } @Test public void runWithOpenEntrySellLeft() { List<TimeSeries> subseries = seriesForRun.split(Period.years(1)); TimeSeries slice = subseries.get(0); Strategy aStrategy = new Strategy(new FixedRule(1), new FixedRule(3)); List<Trade> trades = slice.run(aStrategy, OrderType.SELL).getTrades(); assertEquals(1, trades.size()); assertEquals(Order.sellAt(1, slice.getTick(1).getClosePrice(), Decimal.NaN), trades.get(0).getEntry()); assertEquals(Order.buyAt(3, slice.getTick(3).getClosePrice(), Decimal.NaN), trades.get(0).getExit()); } @Test public void runSplitted() { List<TimeSeries> subseries = seriesForRun.split(Period.years(1)); TimeSeries slice0 = subseries.get(0); TimeSeries slice1 = subseries.get(1); TimeSeries slice2 = subseries.get(2); List<Trade> trades = slice0.run(strategy).getTrades(); assertEquals(1, trades.size()); assertEquals(Order.buyAt(2, slice0.getTick(2).getClosePrice(), Decimal.NaN), trades.get(0).getEntry()); assertEquals(Order.sellAt(4, slice0.getTick(4).getClosePrice(), Decimal.NaN), trades.get(0).getExit()); trades = slice1.run(strategy).getTrades(); assertTrue(trades.isEmpty()); trades = slice2.run(strategy).getTrades(); assertEquals(1, trades.size()); assertEquals(Order.buyAt(6, slice2.getTick(6).getClosePrice(), Decimal.NaN), trades.get(0).getEntry()); assertEquals(Order.sellAt(7, slice2.getTick(7).getClosePrice(), Decimal.NaN), trades.get(0).getExit()); } @Test public void splitted(){ DateTime date = new DateTime(); TimeSeries series = new MockTimeSeries(new double[]{1d, 2d, 3d, 4d, 5d, 6d, 7d, 8d, 9d, 10d}, new DateTime[]{date.withYear(2000), date.withYear(2000), date.withYear(2001), date.withYear(2001), date.withYear(2002), date.withYear(2002), date.withYear(2002), date.withYear(2003), date.withYear(2004), date.withYear(2005)}); Strategy aStrategy = new Strategy(new FixedRule(0, 3, 5, 7), new FixedRule(2, 4, 6, 9)); List<TimeSeries> subseries = series.split(Period.years(1)); TimeSeries slice0 = subseries.get(0); TimeSeries slice1 = subseries.get(1); TimeSeries slice2 = subseries.get(2); TimeSeries slice3 = subseries.get(3); TimeSeries slice4 = subseries.get(4); TimeSeries slice5 = subseries.get(5); List<Trade> trades = slice0.run(aStrategy).getTrades(); assertEquals(1, trades.size()); assertEquals(Order.buyAt(0, slice0.getTick(0).getClosePrice(), Decimal.NaN),trades.get(0).getEntry()); assertEquals(Order.sellAt(2, slice0.getTick(2).getClosePrice(), Decimal.NaN), trades.get(0).getExit()); trades = slice1.run(aStrategy).getTrades(); assertEquals(1, trades.size()); assertEquals(Order.buyAt(3, slice1.getTick(3).getClosePrice(), Decimal.NaN), trades.get(0).getEntry()); assertEquals(Order.sellAt(4, slice1.getTick(4).getClosePrice(), Decimal.NaN), trades.get(0).getExit()); trades = slice2.run(aStrategy).getTrades(); assertEquals(1, trades.size()); assertEquals(Order.buyAt(5, slice2.getTick(5).getClosePrice(), Decimal.NaN), trades.get(0).getEntry()); assertEquals(Order.sellAt(6, slice2.getTick(6).getClosePrice(), Decimal.NaN), trades.get(0).getExit()); trades = slice3.run(aStrategy).getTrades(); assertEquals(1, trades.size()); assertEquals(Order.buyAt(7, slice3.getTick(7).getClosePrice(), Decimal.NaN), trades.get(0).getEntry()); assertEquals(Order.sellAt(9, slice3.getTick(9).getClosePrice(), Decimal.NaN), trades.get(0).getExit()); trades = slice4.run(aStrategy).getTrades(); assertTrue(trades.isEmpty()); trades = slice5.run(aStrategy).getTrades(); assertTrue(trades.isEmpty()); } }