Tutorial: Stock market analysis using the Hurst Exponent in C#

Tutorial: Stock market analysis using the Hurst Exponent in C#

Disclaimer: This tutorial is for simplified demonstration/educational purposes and not intended for production applications. We cannot be held responsible for any misuse, errors, damages, or losses. Use at your own risk.

1. Overview

As a result of requests from our clients, we’ve decided to publish an article about the Hurst Exponent (H), which is a ubiquitously used econometric measure used for potential stock market studies in investment applications. H indicates the long-term memory of a time series (Y(t)) by examining the time series’ tendency to regress to the mean; H is a number between 0 and 1. When H is closer to 0.5, the data series is mean-reversive, indicating the tendency for Y to return to the mean. Values closer to 1 indicate that increases in Y typically correlate with increases in Y at future points in time. However, values closer to 0 indicate long-term switching between sequence values.

There are a gamut of implementations in MATLAB and Python, but not many in C#. In this discussion, we elucidate our implementation in the following sections:

  • Current Capability
  • Implementation
  • Test Results
  • Possible Enhancements & Next Steps
  • Classes & Methods Declaration
  • Future Thinking
  • Appendix – Code Files & References

2. Current Capability

We developed a Hurst Exponent calculation in C# that:

  • Performs an R/S Hurst Exponent (uncorrected) calculation for inputs with integer powers of two length
  • Implements a least squares linear regression for the final R/S calculation
  • Reads input data series from a CSV file
  • Takes care of some exception handling conditions
  • Was tested with Python Hurst Library
  • Passed test conditions when inputs Type==change and simplified==true were set [1]
  • Was tested with input sizes of 128, 256, 512, 2048, 4096, and 8192

3. Implementation

Fundamentally, we implemented the Hurst Exponent by the conventional R/S method. The variants of this method are apparent in different applications with various assumptions on how the input data is modeled [1]:

  • Change
  • Price
  • Random-Walk

We implemented the change variant, which is described below (and can result in different answers based on the variant). Support for different variants is another area of possible improvement we can focus on. Let’s walk through the algorithm.

Our code exposes the following interface

public Tuple<doubledouble> calcHurstExp(double[] inputData)

to calculate the Hurst Exponent. Here are additional details of what this function does.

1.  First, we determine sizes of our division arrays. Assume N = 512 elements in the inputData array. In our case, we have the following table.

Division (D) Chunk Size (C­s)
0 512
1 256
2 128
3 64
4 32
5 16
6 8
7 4

2. Next, we loop through each division. For each division, calculate the normalized R/S value for that division and keep it later for linear regression (one of the last steps below). For this example, let’s choose D = 2.

var divCaRS = GetDivR_S(double[] inputData, int div);

3. Furthermore, we need to loop through the input array and create N/CS chunks for analysis. For each double[] chunk, we need to calculate the R/S value.

double RS = getChunkRS(chunk);

4. To calculate the non-normalized R/S value for a given chunk we follow the following steps:

4.1. Find the mean of the chunk

4.2. Find the standard deviation (S) of the chunk

4.3. Create a mean-centered series

4.4. Find the cumulative deviation of the mean-centered series

4.5. Calculate the range(R) of the cumulative deviation

4.6. Calculate the non re-scaled range (R/S)

5. Furthermore, we will average all the R/S values for the chunk in a given division

double RS_Div = RSArr.Average();

6. Additionally, the Natural Log[RS_Div] and Natural Log[size(chunk)] needs to be determined so we can linearly fit the power curve corresponding to the overall data.

Log_RS_Div_Arr[div] = Math.Log(RS_Div, mathBase);
Log_Size_Div_Arr[div] = Math.Log(chunkSize, mathBase);

7. Finally, by using the least squares linear regression, we can find the slope of logarithm of R/S with respect to the logarithm of the division size. The slope of this line is the Hurst Exponent.

Tuple<doubledouble> HC = LinearRegression(Log_Size_Div_Arr, Log_RS_Div_Arr);

4. Test Results

Currently, our implementation was tested against the Python Hurst Library [1] as well as the MATLAB/Octave example [2]. When our code was tested against the Python code, the following prototype was used: [H, c, data = compute_Hc2(series, kind=‘change’, simplified=False)], yielding results within 1-2% [2]. Deviations from identical results is mostly due to the use of different division windows. Our code was significantly faster than the Python implementation. Of course, more rigorous testing will be needed for accuracy tweaking.

