diff --git a/Docs/coverage.md b/Docs/coverage.md
index f8de0937..aff5dc6c 100644
--- a/Docs/coverage.md
+++ b/Docs/coverage.md
@@ -20,7 +20,7 @@
| BIAS - Bias |✔️|||✔️|
| ENTR - Entropy |✔️|||✔️|
| KUR - Kurtosis |✔️|||✔️|
-| LINREG - Linear Regression ||✔️|✔️||
+| LINREG - Linear Regression |✔️|✔️|✔️||
| MAD - Mean Absolute Deviation |✔️||✔️|✔️|
| MAPE - Mean Absolute Percent Error |✔️||✔️||
| MAX - Max value |✔️|✔️|||
@@ -30,9 +30,7 @@
| PSDEV - Population Standard Deviation |✔️||||
| PVAR - Population Variance |✔️||||
| QUANTILE ||||✔️|
-| RS - R-Squared Coefficient |||✔️||
| SKEW - Skewness ||||✔️|
-| SLOPE - Slope |||✔️||
| SMAPE - Symmetric Mean Absolute Percent Error |✔️||||
| SDEV - Sample Standard Deviation |✔️|✔️|✔️|✔️|
| VAR - Sample Variance |✔️|||✔️|
diff --git a/Source/Statistics/LINREG_Series.cs b/Source/Statistics/LINREG_Series.cs
new file mode 100644
index 00000000..9c7aeff0
--- /dev/null
+++ b/Source/Statistics/LINREG_Series.cs
@@ -0,0 +1,93 @@
+namespace QuanTAlib;
+using System;
+
+/*
+LINREG: Linear Regression (using Least Square Method)
+ Linear Regression provides a slope of a straight line that is the best approximation of the given set of data.
+ The method of least squares is a standard approach in linear regression analysis to approximate the solution
+ by minimizing the sum of the squares of the residuals made in the results of each individual equation.
+
+Additional outputs provided by LINREG:
+ .Intercept - y-intercept point of the best fit line
+ .RSquared - R-Squared (R²), Coefficient of Determination
+ .StdDev - Standard Deviation of data over given periods
+
+ y = Slope * x + Intercept
+
+Sources:
+ https://en.wikipedia.org/wiki/Least_squares
+
+ */
+
+public class LINREG_Series : Single_TSeries_Indicator
+{
+ public readonly TSeries Intercept = new();
+ public readonly TSeries RSquared = new();
+ public readonly TSeries StdDev = new();
+ private readonly System.Collections.Generic.List _buffer = new();
+
+ public LINREG_Series(TSeries source, int period, bool useNaN = false)
+ : base(source, period, useNaN)
+ {
+ if (this._data.Count > 0) { base.Add(this._data); }
+ }
+
+ public override void Add((System.DateTime t, double v) TValue, bool update)
+ {
+ if (update) { this._buffer[this._buffer.Count - 1] = TValue.v; }
+ else { this._buffer.Add(TValue.v); }
+ if (this._buffer.Count > this._p) { this._buffer.RemoveAt(0); }
+
+ int _len = this._buffer.Count;
+
+ // get averages for period
+ double sumX = 0;
+ double sumY = 0;
+
+ for (int p = 0; p < _len; p++)
+ {
+ sumX += this.Count - _len + 2 + p;
+ sumY += _buffer[p];
+ }
+ double avgX = sumX / _len;
+ double avgY = sumY / _len;
+
+ // least squares method
+ double sumSqX = 0;
+ double sumSqY = 0;
+ double sumSqXY = 0;
+
+ for (int p = 0; p < _len; p++)
+ {
+ double devX = this.Count - _len + 2 + p - avgX;
+ double devY = _buffer[p] - avgY;
+
+ sumSqX += devX * devX;
+ sumSqY += devY * devY;
+ sumSqXY += devX * devY;
+ }
+
+ double _slope = sumSqXY / sumSqX;
+ double _intercept = avgY - (_slope * avgX);
+
+ // calculate Standard Deviation and R-Squared
+ double stdDevX = Math.Sqrt((double)sumSqX / _len);
+ double stdDevY = Math.Sqrt((double)sumSqY / _len);
+ double _StdDev = stdDevY;
+
+ double arrr = (stdDevX * stdDevY != 0) ? (double)sumSqXY / (stdDevX * stdDevY) / _len : 0;
+ double _RSquared = arrr * arrr;
+
+ var ret = (TValue.t, this.Count < this._p - 1 && this._NaN ? double.NaN : _slope);
+ base.Add(ret, update);
+
+ ret = (TValue.t, this.Count < this._p - 1 && this._NaN ? double.NaN : _intercept);
+ Intercept.Add(ret, update);
+
+ ret = (TValue.t, this.Count < this._p - 1 && this._NaN ? double.NaN : _StdDev);
+ StdDev.Add(ret, update);
+
+ ret = (TValue.t, this.Count < this._p - 1 && this._NaN ? double.NaN : _RSquared);
+ RSquared.Add(ret, update);
+ }
+}
\ No newline at end of file
diff --git a/Tests/Statistics/LINREG_Test.cs b/Tests/Statistics/LINREG_Test.cs
new file mode 100644
index 00000000..c0370af1
--- /dev/null
+++ b/Tests/Statistics/LINREG_Test.cs
@@ -0,0 +1,33 @@
+using Xunit;
+using System;
+using QuanTAlib;
+
+namespace Statistics;
+public class LINREG_Test
+{
+ [Fact]
+ public void Add_Test()
+ {
+ TSeries a = new() { 0, 1, 2, 3, 4, 5 };
+ LINREG_Series c = new(a, 3);
+ Assert.Equal(6, c.Count);
+ a.Add(5);
+ Assert.Equal(a.Count, c.Count);
+ a.Add(0, update: true);
+ Assert.Equal(a.Count, c.Count);
+ }
+
+ [Fact]
+ public void Edge_Test()
+ {
+ TSeries a = new() { double.NaN, double.Epsilon, double.PositiveInfinity, double.MaxValue };
+ LINREG_Series c = new(a, 3);
+ Assert.Equal(a.Count, c.Count);
+ a.Add(double.NaN);
+ Assert.Equal(a.Count, c.Count);
+ a.Add(double.PositiveInfinity);
+ Assert.Equal(a.Count, c.Count);
+
+ }
+
+}
diff --git a/Tests/Validations/Skender_Stock.cs b/Tests/Validations/Skender_Stock.cs
index e2924779..c45517bf 100644
--- a/Tests/Validations/Skender_Stock.cs
+++ b/Tests/Validations/Skender_Stock.cs
@@ -161,4 +161,16 @@ public class Skender_Stock
Assert.Equal(Math.Round((double)SK.Last().Alma!, 8), Math.Round(QL.Last().v, 8));
}
+
+ [Fact]
+ public void LINREG()
+ {
+ LINREG_Series QL = new(this.bars.Close, this.period, useNaN: false);
+ var SK = this.quotes.GetSlope(this.period);
+
+ Assert.Equal(Math.Round((double)SK.Last().Slope!, 8), Math.Round(QL.Last().v, 8));
+ Assert.Equal(Math.Round((double)SK.Last().Intercept!, 8), Math.Round(QL.Intercept.Last().v, 8));
+ Assert.Equal(Math.Round((double)SK.Last().RSquared!, 8), Math.Round(QL.RSquared.Last().v, 8));
+ Assert.Equal(Math.Round((double)SK.Last().StdDev!, 8), Math.Round(QL.StdDev.Last().v, 8));
+ }
}