mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-18 02:28:05 +00:00
New version merge
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
namespace QuanTAlib;
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
// Shannon's Entropy calculation
|
||||
public class Entropy : AbstractBase
|
||||
{
|
||||
public readonly int Period;
|
||||
private CircularBuffer _buffer;
|
||||
|
||||
public Entropy(int period) : base()
|
||||
{
|
||||
if (period < 2)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 2 for entropy calculation.");
|
||||
}
|
||||
Period = period;
|
||||
WarmupPeriod = 2;
|
||||
_buffer = new CircularBuffer(period);
|
||||
Name = $"Entropy(period={period})";
|
||||
Init();
|
||||
}
|
||||
|
||||
public Entropy(object source, int period) : this(period)
|
||||
{
|
||||
var pubEvent = source.GetType().GetEvent("Pub");
|
||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
base.Init();
|
||||
_buffer.Clear();
|
||||
}
|
||||
|
||||
protected override void ManageState(bool isNew)
|
||||
{
|
||||
if (isNew)
|
||||
{
|
||||
_lastValidValue = Input.Value;
|
||||
_index++;
|
||||
}
|
||||
}
|
||||
|
||||
protected override double Calculation()
|
||||
{
|
||||
ManageState(Input.IsNew);
|
||||
|
||||
_buffer.Add(Input.Value, Input.IsNew);
|
||||
|
||||
double entropy = 0;
|
||||
if (_index > 1) // We need at least two data points for entropy calculation
|
||||
{
|
||||
var values = _buffer.GetSpan().ToArray();
|
||||
int n = values.Length;
|
||||
|
||||
// Calculate probabilities
|
||||
var groupedValues = values.GroupBy(x => x).Select(g => new { Value = g.Key, Count = g.Count() });
|
||||
|
||||
// Use the actual count of values for probability calculation
|
||||
foreach (var group in groupedValues)
|
||||
{
|
||||
double probability = (double)group.Count / n;
|
||||
entropy -= probability * Math.Log2(probability);
|
||||
}
|
||||
|
||||
// Normalize the entropy based on the current number of unique values
|
||||
int uniqueValueCount = groupedValues.Count();
|
||||
double maxEntropy = Math.Log2(uniqueValueCount);
|
||||
|
||||
entropy = entropy == 0 ? 1 : entropy / maxEntropy;
|
||||
|
||||
}
|
||||
else { entropy = 1; }
|
||||
|
||||
IsHot = _buffer.Count >= Period;
|
||||
return entropy;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
namespace QuanTAlib;
|
||||
|
||||
// Excess kurtosis calculated with Sheskin Algorithm
|
||||
public class Kurtosis : AbstractBase
|
||||
{
|
||||
public readonly int Period;
|
||||
private CircularBuffer _buffer;
|
||||
|
||||
public Kurtosis(int period) : base()
|
||||
{
|
||||
if (period < 4)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 4 for kurtosis calculation.");
|
||||
}
|
||||
Period = period;
|
||||
WarmupPeriod = Period - 1;
|
||||
_buffer = new CircularBuffer(period);
|
||||
Name = $"Kurtosis(period={period})";
|
||||
Init();
|
||||
}
|
||||
|
||||
public Kurtosis(object source, int period) : this(period)
|
||||
{
|
||||
var pubEvent = source.GetType().GetEvent("Pub");
|
||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
base.Init();
|
||||
_buffer.Clear();
|
||||
}
|
||||
|
||||
protected override void ManageState(bool isNew)
|
||||
{
|
||||
if (isNew)
|
||||
{
|
||||
_lastValidValue = Input.Value;
|
||||
_index++;
|
||||
}
|
||||
}
|
||||
|
||||
protected override double Calculation()
|
||||
{
|
||||
ManageState(Input.IsNew);
|
||||
|
||||
_buffer.Add(Input.Value, Input.IsNew);
|
||||
|
||||
double kurtosis = 0;
|
||||
if (_buffer.Count > 3)
|
||||
{
|
||||
var values = _buffer.GetSpan().ToArray();
|
||||
double mean = values.Average();
|
||||
double n = values.Length;
|
||||
|
||||
double s2 = 0;
|
||||
double s4 = 0;
|
||||
|
||||
for (int i = 0; i < values.Length; i++)
|
||||
{
|
||||
double diff = values[i] - mean;
|
||||
s2 += diff * diff;
|
||||
s4 += diff * diff * diff * diff;
|
||||
}
|
||||
|
||||
double variance = s2 / (n - 1);
|
||||
|
||||
// Using the Sheskin Algorithm for kurtosis
|
||||
kurtosis = (n * (n + 1) * s4) / (variance * variance * (n - 3) * (n - 1) * (n - 2))
|
||||
- (3 * (n - 1) * (n - 1) / ((n - 2) * (n - 3)));
|
||||
}
|
||||
|
||||
IsHot = _buffer.Count >= Period;
|
||||
return kurtosis;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
using System;
|
||||
|
||||
namespace QuanTAlib
|
||||
{
|
||||
public class Max : AbstractBase
|
||||
{
|
||||
public readonly int Period;
|
||||
private CircularBuffer _buffer;
|
||||
private readonly double _halfLife;
|
||||
private double _currentMax, _p_currentMax;
|
||||
private int _timeSinceNewMax, _p_timeSinceNewMax;
|
||||
|
||||
public Max(int period, double decay = 0) : base()
|
||||
{
|
||||
if (period < 1)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1.");
|
||||
}
|
||||
if (decay < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(decay), "Half-life must be non-negative.");
|
||||
}
|
||||
Period = period;
|
||||
WarmupPeriod = 0;
|
||||
_buffer = new CircularBuffer(period);
|
||||
_halfLife = decay * 0.1;
|
||||
Name = $"Max(period={period}, halfLife={decay:F2})";
|
||||
Init();
|
||||
}
|
||||
|
||||
public Max(object source, int period, double decay = 0) : this(period, decay)
|
||||
{
|
||||
var pubEvent = source.GetType().GetEvent("Pub");
|
||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
base.Init();
|
||||
_currentMax = double.MinValue;
|
||||
_timeSinceNewMax = 0;
|
||||
}
|
||||
|
||||
protected override void ManageState(bool isNew)
|
||||
{
|
||||
if (isNew)
|
||||
{
|
||||
_p_currentMax = _currentMax;
|
||||
_lastValidValue = Input.Value;
|
||||
_index++;
|
||||
_timeSinceNewMax++;
|
||||
_p_timeSinceNewMax = _timeSinceNewMax;
|
||||
}
|
||||
else
|
||||
{
|
||||
_currentMax = _p_currentMax;
|
||||
_timeSinceNewMax = _p_timeSinceNewMax;
|
||||
}
|
||||
}
|
||||
|
||||
protected override double Calculation()
|
||||
{
|
||||
ManageState(Input.IsNew);
|
||||
_buffer.Add(Input.Value, Input.IsNew);
|
||||
|
||||
if (Input.Value >= _currentMax)
|
||||
{
|
||||
_currentMax = Input.Value;
|
||||
_timeSinceNewMax = 0;
|
||||
}
|
||||
|
||||
double decayRate = 1 - Math.Exp(-_halfLife * _timeSinceNewMax / Period);
|
||||
_currentMax = _currentMax - decayRate * (_currentMax - _buffer.Average());
|
||||
_currentMax = Math.Min(_currentMax, _buffer.Max());
|
||||
|
||||
IsHot = true;
|
||||
return _currentMax;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace QuanTAlib
|
||||
{
|
||||
public class Median : AbstractBase
|
||||
{
|
||||
public readonly int Period;
|
||||
private CircularBuffer _buffer;
|
||||
|
||||
public Median(int period) : base()
|
||||
{
|
||||
if (period < 1)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1.");
|
||||
}
|
||||
Period = period;
|
||||
WarmupPeriod = period;
|
||||
_buffer = new CircularBuffer(period);
|
||||
Name = $"Median(period={period})";
|
||||
Init();
|
||||
}
|
||||
|
||||
public Median(object source, int period) : this(period)
|
||||
{
|
||||
var pubEvent = source.GetType().GetEvent("Pub");
|
||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
base.Init();
|
||||
}
|
||||
|
||||
protected override void ManageState(bool isNew)
|
||||
{
|
||||
if (isNew)
|
||||
{
|
||||
_lastValidValue = Input.Value;
|
||||
_index++;
|
||||
}
|
||||
}
|
||||
|
||||
protected override double Calculation()
|
||||
{
|
||||
ManageState(Input.IsNew);
|
||||
_buffer.Add(Input.Value, Input.IsNew);
|
||||
|
||||
double median;
|
||||
if (_index >= Period)
|
||||
{
|
||||
var sortedValues = _buffer.GetSpan().ToArray();
|
||||
Array.Sort(sortedValues);
|
||||
int middleIndex = sortedValues.Length / 2;
|
||||
|
||||
if (sortedValues.Length % 2 == 0)
|
||||
{
|
||||
median = (sortedValues[middleIndex - 1] + sortedValues[middleIndex]) / 2.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
median = sortedValues[middleIndex];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
median = _buffer.Average(); // Use average until we have enough data points
|
||||
}
|
||||
|
||||
IsHot = _index >= WarmupPeriod;
|
||||
return median;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
using System;
|
||||
|
||||
namespace QuanTAlib
|
||||
{
|
||||
public class Min : AbstractBase
|
||||
{
|
||||
public readonly int Period;
|
||||
private CircularBuffer _buffer;
|
||||
private readonly double _halfLife;
|
||||
private double _currentMin, _p_currentMin;
|
||||
private int _timeSinceNewMin, _p_timeSinceNewMin;
|
||||
|
||||
public Min(int period, double decay = 0) : base()
|
||||
{
|
||||
if (period < 1)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1.");
|
||||
}
|
||||
if (decay < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(decay), "Half-life must be non-negative.");
|
||||
}
|
||||
Period = period;
|
||||
WarmupPeriod = 0;
|
||||
_buffer = new CircularBuffer(period);
|
||||
_halfLife = decay * 0.1;
|
||||
Name = $"Min(period={period}, halfLife={decay:F2})";
|
||||
Init();
|
||||
}
|
||||
|
||||
public Min(object source, int period, double decay = 0) : this(period, decay)
|
||||
{
|
||||
var pubEvent = source.GetType().GetEvent("Pub");
|
||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
base.Init();
|
||||
_currentMin = double.MaxValue;
|
||||
_timeSinceNewMin = 0;
|
||||
}
|
||||
|
||||
protected override void ManageState(bool isNew)
|
||||
{
|
||||
if (isNew)
|
||||
{
|
||||
_p_currentMin = _currentMin;
|
||||
_lastValidValue = Input.Value;
|
||||
_index++;
|
||||
_timeSinceNewMin++;
|
||||
_p_timeSinceNewMin = _timeSinceNewMin;
|
||||
}
|
||||
else
|
||||
{
|
||||
_currentMin = _p_currentMin;
|
||||
_timeSinceNewMin = _p_timeSinceNewMin;
|
||||
}
|
||||
}
|
||||
|
||||
protected override double Calculation()
|
||||
{
|
||||
ManageState(Input.IsNew);
|
||||
_buffer.Add(Input.Value, Input.IsNew);
|
||||
|
||||
if (Input.Value <= _currentMin)
|
||||
{
|
||||
_currentMin = Input.Value;
|
||||
_timeSinceNewMin = 0;
|
||||
}
|
||||
|
||||
double decayRate = 1 - Math.Exp(-_halfLife * _timeSinceNewMin / Period);
|
||||
_currentMin = _currentMin + decayRate * (_buffer.Average() - _currentMin);
|
||||
_currentMin = Math.Max(_currentMin, _buffer.Min());
|
||||
|
||||
IsHot = true;
|
||||
return _currentMin;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class Mode : AbstractBase
|
||||
{
|
||||
public readonly int Period;
|
||||
private CircularBuffer _buffer;
|
||||
|
||||
public Mode(int period) : base()
|
||||
{
|
||||
if (period < 1)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1.");
|
||||
}
|
||||
Period = period;
|
||||
WarmupPeriod = period;
|
||||
_buffer = new CircularBuffer(period);
|
||||
Name = $"Mode(period={period})";
|
||||
Init();
|
||||
}
|
||||
|
||||
public Mode(object source, int period) : this(period)
|
||||
{
|
||||
var pubEvent = source.GetType().GetEvent("Pub");
|
||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
base.Init();
|
||||
}
|
||||
|
||||
protected override void ManageState(bool isNew)
|
||||
{
|
||||
if (isNew)
|
||||
{
|
||||
_lastValidValue = Input.Value;
|
||||
_index++;
|
||||
}
|
||||
}
|
||||
|
||||
protected override double Calculation()
|
||||
{
|
||||
ManageState(Input.IsNew);
|
||||
_buffer.Add(Input.Value, Input.IsNew);
|
||||
|
||||
double mode;
|
||||
if (_index >= Period)
|
||||
{
|
||||
var values = _buffer.GetSpan().ToArray();
|
||||
var groupedValues = values.GroupBy(v => v)
|
||||
.OrderByDescending(g => g.Count())
|
||||
.ThenBy(g => g.Key)
|
||||
.ToList();
|
||||
|
||||
int maxCount = groupedValues.First().Count();
|
||||
var modes = groupedValues.TakeWhile(g => g.Count() == maxCount)
|
||||
.Select(g => g.Key)
|
||||
.ToList();
|
||||
|
||||
mode = modes.Average(); // If there are multiple modes, we return their average
|
||||
}
|
||||
else
|
||||
{
|
||||
mode = _buffer.Average(); // Use average until we have enough data points
|
||||
}
|
||||
|
||||
IsHot = _index >= WarmupPeriod;
|
||||
return mode;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
namespace QuanTAlib;
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
public class Percentile : AbstractBase
|
||||
{
|
||||
public readonly int Period;
|
||||
public readonly double Percent;
|
||||
private CircularBuffer _buffer;
|
||||
|
||||
public Percentile(int period, double percent) : base()
|
||||
{
|
||||
if (period < 2)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 2 for percentile calculation.");
|
||||
}
|
||||
if (percent < 0 || percent > 100)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(percent), "Percent must be between 0 and 100.");
|
||||
}
|
||||
Period = period;
|
||||
Percent = percent;
|
||||
WarmupPeriod = 2;
|
||||
_buffer = new CircularBuffer(period);
|
||||
Name = $"Percentile(period={period}, percent={percent})";
|
||||
Init();
|
||||
}
|
||||
|
||||
public Percentile(object source, int period, double percent) : this(period, percent)
|
||||
{
|
||||
var pubEvent = source.GetType().GetEvent("Pub");
|
||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
base.Init();
|
||||
_buffer.Clear();
|
||||
}
|
||||
|
||||
protected override void ManageState(bool isNew)
|
||||
{
|
||||
if (isNew)
|
||||
{
|
||||
_lastValidValue = Input.Value;
|
||||
_index++;
|
||||
}
|
||||
}
|
||||
|
||||
protected override double Calculation()
|
||||
{
|
||||
ManageState(Input.IsNew);
|
||||
_buffer.Add(Input.Value, Input.IsNew);
|
||||
|
||||
double result;
|
||||
if (_buffer.Count >= Period)
|
||||
{
|
||||
var values = _buffer.GetSpan().ToArray();
|
||||
Array.Sort(values);
|
||||
|
||||
double position = (Percent / 100.0) * (values.Length - 1);
|
||||
int lowerIndex = (int)Math.Floor(position);
|
||||
int upperIndex = (int)Math.Ceiling(position);
|
||||
|
||||
if (lowerIndex == upperIndex)
|
||||
{
|
||||
result = values[lowerIndex];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Interpolate between the two nearest values
|
||||
double lowerValue = values[lowerIndex];
|
||||
double upperValue = values[upperIndex];
|
||||
double fraction = position - lowerIndex;
|
||||
result = lowerValue + (upperValue - lowerValue) * fraction;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use average for insufficient data, like the Median class
|
||||
result = _buffer.Average();
|
||||
}
|
||||
|
||||
IsHot = _buffer.Count >= Period;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
namespace QuanTAlib;
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
public class Skew : AbstractBase
|
||||
{
|
||||
public readonly int Period;
|
||||
private CircularBuffer _buffer;
|
||||
|
||||
public Skew(int period) : base()
|
||||
{
|
||||
if (period < 3)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 3 for skewness calculation.");
|
||||
}
|
||||
Period = period;
|
||||
WarmupPeriod = 3;
|
||||
_buffer = new CircularBuffer(period);
|
||||
Name = $"Skew(period={period})";
|
||||
Init();
|
||||
}
|
||||
|
||||
public Skew(object source, int period) : this(period)
|
||||
{
|
||||
var pubEvent = source.GetType().GetEvent("Pub");
|
||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
base.Init();
|
||||
_buffer.Clear();
|
||||
}
|
||||
|
||||
protected override void ManageState(bool isNew)
|
||||
{
|
||||
if (isNew)
|
||||
{
|
||||
_lastValidValue = Input.Value;
|
||||
_index++;
|
||||
}
|
||||
}
|
||||
|
||||
protected override double Calculation()
|
||||
{
|
||||
ManageState(Input.IsNew);
|
||||
|
||||
_buffer.Add(Input.Value, Input.IsNew);
|
||||
|
||||
double skew = 0;
|
||||
if (_buffer.Count >= 3) // We need at least 3 data points for skewness
|
||||
{
|
||||
var values = _buffer.GetSpan().ToArray();
|
||||
double mean = values.Average();
|
||||
double n = values.Length;
|
||||
|
||||
double sumCubedDeviations = 0;
|
||||
double sumSquaredDeviations = 0;
|
||||
|
||||
foreach (var value in values)
|
||||
{
|
||||
double deviation = value - mean;
|
||||
sumCubedDeviations += Math.Pow(deviation, 3);
|
||||
sumSquaredDeviations += Math.Pow(deviation, 2);
|
||||
}
|
||||
|
||||
// Calculate sample skewness using the adjusted Fisher-Pearson standardized moment coefficient
|
||||
double m3 = sumCubedDeviations / n;
|
||||
double m2 = sumSquaredDeviations / n;
|
||||
double s3 = Math.Pow(m2, 1.5);
|
||||
|
||||
if (s3 != 0) // Avoid division by zero
|
||||
{
|
||||
skew = (Math.Sqrt(n * (n - 1)) / (n - 2)) * (m3 / s3);
|
||||
}
|
||||
}
|
||||
|
||||
IsHot = _buffer.Count >= Period;
|
||||
return skew;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace QuanTAlib
|
||||
{
|
||||
public class Stddev : AbstractBase
|
||||
{
|
||||
public readonly int Period;
|
||||
public readonly bool IsPopulation;
|
||||
private CircularBuffer _buffer;
|
||||
|
||||
public Stddev(int period, bool isPopulation = false) : base()
|
||||
{
|
||||
if (period < 2)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 2.");
|
||||
}
|
||||
Period = period;
|
||||
IsPopulation = isPopulation;
|
||||
WarmupPeriod = 0;
|
||||
_buffer = new CircularBuffer(period);
|
||||
Name = $"Stddev(period={period}, population={isPopulation})";
|
||||
Init();
|
||||
}
|
||||
|
||||
public Stddev(object source, int period, bool isPopulation = false) : this(period, isPopulation)
|
||||
{
|
||||
var pubEvent = source.GetType().GetEvent("Pub");
|
||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
base.Init();
|
||||
_buffer.Clear();
|
||||
}
|
||||
|
||||
protected override void ManageState(bool isNew)
|
||||
{
|
||||
if (isNew)
|
||||
{
|
||||
_lastValidValue = Input.Value;
|
||||
_index++;
|
||||
}
|
||||
}
|
||||
|
||||
protected override double Calculation()
|
||||
{
|
||||
ManageState(Input.IsNew);
|
||||
|
||||
_buffer.Add(Input.Value, Input.IsNew);
|
||||
|
||||
double stddev = 0;
|
||||
if (_buffer.Count > 1)
|
||||
{
|
||||
var values = _buffer.GetSpan().ToArray();
|
||||
double mean = values.Average();
|
||||
double sumOfSquaredDifferences = values.Sum(x => Math.Pow(x - mean, 2));
|
||||
|
||||
double divisor = IsPopulation ? _buffer.Count : _buffer.Count - 1;
|
||||
double variance = sumOfSquaredDifferences / divisor;
|
||||
stddev = Math.Sqrt(variance);
|
||||
}
|
||||
|
||||
IsHot = true; // StdDev calc is valid from bar 1
|
||||
return stddev;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace QuanTAlib
|
||||
{
|
||||
public class Variance : AbstractBase
|
||||
{
|
||||
public readonly int Period;
|
||||
public readonly bool IsPopulation;
|
||||
private CircularBuffer _buffer;
|
||||
|
||||
public Variance(int period, bool isPopulation = false) : base()
|
||||
{
|
||||
if (period < 2)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 2.");
|
||||
}
|
||||
Period = period;
|
||||
IsPopulation = isPopulation;
|
||||
WarmupPeriod = 0;
|
||||
_buffer = new CircularBuffer(period);
|
||||
Name = $"Variance(period={period}, population={isPopulation})";
|
||||
Init();
|
||||
}
|
||||
|
||||
public Variance(object source, int period, bool isPopulation = false) : this(period, isPopulation)
|
||||
{
|
||||
var pubEvent = source.GetType().GetEvent("Pub");
|
||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
base.Init();
|
||||
_buffer.Clear();
|
||||
}
|
||||
|
||||
protected override void ManageState(bool isNew)
|
||||
{
|
||||
if (isNew)
|
||||
{
|
||||
_lastValidValue = Input.Value;
|
||||
_index++;
|
||||
}
|
||||
}
|
||||
|
||||
protected override double Calculation()
|
||||
{
|
||||
ManageState(Input.IsNew);
|
||||
|
||||
_buffer.Add(Input.Value, Input.IsNew);
|
||||
|
||||
double variance = 0;
|
||||
if (_buffer.Count > 1)
|
||||
{
|
||||
var values = _buffer.GetSpan().ToArray();
|
||||
double mean = values.Average();
|
||||
double sumOfSquaredDifferences = values.Sum(x => Math.Pow(x - mean, 2));
|
||||
|
||||
double divisor = IsPopulation ? _buffer.Count : _buffer.Count - 1;
|
||||
variance = sumOfSquaredDifferences / divisor;
|
||||
}
|
||||
|
||||
IsHot = true;
|
||||
return variance;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
namespace QuanTAlib;
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
public class Zscore : AbstractBase
|
||||
{
|
||||
public readonly int Period;
|
||||
private CircularBuffer _buffer;
|
||||
|
||||
public Zscore(int period) : base()
|
||||
{
|
||||
if (period < 2)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 2 for Z-score calculation.");
|
||||
}
|
||||
Period = period;
|
||||
WarmupPeriod = 2;
|
||||
_buffer = new CircularBuffer(period);
|
||||
Name = $"ZScore(period={period})";
|
||||
Init();
|
||||
}
|
||||
|
||||
public Zscore(object source, int period) : this(period)
|
||||
{
|
||||
var pubEvent = source.GetType().GetEvent("Pub");
|
||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
base.Init();
|
||||
_buffer.Clear();
|
||||
}
|
||||
|
||||
protected override void ManageState(bool isNew)
|
||||
{
|
||||
if (isNew)
|
||||
{
|
||||
_lastValidValue = Input.Value;
|
||||
_index++;
|
||||
}
|
||||
}
|
||||
|
||||
protected override double Calculation()
|
||||
{
|
||||
ManageState(Input.IsNew);
|
||||
|
||||
_buffer.Add(Input.Value, Input.IsNew);
|
||||
|
||||
double zScore = 0;
|
||||
if (_buffer.Count >= 2) // We need at least 2 data points for Z-score
|
||||
{
|
||||
var values = _buffer.GetSpan().ToArray();
|
||||
double mean = values.Average();
|
||||
double n = values.Length;
|
||||
|
||||
double sumSquaredDeviations = values.Sum(x => Math.Pow(x - mean, 2));
|
||||
double standardDeviation = Math.Sqrt(sumSquaredDeviations / (n - 1)); // Sample standard deviation
|
||||
|
||||
if (standardDeviation != 0) // Avoid division by zero
|
||||
{
|
||||
zScore = (Input.Value - mean) / standardDeviation;
|
||||
}
|
||||
}
|
||||
|
||||
IsHot = _buffer.Count >= Period;
|
||||
return zScore;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user