From b2fcdda7855fa8de82b3ca154e885932e993cbce Mon Sep 17 00:00:00 2001 From: Miha Date: Sun, 27 Oct 2024 09:38:53 -0700 Subject: [PATCH] xml doc rewrite --- .github/workflows/Publish.yml | 9 ++- .sonarlint/QuanTAlib.json | 2 +- .vscode/launch.json | 33 ++++++++++ .vscode/settings.json | 4 +- .vscode/tasks.json | 62 ++++++++++++++++++ Tests/Tests.csproj | 4 ++ lib/averages/Afirma.cs | 21 ++++++- lib/averages/Convolution.cs | 17 ++++- lib/averages/Epma.cs | 34 +++++++++- lib/averages/Frama.cs | 29 ++++++++- lib/averages/Fwma.cs | 35 ++++++++++- lib/averages/Gma.cs | 36 ++++++++++- lib/averages/Hma.cs | 37 ++++++++++- lib/averages/Htit.cs | 40 ++++++++++-- lib/averages/Hwma.cs | 42 ++++++++++++- lib/averages/Jma.cs | 68 +++++++++++--------- lib/averages/Kama.cs | 35 ++++++++++- lib/averages/Ltma.cs | 33 +++++++++- lib/averages/Maaf.cs | 37 +++++++++-- lib/averages/Mama.cs | 35 +++++++++++ lib/averages/Mgdi.cs | 34 +++++++++- lib/averages/Mma.cs | 34 ++++++++++ lib/averages/Pwma.cs | 36 +++++++++++ lib/averages/Qema.cs | 40 +++++++++++- lib/averages/Rema.cs | 42 ++++++++++++- lib/averages/Sinema.cs | 37 ++++++++++- lib/averages/Sma.cs | 36 +++++++++-- lib/averages/Smma.cs | 32 +++++++++- lib/averages/T3.cs | 38 ++++++++++- lib/averages/Tema.cs | 34 +++++++++- lib/averages/Trima.cs | 37 ++++++++++- lib/averages/Vidya.cs | 41 +++++++++++- lib/averages/Wma.cs | 38 ++++++++++- lib/averages/Zlema.cs | 32 ++++++++++ lib/errors/Huber.cs | 40 +++++++++++- lib/errors/Mae.cs | 34 +++++++++- lib/errors/Mapd.cs | 36 ++++++++++- lib/errors/Mape.cs | 35 +++++++++++ lib/errors/Mase.cs | 63 ++++++++++++------- lib/errors/Mda.cs | 35 +++++++++++ lib/errors/Me.cs | 35 +++++++++++ lib/errors/Mpe.cs | 36 +++++++++++ lib/errors/Mse.cs | 65 +++++++++---------- lib/errors/Msle.cs | 36 +++++++++++ lib/errors/Rae.cs | 35 +++++++++++ lib/errors/Rmse.cs | 36 +++++++++++ lib/errors/Rmsle.cs | 37 +++++++++++ lib/errors/Rse.cs | 36 +++++++++++ lib/errors/Rsquared.cs | 36 +++++++++++ lib/errors/Smape.cs | 35 +++++++++++ lib/oscillators/Cmo.cs | 49 ++++++++++++--- lib/oscillators/Rsi.cs | 46 +++++++++++--- lib/oscillators/Rsx.cs | 53 +++++++++++++--- lib/statistics/Curvature.cs | 71 ++++++++++----------- lib/statistics/Entropy.cs | 93 ++++++++++++++------------- lib/statistics/Kurtosis.cs | 99 +++++++++++++---------------- lib/statistics/Max.cs | 111 +++++++++++++------------------- lib/statistics/Median.cs | 85 +++++++++++++------------ lib/statistics/Min.cs | 111 +++++++++++++------------------- lib/statistics/Mode.cs | 100 ++++++++++++++--------------- lib/statistics/Percentile.cs | 101 ++++++++++++++--------------- lib/statistics/Skew.cs | 109 +++++++++++++++----------------- lib/statistics/Slope.cs | 114 +++++++++++++++------------------ lib/statistics/Stddev.cs | 114 +++++++++++++++------------------ lib/statistics/Variance.cs | 115 +++++++++++++++------------------- lib/statistics/Zscore.cs | 113 +++++++++++++++------------------ lib/volatility/Atr.cs | 83 +++++++++++++----------- lib/volatility/Hv.cs | 99 ++++++++++++++++------------- lib/volatility/Jvolty.cs | 97 ++++++++++++++++------------ lib/volatility/Rv.cs | 99 +++++++++++++++-------------- lib/volatility/Rvi.cs | 93 ++++++++++++++------------- 71 files changed, 2607 insertions(+), 1102 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 .vscode/tasks.json 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;