mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-12 15:48:05 +00:00
xml doc rewrite
This commit is contained in:
@@ -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
|
name: Publish Workflow
|
||||||
|
|
||||||
on:
|
on:
|
||||||
@@ -82,7 +78,10 @@ jobs:
|
|||||||
-Dsonar.sources=.
|
-Dsonar.sources=.
|
||||||
-Dsonar.cs.opencover.reportsPaths=**/*cover*.xml
|
-Dsonar.cs.opencover.reportsPaths=**/*cover*.xml
|
||||||
-Dsonar.cs.dotcover.reportsPaths=**/dotcover.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:
|
CodeQL:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"sonarCloudOrganization": "mihakralj-quantalib",
|
"sonarCloudOrganization": "mihakralj",
|
||||||
"projectKey": "mihakralj_QuanTAlib"
|
"projectKey": "mihakralj_QuanTAlib"
|
||||||
}
|
}
|
||||||
Vendored
+33
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Vendored
+2
-2
@@ -7,9 +7,9 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"sarif-viewer.connectToGithubCodeScanning": "on",
|
"sarif-viewer.connectToGithubCodeScanning": "on",
|
||||||
"dotnet.dotnetPath": "C:/Program Files/dotnet",
|
"dotnet.dotnetPath": "/opt/homebrew/bin/",
|
||||||
"omnisharp.useModernNet": true,
|
"omnisharp.useModernNet": true,
|
||||||
"omnisharp.sdkPath": "C:/Program Files/dotnet/sdk",
|
"omnisharp.sdkPath": "/opt/homebrew/bin/dotnet",
|
||||||
"sonarlint.connectedMode.project": {
|
"sonarlint.connectedMode.project": {
|
||||||
"connectionId": "mihakralj",
|
"connectionId": "mihakralj",
|
||||||
"projectKey": "mihakralj_QuanTAlib"
|
"projectKey": "mihakralj_QuanTAlib"
|
||||||
|
|||||||
Vendored
+62
@@ -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,6 +4,10 @@
|
|||||||
<AssemblyName>QuanTAlib.Tests</AssemblyName>
|
<AssemblyName>QuanTAlib.Tests</AssemblyName>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<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="xunit" Version="2.9.2" />
|
||||||
<PackageReference Include="coverlet.collector" Version="6.0.2" />
|
<PackageReference Include="coverlet.collector" Version="6.0.2" />
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="3.0.0-pre.35">
|
<PackageReference Include="xunit.runner.visualstudio" Version="3.0.0-pre.35">
|
||||||
|
|||||||
+19
-2
@@ -1,8 +1,18 @@
|
|||||||
|
using System;
|
||||||
namespace QuanTAlib;
|
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 class Afirma : AbstractBase
|
||||||
{
|
{
|
||||||
|
|
||||||
public enum WindowType
|
public enum WindowType
|
||||||
{
|
{
|
||||||
Rectangular,
|
Rectangular,
|
||||||
@@ -22,6 +32,10 @@ public class Afirma : AbstractBase
|
|||||||
private readonly int _n;
|
private readonly int _n;
|
||||||
private readonly double _sx2, _sx3, _sx4, _sx5, _sx6, _den;
|
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)
|
public Afirma(int periods, int taps, WindowType window)
|
||||||
{
|
{
|
||||||
if (periods < 1)
|
if (periods < 1)
|
||||||
@@ -54,6 +68,10 @@ public class Afirma : AbstractBase
|
|||||||
Init();
|
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)
|
public Afirma(object source, int periods, int taps, WindowType window) : this(periods, taps, window)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
@@ -147,5 +165,4 @@ public class Afirma : AbstractBase
|
|||||||
}
|
}
|
||||||
return wsum;
|
return wsum;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,16 @@
|
|||||||
|
using System;
|
||||||
namespace QuanTAlib;
|
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
|
public class Convolution : AbstractBase
|
||||||
{
|
{
|
||||||
private readonly double[] _kernel;
|
private readonly double[] _kernel;
|
||||||
@@ -7,6 +18,8 @@ public class Convolution : AbstractBase
|
|||||||
private readonly CircularBuffer _buffer;
|
private readonly CircularBuffer _buffer;
|
||||||
private readonly double[] _normalizedKernel;
|
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)
|
public Convolution(double[] kernel)
|
||||||
{
|
{
|
||||||
if (kernel == null || kernel.Length == 0)
|
if (kernel == null || kernel.Length == 0)
|
||||||
@@ -20,6 +33,8 @@ public class Convolution : AbstractBase
|
|||||||
Init();
|
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)
|
public Convolution(object source, double[] kernel) : this(kernel)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
@@ -100,4 +115,4 @@ public class Convolution : AbstractBase
|
|||||||
|
|
||||||
return sum;
|
return sum;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+33
-1
@@ -1,10 +1,35 @@
|
|||||||
|
using System;
|
||||||
namespace QuanTAlib;
|
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
|
public class Epma : AbstractBase
|
||||||
{
|
{
|
||||||
private readonly int _period;
|
private readonly int _period;
|
||||||
private readonly Convolution _convolution;
|
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)
|
public Epma(int period)
|
||||||
{
|
{
|
||||||
if (period < 1)
|
if (period < 1)
|
||||||
@@ -18,6 +43,8 @@ public class Epma : AbstractBase
|
|||||||
Init();
|
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)
|
public Epma(object source, int period) : this(period)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
@@ -60,6 +87,11 @@ public class Epma : AbstractBase
|
|||||||
return result;
|
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)
|
public static double[] GenerateKernel(int period)
|
||||||
{
|
{
|
||||||
double[] kernel = new double[period];
|
double[] kernel = new double[period];
|
||||||
@@ -79,4 +111,4 @@ public class Epma : AbstractBase
|
|||||||
|
|
||||||
return kernel;
|
return kernel;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+28
-1
@@ -2,6 +2,29 @@ using System;
|
|||||||
|
|
||||||
namespace QuanTAlib;
|
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
|
public class Frama : AbstractBase
|
||||||
{
|
{
|
||||||
private readonly int _period;
|
private readonly int _period;
|
||||||
@@ -9,6 +32,8 @@ public class Frama : AbstractBase
|
|||||||
private double _lastFrama;
|
private double _lastFrama;
|
||||||
private double _prevLastFrama;
|
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)
|
public Frama(int period)
|
||||||
{
|
{
|
||||||
if (period < 2)
|
if (period < 2)
|
||||||
@@ -19,6 +44,8 @@ public class Frama : AbstractBase
|
|||||||
WarmupPeriod = period;
|
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)
|
public Frama(object source, int period) : this(period)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
@@ -99,4 +126,4 @@ public class Frama : AbstractBase
|
|||||||
{
|
{
|
||||||
return _lastFrama;
|
return _lastFrama;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+34
-1
@@ -1,9 +1,35 @@
|
|||||||
|
using System;
|
||||||
namespace QuanTAlib;
|
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
|
public class Fwma : AbstractBase
|
||||||
{
|
{
|
||||||
private readonly Convolution _convolution;
|
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)
|
public Fwma(int period)
|
||||||
{
|
{
|
||||||
if (period < 1)
|
if (period < 1)
|
||||||
@@ -16,12 +42,19 @@ public class Fwma : AbstractBase
|
|||||||
Init();
|
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)
|
public Fwma(object source, int period) : this(period)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
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)
|
public static double[] GenerateKernel(int period)
|
||||||
{
|
{
|
||||||
double[] kernel = new double[period];
|
double[] kernel = new double[period];
|
||||||
@@ -78,4 +111,4 @@ public class Fwma : AbstractBase
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+35
-1
@@ -1,9 +1,35 @@
|
|||||||
|
using System;
|
||||||
namespace QuanTAlib;
|
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
|
public class Gma : AbstractBase
|
||||||
{
|
{
|
||||||
private readonly Convolution _convolution;
|
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)
|
public Gma(int period)
|
||||||
{
|
{
|
||||||
if (period < 1)
|
if (period < 1)
|
||||||
@@ -16,12 +42,20 @@ public class Gma : AbstractBase
|
|||||||
Init();
|
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)
|
public Gma(object source, int period) : this(period)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
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)
|
public static double[] GenerateKernel(int period, double sigma = 1.0)
|
||||||
{
|
{
|
||||||
double[] kernel = new double[period];
|
double[] kernel = new double[period];
|
||||||
@@ -71,4 +105,4 @@ public class Gma : AbstractBase
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+36
-1
@@ -1,9 +1,37 @@
|
|||||||
|
using System;
|
||||||
namespace QuanTAlib;
|
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
|
public class Hma : AbstractBase
|
||||||
{
|
{
|
||||||
private readonly Convolution _wmaHalf, _wmaFull, _wmaFinal;
|
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)
|
public Hma(int period)
|
||||||
{
|
{
|
||||||
if (period < 2)
|
if (period < 2)
|
||||||
@@ -19,12 +47,19 @@ public class Hma : AbstractBase
|
|||||||
Init();
|
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)
|
public Hma(object source, int period) : this(period)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
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)
|
private static double[] GenerateWmaKernel(int period)
|
||||||
{
|
{
|
||||||
double[] kernel = new double[period];
|
double[] kernel = new double[period];
|
||||||
@@ -72,4 +107,4 @@ public class Hma : AbstractBase
|
|||||||
IsHot = _index >= WarmupPeriod;
|
IsHot = _index >= WarmupPeriod;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+36
-4
@@ -1,8 +1,32 @@
|
|||||||
//not working yet
|
using System;
|
||||||
//TODO fails consistency test
|
|
||||||
|
|
||||||
namespace QuanTAlib;
|
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
|
public class Htit : AbstractBase
|
||||||
{
|
{
|
||||||
private readonly CircularBuffer _priceBuffer = new(7);
|
private readonly CircularBuffer _priceBuffer = new(7);
|
||||||
@@ -21,12 +45,19 @@ public class Htit : AbstractBase
|
|||||||
private double _lastPd = 0;
|
private double _lastPd = 0;
|
||||||
private double _p_lastPd = 0;
|
private double _p_lastPd = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the Htit class.
|
||||||
|
/// </summary>
|
||||||
public Htit()
|
public Htit()
|
||||||
{
|
{
|
||||||
Name = "Htit";
|
Name = "Htit";
|
||||||
WarmupPeriod = 12;
|
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()
|
public Htit(object source) : this()
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
@@ -45,6 +76,7 @@ public class Htit : AbstractBase
|
|||||||
_lastPd = _p_lastPd;
|
_lastPd = _p_lastPd;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override double Calculation()
|
protected override double Calculation()
|
||||||
{
|
{
|
||||||
ManageState(Input.IsNew);
|
ManageState(Input.IsNew);
|
||||||
@@ -141,4 +173,4 @@ public class Htit : AbstractBase
|
|||||||
|
|
||||||
return pr;
|
return pr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+40
-2
@@ -1,5 +1,33 @@
|
|||||||
|
using System;
|
||||||
namespace QuanTAlib;
|
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
|
public class Hwma : AbstractBase
|
||||||
{
|
{
|
||||||
private readonly int _period;
|
private readonly int _period;
|
||||||
@@ -7,14 +35,23 @@ public class Hwma : AbstractBase
|
|||||||
private double _pF, _pV, _pA;
|
private double _pF, _pV, _pA;
|
||||||
private double _ppF, _ppV, _ppA;
|
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)
|
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)
|
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)
|
public Hwma(int period, double nA, double nB, double nC)
|
||||||
{
|
{
|
||||||
if (period < 1)
|
if (period < 1)
|
||||||
@@ -30,6 +67,8 @@ public class Hwma : AbstractBase
|
|||||||
Init();
|
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)
|
public Hwma(object source, int period) : this(period)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
@@ -58,7 +97,6 @@ public class Hwma : AbstractBase
|
|||||||
_pF = _ppF;
|
_pF = _ppF;
|
||||||
_pV = _ppV;
|
_pV = _ppV;
|
||||||
_pA = _ppA;
|
_pA = _ppA;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,4 +131,4 @@ public class Hwma : AbstractBase
|
|||||||
IsHot = _index >= WarmupPeriod;
|
IsHot = _index >= WarmupPeriod;
|
||||||
return hwma;
|
return hwma;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+39
-29
@@ -1,9 +1,31 @@
|
|||||||
/// <summary>
|
using System;
|
||||||
/// Represents a Jurik Moving Average, based on known and reverse-engineered insights
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
namespace QuanTAlib;
|
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
|
public class Jma : AbstractBase
|
||||||
{
|
{
|
||||||
private readonly double _period;
|
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 _prevMa1, _prevDet0, _prevDet1, _prevJma, _p_prevMa1, _p_prevDet0, _p_prevDet1, _p_prevJma;
|
||||||
private double _vSum, _p_vSum;
|
private double _vSum, _p_vSum;
|
||||||
|
|
||||||
|
|
||||||
public double UpperBand { get; set; }
|
public double UpperBand { get; set; }
|
||||||
public double LowerBand { get; set; }
|
public double LowerBand { get; set; }
|
||||||
public double Volty { get; set; }
|
public double Volty { get; set; }
|
||||||
@@ -27,11 +48,11 @@ public class Jma : AbstractBase
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the Jma class with the specified parameters.
|
/// Initializes a new instance of the Jma class with the specified parameters.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="period">The period over which to calculate the Jvolty.</param>
|
/// <param name="period">The period over which to calculate the JMA.</param>
|
||||||
/// <param name="phase">The phase parameter for the JMA-style calculation.</param>
|
/// <param name="phase">The phase parameter (-100 to +100) controlling lag compensation.</param>
|
||||||
/// <exception cref="ArgumentOutOfRangeException">
|
/// <param name="factor">The factor controlling volatility adaptation (default 0.45).</param>
|
||||||
/// Thrown when period is less than 1.
|
/// <param name="buffer">The size of the volatility buffer (default 10).</param>
|
||||||
/// </exception>
|
/// <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)
|
public Jma(int period, int phase = 0, double factor = 0.45, int buffer = 10)
|
||||||
{
|
{
|
||||||
if (period < 1)
|
if (period < 1)
|
||||||
@@ -51,20 +72,19 @@ public class Jma : AbstractBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
/// <param name="source">The source object to subscribe to for value updates.</param>
|
/// <param name="source">The data source object that publishes updates.</param>
|
||||||
/// <param name="period">The period over which to calculate the Jvolty.</param>
|
/// <param name="period">The period over which to calculate the JMA.</param>
|
||||||
/// <param name="phase">The phase parameter for the JMA-style calculation.</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)
|
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");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes the Jma instance by setting up the initial state.
|
|
||||||
/// </summary>
|
|
||||||
public override void Init()
|
public override void Init()
|
||||||
{
|
{
|
||||||
base.Init();
|
base.Init();
|
||||||
@@ -76,10 +96,6 @@ public class Jma : AbstractBase
|
|||||||
_vsumBuff.Clear();
|
_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)
|
protected override void ManageState(bool isNew)
|
||||||
{
|
{
|
||||||
if (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()
|
protected override double Calculation()
|
||||||
{
|
{
|
||||||
ManageState(Input.IsNew);
|
ManageState(Input.IsNew);
|
||||||
@@ -141,10 +151,10 @@ public class Jma : AbstractBase
|
|||||||
_lowerBand = (del2 <= 0) ? price : price - (Kv * del2);
|
_lowerBand = (del2 <= 0) ? price : price - (Kv * del2);
|
||||||
|
|
||||||
double _alpha = Math.Pow(_beta, pow2);
|
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;
|
_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;
|
_prevDet0 = det0;
|
||||||
double ma2 = ma1 + _phase * det0;
|
double ma2 = ma1 + _phase * det0;
|
||||||
|
|
||||||
|
|||||||
+34
-1
@@ -1,5 +1,30 @@
|
|||||||
|
using System;
|
||||||
namespace QuanTAlib;
|
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
|
public class Kama : AbstractBase
|
||||||
{
|
{
|
||||||
private readonly int _period;
|
private readonly int _period;
|
||||||
@@ -7,6 +32,10 @@ public class Kama : AbstractBase
|
|||||||
private CircularBuffer? _buffer;
|
private CircularBuffer? _buffer;
|
||||||
private double _lastKama, _p_lastKama;
|
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)
|
public Kama(int period, int fast = 2, int slow = 30)
|
||||||
{
|
{
|
||||||
if (period < 1)
|
if (period < 1)
|
||||||
@@ -21,6 +50,10 @@ public class Kama : AbstractBase
|
|||||||
Init();
|
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)
|
public Kama(object source, int period, int fast = 2, int slow = 30) : this(period, fast, slow)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
@@ -80,4 +113,4 @@ public class Kama : AbstractBase
|
|||||||
|
|
||||||
return kama;
|
return kama;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+32
-1
@@ -1,6 +1,30 @@
|
|||||||
|
using System;
|
||||||
namespace QuanTAlib;
|
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
|
public class Ltma : AbstractBase
|
||||||
{
|
{
|
||||||
@@ -8,8 +32,13 @@ public class Ltma : AbstractBase
|
|||||||
private double _prevL0, _prevL1, _prevL2, _prevL3;
|
private double _prevL0, _prevL1, _prevL2, _prevL3;
|
||||||
private double _p_prevL0, _p_prevL1, _p_prevL2, _p_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;
|
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)
|
public Ltma(double gamma = 0.1)
|
||||||
{
|
{
|
||||||
if (gamma < 0 || gamma > 1)
|
if (gamma < 0 || gamma > 1)
|
||||||
@@ -20,6 +49,8 @@ public class Ltma : AbstractBase
|
|||||||
Init();
|
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)
|
public Ltma(object source, double gamma = 0.1) : this(gamma)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
|
|||||||
+33
-4
@@ -1,8 +1,33 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
namespace QuanTAlib;
|
namespace QuanTAlib;
|
||||||
|
|
||||||
// https://efs.kb.esignal.com/hc/en-us/articles/6362791434395-2005-Mar-The-Secret-Behind-The-Filter-MedianAdaptiveFilter-efs
|
/// <summary>
|
||||||
|
/// MAAF: Median Adaptive Average Filter
|
||||||
//TODO Fix initial values
|
/// 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
|
public class Maaf : AbstractBase
|
||||||
{
|
{
|
||||||
@@ -14,6 +39,8 @@ public class Maaf : AbstractBase
|
|||||||
|
|
||||||
private readonly int _period;
|
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)
|
public Maaf(int period = 39, double threshold = 0.002)
|
||||||
{
|
{
|
||||||
_period = period;
|
_period = period;
|
||||||
@@ -25,6 +52,9 @@ public class Maaf : AbstractBase
|
|||||||
Init();
|
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)
|
public Maaf(object source, int period = 39, double threshold = 0.002) : this(period, threshold)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
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;
|
double smooth = (_priceBuffer[^1] + (2 * _priceBuffer[^2]) + (2 * _priceBuffer[^3]) + _priceBuffer[^4]) / 6;
|
||||||
_smoothBuffer.Add(smooth, Input.IsNew);
|
_smoothBuffer.Add(smooth, Input.IsNew);
|
||||||
|
|
||||||
|
|
||||||
if (_smoothBuffer.Count < _period)
|
if (_smoothBuffer.Count < _period)
|
||||||
{
|
{
|
||||||
return smooth;
|
return smooth;
|
||||||
|
|||||||
@@ -1,5 +1,32 @@
|
|||||||
|
using System;
|
||||||
namespace QuanTAlib;
|
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
|
public class Mama : AbstractBase
|
||||||
{
|
{
|
||||||
private readonly double _fastLimit, _slowLimit;
|
private readonly double _fastLimit, _slowLimit;
|
||||||
@@ -8,8 +35,13 @@ public class Mama : AbstractBase
|
|||||||
private double _prevMama, _prevFama, _sumPr;
|
private double _prevMama, _prevFama, _sumPr;
|
||||||
private double _p_prevMama, _p_prevFama, _p_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; }
|
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)
|
public Mama(double fastLimit = 0.5, double slowLimit = 0.05)
|
||||||
{
|
{
|
||||||
Fama = new TValue();
|
Fama = new TValue();
|
||||||
@@ -30,6 +62,9 @@ public class Mama : AbstractBase
|
|||||||
Init();
|
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)
|
public Mama(object source, double fastLimit = 0.5, double slowLimit = 0.05) : this(fastLimit, slowLimit)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
|
|||||||
+33
-1
@@ -1,10 +1,39 @@
|
|||||||
|
using System;
|
||||||
namespace QuanTAlib;
|
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
|
public class Mgdi : AbstractBase
|
||||||
{
|
{
|
||||||
private readonly int _period;
|
private readonly int _period;
|
||||||
private readonly double _kFactor;
|
private readonly double _kFactor;
|
||||||
private double _prevMd, _p_prevMd;
|
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)
|
public Mgdi(int period, double kFactor = 0.6)
|
||||||
{
|
{
|
||||||
if (period <= 0)
|
if (period <= 0)
|
||||||
@@ -22,6 +51,9 @@ public class Mgdi : AbstractBase
|
|||||||
Init();
|
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)
|
public Mgdi(object source, int period, double kFactor = 0.6) : this(period, kFactor)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
@@ -67,4 +99,4 @@ public class Mgdi : AbstractBase
|
|||||||
IsHot = _index >= _period;
|
IsHot = _index >= _period;
|
||||||
return _prevMd;
|
return _prevMd;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,38 @@
|
|||||||
|
using System;
|
||||||
namespace QuanTAlib;
|
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
|
public class Mma : AbstractBase
|
||||||
{
|
{
|
||||||
private readonly int _period;
|
private readonly int _period;
|
||||||
private readonly CircularBuffer _buffer;
|
private readonly CircularBuffer _buffer;
|
||||||
private double _lastMma;
|
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)
|
public Mma(int period)
|
||||||
{
|
{
|
||||||
if (period < 2)
|
if (period < 2)
|
||||||
@@ -19,6 +46,8 @@ public class Mma : AbstractBase
|
|||||||
Init();
|
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)
|
public Mma(object source, int period) : this(period)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
@@ -61,6 +90,11 @@ public class Mma : AbstractBase
|
|||||||
return _lastMma;
|
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()
|
private double CalculateWeightedSum()
|
||||||
{
|
{
|
||||||
double sum = 0;
|
double sum = 0;
|
||||||
|
|||||||
@@ -1,10 +1,39 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
namespace QuanTAlib;
|
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
|
public class Pwma : AbstractBase
|
||||||
{
|
{
|
||||||
private readonly int _period;
|
private readonly int _period;
|
||||||
private readonly Convolution _convolution;
|
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)
|
public Pwma(int period)
|
||||||
{
|
{
|
||||||
if (period < 1)
|
if (period < 1)
|
||||||
@@ -18,6 +47,8 @@ public class Pwma : AbstractBase
|
|||||||
Init();
|
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)
|
public Pwma(object source, int period) : this(period)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
@@ -60,6 +91,11 @@ public class Pwma : AbstractBase
|
|||||||
return result;
|
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)
|
public static double[] GenerateKernel(int period)
|
||||||
{
|
{
|
||||||
double[] kernel = new double[period];
|
double[] kernel = new double[period];
|
||||||
|
|||||||
+38
-2
@@ -1,11 +1,42 @@
|
|||||||
|
using System;
|
||||||
namespace QuanTAlib;
|
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
|
public class Qema : AbstractBase
|
||||||
{
|
{
|
||||||
|
|
||||||
private readonly Ema _ema1, _ema2, _ema3, _ema4;
|
private readonly Ema _ema1, _ema2, _ema3, _ema4;
|
||||||
private double _lastQema, _p_lastQema;
|
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)
|
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)
|
if (k1 <= 0 || k2 <= 0 || k3 <= 0 || k4 <= 0)
|
||||||
@@ -25,6 +56,11 @@ public class Qema : AbstractBase
|
|||||||
Init();
|
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)
|
public Qema(object source, double k1, double k2, double k3, double k4)
|
||||||
: this(k1, k2, k3, k4)
|
: this(k1, k2, k3, k4)
|
||||||
{
|
{
|
||||||
@@ -66,4 +102,4 @@ public class Qema : AbstractBase
|
|||||||
IsHot = _index >= WarmupPeriod;
|
IsHot = _index >= WarmupPeriod;
|
||||||
return _lastQema;
|
return _lastQema;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+40
-2
@@ -1,6 +1,29 @@
|
|||||||
|
using System;
|
||||||
namespace QuanTAlib;
|
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
|
public class Rema : AbstractBase
|
||||||
{
|
{
|
||||||
@@ -9,9 +32,19 @@ public class Rema : AbstractBase
|
|||||||
private double _lastRema, _prevRema;
|
private double _lastRema, _prevRema;
|
||||||
private double _savedLastRema, _savedPrevRema;
|
private double _savedLastRema, _savedPrevRema;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the period used in the REMA calculation.
|
||||||
|
/// </summary>
|
||||||
public int Period => _period;
|
public int Period => _period;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the lambda (regularization) parameter value.
|
||||||
|
/// </summary>
|
||||||
public double Lambda => _lambda;
|
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)
|
public Rema(int period, double lambda = 0.5)
|
||||||
{
|
{
|
||||||
if (period < 1)
|
if (period < 1)
|
||||||
@@ -25,11 +58,16 @@ public class Rema : AbstractBase
|
|||||||
WarmupPeriod = period;
|
WarmupPeriod = period;
|
||||||
Init();
|
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)
|
public Rema(object source, int period, double lambda = 0.5) : this(period, lambda)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Init()
|
public override void Init()
|
||||||
{
|
{
|
||||||
base.Init();
|
base.Init();
|
||||||
@@ -79,4 +117,4 @@ public class Rema : AbstractBase
|
|||||||
IsHot = _index >= WarmupPeriod;
|
IsHot = _index >= WarmupPeriod;
|
||||||
return _lastRema;
|
return _lastRema;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+36
-1
@@ -1,9 +1,37 @@
|
|||||||
|
using System;
|
||||||
namespace QuanTAlib;
|
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
|
public class Sinema : AbstractBase
|
||||||
{
|
{
|
||||||
private readonly Convolution _convolution;
|
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)
|
public Sinema(int period)
|
||||||
{
|
{
|
||||||
if (period < 1)
|
if (period < 1)
|
||||||
@@ -16,6 +44,8 @@ public class Sinema : AbstractBase
|
|||||||
Init();
|
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)
|
public Sinema(object source, int period) : this(period)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
@@ -50,6 +80,11 @@ public class Sinema : AbstractBase
|
|||||||
return result;
|
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)
|
public static double[] GenerateKernel(int period)
|
||||||
{
|
{
|
||||||
double[] kernel = new double[period];
|
double[] kernel = new double[period];
|
||||||
@@ -70,4 +105,4 @@ public class Sinema : AbstractBase
|
|||||||
|
|
||||||
return kernel;
|
return kernel;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+32
-4
@@ -1,11 +1,38 @@
|
|||||||
|
using System;
|
||||||
namespace QuanTAlib;
|
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
|
public class Sma : AbstractBase
|
||||||
{
|
{
|
||||||
// inherited _index
|
// inherited _index
|
||||||
// inherited _value
|
// inherited _value
|
||||||
private readonly CircularBuffer _buffer;
|
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)
|
public Sma(int period)
|
||||||
{
|
{
|
||||||
if (period < 1)
|
if (period < 1)
|
||||||
@@ -19,12 +46,13 @@ public class Sma : AbstractBase
|
|||||||
Init();
|
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)
|
public Sma(object source, int period) : this(period: period)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||||
}
|
}
|
||||||
//inhereted public void Sub(object source, in ValueEventArgs args)
|
|
||||||
|
|
||||||
protected override void ManageState(bool isNew)
|
protected override void ManageState(bool isNew)
|
||||||
{
|
{
|
||||||
@@ -36,9 +64,9 @@ public class Sma : AbstractBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Core SMA calculation - using _buffer.Average
|
/// Performs the core SMA calculation using the circular buffer's average.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <returns>The calculated SMA value.</returns>
|
||||||
protected override double Calculation()
|
protected override double Calculation()
|
||||||
{
|
{
|
||||||
double result;
|
double result;
|
||||||
@@ -49,4 +77,4 @@ public class Sma : AbstractBase
|
|||||||
IsHot = _index >= WarmupPeriod;
|
IsHot = _index >= WarmupPeriod;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+29
-3
@@ -1,13 +1,38 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace QuanTAlib;
|
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
|
public class Smma : AbstractBase
|
||||||
{
|
{
|
||||||
private readonly int _period;
|
private readonly int _period;
|
||||||
private CircularBuffer? _buffer;
|
private CircularBuffer? _buffer;
|
||||||
private double _lastSmma, _p_lastSmma;
|
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)
|
public Smma(int period)
|
||||||
{
|
{
|
||||||
if (period < 1)
|
if (period < 1)
|
||||||
@@ -20,6 +45,8 @@ public class Smma : AbstractBase
|
|||||||
Init();
|
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)
|
public Smma(object source, int period) : this(period)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
@@ -47,7 +74,6 @@ public class Smma : AbstractBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected override double Calculation()
|
protected override double Calculation()
|
||||||
{
|
{
|
||||||
ManageState(Input.IsNew);
|
ManageState(Input.IsNew);
|
||||||
@@ -75,4 +101,4 @@ public class Smma : AbstractBase
|
|||||||
|
|
||||||
return smma;
|
return smma;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+35
-3
@@ -1,5 +1,31 @@
|
|||||||
|
using System;
|
||||||
namespace QuanTAlib;
|
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
|
public class T3 : AbstractBase
|
||||||
{
|
{
|
||||||
private readonly int _period;
|
private readonly int _period;
|
||||||
@@ -10,6 +36,10 @@ public class T3 : AbstractBase
|
|||||||
private double _lastEma1, _lastEma2, _lastEma3, _lastEma4, _lastEma5, _lastEma6;
|
private double _lastEma1, _lastEma2, _lastEma3, _lastEma4, _lastEma5, _lastEma6;
|
||||||
private double _p_lastEma1, _p_lastEma2, _p_lastEma3, _p_lastEma4, _p_lastEma5, _p_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)
|
public T3(int period, double vfactor = 0.7, bool useSma = true)
|
||||||
{
|
{
|
||||||
if (period < 1)
|
if (period < 1)
|
||||||
@@ -35,11 +65,14 @@ public class T3 : AbstractBase
|
|||||||
_buffer5 = new(period);
|
_buffer5 = new(period);
|
||||||
_buffer6 = new(period);
|
_buffer6 = new(period);
|
||||||
|
|
||||||
|
|
||||||
Name = $"T3({_period}, {_vfactor})";
|
Name = $"T3({_period}, {_vfactor})";
|
||||||
Init();
|
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)
|
public T3(object source, int period, double vfactor = 0.7, bool useSma = true) : this(period, vfactor, useSma)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
@@ -81,7 +114,6 @@ public class T3 : AbstractBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected override double Calculation()
|
protected override double Calculation()
|
||||||
{
|
{
|
||||||
ManageState(Input.IsNew);
|
ManageState(Input.IsNew);
|
||||||
@@ -129,4 +161,4 @@ public class T3 : AbstractBase
|
|||||||
IsHot = _index >= WarmupPeriod;
|
IsHot = _index >= WarmupPeriod;
|
||||||
return t3;
|
return t3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+31
-3
@@ -1,5 +1,31 @@
|
|||||||
|
using System;
|
||||||
namespace QuanTAlib;
|
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
|
public class Tema : AbstractBase
|
||||||
{
|
{
|
||||||
private readonly int _period;
|
private readonly int _period;
|
||||||
@@ -8,6 +34,8 @@ public class Tema : AbstractBase
|
|||||||
private double _lastEma3, _p_lastEma3;
|
private double _lastEma3, _p_lastEma3;
|
||||||
private double _k, _e, _p_e;
|
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)
|
public Tema(int period)
|
||||||
{
|
{
|
||||||
if (period < 1)
|
if (period < 1)
|
||||||
@@ -21,6 +49,8 @@ public class Tema : AbstractBase
|
|||||||
Init();
|
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)
|
public Tema(object source, int period) : this(period)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
@@ -63,9 +93,7 @@ public class Tema : AbstractBase
|
|||||||
double _invE = (_e > 1e-10) ? 1 / (1 - _e) : 1;
|
double _invE = (_e > 1e-10) ? 1 / (1 - _e) : 1;
|
||||||
|
|
||||||
_ema1 = _k * (Input.Value - _lastEma1) + _lastEma1;
|
_ema1 = _k * (Input.Value - _lastEma1) + _lastEma1;
|
||||||
|
|
||||||
_ema2 = _k * (_ema1 * _invE - _lastEma2) + _lastEma2;
|
_ema2 = _k * (_ema1 * _invE - _lastEma2) + _lastEma2;
|
||||||
|
|
||||||
_ema3 = _k * (_ema2 * _invE - _lastEma3) + _lastEma3;
|
_ema3 = _k * (_ema2 * _invE - _lastEma3) + _lastEma3;
|
||||||
|
|
||||||
double _tema = 3 * _ema1 * _invE - 3 * _ema2 * _invE + _ema3 * _invE;
|
double _tema = 3 * _ema1 * _invE - 3 * _ema2 * _invE + _ema3 * _invE;
|
||||||
@@ -78,4 +106,4 @@ public class Tema : AbstractBase
|
|||||||
IsHot = _index >= WarmupPeriod;
|
IsHot = _index >= WarmupPeriod;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+36
-1
@@ -1,9 +1,37 @@
|
|||||||
|
using System;
|
||||||
namespace QuanTAlib;
|
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
|
public class Trima : AbstractBase
|
||||||
{
|
{
|
||||||
private readonly Convolution _convolution;
|
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)
|
public Trima(int period)
|
||||||
{
|
{
|
||||||
if (period < 1)
|
if (period < 1)
|
||||||
@@ -16,12 +44,19 @@ public class Trima : AbstractBase
|
|||||||
Init();
|
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)
|
public Trima(object source, int period) : this(period)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
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)
|
private static double[] GenerateKernel(int period)
|
||||||
{
|
{
|
||||||
double[] kernel = new double[period];
|
double[] kernel = new double[period];
|
||||||
@@ -70,4 +105,4 @@ public class Trima : AbstractBase
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+39
-2
@@ -4,15 +4,43 @@ using System.Runtime.CompilerServices;
|
|||||||
|
|
||||||
namespace QuanTAlib;
|
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
|
public class Vidya : AbstractBase
|
||||||
{
|
{
|
||||||
|
|
||||||
private readonly int _longPeriod;
|
private readonly int _longPeriod;
|
||||||
private readonly double _alpha;
|
private readonly double _alpha;
|
||||||
private double _lastVIDYA, _p_lastVIDYA;
|
private double _lastVIDYA, _p_lastVIDYA;
|
||||||
private readonly CircularBuffer? _shortBuffer;
|
private readonly CircularBuffer? _shortBuffer;
|
||||||
private readonly CircularBuffer? _longBuffer;
|
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)
|
public Vidya(int shortPeriod, int longPeriod = 0, double alpha = 0.2)
|
||||||
{
|
{
|
||||||
if (shortPeriod < 1)
|
if (shortPeriod < 1)
|
||||||
@@ -28,6 +56,10 @@ public class Vidya : AbstractBase
|
|||||||
Init();
|
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)
|
public Vidya(object source, int shortPeriod, int longPeriod = 0, double alpha = 0.2)
|
||||||
: this(shortPeriod, longPeriod, alpha)
|
: this(shortPeriod, longPeriod, alpha)
|
||||||
{
|
{
|
||||||
@@ -81,6 +113,11 @@ public class Vidya : AbstractBase
|
|||||||
return vidya;
|
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)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static double CalculateStdDev(CircularBuffer buffer)
|
private static double CalculateStdDev(CircularBuffer buffer)
|
||||||
{
|
{
|
||||||
@@ -88,4 +125,4 @@ public class Vidya : AbstractBase
|
|||||||
double sumSquaredDiff = buffer.Sum(x => Math.Pow(x - mean, 2));
|
double sumSquaredDiff = buffer.Sum(x => Math.Pow(x - mean, 2));
|
||||||
return Math.Sqrt(sumSquaredDiff / buffer.Count);
|
return Math.Sqrt(sumSquaredDiff / buffer.Count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+37
-1
@@ -1,10 +1,39 @@
|
|||||||
|
using System;
|
||||||
namespace QuanTAlib;
|
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
|
public class Wma : AbstractBase
|
||||||
{
|
{
|
||||||
private readonly int _period;
|
private readonly int _period;
|
||||||
private readonly Convolution _convolution;
|
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)
|
public Wma(int period)
|
||||||
{
|
{
|
||||||
if (period < 1)
|
if (period < 1)
|
||||||
@@ -18,12 +47,19 @@ public class Wma : AbstractBase
|
|||||||
Init();
|
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)
|
public Wma(object source, int period) : this(period)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
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)
|
private static double[] GenerateWmaKernel(int period)
|
||||||
{
|
{
|
||||||
double[] kernel = new double[period];
|
double[] kernel = new double[period];
|
||||||
@@ -64,4 +100,4 @@ public class Wma : AbstractBase
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,31 @@ using System.Runtime.CompilerServices;
|
|||||||
|
|
||||||
namespace QuanTAlib
|
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
|
public class Zlema : AbstractBase
|
||||||
{
|
{
|
||||||
private readonly CircularBuffer _buffer;
|
private readonly CircularBuffer _buffer;
|
||||||
@@ -10,6 +35,8 @@ namespace QuanTAlib
|
|||||||
private readonly Ema _ema;
|
private readonly Ema _ema;
|
||||||
private double _lastZLEMA, _p_lastZLEMA;
|
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)
|
public Zlema(int period)
|
||||||
{
|
{
|
||||||
if (period < 1)
|
if (period < 1)
|
||||||
@@ -24,6 +51,8 @@ namespace QuanTAlib
|
|||||||
Init();
|
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)
|
public Zlema(object source, int period) : this(period)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
@@ -59,8 +88,11 @@ namespace QuanTAlib
|
|||||||
|
|
||||||
_buffer.Add(Input.Value, Input.IsNew);
|
_buffer.Add(Input.Value, Input.IsNew);
|
||||||
|
|
||||||
|
// Get lagged value and calculate error correction
|
||||||
double lagValue = _buffer[Math.Max(0, _buffer.Count - 1 - _lag)];
|
double lagValue = _buffer[Math.Max(0, _buffer.Count - 1 - _lag)];
|
||||||
double errorCorrection = 2 * Input.Value - lagValue;
|
double errorCorrection = 2 * Input.Value - lagValue;
|
||||||
|
|
||||||
|
// Apply EMA to error-corrected value
|
||||||
double zlema = _ema.Calc(new TValue(errorCorrection, Input.IsNew)).Value;
|
double zlema = _ema.Calc(new TValue(errorCorrection, Input.IsNew)).Value;
|
||||||
|
|
||||||
_lastZLEMA = zlema;
|
_lastZLEMA = zlema;
|
||||||
|
|||||||
+39
-1
@@ -1,11 +1,44 @@
|
|||||||
|
using System;
|
||||||
namespace QuanTAlib;
|
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
|
public class Huber : AbstractBase
|
||||||
{
|
{
|
||||||
private readonly CircularBuffer _actualBuffer;
|
private readonly CircularBuffer _actualBuffer;
|
||||||
private readonly CircularBuffer _predictedBuffer;
|
private readonly CircularBuffer _predictedBuffer;
|
||||||
private readonly double _delta;
|
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)
|
public Huber(int period, double delta = 1.0)
|
||||||
{
|
{
|
||||||
if (period < 1)
|
if (period < 1)
|
||||||
@@ -24,6 +57,9 @@ public class Huber : AbstractBase
|
|||||||
Init();
|
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)
|
public Huber(object source, int period, double delta = 1.0) : this(period, delta)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
@@ -53,6 +89,7 @@ public class Huber : AbstractBase
|
|||||||
double actual = Input.Value;
|
double actual = Input.Value;
|
||||||
_actualBuffer.Add(actual, Input.IsNew);
|
_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;
|
double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value;
|
||||||
_predictedBuffer.Add(predicted, Input.IsNew);
|
_predictedBuffer.Add(predicted, Input.IsNew);
|
||||||
|
|
||||||
@@ -70,10 +107,12 @@ public class Huber : AbstractBase
|
|||||||
|
|
||||||
if (absError <= _delta)
|
if (absError <= _delta)
|
||||||
{
|
{
|
||||||
|
// Squared error for small deviations
|
||||||
sumLoss += 0.5 * error * error;
|
sumLoss += 0.5 * error * error;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// Linear error for large deviations
|
||||||
sumLoss += _delta * (absError - 0.5 * _delta);
|
sumLoss += _delta * (absError - 0.5 * _delta);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -84,5 +123,4 @@ public class Huber : AbstractBase
|
|||||||
IsHot = _index >= WarmupPeriod;
|
IsHot = _index >= WarmupPeriod;
|
||||||
return huberloss;
|
return huberloss;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+33
-1
@@ -1,10 +1,40 @@
|
|||||||
|
using System;
|
||||||
namespace QuanTAlib;
|
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
|
public class Mae : AbstractBase
|
||||||
{
|
{
|
||||||
private readonly CircularBuffer _actualBuffer;
|
private readonly CircularBuffer _actualBuffer;
|
||||||
private readonly CircularBuffer _predictedBuffer;
|
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)
|
public Mae(int period)
|
||||||
{
|
{
|
||||||
if (period < 1)
|
if (period < 1)
|
||||||
@@ -18,6 +48,8 @@ public class Mae : AbstractBase
|
|||||||
Init();
|
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)
|
public Mae(object source, int period) : this(period)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
@@ -47,6 +79,7 @@ public class Mae : AbstractBase
|
|||||||
double actual = Input.Value;
|
double actual = Input.Value;
|
||||||
_actualBuffer.Add(actual, Input.IsNew);
|
_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;
|
double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value;
|
||||||
_predictedBuffer.Add(predicted, Input.IsNew);
|
_predictedBuffer.Add(predicted, Input.IsNew);
|
||||||
|
|
||||||
@@ -68,5 +101,4 @@ public class Mae : AbstractBase
|
|||||||
IsHot = _index >= WarmupPeriod;
|
IsHot = _index >= WarmupPeriod;
|
||||||
return mae;
|
return mae;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+35
-1
@@ -1,10 +1,42 @@
|
|||||||
|
using System;
|
||||||
namespace QuanTAlib;
|
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
|
public class Mapd : AbstractBase
|
||||||
{
|
{
|
||||||
private readonly CircularBuffer _actualBuffer;
|
private readonly CircularBuffer _actualBuffer;
|
||||||
private readonly CircularBuffer _predictedBuffer;
|
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)
|
public Mapd(int period)
|
||||||
{
|
{
|
||||||
if (period < 1)
|
if (period < 1)
|
||||||
@@ -18,6 +50,8 @@ public class Mapd : AbstractBase
|
|||||||
Init();
|
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)
|
public Mapd(object source, int period) : this(period)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
@@ -47,6 +81,7 @@ public class Mapd : AbstractBase
|
|||||||
double actual = Input.Value;
|
double actual = Input.Value;
|
||||||
_actualBuffer.Add(actual, Input.IsNew);
|
_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;
|
double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value;
|
||||||
_predictedBuffer.Add(predicted, Input.IsNew);
|
_predictedBuffer.Add(predicted, Input.IsNew);
|
||||||
|
|
||||||
@@ -71,5 +106,4 @@ public class Mapd : AbstractBase
|
|||||||
IsHot = _index >= WarmupPeriod;
|
IsHot = _index >= WarmupPeriod;
|
||||||
return mapd;
|
return mapd;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,42 @@
|
|||||||
|
using System;
|
||||||
namespace QuanTAlib;
|
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
|
public class Mape : AbstractBase
|
||||||
{
|
{
|
||||||
private readonly CircularBuffer _actualBuffer;
|
private readonly CircularBuffer _actualBuffer;
|
||||||
private readonly CircularBuffer _predictedBuffer;
|
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)
|
public Mape(int period)
|
||||||
{
|
{
|
||||||
if (period < 1)
|
if (period < 1)
|
||||||
@@ -18,6 +50,8 @@ public class Mape : AbstractBase
|
|||||||
Init();
|
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)
|
public Mape(object source, int period) : this(period)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
@@ -47,6 +81,7 @@ public class Mape : AbstractBase
|
|||||||
double actual = Input.Value;
|
double actual = Input.Value;
|
||||||
_actualBuffer.Add(actual, Input.IsNew);
|
_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;
|
double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value;
|
||||||
_predictedBuffer.Add(predicted, Input.IsNew);
|
_predictedBuffer.Add(predicted, Input.IsNew);
|
||||||
|
|
||||||
|
|||||||
+41
-22
@@ -1,20 +1,41 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace QuanTAlib;
|
namespace QuanTAlib;
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </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
|
public class Mase : AbstractBase
|
||||||
{
|
{
|
||||||
private readonly CircularBuffer _actualBuffer;
|
private readonly CircularBuffer _actualBuffer;
|
||||||
private readonly CircularBuffer _predictedBuffer;
|
private readonly CircularBuffer _predictedBuffer;
|
||||||
private readonly CircularBuffer _naiveBuffer;
|
private readonly CircularBuffer _naiveBuffer;
|
||||||
|
|
||||||
/// <summary>
|
/// <param name="period">The number of points over which to calculate the MASE.</param>
|
||||||
/// Initializes a new instance of the Mase class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="period">The period for MASE calculation.</param>
|
|
||||||
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
|
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
|
||||||
public Mase(int period)
|
public Mase(int period)
|
||||||
{
|
{
|
||||||
@@ -30,20 +51,14 @@ public class Mase : AbstractBase
|
|||||||
Init();
|
Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <param name="source">The data source object that publishes updates.</param>
|
||||||
/// Initializes a new instance of the Mase class with a source object.
|
/// <param name="period">The number of points over which to calculate the MASE.</param>
|
||||||
/// </summary>
|
|
||||||
/// <param name="source">The source object for event subscription.</param>
|
|
||||||
/// <param name="period">The period for MASE calculation.</param>
|
|
||||||
public Mase(object source, int period) : this(period)
|
public Mase(object source, int period) : this(period)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes the Mase instance.
|
|
||||||
/// </summary>
|
|
||||||
public override void Init()
|
public override void Init()
|
||||||
{
|
{
|
||||||
base.Init();
|
base.Init();
|
||||||
@@ -52,10 +67,6 @@ public class Mase : AbstractBase
|
|||||||
_naiveBuffer.Clear();
|
_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)
|
protected override void ManageState(bool isNew)
|
||||||
{
|
{
|
||||||
if (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()
|
protected override double Calculation()
|
||||||
{
|
{
|
||||||
ManageState(Input.IsNew);
|
ManageState(Input.IsNew);
|
||||||
@@ -76,9 +83,11 @@ public class Mase : AbstractBase
|
|||||||
double actual = Input.Value;
|
double actual = Input.Value;
|
||||||
_actualBuffer.Add(actual, Input.IsNew);
|
_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;
|
double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value;
|
||||||
_predictedBuffer.Add(predicted, Input.IsNew);
|
_predictedBuffer.Add(predicted, Input.IsNew);
|
||||||
|
|
||||||
|
// Naive forecast uses previous actual value
|
||||||
if (_actualBuffer.Count > 1)
|
if (_actualBuffer.Count > 1)
|
||||||
{
|
{
|
||||||
_naiveBuffer.Add(_actualBuffer.GetSpan()[^2], Input.IsNew);
|
_naiveBuffer.Add(_actualBuffer.GetSpan()[^2], Input.IsNew);
|
||||||
@@ -90,6 +99,10 @@ public class Mase : AbstractBase
|
|||||||
return mase;
|
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()
|
private double CalculateMase()
|
||||||
{
|
{
|
||||||
if (_actualBuffer.Count <= 1) return 0;
|
if (_actualBuffer.Count <= 1) return 0;
|
||||||
@@ -104,6 +117,9 @@ public class Mase : AbstractBase
|
|||||||
return _naiveForecastError != 0 ? (sumAbsoluteError / _actualBuffer.Count) / _naiveForecastError : double.PositiveInfinity;
|
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)
|
private static double CalculateSumAbsoluteError(ReadOnlySpan<double> actualValues, ReadOnlySpan<double> predictedValues)
|
||||||
{
|
{
|
||||||
double sum = 0;
|
double sum = 0;
|
||||||
@@ -114,6 +130,9 @@ public class Mase : AbstractBase
|
|||||||
return sum;
|
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)
|
private static double CalculateNaiveForecastError(ReadOnlySpan<double> actualValues, ReadOnlySpan<double> naiveValues)
|
||||||
{
|
{
|
||||||
double sum = 0;
|
double sum = 0;
|
||||||
|
|||||||
@@ -1,10 +1,42 @@
|
|||||||
|
using System;
|
||||||
namespace QuanTAlib;
|
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
|
public class Mda : AbstractBase
|
||||||
{
|
{
|
||||||
private readonly CircularBuffer _actualBuffer;
|
private readonly CircularBuffer _actualBuffer;
|
||||||
private readonly CircularBuffer _predictedBuffer;
|
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)
|
public Mda(int period)
|
||||||
{
|
{
|
||||||
if (period < 1)
|
if (period < 1)
|
||||||
@@ -18,6 +50,8 @@ public class Mda : AbstractBase
|
|||||||
Init();
|
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)
|
public Mda(object source, int period) : this(period)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
@@ -47,6 +81,7 @@ public class Mda : AbstractBase
|
|||||||
double actual = Input.Value;
|
double actual = Input.Value;
|
||||||
_actualBuffer.Add(actual, Input.IsNew);
|
_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;
|
double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value;
|
||||||
_predictedBuffer.Add(predicted, Input.IsNew);
|
_predictedBuffer.Add(predicted, Input.IsNew);
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,42 @@
|
|||||||
|
using System;
|
||||||
namespace QuanTAlib;
|
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
|
public class Me : AbstractBase
|
||||||
{
|
{
|
||||||
private readonly CircularBuffer _actualBuffer;
|
private readonly CircularBuffer _actualBuffer;
|
||||||
private readonly CircularBuffer _predictedBuffer;
|
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)
|
public Me(int period)
|
||||||
{
|
{
|
||||||
if (period < 1)
|
if (period < 1)
|
||||||
@@ -18,6 +50,8 @@ public class Me : AbstractBase
|
|||||||
Init();
|
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)
|
public Me(object source, int period) : this(period)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
@@ -47,6 +81,7 @@ public class Me : AbstractBase
|
|||||||
double actual = Input.Value;
|
double actual = Input.Value;
|
||||||
_actualBuffer.Add(actual, Input.IsNew);
|
_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;
|
double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value;
|
||||||
_predictedBuffer.Add(predicted, Input.IsNew);
|
_predictedBuffer.Add(predicted, Input.IsNew);
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,43 @@
|
|||||||
|
using System;
|
||||||
namespace QuanTAlib;
|
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
|
public class Mpe : AbstractBase
|
||||||
{
|
{
|
||||||
private readonly CircularBuffer _actualBuffer;
|
private readonly CircularBuffer _actualBuffer;
|
||||||
private readonly CircularBuffer _predictedBuffer;
|
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)
|
public Mpe(int period)
|
||||||
{
|
{
|
||||||
if (period < 1)
|
if (period < 1)
|
||||||
@@ -18,6 +51,8 @@ public class Mpe : AbstractBase
|
|||||||
Init();
|
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)
|
public Mpe(object source, int period) : this(period)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
@@ -47,6 +82,7 @@ public class Mpe : AbstractBase
|
|||||||
double actual = Input.Value;
|
double actual = Input.Value;
|
||||||
_actualBuffer.Add(actual, Input.IsNew);
|
_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;
|
double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value;
|
||||||
_predictedBuffer.Add(predicted, Input.IsNew);
|
_predictedBuffer.Add(predicted, Input.IsNew);
|
||||||
|
|
||||||
|
|||||||
+31
-34
@@ -1,25 +1,42 @@
|
|||||||
|
using System;
|
||||||
namespace QuanTAlib;
|
namespace QuanTAlib;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a Mean Squared Error calculator that measures the average of the squares
|
/// MSE: Mean Squared Error
|
||||||
/// of the differences between actual values and predicted values.
|
/// 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>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// The Mse class calculates the Mean Squared Error using a circular buffer
|
/// The MSE calculation process:
|
||||||
/// to efficiently manage the data points within the specified period.
|
/// 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>
|
/// </remarks>
|
||||||
|
|
||||||
public class Mse : AbstractBase
|
public class Mse : AbstractBase
|
||||||
{
|
{
|
||||||
private readonly CircularBuffer _actualBuffer;
|
private readonly CircularBuffer _actualBuffer;
|
||||||
private readonly CircularBuffer _predictedBuffer;
|
private readonly CircularBuffer _predictedBuffer;
|
||||||
|
|
||||||
/// <summary>
|
/// <param name="period">The number of points over which to calculate the MSE.</param>
|
||||||
/// Initializes a new instance of the Mse class with the specified period.
|
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
|
||||||
/// </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>
|
|
||||||
public Mse(int period)
|
public Mse(int period)
|
||||||
{
|
{
|
||||||
if (period < 1)
|
if (period < 1)
|
||||||
@@ -33,20 +50,14 @@ public class Mse : AbstractBase
|
|||||||
Init();
|
Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <param name="source">The data source object that publishes updates.</param>
|
||||||
/// Initializes a new instance of the Mse class with the specified source and period.
|
/// <param name="period">The number of points over which to calculate the MSE.</param>
|
||||||
/// </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>
|
|
||||||
public Mse(object source, int period) : this(period)
|
public Mse(object source, int period) : this(period)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes the Mse instance by clearing the buffers.
|
|
||||||
/// </summary>
|
|
||||||
public override void Init()
|
public override void Init()
|
||||||
{
|
{
|
||||||
base.Init();
|
base.Init();
|
||||||
@@ -54,10 +65,6 @@ public class Mse : AbstractBase
|
|||||||
_predictedBuffer.Clear();
|
_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)
|
protected override void ManageState(bool isNew)
|
||||||
{
|
{
|
||||||
if (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()
|
protected override double Calculation()
|
||||||
{
|
{
|
||||||
ManageState(Input.IsNew);
|
ManageState(Input.IsNew);
|
||||||
@@ -85,6 +81,7 @@ public class Mse : AbstractBase
|
|||||||
double actual = Input.Value;
|
double actual = Input.Value;
|
||||||
_actualBuffer.Add(actual, Input.IsNew);
|
_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;
|
double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value;
|
||||||
_predictedBuffer.Add(predicted, Input.IsNew);
|
_predictedBuffer.Add(predicted, Input.IsNew);
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,43 @@
|
|||||||
|
using System;
|
||||||
namespace QuanTAlib;
|
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
|
public class Msle : AbstractBase
|
||||||
{
|
{
|
||||||
private readonly CircularBuffer _actualBuffer;
|
private readonly CircularBuffer _actualBuffer;
|
||||||
private readonly CircularBuffer _predictedBuffer;
|
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)
|
public Msle(int period)
|
||||||
{
|
{
|
||||||
if (period < 1)
|
if (period < 1)
|
||||||
@@ -18,6 +51,8 @@ public class Msle : AbstractBase
|
|||||||
Init();
|
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)
|
public Msle(object source, int period) : this(period)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
@@ -47,6 +82,7 @@ public class Msle : AbstractBase
|
|||||||
double actual = Input.Value;
|
double actual = Input.Value;
|
||||||
_actualBuffer.Add(actual, Input.IsNew);
|
_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;
|
double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value;
|
||||||
_predictedBuffer.Add(predicted, Input.IsNew);
|
_predictedBuffer.Add(predicted, Input.IsNew);
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,42 @@
|
|||||||
|
using System;
|
||||||
namespace QuanTAlib;
|
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
|
public class Rae : AbstractBase
|
||||||
{
|
{
|
||||||
private readonly CircularBuffer _actualBuffer;
|
private readonly CircularBuffer _actualBuffer;
|
||||||
private readonly CircularBuffer _predictedBuffer;
|
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)
|
public Rae(int period)
|
||||||
{
|
{
|
||||||
if (period < 1)
|
if (period < 1)
|
||||||
@@ -18,6 +50,8 @@ public class Rae : AbstractBase
|
|||||||
Init();
|
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)
|
public Rae(object source, int period) : this(period)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
@@ -47,6 +81,7 @@ public class Rae : AbstractBase
|
|||||||
double actual = Input.Value;
|
double actual = Input.Value;
|
||||||
_actualBuffer.Add(actual, Input.IsNew);
|
_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;
|
double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value;
|
||||||
_predictedBuffer.Add(predicted, Input.IsNew);
|
_predictedBuffer.Add(predicted, Input.IsNew);
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,43 @@
|
|||||||
|
using System;
|
||||||
namespace QuanTAlib;
|
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
|
public class Rmse : AbstractBase
|
||||||
{
|
{
|
||||||
private readonly CircularBuffer _actualBuffer;
|
private readonly CircularBuffer _actualBuffer;
|
||||||
private readonly CircularBuffer _predictedBuffer;
|
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)
|
public Rmse(int period)
|
||||||
{
|
{
|
||||||
if (period < 1)
|
if (period < 1)
|
||||||
@@ -18,6 +51,8 @@ public class Rmse : AbstractBase
|
|||||||
Init();
|
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)
|
public Rmse(object source, int period) : this(period)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
@@ -47,6 +82,7 @@ public class Rmse : AbstractBase
|
|||||||
double actual = Input.Value;
|
double actual = Input.Value;
|
||||||
_actualBuffer.Add(actual, Input.IsNew);
|
_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;
|
double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value;
|
||||||
_predictedBuffer.Add(predicted, Input.IsNew);
|
_predictedBuffer.Add(predicted, Input.IsNew);
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,44 @@
|
|||||||
|
using System;
|
||||||
namespace QuanTAlib;
|
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
|
public class Rmsle : AbstractBase
|
||||||
{
|
{
|
||||||
private readonly CircularBuffer _actualBuffer;
|
private readonly CircularBuffer _actualBuffer;
|
||||||
private readonly CircularBuffer _predictedBuffer;
|
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)
|
public Rmsle(int period)
|
||||||
{
|
{
|
||||||
if (period < 1)
|
if (period < 1)
|
||||||
@@ -18,6 +52,8 @@ public class Rmsle : AbstractBase
|
|||||||
Init();
|
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)
|
public Rmsle(object source, int period) : this(period)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
@@ -47,6 +83,7 @@ public class Rmsle : AbstractBase
|
|||||||
double actual = Input.Value;
|
double actual = Input.Value;
|
||||||
_actualBuffer.Add(actual, Input.IsNew);
|
_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;
|
double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value;
|
||||||
_predictedBuffer.Add(predicted, Input.IsNew);
|
_predictedBuffer.Add(predicted, Input.IsNew);
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,43 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
namespace QuanTAlib;
|
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
|
public class Rse : AbstractBase
|
||||||
{
|
{
|
||||||
private readonly CircularBuffer _actualBuffer;
|
private readonly CircularBuffer _actualBuffer;
|
||||||
private readonly CircularBuffer _predictedBuffer;
|
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)
|
public Rse(int period)
|
||||||
{
|
{
|
||||||
if (period < 1)
|
if (period < 1)
|
||||||
@@ -18,6 +51,8 @@ public class Rse : AbstractBase
|
|||||||
Init();
|
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)
|
public Rse(object source, int period) : this(period)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
@@ -47,6 +82,7 @@ public class Rse : AbstractBase
|
|||||||
double actual = Input.Value;
|
double actual = Input.Value;
|
||||||
_actualBuffer.Add(actual, Input.IsNew);
|
_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;
|
double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value;
|
||||||
_predictedBuffer.Add(predicted, Input.IsNew);
|
_predictedBuffer.Add(predicted, Input.IsNew);
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,43 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
namespace QuanTAlib;
|
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
|
public class Rsquared : AbstractBase
|
||||||
{
|
{
|
||||||
private readonly CircularBuffer _actualBuffer;
|
private readonly CircularBuffer _actualBuffer;
|
||||||
private readonly CircularBuffer _predictedBuffer;
|
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)
|
public Rsquared(int period)
|
||||||
{
|
{
|
||||||
if (period < 1)
|
if (period < 1)
|
||||||
@@ -18,6 +51,8 @@ public class Rsquared : AbstractBase
|
|||||||
Init();
|
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)
|
public Rsquared(object source, int period) : this(period)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
@@ -47,6 +82,7 @@ public class Rsquared : AbstractBase
|
|||||||
double actual = Input.Value;
|
double actual = Input.Value;
|
||||||
_actualBuffer.Add(actual, Input.IsNew);
|
_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;
|
double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value;
|
||||||
_predictedBuffer.Add(predicted, Input.IsNew);
|
_predictedBuffer.Add(predicted, Input.IsNew);
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,42 @@
|
|||||||
|
using System;
|
||||||
namespace QuanTAlib;
|
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
|
public class Smape : AbstractBase
|
||||||
{
|
{
|
||||||
private readonly CircularBuffer _actualBuffer;
|
private readonly CircularBuffer _actualBuffer;
|
||||||
private readonly CircularBuffer _predictedBuffer;
|
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)
|
public Smape(int period)
|
||||||
{
|
{
|
||||||
if (period < 1)
|
if (period < 1)
|
||||||
@@ -18,6 +50,8 @@ public class Smape : AbstractBase
|
|||||||
Init();
|
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)
|
public Smape(object source, int period) : this(period)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
@@ -47,6 +81,7 @@ public class Smape : AbstractBase
|
|||||||
double actual = Input.Value;
|
double actual = Input.Value;
|
||||||
_actualBuffer.Add(actual, Input.IsNew);
|
_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;
|
double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value;
|
||||||
_predictedBuffer.Add(predicted, Input.IsNew);
|
_predictedBuffer.Add(predicted, Input.IsNew);
|
||||||
|
|
||||||
|
|||||||
+41
-8
@@ -1,14 +1,47 @@
|
|||||||
|
using System;
|
||||||
namespace QuanTAlib;
|
namespace QuanTAlib;
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </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
|
public class Cmo : AbstractBase
|
||||||
{
|
{
|
||||||
private readonly CircularBuffer _sumH;
|
private readonly CircularBuffer _sumH;
|
||||||
private readonly CircularBuffer _sumL;
|
private readonly CircularBuffer _sumL;
|
||||||
private double _prevValue, _p_prevValue;
|
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)
|
public Cmo(int period)
|
||||||
{
|
{
|
||||||
if (period < 1)
|
if (period < 1)
|
||||||
@@ -16,15 +49,12 @@ public class Cmo : AbstractBase
|
|||||||
_sumH = new(period);
|
_sumH = new(period);
|
||||||
_sumL = new(period);
|
_sumL = new(period);
|
||||||
|
|
||||||
WarmupPeriod = period+1;
|
WarmupPeriod = period + 1;
|
||||||
Name = $"CMO({period})";
|
Name = $"CMO({period})";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <param name="source">The data source object that publishes updates.</param>
|
||||||
/// Initializes a new instance of the CMO class with a data source.
|
/// <param name="period">The number of periods used in the CMO calculation.</param>
|
||||||
/// </summary>
|
|
||||||
/// <param name="source">The source object that publishes data.</param>
|
|
||||||
/// <param name="period">The number of data points to consider.</param>
|
|
||||||
public Cmo(object source, int period) : this(period)
|
public Cmo(object source, int period) : this(period)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
@@ -53,9 +83,11 @@ public class Cmo : AbstractBase
|
|||||||
_prevValue = Input.Value;
|
_prevValue = Input.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calculate price difference
|
||||||
double diff = Input.Value - _prevValue;
|
double diff = Input.Value - _prevValue;
|
||||||
_prevValue = Input.Value;
|
_prevValue = Input.Value;
|
||||||
|
|
||||||
|
// Separate upward and downward movements
|
||||||
if (diff > 0)
|
if (diff > 0)
|
||||||
{
|
{
|
||||||
_sumH.Add(diff, Input.IsNew);
|
_sumH.Add(diff, Input.IsNew);
|
||||||
@@ -67,11 +99,12 @@ public class Cmo : AbstractBase
|
|||||||
_sumL.Add(-diff, Input.IsNew);
|
_sumL.Add(-diff, Input.IsNew);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate sums for the specified period only
|
// Calculate sums for the specified period
|
||||||
double sumH = _sumH.Sum();
|
double sumH = _sumH.Sum();
|
||||||
double sumL = _sumL.Sum();
|
double sumL = _sumL.Sum();
|
||||||
double divisor = sumH + sumL;
|
double divisor = sumH + sumL;
|
||||||
|
|
||||||
|
// Calculate CMO value
|
||||||
return (Math.Abs(divisor) > double.Epsilon) ?
|
return (Math.Abs(divisor) > double.Epsilon) ?
|
||||||
100.0 * ((sumH - sumL) / divisor) :
|
100.0 * ((sumH - sumL) / divisor) :
|
||||||
0.0;
|
0.0;
|
||||||
|
|||||||
+39
-7
@@ -1,16 +1,48 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace QuanTAlib;
|
namespace QuanTAlib;
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </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
|
public class Rsi : AbstractBase
|
||||||
{
|
{
|
||||||
private readonly Rma _avgGain;
|
private readonly Rma _avgGain;
|
||||||
private readonly Rma _avgLoss;
|
private readonly Rma _avgLoss;
|
||||||
private double _prevValue, _p_prevValue;
|
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)
|
public Rsi(int period = 14)
|
||||||
{
|
{
|
||||||
if (period < 1)
|
if (period < 1)
|
||||||
@@ -22,11 +54,8 @@ public class Rsi : AbstractBase
|
|||||||
Name = $"RSI({period})";
|
Name = $"RSI({period})";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <param name="source">The data source object that publishes updates.</param>
|
||||||
/// Initializes a new instance of the RSI class with a data source.
|
/// <param name="period">The number of periods used in the RSI calculation.</param>
|
||||||
/// </summary>
|
|
||||||
/// <param name="source">The source object that publishes data.</param>
|
|
||||||
/// <param name="period">The number of data points to consider.</param>
|
|
||||||
public Rsi(object source, int period) : this(period)
|
public Rsi(object source, int period) : this(period)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
@@ -55,14 +84,17 @@ public class Rsi : AbstractBase
|
|||||||
_prevValue = Input.Value;
|
_prevValue = Input.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calculate price change and separate gains/losses
|
||||||
double change = Input.Value - _prevValue;
|
double change = Input.Value - _prevValue;
|
||||||
double gain = Math.Max(change, 0);
|
double gain = Math.Max(change, 0);
|
||||||
double loss = Math.Max(-change, 0);
|
double loss = Math.Max(-change, 0);
|
||||||
_prevValue = Input.Value;
|
_prevValue = Input.Value;
|
||||||
|
|
||||||
|
// Calculate smoothed averages using Wilder's method
|
||||||
_avgGain.Calc(gain, IsNew: Input.IsNew);
|
_avgGain.Calc(gain, IsNew: Input.IsNew);
|
||||||
_avgLoss.Calc(loss, IsNew: Input.IsNew);
|
_avgLoss.Calc(loss, IsNew: Input.IsNew);
|
||||||
|
|
||||||
|
// Calculate RSI
|
||||||
double rsi = (_avgLoss.Value > 0) ? 100 - (100 / (1 + (_avgGain.Value / _avgLoss.Value))) : 100;
|
double rsi = (_avgLoss.Value > 0) ? 100 - (100 / (1 + (_avgGain.Value / _avgLoss.Value))) : 100;
|
||||||
|
|
||||||
return rsi;
|
return rsi;
|
||||||
|
|||||||
+43
-10
@@ -1,10 +1,39 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace QuanTAlib;
|
namespace QuanTAlib;
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </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
|
public class Rsx : AbstractBase
|
||||||
{
|
{
|
||||||
private readonly Rma _avgGain;
|
private readonly Rma _avgGain;
|
||||||
@@ -12,6 +41,10 @@ public class Rsx : AbstractBase
|
|||||||
private readonly Jma _rsx;
|
private readonly Jma _rsx;
|
||||||
private double _prevValue, _p_prevValue;
|
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)
|
public Rsx(int period = 14, int phase = 0, double factor = 0.55)
|
||||||
{
|
{
|
||||||
if (period < 1)
|
if (period < 1)
|
||||||
@@ -24,13 +57,10 @@ public class Rsx : AbstractBase
|
|||||||
Name = $"RSX({period})";
|
Name = $"RSX({period})";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <param name="source">The data source object that publishes updates.</param>
|
||||||
/// Initializes a new instance of the RSX class with a data source.
|
/// <param name="period">The number of periods for RSI calculation.</param>
|
||||||
/// </summary>
|
/// <param name="phase">The phase parameter for JMA smoothing.</param>
|
||||||
/// <param name="source">The source object that publishes data.</param>
|
/// <param name="factor">The factor parameter for smoothing control.</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>
|
|
||||||
public Rsx(object source, int period, int phase = 0, double factor = 0.55) : this(period, phase, factor)
|
public Rsx(object source, int period, int phase = 0, double factor = 0.55) : this(period, phase, factor)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
@@ -59,15 +89,18 @@ public class Rsx : AbstractBase
|
|||||||
_prevValue = Input.Value;
|
_prevValue = Input.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calculate RSI components
|
||||||
double change = Input.Value - _prevValue;
|
double change = Input.Value - _prevValue;
|
||||||
double gain = Math.Max(change, 0);
|
double gain = Math.Max(change, 0);
|
||||||
double loss = Math.Max(-change, 0);
|
double loss = Math.Max(-change, 0);
|
||||||
_prevValue = Input.Value;
|
_prevValue = Input.Value;
|
||||||
|
|
||||||
|
// Calculate RSI
|
||||||
_avgGain.Calc(gain, IsNew: Input.IsNew);
|
_avgGain.Calc(gain, IsNew: Input.IsNew);
|
||||||
_avgLoss.Calc(loss, IsNew: Input.IsNew);
|
_avgLoss.Calc(loss, IsNew: Input.IsNew);
|
||||||
|
|
||||||
double rsi = (_avgLoss.Value > 0) ? 100 - (100 / (1 + (_avgGain.Value / _avgLoss.Value))) : 100;
|
double rsi = (_avgLoss.Value > 0) ? 100 - (100 / (1 + (_avgGain.Value / _avgLoss.Value))) : 100;
|
||||||
|
|
||||||
|
// Apply JMA smoothing
|
||||||
double rsx = _rsx.Calc(rsi, Input.IsNew);
|
double rsx = _rsx.Calc(rsi, Input.IsNew);
|
||||||
|
|
||||||
return rsx;
|
return rsx;
|
||||||
|
|||||||
+36
-35
@@ -1,15 +1,41 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
namespace QuanTAlib;
|
namespace QuanTAlib;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Calculates the rate of change of the slope over a specified period.
|
/// Curvature: Second Derivative Rate of Change
|
||||||
/// Provides insights into trend acceleration or deceleration.
|
/// 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>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Curvature is a second-order derivative that measures how quickly the slope (first-order derivative) is changing.
|
/// The Curvature calculation process:
|
||||||
/// Positive curvature indicates accelerating uptrends or decelerating downtrends.
|
/// 1. Calculates slope values over the specified period
|
||||||
/// Negative curvature indicates decelerating uptrends or accelerating downtrends.
|
/// 2. Applies least squares regression to slope values
|
||||||
/// This indicator can be useful for identifying potential trend reversals or confirming trend strength.
|
/// 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>
|
/// </remarks>
|
||||||
|
|
||||||
public class Curvature : AbstractBase
|
public class Curvature : AbstractBase
|
||||||
{
|
{
|
||||||
private readonly int _period;
|
private readonly int _period;
|
||||||
@@ -36,13 +62,8 @@ public class Curvature : AbstractBase
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public double? Line { get; private set; }
|
public double? Line { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <param name="period">The number of points to consider for calculation.</param>
|
||||||
/// Initializes a new instance of the Curvature class.
|
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is 2 or less.</exception>
|
||||||
/// </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>
|
|
||||||
public Curvature(int period)
|
public Curvature(int period)
|
||||||
{
|
{
|
||||||
if (period <= 2)
|
if (period <= 2)
|
||||||
@@ -59,20 +80,14 @@ public class Curvature : AbstractBase
|
|||||||
Init();
|
Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <param name="source">The data source object that publishes updates.</param>
|
||||||
/// Initializes a new instance of the Curvature class with a data source.
|
/// <param name="period">The number of points to consider for calculation.</param>
|
||||||
/// </summary>
|
|
||||||
/// <param name="source">The source object that publishes data.</param>
|
|
||||||
/// <param name="period">The number of data points to consider.</param>
|
|
||||||
public Curvature(object source, int period) : this(period)
|
public Curvature(object source, int period) : this(period)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Resets the Curvature indicator to its initial state.
|
|
||||||
/// </summary>
|
|
||||||
public override void Init()
|
public override void Init()
|
||||||
{
|
{
|
||||||
base.Init();
|
base.Init();
|
||||||
@@ -83,10 +98,6 @@ public class Curvature : AbstractBase
|
|||||||
Line = null;
|
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)
|
protected override void ManageState(bool isNew)
|
||||||
{
|
{
|
||||||
if (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()
|
protected override double Calculation()
|
||||||
{
|
{
|
||||||
ManageState(Input.IsNew);
|
ManageState(Input.IsNew);
|
||||||
|
|||||||
+46
-47
@@ -1,33 +1,53 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
namespace QuanTAlib;
|
namespace QuanTAlib;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Measures the unpredictability of data using Shannon's Entropy.
|
/// Entropy: Information Content Measure
|
||||||
/// Provides insights into the randomness or information content of the time series.
|
/// 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>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Shannon's Entropy quantifies the average amount of information contained in a message.
|
/// The Entropy calculation process:
|
||||||
/// In the context of time series analysis, it can be used to:
|
/// 1. Groups values to calculate probabilities
|
||||||
/// - Detect regime changes or structural breaks in the data.
|
/// 2. Applies Shannon's entropy formula
|
||||||
/// - Assess the complexity or predictability of price movements.
|
/// 3. Normalizes result to 0-1 range
|
||||||
/// - Identify periods of high uncertainty or information flow in the market.
|
/// 4. Adjusts for number of unique values
|
||||||
/// The entropy value is normalized between 0 and 1, where 1 indicates maximum randomness
|
///
|
||||||
/// and 0 indicates perfect predictability.
|
/// 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>
|
/// </remarks>
|
||||||
|
|
||||||
public class Entropy : AbstractBase
|
public class Entropy : AbstractBase
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// The number of data points to consider for the entropy calculation.
|
|
||||||
/// </summary>
|
|
||||||
private readonly int Period;
|
private readonly int Period;
|
||||||
private readonly CircularBuffer _buffer;
|
private readonly CircularBuffer _buffer;
|
||||||
|
|
||||||
/// <summary>
|
/// <param name="period">The number of points to consider for entropy calculation.</param>
|
||||||
/// Initializes a new instance of the Entropy class.
|
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 2.</exception>
|
||||||
/// </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>
|
|
||||||
public Entropy(int period)
|
public Entropy(int period)
|
||||||
{
|
{
|
||||||
if (period < 2)
|
if (period < 2)
|
||||||
@@ -42,30 +62,20 @@ public class Entropy : AbstractBase
|
|||||||
Init();
|
Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <param name="source">The data source object that publishes updates.</param>
|
||||||
/// Initializes a new instance of the Entropy class with a data source.
|
/// <param name="period">The number of points to consider for entropy calculation.</param>
|
||||||
/// </summary>
|
|
||||||
/// <param name="source">The source object that publishes data.</param>
|
|
||||||
/// <param name="period">The number of data points to consider.</param>
|
|
||||||
public Entropy(object source, int period) : this(period)
|
public Entropy(object source, int period) : this(period)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Resets the Entropy indicator to its initial state.
|
|
||||||
/// </summary>
|
|
||||||
public override void Init()
|
public override void Init()
|
||||||
{
|
{
|
||||||
base.Init();
|
base.Init();
|
||||||
_buffer.Clear();
|
_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)
|
protected override void ManageState(bool isNew)
|
||||||
{
|
{
|
||||||
if (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()
|
protected override double Calculation()
|
||||||
{
|
{
|
||||||
ManageState(Input.IsNew);
|
ManageState(Input.IsNew);
|
||||||
@@ -93,22 +92,22 @@ public class Entropy : AbstractBase
|
|||||||
_buffer.Add(Input.Value, Input.IsNew);
|
_buffer.Add(Input.Value, Input.IsNew);
|
||||||
|
|
||||||
double entropy = 0;
|
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();
|
var values = _buffer.GetSpan().ToArray();
|
||||||
int n = values.Length;
|
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() });
|
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)
|
foreach (var group in groupedValues)
|
||||||
{
|
{
|
||||||
double probability = (double)group.Count / n;
|
double probability = (double)group.Count / n;
|
||||||
entropy -= probability * Math.Log2(probability);
|
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();
|
int uniqueValueCount = groupedValues.Count();
|
||||||
double maxEntropy = Math.Log2(uniqueValueCount);
|
double maxEntropy = Math.Log2(uniqueValueCount);
|
||||||
|
|
||||||
@@ -116,7 +115,7 @@ public class Entropy : AbstractBase
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
entropy = 1; // Default to maximum entropy when insufficient data
|
entropy = 1; // Maximum entropy when insufficient data
|
||||||
}
|
}
|
||||||
|
|
||||||
IsHot = _buffer.Count >= Period;
|
IsHot = _buffer.Count >= Period;
|
||||||
|
|||||||
+45
-54
@@ -1,38 +1,54 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
namespace QuanTAlib;
|
namespace QuanTAlib;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Calculates excess kurtosis using the Sheskin Algorithm.
|
/// Kurtosis: Distribution Tail Weight Measure
|
||||||
/// Measures the "tailedness" of the probability distribution of a real-valued random variable.
|
/// 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>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Kurtosis is a measure of the combined weight of a distribution's tails relative to the center of the distribution.
|
/// The Kurtosis calculation process:
|
||||||
/// In financial time series analysis, kurtosis can provide insights into:
|
/// 1. Calculates mean of the data
|
||||||
/// - The frequency and magnitude of extreme returns.
|
/// 2. Computes squared and fourth power deviations
|
||||||
/// - The potential for outliers or "black swan" events.
|
/// 3. Applies Sheskin Algorithm for excess kurtosis
|
||||||
/// - The shape of the return distribution compared to a normal distribution.
|
/// 4. Adjusts for sample size bias
|
||||||
///
|
///
|
||||||
/// Interpretation:
|
/// Key characteristics:
|
||||||
/// - Excess kurtosis > 0: Heavy-tailed distribution (more extreme values than a normal distribution)
|
/// - Measures tail weight relative to normal distribution
|
||||||
/// - Excess kurtosis = 0: Normal distribution
|
/// - Positive values indicate heavy tails
|
||||||
/// - Excess kurtosis < 0: Light-tailed distribution (fewer extreme values than a normal distribution)
|
/// - 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>
|
/// </remarks>
|
||||||
|
|
||||||
public class Kurtosis : AbstractBase
|
public class Kurtosis : AbstractBase
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// The number of data points to consider for the kurtosis calculation.
|
|
||||||
/// </summary>
|
|
||||||
private readonly int Period;
|
private readonly int Period;
|
||||||
private readonly CircularBuffer _buffer;
|
private readonly CircularBuffer _buffer;
|
||||||
|
|
||||||
/// <summary>
|
/// <param name="period">The number of points to consider for kurtosis calculation.</param>
|
||||||
/// Initializes a new instance of the Kurtosis class.
|
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 4.</exception>
|
||||||
/// </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>
|
|
||||||
public Kurtosis(int period)
|
public Kurtosis(int period)
|
||||||
{
|
{
|
||||||
if (period < 4)
|
if (period < 4)
|
||||||
@@ -47,30 +63,20 @@ public class Kurtosis : AbstractBase
|
|||||||
Init();
|
Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <param name="source">The data source object that publishes updates.</param>
|
||||||
/// Initializes a new instance of the Kurtosis class with a data source.
|
/// <param name="period">The number of points to consider for kurtosis calculation.</param>
|
||||||
/// </summary>
|
|
||||||
/// <param name="source">The source object that publishes data.</param>
|
|
||||||
/// <param name="period">The number of data points to consider.</param>
|
|
||||||
public Kurtosis(object source, int period) : this(period)
|
public Kurtosis(object source, int period) : this(period)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Resets the Kurtosis indicator to its initial state.
|
|
||||||
/// </summary>
|
|
||||||
public override void Init()
|
public override void Init()
|
||||||
{
|
{
|
||||||
base.Init();
|
base.Init();
|
||||||
_buffer.Clear();
|
_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)
|
protected override void ManageState(bool isNew)
|
||||||
{
|
{
|
||||||
if (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()
|
protected override double Calculation()
|
||||||
{
|
{
|
||||||
ManageState(Input.IsNew);
|
ManageState(Input.IsNew);
|
||||||
@@ -103,14 +93,15 @@ public class Kurtosis : AbstractBase
|
|||||||
_buffer.Add(Input.Value, Input.IsNew);
|
_buffer.Add(Input.Value, Input.IsNew);
|
||||||
|
|
||||||
double kurtosis = 0;
|
double kurtosis = 0;
|
||||||
if (_buffer.Count > 3)
|
if (_buffer.Count > 3) // Need at least 4 points for valid calculation
|
||||||
{
|
{
|
||||||
var values = _buffer.GetSpan().ToArray();
|
var values = _buffer.GetSpan().ToArray();
|
||||||
double mean = values.Average();
|
double mean = values.Average();
|
||||||
double n = values.Length;
|
double n = values.Length;
|
||||||
|
|
||||||
double s2 = 0;
|
// Calculate squared and fourth power deviations
|
||||||
double s4 = 0;
|
double s2 = 0; // Sum of squared deviations
|
||||||
|
double s4 = 0; // Sum of fourth power deviations
|
||||||
|
|
||||||
for (int i = 0; i < values.Length; i++)
|
for (int i = 0; i < values.Length; i++)
|
||||||
{
|
{
|
||||||
@@ -121,7 +112,7 @@ public class Kurtosis : AbstractBase
|
|||||||
|
|
||||||
double variance = s2 / (n - 1);
|
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))
|
kurtosis = (n * (n + 1) * s4) / (variance * variance * (n - 3) * (n - 1) * (n - 2))
|
||||||
- (3 * (n - 1) * (n - 1) / ((n - 2) * (n - 3)));
|
- (3 * (n - 1) * (n - 1) / ((n - 2) * (n - 3)));
|
||||||
}
|
}
|
||||||
|
|||||||
+45
-66
@@ -1,63 +1,58 @@
|
|||||||
|
using System;
|
||||||
namespace QuanTAlib;
|
namespace QuanTAlib;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Calculates the maximum value over a specified period, with an optional decay factor.
|
/// MAX: Maximum Value with Decay
|
||||||
/// Useful for tracking the highest point in a time series with the ability to gradually forget old peaks.
|
/// 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>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// The Max indicator is particularly useful in financial analysis for:
|
/// The MAX calculation process:
|
||||||
/// - Identifying resistance levels in price charts.
|
/// 1. Tracks highest value in current period
|
||||||
/// - Tracking the highest price over a given period.
|
/// 2. Applies exponential decay to old peaks
|
||||||
/// - Implementing trailing stop-loss strategies.
|
/// 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
|
/// Key characteristics:
|
||||||
/// gradually reducing the influence of older maximum values.
|
/// - 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>
|
/// </remarks>
|
||||||
|
|
||||||
public class Max : AbstractBase
|
public class Max : AbstractBase
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// The number of data points to consider for the maximum calculation.
|
|
||||||
/// </summary>
|
|
||||||
private readonly int Period;
|
private readonly int Period;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Circular buffer to store the most recent data points.
|
|
||||||
/// </summary>
|
|
||||||
private readonly CircularBuffer _buffer;
|
private readonly CircularBuffer _buffer;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The half-life decay factor used to gradually forget old peaks.
|
|
||||||
/// </summary>
|
|
||||||
private readonly double _halfLife;
|
private readonly double _halfLife;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The current maximum value.
|
|
||||||
/// </summary>
|
|
||||||
private double _currentMax;
|
private double _currentMax;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The previous maximum value.
|
|
||||||
/// </summary>
|
|
||||||
private double _p_currentMax;
|
private double _p_currentMax;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The number of periods since a new maximum was set.
|
|
||||||
/// </summary>
|
|
||||||
private int _timeSinceNewMax;
|
private int _timeSinceNewMax;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The previous value of _timeSinceNewMax.
|
|
||||||
/// </summary>
|
|
||||||
private int _p_timeSinceNewMax;
|
private int _p_timeSinceNewMax;
|
||||||
|
|
||||||
/// <summary>
|
/// <param name="period">The number of points to consider for maximum calculation.</param>
|
||||||
/// Initializes a new instance of the Max class.
|
/// <param name="decay">Half-life decay factor (0 for no decay, higher for faster forgetting).</param>
|
||||||
/// </summary>
|
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1 or decay is negative.</exception>
|
||||||
/// <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>
|
|
||||||
public Max(int period, double decay = 0)
|
public Max(int period, double decay = 0)
|
||||||
{
|
{
|
||||||
if (period < 1)
|
if (period < 1)
|
||||||
@@ -78,21 +73,15 @@ public class Max : AbstractBase
|
|||||||
Init();
|
Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <param name="source">The data source object that publishes updates.</param>
|
||||||
/// Initializes a new instance of the Max class with a data source.
|
/// <param name="period">The number of points to consider for maximum calculation.</param>
|
||||||
/// </summary>
|
/// <param name="decay">Half-life decay factor (default 0).</param>
|
||||||
/// <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>
|
|
||||||
public Max(object source, int period, double decay = 0) : this(period, decay)
|
public Max(object source, int period, double decay = 0) : this(period, decay)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Resets the Max indicator to its initial state.
|
|
||||||
/// </summary>
|
|
||||||
public override void Init()
|
public override void Init()
|
||||||
{
|
{
|
||||||
base.Init();
|
base.Init();
|
||||||
@@ -100,10 +89,6 @@ public class Max : AbstractBase
|
|||||||
_timeSinceNewMax = 0;
|
_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)
|
protected override void ManageState(bool isNew)
|
||||||
{
|
{
|
||||||
if (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()
|
protected override double Calculation()
|
||||||
{
|
{
|
||||||
ManageState(Input.IsNew);
|
ManageState(Input.IsNew);
|
||||||
_buffer.Add(Input.Value, Input.IsNew);
|
_buffer.Add(Input.Value, Input.IsNew);
|
||||||
|
|
||||||
|
// Update maximum if new value is higher
|
||||||
if (Input.Value >= _currentMax)
|
if (Input.Value >= _currentMax)
|
||||||
{
|
{
|
||||||
_currentMax = Input.Value;
|
_currentMax = Input.Value;
|
||||||
_timeSinceNewMax = 0;
|
_timeSinceNewMax = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply decay based on time since last maximum
|
||||||
double decayRate = 1 - Math.Exp(-_halfLife * _timeSinceNewMax / Period);
|
double decayRate = 1 - Math.Exp(-_halfLife * _timeSinceNewMax / Period);
|
||||||
_currentMax -= decayRate * (_currentMax - _buffer.Average());
|
_currentMax -= decayRate * (_currentMax - _buffer.Average());
|
||||||
|
|
||||||
|
// Ensure maximum doesn't exceed current period's highest value
|
||||||
_currentMax = Math.Min(_currentMax, _buffer.Max());
|
_currentMax = Math.Min(_currentMax, _buffer.Max());
|
||||||
|
|
||||||
IsHot = true;
|
IsHot = true;
|
||||||
|
|||||||
+44
-41
@@ -1,33 +1,52 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
namespace QuanTAlib;
|
namespace QuanTAlib;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Calculates the median value over a specified period.
|
/// Median: Central Tendency Measure
|
||||||
/// Provides a measure of central tendency that is robust to outliers.
|
/// 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>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// The Median indicator is particularly useful in financial analysis for:
|
/// The Median calculation process:
|
||||||
/// - Providing a robust measure of central tendency that is less affected by extreme values than the mean.
|
/// 1. Collects values over specified period
|
||||||
/// - Identifying the middle value in a dataset, which can be helpful in understanding price distributions.
|
/// 2. Sorts values in ascending order
|
||||||
/// - Serving as a basis for other indicators or trading strategies that require a stable reference point.
|
/// 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
|
/// Key characteristics:
|
||||||
/// in markets with occasional large price swings or in the presence of data anomalies.
|
/// - 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>
|
/// </remarks>
|
||||||
|
|
||||||
public class Median : AbstractBase
|
public class Median : AbstractBase
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// The number of data points to consider for the median calculation.
|
|
||||||
/// </summary>
|
|
||||||
private readonly int Period;
|
private readonly int Period;
|
||||||
private readonly CircularBuffer _buffer;
|
private readonly CircularBuffer _buffer;
|
||||||
|
|
||||||
/// <summary>
|
/// <param name="period">The number of points to consider for median calculation.</param>
|
||||||
/// Initializes a new instance of the Median class.
|
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
|
||||||
/// </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>
|
|
||||||
public Median(int period)
|
public Median(int period)
|
||||||
{
|
{
|
||||||
if (period < 1)
|
if (period < 1)
|
||||||
@@ -42,30 +61,20 @@ public class Median : AbstractBase
|
|||||||
Init();
|
Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <param name="source">The data source object that publishes updates.</param>
|
||||||
/// Initializes a new instance of the Median class with a data source.
|
/// <param name="period">The number of points to consider for median calculation.</param>
|
||||||
/// </summary>
|
|
||||||
/// <param name="source">The source object that publishes data.</param>
|
|
||||||
/// <param name="period">The number of data points to consider.</param>
|
|
||||||
public Median(object source, int period) : this(period)
|
public Median(object source, int period) : this(period)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Resets the Median indicator to its initial state.
|
|
||||||
/// </summary>
|
|
||||||
public override void Init()
|
public override void Init()
|
||||||
{
|
{
|
||||||
base.Init();
|
base.Init();
|
||||||
_buffer.Clear();
|
_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)
|
protected override void ManageState(bool isNew)
|
||||||
{
|
{
|
||||||
if (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()
|
protected override double Calculation()
|
||||||
{
|
{
|
||||||
ManageState(Input.IsNew);
|
ManageState(Input.IsNew);
|
||||||
@@ -93,11 +92,15 @@ public class Median : AbstractBase
|
|||||||
double median;
|
double median;
|
||||||
if (_index >= Period)
|
if (_index >= Period)
|
||||||
{
|
{
|
||||||
|
// Get sorted copy of values
|
||||||
var sortedValues = _buffer.GetSpan().ToArray();
|
var sortedValues = _buffer.GetSpan().ToArray();
|
||||||
Array.Sort(sortedValues);
|
Array.Sort(sortedValues);
|
||||||
int middleIndex = sortedValues.Length / 2;
|
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
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
+45
-66
@@ -1,63 +1,58 @@
|
|||||||
|
using System;
|
||||||
namespace QuanTAlib;
|
namespace QuanTAlib;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a minimum value calculator with optional decay over a specified period.
|
/// MIN: Minimum Value with Decay
|
||||||
/// This class calculates the minimum value within a given period, with the ability to
|
/// A statistical measure that tracks the lowest value over a specified period,
|
||||||
/// apply a decay factor to give more weight to recent values.
|
/// 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>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// The Min class uses a circular buffer to store values and calculates the minimum
|
/// The MIN calculation process:
|
||||||
/// efficiently. It also implements a decay mechanism to adjust the minimum value over
|
/// 1. Tracks lowest value in current period
|
||||||
/// time, allowing for a more responsive indicator in changing market conditions.
|
/// 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,
|
/// Key characteristics:
|
||||||
/// which can be useful in adapting to new price trends or market regimes.
|
/// - 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>
|
/// </remarks>
|
||||||
|
|
||||||
public class Min : AbstractBase
|
public class Min : AbstractBase
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// The number of data points to consider for the minimum calculation.
|
|
||||||
/// </summary>
|
|
||||||
private readonly int Period;
|
private readonly int Period;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Circular buffer to store the most recent data points.
|
|
||||||
/// </summary>
|
|
||||||
private readonly CircularBuffer _buffer;
|
private readonly CircularBuffer _buffer;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The half-life decay factor used to gradually forget old minimums.
|
|
||||||
/// </summary>
|
|
||||||
private readonly double _halfLife;
|
private readonly double _halfLife;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The current minimum value.
|
|
||||||
/// </summary>
|
|
||||||
private double _currentMin;
|
private double _currentMin;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The previous minimum value.
|
|
||||||
/// </summary>
|
|
||||||
private double _p_currentMin;
|
private double _p_currentMin;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The number of periods since a new minimum was set.
|
|
||||||
/// </summary>
|
|
||||||
private int _timeSinceNewMin;
|
private int _timeSinceNewMin;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The previous value of _timeSinceNewMin.
|
|
||||||
/// </summary>
|
|
||||||
private int _p_timeSinceNewMin;
|
private int _p_timeSinceNewMin;
|
||||||
|
|
||||||
/// <summary>
|
/// <param name="period">The number of points to consider for minimum calculation.</param>
|
||||||
/// Initializes a new instance of the Min class with the specified period and decay.
|
/// <param name="decay">Half-life decay factor (0 for no decay, higher for faster forgetting).</param>
|
||||||
/// </summary>
|
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1 or decay is negative.</exception>
|
||||||
/// <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>
|
|
||||||
public Min(int period, double decay = 0)
|
public Min(int period, double decay = 0)
|
||||||
{
|
{
|
||||||
if (period < 1)
|
if (period < 1)
|
||||||
@@ -76,21 +71,15 @@ public class Min : AbstractBase
|
|||||||
Init();
|
Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <param name="source">The data source object that publishes updates.</param>
|
||||||
/// Initializes a new instance of the Min class with the specified source, period, and decay.
|
/// <param name="period">The number of points to consider for minimum calculation.</param>
|
||||||
/// </summary>
|
/// <param name="decay">Half-life decay factor (default 0).</param>
|
||||||
/// <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>
|
|
||||||
public Min(object source, int period, double decay = 0) : this(period, decay)
|
public Min(object source, int period, double decay = 0) : this(period, decay)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes the Min instance by setting initial values.
|
|
||||||
/// </summary>
|
|
||||||
public override void Init()
|
public override void Init()
|
||||||
{
|
{
|
||||||
base.Init();
|
base.Init();
|
||||||
@@ -98,10 +87,6 @@ public class Min : AbstractBase
|
|||||||
_timeSinceNewMin = 0;
|
_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)
|
protected override void ManageState(bool isNew)
|
||||||
{
|
{
|
||||||
if (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()
|
protected override double Calculation()
|
||||||
{
|
{
|
||||||
ManageState(Input.IsNew);
|
ManageState(Input.IsNew);
|
||||||
_buffer.Add(Input.Value, Input.IsNew);
|
_buffer.Add(Input.Value, Input.IsNew);
|
||||||
|
|
||||||
|
// Update minimum if new value is lower
|
||||||
if (Input.Value <= _currentMin)
|
if (Input.Value <= _currentMin)
|
||||||
{
|
{
|
||||||
_currentMin = Input.Value;
|
_currentMin = Input.Value;
|
||||||
_timeSinceNewMin = 0;
|
_timeSinceNewMin = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply decay based on time since last minimum
|
||||||
double decayRate = 1 - Math.Exp(-_halfLife * _timeSinceNewMin / Period);
|
double decayRate = 1 - Math.Exp(-_halfLife * _timeSinceNewMin / Period);
|
||||||
_currentMin += decayRate * (_buffer.Average() - _currentMin);
|
_currentMin += decayRate * (_buffer.Average() - _currentMin);
|
||||||
|
|
||||||
|
// Ensure minimum doesn't fall below current period's lowest value
|
||||||
_currentMin = Math.Max(_currentMin, _buffer.Min());
|
_currentMin = Math.Max(_currentMin, _buffer.Min());
|
||||||
|
|
||||||
IsHot = true;
|
IsHot = true;
|
||||||
|
|||||||
+50
-50
@@ -1,34 +1,52 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
namespace QuanTAlib;
|
namespace QuanTAlib;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a mode calculator that determines the most frequent value in a specified period.
|
/// MODE: Most Frequent Value Measure
|
||||||
/// If multiple values have the same highest frequency, it returns their average.
|
/// 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>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// The Mode class uses a circular buffer to store values and calculates the mode
|
/// The Mode calculation process:
|
||||||
/// efficiently. Before the specified period is reached, it returns the average of
|
/// 1. Groups values by frequency
|
||||||
/// the available values as an approximation.
|
/// 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:
|
/// Key characteristics:
|
||||||
/// - Identifying the most common price levels, which could indicate support or resistance.
|
/// - Identifies most common values
|
||||||
/// - Analyzing the distribution of returns or other financial metrics.
|
/// - Handles multiple modes
|
||||||
/// - Detecting patterns in trading volume or other discrete financial data.
|
/// - 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>
|
/// </remarks>
|
||||||
|
|
||||||
public class Mode : AbstractBase
|
public class Mode : AbstractBase
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// The number of data points to consider for the mode calculation.
|
|
||||||
/// </summary>
|
|
||||||
private readonly int Period;
|
private readonly int Period;
|
||||||
private readonly CircularBuffer _buffer;
|
private readonly CircularBuffer _buffer;
|
||||||
|
|
||||||
/// <summary>
|
/// <param name="period">The number of points to consider for mode calculation.</param>
|
||||||
/// Initializes a new instance of the Mode class with the specified period.
|
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
|
||||||
/// </summary>
|
|
||||||
/// <param name="period">The period over which to calculate the mode.</param>
|
|
||||||
/// <exception cref="ArgumentOutOfRangeException">
|
|
||||||
/// Thrown when period is less than 1.
|
|
||||||
/// </exception>
|
|
||||||
public Mode(int period)
|
public Mode(int period)
|
||||||
{
|
{
|
||||||
if (period < 1)
|
if (period < 1)
|
||||||
@@ -42,30 +60,20 @@ public class Mode : AbstractBase
|
|||||||
Init();
|
Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <param name="source">The data source object that publishes updates.</param>
|
||||||
/// Initializes a new instance of the Mode class with the specified source and period.
|
/// <param name="period">The number of points to consider for mode calculation.</param>
|
||||||
/// </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>
|
|
||||||
public Mode(object source, int period) : this(period)
|
public Mode(object source, int period) : this(period)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Resets the Mode indicator to its initial state.
|
|
||||||
/// </summary>
|
|
||||||
public override void Init()
|
public override void Init()
|
||||||
{
|
{
|
||||||
base.Init();
|
base.Init();
|
||||||
_buffer.Clear();
|
_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)
|
protected override void ManageState(bool isNew)
|
||||||
{
|
{
|
||||||
if (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()
|
protected override double Calculation()
|
||||||
{
|
{
|
||||||
ManageState(Input.IsNew);
|
ManageState(Input.IsNew);
|
||||||
@@ -95,22 +91,26 @@ public class Mode : AbstractBase
|
|||||||
double mode;
|
double mode;
|
||||||
if (_index >= Period)
|
if (_index >= Period)
|
||||||
{
|
{
|
||||||
|
// Group values by frequency and order by count
|
||||||
var values = _buffer.GetSpan().ToArray();
|
var values = _buffer.GetSpan().ToArray();
|
||||||
var groupedValues = values.GroupBy(v => v)
|
var groupedValues = values.GroupBy(v => v)
|
||||||
.OrderByDescending(g => g.Count())
|
.OrderByDescending(g => g.Count())
|
||||||
.ThenBy(g => g.Key)
|
.ThenBy(g => g.Key)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
|
// Find all values with highest frequency
|
||||||
int maxCount = groupedValues.First().Count();
|
int maxCount = groupedValues.First().Count();
|
||||||
var modes = groupedValues.TakeWhile(g => g.Count() == maxCount)
|
var modes = groupedValues.TakeWhile(g => g.Count() == maxCount)
|
||||||
.Select(g => g.Key)
|
.Select(g => g.Key)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
mode = modes.Average(); // If there are multiple modes, we return their average
|
// Average multiple modes if present
|
||||||
|
mode = modes.Average();
|
||||||
}
|
}
|
||||||
else
|
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;
|
IsHot = _index >= WarmupPeriod;
|
||||||
|
|||||||
@@ -1,40 +1,54 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
namespace QuanTAlib;
|
namespace QuanTAlib;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a percentile calculator that determines the value at a specified percentile
|
/// Percentile: Distribution Position Measure
|
||||||
/// in a given period of data points.
|
/// 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>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// The Percentile class uses a circular buffer to store values and calculates the
|
/// The Percentile calculation process:
|
||||||
/// percentile efficiently. It uses linear interpolation when the percentile falls
|
/// 1. Sorts values in ascending order
|
||||||
/// between two data points. Before the specified period is reached, it returns the
|
/// 2. Calculates position based on percentile
|
||||||
/// average of the available values as an approximation.
|
/// 3. Interpolates between adjacent values
|
||||||
|
/// 4. Uses mean until period filled
|
||||||
///
|
///
|
||||||
/// In financial analysis, percentiles are useful for:
|
/// Key characteristics:
|
||||||
/// - Assessing the relative standing of a value within a distribution.
|
/// - Range specific value identification
|
||||||
/// - Identifying outliers or extreme values in financial data.
|
/// - Linear interpolation for precision
|
||||||
/// - Creating risk measures, such as Value at Risk (VaR) calculations.
|
/// - Distribution independent
|
||||||
/// - Analyzing the distribution of returns, trading volumes, or other financial metrics.
|
/// - 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>
|
/// </remarks>
|
||||||
|
|
||||||
public class Percentile : AbstractBase
|
public class Percentile : AbstractBase
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// The number of data points to consider for the percentile calculation.
|
|
||||||
/// </summary>
|
|
||||||
private readonly int Period;
|
private readonly int Period;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The percentile to calculate (between 0 and 100).
|
|
||||||
/// </summary>
|
|
||||||
private readonly double Percent;
|
private readonly double Percent;
|
||||||
|
|
||||||
private readonly CircularBuffer _buffer;
|
private readonly CircularBuffer _buffer;
|
||||||
|
|
||||||
/// <summary>
|
/// <param name="period">The number of points to consider for percentile calculation.</param>
|
||||||
/// Initializes a new instance of the Percentile class with the specified period and percentile.
|
/// <param name="percent">The percentile to calculate (0-100).</param>
|
||||||
/// </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>
|
|
||||||
/// <exception cref="ArgumentOutOfRangeException">
|
/// <exception cref="ArgumentOutOfRangeException">
|
||||||
/// Thrown when period is less than 2 or percent is not between 0 and 100.
|
/// Thrown when period is less than 2 or percent is not between 0 and 100.
|
||||||
/// </exception>
|
/// </exception>
|
||||||
@@ -42,11 +56,13 @@ public class Percentile : AbstractBase
|
|||||||
{
|
{
|
||||||
if (period < 2)
|
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)
|
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;
|
Period = period;
|
||||||
Percent = percent;
|
Percent = percent;
|
||||||
@@ -56,31 +72,21 @@ public class Percentile : AbstractBase
|
|||||||
Init();
|
Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <param name="source">The data source object that publishes updates.</param>
|
||||||
/// Initializes a new instance of the Percentile class with the specified source, period, and percentile.
|
/// <param name="period">The number of points to consider for percentile calculation.</param>
|
||||||
/// </summary>
|
/// <param name="percent">The percentile to calculate (0-100).</param>
|
||||||
/// <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>
|
|
||||||
public Percentile(object source, int period, double percent) : this(period, percent)
|
public Percentile(object source, int period, double percent) : this(period, percent)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes the Percentile instance by clearing the buffer.
|
|
||||||
/// </summary>
|
|
||||||
public override void Init()
|
public override void Init()
|
||||||
{
|
{
|
||||||
base.Init();
|
base.Init();
|
||||||
_buffer.Clear();
|
_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)
|
protected override void ManageState(bool isNew)
|
||||||
{
|
{
|
||||||
if (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()
|
protected override double Calculation()
|
||||||
{
|
{
|
||||||
ManageState(Input.IsNew);
|
ManageState(Input.IsNew);
|
||||||
@@ -110,6 +104,7 @@ public class Percentile : AbstractBase
|
|||||||
double result;
|
double result;
|
||||||
if (_buffer.Count >= Period)
|
if (_buffer.Count >= Period)
|
||||||
{
|
{
|
||||||
|
// Sort values and calculate percentile position
|
||||||
var values = _buffer.GetSpan().ToArray();
|
var values = _buffer.GetSpan().ToArray();
|
||||||
Array.Sort(values);
|
Array.Sort(values);
|
||||||
|
|
||||||
@@ -123,7 +118,7 @@ public class Percentile : AbstractBase
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Interpolate between the two nearest values
|
// Linear interpolation between adjacent values
|
||||||
double lowerValue = values[lowerIndex];
|
double lowerValue = values[lowerIndex];
|
||||||
double upperValue = values[upperIndex];
|
double upperValue = values[upperIndex];
|
||||||
double fraction = position - lowerIndex;
|
double fraction = position - lowerIndex;
|
||||||
@@ -132,7 +127,7 @@ public class Percentile : AbstractBase
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Use average for insufficient data, like the Median class
|
// Use average until we have enough data points
|
||||||
result = _buffer.Average();
|
result = _buffer.Average();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+50
-59
@@ -1,44 +1,62 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
namespace QuanTAlib;
|
namespace QuanTAlib;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a skewness calculator that measures the asymmetry of the probability
|
/// SKEW: Distribution Asymmetry Measure
|
||||||
/// distribution of a real-valued random variable about its mean.
|
/// 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>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// The Skew class uses a circular buffer to store values and calculates the skewness
|
/// The Skew calculation process:
|
||||||
/// efficiently. It uses the adjusted Fisher-Pearson standardized moment coefficient
|
/// 1. Calculates mean of the data
|
||||||
/// for sample skewness calculation. A minimum of 3 data points is required for the
|
/// 2. Computes deviations from mean
|
||||||
/// calculation.
|
/// 3. Calculates third moment (cubed deviations)
|
||||||
|
/// 4. Normalizes by standard deviation cubed
|
||||||
///
|
///
|
||||||
/// In financial analysis, skewness is important for:
|
/// Key characteristics:
|
||||||
/// - Assessing the asymmetry of returns distribution.
|
/// - Measures distribution asymmetry
|
||||||
/// - Evaluating the risk of extreme events in either direction.
|
/// - Positive values indicate right skew
|
||||||
/// - Complementing other risk measures like standard deviation.
|
/// - Negative values indicate left skew
|
||||||
/// - Informing investment decisions and risk management strategies.
|
/// - Zero indicates symmetry
|
||||||
|
/// - Scale-independent measure
|
||||||
///
|
///
|
||||||
/// Positive skewness indicates a longer tail on the right side of the distribution,
|
/// Formula:
|
||||||
/// while negative skewness indicates a longer tail on the left side.
|
/// 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>
|
/// </remarks>
|
||||||
|
|
||||||
public class Skew : AbstractBase
|
public class Skew : AbstractBase
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// The number of data points to consider for the skewness calculation.
|
|
||||||
/// </summary>
|
|
||||||
private readonly int Period;
|
private readonly int Period;
|
||||||
private readonly CircularBuffer _buffer;
|
private readonly CircularBuffer _buffer;
|
||||||
|
|
||||||
/// <summary>
|
/// <param name="period">The number of points to consider for skewness calculation.</param>
|
||||||
/// Initializes a new instance of the Skew class with the specified period.
|
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 3.</exception>
|
||||||
/// </summary>
|
|
||||||
/// <param name="period">The period over which to calculate the skewness.</param>
|
|
||||||
/// <exception cref="ArgumentOutOfRangeException">
|
|
||||||
/// Thrown when period is less than 3.
|
|
||||||
/// </exception>
|
|
||||||
public Skew(int period)
|
public Skew(int period)
|
||||||
{
|
{
|
||||||
if (period < 3)
|
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;
|
Period = period;
|
||||||
WarmupPeriod = 3;
|
WarmupPeriod = 3;
|
||||||
@@ -47,30 +65,20 @@ public class Skew : AbstractBase
|
|||||||
Init();
|
Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <param name="source">The data source object that publishes updates.</param>
|
||||||
/// Initializes a new instance of the Skew class with the specified source and period.
|
/// <param name="period">The number of points to consider for skewness calculation.</param>
|
||||||
/// </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>
|
|
||||||
public Skew(object source, int period) : this(period)
|
public Skew(object source, int period) : this(period)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes the Skew instance by clearing the buffer.
|
|
||||||
/// </summary>
|
|
||||||
public override void Init()
|
public override void Init()
|
||||||
{
|
{
|
||||||
base.Init();
|
base.Init();
|
||||||
_buffer.Clear();
|
_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)
|
protected override void ManageState(bool isNew)
|
||||||
{
|
{
|
||||||
if (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()
|
protected override double Calculation()
|
||||||
{
|
{
|
||||||
ManageState(Input.IsNew);
|
ManageState(Input.IsNew);
|
||||||
|
|
||||||
_buffer.Add(Input.Value, Input.IsNew);
|
_buffer.Add(Input.Value, Input.IsNew);
|
||||||
|
|
||||||
double skew = 0;
|
double skew = 0;
|
||||||
if (_buffer.Count >= 3)
|
if (_buffer.Count >= 3) // Need at least 3 points for skewness
|
||||||
{ // We need at least 3 data points for skewness
|
{
|
||||||
var values = _buffer.GetSpan().ToArray();
|
var values = _buffer.GetSpan().ToArray();
|
||||||
double mean = values.Average();
|
double mean = values.Average();
|
||||||
double n = values.Length;
|
double n = values.Length;
|
||||||
|
|
||||||
|
// Calculate third and second moments
|
||||||
double sumCubedDeviations = 0;
|
double sumCubedDeviations = 0;
|
||||||
double sumSquaredDeviations = 0;
|
double sumSquaredDeviations = 0;
|
||||||
|
|
||||||
@@ -120,13 +111,13 @@ public class Skew : AbstractBase
|
|||||||
sumSquaredDeviations += Math.Pow(deviation, 2);
|
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 m3 = sumCubedDeviations / n;
|
||||||
double m2 = sumSquaredDeviations / n;
|
double m2 = sumSquaredDeviations / n;
|
||||||
double s3 = Math.Pow(m2, 1.5);
|
double s3 = Math.Pow(m2, 1.5);
|
||||||
|
|
||||||
if (s3 != 0)
|
if (s3 != 0) // Avoid division by zero
|
||||||
{ // Avoid division by zero
|
{
|
||||||
skew = (Math.Sqrt(n * (n - 1)) / (n - 2)) * (m3 / s3);
|
skew = (Math.Sqrt(n * (n - 1)) / (n - 2)) * (m3 / s3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+50
-64
@@ -1,52 +1,68 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
namespace QuanTAlib;
|
namespace QuanTAlib;
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// The Slope class calculates the slope of a linear regression line, along with other
|
/// The Slope calculation process:
|
||||||
/// statistical measures such as intercept, standard deviation, R-squared, and the last
|
/// 1. Calculates means of x and y values
|
||||||
/// point on the regression line. It uses the least squares method for calculation.
|
/// 2. Computes deviations from means
|
||||||
|
/// 3. Applies least squares method
|
||||||
|
/// 4. Provides additional regression statistics
|
||||||
///
|
///
|
||||||
/// In financial analysis, slope is important for:
|
/// Key characteristics:
|
||||||
/// - Identifying trends in price movements or other financial metrics.
|
/// - Measures trend direction and strength
|
||||||
/// - Measuring the rate of change in a financial time series.
|
/// - Provides rate of change
|
||||||
/// - Assessing the strength and direction of relationships between variables.
|
/// - Scale-dependent measure
|
||||||
/// - Supporting technical analysis indicators and trading strategies.
|
/// - 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>
|
/// </remarks>
|
||||||
|
|
||||||
public class Slope : AbstractBase
|
public class Slope : AbstractBase
|
||||||
{
|
{
|
||||||
private readonly int _period;
|
private readonly int _period;
|
||||||
private readonly CircularBuffer _buffer;
|
private readonly CircularBuffer _buffer;
|
||||||
private readonly CircularBuffer _timeBuffer;
|
private readonly CircularBuffer _timeBuffer;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>Gets the y-intercept of the regression line.</summary>
|
||||||
/// Gets the y-intercept of the regression line.
|
|
||||||
/// </summary>
|
|
||||||
public double? Intercept { get; private set; }
|
public double? Intercept { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>Gets the standard deviation of the y-values.</summary>
|
||||||
/// Gets the standard deviation of the y-values.
|
|
||||||
/// </summary>
|
|
||||||
public double? StdDev { get; private set; }
|
public double? StdDev { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>Gets the R-squared value, indicating regression fit quality.</summary>
|
||||||
/// Gets the R-squared value, indicating the goodness of fit of the regression line.
|
|
||||||
/// </summary>
|
|
||||||
public double? RSquared { get; private set; }
|
public double? RSquared { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>Gets the last point on the regression line.</summary>
|
||||||
/// Gets the y-value of the last point on the regression line.
|
|
||||||
/// </summary>
|
|
||||||
public double? Line { get; private set; }
|
public double? Line { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <param name="period">The number of points to consider for slope calculation.</param>
|
||||||
/// Initializes a new instance of the Slope class with the specified period.
|
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than or equal to 1.</exception>
|
||||||
/// </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>
|
|
||||||
public Slope(int period)
|
public Slope(int period)
|
||||||
{
|
{
|
||||||
if (period <= 1)
|
if (period <= 1)
|
||||||
@@ -59,24 +75,17 @@ public class Slope : AbstractBase
|
|||||||
_buffer = new CircularBuffer(period);
|
_buffer = new CircularBuffer(period);
|
||||||
_timeBuffer = new CircularBuffer(period);
|
_timeBuffer = new CircularBuffer(period);
|
||||||
Name = $"Slope(period={period})";
|
Name = $"Slope(period={period})";
|
||||||
|
|
||||||
Init();
|
Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <param name="source">The data source object that publishes updates.</param>
|
||||||
/// Initializes a new instance of the Slope class with the specified source and period.
|
/// <param name="period">The number of points to consider for slope calculation.</param>
|
||||||
/// </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>
|
|
||||||
public Slope(object source, int period) : this(period)
|
public Slope(object source, int period) : this(period)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes the Slope instance by clearing buffers and resetting calculated values.
|
|
||||||
/// </summary>
|
|
||||||
public override void Init()
|
public override void Init()
|
||||||
{
|
{
|
||||||
base.Init();
|
base.Init();
|
||||||
@@ -88,10 +97,6 @@ public class Slope : AbstractBase
|
|||||||
Line = null;
|
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)
|
protected override void ManageState(bool isNew)
|
||||||
{
|
{
|
||||||
if (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()
|
protected override double Calculation()
|
||||||
{
|
{
|
||||||
ManageState(Input.IsNew);
|
ManageState(Input.IsNew);
|
||||||
@@ -128,10 +114,9 @@ public class Slope : AbstractBase
|
|||||||
_timeBuffer.Add(Input.Time.Ticks, Input.IsNew);
|
_timeBuffer.Add(Input.Time.Ticks, Input.IsNew);
|
||||||
|
|
||||||
double slope = 0;
|
double slope = 0;
|
||||||
|
|
||||||
if (_buffer.Count < 2)
|
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);
|
int count = Math.Min(_buffer.Count, _period);
|
||||||
@@ -147,7 +132,7 @@ public class Slope : AbstractBase
|
|||||||
double avgX = sumX / count;
|
double avgX = sumX / count;
|
||||||
double avgY = sumY / count;
|
double avgY = sumY / count;
|
||||||
|
|
||||||
// Least squares method
|
// Least squares regression
|
||||||
double sumSqX = 0, sumSqY = 0, sumSqXY = 0;
|
double sumSqX = 0, sumSqY = 0, sumSqXY = 0;
|
||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
@@ -160,6 +145,7 @@ public class Slope : AbstractBase
|
|||||||
|
|
||||||
if (sumSqX > 0)
|
if (sumSqX > 0)
|
||||||
{
|
{
|
||||||
|
// Calculate slope and related statistics
|
||||||
slope = sumSqXY / sumSqX;
|
slope = sumSqXY / sumSqX;
|
||||||
Intercept = avgY - (slope * avgX);
|
Intercept = avgY - (slope * avgX);
|
||||||
|
|
||||||
@@ -174,7 +160,7 @@ public class Slope : AbstractBase
|
|||||||
RSquared = r * r;
|
RSquared = r * r;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate last Line value (y = mx + b)
|
// Calculate regression line endpoint
|
||||||
Line = (slope * count) + Intercept;
|
Line = (slope * count) + Intercept;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
+50
-64
@@ -1,48 +1,63 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
namespace QuanTAlib;
|
namespace QuanTAlib;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a standard deviation calculator that measures the amount of variation or
|
/// STDDEV: Standard Deviation Volatility Measure
|
||||||
/// dispersion of a set of values.
|
/// 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>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// The Stddev class calculates either the population standard deviation or the sample
|
/// The StdDev calculation process:
|
||||||
/// standard deviation based on the isPopulation parameter. It uses a circular buffer
|
/// 1. Calculates mean of the data
|
||||||
/// to efficiently manage the data points within the specified period.
|
/// 2. Computes squared deviations from mean
|
||||||
|
/// 3. Averages squared deviations
|
||||||
|
/// 4. Takes square root of average
|
||||||
///
|
///
|
||||||
/// In financial analysis, standard deviation is important for:
|
/// Key characteristics:
|
||||||
/// - Measuring volatility of financial instruments or portfolios.
|
/// - Measures data dispersion
|
||||||
/// - Assessing risk in investments.
|
/// - Same units as input data
|
||||||
/// - Calculating Sharpe ratios and other risk-adjusted performance measures.
|
/// - Sensitive to outliers
|
||||||
/// - Identifying potential outliers or unusual market behavior.
|
/// - 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>
|
/// </remarks>
|
||||||
|
|
||||||
public class Stddev : AbstractBase
|
public class Stddev : AbstractBase
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Indicates whether to calculate population (true) or sample (false) standard deviation.
|
|
||||||
/// </summary>
|
|
||||||
private readonly bool IsPopulation;
|
private readonly bool IsPopulation;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Circular buffer to store the most recent data points.
|
|
||||||
/// </summary>
|
|
||||||
private readonly CircularBuffer _buffer;
|
private readonly CircularBuffer _buffer;
|
||||||
|
|
||||||
/// <summary>
|
/// <param name="period">The number of points to consider for standard deviation calculation.</param>
|
||||||
/// Initializes a new instance of the Stddev class with the specified period and
|
/// <param name="isPopulation">True for population stddev, false for sample stddev (default).</param>
|
||||||
/// population flag.
|
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 2.</exception>
|
||||||
/// </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>
|
|
||||||
public Stddev(int period, bool isPopulation = false)
|
public Stddev(int period, bool isPopulation = false)
|
||||||
{
|
{
|
||||||
if (period < 2)
|
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;
|
IsPopulation = isPopulation;
|
||||||
WarmupPeriod = 0;
|
WarmupPeriod = 0;
|
||||||
@@ -51,34 +66,21 @@ public class Stddev : AbstractBase
|
|||||||
Init();
|
Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <param name="source">The data source object that publishes updates.</param>
|
||||||
/// Initializes a new instance of the Stddev class with the specified source, period,
|
/// <param name="period">The number of points to consider for standard deviation calculation.</param>
|
||||||
/// and population flag.
|
/// <param name="isPopulation">True for population stddev, false for sample stddev (default).</param>
|
||||||
/// </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>
|
|
||||||
public Stddev(object source, int period, bool isPopulation = false) : this(period, isPopulation)
|
public Stddev(object source, int period, bool isPopulation = false) : this(period, isPopulation)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes the Stddev instance by clearing the buffer.
|
|
||||||
/// </summary>
|
|
||||||
public override void Init()
|
public override void Init()
|
||||||
{
|
{
|
||||||
base.Init();
|
base.Init();
|
||||||
_buffer.Clear();
|
_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)
|
protected override void ManageState(bool isNew)
|
||||||
{
|
{
|
||||||
if (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()
|
protected override double Calculation()
|
||||||
{
|
{
|
||||||
ManageState(Input.IsNew);
|
ManageState(Input.IsNew);
|
||||||
|
|
||||||
_buffer.Add(Input.Value, Input.IsNew);
|
_buffer.Add(Input.Value, Input.IsNew);
|
||||||
|
|
||||||
double stddev = 0;
|
double stddev = 0;
|
||||||
@@ -117,8 +100,11 @@ public class Stddev : AbstractBase
|
|||||||
{
|
{
|
||||||
var values = _buffer.GetSpan().ToArray();
|
var values = _buffer.GetSpan().ToArray();
|
||||||
double mean = values.Average();
|
double mean = values.Average();
|
||||||
|
|
||||||
|
// Calculate sum of squared deviations
|
||||||
double sumOfSquaredDifferences = values.Sum(x => Math.Pow(x - mean, 2));
|
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 divisor = IsPopulation ? _buffer.Count : _buffer.Count - 1;
|
||||||
double variance = sumOfSquaredDifferences / divisor;
|
double variance = sumOfSquaredDifferences / divisor;
|
||||||
stddev = Math.Sqrt(variance);
|
stddev = Math.Sqrt(variance);
|
||||||
|
|||||||
+50
-65
@@ -1,48 +1,63 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
namespace QuanTAlib;
|
namespace QuanTAlib;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a variance calculator that measures the spread of a set of numbers
|
/// VARIANCE: Squared Deviation Risk Measure
|
||||||
/// from their average value.
|
/// 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>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// The Variance class calculates either the population variance or the sample
|
/// The Variance calculation process:
|
||||||
/// variance based on the isPopulation parameter. It uses a circular buffer
|
/// 1. Calculates mean of the data
|
||||||
/// to efficiently manage the data points within the specified period.
|
/// 2. Computes squared deviations from mean
|
||||||
|
/// 3. Sums squared deviations
|
||||||
|
/// 4. Divides by n or (n-1)
|
||||||
///
|
///
|
||||||
/// In financial analysis, variance is important for:
|
/// Key characteristics:
|
||||||
/// - Measuring the dispersion of returns around the mean.
|
/// - Measures data dispersion
|
||||||
/// - Assessing risk and volatility in financial instruments or portfolios.
|
/// - Squared units of input data
|
||||||
/// - Serving as a basis for other risk measures like standard deviation and beta.
|
/// - Always non-negative
|
||||||
/// - Contributing to portfolio optimization techniques, such as Modern Portfolio Theory.
|
/// - 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>
|
/// </remarks>
|
||||||
|
|
||||||
public class Variance : AbstractBase
|
public class Variance : AbstractBase
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Indicates whether to calculate population (true) or sample (false) variance.
|
|
||||||
/// </summary>
|
|
||||||
private readonly bool IsPopulation;
|
private readonly bool IsPopulation;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Circular buffer to store the most recent data points.
|
|
||||||
/// </summary>
|
|
||||||
private readonly CircularBuffer _buffer;
|
private readonly CircularBuffer _buffer;
|
||||||
|
|
||||||
/// <summary>
|
/// <param name="period">The number of points to consider for variance calculation.</param>
|
||||||
/// Initializes a new instance of the Variance class with the specified period and
|
/// <param name="isPopulation">True for population variance, false for sample variance (default).</param>
|
||||||
/// population flag.
|
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 2.</exception>
|
||||||
/// </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>
|
|
||||||
public Variance(int period, bool isPopulation = false)
|
public Variance(int period, bool isPopulation = false)
|
||||||
{
|
{
|
||||||
if (period < 2)
|
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;
|
IsPopulation = isPopulation;
|
||||||
WarmupPeriod = 0;
|
WarmupPeriod = 0;
|
||||||
@@ -51,34 +66,21 @@ public class Variance : AbstractBase
|
|||||||
Init();
|
Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <param name="source">The data source object that publishes updates.</param>
|
||||||
/// Initializes a new instance of the Variance class with the specified source, period,
|
/// <param name="period">The number of points to consider for variance calculation.</param>
|
||||||
/// and population flag.
|
/// <param name="isPopulation">True for population variance, false for sample variance (default).</param>
|
||||||
/// </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>
|
|
||||||
public Variance(object source, int period, bool isPopulation = false) : this(period, isPopulation)
|
public Variance(object source, int period, bool isPopulation = false) : this(period, isPopulation)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes the Variance instance by clearing the buffer.
|
|
||||||
/// </summary>
|
|
||||||
public override void Init()
|
public override void Init()
|
||||||
{
|
{
|
||||||
base.Init();
|
base.Init();
|
||||||
_buffer.Clear();
|
_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)
|
protected override void ManageState(bool isNew)
|
||||||
{
|
{
|
||||||
if (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()
|
protected override double Calculation()
|
||||||
{
|
{
|
||||||
ManageState(Input.IsNew);
|
ManageState(Input.IsNew);
|
||||||
|
|
||||||
_buffer.Add(Input.Value, Input.IsNew);
|
_buffer.Add(Input.Value, Input.IsNew);
|
||||||
|
|
||||||
double variance = 0;
|
double variance = 0;
|
||||||
@@ -118,8 +100,11 @@ public class Variance : AbstractBase
|
|||||||
{
|
{
|
||||||
var values = _buffer.GetSpan().ToArray();
|
var values = _buffer.GetSpan().ToArray();
|
||||||
double mean = values.Average();
|
double mean = values.Average();
|
||||||
|
|
||||||
|
// Calculate sum of squared deviations
|
||||||
double sumOfSquaredDifferences = values.Sum(x => Math.Pow(x - mean, 2));
|
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 divisor = IsPopulation ? _buffer.Count : _buffer.Count - 1;
|
||||||
variance = sumOfSquaredDifferences / divisor;
|
variance = sumOfSquaredDifferences / divisor;
|
||||||
}
|
}
|
||||||
|
|||||||
+50
-63
@@ -1,44 +1,61 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
namespace QuanTAlib;
|
namespace QuanTAlib;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a Z-score calculator that measures how many standard deviations
|
/// ZSCORE: Standardized Distance Measure
|
||||||
/// an element is from the mean of a set of values.
|
/// 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>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// The Zscore class calculates the Z-score (also known as standard score) for
|
/// The Zscore calculation process:
|
||||||
/// the most recent value in a given period. It uses a circular buffer to
|
/// 1. Calculates mean of the period
|
||||||
/// efficiently manage the data points within the specified period.
|
/// 2. Computes standard deviation
|
||||||
|
/// 3. Measures distance from mean
|
||||||
|
/// 4. Normalizes by standard deviation
|
||||||
///
|
///
|
||||||
/// In financial analysis, Z-score is important for:
|
/// Key characteristics:
|
||||||
/// - Identifying outliers or unusual price movements.
|
/// - Scale-independent measure
|
||||||
/// - Normalizing data across different scales or time periods.
|
/// - Symmetric around zero
|
||||||
/// - Assessing the relative position of a value within its historical distribution.
|
/// - Normal distribution context
|
||||||
/// - Supporting trading strategies based on mean reversion or momentum.
|
/// - 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>
|
/// </remarks>
|
||||||
|
|
||||||
public class Zscore : AbstractBase
|
public class Zscore : AbstractBase
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// The number of data points to consider for the Z-score calculation.
|
|
||||||
/// </summary>
|
|
||||||
private readonly int Period;
|
private readonly int Period;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Circular buffer to store the most recent data points.
|
|
||||||
/// </summary>
|
|
||||||
private readonly CircularBuffer _buffer;
|
private readonly CircularBuffer _buffer;
|
||||||
|
|
||||||
/// <summary>
|
/// <param name="period">The number of points to consider for Z-score calculation.</param>
|
||||||
/// Initializes a new instance of the Zscore class with the specified period.
|
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 2.</exception>
|
||||||
/// </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>
|
|
||||||
public Zscore(int period)
|
public Zscore(int period)
|
||||||
{
|
{
|
||||||
if (period < 2)
|
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;
|
Period = period;
|
||||||
WarmupPeriod = 2;
|
WarmupPeriod = 2;
|
||||||
@@ -47,30 +64,20 @@ public class Zscore : AbstractBase
|
|||||||
Init();
|
Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <param name="source">The data source object that publishes updates.</param>
|
||||||
/// Initializes a new instance of the Zscore class with the specified source and period.
|
/// <param name="period">The number of points to consider for Z-score calculation.</param>
|
||||||
/// </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>
|
|
||||||
public Zscore(object source, int period) : this(period)
|
public Zscore(object source, int period) : this(period)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes the Zscore instance by clearing the buffer.
|
|
||||||
/// </summary>
|
|
||||||
public override void Init()
|
public override void Init()
|
||||||
{
|
{
|
||||||
base.Init();
|
base.Init();
|
||||||
_buffer.Clear();
|
_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)
|
protected override void ManageState(bool isNew)
|
||||||
{
|
{
|
||||||
if (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()
|
protected override double Calculation()
|
||||||
{
|
{
|
||||||
ManageState(Input.IsNew);
|
ManageState(Input.IsNew);
|
||||||
|
|
||||||
_buffer.Add(Input.Value, Input.IsNew);
|
_buffer.Add(Input.Value, Input.IsNew);
|
||||||
|
|
||||||
double zScore = 0;
|
double zScore = 0;
|
||||||
if (_buffer.Count >= 2)
|
if (_buffer.Count >= 2) // Need at least 2 points for standard deviation
|
||||||
{ // We need at least 2 data points for Z-score
|
{
|
||||||
var values = _buffer.GetSpan().ToArray();
|
var values = _buffer.GetSpan().ToArray();
|
||||||
double mean = values.Average();
|
double mean = values.Average();
|
||||||
double n = values.Length;
|
double n = values.Length;
|
||||||
|
|
||||||
|
// Calculate sample standard deviation
|
||||||
double sumSquaredDeviations = values.Sum(x => Math.Pow(x - mean, 2));
|
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)
|
if (standardDeviation != 0) // Avoid division by zero
|
||||||
{ // Avoid division by zero
|
{
|
||||||
zScore = (Input.Value - mean) / standardDeviation;
|
zScore = (Input.Value - mean) / standardDeviation;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+48
-35
@@ -1,51 +1,75 @@
|
|||||||
|
using System;
|
||||||
namespace QuanTAlib;
|
namespace QuanTAlib;
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// The ATR class calculates the average true range using a Relative Moving Average (RMA)
|
/// The ATR calculation process:
|
||||||
/// of the true range. The true range is the greatest of: current high - current low,
|
/// 1. Calculates True Range (TR) as maximum of:
|
||||||
/// absolute value of current high - previous close, or absolute value of current low - previous close.
|
/// - 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>
|
/// </remarks>
|
||||||
|
|
||||||
public class Atr : AbstractBase
|
public class Atr : AbstractBase
|
||||||
{
|
{
|
||||||
public double Tr { get; private set; }
|
public double Tr { get; private set; }
|
||||||
private readonly Rma _ma;
|
private readonly Rma _ma;
|
||||||
private double _prevClose, _p_prevClose;
|
private double _prevClose, _p_prevClose;
|
||||||
|
|
||||||
/// <summary>
|
/// <param name="period">The number of periods for ATR calculation.</param>
|
||||||
/// Initializes a new instance of the Atr class with the specified period.
|
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
|
||||||
/// </summary>
|
|
||||||
/// <param name="period">The period over which to calculate the ATR.</param>
|
|
||||||
/// <exception cref="ArgumentOutOfRangeException">
|
|
||||||
/// Thrown when period is less than 1.
|
|
||||||
/// </exception>
|
|
||||||
public Atr(int period)
|
public Atr(int period)
|
||||||
{
|
{
|
||||||
if (period < 1)
|
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);
|
_ma = new(period, useSma: true);
|
||||||
WarmupPeriod = _ma.WarmupPeriod;
|
WarmupPeriod = _ma.WarmupPeriod;
|
||||||
Name = $"ATR({period})";
|
Name = $"ATR({period})";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <param name="source">The data source object that publishes updates.</param>
|
||||||
/// Initializes a new instance of the Atr class with the specified source and period.
|
/// <param name="period">The number of periods for ATR calculation.</param>
|
||||||
/// </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>
|
|
||||||
public Atr(object source, int period) : this(period)
|
public Atr(object source, int period) : this(period)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
|
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes the Atr instance by setting up the initial state.
|
|
||||||
/// </summary>
|
|
||||||
public override void Init()
|
public override void Init()
|
||||||
{
|
{
|
||||||
base.Init();
|
base.Init();
|
||||||
@@ -54,10 +78,6 @@ public class Atr : AbstractBase
|
|||||||
Tr = 0;
|
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)
|
protected override void ManageState(bool isNew)
|
||||||
{
|
{
|
||||||
if (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()
|
protected override double Calculation()
|
||||||
{
|
{
|
||||||
ManageState(BarInput.IsNew);
|
ManageState(BarInput.IsNew);
|
||||||
|
|
||||||
if (_index == 1)
|
if (_index == 1)
|
||||||
{
|
{
|
||||||
|
// First bar uses simple high-low range
|
||||||
Tr = BarInput.High - BarInput.Low;
|
Tr = BarInput.High - BarInput.Low;
|
||||||
_prevClose = BarInput.Close;
|
_prevClose = BarInput.Close;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// Calculate True Range as maximum of three measures
|
||||||
Tr = Math.Max(
|
Tr = Math.Max(
|
||||||
BarInput.High - BarInput.Low,
|
BarInput.High - BarInput.Low,
|
||||||
Math.Max(
|
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));
|
_ma.Calc(new TValue(Input.Time, Tr, BarInput.IsNew));
|
||||||
|
|
||||||
IsHot = _ma.IsHot;
|
IsHot = _ma.IsHot;
|
||||||
|
|||||||
+54
-45
@@ -1,14 +1,49 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
namespace QuanTAlib;
|
namespace QuanTAlib;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a historical volatility calculator that measures the dispersion of returns
|
/// HV: Historical Volatility
|
||||||
/// for a given security or market index over a specific period.
|
/// 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>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// The Historical class calculates volatility based on logarithmic returns. It can provide
|
/// The HV calculation process:
|
||||||
/// both annualized and non-annualized volatility measures. The calculation uses a sample
|
/// 1. Computes daily log returns
|
||||||
/// standard deviation formula and assumes 252 trading days in a year for annualization.
|
/// 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>
|
/// </remarks>
|
||||||
|
|
||||||
public class Hv : AbstractBase
|
public class Hv : AbstractBase
|
||||||
{
|
{
|
||||||
private readonly int Period;
|
private readonly int Period;
|
||||||
@@ -17,44 +52,34 @@ public class Hv : AbstractBase
|
|||||||
private readonly CircularBuffer _logReturns;
|
private readonly CircularBuffer _logReturns;
|
||||||
private double _previousClose;
|
private double _previousClose;
|
||||||
|
|
||||||
/// <summary>
|
/// <param name="period">The number of periods for volatility calculation.</param>
|
||||||
/// Initializes a new instance of the Historical class with the specified period and annualization flag.
|
/// <param name="isAnnualized">Whether to annualize the result (default true).</param>
|
||||||
/// </summary>
|
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 2.</exception>
|
||||||
/// <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>
|
|
||||||
public Hv(int period, bool isAnnualized = true)
|
public Hv(int period, bool isAnnualized = true)
|
||||||
{
|
{
|
||||||
if (period < 2)
|
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;
|
Period = period;
|
||||||
IsAnnualized = isAnnualized;
|
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);
|
_buffer = new CircularBuffer(period + 1);
|
||||||
_logReturns = new CircularBuffer(period);
|
_logReturns = new CircularBuffer(period);
|
||||||
Name = $"Historical(period={period}, annualized={isAnnualized})";
|
Name = $"Historical(period={period}, annualized={isAnnualized})";
|
||||||
Init();
|
Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <param name="source">The data source object that publishes updates.</param>
|
||||||
/// Initializes a new instance of the Historical class with the specified source, period, and annualization flag.
|
/// <param name="period">The number of periods for volatility calculation.</param>
|
||||||
/// </summary>
|
/// <param name="isAnnualized">Whether to annualize the result (default true).</param>
|
||||||
/// <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>
|
|
||||||
public Hv(object source, int period, bool isAnnualized = true) : this(period, isAnnualized)
|
public Hv(object source, int period, bool isAnnualized = true) : this(period, isAnnualized)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
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()
|
public override void Init()
|
||||||
{
|
{
|
||||||
base.Init();
|
base.Init();
|
||||||
@@ -63,10 +88,6 @@ public class Hv : AbstractBase
|
|||||||
_previousClose = 0;
|
_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)
|
protected override void ManageState(bool isNew)
|
||||||
{
|
{
|
||||||
if (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()
|
protected override double Calculation()
|
||||||
{
|
{
|
||||||
ManageState(Input.IsNew);
|
ManageState(Input.IsNew);
|
||||||
|
|
||||||
_buffer.Add(Input.Value, Input.IsNew);
|
_buffer.Add(Input.Value, Input.IsNew);
|
||||||
|
|
||||||
double volatility = 0;
|
double volatility = 0;
|
||||||
if (_buffer.Count > 1)
|
if (_buffer.Count > 1)
|
||||||
{
|
{
|
||||||
|
// Calculate log return if we have previous close
|
||||||
if (_previousClose != 0)
|
if (_previousClose != 0)
|
||||||
{
|
{
|
||||||
double logReturn = Math.Log(Input.Value / _previousClose);
|
double logReturn = Math.Log(Input.Value / _previousClose);
|
||||||
_logReturns.Add(logReturn, Input.IsNew);
|
_logReturns.Add(logReturn, Input.IsNew);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calculate volatility when we have enough returns
|
||||||
if (_logReturns.Count == Period)
|
if (_logReturns.Count == Period)
|
||||||
{
|
{
|
||||||
var returns = _logReturns.GetSpan().ToArray();
|
var returns = _logReturns.GetSpan().ToArray();
|
||||||
double mean = returns.Average();
|
double mean = returns.Average();
|
||||||
double sumOfSquaredDifferences = returns.Sum(x => Math.Pow(x - mean, 2));
|
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);
|
volatility = Math.Sqrt(variance);
|
||||||
|
|
||||||
if (IsAnnualized)
|
if (IsAnnualized)
|
||||||
{
|
{
|
||||||
// Assuming 252 trading days in a year. Adjust as needed.
|
volatility *= Math.Sqrt(252); // Annualize using trading days
|
||||||
volatility *= Math.Sqrt(252);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+56
-41
@@ -1,9 +1,46 @@
|
|||||||
/// <summary>
|
using System;
|
||||||
/// Represents a Jurik Volatility (Jvolty) calculator, a measure of market volatility based on Jurik Moving Average (JMA) concepts.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
namespace QuanTAlib;
|
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
|
public class Jvolty : AbstractBase
|
||||||
{
|
{
|
||||||
private readonly int _period;
|
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 _prevMa1, _prevDet0, _prevDet1, _prevJma, _p_prevMa1, _p_prevDet0, _p_prevDet1, _p_prevJma;
|
||||||
private double _vSum, _p_vSum;
|
private double _vSum, _p_vSum;
|
||||||
|
|
||||||
|
|
||||||
public double UpperBand { get; set; }
|
public double UpperBand { get; set; }
|
||||||
public double LowerBand { get; set; }
|
public double LowerBand { get; set; }
|
||||||
public double Volty { get; set; }
|
public double Volty { get; set; }
|
||||||
@@ -26,21 +62,15 @@ public class Jvolty : AbstractBase
|
|||||||
public double Jma { get; set; }
|
public double Jma { get; set; }
|
||||||
public double AvgVolty { get; set; }
|
public double AvgVolty { get; set; }
|
||||||
|
|
||||||
|
/// <param name="period">The number of periods for volatility calculation.</param>
|
||||||
/// <summary>
|
/// <param name="phase">Phase parameter for JMA smoothing (default 0).</param>
|
||||||
/// Initializes a new instance of the Jvolty class with the specified parameters.
|
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
|
||||||
/// </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>
|
|
||||||
public Jvolty(int period, int phase = 0)
|
public Jvolty(int period, int phase = 0)
|
||||||
{
|
{
|
||||||
if (period < 1)
|
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;
|
_period = period;
|
||||||
_phase = Math.Clamp((phase * 0.01) + 1.5, 0.5, 2.5);
|
_phase = Math.Clamp((phase * 0.01) + 1.5, 0.5, 2.5);
|
||||||
@@ -53,22 +83,15 @@ public class Jvolty : AbstractBase
|
|||||||
Name = $"JVOLTY({period})";
|
Name = $"JVOLTY({period})";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <param name="source">The data source object that publishes updates.</param>
|
||||||
/// Initializes a new instance of the Jvolty class with the specified source and parameters.
|
/// <param name="period">The number of periods for volatility calculation.</param>
|
||||||
/// </summary>
|
/// <param name="phase">Phase parameter for JMA smoothing (default 0).</param>
|
||||||
/// <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>
|
|
||||||
public Jvolty(object source, int period, int phase = 0) : this(period, phase)
|
public Jvolty(object source, int period, int phase = 0) : this(period, phase)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
|
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes the Jvolty instance by setting up the initial state.
|
|
||||||
/// </summary>
|
|
||||||
public override void Init()
|
public override void Init()
|
||||||
{
|
{
|
||||||
base.Init();
|
base.Init();
|
||||||
@@ -80,10 +103,6 @@ public class Jvolty : AbstractBase
|
|||||||
_vsumBuff.Clear();
|
_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)
|
protected override void ManageState(bool isNew)
|
||||||
{
|
{
|
||||||
if (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()
|
protected override double Calculation()
|
||||||
{
|
{
|
||||||
ManageState(Input.IsNew);
|
ManageState(Input.IsNew);
|
||||||
@@ -125,26 +138,29 @@ public class Jvolty : AbstractBase
|
|||||||
_upperBand = _lowerBand = price;
|
_upperBand = _lowerBand = price;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calculate volatility from band distances
|
||||||
double del1 = price - _upperBand;
|
double del1 = price - _upperBand;
|
||||||
double del2 = price - _lowerBand;
|
double del2 = price - _lowerBand;
|
||||||
double volty = Math.Max(Math.Abs(del1), Math.Abs(del2));
|
double volty = Math.Max(Math.Abs(del1), Math.Abs(del2));
|
||||||
|
|
||||||
|
// Calculate moving averages of volatility
|
||||||
_vsumBuff.Add(volty, Input.IsNew);
|
_vsumBuff.Add(volty, Input.IsNew);
|
||||||
_vSum += (_vsumBuff[^1] - _vsumBuff[0]) / 10;
|
_vSum += (_vsumBuff[^1] - _vsumBuff[0]) / 10;
|
||||||
_avoltyBuff.Add(_vSum, Input.IsNew);
|
_avoltyBuff.Add(_vSum, Input.IsNew);
|
||||||
double avgvolty = _avoltyBuff.Average();
|
double avgvolty = _avoltyBuff.Average();
|
||||||
|
|
||||||
|
// Normalize and adjust volatility
|
||||||
double rvolty = (avgvolty > 0) ? volty / avgvolty : 1;
|
double rvolty = (avgvolty > 0) ? volty / avgvolty : 1;
|
||||||
rvolty = Math.Min(Math.Max(rvolty, 1.0), Math.Pow(_len1, 1.0 / _pow1));
|
rvolty = Math.Min(Math.Max(rvolty, 1.0), Math.Pow(_len1, 1.0 / _pow1));
|
||||||
|
|
||||||
double pow2 = Math.Pow(rvolty, _pow1);
|
double pow2 = Math.Pow(rvolty, _pow1);
|
||||||
double Kv = Math.Pow(_beta, Math.Sqrt(pow2));
|
double Kv = Math.Pow(_beta, Math.Sqrt(pow2));
|
||||||
|
|
||||||
|
// Update adaptive bands
|
||||||
_upperBand = (del1 >= 0) ? price : price - (Kv * del1);
|
_upperBand = (del1 >= 0) ? price : price - (Kv * del1);
|
||||||
_lowerBand = (del2 <= 0) ? price : price - (Kv * del2);
|
_lowerBand = (del2 <= 0) ? price : price - (Kv * del2);
|
||||||
|
|
||||||
|
// Apply JMA smoothing
|
||||||
|
|
||||||
double alpha = Math.Pow(_beta, pow2);
|
double alpha = Math.Pow(_beta, pow2);
|
||||||
double ma1 = (1 - alpha) * Input.Value + alpha * _prevMa1;
|
double ma1 = (1 - alpha) * Input.Value + alpha * _prevMa1;
|
||||||
_prevMa1 = ma1;
|
_prevMa1 = ma1;
|
||||||
@@ -153,11 +169,12 @@ public class Jvolty : AbstractBase
|
|||||||
_prevDet0 = det0;
|
_prevDet0 = det0;
|
||||||
double ma2 = ma1 + _phase * 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;
|
_prevDet1 = det1;
|
||||||
double jma = _prevJma + det1;
|
double jma = _prevJma + det1;
|
||||||
_prevJma = jma;
|
_prevJma = jma;
|
||||||
|
|
||||||
|
// Update public properties
|
||||||
UpperBand = _upperBand;
|
UpperBand = _upperBand;
|
||||||
LowerBand = _lowerBand;
|
LowerBand = _lowerBand;
|
||||||
Volty = volty;
|
Volty = volty;
|
||||||
@@ -169,5 +186,3 @@ public class Jvolty : AbstractBase
|
|||||||
return volty;
|
return volty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+53
-46
@@ -1,14 +1,48 @@
|
|||||||
|
using System;
|
||||||
namespace QuanTAlib;
|
namespace QuanTAlib;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a realized volatility calculator that measures the actual price fluctuations
|
/// RV: Realized Volatility
|
||||||
/// observed in the market over a specific period.
|
/// 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>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// The Realized class calculates volatility based on logarithmic returns. It can provide
|
/// The RV calculation process:
|
||||||
/// both annualized and non-annualized volatility measures. The calculation uses a rolling
|
/// 1. Computes log returns
|
||||||
/// sum of squared returns for efficiency and assumes 252 trading days in a year for annualization.
|
/// 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>
|
/// </remarks>
|
||||||
|
|
||||||
public class Rv : AbstractBase
|
public class Rv : AbstractBase
|
||||||
{
|
{
|
||||||
private readonly int Period;
|
private readonly int Period;
|
||||||
@@ -17,43 +51,33 @@ public class Rv : AbstractBase
|
|||||||
private double _previousClose;
|
private double _previousClose;
|
||||||
private double _sumSquaredReturns;
|
private double _sumSquaredReturns;
|
||||||
|
|
||||||
/// <summary>
|
/// <param name="period">The number of periods for volatility calculation.</param>
|
||||||
/// Initializes a new instance of the Realized class with the specified period and annualization flag.
|
/// <param name="isAnnualized">Whether to annualize the result (default true).</param>
|
||||||
/// </summary>
|
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 2.</exception>
|
||||||
/// <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>
|
|
||||||
public Rv(int period, bool isAnnualized = true)
|
public Rv(int period, bool isAnnualized = true)
|
||||||
{
|
{
|
||||||
if (period < 2)
|
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;
|
Period = period;
|
||||||
IsAnnualized = isAnnualized;
|
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);
|
_returns = new CircularBuffer(period);
|
||||||
Name = $"Realized(period={period}, annualized={isAnnualized})";
|
Name = $"Realized(period={period}, annualized={isAnnualized})";
|
||||||
Init();
|
Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <param name="source">The data source object that publishes updates.</param>
|
||||||
/// Initializes a new instance of the Realized class with a data source.
|
/// <param name="period">The number of periods for volatility calculation.</param>
|
||||||
/// </summary>
|
/// <param name="isAnnualized">Whether to annualize the result (default true).</param>
|
||||||
/// <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>
|
|
||||||
public Rv(object source, int period, bool isAnnualized = true) : this(period, isAnnualized)
|
public Rv(object source, int period, bool isAnnualized = true) : this(period, isAnnualized)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes the Realized instance by clearing buffers and resetting calculation variables.
|
|
||||||
/// </summary>
|
|
||||||
public override void Init()
|
public override void Init()
|
||||||
{
|
{
|
||||||
base.Init();
|
base.Init();
|
||||||
@@ -62,10 +86,6 @@ public class Rv : AbstractBase
|
|||||||
_sumSquaredReturns = 0;
|
_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)
|
protected override void ManageState(bool isNew)
|
||||||
{
|
{
|
||||||
if (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()
|
protected override double Calculation()
|
||||||
{
|
{
|
||||||
ManageState(Input.IsNew);
|
ManageState(Input.IsNew);
|
||||||
@@ -97,26 +102,28 @@ public class Rv : AbstractBase
|
|||||||
double volatility = 0;
|
double volatility = 0;
|
||||||
if (_previousClose != 0)
|
if (_previousClose != 0)
|
||||||
{
|
{
|
||||||
|
// Calculate log return
|
||||||
double logReturn = Math.Log(Input.Value / _previousClose);
|
double logReturn = Math.Log(Input.Value / _previousClose);
|
||||||
|
|
||||||
if (_returns.Count == Period)
|
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);
|
_sumSquaredReturns -= Math.Pow(_returns[0], 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add new return and update sum
|
||||||
_returns.Add(logReturn, Input.IsNew);
|
_returns.Add(logReturn, Input.IsNew);
|
||||||
_sumSquaredReturns += Math.Pow(logReturn, 2);
|
_sumSquaredReturns += Math.Pow(logReturn, 2);
|
||||||
|
|
||||||
if (_returns.Count == Period)
|
if (_returns.Count == Period)
|
||||||
{
|
{
|
||||||
|
// Calculate realized volatility
|
||||||
double variance = _sumSquaredReturns / Period;
|
double variance = _sumSquaredReturns / Period;
|
||||||
volatility = Math.Sqrt(variance);
|
volatility = Math.Sqrt(variance);
|
||||||
|
|
||||||
if (IsAnnualized)
|
if (IsAnnualized)
|
||||||
{
|
{
|
||||||
// Assuming 252 trading days in a year. Adjust as needed.
|
volatility *= Math.Sqrt(252); // Annualize using trading days
|
||||||
volatility *= Math.Sqrt(252);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+49
-44
@@ -1,36 +1,61 @@
|
|||||||
|
using System;
|
||||||
namespace QuanTAlib;
|
namespace QuanTAlib;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a Relative Volatility Index (RVI) calculator, which measures the direction
|
/// RVI: Relative Volatility Index
|
||||||
/// of volatility in relation to price movements.
|
/// 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>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// The RVI was introduced by Donald Dorsey in the 1993 issue of Technical Analysis
|
/// The RVI calculation process:
|
||||||
/// of Stocks & Commodities Magazine. It focuses on the direction of price movements
|
/// 1. Separates price changes into up/down moves
|
||||||
/// in relation to volatility. The indicator uses standard deviation calculations
|
/// 2. Calculates standard deviation for each
|
||||||
/// to determine whether volatility is increasing more in up moves or down moves.
|
/// 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
|
/// Key characteristics:
|
||||||
/// calculations to compute the RVI.
|
/// - 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>
|
/// </remarks>
|
||||||
|
|
||||||
public class Rvi : AbstractBase
|
public class Rvi : AbstractBase
|
||||||
{
|
{
|
||||||
private readonly Stddev _upStdDev, _downStdDev;
|
private readonly Stddev _upStdDev, _downStdDev;
|
||||||
private readonly Sma _upSma, _downSma;
|
private readonly Sma _upSma, _downSma;
|
||||||
private double _previousClose;
|
private double _previousClose;
|
||||||
|
|
||||||
/// <summary>
|
/// <param name="period">The number of periods for RVI calculation.</param>
|
||||||
/// Initializes a new instance of the Rvi class with the specified period.
|
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 2.</exception>
|
||||||
/// </summary>
|
|
||||||
/// <param name="period">The period over which to calculate the RVI.</param>
|
|
||||||
/// <exception cref="ArgumentOutOfRangeException">
|
|
||||||
/// Thrown when period is less than 2.
|
|
||||||
/// </exception>
|
|
||||||
public Rvi(int period)
|
public Rvi(int period)
|
||||||
{
|
{
|
||||||
if (period < 2)
|
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;
|
int Period = period;
|
||||||
WarmupPeriod = period;
|
WarmupPeriod = period;
|
||||||
@@ -42,30 +67,20 @@ public class Rvi : AbstractBase
|
|||||||
Init();
|
Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <param name="source">The data source object that publishes updates.</param>
|
||||||
/// Initializes a new instance of the Rvi class with the specified source and period.
|
/// <param name="period">The number of periods for RVI calculation.</param>
|
||||||
/// </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>
|
|
||||||
public Rvi(object source, int period) : this(period)
|
public Rvi(object source, int period) : this(period)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes the Rvi instance by setting up the initial state.
|
|
||||||
/// </summary>
|
|
||||||
public override void Init()
|
public override void Init()
|
||||||
{
|
{
|
||||||
base.Init();
|
base.Init();
|
||||||
_previousClose = 0;
|
_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)
|
protected override void ManageState(bool isNew)
|
||||||
{
|
{
|
||||||
if (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()
|
protected override double Calculation()
|
||||||
{
|
{
|
||||||
ManageState(Input.IsNew);
|
ManageState(Input.IsNew);
|
||||||
@@ -97,14 +97,19 @@ public class Rvi : AbstractBase
|
|||||||
double close = Input.Value;
|
double close = Input.Value;
|
||||||
double change = close - _previousClose;
|
double change = close - _previousClose;
|
||||||
|
|
||||||
|
// Separate into up and down moves
|
||||||
double upMove = Math.Max(change, 0);
|
double upMove = Math.Max(change, 0);
|
||||||
double downMove = 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)));
|
_upSma.Calc(_upStdDev.Calc(new TValue(Input.Time, upMove, Input.IsNew)));
|
||||||
_downSma.Calc(_downStdDev.Calc(new TValue(Input.Time, downMove, Input.IsNew)));
|
_downSma.Calc(_downStdDev.Calc(new TValue(Input.Time, downMove, Input.IsNew)));
|
||||||
|
|
||||||
|
// Calculate RVI ratio
|
||||||
double rvi;
|
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;
|
_previousClose = close;
|
||||||
IsHot = _index >= WarmupPeriod;
|
IsHot = _index >= WarmupPeriod;
|
||||||
|
|||||||
Reference in New Issue
Block a user