sonar fixes

This commit is contained in:
Miha Kralj
2024-11-05 15:51:29 -08:00
parent 2b5c89640d
commit 0bae9ce15b
19 changed files with 801 additions and 141 deletions
+3 -8
View File
@@ -43,14 +43,9 @@ public sealed class Huber : AbstractBase
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Huber(int period, double delta = 1.0)
{
if (period < 1)
{
throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1.");
}
if (delta <= 0)
{
throw new ArgumentOutOfRangeException(nameof(delta), "Delta must be greater than 0.");
}
ArgumentOutOfRangeException.ThrowIfLessThan(period, 1);
ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(delta, 0);
WarmupPeriod = period;
_actualBuffer = new CircularBuffer(period);
_predictedBuffer = new CircularBuffer(period);
+7 -7
View File
@@ -60,14 +60,14 @@ public sealed class Macd : AbstractBase
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Macd(int fastPeriod = DefaultFastPeriod, int slowPeriod = DefaultSlowPeriod, int signalPeriod = DefaultSignalPeriod)
{
if (fastPeriod < 1)
throw new ArgumentOutOfRangeException(nameof(fastPeriod));
if (slowPeriod < 1)
throw new ArgumentOutOfRangeException(nameof(slowPeriod));
if (signalPeriod < 1)
throw new ArgumentOutOfRangeException(nameof(signalPeriod));
ArgumentOutOfRangeException.ThrowIfLessThan(fastPeriod, 1);
ArgumentOutOfRangeException.ThrowIfLessThan(slowPeriod, 1);
ArgumentOutOfRangeException.ThrowIfLessThan(signalPeriod, 1);
if (fastPeriod >= slowPeriod)
throw new ArgumentException("Fast period must be less than slow period");
{
throw new ArgumentOutOfRangeException(nameof(fastPeriod), "Fast period must be less than slow period");
}
_fastEma = new(fastPeriod);
_slowEma = new(slowPeriod);
+1 -1
View File
@@ -4,7 +4,7 @@ Done: 15, Todo: 2
✔️ ADX - Average Directional Movement Index
✔️ ADXR - Average Directional Movement Index Rating
✔️ APO - Absolute Price Oscillator
✔️ *DMI - Directional Movement Index (DI+, DI-)
✔️ DMI - Directional Movement Index (DI+, DI-)
✔️ DMX - Jurik Directional Movement Index
✔️ DPO - Detrended Price Oscillator
✔️ *MACD - Moving Average Convergence/Divergence (MACD, Signal, Histogram)
+3 -6
View File
@@ -51,12 +51,9 @@ public sealed class Coppock : AbstractBase
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Coppock(int roc1Period = DefaultRoc1Period, int roc2Period = DefaultRoc2Period, int wmaPeriod = DefaultWmaPeriod)
{
if (roc1Period < 1)
throw new ArgumentOutOfRangeException(nameof(roc1Period), "ROC1 period must be greater than 0");
if (roc2Period < 1)
throw new ArgumentOutOfRangeException(nameof(roc2Period), "ROC2 period must be greater than 0");
if (wmaPeriod < 1)
throw new ArgumentOutOfRangeException(nameof(wmaPeriod), "WMA period must be greater than 0");
ArgumentOutOfRangeException.ThrowIfLessThan(roc1Period, 1);
ArgumentOutOfRangeException.ThrowIfLessThan(roc2Period, 1);
ArgumentOutOfRangeException.ThrowIfLessThan(wmaPeriod, 1);
_roc1Period = roc1Period;
_roc2Period = roc2Period;
+1 -2
View File
@@ -48,8 +48,7 @@ public sealed class Rsi : AbstractBase
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Rsi(int period = DefaultPeriod)
{
if (period < 1)
throw new ArgumentOutOfRangeException(nameof(period));
ArgumentOutOfRangeException.ThrowIfLessThan(period, 1);
_avgGain = new(period, useSma: true);
_avgLoss = new(period, useSma: true);
_index = 0;
+3 -6
View File
@@ -57,12 +57,9 @@ public sealed class Smi : AbstractBase
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Smi(int period = DefaultPeriod, int smooth1 = DefaultSmooth1, int smooth2 = DefaultSmooth2)
{
if (period < 1)
throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than 0");
if (smooth1 < 1)
throw new ArgumentOutOfRangeException(nameof(smooth1), "Smooth1 must be greater than 0");
if (smooth2 < 1)
throw new ArgumentOutOfRangeException(nameof(smooth2), "Smooth2 must be greater than 0");
ArgumentOutOfRangeException.ThrowIfLessThan(period, 1);
ArgumentOutOfRangeException.ThrowIfLessThan(smooth1, 1);
ArgumentOutOfRangeException.ThrowIfLessThan(smooth2, 1);
_highs = new(period);
_lows = new(period);
+4 -18
View File
@@ -40,7 +40,6 @@ public sealed class Srsi : AbstractBase
private readonly CircularBuffer _srsiValues;
private readonly Sma _signal;
private readonly int _rsiPeriod;
private readonly int _stochPeriod;
private const int DefaultRsiPeriod = 14;
private const int DefaultStochPeriod = 14;
private const int DefaultSmoothK = 3;
@@ -56,25 +55,12 @@ public sealed class Srsi : AbstractBase
public Srsi(int rsiPeriod = DefaultRsiPeriod, int stochPeriod = DefaultStochPeriod,
int smoothK = DefaultSmoothK, int smoothD = DefaultSmoothD)
{
if (rsiPeriod < 1)
{
throw new ArgumentOutOfRangeException(nameof(rsiPeriod), "Period must be greater than 0");
}
if (stochPeriod < 1)
{
throw new ArgumentOutOfRangeException(nameof(stochPeriod), "Period must be greater than 0");
}
if (smoothK < 1)
{
throw new ArgumentOutOfRangeException(nameof(smoothK), "Period must be greater than 0");
}
if (smoothD < 1)
{
throw new ArgumentOutOfRangeException(nameof(smoothD), "Period must be greater than 0");
}
ArgumentOutOfRangeException.ThrowIfLessThan(rsiPeriod, 1);
ArgumentOutOfRangeException.ThrowIfLessThan(stochPeriod, 1);
ArgumentOutOfRangeException.ThrowIfLessThan(smoothK, 1);
ArgumentOutOfRangeException.ThrowIfLessThan(smoothD, 1);
_rsiPeriod = rsiPeriod;
_stochPeriod = stochPeriod;
_rsi = new(rsiPeriod);
_rsiValues = new(stochPeriod);
_srsiValues = new(smoothK);
+6 -21
View File
@@ -62,32 +62,17 @@ public sealed class Stc : AbstractBase
int slowPeriod = DefaultSlowPeriod, int d1Period = DefaultD1Period,
int stcPeriod = DefaultStcPeriod)
{
string err = "All periods must be greater than 0";
ArgumentOutOfRangeException.ThrowIfLessThan(cyclePeriod, 1);
ArgumentOutOfRangeException.ThrowIfLessThan(fastPeriod, 1);
ArgumentOutOfRangeException.ThrowIfLessThan(slowPeriod, 1);
ArgumentOutOfRangeException.ThrowIfLessThan(d1Period, 1);
ArgumentOutOfRangeException.ThrowIfLessThan(stcPeriod, 1);
if (cyclePeriod < 1)
{
throw new ArgumentOutOfRangeException(nameof(cyclePeriod), err);
}
if (fastPeriod < 1)
{
throw new ArgumentOutOfRangeException(nameof(fastPeriod), err);
}
if (slowPeriod < 1)
{
throw new ArgumentOutOfRangeException(nameof(slowPeriod), err);
}
if (d1Period < 1)
{
throw new ArgumentOutOfRangeException(nameof(d1Period), err);
}
if (stcPeriod < 1)
{
throw new ArgumentOutOfRangeException(nameof(stcPeriod), err);
}
if (fastPeriod >= slowPeriod)
{
throw new ArgumentOutOfRangeException(nameof(fastPeriod), "Fast period must be less than slow period");
}
_fastEma = new(fastPeriod);
_slowEma = new(slowPeriod);
_macdValues = new(cyclePeriod);
+3 -6
View File
@@ -52,12 +52,9 @@ public sealed class Stoch : AbstractBase
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Stoch(int period = DefaultPeriod, int smoothK = DefaultSmoothK, int smoothD = DefaultSmoothD)
{
if (period < 1)
throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than 0");
if (smoothK < 1)
throw new ArgumentOutOfRangeException(nameof(smoothK), "%K smoothing period must be greater than 0");
if (smoothD < 1)
throw new ArgumentOutOfRangeException(nameof(smoothD), "%D smoothing period must be greater than 0");
ArgumentOutOfRangeException.ThrowIfLessThan(period, 1);
ArgumentOutOfRangeException.ThrowIfLessThan(smoothK, 1);
ArgumentOutOfRangeException.ThrowIfLessThan(smoothD, 1);
_highs = new(period);
_lows = new(period);
+6 -24
View File
@@ -67,30 +67,12 @@ public sealed class Uo : AbstractBase
public Uo(int period1 = DefaultPeriod1, int period2 = DefaultPeriod2, int period3 = DefaultPeriod3,
double weight1 = DefaultWeight1, double weight2 = DefaultWeight2, double weight3 = DefaultWeight3)
{
if (period1 < 1)
{
throw new ArgumentOutOfRangeException(nameof(period1), "Period1 must be greater than 0");
}
if (period2 < 1)
{
throw new ArgumentOutOfRangeException(nameof(period2), "Period2 must be greater than 0");
}
if (period3 < 1)
{
throw new ArgumentOutOfRangeException(nameof(period3), "Period3 must be greater than 0");
}
if (weight1 <= 0)
{
throw new ArgumentOutOfRangeException(nameof(weight1), "Weight1 must be greater than 0");
}
if (weight2 <= 0)
{
throw new ArgumentOutOfRangeException(nameof(weight2), "Weight2 must be greater than 0");
}
if (weight3 <= 0)
{
throw new ArgumentOutOfRangeException(nameof(weight3), "Weight3 must be greater than 0");
}
ArgumentOutOfRangeException.ThrowIfLessThan(period1, 1);
ArgumentOutOfRangeException.ThrowIfLessThan(period2, 1);
ArgumentOutOfRangeException.ThrowIfLessThan(period3, 1);
ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(weight1, 0);
ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(weight2, 0);
ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(weight3, 0);
_weight1 = weight1;
_weight2 = weight2;
+159
View File
@@ -0,0 +1,159 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// BETA: Beta Coefficient
/// A statistical measure that quantifies the volatility of an asset or portfolio
/// in relation to the overall market. Beta is used to assess the risk and return
/// characteristics of an investment.
/// </summary>
/// <remarks>
/// The Beta calculation process:
/// 1. Calculates covariance between asset and market returns
/// 2. Computes variance of market returns
/// 3. Divides covariance by market variance
///
/// Key characteristics:
/// - Measures relative volatility
/// - Beta > 1: More volatile than market
/// - Beta < 1: Less volatile than market
/// - Beta = 1: Same volatility as market
/// - Beta < 0: Inverse relationship with market
///
/// Formula:
/// β = Cov(Ra, Rm) / Var(Rm)
/// where:
/// Ra = asset returns
/// Rm = market returns
///
/// Market Applications:
/// - Risk assessment
/// - Portfolio management
/// - Asset allocation
/// - Performance analysis
/// - Hedging strategies
///
/// Sources:
/// https://en.wikipedia.org/wiki/Beta_(finance)
/// "Modern Portfolio Theory" - Harry Markowitz
///
/// Note: Assumes linear relationship between asset and market returns
/// </remarks>
[SkipLocalsInit]
public sealed class Beta : AbstractBase
{
private readonly int Period;
private readonly CircularBuffer _assetReturns;
private readonly CircularBuffer _marketReturns;
private const double Epsilon = 1e-10;
private const int MinimumPoints = 2;
/// <param name="period">The number of points to consider for beta calculation.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 2.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Beta(int period)
{
if (period < MinimumPoints)
{
throw new ArgumentOutOfRangeException(nameof(period),
"Period must be greater than or equal to 2 for beta calculation.");
}
Period = period;
WarmupPeriod = MinimumPoints;
_assetReturns = new CircularBuffer(period);
_marketReturns = new CircularBuffer(period);
Name = $"Beta(period={period})";
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of points to consider for beta calculation.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Beta(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_assetReturns.Clear();
_marketReturns.Clear();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Input.Value;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static double CalculateMean(ReadOnlySpan<double> values)
{
double sum = 0;
for (int i = 0; i < values.Length; i++)
{
sum += values[i];
}
return sum / values.Length;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static double CalculateCovariance(ReadOnlySpan<double> assetReturns, ReadOnlySpan<double> marketReturns, double assetMean, double marketMean)
{
double covariance = 0;
for (int i = 0; i < assetReturns.Length; i++)
{
covariance += (assetReturns[i] - assetMean) * (marketReturns[i] - marketMean);
}
return covariance / assetReturns.Length;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static double CalculateVariance(ReadOnlySpan<double> values, double mean)
{
double variance = 0;
for (int i = 0; i < values.Length; i++)
{
double diff = values[i] - mean;
variance += diff * diff;
}
return variance / values.Length;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(Input.IsNew);
_assetReturns.Add(Input.Value, Input.IsNew);
_marketReturns.Add(Input2.Value, Input.IsNew);
double beta = 0;
if (_assetReturns.Count >= MinimumPoints && _marketReturns.Count >= MinimumPoints)
{
ReadOnlySpan<double> assetValues = _assetReturns.GetSpan();
ReadOnlySpan<double> marketValues = _marketReturns.GetSpan();
double assetMean = CalculateMean(assetValues);
double marketMean = CalculateMean(marketValues);
double covariance = CalculateCovariance(assetValues, marketValues, assetMean, marketMean);
double marketVariance = CalculateVariance(marketValues, marketMean);
if (marketVariance > Epsilon)
{
beta = covariance / marketVariance;
}
}
IsHot = _assetReturns.Count >= Period && _marketReturns.Count >= Period;
return beta;
}
}
+163
View File
@@ -0,0 +1,163 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// CORR: Correlation Coefficient
/// A statistical measure that quantifies the strength and direction of the relationship
/// between two variables. The correlation coefficient ranges from -1 to 1, where 1 indicates
/// a perfect positive correlation, -1 indicates a perfect negative correlation, and 0 indicates
/// no correlation.
/// </summary>
/// <remarks>
/// The Correlation calculation process:
/// 1. Calculates mean of both variables
/// 2. Computes covariance between variables
/// 3. Calculates standard deviation of both variables
/// 4. Divides covariance by product of standard deviations
///
/// Key characteristics:
/// - Measures linear relationship strength
/// - Symmetric around zero
/// - Scale-independent measure
/// - Sensitive to outliers
/// - Useful for portfolio diversification
///
/// Formula:
/// ρ = Cov(X, Y) / (σX * σY)
/// where:
/// X, Y = variables
/// Cov = covariance
/// σ = standard deviation
///
/// Market Applications:
/// - Portfolio diversification
/// - Risk management
/// - Pairs trading
/// - Performance analysis
/// - Market sentiment analysis
///
/// Sources:
/// https://en.wikipedia.org/wiki/Correlation_coefficient
/// "Modern Portfolio Theory" - Harry Markowitz
///
/// Note: Assumes linear relationship between variables
/// </remarks>
[SkipLocalsInit]
public sealed class Corr : AbstractBase
{
private readonly int Period;
private readonly CircularBuffer _xValues;
private readonly CircularBuffer _yValues;
private const double Epsilon = 1e-10;
private const int MinimumPoints = 2;
/// <param name="period">The number of points to consider for correlation calculation.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 2.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Corr(int period)
{
if (period < MinimumPoints)
{
throw new ArgumentOutOfRangeException(nameof(period),
"Period must be greater than or equal to 2 for correlation calculation.");
}
Period = period;
WarmupPeriod = MinimumPoints;
_xValues = new CircularBuffer(period);
_yValues = new CircularBuffer(period);
Name = $"Corr(period={period})";
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of points to consider for correlation calculation.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Corr(object source, int period) : this(period)
{
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 static double CalculateMean(ReadOnlySpan<double> values)
{
double sum = 0;
for (int i = 0; i < values.Length; i++)
{
sum += values[i];
}
return sum / values.Length;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static double CalculateCovariance(ReadOnlySpan<double> xValues, ReadOnlySpan<double> yValues, double xMean, double yMean)
{
double covariance = 0;
for (int i = 0; i < xValues.Length; i++)
{
covariance += (xValues[i] - xMean) * (yValues[i] - yMean);
}
return covariance / xValues.Length;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static double CalculateStandardDeviation(ReadOnlySpan<double> values, double mean)
{
double sumSquaredDeviations = 0;
for (int i = 0; i < values.Length; i++)
{
double deviation = values[i] - mean;
sumSquaredDeviations += deviation * deviation;
}
return Math.Sqrt(sumSquaredDeviations / values.Length);
}
[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 correlation = 0;
if (_xValues.Count >= MinimumPoints && _yValues.Count >= MinimumPoints)
{
ReadOnlySpan<double> xValues = _xValues.GetSpan();
ReadOnlySpan<double> yValues = _yValues.GetSpan();
double xMean = CalculateMean(xValues);
double yMean = CalculateMean(yValues);
double covariance = CalculateCovariance(xValues, yValues, xMean, yMean);
double xStdDev = CalculateStandardDeviation(xValues, xMean);
double yStdDev = CalculateStandardDeviation(yValues, yMean);
if (xStdDev > Epsilon && yStdDev > Epsilon)
{
correlation = covariance / (xStdDev * yStdDev);
}
}
IsHot = _xValues.Count >= Period && _yValues.Count >= Period;
return correlation;
}
}
+4 -11
View File
@@ -45,7 +45,6 @@ public sealed class Percentile : AbstractBase
private readonly int Period;
private readonly double Percent;
private readonly CircularBuffer _buffer;
private const double Epsilon = 1e-10;
private const int MinimumPoints = 2;
/// <param name="period">The number of points to consider for percentile calculation.</param>
@@ -56,16 +55,10 @@ public sealed class Percentile : AbstractBase
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Percentile(int period, double percent)
{
if (period < MinimumPoints)
{
throw new ArgumentOutOfRangeException(nameof(period),
"Period must be greater than or equal to 2 for percentile calculation.");
}
if (percent < 0 || percent > 100)
{
throw new ArgumentOutOfRangeException(nameof(percent),
"Percent must be between 0 and 100.");
}
ArgumentOutOfRangeException.ThrowIfLessThan(period, MinimumPoints);
ArgumentOutOfRangeException.ThrowIfLessThan(percent, 0);
ArgumentOutOfRangeException.ThrowIfGreaterThan(percent, 100);
Period = period;
Percent = percent;
WarmupPeriod = MinimumPoints; // Minimum number of points needed for percentile calculation
+167
View File
@@ -0,0 +1,167 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// THEIL: Theil's U Statistics (U1, U2)
/// A statistical measure that quantifies the accuracy of forecasts compared to actual values
/// and naive forecasts.
/// </summary>
/// <remarks>
/// The Theil's U calculation process:
/// 1. Calculate U1 statistic (relative accuracy)
/// 2. Calculate U2 statistic (comparison with naive forecast)
///
/// Key characteristics:
/// - U1 ranges from 0 to 1, with 0 indicating perfect forecast
/// - U2 &lt; 1: forecast better than naive forecast
/// - U2 = 1: forecast equal to naive forecast
/// - U2 &gt; 1: forecast worse than naive forecast
///
/// Formula:
/// U1 = √[Σ(Ft - At)² / Σ(At)²]
/// U2 = √[Σ(Ft - At)² / Σ(At - At-1)²]
/// where:
/// Ft = forecasted value
/// At = actual value
/// At-1 = previous actual value
///
/// Market Applications:
/// - Evaluating forecast accuracy
/// - Comparing forecasting models
/// - Assessing forecasting methods
/// - Model selection
/// - Performance analysis
///
/// Sources:
/// https://en.wikipedia.org/wiki/Theil%27s_U
/// "Forecasting: Principles and Practice" - Rob J Hyndman
///
/// Note: Should be used alongside other accuracy measures
/// </remarks>
[SkipLocalsInit]
public sealed class Theil : AbstractBase
{
private readonly int Period;
private readonly CircularBuffer _actual;
private readonly CircularBuffer _forecast;
private const int MinimumPoints = 2;
/// <summary>
/// Gets the U2 statistic comparing forecast with naive forecast
/// </summary>
public double U2 { get; private set; }
/// <param name="period">The number of points to consider for Theil's U calculation.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 2.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Theil(int period)
{
if (period < MinimumPoints)
{
throw new ArgumentOutOfRangeException(nameof(period),
"Period must be greater than or equal to 2 for Theil's U calculation.");
}
Period = period;
WarmupPeriod = MinimumPoints;
_actual = new CircularBuffer(period);
_forecast = new CircularBuffer(period);
Name = $"Theil(period={period})";
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of points to consider for Theil's U calculation.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Theil(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_actual.Clear();
_forecast.Clear();
U2 = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Input.Value;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static double CalculateSquaredSum(ReadOnlySpan<double> values)
{
double sum = 0;
for (int i = 0; i < values.Length; i++)
{
sum += values[i] * values[i];
}
return sum;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static double CalculateSquaredErrorSum(ReadOnlySpan<double> forecast, ReadOnlySpan<double> actual)
{
double sum = 0;
for (int i = 0; i < forecast.Length; i++)
{
double error = forecast[i] - actual[i];
sum += error * error;
}
return sum;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static double CalculateNaiveSquaredErrorSum(ReadOnlySpan<double> actual)
{
double sum = 0;
for (int i = 1; i < actual.Length; i++)
{
double error = actual[i] - actual[i - 1];
sum += error * error;
}
return sum;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(Input.IsNew);
_actual.Add(Input.Value, Input.IsNew);
_forecast.Add(Input2.Value, Input.IsNew);
double u1 = 0;
if (_actual.Count >= MinimumPoints && _forecast.Count >= MinimumPoints)
{
ReadOnlySpan<double> actualValues = _actual.GetSpan();
ReadOnlySpan<double> forecastValues = _forecast.GetSpan();
double squaredErrorSum = CalculateSquaredErrorSum(forecastValues, actualValues);
double squaredActualSum = CalculateSquaredSum(actualValues);
double naiveSquaredErrorSum = CalculateNaiveSquaredErrorSum(actualValues);
if (squaredActualSum > double.Epsilon)
{
u1 = Math.Sqrt(squaredErrorSum / squaredActualSum);
}
if (naiveSquaredErrorSum > double.Epsilon)
{
U2 = Math.Sqrt(squaredErrorSum / naiveSquaredErrorSum);
}
}
IsHot = _actual.Count >= Period && _forecast.Count >= Period;
return u1;
}
}
+185
View File
@@ -0,0 +1,185 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// TSF: Time Series Forecast
/// A statistical indicator that provides a linear regression forecast of future values
/// based on historical data. It includes both the forecast value and a confidence interval.
/// </summary>
/// <remarks>
/// The Time Series Forecast calculation process:
/// 1. Calculates linear regression on the input data
/// 2. Extrapolates the regression line to forecast future values
/// 3. Computes confidence intervals based on the standard error of the forecast
///
/// Key characteristics:
/// - Provides point forecast and confidence interval
/// - Based on linear regression principles
/// - Assumes trend continuity
/// - Sensitive to recent data changes
/// - Useful for short-term predictions
///
/// Formula:
/// Forecast = a + b * (n + 1)
/// where:
/// a = y-intercept
/// b = slope
/// n = number of periods
///
/// Confidence Interval = Forecast ± (t * SE)
/// where:
/// t = t-value for desired confidence level
/// SE = Standard Error of the forecast
///
/// Market Applications:
/// - Price target estimation
/// - Trend analysis
/// - Risk assessment
/// - Trading strategy development
/// - Market behavior prediction
///
/// Sources:
/// https://en.wikipedia.org/wiki/Time_series
/// "Forecasting: Principles and Practice" - Rob J Hyndman and George Athanasopoulos
///
/// Note: Assumes linear trend in the data and may not capture non-linear patterns
/// </remarks>
[SkipLocalsInit]
public sealed class Tsf : AbstractBase
{
private readonly int Period;
private readonly CircularBuffer _values;
private const int MinimumPoints = 2;
/// <summary>
/// The forecasted value for the next period.
/// </summary>
public double Forecast { get; private set; }
/// <summary>
/// The lower bound of the confidence interval.
/// </summary>
public double LowerBound { get; private set; }
/// <summary>
/// The upper bound of the confidence interval.
/// </summary>
public double UpperBound { get; private set; }
/// <param name="period">The number of historical data points to consider for forecasting.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 2.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Tsf(int period)
{
if (period < MinimumPoints)
{
throw new ArgumentOutOfRangeException(nameof(period),
"Period must be greater than or equal to 2 for time series forecasting.");
}
Period = period;
WarmupPeriod = MinimumPoints;
_values = new CircularBuffer(period);
Name = $"TSF(period={period})";
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of historical data points to consider for forecasting.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Tsf(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_values.Clear();
Forecast = 0;
LowerBound = 0;
UpperBound = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Input.Value;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static (double slope, double intercept) CalculateLinearRegression(ReadOnlySpan<double> values)
{
int n = values.Length;
double sumX = 0, sumY = 0, sumXY = 0, sumX2 = 0;
for (int i = 0; i < n; i++)
{
double x = i + 1;
double y = values[i];
sumX += x;
sumY += y;
sumXY += x * y;
sumX2 += x * x;
}
double slope = (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX);
double intercept = (sumY - slope * sumX) / n;
return (slope, intercept);
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static double CalculateStandardError(ReadOnlySpan<double> values, double slope, double intercept)
{
int n = values.Length;
double sumSquaredResiduals = 0;
for (int i = 0; i < n; i++)
{
double x = i + 1;
double y = values[i];
double predicted = slope * x + intercept;
double residual = y - predicted;
sumSquaredResiduals += residual * residual;
}
return Math.Sqrt(sumSquaredResiduals / (n - 2));
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(Input.IsNew);
_values.Add(Input.Value, Input.IsNew);
if (_values.Count >= MinimumPoints)
{
ReadOnlySpan<double> values = _values.GetSpan();
var (slope, intercept) = CalculateLinearRegression(values);
// Calculate forecast for the next period
Forecast = slope * (Period + 1) + intercept;
// Calculate standard error
double standardError = CalculateStandardError(values, slope, intercept);
// Calculate confidence interval (using t-distribution with n-2 degrees of freedom)
double tValue = 1.96; // Approximation for 95% confidence interval
double marginOfError = tValue * standardError * Math.Sqrt(1 + 1.0 / Period);
LowerBound = Forecast - marginOfError;
UpperBound = Forecast + marginOfError;
}
IsHot = _values.Count >= Period;
return Forecast;
}
}
+31 -21
View File
@@ -1,22 +1,32 @@
# Statistics indicators
Done: 13, Todo: 6
# Statistics
*BETA - Beta coefficient (Beta, R-squared)
*CORR - Correlation Coefficient (Correlation, P-value)
✔️ CURVATURE - Rate of Change in Direction or Slope
✔️ ENTROPY - Measure of Uncertainty or Disorder
✔️ HURST - Hurst Exponent
✔️ KURTOSIS - Measure of Tails/Peakedness
✔️ MAX - Maximum with exponential decay
✔️ MEDIAN - Middle value
✔️ MIN - Minimum with exponential decay
✔️ MODE - Most Frequent Value
✔️ PERCENTILE - Rank Order
*RSQUARED - Coefficient of Determination (R-squared, Adjusted R-squared)
✔️ SKEW - Skewness, asymmetry of distribution
✔️ SLOPE - Rate of Change, Linear Regression
✔️ STDDEV - Standard Deviation, Measure of Spread
*THEIL - Theil's U Statistics (U1, U2)
*TSF - Time Series Forecast (Forecast, Confidence Interval)
✔️ VARIANCE - Average of Squared Deviations
✔️ ZSCORE - Standardized Score
Statistical functions and indicators for financial analysis.
## Implemented
- [Beta](Beta.cs) - Beta coefficient measuring volatility relative to market
- [Corr](Corr.cs) - Correlation coefficient between two series
- [Curvature](Curvature.cs) - Curvature of a time series
- [Entropy](Entropy.cs) - Information entropy of a series
- [Hurst](Hurst.cs) - Hurst exponent for trend strength
- [Kurtosis](Kurtosis.cs) - Kurtosis measuring tail extremity
- [Max](Max.cs) - Maximum value over period
- [Median](Median.cs) - Median value over period
- [Min](Min.cs) - Minimum value over period
- [Mode](Mode.cs) - Mode (most frequent value)
- [Percentile](Percentile.cs) - Percentile rank calculation
- [Skew](Skew.cs) - Skewness measuring distribution asymmetry
- [Slope](Slope.cs) - Linear regression slope
- [Stddev](Stddev.cs) - Standard deviation
- [Theil](Theil.cs) - Theil's U statistics for forecast accuracy
- [Tsf](Tsf.cs) - Time series forecast
- [Variance](Variance.cs) - Statistical variance
- [Zscore](Zscore.cs) - Z-score standardization
## Planned
- Cointegration - Test for cointegrated series
- Granger - Granger causality test
- Jarque-Bera - Normality test
- Kendall - Kendall rank correlation
- Spearman - Spearman rank correlation