diff --git a/.github/workflows/Publish.yml b/.github/workflows/Publish.yml
index 44d7bdaf..9fb5e4f9 100644
--- a/.github/workflows/Publish.yml
+++ b/.github/workflows/Publish.yml
@@ -1,7 +1,3 @@
-# This workflow integrates SonarCloud analysis, coverage reporting,
-# CodeQL analysis, SecurityCodeScan, and Codacy Security Scan
-# for code scanning and vulnerability detection - and if they all pass, publish
-
name: Publish Workflow
on:
@@ -82,7 +78,10 @@ jobs:
-Dsonar.sources=.
-Dsonar.cs.opencover.reportsPaths=**/*cover*.xml
-Dsonar.cs.dotcover.reportsPaths=**/dotcover.xml
- -Dsonar.coverage.exclusions=**Tests.cs
+ -Dsonar.coverage.exclusions=**Tests.cs,**/*.md,**/*.html,**/*.css,**/docs/**/*,**/archive/**/*,**/notebooks/**/*
+ -Dsonar.exclusions=**/TestResults/**/*,**/bin/**/*,**/obj/**/*,**/*.html,**/coverage/**/*,**/CoverageReport/**/*,**/*.md,**/*.css,**/docs/**/*,**/archive/**/*,**/notebooks/**/*
+ -Dsonar.cpd.exclusions=**Tests.cs
+ -Dsonar.test.exclusions=**Tests.cs
CodeQL:
runs-on: ubuntu-latest
diff --git a/.sonarlint/QuanTAlib.json b/.sonarlint/QuanTAlib.json
index f75a1bc1..c44cece3 100644
--- a/.sonarlint/QuanTAlib.json
+++ b/.sonarlint/QuanTAlib.json
@@ -1,4 +1,4 @@
{
- "sonarCloudOrganization": "mihakralj-quantalib",
+ "sonarCloudOrganization": "mihakralj",
"projectKey": "mihakralj_QuanTAlib"
}
\ No newline at end of file
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 00000000..dcc54733
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,33 @@
+{
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "name": "Debug Tests",
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "build",
+ "program": "dotnet",
+ "args": [
+ "test",
+ "${workspaceFolder}/QuanTAlib.sln",
+ "--no-build"
+ ],
+ "cwd": "${workspaceFolder}",
+ "stopAtEntry": false,
+ "console": "internalConsole",
+ "logging": {
+ "moduleLoad": false
+ }
+ },
+ {
+ "name": "Debug Library",
+ "type": "coreclr",
+ "request": "attach",
+ "processId": "${command:pickProcess}",
+ "justMyCode": true,
+ "logging": {
+ "moduleLoad": false
+ }
+ }
+ ]
+}
diff --git a/.vscode/settings.json b/.vscode/settings.json
index dbbc4e17..335e6e7c 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -7,9 +7,9 @@
}
],
"sarif-viewer.connectToGithubCodeScanning": "on",
- "dotnet.dotnetPath": "C:/Program Files/dotnet",
+ "dotnet.dotnetPath": "/opt/homebrew/bin/",
"omnisharp.useModernNet": true,
- "omnisharp.sdkPath": "C:/Program Files/dotnet/sdk",
+ "omnisharp.sdkPath": "/opt/homebrew/bin/dotnet",
"sonarlint.connectedMode.project": {
"connectionId": "mihakralj",
"projectKey": "mihakralj_QuanTAlib"
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
new file mode 100644
index 00000000..33d40edd
--- /dev/null
+++ b/.vscode/tasks.json
@@ -0,0 +1,62 @@
+{
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "label": "build",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "build",
+ "${workspaceFolder}/QuanTAlib.sln",
+ "/property:GenerateFullPaths=true",
+ "/consoleloggerparameters:NoSummary"
+ ],
+ "problemMatcher": "$msCompile",
+ "group": {
+ "kind": "build",
+ "isDefault": true
+ }
+ },
+ {
+ "label": "test",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "test",
+ "${workspaceFolder}/QuanTAlib.sln",
+ "--no-build",
+ "--verbosity:normal"
+ ],
+ "problemMatcher": "$msCompile",
+ "group": {
+ "kind": "test",
+ "isDefault": true
+ },
+ "dependsOn": ["build"]
+ },
+ {
+ "label": "test with coverage",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "test",
+ "${workspaceFolder}/QuanTAlib.sln",
+ "/p:CollectCoverage=true",
+ "/p:CoverletOutputFormat=lcov",
+ "/p:CoverletOutput=./lcov.info",
+ "--no-build"
+ ],
+ "problemMatcher": "$msCompile"
+ },
+ {
+ "label": "clean",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "clean",
+ "${workspaceFolder}/QuanTAlib.sln"
+ ],
+ "problemMatcher": "$msCompile"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Tests/Tests.csproj b/Tests/Tests.csproj
index 18f54f05..11c1e048 100644
--- a/Tests/Tests.csproj
+++ b/Tests/Tests.csproj
@@ -4,6 +4,10 @@
QuanTAlib.Tests
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
diff --git a/lib/averages/Afirma.cs b/lib/averages/Afirma.cs
index 3832ad55..e80f2caf 100644
--- a/lib/averages/Afirma.cs
+++ b/lib/averages/Afirma.cs
@@ -1,8 +1,18 @@
+using System;
namespace QuanTAlib;
+///
+/// AFIRMA: Adaptive FIR Moving Average
+/// A finite impulse response (FIR) filter that combines windowing functions with sinc-based filtering.
+/// Provides superior noise reduction while maintaining signal fidelity through adaptive filtering.
+///
+///
+/// Implementation:
+/// Original implementation based on FIR filter design principles
+///
+
public class Afirma : AbstractBase
{
-
public enum WindowType
{
Rectangular,
@@ -22,6 +32,10 @@ public class Afirma : AbstractBase
private readonly int _n;
private readonly double _sx2, _sx3, _sx4, _sx5, _sx6, _den;
+ /// The number of periods for the sinc filter calculation.
+ /// The number of filter taps (filter length). Must be odd number.
+ /// The type of window function to apply (Rectangular, Hanning1, Hanning2, Blackman, or BlackmanHarris).
+ /// Thrown when periods or taps is less than 1.
public Afirma(int periods, int taps, WindowType window)
{
if (periods < 1)
@@ -54,6 +68,10 @@ public class Afirma : AbstractBase
Init();
}
+ /// The data source object that publishes updates.
+ /// The number of periods for the sinc filter calculation.
+ /// The number of filter taps (filter length). Must be odd number.
+ /// The type of window function to apply (Rectangular, Hanning1, Hanning2, Blackman, or BlackmanHarris).
public Afirma(object source, int periods, int taps, WindowType window) : this(periods, taps, window)
{
var pubEvent = source.GetType().GetEvent("Pub");
@@ -147,5 +165,4 @@ public class Afirma : AbstractBase
}
return wsum;
}
-
}
diff --git a/lib/averages/Convolution.cs b/lib/averages/Convolution.cs
index 60226d61..4a7a75fc 100644
--- a/lib/averages/Convolution.cs
+++ b/lib/averages/Convolution.cs
@@ -1,5 +1,16 @@
+using System;
namespace QuanTAlib;
+///
+/// Convolution: A fundamental signal processing operation that combines two signals to form a third signal
+/// Applies a custom kernel (weight array) to the input data through convolution, allowing for flexible
+/// filtering operations. The kernel is automatically normalized to ensure consistent output scaling.
+///
+///
+/// Implementation:
+/// Based on standard discrete convolution principles from signal processing
+///
+
public class Convolution : AbstractBase
{
private readonly double[] _kernel;
@@ -7,6 +18,8 @@ public class Convolution : AbstractBase
private readonly CircularBuffer _buffer;
private readonly double[] _normalizedKernel;
+ /// Array of weights defining the convolution operation. The length of this array determines the filter's window size.
+ /// Thrown when kernel is null or empty.
public Convolution(double[] kernel)
{
if (kernel == null || kernel.Length == 0)
@@ -20,6 +33,8 @@ public class Convolution : AbstractBase
Init();
}
+ /// The data source object that publishes updates.
+ /// Array of weights defining the convolution operation.
public Convolution(object source, double[] kernel) : this(kernel)
{
var pubEvent = source.GetType().GetEvent("Pub");
@@ -100,4 +115,4 @@ public class Convolution : AbstractBase
return sum;
}
-}
\ No newline at end of file
+}
diff --git a/lib/averages/Epma.cs b/lib/averages/Epma.cs
index 4d55e203..f9985e9b 100644
--- a/lib/averages/Epma.cs
+++ b/lib/averages/Epma.cs
@@ -1,10 +1,35 @@
+using System;
namespace QuanTAlib;
+///
+/// EPMA: Endpoint Moving Average
+/// A moving average that uses a specialized convolution kernel to emphasize recent price movements
+/// while maintaining a connection to historical data. The weights decrease linearly with a focus
+/// on endpoints.
+///
+///
+/// The EPMA uses a unique weighting scheme where:
+/// - The most recent price gets the highest weight: (2 * period - 1)
+/// - Each previous price gets a weight reduced by 3: (2 * period - 1) - 3i
+/// - Weights are normalized to sum to 1
+///
+/// Key characteristics:
+/// - Emphasizes recent price movements more than traditional moving averages
+/// - Maintains some influence from historical data
+/// - Uses convolution for efficient calculation
+/// - Provides better endpoint preservation than simple moving averages
+///
+/// Implementation:
+/// Original implementation based on convolution principles
+///
+
public class Epma : AbstractBase
{
private readonly int _period;
private readonly Convolution _convolution;
+ /// The number of data points used in the EPMA calculation.
+ /// Thrown when period is less than 1.
public Epma(int period)
{
if (period < 1)
@@ -18,6 +43,8 @@ public class Epma : AbstractBase
Init();
}
+ /// The data source object that publishes updates.
+ /// The number of data points used in the EPMA calculation.
public Epma(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
@@ -60,6 +87,11 @@ public class Epma : AbstractBase
return result;
}
+ ///
+ /// Generates the convolution kernel for the EPMA calculation.
+ ///
+ /// The period for which to generate the kernel.
+ /// An array of normalized weights for the convolution operation.
public static double[] GenerateKernel(int period)
{
double[] kernel = new double[period];
@@ -79,4 +111,4 @@ public class Epma : AbstractBase
return kernel;
}
-}
\ No newline at end of file
+}
diff --git a/lib/averages/Frama.cs b/lib/averages/Frama.cs
index 223b1458..9e509b82 100644
--- a/lib/averages/Frama.cs
+++ b/lib/averages/Frama.cs
@@ -2,6 +2,29 @@ using System;
namespace QuanTAlib;
+///
+/// FRAMA: Fractal Adaptive Moving Average
+/// An adaptive moving average that adjusts its smoothing factor based on the fractal dimension
+/// of the price series. FRAMA automatically adapts to market conditions, becoming more responsive
+/// during trends and more stable during sideways markets.
+///
+///
+/// The FRAMA algorithm works by:
+/// 1. Calculating the fractal dimension of the price series
+/// 2. Using this dimension to determine the optimal alpha (smoothing factor)
+/// 3. Applying an EMA with the adaptive alpha
+///
+/// Key characteristics:
+/// - Self-adaptive to market conditions
+/// - Reduces lag during trending periods
+/// - Increases smoothing during sideways markets
+/// - Uses fractal geometry principles for market analysis
+///
+/// Sources:
+/// John Ehlers - "FRAMA: A Trend-Following Indicator"
+/// https://www.mesasoftware.com/papers/FRAMA.pdf
+///
+
public class Frama : AbstractBase
{
private readonly int _period;
@@ -9,6 +32,8 @@ public class Frama : AbstractBase
private double _lastFrama;
private double _prevLastFrama;
+ /// The number of periods used for fractal dimension calculation. Must be at least 2.
+ /// Thrown when period is less than 2.
public Frama(int period)
{
if (period < 2)
@@ -19,6 +44,8 @@ public class Frama : AbstractBase
WarmupPeriod = period;
}
+ /// The data source object that publishes updates.
+ /// The number of periods used for fractal dimension calculation.
public Frama(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
@@ -99,4 +126,4 @@ public class Frama : AbstractBase
{
return _lastFrama;
}
-}
\ No newline at end of file
+}
diff --git a/lib/averages/Fwma.cs b/lib/averages/Fwma.cs
index f5644d6a..98ee1bb1 100644
--- a/lib/averages/Fwma.cs
+++ b/lib/averages/Fwma.cs
@@ -1,9 +1,35 @@
+using System;
namespace QuanTAlib;
+///
+/// FWMA: Fibonacci Weighted Moving Average
+/// A moving average that uses Fibonacci numbers as weights in its calculation. The weights
+/// are arranged in reverse order so that recent prices receive higher weights corresponding
+/// to larger Fibonacci numbers.
+///
+///
+/// The FWMA calculation process:
+/// 1. Generates a Fibonacci sequence up to the specified period
+/// 2. Reverses the sequence to give higher weights to recent prices
+/// 3. Normalizes the weights to sum to 1
+/// 4. Applies the weights through convolution
+///
+/// Key characteristics:
+/// - Uses Fibonacci sequence for weight distribution
+/// - Recent prices receive higher weights
+/// - Natural progression of weights based on the golden ratio
+/// - Implemented using efficient convolution operations
+///
+/// Implementation:
+/// Original implementation based on Fibonacci sequence principles
+///
+
public class Fwma : AbstractBase
{
private readonly Convolution _convolution;
+ /// The number of data points used in the FWMA calculation.
+ /// Thrown when period is less than 1.
public Fwma(int period)
{
if (period < 1)
@@ -16,12 +42,19 @@ public class Fwma : AbstractBase
Init();
}
+ /// The data source object that publishes updates.
+ /// The number of data points used in the FWMA calculation.
public Fwma(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
+ ///
+ /// Generates the Fibonacci-based convolution kernel for the FWMA calculation.
+ ///
+ /// The period for which to generate the kernel.
+ /// An array of normalized Fibonacci-based weights for the convolution operation.
public static double[] GenerateKernel(int period)
{
double[] kernel = new double[period];
@@ -78,4 +111,4 @@ public class Fwma : AbstractBase
return result;
}
-}
\ No newline at end of file
+}
diff --git a/lib/averages/Gma.cs b/lib/averages/Gma.cs
index 4f003f5c..74fafc08 100644
--- a/lib/averages/Gma.cs
+++ b/lib/averages/Gma.cs
@@ -1,9 +1,35 @@
+using System;
namespace QuanTAlib;
+///
+/// GMA: Gaussian Moving Average
+/// A moving average that uses weights based on the Gaussian (normal) distribution curve.
+/// This creates a smooth, bell-shaped weighting scheme that gives maximum weight to the
+/// center of the period and gradually decreasing weights towards the edges.
+///
+///
+/// The GMA calculation process:
+/// 1. Creates a Gaussian distribution of weights centered on the period
+/// 2. Normalizes the weights to sum to 1
+/// 3. Applies the weights through convolution
+///
+/// Key characteristics:
+/// - Smooth, symmetric weight distribution
+/// - Natural bell curve weighting
+/// - Reduces noise while preserving signal characteristics
+/// - Less sensitive to outliers than simple moving averages
+/// - Implemented using efficient convolution operations
+///
+/// Implementation:
+/// Based on Gaussian distribution principles from statistics
+///
+
public class Gma : AbstractBase
{
private readonly Convolution _convolution;
+ /// The number of data points used in the GMA calculation.
+ /// Thrown when period is less than 1.
public Gma(int period)
{
if (period < 1)
@@ -16,12 +42,20 @@ public class Gma : AbstractBase
Init();
}
+ /// The data source object that publishes updates.
+ /// The number of data points used in the GMA calculation.
public Gma(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
+ ///
+ /// Generates the Gaussian-based convolution kernel for the GMA calculation.
+ ///
+ /// The period for which to generate the kernel.
+ /// The standard deviation parameter controlling the spread of the Gaussian curve. Default is 1.0.
+ /// An array of normalized Gaussian-based weights for the convolution operation.
public static double[] GenerateKernel(int period, double sigma = 1.0)
{
double[] kernel = new double[period];
@@ -71,4 +105,4 @@ public class Gma : AbstractBase
return result;
}
-}
\ No newline at end of file
+}
diff --git a/lib/averages/Hma.cs b/lib/averages/Hma.cs
index 220b419d..5b78c796 100644
--- a/lib/averages/Hma.cs
+++ b/lib/averages/Hma.cs
@@ -1,9 +1,37 @@
+using System;
namespace QuanTAlib;
+///
+/// HMA: Hull Moving Average
+/// A moving average designed by Alan Hull to reduce lag while maintaining smoothness.
+/// It combines weighted moving averages of different periods to achieve better
+/// responsiveness to price changes while minimizing noise.
+///
+///
+/// The HMA calculation process:
+/// 1. Calculate WMA with period n/2
+/// 2. Calculate WMA with period n
+/// 3. Calculate difference: 2*WMA(n/2) - WMA(n)
+/// 4. Apply final WMA with period sqrt(n) to the difference
+///
+/// Key characteristics:
+/// - Significantly reduced lag compared to traditional moving averages
+/// - Maintains smoothness despite the reduced lag
+/// - Responds more quickly to price changes
+/// - Better at identifying trend changes
+/// - Uses weighted moving averages for all calculations
+///
+/// Sources:
+/// Alan Hull - "Better Trading with Hull Moving Average"
+/// https://alanhull.com/hull-moving-average
+///
+
public class Hma : AbstractBase
{
private readonly Convolution _wmaHalf, _wmaFull, _wmaFinal;
+ /// The number of data points used in the HMA calculation. Must be at least 2.
+ /// Thrown when period is less than 2.
public Hma(int period)
{
if (period < 2)
@@ -19,12 +47,19 @@ public class Hma : AbstractBase
Init();
}
+ /// The data source object that publishes updates.
+ /// The number of data points used in the HMA calculation.
public Hma(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
+ ///
+ /// Generates the weighted moving average kernel for the HMA calculation.
+ ///
+ /// The period for which to generate the kernel.
+ /// An array of linearly weighted values for the convolution operation.
private static double[] GenerateWmaKernel(int period)
{
double[] kernel = new double[period];
@@ -72,4 +107,4 @@ public class Hma : AbstractBase
IsHot = _index >= WarmupPeriod;
return result;
}
-}
\ No newline at end of file
+}
diff --git a/lib/averages/Htit.cs b/lib/averages/Htit.cs
index 487a89f5..0c911f88 100644
--- a/lib/averages/Htit.cs
+++ b/lib/averages/Htit.cs
@@ -1,8 +1,32 @@
-//not working yet
-//TODO fails consistency test
-
+using System;
namespace QuanTAlib;
+///
+/// HTIT: Hilbert Transform Instantaneous Trendline
+/// A sophisticated moving average that uses the Hilbert Transform to identify the dominant cycle
+/// period in price data and create a smooth trend line. It adapts to the market's natural cycles
+/// and provides a dynamic moving average.
+///
+///
+/// The HTIT calculation process:
+/// 1. Uses a Hilbert Transform to decompose price into in-phase and quadrature components
+/// 2. Employs a homodyne discriminator to determine the dominant cycle period
+/// 3. Applies smoothing based on the detected cycle period
+/// 4. Creates a trend line that automatically adapts to market cycles
+///
+/// Key characteristics:
+/// - Automatically adapts to market cycles
+/// - Reduces lag by using cycle analysis
+/// - Complex signal processing for better trend identification
+/// - Combines multiple digital signal processing techniques
+///
+/// Sources:
+/// John Ehlers - "Cycle Analytics for Traders"
+///
+/// Note: This implementation is currently under development and may not pass
+/// all consistency tests.
+///
+
public class Htit : AbstractBase
{
private readonly CircularBuffer _priceBuffer = new(7);
@@ -21,12 +45,19 @@ public class Htit : AbstractBase
private double _lastPd = 0;
private double _p_lastPd = 0;
+ ///
+ /// Initializes a new instance of the Htit class.
+ ///
public Htit()
{
Name = "Htit";
WarmupPeriod = 12;
}
+ ///
+ /// Initializes a new instance of the Htit class with a specified source.
+ ///
+ /// The data source object that publishes updates.
public Htit(object source) : this()
{
var pubEvent = source.GetType().GetEvent("Pub");
@@ -45,6 +76,7 @@ public class Htit : AbstractBase
_lastPd = _p_lastPd;
}
}
+
protected override double Calculation()
{
ManageState(Input.IsNew);
@@ -141,4 +173,4 @@ public class Htit : AbstractBase
return pr;
}
-}
\ No newline at end of file
+}
diff --git a/lib/averages/Hwma.cs b/lib/averages/Hwma.cs
index c2473349..20c67659 100644
--- a/lib/averages/Hwma.cs
+++ b/lib/averages/Hwma.cs
@@ -1,5 +1,33 @@
+using System;
namespace QuanTAlib;
+///
+/// HWMA: Holt-Winters Moving Average
+/// A triple exponential smoothing method that incorporates level (F), velocity (V), and
+/// acceleration (A) components to create a responsive yet smooth moving average. This
+/// implementation uses optimized smoothing factors for each component.
+///
+///
+/// The HWMA calculation process:
+/// 1. Updates the level (F) component using alpha smoothing
+/// 2. Updates the velocity (V) component using beta smoothing
+/// 3. Updates the acceleration (A) component using gamma smoothing
+/// 4. Combines all components for final value: F + V + 0.5A
+///
+/// Key characteristics:
+/// - Adapts to both trends and acceleration in price movement
+/// - Three separate smoothing factors for fine-tuned control
+/// - More responsive to changes than simple moving averages
+/// - Handles both linear and non-linear trends
+///
+/// Implementation:
+/// Based on Holt-Winters triple exponential smoothing principles
+/// with optimized default parameters:
+/// - Alpha (nA) = 2/(period + 1)
+/// - Beta (nB) = 1/period
+/// - Gamma (nC) = 1/period
+///
+
public class Hwma : AbstractBase
{
private readonly int _period;
@@ -7,14 +35,23 @@ public class Hwma : AbstractBase
private double _pF, _pV, _pA;
private double _ppF, _ppV, _ppA;
+ /// The number of data points used in the HWMA calculation.
public Hwma(int period) : this(period, 2.0 / (1 + period), 1.0 / period, 1.0 / period)
{
}
+ /// Alpha smoothing factor for the level component.
+ /// Beta smoothing factor for the velocity component.
+ /// Gamma smoothing factor for the acceleration component.
public Hwma(double nA, double nB, double nC) : this((int)((2 - nA) / nA), nA, nB, nC)
{
}
+ /// The number of data points used in the HWMA calculation.
+ /// Alpha smoothing factor for the level component.
+ /// Beta smoothing factor for the velocity component.
+ /// Gamma smoothing factor for the acceleration component.
+ /// Thrown when period is less than 1.
public Hwma(int period, double nA, double nB, double nC)
{
if (period < 1)
@@ -30,6 +67,8 @@ public class Hwma : AbstractBase
Init();
}
+ /// The data source object that publishes updates.
+ /// The number of data points used in the HWMA calculation.
public Hwma(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
@@ -58,7 +97,6 @@ public class Hwma : AbstractBase
_pF = _ppF;
_pV = _ppV;
_pA = _ppA;
-
}
}
@@ -93,4 +131,4 @@ public class Hwma : AbstractBase
IsHot = _index >= WarmupPeriod;
return hwma;
}
-}
\ No newline at end of file
+}
diff --git a/lib/averages/Jma.cs b/lib/averages/Jma.cs
index 0d3777cb..e250ca15 100644
--- a/lib/averages/Jma.cs
+++ b/lib/averages/Jma.cs
@@ -1,9 +1,31 @@
-///
-/// Represents a Jurik Moving Average, based on known and reverse-engineered insights
-///
-
+using System;
namespace QuanTAlib;
+///
+/// JMA: Jurik Moving Average
+/// A sophisticated moving average that combines adaptive volatility measurement with
+/// phase-shifted smoothing. JMA provides excellent noise reduction while maintaining
+/// responsiveness to significant price movements.
+///
+///
+/// The JMA calculation process:
+/// 1. Calculates adaptive volatility bands
+/// 2. Uses volatility to adjust smoothing parameters
+/// 3. Applies phase-shifted smoothing for lag reduction
+/// 4. Combines multiple smoothing stages for final output
+///
+/// Key characteristics:
+/// - Adaptive smoothing based on price volatility
+/// - Phase-shifting to reduce lag
+/// - Excellent noise reduction
+/// - Maintains responsiveness to significant moves
+/// - Provides volatility bands as additional outputs
+///
+/// Implementation:
+/// Based on known and reverse-engineered insights from Jurik Research
+/// Original work by Mark Jurik
+///
+
public class Jma : AbstractBase
{
private readonly double _period;
@@ -18,7 +40,6 @@ public class Jma : AbstractBase
private double _prevMa1, _prevDet0, _prevDet1, _prevJma, _p_prevMa1, _p_prevDet0, _p_prevDet1, _p_prevJma;
private double _vSum, _p_vSum;
-
public double UpperBand { get; set; }
public double LowerBand { get; set; }
public double Volty { get; set; }
@@ -27,11 +48,11 @@ public class Jma : AbstractBase
///
/// Initializes a new instance of the Jma class with the specified parameters.
///
- /// The period over which to calculate the Jvolty.
- /// The phase parameter for the JMA-style calculation.
- ///
- /// Thrown when period is less than 1.
- ///
+ /// The period over which to calculate the JMA.
+ /// The phase parameter (-100 to +100) controlling lag compensation.
+ /// The factor controlling volatility adaptation (default 0.45).
+ /// The size of the volatility buffer (default 10).
+ /// Thrown when period is less than 1.
public Jma(int period, int phase = 0, double factor = 0.45, int buffer = 10)
{
if (period < 1)
@@ -51,20 +72,19 @@ public class Jma : AbstractBase
}
///
- /// Initializes a new instance of the Jvolty class with the specified source and parameters.
+ /// Initializes a new instance of the Jma class with a specified source.
///
- /// The source object to subscribe to for value updates.
- /// The period over which to calculate the Jvolty.
- /// The phase parameter for the JMA-style calculation.
+ /// The data source object that publishes updates.
+ /// The period over which to calculate the JMA.
+ /// The phase parameter (-100 to +100) controlling lag compensation.
+ /// The factor controlling volatility adaptation (default 0.45).
+ /// The size of the volatility buffer (default 10).
public Jma(object source, int period, int phase = 0, double factor = 0.45, int buffer = 10) : this(period, phase, factor, buffer)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
- ///
- /// Initializes the Jma instance by setting up the initial state.
- ///
public override void Init()
{
base.Init();
@@ -76,10 +96,6 @@ public class Jma : AbstractBase
_vsumBuff.Clear();
}
- ///
- /// Manages the state of the Jma instance based on whether a new value is being processed.
- ///
- /// Indicates whether the current input is a new value.
protected override void ManageState(bool isNew)
{
if (isNew)
@@ -105,12 +121,6 @@ public class Jma : AbstractBase
}
}
- ///
- /// Performs the Jma calculation for the current value.
- ///
- ///
- /// The calculated Jma value for the current input.
- ///
protected override double Calculation()
{
ManageState(Input.IsNew);
@@ -141,10 +151,10 @@ public class Jma : AbstractBase
_lowerBand = (del2 <= 0) ? price : price - (Kv * del2);
double _alpha = Math.Pow(_beta, pow2);
- double ma1 = Input.Value + _alpha * (_prevMa1 - Input.Value); //original: (1 - _alpha) * Input.Value + _alpha * _prevMa1;
+ double ma1 = Input.Value + _alpha * (_prevMa1 - Input.Value);
_prevMa1 = ma1;
- double det0 = price + _beta * (_prevDet0 - price + ma1) - ma1; //original: (price - ma1) * (1 - _beta) + _beta * _prevDet0;
+ double det0 = price + _beta * (_prevDet0 - price + ma1) - ma1;
_prevDet0 = det0;
double ma2 = ma1 + _phase * det0;
diff --git a/lib/averages/Kama.cs b/lib/averages/Kama.cs
index e509c5e3..7c15a827 100644
--- a/lib/averages/Kama.cs
+++ b/lib/averages/Kama.cs
@@ -1,5 +1,30 @@
+using System;
namespace QuanTAlib;
+///
+/// KAMA: Kaufman's Adaptive Moving Average
+/// An adaptive moving average that adjusts its smoothing based on market efficiency.
+/// KAMA responds quickly during trending periods and becomes more stable during
+/// sideways or choppy markets.
+///
+///
+/// The KAMA calculation process:
+/// 1. Calculates the Efficiency Ratio (ER) to measure market noise
+/// 2. Uses ER to determine the optimal smoothing between fast and slow constants
+/// 3. Applies the adaptive smoothing to create the moving average
+///
+/// Key characteristics:
+/// - Self-adaptive to market conditions
+/// - Fast response during trends
+/// - Stable during sideways markets
+/// - Uses market efficiency for smoothing adjustment
+/// - Reduces whipsaws in choppy markets
+///
+/// Sources:
+/// Perry Kaufman - "Smarter Trading"
+/// https://www.investopedia.com/terms/k/kaufmansadaptivemovingaverage.asp
+///
+
public class Kama : AbstractBase
{
private readonly int _period;
@@ -7,6 +32,10 @@ public class Kama : AbstractBase
private CircularBuffer? _buffer;
private double _lastKama, _p_lastKama;
+ /// The number of periods used to calculate the Efficiency Ratio.
+ /// The number of periods for the fastest EMA response (default 2).
+ /// The number of periods for the slowest EMA response (default 30).
+ /// Thrown when period is less than 1.
public Kama(int period, int fast = 2, int slow = 30)
{
if (period < 1)
@@ -21,6 +50,10 @@ public class Kama : AbstractBase
Init();
}
+ /// The data source object that publishes updates.
+ /// The number of periods used to calculate the Efficiency Ratio.
+ /// The number of periods for the fastest EMA response (default 2).
+ /// The number of periods for the slowest EMA response (default 30).
public Kama(object source, int period, int fast = 2, int slow = 30) : this(period, fast, slow)
{
var pubEvent = source.GetType().GetEvent("Pub");
@@ -80,4 +113,4 @@ public class Kama : AbstractBase
return kama;
}
-}
\ No newline at end of file
+}
diff --git a/lib/averages/Ltma.cs b/lib/averages/Ltma.cs
index e0aca68b..910322ab 100644
--- a/lib/averages/Ltma.cs
+++ b/lib/averages/Ltma.cs
@@ -1,6 +1,30 @@
+using System;
namespace QuanTAlib;
-// https://www.mesasoftware.com/papers/TimeWarp.pdf
+///
+/// LTMA: Laguerre Time Moving Average
+/// A sophisticated moving average that uses Laguerre polynomials to create a time-based
+/// filter. This approach provides excellent noise reduction while maintaining
+/// responsiveness to price changes.
+///
+///
+/// The LTMA calculation process:
+/// 1. Applies a cascade of four Laguerre filters
+/// 2. Each filter stage provides additional smoothing
+/// 3. Combines the filtered outputs with optimal weights
+/// 4. Produces a smooth output with minimal lag
+///
+/// Key characteristics:
+/// - Time-based filtering using Laguerre polynomials
+/// - Excellent noise reduction
+/// - Maintains good responsiveness
+/// - Single parameter (gamma) controls smoothing
+/// - Computationally efficient
+///
+/// Sources:
+/// John Ehlers - "Time Warp - Without Space Travel"
+/// https://www.mesasoftware.com/papers/TimeWarp.pdf
+///
public class Ltma : AbstractBase
{
@@ -8,8 +32,13 @@ public class Ltma : AbstractBase
private double _prevL0, _prevL1, _prevL2, _prevL3;
private double _p_prevL0, _p_prevL1, _p_prevL2, _p_prevL3;
+ ///
+ /// Gets the gamma parameter value used in the Laguerre filter.
+ ///
public double Gamma => _gamma;
+ /// The damping factor (0 to 1) controlling the smoothing. Lower values provide more smoothing.
+ /// Thrown when gamma is not between 0 and 1.
public Ltma(double gamma = 0.1)
{
if (gamma < 0 || gamma > 1)
@@ -20,6 +49,8 @@ public class Ltma : AbstractBase
Init();
}
+ /// The data source object that publishes updates.
+ /// The damping factor (0 to 1) controlling the smoothing.
public Ltma(object source, double gamma = 0.1) : this(gamma)
{
var pubEvent = source.GetType().GetEvent("Pub");
diff --git a/lib/averages/Maaf.cs b/lib/averages/Maaf.cs
index 2b31ddf0..b3ddc808 100644
--- a/lib/averages/Maaf.cs
+++ b/lib/averages/Maaf.cs
@@ -1,8 +1,33 @@
+using System;
+using System.Linq;
namespace QuanTAlib;
-// https://efs.kb.esignal.com/hc/en-us/articles/6362791434395-2005-Mar-The-Secret-Behind-The-Filter-MedianAdaptiveFilter-efs
-
-//TODO Fix initial values
+///
+/// MAAF: Median Adaptive Average Filter
+/// A sophisticated moving average that combines median filtering with adaptive smoothing
+/// to provide robust noise reduction while maintaining signal fidelity. The filter
+/// automatically adjusts its length based on market conditions.
+///
+///
+/// The MAAF calculation process:
+/// 1. Applies initial smoothing using weighted moving average
+/// 2. Uses median filtering to remove outliers
+/// 3. Adaptively adjusts filter length based on price deviation
+/// 4. Applies final EMA smoothing with adaptive period
+///
+/// Key characteristics:
+/// - Combines median and exponential filtering
+/// - Adaptive period adjustment
+/// - Robust noise reduction
+/// - Preserves significant price movements
+/// - Reduces impact of outliers
+///
+/// Sources:
+/// John F. Ehlers - "The Secret Behind The Filter"
+/// https://efs.kb.esignal.com/hc/en-us/articles/6362791434395-2005-Mar-The-Secret-Behind-The-Filter-MedianAdaptiveFilter-efs
+///
+/// Note: Initial values handling is currently under development.
+///
public class Maaf : AbstractBase
{
@@ -14,6 +39,8 @@ public class Maaf : AbstractBase
private readonly int _period;
+ /// The initial period for the filter (default 39).
+ /// The threshold for adaptive adjustment (default 0.002).
public Maaf(int period = 39, double threshold = 0.002)
{
_period = period;
@@ -25,6 +52,9 @@ public class Maaf : AbstractBase
Init();
}
+ /// The data source object that publishes updates.
+ /// The initial period for the filter (default 39).
+ /// The threshold for adaptive adjustment (default 0.002).
public Maaf(object source, int period = 39, double threshold = 0.002) : this(period, threshold)
{
var pubEvent = source.GetType().GetEvent("Pub");
@@ -70,7 +100,6 @@ public class Maaf : AbstractBase
double smooth = (_priceBuffer[^1] + (2 * _priceBuffer[^2]) + (2 * _priceBuffer[^3]) + _priceBuffer[^4]) / 6;
_smoothBuffer.Add(smooth, Input.IsNew);
-
if (_smoothBuffer.Count < _period)
{
return smooth;
diff --git a/lib/averages/Mama.cs b/lib/averages/Mama.cs
index 3af81ddb..74e82a73 100644
--- a/lib/averages/Mama.cs
+++ b/lib/averages/Mama.cs
@@ -1,5 +1,32 @@
+using System;
namespace QuanTAlib;
+///
+/// MAMA: MESA Adaptive Moving Average
+/// A highly sophisticated adaptive moving average that uses the MESA (Maximum Entropy
+/// Spectral Analysis) algorithm to detect market cycles and adjust its smoothing
+/// accordingly. MAMA provides both a faster (MAMA) and slower (FAMA) moving average.
+///
+///
+/// The MAMA calculation process:
+/// 1. Uses Hilbert Transform to decompose price into phase and amplitude
+/// 2. Calculates the dominant cycle period using phase analysis
+/// 3. Determines phase position and rate of change
+/// 4. Adapts smoothing based on phase changes
+/// 5. Generates both MAMA and FAMA (Following Adaptive Moving Average)
+///
+/// Key characteristics:
+/// - Highly adaptive to market conditions
+/// - Provides two synchronized moving averages
+/// - Uses cycle analysis for adaptation
+/// - Excellent at identifying trend changes
+/// - Combines multiple signal processing techniques
+///
+/// Sources:
+/// John Ehlers - "MESA Adaptive Moving Averages"
+/// https://www.mesasoftware.com/papers/MAMA.pdf
+///
+
public class Mama : AbstractBase
{
private readonly double _fastLimit, _slowLimit;
@@ -8,8 +35,13 @@ public class Mama : AbstractBase
private double _prevMama, _prevFama, _sumPr;
private double _p_prevMama, _p_prevFama, _p_sumPr;
+ ///
+ /// Gets the Following Adaptive Moving Average (FAMA) value.
+ ///
public TValue Fama { get; private set; }
+ /// The maximum adaptation speed (default 0.5).
+ /// The minimum adaptation speed (default 0.05).
public Mama(double fastLimit = 0.5, double slowLimit = 0.05)
{
Fama = new TValue();
@@ -30,6 +62,9 @@ public class Mama : AbstractBase
Init();
}
+ /// The data source object that publishes updates.
+ /// The maximum adaptation speed (default 0.5).
+ /// The minimum adaptation speed (default 0.05).
public Mama(object source, double fastLimit = 0.5, double slowLimit = 0.05) : this(fastLimit, slowLimit)
{
var pubEvent = source.GetType().GetEvent("Pub");
diff --git a/lib/averages/Mgdi.cs b/lib/averages/Mgdi.cs
index 27d8dd4a..70ec070c 100644
--- a/lib/averages/Mgdi.cs
+++ b/lib/averages/Mgdi.cs
@@ -1,10 +1,39 @@
+using System;
namespace QuanTAlib;
+///
+/// MGDI: Modified Geometric Decay Index
+/// A moving average that uses geometric decay with a ratio-based adjustment factor.
+/// The decay rate is modified based on the ratio between current and previous values,
+/// allowing for adaptive smoothing based on price movement magnitude.
+///
+///
+/// The MGDI calculation process:
+/// 1. Calculates ratio between current and previous values
+/// 2. Uses ratio to modify the geometric decay rate
+/// 3. Applies modified decay to smooth the data
+/// 4. Adjusts smoothing based on K-factor parameter
+///
+/// Key characteristics:
+/// - Geometric decay-based smoothing
+/// - Adaptive to price movement magnitude
+/// - Adjustable smoothing via K-factor
+/// - More responsive to large price changes
+/// - Maintains smoothness during small fluctuations
+///
+/// Implementation:
+/// Based on geometric decay principles with ratio-based modification
+///
+
public class Mgdi : AbstractBase
{
private readonly int _period;
private readonly double _kFactor;
private double _prevMd, _p_prevMd;
+
+ /// The number of periods used in the MGDI calculation.
+ /// The K-factor controlling the decay rate adjustment (default 0.6).
+ /// Thrown when period or kFactor is less than or equal to 0.
public Mgdi(int period, double kFactor = 0.6)
{
if (period <= 0)
@@ -22,6 +51,9 @@ public class Mgdi : AbstractBase
Init();
}
+ /// The data source object that publishes updates.
+ /// The number of periods used in the MGDI calculation.
+ /// The K-factor controlling the decay rate adjustment (default 0.6).
public Mgdi(object source, int period, double kFactor = 0.6) : this(period, kFactor)
{
var pubEvent = source.GetType().GetEvent("Pub");
@@ -67,4 +99,4 @@ public class Mgdi : AbstractBase
IsHot = _index >= _period;
return _prevMd;
}
-}
\ No newline at end of file
+}
diff --git a/lib/averages/Mma.cs b/lib/averages/Mma.cs
index cc02e1bc..99d92ab3 100644
--- a/lib/averages/Mma.cs
+++ b/lib/averages/Mma.cs
@@ -1,11 +1,38 @@
+using System;
namespace QuanTAlib;
+///
+/// MMA: Modified Moving Average
+/// A moving average that combines a simple moving average with a weighted component
+/// to provide a balanced smoothing effect. The weighting scheme emphasizes central
+/// values while maintaining overall data representation.
+///
+///
+/// The MMA calculation process:
+/// 1. Calculates the simple moving average component (T/period)
+/// 2. Calculates a weighted sum with symmetric weights around the center
+/// 3. Combines both components using the formula: SMA + 6*WeightedSum/((period+1)*period)
+///
+/// Key characteristics:
+/// - Combines simple and weighted moving averages
+/// - Symmetric weighting around the center
+/// - Better balance between smoothing and responsiveness
+/// - Reduces lag compared to simple moving average
+/// - Maintains stability through dual-component approach
+///
+/// Implementation:
+/// Based on modified moving average principles combining
+/// simple and weighted components for optimal smoothing
+///
+
public class Mma : AbstractBase
{
private readonly int _period;
private readonly CircularBuffer _buffer;
private double _lastMma;
+ /// The number of periods used in the MMA calculation. Must be at least 2.
+ /// Thrown when period is less than 2.
public Mma(int period)
{
if (period < 2)
@@ -19,6 +46,8 @@ public class Mma : AbstractBase
Init();
}
+ /// The data source object that publishes updates.
+ /// The number of periods used in the MMA calculation.
public Mma(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
@@ -61,6 +90,11 @@ public class Mma : AbstractBase
return _lastMma;
}
+ ///
+ /// Calculates the weighted sum component of the MMA.
+ /// The weights are symmetric around the center, decreasing linearly from the center outward.
+ ///
+ /// The weighted sum of the data points.
private double CalculateWeightedSum()
{
double sum = 0;
diff --git a/lib/averages/Pwma.cs b/lib/averages/Pwma.cs
index fc2aac76..73c27470 100644
--- a/lib/averages/Pwma.cs
+++ b/lib/averages/Pwma.cs
@@ -1,10 +1,39 @@
+using System;
+using System.Linq;
namespace QuanTAlib;
+///
+/// PWMA: Pascal Weighted Moving Average
+/// A moving average that uses Pascal's triangle coefficients as weights, providing
+/// a natural distribution of weights that increases towards the center of the period.
+/// This creates a smooth average with balanced emphasis on central values.
+///
+///
+/// The PWMA calculation process:
+/// 1. Generates weights using Pascal's triangle coefficients
+/// 2. Normalizes the weights to sum to 1
+/// 3. Applies the weights through convolution
+/// 4. Adjusts for partial periods during warmup
+///
+/// Key characteristics:
+/// - Natural weight distribution from Pascal's triangle
+/// - Symmetric weighting around the center
+/// - Smooth response to price changes
+/// - Balanced between recent and historical data
+/// - Implemented using efficient convolution operations
+///
+/// Implementation:
+/// Based on Pascal's triangle principles for weight generation
+/// Uses convolution for efficient calculation
+///
+
public class Pwma : AbstractBase
{
private readonly int _period;
private readonly Convolution _convolution;
+ /// The number of data points used in the PWMA calculation.
+ /// Thrown when period is less than 1.
public Pwma(int period)
{
if (period < 1)
@@ -18,6 +47,8 @@ public class Pwma : AbstractBase
Init();
}
+ /// The data source object that publishes updates.
+ /// The number of data points used in the PWMA calculation.
public Pwma(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
@@ -60,6 +91,11 @@ public class Pwma : AbstractBase
return result;
}
+ ///
+ /// Generates the Pascal's triangle-based convolution kernel for the PWMA calculation.
+ ///
+ /// The period for which to generate the kernel.
+ /// An array of normalized Pascal's triangle-based weights for the convolution operation.
public static double[] GenerateKernel(int period)
{
double[] kernel = new double[period];
diff --git a/lib/averages/Qema.cs b/lib/averages/Qema.cs
index d09e6ef0..cdf08f82 100644
--- a/lib/averages/Qema.cs
+++ b/lib/averages/Qema.cs
@@ -1,11 +1,42 @@
+using System;
namespace QuanTAlib;
+///
+/// QEMA: Quadruple Exponential Moving Average
+/// A sophisticated moving average that applies four exponential moving averages in sequence
+/// and combines them using a specific formula to reduce lag while maintaining smoothness.
+/// The final combination is: 4*EMA1 - 6*EMA2 + 4*EMA3 - EMA4
+///
+///
+/// The QEMA calculation process:
+/// 1. Applies first EMA to price data
+/// 2. Applies second EMA to result of first EMA
+/// 3. Applies third EMA to result of second EMA
+/// 4. Applies fourth EMA to result of third EMA
+/// 5. Combines results using the formula: 4*EMA1 - 6*EMA2 + 4*EMA3 - EMA4
+///
+/// Key characteristics:
+/// - Multiple EMA smoothing stages
+/// - Reduced lag through combination formula
+/// - Customizable smoothing factors for each EMA
+/// - Better noise reduction than single EMA
+/// - Maintains responsiveness to significant moves
+///
+/// Implementation:
+/// Based on quadruple exponential smoothing principles
+/// with optimized combination formula
+///
+
public class Qema : AbstractBase
{
-
private readonly Ema _ema1, _ema2, _ema3, _ema4;
private double _lastQema, _p_lastQema;
+ /// Smoothing factor for first EMA (default 0.2).
+ /// Smoothing factor for second EMA (default 0.2).
+ /// Smoothing factor for third EMA (default 0.2).
+ /// Smoothing factor for fourth EMA (default 0.2).
+ /// Thrown when any k value is less than or equal to 0.
public Qema(double k1 = 0.2, double k2 = 0.2, double k3 = 0.2, double k4 = 0.2)
{
if (k1 <= 0 || k2 <= 0 || k3 <= 0 || k4 <= 0)
@@ -25,6 +56,11 @@ public class Qema : AbstractBase
Init();
}
+ /// The data source object that publishes updates.
+ /// Smoothing factor for first EMA.
+ /// Smoothing factor for second EMA.
+ /// Smoothing factor for third EMA.
+ /// Smoothing factor for fourth EMA.
public Qema(object source, double k1, double k2, double k3, double k4)
: this(k1, k2, k3, k4)
{
@@ -66,4 +102,4 @@ public class Qema : AbstractBase
IsHot = _index >= WarmupPeriod;
return _lastQema;
}
-}
\ No newline at end of file
+}
diff --git a/lib/averages/Rema.cs b/lib/averages/Rema.cs
index afa22046..b8420a3d 100644
--- a/lib/averages/Rema.cs
+++ b/lib/averages/Rema.cs
@@ -1,6 +1,29 @@
+using System;
namespace QuanTAlib;
-//https://user42.tuxfamily.org/chart/manual/Regularized-Exponential-Moving-Average.html
+///
+/// REMA: Regularized Exponential Moving Average
+/// A modified exponential moving average that includes a regularization term to reduce
+/// noise and improve trend following. The regularization helps to smooth the output
+/// while maintaining responsiveness to significant price movements.
+///
+///
+/// The REMA calculation process:
+/// 1. Uses standard EMA smoothing with adaptive alpha
+/// 2. Adds regularization term based on previous values
+/// 3. Balances new and regularized terms using lambda parameter
+/// 4. Provides smoother output than standard EMA
+///
+/// Key characteristics:
+/// - Improved noise reduction through regularization
+/// - Better trend following than standard EMA
+/// - Adjustable regularization via lambda parameter
+/// - Adaptive alpha based on period
+/// - Reduced whipsaws in choppy markets
+///
+/// Sources:
+/// https://user42.tuxfamily.org/chart/manual/Regularized-Exponential-Moving-Average.html
+///
public class Rema : AbstractBase
{
@@ -9,9 +32,19 @@ public class Rema : AbstractBase
private double _lastRema, _prevRema;
private double _savedLastRema, _savedPrevRema;
+ ///
+ /// Gets the period used in the REMA calculation.
+ ///
public int Period => _period;
+
+ ///
+ /// Gets the lambda (regularization) parameter value.
+ ///
public double Lambda => _lambda;
+ /// The number of periods used in the REMA calculation.
+ /// The regularization parameter (default 0.5). Higher values increase smoothing.
+ /// Thrown when period is less than 1 or lambda is negative.
public Rema(int period, double lambda = 0.5)
{
if (period < 1)
@@ -25,11 +58,16 @@ public class Rema : AbstractBase
WarmupPeriod = period;
Init();
}
+
+ /// The data source object that publishes updates.
+ /// The number of periods used in the REMA calculation.
+ /// The regularization parameter (default 0.5).
public Rema(object source, int period, double lambda = 0.5) : this(period, lambda)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
+
public override void Init()
{
base.Init();
@@ -79,4 +117,4 @@ public class Rema : AbstractBase
IsHot = _index >= WarmupPeriod;
return _lastRema;
}
-}
\ No newline at end of file
+}
diff --git a/lib/averages/Sinema.cs b/lib/averages/Sinema.cs
index 6834dc64..64efde2e 100644
--- a/lib/averages/Sinema.cs
+++ b/lib/averages/Sinema.cs
@@ -1,9 +1,37 @@
+using System;
namespace QuanTAlib;
+///
+/// SINEMA: Sine-weighted Exponential Moving Average
+/// A moving average that uses sine function-based weights to create a natural
+/// distribution of importance across the period. The weights follow a sine curve,
+/// providing smooth transitions and natural emphasis on different parts of the data.
+///
+///
+/// The SINEMA calculation process:
+/// 1. Generates weights using sine function over the period
+/// 2. Normalizes weights to sum to 1
+/// 3. Applies weights through convolution
+/// 4. Produces smooth output with natural weight distribution
+///
+/// Key characteristics:
+/// - Sine-based weight distribution
+/// - Natural smoothing through trigonometric weights
+/// - No sharp transitions in weight values
+/// - Balanced emphasis across the period
+/// - Implemented using efficient convolution operations
+///
+/// Implementation:
+/// Based on sine function principles for weight generation
+/// Uses convolution for efficient calculation
+///
+
public class Sinema : AbstractBase
{
private readonly Convolution _convolution;
+ /// The number of data points used in the SINEMA calculation.
+ /// Thrown when period is less than 1.
public Sinema(int period)
{
if (period < 1)
@@ -16,6 +44,8 @@ public class Sinema : AbstractBase
Init();
}
+ /// The data source object that publishes updates.
+ /// The number of data points used in the SINEMA calculation.
public Sinema(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
@@ -50,6 +80,11 @@ public class Sinema : AbstractBase
return result;
}
+ ///
+ /// Generates the sine-based convolution kernel for the SINEMA calculation.
+ ///
+ /// The period for which to generate the kernel.
+ /// An array of normalized sine-based weights for the convolution operation.
public static double[] GenerateKernel(int period)
{
double[] kernel = new double[period];
@@ -70,4 +105,4 @@ public class Sinema : AbstractBase
return kernel;
}
-}
\ No newline at end of file
+}
diff --git a/lib/averages/Sma.cs b/lib/averages/Sma.cs
index c0294bd0..6c797162 100644
--- a/lib/averages/Sma.cs
+++ b/lib/averages/Sma.cs
@@ -1,11 +1,38 @@
+using System;
namespace QuanTAlib;
+///
+/// SMA: Simple Moving Average
+/// The most basic form of moving average, calculating the arithmetic mean over a
+/// specified period. Each data point in the period has equal weight in the
+/// calculation.
+///
+///
+/// The SMA calculation process:
+/// 1. Maintains a buffer of the last 'period' values
+/// 2. Calculates arithmetic mean of all values in the buffer
+/// 3. Updates buffer with new values in FIFO manner
+///
+/// Key characteristics:
+/// - Equal weight for all values in the period
+/// - Simple and straightforward calculation
+/// - Significant lag due to equal weighting
+/// - Smooth output with good noise reduction
+/// - Most basic form of trend following
+///
+/// Sources:
+/// https://www.investopedia.com/terms/s/sma.asp
+/// https://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:moving_averages
+///
+
public class Sma : AbstractBase
{
// inherited _index
// inherited _value
private readonly CircularBuffer _buffer;
+ /// The number of data points used in the SMA calculation.
+ /// Thrown when period is less than 1.
public Sma(int period)
{
if (period < 1)
@@ -19,12 +46,13 @@ public class Sma : AbstractBase
Init();
}
+ /// The data source object that publishes updates.
+ /// The number of data points used in the SMA calculation.
public Sma(object source, int period) : this(period: period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
- //inhereted public void Sub(object source, in ValueEventArgs args)
protected override void ManageState(bool isNew)
{
@@ -36,9 +64,9 @@ public class Sma : AbstractBase
}
///
- /// Core SMA calculation - using _buffer.Average
+ /// Performs the core SMA calculation using the circular buffer's average.
///
-
+ /// The calculated SMA value.
protected override double Calculation()
{
double result;
@@ -49,4 +77,4 @@ public class Sma : AbstractBase
IsHot = _index >= WarmupPeriod;
return result;
}
-}
\ No newline at end of file
+}
diff --git a/lib/averages/Smma.cs b/lib/averages/Smma.cs
index 0ca8c403..f66262b0 100644
--- a/lib/averages/Smma.cs
+++ b/lib/averages/Smma.cs
@@ -1,13 +1,38 @@
using System;
-
namespace QuanTAlib;
+///
+/// SMMA: Smoothed Moving Average
+/// A modified moving average that gives more weight to recent prices while maintaining
+/// a smooth output. It uses the previous SMMA value in its calculation, creating
+/// a smoother line than traditional moving averages.
+///
+///
+/// The SMMA calculation process:
+/// 1. Uses SMA for initial value (first period points)
+/// 2. For subsequent points, calculates: (prevSMMA * (period-1) + price) / period
+/// 3. This creates a smoothed effect with reduced volatility
+///
+/// Key characteristics:
+/// - Smoother than traditional moving averages
+/// - Reduced volatility in output
+/// - Takes into account all previous prices
+/// - Good for identifying overall trends
+/// - Less lag than SMA but more than EMA
+///
+/// Implementation:
+/// Based on smoothed moving average principles with
+/// initial SMA seeding for stability
+///
+
public class Smma : AbstractBase
{
private readonly int _period;
private CircularBuffer? _buffer;
private double _lastSmma, _p_lastSmma;
+ /// The number of data points used in the SMMA calculation.
+ /// Thrown when period is less than 1.
public Smma(int period)
{
if (period < 1)
@@ -20,6 +45,8 @@ public class Smma : AbstractBase
Init();
}
+ /// The data source object that publishes updates.
+ /// The number of data points used in the SMMA calculation.
public Smma(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
@@ -47,7 +74,6 @@ public class Smma : AbstractBase
}
}
-
protected override double Calculation()
{
ManageState(Input.IsNew);
@@ -75,4 +101,4 @@ public class Smma : AbstractBase
return smma;
}
-}
\ No newline at end of file
+}
diff --git a/lib/averages/T3.cs b/lib/averages/T3.cs
index 2924fe95..49a00cb5 100644
--- a/lib/averages/T3.cs
+++ b/lib/averages/T3.cs
@@ -1,5 +1,31 @@
+using System;
namespace QuanTAlib;
+///
+/// T3: Tillson T3 Moving Average
+/// A sophisticated moving average developed by Tim Tillson that applies six EMAs
+/// in sequence with optimized coefficients. The T3 provides excellent smoothing
+/// while maintaining responsiveness and minimal lag.
+///
+///
+/// The T3 calculation process:
+/// 1. Applies six EMAs in sequence
+/// 2. Uses volume factor to determine optimal coefficients
+/// 3. Combines EMAs using specific formula: c1*EMA6 + c2*EMA5 + c3*EMA4 + c4*EMA3
+/// 4. Coefficients are based on the volume factor parameter
+///
+/// Key characteristics:
+/// - Excellent smoothing with minimal lag
+/// - Adjustable via volume factor parameter
+/// - No overshooting like triple EMA
+/// - Better noise reduction than traditional EMAs
+/// - Maintains responsiveness to significant moves
+///
+/// Sources:
+/// Tim Tillson - "Better Moving Averages"
+/// TASC Magazine, 1998
+///
+
public class T3 : AbstractBase
{
private readonly int _period;
@@ -10,6 +36,10 @@ public class T3 : AbstractBase
private double _lastEma1, _lastEma2, _lastEma3, _lastEma4, _lastEma5, _lastEma6;
private double _p_lastEma1, _p_lastEma2, _p_lastEma3, _p_lastEma4, _p_lastEma5, _p_lastEma6;
+ /// The number of periods used in each EMA calculation.
+ /// Volume factor controlling smoothing (default 0.7).
+ /// Whether to use SMA for initial values (default true).
+ /// Thrown when period is less than 1.
public T3(int period, double vfactor = 0.7, bool useSma = true)
{
if (period < 1)
@@ -35,11 +65,14 @@ public class T3 : AbstractBase
_buffer5 = new(period);
_buffer6 = new(period);
-
Name = $"T3({_period}, {_vfactor})";
Init();
}
+ /// The data source object that publishes updates.
+ /// The number of periods used in each EMA calculation.
+ /// Volume factor controlling smoothing (default 0.7).
+ /// Whether to use SMA for initial values (default true).
public T3(object source, int period, double vfactor = 0.7, bool useSma = true) : this(period, vfactor, useSma)
{
var pubEvent = source.GetType().GetEvent("Pub");
@@ -81,7 +114,6 @@ public class T3 : AbstractBase
}
}
-
protected override double Calculation()
{
ManageState(Input.IsNew);
@@ -129,4 +161,4 @@ public class T3 : AbstractBase
IsHot = _index >= WarmupPeriod;
return t3;
}
-}
\ No newline at end of file
+}
diff --git a/lib/averages/Tema.cs b/lib/averages/Tema.cs
index 4d4abef7..1090cd2a 100644
--- a/lib/averages/Tema.cs
+++ b/lib/averages/Tema.cs
@@ -1,5 +1,31 @@
+using System;
namespace QuanTAlib;
+///
+/// TEMA: Triple Exponential Moving Average
+/// A sophisticated moving average that applies three EMAs in sequence with a specific
+/// combination formula to reduce lag while maintaining smoothness. The formula
+/// 3*EMA1 - 3*EMA2 + EMA3 helps eliminate lag in trending markets.
+///
+///
+/// The TEMA calculation process:
+/// 1. Calculates first EMA of the price
+/// 2. Calculates second EMA of the first EMA
+/// 3. Calculates third EMA of the second EMA
+/// 4. Combines using formula: 3*EMA1 - 3*EMA2 + EMA3
+///
+/// Key characteristics:
+/// - Significantly reduced lag compared to single EMA
+/// - Better response to trends than standard EMAs
+/// - Maintains smoothness despite reduced lag
+/// - More responsive than double EMA (DEMA)
+/// - Uses compensator for early values
+///
+/// Sources:
+/// Patrick Mulloy - "Smoothing Data with Faster Moving Averages"
+/// Technical Analysis of Stocks and Commodities, 1994
+///
+
public class Tema : AbstractBase
{
private readonly int _period;
@@ -8,6 +34,8 @@ public class Tema : AbstractBase
private double _lastEma3, _p_lastEma3;
private double _k, _e, _p_e;
+ /// The number of periods used in each EMA calculation.
+ /// Thrown when period is less than 1.
public Tema(int period)
{
if (period < 1)
@@ -21,6 +49,8 @@ public class Tema : AbstractBase
Init();
}
+ /// The data source object that publishes updates.
+ /// The number of periods used in each EMA calculation.
public Tema(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
@@ -63,9 +93,7 @@ public class Tema : AbstractBase
double _invE = (_e > 1e-10) ? 1 / (1 - _e) : 1;
_ema1 = _k * (Input.Value - _lastEma1) + _lastEma1;
-
_ema2 = _k * (_ema1 * _invE - _lastEma2) + _lastEma2;
-
_ema3 = _k * (_ema2 * _invE - _lastEma3) + _lastEma3;
double _tema = 3 * _ema1 * _invE - 3 * _ema2 * _invE + _ema3 * _invE;
@@ -78,4 +106,4 @@ public class Tema : AbstractBase
IsHot = _index >= WarmupPeriod;
return result;
}
-}
\ No newline at end of file
+}
diff --git a/lib/averages/Trima.cs b/lib/averages/Trima.cs
index 7cfeb715..93e8a17b 100644
--- a/lib/averages/Trima.cs
+++ b/lib/averages/Trima.cs
@@ -1,9 +1,37 @@
+using System;
namespace QuanTAlib;
+///
+/// TRIMA: Triangular Moving Average
+/// A moving average that uses triangular-shaped weights that increase linearly to
+/// the middle of the period and then decrease linearly. This creates a smoother
+/// output than simple moving averages.
+///
+///
+/// The TRIMA calculation process:
+/// 1. Generates triangular weights that peak at the center
+/// 2. Weights increase linearly to middle point
+/// 3. Weights decrease linearly from middle point
+/// 4. Applies normalized weights through convolution
+///
+/// Key characteristics:
+/// - Smoother than simple moving average
+/// - Natural emphasis on central values
+/// - Reduced noise sensitivity
+/// - Double smoothing effect
+/// - Implemented using efficient convolution operations
+///
+/// Sources:
+/// https://www.investopedia.com/terms/t/triangularaverage.asp
+/// Technical Analysis of Stocks & Commodities magazine
+///
+
public class Trima : AbstractBase
{
private readonly Convolution _convolution;
+ /// The number of data points used in the TRIMA calculation.
+ /// Thrown when period is less than 1.
public Trima(int period)
{
if (period < 1)
@@ -16,12 +44,19 @@ public class Trima : AbstractBase
Init();
}
+ /// The data source object that publishes updates.
+ /// The number of data points used in the TRIMA calculation.
public Trima(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
+ ///
+ /// Generates the triangular-shaped convolution kernel for the TRIMA calculation.
+ ///
+ /// The period for which to generate the kernel.
+ /// An array of normalized triangular weights for the convolution operation.
private static double[] GenerateKernel(int period)
{
double[] kernel = new double[period];
@@ -70,4 +105,4 @@ public class Trima : AbstractBase
return result;
}
-}
\ No newline at end of file
+}
diff --git a/lib/averages/Vidya.cs b/lib/averages/Vidya.cs
index 539aced4..125b3e68 100644
--- a/lib/averages/Vidya.cs
+++ b/lib/averages/Vidya.cs
@@ -4,15 +4,43 @@ using System.Runtime.CompilerServices;
namespace QuanTAlib;
+///
+/// VIDYA: Variable Index Dynamic Average
+/// An adaptive moving average that adjusts its smoothing based on the ratio of
+/// short-term to long-term volatility. This allows the average to become more
+/// responsive during volatile periods and more stable during quiet periods.
+///
+///
+/// The VIDYA calculation process:
+/// 1. Calculates standard deviation for short and long periods
+/// 2. Uses ratio of short/long volatility to determine smoothing
+/// 3. Applies variable smoothing factor to price data
+/// 4. Adapts automatically to changing market conditions
+///
+/// Key characteristics:
+/// - Adaptive smoothing based on volatility
+/// - More responsive during volatile periods
+/// - More stable during quiet periods
+/// - Uses standard deviation for volatility measurement
+/// - Combines short and long-term market analysis
+///
+/// Sources:
+/// Tushar Chande - "Beyond Technical Analysis"
+/// https://www.investopedia.com/terms/v/vidya.asp
+///
+
public class Vidya : AbstractBase
{
-
private readonly int _longPeriod;
private readonly double _alpha;
private double _lastVIDYA, _p_lastVIDYA;
private readonly CircularBuffer? _shortBuffer;
private readonly CircularBuffer? _longBuffer;
+ /// The number of periods for short-term volatility calculation.
+ /// The number of periods for long-term volatility calculation (default is 4x shortPeriod).
+ /// The alpha parameter controlling the base smoothing factor (default 0.2).
+ /// Thrown when shortPeriod is less than 1.
public Vidya(int shortPeriod, int longPeriod = 0, double alpha = 0.2)
{
if (shortPeriod < 1)
@@ -28,6 +56,10 @@ public class Vidya : AbstractBase
Init();
}
+ /// The data source object that publishes updates.
+ /// The number of periods for short-term volatility calculation.
+ /// The number of periods for long-term volatility calculation (default is 4x shortPeriod).
+ /// The alpha parameter controlling the base smoothing factor (default 0.2).
public Vidya(object source, int shortPeriod, int longPeriod = 0, double alpha = 0.2)
: this(shortPeriod, longPeriod, alpha)
{
@@ -81,6 +113,11 @@ public class Vidya : AbstractBase
return vidya;
}
+ ///
+ /// Calculates the standard deviation of values in a circular buffer.
+ ///
+ /// The circular buffer containing the values.
+ /// The standard deviation of the values in the buffer.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static double CalculateStdDev(CircularBuffer buffer)
{
@@ -88,4 +125,4 @@ public class Vidya : AbstractBase
double sumSquaredDiff = buffer.Sum(x => Math.Pow(x - mean, 2));
return Math.Sqrt(sumSquaredDiff / buffer.Count);
}
-}
\ No newline at end of file
+}
diff --git a/lib/averages/Wma.cs b/lib/averages/Wma.cs
index 9497ba25..b3ae1167 100644
--- a/lib/averages/Wma.cs
+++ b/lib/averages/Wma.cs
@@ -1,10 +1,39 @@
+using System;
namespace QuanTAlib;
+///
+/// WMA: Weighted Moving Average
+/// A moving average that assigns linearly decreasing weights to older data points.
+/// The most recent price has the highest weight, and each older price receives
+/// linearly less weight, creating a more responsive average than SMA.
+///
+///
+/// The WMA calculation process:
+/// 1. Assigns weights linearly decreasing with age
+/// 2. Most recent price gets weight of period
+/// 3. Each older price gets decremented weight
+/// 4. Normalizes weights by sum of weights
+/// 5. Applies weights through convolution
+///
+/// Key characteristics:
+/// - Linear weight distribution
+/// - More responsive than SMA
+/// - Less lag than SMA
+/// - Emphasizes recent prices
+/// - Implemented using efficient convolution operations
+///
+/// Sources:
+/// https://www.investopedia.com/articles/technical/060401.asp
+/// https://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:weighted_moving_average
+///
+
public class Wma : AbstractBase
{
private readonly int _period;
private readonly Convolution _convolution;
+ /// The number of data points used in the WMA calculation.
+ /// Thrown when period is less than 1.
public Wma(int period)
{
if (period < 1)
@@ -18,12 +47,19 @@ public class Wma : AbstractBase
Init();
}
+ /// The data source object that publishes updates.
+ /// The number of data points used in the WMA calculation.
public Wma(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
+ ///
+ /// Generates the linearly weighted convolution kernel for the WMA calculation.
+ ///
+ /// The period for which to generate the kernel.
+ /// An array of normalized linearly decreasing weights for the convolution operation.
private static double[] GenerateWmaKernel(int period)
{
double[] kernel = new double[period];
@@ -64,4 +100,4 @@ public class Wma : AbstractBase
return result;
}
-}
\ No newline at end of file
+}
diff --git a/lib/averages/Zlema.cs b/lib/averages/Zlema.cs
index 1aae1e16..b88374b1 100644
--- a/lib/averages/Zlema.cs
+++ b/lib/averages/Zlema.cs
@@ -3,6 +3,31 @@ using System.Runtime.CompilerServices;
namespace QuanTAlib
{
+ ///
+ /// ZLEMA: Zero Lag Exponential Moving Average
+ /// A modified exponential moving average designed to reduce lag by incorporating
+ /// error correction based on predicted values. It estimates and removes lag by
+ /// extrapolating the trend using the difference between current and lagged prices.
+ ///
+ ///
+ /// The ZLEMA calculation process:
+ /// 1. Calculates lag period as (period - 1) / 2
+ /// 2. Gets error correction term: 2 * price - lag_price
+ /// 3. Applies EMA to error-corrected price
+ /// 4. Results in reduced lag compared to standard EMA
+ ///
+ /// Key characteristics:
+ /// - Significantly reduced lag compared to EMA
+ /// - More responsive to price changes
+ /// - Uses error correction mechanism
+ /// - Maintains smoothness despite reduced lag
+ /// - Better trend following capabilities
+ ///
+ /// Sources:
+ /// John Ehlers and Ric Way - "Zero Lag (Well, Almost)"
+ /// Technical Analysis of Stocks and Commodities, 2010
+ ///
+
public class Zlema : AbstractBase
{
private readonly CircularBuffer _buffer;
@@ -10,6 +35,8 @@ namespace QuanTAlib
private readonly Ema _ema;
private double _lastZLEMA, _p_lastZLEMA;
+ /// The number of periods used in the ZLEMA calculation.
+ /// Thrown when period is less than 1.
public Zlema(int period)
{
if (period < 1)
@@ -24,6 +51,8 @@ namespace QuanTAlib
Init();
}
+ /// The data source object that publishes updates.
+ /// The number of periods used in the ZLEMA calculation.
public Zlema(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
@@ -59,8 +88,11 @@ namespace QuanTAlib
_buffer.Add(Input.Value, Input.IsNew);
+ // Get lagged value and calculate error correction
double lagValue = _buffer[Math.Max(0, _buffer.Count - 1 - _lag)];
double errorCorrection = 2 * Input.Value - lagValue;
+
+ // Apply EMA to error-corrected value
double zlema = _ema.Calc(new TValue(errorCorrection, Input.IsNew)).Value;
_lastZLEMA = zlema;
diff --git a/lib/errors/Huber.cs b/lib/errors/Huber.cs
index c7510344..44d4915f 100644
--- a/lib/errors/Huber.cs
+++ b/lib/errors/Huber.cs
@@ -1,11 +1,44 @@
+using System;
namespace QuanTAlib;
+///
+/// Huber Loss: A robust error metric that combines squared error for small deviations
+/// and absolute error for large deviations. This provides a balance between the high
+/// sensitivity of MSE to outliers and the constant gradient of MAE.
+///
+///
+/// The Huber Loss calculation process:
+/// 1. For each point, calculates error between actual and predicted values
+/// 2. If absolute error ≤ delta: uses squared error (like MSE)
+/// 3. If absolute error > delta: uses linear error (like MAE)
+/// 4. Averages the losses over the period
+///
+/// Key characteristics:
+/// - Combines benefits of MSE and MAE
+/// - Less sensitive to outliers than MSE
+/// - More sensitive to small errors than MAE
+/// - Differentiable at all points
+/// - Adjustable via delta parameter
+///
+/// Formula:
+/// For error e = actual - predicted:
+/// L(e) = 0.5 * e² if |e| ≤ δ
+/// L(e) = δ * (|e| - 0.5δ) if |e| > δ
+///
+/// Sources:
+/// Peter J. Huber - "Robust Estimation of a Location Parameter"
+/// https://projecteuclid.org/euclid.aoms/1177703732
+///
+
public class Huber : AbstractBase
{
private readonly CircularBuffer _actualBuffer;
private readonly CircularBuffer _predictedBuffer;
private readonly double _delta;
+ /// The number of points over which to calculate the loss.
+ /// The threshold between squared and linear loss (default 1.0).
+ /// Thrown when period is less than 1 or delta is not positive.
public Huber(int period, double delta = 1.0)
{
if (period < 1)
@@ -24,6 +57,9 @@ public class Huber : AbstractBase
Init();
}
+ /// The data source object that publishes updates.
+ /// The number of points over which to calculate the loss.
+ /// The threshold between squared and linear loss (default 1.0).
public Huber(object source, int period, double delta = 1.0) : this(period, delta)
{
var pubEvent = source.GetType().GetEvent("Pub");
@@ -53,6 +89,7 @@ public class Huber : AbstractBase
double actual = Input.Value;
_actualBuffer.Add(actual, Input.IsNew);
+ // If no predicted value provided, use mean of actual values
double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value;
_predictedBuffer.Add(predicted, Input.IsNew);
@@ -70,10 +107,12 @@ public class Huber : AbstractBase
if (absError <= _delta)
{
+ // Squared error for small deviations
sumLoss += 0.5 * error * error;
}
else
{
+ // Linear error for large deviations
sumLoss += _delta * (absError - 0.5 * _delta);
}
}
@@ -84,5 +123,4 @@ public class Huber : AbstractBase
IsHot = _index >= WarmupPeriod;
return huberloss;
}
-
}
diff --git a/lib/errors/Mae.cs b/lib/errors/Mae.cs
index f043af52..3fc8efec 100644
--- a/lib/errors/Mae.cs
+++ b/lib/errors/Mae.cs
@@ -1,10 +1,40 @@
+using System;
namespace QuanTAlib;
+///
+/// MAE: Mean Absolute Error
+/// A straightforward error metric that measures the average magnitude of errors
+/// between predicted and actual values, without considering their direction.
+/// MAE treats all individual differences equally in the average.
+///
+///
+/// The MAE calculation process:
+/// 1. Calculates absolute difference between each actual and predicted value
+/// 2. Sums all absolute differences
+/// 3. Divides by the number of observations
+///
+/// Key characteristics:
+/// - Linear scale (all differences weighted equally)
+/// - Robust to outliers compared to MSE
+/// - Easy to interpret (same units as data)
+/// - Constant gradient for optimization
+/// - Less sensitive to large errors than MSE
+///
+/// Formula:
+/// MAE = (1/n) * Σ|actual - predicted|
+///
+/// Sources:
+/// https://en.wikipedia.org/wiki/Mean_absolute_error
+/// https://www.statisticshowto.com/absolute-error/
+///
+
public class Mae : AbstractBase
{
private readonly CircularBuffer _actualBuffer;
private readonly CircularBuffer _predictedBuffer;
+ /// The number of points over which to calculate the MAE.
+ /// Thrown when period is less than 1.
public Mae(int period)
{
if (period < 1)
@@ -18,6 +48,8 @@ public class Mae : AbstractBase
Init();
}
+ /// The data source object that publishes updates.
+ /// The number of points over which to calculate the MAE.
public Mae(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
@@ -47,6 +79,7 @@ public class Mae : AbstractBase
double actual = Input.Value;
_actualBuffer.Add(actual, Input.IsNew);
+ // If no predicted value provided, use mean of actual values
double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value;
_predictedBuffer.Add(predicted, Input.IsNew);
@@ -68,5 +101,4 @@ public class Mae : AbstractBase
IsHot = _index >= WarmupPeriod;
return mae;
}
-
}
diff --git a/lib/errors/Mapd.cs b/lib/errors/Mapd.cs
index 55a15d73..e36b4889 100644
--- a/lib/errors/Mapd.cs
+++ b/lib/errors/Mapd.cs
@@ -1,10 +1,42 @@
+using System;
namespace QuanTAlib;
+///
+/// MAPD: Mean Absolute Percentage Deviation
+/// A percentage-based error metric that measures the average absolute percentage
+/// difference between predicted and actual values. MAPD expresses accuracy as a
+/// percentage, making it scale-independent and easy to interpret.
+///
+///
+/// The MAPD calculation process:
+/// 1. Calculates absolute percentage difference for each point
+/// 2. Sums all absolute percentage differences
+/// 3. Divides by the number of observations
+///
+/// Key characteristics:
+/// - Scale-independent (percentage-based)
+/// - Easy to interpret (0-100% range)
+/// - Useful for comparing different scales
+/// - Cannot handle zero actual values
+/// - Asymmetric (treats over/under predictions differently)
+///
+/// Formula:
+/// MAPD = (1/n) * Σ|((actual - predicted) / actual)|
+///
+/// Sources:
+/// https://en.wikipedia.org/wiki/Mean_absolute_percentage_error
+/// https://www.statisticshowto.com/mean-absolute-percentage-error-mape/
+///
+/// Note: Also known as MAPE (Mean Absolute Percentage Error) in some contexts
+///
+
public class Mapd : AbstractBase
{
private readonly CircularBuffer _actualBuffer;
private readonly CircularBuffer _predictedBuffer;
+ /// The number of points over which to calculate the MAPD.
+ /// Thrown when period is less than 1.
public Mapd(int period)
{
if (period < 1)
@@ -18,6 +50,8 @@ public class Mapd : AbstractBase
Init();
}
+ /// The data source object that publishes updates.
+ /// The number of points over which to calculate the MAPD.
public Mapd(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
@@ -47,6 +81,7 @@ public class Mapd : AbstractBase
double actual = Input.Value;
_actualBuffer.Add(actual, Input.IsNew);
+ // If no predicted value provided, use mean of actual values
double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value;
_predictedBuffer.Add(predicted, Input.IsNew);
@@ -71,5 +106,4 @@ public class Mapd : AbstractBase
IsHot = _index >= WarmupPeriod;
return mapd;
}
-
}
diff --git a/lib/errors/Mape.cs b/lib/errors/Mape.cs
index f19299f9..26d2492d 100644
--- a/lib/errors/Mape.cs
+++ b/lib/errors/Mape.cs
@@ -1,10 +1,42 @@
+using System;
namespace QuanTAlib;
+///
+/// MAPE: Mean Absolute Percentage Error
+/// A percentage-based error metric that measures the average absolute percentage
+/// difference between predicted and actual values. MAPE expresses accuracy as a
+/// percentage, making it scale-independent and easy to interpret.
+///
+///
+/// The MAPE calculation process:
+/// 1. Calculates absolute percentage error for each point
+/// 2. Sums all absolute percentage errors
+/// 3. Divides by the number of observations
+///
+/// Key characteristics:
+/// - Scale-independent (percentage-based)
+/// - Easy to interpret (0-100% range)
+/// - Useful for comparing different scales
+/// - Cannot handle zero actual values
+/// - Asymmetric (treats over/under predictions differently)
+///
+/// Formula:
+/// MAPE = (1/n) * Σ|((actual - predicted) / actual)| * 100%
+///
+/// Sources:
+/// https://en.wikipedia.org/wiki/Mean_absolute_percentage_error
+/// https://www.statisticshowto.com/mean-absolute-percentage-error-mape/
+///
+/// Note: Also known as MAPD (Mean Absolute Percentage Deviation) in some contexts
+///
+
public class Mape : AbstractBase
{
private readonly CircularBuffer _actualBuffer;
private readonly CircularBuffer _predictedBuffer;
+ /// The number of points over which to calculate the MAPE.
+ /// Thrown when period is less than 1.
public Mape(int period)
{
if (period < 1)
@@ -18,6 +50,8 @@ public class Mape : AbstractBase
Init();
}
+ /// The data source object that publishes updates.
+ /// The number of points over which to calculate the MAPE.
public Mape(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
@@ -47,6 +81,7 @@ public class Mape : AbstractBase
double actual = Input.Value;
_actualBuffer.Add(actual, Input.IsNew);
+ // If no predicted value provided, use mean of actual values
double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value;
_predictedBuffer.Add(predicted, Input.IsNew);
diff --git a/lib/errors/Mase.cs b/lib/errors/Mase.cs
index 5b486ba7..073819ec 100644
--- a/lib/errors/Mase.cs
+++ b/lib/errors/Mase.cs
@@ -1,20 +1,41 @@
using System;
-
namespace QuanTAlib;
///
-/// Represents the Mean Absolute Scaled Error (MASE) calculation.
+/// MASE: Mean Absolute Scaled Error
+/// A scale-free error metric that compares the mean absolute error of the forecast
+/// with the mean absolute error of the naive forecast. MASE is particularly useful
+/// for comparing forecast accuracy across different datasets.
///
+///
+/// The MASE calculation process:
+/// 1. Calculates mean absolute error of the forecast
+/// 2. Calculates mean absolute error of naive forecast (using previous value)
+/// 3. Divides forecast error by naive forecast error
+///
+/// Key characteristics:
+/// - Scale-free (independent of data scale)
+/// - Handles zero values unlike percentage errors
+/// - Symmetric (treats over/under predictions equally)
+/// - Easy interpretation (MASE < 1 means better than naive forecast)
+/// - Robust to outliers
+///
+/// Formula:
+/// MASE = MAE(forecast) / MAE(naive_forecast)
+/// where naive_forecast[t] = actual[t-1]
+///
+/// Sources:
+/// Rob J. Hyndman - "Another Look at Forecast-Accuracy Metrics for Intermittent Demand"
+/// https://robjhyndman.com/papers/another-look-at-measures-of-forecast-accuracy/
+///
+
public class Mase : AbstractBase
{
private readonly CircularBuffer _actualBuffer;
private readonly CircularBuffer _predictedBuffer;
private readonly CircularBuffer _naiveBuffer;
- ///
- /// Initializes a new instance of the Mase class.
- ///
- /// The period for MASE calculation.
+ /// The number of points over which to calculate the MASE.
/// Thrown when period is less than 1.
public Mase(int period)
{
@@ -30,20 +51,14 @@ public class Mase : AbstractBase
Init();
}
- ///
- /// Initializes a new instance of the Mase class with a source object.
- ///
- /// The source object for event subscription.
- /// The period for MASE calculation.
+ /// The data source object that publishes updates.
+ /// The number of points over which to calculate the MASE.
public Mase(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
- ///
- /// Initializes the Mase instance.
- ///
public override void Init()
{
base.Init();
@@ -52,10 +67,6 @@ public class Mase : AbstractBase
_naiveBuffer.Clear();
}
- ///
- /// Manages the state of the Mase instance.
- ///
- /// Indicates if the input is new.
protected override void ManageState(bool isNew)
{
if (isNew)
@@ -65,10 +76,6 @@ public class Mase : AbstractBase
}
}
- ///
- /// Performs the MASE calculation.
- ///
- /// The calculated MASE value.
protected override double Calculation()
{
ManageState(Input.IsNew);
@@ -76,9 +83,11 @@ public class Mase : AbstractBase
double actual = Input.Value;
_actualBuffer.Add(actual, Input.IsNew);
+ // If no predicted value provided, use mean of actual values
double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value;
_predictedBuffer.Add(predicted, Input.IsNew);
+ // Naive forecast uses previous actual value
if (_actualBuffer.Count > 1)
{
_naiveBuffer.Add(_actualBuffer.GetSpan()[^2], Input.IsNew);
@@ -90,6 +99,10 @@ public class Mase : AbstractBase
return mase;
}
+ ///
+ /// Calculates the MASE value by comparing forecast error to naive forecast error.
+ ///
+ /// The calculated MASE value, or positive infinity if naive error is zero.
private double CalculateMase()
{
if (_actualBuffer.Count <= 1) return 0;
@@ -104,6 +117,9 @@ public class Mase : AbstractBase
return _naiveForecastError != 0 ? (sumAbsoluteError / _actualBuffer.Count) / _naiveForecastError : double.PositiveInfinity;
}
+ ///
+ /// Calculates the sum of absolute errors between actual and predicted values.
+ ///
private static double CalculateSumAbsoluteError(ReadOnlySpan actualValues, ReadOnlySpan predictedValues)
{
double sum = 0;
@@ -114,6 +130,9 @@ public class Mase : AbstractBase
return sum;
}
+ ///
+ /// Calculates the naive forecast error using the previous value as prediction.
+ ///
private static double CalculateNaiveForecastError(ReadOnlySpan actualValues, ReadOnlySpan naiveValues)
{
double sum = 0;
diff --git a/lib/errors/Mda.cs b/lib/errors/Mda.cs
index ba03bced..332337b8 100644
--- a/lib/errors/Mda.cs
+++ b/lib/errors/Mda.cs
@@ -1,10 +1,42 @@
+using System;
namespace QuanTAlib;
+///
+/// MDA: Mean Directional Accuracy
+/// A metric that measures how well a forecast predicts the direction of change
+/// rather than the magnitude. MDA focuses on whether the predicted movement
+/// (up or down) matches the actual movement.
+///
+///
+/// The MDA calculation process:
+/// 1. For each consecutive pair of points:
+/// - Calculate direction of actual change
+/// - Calculate direction of predicted change
+/// - Compare directions (match = 1, mismatch = 0)
+/// 2. Average the directional matches
+///
+/// Key characteristics:
+/// - Scale-independent (only considers direction)
+/// - Range is 0 to 1 (easy interpretation)
+/// - Useful for trend prediction evaluation
+/// - Ignores magnitude of changes
+/// - Equal weight to all directional changes
+///
+/// Formula:
+/// MDA = (1/(n-1)) * Σ(sign(actual[t] - actual[t-1]) == sign(pred[t] - pred[t-1]))
+///
+/// Sources:
+/// https://www.sciencedirect.com/science/article/abs/pii/S0169207016000121
+/// "Evaluating Forecasting Performance" - International Journal of Forecasting
+///
+
public class Mda : AbstractBase
{
private readonly CircularBuffer _actualBuffer;
private readonly CircularBuffer _predictedBuffer;
+ /// The number of points over which to calculate the MDA.
+ /// Thrown when period is less than 1.
public Mda(int period)
{
if (period < 1)
@@ -18,6 +50,8 @@ public class Mda : AbstractBase
Init();
}
+ /// The data source object that publishes updates.
+ /// The number of points over which to calculate the MDA.
public Mda(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
@@ -47,6 +81,7 @@ public class Mda : AbstractBase
double actual = Input.Value;
_actualBuffer.Add(actual, Input.IsNew);
+ // If no predicted value provided, use mean of actual values
double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value;
_predictedBuffer.Add(predicted, Input.IsNew);
diff --git a/lib/errors/Me.cs b/lib/errors/Me.cs
index a9118b39..ab75705a 100644
--- a/lib/errors/Me.cs
+++ b/lib/errors/Me.cs
@@ -1,10 +1,42 @@
+using System;
namespace QuanTAlib;
+///
+/// ME: Mean Error
+/// A basic error metric that measures the average difference between actual and
+/// predicted values. Unlike MAE, it allows positive and negative errors to cancel
+/// out, making it useful for detecting systematic bias in predictions.
+///
+///
+/// The ME calculation process:
+/// 1. Calculates error (actual - predicted) for each point
+/// 2. Sums all errors (allowing cancellation)
+/// 3. Divides by the number of observations
+///
+/// Key characteristics:
+/// - Same units as input data
+/// - Can detect systematic bias
+/// - Positive ME indicates underprediction
+/// - Negative ME indicates overprediction
+/// - Errors can cancel out
+///
+/// Formula:
+/// ME = (1/n) * Σ(actual - predicted)
+///
+/// Sources:
+/// https://en.wikipedia.org/wiki/Mean_signed_difference
+/// https://www.statisticshowto.com/mean-error/
+///
+/// Note: Also known as Mean Bias Error (MBE) or Mean Signed Difference (MSD)
+///
+
public class Me : AbstractBase
{
private readonly CircularBuffer _actualBuffer;
private readonly CircularBuffer _predictedBuffer;
+ /// The number of points over which to calculate the ME.
+ /// Thrown when period is less than 1.
public Me(int period)
{
if (period < 1)
@@ -18,6 +50,8 @@ public class Me : AbstractBase
Init();
}
+ /// The data source object that publishes updates.
+ /// The number of points over which to calculate the ME.
public Me(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
@@ -47,6 +81,7 @@ public class Me : AbstractBase
double actual = Input.Value;
_actualBuffer.Add(actual, Input.IsNew);
+ // If no predicted value provided, use mean of actual values
double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value;
_predictedBuffer.Add(predicted, Input.IsNew);
diff --git a/lib/errors/Mpe.cs b/lib/errors/Mpe.cs
index c9fc3a88..cf18b88d 100644
--- a/lib/errors/Mpe.cs
+++ b/lib/errors/Mpe.cs
@@ -1,10 +1,43 @@
+using System;
namespace QuanTAlib;
+///
+/// MPE: Mean Percentage Error
+/// A percentage-based error metric that measures the average percentage difference
+/// between actual and predicted values. Like ME, it allows positive and negative
+/// errors to cancel out, but expresses the bias in percentage terms.
+///
+///
+/// The MPE calculation process:
+/// 1. Calculates percentage error for each point
+/// 2. Sums all percentage errors (allowing cancellation)
+/// 3. Divides by the number of observations
+///
+/// Key characteristics:
+/// - Scale-independent (percentage-based)
+/// - Can detect systematic bias
+/// - Positive MPE indicates underprediction
+/// - Negative MPE indicates overprediction
+/// - Cannot handle zero actual values
+/// - Errors can cancel out
+///
+/// Formula:
+/// MPE = (1/n) * Σ((actual - predicted) / actual) * 100%
+///
+/// Sources:
+/// https://en.wikipedia.org/wiki/Mean_percentage_error
+/// https://www.statisticshowto.com/mean-percentage-error/
+///
+/// Note: Similar to MAPE but allows error cancellation
+///
+
public class Mpe : AbstractBase
{
private readonly CircularBuffer _actualBuffer;
private readonly CircularBuffer _predictedBuffer;
+ /// The number of points over which to calculate the MPE.
+ /// Thrown when period is less than 1.
public Mpe(int period)
{
if (period < 1)
@@ -18,6 +51,8 @@ public class Mpe : AbstractBase
Init();
}
+ /// The data source object that publishes updates.
+ /// The number of points over which to calculate the MPE.
public Mpe(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
@@ -47,6 +82,7 @@ public class Mpe : AbstractBase
double actual = Input.Value;
_actualBuffer.Add(actual, Input.IsNew);
+ // If no predicted value provided, use mean of actual values
double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value;
_predictedBuffer.Add(predicted, Input.IsNew);
diff --git a/lib/errors/Mse.cs b/lib/errors/Mse.cs
index 7a3e33b2..2c8fa2f6 100644
--- a/lib/errors/Mse.cs
+++ b/lib/errors/Mse.cs
@@ -1,25 +1,42 @@
+using System;
namespace QuanTAlib;
///
-/// Represents a Mean Squared Error calculator that measures the average of the squares
-/// of the differences between actual values and predicted values.
+/// MSE: Mean Squared Error
+/// A fundamental error metric that measures the average of squared differences
+/// between predicted and actual values. MSE heavily penalizes large errors due
+/// to the squaring operation.
///
///
-/// The Mse class calculates the Mean Squared Error using a circular buffer
-/// to efficiently manage the data points within the specified period.
+/// The MSE calculation process:
+/// 1. Calculates error (actual - predicted) for each point
+/// 2. Squares each error value
+/// 3. Averages the squared errors
+///
+/// Key characteristics:
+/// - Heavily penalizes large errors
+/// - Always non-negative
+/// - Units are squared (harder to interpret)
+/// - More sensitive to outliers than MAE
+/// - Differentiable (useful for optimization)
+///
+/// Formula:
+/// MSE = (1/n) * Σ(actual - predicted)²
+///
+/// Sources:
+/// https://en.wikipedia.org/wiki/Mean_squared_error
+/// https://www.statisticshowto.com/probability-and-statistics/statistics-definitions/mean-squared-error/
+///
+/// Note: Often used in optimization due to its mathematical properties
///
+
public class Mse : AbstractBase
{
private readonly CircularBuffer _actualBuffer;
private readonly CircularBuffer _predictedBuffer;
- ///
- /// Initializes a new instance of the Mse class with the specified period.
- ///
- /// The period over which to calculate the Mean Squared Error.
- ///
- /// Thrown when period is less than 1.
- ///
+ /// The number of points over which to calculate the MSE.
+ /// Thrown when period is less than 1.
public Mse(int period)
{
if (period < 1)
@@ -33,20 +50,14 @@ public class Mse : AbstractBase
Init();
}
- ///
- /// Initializes a new instance of the Mse class with the specified source and period.
- ///
- /// The source object to subscribe to for value updates.
- /// The period over which to calculate the Mean Squared Error.
+ /// The data source object that publishes updates.
+ /// The number of points over which to calculate the MSE.
public Mse(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
- ///
- /// Initializes the Mse instance by clearing the buffers.
- ///
public override void Init()
{
base.Init();
@@ -54,10 +65,6 @@ public class Mse : AbstractBase
_predictedBuffer.Clear();
}
- ///
- /// Manages the state of the Mse instance based on whether new values are being processed.
- ///
- /// Indicates whether the current inputs are new values.
protected override void ManageState(bool isNew)
{
if (isNew)
@@ -67,17 +74,6 @@ public class Mse : AbstractBase
}
}
- ///
- /// Performs the Mean Squared Error calculation for the current period.
- ///
- ///
- /// The calculated Mean Squared Error value for the current period.
- ///
- ///
- /// This method calculates the Mean Squared Error using the formula:
- /// MSE = sum((actual - predicted)^2) / n
- /// where actual is each actual value, predicted is each predicted value, and n is the number of values.
- ///
protected override double Calculation()
{
ManageState(Input.IsNew);
@@ -85,6 +81,7 @@ public class Mse : AbstractBase
double actual = Input.Value;
_actualBuffer.Add(actual, Input.IsNew);
+ // If no predicted value provided, use mean of actual values
double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value;
_predictedBuffer.Add(predicted, Input.IsNew);
diff --git a/lib/errors/Msle.cs b/lib/errors/Msle.cs
index 464f4e10..27da073b 100644
--- a/lib/errors/Msle.cs
+++ b/lib/errors/Msle.cs
@@ -1,10 +1,43 @@
+using System;
namespace QuanTAlib;
+///
+/// MSLE: Mean Squared Logarithmic Error
+/// A variation of MSE that operates on log-transformed values. MSLE is particularly
+/// useful for data with exponential growth or when errors in larger values should
+/// not be penalized more heavily than errors in smaller values.
+///
+///
+/// The MSLE calculation process:
+/// 1. Adds 1 to both actual and predicted values (to handle zeros)
+/// 2. Takes natural log of both values
+/// 3. Calculates squared difference of logs
+/// 4. Averages the squared differences
+///
+/// Key characteristics:
+/// - Scale-independent due to log transformation
+/// - Penalizes underestimates more than overestimates
+/// - Handles exponential trends well
+/// - More sensitive to relative differences
+/// - Can handle zero values (adds 1 before log)
+///
+/// Formula:
+/// MSLE = (1/n) * Σ(log(actual + 1) - log(predicted + 1))²
+///
+/// Sources:
+/// https://scikit-learn.org/stable/modules/model_evaluation.html#mean-squared-logarithmic-error
+/// https://medium.com/analytics-vidhya/root-mean-square-log-error-rmse-vs-rmlse-935c6cc1802a
+///
+/// Note: Often used in cases where target values follow exponential growth
+///
+
public class Msle : AbstractBase
{
private readonly CircularBuffer _actualBuffer;
private readonly CircularBuffer _predictedBuffer;
+ /// The number of points over which to calculate the MSLE.
+ /// Thrown when period is less than 1.
public Msle(int period)
{
if (period < 1)
@@ -18,6 +51,8 @@ public class Msle : AbstractBase
Init();
}
+ /// The data source object that publishes updates.
+ /// The number of points over which to calculate the MSLE.
public Msle(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
@@ -47,6 +82,7 @@ public class Msle : AbstractBase
double actual = Input.Value;
_actualBuffer.Add(actual, Input.IsNew);
+ // If no predicted value provided, use mean of actual values
double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value;
_predictedBuffer.Add(predicted, Input.IsNew);
diff --git a/lib/errors/Rae.cs b/lib/errors/Rae.cs
index 6b25a161..a9567a02 100644
--- a/lib/errors/Rae.cs
+++ b/lib/errors/Rae.cs
@@ -1,10 +1,42 @@
+using System;
namespace QuanTAlib;
+///
+/// RAE: Relative Absolute Error
+/// A normalized error metric that compares the total absolute error to the total
+/// magnitude of actual values. RAE provides a scale-independent measure of error
+/// that is robust to the overall magnitude of the data.
+///
+///
+/// The RAE calculation process:
+/// 1. Calculates sum of absolute errors
+/// 2. Calculates sum of absolute actual values
+/// 3. Divides total error by total actual magnitude
+///
+/// Key characteristics:
+/// - Scale-independent (normalized by actual values)
+/// - Range typically between 0 and 1
+/// - Easy to interpret (0 is perfect, 1 means error equals data magnitude)
+/// - Robust to data scale changes
+/// - Less sensitive to outliers than squared errors
+///
+/// Formula:
+/// RAE = Σ|actual - predicted| / Σ|actual|
+///
+/// Sources:
+/// https://en.wikipedia.org/wiki/Relative_absolute_error
+/// https://www.sciencedirect.com/topics/engineering/relative-absolute-error
+///
+/// Note: Values greater than 1 indicate predictions worse than using zero
+///
+
public class Rae : AbstractBase
{
private readonly CircularBuffer _actualBuffer;
private readonly CircularBuffer _predictedBuffer;
+ /// The number of points over which to calculate the RAE.
+ /// Thrown when period is less than 1.
public Rae(int period)
{
if (period < 1)
@@ -18,6 +50,8 @@ public class Rae : AbstractBase
Init();
}
+ /// The data source object that publishes updates.
+ /// The number of points over which to calculate the RAE.
public Rae(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
@@ -47,6 +81,7 @@ public class Rae : AbstractBase
double actual = Input.Value;
_actualBuffer.Add(actual, Input.IsNew);
+ // If no predicted value provided, use mean of actual values
double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value;
_predictedBuffer.Add(predicted, Input.IsNew);
diff --git a/lib/errors/Rmse.cs b/lib/errors/Rmse.cs
index ba56b346..1474fd12 100644
--- a/lib/errors/Rmse.cs
+++ b/lib/errors/Rmse.cs
@@ -1,10 +1,43 @@
+using System;
namespace QuanTAlib;
+///
+/// RMSE: Root Mean Square Error
+/// A widely used error metric that measures the square root of the average squared
+/// differences between predicted and actual values. RMSE provides error measurements
+/// in the same units as the original data.
+///
+///
+/// The RMSE calculation process:
+/// 1. Calculates error (actual - predicted) for each point
+/// 2. Squares each error value
+/// 3. Averages the squared errors
+/// 4. Takes the square root of the average
+///
+/// Key characteristics:
+/// - Same units as input data (unlike MSE)
+/// - Penalizes large errors more than small ones
+/// - Always non-negative
+/// - More interpretable than MSE
+/// - Commonly used in regression problems
+///
+/// Formula:
+/// RMSE = √((1/n) * Σ(actual - predicted)²)
+///
+/// Sources:
+/// https://en.wikipedia.org/wiki/Root-mean-square_deviation
+/// https://www.statisticshowto.com/probability-and-statistics/regression-analysis/rmse-root-mean-square-error/
+///
+/// Note: Square root of MSE, making it more interpretable in original units
+///
+
public class Rmse : AbstractBase
{
private readonly CircularBuffer _actualBuffer;
private readonly CircularBuffer _predictedBuffer;
+ /// The number of points over which to calculate the RMSE.
+ /// Thrown when period is less than 1.
public Rmse(int period)
{
if (period < 1)
@@ -18,6 +51,8 @@ public class Rmse : AbstractBase
Init();
}
+ /// The data source object that publishes updates.
+ /// The number of points over which to calculate the RMSE.
public Rmse(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
@@ -47,6 +82,7 @@ public class Rmse : AbstractBase
double actual = Input.Value;
_actualBuffer.Add(actual, Input.IsNew);
+ // If no predicted value provided, use mean of actual values
double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value;
_predictedBuffer.Add(predicted, Input.IsNew);
diff --git a/lib/errors/Rmsle.cs b/lib/errors/Rmsle.cs
index cfeb4e35..846e8a65 100644
--- a/lib/errors/Rmsle.cs
+++ b/lib/errors/Rmsle.cs
@@ -1,10 +1,44 @@
+using System;
namespace QuanTAlib;
+///
+/// RMSLE: Root Mean Square Logarithmic Error
+/// A variation of RMSE that operates on log-transformed values. RMSLE is particularly
+/// useful for data with exponential growth or when relative errors in larger values
+/// should be treated similarly to relative errors in smaller values.
+///
+///
+/// The RMSLE calculation process:
+/// 1. Adds 1 to both actual and predicted values (to handle zeros)
+/// 2. Takes natural log of both values
+/// 3. Calculates squared difference of logs
+/// 4. Averages the squared differences
+/// 5. Takes the square root
+///
+/// Key characteristics:
+/// - Scale-independent due to log transformation
+/// - Penalizes underestimates more than overestimates
+/// - Handles exponential trends well
+/// - More sensitive to relative differences
+/// - Can handle zero values (adds 1 before log)
+///
+/// Formula:
+/// RMSLE = √((1/n) * Σ(log(actual + 1) - log(predicted + 1))²)
+///
+/// Sources:
+/// https://www.kaggle.com/wiki/RootMeanSquaredLogarithmicError
+/// https://medium.com/analytics-vidhya/root-mean-square-log-error-rmse-vs-rmlse-935c6cc1802a
+///
+/// Note: Square root of MSLE, useful for data with exponential growth
+///
+
public class Rmsle : AbstractBase
{
private readonly CircularBuffer _actualBuffer;
private readonly CircularBuffer _predictedBuffer;
+ /// The number of points over which to calculate the RMSLE.
+ /// Thrown when period is less than 1.
public Rmsle(int period)
{
if (period < 1)
@@ -18,6 +52,8 @@ public class Rmsle : AbstractBase
Init();
}
+ /// The data source object that publishes updates.
+ /// The number of points over which to calculate the RMSLE.
public Rmsle(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
@@ -47,6 +83,7 @@ public class Rmsle : AbstractBase
double actual = Input.Value;
_actualBuffer.Add(actual, Input.IsNew);
+ // If no predicted value provided, use mean of actual values
double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value;
_predictedBuffer.Add(predicted, Input.IsNew);
diff --git a/lib/errors/Rse.cs b/lib/errors/Rse.cs
index dc26bc95..0074cd1b 100644
--- a/lib/errors/Rse.cs
+++ b/lib/errors/Rse.cs
@@ -1,10 +1,43 @@
+using System;
+using System.Linq;
namespace QuanTAlib;
+///
+/// RSE: Relative Squared Error
+/// A normalized error metric that compares the squared error of predictions to
+/// the variance of actual values. RSE provides a scale-independent measure of
+/// prediction accuracy relative to the inherent variability in the data.
+///
+///
+/// The RSE calculation process:
+/// 1. Calculates sum of squared prediction errors
+/// 2. Calculates sum of squared deviations from mean (variance)
+/// 3. Divides squared error by variance and takes square root
+///
+/// Key characteristics:
+/// - Scale-independent (normalized by data variance)
+/// - Range typically between 0 and 1
+/// - Easy interpretation relative to data variance
+/// - Penalizes large errors more than small ones
+/// - Accounts for data variability
+///
+/// Formula:
+/// RSE = √(Σ(actual - predicted)² / Σ(actual - mean(actual))²)
+///
+/// Sources:
+/// https://en.wikipedia.org/wiki/Relative_squared_error
+/// https://www.sciencedirect.com/topics/engineering/relative-squared-error
+///
+/// Note: Values less than 1 indicate predictions better than using mean
+///
+
public class Rse : AbstractBase
{
private readonly CircularBuffer _actualBuffer;
private readonly CircularBuffer _predictedBuffer;
+ /// The number of points over which to calculate the RSE.
+ /// Thrown when period is less than 1.
public Rse(int period)
{
if (period < 1)
@@ -18,6 +51,8 @@ public class Rse : AbstractBase
Init();
}
+ /// The data source object that publishes updates.
+ /// The number of points over which to calculate the RSE.
public Rse(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
@@ -47,6 +82,7 @@ public class Rse : AbstractBase
double actual = Input.Value;
_actualBuffer.Add(actual, Input.IsNew);
+ // If no predicted value provided, use mean of actual values
double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value;
_predictedBuffer.Add(predicted, Input.IsNew);
diff --git a/lib/errors/Rsquared.cs b/lib/errors/Rsquared.cs
index e6213cb1..c7994def 100644
--- a/lib/errors/Rsquared.cs
+++ b/lib/errors/Rsquared.cs
@@ -1,10 +1,43 @@
+using System;
+using System.Linq;
namespace QuanTAlib;
+///
+/// R-squared: Coefficient of Determination
+/// A statistical measure that represents the proportion of variance in the dependent
+/// variable that is predictable from the independent variable. R-squared provides
+/// a measure of how well the predictions approximate the actual data.
+///
+///
+/// The R-squared calculation process:
+/// 1. Calculates total sum of squares (variance from mean)
+/// 2. Calculates residual sum of squares (prediction errors)
+/// 3. Computes 1 - (residual SS / total SS)
+///
+/// Key characteristics:
+/// - Range is typically 0 to 1
+/// - 1 indicates perfect prediction
+/// - 0 indicates prediction no better than mean
+/// - Scale-independent
+/// - Widely used in regression analysis
+///
+/// Formula:
+/// R² = 1 - (Σ(actual - predicted)² / Σ(actual - mean(actual))²)
+///
+/// Sources:
+/// https://en.wikipedia.org/wiki/Coefficient_of_determination
+/// https://www.statisticshowto.com/probability-and-statistics/coefficient-of-determination-r-squared/
+///
+/// Note: Can be negative if predictions are worse than using the mean
+///
+
public class Rsquared : AbstractBase
{
private readonly CircularBuffer _actualBuffer;
private readonly CircularBuffer _predictedBuffer;
+ /// The number of points over which to calculate the R-squared value.
+ /// Thrown when period is less than 1.
public Rsquared(int period)
{
if (period < 1)
@@ -18,6 +51,8 @@ public class Rsquared : AbstractBase
Init();
}
+ /// The data source object that publishes updates.
+ /// The number of points over which to calculate the R-squared value.
public Rsquared(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
@@ -47,6 +82,7 @@ public class Rsquared : AbstractBase
double actual = Input.Value;
_actualBuffer.Add(actual, Input.IsNew);
+ // If no predicted value provided, use mean of actual values
double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value;
_predictedBuffer.Add(predicted, Input.IsNew);
diff --git a/lib/errors/Smape.cs b/lib/errors/Smape.cs
index 869ac820..73b47bb5 100644
--- a/lib/errors/Smape.cs
+++ b/lib/errors/Smape.cs
@@ -1,10 +1,42 @@
+using System;
namespace QuanTAlib;
+///
+/// SMAPE: Symmetric Mean Absolute Percentage Error
+/// A variation of MAPE that treats positive and negative errors symmetrically.
+/// SMAPE uses the average of actual and predicted values in the denominator,
+/// making it more robust than MAPE for values close to zero.
+///
+///
+/// The SMAPE calculation process:
+/// 1. Calculates absolute difference between actual and predicted
+/// 2. Divides by sum of absolute actual and predicted values
+/// 3. Averages these ratios and multiplies by 200%
+///
+/// Key characteristics:
+/// - Symmetric treatment of errors
+/// - Range is 0% to 200%
+/// - More robust than MAPE near zero
+/// - Scale-independent
+/// - Handles both positive and negative values
+///
+/// Formula:
+/// SMAPE = (200/n) * Σ|actual - predicted| / (|actual| + |predicted|)
+///
+/// Sources:
+/// https://en.wikipedia.org/wiki/Symmetric_mean_absolute_percentage_error
+/// https://www.sciencedirect.com/science/article/abs/pii/0169207085900059
+///
+/// Note: More stable than MAPE when actual values are close to zero
+///
+
public class Smape : AbstractBase
{
private readonly CircularBuffer _actualBuffer;
private readonly CircularBuffer _predictedBuffer;
+ /// The number of points over which to calculate the SMAPE.
+ /// Thrown when period is less than 1.
public Smape(int period)
{
if (period < 1)
@@ -18,6 +50,8 @@ public class Smape : AbstractBase
Init();
}
+ /// The data source object that publishes updates.
+ /// The number of points over which to calculate the SMAPE.
public Smape(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
@@ -47,6 +81,7 @@ public class Smape : AbstractBase
double actual = Input.Value;
_actualBuffer.Add(actual, Input.IsNew);
+ // If no predicted value provided, use mean of actual values
double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value;
_predictedBuffer.Add(predicted, Input.IsNew);
diff --git a/lib/oscillators/Cmo.cs b/lib/oscillators/Cmo.cs
index db0c545c..a303f67e 100644
--- a/lib/oscillators/Cmo.cs
+++ b/lib/oscillators/Cmo.cs
@@ -1,14 +1,47 @@
+using System;
namespace QuanTAlib;
///
-/// Represents a Chande Momentum Oscillator (CMO) calculator.
+/// CMO: Chande Momentum Oscillator
+/// A technical momentum indicator that measures the difference between upward and
+/// downward momentum. CMO helps identify overbought and oversold conditions, as
+/// well as trend strength and potential reversals.
///
+///
+/// The CMO calculation process:
+/// 1. Calculates price differences from previous period
+/// 2. Separates positive (upward) and negative (downward) movements
+/// 3. Sums upward and downward movements over period
+/// 4. Calculates: 100 * ((sumUp - sumDown) / (sumUp + sumDown))
+///
+/// Key characteristics:
+/// - Oscillates between -100 and +100
+/// - Values above +50 indicate overbought
+/// - Values below -50 indicate oversold
+/// - Zero line crossovers signal trend changes
+/// - High absolute values suggest strong trends
+///
+/// Formula:
+/// CMO = 100 * ((ΣUp - ΣDown) / (ΣUp + ΣDown))
+/// where:
+/// Up = positive price changes
+/// Down = absolute negative price changes
+///
+/// Sources:
+/// Tushar Chande - "The New Technical Trader" (1994)
+/// https://www.investopedia.com/terms/c/chandemomentumoscillator.asp
+///
+/// Note: Similar to RSI but with different scaling and calculation method
+///
+
public class Cmo : AbstractBase
{
private readonly CircularBuffer _sumH;
private readonly CircularBuffer _sumL;
private double _prevValue, _p_prevValue;
+ /// The number of periods used in the CMO calculation.
+ /// Thrown when period is less than 1.
public Cmo(int period)
{
if (period < 1)
@@ -16,15 +49,12 @@ public class Cmo : AbstractBase
_sumH = new(period);
_sumL = new(period);
- WarmupPeriod = period+1;
+ WarmupPeriod = period + 1;
Name = $"CMO({period})";
}
- ///
- /// Initializes a new instance of the CMO class with a data source.
- ///
- /// The source object that publishes data.
- /// The number of data points to consider.
+ /// The data source object that publishes updates.
+ /// The number of periods used in the CMO calculation.
public Cmo(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
@@ -53,9 +83,11 @@ public class Cmo : AbstractBase
_prevValue = Input.Value;
}
+ // Calculate price difference
double diff = Input.Value - _prevValue;
_prevValue = Input.Value;
+ // Separate upward and downward movements
if (diff > 0)
{
_sumH.Add(diff, Input.IsNew);
@@ -67,11 +99,12 @@ public class Cmo : AbstractBase
_sumL.Add(-diff, Input.IsNew);
}
- // Calculate sums for the specified period only
+ // Calculate sums for the specified period
double sumH = _sumH.Sum();
double sumL = _sumL.Sum();
double divisor = sumH + sumL;
+ // Calculate CMO value
return (Math.Abs(divisor) > double.Epsilon) ?
100.0 * ((sumH - sumL) / divisor) :
0.0;
diff --git a/lib/oscillators/Rsi.cs b/lib/oscillators/Rsi.cs
index 358e4d03..5f61fe27 100644
--- a/lib/oscillators/Rsi.cs
+++ b/lib/oscillators/Rsi.cs
@@ -1,16 +1,48 @@
using System;
-
namespace QuanTAlib;
///
-/// Represents a Relative Strength Index (RSI) calculator following Wilder's algorithm.
+/// RSI: Relative Strength Index
+/// A momentum oscillator that measures the speed and magnitude of recent price
+/// changes to evaluate overbought or oversold conditions. RSI compares the
+/// magnitude of recent gains to recent losses.
///
+///
+/// The RSI calculation process:
+/// 1. Calculates price changes from previous period
+/// 2. Separates gains and losses
+/// 3. Calculates average gain and loss using Wilder's smoothing
+/// 4. Computes relative strength (avg gain / avg loss)
+/// 5. Normalizes to 0-100 scale: 100 - (100 / (1 + RS))
+///
+/// Key characteristics:
+/// - Oscillates between 0 and 100
+/// - Traditional overbought level at 70
+/// - Traditional oversold level at 30
+/// - Centerline (50) crossovers signal trend changes
+/// - Divergences suggest potential reversals
+///
+/// Formula:
+/// RSI = 100 - (100 / (1 + RS))
+/// where:
+/// RS = Average Gain / Average Loss
+/// Average Gain/Loss = Wilder's smoothed average over period
+///
+/// Sources:
+/// J. Welles Wilder Jr. - "New Concepts in Technical Trading Systems" (1978)
+/// https://www.investopedia.com/terms/r/rsi.asp
+///
+/// Note: Default period of 14 was recommended by Wilder
+///
+
public class Rsi : AbstractBase
{
private readonly Rma _avgGain;
private readonly Rma _avgLoss;
private double _prevValue, _p_prevValue;
+ /// The number of periods used in the RSI calculation (default 14).
+ /// Thrown when period is less than 1.
public Rsi(int period = 14)
{
if (period < 1)
@@ -22,11 +54,8 @@ public class Rsi : AbstractBase
Name = $"RSI({period})";
}
- ///
- /// Initializes a new instance of the RSI class with a data source.
- ///
- /// The source object that publishes data.
- /// The number of data points to consider.
+ /// The data source object that publishes updates.
+ /// The number of periods used in the RSI calculation.
public Rsi(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
@@ -55,14 +84,17 @@ public class Rsi : AbstractBase
_prevValue = Input.Value;
}
+ // Calculate price change and separate gains/losses
double change = Input.Value - _prevValue;
double gain = Math.Max(change, 0);
double loss = Math.Max(-change, 0);
_prevValue = Input.Value;
+ // Calculate smoothed averages using Wilder's method
_avgGain.Calc(gain, IsNew: Input.IsNew);
_avgLoss.Calc(loss, IsNew: Input.IsNew);
+ // Calculate RSI
double rsi = (_avgLoss.Value > 0) ? 100 - (100 / (1 + (_avgGain.Value / _avgLoss.Value))) : 100;
return rsi;
diff --git a/lib/oscillators/Rsx.cs b/lib/oscillators/Rsx.cs
index b2d365c6..6401c244 100644
--- a/lib/oscillators/Rsx.cs
+++ b/lib/oscillators/Rsx.cs
@@ -1,10 +1,39 @@
using System;
-
namespace QuanTAlib;
///
-/// Jurik's superior replacement for RSI
+/// RSX: Relative Strength eXtended
+/// An enhanced version of RSI developed by Mark Jurik that applies JMA (Jurik Moving
+/// Average) smoothing to the RSI calculation. RSX provides smoother signals with
+/// less noise while maintaining responsiveness to significant price movements.
///
+///
+/// The RSX calculation process:
+/// 1. Calculates traditional RSI values
+/// 2. Applies JMA smoothing to RSI output
+/// 3. Uses optimized parameters for noise reduction
+/// 4. Maintains RSI's 0-100 scale
+///
+/// Key characteristics:
+/// - Smoother than traditional RSI
+/// - Better noise reduction
+/// - Maintains responsiveness to significant moves
+/// - Same interpretation as RSI (0-100 scale)
+/// - Fewer false signals than RSI
+///
+/// Formula:
+/// RSX = JMA(RSI(price))
+/// where:
+/// RSI = standard Relative Strength Index
+/// JMA = Jurik Moving Average with optimized parameters
+///
+/// Sources:
+/// Mark Jurik - "The Jurik RSX"
+/// https://www.jurikresearch.com/
+///
+/// Note: Proprietary enhancement of RSI using JMA technology
+///
+
public class Rsx : AbstractBase
{
private readonly Rma _avgGain;
@@ -12,6 +41,10 @@ public class Rsx : AbstractBase
private readonly Jma _rsx;
private double _prevValue, _p_prevValue;
+ /// The number of periods for RSI calculation (default 14).
+ /// The phase parameter for JMA smoothing (default 0).
+ /// The factor parameter for smoothing control (default 0.55).
+ /// Thrown when period is less than 1.
public Rsx(int period = 14, int phase = 0, double factor = 0.55)
{
if (period < 1)
@@ -24,13 +57,10 @@ public class Rsx : AbstractBase
Name = $"RSX({period})";
}
- ///
- /// Initializes a new instance of the RSX class with a data source.
- ///
- /// The source object that publishes data.
- /// The number of data points to consider.
- /// The phase parameter.
- /// The factor parameter.
+ /// The data source object that publishes updates.
+ /// The number of periods for RSI calculation.
+ /// The phase parameter for JMA smoothing.
+ /// The factor parameter for smoothing control.
public Rsx(object source, int period, int phase = 0, double factor = 0.55) : this(period, phase, factor)
{
var pubEvent = source.GetType().GetEvent("Pub");
@@ -59,15 +89,18 @@ public class Rsx : AbstractBase
_prevValue = Input.Value;
}
+ // Calculate RSI components
double change = Input.Value - _prevValue;
double gain = Math.Max(change, 0);
double loss = Math.Max(-change, 0);
_prevValue = Input.Value;
+ // Calculate RSI
_avgGain.Calc(gain, IsNew: Input.IsNew);
_avgLoss.Calc(loss, IsNew: Input.IsNew);
-
double rsi = (_avgLoss.Value > 0) ? 100 - (100 / (1 + (_avgGain.Value / _avgLoss.Value))) : 100;
+
+ // Apply JMA smoothing
double rsx = _rsx.Calc(rsi, Input.IsNew);
return rsx;
diff --git a/lib/statistics/Curvature.cs b/lib/statistics/Curvature.cs
index 59330724..2d51c8d3 100644
--- a/lib/statistics/Curvature.cs
+++ b/lib/statistics/Curvature.cs
@@ -1,15 +1,41 @@
+using System;
+using System.Linq;
namespace QuanTAlib;
///
-/// Calculates the rate of change of the slope over a specified period.
-/// Provides insights into trend acceleration or deceleration.
+/// Curvature: Second Derivative Rate of Change
+/// A statistical measure that calculates the rate of change of the slope over time.
+/// Curvature provides insights into trend acceleration or deceleration by measuring
+/// how quickly the slope (first derivative) is changing.
///
///
-/// Curvature is a second-order derivative that measures how quickly the slope (first-order derivative) is changing.
-/// Positive curvature indicates accelerating uptrends or decelerating downtrends.
-/// Negative curvature indicates decelerating uptrends or accelerating downtrends.
-/// This indicator can be useful for identifying potential trend reversals or confirming trend strength.
+/// The Curvature calculation process:
+/// 1. Calculates slope values over the specified period
+/// 2. Applies least squares regression to slope values
+/// 3. Provides slope of slopes (curvature)
+/// 4. Includes additional statistical measures (R², StdDev)
+///
+/// Key characteristics:
+/// - Measures trend acceleration/deceleration
+/// - Positive values indicate accelerating uptrends or decelerating downtrends
+/// - Negative values indicate decelerating uptrends or accelerating downtrends
+/// - Helps identify potential trend reversals
+/// - Provides trend momentum information
+///
+/// Formula:
+/// Curvature = Σ((x - x̄)(y - ȳ)) / Σ((x - x̄)²)
+/// where:
+/// x = time points
+/// y = slope values
+/// x̄, ȳ = respective means
+///
+/// Sources:
+/// https://en.wikipedia.org/wiki/Curvature
+/// https://www.sciencedirect.com/topics/mathematics/curve-fitting
+///
+/// Note: Second-order derivative providing acceleration insights
///
+
public class Curvature : AbstractBase
{
private readonly int _period;
@@ -36,13 +62,8 @@ public class Curvature : AbstractBase
///
public double? Line { get; private set; }
- ///
- /// Initializes a new instance of the Curvature class.
- ///
- /// The number of data points to consider for calculation.
- ///
- /// Thrown when the period is 2 or less.
- ///
+ /// The number of points to consider for calculation.
+ /// Thrown when period is 2 or less.
public Curvature(int period)
{
if (period <= 2)
@@ -59,20 +80,14 @@ public class Curvature : AbstractBase
Init();
}
- ///
- /// Initializes a new instance of the Curvature class with a data source.
- ///
- /// The source object that publishes data.
- /// The number of data points to consider.
+ /// The data source object that publishes updates.
+ /// The number of points to consider for calculation.
public Curvature(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
- ///
- /// Resets the Curvature indicator to its initial state.
- ///
public override void Init()
{
base.Init();
@@ -83,10 +98,6 @@ public class Curvature : AbstractBase
Line = null;
}
- ///
- /// Manages the state of the indicator.
- ///
- /// Indicates if the current data point is new.
protected override void ManageState(bool isNew)
{
if (isNew)
@@ -96,16 +107,6 @@ public class Curvature : AbstractBase
}
}
- ///
- /// Performs the curvature calculation.
- ///
- ///
- /// The calculated curvature value. Positive for increasing slope, negative for decreasing.
- ///
- ///
- /// Uses least squares method for optimal calculation. Also computes additional statistics
- /// such as Intercept, Standard Deviation, R-Squared, and Line value.
- ///
protected override double Calculation()
{
ManageState(Input.IsNew);
diff --git a/lib/statistics/Entropy.cs b/lib/statistics/Entropy.cs
index 7803f7e4..f388b183 100644
--- a/lib/statistics/Entropy.cs
+++ b/lib/statistics/Entropy.cs
@@ -1,33 +1,53 @@
+using System;
+using System.Linq;
namespace QuanTAlib;
///
-/// Measures the unpredictability of data using Shannon's Entropy.
-/// Provides insights into the randomness or information content of the time series.
+/// Entropy: Information Content Measure
+/// A statistical measure that quantifies the unpredictability or randomness in
+/// a time series using Shannon's Entropy. Higher entropy indicates more randomness
+/// and uncertainty in the data.
///
///
-/// Shannon's Entropy quantifies the average amount of information contained in a message.
-/// In the context of time series analysis, it can be used to:
-/// - Detect regime changes or structural breaks in the data.
-/// - Assess the complexity or predictability of price movements.
-/// - Identify periods of high uncertainty or information flow in the market.
-/// The entropy value is normalized between 0 and 1, where 1 indicates maximum randomness
-/// and 0 indicates perfect predictability.
+/// The Entropy calculation process:
+/// 1. Groups values to calculate probabilities
+/// 2. Applies Shannon's entropy formula
+/// 3. Normalizes result to 0-1 range
+/// 4. Adjusts for number of unique values
+///
+/// Key characteristics:
+/// - Range from 0 (predictable) to 1 (random)
+/// - Measures information content
+/// - Detects regime changes
+/// - Identifies market uncertainty
+/// - Scale-independent measure
+///
+/// Formula:
+/// H = -Σ(p(x) * log₂(p(x))) / log₂(n)
+/// where:
+/// p(x) = probability of value x
+/// n = number of unique values
+///
+/// Applications:
+/// - Detect market regime changes
+/// - Assess price movement predictability
+/// - Identify periods of high uncertainty
+/// - Measure information flow in markets
+///
+/// Sources:
+/// Claude Shannon - "A Mathematical Theory of Communication" (1948)
+/// https://en.wikipedia.org/wiki/Entropy_(information_theory)
+///
+/// Note: Normalized to [0,1] for easier interpretation
///
+
public class Entropy : AbstractBase
{
- ///
- /// The number of data points to consider for the entropy calculation.
- ///
private readonly int Period;
private readonly CircularBuffer _buffer;
- ///
- /// Initializes a new instance of the Entropy class.
- ///
- /// The number of data points to consider for calculation.
- ///
- /// Thrown when the period is less than 2.
- ///
+ /// The number of points to consider for entropy calculation.
+ /// Thrown when period is less than 2.
public Entropy(int period)
{
if (period < 2)
@@ -42,30 +62,20 @@ public class Entropy : AbstractBase
Init();
}
- ///
- /// Initializes a new instance of the Entropy class with a data source.
- ///
- /// The source object that publishes data.
- /// The number of data points to consider.
+ /// The data source object that publishes updates.
+ /// The number of points to consider for entropy calculation.
public Entropy(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
- ///
- /// Resets the Entropy indicator to its initial state.
- ///
public override void Init()
{
base.Init();
_buffer.Clear();
}
- ///
- /// Manages the state of the indicator.
- ///
- /// Indicates if the current data point is new.
protected override void ManageState(bool isNew)
{
if (isNew)
@@ -75,17 +85,6 @@ public class Entropy : AbstractBase
}
}
- ///
- /// Performs the entropy calculation.
- ///
- ///
- /// The calculated entropy value, normalized between 0 and 1.
- /// 1 indicates maximum randomness, 0 indicates perfect predictability.
- ///
- ///
- /// Uses Shannon's Entropy formula and normalizes the result based on the
- /// number of unique values in the current period.
- ///
protected override double Calculation()
{
ManageState(Input.IsNew);
@@ -93,22 +92,22 @@ public class Entropy : AbstractBase
_buffer.Add(Input.Value, Input.IsNew);
double entropy = 0;
- if (_index > 1) // We need at least two data points for entropy calculation
+ if (_index > 1) // Need at least two data points for entropy calculation
{
var values = _buffer.GetSpan().ToArray();
int n = values.Length;
- // Calculate probabilities
+ // Calculate probabilities for each unique value
var groupedValues = values.GroupBy(x => x).Select(g => new { Value = g.Key, Count = g.Count() });
- // Use the actual count of values for probability calculation
+ // Calculate Shannon's entropy
foreach (var group in groupedValues)
{
double probability = (double)group.Count / n;
entropy -= probability * Math.Log2(probability);
}
- // Normalize the entropy based on the current number of unique values
+ // Normalize by maximum possible entropy for current unique values
int uniqueValueCount = groupedValues.Count();
double maxEntropy = Math.Log2(uniqueValueCount);
@@ -116,7 +115,7 @@ public class Entropy : AbstractBase
}
else
{
- entropy = 1; // Default to maximum entropy when insufficient data
+ entropy = 1; // Maximum entropy when insufficient data
}
IsHot = _buffer.Count >= Period;
diff --git a/lib/statistics/Kurtosis.cs b/lib/statistics/Kurtosis.cs
index cd32c92f..0166b65b 100644
--- a/lib/statistics/Kurtosis.cs
+++ b/lib/statistics/Kurtosis.cs
@@ -1,38 +1,54 @@
+using System;
+using System.Linq;
namespace QuanTAlib;
///
-/// Calculates excess kurtosis using the Sheskin Algorithm.
-/// Measures the "tailedness" of the probability distribution of a real-valued random variable.
+/// Kurtosis: Distribution Tail Weight Measure
+/// A statistical measure that quantifies the "tailedness" of a distribution using
+/// the Sheskin Algorithm. Kurtosis indicates whether data has heavy tails (more
+/// outliers) or light tails (fewer outliers) compared to a normal distribution.
///
///
-/// Kurtosis is a measure of the combined weight of a distribution's tails relative to the center of the distribution.
-/// In financial time series analysis, kurtosis can provide insights into:
-/// - The frequency and magnitude of extreme returns.
-/// - The potential for outliers or "black swan" events.
-/// - The shape of the return distribution compared to a normal distribution.
+/// The Kurtosis calculation process:
+/// 1. Calculates mean of the data
+/// 2. Computes squared and fourth power deviations
+/// 3. Applies Sheskin Algorithm for excess kurtosis
+/// 4. Adjusts for sample size bias
///
-/// Interpretation:
-/// - Excess kurtosis > 0: Heavy-tailed distribution (more extreme values than a normal distribution)
-/// - Excess kurtosis = 0: Normal distribution
-/// - Excess kurtosis < 0: Light-tailed distribution (fewer extreme values than a normal distribution)
+/// Key characteristics:
+/// - Measures tail weight relative to normal distribution
+/// - Positive values indicate heavy tails
+/// - Negative values indicate light tails
+/// - Zero indicates normal distribution
+/// - Sensitive to extreme values
///
-/// High kurtosis in financial returns may indicate a higher risk of extreme events.
+/// Formula:
+/// K = [n(n+1)Σ(x-μ)⁴] / [s⁴(n-1)(n-2)(n-3)] - [3(n-1)²]/[(n-2)(n-3)]
+/// where:
+/// n = sample size
+/// μ = mean
+/// s = standard deviation
+///
+/// Market Applications:
+/// - Identify potential for extreme moves
+/// - Assess risk of "black swan" events
+/// - Compare return distributions
+/// - Risk management tool
+///
+/// Sources:
+/// David J. Sheskin - "Handbook of Parametric and Nonparametric Statistical Procedures"
+/// https://en.wikipedia.org/wiki/Kurtosis
+///
+/// Note: Returns excess kurtosis (normal distribution = 0)
///
+
public class Kurtosis : AbstractBase
{
- ///
- /// The number of data points to consider for the kurtosis calculation.
- ///
private readonly int Period;
private readonly CircularBuffer _buffer;
- ///
- /// Initializes a new instance of the Kurtosis class.
- ///
- /// The number of data points to consider for calculation.
- ///
- /// Thrown when the period is less than 4.
- ///
+ /// The number of points to consider for kurtosis calculation.
+ /// Thrown when period is less than 4.
public Kurtosis(int period)
{
if (period < 4)
@@ -47,30 +63,20 @@ public class Kurtosis : AbstractBase
Init();
}
- ///
- /// Initializes a new instance of the Kurtosis class with a data source.
- ///
- /// The source object that publishes data.
- /// The number of data points to consider.
+ /// The data source object that publishes updates.
+ /// The number of points to consider for kurtosis calculation.
public Kurtosis(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
- ///
- /// Resets the Kurtosis indicator to its initial state.
- ///
public override void Init()
{
base.Init();
_buffer.Clear();
}
- ///
- /// Manages the state of the indicator.
- ///
- /// Indicates if the current data point is new.
protected override void ManageState(bool isNew)
{
if (isNew)
@@ -80,22 +86,6 @@ public class Kurtosis : AbstractBase
}
}
- ///
- /// Performs the kurtosis calculation.
- ///
- ///
- /// The calculated excess kurtosis. Positive for heavy-tailed distributions,
- /// negative for light-tailed distributions.
- ///
- ///
- /// Uses the Sheskin Algorithm for kurtosis calculation.
- /// Requires at least 4 data points for a valid calculation.
- ///
- /// Interpretation of results:
- /// - Positive values indicate a distribution with heavier tails and a higher peak compared to a normal distribution.
- /// - Negative values indicate a distribution with lighter tails and a lower peak compared to a normal distribution.
- /// - A value close to 0 suggests a distribution similar to a normal distribution in terms of tailedness.
- ///
protected override double Calculation()
{
ManageState(Input.IsNew);
@@ -103,14 +93,15 @@ public class Kurtosis : AbstractBase
_buffer.Add(Input.Value, Input.IsNew);
double kurtosis = 0;
- if (_buffer.Count > 3)
+ if (_buffer.Count > 3) // Need at least 4 points for valid calculation
{
var values = _buffer.GetSpan().ToArray();
double mean = values.Average();
double n = values.Length;
- double s2 = 0;
- double s4 = 0;
+ // Calculate squared and fourth power deviations
+ double s2 = 0; // Sum of squared deviations
+ double s4 = 0; // Sum of fourth power deviations
for (int i = 0; i < values.Length; i++)
{
@@ -121,7 +112,7 @@ public class Kurtosis : AbstractBase
double variance = s2 / (n - 1);
- // Sheskin Algorithm
+ // Sheskin Algorithm for excess kurtosis
kurtosis = (n * (n + 1) * s4) / (variance * variance * (n - 3) * (n - 1) * (n - 2))
- (3 * (n - 1) * (n - 1) / ((n - 2) * (n - 3)));
}
diff --git a/lib/statistics/Max.cs b/lib/statistics/Max.cs
index 2f110a03..13ae8ad1 100644
--- a/lib/statistics/Max.cs
+++ b/lib/statistics/Max.cs
@@ -1,63 +1,58 @@
+using System;
namespace QuanTAlib;
///
-/// Calculates the maximum value over a specified period, with an optional decay factor.
-/// Useful for tracking the highest point in a time series with the ability to gradually forget old peaks.
+/// MAX: Maximum Value with Decay
+/// A statistical measure that tracks the highest value over a specified period,
+/// with an optional decay factor to gradually reduce the influence of older peaks.
+/// This adaptive approach allows the indicator to respond to changing market conditions.
///
///
-/// The Max indicator is particularly useful in financial analysis for:
-/// - Identifying resistance levels in price charts.
-/// - Tracking the highest price over a given period.
-/// - Implementing trailing stop-loss strategies.
+/// The MAX calculation process:
+/// 1. Tracks highest value in current period
+/// 2. Applies exponential decay to old peaks
+/// 3. Adjusts decay based on time since last peak
+/// 4. Caps result at current period's maximum
///
-/// The decay factor allows the indicator to adapt to changing market conditions by
-/// gradually reducing the influence of older maximum values.
+/// Key characteristics:
+/// - Tracks absolute highest values
+/// - Optional decay for adaptivity
+/// - Maintains historical context
+/// - Smooth transitions with decay
+/// - Period-based windowing
+///
+/// Formula:
+/// decay = 1 - e^(-halfLife * timeSinceMax / period)
+/// max = max - decay * (max - periodAverage)
+/// max = min(max, periodMaximum)
+///
+/// Market Applications:
+/// - Identify resistance levels
+/// - Track price peaks
+/// - Implement trailing stops
+/// - Monitor price extremes
+/// - Adaptive trend following
+///
+/// Sources:
+/// Technical Analysis of Financial Markets
+/// https://www.investopedia.com/terms/r/resistance.asp
+///
+/// Note: Decay factor allows for adaptive peak tracking
///
+
public class Max : AbstractBase
{
- ///
- /// The number of data points to consider for the maximum calculation.
- ///
private readonly int Period;
-
- ///
- /// Circular buffer to store the most recent data points.
- ///
private readonly CircularBuffer _buffer;
-
- ///
- /// The half-life decay factor used to gradually forget old peaks.
- ///
private readonly double _halfLife;
-
- ///
- /// The current maximum value.
- ///
private double _currentMax;
-
- ///
- /// The previous maximum value.
- ///
private double _p_currentMax;
-
- ///
- /// The number of periods since a new maximum was set.
- ///
private int _timeSinceNewMax;
-
- ///
- /// The previous value of _timeSinceNewMax.
- ///
private int _p_timeSinceNewMax;
- ///
- /// Initializes a new instance of the Max class.
- ///
- /// The number of data points to consider. Must be at least 1.
- /// Half-life decay factor. Set to 0 for no decay, higher for faster forgetting of old peaks. Default is 0.
- ///
- /// Thrown when the period is less than 1 or decay is negative.
- ///
+ /// The number of points to consider for maximum calculation.
+ /// Half-life decay factor (0 for no decay, higher for faster forgetting).
+ /// Thrown when period is less than 1 or decay is negative.
public Max(int period, double decay = 0)
{
if (period < 1)
@@ -78,21 +73,15 @@ public class Max : AbstractBase
Init();
}
- ///
- /// Initializes a new instance of the Max class with a data source.
- ///
- /// The source object that publishes data.
- /// The number of data points to consider.
- /// Half-life decay factor. Default is 0.
+ /// The data source object that publishes updates.
+ /// The number of points to consider for maximum calculation.
+ /// Half-life decay factor (default 0).
public Max(object source, int period, double decay = 0) : this(period, decay)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
- ///
- /// Resets the Max indicator to its initial state.
- ///
public override void Init()
{
base.Init();
@@ -100,10 +89,6 @@ public class Max : AbstractBase
_timeSinceNewMax = 0;
}
- ///
- /// Manages the state of the indicator.
- ///
- /// Indicates if the current data point is new.
protected override void ManageState(bool isNew)
{
if (isNew)
@@ -121,29 +106,23 @@ public class Max : AbstractBase
}
}
- ///
- /// Performs the max calculation.
- ///
- ///
- /// The current maximum value, potentially adjusted by the decay factor.
- ///
- ///
- /// Uses a decay factor to gradually forget old peaks. The max value is always
- /// capped by the highest value in the current period.
- ///
protected override double Calculation()
{
ManageState(Input.IsNew);
_buffer.Add(Input.Value, Input.IsNew);
+ // Update maximum if new value is higher
if (Input.Value >= _currentMax)
{
_currentMax = Input.Value;
_timeSinceNewMax = 0;
}
+ // Apply decay based on time since last maximum
double decayRate = 1 - Math.Exp(-_halfLife * _timeSinceNewMax / Period);
_currentMax -= decayRate * (_currentMax - _buffer.Average());
+
+ // Ensure maximum doesn't exceed current period's highest value
_currentMax = Math.Min(_currentMax, _buffer.Max());
IsHot = true;
diff --git a/lib/statistics/Median.cs b/lib/statistics/Median.cs
index c999099e..fc203704 100644
--- a/lib/statistics/Median.cs
+++ b/lib/statistics/Median.cs
@@ -1,33 +1,52 @@
+using System;
+using System.Linq;
namespace QuanTAlib;
///
-/// Calculates the median value over a specified period.
-/// Provides a measure of central tendency that is robust to outliers.
+/// Median: Central Tendency Measure
+/// A robust statistical measure that finds the middle value in a sorted dataset.
+/// The median is less sensitive to outliers than the mean, making it particularly
+/// useful for analyzing price data with extreme values.
///
///
-/// The Median indicator is particularly useful in financial analysis for:
-/// - Providing a robust measure of central tendency that is less affected by extreme values than the mean.
-/// - Identifying the middle value in a dataset, which can be helpful in understanding price distributions.
-/// - Serving as a basis for other indicators or trading strategies that require a stable reference point.
+/// The Median calculation process:
+/// 1. Collects values over specified period
+/// 2. Sorts values in ascending order
+/// 3. Finds middle value(s)
+/// 4. Averages two middle values if even count
///
-/// Unlike the mean, the median is not influenced by extreme outliers, making it valuable
-/// in markets with occasional large price swings or in the presence of data anomalies.
+/// Key characteristics:
+/// - Robust to outliers
+/// - Always represents actual data point
+/// - Splits dataset in half
+/// - More stable than mean
+/// - Maintains data scale
+///
+/// Formula:
+/// For odd n: median = value at position (n+1)/2
+/// For even n: median = (value at n/2 + value at (n/2)+1) / 2
+///
+/// Market Applications:
+/// - Price distribution analysis
+/// - Trend identification
+/// - Outlier detection
+/// - Support/resistance levels
+/// - Filter extreme movements
+///
+/// Sources:
+/// https://en.wikipedia.org/wiki/Median
+/// "Statistics for Trading" - Technical Analysis of Financial Markets
+///
+/// Note: More robust than mean for non-normal distributions
///
+
public class Median : AbstractBase
{
- ///
- /// The number of data points to consider for the median calculation.
- ///
private readonly int Period;
private readonly CircularBuffer _buffer;
- ///
- /// Initializes a new instance of the Median class.
- ///
- /// The number of data points to consider. Must be at least 1.
- ///
- /// Thrown when the period is less than 1.
- ///
+ /// The number of points to consider for median calculation.
+ /// Thrown when period is less than 1.
public Median(int period)
{
if (period < 1)
@@ -42,30 +61,20 @@ public class Median : AbstractBase
Init();
}
- ///
- /// Initializes a new instance of the Median class with a data source.
- ///
- /// The source object that publishes data.
- /// The number of data points to consider.
+ /// The data source object that publishes updates.
+ /// The number of points to consider for median calculation.
public Median(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
- ///
- /// Resets the Median indicator to its initial state.
- ///
public override void Init()
{
base.Init();
_buffer.Clear();
}
- ///
- /// Manages the state of the indicator.
- ///
- /// Indicates if the current data point is new.
protected override void ManageState(bool isNew)
{
if (isNew)
@@ -75,16 +84,6 @@ public class Median : AbstractBase
}
}
- ///
- /// Performs the median calculation.
- ///
- ///
- /// The current median value of the dataset.
- ///
- ///
- /// Uses a sorting approach to find the median. If there's not enough data,
- /// it uses the average as a temporary measure.
- ///
protected override double Calculation()
{
ManageState(Input.IsNew);
@@ -93,11 +92,15 @@ public class Median : AbstractBase
double median;
if (_index >= Period)
{
+ // Get sorted copy of values
var sortedValues = _buffer.GetSpan().ToArray();
Array.Sort(sortedValues);
int middleIndex = sortedValues.Length / 2;
- median = (sortedValues.Length % 2 == 0) ? (sortedValues[middleIndex - 1] + sortedValues[middleIndex]) / 2.0 : sortedValues[middleIndex];
+ // Calculate median based on odd/even count
+ median = (sortedValues.Length % 2 == 0)
+ ? (sortedValues[middleIndex - 1] + sortedValues[middleIndex]) / 2.0
+ : sortedValues[middleIndex];
}
else
{
diff --git a/lib/statistics/Min.cs b/lib/statistics/Min.cs
index 5832bdc4..2ebede8c 100644
--- a/lib/statistics/Min.cs
+++ b/lib/statistics/Min.cs
@@ -1,63 +1,58 @@
+using System;
namespace QuanTAlib;
///
-/// Represents a minimum value calculator with optional decay over a specified period.
-/// This class calculates the minimum value within a given period, with the ability to
-/// apply a decay factor to give more weight to recent values.
+/// MIN: Minimum Value with Decay
+/// A statistical measure that tracks the lowest value over a specified period,
+/// with an optional decay factor to gradually reduce the influence of older lows.
+/// This adaptive approach allows the indicator to respond to changing market conditions.
///
///
-/// The Min class uses a circular buffer to store values and calculates the minimum
-/// efficiently. It also implements a decay mechanism to adjust the minimum value over
-/// time, allowing for a more responsive indicator in changing market conditions.
+/// The MIN calculation process:
+/// 1. Tracks lowest value in current period
+/// 2. Applies exponential decay to old lows
+/// 3. Adjusts decay based on time since last low
+/// 4. Caps result at current period's minimum
///
-/// The decay factor allows the indicator to "forget" old minimum values gradually,
-/// which can be useful in adapting to new price trends or market regimes.
+/// Key characteristics:
+/// - Tracks absolute lowest values
+/// - Optional decay for adaptivity
+/// - Maintains historical context
+/// - Smooth transitions with decay
+/// - Period-based windowing
+///
+/// Formula:
+/// decay = 1 - e^(-halfLife * timeSinceMin / period)
+/// min = min + decay * (periodAverage - min)
+/// min = max(min, periodMinimum)
+///
+/// Market Applications:
+/// - Identify support levels
+/// - Track price troughs
+/// - Implement trailing stops
+/// - Monitor price extremes
+/// - Adaptive trend following
+///
+/// Sources:
+/// Technical Analysis of Financial Markets
+/// https://www.investopedia.com/terms/s/support.asp
+///
+/// Note: Decay factor allows for adaptive low tracking
///
+
public class Min : AbstractBase
{
- ///
- /// The number of data points to consider for the minimum calculation.
- ///
private readonly int Period;
-
- ///
- /// Circular buffer to store the most recent data points.
- ///
private readonly CircularBuffer _buffer;
-
- ///
- /// The half-life decay factor used to gradually forget old minimums.
- ///
private readonly double _halfLife;
-
- ///
- /// The current minimum value.
- ///
private double _currentMin;
-
- ///
- /// The previous minimum value.
- ///
private double _p_currentMin;
-
- ///
- /// The number of periods since a new minimum was set.
- ///
private int _timeSinceNewMin;
-
- ///
- /// The previous value of _timeSinceNewMin.
- ///
private int _p_timeSinceNewMin;
- ///
- /// Initializes a new instance of the Min class with the specified period and decay.
- ///
- /// The period over which to calculate the minimum value.
- /// The decay factor to apply to older values. Higher values cause faster forgetting of old minimums. Default is 0 (no decay).
- ///
- /// Thrown when period is less than 1 or decay is negative.
- ///
+ /// The number of points to consider for minimum calculation.
+ /// Half-life decay factor (0 for no decay, higher for faster forgetting).
+ /// Thrown when period is less than 1 or decay is negative.
public Min(int period, double decay = 0)
{
if (period < 1)
@@ -76,21 +71,15 @@ public class Min : AbstractBase
Init();
}
- ///
- /// Initializes a new instance of the Min class with the specified source, period, and decay.
- ///
- /// The source object to subscribe to for value updates.
- /// The period over which to calculate the minimum value.
- /// The decay factor to apply to older values. Higher values cause faster forgetting of old minimums. Default is 0 (no decay).
+ /// The data source object that publishes updates.
+ /// The number of points to consider for minimum calculation.
+ /// Half-life decay factor (default 0).
public Min(object source, int period, double decay = 0) : this(period, decay)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
- ///
- /// Initializes the Min instance by setting initial values.
- ///
public override void Init()
{
base.Init();
@@ -98,10 +87,6 @@ public class Min : AbstractBase
_timeSinceNewMin = 0;
}
- ///
- /// Manages the state of the Min instance based on whether a new value is being processed.
- ///
- /// Indicates whether the current input is a new value.
protected override void ManageState(bool isNew)
{
if (isNew)
@@ -119,29 +104,23 @@ public class Min : AbstractBase
}
}
- ///
- /// Performs the minimum value calculation with decay.
- ///
- /// The calculated minimum value for the current period.
- ///
- /// This method updates the current minimum value based on the input, applies the decay
- /// factor, and ensures the result is not lower than the actual minimum in the buffer.
- /// The decay rate is calculated using an exponential function based on the time since
- /// the last new minimum and the specified half-life.
- ///
protected override double Calculation()
{
ManageState(Input.IsNew);
_buffer.Add(Input.Value, Input.IsNew);
+ // Update minimum if new value is lower
if (Input.Value <= _currentMin)
{
_currentMin = Input.Value;
_timeSinceNewMin = 0;
}
+ // Apply decay based on time since last minimum
double decayRate = 1 - Math.Exp(-_halfLife * _timeSinceNewMin / Period);
_currentMin += decayRate * (_buffer.Average() - _currentMin);
+
+ // Ensure minimum doesn't fall below current period's lowest value
_currentMin = Math.Max(_currentMin, _buffer.Min());
IsHot = true;
diff --git a/lib/statistics/Mode.cs b/lib/statistics/Mode.cs
index a12a1fd6..91629638 100644
--- a/lib/statistics/Mode.cs
+++ b/lib/statistics/Mode.cs
@@ -1,34 +1,52 @@
+using System;
+using System.Linq;
namespace QuanTAlib;
///
-/// Represents a mode calculator that determines the most frequent value in a specified period.
-/// If multiple values have the same highest frequency, it returns their average.
+/// MODE: Most Frequent Value Measure
+/// A statistical measure that identifies the most frequently occurring value(s)
+/// in a dataset. When multiple values share the highest frequency, it returns
+/// their average to provide a representative central value.
///
///
-/// The Mode class uses a circular buffer to store values and calculates the mode
-/// efficiently. Before the specified period is reached, it returns the average of
-/// the available values as an approximation.
+/// The Mode calculation process:
+/// 1. Groups values by frequency
+/// 2. Identifies highest frequency group(s)
+/// 3. Averages multiple modes if present
+/// 4. Uses mean until period filled
///
-/// In financial analysis, the mode can be useful for:
-/// - Identifying the most common price levels, which could indicate support or resistance.
-/// - Analyzing the distribution of returns or other financial metrics.
-/// - Detecting patterns in trading volume or other discrete financial data.
+/// Key characteristics:
+/// - Identifies most common values
+/// - Handles multiple modes
+/// - Robust to distribution shape
+/// - Useful for discrete data
+/// - Returns actual data points
+///
+/// Formula:
+/// mode = value with highest frequency count
+/// if multiple modes: average of mode values
+///
+/// Market Applications:
+/// - Identify common price levels
+/// - Detect support/resistance zones
+/// - Analyze volume clusters
+/// - Find price congestion areas
+/// - Pattern recognition
+///
+/// Sources:
+/// https://en.wikipedia.org/wiki/Mode_(statistics)
+/// "Statistical Analysis in Financial Markets"
+///
+/// Note: Particularly useful for price level analysis
///
+
public class Mode : AbstractBase
{
- ///
- /// The number of data points to consider for the mode calculation.
- ///
private readonly int Period;
private readonly CircularBuffer _buffer;
- ///
- /// Initializes a new instance of the Mode class with the specified period.
- ///
- /// The period over which to calculate the mode.
- ///
- /// Thrown when period is less than 1.
- ///
+ /// The number of points to consider for mode calculation.
+ /// Thrown when period is less than 1.
public Mode(int period)
{
if (period < 1)
@@ -42,30 +60,20 @@ public class Mode : AbstractBase
Init();
}
- ///
- /// Initializes a new instance of the Mode class with the specified source and period.
- ///
- /// The source object to subscribe to for value updates.
- /// The period over which to calculate the mode.
+ /// The data source object that publishes updates.
+ /// The number of points to consider for mode calculation.
public Mode(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
- ///
- /// Resets the Mode indicator to its initial state.
- ///
public override void Init()
{
base.Init();
_buffer.Clear();
}
- ///
- /// Manages the state of the Mode instance based on whether a new value is being processed.
- ///
- /// Indicates whether the current input is a new value.
protected override void ManageState(bool isNew)
{
if (isNew)
@@ -75,18 +83,6 @@ public class Mode : AbstractBase
}
}
- ///
- /// Performs the mode calculation for the current period.
- ///
- ///
- /// The calculated mode (most frequent value) for the current period.
- /// If multiple values have the same highest frequency, returns their average.
- ///
- ///
- /// Before the specified period is reached, this method returns the average of
- /// the available values as an approximation of the mode. Once the period is
- /// reached, it calculates the true mode by grouping and counting the values.
- ///
protected override double Calculation()
{
ManageState(Input.IsNew);
@@ -95,22 +91,26 @@ public class Mode : AbstractBase
double mode;
if (_index >= Period)
{
+ // Group values by frequency and order by count
var values = _buffer.GetSpan().ToArray();
var groupedValues = values.GroupBy(v => v)
- .OrderByDescending(g => g.Count())
- .ThenBy(g => g.Key)
- .ToList();
+ .OrderByDescending(g => g.Count())
+ .ThenBy(g => g.Key)
+ .ToList();
+ // Find all values with highest frequency
int maxCount = groupedValues.First().Count();
var modes = groupedValues.TakeWhile(g => g.Count() == maxCount)
- .Select(g => g.Key)
- .ToList();
+ .Select(g => g.Key)
+ .ToList();
- mode = modes.Average(); // If there are multiple modes, we return their average
+ // Average multiple modes if present
+ mode = modes.Average();
}
else
{
- mode = _buffer.Average(); // Use average until we have enough data points
+ // Use average until we have enough data points
+ mode = _buffer.Average();
}
IsHot = _index >= WarmupPeriod;
diff --git a/lib/statistics/Percentile.cs b/lib/statistics/Percentile.cs
index ad1bdba6..4978a6ae 100644
--- a/lib/statistics/Percentile.cs
+++ b/lib/statistics/Percentile.cs
@@ -1,40 +1,54 @@
+using System;
+using System.Linq;
namespace QuanTAlib;
///
-/// Represents a percentile calculator that determines the value at a specified percentile
-/// in a given period of data points.
+/// Percentile: Distribution Position Measure
+/// A statistical measure that indicates the value below which a given percentage
+/// of observations falls. Percentiles provide insights into data distribution
+/// and are particularly useful for risk assessment and outlier detection.
///
///
-/// The Percentile class uses a circular buffer to store values and calculates the
-/// percentile efficiently. It uses linear interpolation when the percentile falls
-/// between two data points. Before the specified period is reached, it returns the
-/// average of the available values as an approximation.
+/// The Percentile calculation process:
+/// 1. Sorts values in ascending order
+/// 2. Calculates position based on percentile
+/// 3. Interpolates between adjacent values
+/// 4. Uses mean until period filled
///
-/// In financial analysis, percentiles are useful for:
-/// - Assessing the relative standing of a value within a distribution.
-/// - Identifying outliers or extreme values in financial data.
-/// - Creating risk measures, such as Value at Risk (VaR) calculations.
-/// - Analyzing the distribution of returns, trading volumes, or other financial metrics.
+/// Key characteristics:
+/// - Range specific value identification
+/// - Linear interpolation for precision
+/// - Distribution independent
+/// - Robust to outliers
+/// - Useful for risk metrics
+///
+/// Formula:
+/// position = (percentile/100) * (n-1)
+/// value = v[floor(pos)] + (v[ceil(pos)] - v[floor(pos)]) * (pos - floor(pos))
+/// where n = number of observations, v = sorted values
+///
+/// Market Applications:
+/// - Value at Risk (VaR) calculation
+/// - Risk management metrics
+/// - Performance analysis
+/// - Volatility assessment
+/// - Outlier detection
+///
+/// Sources:
+/// https://en.wikipedia.org/wiki/Percentile
+/// "Risk Management in Trading" - Davis Edwards
+///
+/// Note: Particularly useful for risk metrics like VaR
///
+
public class Percentile : AbstractBase
{
- ///
- /// The number of data points to consider for the percentile calculation.
- ///
private readonly int Period;
-
- ///
- /// The percentile to calculate (between 0 and 100).
- ///
private readonly double Percent;
-
private readonly CircularBuffer _buffer;
- ///
- /// Initializes a new instance of the Percentile class with the specified period and percentile.
- ///
- /// The period over which to calculate the percentile.
- /// The percentile to calculate (between 0 and 100).
+ /// The number of points to consider for percentile calculation.
+ /// The percentile to calculate (0-100).
///
/// Thrown when period is less than 2 or percent is not between 0 and 100.
///
@@ -42,11 +56,13 @@ public class Percentile : AbstractBase
{
if (period < 2)
{
- throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 2 for percentile calculation.");
+ 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.");
+ throw new ArgumentOutOfRangeException(nameof(percent),
+ "Percent must be between 0 and 100.");
}
Period = period;
Percent = percent;
@@ -56,31 +72,21 @@ public class Percentile : AbstractBase
Init();
}
- ///
- /// Initializes a new instance of the Percentile class with the specified source, period, and percentile.
- ///
- /// The source object to subscribe to for value updates.
- /// The period over which to calculate the percentile.
- /// The percentile to calculate (between 0 and 100).
+ /// The data source object that publishes updates.
+ /// The number of points to consider for percentile calculation.
+ /// The percentile to calculate (0-100).
public Percentile(object source, int period, double percent) : this(period, percent)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
- ///
- /// Initializes the Percentile instance by clearing the buffer.
- ///
public override void Init()
{
base.Init();
_buffer.Clear();
}
- ///
- /// Manages the state of the Percentile instance based on whether a new value is being processed.
- ///
- /// Indicates whether the current input is a new value.
protected override void ManageState(bool isNew)
{
if (isNew)
@@ -90,18 +96,6 @@ public class Percentile : AbstractBase
}
}
- ///
- /// Performs the percentile calculation for the current period.
- ///
- ///
- /// The calculated percentile value for the current period.
- ///
- ///
- /// This method uses linear interpolation when the percentile falls between two data points.
- /// Before the specified period is reached, it returns the average of the available values
- /// as an approximation. Once the period is reached, it calculates the true percentile by
- /// sorting the values and interpolating as necessary.
- ///
protected override double Calculation()
{
ManageState(Input.IsNew);
@@ -110,6 +104,7 @@ public class Percentile : AbstractBase
double result;
if (_buffer.Count >= Period)
{
+ // Sort values and calculate percentile position
var values = _buffer.GetSpan().ToArray();
Array.Sort(values);
@@ -123,7 +118,7 @@ public class Percentile : AbstractBase
}
else
{
- // Interpolate between the two nearest values
+ // Linear interpolation between adjacent values
double lowerValue = values[lowerIndex];
double upperValue = values[upperIndex];
double fraction = position - lowerIndex;
@@ -132,7 +127,7 @@ public class Percentile : AbstractBase
}
else
{
- // Use average for insufficient data, like the Median class
+ // Use average until we have enough data points
result = _buffer.Average();
}
diff --git a/lib/statistics/Skew.cs b/lib/statistics/Skew.cs
index 75711882..3ab7db22 100644
--- a/lib/statistics/Skew.cs
+++ b/lib/statistics/Skew.cs
@@ -1,44 +1,62 @@
+using System;
+using System.Linq;
namespace QuanTAlib;
///
-/// Represents a skewness calculator that measures the asymmetry of the probability
-/// distribution of a real-valued random variable about its mean.
+/// SKEW: Distribution Asymmetry Measure
+/// A statistical measure that quantifies the asymmetry of a probability distribution
+/// around its mean. Skewness indicates whether deviations from the mean are more
+/// likely in one direction than the other.
///
///
-/// The Skew class uses a circular buffer to store values and calculates the skewness
-/// efficiently. It uses the adjusted Fisher-Pearson standardized moment coefficient
-/// for sample skewness calculation. A minimum of 3 data points is required for the
-/// calculation.
+/// The Skew calculation process:
+/// 1. Calculates mean of the data
+/// 2. Computes deviations from mean
+/// 3. Calculates third moment (cubed deviations)
+/// 4. Normalizes by standard deviation cubed
///
-/// In financial analysis, skewness is important for:
-/// - Assessing the asymmetry of returns distribution.
-/// - Evaluating the risk of extreme events in either direction.
-/// - Complementing other risk measures like standard deviation.
-/// - Informing investment decisions and risk management strategies.
+/// Key characteristics:
+/// - Measures distribution asymmetry
+/// - Positive values indicate right skew
+/// - Negative values indicate left skew
+/// - Zero indicates symmetry
+/// - Scale-independent measure
///
-/// Positive skewness indicates a longer tail on the right side of the distribution,
-/// while negative skewness indicates a longer tail on the left side.
+/// Formula:
+/// skew = [√(n(n-1))/(n-2)] * [m₃/s³]
+/// where:
+/// m₃ = third moment about the mean
+/// s = standard deviation
+/// n = sample size
+///
+/// Market Applications:
+/// - Risk assessment in returns
+/// - Options pricing models
+/// - Trading strategy development
+/// - Portfolio risk management
+/// - Market sentiment analysis
+///
+/// Sources:
+/// Fisher-Pearson standardized moment coefficient
+/// https://en.wikipedia.org/wiki/Skewness
+/// "The Analysis of Financial Time Series" - Ruey S. Tsay
+///
+/// Note: Requires minimum of 3 data points for calculation
///
+
public class Skew : AbstractBase
{
- ///
- /// The number of data points to consider for the skewness calculation.
- ///
private readonly int Period;
private readonly CircularBuffer _buffer;
- ///
- /// Initializes a new instance of the Skew class with the specified period.
- ///
- /// The period over which to calculate the skewness.
- ///
- /// Thrown when period is less than 3.
- ///
+ /// The number of points to consider for skewness calculation.
+ /// Thrown when period is less than 3.
public Skew(int period)
{
if (period < 3)
{
- throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 3 for skewness calculation.");
+ throw new ArgumentOutOfRangeException(nameof(period),
+ "Period must be greater than or equal to 3 for skewness calculation.");
}
Period = period;
WarmupPeriod = 3;
@@ -47,30 +65,20 @@ public class Skew : AbstractBase
Init();
}
- ///
- /// Initializes a new instance of the Skew class with the specified source and period.
- ///
- /// The source object to subscribe to for value updates.
- /// The period over which to calculate the skewness.
+ /// The data source object that publishes updates.
+ /// The number of points to consider for skewness calculation.
public Skew(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
- ///
- /// Initializes the Skew instance by clearing the buffer.
- ///
public override void Init()
{
base.Init();
_buffer.Clear();
}
- ///
- /// Manages the state of the Skew instance based on whether a new value is being processed.
- ///
- /// Indicates whether the current input is a new value.
protected override void ManageState(bool isNew)
{
if (isNew)
@@ -80,36 +88,19 @@ public class Skew : AbstractBase
}
}
- ///
- /// Performs the skewness calculation for the current period.
- ///
- ///
- /// The calculated skewness value for the current period.
- ///
- ///
- /// This method uses the adjusted Fisher-Pearson standardized moment coefficient
- /// to calculate the sample skewness. It requires at least 3 data points for the
- /// calculation. If there are fewer than 3 data points, or if the standard
- /// deviation is zero, the method returns 0.
- ///
- /// Interpretation of results:
- /// - Positive values indicate right-skewed distribution (longer tail on the right side).
- /// - Negative values indicate left-skewed distribution (longer tail on the left side).
- /// - Values close to 0 suggest a relatively symmetric distribution.
- ///
protected override double Calculation()
{
ManageState(Input.IsNew);
-
_buffer.Add(Input.Value, Input.IsNew);
double skew = 0;
- if (_buffer.Count >= 3)
- { // We need at least 3 data points for skewness
+ if (_buffer.Count >= 3) // Need at least 3 points for skewness
+ {
var values = _buffer.GetSpan().ToArray();
double mean = values.Average();
double n = values.Length;
+ // Calculate third and second moments
double sumCubedDeviations = 0;
double sumSquaredDeviations = 0;
@@ -120,13 +111,13 @@ public class Skew : AbstractBase
sumSquaredDeviations += Math.Pow(deviation, 2);
}
- // Calculate sample skewness using the adjusted Fisher-Pearson standardized moment coefficient
+ // Fisher-Pearson standardized moment coefficient
double m3 = sumCubedDeviations / n;
double m2 = sumSquaredDeviations / n;
double s3 = Math.Pow(m2, 1.5);
- if (s3 != 0)
- { // Avoid division by zero
+ if (s3 != 0) // Avoid division by zero
+ {
skew = (Math.Sqrt(n * (n - 1)) / (n - 2)) * (m3 / s3);
}
}
diff --git a/lib/statistics/Slope.cs b/lib/statistics/Slope.cs
index e8681069..8731e507 100644
--- a/lib/statistics/Slope.cs
+++ b/lib/statistics/Slope.cs
@@ -1,52 +1,68 @@
+using System;
+using System.Linq;
namespace QuanTAlib;
///
-/// Represents a slope calculator that performs linear regression on a series of data points.
+/// SLOPE: Linear Regression Trend Measure
+/// A statistical measure that calculates the rate of change using linear regression.
+/// Slope indicates the direction and steepness of a trend, providing insights into
+/// momentum and potential trend changes.
///
///
-/// The Slope class calculates the slope of a linear regression line, along with other
-/// statistical measures such as intercept, standard deviation, R-squared, and the last
-/// point on the regression line. It uses the least squares method for calculation.
+/// The Slope calculation process:
+/// 1. Calculates means of x and y values
+/// 2. Computes deviations from means
+/// 3. Applies least squares method
+/// 4. Provides additional regression statistics
///
-/// In financial analysis, slope is important for:
-/// - Identifying trends in price movements or other financial metrics.
-/// - Measuring the rate of change in a financial time series.
-/// - Assessing the strength and direction of relationships between variables.
-/// - Supporting technical analysis indicators and trading strategies.
+/// Key characteristics:
+/// - Measures trend direction and strength
+/// - Provides rate of change
+/// - Scale-dependent measure
+/// - Includes regression statistics
+/// - Time-weighted calculation
+///
+/// Formula:
+/// slope = Σ((x - x̄)(y - ȳ)) / Σ((x - x̄)²)
+/// where:
+/// x = time points
+/// y = price values
+/// x̄, ȳ = respective means
+///
+/// Market Applications:
+/// - Trend identification
+/// - Momentum measurement
+/// - Support/resistance angles
+/// - Price target projection
+/// - Trend strength analysis
+///
+/// Sources:
+/// https://en.wikipedia.org/wiki/Simple_linear_regression
+/// "Technical Analysis of Financial Markets" - John J. Murphy
+///
+/// Note: Provides additional regression statistics (R², intercept)
///
+
public class Slope : AbstractBase
{
private readonly int _period;
private readonly CircularBuffer _buffer;
private readonly CircularBuffer _timeBuffer;
- ///
- /// Gets the y-intercept of the regression line.
- ///
+ /// Gets the y-intercept of the regression line.
public double? Intercept { get; private set; }
- ///
- /// Gets the standard deviation of the y-values.
- ///
+ /// Gets the standard deviation of the y-values.
public double? StdDev { get; private set; }
- ///
- /// Gets the R-squared value, indicating the goodness of fit of the regression line.
- ///
+ /// Gets the R-squared value, indicating regression fit quality.
public double? RSquared { get; private set; }
- ///
- /// Gets the y-value of the last point on the regression line.
- ///
+ /// Gets the last point on the regression line.
public double? Line { get; private set; }
- ///
- /// Initializes a new instance of the Slope class with the specified period.
- ///
- /// The period over which to calculate the slope.
- ///
- /// Thrown when period is less than or equal to 1.
- ///
+ /// The number of points to consider for slope calculation.
+ /// Thrown when period is less than or equal to 1.
public Slope(int period)
{
if (period <= 1)
@@ -59,24 +75,17 @@ public class Slope : AbstractBase
_buffer = new CircularBuffer(period);
_timeBuffer = new CircularBuffer(period);
Name = $"Slope(period={period})";
-
Init();
}
- ///
- /// Initializes a new instance of the Slope class with the specified source and period.
- ///
- /// The source object to subscribe to for value updates.
- /// The period over which to calculate the slope.
+ /// The data source object that publishes updates.
+ /// The number of points to consider for slope calculation.
public Slope(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
- ///
- /// Initializes the Slope instance by clearing buffers and resetting calculated values.
- ///
public override void Init()
{
base.Init();
@@ -88,10 +97,6 @@ public class Slope : AbstractBase
Line = null;
}
- ///
- /// Manages the state of the Slope instance based on whether a new value is being processed.
- ///
- /// Indicates whether the current input is a new value.
protected override void ManageState(bool isNew)
{
if (isNew)
@@ -101,25 +106,6 @@ public class Slope : AbstractBase
}
}
- ///
- /// Performs the slope calculation using linear regression for the current period.
- ///
- ///
- /// The calculated slope value for the current period.
- ///
- ///
- /// This method uses the least squares method to calculate the slope of the regression line.
- /// It also calculates and updates the Intercept, StdDev, RSquared, and Line properties.
- /// If there are fewer than 2 data points, or if the sum of squared x deviations is 0,
- /// the method returns 0 and sets the additional properties to null.
- ///
- /// Interpretation of results:
- /// - Positive slope: Indicates an upward trend in the data.
- /// - Negative slope: Indicates a downward trend in the data.
- /// - Slope close to 0: Indicates a relatively flat or no clear trend in the data.
- /// The magnitude of the slope represents the rate of change in the dependent variable
- /// (y) for each unit change in the independent variable (x).
- ///
protected override double Calculation()
{
ManageState(Input.IsNew);
@@ -128,10 +114,9 @@ public class Slope : AbstractBase
_timeBuffer.Add(Input.Time.Ticks, Input.IsNew);
double slope = 0;
-
if (_buffer.Count < 2)
{
- return slope; // Return 0 when there are fewer than 2 points
+ return slope; // Need at least 2 points
}
int count = Math.Min(_buffer.Count, _period);
@@ -147,7 +132,7 @@ public class Slope : AbstractBase
double avgX = sumX / count;
double avgY = sumY / count;
- // Least squares method
+ // Least squares regression
double sumSqX = 0, sumSqY = 0, sumSqXY = 0;
for (int i = 0; i < count; i++)
{
@@ -160,6 +145,7 @@ public class Slope : AbstractBase
if (sumSqX > 0)
{
+ // Calculate slope and related statistics
slope = sumSqXY / sumSqX;
Intercept = avgY - (slope * avgX);
@@ -174,7 +160,7 @@ public class Slope : AbstractBase
RSquared = r * r;
}
- // Calculate last Line value (y = mx + b)
+ // Calculate regression line endpoint
Line = (slope * count) + Intercept;
}
else
diff --git a/lib/statistics/Stddev.cs b/lib/statistics/Stddev.cs
index aae6fb20..47433a91 100644
--- a/lib/statistics/Stddev.cs
+++ b/lib/statistics/Stddev.cs
@@ -1,48 +1,63 @@
+using System;
+using System.Linq;
namespace QuanTAlib;
///
-/// Represents a standard deviation calculator that measures the amount of variation or
-/// dispersion of a set of values.
+/// STDDEV: Standard Deviation Volatility Measure
+/// A statistical measure that quantifies the amount of variation or dispersion
+/// in a dataset. Standard deviation is widely used in finance as a measure of
+/// volatility and risk assessment.
///
///
-/// The Stddev class calculates either the population standard deviation or the sample
-/// standard deviation based on the isPopulation parameter. It uses a circular buffer
-/// to efficiently manage the data points within the specified period.
+/// The StdDev calculation process:
+/// 1. Calculates mean of the data
+/// 2. Computes squared deviations from mean
+/// 3. Averages squared deviations
+/// 4. Takes square root of average
///
-/// In financial analysis, standard deviation is important for:
-/// - Measuring volatility of financial instruments or portfolios.
-/// - Assessing risk in investments.
-/// - Calculating Sharpe ratios and other risk-adjusted performance measures.
-/// - Identifying potential outliers or unusual market behavior.
+/// Key characteristics:
+/// - Measures data dispersion
+/// - Same units as input data
+/// - Sensitive to outliers
+/// - Population or sample versions
+/// - Key volatility indicator
+///
+/// Formula:
+/// Population: σ = √(Σ(x - μ)² / N)
+/// Sample: s = √(Σ(x - x̄)² / (n-1))
+/// where:
+/// x = values
+/// μ, x̄ = mean
+/// N, n = count
+///
+/// Market Applications:
+/// - Volatility measurement
+/// - Risk assessment
+/// - Bollinger Bands
+/// - Option pricing
+/// - Portfolio management
+///
+/// Sources:
+/// https://en.wikipedia.org/wiki/Standard_deviation
+/// "Options, Futures, and Other Derivatives" - John C. Hull
+///
+/// Note: Foundation for many volatility-based indicators
///
+
public class Stddev : AbstractBase
{
- ///
- /// Indicates whether to calculate population (true) or sample (false) standard deviation.
- ///
private readonly bool IsPopulation;
-
- ///
- /// Circular buffer to store the most recent data points.
- ///
private readonly CircularBuffer _buffer;
- ///
- /// Initializes a new instance of the Stddev class with the specified period and
- /// population flag.
- ///
- /// The period over which to calculate the standard deviation.
- ///
- /// A flag indicating whether to calculate population (true) or sample (false) standard deviation.
- ///
- ///
- /// Thrown when period is less than 2.
- ///
+ /// The number of points to consider for standard deviation calculation.
+ /// True for population stddev, false for sample stddev (default).
+ /// Thrown when period is less than 2.
public Stddev(int period, bool isPopulation = false)
{
if (period < 2)
{
- throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 2.");
+ throw new ArgumentOutOfRangeException(nameof(period),
+ "Period must be greater than or equal to 2.");
}
IsPopulation = isPopulation;
WarmupPeriod = 0;
@@ -51,34 +66,21 @@ public class Stddev : AbstractBase
Init();
}
- ///
- /// Initializes a new instance of the Stddev class with the specified source, period,
- /// and population flag.
- ///
- /// The source object to subscribe to for value updates.
- /// The period over which to calculate the standard deviation.
- ///
- /// A flag indicating whether to calculate population (true) or sample (false) standard deviation.
- ///
+ /// The data source object that publishes updates.
+ /// The number of points to consider for standard deviation calculation.
+ /// True for population stddev, false for sample stddev (default).
public Stddev(object source, int period, bool isPopulation = false) : this(period, isPopulation)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
- ///
- /// Initializes the Stddev instance by clearing the buffer.
- ///
public override void Init()
{
base.Init();
_buffer.Clear();
}
- ///
- /// Manages the state of the Stddev instance based on whether a new value is being processed.
- ///
- /// Indicates whether the current input is a new value.
protected override void ManageState(bool isNew)
{
if (isNew)
@@ -88,28 +90,9 @@ public class Stddev : AbstractBase
}
}
- ///
- /// Performs the standard deviation calculation for the current period.
- ///
- ///
- /// The calculated standard deviation value for the current period.
- ///
- ///
- /// This method calculates the standard deviation using the formula:
- /// sqrt(sum((x - mean)^2) / n) for population, or
- /// sqrt(sum((x - mean)^2) / (n - 1)) for sample,
- /// where x is each value, mean is the average of all values, and n is the number of values.
- /// If there's only one value in the buffer, the method returns 0.
- ///
- /// Interpretation of results:
- /// - A low standard deviation indicates that the values tend to be close to the mean.
- /// - A high standard deviation indicates that the values are spread out over a wider range.
- /// - In financial contexts, higher standard deviation often implies higher volatility or risk.
- ///
protected override double Calculation()
{
ManageState(Input.IsNew);
-
_buffer.Add(Input.Value, Input.IsNew);
double stddev = 0;
@@ -117,8 +100,11 @@ public class Stddev : AbstractBase
{
var values = _buffer.GetSpan().ToArray();
double mean = values.Average();
+
+ // Calculate sum of squared deviations
double sumOfSquaredDifferences = values.Sum(x => Math.Pow(x - mean, 2));
+ // Use appropriate divisor based on population/sample calculation
double divisor = IsPopulation ? _buffer.Count : _buffer.Count - 1;
double variance = sumOfSquaredDifferences / divisor;
stddev = Math.Sqrt(variance);
diff --git a/lib/statistics/Variance.cs b/lib/statistics/Variance.cs
index e1bc4c67..12916507 100644
--- a/lib/statistics/Variance.cs
+++ b/lib/statistics/Variance.cs
@@ -1,48 +1,63 @@
+using System;
+using System.Linq;
namespace QuanTAlib;
///
-/// Represents a variance calculator that measures the spread of a set of numbers
-/// from their average value.
+/// VARIANCE: Squared Deviation Risk Measure
+/// A statistical measure that quantifies the spread of data points around their
+/// mean value. Variance is fundamental to risk assessment and portfolio theory,
+/// providing the basis for many financial models.
///
///
-/// The Variance class calculates either the population variance or the sample
-/// variance based on the isPopulation parameter. It uses a circular buffer
-/// to efficiently manage the data points within the specified period.
+/// The Variance calculation process:
+/// 1. Calculates mean of the data
+/// 2. Computes squared deviations from mean
+/// 3. Sums squared deviations
+/// 4. Divides by n or (n-1)
///
-/// In financial analysis, variance is important for:
-/// - Measuring the dispersion of returns around the mean.
-/// - Assessing risk and volatility in financial instruments or portfolios.
-/// - Serving as a basis for other risk measures like standard deviation and beta.
-/// - Contributing to portfolio optimization techniques, such as Modern Portfolio Theory.
+/// Key characteristics:
+/// - Measures data dispersion
+/// - Squared units of input data
+/// - Always non-negative
+/// - Population or sample versions
+/// - Foundation for risk metrics
+///
+/// Formula:
+/// Population: σ² = Σ(x - μ)² / N
+/// Sample: s² = Σ(x - x̄)² / (n-1)
+/// where:
+/// x = values
+/// μ, x̄ = mean
+/// N, n = count
+///
+/// Market Applications:
+/// - Portfolio optimization
+/// - Risk measurement
+/// - Modern Portfolio Theory
+/// - Asset allocation
+/// - Volatility analysis
+///
+/// Sources:
+/// Harry Markowitz - "Portfolio Selection" (1952)
+/// https://en.wikipedia.org/wiki/Variance
+///
+/// Note: Basis for Modern Portfolio Theory and risk models
///
+
public class Variance : AbstractBase
{
- ///
- /// Indicates whether to calculate population (true) or sample (false) variance.
- ///
private readonly bool IsPopulation;
-
- ///
- /// Circular buffer to store the most recent data points.
- ///
private readonly CircularBuffer _buffer;
- ///
- /// Initializes a new instance of the Variance class with the specified period and
- /// population flag.
- ///
- /// The period over which to calculate the variance.
- ///
- /// A flag indicating whether to calculate population (true) or sample (false) variance.
- ///
- ///
- /// Thrown when period is less than 2.
- ///
+ /// The number of points to consider for variance calculation.
+ /// True for population variance, false for sample variance (default).
+ /// Thrown when period is less than 2.
public Variance(int period, bool isPopulation = false)
{
if (period < 2)
{
- throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 2.");
+ throw new ArgumentOutOfRangeException(nameof(period),
+ "Period must be greater than or equal to 2.");
}
IsPopulation = isPopulation;
WarmupPeriod = 0;
@@ -51,34 +66,21 @@ public class Variance : AbstractBase
Init();
}
- ///
- /// Initializes a new instance of the Variance class with the specified source, period,
- /// and population flag.
- ///
- /// The source object to subscribe to for value updates.
- /// The period over which to calculate the variance.
- ///
- /// A flag indicating whether to calculate population (true) or sample (false) variance.
- ///
+ /// The data source object that publishes updates.
+ /// The number of points to consider for variance calculation.
+ /// True for population variance, false for sample variance (default).
public Variance(object source, int period, bool isPopulation = false) : this(period, isPopulation)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
- ///
- /// Initializes the Variance instance by clearing the buffer.
- ///
public override void Init()
{
base.Init();
_buffer.Clear();
}
- ///
- /// Manages the state of the Variance instance based on whether a new value is being processed.
- ///
- /// Indicates whether the current input is a new value.
protected override void ManageState(bool isNew)
{
if (isNew)
@@ -88,29 +90,9 @@ public class Variance : AbstractBase
}
}
- ///
- /// Performs the variance calculation for the current period.
- ///
- ///
- /// The calculated variance value for the current period.
- ///
- ///
- /// This method calculates the variance using the formula:
- /// sum((x - mean)^2) / n for population, or
- /// sum((x - mean)^2) / (n - 1) for sample,
- /// where x is each value, mean is the average of all values, and n is the number of values.
- /// If there's only one value in the buffer, the method returns 0.
- ///
- /// Interpretation of results:
- /// - A low variance indicates that the values tend to be close to the mean and to each other.
- /// - A high variance indicates that the values are spread out over a wider range.
- /// - In financial contexts, higher variance often implies higher volatility or risk.
- /// - Variance is always non-negative, and its units are squared units of the original data.
- ///
protected override double Calculation()
{
ManageState(Input.IsNew);
-
_buffer.Add(Input.Value, Input.IsNew);
double variance = 0;
@@ -118,8 +100,11 @@ public class Variance : AbstractBase
{
var values = _buffer.GetSpan().ToArray();
double mean = values.Average();
+
+ // Calculate sum of squared deviations
double sumOfSquaredDifferences = values.Sum(x => Math.Pow(x - mean, 2));
+ // Use appropriate divisor based on population/sample calculation
double divisor = IsPopulation ? _buffer.Count : _buffer.Count - 1;
variance = sumOfSquaredDifferences / divisor;
}
diff --git a/lib/statistics/Zscore.cs b/lib/statistics/Zscore.cs
index de94054c..c3c72769 100644
--- a/lib/statistics/Zscore.cs
+++ b/lib/statistics/Zscore.cs
@@ -1,44 +1,61 @@
+using System;
+using System.Linq;
namespace QuanTAlib;
///
-/// Represents a Z-score calculator that measures how many standard deviations
-/// an element is from the mean of a set of values.
+/// ZSCORE: Standardized Distance Measure
+/// A statistical measure that indicates how many standard deviations an observation
+/// is from the mean. Z-scores normalize data to a standard scale, making it useful
+/// for comparing values across different distributions.
///
///
-/// The Zscore class calculates the Z-score (also known as standard score) for
-/// the most recent value in a given period. It uses a circular buffer to
-/// efficiently manage the data points within the specified period.
+/// The Zscore calculation process:
+/// 1. Calculates mean of the period
+/// 2. Computes standard deviation
+/// 3. Measures distance from mean
+/// 4. Normalizes by standard deviation
///
-/// In financial analysis, Z-score is important for:
-/// - Identifying outliers or unusual price movements.
-/// - Normalizing data across different scales or time periods.
-/// - Assessing the relative position of a value within its historical distribution.
-/// - Supporting trading strategies based on mean reversion or momentum.
+/// Key characteristics:
+/// - Scale-independent measure
+/// - Symmetric around zero
+/// - Normal distribution context
+/// - Outlier identification
+/// - Comparative analysis tool
+///
+/// Formula:
+/// Z = (x - μ) / σ
+/// where:
+/// x = current value
+/// μ = mean
+/// σ = standard deviation
+///
+/// Market Applications:
+/// - Mean reversion strategies
+/// - Overbought/oversold signals
+/// - Volatility breakouts
+/// - Cross-asset comparison
+/// - Statistical arbitrage
+///
+/// Sources:
+/// https://en.wikipedia.org/wiki/Standard_score
+/// "Statistical Analysis in Trading" - Technical Analysis
+///
+/// Note: Assumes approximately normal distribution
///
+
public class Zscore : AbstractBase
{
- ///
- /// The number of data points to consider for the Z-score calculation.
- ///
private readonly int Period;
-
- ///
- /// Circular buffer to store the most recent data points.
- ///
private readonly CircularBuffer _buffer;
- ///
- /// Initializes a new instance of the Zscore class with the specified period.
- ///
- /// The period over which to calculate the Z-score.
- ///
- /// Thrown when period is less than 2.
- ///
+ /// The number of points to consider for Z-score calculation.
+ /// Thrown when period is less than 2.
public Zscore(int period)
{
if (period < 2)
{
- throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 2 for Z-score calculation.");
+ throw new ArgumentOutOfRangeException(nameof(period),
+ "Period must be greater than or equal to 2 for Z-score calculation.");
}
Period = period;
WarmupPeriod = 2;
@@ -47,30 +64,20 @@ public class Zscore : AbstractBase
Init();
}
- ///
- /// Initializes a new instance of the Zscore class with the specified source and period.
- ///
- /// The source object to subscribe to for value updates.
- /// The period over which to calculate the Z-score.
+ /// The data source object that publishes updates.
+ /// The number of points to consider for Z-score calculation.
public Zscore(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
- ///
- /// Initializes the Zscore instance by clearing the buffer.
- ///
public override void Init()
{
base.Init();
_buffer.Clear();
}
- ///
- /// Manages the state of the Zscore instance based on whether a new value is being processed.
- ///
- /// Indicates whether the current input is a new value.
protected override void ManageState(bool isNew)
{
if (isNew)
@@ -80,44 +87,24 @@ public class Zscore : AbstractBase
}
}
- ///
- /// Performs the Z-score calculation for the current period.
- ///
- ///
- /// The calculated Z-score value for the most recent input in the current period.
- ///
- ///
- /// This method calculates the Z-score using the formula:
- /// Z = (x - μ) / σ
- /// where x is the input value, μ is the mean of the period, and σ is the sample standard deviation.
- /// If there are fewer than 2 data points or if the standard deviation is 0, the method returns 0.
- ///
- /// Interpretation of results:
- /// - A Z-score of 0 indicates that the data point is exactly on the mean.
- /// - A positive Z-score indicates the data point is above the mean.
- /// - A negative Z-score indicates the data point is below the mean.
- /// - The magnitude of the Z-score represents how many standard deviations away from the mean the data point is.
- /// - In a normal distribution, about 68% of the values have a Z-score between -1 and 1,
- /// 95% between -2 and 2, and 99.7% between -3 and 3.
- ///
protected override double Calculation()
{
ManageState(Input.IsNew);
-
_buffer.Add(Input.Value, Input.IsNew);
double zScore = 0;
- if (_buffer.Count >= 2)
- { // We need at least 2 data points for Z-score
+ if (_buffer.Count >= 2) // Need at least 2 points for standard deviation
+ {
var values = _buffer.GetSpan().ToArray();
double mean = values.Average();
double n = values.Length;
+ // Calculate sample standard deviation
double sumSquaredDeviations = values.Sum(x => Math.Pow(x - mean, 2));
- double standardDeviation = Math.Sqrt(sumSquaredDeviations / (n - 1)); // Sample standard deviation
+ double standardDeviation = Math.Sqrt(sumSquaredDeviations / (n - 1));
- if (standardDeviation != 0)
- { // Avoid division by zero
+ if (standardDeviation != 0) // Avoid division by zero
+ {
zScore = (Input.Value - mean) / standardDeviation;
}
}
diff --git a/lib/volatility/Atr.cs b/lib/volatility/Atr.cs
index 3ea3e5f0..43d1983a 100644
--- a/lib/volatility/Atr.cs
+++ b/lib/volatility/Atr.cs
@@ -1,51 +1,75 @@
+using System;
namespace QuanTAlib;
///
-/// Represents an Average True Range (ATR) calculator, a measure of market volatility.
+/// ATR: Average True Range
+/// A technical indicator that measures market volatility by decomposing the entire
+/// range of an asset's price for a period. ATR accounts for gaps between periods
+/// and provides a comprehensive view of price volatility.
///
///
-/// The ATR class calculates the average true range using a Relative Moving Average (RMA)
-/// of the true range. The true range is the greatest of: current high - current low,
-/// absolute value of current high - previous close, or absolute value of current low - previous close.
+/// The ATR calculation process:
+/// 1. Calculates True Range (TR) as maximum of:
+/// - Current High - Current Low
+/// - |Current High - Previous Close|
+/// - |Current Low - Previous Close|
+/// 2. Applies RMA smoothing to TR values
+/// 3. Updates with each new price bar
+/// 4. Adapts to changing volatility
+///
+/// Key characteristics:
+/// - Absolute price measure
+/// - Gap-inclusive calculation
+/// - Trend independent
+/// - Volatility focused
+/// - Smoothed output
+///
+/// Formula:
+/// TR = max(high-low, |high-prevClose|, |low-prevClose|)
+/// ATR = RMA(TR, period)
+///
+/// Market Applications:
+/// - Position sizing
+/// - Stop loss placement
+/// - Volatility breakouts
+/// - Risk assessment
+/// - Entry/exit timing
+///
+/// Sources:
+/// J. Welles Wilder - "New Concepts in Technical Trading Systems"
+/// https://www.investopedia.com/terms/a/atr.asp
+///
+/// Note: Higher ATR indicates higher volatility
///
+
public class Atr : AbstractBase
{
public double Tr { get; private set; }
private readonly Rma _ma;
private double _prevClose, _p_prevClose;
- ///
- /// Initializes a new instance of the Atr class with the specified period.
- ///
- /// The period over which to calculate the ATR.
- ///
- /// Thrown when period is less than 1.
- ///
+ /// The number of periods for ATR calculation.
+ /// Thrown when period is less than 1.
public Atr(int period)
{
if (period < 1)
{
- throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1.");
+ throw new ArgumentOutOfRangeException(nameof(period),
+ "Period must be greater than or equal to 1.");
}
_ma = new(period, useSma: true);
WarmupPeriod = _ma.WarmupPeriod;
Name = $"ATR({period})";
}
- ///
- /// Initializes a new instance of the Atr class with the specified source and period.
- ///
- /// The source object to subscribe to for bar updates.
- /// The period over which to calculate the ATR.
+ /// The data source object that publishes updates.
+ /// The number of periods for ATR calculation.
public Atr(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
- ///
- /// Initializes the Atr instance by setting up the initial state.
- ///
public override void Init()
{
base.Init();
@@ -54,10 +78,6 @@ public class Atr : AbstractBase
Tr = 0;
}
- ///
- /// Manages the state of the Atr instance based on whether a new bar is being processed.
- ///
- /// Indicates whether the current input is a new bar.
protected override void ManageState(bool isNew)
{
if (isNew)
@@ -71,28 +91,19 @@ public class Atr : AbstractBase
}
}
- ///
- /// Performs the ATR calculation for the current bar.
- ///
- ///
- /// The calculated ATR value for the current bar.
- ///
- ///
- /// This method calculates the true range for the current bar and then uses an RMA
- /// to smooth the true range values. For the first bar, it uses the high-low range
- /// as the true range.
- ///
protected override double Calculation()
{
ManageState(BarInput.IsNew);
if (_index == 1)
{
+ // First bar uses simple high-low range
Tr = BarInput.High - BarInput.Low;
_prevClose = BarInput.Close;
}
else
{
+ // Calculate True Range as maximum of three measures
Tr = Math.Max(
BarInput.High - BarInput.Low,
Math.Max(
@@ -101,6 +112,8 @@ public class Atr : AbstractBase
)
);
}
+
+ // Apply RMA smoothing to True Range
_ma.Calc(new TValue(Input.Time, Tr, BarInput.IsNew));
IsHot = _ma.IsHot;
diff --git a/lib/volatility/Hv.cs b/lib/volatility/Hv.cs
index ef74e37e..f6c73cc7 100644
--- a/lib/volatility/Hv.cs
+++ b/lib/volatility/Hv.cs
@@ -1,14 +1,49 @@
+using System;
+using System.Linq;
namespace QuanTAlib;
///
-/// Represents a historical volatility calculator that measures the dispersion of returns
-/// for a given security or market index over a specific period.
+/// HV: Historical Volatility
+/// A statistical measure that calculates the dispersion of returns over time,
+/// providing insights into past price variability. Historical volatility is
+/// fundamental to options pricing and risk assessment.
///
///
-/// The Historical class calculates volatility based on logarithmic returns. It can provide
-/// both annualized and non-annualized volatility measures. The calculation uses a sample
-/// standard deviation formula and assumes 252 trading days in a year for annualization.
+/// The HV calculation process:
+/// 1. Computes daily log returns
+/// 2. Calculates standard deviation
+/// 3. Annualizes if specified
+/// 4. Uses sample variance formula
+///
+/// Key characteristics:
+/// - Backward-looking measure
+/// - Log-return based
+/// - Optional annualization
+/// - Sample-based calculation
+/// - Trading-day adjusted
+///
+/// Formula:
+/// HV = √[(Σ(ln(P[t]/P[t-1]) - μ)²)/(n-1)] * √252
+/// where:
+/// P = price
+/// μ = mean of log returns
+/// n = number of observations
+/// 252 = trading days per year
+///
+/// Market Applications:
+/// - Options pricing
+/// - Risk assessment
+/// - Trading ranges
+/// - Portfolio management
+/// - Volatility trading
+///
+/// Sources:
+/// Black-Scholes Option Pricing Model
+/// https://en.wikipedia.org/wiki/Volatility_(finance)
+///
+/// Note: Assumes 252 trading days for annualization
///
+
public class Hv : AbstractBase
{
private readonly int Period;
@@ -17,44 +52,34 @@ public class Hv : AbstractBase
private readonly CircularBuffer _logReturns;
private double _previousClose;
- ///
- /// Initializes a new instance of the Historical class with the specified period and annualization flag.
- ///
- /// The period over which to calculate historical volatility.
- /// Whether to annualize the volatility (default is true).
- ///
- /// Thrown when period is less than 2.
- ///
+ /// The number of periods for volatility calculation.
+ /// Whether to annualize the result (default true).
+ /// Thrown when period is less than 2.
public Hv(int period, bool isAnnualized = true)
{
if (period < 2)
{
- throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 2.");
+ throw new ArgumentOutOfRangeException(nameof(period),
+ "Period must be greater than or equal to 2.");
}
Period = period;
IsAnnualized = isAnnualized;
- WarmupPeriod = period + 1; // We need one extra data point to calculate the first return
+ WarmupPeriod = period + 1; // Need extra point for first return
_buffer = new CircularBuffer(period + 1);
_logReturns = new CircularBuffer(period);
Name = $"Historical(period={period}, annualized={isAnnualized})";
Init();
}
- ///
- /// Initializes a new instance of the Historical class with the specified source, period, and annualization flag.
- ///
- /// The source object to subscribe to for value updates.
- /// The period over which to calculate historical volatility.
- /// Whether to annualize the volatility (default is true).
+ /// The data source object that publishes updates.
+ /// The number of periods for volatility calculation.
+ /// Whether to annualize the result (default true).
public Hv(object source, int period, bool isAnnualized = true) : this(period, isAnnualized)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
- ///
- /// Initializes the Historical instance by clearing buffers and resetting the previous close value.
- ///
public override void Init()
{
base.Init();
@@ -63,10 +88,6 @@ public class Hv : AbstractBase
_previousClose = 0;
}
- ///
- /// Manages the state of the Historical instance based on whether a new value is being processed.
- ///
- /// Indicates whether the current input is a new value.
protected override void ManageState(bool isNew)
{
if (isNew)
@@ -76,47 +97,35 @@ public class Hv : AbstractBase
}
}
- ///
- /// Performs the historical volatility calculation for the current period.
- ///
- ///
- /// The calculated historical volatility value for the current period.
- ///
- ///
- /// This method calculates the volatility using the following steps:
- /// 1. Compute logarithmic returns.
- /// 2. Calculate the sample standard deviation of the log returns.
- /// 3. If annualized, multiply by the square root of 252 (assumed trading days in a year).
- /// The method returns 0 until enough data points are available for the calculation.
- ///
protected override double Calculation()
{
ManageState(Input.IsNew);
-
_buffer.Add(Input.Value, Input.IsNew);
double volatility = 0;
if (_buffer.Count > 1)
{
+ // Calculate log return if we have previous close
if (_previousClose != 0)
{
double logReturn = Math.Log(Input.Value / _previousClose);
_logReturns.Add(logReturn, Input.IsNew);
}
+ // Calculate volatility when we have enough returns
if (_logReturns.Count == Period)
{
var returns = _logReturns.GetSpan().ToArray();
double mean = returns.Average();
double sumOfSquaredDifferences = returns.Sum(x => Math.Pow(x - mean, 2));
- double variance = sumOfSquaredDifferences / (Period - 1); // Using sample standard deviation
+ // Sample standard deviation
+ double variance = sumOfSquaredDifferences / (Period - 1);
volatility = Math.Sqrt(variance);
if (IsAnnualized)
{
- // Assuming 252 trading days in a year. Adjust as needed.
- volatility *= Math.Sqrt(252);
+ volatility *= Math.Sqrt(252); // Annualize using trading days
}
}
}
diff --git a/lib/volatility/Jvolty.cs b/lib/volatility/Jvolty.cs
index 23f6ae97..af7c305e 100644
--- a/lib/volatility/Jvolty.cs
+++ b/lib/volatility/Jvolty.cs
@@ -1,9 +1,46 @@
-///
-/// Represents a Jurik Volatility (Jvolty) calculator, a measure of market volatility based on Jurik Moving Average (JMA) concepts.
-///
-
+using System;
namespace QuanTAlib;
+///
+/// JVOLTY: Jurik Volatility
+/// An advanced volatility measure developed by Mark Jurik that combines adaptive
+/// bands with JMA smoothing. JVOLTY provides a sophisticated approach to measuring
+/// market volatility with reduced noise and better responsiveness.
+///
+///
+/// The JVOLTY calculation process:
+/// 1. Calculates adaptive price bands
+/// 2. Measures volatility from band distances
+/// 3. Applies volatility normalization
+/// 4. Uses JMA-style smoothing
+/// 5. Provides multiple outputs
+///
+/// Key characteristics:
+/// - Adaptive measurement
+/// - Noise reduction
+/// - Multiple timeframe analysis
+/// - Price band integration
+/// - Volatility normalization
+///
+/// Formula:
+/// volty = max(|price - upperBand|, |price - lowerBand|)
+/// bands = adaptive calculation using Jurik's methods
+/// final = JMA smoothing of normalized volatility
+///
+/// Market Applications:
+/// - Dynamic position sizing
+/// - Adaptive stop placement
+/// - Volatility breakout systems
+/// - Risk management
+/// - Market regime detection
+///
+/// Sources:
+/// Mark Jurik Research
+/// https://www.jurikresearch.com/
+///
+/// Note: Proprietary enhancement of volatility measurement
+///
+
public class Jvolty : AbstractBase
{
private readonly int _period;
@@ -18,7 +55,6 @@ public class Jvolty : AbstractBase
private double _prevMa1, _prevDet0, _prevDet1, _prevJma, _p_prevMa1, _p_prevDet0, _p_prevDet1, _p_prevJma;
private double _vSum, _p_vSum;
-
public double UpperBand { get; set; }
public double LowerBand { get; set; }
public double Volty { get; set; }
@@ -26,21 +62,15 @@ public class Jvolty : AbstractBase
public double Jma { get; set; }
public double AvgVolty { get; set; }
-
- ///
- /// Initializes a new instance of the Jvolty class with the specified parameters.
- ///
- /// The period over which to calculate the Jvolty.
- /// The phase parameter for the JMA-style calculation.
- /// The short-term volatility period.
- ///
- /// Thrown when period is less than 1.
- ///
+ /// The number of periods for volatility calculation.
+ /// Phase parameter for JMA smoothing (default 0).
+ /// Thrown when period is less than 1.
public Jvolty(int period, int phase = 0)
{
if (period < 1)
{
- throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1.");
+ throw new ArgumentOutOfRangeException(nameof(period),
+ "Period must be greater than or equal to 1.");
}
_period = period;
_phase = Math.Clamp((phase * 0.01) + 1.5, 0.5, 2.5);
@@ -53,22 +83,15 @@ public class Jvolty : AbstractBase
Name = $"JVOLTY({period})";
}
- ///
- /// Initializes a new instance of the Jvolty class with the specified source and parameters.
- ///
- /// The source object to subscribe to for bar updates.
- /// The period over which to calculate the Jvolty.
- /// The phase parameter for the JMA-style calculation.
- /// The short-term volatility period.
+ /// The data source object that publishes updates.
+ /// The number of periods for volatility calculation.
+ /// Phase parameter for JMA smoothing (default 0).
public Jvolty(object source, int period, int phase = 0) : this(period, phase)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
- ///
- /// Initializes the Jvolty instance by setting up the initial state.
- ///
public override void Init()
{
base.Init();
@@ -80,10 +103,6 @@ public class Jvolty : AbstractBase
_vsumBuff.Clear();
}
- ///
- /// Manages the state of the Jvolty instance based on whether a new bar is being processed.
- ///
- /// Indicates whether the current input is a new bar.
protected override void ManageState(bool isNew)
{
if (isNew)
@@ -109,12 +128,6 @@ public class Jvolty : AbstractBase
}
}
- ///
- /// Performs the Jvolty calculation for the current bar.
- ///
- ///
- /// The calculated Jvolty value for the current bar.
- ///
protected override double Calculation()
{
ManageState(Input.IsNew);
@@ -125,26 +138,29 @@ public class Jvolty : AbstractBase
_upperBand = _lowerBand = price;
}
+ // Calculate volatility from band distances
double del1 = price - _upperBand;
double del2 = price - _lowerBand;
double volty = Math.Max(Math.Abs(del1), Math.Abs(del2));
+ // Calculate moving averages of volatility
_vsumBuff.Add(volty, Input.IsNew);
_vSum += (_vsumBuff[^1] - _vsumBuff[0]) / 10;
_avoltyBuff.Add(_vSum, Input.IsNew);
double avgvolty = _avoltyBuff.Average();
+ // Normalize and adjust volatility
double rvolty = (avgvolty > 0) ? volty / avgvolty : 1;
rvolty = Math.Min(Math.Max(rvolty, 1.0), Math.Pow(_len1, 1.0 / _pow1));
double pow2 = Math.Pow(rvolty, _pow1);
double Kv = Math.Pow(_beta, Math.Sqrt(pow2));
+ // Update adaptive bands
_upperBand = (del1 >= 0) ? price : price - (Kv * del1);
_lowerBand = (del2 <= 0) ? price : price - (Kv * del2);
-
-
+ // Apply JMA smoothing
double alpha = Math.Pow(_beta, pow2);
double ma1 = (1 - alpha) * Input.Value + alpha * _prevMa1;
_prevMa1 = ma1;
@@ -153,11 +169,12 @@ public class Jvolty : AbstractBase
_prevDet0 = det0;
double ma2 = ma1 + _phase * det0;
- double det1 = ((ma2 - _prevJma) * (1 - alpha) * (1 - alpha) ) + (alpha * alpha * _prevDet1);
+ double det1 = ((ma2 - _prevJma) * (1 - alpha) * (1 - alpha)) + (alpha * alpha * _prevDet1);
_prevDet1 = det1;
double jma = _prevJma + det1;
_prevJma = jma;
+ // Update public properties
UpperBand = _upperBand;
LowerBand = _lowerBand;
Volty = volty;
@@ -169,5 +186,3 @@ public class Jvolty : AbstractBase
return volty;
}
}
-
-
diff --git a/lib/volatility/Rv.cs b/lib/volatility/Rv.cs
index 4aec3783..b171a173 100644
--- a/lib/volatility/Rv.cs
+++ b/lib/volatility/Rv.cs
@@ -1,14 +1,48 @@
+using System;
namespace QuanTAlib;
///
-/// Represents a realized volatility calculator that measures the actual price fluctuations
-/// observed in the market over a specific period.
+/// RV: Realized Volatility
+/// A precise volatility measure that captures actual observed price fluctuations
+/// using high-frequency returns. RV provides a more accurate assessment of true
+/// market volatility compared to traditional estimators.
///
///
-/// The Realized class calculates volatility based on logarithmic returns. It can provide
-/// both annualized and non-annualized volatility measures. The calculation uses a rolling
-/// sum of squared returns for efficiency and assumes 252 trading days in a year for annualization.
+/// The RV calculation process:
+/// 1. Computes log returns
+/// 2. Squares each return
+/// 3. Maintains rolling sum
+/// 4. Takes square root of average
+/// 5. Optionally annualizes
+///
+/// Key characteristics:
+/// - Model-free measurement
+/// - High-frequency capable
+/// - Rolling calculation
+/// - Memory efficient
+/// - Optional annualization
+///
+/// Formula:
+/// RV = √(Σ(ln(P[t]/P[t-1]))²/n) * √252
+/// where:
+/// P = price
+/// n = number of observations
+/// 252 = trading days per year
+///
+/// Market Applications:
+/// - High-frequency trading
+/// - Options pricing
+/// - Risk forecasting
+/// - Market microstructure
+/// - Volatility trading
+///
+/// Sources:
+/// Andersen, Bollerslev - "Answering the Skeptics"
+/// https://en.wikipedia.org/wiki/Realized_volatility
+///
+/// Note: Efficient implementation using rolling sums
///
+
public class Rv : AbstractBase
{
private readonly int Period;
@@ -17,43 +51,33 @@ public class Rv : AbstractBase
private double _previousClose;
private double _sumSquaredReturns;
- ///
- /// Initializes a new instance of the Realized class with the specified period and annualization flag.
- ///
- /// The period over which to calculate realized volatility.
- /// Whether to annualize the volatility (default is true).
- ///
- /// Thrown when period is less than 2.
- ///
+ /// The number of periods for volatility calculation.
+ /// Whether to annualize the result (default true).
+ /// Thrown when period is less than 2.
public Rv(int period, bool isAnnualized = true)
{
if (period < 2)
{
- throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 2.");
+ throw new ArgumentOutOfRangeException(nameof(period),
+ "Period must be greater than or equal to 2.");
}
Period = period;
IsAnnualized = isAnnualized;
- WarmupPeriod = period + 1; // We need one extra data point to calculate the first return
+ WarmupPeriod = period + 1; // Need extra point for first return
_returns = new CircularBuffer(period);
Name = $"Realized(period={period}, annualized={isAnnualized})";
Init();
}
- ///
- /// Initializes a new instance of the Realized class with a data source.
- ///
- /// The source object that publishes data.
- /// The period over which to calculate realized volatility.
- /// Whether to annualize the volatility (default is true).
+ /// The data source object that publishes updates.
+ /// The number of periods for volatility calculation.
+ /// Whether to annualize the result (default true).
public Rv(object source, int period, bool isAnnualized = true) : this(period, isAnnualized)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
- ///
- /// Initializes the Realized instance by clearing buffers and resetting calculation variables.
- ///
public override void Init()
{
base.Init();
@@ -62,10 +86,6 @@ public class Rv : AbstractBase
_sumSquaredReturns = 0;
}
- ///
- /// Manages the state of the Realized instance based on whether a new value is being processed.
- ///
- /// Indicates whether the current input is a new value.
protected override void ManageState(bool isNew)
{
if (isNew)
@@ -75,21 +95,6 @@ public class Rv : AbstractBase
}
}
- ///
- /// Performs the realized volatility calculation for the current period.
- ///
- ///
- /// The calculated realized volatility value for the current period.
- ///
- ///
- /// This method calculates the volatility using the following steps:
- /// 1. Compute logarithmic returns.
- /// 2. Maintain a rolling sum of squared returns.
- /// 3. Calculate the variance using the sum of squared returns.
- /// 4. Take the square root of the variance to get volatility.
- /// 5. If annualized, multiply by the square root of 252 (assumed trading days in a year).
- /// The method returns 0 until enough data points are available for the calculation.
- ///
protected override double Calculation()
{
ManageState(Input.IsNew);
@@ -97,26 +102,28 @@ public class Rv : AbstractBase
double volatility = 0;
if (_previousClose != 0)
{
+ // Calculate log return
double logReturn = Math.Log(Input.Value / _previousClose);
if (_returns.Count == Period)
{
- // Remove the oldest squared return from the sum
+ // Maintain rolling sum by removing oldest squared return
_sumSquaredReturns -= Math.Pow(_returns[0], 2);
}
+ // Add new return and update sum
_returns.Add(logReturn, Input.IsNew);
_sumSquaredReturns += Math.Pow(logReturn, 2);
if (_returns.Count == Period)
{
+ // Calculate realized volatility
double variance = _sumSquaredReturns / Period;
volatility = Math.Sqrt(variance);
if (IsAnnualized)
{
- // Assuming 252 trading days in a year. Adjust as needed.
- volatility *= Math.Sqrt(252);
+ volatility *= Math.Sqrt(252); // Annualize using trading days
}
}
}
diff --git a/lib/volatility/Rvi.cs b/lib/volatility/Rvi.cs
index 7047176c..f6468816 100644
--- a/lib/volatility/Rvi.cs
+++ b/lib/volatility/Rvi.cs
@@ -1,36 +1,61 @@
+using System;
namespace QuanTAlib;
///
-/// Represents a Relative Volatility Index (RVI) calculator, which measures the direction
-/// of volatility in relation to price movements.
+/// RVI: Relative Volatility Index
+/// A technical indicator developed by Donald Dorsey that measures the direction
+/// of volatility by comparing upward and downward price movements. RVI helps
+/// identify whether volatility is increasing more in up or down moves.
///
///
-/// The RVI was introduced by Donald Dorsey in the 1993 issue of Technical Analysis
-/// of Stocks & Commodities Magazine. It focuses on the direction of price movements
-/// in relation to volatility. The indicator uses standard deviation calculations
-/// to determine whether volatility is increasing more in up moves or down moves.
+/// The RVI calculation process:
+/// 1. Separates price changes into up/down moves
+/// 2. Calculates standard deviation for each
+/// 3. Applies moving average smoothing
+/// 4. Computes relative strength ratio
+/// 5. Scales to percentage (0-100)
///
-/// This implementation uses a combination of Standard Deviation and Simple Moving Average
-/// calculations to compute the RVI.
+/// Key characteristics:
+/// - Oscillator (0-100 range)
+/// - Directional volatility measure
+/// - Combines volatility and momentum
+/// - Uses standard deviation
+/// - Smoothed output
+///
+/// Formula:
+/// RVI = 100 * SMA(StdDev(upMoves)) / (SMA(StdDev(upMoves)) + SMA(StdDev(downMoves)))
+/// where:
+/// upMove = max(close - prevClose, 0)
+/// downMove = max(prevClose - close, 0)
+///
+/// Market Applications:
+/// - Trend confirmation
+/// - Divergence analysis
+/// - Volatility breakouts
+/// - Market reversals
+/// - Overbought/oversold levels
+///
+/// Sources:
+/// Donald Dorsey - "Technical Analysis of Stocks & Commodities" (1993)
+/// https://www.investopedia.com/terms/r/relative_volatility_index.asp
+///
+/// Note: Similar concept to RSI but using volatility
///
+
public class Rvi : AbstractBase
{
private readonly Stddev _upStdDev, _downStdDev;
private readonly Sma _upSma, _downSma;
private double _previousClose;
- ///
- /// Initializes a new instance of the Rvi class with the specified period.
- ///
- /// The period over which to calculate the RVI.
- ///
- /// Thrown when period is less than 2.
- ///
+ /// The number of periods for RVI calculation.
+ /// Thrown when period is less than 2.
public Rvi(int period)
{
if (period < 2)
{
- throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 2.");
+ throw new ArgumentOutOfRangeException(nameof(period),
+ "Period must be greater than or equal to 2.");
}
int Period = period;
WarmupPeriod = period;
@@ -42,30 +67,20 @@ public class Rvi : AbstractBase
Init();
}
- ///
- /// Initializes a new instance of the Rvi class with the specified source and period.
- ///
- /// The source object to subscribe to for value updates.
- /// The period over which to calculate the RVI.
+ /// The data source object that publishes updates.
+ /// The number of periods for RVI calculation.
public Rvi(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
- ///
- /// Initializes the Rvi instance by setting up the initial state.
- ///
public override void Init()
{
base.Init();
_previousClose = 0;
}
- ///
- /// Manages the state of the Rvi instance based on whether a new value is being processed.
- ///
- /// Indicates whether the current input is a new value.
protected override void ManageState(bool isNew)
{
if (isNew)
@@ -75,21 +90,6 @@ public class Rvi : AbstractBase
}
}
- ///
- /// Performs the RVI calculation for the current input.
- ///
- ///
- /// The calculated RVI value for the current input.
- ///
- ///
- /// This method calculates the RVI using the following steps:
- /// 1. Calculate the change in price from the previous close.
- /// 2. Determine the up move and down move based on the change.
- /// 3. Calculate standard deviations of up and down moves.
- /// 4. Apply a simple moving average to the standard deviations.
- /// 5. Compute the RVI as a percentage of up volatility to total volatility.
- /// The method returns 0 if the sum of up and down volatility is zero.
- ///
protected override double Calculation()
{
ManageState(Input.IsNew);
@@ -97,14 +97,19 @@ public class Rvi : AbstractBase
double close = Input.Value;
double change = close - _previousClose;
+ // Separate into up and down moves
double upMove = Math.Max(change, 0);
double downMove = Math.Max(-change, 0);
+ // Calculate standard deviations and apply smoothing
_upSma.Calc(_upStdDev.Calc(new TValue(Input.Time, upMove, Input.IsNew)));
_downSma.Calc(_downStdDev.Calc(new TValue(Input.Time, downMove, Input.IsNew)));
+ // Calculate RVI ratio
double rvi;
- rvi = (_upSma.Value + _downSma.Value != 0) ? 100 * _upSma.Value / (_upSma.Value + _downSma.Value) : 0;
+ rvi = (_upSma.Value + _downSma.Value != 0)
+ ? 100 * _upSma.Value / (_upSma.Value + _downSma.Value)
+ : 0;
_previousClose = close;
IsHot = _index >= WarmupPeriod;