Skip to content

Commit

Permalink
Services
Browse files Browse the repository at this point in the history
  • Loading branch information
artemiusgreat committed Oct 23, 2024
1 parent f4af3b8 commit 197d14b
Show file tree
Hide file tree
Showing 13 changed files with 420 additions and 30 deletions.
6 changes: 5 additions & 1 deletion Libs/Estimator.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<PropertyGroup>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<Version>1.0.3</Version>
<Version>1.0.4</Version>
<Description>Statistics and performance metrics in trading, CAGR, Sharpe, MAE, MFE, and others.</Description>
<Authors>artemiusgreat</Authors>
<Copyright>indemos.com</Copyright>
Expand All @@ -15,5 +15,9 @@
<PackageTags>finance trading metrics forex performance-metrics stock-market stocks futures strategies backtestings</PackageTags>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="MathNet.Numerics" Version="5.0.0" />
</ItemGroup>

</Project>
3 changes: 1 addition & 2 deletions Libs/Estimators/CAGR/CAGR.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using Estimator.Extensions;
using Estimator.Models;
using System;
using System.Collections.Generic;
Expand All @@ -21,6 +20,6 @@ public class CAGR
/// <summary>
/// Calculate
/// </summary>
public virtual double Calculate() => (Math.Pow(Items.Sum(o => o.Value), 1.0 / Count) - 1.0).Round();
public virtual double Calculate() => Math.Pow(Items.Sum(o => o.Value), 1.0 / Count) - 1.0;
}
}
4 changes: 2 additions & 2 deletions Libs/Estimators/Edge/EdgeRatio.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using Estimator.Extensions;
using Estimator.Models;
using System;
using System.Collections.Generic;

namespace Estimator.Estimators
Expand All @@ -19,7 +19,7 @@ public virtual double Calculate()
var averageGain = new MFE { Items = Items }.Calculate();
var averageLoss = new MAE { Items = Items }.Calculate();

return (averageGain / averageLoss).Round();
return averageGain / averageLoss;
}
}
}
3 changes: 1 addition & 2 deletions Libs/Estimators/Kestner/KestnerRatio.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using Estimator.Extensions;
using Estimator.Models;
using System;
using System.Collections.Generic;
Expand Down Expand Up @@ -26,7 +25,7 @@ public virtual double Calculate()
error = 1.0;
}

return (slope / error).Round();
return slope / error;
}
}
}
4 changes: 2 additions & 2 deletions Libs/Estimators/MAE/MAE.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using Estimator.Extensions;
using Estimator.Models;
using System;
using System.Collections.Generic;
using System.Linq;

Expand All @@ -15,6 +15,6 @@ public class MAE
/// <summary>
/// Calculate
/// </summary>
public virtual double Calculate() => Items.Average(o => o.Value - o.Min).Round();
public virtual double Calculate() => Items.Average(o => o.Value - o.Min);
}
}
4 changes: 2 additions & 2 deletions Libs/Estimators/MFE/MFE.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using Estimator.Extensions;
using Estimator.Models;
using System;
using System.Collections.Generic;
using System.Linq;

Expand All @@ -15,6 +15,6 @@ public class MFE
/// <summary>
/// Calculate
/// </summary>
public virtual double Calculate() => Items.Average(o => o.Max - o.Value).Round();
public virtual double Calculate() => Items.Average(o => o.Max - o.Value);
}
}
3 changes: 1 addition & 2 deletions Libs/Estimators/PF/PF.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using Estimator.Extensions;
using Estimator.Models;
using System;
using System.Collections.Generic;
Expand Down Expand Up @@ -26,7 +25,7 @@ public virtual double Calculate()
gains = 1.0;
}

return Math.Abs(gains / losses).Round();
return Math.Abs(gains / losses);
}
}
}
3 changes: 1 addition & 2 deletions Libs/Estimators/RAR/RAR.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using Estimator.Extensions;
using Estimator.Models;
using System;
using System.Collections.Generic;
Expand Down Expand Up @@ -70,7 +69,7 @@ public virtual double Calculate()
denominator = 1.0;
}

return (Math.Sqrt(Count) * (mean - Rate) / denominator).Round();
return Math.Sqrt(Count) * (mean - Rate) / denominator;
}
}
}
1 change: 0 additions & 1 deletion Libs/Estimators/Regression/Regression.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using Estimator.Extensions;
using Estimator.Models;
using System;
using System.Collections.Generic;
Expand Down
3 changes: 1 addition & 2 deletions Libs/Estimators/StandardScore/StandardScore.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using Estimator.Extensions;
using Estimator.Models;
using System;
using System.Collections.Generic;
Expand Down Expand Up @@ -48,7 +47,7 @@ public virtual double Calculate()
deviation = 1.0;
}

return ((seriesCount - mean - 0.5) / deviation).Round();
return (seriesCount - mean - 0.5) / deviation;
}
}
}
12 changes: 0 additions & 12 deletions Libs/Extensions/Double.cs

This file was deleted.

147 changes: 147 additions & 0 deletions Libs/Services/CointegrationService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
using MathNet.Numerics;
using MathNet.Numerics.LinearAlgebra;
using MathNet.Numerics.LinearAlgebra.Factorization;
using System;
using System.Linq;