Figure 1 – C# Basic Hurst Testing (Simplonics Implemented)

Figure 2 – Hurst Verification in Python based on [1]

Figure 3. Python with modified window sizes based on [1]

Figure 4 – MATLAB/Octave Result based on [2]

Figure 5 – MATLAB/Octave Result based on [2]

5. Possible Enhancements & Next Steps

Even though the C# implementation agrees with several prominent benchmarks, it (as well as some of those benchmarks) fail to work under all circumstances. Therefore, this application is not ready for production deployment until Simplonics implements these enhancements. We outline several steps below that we can follow to make the application more accurate, faster, or more memory friendly. Some include resolving current limitations:

  1. Adding support for inputs whose sizes are not integer powers of 2
  2. Theoretical/Empirical R/S Correction, such as Anis-Lloyd/Peters Correction
  3. Ensure program works correctly with different input types
    • Currently, there are some corrections and other algorithms needed for reliable accuracy that are not yet implemented
      • The program sometimes outputs a Hurst indicator slightly above one as a result of not-yet implemented corrective measures.
  4. Optimize Speed
    • This becomes more critical as the size of the input increases. Forms of parallel algorithms can be employed
  5. Utilize other algorithms, such as wavelets, FD4, and others to avoid biases that exist with current R/S calculation method as input size increases

6. Classes & Methods Declaration

C# Files:

  • FileHandler.cs – Opens and reads files, such as CSV files for data inputs

class FileHandler
{
    public string GetTestPath();
    public double[] ReadCSV(string filePath)
}

HurstExponent.cs– C# file that performs the Hurst exponent calculation on a given input. The main function is here.

namespace HurstExponential
{
    class HurstExp
    {   
        public double mathBase = 10;      
        public double StdDev(double[] arr, double mean, int N)
        public double Mean(double[] arr, int N)
        public double[] MeanCenteredArr(double[] arr, int N, 
                                        double mean)
        public bool aEqual(double x, double y)
        public double[] CumDevArr(double[] mcArr, int N)
        public double getChunkRS(double[] chunkArr)
        public void PrintArray(double[] X)
        private void assert(bool v)
        public Tuple<int, double[]> GetDivR_S(double[] arr, int div)
        /* Gets specific divisions's R/S Ratio as an array of each
         * chunk's natural (non re-scaled) R_S value*/
        public double[] Slice(double[] arr, int start, int end)
        private bool CheckForValidInputs(double[] inputData)
        public void Print_RS_Table(double[] Log_RS_Div_Arr,
                                   double[] Log_Size_Div_Arr)
        public Tuple<double, double> calcHurstExp(double[] inputData)
        /*Highest-Level function that calculates the Hurst Exponential
         * Assumes input length is an integer power of 2 */
        
        public Tuple <double, double> LinearRegression(double[] X,
                                                       double[] Y)
        /* Calculating a Least Squares Regression -  
         *Returns slope and yint of linear regression for a best fit curve*/
        class HurstExpWrapper
         {
           static void Main(string[] args)
         }

}
}

  • UnitTest.cs– Unit tests that that performs the Hurst exponent calculation on a given input.
public class UnitTests
{
    bool AlmostEqual(double X, double Y, double t)
    public bool performUnitTest(double expH, double expC,
                  string fn = "pyTest_256.csv", double t = 0.02)
    public bool MainUnitTests()
}

7. Future Thinking

What are your thoughts on these additional concepts that Simplonics can help you realize?

  • Econometric Developments
    • Dickey-Fuller Test
    • Other algorithms
  • C# GUI Implementation
    • For an enhanced customer user experience and integration with your existing code base
  • Network Programming
    • Hosting your financial solution on a globally accessible platform for your different customers to use

8. Appendix:

a. Code Files

Available upon request here

b. References:

[1] https://pypi.org/project/hurst/

[2] http://prac.im.pwr.edu.pl/~hugo/RePEc/wuu/hscode/hurst.m

Leave a Reply

Discover more from Simplonics

Subscribe now to keep reading and get access to the full archive.

Continue reading