-
-
Notifications
You must be signed in to change notification settings - Fork 3.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement Hurst Exponent (HE) indicator (#8434)
* Implement Hurst Exponent indicator and unit test - Implemented Hurst Exponent indicator class - Created unit tests to validate indicator functionality - Added helper methods for standard deviation and regression line slope * Optimize Hurst Exponent calculation - Removed redundat loops and unnecessary operations. - Precomputed values fro time lag logarithms to avoid recalculation. - Simplified logic in ComputeNextValue for better performance. - Updated and clarified comments for improved readability. * Refactor HurstExponent indicator - Made _sumX and _sumX2 fields readonly. - Updated loop for calculating time lag differences to avoid out of bound. - Renamed lookbackPeriod with period. - Replaced DateTime.Now with a fixed date.
- Loading branch information
Showing
5 changed files
with
1,234 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
/* | ||
* 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 System; | ||
using System.Collections.Generic; | ||
|
||
namespace QuantConnect.Indicators | ||
{ | ||
/// <summary> | ||
/// Represents the Hurst Exponent indicator, which is used to measure the long-term memory of a time series. | ||
/// - H less than 0.5: Mean-reverting; high values followed by low ones, stronger as H approaches 0. | ||
/// - H equal to 0.5: Random walk (geometric). | ||
/// - H greater than 0.5: Trending; high values followed by higher ones, stronger as H approaches 1. | ||
/// </summary> | ||
public class HurstExponent : Indicator, IIndicatorWarmUpPeriodProvider | ||
{ | ||
/// <summary> | ||
/// A rolling window that holds the most recent price values. | ||
/// </summary> | ||
private readonly RollingWindow<decimal> _priceWindow; | ||
|
||
/// <summary> | ||
/// The list of time lags used to calculate tau values. | ||
/// </summary> | ||
private readonly List<int> _timeLags; | ||
|
||
/// <summary> | ||
/// Sum of the logarithms of the time lags, precomputed for efficiency. | ||
/// </summary> | ||
private readonly decimal _sumX; | ||
|
||
/// <summary> | ||
/// Sum of the squares of the logarithms of the time lags, precomputed for efficiency. | ||
/// </summary> | ||
private readonly decimal _sumX2; | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="HurstExponent"/> class. | ||
/// The default maxLag value of 20 is chosen for reliable and accurate results, but using a higher lag may reduce precision. | ||
/// </summary> | ||
/// <param name="name">The name of the indicator.</param> | ||
/// <param name="period">The period over which to calculate the Hurst Exponent.</param> | ||
/// <param name="maxLag">The maximum lag to consider for time series analysis.</param> | ||
public HurstExponent(string name, int period, int maxLag = 20) : base(name) | ||
{ | ||
if (maxLag < 2) | ||
{ | ||
throw new ArgumentException("The maxLag parameter must be greater than 2 to compute the Hurst Exponent.", nameof(maxLag)); | ||
} | ||
_priceWindow = new RollingWindow<decimal>(period); | ||
_timeLags = new List<int>(); | ||
|
||
// Precompute logarithms of time lags and their squares for regression calculations | ||
for (var i = 2; i < maxLag; i++) | ||
{ | ||
var logTimeLag = (decimal)Math.Log(i); | ||
_timeLags.Add(i); | ||
_sumX += logTimeLag; | ||
_sumX2 += logTimeLag * logTimeLag; | ||
} | ||
WarmUpPeriod = period; | ||
} | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="HurstExponent"/> class with the specified period and maxLag. | ||
/// The default maxLag value of 20 is chosen for reliable and accurate results, but using a higher lag may reduce precision. | ||
/// </summary> | ||
/// <param name="period">The period over which to calculate the Hurst Exponent.</param> | ||
/// <param name="maxLag">The maximum lag to consider for time series analysis.</param> | ||
public HurstExponent(int period, int maxLag = 20) | ||
: this($"HE({period},{maxLag})", period, maxLag) | ||
{ | ||
} | ||
|
||
/// <summary> | ||
/// Gets the period over which the indicator is calculated. | ||
/// </summary> | ||
public int WarmUpPeriod { get; } | ||
|
||
/// <summary> | ||
/// Indicates whether the indicator has enough data to produce a valid result. | ||
/// </summary> | ||
public override bool IsReady => _priceWindow.IsReady; | ||
|
||
/// <summary> | ||
/// Computes the next value of the Hurst Exponent indicator. | ||
/// </summary> | ||
/// <param name="input">The input data point to use for the next value computation.</param> | ||
/// <returns>The computed Hurst Exponent value, or zero if insufficient data is available.</returns> | ||
protected override decimal ComputeNextValue(IndicatorDataPoint input) | ||
{ | ||
_priceWindow.Add(input.Value); | ||
if (!_priceWindow.IsReady) | ||
{ | ||
return decimal.Zero; | ||
} | ||
|
||
// Sum of log(standard deviation) values | ||
var sumY = 0m; | ||
|
||
// Sum of log(lag) * log(standard deviation) | ||
var sumXY = 0m; | ||
|
||
foreach (var lag in _timeLags) | ||
{ | ||
var mean = 0m; | ||
var sumOfSquares = 0m; | ||
var count = Math.Max(0, _priceWindow.Size - lag); | ||
// Calculate the differences between values separated by the given lag | ||
for (var i = 0; i < count; i++) | ||
{ | ||
var value = _priceWindow[i + lag] - _priceWindow[i]; | ||
sumOfSquares += value * value; | ||
mean += value; | ||
} | ||
|
||
var standardDeviation = 0.0; | ||
// Avoid division by zero | ||
if (count > 0) | ||
{ | ||
mean = mean / count; | ||
var variance = (sumOfSquares / count) - (mean * mean); | ||
standardDeviation = Math.Sqrt((double)variance); | ||
} | ||
|
||
// Compute log(standard deviation) and log(lag) for the regression. | ||
var logTau = standardDeviation == 0.0 ? 0m : (decimal)Math.Log(standardDeviation); | ||
var logLag = (decimal)Math.Log(lag); | ||
|
||
// Accumulate sums for the regression equation. | ||
sumY += logTau; | ||
sumXY += logLag * logTau; | ||
} | ||
|
||
// Number of time lags used for the computation | ||
var n = _timeLags.Count; | ||
|
||
// Compute the Hurst Exponent using the slope of the log-log regression. | ||
var hurstExponent = (n * sumXY - _sumX * sumY) / (n * _sumX2 - _sumX * _sumX); | ||
return hurstExponent; | ||
} | ||
|
||
/// <summary> | ||
/// Resets the indicator to its initial state. This clears all internal data and resets | ||
/// </summary> | ||
public override void Reset() | ||
{ | ||
_priceWindow.Reset(); | ||
base.Reset(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
/* | ||
* 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 System; | ||
using NUnit.Framework; | ||
using QuantConnect.Indicators; | ||
|
||
namespace QuantConnect.Tests.Indicators | ||
{ | ||
[TestFixture] | ||
public class HurstExponentTests : CommonIndicatorTests<IndicatorDataPoint> | ||
{ | ||
protected override IndicatorBase<IndicatorDataPoint> CreateIndicator() | ||
{ | ||
return new HurstExponent("HE", 252, 20); | ||
} | ||
protected override string TestFileName => "spy_hurst_exponent.csv"; | ||
|
||
protected override string TestColumnName => "hurst_exponent"; | ||
|
||
[Test] | ||
public void DoesNotThrowDivisionByZero() | ||
{ | ||
var he = new HurstExponent(2); | ||
var date = new DateTime(2024, 12, 2, 12, 0, 0); | ||
|
||
for (var i = 0; i < 10; i++) | ||
{ | ||
Assert.DoesNotThrow(() => he.Update(date, 0m)); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.