namespace Estimator.Services
{
/// <summary>
/// References
/// Numerically Stable Cointegration Analysis - Jurgen Doornik
/// Alternative asymptotics for cointegration tests in large VARs - Alexei Onatski and Chen Wang
/// Testing for Cointegration Using the Johansen Methodology when Variables are Near-Integrated - Erik Hjalmarsson
/// Time Series Analysis - Hamilton, J.D.
/// https://github.com/iisayoo/johansen/blob/master/johansen/johansen.py
/// </summary>
public class CointegrationService
{
/// <summary>
/// Cointegration
/// </summary>
/// <param name="inputMatrix"></param>
/// <param name="lags"></param>
/// <returns></returns>
public static (double, Vector<double>) Johansen(Matrix<double> inputMatrix, int lags = 1)
{
var matrix = Center(inputMatrix);
var variables = matrix.ColumnCount;
var observations = matrix.RowCount - Math.Max(1, lags);

// Step 1: First difference
var xSub =
matrix.SubMatrix(1, matrix.RowCount - 1, 0, variables) -
matrix.SubMatrix(0, matrix.RowCount - 1, 0, variables);

// Step 2: Create lagged matrices
var xLag = Lag(matrix, lags);
var xSubLag = Lag(xSub, lags);

// Step 3: Resize matrices
xSub = xSub.Resize(observations, xSub.ColumnCount);
xLag = xLag.Resize(observations, xLag.ColumnCount);
xSubLag = xSubLag.Resize(observations, xSubLag.ColumnCount);

// Step 4: Compute residuals of xSub and xLag on xSubLag
var xSubLagInv = xSubLag.PseudoInverse();
var u = xSub - xSubLag * xSubLagInv * xSub;
var v = xLag - xSubLag * xSubLagInv * xLag;

// Step 5: Compute eigenvalues and eigenvectors
var Suu = u.TransposeThisAndMultiply(u) / observations;
var Svv = v.TransposeThisAndMultiply(v) / observations;
var Suv = u.TransposeThisAndMultiply(v) / observations;
var evd = (Svv.PseudoInverse() * Suv.TransposeThisAndMultiply(Suu.PseudoInverse() * Suv)).Evd();
var eigenValues = evd.EigenValues.Real();
var maxIndex = eigenValues.MaximumIndex();
var eigenVectors = evd.EigenVectors.Column(maxIndex);

// Step 6: Compare trace statistics with critical values to determine cointegration rank
var rank = GetRank(eigenValues, observations);

return (rank, eigenVectors);
}

/// <summary>
/// Subtract mean
/// </summary>
/// <param name="matrix"></param>
/// <returns></returns>
private static Matrix<double> Center(Matrix<double> matrix)
{
var columnMeans = matrix.ColumnSums() / matrix.RowCount;
var mean = Matrix<double>.Build.Dense(matrix.RowCount, matrix.ColumnCount, (i, ii) => columnMeans[ii]);
return matrix - mean;
}

/// <summary>
/// Get approximated critical value for specified rank
/// </summary>
/// <param name="vars"></param>
/// <param name="rank"></param>
/// <returns></returns>
private static double GetCriticalValue(int rank, int numVariables)
{
// Higher base critical value for better scaling
double baseCriticalValue = 3.76;

// Stronger scaling factors to match real-world growth in critical values
double variableFactor = Math.Pow(2.5, numVariables - 2); // More aggressive scaling for variables
double rankFactor = Math.Pow(2.5, rank); // Steep scaling for rank

// Final critical value is the base value multiplied by both factors
double criticalValue = baseCriticalValue * variableFactor * rankFactor;

return criticalValue;
}

/// <summary>
/// Lag matrix
/// </summary>
/// <param name="matrix"></param>
/// <param name="lags"></param>
/// <param name="sub"></param>
/// <returns></returns>
private static Matrix<double> Lag(Matrix<double> matrix, int lags = 1)
{
var response = Matrix<double>.Build.Dense(matrix.RowCount, matrix.ColumnCount * lags);

for (var i = 0; i < lags; i++)
{
var subMatrix = matrix.SubMatrix(0, matrix.RowCount - i, 0, matrix.ColumnCount);
response.SetSubMatrix(i + 1, subMatrix.RowCount - 1, matrix.ColumnCount * i, subMatrix.ColumnCount, subMatrix);
}

return response;
}

/// <summary>
/// Get
/// </summary>
/// <param name="eigenValues"></param>
/// <param name="observations"></param>
/// <returns></returns>
private static int GetRank(Vector<double> eigenValues, int observations)
{
var variables = eigenValues.Count;

// Loop through from rank r to m (the number of variables)
for (var i = 0; i < variables; i++)
{
// Calculate the test statistic: -t * sum(log(1 - eigenvalues[i:]))
var statistic = -observations * eigenValues.SubVector(i + 1, variables - i - 1).Sum(o => Math.Log(1 - o));
// Get the corresponding critical value for this rank
var critValue = GetCriticalValue(i, variables);

// If the statistic is less than the critical value, return the rank i
if (statistic < critValue)
{
return i;
}
}

// If no rank satisfies the condition, return m
return variables;
}
}
}
Loading

0 comments on commit 197d14b

Please sign in to comment.