Merge branch 'dev'

This commit is contained in:
Miha Kralj
2024-11-08 10:11:20 -08:00
114 changed files with 2951 additions and 1294 deletions
+2 -2
View File
@@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<LangVersion>preview</LangVersion>
<NoWarn>$(NoWarn);NU1903;NU5104</NoWarn>
<NoWarn>$(NoWarn);NU1903;NU5104;NETSDK1057</NoWarn>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Deterministic>true</Deterministic>
@@ -41,7 +41,7 @@
<InvariantGlobalization>true</InvariantGlobalization>
<MetadataUpdaterSupport>false</MetadataUpdaterSupport>
<UseSystemResourceKeys>true</UseSystemResourceKeys>
</PropertyGroup>
<PropertyGroup>
+33 -6
View File
@@ -12,6 +12,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Averages", "quantower\Avera
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volatility", "quantower\Volatility\_Volatility.csproj", "{B7DC44F7-D3A3-4C70-9025-513E0182B646}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Oscillators", "quantower\Oscillators\_Oscillators.csproj", "{C4D8F5D0-E6A7-4B7D-B8E9-F55C3F8D9D01}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volume", "quantower\Volume\_Volume.csproj", "{D5E9F6D1-B8A8-4C7E-9FA0-F66C3F8D9D02}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Momentum", "quantower\Momentum\_Momentum.csproj", "{E6F0F7D2-C9B9-4D8F-0FA1-F77C4F9D9D03}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Experiments", "quantower\Experiments\_Experiments.csproj", "{F7F1F8D3-DAC0-4E9F-1FB2-F88D5F0E0E04}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SyntheticVendor", "SyntheticVendor\SyntheticVendor.csproj", "{1CF111D9-33E6-4A11-8FEC-F23300A78D15}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{2D97C971-20BF-40DB-94AA-3279F787D3CB}"
@@ -40,12 +48,27 @@ Global
{B7DC44F7-D3A3-4C70-9025-513E0182B646}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B7DC44F7-D3A3-4C70-9025-513E0182B646}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B7DC44F7-D3A3-4C70-9025-513E0182B646}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B7DC44F7-D3A3-4C70-9025-513E0182B646}.Release | Any CPU.ActiveCfg = Release | Any CPU
{B7DC44F7-D3A3-4C70-9025-513E0182B646}.Release | Any CPU.Build.0 = Release | Any CPU
{1CF111D9-33E6-4A11-8FEC-F23300A78D15}.Debug | Any CPU.ActiveCfg = Debug | Any CPU
{1CF111D9-33E6-4A11-8FEC-F23300A78D15}.Debug | Any CPU.Build.0 = Debug | Any CPU
{1CF111D9-33E6-4A11-8FEC-F23300A78D15}.Release | Any CPU.ActiveCfg = Release | Any CPU
{1CF111D9-33E6-4A11-8FEC-F23300A78D15}.Release | Any CPU.Build.0 = Release | Any CPU
{B7DC44F7-D3A3-4C70-9025-513E0182B646}.Release|Any CPU.Build.0 = Release|Any CPU
{C4D8F5D0-E6A7-4B7D-B8E9-F55C3F8D9D01}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C4D8F5D0-E6A7-4B7D-B8E9-F55C3F8D9D01}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C4D8F5D0-E6A7-4B7D-B8E9-F55C3F8D9D01}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C4D8F5D0-E6A7-4B7D-B8E9-F55C3F8D9D01}.Release|Any CPU.Build.0 = Release|Any CPU
{D5E9F6D1-B8A8-4C7E-9FA0-F66C3F8D9D02}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D5E9F6D1-B8A8-4C7E-9FA0-F66C3F8D9D02}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D5E9F6D1-B8A8-4C7E-9FA0-F66C3F8D9D02}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D5E9F6D1-B8A8-4C7E-9FA0-F66C3F8D9D02}.Release|Any CPU.Build.0 = Release|Any CPU
{E6F0F7D2-C9B9-4D8F-0FA1-F77C4F9D9D03}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E6F0F7D2-C9B9-4D8F-0FA1-F77C4F9D9D03}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E6F0F7D2-C9B9-4D8F-0FA1-F77C4F9D9D03}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E6F0F7D2-C9B9-4D8F-0FA1-F77C4F9D9D03}.Release|Any CPU.Build.0 = Release|Any CPU
{F7F1F8D3-DAC0-4E9F-1FB2-F88D5F0E0E04}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F7F1F8D3-DAC0-4E9F-1FB2-F88D5F0E0E04}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F7F1F8D3-DAC0-4E9F-1FB2-F88D5F0E0E04}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F7F1F8D3-DAC0-4E9F-1FB2-F88D5F0E0E04}.Release|Any CPU.Build.0 = Release|Any CPU
{1CF111D9-33E6-4A11-8FEC-F23300A78D15}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1CF111D9-33E6-4A11-8FEC-F23300A78D15}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1CF111D9-33E6-4A11-8FEC-F23300A78D15}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1CF111D9-33E6-4A11-8FEC-F23300A78D15}.Release|Any CPU.Build.0 = Release|Any CPU
{2D97C971-20BF-40DB-94AA-3279F787D3CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2D97C971-20BF-40DB-94AA-3279F787D3CB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2D97C971-20BF-40DB-94AA-3279F787D3CB}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -55,5 +78,9 @@ Global
{2E9427C7-144F-488E-A29D-789ACC1C32AE} = {1B9AC248-76F8-44DD-958D-F1DC08EE1E87}
{6BE10C39-4127-446C-818B-7976FCDD51D5} = {1B9AC248-76F8-44DD-958D-F1DC08EE1E87}
{B7DC44F7-D3A3-4C70-9025-513E0182B646} = {1B9AC248-76F8-44DD-958D-F1DC08EE1E87}
{C4D8F5D0-E6A7-4B7D-B8E9-F55C3F8D9D01} = {1B9AC248-76F8-44DD-958D-F1DC08EE1E87}
{D5E9F6D1-B8A8-4C7E-9FA0-F66C3F8D9D02} = {1B9AC248-76F8-44DD-958D-F1DC08EE1E87}
{E6F0F7D2-C9B9-4D8F-0FA1-F77C4F9D9D03} = {1B9AC248-76F8-44DD-958D-F1DC08EE1E87}
{F7F1F8D3-DAC0-4E9F-1FB2-F88D5F0E0E04} = {1B9AC248-76F8-44DD-958D-F1DC08EE1E87}
EndGlobalSection
EndGlobal
+4
View File
@@ -46,6 +46,10 @@
<ProjectReference Include="..\quantower\Volatility\_Volatility.csproj" Aliases="volatility" />
<ProjectReference Include="..\quantower\Averages\_Averages.csproj" Aliases="averages" />
<ProjectReference Include="..\quantower\Statistics\_Statistics.csproj" Aliases="statistics" />
<ProjectReference Include="..\quantower\Momentum\_Momentum.csproj" Aliases="momentum" />
<ProjectReference Include="..\quantower\Oscillators\_Oscillators.csproj" Aliases="oscillators" />
<ProjectReference Include="..\quantower\Volume\_Volume.csproj" Aliases="volume" />
<ProjectReference Include="..\quantower\Experiments\_Experiments.csproj" Aliases="experiments" />
</ItemGroup>
</Project>
+91
View File
@@ -0,0 +1,91 @@
using Xunit;
using System.Security.Cryptography;
namespace QuanTAlib.Tests;
public abstract class UpdateTestBase
{
protected readonly RandomNumberGenerator rng = RandomNumberGenerator.Create();
protected const int RandomUpdates = 100;
protected const double ReferenceValue = 100.0;
protected const int precision = 8;
protected double GetRandomDouble()
{
byte[] bytes = new byte[8];
rng.GetBytes(bytes);
return ((double)BitConverter.ToUInt64(bytes, 0) / ulong.MaxValue * 200) - 100; // Range: -100 to 100
}
protected TBar GetRandomBar(bool IsNew)
{
double open = GetRandomDouble();
double high = open + Math.Abs(GetRandomDouble());
double low = open - Math.Abs(GetRandomDouble());
double close = low + ((high - low) * GetRandomDouble());
return new TBar(DateTime.Now, open, high, low, close, 1000, IsNew);
}
protected void TestTValueUpdate<T>(T indicator, Func<TValue, TValue> calc) where T : class
{
var initialValue = calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true));
for (int i = 0; i < RandomUpdates; i++)
{
calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false));
}
var finalValue = calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false));
Assert.Equal(initialValue.Value, finalValue.Value, precision);
}
protected void TestTBarUpdate<T>(T indicator, Func<TBar, TValue> calc) where T : class
{
TBar r = GetRandomBar(true);
var initialValue = calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
calc(GetRandomBar(IsNew: false));
}
var finalValue = calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue.Value, finalValue.Value, precision);
}
protected void TestDualTValueUpdate<T>(T indicator, Func<TValue, TValue, TValue> calc) where T : class
{
var initialValue = calc(
new TValue(DateTime.Now, ReferenceValue, IsNew: true),
new TValue(DateTime.Now, ReferenceValue, IsNew: true));
for (int i = 0; i < RandomUpdates; i++)
{
calc(
new TValue(DateTime.Now, GetRandomDouble(), IsNew: false),
new TValue(DateTime.Now, GetRandomDouble(), IsNew: false));
}
var finalValue = calc(
new TValue(DateTime.Now, ReferenceValue, IsNew: false),
new TValue(DateTime.Now, ReferenceValue, IsNew: false));
Assert.Equal(initialValue.Value, finalValue.Value, precision);
}
protected void TestDualTBarUpdate<T>(T indicator, Func<TBar, TBar, TValue> calc) where T : class
{
TBar bar1 = GetRandomBar(true);
TBar bar2 = GetRandomBar(true);
var initialValue = calc(bar1, bar2);
for (int i = 0; i < RandomUpdates; i++)
{
calc(GetRandomBar(false), GetRandomBar(false));
}
var finalValue = calc(
new TBar(bar1.Time, bar1.Open, bar1.High, bar1.Low, bar1.Close, bar1.Volume, false),
new TBar(bar2.Time, bar2.Open, bar2.High, bar2.Low, bar2.Close, bar2.Volume, false));
Assert.Equal(initialValue.Value, finalValue.Value, precision);
}
}
+166 -157
View File
@@ -1,170 +1,114 @@
using Xunit;
using System.Security.Cryptography;
using System.Reflection;
#pragma warning disable S1944, S2053, S2222, S2259, S2583, S2589, S3329, S3655, S3900, S3949, S3966, S4158, S4347, S5773, S6781
namespace QuanTAlib;
namespace QuanTAlib.Tests;
public class EventingTests
{
[Fact]
public void EventBasedCalculations()
private const int TestDataPoints = 200;
private const int DefaultPeriod = 10;
private const double Tolerance = 1e-9;
private static readonly (string Name, object[] DirectParams, object[] EventParams)[] ValueIndicators = new[]
{
// Create a cryptographically secure random number generator
using var rng = RandomNumberGenerator.Create();
("Afirma", new object[] { DefaultPeriod, DefaultPeriod, Afirma.WindowType.BlackmanHarris }, new object[] { new TSeries(), DefaultPeriod, DefaultPeriod, Afirma.WindowType.BlackmanHarris }),
("Alma", new object[] { DefaultPeriod, 0.85, 6.0 }, new object[] { new TSeries(), DefaultPeriod, 0.85, 6.0 }),
("Convolution", new object[] { new double[] {1,2,3,2,1} }, new object[] { new TSeries(), new double[] {1,2,3,2,1} }),
("Dema", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
("Dsma", new object[] { DefaultPeriod, 0.9 }, new object[] { new TSeries(), DefaultPeriod, 0.9 }),
("Dwma", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
("Ema", new object[] { DefaultPeriod, true }, new object[] { new TSeries(), DefaultPeriod, true }),
("Epma", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
("Pwma", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
("Fisher", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
("Frama", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
("Fwma", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
("Gma", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
("Hma", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
("Htit", System.Array.Empty<object>(), new object[] { new TSeries() }),
("Hwma", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
("Jma", new object[] { DefaultPeriod, 0, 0.45, 10 }, new object[] { new TSeries(), DefaultPeriod, 0, 0.45, 10 }),
("Kama", new object[] { DefaultPeriod, 2, 30 }, new object[] { new TSeries(), DefaultPeriod, 2, 30 }),
("Ltma", new object[] { 0.2 }, new object[] { new TSeries(), 0.2 }),
("Maaf", new object[] { 39, 0.002 }, new object[] { new TSeries(), 39, 0.002 }),
("Mama", new object[] { 0.5, 0.05 }, new object[] { new TSeries(), 0.5, 0.05 }),
("Mgdi", new object[] { DefaultPeriod, 0.6 }, new object[] { new TSeries(), DefaultPeriod, 0.6 }),
("Mma", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
("Qema", new object[] { 0.2, 0.2, 0.2, 0.2 }, new object[] { new TSeries(), 0.2, 0.2, 0.2, 0.2 }),
("Rema", new object[] { DefaultPeriod, 0.5 }, new object[] { new TSeries(), DefaultPeriod, 0.5 }),
("Rma", new object[] { DefaultPeriod, true }, new object[] { new TSeries(), DefaultPeriod, true }),
("Sma", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
("Wma", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
("Tema", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
("Zlema", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
("Sinema", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
("Smma", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
("T3", new object[] { DefaultPeriod, 0.7, true }, new object[] { new TSeries(), DefaultPeriod, 0.7, true }),
("Trima", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
("Vidya", new object[] { DefaultPeriod, 0, 0.2 }, new object[] { new TSeries(), DefaultPeriod, 0, 0.2 }),
("Apo", new object[] { 12, 26 }, new object[] { new TSeries(), 12, 26 }),
("Macd", new object[] { 12, 26, 9 }, new object[] { new TSeries(), 12, 26, 9 }),
("Rsi", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
("Rsx", new object[] { DefaultPeriod, 0, 0.55 }, new object[] { new TSeries(), DefaultPeriod, 0, 0.55 }),
("Cmo", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
("Cog", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
("Curvature", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
("Entropy", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
("Kurtosis", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
("Max", new object[] { DefaultPeriod, 0.0 }, new object[] { new TSeries(), DefaultPeriod, 0.0 }),
("Median", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
("Min", new object[] { DefaultPeriod, 0.0 }, new object[] { new TSeries(), DefaultPeriod, 0.0 }),
("Mode", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
("Percentile", new object[] { DefaultPeriod, 0.5 }, new object[] { new TSeries(), DefaultPeriod, 0.5 }),
("Skew", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
("Slope", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
("Stddev", new object[] { DefaultPeriod, false }, new object[] { new TSeries(), DefaultPeriod, false }),
("Variance", new object[] { DefaultPeriod, false }, new object[] { new TSeries(), DefaultPeriod, false }),
("Zscore", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
("Beta", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
("Corr", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
("Hv", new object[] { DefaultPeriod, false }, new object[] { new TSeries(), DefaultPeriod, false }),
("Jvolty", new object[] { DefaultPeriod, 0 }, new object[] { new TSeries(), DefaultPeriod, 0 }),
("Rv", new object[] { DefaultPeriod, false }, new object[] { new TSeries(), DefaultPeriod, false }),
("Rvi", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
("Mae", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
("Mapd", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
("Mape", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
("Mase", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
("Mda", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
("Me", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
("Mpe", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
("Mse", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
("Msle", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
("Rae", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
("Rmse", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
("Rmsle", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
("Rse", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
("Smape", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
("Rsquared", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
("Huber", new object[] { DefaultPeriod, 1.0 }, new object[] { new TSeries(), DefaultPeriod, 1.0 }),
("Cti", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod })
};
// Create input series to hold our random values
var input = new TSeries();
var barInput = new TBarSeries();
int p = 10;
private static readonly (string Name, object[] DirectParams, object[] EventParams)[] BarIndicators = new[]
{
("Adl", System.Array.Empty<object>(), new object[] { new TBarSeries() }),
("Adosc", new object[] { 3, 10 }, new object[] { new TBarSeries(), 3, 10 }),
("Aobv", System.Array.Empty<object>(), new object[] { new TBarSeries() }),
("Cmf", new object[] { 20 }, new object[] { new TBarSeries(), 20 }),
("Eom", new object[] { 14 }, new object[] { new TBarSeries(), 14 }),
("Kvo", new object[] { 34, 55 }, new object[] { new TBarSeries(), 34, 55 }),
("Atr", new object[] { 14 }, new object[] { new TBarSeries(), 14 }),
("Chop", new object[] { 14 }, new object[] { new TBarSeries(), 14 }),
("Dosc", System.Array.Empty<object>(), new object[] { new TBarSeries() })
};
// Create a list of value-based indicator pairs
var valueIndicators = new List<(string Name, AbstractBase Direct, AbstractBase EventBased)>
{
("Afirma", new Afirma(p,p,Afirma.WindowType.BlackmanHarris), new Afirma(input, p,p,Afirma.WindowType.BlackmanHarris)),
("Alma", new Alma(p), new Alma(input, p)),
("Convolution", new Convolution(new double[] {1,2,3,2,1}), new Convolution(input, new double[] {1,2,3,2,1})),
("Dema", new Dema(p), new Dema(input, p)),
("Dsma", new Dsma(p), new Dsma(input, p)),
("Dwma", new Dwma(p), new Dwma(input, p)),
("Ema", new Ema(p), new Ema(input, p)),
("Epma", new Epma(p), new Epma(input, p)),
("Pwma", new Pwma(p), new Pwma(input, p)),
("Frama", new Frama(p), new Frama(input, p)),
("Fwma", new Fwma(p), new Fwma(input, p)),
("Gma", new Gma(p), new Gma(input, p)),
("Hma", new Hma(p), new Hma(input, p)),
("Htit", new Htit(), new Htit(input)),
("Hwma", new Hwma(p), new Hwma(input, p)),
("Jma", new Jma(p), new Jma(input, p)),
("Kama", new Kama(p), new Kama(input, p)),
("Ltma", new Ltma(gamma: 0.2), new Ltma(input, gamma: 0.2)),
("Maaf", new Maaf(p), new Maaf(input, p)),
("Mama", new Mama(p), new Mama(input, p)),
("Mgdi", new Mgdi(p, kFactor: 0.6), new Mgdi(input, p, kFactor: 0.6)),
("Mma", new Mma(p), new Mma(input, p)),
("Qema", new Qema(k1: 0.2, k2: 0.2, k3: 0.2, k4: 0.2), new Qema(input, k1: 0.2, k2: 0.2, k3: 0.2, k4: 0.2)),
("Rema", new Rema(p), new Rema(input, p)),
("Rma", new Rma(p), new Rma(input, p)),
("Sma", new Sma(p), new Sma(input, p)),
("Wma", new Wma(p), new Wma(input, p)),
("Rma", new Rma(p), new Rma(input, p)),
("Tema", new Tema(p), new Tema(input, p)),
("Kama", new Kama(2, 30, 6), new Kama(input, 2, 30, 6)),
("Zlema", new Zlema(p), new Zlema(input, p)),
("Sinema", new Sinema(p), new Sinema(input, p)),
("Smma", new Smma(p), new Smma(input, p)),
("T3", new T3(p), new T3(input, p)),
("Trima", new Trima(p), new Trima(input, p)),
("Vidya", new Vidya(p), new Vidya(input, p)),
("Apo", new Apo(12, 26), new Apo(input, 12, 26)),
("Macd", new Macd(12, 26, 9), new Macd(input, 12, 26, 9)),
("Rsi", new Rsi(p), new Rsi(input, p)),
("Rsx", new Rsx(p), new Rsx(input, p)),
("Cmo", new Cmo(p), new Cmo(input, p)),
("Cog", new Cog(p), new Cog(input, p)),
("Curvature", new Curvature(p), new Curvature(input, p)),
("Entropy", new Entropy(p), new Entropy(input, p)),
("Kurtosis", new Kurtosis(p), new Kurtosis(input, p)),
("Max", new Max(p), new Max(input, p)),
("Median", new Median(p), new Median(input, p)),
("Min", new Min(p), new Min(input, p)),
("Mode", new Mode(p), new Mode(input, p)),
("Percentile", new Percentile(p, 0.5), new Percentile(input, p, 0.5)),
("Skew", new Skew(p), new Skew(input, p)),
("Slope", new Slope(p), new Slope(input, p)),
("Stddev", new Stddev(p), new Stddev(input, p)),
("Variance", new Variance(p), new Variance(input, p)),
("Zscore", new Zscore(p), new Zscore(input, p)),
// Volatility indicators (value-based)
("Hv", new Hv(p), new Hv(input, p)),
("Jvolty", new Jvolty(p), new Jvolty(input, p)),
("Rv", new Rv(p), new Rv(input, p)),
("Rvi", new Rvi(p), new Rvi(input, p)),
// Error classes
("Mae", new Mae(p), new Mae(input, p)),
("Mapd", new Mapd(p), new Mapd(input, p)),
("Mape", new Mape(p), new Mape(input, p)),
("Mase", new Mase(p), new Mase(input, p)),
("Mda", new Mda(p), new Mda(input, p)),
("Me", new Me(p), new Me(input, p)),
("Mpe", new Mpe(p), new Mpe(input, p)),
("Mse", new Mse(p), new Mse(input, p)),
("Msle", new Msle(p), new Msle(input, p)),
("Rae", new Rae(p), new Rae(input, p)),
("Rmse", new Rmse(p), new Rmse(input, p)),
("Rmsle", new Rmsle(p), new Rmsle(input, p)),
("Rse", new Rse(p), new Rse(input, p)),
("Smape", new Smape(p), new Smape(input, p)),
("Rsquared", new Rsquared(p), new Rsquared(input, p)),
("Huber", new Huber(p), new Huber(input, p))
};
public static IEnumerable<object[]> GetValueIndicatorData()
=> ValueIndicators.Select(x => new object[] { x.Name, x.DirectParams, x.EventParams });
// Create a list of bar-based indicator pairs
var barIndicators = new List<(string Name, AbstractBase Direct, AbstractBase EventBased)>
{
// Volume indicators
("Adl", new Adl(), new Adl(barInput)),
("Adosc", new Adosc(3, 10), new Adosc(barInput, 3, 10)),
("Aobv", new Aobv(), new Aobv(barInput)),
("Cmf", new Cmf(20), new Cmf(barInput, 20)),
("Eom", new Eom(14), new Eom(barInput, 14)),
("Kvo", new Kvo(34, 55), new Kvo(barInput, 34, 55)),
// Volatility indicators (bar-based)
("Atr", new Atr(14), new Atr(barInput, 14)),
// Oscillators (bar-based)
("Chop", new Chop(14), new Chop(barInput, 14)),
("Dosc", new Dosc(), new Dosc(barInput))
};
// Generate 200 random values and feed them to indicators
for (int i = 0; i < 200; i++)
{
// Generate random value for value-based indicators
double randomValue = GetRandomDouble(rng) * 100;
input.Add(randomValue);
// Calculate value-based indicators
foreach (var (_, direct, _) in valueIndicators)
{
direct.Calc(randomValue);
}
// Generate random bar for bar-based indicators
var bar = new TBar(
DateTime.Now,
randomValue,
randomValue + Math.Abs(GetRandomDouble(rng) * 10),
randomValue - Math.Abs(GetRandomDouble(rng) * 10),
randomValue + (GetRandomDouble(rng) * 5),
Math.Abs(GetRandomDouble(rng) * 1000),
true
);
barInput.Add(bar);
// Calculate bar-based indicators
foreach (var (_, direct, _) in barIndicators)
{
direct.Calc(bar);
}
}
// Compare the results for value-based indicators
foreach (var (name, direct, eventBased) in valueIndicators)
{
bool areEqual = (double.IsNaN(direct.Value) && double.IsNaN(eventBased.Value)) ||
Math.Abs(direct.Value - eventBased.Value) < 1e-9;
Assert.True(areEqual, $"Value indicator {name} failed: Expected {direct.Value}, Actual {eventBased.Value}");
}
// Compare the results for bar-based indicators
foreach (var (name, direct, eventBased) in barIndicators)
{
bool areEqual = (double.IsNaN(direct.Value) && double.IsNaN(eventBased.Value)) ||
Math.Abs(direct.Value - eventBased.Value) < 1e-9;
Assert.True(areEqual, $"Bar indicator {name} failed: Expected {direct.Value}, Actual {eventBased.Value}");
}
}
public static IEnumerable<object[]> GetBarIndicatorData()
=> BarIndicators.Select(x => new object[] { x.Name, x.DirectParams, x.EventParams });
private static double GetRandomDouble(RandomNumberGenerator rng)
{
@@ -172,4 +116,69 @@ public class EventingTests
rng.GetBytes(bytes);
return (double)BitConverter.ToUInt64(bytes, 0) / ulong.MaxValue;
}
private static TBar GenerateRandomBar(RandomNumberGenerator rng, double baseValue)
{
return new TBar(
DateTime.Now,
baseValue,
baseValue + Math.Abs(GetRandomDouble(rng) * 10),
baseValue - Math.Abs(GetRandomDouble(rng) * 10),
baseValue + (GetRandomDouble(rng) * 5),
Math.Abs(GetRandomDouble(rng) * 1000),
true
);
}
[Theory]
[MemberData(nameof(GetValueIndicatorData))]
public void ValueIndicatorEventTest(string indicatorName, object[] directParams, object[] eventParams)
{
using var rng = RandomNumberGenerator.Create();
var input = (TSeries)eventParams[0];
// Create indicator instances using reflection
var indicatorType = Type.GetType($"QuanTAlib.{indicatorName}, QuanTAlib")!;
var directIndicator = (AbstractBase)Activator.CreateInstance(indicatorType, directParams)!;
var eventIndicator = (AbstractBase)Activator.CreateInstance(indicatorType, eventParams)!;
// Generate test data and calculate
for (int i = 0; i < TestDataPoints; i++)
{
double randomValue = GetRandomDouble(rng) * 100;
input.Add(randomValue);
directIndicator.Calc(randomValue);
}
bool areEqual = (double.IsNaN(directIndicator.Value) && double.IsNaN(eventIndicator.Value)) ||
Math.Abs(directIndicator.Value - eventIndicator.Value) < Tolerance;
Assert.True(areEqual, $"Value indicator {indicatorName} failed: Expected {directIndicator.Value}, Actual {eventIndicator.Value}");
}
[Theory]
[MemberData(nameof(GetBarIndicatorData))]
public void BarIndicatorEventTest(string indicatorName, object[] directParams, object[] eventParams)
{
using var rng = RandomNumberGenerator.Create();
var barInput = (TBarSeries)eventParams[0];
// Create indicator instances using reflection
var indicatorType = Type.GetType($"QuanTAlib.{indicatorName}, QuanTAlib")!;
var directIndicator = (AbstractBase)Activator.CreateInstance(indicatorType, directParams)!;
var eventIndicator = (AbstractBase)Activator.CreateInstance(indicatorType, eventParams)!;
// Generate test data and calculate
for (int i = 0; i < TestDataPoints; i++)
{
var bar = GenerateRandomBar(rng, GetRandomDouble(rng) * 100);
barInput.Add(bar);
directIndicator.Calc(bar);
}
bool areEqual = (double.IsNaN(directIndicator.Value) && double.IsNaN(eventIndicator.Value)) ||
Math.Abs(directIndicator.Value - eventIndicator.Value) < Tolerance;
Assert.True(areEqual, $"Bar indicator {indicatorName} failed: Expected {directIndicator.Value}, Actual {eventIndicator.Value}");
}
}
+65 -1
View File
@@ -1,6 +1,10 @@
extern alias volatility;
extern alias averages;
extern alias statistics;
extern alias momentum;
extern alias oscillators;
extern alias volume;
extern alias experiments;
using Xunit;
using System.Reflection;
@@ -8,6 +12,10 @@ using TradingPlatform.BusinessLayer;
using statistics::QuanTAlib;
using averages::QuanTAlib;
using volatility::QuanTAlib;
using momentum::QuanTAlib;
using oscillators::QuanTAlib;
using volume::QuanTAlib;
using experiments::QuanTAlib;
namespace QuanTAlib
{
@@ -43,6 +51,39 @@ namespace QuanTAlib
}
}
private static void TestIndicatorMultipleFields<T>(string[] fieldNames) where T : Indicator, new()
{
var indicator = new T();
try
{
var onInitMethod = typeof(T).GetMethod("OnInit", BindingFlags.NonPublic | BindingFlags.Instance);
Assert.NotNull(onInitMethod);
onInitMethod.Invoke(indicator, null);
var onUpdateMethod = typeof(T).GetMethod("OnUpdate", BindingFlags.NonPublic | BindingFlags.Instance);
Assert.NotNull(onUpdateMethod);
foreach (var fieldName in fieldNames)
{
var field = typeof(T).GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance);
Assert.NotNull(field);
var fieldValue = field.GetValue(indicator);
Assert.NotNull(fieldValue);
}
Assert.NotNull(indicator.ShortName);
Assert.NotEmpty(indicator.ShortName);
Assert.NotNull(indicator.Name);
Assert.NotEmpty(indicator.Name);
Assert.NotNull(indicator.Description);
Assert.NotEmpty(indicator.Description);
Assert.IsAssignableFrom<Indicator>(indicator);
}
catch (Exception ex)
{
throw new Xunit.Sdk.XunitException($"Test failed for {typeof(T).Name}: {ex.Message}");
}
}
// Averages Indicators
[Fact] public void Afirma() => TestIndicator<AfirmaIndicator>();
[Fact] public void Alma() => TestIndicator<AlmaIndicator>();
@@ -95,9 +136,32 @@ namespace QuanTAlib
// Volatility Indicators
[Fact] public void Atr() => TestIndicator<AtrIndicator>("atr");
[Fact] public void Cmo() => TestIndicator<CmoIndicator>("cmo");
[Fact] public void Cvi() => TestIndicator<CviIndicator>("cvi");
[Fact] public void Historical() => TestIndicator<HistoricalIndicator>("historical");
[Fact] public void Jbands() => TestIndicatorMultipleFields<JbandsIndicator>(new[] { "jmaUp", "jmaLo" });
[Fact] public void Jvolty() => TestIndicator<JvoltyIndicator>("jma");
[Fact] public void Realized() => TestIndicator<RealizedIndicator>("realized");
[Fact] public void Rvi() => TestIndicator<RviIndicator>("rvi");
// Momentum Indicators
[Fact] public void Adx() => TestIndicator<momentum::QuanTAlib.AdxIndicator>("adx");
[Fact] public void Adxr() => TestIndicator<momentum::QuanTAlib.AdxrIndicator>("adxr");
[Fact] public void Apo() => TestIndicator<momentum::QuanTAlib.ApoIndicator>("apo");
[Fact] public void Dmi() => TestIndicator<momentum::QuanTAlib.DmiIndicator>("dmi");
[Fact] public void Dmx() => TestIndicator<momentum::QuanTAlib.DmxIndicator>("dmx");
[Fact] public void Dpo() => TestIndicator<momentum::QuanTAlib.DpoIndicator>("dpo");
[Fact] public void Macd() => TestIndicator<momentum::QuanTAlib.MacdIndicator>("macd");
[Fact] public void Mom() => TestIndicator<momentum::QuanTAlib.MomIndicator>("Series");
[Fact] public void Pmo() => TestIndicator<momentum::QuanTAlib.PmoIndicator>("Series");
[Fact] public void Po() => TestIndicator<momentum::QuanTAlib.PoIndicator>("Series");
[Fact] public void Ppo() => TestIndicator<momentum::QuanTAlib.PpoIndicator>("Series");
[Fact] public void Roc() => TestIndicator<momentum::QuanTAlib.RocIndicator>("Series");
[Fact] public void Trix() => TestIndicator<momentum::QuanTAlib.TrixIndicator>("Series");
[Fact] public void Vel() => TestIndicator<momentum::QuanTAlib.VelIndicator>("Series");
[Fact] public void Vortex() => TestIndicatorMultipleFields<momentum::QuanTAlib.VortexIndicator>(new[] { "PlusLine", "MinusLine" });
// Oscillators Indicators
[Fact] public void Cti() => TestIndicator<oscillator::QuanTAlib.CtiIndicator>("Series");
}
}
+66 -208
View File
@@ -1,268 +1,167 @@
using Xunit;
using System.Security.Cryptography;
namespace QuanTAlib.Tests;
public class OscillatorsUpdateTests
public class OscillatorsUpdateTests : UpdateTestBase
{
private readonly RandomNumberGenerator rng = RandomNumberGenerator.Create();
private const int RandomUpdates = 100;
private const double ReferenceValue = 100.0;
private const int precision = 8;
private double GetRandomDouble()
{
byte[] bytes = new byte[8];
rng.GetBytes(bytes);
return ((double)BitConverter.ToUInt64(bytes, 0) / ulong.MaxValue * 200) - 100; // Range: -100 to 100
}
private TBar GetRandomBar(bool IsNew)
{
double open = GetRandomDouble();
double high = open + Math.Abs(GetRandomDouble());
double low = open - Math.Abs(GetRandomDouble());
double close = low + ((high - low) * GetRandomDouble());
return new TBar(DateTime.Now, open, high, low, close, 1000, IsNew);
}
[Fact]
public void Rsi_Update()
{
var indicator = new Rsi(period: 14);
double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true));
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false));
}
double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTValueUpdate(indicator, indicator.Calc);
}
[Fact]
public void Rsx_Update()
{
var indicator = new Rsx(period: 14);
double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true));
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false));
}
double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTValueUpdate(indicator, indicator.Calc);
}
[Fact]
public void Cmo_Update()
{
var indicator = new Cmo(period: 14);
double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true));
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false));
}
double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTValueUpdate(indicator, indicator.Calc);
}
[Fact]
public void Ao_Update()
{
var indicator = new Ao();
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTBarUpdate(indicator, indicator.Calc);
}
[Fact]
public void Ac_Update()
{
var indicator = new Ac();
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTBarUpdate(indicator, indicator.Calc);
}
[Fact]
public void Aroon_Update()
{
var indicator = new Aroon(period: 25);
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTBarUpdate(indicator, indicator.Calc);
}
[Fact]
public void Bop_Update()
{
var indicator = new Bop();
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTBarUpdate(indicator, indicator.Calc);
}
[Fact]
public void Cci_Update()
{
var indicator = new Cci(period: 20);
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTBarUpdate(indicator, indicator.Calc);
}
[Fact]
public void Cfo_Update()
{
var indicator = new Cfo(period: 14);
double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true));
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false));
}
double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTValueUpdate(indicator, indicator.Calc);
}
[Fact]
public void Chop_Update()
{
var indicator = new Chop(period: 14);
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTBarUpdate(indicator, indicator.Calc);
}
[Fact]
public void Cog_Update()
{
var indicator = new Cog(period: 10);
double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true));
TestTValueUpdate(indicator, indicator.Calc);
}
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false));
}
double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false));
[Fact]
public void Coppock_Update()
{
var indicator = new Coppock(roc1Period: 14, roc2Period: 11, wmaPeriod: 10);
TestTValueUpdate(indicator, indicator.Calc);
}
Assert.Equal(initialValue, finalValue, precision);
[Fact]
public void Crsi_Update()
{
var indicator = new Crsi(period1: 10, period2: 14, period3: 30);
TestTValueUpdate(indicator, indicator.Calc);
}
[Fact]
public void Smi_Update()
{
var indicator = new Smi(period: 10, smooth1: 3, smooth2: 3);
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTBarUpdate(indicator, indicator.Calc);
}
[Fact]
public void Srsi_Update()
{
var indicator = new Srsi(rsiPeriod: 14, stochPeriod: 14, smoothK: 3, smoothD: 3);
double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true));
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false));
}
double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTValueUpdate(indicator, indicator.Calc);
}
[Fact]
public void Stc_Update()
{
var indicator = new Stc(cyclePeriod: 10, fastPeriod: 23, slowPeriod: 50);
double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true));
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false));
}
double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTValueUpdate(indicator, indicator.Calc);
}
[Fact]
public void Stoch_Update()
{
var indicator = new Stoch(period: 14, smoothK: 3, smoothD: 3);
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTBarUpdate(indicator, indicator.Calc);
}
[Fact]
public void Tsi_Update()
{
var indicator = new Tsi(firstPeriod: 25, secondPeriod: 13);
TestTValueUpdate(indicator, indicator.Calc);
}
[Fact]
public void Uo_Update()
{
var indicator = new Uo(period1: 7, period2: 14, period3: 28);
TestTBarUpdate(indicator, indicator.Calc);
}
[Fact]
public void Willr_Update()
{
var indicator = new Willr(period: 14);
TestTBarUpdate(indicator, indicator.Calc);
}
[Fact]
public void Dosc_Update()
{
var indicator = new Dosc();
TestTBarUpdate(indicator, indicator.Calc);
}
[Fact]
public void Efi_Update()
{
var indicator = new Efi(period: 13);
TestTBarUpdate(indicator, indicator.Calc);
}
[Fact]
public void Fisher_Update()
{
var indicator = new Fisher(period: 10);
double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true));
for (int i = 0; i < RandomUpdates; i++)
@@ -275,50 +174,9 @@ public class OscillatorsUpdateTests
}
[Fact]
public void Uo_Update()
public void Cti_Update()
{
var indicator = new Uo(period1: 7, period2: 14, period3: 28);
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
}
[Fact]
public void Willr_Update()
{
var indicator = new Willr(period: 14);
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
}
[Fact]
public void Dosc_Update()
{
var indicator = new Dosc();
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
var indicator = new Cti(period: 20);
TestTValueUpdate(indicator, indicator.Calc);
}
}
+35 -142
View File
@@ -1,239 +1,132 @@
using Xunit;
using System.Security.Cryptography;
namespace QuanTAlib.Tests;
public class StatisticsUpdateTests
public class StatisticsUpdateTests : UpdateTestBase
{
private readonly RandomNumberGenerator rng = RandomNumberGenerator.Create();
private const int RandomUpdates = 100;
private const double ReferenceValue = 100.0;
private const int precision = 8;
private double GetRandomDouble()
[Fact]
public void Beta_Update()
{
byte[] bytes = new byte[8];
rng.GetBytes(bytes);
return ((double)BitConverter.ToUInt64(bytes, 0) / ulong.MaxValue * 200) - 100; // Range: -100 to 100
var indicator = new Beta(period: 14);
TestDualTBarUpdate(indicator, indicator.Calc);
}
private TBar GetRandomBar(bool IsNew)
[Fact]
public void Corr_Update()
{
double open = GetRandomDouble();
double high = open + Math.Abs(GetRandomDouble());
double low = open - Math.Abs(GetRandomDouble());
double close = low + ((high - low) * GetRandomDouble());
return new TBar(DateTime.Now, open, high, low, close, 1000, IsNew);
var indicator = new Corr(period: 14);
TestDualTValueUpdate(indicator, indicator.Calc);
}
[Fact]
public void Curvature_Update()
{
var indicator = new Curvature(period: 14);
double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true));
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false));
}
double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTValueUpdate(indicator, indicator.Calc);
}
[Fact]
public void Entropy_Update()
{
var indicator = new Entropy(period: 14);
double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true));
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false));
}
double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTValueUpdate(indicator, indicator.Calc);
}
[Fact]
public void Hurst_Update()
{
var indicator = new Hurst(period: 100, minLength: 10);
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTBarUpdate(indicator, indicator.Calc);
}
[Fact]
public void Kurtosis_Update()
{
var indicator = new Kurtosis(period: 14);
double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true));
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false));
}
double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTValueUpdate(indicator, indicator.Calc);
}
[Fact]
public void Max_Update()
{
var indicator = new Max(period: 14);
double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true));
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false));
}
double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTValueUpdate(indicator, indicator.Calc);
}
[Fact]
public void Median_Update()
{
var indicator = new Median(period: 14);
double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true));
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false));
}
double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTValueUpdate(indicator, indicator.Calc);
}
[Fact]
public void Min_Update()
{
var indicator = new Min(period: 14);
double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true));
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false));
}
double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTValueUpdate(indicator, indicator.Calc);
}
[Fact]
public void Mode_Update()
{
var indicator = new Mode(period: 14);
double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true));
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false));
}
double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTValueUpdate(indicator, indicator.Calc);
}
[Fact]
public void Percentile_Update()
{
var indicator = new Percentile(period: 14, percent: 50);
double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true));
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false));
}
double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTValueUpdate(indicator, indicator.Calc);
}
[Fact]
public void Skew_Update()
{
var indicator = new Skew(period: 14);
double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true));
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false));
}
double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTValueUpdate(indicator, indicator.Calc);
}
[Fact]
public void Slope_Update()
{
var indicator = new Slope(period: 14);
double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true));
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false));
}
double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTValueUpdate(indicator, indicator.Calc);
}
[Fact]
public void Stddev_Update()
{
var indicator = new Stddev(period: 14);
double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true));
TestTValueUpdate(indicator, indicator.Calc);
}
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false));
}
double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false));
[Fact]
public void Theil_Update()
{
var indicator = new Theil(period: 14);
TestTValueUpdate(indicator, indicator.Calc);
}
Assert.Equal(initialValue, finalValue, precision);
[Fact]
public void Tsf_Update()
{
var indicator = new Tsf(period: 14);
TestTValueUpdate(indicator, indicator.Calc);
}
[Fact]
public void Variance_Update()
{
var indicator = new Variance(period: 14);
double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true));
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false));
}
double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTValueUpdate(indicator, indicator.Calc);
}
[Fact]
public void Zscore_Update()
{
var indicator = new Zscore(period: 14);
double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true));
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false));
}
double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTValueUpdate(indicator, indicator.Calc);
}
}
+37 -318
View File
@@ -1,504 +1,223 @@
using Xunit;
using System.Security.Cryptography;
namespace QuanTAlib.Tests;
public class VolatilityUpdateTests
public class VolatilityUpdateTests : UpdateTestBase
{
private readonly RandomNumberGenerator rng = RandomNumberGenerator.Create();
private const int RandomUpdates = 100;
private const double ReferenceValue = 100.0;
private const int precision = 8;
private double GetRandomDouble()
{
byte[] bytes = new byte[8];
rng.GetBytes(bytes);
return ((double)BitConverter.ToUInt64(bytes, 0) / ulong.MaxValue * 200) - 100; // Range: -100 to 100
}
private TBar GetRandomBar(bool IsNew)
{
double open = GetRandomDouble();
double high = open + Math.Abs(GetRandomDouble());
double low = open - Math.Abs(GetRandomDouble());
double close = low + ((high - low) * GetRandomDouble());
return new TBar(DateTime.Now, open, high, low, close, 1000, IsNew);
}
[Fact]
public void Adr_Update()
{
var indicator = new Adr(period: 14);
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTBarUpdate(indicator, indicator.Calc);
}
[Fact]
public void Atr_Update()
{
var indicator = new Atr(period: 14);
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
TestTBarUpdate(indicator, indicator.Calc);
}
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
[Fact]
public void Atrs_Update()
{
var indicator = new Atrs(period: 14, factor: 2.0);
TestTBarUpdate(indicator, indicator.Calc);
}
[Fact]
public void Ap_Update()
{
var indicator = new Ap(period: 20);
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTBarUpdate(indicator, indicator.Calc);
}
[Fact]
public void Atrp_Update()
{
var indicator = new Atrp(period: 14);
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTBarUpdate(indicator, indicator.Calc);
}
[Fact]
public void Bband_Update()
{
var indicator = new Bband(period: 20, multiplier: 2.0);
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTBarUpdate(indicator, indicator.Calc);
}
[Fact]
public void Ccv_Update()
{
var indicator = new Ccv(period: 20);
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTBarUpdate(indicator, indicator.Calc);
}
[Fact]
public void Ce_Update()
{
var indicator = new Ce(period: 22, multiplier: 3.0);
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTBarUpdate(indicator, indicator.Calc);
}
[Fact]
public void Cv_Update()
{
var indicator = new Cv(period: 20);
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTBarUpdate(indicator, indicator.Calc);
}
[Fact]
public void Cvi_Update()
{
var indicator = new Cvi(period: 10, smoothPeriod: 10);
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTBarUpdate(indicator, indicator.Calc);
}
[Fact]
public void Dchn_Update()
{
var indicator = new Dchn(period: 20);
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTBarUpdate(indicator, indicator.Calc);
}
[Fact]
public void Ewma_Update()
{
var indicator = new Ewma(period: 20, lambda: 0.94);
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTBarUpdate(indicator, indicator.Calc);
}
[Fact]
public void Fcb_Update()
{
var indicator = new Fcb(period: 20, smoothing: 0.5);
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTBarUpdate(indicator, indicator.Calc);
}
[Fact]
public void Gkv_Update()
{
var indicator = new Gkv(period: 20);
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTBarUpdate(indicator, indicator.Calc);
}
[Fact]
public void Historical_Update()
{
var indicator = new Hv(period: 14);
double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true));
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false));
}
double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTValueUpdate(indicator, indicator.Calc);
}
[Fact]
public void Hlv_Update()
{
var indicator = new Hlv(period: 20);
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTBarUpdate(indicator, indicator.Calc);
}
[Fact]
public void Jvolty_Update()
{
var indicator = new Jvolty(period: 14);
double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true));
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false));
}
double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTValueUpdate(indicator, indicator.Calc);
}
[Fact]
public void Natr_Update()
{
var indicator = new Natr(period: 14);
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTBarUpdate(indicator, indicator.Calc);
}
[Fact]
public void Pch_Update()
{
var indicator = new Pch(period: 20);
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTBarUpdate(indicator, indicator.Calc);
}
[Fact]
public void Pv_Update()
{
var indicator = new Pv(period: 10);
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTBarUpdate(indicator, indicator.Calc);
}
[Fact]
public void Realized_Update()
{
var indicator = new Rv(period: 14);
double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true));
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false));
}
double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTValueUpdate(indicator, indicator.Calc);
}
[Fact]
public void Rsv_Update()
{
var indicator = new Rsv(period: 10);
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTBarUpdate(indicator, indicator.Calc);
}
[Fact]
public void Rvi_Update()
{
var indicator = new Rvi(period: 14);
double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true));
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false));
}
double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTValueUpdate(indicator, indicator.Calc);
}
[Fact]
public void Sv_Update()
{
var indicator = new Sv(period: 20, lambda: 0.94);
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTBarUpdate(indicator, indicator.Calc);
}
[Fact]
public void Tr_Update()
{
var indicator = new Tr();
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTBarUpdate(indicator, indicator.Calc);
}
[Fact]
public void Ui_Update()
{
var indicator = new Ui(period: 14);
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTBarUpdate(indicator, indicator.Calc);
}
[Fact]
public void Vc_Update()
{
var indicator = new Vc(period: 20, deviations: 2.0);
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTBarUpdate(indicator, indicator.Calc);
}
[Fact]
public void Vov_Update()
{
var indicator = new Vov(period: 20);
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTBarUpdate(indicator, indicator.Calc);
}
[Fact]
public void Vr_Update()
{
var indicator = new Vr(shortPeriod: 10, longPeriod: 20);
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTBarUpdate(indicator, indicator.Calc);
}
[Fact]
public void Vs_Update()
{
var indicator = new Vs(period: 14, multiplier: 2.0);
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTBarUpdate(indicator, indicator.Calc);
}
[Fact]
public void Yzv_Update()
{
var indicator = new Yzv(period: 20);
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
TestTBarUpdate(indicator, indicator.Calc);
}
}
+21 -11
View File
@@ -5,12 +5,13 @@
| Basic Transforms | 6 of 6 | 100% |
| Averages & Trends | 33 of 33 | 100% |
| Momentum | 16 of 16 | 100% |
| Oscillators | 21 of 29 | 72% |
| Volatility | 24 of 35 | 69% |
| Volume | 15 of 19 | 79% |
| Numerical Analysis | 13 of 19 | 68% |
| Oscillators | 24 of 29 | 83% |
| Volatility | 29 of 35 | 83% |
| Volume | 19 of 19 | 100% |
| Numerical Analysis | 15 of 19 | 79% |
| Errors | 16 of 16 | 100% |
| **Total** | **144 of 173** | **83%** |
| Patterns | 0 of 8 | 0% |
| **Total** | **158 of 181** | **87%** |
|Technical Indicator Name| Class Name|
|-----------|:----------:|
@@ -85,7 +86,8 @@
|COPPOCK - Coppock Curve|`Coppock`|
|CRSI - Connor RSI|`Crsi`|
|🚧 CTI - Ehler's Correlation Trend Indicator|`Cti`|
|🚧 EFI - Elder Ray's Force Index|`Efi`|
|DOSC - Derivative Oscillator|`Dosc`|
|EFI - Elder Ray's Force Index|`Efi`|
|🚧 FISHER - Fisher Transform|`Fisher`|
|🚧 FOSC - Forecast Oscillator|`Fosc`|
|🚧 GATOR* - Williams Alliator Oscillator (Upper Jaw, Lower Jaw, Teeth)|`Gator`|
@@ -101,7 +103,15 @@
|TSI - True Strength Index|`Tsi`|
|UO - Ultimate Oscillator|`Uo`|
|WILLR - Larry Williams' %R|`Willr`|
|DOSC - Derivative Oscillator|`Dosc`|
|**PATTERNS**||
|🚧 DOJI - Doji Candlestick Pattern|`Doji`|
|🚧 ER* - Elder Ray Pattern (Bull Power, Bear Power)|`Er`|
|🚧 MARU - Marubozu Candlestick Pattern|`Maru`|
|🚧 PIV* - Pivot Points (Support 1-3, Pivot, Resistance 1-3)|`Piv`|
|🚧 PP* - Price Pivots (Support 1-3, Pivot, Resistance 1-3)|`Pp`|
|🚧 RPP* - Rolling Pivot Points (Support 1-3, Pivot, Resistance 1-3)|`Rpp`|
|🚧 WF - Williams Fractal|`Wf`|
|🚧 ZZ - Zig Zag Pattern|`Zz`|
|**VOLATILITY INDICATORS**||
|ADR - Average Daily Range|`Adr`|
|AP - Andrew's Pitchfork|`Ap`|
@@ -159,12 +169,12 @@
|VWAP - Volume Weighted Average Price|`Vwap`|
|VWMA - Volume Weighted Moving Average|`Vwma`|
|**NUMERICAL ANALYSIS**||
|🚧 BETA* - Beta coefficient (Beta, R-squared)|`Beta`|
|🚧 CORR* - Correlation Coefficient (Correlation, P-value)|`Corr`|
|BETA* - Beta coefficient (Beta, R-squared)|`Beta`|
|CORR* - Correlation Coefficient (Correlation, P-value)|`Corr`|
|CURVATURE - Rate of Change in Direction or Slope|`Curvature`|
|ENTROPY - Measure of Uncertainty or Disorder|`Entropy`|
|🚧 HUBER - Huber Loss|`Huber`|
|🚧 HURST - Hurst Exponent|`Hurst`|
|HUBER - Huber Loss|`Huber`|
|HURST - Hurst Exponent|`Hurst`|
|KURTOSIS - Measure of Tails/Peakedness|`Kurtosis`|
|MAX - Maximum with exponential decay|`Max`|
|MEDIAN - Middle value|`Median`|
+2 -1
View File
@@ -2,6 +2,7 @@
✔️ AFIRMA - Adaptive FIR Moving Average
✔️ ALMA - Arnaud Legoux Moving Average
✔️ CONVOLUTION - 1D Convolution with sliding kernel
✔️ DEMA - Double Exponential Moving Average
✔️ DSMA - Dynamic Simple Moving Average
✔️ DWMA - Dynamic Weighted Moving Average
@@ -17,7 +18,7 @@
✔️ KAMA - Kaufman Adaptive Moving Average
✔️ LTMA - Linear Time Moving Average
✔️ MAAF - Moving Average Adaptive Filter
✔️ *MAMA - MESA Adaptive Moving Average (MAMA, FAMA)
✔️ MAMA - MESA Adaptive Moving Average (MAMA, FAMA)
✔️ MGDI - McGinley Dynamic Indicator
✔️ MMA - Modified Moving Average
✔️ PWMA - Parabolic Weighted Moving Average
+3 -8
View File
@@ -43,14 +43,9 @@ public sealed class Huber : AbstractBase
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Huber(int period, double delta = 1.0)
{
if (period < 1)
{
throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1.");
}
if (delta <= 0)
{
throw new ArgumentOutOfRangeException(nameof(delta), "Delta must be greater than 0.");
}
ArgumentOutOfRangeException.ThrowIfLessThan(period, 1);
ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(delta, 0);
WarmupPeriod = period;
_actualBuffer = new CircularBuffer(period);
_predictedBuffer = new CircularBuffer(period);
+45 -68
View File
@@ -24,10 +24,13 @@ namespace QuanTAlib;
///
/// Formula:
/// TR = max(high-low, abs(high-prevClose), abs(low-prevClose))
/// +DM = if(high-prevHigh > prevLow-low) then max(high-prevHigh, 0) else 0
/// -DM = if(prevLow-low > high-prevHigh) then max(prevLow-low, 0) else 0
/// +DI = 100 * smoothed(+DM) / smoothed(TR)
/// -DI = 100 * smoothed(-DM) / smoothed(TR)
/// +DM = if(high-prevHigh > prevLow-low && high-prevHigh > 0) then high-prevHigh else 0
/// -DM = if(prevLow-low > high-prevHigh && prevLow-low > 0) then prevLow-low else 0
/// Smoothed TR = Wilder's smoothing of TR (ATR)
/// Smoothed +DM = Wilder's smoothing of +DM
/// Smoothed -DM = Wilder's smoothing of -DM
/// +DI = 100 * Smoothed(+DM) / Smoothed(TR)
/// -DI = 100 * Smoothed(-DM) / Smoothed(TR)
///
/// Sources:
/// J. Welles Wilder Jr. - "New Concepts in Technical Trading Systems" (1978)
@@ -36,49 +39,41 @@ namespace QuanTAlib;
/// Note: Default period of 14 was recommended by Wilder
/// </remarks>
[SkipLocalsInit]
public sealed class Dmi : AbstractBarBase
public sealed class Dmi : AbstractBase
{
private readonly Rma _smoothedTr;
private readonly Atr _atr;
private readonly Rma _smoothedPlusDm;
private readonly Rma _smoothedMinusDm;
private double _prevHigh, _prevLow, _prevClose;
private double _p_prevHigh, _p_prevLow, _p_prevClose;
private double _prevHigh, _prevLow;
private double _p_prevHigh, _p_prevLow;
private double _plusDi, _minusDi;
private const double ScalingFactor = 100.0;
private const int DefaultPeriod = 14;
/// <summary>
/// Gets the most recent +DI value
/// </summary>
public double PlusDI => _plusDi;
/// <summary>
/// Gets the most recent -DI value
/// </summary>
public double MinusDI => _minusDi;
/// <param name="period">The number of periods used in the DMI calculation (default 14).</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Dmi(int period = DefaultPeriod)
{
if (period < 1)
throw new ArgumentOutOfRangeException(nameof(period));
_smoothedTr = new(period, useSma: true);
_smoothedPlusDm = new(period, useSma: true);
_smoothedMinusDm = new(period, useSma: true);
_index = 0;
_atr = new(period);
_smoothedPlusDm = new(period);
_smoothedMinusDm = new(period);
WarmupPeriod = period + 1;
Name = $"DMI({period})";
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of periods used in the DMI calculation.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Dmi(object source, int period) : this(period)
public override void Init()
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
base.Init();
_atr.Init();
_smoothedPlusDm.Init();
_smoothedMinusDm.Init();
_prevHigh = _prevLow = double.NaN;
_p_prevHigh = _p_prevLow = double.NaN;
_plusDi = _minusDi = 0;
_index = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -89,25 +84,14 @@ public sealed class Dmi : AbstractBarBase
_index++;
_p_prevHigh = _prevHigh;
_p_prevLow = _prevLow;
_p_prevClose = _prevClose;
}
else
{
_prevHigh = _p_prevHigh;
_prevLow = _p_prevLow;
_prevClose = _p_prevClose;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static double CalculateTrueRange(double high, double low, double prevClose)
{
double hl = high - low;
double hpc = Math.Abs(high - prevClose);
double lpc = Math.Abs(low - prevClose);
return Math.Max(hl, Math.Max(hpc, lpc));
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static (double plusDm, double minusDm) CalculateDirectionalMovement(
double high, double low, double prevHigh, double prevLow)
@@ -115,13 +99,8 @@ public sealed class Dmi : AbstractBarBase
double upMove = high - prevHigh;
double downMove = prevLow - low;
double plusDm = 0.0;
double minusDm = 0.0;
if (upMove > downMove && upMove > 0)
plusDm = upMove;
else if (downMove > upMove && downMove > 0)
minusDm = downMove;
double plusDm = (upMove > downMove && upMove > 0) ? upMove : 0;
double minusDm = (downMove > upMove && downMove > 0) ? downMove : 0;
return (plusDm, minusDm);
}
@@ -129,38 +108,36 @@ public sealed class Dmi : AbstractBarBase
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(Input.IsNew);
ManageState(BarInput.IsNew);
if (_index == 1)
if (double.IsNaN(_prevHigh))
{
_prevHigh = Input.High;
_prevLow = Input.Low;
_prevClose = Input.Close;
_prevHigh = BarInput.High;
_prevLow = BarInput.Low;
return 0.0;
}
// Calculate True Range and Directional Movement
double tr = CalculateTrueRange(Input.High, Input.Low, _prevClose);
// Calculate ATR
double atr = _atr.Calc(BarInput).Value;
// Calculate Directional Movement
var (plusDm, minusDm) = CalculateDirectionalMovement(
Input.High, Input.Low, _prevHigh, _prevLow);
BarInput.High, BarInput.Low, _prevHigh, _prevLow);
// Update previous values
_prevHigh = Input.High;
_prevLow = Input.Low;
_prevClose = Input.Close;
// Update previous values for next calculation
_prevHigh = BarInput.High;
_prevLow = BarInput.Low;
// Smooth the indicators using Wilder's method
_smoothedTr.Calc(tr, Input.IsNew);
_smoothedPlusDm.Calc(plusDm, Input.IsNew);
_smoothedMinusDm.Calc(minusDm, Input.IsNew);
// Smooth DM values using Wilder's method
double smoothedPlusDm = _smoothedPlusDm.Calc(plusDm, BarInput.IsNew).Value;
double smoothedMinusDm = _smoothedMinusDm.Calc(minusDm, BarInput.IsNew).Value;
// Calculate +DI and -DI
double smoothedTr = _smoothedTr.Value;
if (smoothedTr > 0)
// Calculate DI values
if (atr > 0)
{
_plusDi = ScalingFactor * _smoothedPlusDm.Value / smoothedTr;
_minusDi = ScalingFactor * _smoothedMinusDm.Value / smoothedTr;
return _plusDi - _minusDi; // Return the difference as main value
_plusDi = ScalingFactor * smoothedPlusDm / atr;
_minusDi = ScalingFactor * smoothedMinusDm / atr;
return _plusDi - _minusDi;
}
_plusDi = 0.0;
+32 -113
View File
@@ -4,16 +4,13 @@ namespace QuanTAlib;
/// <summary>
/// DMX: Enhanced Directional Movement Index using JMA smoothing
/// An improvement over the traditional DMI indicator that uses Jurik Moving Average (JMA)
/// for smoothing instead of Wilder's moving average. This enhancement provides better
/// noise reduction while maintaining responsiveness to significant price movements.
/// for smoothing. This enhancement provides better noise reduction while maintaining
/// responsiveness to significant price movements.
/// </summary>
/// <remarks>
/// The DMX calculation process:
/// 1. Calculate True Range (TR)
/// 2. Calculate +DM (Positive Directional Movement)
/// 3. Calculate -DM (Negative Directional Movement)
/// 4. Smooth TR, +DM, and -DM using JMA instead of Wilder's smoothing
/// 5. Calculate +DI and -DI as percentages
/// 1. Calculate DMI using the standard Dmi class
/// 2. Apply JMA smoothing to the +DI and -DI values
///
/// Key improvements over DMI:
/// - Uses JMA's adaptive volatility-based smoothing
@@ -22,11 +19,9 @@ namespace QuanTAlib;
/// - Reduced lag through JMA's phase-shifting
///
/// Formula:
/// TR = max(high-low, abs(high-prevClose), abs(low-prevClose))
/// +DM = if(high-prevHigh > prevLow-low) then max(high-prevHigh, 0) else 0
/// -DM = if(prevLow-low > high-prevHigh) then max(prevLow-low, 0) else 0
/// +DI = 100 * JMA(+DM) / JMA(TR)
/// -DI = 100 * JMA(-DM) / JMA(TR)
/// DMI calculation as per standard DMI
/// DMX +DI = JMA(DMI +DI)
/// DMX -DI = JMA(DMI -DI)
///
/// Sources:
/// Original DMI by J. Welles Wilder Jr. - "New Concepts in Technical Trading Systems" (1978)
@@ -35,53 +30,40 @@ namespace QuanTAlib;
[SkipLocalsInit]
public sealed class Dmx : AbstractBarBase
{
private readonly Jma _smoothedTr;
private readonly Jma _smoothedPlusDm;
private readonly Jma _smoothedMinusDm;
private double _prevHigh, _prevLow, _prevClose;
private double _p_prevHigh, _p_prevLow, _p_prevClose;
private readonly Dmi _dmi;
private readonly Jma _smoothedPlusDi;
private readonly Jma _smoothedMinusDi;
private double _plusDi, _minusDi;
private const double ScalingFactor = 100.0;
private const int DefaultPeriod = 10;
private const int DefaultDmiPeriod = 14;
private const int DefaultJmaPeriod = 7;
private const int DefaultPhase = 100;
private const double DefaultFactor = 0.25;
/// <summary>
/// Gets the most recent +DI value
/// Gets the most recent smoothed +DI value
/// </summary>
public double PlusDI => _plusDi;
/// <summary>
/// Gets the most recent -DI value
/// Gets the most recent smoothed -DI value
/// </summary>
public double MinusDI => _minusDi;
/// <param name="period">The number of periods used in the DMX calculation (default 14).</param>
/// <param name="phase">The phase for the JMA smoothing (default 0).</param>
/// <param name="factor">The factor for the JMA smoothing (default 0.45).</param>
/// <param name="dmiPeriod">The number of periods used in the DMI calculation (default 14).</param>
/// <param name="jmaPeriod">The number of periods used in the JMA smoothing (default 10).</param>
/// <param name="phase">The phase for the JMA smoothing (default 100).</param>
/// <param name="factor">The factor for the JMA smoothing (default 0.25).</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Dmx(int period = DefaultPeriod, int phase = DefaultPhase, double factor = DefaultFactor)
public Dmx(int period = DefaultDmiPeriod, int jmaPeriod = DefaultJmaPeriod, int phase = DefaultPhase, double factor = DefaultFactor)
{
if (period < 1)
throw new ArgumentOutOfRangeException(nameof(period));
_smoothedTr = new(period, phase, factor);
_smoothedPlusDm = new(period, phase, factor);
_smoothedMinusDm = new(period, phase, factor);
_index = 0;
WarmupPeriod = period * 2; // JMA needs more warmup periods than RMA
Name = $"DMX({period})";
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of periods used in the DMX calculation.</param>
/// <param name="phase">The phase for the JMA smoothing.</param>
/// <param name="factor">The factor for the JMA smoothing.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Dmx(object source, int period, int phase = DefaultPhase, double factor = DefaultFactor) : this(period, phase, factor)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
if (period < 1 || jmaPeriod < 1)
throw new ArgumentOutOfRangeException(nameof(period), "Periods must be greater than or equal to 1.");
_dmi = new(period);
_smoothedPlusDi = new(jmaPeriod, phase, factor);
_smoothedMinusDi = new(jmaPeriod, phase, factor);
WarmupPeriod = period + jmaPeriod;
Name = $"DMX({period},{jmaPeriod})";
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -90,43 +72,7 @@ public sealed class Dmx : AbstractBarBase
if (isNew)
{
_index++;
_p_prevHigh = _prevHigh;
_p_prevLow = _prevLow;
_p_prevClose = _prevClose;
}
else
{
_prevHigh = _p_prevHigh;
_prevLow = _p_prevLow;
_prevClose = _p_prevClose;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static double CalculateTrueRange(double high, double low, double prevClose)
{
double hl = high - low;
double hpc = Math.Abs(high - prevClose);
double lpc = Math.Abs(low - prevClose);
return Math.Max(hl, Math.Max(hpc, lpc));
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static (double plusDm, double minusDm) CalculateDirectionalMovement(
double high, double low, double prevHigh, double prevLow)
{
double upMove = high - prevHigh;
double downMove = prevLow - low;
double plusDm = 0.0;
double minusDm = 0.0;
if (upMove > downMove && upMove > 0)
plusDm = upMove;
else if (downMove > upMove && downMove > 0)
minusDm = downMove;
return (plusDm, minusDm);
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
@@ -134,40 +80,13 @@ public sealed class Dmx : AbstractBarBase
{
ManageState(Input.IsNew);
if (_index == 1)
{
_prevHigh = Input.High;
_prevLow = Input.Low;
_prevClose = Input.Close;
return 0.0;
}
// Calculate DMI
_dmi.Calc(Input);
// Calculate True Range and Directional Movement
double tr = CalculateTrueRange(Input.High, Input.Low, _prevClose);
var (plusDm, minusDm) = CalculateDirectionalMovement(
Input.High, Input.Low, _prevHigh, _prevLow);
// Smooth the DMI values using JMA
_plusDi = _smoothedPlusDi.Calc(_dmi.PlusDI, Input.IsNew).Value;
_minusDi = _smoothedMinusDi.Calc(_dmi.MinusDI, Input.IsNew).Value;
// Update previous values
_prevHigh = Input.High;
_prevLow = Input.Low;
_prevClose = Input.Close;
// Smooth the indicators using JMA
_smoothedTr.Calc(tr, Input.IsNew);
_smoothedPlusDm.Calc(plusDm, Input.IsNew);
_smoothedMinusDm.Calc(minusDm, Input.IsNew);
// Calculate +DI and -DI
double smoothedTr = _smoothedTr.Value;
if (smoothedTr > 0)
{
_plusDi = ScalingFactor * _smoothedPlusDm.Value / smoothedTr;
_minusDi = ScalingFactor * _smoothedMinusDm.Value / smoothedTr;
return _plusDi - _minusDi; // Return the difference as main value
}
_plusDi = 0.0;
_minusDi = 0.0;
return 0.0;
return _plusDi - _minusDi; // Return the difference as main value
}
}
+2 -8
View File
@@ -85,9 +85,9 @@ public sealed class Dpo : AbstractBase
ManageState(BarInput.IsNew);
// Add current price to buffer
_prices.Add(BarInput.Close);
_prices.Add(BarInput.Close, BarInput.IsNew);
// Need enough prices for the shifted SMA calculation
if (_index <= _shift)
{
return 0;
@@ -96,12 +96,6 @@ public sealed class Dpo : AbstractBase
// Add price from shift periods ago to SMA buffer
_sma.Add(_prices[_shift]);
// Need enough prices for full calculation
if (_index <= WarmupPeriod)
{
return 0;
}
// Calculate DPO
double dpo = BarInput.Close - _sma.Average();
+7 -7
View File
@@ -60,14 +60,14 @@ public sealed class Macd : AbstractBase
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Macd(int fastPeriod = DefaultFastPeriod, int slowPeriod = DefaultSlowPeriod, int signalPeriod = DefaultSignalPeriod)
{
if (fastPeriod < 1)
throw new ArgumentOutOfRangeException(nameof(fastPeriod));
if (slowPeriod < 1)
throw new ArgumentOutOfRangeException(nameof(slowPeriod));
if (signalPeriod < 1)
throw new ArgumentOutOfRangeException(nameof(signalPeriod));
ArgumentOutOfRangeException.ThrowIfLessThan(fastPeriod, 1);
ArgumentOutOfRangeException.ThrowIfLessThan(slowPeriod, 1);
ArgumentOutOfRangeException.ThrowIfLessThan(signalPeriod, 1);
if (fastPeriod >= slowPeriod)
throw new ArgumentException("Fast period must be less than slow period");
{
throw new ArgumentOutOfRangeException(nameof(fastPeriod), "Fast period must be less than slow period");
}
_fastEma = new(fastPeriod);
_slowEma = new(slowPeriod);
+1 -2
View File
@@ -1,10 +1,9 @@
# Momentum indicators
Done: 15, Todo: 2
✔️ ADX - Average Directional Movement Index
✔️ ADXR - Average Directional Movement Index Rating
✔️ APO - Absolute Price Oscillator
✔️ *DMI - Directional Movement Index (DI+, DI-)
✔️ DMI - Directional Movement Index (DI+, DI-)
✔️ DMX - Jurik Directional Movement Index
✔️ DPO - Detrended Price Oscillator
✔️ *MACD - Moving Average Convergence/Divergence (MACD, Signal, Histogram)
+3 -6
View File
@@ -51,12 +51,9 @@ public sealed class Coppock : AbstractBase
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Coppock(int roc1Period = DefaultRoc1Period, int roc2Period = DefaultRoc2Period, int wmaPeriod = DefaultWmaPeriod)
{
if (roc1Period < 1)
throw new ArgumentOutOfRangeException(nameof(roc1Period), "ROC1 period must be greater than 0");
if (roc2Period < 1)
throw new ArgumentOutOfRangeException(nameof(roc2Period), "ROC2 period must be greater than 0");
if (wmaPeriod < 1)
throw new ArgumentOutOfRangeException(nameof(wmaPeriod), "WMA period must be greater than 0");
ArgumentOutOfRangeException.ThrowIfLessThan(roc1Period, 1);
ArgumentOutOfRangeException.ThrowIfLessThan(roc2Period, 1);
ArgumentOutOfRangeException.ThrowIfLessThan(wmaPeriod, 1);
_roc1Period = roc1Period;
_roc2Period = roc2Period;
+109
View File
@@ -0,0 +1,109 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// CTI: Ehler's Correlation Trend Indicator
/// Measures the correlation between price and an ideal trend line.
/// </summary>
/// <remarks>
/// The CTI calculation process:
/// 1. Correlates price curve with an ideal trend line (negative count due to backwards data storage)
/// 2. Uses Spearman's correlation algorithm
/// 3. Returns values between -1 and 1
///
/// Key characteristics:
/// - Oscillates between -1 and 1
/// - Positive values indicate price follows uptrend
/// - Negative values indicate price follows downtrend
///
/// Formula:
/// CTI = (n∑xy - ∑x∑y) / sqrt((n∑x² - (∑x)²)(n∑y² - (∑y)²))
/// where:
/// x = price curve
/// y = -count (ideal trend line)
/// n = period length
///
/// Sources:
/// John Ehlers - "Cybernetic Analysis for Stocks and Futures" (2004)
/// John Ehlers, Correlation Trend Indicator, Stocks & Commodities May-2020
/// </remarks>
[SkipLocalsInit]
public sealed class Cti : AbstractBase
{
private readonly int _period;
private readonly CircularBuffer _priceBuffer;
private readonly double[] _trendLine;
private const int MinimumPoints = 2; // Minimum points needed for correlation
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The calculation period (default: 20)</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Cti(object source, int period = 20) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Cti(int period = 20)
{
_period = period;
_priceBuffer = new CircularBuffer(period);
// Pre-calculate trend line values since they're static
_trendLine = new double[period];
for (int i = 0; i < period; i++)
{
_trendLine[i] = -i; // negative count for backwards data
}
WarmupPeriod = period;
Name = "CTI";
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override double Calculation()
{
ManageState(Input.IsNew);
_priceBuffer.Add(Input.Value, Input.IsNew);
// Use available points for early calculations
int points = Math.Min(_index + 1, _period);
if (points < MinimumPoints) return 0; // Need at least 2 points for correlation
double sx = 0, sy = 0, sxx = 0, sxy = 0, syy = 0;
// Calculate correlation components using available points
for (int i = 0; i < points; i++)
{
double x = _priceBuffer[i]; // price curve
double y = _trendLine[i]; // pre-calculated trend line
sx += x;
sy += y;
sxx += x * x;
sxy += x * y;
syy += y * y;
}
// Check for numerical stability
double denomX = (points * sxx) - (sx * sx);
double denomY = (points * syy) - (sy * sy);
if (denomX > 0 && denomY > 0)
{
return ((points * sxy) - (sx * sy)) / Math.Sqrt(denomX * denomY);
}
return 0;
}
}
+102
View File
@@ -0,0 +1,102 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// EFI: Elder Ray's Force Index
/// A volume-based oscillator that measures the strength of price movements using volume.
/// It helps identify potential trend reversals and confirm price movements.
/// </summary>
/// <remarks>
/// The EFI calculation process:
/// 1. Calculate the difference between the current close and the previous close
/// 2. Multiply the difference by the current volume
/// 3. Apply an exponential moving average (EMA) to smooth the result
///
/// Key characteristics:
/// - Oscillates above and below zero
/// - Positive values indicate buying pressure
/// - Negative values indicate selling pressure
/// - Crosses above zero suggest buying opportunities
/// - Crosses below zero suggest selling opportunities
///
/// Formula:
/// EFI = EMA((Close - Close[1]) * Volume, period)
///
/// Sources:
/// Alexander Elder - "Trading for a Living" (1993)
/// https://www.investopedia.com/terms/f/force-index.asp
///
/// Note: Default period is 13
/// </remarks>
[SkipLocalsInit]
public sealed class Efi : AbstractBase
{
private readonly Ema _ema;
private double _prevClose;
private double _p_prevClose;
private const int DefaultPeriod = 13;
/// <param name="period">The smoothing period for EMA calculation (default 13).</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Efi(int period = DefaultPeriod)
{
ArgumentOutOfRangeException.ThrowIfLessThan(period, 1);
_ema = new(period);
WarmupPeriod = period + 1;
Name = $"EFI({period})";
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The smoothing period for EMA calculation.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Efi(object source, int period = DefaultPeriod) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_ema.Init();
_prevClose = double.NaN;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_index++;
_p_prevClose = _prevClose;
}
else
{
_prevClose = _p_prevClose;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
if (_index == 1)
{
_prevClose = BarInput.Close;
return 0;
}
// Calculate raw force index
double priceChange = BarInput.Close - _prevClose;
double forceIndex = priceChange * BarInput.Volume;
// Update previous close
_prevClose = BarInput.Close;
// Apply EMA smoothing
return _ema.Calc(forceIndex, BarInput.IsNew);
}
}
+94
View File
@@ -0,0 +1,94 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// FISHER: Fisher Transform
/// A technical indicator that converts prices into a Gaussian normal distribution.
/// </summary>
/// <remarks>
/// The Fisher Transform calculation process:
/// 1. Calculate the value of the price relative to its high-low range.
/// 2. Apply the Fisher Transform formula to the normalized price.
/// 3. Smooth the result using an exponential moving average.
///
/// Key characteristics:
/// - Oscillates between -1 and 1
/// - Emphasizes price reversals
/// - Can be used to identify overbought and oversold conditions
///
/// Formula:
/// Fisher Transform = 0.5 * log((1 + x) / (1 - x))
/// where:
/// x = 2 * ((price - min) / (max - min) - 0.5)
///
/// Sources:
/// John F. Ehlers - "Rocket Science for Traders" (2001)
/// https://www.investopedia.com/terms/f/fisher-transform.asp
/// </remarks>
[SkipLocalsInit]
public sealed class Fisher : AbstractBase
{
private readonly int _period;
private readonly double[] _prices;
private double _prevFisher;
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The calculation period (default: 10)</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Fisher(object source, int period = 10) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Fisher(int period = 10)
{
_period = period;
_prices = new double[period];
WarmupPeriod = period;
Name = "FISHER";
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private double NormalizePrice(double price, double min, double max)
{
return 2 * (((price - min) / (max - min)) - 0.5);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private double FisherTransform(double value)
{
return 0.5 * System.Math.Log((1 + value) / (1 - value));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override double Calculation()
{
ManageState(Input.IsNew);
var idx = _index % _period;
_prices[idx] = Input.Value;
if (_index < _period - 1) return double.NaN;
var min = _prices.Min();
var max = _prices.Max();
var normalizedPrice = NormalizePrice(Input.Value, min, max);
var fisherValue = FisherTransform(normalizedPrice);
var smoothedFisher = 0.5 * (fisherValue + _prevFisher);
_prevFisher = smoothedFisher;
return smoothedFisher;
}
}
+1 -2
View File
@@ -48,8 +48,7 @@ public sealed class Rsi : AbstractBase
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Rsi(int period = DefaultPeriod)
{
if (period < 1)
throw new ArgumentOutOfRangeException(nameof(period));
ArgumentOutOfRangeException.ThrowIfLessThan(period, 1);
_avgGain = new(period, useSma: true);
_avgLoss = new(period, useSma: true);
_index = 0;
+3 -6
View File
@@ -57,12 +57,9 @@ public sealed class Smi : AbstractBase
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Smi(int period = DefaultPeriod, int smooth1 = DefaultSmooth1, int smooth2 = DefaultSmooth2)
{
if (period < 1)
throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than 0");
if (smooth1 < 1)
throw new ArgumentOutOfRangeException(nameof(smooth1), "Smooth1 must be greater than 0");
if (smooth2 < 1)
throw new ArgumentOutOfRangeException(nameof(smooth2), "Smooth2 must be greater than 0");
ArgumentOutOfRangeException.ThrowIfLessThan(period, 1);
ArgumentOutOfRangeException.ThrowIfLessThan(smooth1, 1);
ArgumentOutOfRangeException.ThrowIfLessThan(smooth2, 1);
_highs = new(period);
_lows = new(period);
+4 -18
View File
@@ -40,7 +40,6 @@ public sealed class Srsi : AbstractBase
private readonly CircularBuffer _srsiValues;
private readonly Sma _signal;
private readonly int _rsiPeriod;
private readonly int _stochPeriod;
private const int DefaultRsiPeriod = 14;
private const int DefaultStochPeriod = 14;
private const int DefaultSmoothK = 3;
@@ -56,25 +55,12 @@ public sealed class Srsi : AbstractBase
public Srsi(int rsiPeriod = DefaultRsiPeriod, int stochPeriod = DefaultStochPeriod,
int smoothK = DefaultSmoothK, int smoothD = DefaultSmoothD)
{
if (rsiPeriod < 1)
{
throw new ArgumentOutOfRangeException(nameof(rsiPeriod), "Period must be greater than 0");
}
if (stochPeriod < 1)
{
throw new ArgumentOutOfRangeException(nameof(stochPeriod), "Period must be greater than 0");
}
if (smoothK < 1)
{
throw new ArgumentOutOfRangeException(nameof(smoothK), "Period must be greater than 0");
}
if (smoothD < 1)
{
throw new ArgumentOutOfRangeException(nameof(smoothD), "Period must be greater than 0");
}
ArgumentOutOfRangeException.ThrowIfLessThan(rsiPeriod, 1);
ArgumentOutOfRangeException.ThrowIfLessThan(stochPeriod, 1);
ArgumentOutOfRangeException.ThrowIfLessThan(smoothK, 1);
ArgumentOutOfRangeException.ThrowIfLessThan(smoothD, 1);
_rsiPeriod = rsiPeriod;
_stochPeriod = stochPeriod;
_rsi = new(rsiPeriod);
_rsiValues = new(stochPeriod);
_srsiValues = new(smoothK);
+6 -21
View File
@@ -62,32 +62,17 @@ public sealed class Stc : AbstractBase
int slowPeriod = DefaultSlowPeriod, int d1Period = DefaultD1Period,
int stcPeriod = DefaultStcPeriod)
{
string err = "All periods must be greater than 0";
ArgumentOutOfRangeException.ThrowIfLessThan(cyclePeriod, 1);
ArgumentOutOfRangeException.ThrowIfLessThan(fastPeriod, 1);
ArgumentOutOfRangeException.ThrowIfLessThan(slowPeriod, 1);
ArgumentOutOfRangeException.ThrowIfLessThan(d1Period, 1);
ArgumentOutOfRangeException.ThrowIfLessThan(stcPeriod, 1);
if (cyclePeriod < 1)
{
throw new ArgumentOutOfRangeException(nameof(cyclePeriod), err);
}
if (fastPeriod < 1)
{
throw new ArgumentOutOfRangeException(nameof(fastPeriod), err);
}
if (slowPeriod < 1)
{
throw new ArgumentOutOfRangeException(nameof(slowPeriod), err);
}
if (d1Period < 1)
{
throw new ArgumentOutOfRangeException(nameof(d1Period), err);
}
if (stcPeriod < 1)
{
throw new ArgumentOutOfRangeException(nameof(stcPeriod), err);
}
if (fastPeriod >= slowPeriod)
{
throw new ArgumentOutOfRangeException(nameof(fastPeriod), "Fast period must be less than slow period");
}
_fastEma = new(fastPeriod);
_slowEma = new(slowPeriod);
_macdValues = new(cyclePeriod);
+3 -6
View File
@@ -52,12 +52,9 @@ public sealed class Stoch : AbstractBase
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Stoch(int period = DefaultPeriod, int smoothK = DefaultSmoothK, int smoothD = DefaultSmoothD)
{
if (period < 1)
throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than 0");
if (smoothK < 1)
throw new ArgumentOutOfRangeException(nameof(smoothK), "%K smoothing period must be greater than 0");
if (smoothD < 1)
throw new ArgumentOutOfRangeException(nameof(smoothD), "%D smoothing period must be greater than 0");
ArgumentOutOfRangeException.ThrowIfLessThan(period, 1);
ArgumentOutOfRangeException.ThrowIfLessThan(smoothK, 1);
ArgumentOutOfRangeException.ThrowIfLessThan(smoothD, 1);
_highs = new(period);
_lows = new(period);
+6 -24
View File
@@ -67,30 +67,12 @@ public sealed class Uo : AbstractBase
public Uo(int period1 = DefaultPeriod1, int period2 = DefaultPeriod2, int period3 = DefaultPeriod3,
double weight1 = DefaultWeight1, double weight2 = DefaultWeight2, double weight3 = DefaultWeight3)
{
if (period1 < 1)
{
throw new ArgumentOutOfRangeException(nameof(period1), "Period1 must be greater than 0");
}
if (period2 < 1)
{
throw new ArgumentOutOfRangeException(nameof(period2), "Period2 must be greater than 0");
}
if (period3 < 1)
{
throw new ArgumentOutOfRangeException(nameof(period3), "Period3 must be greater than 0");
}
if (weight1 <= 0)
{
throw new ArgumentOutOfRangeException(nameof(weight1), "Weight1 must be greater than 0");
}
if (weight2 <= 0)
{
throw new ArgumentOutOfRangeException(nameof(weight2), "Weight2 must be greater than 0");
}
if (weight3 <= 0)
{
throw new ArgumentOutOfRangeException(nameof(weight3), "Weight3 must be greater than 0");
}
ArgumentOutOfRangeException.ThrowIfLessThan(period1, 1);
ArgumentOutOfRangeException.ThrowIfLessThan(period2, 1);
ArgumentOutOfRangeException.ThrowIfLessThan(period3, 1);
ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(weight1, 0);
ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(weight2, 0);
ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(weight3, 0);
_weight1 = weight1;
_weight2 = weight2;
+4 -4
View File
@@ -1,5 +1,5 @@
# Oscillators indicators
Done: 21, Todo: 8
Done: 24, Todo: 5
✔️ AC - Acceleration Oscillator
✔️ AO - Awesome Oscillator
@@ -12,10 +12,10 @@ Done: 21, Todo: 8
✔️ COG - Ehler's Center of Gravity
✔️ COPPOCK - Coppock Curve
✔️ CRSI - Connor RSI
CTI - Ehler's Correlation Trend Indicator
✔️ CTI - Ehler's Correlation Trend Indicator
✔️ DOSC - Derivative Oscillator
EFI - Elder Ray's Force Index
FISHER - Fisher Transform
✔️ FISHER - Fisher Transform
✔️ EFI - Elder Ray's Force Index
FOSC - Forecast Oscillator
*GATOR - Williams Alliator Oscillator (Upper Jaw, Lower Jaw, Teeth)
*KDJ - KDJ Indicator (K, D, J lines)
+159
View File
@@ -0,0 +1,159 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// BETA: Beta Coefficient
/// A statistical measure that quantifies the volatility of an asset or portfolio
/// in relation to the overall market. Beta is used to assess the risk and return
/// characteristics of an investment.
/// </summary>
/// <remarks>
/// The Beta calculation process:
/// 1. Calculates covariance between asset and market returns
/// 2. Computes variance of market returns
/// 3. Divides covariance by market variance
///
/// Key characteristics:
/// - Measures relative volatility
/// - Beta > 1: More volatile than market
/// - Beta < 1: Less volatile than market
/// - Beta = 1: Same volatility as market
/// - Beta < 0: Inverse relationship with market
///
/// Formula:
/// β = Cov(Ra, Rm) / Var(Rm)
/// where:
/// Ra = asset returns
/// Rm = market returns
///
/// Market Applications:
/// - Risk assessment
/// - Portfolio management
/// - Asset allocation
/// - Performance analysis
/// - Hedging strategies
///
/// Sources:
/// https://en.wikipedia.org/wiki/Beta_(finance)
/// "Modern Portfolio Theory" - Harry Markowitz
///
/// Note: Assumes linear relationship between asset and market returns
/// </remarks>
[SkipLocalsInit]
public sealed class Beta : AbstractBase
{
private readonly int Period;
private readonly CircularBuffer _assetReturns;
private readonly CircularBuffer _marketReturns;
private const double Epsilon = 1e-10;
private const int MinimumPoints = 2;
/// <param name="period">The number of points to consider for beta calculation.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 2.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Beta(int period)
{
if (period < MinimumPoints)
{
throw new ArgumentOutOfRangeException(nameof(period),
"Period must be greater than or equal to 2 for beta calculation.");
}
Period = period;
WarmupPeriod = MinimumPoints;
_assetReturns = new CircularBuffer(period);
_marketReturns = new CircularBuffer(period);
Name = $"Beta(period={period})";
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of points to consider for beta calculation.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Beta(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_assetReturns.Clear();
_marketReturns.Clear();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Input.Value;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static double CalculateMean(ReadOnlySpan<double> values)
{
double sum = 0;
for (int i = 0; i < values.Length; i++)
{
sum += values[i];
}
return sum / values.Length;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static double CalculateCovariance(ReadOnlySpan<double> assetReturns, ReadOnlySpan<double> marketReturns, double assetMean, double marketMean)
{
double covariance = 0;
for (int i = 0; i < assetReturns.Length; i++)
{
covariance += (assetReturns[i] - assetMean) * (marketReturns[i] - marketMean);
}
return covariance / assetReturns.Length;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static double CalculateVariance(ReadOnlySpan<double> values, double mean)
{
double variance = 0;
for (int i = 0; i < values.Length; i++)
{
double diff = values[i] - mean;
variance += diff * diff;
}
return variance / values.Length;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(Input.IsNew);
_assetReturns.Add(Input.Value, Input.IsNew);
_marketReturns.Add(Input2.Value, Input.IsNew);
double beta = 0;
if (_assetReturns.Count >= MinimumPoints && _marketReturns.Count >= MinimumPoints)
{
ReadOnlySpan<double> assetValues = _assetReturns.GetSpan();
ReadOnlySpan<double> marketValues = _marketReturns.GetSpan();
double assetMean = CalculateMean(assetValues);
double marketMean = CalculateMean(marketValues);
double covariance = CalculateCovariance(assetValues, marketValues, assetMean, marketMean);
double marketVariance = CalculateVariance(marketValues, marketMean);
if (marketVariance > Epsilon)
{
beta = covariance / marketVariance;
}
}
IsHot = _assetReturns.Count >= Period && _marketReturns.Count >= Period;
return beta;
}
}
+163
View File
@@ -0,0 +1,163 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// CORR: Correlation Coefficient
/// A statistical measure that quantifies the strength and direction of the relationship
/// between two variables. The correlation coefficient ranges from -1 to 1, where 1 indicates
/// a perfect positive correlation, -1 indicates a perfect negative correlation, and 0 indicates
/// no correlation.
/// </summary>
/// <remarks>
/// The Correlation calculation process:
/// 1. Calculates mean of both variables
/// 2. Computes covariance between variables
/// 3. Calculates standard deviation of both variables
/// 4. Divides covariance by product of standard deviations
///
/// Key characteristics:
/// - Measures linear relationship strength
/// - Symmetric around zero
/// - Scale-independent measure
/// - Sensitive to outliers
/// - Useful for portfolio diversification
///
/// Formula:
/// ρ = Cov(X, Y) / (σX * σY)
/// where:
/// X, Y = variables
/// Cov = covariance
/// σ = standard deviation
///
/// Market Applications:
/// - Portfolio diversification
/// - Risk management
/// - Pairs trading
/// - Performance analysis
/// - Market sentiment analysis
///
/// Sources:
/// https://en.wikipedia.org/wiki/Correlation_coefficient
/// "Modern Portfolio Theory" - Harry Markowitz
///
/// Note: Assumes linear relationship between variables
/// </remarks>
[SkipLocalsInit]
public sealed class Corr : AbstractBase
{
private readonly int Period;
private readonly CircularBuffer _xValues;
private readonly CircularBuffer _yValues;
private const double Epsilon = 1e-10;
private const int MinimumPoints = 2;
/// <param name="period">The number of points to consider for correlation calculation.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 2.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Corr(int period)
{
if (period < MinimumPoints)
{
throw new ArgumentOutOfRangeException(nameof(period),
"Period must be greater than or equal to 2 for correlation calculation.");
}
Period = period;
WarmupPeriod = MinimumPoints;
_xValues = new CircularBuffer(period);
_yValues = new CircularBuffer(period);
Name = $"Corr(period={period})";
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of points to consider for correlation calculation.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Corr(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_xValues.Clear();
_yValues.Clear();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Input.Value;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static double CalculateMean(ReadOnlySpan<double> values)
{
double sum = 0;
for (int i = 0; i < values.Length; i++)
{
sum += values[i];
}
return sum / values.Length;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static double CalculateCovariance(ReadOnlySpan<double> xValues, ReadOnlySpan<double> yValues, double xMean, double yMean)
{
double covariance = 0;
for (int i = 0; i < xValues.Length; i++)
{
covariance += (xValues[i] - xMean) * (yValues[i] - yMean);
}
return covariance / xValues.Length;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static double CalculateStandardDeviation(ReadOnlySpan<double> values, double mean)
{
double sumSquaredDeviations = 0;
for (int i = 0; i < values.Length; i++)
{
double deviation = values[i] - mean;
sumSquaredDeviations += deviation * deviation;
}
return Math.Sqrt(sumSquaredDeviations / values.Length);
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(Input.IsNew);
_xValues.Add(Input.Value, Input.IsNew);
_yValues.Add(Input2.Value, Input.IsNew);
double correlation = 0;
if (_xValues.Count >= MinimumPoints && _yValues.Count >= MinimumPoints)
{
ReadOnlySpan<double> xValues = _xValues.GetSpan();
ReadOnlySpan<double> yValues = _yValues.GetSpan();
double xMean = CalculateMean(xValues);
double yMean = CalculateMean(yValues);
double covariance = CalculateCovariance(xValues, yValues, xMean, yMean);
double xStdDev = CalculateStandardDeviation(xValues, xMean);
double yStdDev = CalculateStandardDeviation(yValues, yMean);
if (xStdDev > Epsilon && yStdDev > Epsilon)
{
correlation = covariance / (xStdDev * yStdDev);
}
}
IsHot = _xValues.Count >= Period && _yValues.Count >= Period;
return correlation;
}
}
+4 -11
View File
@@ -45,7 +45,6 @@ public sealed class Percentile : AbstractBase
private readonly int Period;
private readonly double Percent;
private readonly CircularBuffer _buffer;
private const double Epsilon = 1e-10;
private const int MinimumPoints = 2;
/// <param name="period">The number of points to consider for percentile calculation.</param>
@@ -56,16 +55,10 @@ public sealed class Percentile : AbstractBase
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Percentile(int period, double percent)
{
if (period < MinimumPoints)
{
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.");
}
ArgumentOutOfRangeException.ThrowIfLessThan(period, MinimumPoints);
ArgumentOutOfRangeException.ThrowIfLessThan(percent, 0);
ArgumentOutOfRangeException.ThrowIfGreaterThan(percent, 100);
Period = period;
Percent = percent;
WarmupPeriod = MinimumPoints; // Minimum number of points needed for percentile calculation
+167
View File
@@ -0,0 +1,167 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// THEIL: Theil's U Statistics (U1, U2)
/// A statistical measure that quantifies the accuracy of forecasts compared to actual values
/// and naive forecasts.
/// </summary>
/// <remarks>
/// The Theil's U calculation process:
/// 1. Calculate U1 statistic (relative accuracy)
/// 2. Calculate U2 statistic (comparison with naive forecast)
///
/// Key characteristics:
/// - U1 ranges from 0 to 1, with 0 indicating perfect forecast
/// - U2 &lt; 1: forecast better than naive forecast
/// - U2 = 1: forecast equal to naive forecast
/// - U2 &gt; 1: forecast worse than naive forecast
///
/// Formula:
/// U1 = √[Σ(Ft - At)² / Σ(At)²]
/// U2 = √[Σ(Ft - At)² / Σ(At - At-1)²]
/// where:
/// Ft = forecasted value
/// At = actual value
/// At-1 = previous actual value
///
/// Market Applications:
/// - Evaluating forecast accuracy
/// - Comparing forecasting models
/// - Assessing forecasting methods
/// - Model selection
/// - Performance analysis
///
/// Sources:
/// https://en.wikipedia.org/wiki/Theil%27s_U
/// "Forecasting: Principles and Practice" - Rob J Hyndman
///
/// Note: Should be used alongside other accuracy measures
/// </remarks>
[SkipLocalsInit]
public sealed class Theil : AbstractBase
{
private readonly int Period;
private readonly CircularBuffer _actual;
private readonly CircularBuffer _forecast;
private const int MinimumPoints = 2;
/// <summary>
/// Gets the U2 statistic comparing forecast with naive forecast
/// </summary>
public double U2 { get; private set; }
/// <param name="period">The number of points to consider for Theil's U calculation.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 2.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Theil(int period)
{
if (period < MinimumPoints)
{
throw new ArgumentOutOfRangeException(nameof(period),
"Period must be greater than or equal to 2 for Theil's U calculation.");
}
Period = period;
WarmupPeriod = MinimumPoints;
_actual = new CircularBuffer(period);
_forecast = new CircularBuffer(period);
Name = $"Theil(period={period})";
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of points to consider for Theil's U calculation.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Theil(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_actual.Clear();
_forecast.Clear();
U2 = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Input.Value;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static double CalculateSquaredSum(ReadOnlySpan<double> values)
{
double sum = 0;
for (int i = 0; i < values.Length; i++)
{
sum += values[i] * values[i];
}
return sum;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static double CalculateSquaredErrorSum(ReadOnlySpan<double> forecast, ReadOnlySpan<double> actual)
{
double sum = 0;
for (int i = 0; i < forecast.Length; i++)
{
double error = forecast[i] - actual[i];
sum += error * error;
}
return sum;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static double CalculateNaiveSquaredErrorSum(ReadOnlySpan<double> actual)
{
double sum = 0;
for (int i = 1; i < actual.Length; i++)
{
double error = actual[i] - actual[i - 1];
sum += error * error;
}
return sum;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(Input.IsNew);
_actual.Add(Input.Value, Input.IsNew);
_forecast.Add(Input2.Value, Input.IsNew);
double u1 = 0;
if (_actual.Count >= MinimumPoints && _forecast.Count >= MinimumPoints)
{
ReadOnlySpan<double> actualValues = _actual.GetSpan();
ReadOnlySpan<double> forecastValues = _forecast.GetSpan();
double squaredErrorSum = CalculateSquaredErrorSum(forecastValues, actualValues);
double squaredActualSum = CalculateSquaredSum(actualValues);
double naiveSquaredErrorSum = CalculateNaiveSquaredErrorSum(actualValues);
if (squaredActualSum > double.Epsilon)
{
u1 = Math.Sqrt(squaredErrorSum / squaredActualSum);
}
if (naiveSquaredErrorSum > double.Epsilon)
{
U2 = Math.Sqrt(squaredErrorSum / naiveSquaredErrorSum);
}
}
IsHot = _actual.Count >= Period && _forecast.Count >= Period;
return u1;
}
}
+185
View File
@@ -0,0 +1,185 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// TSF: Time Series Forecast
/// A statistical indicator that provides a linear regression forecast of future values
/// based on historical data. It includes both the forecast value and a confidence interval.
/// </summary>
/// <remarks>
/// The Time Series Forecast calculation process:
/// 1. Calculates linear regression on the input data
/// 2. Extrapolates the regression line to forecast future values
/// 3. Computes confidence intervals based on the standard error of the forecast
///
/// Key characteristics:
/// - Provides point forecast and confidence interval
/// - Based on linear regression principles
/// - Assumes trend continuity
/// - Sensitive to recent data changes
/// - Useful for short-term predictions
///
/// Formula:
/// Forecast = a + b * (n + 1)
/// where:
/// a = y-intercept
/// b = slope
/// n = number of periods
///
/// Confidence Interval = Forecast ± (t * SE)
/// where:
/// t = t-value for desired confidence level
/// SE = Standard Error of the forecast
///
/// Market Applications:
/// - Price target estimation
/// - Trend analysis
/// - Risk assessment
/// - Trading strategy development
/// - Market behavior prediction
///
/// Sources:
/// https://en.wikipedia.org/wiki/Time_series
/// "Forecasting: Principles and Practice" - Rob J Hyndman and George Athanasopoulos
///
/// Note: Assumes linear trend in the data and may not capture non-linear patterns
/// </remarks>
[SkipLocalsInit]
public sealed class Tsf : AbstractBase
{
private readonly int Period;
private readonly CircularBuffer _values;
private const int MinimumPoints = 2;
/// <summary>
/// The forecasted value for the next period.
/// </summary>
public double Forecast { get; private set; }
/// <summary>
/// The lower bound of the confidence interval.
/// </summary>
public double LowerBound { get; private set; }
/// <summary>
/// The upper bound of the confidence interval.
/// </summary>
public double UpperBound { get; private set; }
/// <param name="period">The number of historical data points to consider for forecasting.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 2.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Tsf(int period)
{
if (period < MinimumPoints)
{
throw new ArgumentOutOfRangeException(nameof(period),
"Period must be greater than or equal to 2 for time series forecasting.");
}
Period = period;
WarmupPeriod = MinimumPoints;
_values = new CircularBuffer(period);
Name = $"TSF(period={period})";
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of historical data points to consider for forecasting.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Tsf(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_values.Clear();
Forecast = 0;
LowerBound = 0;
UpperBound = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Input.Value;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static (double slope, double intercept) CalculateLinearRegression(ReadOnlySpan<double> values)
{
int n = values.Length;
double sumX = 0, sumY = 0, sumXY = 0, sumX2 = 0;
for (int i = 0; i < n; i++)
{
double x = i + 1;
double y = values[i];
sumX += x;
sumY += y;
sumXY += x * y;
sumX2 += x * x;
}
double slope = ((n * sumXY) - (sumX * sumY)) / ((n * sumX2) - (sumX * sumX));
double intercept = (sumY - (slope * sumX)) / n;
return (slope, intercept);
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static double CalculateStandardError(ReadOnlySpan<double> values, double slope, double intercept)
{
int n = values.Length;
double sumSquaredResiduals = 0;
for (int i = 0; i < n; i++)
{
double x = i + 1;
double y = values[i];
double predicted = (slope * x) + intercept;
double residual = y - predicted;
sumSquaredResiduals += residual * residual;
}
return Math.Sqrt(sumSquaredResiduals / (n - 2));
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(Input.IsNew);
_values.Add(Input.Value, Input.IsNew);
if (_values.Count >= MinimumPoints)
{
ReadOnlySpan<double> values = _values.GetSpan();
var (slope, intercept) = CalculateLinearRegression(values);
// Calculate forecast for the next period
Forecast = (slope * (Period + 1)) + intercept;
// Calculate standard error
double standardError = CalculateStandardError(values, slope, intercept);
// Calculate confidence interval (using t-distribution with n-2 degrees of freedom)
double tValue = 1.96; // Approximation for 95% confidence interval
double marginOfError = tValue * standardError * Math.Sqrt(1 + (1.0 / Period));
LowerBound = Forecast - marginOfError;
UpperBound = Forecast + marginOfError;
}
IsHot = _values.Count >= Period;
return Forecast;
}
}
+31 -21
View File
@@ -1,22 +1,32 @@
# Statistics indicators
Done: 13, Todo: 6
# Statistics
*BETA - Beta coefficient (Beta, R-squared)
*CORR - Correlation Coefficient (Correlation, P-value)
✔️ CURVATURE - Rate of Change in Direction or Slope
✔️ ENTROPY - Measure of Uncertainty or Disorder
✔️ HURST - Hurst Exponent
✔️ KURTOSIS - Measure of Tails/Peakedness
✔️ MAX - Maximum with exponential decay
✔️ MEDIAN - Middle value
✔️ MIN - Minimum with exponential decay
✔️ MODE - Most Frequent Value
✔️ PERCENTILE - Rank Order
*RSQUARED - Coefficient of Determination (R-squared, Adjusted R-squared)
✔️ SKEW - Skewness, asymmetry of distribution
✔️ SLOPE - Rate of Change, Linear Regression
✔️ STDDEV - Standard Deviation, Measure of Spread
*THEIL - Theil's U Statistics (U1, U2)
*TSF - Time Series Forecast (Forecast, Confidence Interval)
✔️ VARIANCE - Average of Squared Deviations
✔️ ZSCORE - Standardized Score
Statistical functions and indicators for financial analysis.
## Implemented
- [Beta](Beta.cs) - Beta coefficient measuring volatility relative to market
- [Corr](Corr.cs) - Correlation coefficient between two series
- [Curvature](Curvature.cs) - Curvature of a time series
- [Entropy](Entropy.cs) - Information entropy of a series
- [Hurst](Hurst.cs) - Hurst exponent for trend strength
- [Kurtosis](Kurtosis.cs) - Kurtosis measuring tail extremity
- [Max](Max.cs) - Maximum value over period
- [Median](Median.cs) - Median value over period
- [Min](Min.cs) - Minimum value over period
- [Mode](Mode.cs) - Mode (most frequent value)
- [Percentile](Percentile.cs) - Percentile rank calculation
- [Skew](Skew.cs) - Skewness measuring distribution asymmetry
- [Slope](Slope.cs) - Linear regression slope
- [Stddev](Stddev.cs) - Standard deviation
- [Theil](Theil.cs) - Theil's U statistics for forecast accuracy
- [Tsf](Tsf.cs) - Time series forecast
- [Variance](Variance.cs) - Statistical variance
- [Zscore](Zscore.cs) - Z-score standardization
## Planned
- Cointegration - Test for cointegrated series
- Granger - Granger causality test
- Jarque-Bera - Normality test
- Kendall - Kendall rank correlation
- Spearman - Spearman rank correlation
+1 -3
View File
@@ -50,7 +50,7 @@ public class AfirmaIndicator : Indicator, IWatchlistIndicator
Name = "AFIRMA - Adaptive Finite Impulse Response Moving Average";
Description = "Adaptive Finite Impulse Response Moving Average with ARMA component";
Series = new(name: $"AFIRMA {Taps}:{Periods}:{Window}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
Series = new(name: $"AFIRMA {Taps}:{Periods}:{Window}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
@@ -76,7 +76,5 @@ public class AfirmaIndicator : Indicator, IWatchlistIndicator
{
base.OnPaintChart(args);
this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
this.DrawText(args, Description);
}
}
+1 -2
View File
@@ -46,7 +46,7 @@ public class AlmaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "ALMA - Arnaud Legoux Moving Average";
Description = "Arnaud Legoux Moving Average";
Series = new(name: $"ALMA {Period}:{Offset:F2}:{Sigma:F0}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
Series = new(name: $"ALMA {Period}:{Offset:F2}:{Sigma:F0}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
@@ -70,6 +70,5 @@ public class AlmaIndicator : Indicator, IWatchlistIndicator
{
base.OnPaintChart(args);
this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
this.DrawText(args, Description);
}
}
+1 -2
View File
@@ -40,7 +40,7 @@ public class DemaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "DEMA - Double Exponential Moving Average";
Description = "A faster-responding moving average that reduces lag by applying the EMA twice.";
Series = new(name: $"DEMA {Period}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
Series = new(name: $"DEMA {Period}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
@@ -64,6 +64,5 @@ public class DemaIndicator : Indicator, IWatchlistIndicator
{
base.OnPaintChart(args);
this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
this.DrawText(args, Description);
}
}
+1 -2
View File
@@ -43,7 +43,7 @@ public class DsmaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "DSMA - Deviation Scaled Moving Average";
Description = "A moving average that adjusts its responsiveness based on price deviations from the mean.";
Series = new(name: $"DSMA {Period}:{Scale:F2}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
Series = new(name: $"DSMA {Period}:{Scale:F2}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
@@ -68,6 +68,5 @@ public class DsmaIndicator : Indicator, IWatchlistIndicator
{
base.OnPaintChart(args);
this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
this.DrawText(args, Description);
}
}
+1 -2
View File
@@ -40,7 +40,7 @@ public class DwmaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "DWMA - Double Weighted Moving Average";
Description = "A moving average that applies double weighting to recent prices for increased responsiveness.";
Series = new(name: $"DWMA {Period}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
Series = new(name: $"DWMA {Period}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
@@ -64,6 +64,5 @@ public class DwmaIndicator : Indicator, IWatchlistIndicator
{
base.OnPaintChart(args);
this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
this.DrawText(args, Description);
}
}
+2 -3
View File
@@ -7,7 +7,7 @@ public class EmaIndicator : Indicator, IWatchlistIndicator
{
[InputParameter("Periods", sortIndex: 1, 1, 1000, 1, 0)]
public int Periods { get; set; } = 10;
[InputParameter("Use SMA for warmup period", sortIndex: 2)]
[InputParameter("Use SMA for warmup period", sortIndex: 2)]
public bool UseSMA { get; set; } = false;
[InputParameter("Data source", sortIndex: 3, variants: [
@@ -42,7 +42,7 @@ public class EmaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "EMA - Exponential Moving Average";
Description = "Exponential Moving Average";
Series = new(name: $"EMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
Series = new(name: $"EMA {Periods}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
@@ -66,6 +66,5 @@ public class EmaIndicator : Indicator, IWatchlistIndicator
{
base.OnPaintChart(args);
this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
this.DrawText(args, Description);
}
}
+1 -2
View File
@@ -40,7 +40,7 @@ public class EpmaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "EPMA - Exponential Percentage Moving Average";
Description = "Exponential Percentage Moving Average";
Series = new(name: $"EPMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
Series = new(name: $"EPMA {Periods}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
@@ -64,6 +64,5 @@ public class EpmaIndicator : Indicator, IWatchlistIndicator
{
base.OnPaintChart(args);
this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
this.DrawText(args, Description);
}
}
+1 -2
View File
@@ -40,7 +40,7 @@ public class FramaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "FRAMA - Fractal Adaptive Moving Average";
Description = "Fractal Adaptive Moving Average";
Series = new(name: $"FRAMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
Series = new(name: $"FRAMA {Periods}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
@@ -64,6 +64,5 @@ public class FramaIndicator : Indicator, IWatchlistIndicator
{
base.OnPaintChart(args);
this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
this.DrawText(args, Description);
}
}
+1 -2
View File
@@ -40,7 +40,7 @@ public class FwmaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "FWMA - Fibonacci Weighted Moving Average";
Description = "Fibonacci Weighted Moving Average";
Series = new(name: $"FWMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
Series = new(name: $"FWMA {Periods}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
@@ -64,6 +64,5 @@ public class FwmaIndicator : Indicator, IWatchlistIndicator
{
base.OnPaintChart(args);
this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
this.DrawText(args, Description);
}
}
+1 -2
View File
@@ -40,7 +40,7 @@ public class GmaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "GMA - Gaussian Moving Average";
Description = "Gaussian Moving Average";
Series = new(name: $"GMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
Series = new(name: $"GMA {Periods}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
@@ -64,6 +64,5 @@ public class GmaIndicator : Indicator, IWatchlistIndicator
{
base.OnPaintChart(args);
this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
this.DrawText(args, Description);
}
}
+1 -2
View File
@@ -40,7 +40,7 @@ public class HmaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "HMA - Hull Moving Average";
Description = "Hull Moving Average";
Series = new(name: $"HMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
Series = new(name: $"HMA {Periods}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
@@ -64,6 +64,5 @@ public class HmaIndicator : Indicator, IWatchlistIndicator
{
base.OnPaintChart(args);
this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
this.DrawText(args, Description);
}
}
+1 -2
View File
@@ -37,7 +37,7 @@ public class HtitIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "HTIT - Hilbert Transform Instantaneous Trendline";
Description = "Hilbert Transform Instantaneous Trendline (Note: This indicator may not be fully functional)";
Series = new(name: "HTIT", color: Color.Yellow, width: 2, style: LineStyle.Solid);
Series = new(name: "HTIT", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
@@ -61,6 +61,5 @@ public class HtitIndicator : Indicator, IWatchlistIndicator
{
base.OnPaintChart(args);
this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
this.DrawText(args, Description);
}
}
+2 -3
View File
@@ -49,13 +49,13 @@ public class HwmaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "HWMA - Holt-Winter Moving Average";
Description = "Holt-Winter Moving Average";
Series = new(name: $"HWMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
Series = new(name: $"HWMA {Periods}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
protected override void OnInit()
{
if (NA == 0 && NB == 0 && NC == 0)
if ((NA, NB, NC) == (0, 0, 0))
{
ma = new Hwma(Periods);
}
@@ -80,6 +80,5 @@ public class HwmaIndicator : Indicator, IWatchlistIndicator
{
base.OnPaintChart(args);
this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
this.DrawText(args, Description);
}
}
+3 -4
View File
@@ -11,7 +11,7 @@ public class JmaIndicator : Indicator, IWatchlistIndicator
[InputParameter("Phase", sortIndex: 2, -100, 100, 1, 0)]
public int Phase { get; set; } = 0;
[InputParameter("Beta factor", sortIndex: 3, minimum: 0, maximum:5 , increment: 0.01, decimalPlaces: 2)]
[InputParameter("Beta factor", sortIndex: 3, minimum: 0, maximum: 5, increment: 0.01, decimalPlaces: 2)]
public double Factor { get; set; } = 0.45;
[InputParameter("Data source", sortIndex: 4, variants: [
@@ -34,7 +34,7 @@ public class JmaIndicator : Indicator, IWatchlistIndicator
private Jma? ma;
protected LineSeries? Series;
protected string? SourceName;
public int MinHistoryDepths => Math.Max(65,Periods * 2);
public int MinHistoryDepths => Math.Max(65, Periods * 2);
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
public override string ShortName => $"JMA {Periods}:{Phase}:{Factor:F2}:{SourceName}";
@@ -46,7 +46,7 @@ public class JmaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "JMA - Jurik Moving Average";
Description = "Jurik Moving Average (Note: This indicator may have consistency issues)";
Series = new(name: $"JMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
Series = new(name: $"JMA {Periods}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
@@ -70,6 +70,5 @@ public class JmaIndicator : Indicator, IWatchlistIndicator
{
base.OnPaintChart(args);
this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
this.DrawText(args, Description);
}
}
+1 -2
View File
@@ -46,7 +46,7 @@ public class KamaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "KAMA - Kaufman's Adaptive Moving Average";
Description = "Kaufman's Adaptive Moving Average";
Series = new(name: $"KAMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
Series = new(name: $"KAMA {Periods}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
@@ -70,6 +70,5 @@ public class KamaIndicator : Indicator, IWatchlistIndicator
{
base.OnPaintChart(args);
this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
this.DrawText(args, Description);
}
}
+1 -2
View File
@@ -40,7 +40,7 @@ public class LtmaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "LTMA - Laguerre Time Moving Average";
Description = "Laguerre Time Moving Average";
Series = new(name: $"LTMA {Gamma}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
Series = new(name: $"LTMA {Gamma}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
@@ -64,6 +64,5 @@ public class LtmaIndicator : Indicator, IWatchlistIndicator
{
base.OnPaintChart(args);
this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
this.DrawText(args, Description);
}
}
+1 -2
View File
@@ -43,7 +43,7 @@ public class MaafIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "MAAF - Median Adaptive Averaging Filter";
Description = "Median Adaptive Averaging Filter (Note: This indicator may have consistency issues)";
Series = new(name: $"MAAF {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
Series = new(name: $"MAAF {Periods}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
@@ -67,6 +67,5 @@ public class MaafIndicator : Indicator, IWatchlistIndicator
{
base.OnPaintChart(args);
this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
this.DrawText(args, Description);
}
}
+1 -2
View File
@@ -44,7 +44,7 @@ public class MamaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "MAMA - MESA Adaptive Moving Average";
Description = "MESA Adaptive Moving Average";
MamaSeries = new(name: "MAMA", color: Color.Yellow, width: 2, style: LineStyle.Solid);
MamaSeries = new(name: "MAMA", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
FamaSeries = new(name: "FAMA", color: Color.Red, width: 2, style: LineStyle.Solid);
AddLineSeries(MamaSeries);
AddLineSeries(FamaSeries);
@@ -73,6 +73,5 @@ public class MamaIndicator : Indicator, IWatchlistIndicator
base.OnPaintChart(args);
this.PaintSmoothCurve(args, MamaSeries!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
this.PaintSmoothCurve(args, FamaSeries!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
this.DrawText(args, Description);
}
}
+1 -2
View File
@@ -43,7 +43,7 @@ public class MgdiIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "MGDI - McGinley Dynamic Indicator";
Description = "McGinley Dynamic Indicator";
Series = new(name: $"MGDI {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
Series = new(name: $"MGDI {Periods}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
@@ -67,6 +67,5 @@ public class MgdiIndicator : Indicator, IWatchlistIndicator
{
base.OnPaintChart(args);
this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
this.DrawText(args, Description);
}
}
+1 -2
View File
@@ -40,7 +40,7 @@ public class MmaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "MMA - Modified Moving Average";
Description = "Modified Moving Average";
Series = new(name: $"MMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
Series = new(name: $"MMA {Periods}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
@@ -64,6 +64,5 @@ public class MmaIndicator : Indicator, IWatchlistIndicator
{
base.OnPaintChart(args);
this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
this.DrawText(args, Description);
}
}
+1 -2
View File
@@ -40,7 +40,7 @@ public class PwmaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "PWMA - Pascal's Weighted Moving Average";
Description = "Pascal's Weighted Moving Average";
Series = new(name: $"PWMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
Series = new(name: $"PWMA {Periods}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
@@ -64,6 +64,5 @@ public class PwmaIndicator : Indicator, IWatchlistIndicator
{
base.OnPaintChart(args);
this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
this.DrawText(args, Description);
}
}
+1 -2
View File
@@ -43,7 +43,7 @@ public class RemaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "REMA - Regularized Exponential Moving Average";
Description = "Regularized Exponential Moving Average";
Series = new(name: $"REMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
Series = new(name: $"REMA {Periods}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
@@ -67,6 +67,5 @@ public class RemaIndicator : Indicator, IWatchlistIndicator
{
base.OnPaintChart(args);
this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
this.DrawText(args, Description);
}
}
+1 -2
View File
@@ -40,7 +40,7 @@ public class RmaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "RMA - Relative Moving Average (Wilder's Moving Average)";
Description = "Relative Moving Average, also known as Wilder's Moving Average";
Series = new(name: $"RMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
Series = new(name: $"RMA {Periods}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
@@ -64,6 +64,5 @@ public class RmaIndicator : Indicator, IWatchlistIndicator
{
base.OnPaintChart(args);
this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
this.DrawText(args, Description);
}
}
+1 -2
View File
@@ -40,7 +40,7 @@ public class SinemaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "SINEMA - Sine-Weighted Moving Average";
Description = "Sine-Weighted Moving Average";
Series = new(name: $"SINEMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
Series = new(name: $"SINEMA {Periods}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
@@ -64,6 +64,5 @@ public class SinemaIndicator : Indicator, IWatchlistIndicator
{
base.OnPaintChart(args);
this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
this.DrawText(args, Description);
}
}
+2 -3
View File
@@ -5,7 +5,7 @@ namespace QuanTAlib;
public class SmaIndicator : Indicator, IWatchlistIndicator
{
[InputParameter("Periods", sortIndex: 1, 1, 1000, 1, 0)]
[InputParameter("Period", sortIndex: 1, 1, 1000, 1, 0)]
public int Period { get; set; } = 14;
[InputParameter("Data source", sortIndex: 2, variants: [
@@ -39,7 +39,7 @@ public class SmaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "SMA - Simple Moving Average";
Description = "Simple Moving Average";
Series = new(name: $"SMA {Period}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
Series = new(name: $"SMA {Period}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
@@ -67,6 +67,5 @@ public class SmaIndicator : Indicator, IWatchlistIndicator
{
base.OnPaintChart(args);
this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
this.DrawText(args, error!.Value.ToString());
}
}
+1 -2
View File
@@ -40,7 +40,7 @@ public class SmmaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "SMMA - Smoothed Moving Average";
Description = "Smoothed Moving Average";
Series = new(name: $"SMMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
Series = new(name: $"SMMA {Periods}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
@@ -64,6 +64,5 @@ public class SmmaIndicator : Indicator, IWatchlistIndicator
{
base.OnPaintChart(args);
this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
this.DrawText(args, Description);
}
}
+1 -2
View File
@@ -46,7 +46,7 @@ public class T3Indicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "T3 - Tillson T3 Moving Average";
Description = "Tillson T3 Moving Average";
Series = new(name: $"T3 {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
Series = new(name: $"T3 {Periods}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
@@ -70,6 +70,5 @@ public class T3Indicator : Indicator, IWatchlistIndicator
{
base.OnPaintChart(args);
this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
this.DrawText(args, Description);
}
}
+1 -2
View File
@@ -40,7 +40,7 @@ public class TemaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "TEMA - Triple Exponential Moving Average";
Description = "Triple Exponential Moving Average";
Series = new(name: $"TEMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
Series = new(name: $"TEMA {Periods}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
@@ -64,6 +64,5 @@ public class TemaIndicator : Indicator, IWatchlistIndicator
{
base.OnPaintChart(args);
this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
this.DrawText(args, Description);
}
}
+1 -2
View File
@@ -40,7 +40,7 @@ public class TrimaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "TRIMA - Triangular Moving Average";
Description = "Triangular Moving Average";
Series = new(name: $"TRIMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
Series = new(name: $"TRIMA {Periods}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
@@ -64,6 +64,5 @@ public class TrimaIndicator : Indicator, IWatchlistIndicator
{
base.OnPaintChart(args);
this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
this.DrawText(args, Description);
}
}
+1 -2
View File
@@ -46,7 +46,7 @@ public class VidyaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "VIDYA - Variable Index Dynamic Average";
Description = "Variable Index Dynamic Average";
Series = new(name: $"VIDYA {ShortPeriod}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
Series = new(name: $"VIDYA {ShortPeriod}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
@@ -70,6 +70,5 @@ public class VidyaIndicator : Indicator, IWatchlistIndicator
{
base.OnPaintChart(args);
this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
this.DrawText(args, Description);
}
}
+2 -2
View File
@@ -40,7 +40,7 @@ public class WmaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "WMA - Weighted Moving Average";
Description = "Weighted Moving Average";
Series = new(name: $"WMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
Series = new(name: $"WMA {Periods}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
@@ -59,11 +59,11 @@ public class WmaIndicator : Indicator, IWatchlistIndicator
Series!.SetValue(result.Value);
Series!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here
}
#pragma warning disable CA1416 // Validate platform compatibility
public override void OnPaintChart(PaintChartEventArgs args)
{
base.OnPaintChart(args);
this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
this.DrawText(args, Description);
}
}
+1 -2
View File
@@ -41,7 +41,7 @@ public class ZlemaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "ZLEMA - Zero Lag Exponential Moving Average";
Description = "Zero Lag Exponential Moving Average";
Series = new(name: $"ZLEMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
Series = new(name: $"ZLEMA {Periods}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
@@ -67,6 +67,5 @@ public class ZlemaIndicator : Indicator, IWatchlistIndicator
{
base.OnPaintChart(args);
this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
this.DrawText(args, err!.Value.ToString());
}
}
@@ -0,0 +1,97 @@
using System.Drawing;
using System.Linq;
using TradingPlatform.BusinessLayer;
namespace QuanTAlib;
public class ConvolutionIndicator : Indicator, IWatchlistIndicator
{
[InputParameter("Kernel (comma/space/semicolon separated numbers)", sortIndex: 1)]
public string KernelString { get; set; } = "0.25, 0.5, 0.25, -0.5";
[InputParameter("Data source", sortIndex: 2, variants: [
"Open", SourceType.Open,
"High", SourceType.High,
"Low", SourceType.Low,
"Close", SourceType.Close,
"HL/2 (Median)", SourceType.HL2,
"OC/2 (Midpoint)", SourceType.OC2,
"OHL/3 (Mean)", SourceType.OHL3,
"HLC/3 (Typical)", SourceType.HLC3,
"OHLC/4 (Average)", SourceType.OHLC4,
"HLCC/4 (Weighted)", SourceType.HLCC4
])]
public SourceType Source { get; set; } = SourceType.Close;
[InputParameter("Show cold values", sortIndex: 21)]
public bool ShowColdValues { get; set; } = true;
private Convolution? conv;
private Mape? error;
protected LineSeries? Series;
protected string? SourceName;
private double[]? kernel;
public int MinHistoryDepths => kernel?.Length ?? 3;
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
public ConvolutionIndicator()
{
OnBackGround = true;
SeparateWindow = false;
SourceName = Source.ToString();
Name = "CONV - Convolution Filter";
Description = "Convolution Filter with custom kernel";
kernel = ParseKernel(KernelString);
Series = new(name: $"CONV {string.Join(",", kernel.Select(x => x.ToString("F2")))}",
color: IndicatorExtensions.Averages,
width: 2,
style: LineStyle.Solid);
AddLineSeries(Series);
}
private static double[] ParseKernel(string kernelStr)
{
// Split on common delimiters: comma, semicolon, space, tab, pipe
var numbers = kernelStr.Split(new[] { ',', ';', ' ', '\t', '|' },
StringSplitOptions.RemoveEmptyEntries |
StringSplitOptions.TrimEntries);
var kernel = new double[numbers.Length];
for (int i = 0; i < numbers.Length; i++)
{
if (!double.TryParse(numbers[i], out kernel[i]))
{
// Default to simple 3-point moving average if parsing fails
return new double[] { 0.25, 0.5, 0.25, -0.5 };
}
}
return kernel;
}
protected override void OnInit()
{
kernel = ParseKernel(KernelString);
conv = new Convolution(kernel);
error = new(kernel.Length);
SourceName = Source.ToString();
base.OnInit();
}
protected override void OnUpdate(UpdateArgs args)
{
TValue input = this.GetInputValue(args, Source);
TValue result = conv!.Calc(input);
error!.Calc(input, result);
Series!.SetMarker(0, Color.Transparent);
Series!.SetValue(result.Value);
}
public override string ShortName => $"CONV {KernelString}:{SourceName}";
public override void OnPaintChart(PaintChartEventArgs args)
{
base.OnPaintChart(args);
this.PaintSmoothCurve(args, Series!, kernel!.Length, showColdValues: ShowColdValues, tension: 0.2);
}
}
@@ -49,7 +49,7 @@ public class QemaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "QEMA - Quadruple Exponential Moving Average";
Description = "Quadruple Exponential Moving Average";
Series = new(name: $"QEMA {K1},{K2},{K3},{K4}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
Series = new(name: $"QEMA {K1},{K2},{K3},{K4}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
@@ -73,6 +73,5 @@ public class QemaIndicator : Indicator, IWatchlistIndicator
{
base.OnPaintChart(args);
this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
this.DrawText(args, Description);
}
}
@@ -37,7 +37,7 @@ public class TestIndicator : Indicator, IWatchlistIndicator
SeparateWindow = false;
Name = "TEST";
Description = "test and test and test and more test.";
Series = new(name: $"{Name}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
Series = new(name: $"{Name}", color: IndicatorExtensions.Volatility, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
@@ -59,7 +59,5 @@ public class TestIndicator : Indicator, IWatchlistIndicator
{
base.OnPaintChart(args);
this.PaintSmoothCurve(args, Series!, ma!.WarmupPeriod, ShowColdValues, tension: 0.2);
this.DrawText(args, Description);
}
}
+30
View File
@@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<AssemblyName>Experiments</AssemblyName>
<AlgoType>Indicator</AlgoType>
<OutputPath>bin\$(Configuration)\</OutputPath>
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Drawing.Common" Version="8.0.0" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\*.cs" />
<Compile Include="*.cs" />
<Compile Include="..\..\lib\**\*.cs" Exclude="..\..\lib\bin\**;..\..\lib\obj\**" />
<Reference Include="TradingPlatform.BusinessLayer">
<HintPath>..\..\.github\TradingPlatform.BusinessLayer.dll</HintPath>
</Reference>
<None Include="..\..\.github\TradingPlatform.BusinessLayer.xml">
<Link>TradingPlatform.BusinessLayer.xml</Link>
</None>
</ItemGroup>
<Target Name="CopyCustomContent" AfterTargets="AfterBuild"
Condition="'$(IsLocalBuild)' == 'true' AND $([MSBuild]::IsOSPlatform('Windows'))">
<Copy SourceFiles="$(OutputPath)\Experiments.dll" DestinationFolder="$(QuantowerRoot)\Settings\Scripts\Indicators\QuanTAlib\Experiments" />
</Target>
</Project>
+8 -5
View File
@@ -16,6 +16,14 @@ public enum MaType
public static class IndicatorExtensions
{
public static readonly Color Averages = Color.FromArgb(255, 255, 128); // #FFFF80 - Yellow
public static readonly Color Volume = Color.FromArgb(128, 255, 128); // #80FF80 - Green
public static readonly Color Volatility = Color.FromArgb(255, 128, 128); // #FF8080 - Red
public static readonly Color Statistics = Color.FromArgb(128, 128, 255); // #8080FF - Blue
public static readonly Color Oscillators = Color.FromArgb(255, 128, 255); // #FF80FF - Magenta
public static readonly Color Momentum = Color.FromArgb(128, 255, 255); // #80FFFF - Cyan
public static readonly Color Experiments = Color.FromArgb(255, 165, 0); // #FFA500 - Orange
public static TValue GetInputValue(this Indicator indicator, UpdateArgs args, SourceType source)
{
var historicalData = indicator.HistoricalData;
@@ -179,7 +187,6 @@ public static class IndicatorExtensions
}
}
public static void DrawText(this Indicator indicator, PaintChartEventArgs args, string text)
{
if (indicator.CurrentChart == null)
@@ -210,7 +217,3 @@ public static class IndicatorExtensions
};
}
}
+53
View File
@@ -0,0 +1,53 @@
using System.Drawing;
using TradingPlatform.BusinessLayer;
namespace QuanTAlib;
public class AdxIndicator : Indicator, IWatchlistIndicator
{
[InputParameter("Periods", sortIndex: 1, 1, 2000, 1, 0)]
public int Periods { get; set; } = 14;
[InputParameter("Show cold values", sortIndex: 21)]
public bool ShowColdValues { get; set; } = true;
private Adx? adx;
protected LineSeries? AdxSeries;
public int MinHistoryDepths => Math.Max(5, Periods * 3); // Need extra periods for ADX calculation
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
public AdxIndicator()
{
Name = "ADX - Average Directional Movement Index";
Description = "Measures the strength of a trend, regardless of its direction.";
SeparateWindow = true;
AdxSeries = new($"ADX {Periods}", color: IndicatorExtensions.Momentum, 2, LineStyle.Solid);
AddLineSeries(AdxSeries);
}
protected override void OnInit()
{
adx = new Adx(Periods);
base.OnInit();
}
protected override void OnUpdate(UpdateArgs args)
{
TBar input = IndicatorExtensions.GetInputBar(this, args);
TValue result = adx!.Calc(input);
AdxSeries!.SetValue(result.Value);
AdxSeries!.SetMarker(0, Color.Transparent);
}
#pragma warning disable CA1416 // Validate platform compatibility
public override string ShortName => $"ADX ({Periods})";
public override void OnPaintChart(PaintChartEventArgs args)
{
base.OnPaintChart(args);
this.PaintSmoothCurve(args, AdxSeries!, adx!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
}
}
+55
View File
@@ -0,0 +1,55 @@
using System.Drawing;
using TradingPlatform.BusinessLayer;
namespace QuanTAlib;
public class AdxrIndicator : Indicator, IWatchlistIndicator
{
[InputParameter("Periods", sortIndex: 1, 1, 2000, 1, 0)]
public int Periods { get; set; } = 14;
[InputParameter("Show cold values", sortIndex: 21)]
public bool ShowColdValues { get; set; } = true;
private Adxr? adxr;
protected LineSeries? AdxrSeries;
public int MinHistoryDepths => Math.Max(5, Periods * 4); // Need extra periods for ADXR calculation
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
public AdxrIndicator()
{
Name = "ADXR - Average Directional Movement Index Rating";
Description = "Measures trend strength by comparing current ADX with historical ADX values.";
SeparateWindow = true;
AdxrSeries = new($"ADXR {Periods}", Color.Blue, 2, LineStyle.Solid);
AddLineSeries(AdxrSeries);
}
protected override void OnInit()
{
adxr = new Adxr(Periods);
base.OnInit();
}
protected override void OnUpdate(UpdateArgs args)
{
TBar input = IndicatorExtensions.GetInputBar(this, args);
TValue result = adxr!.Calc(input);
AdxrSeries!.SetValue(result.Value);
AdxrSeries!.SetMarker(0, Color.Transparent);
}
#pragma warning disable CA1416 // Validate platform compatibility
public override string ShortName => $"ADXR ({Periods})";
public override void OnPaintChart(PaintChartEventArgs args)
{
base.OnPaintChart(args);
this.PaintHLine(args, 25, new Pen(color: IndicatorExtensions.Momentum, width: 1)); // Strong trend line
this.PaintHLine(args, 20, new Pen(color: IndicatorExtensions.Momentum, width: 1)); // Weak trend line
this.PaintSmoothCurve(args, AdxrSeries!, adxr!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
}
}
+71
View File
@@ -0,0 +1,71 @@
using System.Drawing;
using TradingPlatform.BusinessLayer;
namespace QuanTAlib;
public class ApoIndicator : Indicator, IWatchlistIndicator
{
[InputParameter("Fast Period", sortIndex: 1, 1, 2000, 1, 0)]
public int FastPeriod { get; set; } = 12;
[InputParameter("Slow Period", sortIndex: 2, 1, 2000, 1, 0)]
public int SlowPeriod { get; set; } = 26;
[InputParameter("Data source", sortIndex: 4, variants: [
"Open", SourceType.Open,
"High", SourceType.High,
"Low", SourceType.Low,
"Close", SourceType.Close,
"HL/2 (Median)", SourceType.HL2,
"OC/2 (Midpoint)", SourceType.OC2,
"OHL/3 (Mean)", SourceType.OHL3,
"HLC/3 (Typical)", SourceType.HLC3,
"OHLC/4 (Average)", SourceType.OHLC4,
"HLCC/4 (Weighted)", SourceType.HLCC4
])]
public SourceType Source { get; set; } = SourceType.Close;
[InputParameter("Show cold values", sortIndex: 21)]
public bool ShowColdValues { get; set; } = true;
private Apo? apo;
protected LineSeries? ApoSeries;
public int MinHistoryDepths => Math.Max(FastPeriod, SlowPeriod) * 2;
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
public ApoIndicator()
{
Name = "APO - Absolute Price Oscillator";
Description = "Shows the difference between two moving averages of different periods.";
SeparateWindow = true;
ApoSeries = new($"APO {FastPeriod},{SlowPeriod}", color: IndicatorExtensions.Momentum, 2, LineStyle.Solid);
AddLineSeries(ApoSeries);
}
protected override void OnInit()
{
apo = new Apo(FastPeriod, SlowPeriod);
base.OnInit();
}
protected override void OnUpdate(UpdateArgs args)
{
TValue input = this.GetInputValue(args, Source);
TValue result = apo!.Calc(input);
ApoSeries!.SetValue(result.Value);
ApoSeries!.SetMarker(0, Color.Transparent);
}
#pragma warning disable CA1416 // Validate platform compatibility
public override string ShortName => $"APO ({FastPeriod},{SlowPeriod})";
public override void OnPaintChart(PaintChartEventArgs args)
{
base.OnPaintChart(args);
this.PaintSmoothCurve(args, ApoSeries!, apo!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
}
}
+59
View File
@@ -0,0 +1,59 @@
using System.Drawing;
using TradingPlatform.BusinessLayer;
namespace QuanTAlib;
public class DmiIndicator : Indicator, IWatchlistIndicator
{
[InputParameter("Periods", sortIndex: 1, 1, 2000, 1, 0)]
public int Periods { get; set; } = 14;
[InputParameter("Show cold values", sortIndex: 21)]
public bool ShowColdValues { get; set; } = true;
private Dmi? dmi;
protected LineSeries? PlusDiSeries;
protected LineSeries? MinusDiSeries;
public int MinHistoryDepths => Math.Max(5, Periods * 2);
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
public DmiIndicator()
{
Name = "DMI - Directional Movement Index";
Description = "Identifies the directional movement of a price by comparing successive highs and lows.";
SeparateWindow = true;
PlusDiSeries = new($"+DI {Periods}", color: Color.Red, 2, LineStyle.Solid);
MinusDiSeries = new($"-DI {Periods}", color: Color.Blue, 2, LineStyle.Solid);
AddLineSeries(PlusDiSeries);
AddLineSeries(MinusDiSeries);
}
protected override void OnInit()
{
dmi = new Dmi(Periods);
base.OnInit();
}
protected override void OnUpdate(UpdateArgs args)
{
TBar input = IndicatorExtensions.GetInputBar(this, args);
var result = dmi!.Calc(input);
PlusDiSeries!.SetValue(dmi.PlusDI);
MinusDiSeries!.SetValue(dmi.MinusDI);
PlusDiSeries!.SetMarker(0, Color.Transparent);
MinusDiSeries!.SetMarker(0, Color.Transparent);
}
#pragma warning disable CA1416 // Validate platform compatibility
public override string ShortName => $"DMI ({Periods})";
public override void OnPaintChart(PaintChartEventArgs args)
{
base.OnPaintChart(args);
this.PaintSmoothCurve(args, PlusDiSeries!, dmi!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
this.PaintSmoothCurve(args, MinusDiSeries!, dmi!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
}
}
+68
View File
@@ -0,0 +1,68 @@
using System.Drawing;
using TradingPlatform.BusinessLayer;
namespace QuanTAlib;
public class DmxIndicator : Indicator, IWatchlistIndicator
{
[InputParameter("DMI Periods", sortIndex: 1, 1, 2000, 1, 0)]
public int DmiPeriods { get; set; } = 14;
[InputParameter("JMA Smoothing Periods", sortIndex: 2, 1, 2000, 1, 0)]
public int JmaPeriods { get; set; } = 12;
[InputParameter("JMA Phase", sortIndex: 3, -100, 100, 1, 0)]
public int JmaPhase { get; set; } = 100;
[InputParameter("JMA Factor", sortIndex: 4, 0.01, 1, 0.01, 2)]
public double JmaFactor { get; set; } = 0.3;
[InputParameter("Show cold values", sortIndex: 21)]
public bool ShowColdValues { get; set; } = true;
private Dmx? dmx;
protected LineSeries? PlusDiSeries;
protected LineSeries? MinusDiSeries;
public int MinHistoryDepths => Math.Max(5, (DmiPeriods + JmaPeriods) * 2);
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
public DmxIndicator()
{
Name = "DMX - Enhanced Directional Movement Index";
Description = "An enhanced version of DMI using JMA smoothing for better noise reduction and responsiveness.";
SeparateWindow = true;
PlusDiSeries = new($"+DI {DmiPeriods}", color: Color.Red, 2, LineStyle.Solid);
MinusDiSeries = new($"-DI {DmiPeriods}", color: Color.Blue, 2, LineStyle.Solid);
AddLineSeries(PlusDiSeries);
AddLineSeries(MinusDiSeries);
}
protected override void OnInit()
{
dmx = new Dmx(DmiPeriods, JmaPeriods, JmaPhase, JmaFactor);
base.OnInit();
}
protected override void OnUpdate(UpdateArgs args)
{
TBar input = IndicatorExtensions.GetInputBar(this, args);
var result = dmx!.Calc(input);
PlusDiSeries!.SetValue(dmx.PlusDI);
MinusDiSeries!.SetValue(dmx.MinusDI);
PlusDiSeries!.SetMarker(0, Color.Transparent);
MinusDiSeries!.SetMarker(0, Color.Transparent);
}
#pragma warning disable CA1416 // Validate platform compatibility
public override string ShortName => $"DMX ({DmiPeriods})";
public override void OnPaintChart(PaintChartEventArgs args)
{
base.OnPaintChart(args);
this.PaintSmoothCurve(args, PlusDiSeries!, dmx!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
this.PaintSmoothCurve(args, MinusDiSeries!, dmx!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
}
}
+67
View File
@@ -0,0 +1,67 @@
using System.Drawing;
using TradingPlatform.BusinessLayer;
namespace QuanTAlib;
public class DpoIndicator : Indicator, IWatchlistIndicator
{
[InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)]
public int Period { get; set; } = 20;
[InputParameter("Data source", sortIndex: 2, variants: [
"Open", SourceType.Open,
"High", SourceType.High,
"Low", SourceType.Low,
"Close", SourceType.Close,
"HL/2 (Median)", SourceType.HL2,
"OC/2 (Midpoint)", SourceType.OC2,
"OHL/3 (Mean)", SourceType.OHL3,
"HLC/3 (Typical)", SourceType.HLC3,
"OHLC/4 (Average)", SourceType.OHLC4,
"HLCC/4 (Weighted)", SourceType.HLCC4
])]
public SourceType Source { get; set; } = SourceType.Close;
[InputParameter("Show cold values", sortIndex: 3)]
public bool ShowColdValues { get; set; } = true;
private Dpo? dpo;
protected LineSeries? DpoSeries;
public int MinHistoryDepths => Period * 2;
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
public DpoIndicator()
{
Name = "DPO - Detrended Price Oscillator";
Description = "Removes trend from price by comparing current price to a past moving average, helping identify cycles in the price.";
SeparateWindow = true;
DpoSeries = new($"DPO {Period}", color: IndicatorExtensions.Momentum, 2, LineStyle.Solid);
AddLineSeries(DpoSeries);
}
protected override void OnInit()
{
dpo = new Dpo(Period);
base.OnInit();
}
protected override void OnUpdate(UpdateArgs args)
{
TBar input = this.GetInputBar(args);
TValue result = dpo!.Calc(input);
DpoSeries!.SetValue(result.Value);
DpoSeries!.SetMarker(0, Color.Transparent);
}
#pragma warning disable CA1416 // Validate platform compatibility
public override string ShortName => $"DPO ({Period})";
public override void OnPaintChart(PaintChartEventArgs args)
{
base.OnPaintChart(args);
this.PaintSmoothCurve(args, DpoSeries!, dpo!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
}
}
@@ -36,9 +36,7 @@ public class MacdIndicator : Indicator, IWatchlistIndicator
[InputParameter("Show cold values", sortIndex: 21)]
public bool ShowColdValues { get; set; } = true;
private Ema? slow_ma;
private Ema? fast_ma;
private Ema? signal_ma;
private Macd? macd;
private Slope? histSlope;
protected LineSeries? MainSeries;
protected LineSeries? SignalSeries;
@@ -58,8 +56,8 @@ public class MacdIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "MACD - Moving Average Convergence Divergence";
Description = "MACD";
MainSeries = new(name: $"MAIN", color: Color.Blue, width: 2, style: LineStyle.Solid);
SignalSeries = new(name: $"SIGNAL", color: Color.Yellow, width: 2, style: LineStyle.Solid);
MainSeries = new(name: $"MAIN", color: Color.RoyalBlue, width: 2, style: LineStyle.Solid);
SignalSeries = new(name: $"SIGNAL", color: Color.Red, width: 2, style: LineStyle.Solid);
HistogramSeries = new(name: $"HISTOGRAM", color: Color.White, width: 2, style: LineStyle.Solid);
HistSlopeSeries = new(name: $"SLOPE", color: Color.Transparent, width: 2, style: LineStyle.Solid);
HistSlopeSeries.Visible = false;
@@ -72,9 +70,7 @@ public class MacdIndicator : Indicator, IWatchlistIndicator
protected override void OnInit()
{
slow_ma = new(Slow, useSma: UseSMA);
fast_ma = new(Fast, useSma: UseSMA);
signal_ma = new(Signal, useSma: UseSMA);
macd = new(fastPeriod: Fast, slowPeriod: Slow, signalPeriod: Signal);
histSlope = new(2);
SourceName = Source.ToString();
base.OnInit();
@@ -83,19 +79,22 @@ public class MacdIndicator : Indicator, IWatchlistIndicator
protected override void OnUpdate(UpdateArgs args)
{
TValue input = this.GetInputValue(args, Source);
slow_ma!.Calc(input);
fast_ma!.Calc(input);
double main = fast_ma.Value - slow_ma.Value;
double signal = signal_ma!.Calc(main);
double histogram = main - signal;
macd!.Calc(input);
double main = macd.MacdLine;
double signal = macd.SignalLine;
double histogram = macd.Value;
histSlope!.Calc(histogram);
MainSeries!.SetValue(main);
MainSeries!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here
SignalSeries!.SetValue(signal);
SignalSeries!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here
HistogramSeries!.SetValue(histogram);
HistogramSeries!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here
HistSlopeSeries!.SetValue(histSlope.Value);
HistSlopeSeries!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here
}
@@ -118,7 +117,7 @@ public class MacdIndicator : Indicator, IWatchlistIndicator
for (int i = rightIndex; i < leftIndex; i++)
{
int barX = (int)converter.GetChartX(this.HistoricalData.Time(i));
int barY = (int)converter.GetChartY(HistogramSeries![i]*2.0);
int barY = (int)converter.GetChartY(HistogramSeries![i] * 2.0);
int barY0 = (int)converter.GetChartY(0);
int HistBarWidth = this.CurrentChart.BarsWidth - 2;
@@ -139,8 +138,8 @@ public class MacdIndicator : Indicator, IWatchlistIndicator
}
}
this.PaintSmoothCurve(args, MainSeries!, slow_ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.3);
this.PaintSmoothCurve(args, SignalSeries!, slow_ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
this.PaintSmoothCurve(args, MainSeries!, macd!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.3);
this.PaintSmoothCurve(args, SignalSeries!, macd!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
base.OnPaintChart(args);
}
}
+67
View File
@@ -0,0 +1,67 @@
using System.Drawing;
using TradingPlatform.BusinessLayer;
namespace QuanTAlib;
public class MomIndicator : Indicator
{
[InputParameter("Period", sortIndex: 1, minimum: 1, maximum: 2000, increment: 1)]
public int Period { get; set; } = 10;
[InputParameter("Data source", sortIndex: 2, variants: [
"Open", SourceType.Open,
"High", SourceType.High,
"Low", SourceType.Low,
"Close", SourceType.Close,
"HL/2 (Median)", SourceType.HL2,
"OC/2 (Midpoint)", SourceType.OC2,
"OHL/3 (Mean)", SourceType.OHL3,
"HLC/3 (Typical)", SourceType.HLC3,
"OHLC/4 (Average)", SourceType.OHLC4,
"HLCC/4 (Weighted)", SourceType.HLCC4
])]
public SourceType Source { get; set; } = SourceType.Close;
[InputParameter("Show cold values", sortIndex: 21)]
public bool ShowColdValues { get; set; } = true;
private Mom? mom;
protected LineSeries? Series;
protected string? SourceName;
public override string ShortName => $"MOM({Period})";
public MomIndicator()
{
OnBackGround = true;
SeparateWindow = true;
SourceName = Source.ToString();
Name = "MOM - Momentum";
Description = "A basic momentum indicator that measures the change in price over a specified period";
Series = new(name: $"MOM({Period})", color: IndicatorExtensions.Momentum, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
protected override void OnInit()
{
mom = new Mom(period: Period);
SourceName = Source.ToString();
base.OnInit();
}
protected override void OnUpdate(UpdateArgs args)
{
TValue input = this.GetInputValue(args, Source);
TValue result = mom!.Calc(input);
Series!.SetValue(result.Value);
Series!.SetMarker(0, Color.Transparent);
}
public override void OnPaintChart(PaintChartEventArgs args)
{
base.OnPaintChart(args);
this.PaintSmoothCurve(args, Series!, mom!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
}
}
+70
View File
@@ -0,0 +1,70 @@
using System.Drawing;
using TradingPlatform.BusinessLayer;
namespace QuanTAlib;
public class PmoIndicator : Indicator
{
[InputParameter("First Period", sortIndex: 1, minimum: 1, maximum: 2000, increment: 1)]
public int Period1 { get; set; } = 35;
[InputParameter("Second Period", sortIndex: 2, minimum: 1, maximum: 2000, increment: 1)]
public int Period2 { get; set; } = 20;
[InputParameter("Data source", sortIndex: 3, variants: [
"Open", SourceType.Open,
"High", SourceType.High,
"Low", SourceType.Low,
"Close", SourceType.Close,
"HL/2 (Median)", SourceType.HL2,
"OC/2 (Midpoint)", SourceType.OC2,
"OHL/3 (Mean)", SourceType.OHL3,
"HLC/3 (Typical)", SourceType.HLC3,
"OHLC/4 (Average)", SourceType.OHLC4,
"HLCC/4 (Weighted)", SourceType.HLCC4
])]
public SourceType Source { get; set; } = SourceType.Close;
[InputParameter("Show cold values", sortIndex: 21)]
public bool ShowColdValues { get; set; } = true;
private Pmo? pmo;
protected LineSeries? Series;
protected string? SourceName;
public override string ShortName => $"PMO({Period1},{Period2})";
public PmoIndicator()
{
OnBackGround = true;
SeparateWindow = true;
SourceName = Source.ToString();
Name = "PMO - Price Momentum Oscillator";
Description = "A momentum indicator that uses exponential moving averages of ROC to identify overbought and oversold conditions";
Series = new(name: $"PMO({Period1},{Period2})", color: IndicatorExtensions.Momentum, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
protected override void OnInit()
{
pmo = new Pmo(period1: Period1, period2: Period2);
SourceName = Source.ToString();
base.OnInit();
}
protected override void OnUpdate(UpdateArgs args)
{
TValue input = this.GetInputValue(args, Source);
TValue result = pmo!.Calc(input);
Series!.SetValue(result.Value);
Series!.SetMarker(0, Color.Transparent);
}
public override void OnPaintChart(PaintChartEventArgs args)
{
base.OnPaintChart(args);
this.PaintSmoothCurve(args, Series!, pmo!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
}
}
+75
View File
@@ -0,0 +1,75 @@
using System.Drawing;
using TradingPlatform.BusinessLayer;
namespace QuanTAlib;
public class PoIndicator : Indicator
{
[InputParameter("Fast Period", sortIndex: 1, minimum: 1, maximum: 2000, increment: 1)]
public int FastPeriod { get; set; } = 10;
[InputParameter("Slow Period", sortIndex: 2, minimum: 1, maximum: 2000, increment: 1)]
public int SlowPeriod { get; set; } = 21;
[InputParameter("Data source", sortIndex: 3, variants: [
"Open", SourceType.Open,
"High", SourceType.High,
"Low", SourceType.Low,
"Close", SourceType.Close,
"HL/2 (Median)", SourceType.HL2,
"OC/2 (Midpoint)", SourceType.OC2,
"OHL/3 (Mean)", SourceType.OHL3,
"HLC/3 (Typical)", SourceType.HLC3,
"OHLC/4 (Average)", SourceType.OHLC4,
"HLCC/4 (Weighted)", SourceType.HLCC4
])]
public SourceType Source { get; set; } = SourceType.Close;
[InputParameter("Show cold values", sortIndex: 21)]
public bool ShowColdValues { get; set; } = true;
private Po? po;
protected LineSeries? Series;
protected string? SourceName;
public override string ShortName => $"PO({FastPeriod},{SlowPeriod})";
public PoIndicator()
{
OnBackGround = true;
SeparateWindow = true;
SourceName = Source.ToString();
Name = "PO - Price Oscillator";
Description = "A momentum indicator that measures the difference between two moving averages to identify price momentum";
Series = new(name: $"PO({FastPeriod},{SlowPeriod})", color: IndicatorExtensions.Momentum, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
protected override void OnInit()
{
if (FastPeriod >= SlowPeriod)
{
FastPeriod = 10;
SlowPeriod = 21;
}
po = new Po(fastPeriod: FastPeriod, slowPeriod: SlowPeriod);
SourceName = Source.ToString();
base.OnInit();
}
protected override void OnUpdate(UpdateArgs args)
{
TValue input = this.GetInputValue(args, Source);
TValue result = po!.Calc(input);
Series!.SetValue(result.Value);
Series!.SetMarker(0, Color.Transparent);
}
public override void OnPaintChart(PaintChartEventArgs args)
{
base.OnPaintChart(args);
this.PaintSmoothCurve(args, Series!, po!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
}
}
+75
View File
@@ -0,0 +1,75 @@
using System.Drawing;
using TradingPlatform.BusinessLayer;
namespace QuanTAlib;
public class PpoIndicator : Indicator
{
[InputParameter("Fast Period", sortIndex: 1, minimum: 1, maximum: 2000, increment: 1)]
public int FastPeriod { get; set; } = 12;
[InputParameter("Slow Period", sortIndex: 2, minimum: 1, maximum: 2000, increment: 1)]
public int SlowPeriod { get; set; } = 26;
[InputParameter("Data source", sortIndex: 3, variants: [
"Open", SourceType.Open,
"High", SourceType.High,
"Low", SourceType.Low,
"Close", SourceType.Close,
"HL/2 (Median)", SourceType.HL2,
"OC/2 (Midpoint)", SourceType.OC2,
"OHL/3 (Mean)", SourceType.OHL3,
"HLC/3 (Typical)", SourceType.HLC3,
"OHLC/4 (Average)", SourceType.OHLC4,
"HLCC/4 (Weighted)", SourceType.HLCC4
])]
public SourceType Source { get; set; } = SourceType.Close;
[InputParameter("Show cold values", sortIndex: 21)]
public bool ShowColdValues { get; set; } = true;
private Ppo? ppo;
protected LineSeries? Series;
protected string? SourceName;
public override string ShortName => $"PPO({FastPeriod},{SlowPeriod})";
public PpoIndicator()
{
OnBackGround = true;
SeparateWindow = true;
SourceName = Source.ToString();
Name = "PPO - Percentage Price Oscillator";
Description = "A momentum indicator that shows the percentage difference between two moving averages";
Series = new(name: $"PPO({FastPeriod},{SlowPeriod})", color: IndicatorExtensions.Momentum, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
protected override void OnInit()
{
if (FastPeriod >= SlowPeriod)
{
FastPeriod = 12;
SlowPeriod = 26;
}
ppo = new Ppo(fastPeriod: FastPeriod, slowPeriod: SlowPeriod);
SourceName = Source.ToString();
base.OnInit();
}
protected override void OnUpdate(UpdateArgs args)
{
TValue input = this.GetInputValue(args, Source);
TValue result = ppo!.Calc(input);
Series!.SetValue(result.Value);
Series!.SetMarker(0, Color.Transparent);
}
public override void OnPaintChart(PaintChartEventArgs args)
{
base.OnPaintChart(args);
this.PaintSmoothCurve(args, Series!, ppo!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
}
}
+76
View File
@@ -0,0 +1,76 @@
using System.Drawing;
using TradingPlatform.BusinessLayer;
namespace QuanTAlib;
public class RocIndicator : Indicator, IWatchlistIndicator
{
[InputParameter("Period", sortIndex: 1, minimum: 1, maximum: 2000, increment: 1)]
public int Period { get; set; } = 12;
[InputParameter("Data source", sortIndex: 2, variants: [
"Open", SourceType.Open,
"High", SourceType.High,
"Low", SourceType.Low,
"Close", SourceType.Close,
"HL/2 (Median)", SourceType.HL2,
"OC/2 (Midpoint)", SourceType.OC2,
"OHL/3 (Mean)", SourceType.OHL3,
"HLC/3 (Typical)", SourceType.HLC3,
"OHLC/4 (Average)", SourceType.OHLC4,
"HLCC/4 (Weighted)", SourceType.HLCC4
])]
public SourceType Source { get; set; } = SourceType.Close;
[InputParameter("Show cold values", sortIndex: 21)]
public bool ShowColdValues { get; set; } = true;
private Roc? roc;
protected LineSeries? Series;
protected LineSeries? ZeroLine;
protected string? SourceName;
public int MinHistoryDepths => Math.Max(5, Period * 2);
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
public override string ShortName => $"ROC({Period})";
public RocIndicator()
{
OnBackGround = true;
SeparateWindow = true;
SourceName = Source.ToString();
Name = "ROC - Rate of Change";
Description = "A momentum indicator that measures the percentage change in price over a specified period";
Series = new(name: $"ROC({Period})", color: IndicatorExtensions.Momentum, width: 2, style: LineStyle.Solid);
ZeroLine = new("Zero", Color.Gray, 1, LineStyle.Dot);
AddLineSeries(Series);
AddLineSeries(ZeroLine);
}
protected override void OnInit()
{
roc = new Roc(period: Period);
SourceName = Source.ToString();
base.OnInit();
}
protected override void OnUpdate(UpdateArgs args)
{
if (args.Reason != UpdateReason.NewTick)
return;
TValue input = this.GetInputValue(args, Source);
TValue result = roc!.Calc(input);
Series!.SetValue(result.Value);
ZeroLine!.SetValue(0);
Series!.SetMarker(0, Color.Transparent);
}
public override void OnPaintChart(PaintChartEventArgs args)
{
base.OnPaintChart(args);
this.PaintSmoothCurve(args, Series!, roc!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
}
}
+67
View File
@@ -0,0 +1,67 @@
using System.Drawing;
using TradingPlatform.BusinessLayer;
namespace QuanTAlib;
public class TrixIndicator : Indicator
{
[InputParameter("Period", sortIndex: 1, minimum: 1, maximum: 2000, increment: 1)]
public int Period { get; set; } = 18;
[InputParameter("Data source", sortIndex: 2, variants: [
"Open", SourceType.Open,
"High", SourceType.High,
"Low", SourceType.Low,
"Close", SourceType.Close,
"HL/2 (Median)", SourceType.HL2,
"OC/2 (Midpoint)", SourceType.OC2,
"OHL/3 (Mean)", SourceType.OHL3,
"HLC/3 (Typical)", SourceType.HLC3,
"OHLC/4 (Average)", SourceType.OHLC4,
"HLCC/4 (Weighted)", SourceType.HLCC4
])]
public SourceType Source { get; set; } = SourceType.Close;
[InputParameter("Show cold values", sortIndex: 21)]
public bool ShowColdValues { get; set; } = true;
private Trix? trix;
protected LineSeries? Series;
protected string? SourceName;
public override string ShortName => $"TRIX({Period})";
public TrixIndicator()
{
OnBackGround = true;
SeparateWindow = true;
SourceName = Source.ToString();
Name = "TRIX - Triple Exponential Average Rate of Change";
Description = "A momentum oscillator that shows the percentage rate of change of a triple exponentially smoothed moving average";
Series = new(name: $"TRIX({Period})", color: IndicatorExtensions.Momentum, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
protected override void OnInit()
{
trix = new Trix(period: Period);
SourceName = Source.ToString();
base.OnInit();
}
protected override void OnUpdate(UpdateArgs args)
{
TValue input = this.GetInputValue(args, Source);
TValue result = trix!.Calc(input);
Series!.SetValue(result.Value);
Series!.SetMarker(0, Color.Transparent);
}
public override void OnPaintChart(PaintChartEventArgs args)
{
base.OnPaintChart(args);
this.PaintSmoothCurve(args, Series!, trix!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
}
}
+82
View File
@@ -0,0 +1,82 @@
using System.Drawing;
using TradingPlatform.BusinessLayer;
namespace QuanTAlib;
public class VelIndicator : Indicator, IWatchlistIndicator
{
[InputParameter("Period", sortIndex: 1, minimum: 1, maximum: 2000, increment: 1)]
public int Period { get; set; } = 10;
[InputParameter("Phase", sortIndex: 2, minimum: -100, maximum: 100, increment: 1)]
public int Phase { get; set; } = 100;
[InputParameter("Factor", sortIndex: 3, minimum: 0.1, maximum: 0.9, increment: 0.1, decimalPlaces: 2)]
public double Factor { get; set; } = 0.25;
[InputParameter("Data source", sortIndex: 4, variants: [
"Open", SourceType.Open,
"High", SourceType.High,
"Low", SourceType.Low,
"Close", SourceType.Close,
"HL/2 (Median)", SourceType.HL2,
"OC/2 (Midpoint)", SourceType.OC2,
"OHL/3 (Mean)", SourceType.OHL3,
"HLC/3 (Typical)", SourceType.HLC3,
"OHLC/4 (Average)", SourceType.OHLC4,
"HLCC/4 (Weighted)", SourceType.HLCC4
])]
public SourceType Source { get; set; } = SourceType.Close;
[InputParameter("Show cold values", sortIndex: 21)]
public bool ShowColdValues { get; set; } = true;
private Vel? vel;
protected LineSeries? Series;
protected LineSeries? ZeroLine;
protected string? SourceName;
public int MinHistoryDepths => Math.Max(5, Period * 2);
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
public override string ShortName => $"VEL({Period})";
public VelIndicator()
{
OnBackGround = true;
SeparateWindow = true;
SourceName = Source.ToString();
Name = "VEL - Velocity";
Description = "An enhanced momentum indicator that applies JMA smoothing to momentum calculation";
Series = new(name: $"VEL({Period})", color: IndicatorExtensions.Momentum, width: 2, style: LineStyle.Solid);
ZeroLine = new("Zero", Color.Gray, 1, LineStyle.Dot);
AddLineSeries(Series);
AddLineSeries(ZeroLine);
}
protected override void OnInit()
{
vel = new Vel(period: Period, phase: Phase, factor: Factor);
SourceName = Source.ToString();
base.OnInit();
}
protected override void OnUpdate(UpdateArgs args)
{
if (args.Reason != UpdateReason.NewTick)
return;
TValue input = this.GetInputValue(args, Source);
TValue result = vel!.Calc(input);
Series!.SetValue(result.Value);
ZeroLine!.SetValue(0);
Series!.SetMarker(0, Color.Transparent);
}
public override void OnPaintChart(PaintChartEventArgs args)
{
base.OnPaintChart(args);
this.PaintSmoothCurve(args, Series!, vel!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
}
}
+71
View File
@@ -0,0 +1,71 @@
using System.Drawing;
using TradingPlatform.BusinessLayer;
namespace QuanTAlib;
public class VortexIndicator : Indicator, IWatchlistIndicator
{
[InputParameter("Periods", sortIndex: 1, 1, 2000, 1, 0)]
public int Periods { get; set; } = 14;
[InputParameter("Show cold values", sortIndex: 21)]
public bool ShowColdValues { get; set; } = true;
private Vortex? vortex;
protected LineSeries? ValueSeries;
protected LineSeries? PlusLine;
protected LineSeries? MinusLine;
protected LineSeries? ZeroLine;
public int MinHistoryDepths => Math.Max(5, Periods * 2);
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
public VortexIndicator()
{
Name = "VORTEX - Vortex Indicator";
Description = "A technical indicator consisting of two oscillating lines that identify trend reversals";
SeparateWindow = true;
ValueSeries = new($"VORTEX({Periods})", color: IndicatorExtensions.Momentum, 2, LineStyle.Solid);
PlusLine = new($"VI+({Periods})", color: Color.Green, 2, LineStyle.Solid);
MinusLine = new($"VI-({Periods})", color: Color.Red, 2, LineStyle.Solid);
ZeroLine = new("Zero", Color.Gray, 1, LineStyle.Dot);
AddLineSeries(ValueSeries);
AddLineSeries(PlusLine);
AddLineSeries(MinusLine);
AddLineSeries(ZeroLine);
}
protected override void OnInit()
{
vortex = new Vortex(Periods);
base.OnInit();
}
protected override void OnUpdate(UpdateArgs args)
{
TBar input = IndicatorExtensions.GetInputBar(this, args);
var result = vortex!.Calc(input);
ValueSeries!.SetValue(result);
PlusLine!.SetValue(vortex.ViPlus);
MinusLine!.SetValue(vortex.ViMinus);
ZeroLine!.SetValue(0);
ValueSeries!.SetMarker(0, Color.Transparent);
PlusLine!.SetMarker(0, Color.Transparent);
MinusLine!.SetMarker(0, Color.Transparent);
}
#pragma warning disable CA1416 // Validate platform compatibility
public override string ShortName => $"VORTEX({Periods})";
public override void OnPaintChart(PaintChartEventArgs args)
{
base.OnPaintChart(args);
this.PaintSmoothCurve(args, ValueSeries!, vortex!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
this.PaintSmoothCurve(args, PlusLine!, vortex!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
this.PaintSmoothCurve(args, MinusLine!, vortex!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
}
}
+1 -1
View File
@@ -27,4 +27,4 @@
<Copy SourceFiles="$(OutputPath)\Momentum.dll" DestinationFolder="$(QuantowerRoot)\Settings\Scripts\Indicators\QuanTAlib\Momentum" />
</Target>
</Project>
</Project>
+71
View File
@@ -0,0 +1,71 @@
using TradingPlatform.BusinessLayer;
using System.Drawing;
namespace QuanTAlib
{
public class CtiIndicator : Indicator, IWatchlistIndicator
{
[InputParameter("Period", 0, 1, 100, 1, 0)]
public int Period { get; set; } = 20;
[InputParameter("Source Type", 1, variants: new object[]
{
"Open", SourceType.Open,
"High", SourceType.High,
"Low", SourceType.Low,
"Close", SourceType.Close,
"HL2", SourceType.HL2,
"OC2", SourceType.OC2,
"OHL3", SourceType.OHL3,
"HLC3", SourceType.HLC3,
"OHLC4", SourceType.OHLC4,
"HLCC4", SourceType.HLCC4
})]
public SourceType Source { get; set; } = SourceType.Close;
[InputParameter("Show Cold Values", 2)]
public bool ShowColdValues { get; set; } = true;
private Cti? cti;
protected LineSeries? Series;
protected string? SourceName;
public int MinHistoryDepths => Period + 1;
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
public CtiIndicator()
{
OnBackGround = false;
SeparateWindow = true;
this.Name = "CTI - Ehler's Correlation Trend Indicator";
SourceName = Source.ToString();
this.Description = "A momentum oscillator that measures the correlation between the price and a lagged version of the price.";
Series = new($"CTI {Period}", color: IndicatorExtensions.Oscillators, width: 2, LineStyle.Solid);
AddLineSeries(Series);
}
protected override void OnInit()
{
cti = new Cti(this.Period);
SourceName = Source.ToString();
base.OnInit();
}
protected override void OnUpdate(UpdateArgs args)
{
TValue input = this.GetInputValue(args, Source);
TValue result = cti!.Calc(input);
Series!.SetValue(result);
Series!.SetMarker(0, Color.Transparent);
}
public override string ShortName => $"CTI ({Period}:{SourceName})";
#pragma warning disable CA1416 // Validate platform compatibility
public override void OnPaintChart(PaintChartEventArgs args)
{
base.OnPaintChart(args);
this.PaintSmoothCurve(args, Series!, cti!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.0);
}
}
}
@@ -37,7 +37,7 @@ public class RsiIndicator : Indicator, IWatchlistIndicator
Description = "Measures the speed and magnitude of recent price changes to evaluate overbought or oversold conditions.";
SeparateWindow = true;
SourceName = Source.ToString();
RsiSeries = new($"RSI {Periods}", Color.Blue, 2, LineStyle.Solid);
RsiSeries = new($"RSI {Periods}", color: IndicatorExtensions.Oscillators, 2, LineStyle.Solid);
AddLineSeries(RsiSeries);
}
@@ -37,7 +37,7 @@ public class RsxIndicator : Indicator, IWatchlistIndicator
Description = "Measures the speed and magnitude of recent price changes to evaluate overbought or oversold conditions.";
SeparateWindow = true;
SourceName = Source.ToString();
RsxSeries = new($"RSX {Period}", Color.Blue, 2, LineStyle.Solid);
RsxSeries = new($"RSX {Period}", color: IndicatorExtensions.Oscillators, 2, LineStyle.Solid);
AddLineSeries(RsxSeries);
}
+1 -1
View File
@@ -36,7 +36,7 @@ public class CurvatureIndicator : Indicator, IWatchlistIndicator
SeparateWindow = true;
SourceName = Source.ToString();
CurvatureSeries = new("Curvature", Color.Blue, 2, LineStyle.Solid);
CurvatureSeries = new("Curvature", color: IndicatorExtensions.Statistics, 2, LineStyle.Solid);
AddLineSeries(CurvatureSeries);
}
+1 -1
View File
@@ -34,7 +34,7 @@ public class EntropyIndicator : Indicator, IWatchlistIndicator
SeparateWindow = true;
SourceName = Source.ToString();
EntropySeries = new("Entropy", Color.Blue, 2, LineStyle.Solid);
EntropySeries = new("Entropy", color: IndicatorExtensions.Statistics, 2, LineStyle.Solid);
AddLineSeries(EntropySeries);
}
+1 -1
View File
@@ -35,7 +35,7 @@ public class KurtosisIndicator : Indicator, IWatchlistIndicator
SeparateWindow = true;
SourceName = Source.ToString();
KurtosisSeries = new("Kurtosis", Color.Blue, 2, LineStyle.Solid);
KurtosisSeries = new("Kurtosis", color: IndicatorExtensions.Statistics, 2, LineStyle.Solid);
AddLineSeries(KurtosisSeries);
}
+1 -1
View File
@@ -38,7 +38,7 @@ public class MaxIndicator : Indicator, IWatchlistIndicator
SeparateWindow = false;
SourceName = Source.ToString();
MaxSeries = new("Max", Color.Blue, 2, LineStyle.Solid);
MaxSeries = new("Max", color: IndicatorExtensions.Statistics, 2, LineStyle.Solid);
AddLineSeries(MaxSeries);
}
+1 -1
View File
@@ -35,7 +35,7 @@ public class MedianIndicator : Indicator, IWatchlistIndicator
SeparateWindow = false;
SourceName = Source.ToString();
MedianSeries = new("Median", Color.Blue, 2, LineStyle.Solid);
MedianSeries = new("Median", color: IndicatorExtensions.Statistics, 2, LineStyle.Solid);
AddLineSeries(MedianSeries);
}
+1 -1
View File
@@ -38,7 +38,7 @@ public class MinIndicator : Indicator, IWatchlistIndicator
SeparateWindow = false;
SourceName = Source.ToString();
MinSeries = new("Min", Color.Blue, 2, LineStyle.Solid);
MinSeries = new("Min", color: IndicatorExtensions.Statistics, 2, LineStyle.Solid);
AddLineSeries(MinSeries);
}
+1 -1
View File
@@ -35,7 +35,7 @@ public class ModeIndicator : Indicator, IWatchlistIndicator
SeparateWindow = false;
SourceName = Source.ToString();
ModeSeries = new("Mode", Color.Blue, 2, LineStyle.Solid);
ModeSeries = new("Mode", color: IndicatorExtensions.Statistics, 2, LineStyle.Solid);
AddLineSeries(ModeSeries);
}
+1 -1
View File
@@ -38,7 +38,7 @@ public class PercentileIndicator : Indicator, IWatchlistIndicator
SeparateWindow = false;
SourceName = Source.ToString();
PercentileSeries = new("Percentile", Color.Blue, 2, LineStyle.Solid);
PercentileSeries = new("Percentile", color: IndicatorExtensions.Statistics, 2, LineStyle.Solid);
AddLineSeries(PercentileSeries);
}
+1 -1
View File
@@ -35,7 +35,7 @@ public class SkewIndicator : Indicator, IWatchlistIndicator
SeparateWindow = true;
SourceName = Source.ToString();
SkewSeries = new("Skew", Color.Blue, 2, LineStyle.Solid);
SkewSeries = new("Skew", color: IndicatorExtensions.Statistics, 2, LineStyle.Solid);
AddLineSeries(SkewSeries);
}

Some files were not shown because too many files have changed in this diff Show More