mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-24 21:48:03 +00:00
Merge branch 'dev'
This commit is contained in:
+24
-23
@@ -8,11 +8,12 @@ public class TradyTests
|
|||||||
{
|
{
|
||||||
private readonly TBarSeries bars;
|
private readonly TBarSeries bars;
|
||||||
private readonly GbmFeed feed;
|
private readonly GbmFeed feed;
|
||||||
private Random rnd;
|
private readonly Random rnd;
|
||||||
private readonly double range;
|
private readonly double range;
|
||||||
private int period, iterations;
|
private readonly int iterations;
|
||||||
private int skip;
|
private int period;
|
||||||
private IEnumerable<IOhlcv> Candles;
|
private readonly int skip;
|
||||||
|
private readonly IEnumerable<IOhlcv> Candles;
|
||||||
|
|
||||||
public TradyTests()
|
public TradyTests()
|
||||||
{
|
{
|
||||||
@@ -44,20 +45,20 @@ public class TradyTests
|
|||||||
foreach (TBar item in feed)
|
foreach (TBar item in feed)
|
||||||
{ QL.Add(ma.Calc(new TValue(item.Time, item.Close))); }
|
{ QL.Add(ma.Calc(new TValue(item.Time, item.Close))); }
|
||||||
|
|
||||||
var Trady = new SimpleMovingAverage(Candles, period)
|
var Trady = new SimpleMovingAverage(Candles, period)
|
||||||
.Compute()
|
.Compute()
|
||||||
.Select(result => new
|
.Select(result => new
|
||||||
{
|
{
|
||||||
Date = result.DateTime,
|
Date = result.DateTime,
|
||||||
Value = result.Tick.HasValue ? (double)result.Tick.Value : double.NaN
|
Value = result.Tick.HasValue ? (double)result.Tick.Value : double.NaN
|
||||||
})
|
})
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
Assert.Equal(QL.Length, Trady.Count);
|
Assert.Equal(QL.Length, Trady.Count);
|
||||||
for (int i = QL.Length - 1; i > skip; i--)
|
for (int i = QL.Length - 1; i > skip; i--)
|
||||||
{
|
{
|
||||||
double QL_item = QL[i].Value;
|
double QL_item = QL[i].Value;
|
||||||
double Tr_item = Trady[i].Value;
|
double Tr_item = Trady[i].Value;
|
||||||
Assert.InRange(Tr_item - QL_item, -range, range);
|
Assert.InRange(Tr_item - QL_item, -range, range);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -74,20 +75,20 @@ public class TradyTests
|
|||||||
foreach (TBar item in feed)
|
foreach (TBar item in feed)
|
||||||
{ QL.Add(ma.Calc(new TValue(item.Time, item.Close))); }
|
{ QL.Add(ma.Calc(new TValue(item.Time, item.Close))); }
|
||||||
|
|
||||||
var Trady = new ExponentialMovingAverage(Candles, period)
|
var Trady = new ExponentialMovingAverage(Candles, period)
|
||||||
.Compute()
|
.Compute()
|
||||||
.Select(result => new
|
.Select(result => new
|
||||||
{
|
{
|
||||||
Date = result.DateTime,
|
Date = result.DateTime,
|
||||||
Value = result.Tick.HasValue ? (double)result.Tick.Value : double.NaN
|
Value = result.Tick.HasValue ? (double)result.Tick.Value : double.NaN
|
||||||
})
|
})
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
Assert.Equal(QL.Length, Trady.Count);
|
Assert.Equal(QL.Length, Trady.Count);
|
||||||
for (int i = QL.Length - 1; i > skip*2; i--)
|
for (int i = QL.Length - 1; i > skip * 2; i--)
|
||||||
{
|
{
|
||||||
double QL_item = QL[i].Value;
|
double QL_item = QL[i].Value;
|
||||||
double Tr_item = Trady[i].Value;
|
double Tr_item = Trady[i].Value;
|
||||||
Assert.InRange(Tr_item - QL_item, -range, range);
|
Assert.InRange(Tr_item - QL_item, -range, range);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-9
@@ -6,12 +6,13 @@ public class TulipTests
|
|||||||
{
|
{
|
||||||
private readonly TBarSeries bars;
|
private readonly TBarSeries bars;
|
||||||
private readonly GbmFeed feed;
|
private readonly GbmFeed feed;
|
||||||
private Random rnd;
|
private readonly Random rnd;
|
||||||
private readonly double range;
|
private readonly double range;
|
||||||
private int period, iterations;
|
private int period;
|
||||||
|
private readonly int iterations;
|
||||||
private readonly double[] data;
|
private readonly double[] data;
|
||||||
private readonly double[] outdata;
|
private readonly double[] outdata;
|
||||||
private int skip;
|
private readonly int skip;
|
||||||
|
|
||||||
public TulipTests()
|
public TulipTests()
|
||||||
{
|
{
|
||||||
@@ -44,7 +45,7 @@ public class TulipTests
|
|||||||
for (int i = QL.Length - 1; i > skip; i--)
|
for (int i = QL.Length - 1; i > skip; i--)
|
||||||
{
|
{
|
||||||
double QL_item = QL[i].Value;
|
double QL_item = QL[i].Value;
|
||||||
double TU = i<period-1?double.NaN:arrout[0][i-period+1];
|
double TU = i < period - 1 ? double.NaN : arrout[0][i - period + 1];
|
||||||
Assert.InRange(TU - QL_item, -range, range);
|
Assert.InRange(TU - QL_item, -range, range);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -60,19 +61,18 @@ public class TulipTests
|
|||||||
Ema ma = new(period, useSma: false);
|
Ema ma = new(period, useSma: false);
|
||||||
TSeries QL = new();
|
TSeries QL = new();
|
||||||
foreach (TBar item in feed)
|
foreach (TBar item in feed)
|
||||||
{ QL.Add(ma.Calc(new TValue(item.Time, item.Close))); }
|
{ QL.Add(ma.Calc(new TValue(item.Time, item.Close))); }
|
||||||
|
|
||||||
double[][] arrin = [data];
|
double[][] arrin = [data];
|
||||||
double[][] arrout = [outdata];
|
double[][] arrout = [outdata];
|
||||||
Tulip.Indicators.ema.Run(inputs: arrin, options: [period], outputs: arrout);
|
Tulip.Indicators.ema.Run(inputs: arrin, options: [period], outputs: arrout);
|
||||||
|
|
||||||
Assert.Equal(QL.Length, arrout[0].Count());
|
Assert.Equal(QL.Length, arrout[0].Length);
|
||||||
for (int i = QL.Length - 1; i > skip*2; i--) //Initial Tulip Ema value is (wrongly) set to the first input value - therefore large skip
|
for (int i = QL.Length - 1; i > skip * 2; i--) //Initial Tulip Ema value is (wrongly) set to the first input value - therefore large skip
|
||||||
{
|
{
|
||||||
double QL_item = QL[i].Value;
|
double QL_item = QL[i].Value;
|
||||||
double TU = arrout[0][i];
|
double TU = arrout[0][i];
|
||||||
//Assert.InRange(TU - QL_item, -range, range);
|
Assert.True(Math.Abs(TU - QL_item) <= range, $"Assertion failed at index {i} for period {period}: TU = {TU}, QL_item = {QL_item}, delta = {TU - QL_item}");
|
||||||
Assert.True(Math.Abs(TU - QL_item) <= range, $"Assertion failed at index {i} for period {period}: TU = {TU}, QL_item = {QL_item}, delta = {TU-QL_item}");
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@ namespace QuanTAlib
|
|||||||
{
|
{
|
||||||
public class BarIndicatorTests
|
public class BarIndicatorTests
|
||||||
{
|
{
|
||||||
private Random rnd;
|
private readonly Random rnd;
|
||||||
private const int SeriesLen = 1000;
|
private const int SeriesLen = 1000;
|
||||||
private const int Corrections = 100;
|
private const int Corrections = 100;
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ namespace QuanTAlib
|
|||||||
{
|
{
|
||||||
public class IndicatorTests
|
public class IndicatorTests
|
||||||
{
|
{
|
||||||
private Random rnd;
|
private readonly Random rnd;
|
||||||
private const int SeriesLen = 1000;
|
private const int SeriesLen = 1000;
|
||||||
private const int Corrections = 100;
|
private const int Corrections = 100;
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ public class SkenderTests
|
|||||||
{
|
{
|
||||||
private readonly TBarSeries bars;
|
private readonly TBarSeries bars;
|
||||||
private readonly GbmFeed feed;
|
private readonly GbmFeed feed;
|
||||||
private Random rnd;
|
private readonly Random rnd;
|
||||||
private readonly double range;
|
private readonly double range;
|
||||||
private int period, iterations;
|
private int period, iterations;
|
||||||
private readonly IEnumerable<Quote> quotes;
|
private readonly IEnumerable<Quote> quotes;
|
||||||
|
|||||||
+10
-9
@@ -6,9 +6,10 @@ public class TAlibTests
|
|||||||
{
|
{
|
||||||
private readonly TBarSeries bars;
|
private readonly TBarSeries bars;
|
||||||
private readonly GbmFeed feed;
|
private readonly GbmFeed feed;
|
||||||
private Random rnd;
|
private readonly Random rnd;
|
||||||
private readonly double range;
|
private readonly double range;
|
||||||
private int period, iterations;
|
private int period;
|
||||||
|
private readonly int iterations;
|
||||||
private readonly double[] data;
|
private readonly double[] data;
|
||||||
private readonly double[] TALIB;
|
private readonly double[] TALIB;
|
||||||
|
|
||||||
@@ -77,8 +78,8 @@ public class TAlibTests
|
|||||||
foreach (TBar item in feed)
|
foreach (TBar item in feed)
|
||||||
{ QL.Add(ma.Calc(new TValue(item.Time, item.Close))); }
|
{ QL.Add(ma.Calc(new TValue(item.Time, item.Close))); }
|
||||||
Core.Dema(data, 0, QL.Length - 1, TALIB, out int outBegIdx, out _, period);
|
Core.Dema(data, 0, QL.Length - 1, TALIB, out int outBegIdx, out _, period);
|
||||||
Assert.Equal(QL.Length, TALIB.Count());
|
Assert.Equal(QL.Length, TALIB.Length);
|
||||||
for (int i = QL.Length - 1; i > period*20; i--)
|
for (int i = QL.Length - 1; i > period * 20; i--)
|
||||||
{
|
{
|
||||||
double TL = i < outBegIdx ? double.NaN : TALIB[i - outBegIdx];
|
double TL = i < outBegIdx ? double.NaN : TALIB[i - outBegIdx];
|
||||||
Assert.InRange(TALIB[i - outBegIdx] - QL[i].Value, -range, range);
|
Assert.InRange(TALIB[i - outBegIdx] - QL[i].Value, -range, range);
|
||||||
@@ -97,8 +98,8 @@ public class TAlibTests
|
|||||||
foreach (TBar item in feed)
|
foreach (TBar item in feed)
|
||||||
{ QL.Add(ma.Calc(new TValue(item.Time, item.Close))); }
|
{ QL.Add(ma.Calc(new TValue(item.Time, item.Close))); }
|
||||||
Core.Tema(data, 0, QL.Length - 1, TALIB, out int outBegIdx, out _, period);
|
Core.Tema(data, 0, QL.Length - 1, TALIB, out int outBegIdx, out _, period);
|
||||||
Assert.Equal(QL.Length, TALIB.Count());
|
Assert.Equal(QL.Length, TALIB.Length);
|
||||||
for (int i = QL.Length - 1; i > period*20; i--)
|
for (int i = QL.Length - 1; i > period * 20; i--)
|
||||||
{
|
{
|
||||||
double TL = i < outBegIdx ? double.NaN : TALIB[i - outBegIdx];
|
double TL = i < outBegIdx ? double.NaN : TALIB[i - outBegIdx];
|
||||||
Assert.InRange(TALIB[i - outBegIdx] - QL[i].Value, -range, range);
|
Assert.InRange(TALIB[i - outBegIdx] - QL[i].Value, -range, range);
|
||||||
@@ -139,9 +140,9 @@ public class TAlibTests
|
|||||||
TSeries QL = new();
|
TSeries QL = new();
|
||||||
foreach (TBar item in feed)
|
foreach (TBar item in feed)
|
||||||
{ QL.Add(ma.Calc(new TValue(item.Time, item.Close))); }
|
{ QL.Add(ma.Calc(new TValue(item.Time, item.Close))); }
|
||||||
Core.T3(data, 0, QL.Length - 1, TALIB, out int outBegIdx, out _, optInTimePeriod: period, optInVFactor: 0.7);
|
Core.T3(data, 0, QL.Length - 1, TALIB, out int outBegIdx, out _, optInTimePeriod: period, optInVFactor: 0.7);
|
||||||
Assert.Equal(QL.Length, TALIB.Count());
|
Assert.Equal(QL.Length, TALIB.Length);
|
||||||
for (int i = QL.Length - 1; i > period*20; i--)
|
for (int i = QL.Length - 1; i > period * 20; i--)
|
||||||
{
|
{
|
||||||
double TL = i < outBegIdx ? double.NaN : TALIB[i - outBegIdx];
|
double TL = i < outBegIdx ? double.NaN : TALIB[i - outBegIdx];
|
||||||
Assert.InRange(TALIB[i - outBegIdx] - QL[i].Value, -range, range);
|
Assert.InRange(TALIB[i - outBegIdx] - QL[i].Value, -range, range);
|
||||||
|
|||||||
@@ -0,0 +1,128 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace QuanTAlib
|
||||||
|
{
|
||||||
|
public class Curvature : AbstractBase
|
||||||
|
{
|
||||||
|
private readonly int _period;
|
||||||
|
private readonly Slope _slopeCalculator;
|
||||||
|
private readonly CircularBuffer _slopeBuffer;
|
||||||
|
|
||||||
|
public double? Intercept { get; private set; }
|
||||||
|
public double? StdDev { get; private set; }
|
||||||
|
public double? RSquared { get; private set; }
|
||||||
|
public double? Line { get; private set; }
|
||||||
|
|
||||||
|
public Curvature(int period)
|
||||||
|
{
|
||||||
|
if (period <= 2)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(period), period,
|
||||||
|
"Period must be greater than 2 for Curvature calculation.");
|
||||||
|
}
|
||||||
|
_period = period;
|
||||||
|
WarmupPeriod = period * 2 - 1; // We need this many points to get period number of slopes
|
||||||
|
_slopeCalculator = new Slope(period);
|
||||||
|
_slopeBuffer = new CircularBuffer(period);
|
||||||
|
Name = $"Curvature(period={period})";
|
||||||
|
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Curvature(object source, int period) : this(period)
|
||||||
|
{
|
||||||
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
|
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Init()
|
||||||
|
{
|
||||||
|
base.Init();
|
||||||
|
_slopeBuffer.Clear();
|
||||||
|
Intercept = null;
|
||||||
|
StdDev = null;
|
||||||
|
RSquared = null;
|
||||||
|
Line = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ManageState(bool isNew)
|
||||||
|
{
|
||||||
|
if (isNew)
|
||||||
|
{
|
||||||
|
_lastValidValue = Input.Value;
|
||||||
|
_index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override double Calculation()
|
||||||
|
{
|
||||||
|
ManageState(Input.IsNew);
|
||||||
|
|
||||||
|
// Calculate slope
|
||||||
|
var slopeResult = _slopeCalculator.Calc(Input);
|
||||||
|
_slopeBuffer.Add(slopeResult.Value, Input.IsNew);
|
||||||
|
|
||||||
|
double curvature = 0;
|
||||||
|
|
||||||
|
if (_slopeBuffer.Count < 2)
|
||||||
|
{
|
||||||
|
return curvature; // Return 0 when there are fewer than 2 slope points
|
||||||
|
}
|
||||||
|
|
||||||
|
int count = Math.Min(_slopeBuffer.Count, _period);
|
||||||
|
var slopes = _slopeBuffer.GetSpan().ToArray();
|
||||||
|
|
||||||
|
// Calculate averages
|
||||||
|
double sumX = 0, sumY = 0;
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
sumX += i + 1;
|
||||||
|
sumY += slopes[i];
|
||||||
|
}
|
||||||
|
double avgX = sumX / count;
|
||||||
|
double avgY = sumY / count;
|
||||||
|
|
||||||
|
// Least squares method
|
||||||
|
double sumSqX = 0, sumSqY = 0, sumSqXY = 0;
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
double devX = (i + 1) - avgX;
|
||||||
|
double devY = slopes[i] - avgY;
|
||||||
|
sumSqX += devX * devX;
|
||||||
|
sumSqY += devY * devY;
|
||||||
|
sumSqXY += devX * devY;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sumSqX > 0)
|
||||||
|
{
|
||||||
|
curvature = sumSqXY / sumSqX;
|
||||||
|
Intercept = avgY - (curvature * avgX);
|
||||||
|
|
||||||
|
// Calculate Standard Deviation and R-Squared
|
||||||
|
double stdDevX = Math.Sqrt(sumSqX / count);
|
||||||
|
double stdDevY = Math.Sqrt(sumSqY / count);
|
||||||
|
StdDev = stdDevY;
|
||||||
|
|
||||||
|
if (stdDevX * stdDevY != 0)
|
||||||
|
{
|
||||||
|
double r = sumSqXY / (stdDevX * stdDevY) / count;
|
||||||
|
RSquared = r * r;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate last Line value (y = mx + b)
|
||||||
|
Line = (curvature * count) + Intercept;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Intercept = null;
|
||||||
|
StdDev = null;
|
||||||
|
RSquared = null;
|
||||||
|
Line = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
IsHot = _slopeBuffer.Count == _period;
|
||||||
|
return curvature;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,8 +6,8 @@ using System.Linq;
|
|||||||
// Shannon's Entropy calculation
|
// Shannon's Entropy calculation
|
||||||
public class Entropy : AbstractBase
|
public class Entropy : AbstractBase
|
||||||
{
|
{
|
||||||
public readonly int Period;
|
private readonly int Period;
|
||||||
private CircularBuffer _buffer;
|
private readonly CircularBuffer _buffer;
|
||||||
|
|
||||||
public Entropy(int period) : base()
|
public Entropy(int period) : base()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ namespace QuanTAlib;
|
|||||||
// Excess kurtosis calculated with Sheskin Algorithm
|
// Excess kurtosis calculated with Sheskin Algorithm
|
||||||
public class Kurtosis : AbstractBase
|
public class Kurtosis : AbstractBase
|
||||||
{
|
{
|
||||||
public readonly int Period;
|
private readonly int Period;
|
||||||
private CircularBuffer _buffer;
|
private readonly CircularBuffer _buffer;
|
||||||
|
|
||||||
public Kurtosis(int period) : base()
|
public Kurtosis(int period) : base()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ namespace QuanTAlib
|
|||||||
{
|
{
|
||||||
public class Max : AbstractBase
|
public class Max : AbstractBase
|
||||||
{
|
{
|
||||||
public readonly int Period;
|
private readonly int Period;
|
||||||
private CircularBuffer _buffer;
|
private readonly CircularBuffer _buffer;
|
||||||
private readonly double _halfLife;
|
private readonly double _halfLife;
|
||||||
private double _currentMax, _p_currentMax;
|
private double _currentMax, _p_currentMax;
|
||||||
private int _timeSinceNewMax, _p_timeSinceNewMax;
|
private int _timeSinceNewMax, _p_timeSinceNewMax;
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ namespace QuanTAlib
|
|||||||
{
|
{
|
||||||
public class Median : AbstractBase
|
public class Median : AbstractBase
|
||||||
{
|
{
|
||||||
public readonly int Period;
|
private readonly int Period;
|
||||||
private CircularBuffer _buffer;
|
private readonly CircularBuffer _buffer;
|
||||||
|
|
||||||
public Median(int period) : base()
|
public Median(int period) : base()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ namespace QuanTAlib
|
|||||||
{
|
{
|
||||||
public class Min : AbstractBase
|
public class Min : AbstractBase
|
||||||
{
|
{
|
||||||
public readonly int Period;
|
private readonly int Period;
|
||||||
private CircularBuffer _buffer;
|
private readonly CircularBuffer _buffer;
|
||||||
private readonly double _halfLife;
|
private readonly double _halfLife;
|
||||||
private double _currentMin, _p_currentMin;
|
private double _currentMin, _p_currentMin;
|
||||||
private int _timeSinceNewMin, _p_timeSinceNewMin;
|
private int _timeSinceNewMin, _p_timeSinceNewMin;
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ namespace QuanTAlib;
|
|||||||
|
|
||||||
public class Mode : AbstractBase
|
public class Mode : AbstractBase
|
||||||
{
|
{
|
||||||
public readonly int Period;
|
private readonly int Period;
|
||||||
private CircularBuffer _buffer;
|
private readonly CircularBuffer _buffer;
|
||||||
|
|
||||||
public Mode(int period) : base()
|
public Mode(int period) : base()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ using System.Linq;
|
|||||||
|
|
||||||
public class Percentile : AbstractBase
|
public class Percentile : AbstractBase
|
||||||
{
|
{
|
||||||
public readonly int Period;
|
private readonly int Period;
|
||||||
public readonly double Percent;
|
private readonly double Percent;
|
||||||
private CircularBuffer _buffer;
|
private readonly CircularBuffer _buffer;
|
||||||
|
|
||||||
public Percentile(int period, double percent) : base()
|
public Percentile(int period, double percent) : base()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ using System.Linq;
|
|||||||
|
|
||||||
public class Skew : AbstractBase
|
public class Skew : AbstractBase
|
||||||
{
|
{
|
||||||
public readonly int Period;
|
private readonly int Period;
|
||||||
private CircularBuffer _buffer;
|
private readonly CircularBuffer _buffer;
|
||||||
|
|
||||||
public Skew(int period) : base()
|
public Skew(int period) : base()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,128 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace QuanTAlib
|
||||||
|
{
|
||||||
|
public class Slope : AbstractBase
|
||||||
|
{
|
||||||
|
private readonly int _period;
|
||||||
|
private readonly CircularBuffer _buffer;
|
||||||
|
private readonly CircularBuffer _timeBuffer;
|
||||||
|
|
||||||
|
public double? Intercept { get; private set; }
|
||||||
|
public double? StdDev { get; private set; }
|
||||||
|
public double? RSquared { get; private set; }
|
||||||
|
public double? Line { get; private set; }
|
||||||
|
|
||||||
|
public Slope(int period)
|
||||||
|
{
|
||||||
|
if (period <= 1)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(period), period,
|
||||||
|
"Period must be greater than 1 for Slope/Linear Regression.");
|
||||||
|
}
|
||||||
|
_period = period;
|
||||||
|
WarmupPeriod = period;
|
||||||
|
_buffer = new CircularBuffer(period);
|
||||||
|
_timeBuffer = new CircularBuffer(period);
|
||||||
|
Name = $"Slope(period={period})";
|
||||||
|
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Slope(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();
|
||||||
|
_timeBuffer.Clear();
|
||||||
|
Intercept = null;
|
||||||
|
StdDev = null;
|
||||||
|
RSquared = null;
|
||||||
|
Line = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
_timeBuffer.Add(Input.Time.Ticks, Input.IsNew);
|
||||||
|
|
||||||
|
double slope = 0;
|
||||||
|
|
||||||
|
if (_buffer.Count < 2)
|
||||||
|
{
|
||||||
|
return slope; // Return 0 when there are fewer than 2 points
|
||||||
|
}
|
||||||
|
|
||||||
|
int count = Math.Min(_buffer.Count, _period);
|
||||||
|
var values = _buffer.GetSpan().ToArray();
|
||||||
|
|
||||||
|
// Calculate averages
|
||||||
|
double sumX = 0, sumY = 0;
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
sumX += i + 1;
|
||||||
|
sumY += values[i];
|
||||||
|
}
|
||||||
|
double avgX = sumX / count;
|
||||||
|
double avgY = sumY / count;
|
||||||
|
|
||||||
|
// Least squares method
|
||||||
|
double sumSqX = 0, sumSqY = 0, sumSqXY = 0;
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
double devX = (i + 1) - avgX;
|
||||||
|
double devY = values[i] - avgY;
|
||||||
|
sumSqX += devX * devX;
|
||||||
|
sumSqY += devY * devY;
|
||||||
|
sumSqXY += devX * devY;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sumSqX > 0)
|
||||||
|
{
|
||||||
|
slope = sumSqXY / sumSqX;
|
||||||
|
Intercept = avgY - (slope * avgX);
|
||||||
|
|
||||||
|
// Calculate Standard Deviation and R-Squared
|
||||||
|
double stdDevX = Math.Sqrt(sumSqX / count);
|
||||||
|
double stdDevY = Math.Sqrt(sumSqY / count);
|
||||||
|
StdDev = stdDevY;
|
||||||
|
|
||||||
|
if (stdDevX * stdDevY != 0)
|
||||||
|
{
|
||||||
|
double r = sumSqXY / (stdDevX * stdDevY) / count;
|
||||||
|
RSquared = r * r;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate last Line value (y = mx + b)
|
||||||
|
Line = (slope * count) + Intercept;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Intercept = null;
|
||||||
|
StdDev = null;
|
||||||
|
RSquared = null;
|
||||||
|
Line = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
IsHot = _buffer.Count == _period;
|
||||||
|
return slope;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,9 +5,9 @@ namespace QuanTAlib
|
|||||||
{
|
{
|
||||||
public class Stddev : AbstractBase
|
public class Stddev : AbstractBase
|
||||||
{
|
{
|
||||||
public readonly int Period;
|
private readonly int Period;
|
||||||
public readonly bool IsPopulation;
|
private readonly bool IsPopulation;
|
||||||
private CircularBuffer _buffer;
|
private readonly CircularBuffer _buffer;
|
||||||
|
|
||||||
public Stddev(int period, bool isPopulation = false) : base()
|
public Stddev(int period, bool isPopulation = false) : base()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ namespace QuanTAlib
|
|||||||
{
|
{
|
||||||
public class Variance : AbstractBase
|
public class Variance : AbstractBase
|
||||||
{
|
{
|
||||||
public readonly int Period;
|
private readonly int Period;
|
||||||
public readonly bool IsPopulation;
|
private readonly bool IsPopulation;
|
||||||
private CircularBuffer _buffer;
|
private readonly CircularBuffer _buffer;
|
||||||
|
|
||||||
public Variance(int period, bool isPopulation = false) : base()
|
public Variance(int period, bool isPopulation = false) : base()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ using System.Linq;
|
|||||||
|
|
||||||
public class Zscore : AbstractBase
|
public class Zscore : AbstractBase
|
||||||
{
|
{
|
||||||
public readonly int Period;
|
private readonly int Period;
|
||||||
private CircularBuffer _buffer;
|
private readonly CircularBuffer _buffer;
|
||||||
|
|
||||||
public Zscore(int period) : base()
|
public Zscore(int period) : base()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
using TradingPlatform.BusinessLayer;
|
||||||
|
namespace QuanTAlib;
|
||||||
|
|
||||||
|
public class CurvatureIndicator : IndicatorBase
|
||||||
|
{
|
||||||
|
[InputParameter("Period", sortIndex: 1, 2, 2000, 1, 0)]
|
||||||
|
public int Period { get; set; } = 20;
|
||||||
|
|
||||||
|
private Curvature? curvature;
|
||||||
|
protected override AbstractBase QuanTAlib => curvature!;
|
||||||
|
public override string ShortName => $"CURVATURE {Period} : {SourceName}";
|
||||||
|
|
||||||
|
public CurvatureIndicator()
|
||||||
|
{
|
||||||
|
Name = "CURVATURE - Rate of Change of Slope";
|
||||||
|
SeparateWindow = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void InitIndicator()
|
||||||
|
{
|
||||||
|
curvature = new(Period);
|
||||||
|
MinHistoryDepths = curvature.WarmupPeriod;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
using TradingPlatform.BusinessLayer;
|
||||||
|
namespace QuanTAlib;
|
||||||
|
|
||||||
|
public class SlopeIndicator : IndicatorBase
|
||||||
|
{
|
||||||
|
[InputParameter("Period", sortIndex: 1, 2, 2000, 1, 0)]
|
||||||
|
public int Period { get; set; } = 20;
|
||||||
|
|
||||||
|
private Slope? slope;
|
||||||
|
protected override AbstractBase QuanTAlib => slope!;
|
||||||
|
public override string ShortName => $"SLOPE {Period} : {SourceName}";
|
||||||
|
|
||||||
|
public SlopeIndicator()
|
||||||
|
{
|
||||||
|
Name = "SLOPE - Trend Slope";
|
||||||
|
SeparateWindow = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void InitIndicator()
|
||||||
|
{
|
||||||
|
slope = new(Period);
|
||||||
|
MinHistoryDepths = slope.WarmupPeriod;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user