Skip to content

Commit 50ab04e

Browse files
authored
Implement Time Series Forecast Indicator (#7654)
* initialize TimeSeriesForecast * tests pass * spy_tsf.csv added * added csv to tests * comment * fix requested changes * remove commented out code * remove ds_store and sln --------- Co-authored-by: Daniel Suh <[email protected]>
1 parent ff83d72 commit 50ab04e

File tree

5 files changed

+6042
-0
lines changed

5 files changed

+6042
-0
lines changed

Algorithm/QCAlgorithm.Indicators.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1057,6 +1057,24 @@ public TimeProfile TP(Symbol symbol, int period = 2, decimal valueAreaVolumePerc
10571057
return marketProfile;
10581058
}
10591059

1060+
/// <summary>
1061+
/// Creates a new Time Series Forecast indicator
1062+
/// </summary>
1063+
/// <param name="symbol">The symbol whose TSF we want</param>
1064+
/// <param name="period">The period of the TSF</param>
1065+
/// <param name="resolution">The resolution</param>
1066+
/// <param name="selector">Selects a value from the BaseData to send into the indicator, if null defaults to Value property of BaseData (x => x.Value)</param>
1067+
/// <returns>The TimeSeriesForecast indicator for the requested symbol over the specified period</returns>
1068+
[DocumentationAttribute(Indicators)]
1069+
public TimeSeriesForecast TSF(Symbol symbol, int period, Resolution? resolution = null, Func<IBaseData, decimal> selector = null)
1070+
{
1071+
var name = CreateIndicatorName(symbol, $"TSF({period})", resolution);
1072+
var timeSeriesForecast = new TimeSeriesForecast(name, period);
1073+
InitializeIndicator(symbol, timeSeriesForecast, resolution, selector);
1074+
1075+
return timeSeriesForecast;
1076+
}
1077+
10601078
/// <summary>
10611079
/// Creates a new Maximum indicator to compute the maximum value
10621080
/// </summary>

Indicators/TimeSeriesForecast.cs

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
3+
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
using System;
17+
18+
namespace QuantConnect.Indicators
19+
{
20+
/// <summary>
21+
/// Represents an indicator capable of predicting new values given previous data from a window.
22+
/// Source: https://tulipindicators.org/tsf
23+
/// </summary>
24+
public class TimeSeriesForecast : WindowIndicator<IndicatorDataPoint>, IIndicatorWarmUpPeriodProvider
25+
{
26+
/// <summary>
27+
/// Creates a new TimeSeriesForecast indicator with the specified period
28+
/// </summary>
29+
/// <param name="name">The name of this indicator</param>
30+
/// <param name="period">The period over which to look back</param>
31+
public TimeSeriesForecast(string name, int period)
32+
: base(name, period)
33+
{
34+
if (period < 2)
35+
{
36+
throw new ArgumentException(Messages.RollingWindow.InvalidSize, nameof(period));
37+
}
38+
}
39+
40+
/// <summary>
41+
/// Creates a new TimeSeriesForecast indicator with the specified period
42+
/// </summary>
43+
/// <param name="period">The period over which to look back</param>
44+
public TimeSeriesForecast(int period)
45+
: this($"TSF{period})", period)
46+
{
47+
}
48+
49+
/// <summary>
50+
/// Computes the next value for this indicator from the given state.
51+
/// </summary>
52+
/// <param name="window">The window of data held in this indicator</param>
53+
/// <param name="input">The input value to this indicator on this time step</param>
54+
/// <returns>A new value for this indicator</returns>
55+
protected override decimal ComputeNextValue(IReadOnlyWindow<IndicatorDataPoint> window, IndicatorDataPoint input)
56+
{
57+
if (!IsReady)
58+
{
59+
return 0;
60+
}
61+
62+
// calculations are derived from https://tulipindicators.org/tsf
63+
decimal x1 = 0;
64+
decimal x2 = 0;
65+
decimal xy = 0;
66+
decimal y = 0;
67+
68+
var i = Period - 1;
69+
for (; i > 0; i--)
70+
{
71+
x1 += i;
72+
x2 += i * i;
73+
xy += window[i].Value * (Period - i);
74+
y += window[i].Value;
75+
}
76+
77+
x1 += Period;
78+
x2 += Period * Period;
79+
80+
xy += window[0].Value * Period;
81+
y += window[0].Value;
82+
83+
var bd = 1 / (Period * x2 - x1 * x1);
84+
var b = (Period * xy - x1 * y) * bd;
85+
var a = (y - b * x1) * (1m / Period);
86+
87+
return a + b * (Period + 1);
88+
}
89+
}
90+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
3+
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
using System;
17+
using NUnit.Framework;
18+
using QuantConnect.Indicators;
19+
20+
namespace QuantConnect.Tests.Indicators
21+
{
22+
[TestFixture]
23+
public class TimeSeriesForecastTests : CommonIndicatorTests<IndicatorDataPoint>
24+
{
25+
protected override IndicatorBase<IndicatorDataPoint> CreateIndicator()
26+
{
27+
var tsf = new TimeSeriesForecast(5);
28+
29+
return tsf;
30+
}
31+
32+
protected override string TestFileName => "spy_tsf.csv";
33+
protected override string TestColumnName => "tsf";
34+
35+
[Test]
36+
public void ComputesCorrectly()
37+
{
38+
var tsf = CreateIndicator();
39+
const int period = 5;
40+
41+
// Data source: https://tulipindicators.org/tsf
42+
var data = new[] {81.59m, 81.06m, 82.87m, 83.00m, 83.61m, 83.15m, 82.84m, 83.99m, 84.55m, 84.36m, 85.53m, 86.54m, 86.89m, 87.77m, 87.29m};
43+
var output = new [] {0m, 0m, 0m, 0m, 84.22m, 84.21m, 83.12m, 83.68m, 84.44m, 85.02m, 85.98m, 86.82m, 87.63m, 88.67m, 88.23m};
44+
45+
var reference = DateTime.MinValue;
46+
47+
for (var i = 0; i < output.Length; i++)
48+
{
49+
tsf.Update(reference.AddDays(i + 1), data[i]);
50+
if (i >= period)
51+
{
52+
Assert.AreEqual(output[i], decimal.Round(tsf.Current.Value, 2));
53+
}
54+
}
55+
}
56+
57+
[Test]
58+
public override void ResetsProperly()
59+
{
60+
const int period = 3;
61+
var tsf = new TimeSeriesForecast(period);
62+
var reference = DateTime.MinValue;
63+
64+
tsf.Update(reference, 1m);
65+
tsf.Update(reference.AddDays(1), 1m);
66+
tsf.Update(reference.AddDays(2), 1m);
67+
Assert.IsTrue(tsf.IsReady);
68+
69+
tsf.Reset();
70+
Assert.IsFalse(tsf.IsReady);
71+
TestHelper.AssertIndicatorIsInDefaultState(tsf);
72+
}
73+
74+
[Test]
75+
public void CorrectPeriodSize()
76+
{
77+
Assert.Throws<ArgumentException>(() => new TimeSeriesForecast(1));
78+
Assert.DoesNotThrow(() => new TimeSeriesForecast(2));
79+
}
80+
}
81+
}

Tests/QuantConnect.Tests.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,9 @@
469469
<Content Include="TestData\spy_t3.txt">
470470
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
471471
</Content>
472+
<Content Include="TestData\spy_tsf.csv">
473+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
474+
</Content>
472475
<Content Include="TestData\spy_rocr.txt">
473476
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
474477
</Content>

0 commit comments

Comments
 (0)