xml doc rewrite

This commit is contained in:
Miha
2024-10-27 09:38:53 -07:00
parent c21b96152c
commit b2fcdda785
71 changed files with 2607 additions and 1102 deletions
+4 -5
View File
@@ -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
+1 -1
View File
@@ -1,4 +1,4 @@
{
"sonarCloudOrganization": "mihakralj-quantalib",
"sonarCloudOrganization": "mihakralj",
"projectKey": "mihakralj_QuanTAlib"
}
+33
View File
@@ -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
}
}
]
}
+2 -2
View File
@@ -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"
+62
View File
@@ -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"
}
]
}
+4
View File
@@ -4,6 +4,10 @@
<AssemblyName>QuanTAlib.Tests</AssemblyName>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.msbuild" Version="6.0.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="xunit" Version="2.9.2" />
<PackageReference Include="coverlet.collector" Version="6.0.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.0.0-pre.35">
+19 -2
View File
@@ -1,8 +1,18 @@
using System;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// Implementation:
/// Original implementation based on FIR filter design principles
/// </remarks>
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;
/// <param name="periods">The number of periods for the sinc filter calculation.</param>
/// <param name="taps">The number of filter taps (filter length). Must be odd number.</param>
/// <param name="window">The type of window function to apply (Rectangular, Hanning1, Hanning2, Blackman, or BlackmanHarris).</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when periods or taps is less than 1.</exception>
public Afirma(int periods, int taps, WindowType window)
{
if (periods < 1)
@@ -54,6 +68,10 @@ public class Afirma : AbstractBase
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="periods">The number of periods for the sinc filter calculation.</param>
/// <param name="taps">The number of filter taps (filter length). Must be odd number.</param>
/// <param name="window">The type of window function to apply (Rectangular, Hanning1, Hanning2, Blackman, or BlackmanHarris).</param>
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;
}
}
+16 -1
View File
@@ -1,5 +1,16 @@
using System;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// Implementation:
/// Based on standard discrete convolution principles from signal processing
/// </remarks>
public class Convolution : AbstractBase
{
private readonly double[] _kernel;
@@ -7,6 +18,8 @@ public class Convolution : AbstractBase
private readonly CircularBuffer _buffer;
private readonly double[] _normalizedKernel;
/// <param name="kernel">Array of weights defining the convolution operation. The length of this array determines the filter's window size.</param>
/// <exception cref="ArgumentException">Thrown when kernel is null or empty.</exception>
public Convolution(double[] kernel)
{
if (kernel == null || kernel.Length == 0)
@@ -20,6 +33,8 @@ public class Convolution : AbstractBase
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="kernel">Array of weights defining the convolution operation.</param>
public Convolution(object source, double[] kernel) : this(kernel)
{
var pubEvent = source.GetType().GetEvent("Pub");
@@ -100,4 +115,4 @@ public class Convolution : AbstractBase
return sum;
}
}
}
+33 -1
View File
@@ -1,10 +1,35 @@
using System;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
public class Epma : AbstractBase
{
private readonly int _period;
private readonly Convolution _convolution;
/// <param name="period">The number of data points used in the EPMA calculation.</param>
/// <exception cref="ArgumentException">Thrown when period is less than 1.</exception>
public Epma(int period)
{
if (period < 1)
@@ -18,6 +43,8 @@ public class Epma : AbstractBase
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of data points used in the EPMA calculation.</param>
public Epma(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
@@ -60,6 +87,11 @@ public class Epma : AbstractBase
return result;
}
/// <summary>
/// Generates the convolution kernel for the EPMA calculation.
/// </summary>
/// <param name="period">The period for which to generate the kernel.</param>
/// <returns>An array of normalized weights for the convolution operation.</returns>
public static double[] GenerateKernel(int period)
{
double[] kernel = new double[period];
@@ -79,4 +111,4 @@ public class Epma : AbstractBase
return kernel;
}
}
}
+28 -1
View File
@@ -2,6 +2,29 @@ using System;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
public class Frama : AbstractBase
{
private readonly int _period;
@@ -9,6 +32,8 @@ public class Frama : AbstractBase
private double _lastFrama;
private double _prevLastFrama;
/// <param name="period">The number of periods used for fractal dimension calculation. Must be at least 2.</param>
/// <exception cref="ArgumentException">Thrown when period is less than 2.</exception>
public Frama(int period)
{
if (period < 2)
@@ -19,6 +44,8 @@ public class Frama : AbstractBase
WarmupPeriod = period;
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of periods used for fractal dimension calculation.</param>
public Frama(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
@@ -99,4 +126,4 @@ public class Frama : AbstractBase
{
return _lastFrama;
}
}
}
+34 -1
View File
@@ -1,9 +1,35 @@
using System;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
public class Fwma : AbstractBase
{
private readonly Convolution _convolution;
/// <param name="period">The number of data points used in the FWMA calculation.</param>
/// <exception cref="ArgumentException">Thrown when period is less than 1.</exception>
public Fwma(int period)
{
if (period < 1)
@@ -16,12 +42,19 @@ public class Fwma : AbstractBase
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of data points used in the FWMA calculation.</param>
public Fwma(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
/// <summary>
/// Generates the Fibonacci-based convolution kernel for the FWMA calculation.
/// </summary>
/// <param name="period">The period for which to generate the kernel.</param>
/// <returns>An array of normalized Fibonacci-based weights for the convolution operation.</returns>
public static double[] GenerateKernel(int period)
{
double[] kernel = new double[period];
@@ -78,4 +111,4 @@ public class Fwma : AbstractBase
return result;
}
}
}
+35 -1
View File
@@ -1,9 +1,35 @@
using System;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
public class Gma : AbstractBase
{
private readonly Convolution _convolution;
/// <param name="period">The number of data points used in the GMA calculation.</param>
/// <exception cref="ArgumentException">Thrown when period is less than 1.</exception>
public Gma(int period)
{
if (period < 1)
@@ -16,12 +42,20 @@ public class Gma : AbstractBase
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of data points used in the GMA calculation.</param>
public Gma(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
/// <summary>
/// Generates the Gaussian-based convolution kernel for the GMA calculation.
/// </summary>
/// <param name="period">The period for which to generate the kernel.</param>
/// <param name="sigma">The standard deviation parameter controlling the spread of the Gaussian curve. Default is 1.0.</param>
/// <returns>An array of normalized Gaussian-based weights for the convolution operation.</returns>
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;
}
}
}
+36 -1
View File
@@ -1,9 +1,37 @@
using System;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
public class Hma : AbstractBase
{
private readonly Convolution _wmaHalf, _wmaFull, _wmaFinal;
/// <param name="period">The number of data points used in the HMA calculation. Must be at least 2.</param>
/// <exception cref="ArgumentException">Thrown when period is less than 2.</exception>
public Hma(int period)
{
if (period < 2)
@@ -19,12 +47,19 @@ public class Hma : AbstractBase
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of data points used in the HMA calculation.</param>
public Hma(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
/// <summary>
/// Generates the weighted moving average kernel for the HMA calculation.
/// </summary>
/// <param name="period">The period for which to generate the kernel.</param>
/// <returns>An array of linearly weighted values for the convolution operation.</returns>
private static double[] GenerateWmaKernel(int period)
{
double[] kernel = new double[period];
@@ -72,4 +107,4 @@ public class Hma : AbstractBase
IsHot = _index >= WarmupPeriod;
return result;
}
}
}
+36 -4
View File
@@ -1,8 +1,32 @@
//not working yet
//TODO fails consistency test
using System;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
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;
/// <summary>
/// Initializes a new instance of the Htit class.
/// </summary>
public Htit()
{
Name = "Htit";
WarmupPeriod = 12;
}
/// <summary>
/// Initializes a new instance of the Htit class with a specified source.
/// </summary>
/// <param name="source">The data source object that publishes updates.</param>
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;
}
}
}
+40 -2
View File
@@ -1,5 +1,33 @@
using System;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
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;
/// <param name="period">The number of data points used in the HWMA calculation.</param>
public Hwma(int period) : this(period, 2.0 / (1 + period), 1.0 / period, 1.0 / period)
{
}
/// <param name="nA">Alpha smoothing factor for the level component.</param>
/// <param name="nB">Beta smoothing factor for the velocity component.</param>
/// <param name="nC">Gamma smoothing factor for the acceleration component.</param>
public Hwma(double nA, double nB, double nC) : this((int)((2 - nA) / nA), nA, nB, nC)
{
}
/// <param name="period">The number of data points used in the HWMA calculation.</param>
/// <param name="nA">Alpha smoothing factor for the level component.</param>
/// <param name="nB">Beta smoothing factor for the velocity component.</param>
/// <param name="nC">Gamma smoothing factor for the acceleration component.</param>
/// <exception cref="ArgumentException">Thrown when period is less than 1.</exception>
public Hwma(int period, double nA, double nB, double nC)
{
if (period < 1)
@@ -30,6 +67,8 @@ public class Hwma : AbstractBase
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of data points used in the HWMA calculation.</param>
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;
}
}
}
+39 -29
View File
@@ -1,9 +1,31 @@
/// <summary>
/// Represents a Jurik Moving Average, based on known and reverse-engineered insights
/// </summary>
using System;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
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
/// <summary>
/// Initializes a new instance of the Jma class with the specified parameters.
/// </summary>
/// <param name="period">The period over which to calculate the Jvolty.</param>
/// <param name="phase">The phase parameter for the JMA-style calculation.</param>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when period is less than 1.
/// </exception>
/// <param name="period">The period over which to calculate the JMA.</param>
/// <param name="phase">The phase parameter (-100 to +100) controlling lag compensation.</param>
/// <param name="factor">The factor controlling volatility adaptation (default 0.45).</param>
/// <param name="buffer">The size of the volatility buffer (default 10).</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
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
}
/// <summary>
/// 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.
/// </summary>
/// <param name="source">The source object to subscribe to for value updates.</param>
/// <param name="period">The period over which to calculate the Jvolty.</param>
/// <param name="phase">The phase parameter for the JMA-style calculation.</param>
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The period over which to calculate the JMA.</param>
/// <param name="phase">The phase parameter (-100 to +100) controlling lag compensation.</param>
/// <param name="factor">The factor controlling volatility adaptation (default 0.45).</param>
/// <param name="buffer">The size of the volatility buffer (default 10).</param>
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));
}
/// <summary>
/// Initializes the Jma instance by setting up the initial state.
/// </summary>
public override void Init()
{
base.Init();
@@ -76,10 +96,6 @@ public class Jma : AbstractBase
_vsumBuff.Clear();
}
/// <summary>
/// Manages the state of the Jma instance based on whether a new value is being processed.
/// </summary>
/// <param name="isNew">Indicates whether the current input is a new value.</param>
protected override void ManageState(bool isNew)
{
if (isNew)
@@ -105,12 +121,6 @@ public class Jma : AbstractBase
}
}
/// <summary>
/// Performs the Jma calculation for the current value.
/// </summary>
/// <returns>
/// The calculated Jma value for the current input.
/// </returns>
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;
+34 -1
View File
@@ -1,5 +1,30 @@
using System;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
public class Kama : AbstractBase
{
private readonly int _period;
@@ -7,6 +32,10 @@ public class Kama : AbstractBase
private CircularBuffer? _buffer;
private double _lastKama, _p_lastKama;
/// <param name="period">The number of periods used to calculate the Efficiency Ratio.</param>
/// <param name="fast">The number of periods for the fastest EMA response (default 2).</param>
/// <param name="slow">The number of periods for the slowest EMA response (default 30).</param>
/// <exception cref="ArgumentException">Thrown when period is less than 1.</exception>
public Kama(int period, int fast = 2, int slow = 30)
{
if (period < 1)
@@ -21,6 +50,10 @@ public class Kama : AbstractBase
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of periods used to calculate the Efficiency Ratio.</param>
/// <param name="fast">The number of periods for the fastest EMA response (default 2).</param>
/// <param name="slow">The number of periods for the slowest EMA response (default 30).</param>
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;
}
}
}
+32 -1
View File
@@ -1,6 +1,30 @@
using System;
namespace QuanTAlib;
// https://www.mesasoftware.com/papers/TimeWarp.pdf
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
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;
/// <summary>
/// Gets the gamma parameter value used in the Laguerre filter.
/// </summary>
public double Gamma => _gamma;
/// <param name="gamma">The damping factor (0 to 1) controlling the smoothing. Lower values provide more smoothing.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when gamma is not between 0 and 1.</exception>
public Ltma(double gamma = 0.1)
{
if (gamma < 0 || gamma > 1)
@@ -20,6 +49,8 @@ public class Ltma : AbstractBase
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="gamma">The damping factor (0 to 1) controlling the smoothing.</param>
public Ltma(object source, double gamma = 0.1) : this(gamma)
{
var pubEvent = source.GetType().GetEvent("Pub");
+33 -4
View File
@@ -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
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
public class Maaf : AbstractBase
{
@@ -14,6 +39,8 @@ public class Maaf : AbstractBase
private readonly int _period;
/// <param name="period">The initial period for the filter (default 39).</param>
/// <param name="threshold">The threshold for adaptive adjustment (default 0.002).</param>
public Maaf(int period = 39, double threshold = 0.002)
{
_period = period;
@@ -25,6 +52,9 @@ public class Maaf : AbstractBase
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The initial period for the filter (default 39).</param>
/// <param name="threshold">The threshold for adaptive adjustment (default 0.002).</param>
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;
+35
View File
@@ -1,5 +1,32 @@
using System;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
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;
/// <summary>
/// Gets the Following Adaptive Moving Average (FAMA) value.
/// </summary>
public TValue Fama { get; private set; }
/// <param name="fastLimit">The maximum adaptation speed (default 0.5).</param>
/// <param name="slowLimit">The minimum adaptation speed (default 0.05).</param>
public Mama(double fastLimit = 0.5, double slowLimit = 0.05)
{
Fama = new TValue();
@@ -30,6 +62,9 @@ public class Mama : AbstractBase
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="fastLimit">The maximum adaptation speed (default 0.5).</param>
/// <param name="slowLimit">The minimum adaptation speed (default 0.05).</param>
public Mama(object source, double fastLimit = 0.5, double slowLimit = 0.05) : this(fastLimit, slowLimit)
{
var pubEvent = source.GetType().GetEvent("Pub");
+33 -1
View File
@@ -1,10 +1,39 @@
using System;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
public class Mgdi : AbstractBase
{
private readonly int _period;
private readonly double _kFactor;
private double _prevMd, _p_prevMd;
/// <param name="period">The number of periods used in the MGDI calculation.</param>
/// <param name="kFactor">The K-factor controlling the decay rate adjustment (default 0.6).</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period or kFactor is less than or equal to 0.</exception>
public Mgdi(int period, double kFactor = 0.6)
{
if (period <= 0)
@@ -22,6 +51,9 @@ public class Mgdi : AbstractBase
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of periods used in the MGDI calculation.</param>
/// <param name="kFactor">The K-factor controlling the decay rate adjustment (default 0.6).</param>
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;
}
}
}
+34
View File
@@ -1,11 +1,38 @@
using System;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
public class Mma : AbstractBase
{
private readonly int _period;
private readonly CircularBuffer _buffer;
private double _lastMma;
/// <param name="period">The number of periods used in the MMA calculation. Must be at least 2.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 2.</exception>
public Mma(int period)
{
if (period < 2)
@@ -19,6 +46,8 @@ public class Mma : AbstractBase
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of periods used in the MMA calculation.</param>
public Mma(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
@@ -61,6 +90,11 @@ public class Mma : AbstractBase
return _lastMma;
}
/// <summary>
/// Calculates the weighted sum component of the MMA.
/// The weights are symmetric around the center, decreasing linearly from the center outward.
/// </summary>
/// <returns>The weighted sum of the data points.</returns>
private double CalculateWeightedSum()
{
double sum = 0;
+36
View File
@@ -1,10 +1,39 @@
using System;
using System.Linq;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
public class Pwma : AbstractBase
{
private readonly int _period;
private readonly Convolution _convolution;
/// <param name="period">The number of data points used in the PWMA calculation.</param>
/// <exception cref="ArgumentException">Thrown when period is less than 1.</exception>
public Pwma(int period)
{
if (period < 1)
@@ -18,6 +47,8 @@ public class Pwma : AbstractBase
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of data points used in the PWMA calculation.</param>
public Pwma(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
@@ -60,6 +91,11 @@ public class Pwma : AbstractBase
return result;
}
/// <summary>
/// Generates the Pascal's triangle-based convolution kernel for the PWMA calculation.
/// </summary>
/// <param name="period">The period for which to generate the kernel.</param>
/// <returns>An array of normalized Pascal's triangle-based weights for the convolution operation.</returns>
public static double[] GenerateKernel(int period)
{
double[] kernel = new double[period];
+38 -2
View File
@@ -1,11 +1,42 @@
using System;
namespace QuanTAlib;
/// <summary>
/// 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
/// </summary>
/// <remarks>
/// 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
/// </remarks>
public class Qema : AbstractBase
{
private readonly Ema _ema1, _ema2, _ema3, _ema4;
private double _lastQema, _p_lastQema;
/// <param name="k1">Smoothing factor for first EMA (default 0.2).</param>
/// <param name="k2">Smoothing factor for second EMA (default 0.2).</param>
/// <param name="k3">Smoothing factor for third EMA (default 0.2).</param>
/// <param name="k4">Smoothing factor for fourth EMA (default 0.2).</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when any k value is less than or equal to 0.</exception>
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();
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="k1">Smoothing factor for first EMA.</param>
/// <param name="k2">Smoothing factor for second EMA.</param>
/// <param name="k3">Smoothing factor for third EMA.</param>
/// <param name="k4">Smoothing factor for fourth EMA.</param>
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;
}
}
}
+40 -2
View File
@@ -1,6 +1,29 @@
using System;
namespace QuanTAlib;
//https://user42.tuxfamily.org/chart/manual/Regularized-Exponential-Moving-Average.html
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
public class Rema : AbstractBase
{
@@ -9,9 +32,19 @@ public class Rema : AbstractBase
private double _lastRema, _prevRema;
private double _savedLastRema, _savedPrevRema;
/// <summary>
/// Gets the period used in the REMA calculation.
/// </summary>
public int Period => _period;
/// <summary>
/// Gets the lambda (regularization) parameter value.
/// </summary>
public double Lambda => _lambda;
/// <param name="period">The number of periods used in the REMA calculation.</param>
/// <param name="lambda">The regularization parameter (default 0.5). Higher values increase smoothing.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1 or lambda is negative.</exception>
public Rema(int period, double lambda = 0.5)
{
if (period < 1)
@@ -25,11 +58,16 @@ public class Rema : AbstractBase
WarmupPeriod = period;
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of periods used in the REMA calculation.</param>
/// <param name="lambda">The regularization parameter (default 0.5).</param>
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;
}
}
}
+36 -1
View File
@@ -1,9 +1,37 @@
using System;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
public class Sinema : AbstractBase
{
private readonly Convolution _convolution;
/// <param name="period">The number of data points used in the SINEMA calculation.</param>
/// <exception cref="ArgumentException">Thrown when period is less than 1.</exception>
public Sinema(int period)
{
if (period < 1)
@@ -16,6 +44,8 @@ public class Sinema : AbstractBase
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of data points used in the SINEMA calculation.</param>
public Sinema(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
@@ -50,6 +80,11 @@ public class Sinema : AbstractBase
return result;
}
/// <summary>
/// Generates the sine-based convolution kernel for the SINEMA calculation.
/// </summary>
/// <param name="period">The period for which to generate the kernel.</param>
/// <returns>An array of normalized sine-based weights for the convolution operation.</returns>
public static double[] GenerateKernel(int period)
{
double[] kernel = new double[period];
@@ -70,4 +105,4 @@ public class Sinema : AbstractBase
return kernel;
}
}
}
+32 -4
View File
@@ -1,11 +1,38 @@
using System;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
public class Sma : AbstractBase
{
// inherited _index
// inherited _value
private readonly CircularBuffer _buffer;
/// <param name="period">The number of data points used in the SMA calculation.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
public Sma(int period)
{
if (period < 1)
@@ -19,12 +46,13 @@ public class Sma : AbstractBase
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of data points used in the SMA calculation.</param>
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
}
/// <summary>
/// Core SMA calculation - using _buffer.Average
/// Performs the core SMA calculation using the circular buffer's average.
/// </summary>
/// <returns>The calculated SMA value.</returns>
protected override double Calculation()
{
double result;
@@ -49,4 +77,4 @@ public class Sma : AbstractBase
IsHot = _index >= WarmupPeriod;
return result;
}
}
}
+29 -3
View File
@@ -1,13 +1,38 @@
using System;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
public class Smma : AbstractBase
{
private readonly int _period;
private CircularBuffer? _buffer;
private double _lastSmma, _p_lastSmma;
/// <param name="period">The number of data points used in the SMMA calculation.</param>
/// <exception cref="ArgumentException">Thrown when period is less than 1.</exception>
public Smma(int period)
{
if (period < 1)
@@ -20,6 +45,8 @@ public class Smma : AbstractBase
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of data points used in the SMMA calculation.</param>
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;
}
}
}
+35 -3
View File
@@ -1,5 +1,31 @@
using System;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
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;
/// <param name="period">The number of periods used in each EMA calculation.</param>
/// <param name="vfactor">Volume factor controlling smoothing (default 0.7).</param>
/// <param name="useSma">Whether to use SMA for initial values (default true).</param>
/// <exception cref="ArgumentException">Thrown when period is less than 1.</exception>
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();
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of periods used in each EMA calculation.</param>
/// <param name="vfactor">Volume factor controlling smoothing (default 0.7).</param>
/// <param name="useSma">Whether to use SMA for initial values (default true).</param>
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;
}
}
}
+31 -3
View File
@@ -1,5 +1,31 @@
using System;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
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;
/// <param name="period">The number of periods used in each EMA calculation.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
public Tema(int period)
{
if (period < 1)
@@ -21,6 +49,8 @@ public class Tema : AbstractBase
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of periods used in each EMA calculation.</param>
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;
}
}
}
+36 -1
View File
@@ -1,9 +1,37 @@
using System;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
public class Trima : AbstractBase
{
private readonly Convolution _convolution;
/// <param name="period">The number of data points used in the TRIMA calculation.</param>
/// <exception cref="ArgumentException">Thrown when period is less than 1.</exception>
public Trima(int period)
{
if (period < 1)
@@ -16,12 +44,19 @@ public class Trima : AbstractBase
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of data points used in the TRIMA calculation.</param>
public Trima(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
/// <summary>
/// Generates the triangular-shaped convolution kernel for the TRIMA calculation.
/// </summary>
/// <param name="period">The period for which to generate the kernel.</param>
/// <returns>An array of normalized triangular weights for the convolution operation.</returns>
private static double[] GenerateKernel(int period)
{
double[] kernel = new double[period];
@@ -70,4 +105,4 @@ public class Trima : AbstractBase
return result;
}
}
}
+39 -2
View File
@@ -4,15 +4,43 @@ using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
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;
/// <param name="shortPeriod">The number of periods for short-term volatility calculation.</param>
/// <param name="longPeriod">The number of periods for long-term volatility calculation (default is 4x shortPeriod).</param>
/// <param name="alpha">The alpha parameter controlling the base smoothing factor (default 0.2).</param>
/// <exception cref="ArgumentException">Thrown when shortPeriod is less than 1.</exception>
public Vidya(int shortPeriod, int longPeriod = 0, double alpha = 0.2)
{
if (shortPeriod < 1)
@@ -28,6 +56,10 @@ public class Vidya : AbstractBase
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="shortPeriod">The number of periods for short-term volatility calculation.</param>
/// <param name="longPeriod">The number of periods for long-term volatility calculation (default is 4x shortPeriod).</param>
/// <param name="alpha">The alpha parameter controlling the base smoothing factor (default 0.2).</param>
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;
}
/// <summary>
/// Calculates the standard deviation of values in a circular buffer.
/// </summary>
/// <param name="buffer">The circular buffer containing the values.</param>
/// <returns>The standard deviation of the values in the buffer.</returns>
[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);
}
}
}
+37 -1
View File
@@ -1,10 +1,39 @@
using System;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
public class Wma : AbstractBase
{
private readonly int _period;
private readonly Convolution _convolution;
/// <param name="period">The number of data points used in the WMA calculation.</param>
/// <exception cref="ArgumentException">Thrown when period is less than 1.</exception>
public Wma(int period)
{
if (period < 1)
@@ -18,12 +47,19 @@ public class Wma : AbstractBase
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of data points used in the WMA calculation.</param>
public Wma(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
/// <summary>
/// Generates the linearly weighted convolution kernel for the WMA calculation.
/// </summary>
/// <param name="period">The period for which to generate the kernel.</param>
/// <returns>An array of normalized linearly decreasing weights for the convolution operation.</returns>
private static double[] GenerateWmaKernel(int period)
{
double[] kernel = new double[period];
@@ -64,4 +100,4 @@ public class Wma : AbstractBase
return result;
}
}
}
+32
View File
@@ -3,6 +3,31 @@ using System.Runtime.CompilerServices;
namespace QuanTAlib
{
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
public class Zlema : AbstractBase
{
private readonly CircularBuffer _buffer;
@@ -10,6 +35,8 @@ namespace QuanTAlib
private readonly Ema _ema;
private double _lastZLEMA, _p_lastZLEMA;
/// <param name="period">The number of periods used in the ZLEMA calculation.</param>
/// <exception cref="ArgumentException">Thrown when period is less than 1.</exception>
public Zlema(int period)
{
if (period < 1)
@@ -24,6 +51,8 @@ namespace QuanTAlib
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of periods used in the ZLEMA calculation.</param>
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;
+39 -1
View File
@@ -1,11 +1,44 @@
using System;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
public class Huber : AbstractBase
{
private readonly CircularBuffer _actualBuffer;
private readonly CircularBuffer _predictedBuffer;
private readonly double _delta;
/// <param name="period">The number of points over which to calculate the loss.</param>
/// <param name="delta">The threshold between squared and linear loss (default 1.0).</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1 or delta is not positive.</exception>
public Huber(int period, double delta = 1.0)
{
if (period < 1)
@@ -24,6 +57,9 @@ public class Huber : AbstractBase
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of points over which to calculate the loss.</param>
/// <param name="delta">The threshold between squared and linear loss (default 1.0).</param>
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;
}
}
+33 -1
View File
@@ -1,10 +1,40 @@
using System;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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/
/// </remarks>
public class Mae : AbstractBase
{
private readonly CircularBuffer _actualBuffer;
private readonly CircularBuffer _predictedBuffer;
/// <param name="period">The number of points over which to calculate the MAE.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
public Mae(int period)
{
if (period < 1)
@@ -18,6 +48,8 @@ public class Mae : AbstractBase
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of points over which to calculate the MAE.</param>
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;
}
}
+35 -1
View File
@@ -1,10 +1,42 @@
using System;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
public class Mapd : AbstractBase
{
private readonly CircularBuffer _actualBuffer;
private readonly CircularBuffer _predictedBuffer;
/// <param name="period">The number of points over which to calculate the MAPD.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
public Mapd(int period)
{
if (period < 1)
@@ -18,6 +50,8 @@ public class Mapd : AbstractBase
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of points over which to calculate the MAPD.</param>
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;
}
}
+35
View File
@@ -1,10 +1,42 @@
using System;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
public class Mape : AbstractBase
{
private readonly CircularBuffer _actualBuffer;
private readonly CircularBuffer _predictedBuffer;
/// <param name="period">The number of points over which to calculate the MAPE.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
public Mape(int period)
{
if (period < 1)
@@ -18,6 +50,8 @@ public class Mape : AbstractBase
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of points over which to calculate the MAPE.</param>
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);
+41 -22
View File
@@ -1,20 +1,41 @@
using System;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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/
/// </remarks>
public class Mase : AbstractBase
{
private readonly CircularBuffer _actualBuffer;
private readonly CircularBuffer _predictedBuffer;
private readonly CircularBuffer _naiveBuffer;
/// <summary>
/// Initializes a new instance of the Mase class.
/// </summary>
/// <param name="period">The period for MASE calculation.</param>
/// <param name="period">The number of points over which to calculate the MASE.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
public Mase(int period)
{
@@ -30,20 +51,14 @@ public class Mase : AbstractBase
Init();
}
/// <summary>
/// Initializes a new instance of the Mase class with a source object.
/// </summary>
/// <param name="source">The source object for event subscription.</param>
/// <param name="period">The period for MASE calculation.</param>
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of points over which to calculate the MASE.</param>
public Mase(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
/// <summary>
/// Initializes the Mase instance.
/// </summary>
public override void Init()
{
base.Init();
@@ -52,10 +67,6 @@ public class Mase : AbstractBase
_naiveBuffer.Clear();
}
/// <summary>
/// Manages the state of the Mase instance.
/// </summary>
/// <param name="isNew">Indicates if the input is new.</param>
protected override void ManageState(bool isNew)
{
if (isNew)
@@ -65,10 +76,6 @@ public class Mase : AbstractBase
}
}
/// <summary>
/// Performs the MASE calculation.
/// </summary>
/// <returns>The calculated MASE value.</returns>
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;
}
/// <summary>
/// Calculates the MASE value by comparing forecast error to naive forecast error.
/// </summary>
/// <returns>The calculated MASE value, or positive infinity if naive error is zero.</returns>
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;
}
/// <summary>
/// Calculates the sum of absolute errors between actual and predicted values.
/// </summary>
private static double CalculateSumAbsoluteError(ReadOnlySpan<double> actualValues, ReadOnlySpan<double> predictedValues)
{
double sum = 0;
@@ -114,6 +130,9 @@ public class Mase : AbstractBase
return sum;
}
/// <summary>
/// Calculates the naive forecast error using the previous value as prediction.
/// </summary>
private static double CalculateNaiveForecastError(ReadOnlySpan<double> actualValues, ReadOnlySpan<double> naiveValues)
{
double sum = 0;
+35
View File
@@ -1,10 +1,42 @@
using System;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
public class Mda : AbstractBase
{
private readonly CircularBuffer _actualBuffer;
private readonly CircularBuffer _predictedBuffer;
/// <param name="period">The number of points over which to calculate the MDA.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
public Mda(int period)
{
if (period < 1)
@@ -18,6 +50,8 @@ public class Mda : AbstractBase
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of points over which to calculate the MDA.</param>
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);
+35
View File
@@ -1,10 +1,42 @@
using System;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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)
/// </remarks>
public class Me : AbstractBase
{
private readonly CircularBuffer _actualBuffer;
private readonly CircularBuffer _predictedBuffer;
/// <param name="period">The number of points over which to calculate the ME.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
public Me(int period)
{
if (period < 1)
@@ -18,6 +50,8 @@ public class Me : AbstractBase
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of points over which to calculate the ME.</param>
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);
+36
View File
@@ -1,10 +1,43 @@
using System;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
public class Mpe : AbstractBase
{
private readonly CircularBuffer _actualBuffer;
private readonly CircularBuffer _predictedBuffer;
/// <param name="period">The number of points over which to calculate the MPE.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
public Mpe(int period)
{
if (period < 1)
@@ -18,6 +51,8 @@ public class Mpe : AbstractBase
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of points over which to calculate the MPE.</param>
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);
+31 -34
View File
@@ -1,25 +1,42 @@
using System;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
public class Mse : AbstractBase
{
private readonly CircularBuffer _actualBuffer;
private readonly CircularBuffer _predictedBuffer;
/// <summary>
/// Initializes a new instance of the Mse class with the specified period.
/// </summary>
/// <param name="period">The period over which to calculate the Mean Squared Error.</param>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when period is less than 1.
/// </exception>
/// <param name="period">The number of points over which to calculate the MSE.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
public Mse(int period)
{
if (period < 1)
@@ -33,20 +50,14 @@ public class Mse : AbstractBase
Init();
}
/// <summary>
/// Initializes a new instance of the Mse class with the specified source and period.
/// </summary>
/// <param name="source">The source object to subscribe to for value updates.</param>
/// <param name="period">The period over which to calculate the Mean Squared Error.</param>
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of points over which to calculate the MSE.</param>
public Mse(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
/// <summary>
/// Initializes the Mse instance by clearing the buffers.
/// </summary>
public override void Init()
{
base.Init();
@@ -54,10 +65,6 @@ public class Mse : AbstractBase
_predictedBuffer.Clear();
}
/// <summary>
/// Manages the state of the Mse instance based on whether new values are being processed.
/// </summary>
/// <param name="isNew">Indicates whether the current inputs are new values.</param>
protected override void ManageState(bool isNew)
{
if (isNew)
@@ -67,17 +74,6 @@ public class Mse : AbstractBase
}
}
/// <summary>
/// Performs the Mean Squared Error calculation for the current period.
/// </summary>
/// <returns>
/// The calculated Mean Squared Error value for the current period.
/// </returns>
/// <remarks>
/// 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.
/// </remarks>
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);
+36
View File
@@ -1,10 +1,43 @@
using System;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
public class Msle : AbstractBase
{
private readonly CircularBuffer _actualBuffer;
private readonly CircularBuffer _predictedBuffer;
/// <param name="period">The number of points over which to calculate the MSLE.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
public Msle(int period)
{
if (period < 1)
@@ -18,6 +51,8 @@ public class Msle : AbstractBase
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of points over which to calculate the MSLE.</param>
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);
+35
View File
@@ -1,10 +1,42 @@
using System;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
public class Rae : AbstractBase
{
private readonly CircularBuffer _actualBuffer;
private readonly CircularBuffer _predictedBuffer;
/// <param name="period">The number of points over which to calculate the RAE.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
public Rae(int period)
{
if (period < 1)
@@ -18,6 +50,8 @@ public class Rae : AbstractBase
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of points over which to calculate the RAE.</param>
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);
+36
View File
@@ -1,10 +1,43 @@
using System;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
public class Rmse : AbstractBase
{
private readonly CircularBuffer _actualBuffer;
private readonly CircularBuffer _predictedBuffer;
/// <param name="period">The number of points over which to calculate the RMSE.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
public Rmse(int period)
{
if (period < 1)
@@ -18,6 +51,8 @@ public class Rmse : AbstractBase
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of points over which to calculate the RMSE.</param>
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);
+37
View File
@@ -1,10 +1,44 @@
using System;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
public class Rmsle : AbstractBase
{
private readonly CircularBuffer _actualBuffer;
private readonly CircularBuffer _predictedBuffer;
/// <param name="period">The number of points over which to calculate the RMSLE.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
public Rmsle(int period)
{
if (period < 1)
@@ -18,6 +52,8 @@ public class Rmsle : AbstractBase
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of points over which to calculate the RMSLE.</param>
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);
+36
View File
@@ -1,10 +1,43 @@
using System;
using System.Linq;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
public class Rse : AbstractBase
{
private readonly CircularBuffer _actualBuffer;
private readonly CircularBuffer _predictedBuffer;
/// <param name="period">The number of points over which to calculate the RSE.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
public Rse(int period)
{
if (period < 1)
@@ -18,6 +51,8 @@ public class Rse : AbstractBase
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of points over which to calculate the RSE.</param>
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);
+36
View File
@@ -1,10 +1,43 @@
using System;
using System.Linq;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
public class Rsquared : AbstractBase
{
private readonly CircularBuffer _actualBuffer;
private readonly CircularBuffer _predictedBuffer;
/// <param name="period">The number of points over which to calculate the R-squared value.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
public Rsquared(int period)
{
if (period < 1)
@@ -18,6 +51,8 @@ public class Rsquared : AbstractBase
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of points over which to calculate the R-squared value.</param>
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);
+35
View File
@@ -1,10 +1,42 @@
using System;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
public class Smape : AbstractBase
{
private readonly CircularBuffer _actualBuffer;
private readonly CircularBuffer _predictedBuffer;
/// <param name="period">The number of points over which to calculate the SMAPE.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
public Smape(int period)
{
if (period < 1)
@@ -18,6 +50,8 @@ public class Smape : AbstractBase
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of points over which to calculate the SMAPE.</param>
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);
+41 -8
View File
@@ -1,14 +1,47 @@
using System;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
public class Cmo : AbstractBase
{
private readonly CircularBuffer _sumH;
private readonly CircularBuffer _sumL;
private double _prevValue, _p_prevValue;
/// <param name="period">The number of periods used in the CMO calculation.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
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})";
}
/// <summary>
/// Initializes a new instance of the CMO class with a data source.
/// </summary>
/// <param name="source">The source object that publishes data.</param>
/// <param name="period">The number of data points to consider.</param>
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of periods used in the CMO calculation.</param>
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;
+39 -7
View File
@@ -1,16 +1,48 @@
using System;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
public class Rsi : AbstractBase
{
private readonly Rma _avgGain;
private readonly Rma _avgLoss;
private double _prevValue, _p_prevValue;
/// <param name="period">The number of periods used in the RSI calculation (default 14).</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
public Rsi(int period = 14)
{
if (period < 1)
@@ -22,11 +54,8 @@ public class Rsi : AbstractBase
Name = $"RSI({period})";
}
/// <summary>
/// Initializes a new instance of the RSI class with a data source.
/// </summary>
/// <param name="source">The source object that publishes data.</param>
/// <param name="period">The number of data points to consider.</param>
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of periods used in the RSI calculation.</param>
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;
+43 -10
View File
@@ -1,10 +1,39 @@
using System;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
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;
/// <param name="period">The number of periods for RSI calculation (default 14).</param>
/// <param name="phase">The phase parameter for JMA smoothing (default 0).</param>
/// <param name="factor">The factor parameter for smoothing control (default 0.55).</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
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})";
}
/// <summary>
/// Initializes a new instance of the RSX class with a data source.
/// </summary>
/// <param name="source">The source object that publishes data.</param>
/// <param name="period">The number of data points to consider.</param>
/// <param name="phase">The phase parameter.</param>
/// <param name="factor">The factor parameter.</param>
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of periods for RSI calculation.</param>
/// <param name="phase">The phase parameter for JMA smoothing.</param>
/// <param name="factor">The factor parameter for smoothing control.</param>
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;
+36 -35
View File
@@ -1,15 +1,41 @@
using System;
using System.Linq;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
public class Curvature : AbstractBase
{
private readonly int _period;
@@ -36,13 +62,8 @@ public class Curvature : AbstractBase
/// </summary>
public double? Line { get; private set; }
/// <summary>
/// Initializes a new instance of the Curvature class.
/// </summary>
/// <param name="period">The number of data points to consider for calculation.</param>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when the period is 2 or less.
/// </exception>
/// <param name="period">The number of points to consider for calculation.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is 2 or less.</exception>
public Curvature(int period)
{
if (period <= 2)
@@ -59,20 +80,14 @@ public class Curvature : AbstractBase
Init();
}
/// <summary>
/// Initializes a new instance of the Curvature class with a data source.
/// </summary>
/// <param name="source">The source object that publishes data.</param>
/// <param name="period">The number of data points to consider.</param>
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of points to consider for calculation.</param>
public Curvature(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
/// <summary>
/// Resets the Curvature indicator to its initial state.
/// </summary>
public override void Init()
{
base.Init();
@@ -83,10 +98,6 @@ public class Curvature : AbstractBase
Line = null;
}
/// <summary>
/// Manages the state of the indicator.
/// </summary>
/// <param name="isNew">Indicates if the current data point is new.</param>
protected override void ManageState(bool isNew)
{
if (isNew)
@@ -96,16 +107,6 @@ public class Curvature : AbstractBase
}
}
/// <summary>
/// Performs the curvature calculation.
/// </summary>
/// <returns>
/// The calculated curvature value. Positive for increasing slope, negative for decreasing.
/// </returns>
/// <remarks>
/// Uses least squares method for optimal calculation. Also computes additional statistics
/// such as Intercept, Standard Deviation, R-Squared, and Line value.
/// </remarks>
protected override double Calculation()
{
ManageState(Input.IsNew);
+46 -47
View File
@@ -1,33 +1,53 @@
using System;
using System.Linq;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
public class Entropy : AbstractBase
{
/// <summary>
/// The number of data points to consider for the entropy calculation.
/// </summary>
private readonly int Period;
private readonly CircularBuffer _buffer;
/// <summary>
/// Initializes a new instance of the Entropy class.
/// </summary>
/// <param name="period">The number of data points to consider for calculation.</param>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when the period is less than 2.
/// </exception>
/// <param name="period">The number of points to consider for entropy calculation.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 2.</exception>
public Entropy(int period)
{
if (period < 2)
@@ -42,30 +62,20 @@ public class Entropy : AbstractBase
Init();
}
/// <summary>
/// Initializes a new instance of the Entropy class with a data source.
/// </summary>
/// <param name="source">The source object that publishes data.</param>
/// <param name="period">The number of data points to consider.</param>
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of points to consider for entropy calculation.</param>
public Entropy(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
/// <summary>
/// Resets the Entropy indicator to its initial state.
/// </summary>
public override void Init()
{
base.Init();
_buffer.Clear();
}
/// <summary>
/// Manages the state of the indicator.
/// </summary>
/// <param name="isNew">Indicates if the current data point is new.</param>
protected override void ManageState(bool isNew)
{
if (isNew)
@@ -75,17 +85,6 @@ public class Entropy : AbstractBase
}
}
/// <summary>
/// Performs the entropy calculation.
/// </summary>
/// <returns>
/// The calculated entropy value, normalized between 0 and 1.
/// 1 indicates maximum randomness, 0 indicates perfect predictability.
/// </returns>
/// <remarks>
/// Uses Shannon's Entropy formula and normalizes the result based on the
/// number of unique values in the current period.
/// </remarks>
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;
+45 -54
View File
@@ -1,38 +1,54 @@
using System;
using System.Linq;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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)
/// </remarks>
public class Kurtosis : AbstractBase
{
/// <summary>
/// The number of data points to consider for the kurtosis calculation.
/// </summary>
private readonly int Period;
private readonly CircularBuffer _buffer;
/// <summary>
/// Initializes a new instance of the Kurtosis class.
/// </summary>
/// <param name="period">The number of data points to consider for calculation.</param>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when the period is less than 4.
/// </exception>
/// <param name="period">The number of points to consider for kurtosis calculation.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 4.</exception>
public Kurtosis(int period)
{
if (period < 4)
@@ -47,30 +63,20 @@ public class Kurtosis : AbstractBase
Init();
}
/// <summary>
/// Initializes a new instance of the Kurtosis class with a data source.
/// </summary>
/// <param name="source">The source object that publishes data.</param>
/// <param name="period">The number of data points to consider.</param>
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of points to consider for kurtosis calculation.</param>
public Kurtosis(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
/// <summary>
/// Resets the Kurtosis indicator to its initial state.
/// </summary>
public override void Init()
{
base.Init();
_buffer.Clear();
}
/// <summary>
/// Manages the state of the indicator.
/// </summary>
/// <param name="isNew">Indicates if the current data point is new.</param>
protected override void ManageState(bool isNew)
{
if (isNew)
@@ -80,22 +86,6 @@ public class Kurtosis : AbstractBase
}
}
/// <summary>
/// Performs the kurtosis calculation.
/// </summary>
/// <returns>
/// The calculated excess kurtosis. Positive for heavy-tailed distributions,
/// negative for light-tailed distributions.
/// </returns>
/// <remarks>
/// 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.
/// </remarks>
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)));
}
+45 -66
View File
@@ -1,63 +1,58 @@
using System;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
public class Max : AbstractBase
{
/// <summary>
/// The number of data points to consider for the maximum calculation.
/// </summary>
private readonly int Period;
/// <summary>
/// Circular buffer to store the most recent data points.
/// </summary>
private readonly CircularBuffer _buffer;
/// <summary>
/// The half-life decay factor used to gradually forget old peaks.
/// </summary>
private readonly double _halfLife;
/// <summary>
/// The current maximum value.
/// </summary>
private double _currentMax;
/// <summary>
/// The previous maximum value.
/// </summary>
private double _p_currentMax;
/// <summary>
/// The number of periods since a new maximum was set.
/// </summary>
private int _timeSinceNewMax;
/// <summary>
/// The previous value of _timeSinceNewMax.
/// </summary>
private int _p_timeSinceNewMax;
/// <summary>
/// Initializes a new instance of the Max class.
/// </summary>
/// <param name="period">The number of data points to consider. Must be at least 1.</param>
/// <param name="decay">Half-life decay factor. Set to 0 for no decay, higher for faster forgetting of old peaks. Default is 0.</param>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when the period is less than 1 or decay is negative.
/// </exception>
/// <param name="period">The number of points to consider for maximum calculation.</param>
/// <param name="decay">Half-life decay factor (0 for no decay, higher for faster forgetting).</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1 or decay is negative.</exception>
public Max(int period, double decay = 0)
{
if (period < 1)
@@ -78,21 +73,15 @@ public class Max : AbstractBase
Init();
}
/// <summary>
/// Initializes a new instance of the Max class with a data source.
/// </summary>
/// <param name="source">The source object that publishes data.</param>
/// <param name="period">The number of data points to consider.</param>
/// <param name="decay">Half-life decay factor. Default is 0.</param>
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of points to consider for maximum calculation.</param>
/// <param name="decay">Half-life decay factor (default 0).</param>
public Max(object source, int period, double decay = 0) : this(period, decay)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
/// <summary>
/// Resets the Max indicator to its initial state.
/// </summary>
public override void Init()
{
base.Init();
@@ -100,10 +89,6 @@ public class Max : AbstractBase
_timeSinceNewMax = 0;
}
/// <summary>
/// Manages the state of the indicator.
/// </summary>
/// <param name="isNew">Indicates if the current data point is new.</param>
protected override void ManageState(bool isNew)
{
if (isNew)
@@ -121,29 +106,23 @@ public class Max : AbstractBase
}
}
/// <summary>
/// Performs the max calculation.
/// </summary>
/// <returns>
/// The current maximum value, potentially adjusted by the decay factor.
/// </returns>
/// <remarks>
/// Uses a decay factor to gradually forget old peaks. The max value is always
/// capped by the highest value in the current period.
/// </remarks>
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;
+44 -41
View File
@@ -1,33 +1,52 @@
using System;
using System.Linq;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
public class Median : AbstractBase
{
/// <summary>
/// The number of data points to consider for the median calculation.
/// </summary>
private readonly int Period;
private readonly CircularBuffer _buffer;
/// <summary>
/// Initializes a new instance of the Median class.
/// </summary>
/// <param name="period">The number of data points to consider. Must be at least 1.</param>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when the period is less than 1.
/// </exception>
/// <param name="period">The number of points to consider for median calculation.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
public Median(int period)
{
if (period < 1)
@@ -42,30 +61,20 @@ public class Median : AbstractBase
Init();
}
/// <summary>
/// Initializes a new instance of the Median class with a data source.
/// </summary>
/// <param name="source">The source object that publishes data.</param>
/// <param name="period">The number of data points to consider.</param>
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of points to consider for median calculation.</param>
public Median(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
/// <summary>
/// Resets the Median indicator to its initial state.
/// </summary>
public override void Init()
{
base.Init();
_buffer.Clear();
}
/// <summary>
/// Manages the state of the indicator.
/// </summary>
/// <param name="isNew">Indicates if the current data point is new.</param>
protected override void ManageState(bool isNew)
{
if (isNew)
@@ -75,16 +84,6 @@ public class Median : AbstractBase
}
}
/// <summary>
/// Performs the median calculation.
/// </summary>
/// <returns>
/// The current median value of the dataset.
/// </returns>
/// <remarks>
/// Uses a sorting approach to find the median. If there's not enough data,
/// it uses the average as a temporary measure.
/// </remarks>
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
{
+45 -66
View File
@@ -1,63 +1,58 @@
using System;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
public class Min : AbstractBase
{
/// <summary>
/// The number of data points to consider for the minimum calculation.
/// </summary>
private readonly int Period;
/// <summary>
/// Circular buffer to store the most recent data points.
/// </summary>
private readonly CircularBuffer _buffer;
/// <summary>
/// The half-life decay factor used to gradually forget old minimums.
/// </summary>
private readonly double _halfLife;
/// <summary>
/// The current minimum value.
/// </summary>
private double _currentMin;
/// <summary>
/// The previous minimum value.
/// </summary>
private double _p_currentMin;
/// <summary>
/// The number of periods since a new minimum was set.
/// </summary>
private int _timeSinceNewMin;
/// <summary>
/// The previous value of _timeSinceNewMin.
/// </summary>
private int _p_timeSinceNewMin;
/// <summary>
/// Initializes a new instance of the Min class with the specified period and decay.
/// </summary>
/// <param name="period">The period over which to calculate the minimum value.</param>
/// <param name="decay">The decay factor to apply to older values. Higher values cause faster forgetting of old minimums. Default is 0 (no decay).</param>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when period is less than 1 or decay is negative.
/// </exception>
/// <param name="period">The number of points to consider for minimum calculation.</param>
/// <param name="decay">Half-life decay factor (0 for no decay, higher for faster forgetting).</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1 or decay is negative.</exception>
public Min(int period, double decay = 0)
{
if (period < 1)
@@ -76,21 +71,15 @@ public class Min : AbstractBase
Init();
}
/// <summary>
/// Initializes a new instance of the Min class with the specified source, period, and decay.
/// </summary>
/// <param name="source">The source object to subscribe to for value updates.</param>
/// <param name="period">The period over which to calculate the minimum value.</param>
/// <param name="decay">The decay factor to apply to older values. Higher values cause faster forgetting of old minimums. Default is 0 (no decay).</param>
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of points to consider for minimum calculation.</param>
/// <param name="decay">Half-life decay factor (default 0).</param>
public Min(object source, int period, double decay = 0) : this(period, decay)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
/// <summary>
/// Initializes the Min instance by setting initial values.
/// </summary>
public override void Init()
{
base.Init();
@@ -98,10 +87,6 @@ public class Min : AbstractBase
_timeSinceNewMin = 0;
}
/// <summary>
/// Manages the state of the Min instance based on whether a new value is being processed.
/// </summary>
/// <param name="isNew">Indicates whether the current input is a new value.</param>
protected override void ManageState(bool isNew)
{
if (isNew)
@@ -119,29 +104,23 @@ public class Min : AbstractBase
}
}
/// <summary>
/// Performs the minimum value calculation with decay.
/// </summary>
/// <returns>The calculated minimum value for the current period.</returns>
/// <remarks>
/// 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.
/// </remarks>
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;
+50 -50
View File
@@ -1,34 +1,52 @@
using System;
using System.Linq;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
public class Mode : AbstractBase
{
/// <summary>
/// The number of data points to consider for the mode calculation.
/// </summary>
private readonly int Period;
private readonly CircularBuffer _buffer;
/// <summary>
/// Initializes a new instance of the Mode class with the specified period.
/// </summary>
/// <param name="period">The period over which to calculate the mode.</param>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when period is less than 1.
/// </exception>
/// <param name="period">The number of points to consider for mode calculation.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
public Mode(int period)
{
if (period < 1)
@@ -42,30 +60,20 @@ public class Mode : AbstractBase
Init();
}
/// <summary>
/// Initializes a new instance of the Mode class with the specified source and period.
/// </summary>
/// <param name="source">The source object to subscribe to for value updates.</param>
/// <param name="period">The period over which to calculate the mode.</param>
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of points to consider for mode calculation.</param>
public Mode(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
/// <summary>
/// Resets the Mode indicator to its initial state.
/// </summary>
public override void Init()
{
base.Init();
_buffer.Clear();
}
/// <summary>
/// Manages the state of the Mode instance based on whether a new value is being processed.
/// </summary>
/// <param name="isNew">Indicates whether the current input is a new value.</param>
protected override void ManageState(bool isNew)
{
if (isNew)
@@ -75,18 +83,6 @@ public class Mode : AbstractBase
}
}
/// <summary>
/// Performs the mode calculation for the current period.
/// </summary>
/// <returns>
/// The calculated mode (most frequent value) for the current period.
/// If multiple values have the same highest frequency, returns their average.
/// </returns>
/// <remarks>
/// 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.
/// </remarks>
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;
+48 -53
View File
@@ -1,40 +1,54 @@
using System;
using System.Linq;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
public class Percentile : AbstractBase
{
/// <summary>
/// The number of data points to consider for the percentile calculation.
/// </summary>
private readonly int Period;
/// <summary>
/// The percentile to calculate (between 0 and 100).
/// </summary>
private readonly double Percent;
private readonly CircularBuffer _buffer;
/// <summary>
/// Initializes a new instance of the Percentile class with the specified period and percentile.
/// </summary>
/// <param name="period">The period over which to calculate the percentile.</param>
/// <param name="percent">The percentile to calculate (between 0 and 100).</param>
/// <param name="period">The number of points to consider for percentile calculation.</param>
/// <param name="percent">The percentile to calculate (0-100).</param>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when period is less than 2 or percent is not between 0 and 100.
/// </exception>
@@ -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();
}
/// <summary>
/// Initializes a new instance of the Percentile class with the specified source, period, and percentile.
/// </summary>
/// <param name="source">The source object to subscribe to for value updates.</param>
/// <param name="period">The period over which to calculate the percentile.</param>
/// <param name="percent">The percentile to calculate (between 0 and 100).</param>
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of points to consider for percentile calculation.</param>
/// <param name="percent">The percentile to calculate (0-100).</param>
public Percentile(object source, int period, double percent) : this(period, percent)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
/// <summary>
/// Initializes the Percentile instance by clearing the buffer.
/// </summary>
public override void Init()
{
base.Init();
_buffer.Clear();
}
/// <summary>
/// Manages the state of the Percentile instance based on whether a new value is being processed.
/// </summary>
/// <param name="isNew">Indicates whether the current input is a new value.</param>
protected override void ManageState(bool isNew)
{
if (isNew)
@@ -90,18 +96,6 @@ public class Percentile : AbstractBase
}
}
/// <summary>
/// Performs the percentile calculation for the current period.
/// </summary>
/// <returns>
/// The calculated percentile value for the current period.
/// </returns>
/// <remarks>
/// 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.
/// </remarks>
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();
}
+50 -59
View File
@@ -1,44 +1,62 @@
using System;
using System.Linq;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
public class Skew : AbstractBase
{
/// <summary>
/// The number of data points to consider for the skewness calculation.
/// </summary>
private readonly int Period;
private readonly CircularBuffer _buffer;
/// <summary>
/// Initializes a new instance of the Skew class with the specified period.
/// </summary>
/// <param name="period">The period over which to calculate the skewness.</param>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when period is less than 3.
/// </exception>
/// <param name="period">The number of points to consider for skewness calculation.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 3.</exception>
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();
}
/// <summary>
/// Initializes a new instance of the Skew class with the specified source and period.
/// </summary>
/// <param name="source">The source object to subscribe to for value updates.</param>
/// <param name="period">The period over which to calculate the skewness.</param>
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of points to consider for skewness calculation.</param>
public Skew(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
/// <summary>
/// Initializes the Skew instance by clearing the buffer.
/// </summary>
public override void Init()
{
base.Init();
_buffer.Clear();
}
/// <summary>
/// Manages the state of the Skew instance based on whether a new value is being processed.
/// </summary>
/// <param name="isNew">Indicates whether the current input is a new value.</param>
protected override void ManageState(bool isNew)
{
if (isNew)
@@ -80,36 +88,19 @@ public class Skew : AbstractBase
}
}
/// <summary>
/// Performs the skewness calculation for the current period.
/// </summary>
/// <returns>
/// The calculated skewness value for the current period.
/// </returns>
/// <remarks>
/// 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.
/// </remarks>
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);
}
}
+50 -64
View File
@@ -1,52 +1,68 @@
using System;
using System.Linq;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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)
/// </remarks>
public class Slope : AbstractBase
{
private readonly int _period;
private readonly CircularBuffer _buffer;
private readonly CircularBuffer _timeBuffer;
/// <summary>
/// Gets the y-intercept of the regression line.
/// </summary>
/// <summary>Gets the y-intercept of the regression line.</summary>
public double? Intercept { get; private set; }
/// <summary>
/// Gets the standard deviation of the y-values.
/// </summary>
/// <summary>Gets the standard deviation of the y-values.</summary>
public double? StdDev { get; private set; }
/// <summary>
/// Gets the R-squared value, indicating the goodness of fit of the regression line.
/// </summary>
/// <summary>Gets the R-squared value, indicating regression fit quality.</summary>
public double? RSquared { get; private set; }
/// <summary>
/// Gets the y-value of the last point on the regression line.
/// </summary>
/// <summary>Gets the last point on the regression line.</summary>
public double? Line { get; private set; }
/// <summary>
/// Initializes a new instance of the Slope class with the specified period.
/// </summary>
/// <param name="period">The period over which to calculate the slope.</param>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when period is less than or equal to 1.
/// </exception>
/// <param name="period">The number of points to consider for slope calculation.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than or equal to 1.</exception>
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();
}
/// <summary>
/// Initializes a new instance of the Slope class with the specified source and period.
/// </summary>
/// <param name="source">The source object to subscribe to for value updates.</param>
/// <param name="period">The period over which to calculate the slope.</param>
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of points to consider for slope calculation.</param>
public Slope(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
/// <summary>
/// Initializes the Slope instance by clearing buffers and resetting calculated values.
/// </summary>
public override void Init()
{
base.Init();
@@ -88,10 +97,6 @@ public class Slope : AbstractBase
Line = null;
}
/// <summary>
/// Manages the state of the Slope instance based on whether a new value is being processed.
/// </summary>
/// <param name="isNew">Indicates whether the current input is a new value.</param>
protected override void ManageState(bool isNew)
{
if (isNew)
@@ -101,25 +106,6 @@ public class Slope : AbstractBase
}
}
/// <summary>
/// Performs the slope calculation using linear regression for the current period.
/// </summary>
/// <returns>
/// The calculated slope value for the current period.
/// </returns>
/// <remarks>
/// 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).
/// </remarks>
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
+50 -64
View File
@@ -1,48 +1,63 @@
using System;
using System.Linq;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
public class Stddev : AbstractBase
{
/// <summary>
/// Indicates whether to calculate population (true) or sample (false) standard deviation.
/// </summary>
private readonly bool IsPopulation;
/// <summary>
/// Circular buffer to store the most recent data points.
/// </summary>
private readonly CircularBuffer _buffer;
/// <summary>
/// Initializes a new instance of the Stddev class with the specified period and
/// population flag.
/// </summary>
/// <param name="period">The period over which to calculate the standard deviation.</param>
/// <param name="isPopulation">
/// A flag indicating whether to calculate population (true) or sample (false) standard deviation.
/// </param>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when period is less than 2.
/// </exception>
/// <param name="period">The number of points to consider for standard deviation calculation.</param>
/// <param name="isPopulation">True for population stddev, false for sample stddev (default).</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 2.</exception>
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();
}
/// <summary>
/// Initializes a new instance of the Stddev class with the specified source, period,
/// and population flag.
/// </summary>
/// <param name="source">The source object to subscribe to for value updates.</param>
/// <param name="period">The period over which to calculate the standard deviation.</param>
/// <param name="isPopulation">
/// A flag indicating whether to calculate population (true) or sample (false) standard deviation.
/// </param>
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of points to consider for standard deviation calculation.</param>
/// <param name="isPopulation">True for population stddev, false for sample stddev (default).</param>
public Stddev(object source, int period, bool isPopulation = false) : this(period, isPopulation)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
/// <summary>
/// Initializes the Stddev instance by clearing the buffer.
/// </summary>
public override void Init()
{
base.Init();
_buffer.Clear();
}
/// <summary>
/// Manages the state of the Stddev instance based on whether a new value is being processed.
/// </summary>
/// <param name="isNew">Indicates whether the current input is a new value.</param>
protected override void ManageState(bool isNew)
{
if (isNew)
@@ -88,28 +90,9 @@ public class Stddev : AbstractBase
}
}
/// <summary>
/// Performs the standard deviation calculation for the current period.
/// </summary>
/// <returns>
/// The calculated standard deviation value for the current period.
/// </returns>
/// <remarks>
/// 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.
/// </remarks>
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);
+50 -65
View File
@@ -1,48 +1,63 @@
using System;
using System.Linq;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
public class Variance : AbstractBase
{
/// <summary>
/// Indicates whether to calculate population (true) or sample (false) variance.
/// </summary>
private readonly bool IsPopulation;
/// <summary>
/// Circular buffer to store the most recent data points.
/// </summary>
private readonly CircularBuffer _buffer;
/// <summary>
/// Initializes a new instance of the Variance class with the specified period and
/// population flag.
/// </summary>
/// <param name="period">The period over which to calculate the variance.</param>
/// <param name="isPopulation">
/// A flag indicating whether to calculate population (true) or sample (false) variance.
/// </param>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when period is less than 2.
/// </exception>
/// <param name="period">The number of points to consider for variance calculation.</param>
/// <param name="isPopulation">True for population variance, false for sample variance (default).</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 2.</exception>
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();
}
/// <summary>
/// Initializes a new instance of the Variance class with the specified source, period,
/// and population flag.
/// </summary>
/// <param name="source">The source object to subscribe to for value updates.</param>
/// <param name="period">The period over which to calculate the variance.</param>
/// <param name="isPopulation">
/// A flag indicating whether to calculate population (true) or sample (false) variance.
/// </param>
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of points to consider for variance calculation.</param>
/// <param name="isPopulation">True for population variance, false for sample variance (default).</param>
public Variance(object source, int period, bool isPopulation = false) : this(period, isPopulation)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
/// <summary>
/// Initializes the Variance instance by clearing the buffer.
/// </summary>
public override void Init()
{
base.Init();
_buffer.Clear();
}
/// <summary>
/// Manages the state of the Variance instance based on whether a new value is being processed.
/// </summary>
/// <param name="isNew">Indicates whether the current input is a new value.</param>
protected override void ManageState(bool isNew)
{
if (isNew)
@@ -88,29 +90,9 @@ public class Variance : AbstractBase
}
}
/// <summary>
/// Performs the variance calculation for the current period.
/// </summary>
/// <returns>
/// The calculated variance value for the current period.
/// </returns>
/// <remarks>
/// 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.
/// </remarks>
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;
}
+50 -63
View File
@@ -1,44 +1,61 @@
using System;
using System.Linq;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
public class Zscore : AbstractBase
{
/// <summary>
/// The number of data points to consider for the Z-score calculation.
/// </summary>
private readonly int Period;
/// <summary>
/// Circular buffer to store the most recent data points.
/// </summary>
private readonly CircularBuffer _buffer;
/// <summary>
/// Initializes a new instance of the Zscore class with the specified period.
/// </summary>
/// <param name="period">The period over which to calculate the Z-score.</param>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when period is less than 2.
/// </exception>
/// <param name="period">The number of points to consider for Z-score calculation.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 2.</exception>
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();
}
/// <summary>
/// Initializes a new instance of the Zscore class with the specified source and period.
/// </summary>
/// <param name="source">The source object to subscribe to for value updates.</param>
/// <param name="period">The period over which to calculate the Z-score.</param>
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of points to consider for Z-score calculation.</param>
public Zscore(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
/// <summary>
/// Initializes the Zscore instance by clearing the buffer.
/// </summary>
public override void Init()
{
base.Init();
_buffer.Clear();
}
/// <summary>
/// Manages the state of the Zscore instance based on whether a new value is being processed.
/// </summary>
/// <param name="isNew">Indicates whether the current input is a new value.</param>
protected override void ManageState(bool isNew)
{
if (isNew)
@@ -80,44 +87,24 @@ public class Zscore : AbstractBase
}
}
/// <summary>
/// Performs the Z-score calculation for the current period.
/// </summary>
/// <returns>
/// The calculated Z-score value for the most recent input in the current period.
/// </returns>
/// <remarks>
/// 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.
/// </remarks>
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;
}
}
+48 -35
View File
@@ -1,51 +1,75 @@
using System;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
public class Atr : AbstractBase
{
public double Tr { get; private set; }
private readonly Rma _ma;
private double _prevClose, _p_prevClose;
/// <summary>
/// Initializes a new instance of the Atr class with the specified period.
/// </summary>
/// <param name="period">The period over which to calculate the ATR.</param>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when period is less than 1.
/// </exception>
/// <param name="period">The number of periods for ATR calculation.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
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})";
}
/// <summary>
/// Initializes a new instance of the Atr class with the specified source and period.
/// </summary>
/// <param name="source">The source object to subscribe to for bar updates.</param>
/// <param name="period">The period over which to calculate the ATR.</param>
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of periods for ATR calculation.</param>
public Atr(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
/// <summary>
/// Initializes the Atr instance by setting up the initial state.
/// </summary>
public override void Init()
{
base.Init();
@@ -54,10 +78,6 @@ public class Atr : AbstractBase
Tr = 0;
}
/// <summary>
/// Manages the state of the Atr instance based on whether a new bar is being processed.
/// </summary>
/// <param name="isNew">Indicates whether the current input is a new bar.</param>
protected override void ManageState(bool isNew)
{
if (isNew)
@@ -71,28 +91,19 @@ public class Atr : AbstractBase
}
}
/// <summary>
/// Performs the ATR calculation for the current bar.
/// </summary>
/// <returns>
/// The calculated ATR value for the current bar.
/// </returns>
/// <remarks>
/// 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.
/// </remarks>
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;
+54 -45
View File
@@ -1,14 +1,49 @@
using System;
using System.Linq;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
public class Hv : AbstractBase
{
private readonly int Period;
@@ -17,44 +52,34 @@ public class Hv : AbstractBase
private readonly CircularBuffer _logReturns;
private double _previousClose;
/// <summary>
/// Initializes a new instance of the Historical class with the specified period and annualization flag.
/// </summary>
/// <param name="period">The period over which to calculate historical volatility.</param>
/// <param name="isAnnualized">Whether to annualize the volatility (default is true).</param>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when period is less than 2.
/// </exception>
/// <param name="period">The number of periods for volatility calculation.</param>
/// <param name="isAnnualized">Whether to annualize the result (default true).</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 2.</exception>
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();
}
/// <summary>
/// Initializes a new instance of the Historical class with the specified source, period, and annualization flag.
/// </summary>
/// <param name="source">The source object to subscribe to for value updates.</param>
/// <param name="period">The period over which to calculate historical volatility.</param>
/// <param name="isAnnualized">Whether to annualize the volatility (default is true).</param>
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of periods for volatility calculation.</param>
/// <param name="isAnnualized">Whether to annualize the result (default true).</param>
public Hv(object source, int period, bool isAnnualized = true) : this(period, isAnnualized)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
/// <summary>
/// Initializes the Historical instance by clearing buffers and resetting the previous close value.
/// </summary>
public override void Init()
{
base.Init();
@@ -63,10 +88,6 @@ public class Hv : AbstractBase
_previousClose = 0;
}
/// <summary>
/// Manages the state of the Historical instance based on whether a new value is being processed.
/// </summary>
/// <param name="isNew">Indicates whether the current input is a new value.</param>
protected override void ManageState(bool isNew)
{
if (isNew)
@@ -76,47 +97,35 @@ public class Hv : AbstractBase
}
}
/// <summary>
/// Performs the historical volatility calculation for the current period.
/// </summary>
/// <returns>
/// The calculated historical volatility value for the current period.
/// </returns>
/// <remarks>
/// 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.
/// </remarks>
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
}
}
}
+56 -41
View File
@@ -1,9 +1,46 @@
/// <summary>
/// Represents a Jurik Volatility (Jvolty) calculator, a measure of market volatility based on Jurik Moving Average (JMA) concepts.
/// </summary>
using System;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
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; }
/// <summary>
/// Initializes a new instance of the Jvolty class with the specified parameters.
/// </summary>
/// <param name="period">The period over which to calculate the Jvolty.</param>
/// <param name="phase">The phase parameter for the JMA-style calculation.</param>
/// <param name="vshort">The short-term volatility period.</param>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when period is less than 1.
/// </exception>
/// <param name="period">The number of periods for volatility calculation.</param>
/// <param name="phase">Phase parameter for JMA smoothing (default 0).</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
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})";
}
/// <summary>
/// Initializes a new instance of the Jvolty class with the specified source and parameters.
/// </summary>
/// <param name="source">The source object to subscribe to for bar updates.</param>
/// <param name="period">The period over which to calculate the Jvolty.</param>
/// <param name="phase">The phase parameter for the JMA-style calculation.</param>
/// <param name="vshort">The short-term volatility period.</param>
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of periods for volatility calculation.</param>
/// <param name="phase">Phase parameter for JMA smoothing (default 0).</param>
public Jvolty(object source, int period, int phase = 0) : this(period, phase)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
/// <summary>
/// Initializes the Jvolty instance by setting up the initial state.
/// </summary>
public override void Init()
{
base.Init();
@@ -80,10 +103,6 @@ public class Jvolty : AbstractBase
_vsumBuff.Clear();
}
/// <summary>
/// Manages the state of the Jvolty instance based on whether a new bar is being processed.
/// </summary>
/// <param name="isNew">Indicates whether the current input is a new bar.</param>
protected override void ManageState(bool isNew)
{
if (isNew)
@@ -109,12 +128,6 @@ public class Jvolty : AbstractBase
}
}
/// <summary>
/// Performs the Jvolty calculation for the current bar.
/// </summary>
/// <returns>
/// The calculated Jvolty value for the current bar.
/// </returns>
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;
}
}
+53 -46
View File
@@ -1,14 +1,48 @@
using System;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
public class Rv : AbstractBase
{
private readonly int Period;
@@ -17,43 +51,33 @@ public class Rv : AbstractBase
private double _previousClose;
private double _sumSquaredReturns;
/// <summary>
/// Initializes a new instance of the Realized class with the specified period and annualization flag.
/// </summary>
/// <param name="period">The period over which to calculate realized volatility.</param>
/// <param name="isAnnualized">Whether to annualize the volatility (default is true).</param>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when period is less than 2.
/// </exception>
/// <param name="period">The number of periods for volatility calculation.</param>
/// <param name="isAnnualized">Whether to annualize the result (default true).</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 2.</exception>
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();
}
/// <summary>
/// Initializes a new instance of the Realized class with a data source.
/// </summary>
/// <param name="source">The source object that publishes data.</param>
/// <param name="period">The period over which to calculate realized volatility.</param>
/// <param name="isAnnualized">Whether to annualize the volatility (default is true).</param>
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of periods for volatility calculation.</param>
/// <param name="isAnnualized">Whether to annualize the result (default true).</param>
public Rv(object source, int period, bool isAnnualized = true) : this(period, isAnnualized)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
/// <summary>
/// Initializes the Realized instance by clearing buffers and resetting calculation variables.
/// </summary>
public override void Init()
{
base.Init();
@@ -62,10 +86,6 @@ public class Rv : AbstractBase
_sumSquaredReturns = 0;
}
/// <summary>
/// Manages the state of the Realized instance based on whether a new value is being processed.
/// </summary>
/// <param name="isNew">Indicates whether the current input is a new value.</param>
protected override void ManageState(bool isNew)
{
if (isNew)
@@ -75,21 +95,6 @@ public class Rv : AbstractBase
}
}
/// <summary>
/// Performs the realized volatility calculation for the current period.
/// </summary>
/// <returns>
/// The calculated realized volatility value for the current period.
/// </returns>
/// <remarks>
/// 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.
/// </remarks>
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
}
}
}
+49 -44
View File
@@ -1,36 +1,61 @@
using System;
namespace QuanTAlib;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// The RVI was introduced by Donald Dorsey in the 1993 issue of Technical Analysis
/// of Stocks &amp; 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
/// </remarks>
public class Rvi : AbstractBase
{
private readonly Stddev _upStdDev, _downStdDev;
private readonly Sma _upSma, _downSma;
private double _previousClose;
/// <summary>
/// Initializes a new instance of the Rvi class with the specified period.
/// </summary>
/// <param name="period">The period over which to calculate the RVI.</param>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when period is less than 2.
/// </exception>
/// <param name="period">The number of periods for RVI calculation.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 2.</exception>
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();
}
/// <summary>
/// Initializes a new instance of the Rvi class with the specified source and period.
/// </summary>
/// <param name="source">The source object to subscribe to for value updates.</param>
/// <param name="period">The period over which to calculate the RVI.</param>
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of periods for RVI calculation.</param>
public Rvi(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
/// <summary>
/// Initializes the Rvi instance by setting up the initial state.
/// </summary>
public override void Init()
{
base.Init();
_previousClose = 0;
}
/// <summary>
/// Manages the state of the Rvi instance based on whether a new value is being processed.
/// </summary>
/// <param name="isNew">Indicates whether the current input is a new value.</param>
protected override void ManageState(bool isNew)
{
if (isNew)
@@ -75,21 +90,6 @@ public class Rvi : AbstractBase
}
}
/// <summary>
/// Performs the RVI calculation for the current input.
/// </summary>
/// <returns>
/// The calculated RVI value for the current input.
/// </returns>
/// <remarks>
/// 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.
/// </remarks>
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;