Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement SqueezeMomentum (SM) indicator #8462

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions Algorithm/QCAlgorithm.Indicators.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1951,6 +1951,26 @@ public SortinoRatio SORTINO(Symbol symbol, int sortinoPeriod, double minimumAcce
return sortinoRatio;
}

/// <summary>
/// Creates a Squeeze Momentum indicator to identify market squeezes and potential breakouts.
/// Compares Bollinger Bands and Keltner Channels to signal low or high volatility periods.
/// </summary>
/// <param name="symbol">The symbol for which the indicator is calculated.</param>
/// <param name="bollingerPeriod">The period for Bollinger Bands.</param>
/// <param name="bollingerMultiplier">The multiplier for the Bollinger Bands' standard deviation.</param>
/// <param name="keltnerPeriod">The period for Keltner Channels.</param>
/// <param name="keltnerMultiplier">The multiplier for the Average True Range in Keltner Channels.</param>
/// <param name="resolution">The resolution of the data.</param>
/// <param name="selector">Selects a value from the BaseData to send into the indicator. If null, defaults to the Value property of BaseData (x => x.Value).</param>
/// <returns>The configured Squeeze Momentum indicator.</returns>
[DocumentationAttribute(Indicators)]
public SqueezeMomentum SM(Symbol symbol, int bollingerPeriod = 20, decimal bollingerMultiplier = 2m, int keltnerPeriod = 20, decimal keltnerMultiplier = 1.5m, Resolution? resolution = null, Func<IBaseData, IBaseDataBar> selector = null)
{
var name = CreateIndicatorName(symbol, $"SM({bollingerPeriod}, {bollingerMultiplier}, {keltnerPeriod}, {keltnerMultiplier})", resolution);
var squeezeMomentum = new SqueezeMomentum(name, bollingerPeriod, bollingerMultiplier, keltnerPeriod, keltnerMultiplier);
InitializeIndicator(squeezeMomentum, resolution, selector, symbol);
return squeezeMomentum;
}

/// <summary>
/// Creates an SimpleMovingAverage indicator for the symbol. The indicator will be automatically
Expand Down
109 changes: 109 additions & 0 deletions Indicators/SqueezeMomentum.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

using QuantConnect.Data.Market;

namespace QuantConnect.Indicators
{
/// <summary>
/// The SqueezeMomentum indicator calculates whether the market is in a "squeeze" condition,
/// determined by comparing Bollinger Bands to Keltner Channels. When the Bollinger Bands are
/// inside the Keltner Channels, the indicator returns 1 (squeeze on). Otherwise, it returns -1 (squeeze off).
/// </summary>
public class SqueezeMomentum : BarIndicator, IIndicatorWarmUpPeriodProvider
{
/// <summary>
/// The Bollinger Bands indicator used to calculate the upper, lower, and middle bands.
/// </summary>
private readonly BollingerBands _bollingerBands;

/// <summary>
/// The Average True Range (ATR) indicator used to calculate the Keltner Channels.
/// </summary>
private readonly AverageTrueRange _averageTrueRange;

/// <summary>
/// The multiplier applied to the Average True Range for calculating Keltner Channels.
/// </summary>
private readonly decimal _keltnerMultiplier;

/// <summary>
/// Initializes a new instance of the <see cref="SqueezeMomentum"/> class.
/// </summary>
/// <param name="name">The name of the indicator.</param>
/// <param name="bollingerPeriod">The period used for the Bollinger Bands calculation.</param>
/// <param name="bollingerMultiplier">The multiplier for the Bollinger Bands width.</param>
/// <param name="keltnerPeriod">The period used for the Average True Range (ATR) calculation in Keltner Channels.</param>
/// <param name="keltnerMultiplier">The multiplier applied to the ATR for calculating Keltner Channels.</param>
public SqueezeMomentum(string name, int bollingerPeriod, decimal bollingerMultiplier, int keltnerPeriod, decimal keltnerMultiplier) : base(name)
{
_bollingerBands = new BollingerBands(bollingerPeriod, bollingerMultiplier);
_averageTrueRange = new AverageTrueRange(keltnerPeriod, MovingAverageType.Simple);
_keltnerMultiplier = keltnerMultiplier;
}

/// <summary>
/// Gets the warm-up period required for the indicator to be ready.
/// This is determined by the warm-up period of the Bollinger Bands indicator.
/// </summary>
public int WarmUpPeriod => _bollingerBands.WarmUpPeriod;

/// <summary>
/// Indicates whether the indicator is ready and has enough data for computation.
/// The indicator is ready when both the Bollinger Bands and the Average True Range are ready.
/// </summary>
public override bool IsReady => _bollingerBands.IsReady && _averageTrueRange.IsReady;

/// <summary>
/// Computes the next value of the indicator based on the input data bar.
/// </summary>
/// <param name="input">The input data bar.</param>
/// <returns>
/// Returns 1 if the Bollinger Bands are inside the Keltner Channels (squeeze on),
/// or -1 if the Bollinger Bands are outside the Keltner Channels (squeeze off).
/// </returns>
protected override decimal ComputeNextValue(IBaseDataBar input)
{
_bollingerBands.Update(new IndicatorDataPoint(input.EndTime, input.Close));
_averageTrueRange.Update(input);
if (!IsReady)
{
return decimal.Zero;
}

// Calculate Bollinger Bands upper, lower, and middle bands
var bbHigh = _bollingerBands.UpperBand.Current.Value;
var bbLow = _bollingerBands.LowerBand.Current.Value;
var simpleMovingAverage = _bollingerBands.MiddleBand.Current.Value;

// Calculate Keltner Channels upper and lower bounds
var kcLow = simpleMovingAverage - _keltnerMultiplier * _averageTrueRange.Current.Value;
var kcHigh = simpleMovingAverage + _keltnerMultiplier * _averageTrueRange.Current.Value;

// Determine if the squeeze condition is on or off
return (kcHigh > bbHigh && kcLow < bbLow) ? 1m : -1m;
}

/// <summary>
/// Resets the state of the indicator, including all sub-indicators.
/// </summary>
public override void Reset()
{
_bollingerBands.Reset();
_averageTrueRange.Reset();
base.Reset();
}
}
}
34 changes: 34 additions & 0 deletions Tests/Indicators/SqueezeMomentumTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

using NUnit.Framework;
using QuantConnect.Data.Market;
using QuantConnect.Indicators;

namespace QuantConnect.Tests.Indicators
{
[TestFixture]
public class SqueezeMomentumTests : CommonIndicatorTests<IBaseDataBar>
{
protected override IndicatorBase<IBaseDataBar> CreateIndicator()
{
VolumeRenkoBarSize = 0.5m;
return new SqueezeMomentum("SM", 20, 2, 20, 1.5m);
}
protected override string TestFileName => "spy_smi.csv";

protected override string TestColumnName => "squeeze on";
}
}
3 changes: 3 additions & 0 deletions Tests/QuantConnect.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -618,6 +618,9 @@
<Content Include="TestData\spy_hurst_exponent.csv">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="TestData\spy_smi.csv">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<Content Include="TestData\symbol-properties\symbol-properties-database.csv">
Expand Down
Loading