diff --git a/Indicators/AutoRegressiveIntegratedMovingAverage.cs b/Indicators/AutoRegressiveIntegratedMovingAverage.cs index dd5fe5a75573..6a9f332c39e5 100644 --- a/Indicators/AutoRegressiveIntegratedMovingAverage.cs +++ b/Indicators/AutoRegressiveIntegratedMovingAverage.cs @@ -131,9 +131,14 @@ public AutoRegressiveIntegratedMovingAverage( "currently available fitting methods."); } - if (period < Math.Max(arOrder, maOrder)) + // ComputeNextValue indexes arrayData and _residuals up to 2 * maOrder, over a + // series DifferenceSeries shortens only when diffOrder is positive. + var shortest = Math.Max(arOrder, maOrder > 0 ? (2 * maOrder) + 1 : 0) + + Math.Max(diffOrder, 0); + if (period < shortest) { - throw new ArgumentException("Period must exceed both arOrder and maOrder"); + throw new ArgumentException($"Period parameter for ARIMA({arOrder}, {diffOrder}, " + + $"{maOrder}) indicator must be at least {shortest} but was {period}"); } _arOrder = arOrder; diff --git a/Tests/Indicators/AutoregressiveIntegratedMovingAverageTests.cs b/Tests/Indicators/AutoregressiveIntegratedMovingAverageTests.cs index 0cca88629ec9..baee7a42147e 100644 --- a/Tests/Indicators/AutoregressiveIntegratedMovingAverageTests.cs +++ b/Tests/Indicators/AutoregressiveIntegratedMovingAverageTests.cs @@ -69,6 +69,31 @@ public void AcceptsAnAutoRegressiveOrderAboveTheMovingAverageOrder(int arOrder, Assert.IsTrue(arima.IsReady); } + [TestCase(1, 0, 1, 3)] + [TestCase(1, 1, 1, 4)] + [TestCase(2, 1, 2, 6)] + [TestCase(1, 2, 0, 3)] + [TestCase(1, -2, 1, 3)] + [TestCase(1, -3, 2, 5)] + public void RejectsAPeriodTooShortForItsOrders(int arOrder, int diffOrder, int maOrder, int shortest) + { + var exception = Assert.Throws(() => + new AutoRegressiveIntegratedMovingAverage(arOrder, diffOrder, maOrder, shortest - 1, true)); + Assert.That(exception.Message, Is.EqualTo( + $"Period parameter for ARIMA({arOrder}, {diffOrder}, {maOrder}) indicator " + + $"must be at least {shortest} but was {shortest - 1}")); + + var arima = new AutoRegressiveIntegratedMovingAverage(arOrder, diffOrder, maOrder, shortest, true); + var reference = new DateTime(2020, 1, 1); + + for (var i = 0; i < shortest + 1; i++) + { + arima.Update(reference.AddDays(i), 100m + (decimal)Math.Sin(i / 3d) * 5m); + } + + Assert.IsTrue(arima.IsReady); + } + [Test] public void PredictionErrorAgainstExternalData() {