Skip to content

ARIMA throws IndexOutOfRangeException when arOrder exceeds maOrder #9712

Description

@mkzung

Expected Behavior

AutoRegressiveIntegratedMovingAverage fits any order set its constructor accepts.

Actual Behavior

Any order set with arOrder > maOrder throws IndexOutOfRangeException on the bar that
fills the window. ARIMA(2, 0, 1) is the smallest. At period 50, 24 of the 80 accepted order
sets throw.

MovingAverageStep walks the lagged errors and indexes the AR lags with the same counter:

var laggedErrors = LaggedSeries(_maOrder, _residuals.ToArray());
for (var i = 0; i < laggedErrors.Length; i++)
{
    var doubles = lags[i].ToList();
    doubles.AddRange(laggedErrors[i]);

lags[i] is the row for time i + _arOrder and laggedErrors[j] the row for j + _maOrder,
so the counters agree only when the orders do. lags holds data.Length - _arOrder rows and
laggedErrors holds data.Length - _maOrder, so _arOrder > _maOrder runs off the end.

The other direction does not throw. It pairs an AR row from one bar with error terms from
another, and the model is fitted on rows that never coexisted.

Potential Solution

Walk time rather than a row index, t from Math.Max(_arOrder, _maOrder) to
data.Length - 1, reading lags[t - _arOrder] and laggedErrors[t - _maOrder], with the
target vector and the residual loop starting at the same offset.

Separately, the two catch blocks added for #8039 read the row width off row zero,
lags.ToArray()[0].Length and appendedData.ToArray()[0].Length, so when the fit fails
because there are no rows the handler throws instead of substituting zeros. Both widths are
known from the orders.

Reproducing the Problem

var arima = new AutoRegressiveIntegratedMovingAverage(2, 0, 1, 50, true);
var reference = new DateTime(2020, 1, 1);
for (var i = 0; i < 60; i++)
{
    arima.Update(reference.AddDays(i), 100m + (decimal)Math.Sin(i / 3d) * 5m);
}
// System.IndexOutOfRangeException on the 50th update

Every test in AutoregressiveIntegratedMovingAverageTests uses
new AutoRegressiveIntegratedMovingAverage("ARIMA", 1, 0, 1, 50), the diagonal where the two
orders match and the loop bound is accidentally right.

System Information

master at d865a40.

Checklist

  • I have completely filled out this template
  • I have confirmed that this issue exists on the current master branch
  • I have confirmed that this is not a duplicate issue by searching issues
  • I have provided detailed steps to reproduce the issue

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions