mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-07-28 01:37:43 +00:00
Slope and Curvature
This commit is contained in:
+22
-21
@@ -10,9 +10,10 @@ public class TradyTests
|
||||
private readonly GbmFeed feed;
|
||||
private readonly Random rnd;
|
||||
private readonly double range;
|
||||
private readonly int period, iterations;
|
||||
private readonly int iterations;
|
||||
private int period;
|
||||
private readonly int skip;
|
||||
private readonly IEnumerable<IOhlcv> Candles;
|
||||
private readonly IEnumerable<IOhlcv> Candles;
|
||||
|
||||
public TradyTests()
|
||||
{
|
||||
@@ -44,20 +45,20 @@ public class TradyTests
|
||||
foreach (TBar item in feed)
|
||||
{ QL.Add(ma.Calc(new TValue(item.Time, item.Close))); }
|
||||
|
||||
var Trady = new SimpleMovingAverage(Candles, period)
|
||||
.Compute()
|
||||
.Select(result => new
|
||||
{
|
||||
Date = result.DateTime,
|
||||
Value = result.Tick.HasValue ? (double)result.Tick.Value : double.NaN
|
||||
})
|
||||
.ToList();
|
||||
var Trady = new SimpleMovingAverage(Candles, period)
|
||||
.Compute()
|
||||
.Select(result => new
|
||||
{
|
||||
Date = result.DateTime,
|
||||
Value = result.Tick.HasValue ? (double)result.Tick.Value : double.NaN
|
||||
})
|
||||
.ToList();
|
||||
|
||||
Assert.Equal(QL.Length, Trady.Count);
|
||||
for (int i = QL.Length - 1; i > skip; i--)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -74,20 +75,20 @@ public class TradyTests
|
||||
foreach (TBar item in feed)
|
||||
{ QL.Add(ma.Calc(new TValue(item.Time, item.Close))); }
|
||||
|
||||
var Trady = new ExponentialMovingAverage(Candles, period)
|
||||
.Compute()
|
||||
.Select(result => new
|
||||
{
|
||||
Date = result.DateTime,
|
||||
Value = result.Tick.HasValue ? (double)result.Tick.Value : double.NaN
|
||||
})
|
||||
.ToList();
|
||||
var Trady = new ExponentialMovingAverage(Candles, period)
|
||||
.Compute()
|
||||
.Select(result => new
|
||||
{
|
||||
Date = result.DateTime,
|
||||
Value = result.Tick.HasValue ? (double)result.Tick.Value : double.NaN
|
||||
})
|
||||
.ToList();
|
||||
|
||||
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 Tr_item = Trady[i].Value;
|
||||
double Tr_item = Trady[i].Value;
|
||||
Assert.InRange(Tr_item - QL_item, -range, range);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@ public class TulipTests
|
||||
private readonly Random rnd;
|
||||
private readonly double range;
|
||||
private int period;
|
||||
private int readonly iterations;
|
||||
private readonly int iterations;
|
||||
private readonly double[] data;
|
||||
private readonly double[] outdata;
|
||||
private readonly int skip;
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@ public class TAlibTests
|
||||
private readonly Random rnd;
|
||||
private readonly double range;
|
||||
private int period;
|
||||
private int readonly iterations;
|
||||
private readonly int iterations;
|
||||
private readonly double[] data;
|
||||
private readonly double[] TALIB;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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