Lean  $LEAN_TAG$
HurstExponent.cs
1 /*
2  * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
3  * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14 */
15 
16 using System;
17 using System.Collections.Generic;
18 
20 {
21  /// <summary>
22  /// Represents the Hurst Exponent indicator, which is used to measure the long-term memory of a time series.
23  /// - H less than 0.5: Mean-reverting; high values followed by low ones, stronger as H approaches 0.
24  /// - H equal to 0.5: Random walk (geometric).
25  /// - H greater than 0.5: Trending; high values followed by higher ones, stronger as H approaches 1.
26  /// </summary>
28  {
29  /// <summary>
30  /// A rolling window that holds the most recent price values.
31  /// </summary>
32  private readonly RollingWindow<decimal> _priceWindow;
33 
34  /// <summary>
35  /// The list of time lags used to calculate tau values.
36  /// </summary>
37  private readonly List<int> _timeLags;
38 
39  /// <summary>
40  /// Sum of the logarithms of the time lags, precomputed for efficiency.
41  /// </summary>
42  private readonly decimal _sumX;
43 
44  /// <summary>
45  /// Sum of the squares of the logarithms of the time lags, precomputed for efficiency.
46  /// </summary>
47  private readonly decimal _sumX2;
48 
49  /// <summary>
50  /// Initializes a new instance of the <see cref="HurstExponent"/> class.
51  /// The default maxLag value of 20 is chosen for reliable and accurate results, but using a higher lag may reduce precision.
52  /// </summary>
53  /// <param name="name">The name of the indicator.</param>
54  /// <param name="period">The period over which to calculate the Hurst Exponent.</param>
55  /// <param name="maxLag">The maximum lag to consider for time series analysis.</param>
56  public HurstExponent(string name, int period, int maxLag = 20) : base(name)
57  {
58  if (maxLag < 2)
59  {
60  throw new ArgumentException("The maxLag parameter must be greater than 2 to compute the Hurst Exponent.", nameof(maxLag));
61  }
62  _priceWindow = new RollingWindow<decimal>(period);
63  _timeLags = new List<int>();
64 
65  // Precompute logarithms of time lags and their squares for regression calculations
66  for (var i = 2; i < maxLag; i++)
67  {
68  var logTimeLag = (decimal)Math.Log(i);
69  _timeLags.Add(i);
70  _sumX += logTimeLag;
71  _sumX2 += logTimeLag * logTimeLag;
72  }
73  WarmUpPeriod = period;
74  }
75 
76  /// <summary>
77  /// Initializes a new instance of the <see cref="HurstExponent"/> class with the specified period and maxLag.
78  /// The default maxLag value of 20 is chosen for reliable and accurate results, but using a higher lag may reduce precision.
79  /// </summary>
80  /// <param name="period">The period over which to calculate the Hurst Exponent.</param>
81  /// <param name="maxLag">The maximum lag to consider for time series analysis.</param>
82  public HurstExponent(int period, int maxLag = 20)
83  : this($"HE({period},{maxLag})", period, maxLag)
84  {
85  }
86 
87  /// <summary>
88  /// Gets the period over which the indicator is calculated.
89  /// </summary>
90  public int WarmUpPeriod { get; }
91 
92  /// <summary>
93  /// Indicates whether the indicator has enough data to produce a valid result.
94  /// </summary>
95  public override bool IsReady => _priceWindow.IsReady;
96 
97  /// <summary>
98  /// Computes the next value of the Hurst Exponent indicator.
99  /// </summary>
100  /// <param name="input">The input data point to use for the next value computation.</param>
101  /// <returns>The computed Hurst Exponent value, or zero if insufficient data is available.</returns>
102  protected override decimal ComputeNextValue(IndicatorDataPoint input)
103  {
104  _priceWindow.Add(input.Value);
105  if (!_priceWindow.IsReady)
106  {
107  return decimal.Zero;
108  }
109 
110  // Sum of log(standard deviation) values
111  var sumY = 0m;
112 
113  // Sum of log(lag) * log(standard deviation)
114  var sumXY = 0m;
115 
116  foreach (var lag in _timeLags)
117  {
118  var mean = 0m;
119  var sumOfSquares = 0m;
120  var count = Math.Max(0, _priceWindow.Size - lag);
121  // Calculate the differences between values separated by the given lag
122  for (var i = 0; i < count; i++)
123  {
124  var value = _priceWindow[i + lag] - _priceWindow[i];
125  sumOfSquares += value * value;
126  mean += value;
127  }
128 
129  var standardDeviation = 0.0;
130  // Avoid division by zero
131  if (count > 0)
132  {
133  mean = mean / count;
134  var variance = (sumOfSquares / count) - (mean * mean);
135  standardDeviation = Math.Sqrt((double)variance);
136  }
137 
138  // Compute log(standard deviation) and log(lag) for the regression.
139  var logTau = standardDeviation == 0.0 ? 0m : (decimal)Math.Log(standardDeviation);
140  var logLag = (decimal)Math.Log(lag);
141 
142  // Accumulate sums for the regression equation.
143  sumY += logTau;
144  sumXY += logLag * logTau;
145  }
146 
147  // Number of time lags used for the computation
148  var n = _timeLags.Count;
149 
150  // Compute the Hurst Exponent using the slope of the log-log regression.
151  var hurstExponent = (n * sumXY - _sumX * sumY) / (n * _sumX2 - _sumX * _sumX);
152  return hurstExponent;
153  }
154 
155  /// <summary>
156  /// Resets the indicator to its initial state. This clears all internal data and resets
157  /// </summary>
158  public override void Reset()
159  {
160  _priceWindow.Reset();
161  base.Reset();
162  }
163  }
164 }