mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-07-27 17:27:43 +00:00
Merge branch 'dev'
This commit is contained in:
@@ -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
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
@@ -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
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,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
@@ -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
@@ -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
@@ -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
@@ -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();
|
||||
|
||||
|
||||
@@ -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,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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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 < 1: forecast better than naive forecast
|
||||
/// - U2 = 1: forecast equal to naive forecast
|
||||
/// - U2 > 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;
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -27,4 +27,4 @@
|
||||
<Copy SourceFiles="$(OutputPath)\Momentum.dll" DestinationFolder="$(QuantowerRoot)\Settings\Scripts\Indicators\QuanTAlib\Momentum" />
|
||||
</Target>
|
||||
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user