From 428611315e71f30c4748539df539ec469911c223 Mon Sep 17 00:00:00 2001 From: Miha Date: Sun, 10 Nov 2024 11:11:38 -0800 Subject: [PATCH] prep --- QuanTAlib.sln | 6 - Tests/test_eventing.cs | 85 ++++------ Tests/test_updates_statistics.cs | 7 + lib/_list.md | 8 +- lib/class_types.md | 86 ++++++++++ lib/statistics/Granger.cs | 272 +++++++++++++++++++++++++++++++ lib/statistics/_list.md | 4 +- notebooks/ema.dib | 2 +- notebooks/validation.dib | 27 +++ 9 files changed, 428 insertions(+), 69 deletions(-) create mode 100644 lib/class_types.md create mode 100644 lib/statistics/Granger.cs create mode 100644 notebooks/validation.dib diff --git a/QuanTAlib.sln b/QuanTAlib.sln index ad664505..561c1471 100644 --- a/QuanTAlib.sln +++ b/QuanTAlib.sln @@ -20,8 +20,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Momentum", "quantower\Momen EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Experiments", "quantower\Experiments\_Experiments.csproj", "{F7F1F8D3-DAC0-4E9F-1FB2-F88D5F0E0E04}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SyntheticVendor", "SyntheticVendor\SyntheticVendor.csproj", "{1CF111D9-33E6-4A11-8FEC-F23300A78D15}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{2D97C971-20BF-40DB-94AA-3279F787D3CB}" EndProject Global @@ -65,10 +63,6 @@ Global {F7F1F8D3-DAC0-4E9F-1FB2-F88D5F0E0E04}.Debug|Any CPU.Build.0 = Debug|Any CPU {F7F1F8D3-DAC0-4E9F-1FB2-F88D5F0E0E04}.Release|Any CPU.ActiveCfg = Release|Any CPU {F7F1F8D3-DAC0-4E9F-1FB2-F88D5F0E0E04}.Release|Any CPU.Build.0 = Release|Any CPU - {1CF111D9-33E6-4A11-8FEC-F23300A78D15}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1CF111D9-33E6-4A11-8FEC-F23300A78D15}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1CF111D9-33E6-4A11-8FEC-F23300A78D15}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1CF111D9-33E6-4A11-8FEC-F23300A78D15}.Release|Any CPU.Build.0 = Release|Any CPU {2D97C971-20BF-40DB-94AA-3279F787D3CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2D97C971-20BF-40DB-94AA-3279F787D3CB}.Debug|Any CPU.Build.0 = Debug|Any CPU {2D97C971-20BF-40DB-94AA-3279F787D3CB}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/Tests/test_eventing.cs b/Tests/test_eventing.cs index 2fcd5510..1d9c6b7c 100644 --- a/Tests/test_eventing.cs +++ b/Tests/test_eventing.cs @@ -10,101 +10,74 @@ public class EventingTests private const int DefaultPeriod = 10; private const double Tolerance = 1e-9; - private static readonly (string Name, object[] DirectParams, object[] EventParams)[] ValueIndicators = new[] + private static readonly (string Name, object[] DirectParams, object[] EventParams)[] ValueIndicators = { ("Afirma", new object[] { DefaultPeriod, DefaultPeriod, Afirma.WindowType.BlackmanHarris }, new object[] { new TSeries(), DefaultPeriod, DefaultPeriod, Afirma.WindowType.BlackmanHarris }), ("Alma", new object[] { DefaultPeriod, 0.85, 6.0 }, new object[] { new TSeries(), DefaultPeriod, 0.85, 6.0 }), + ("Beta", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), ("Convolution", new object[] { new double[] {1,2,3,2,1} }, new object[] { new TSeries(), new double[] {1,2,3,2,1} }), + ("Corr", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), + ("Covar", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), + ("Curvature", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), ("Dema", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), ("Dsma", new object[] { DefaultPeriod, 0.9 }, new object[] { new TSeries(), DefaultPeriod, 0.9 }), ("Dwma", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), ("Ema", new object[] { DefaultPeriod, true }, new object[] { new TSeries(), DefaultPeriod, true }), + ("Entropy", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), ("Epma", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), - ("Pwma", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), ("Fisher", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), ("Frama", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), ("Fwma", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), ("Gma", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), + ("Granger", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), ("Hma", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), - ("Htit", System.Array.Empty(), new object[] { new TSeries() }), + ("Htit", Array.Empty(), new object[] { new TSeries() }), ("Hwma", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), ("Jma", new object[] { DefaultPeriod, 0, 0.45, 10 }, new object[] { new TSeries(), DefaultPeriod, 0, 0.45, 10 }), ("Kama", new object[] { DefaultPeriod, 2, 30 }, new object[] { new TSeries(), DefaultPeriod, 2, 30 }), + ("Kendall", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), + ("Kurtosis", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), ("Ltma", new object[] { 0.2 }, new object[] { new TSeries(), 0.2 }), ("Maaf", new object[] { 39, 0.002 }, new object[] { new TSeries(), 39, 0.002 }), ("Mama", new object[] { 0.5, 0.05 }, new object[] { new TSeries(), 0.5, 0.05 }), + ("Max", new object[] { DefaultPeriod, 0.0 }, new object[] { new TSeries(), DefaultPeriod, 0.0 }), + ("Median", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), ("Mgdi", new object[] { DefaultPeriod, 0.6 }, new object[] { new TSeries(), DefaultPeriod, 0.6 }), + ("Min", new object[] { DefaultPeriod, 0.0 }, new object[] { new TSeries(), DefaultPeriod, 0.0 }), ("Mma", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), + ("Mode", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), + ("Percentile", new object[] { DefaultPeriod, 0.5 }, new object[] { new TSeries(), DefaultPeriod, 0.5 }), + ("Pwma", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), ("Qema", new object[] { 0.2, 0.2, 0.2, 0.2 }, new object[] { new TSeries(), 0.2, 0.2, 0.2, 0.2 }), ("Rema", new object[] { DefaultPeriod, 0.5 }, new object[] { new TSeries(), DefaultPeriod, 0.5 }), ("Rma", new object[] { DefaultPeriod, true }, new object[] { new TSeries(), DefaultPeriod, true }), + ("Skew", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), + ("Slope", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), ("Sma", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), - ("Wma", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), - ("Tema", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), - ("Zlema", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), - ("Sinema", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), ("Smma", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), + ("Spearman", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), + ("Stddev", new object[] { DefaultPeriod, false }, new object[] { new TSeries(), DefaultPeriod, false }), ("T3", new object[] { DefaultPeriod, 0.7, true }, new object[] { new TSeries(), DefaultPeriod, 0.7, true }), + ("Tema", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), ("Trima", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), - ("Vidya", new object[] { DefaultPeriod, 0, 0.2 }, new object[] { new TSeries(), DefaultPeriod, 0, 0.2 }), - ("Apo", new object[] { 12, 26 }, new object[] { new TSeries(), 12, 26 }), - ("Macd", new object[] { 12, 26, 9 }, new object[] { new TSeries(), 12, 26, 9 }), - ("Rsi", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), - ("Rsx", new object[] { DefaultPeriod, 0, 0.55 }, new object[] { new TSeries(), DefaultPeriod, 0, 0.55 }), - ("Cmo", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), - ("Cog", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), - ("Curvature", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), - ("Entropy", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), - ("Kurtosis", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), - ("Max", new object[] { DefaultPeriod, 0.0 }, new object[] { new TSeries(), DefaultPeriod, 0.0 }), - ("Median", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), - ("Min", new object[] { DefaultPeriod, 0.0 }, new object[] { new TSeries(), DefaultPeriod, 0.0 }), - ("Mode", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), - ("Percentile", new object[] { DefaultPeriod, 0.5 }, new object[] { new TSeries(), DefaultPeriod, 0.5 }), - ("Skew", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), - ("Slope", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), - ("Stddev", new object[] { DefaultPeriod, false }, new object[] { new TSeries(), DefaultPeriod, false }), ("Variance", new object[] { DefaultPeriod, false }, new object[] { new TSeries(), DefaultPeriod, false }), - ("Zscore", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), - ("Beta", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), - ("Corr", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), - ("Covar", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), - ("Kendall", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), - ("Spearman", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), - ("Hv", new object[] { DefaultPeriod, false }, new object[] { new TSeries(), DefaultPeriod, false }), - ("Jvolty", new object[] { DefaultPeriod, 0 }, new object[] { new TSeries(), DefaultPeriod, 0 }), - ("Rv", new object[] { DefaultPeriod, false }, new object[] { new TSeries(), DefaultPeriod, false }), - ("Rvi", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), - ("Mae", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), - ("Mapd", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), - ("Mape", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), - ("Mase", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), - ("Mda", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), - ("Me", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), - ("Mpe", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), - ("Mse", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), - ("Msle", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), - ("Rae", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), - ("Rmse", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), - ("Rmsle", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), - ("Rse", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), - ("Smape", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), - ("Rsquared", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), - ("Huber", new object[] { DefaultPeriod, 1.0 }, new object[] { new TSeries(), DefaultPeriod, 1.0 }), - ("Cti", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }) + ("Vidya", new object[] { DefaultPeriod, 0, 0.2 }, new object[] { new TSeries(), DefaultPeriod, 0, 0.2 }), + ("Wma", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), + ("Zlema", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }), + ("Zscore", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }) }; - private static readonly (string Name, object[] DirectParams, object[] EventParams)[] BarIndicators = new[] + private static readonly (string Name, object[] DirectParams, object[] EventParams)[] BarIndicators = { - ("Adl", System.Array.Empty(), new object[] { new TBarSeries() }), + ("Adl", Array.Empty(), new object[] { new TBarSeries() }), ("Adosc", new object[] { 3, 10 }, new object[] { new TBarSeries(), 3, 10 }), - ("Aobv", System.Array.Empty(), new object[] { new TBarSeries() }), + ("Aobv", Array.Empty(), new object[] { new TBarSeries() }), ("Cmf", new object[] { 20 }, new object[] { new TBarSeries(), 20 }), ("Eom", new object[] { 14 }, new object[] { new TBarSeries(), 14 }), ("Kvo", new object[] { 34, 55 }, new object[] { new TBarSeries(), 34, 55 }), ("Atr", new object[] { 14 }, new object[] { new TBarSeries(), 14 }), ("Chop", new object[] { 14 }, new object[] { new TBarSeries(), 14 }), - ("Dosc", System.Array.Empty(), new object[] { new TBarSeries() }) + ("Dosc", Array.Empty(), new object[] { new TBarSeries() }) }; public static IEnumerable GetValueIndicatorData() diff --git a/Tests/test_updates_statistics.cs b/Tests/test_updates_statistics.cs index d33f57f9..5558f5ae 100644 --- a/Tests/test_updates_statistics.cs +++ b/Tests/test_updates_statistics.cs @@ -39,6 +39,13 @@ public void Entropy_Update() TestTValueUpdate(indicator, indicator.Calc); } + [Fact] + public void Granger_Update() + { + var indicator = new Granger(lags: 5); + TestDualTValueUpdate(indicator, indicator.Calc); + } + [Fact] public void Hurst_Update() { diff --git a/lib/_list.md b/lib/_list.md index bd54fd24..ca7b1d84 100644 --- a/lib/_list.md +++ b/lib/_list.md @@ -8,9 +8,9 @@ | Momentum | 17 | 0 | 17 | | Oscillators| 24 | 5 | 29 | | Patterns | 0 | 8 | 8 | -| Statistics | 21 | 2 | 23 | +| Statistics | 22 | 1 | 23 | | Volatility | 31 | 4 | 35 | -| Total | 126 | 19 | 145 | +| Total | 127 | 18 | 145 | ## Indicators by Category @@ -110,12 +110,13 @@ RPP - Rolling Pivot Points (Support 1-3, Pivot, Resistance 1-3) WF - Williams Fractal ZZ - Zig Zag Pattern -### Statistics (21/23) +### Statistics (22/23) ✔️ BETA - Beta coefficient measuring volatility relative to market ✔️ CORR - Correlation coefficient between two series ✔️ COVAR - Covariance between two series ✔️ CURVATURE - Curvature of a time series ✔️ ENTROPY - Information entropy of a series +✔️ GRANGER - Granger causality test ✔️ HURST - Hurst exponent for trend strength ✔️ KENDALL - Kendall rank correlation ✔️ KURTOSIS - Kurtosis measuring tail extremity @@ -133,7 +134,6 @@ ZZ - Zig Zag Pattern ✔️ VARIANCE - Statistical variance ✔️ ZSCORE - Z-score standardization COINTEGRATION - Test for cointegrated series -GRANGER - Granger causality test ### Volatility (31/35) ✔️ ADR - Average Daily Range diff --git a/lib/class_types.md b/lib/class_types.md new file mode 100644 index 00000000..57198155 --- /dev/null +++ b/lib/class_types.md @@ -0,0 +1,86 @@ +# QuanTAlib Class Types by Input Requirements + +## Two TValues Required +- Huber +- Mae +- Mapd +- Mape +- Mase +- Mda +- Me +- Mpe +- Mse +- Msle +- Rae +- Rmse +- Rmsle +- Rse +- Rsquared +- Smape +- Beta (asset vs market returns) +- Corr (correlation between two series) +- Covar (covariance between two series) +- Granger (Granger causality test) +- Kendall (Kendall rank correlation) +- Spearman (Spearman rank correlation) +- Theil (Theil's U statistic) + +## One TValue Required +- Curvature +- Entropy +- Hurst +- Kurtosis +- Max +- Median +- Min +- Mode +- Percentile +- Skew +- Slope +- Stddev +- Tsf +- Variance +- Zscore +- Apo +- Dpo +- Macd +- Mom +- Pmo +- Po +- Ppo +- Roc +- Trix +- Vel +- Ac +- Ao +- Bop +- Cci +- Cfo +- Chop +- Cmo +- Cog +- Coppock +- Crsi +- Cti +- Dosc +- Efi +- Fisher +- Rsi +- Rsx +- Smi +- Srsi +- Stc +- Stoch +- Tsi +- Uo +- Willr + +## One TBar Required +- Aroon (uses high/low) +- Vortex (uses high/low/close) + +## Two TBars Required +- Adx (requires two bars for true range calculation) +- Adxr (requires two bars for directional movement) +- Dmi (requires two bars for directional movement) +- Dmx (requires two bars for directional comparison) diff --git a/lib/statistics/Granger.cs b/lib/statistics/Granger.cs new file mode 100644 index 00000000..1f9e3b8c --- /dev/null +++ b/lib/statistics/Granger.cs @@ -0,0 +1,272 @@ +using System.Runtime.CompilerServices; +namespace QuanTAlib; + +/// +/// GRANGER: Granger Causality Test +/// A statistical test to determine whether one time series is useful in forecasting another. +/// Tests if past values of X help predict future values of Y beyond Y's own past values. +/// Returns a value between 0 and 1 representing the probability that X does not Granger-cause Y. +/// +/// +/// The Granger Causality calculation process: +/// 1. Fits two regression models: +/// - Restricted model: Y(t) = α₀ + Σ(β₁ᵢY(t-i)) + ε(t) +/// - Unrestricted model: Y(t) = α₀ + Σ(β₁ᵢY(t-i)) + Σ(β₂ᵢX(t-i)) + ε(t) +/// 2. Calculates F-statistic comparing the models +/// 3. Computes p-value from F-distribution +/// +/// Key characteristics: +/// - Tests predictive causality, not true causation +/// - Sensitive to lag selection +/// - Assumes stationarity of time series +/// - Useful for lead/lag relationship analysis +/// +/// Formula: +/// F = ((RSS₁ - RSS₂)/p) / (RSS₂/(n-2p-1)) +/// where: +/// RSS₁ = residual sum of squares from restricted model +/// RSS₂ = residual sum of squares from unrestricted model +/// p = number of lags +/// n = number of observations +/// +/// Market Applications: +/// - Lead/lag analysis between markets +/// - Price discovery analysis +/// - Market efficiency testing +/// - Intermarket analysis +/// - Risk spillover detection +/// +/// Sources: +/// https://en.wikipedia.org/wiki/Granger_causality +/// "Investigating Causal Relations by Econometric Models and Cross-spectral Methods" - C.W.J. Granger +/// +/// Note: Assumes linear relationships and stationarity +/// +[SkipLocalsInit] +public sealed class Granger : AbstractBase +{ + private readonly int Lags; + private readonly CircularBuffer _xValues; + private readonly CircularBuffer _yValues; + private const double Epsilon = 1e-10; + private const int MinimumLags = 1; + + /// The number of lags to use in the Granger causality test. + /// Thrown when lags is less than 1. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Granger(int lags) + { + if (lags < MinimumLags) + { + throw new ArgumentOutOfRangeException(nameof(lags), + "Number of lags must be at least 1 for Granger causality test."); + } + Lags = lags; + WarmupPeriod = lags + 1; + _xValues = new CircularBuffer(lags * 2); // Need extra space for lagged values + _yValues = new CircularBuffer(lags * 2); + Name = $"Granger(lags={lags})"; + Init(); + } + + /// The data source object that publishes updates. + /// The number of lags to use in the Granger causality test. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Granger(object source, int lags) : this(lags) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override void Init() + { + base.Init(); + _xValues.Clear(); + _yValues.Clear(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] + private double CalculateRSS(ReadOnlySpan y, ReadOnlySpan yhat) + { + double rss = 0; + for (int i = 0; i < y.Length; i++) + { + double residual = y[i] - yhat[i]; + rss += residual * residual; + } + return rss; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] + private static void FitOLS(ReadOnlySpan y, ReadOnlySpan x, Span beta) + { + // Simple OLS implementation for y = Xβ + ε + int n = y.Length; + int k = beta.Length; + + // Create X matrix (including constant term) + var X = new double[n, k]; + for (int i = 0; i < n; i++) + { + X[i, 0] = 1.0; // Constant term + for (int j = 1; j < k; j++) + { + X[i, j] = x[i * (k - 1) + (j - 1)]; + } + } + + // Calculate β = (X'X)⁻¹X'y + var XtX = new double[k, k]; + var Xty = new double[k]; + + // Calculate X'X and X'y + for (int i = 0; i < k; i++) + { + for (int j = 0; j < k; j++) + { + double sum = 0; + for (int l = 0; l < n; l++) + { + sum += X[l, i] * X[l, j]; + } + XtX[i, j] = sum; + } + + double sum2 = 0; + for (int l = 0; l < n; l++) + { + sum2 += X[l, i] * y[l]; + } + Xty[i] = sum2; + } + + // Solve system of equations + for (int i = 0; i < k; i++) + { + double pivot = XtX[i, i]; + if (Math.Abs(pivot) > Epsilon) + { + for (int j = 0; j < k; j++) + { + XtX[i, j] /= pivot; + } + Xty[i] /= pivot; + + for (int j = 0; j < k; j++) + { + if (i != j) + { + double factor = XtX[j, i]; + for (int l = 0; l < k; l++) + { + XtX[j, l] -= factor * XtX[i, l]; + } + Xty[j] -= factor * Xty[i]; + } + } + } + } + + // Copy results to beta + for (int i = 0; i < k; i++) + { + beta[i] = Xty[i]; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] + private double CalculateFStatistic(double rss1, double rss2, int n, int p) + { + // Calculate F-statistic + double numerator = (rss1 - rss2) / p; + double denominator = rss2 / (n - 2 * p - 1); + return numerator / denominator; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] + private static double FDistributionPValue(double f, int df1, int df2) + { + // Approximate p-value from F-distribution + // Using a simplified approximation for performance + double v = df2 / (df2 + df1 * f); + return Math.Pow(v, df2 / 2.0); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] + protected override double Calculation() + { + ManageState(Input.IsNew); + + _xValues.Add(Input.Value, Input.IsNew); + _yValues.Add(Input2.Value, Input.IsNew); + + double pValue = 1.0; // Null hypothesis: X does not Granger-cause Y + + if (_xValues.Count >= WarmupPeriod && _yValues.Count >= WarmupPeriod) + { + int n = _xValues.Count - Lags; + if (n > 2 * Lags + 1) + { + ReadOnlySpan x = _xValues.GetSpan(); + ReadOnlySpan y = _yValues.GetSpan(); + + // Prepare data for regression + var yData = y.Slice(Lags, n).ToArray(); + var restricted = new double[Lags + 1]; + var unrestricted = new double[2 * Lags + 1]; + + // Fit restricted model (only Y lags) + FitOLS(yData, y.Slice(0, n), restricted); + + // Calculate RSS for restricted model + var yhatRestricted = new double[n]; + for (int i = 0; i < n; i++) + { + yhatRestricted[i] = restricted[0]; + for (int j = 0; j < Lags; j++) + { + yhatRestricted[i] += restricted[j + 1] * y[i + Lags - j - 1]; + } + } + double rss1 = CalculateRSS(yData, yhatRestricted); + + // Fit unrestricted model (Y and X lags) + FitOLS(yData, x.Slice(0, n), unrestricted); + + // Calculate RSS for unrestricted model + var yhatUnrestricted = new double[n]; + for (int i = 0; i < n; i++) + { + yhatUnrestricted[i] = unrestricted[0]; + for (int j = 0; j < Lags; j++) + { + yhatUnrestricted[i] += unrestricted[j + 1] * y[i + Lags - j - 1]; + yhatUnrestricted[i] += unrestricted[j + Lags + 1] * x[i + Lags - j - 1]; + } + } + double rss2 = CalculateRSS(yData, yhatUnrestricted); + + // Calculate F-statistic and p-value + if (rss2 > Epsilon) + { + double f = CalculateFStatistic(rss1, rss2, n, Lags); + pValue = FDistributionPValue(f, Lags, n - 2 * Lags - 1); + } + } + } + + IsHot = _xValues.Count >= WarmupPeriod && _yValues.Count >= WarmupPeriod; + return pValue; + } +} diff --git a/lib/statistics/_list.md b/lib/statistics/_list.md index 3212e989..2c7ec039 100644 --- a/lib/statistics/_list.md +++ b/lib/statistics/_list.md @@ -1,11 +1,12 @@ # Statistics indicators -Done: 21, Todo: 2 +Done: 22, Todo: 1 ✔️ BETA - Beta coefficient measuring volatility relative to market ✔️ CORR - Correlation coefficient between two series ✔️ COVAR - Covariance between two series ✔️ CURVATURE - Curvature of a time series ✔️ ENTROPY - Information entropy of a series +✔️ GRANGER - Granger causality test ✔️ HURST - Hurst exponent for trend strength ✔️ KENDALL - Kendall rank correlation ✔️ KURTOSIS - Kurtosis measuring tail extremity @@ -23,4 +24,3 @@ Done: 21, Todo: 2 ✔️ VARIANCE - Statistical variance ✔️ ZSCORE - Z-score standardization COINTEGRATION - Test for cointegrated series -GRANGER - Granger causality test diff --git a/notebooks/ema.dib b/notebooks/ema.dib index 5f0a18ba..b1b0d5c7 100644 --- a/notebooks/ema.dib +++ b/notebooks/ema.dib @@ -4,7 +4,7 @@ #!csharp -#r "..\lib\obj\Debug\QuanTAlib.dll" +#r "../lib/obj/Debug/QuanTAlib.dll" using QuanTAlib; QuanTAlib.Formatters.Initialize(); diff --git a/notebooks/validation.dib b/notebooks/validation.dib new file mode 100644 index 00000000..7eecc47a --- /dev/null +++ b/notebooks/validation.dib @@ -0,0 +1,27 @@ +#!meta + +{"kernelInfo":{"defaultKernelName":"csharp","items":[{"aliases":[],"name":"csharp"}]}} + +#!csharp + +#r "../lib/obj/Debug/QuanTAlib.dll" +using QuanTAlib; +QuanTAlib.Formatters.Initialize(); + +#!csharp + +#r "nuget: ScottPlot" + +using ScottPlot; +using Microsoft.DotNet.Interactive.Formatting; +Formatter.Register(typeof(ScottPlot.Plot), (p, w) => + w.Write(((ScottPlot.Plot)p).GetSvgXml(600, 300)), HtmlFormatter.MimeType); + +#!csharp + +GbmFeed feed = new(); +TSeries data = new(feed.Close); +Ccv ma = new(feed,5); +TSeries result = new(ma); +feed.Add(12); +display(result);