Skip to content

Extend LeastSquaresMovingAverage to accept a benchmark - #9371

Closed
0xpinara wants to merge 1 commit into
QuantConnect:masterfrom
0xpinara:feature-6984-lsma-benchmark-reference
Closed

Extend LeastSquaresMovingAverage to accept a benchmark#9371
0xpinara wants to merge 1 commit into
QuantConnect:masterfrom
0xpinara:feature-6984-lsma-benchmark-reference

Conversation

@0xpinara

@0xpinara 0xpinara commented Apr 6, 2026

Copy link
Copy Markdown
Contributor

Description

  • Added new constructors to LeastSquaresMovingAverage that accept a Symbol referenceSymbol parameter
  • When a reference is provided, OLS regression uses the reference values as X instead of time indices, and projects using the latest reference value
  • Added a new LSMA(symbol, reference, period, resolution, selector) overload in QCAlgorithm.Indicators.cs
  • Follows the Alpha indicator pattern for dual-symbol data routing and registration

Related Issue

Resolves #6984

Motivation and Context

Previously, LSMA only regressed price against time. Users who wanted to regress against a benchmark (e.g. SPY) had no built-in way to do so. This was specifically requested by a maintainer in the issue comments.

Requires Documentation Change

Yes — the new LSMA overload should be documented.

How Has This Been Tested?

  • All existing LSMA tests continue to pass
  • WithReferenceRegressesAgainstBenchmark — feeds a perfect y=2x+1 relationship, verifies slope=2, intercept=1, projected value=11
  • WithReferenceIsNotReadyUntilBothWindowsFull — verifies IsReady requires both target and reference windows
  • WithReferenceResetsProperly — verifies full state reset
  • WithoutReferenceBehavesIdentically — confirms no regression in original behavior

Types of changes

  • New feature (non-breaking change which adds functionality)

Checklist

  • My code follows the code style of this project
  • I have read the CONTRIBUTING document
  • I have added tests to cover my changes
  • All new and existing unit tests pass
  • My branch follows the naming convention: feature-6984-lsma-benchmark-reference

@AlexCatarino AlexCatarino left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, @0xpinara.

Please do not add multiple commits from other pull requests. Rebase with master to include only relevant changes.
I've left a few comments to improve the implementation.

Comment thread Indicators/LeastSquaresMovingAverage.cs Outdated
/// <summary>
/// The reference symbol to regress against, if provided.
/// </summary>
private readonly Symbol _referenceSymbol;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the default _referenceSymbol is Symbol.None, you can remove the null checks. See line 122.

Comment thread Indicators/LeastSquaresMovingAverage.cs Outdated
/// <summary>
/// Rolling window of reference symbol data points.
/// </summary>
private readonly RollingWindow<IndicatorDataPoint> _referenceWindow;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the default _referenceWindow is new(0), _referenceWindow.IsReady is always true (see IsReady)

Comment thread Indicators/LeastSquaresMovingAverage.cs Outdated
.ToArray();

double[] xValues;
if (_referenceSymbol != null && _referenceWindow.IsReady)

@AlexCatarino AlexCatarino Apr 6, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If default _referenceWindow is new(0), we can use _referenceWindow.Size != 0 && _referenceWindow.IsReady; and also use only one if-block.

decimal x = Period;
double intercept, slope;
 if (_referenceWindow.Size != 0 && _referenceWindow.IsReady)
{
    var xValues = _referenceWindow
        .OrderBy(i => i.EndTime)
        .Select(i => Convert.ToDouble(i.Value))
        .ToArray();
    x = _referenceWindow[0].Value;
    (intercept, slope) = Fit.Line(x: xValues , y: series);
}
else
{
    (intercept, slope) = Fit.Line(x: _t, y: series);
}
Intercept.Update(input.EndTime, intercept.SafeDecimalCast());
Slope.Update(input.EndTime, slope.SafeDecimalCast());

// Calculate the fitted value corresponding to the input
return Intercept.Current.Value + Slope.Current.Value * x;

Add new constructors that accept a reference Symbol, allowing LSMA to
regress the target against a benchmark instead of time. Add corresponding
LSMA overload in QCAlgorithm.Indicators.cs following the Alpha indicator
pattern for dual-symbol registration.

Use Symbol.None and RollingWindow(0) as defaults to simplify null checks.
Use SafeDecimalCast and tuple deconstruction for Fit.Line results.

Resolves QuantConnect#6984
@0xpinara
0xpinara force-pushed the feature-6984-lsma-benchmark-reference branch from 0009d5e to b4b272b Compare April 7, 2026 13:33
@0xpinara

0xpinara commented Apr 7, 2026

Copy link
Copy Markdown
Contributor Author

Hi @AlexCatarino, thanks for the feedback. I've pushed the fixes:

  • Changed _referenceSymbol default to Symbol.None and _referenceWindow to new(0), all null checks are gone now
  • Simplified ComputeNextValue to a single if-block using _referenceWindow.Size != 0 && _referenceWindow.IsReady with tuple deconstruction and SafeDecimalCast() like you asked
  • Rebased to one commit with only the LSMA changes, sorry about that.

Let me know if anything else needs changing.

@0xpinara
0xpinara requested a review from AlexCatarino April 7, 2026 13:45

@Martin-Molinero Martin-Molinero left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey! Sharing some concerns and requests 👍

{
if (input.Symbol == _referenceSymbol)
{
_referenceWindow.Add(input);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't think this will work correctly, if main symbol comes first and later reference ( should rely on data coming in order) it wont be used for the same time, this requires some time awareness, can see similar multi symbol indicators and how they work.
Also generally I think ideally we should compare the indicator values in a test against some external source? like we usually do, at least trying to find one
Minor also but shouldn't create an unrequired rolling window _referenceWindow = new(0);

@Martin-Molinero

Copy link
Copy Markdown
Member

Closing for now until review address, feel free to reopen 👍

@0xpinara

Copy link
Copy Markdown
Contributor Author

Hey @Martin-Molinero, thanks for the feedback! I'll work on fixing these when I get some time and reopen the PR.

@0xpinara

Copy link
Copy Markdown
Contributor Author

Sorry for the long delay on this. I couldn't reopen the PR so I opened #9761
instead. It uses DualSymbolIndicator now, so the ordering problem you pointed out
is solved.

@0xpinara
0xpinara deleted the feature-6984-lsma-benchmark-reference branch August 28, 2026 12:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Extend LeastSquaresMovingAverage to Accept a Benchmark

3 participants