This commit is contained in:
Miha Kralj
2024-10-08 17:34:58 -07:00
parent 00b5bf3242
commit 14ec3cd2b0
11 changed files with 204 additions and 156 deletions
+1 -1
View File
@@ -215,7 +215,7 @@ jobs:
sarif_file: results.sarif sarif_file: results.sarif
build_publish: build_publish:
needs: [SonarCloud, Code_Coverage, CodeQL, Codacy_Scan] needs: [SonarCloud, Code_Coverage, CodeQL, Codacy_Scan, SecurityCodeScan]
if: success() if: success()
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
+3 -1
View File
@@ -2,7 +2,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<LangVersion>preview</LangVersion> <LangVersion>preview</LangVersion>
<NoWarn>$(NoWarn);NU1903;NU5104</NoWarn>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<Deterministic>true</Deterministic> <Deterministic>true</Deterministic>
@@ -49,6 +49,8 @@
</ItemGroup> </ItemGroup>
<PropertyGroup Condition="'$(IsLocalBuild)' == 'true'"> <PropertyGroup Condition="'$(IsLocalBuild)' == 'true'">
<!-- Set the correct path to Quantower here -->
<QuantowerRoot>D:\Quantower</QuantowerRoot> <QuantowerRoot>D:\Quantower</QuantowerRoot>
<QuantowerPath>$([System.IO.Directory]::GetDirectories("$(QuantowerRoot)\TradingPlatform", "v1*")[0])</QuantowerPath> <QuantowerPath>$([System.IO.Directory]::GetDirectories("$(QuantowerRoot)\TradingPlatform", "v1*")[0])</QuantowerPath>
</PropertyGroup> </PropertyGroup>
+13 -7
View File
@@ -3,16 +3,16 @@ using Trady.Analysis.Indicator;
using Trady.Core; using Trady.Core;
using Trady.Core.Infrastructure; using Trady.Core.Infrastructure;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Security.Cryptography;
namespace QuanTAlib; namespace QuanTAlib;
[SuppressMessage("Security", "SCS0005:Weak random number generator.", Justification = "Acceptable for tests")] [SuppressMessage("Security", "SCS0005:Weak random number generator.", Justification = "Acceptable for tests")]
public class TradyTests public class TradyTests
{ {
private readonly TBarSeries bars; private readonly TBarSeries bars;
private readonly GbmFeed feed; private readonly GbmFeed feed;
private readonly Random rnd; private readonly RandomNumberGenerator rng;
private readonly double range; private readonly double range;
private readonly int iterations; private readonly int iterations;
private readonly int skip; private readonly int skip;
@@ -20,7 +20,7 @@ public class TradyTests
public TradyTests() public TradyTests()
{ {
rnd = new((int)DateTime.Now.Ticks); rng = RandomNumberGenerator.Create();
feed = new(sigma: 0.5, mu: 0.0); feed = new(sigma: 0.5, mu: 0.0);
bars = new(feed); bars = new(feed);
range = 1e-9; range = 1e-9;
@@ -37,12 +37,20 @@ public class TradyTests
)).ToList(); )).ToList();
} }
private int GetRandomNumber(int minValue, int maxValue)
{
byte[] randomBytes = new byte[4];
rng.GetBytes(randomBytes);
int randomInt = BitConverter.ToInt32(randomBytes, 0);
return Math.Abs(randomInt % (maxValue - minValue)) + minValue;
}
[Fact] [Fact]
public void SMA() public void SMA()
{ {
for (int run = 0; run < iterations; run++) for (int run = 0; run < iterations; run++)
{ {
int period = rnd.Next(50) + 5; int period = GetRandomNumber(5, 55);
Sma ma = new(period); Sma ma = new(period);
TSeries QL = new(); TSeries QL = new();
foreach (TBar item in feed) foreach (TBar item in feed)
@@ -72,7 +80,7 @@ public class TradyTests
{ {
for (int run = 0; run < iterations; run++) for (int run = 0; run < iterations; run++)
{ {
int period = rnd.Next(50) + 5; int period = GetRandomNumber(5, 55);
Ema ma = new(period); Ema ma = new(period);
TSeries QL = new(); TSeries QL = new();
foreach (TBar item in feed) foreach (TBar item in feed)
@@ -96,6 +104,4 @@ public class TradyTests
} }
} }
} }
} }
+13 -6
View File
@@ -1,15 +1,15 @@
using Xunit; using Xunit;
using Tulip; using Tulip;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Security.Cryptography;
namespace QuanTAlib; namespace QuanTAlib;
[SuppressMessage("Security", "SCS0005:Weak random number generator.", Justification = "Acceptable for tests")] [SuppressMessage("Security", "SCS0005:Weak random number generator.", Justification = "Acceptable for tests")]
public class TulipTests public class TulipTests
{ {
private readonly TBarSeries bars;
private readonly GbmFeed feed; private readonly GbmFeed feed;
private readonly Random rnd; private readonly RandomNumberGenerator rng;
private readonly double range; private readonly double range;
private readonly int iterations; private readonly int iterations;
private readonly double[] data; private readonly double[] data;
@@ -18,9 +18,8 @@ public class TulipTests
public TulipTests() public TulipTests()
{ {
rnd = new((int)DateTime.Now.Ticks); rng = RandomNumberGenerator.Create();
feed = new(sigma: 0.5, mu: 0.0); feed = new(sigma: 0.5, mu: 0.0);
bars = new(feed);
range = 1e-9; range = 1e-9;
feed.Add(10000); feed.Add(10000);
iterations = 3; iterations = 3;
@@ -29,12 +28,20 @@ public class TulipTests
outdata = new double[data.Count()]; outdata = new double[data.Count()];
} }
private int GetRandomNumber(int minValue, int maxValue)
{
byte[] randomBytes = new byte[4];
rng.GetBytes(randomBytes);
int randomInt = BitConverter.ToInt32(randomBytes, 0);
return Math.Abs(randomInt % (maxValue - minValue)) + minValue;
}
[Fact] [Fact]
public void SMA() public void SMA()
{ {
for (int run = 0; run < iterations; run++) for (int run = 0; run < iterations; run++)
{ {
int period = rnd.Next(50) + 5; int period = GetRandomNumber(5, 55);
Sma ma = new(period); Sma ma = new(period);
TSeries QL = new(); TSeries QL = new();
foreach (TBar item in feed) foreach (TBar item in feed)
@@ -58,7 +65,7 @@ public class TulipTests
{ {
for (int run = 0; run < iterations; run++) for (int run = 0; run < iterations; run++)
{ {
int period = rnd.Next(30) + 5; int period = GetRandomNumber(5, 35);
Ema ma = new(period, useSma: false); Ema ma = new(period, useSma: false);
TSeries QL = new(); TSeries QL = new();
foreach (TBar item in feed) foreach (TBar item in feed)
+51 -46
View File
@@ -1,4 +1,5 @@
using Xunit; using Xunit;
using System.Security.Cryptography;
namespace QuanTAlib; namespace QuanTAlib;
@@ -7,8 +8,8 @@ public class EventingTests
[Fact] [Fact]
public void VerifyEventBasedCalculations() public void VerifyEventBasedCalculations()
{ {
// Create a random number generator with a fixed seed for reproducibility // Create a cryptographically secure random number generator
var random = new Random(42); using var rng = RandomNumberGenerator.Create();
// Create an input series to hold our random values // Create an input series to hold our random values
var input = new TSeries(); var input = new TSeries();
@@ -16,59 +17,63 @@ public class EventingTests
// Create a list of indicator pairs (direct calculation and event-based) // Create a list of indicator pairs (direct calculation and event-based)
var indicators = new List<(AbstractBase Direct, AbstractBase EventBased)> var indicators = new List<(AbstractBase Direct, AbstractBase EventBased)>
{ {
(new Afirma(p,p,Afirma.WindowType.BlackmanHarris), new Afirma(input, p,p,Afirma.WindowType.BlackmanHarris)), (new Afirma(p,p,Afirma.WindowType.BlackmanHarris), new Afirma(input, p,p,Afirma.WindowType.BlackmanHarris)),
(new Alma(p), new Alma(input, p)), (new Alma(p), new Alma(input, p)),
(new Convolution([1,2,3,2,1]), new Convolution(input, [1,2,3,2,1])), (new Convolution([1,2,3,2,1]), new Convolution(input, [1,2,3,2,1])),
(new Dema(p), new Dema(input, p)), (new Dema(p), new Dema(input, p)),
(new Dsma(p), new Dsma(input, p)), (new Dsma(p), new Dsma(input, p)),
(new Dwma(p), new Dwma(input, p)), (new Dwma(p), new Dwma(input, p)),
(new Ema(p), new Ema(input, p)), (new Ema(p), new Ema(input, p)),
(new Epma(p), new Epma(input, p)), (new Epma(p), new Epma(input, p)),
(new Frama(p), new Frama(input, p)), (new Frama(p), new Frama(input, p)),
(new Fwma(p), new Fwma(input, p)), (new Fwma(p), new Fwma(input, p)),
(new Gma(p), new Gma(input, p)), (new Gma(p), new Gma(input, p)),
(new Hma(p), new Hma(input, p)), (new Hma(p), new Hma(input, p)),
(new Htit(), new Htit(input)), (new Htit(), new Htit(input)),
(new Hwma(p), new Hwma(input, p)), (new Hwma(p), new Hwma(input, p)),
(new Jma(p), new Jma(input, p)), (new Jma(p), new Jma(input, p)),
(new Kama(p), new Kama(input, p)), (new Kama(p), new Kama(input, p)),
(new Ltma(gamma: 0.2), new Ltma(input, gamma: 0.2)), (new Ltma(gamma: 0.2), new Ltma(input, gamma: 0.2)),
(new Maaf(p), new Maaf(input, p)), (new Maaf(p), new Maaf(input, p)),
(new Mama(p), new Mama(input, p)), (new Mama(p), new Mama(input, p)),
(new Mgdi(p), new Mgdi(input, p)), (new Mgdi(p), new Mgdi(input, p)),
(new Mma(p), new Mma(input, p)), (new Mma(p), new Mma(input, p)),
(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)), (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)),
(new Rema(p), new Rema(input, p)), (new Rema(p), new Rema(input, p)),
(new Rma(p), new Rma(input, p)), (new Rma(p), new Rma(input, p)),
(new Sma(p), new Sma(input, p)),
(new Sma(p), new Sma(input, p)), (new Wma(p), new Wma(input, p)),
(new Wma(p), new Wma(input, p)), (new Rma(p), new Rma(input, p)),
(new Rma(p), new Rma(input, p)), (new Tema(p), new Tema(input, p)),
(new Kama(2, 30, 6), new Kama(input, 2, 30, 6)),
(new Tema(p), new Tema(input, p)), (new Zlema(p), new Zlema(input, p))
(new Kama(2, 30, 6), new Kama(input, 2, 30, 6)), };
(new Zlema(p), new Zlema(input, p))
};
// Generate 200 random values and feed them to both direct and event-based indicators // Generate 200 random values and feed them to both direct and event-based indicators
for (int i = 0; i< 200; i++) for (int i = 0; i < 200; i++)
{ {
double randomValue = random.NextDouble() * 100; double randomValue = GetRandomDouble(rng) * 100;
input.Add(randomValue); input.Add(randomValue);
// Calculate direct indicators // Calculate direct indicators
foreach (var (direct, _) in indicators) foreach (var (direct, _) in indicators)
{ {
direct.Calc(randomValue); direct.Calc(randomValue);
} }
} }
// Compare the results of direct and event-based calculations // Compare the results of direct and event-based calculations
foreach (var (direct, eventBased) in indicators) foreach (var (direct, eventBased) in indicators)
{ {
Assert.Equal(direct.Value, eventBased.Value, 9); Assert.Equal(direct.Value, eventBased.Value, 9);
} }
}
private static double GetRandomDouble(RandomNumberGenerator rng)
{
byte[] bytes = new byte[8];
rng.GetBytes(bytes);
return (double)BitConverter.ToUInt64(bytes, 0) / ulong.MaxValue;
} }
} }
+33 -8
View File
@@ -1,16 +1,16 @@
using Xunit; using Xunit;
using System.Reflection; using System.Reflection;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Security.Cryptography;
namespace QuanTAlib; namespace QuanTAlib;
/// <summary> /// <summary>
/// Contains unit tests for bar-based indicators in QuanTAlib. /// Contains unit tests for bar-based indicators in QuanTAlib.
/// </summary> /// </summary>
[SuppressMessage("Security", "SCS0005:Weak random number generator.", Justification = "Acceptable for tests")]
public class BarIndicatorTests public class BarIndicatorTests
{ {
private readonly Random rnd; private readonly RandomNumberGenerator rng;
private const int SeriesLen = 1000; private const int SeriesLen = 1000;
private const int Corrections = 100; private const int Corrections = 100;
@@ -19,7 +19,7 @@ public class BarIndicatorTests
/// </summary> /// </summary>
public BarIndicatorTests() public BarIndicatorTests()
{ {
rnd = new Random((int)DateTime.Now.Ticks); rng = RandomNumberGenerator.Create();
} }
private static readonly ITValue[] indicators = new ITValue[] private static readonly ITValue[] indicators = new ITValue[]
@@ -124,15 +124,40 @@ public class BarIndicatorTests
/// <returns>A randomly generated TBar.</returns> /// <returns>A randomly generated TBar.</returns>
private TBar GenerateRandomBar(bool isNew) private TBar GenerateRandomBar(bool isNew)
{ {
double open = rnd.NextDouble() * 200 - 100; double open = GetRandomDouble() * 200 - 100;
double close = rnd.NextDouble() * 200 - 100; double close = GetRandomDouble() * 200 - 100;
double high = Math.Max(open, close) + rnd.NextDouble() * 10; double high = Math.Max(open, close) + GetRandomDouble() * 10;
double low = Math.Min(open, close) - rnd.NextDouble() * 10; double low = Math.Min(open, close) - GetRandomDouble() * 10;
long volume = rnd.Next(0, 10000); long volume = GetRandomNumber(0, 10000);
return new TBar(Time: DateTime.Now, Open: open, High: high, Low: low, Close: close, Volume: volume, IsNew: isNew); return new TBar(Time: DateTime.Now, Open: open, High: high, Low: low, Close: close, Volume: volume, IsNew: isNew);
} }
/// <summary>
/// Generates a random double between 0 and 1.
/// </summary>
/// <returns>A random double between 0 and 1.</returns>
private double GetRandomDouble()
{
byte[] bytes = new byte[8];
rng.GetBytes(bytes);
return (double)BitConverter.ToUInt64(bytes, 0) / ulong.MaxValue;
}
/// <summary>
/// Generates a random integer between minValue (inclusive) and maxValue (exclusive).
/// </summary>
/// <param name="minValue">The minimum value (inclusive).</param>
/// <param name="maxValue">The maximum value (exclusive).</param>
/// <returns>A random integer between minValue and maxValue.</returns>
private int GetRandomNumber(int minValue, int maxValue)
{
byte[] randomBytes = new byte[4];
rng.GetBytes(randomBytes);
int randomInt = BitConverter.ToInt32(randomBytes, 0);
return Math.Abs(randomInt % (maxValue - minValue)) + minValue;
}
/// <summary> /// <summary>
/// Provides the list of indicators for parameterized tests. /// Provides the list of indicators for parameterized tests.
/// </summary> /// </summary>
+13 -5
View File
@@ -1,19 +1,27 @@
using Xunit; using Xunit;
using System.Reflection; using System.Reflection;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Security.Cryptography;
namespace QuanTAlib; namespace QuanTAlib;
[SuppressMessage("Security", "SCS0005:Weak random number generator.", Justification = "Acceptable for tests")]
public class IndicatorTests public class IndicatorTests
{ {
private readonly Random rnd; private readonly RandomNumberGenerator rng;
private const int SeriesLen = 1000; private const int SeriesLen = 1000;
private const int Corrections = 100; private const int Corrections = 100;
public IndicatorTests() public IndicatorTests()
{ {
rnd = new Random((int)DateTime.Now.Ticks); rng = RandomNumberGenerator.Create();
}
private int GetRandomNumber(int minValue, int maxValue)
{
byte[] randomBytes = new byte[4];
rng.GetBytes(randomBytes);
int randomInt = BitConverter.ToInt32(randomBytes, 0);
return Math.Abs(randomInt % (maxValue - minValue)) + minValue;
} }
// skipcq: CS-R1055 // skipcq: CS-R1055
@@ -83,12 +91,12 @@ public class IndicatorTests
for (int i = 0; i < SeriesLen; i++) for (int i = 0; i < SeriesLen; i++)
{ {
TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); TValue item1 = new(Time: DateTime.Now, Value: GetRandomNumber(-100, 100), IsNew: true);
InvokeCalc(indicator1, calcMethod, item1); InvokeCalc(indicator1, calcMethod, item1);
for (int j = 0; j < Corrections; j++) for (int j = 0; j < Corrections; j++)
{ {
item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); item1 = new(Time: DateTime.Now, Value: GetRandomNumber(-100, 100), IsNew: false);
InvokeCalc(indicator1, calcMethod, item1); InvokeCalc(indicator1, calcMethod, item1);
} }
+26 -20
View File
@@ -1,16 +1,15 @@
using Xunit; using Xunit;
using Skender.Stock.Indicators; using Skender.Stock.Indicators;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Security.Cryptography;
namespace QuanTAlib; namespace QuanTAlib;
[SuppressMessage("Security", "SCS0005:Weak random number generator.", Justification = "Acceptable for tests")]
public class SkenderTests public class SkenderTests
{ {
private readonly TBarSeries bars; private readonly TBarSeries bars;
private readonly GbmFeed feed; private readonly GbmFeed feed;
private readonly Random rnd; private readonly RandomNumberGenerator rng;
private readonly double range; private readonly double range;
private int period; private int period;
private readonly int iterations = 3; // Initialized directly at declaration private readonly int iterations = 3; // Initialized directly at declaration
@@ -18,7 +17,7 @@ public class SkenderTests
public SkenderTests() public SkenderTests()
{ {
rnd = new((int)DateTime.Now.Ticks); rng = RandomNumberGenerator.Create();
feed = new(sigma: 0.5, mu: 0.0); feed = new(sigma: 0.5, mu: 0.0);
bars = new(feed); bars = new(feed);
range = 1e-9; range = 1e-9;
@@ -34,12 +33,20 @@ public class SkenderTests
}); });
} }
private int GetRandomNumber(int minValue, int maxValue)
{
byte[] randomBytes = new byte[4];
rng.GetBytes(randomBytes);
int randomInt = BitConverter.ToInt32(randomBytes, 0);
return Math.Abs(randomInt % (maxValue - minValue)) + minValue;
}
[Fact] [Fact]
public void SMA() public void SMA()
{ {
for (int run = 0; run < iterations; run++) for (int run = 0; run < iterations; run++)
{ {
period = rnd.Next(50) + 5; period = GetRandomNumber(5, 55);
Sma ma = new(period); Sma ma = new(period);
TSeries QL = new(); TSeries QL = new();
foreach (TBar item in feed) foreach (TBar item in feed)
@@ -58,7 +65,7 @@ public class SkenderTests
{ {
for (int run = 0; run < iterations; run++) for (int run = 0; run < iterations; run++)
{ {
period = rnd.Next(50) + 5; period = GetRandomNumber(5, 55);
Ema ma = new(period, useSma: true); Ema ma = new(period, useSma: true);
TSeries QL = new(); TSeries QL = new();
foreach (TBar item in feed) foreach (TBar item in feed)
@@ -77,7 +84,7 @@ public class SkenderTests
{ {
for (int run = 0; run < iterations; run++) for (int run = 0; run < iterations; run++)
{ {
period = rnd.Next(50) + 5; period = GetRandomNumber(5, 55);
Ema ma = new(period, useSma: false); Ema ma = new(period, useSma: false);
TSeries QL = new(); TSeries QL = new();
foreach (TBar item in feed) foreach (TBar item in feed)
@@ -96,7 +103,7 @@ public class SkenderTests
{ {
for (int run = 0; run < iterations; run++) for (int run = 0; run < iterations; run++)
{ {
period = rnd.Next(50) + 5; period = GetRandomNumber(5, 55);
Dema ma = new(period); Dema ma = new(period);
TSeries QL = new(); TSeries QL = new();
foreach (TBar item in feed) foreach (TBar item in feed)
@@ -115,7 +122,7 @@ public class SkenderTests
{ {
for (int run = 0; run < iterations; run++) for (int run = 0; run < iterations; run++)
{ {
period = rnd.Next(50) + 5; period = GetRandomNumber(5, 55);
Tema ma = new(period); Tema ma = new(period);
TSeries QL = new(); TSeries QL = new();
foreach (TBar item in feed) foreach (TBar item in feed)
@@ -134,7 +141,7 @@ public class SkenderTests
{ {
for (int run = 0; run < iterations; run++) for (int run = 0; run < iterations; run++)
{ {
period = rnd.Next(50) + 5; period = GetRandomNumber(5, 55);
double[] kernel = Enumerable.Repeat(1.0, period).ToArray(); double[] kernel = Enumerable.Repeat(1.0, period).ToArray();
Convolution ma = new(kernel); Convolution ma = new(kernel);
TSeries QL = new(); TSeries QL = new();
@@ -154,7 +161,7 @@ public class SkenderTests
{ {
for (int run = 0; run < iterations; run++) for (int run = 0; run < iterations; run++)
{ {
period = rnd.Next(50) + 5; period = GetRandomNumber(5, 55);
Wma ma = new(period); Wma ma = new(period);
TSeries QL = new(); TSeries QL = new();
foreach (TBar item in feed) foreach (TBar item in feed)
@@ -173,7 +180,7 @@ public class SkenderTests
{ {
for (int run = 0; run < iterations; run++) for (int run = 0; run < iterations; run++)
{ {
period = rnd.Next(50) + 5; period = GetRandomNumber(5, 55);
Hma ma = new(period); Hma ma = new(period);
TSeries QL = new(); TSeries QL = new();
foreach (TBar item in feed) foreach (TBar item in feed)
@@ -192,7 +199,7 @@ public class SkenderTests
{ {
for (int run = 0; run < iterations; run++) for (int run = 0; run < iterations; run++)
{ {
period = rnd.Next(50) + 5; period = GetRandomNumber(5, 55);
Epma ma = new(period); Epma ma = new(period);
TSeries QL = new(); TSeries QL = new();
foreach (TBar item in feed) foreach (TBar item in feed)
@@ -211,7 +218,7 @@ public class SkenderTests
{ {
for (int run = 0; run < iterations; run++) for (int run = 0; run < iterations; run++)
{ {
period = rnd.Next(50) + 5; period = GetRandomNumber(5, 55);
Alma ma = new(period, offset: 0.85, sigma: 6); Alma ma = new(period, offset: 0.85, sigma: 6);
TSeries QL = new(); TSeries QL = new();
foreach (TBar item in feed) foreach (TBar item in feed)
@@ -230,7 +237,7 @@ public class SkenderTests
{ {
for (int run = 0; run < iterations; run++) for (int run = 0; run < iterations; run++)
{ {
period = rnd.Next(50) + 5; period = GetRandomNumber(5, 55);
T3 ma = new(period, vfactor: 0.7, useSma: false); T3 ma = new(period, vfactor: 0.7, useSma: false);
TSeries QL = new(); TSeries QL = new();
foreach (TBar item in feed) foreach (TBar item in feed)
@@ -249,7 +256,7 @@ public class SkenderTests
{ {
for (int run = 0; run < iterations; run++) for (int run = 0; run < iterations; run++)
{ {
period = rnd.Next(50) + 5; period = GetRandomNumber(5, 55);
Smma ma = new(period); Smma ma = new(period);
TSeries QL = new(); TSeries QL = new();
foreach (TBar item in feed) foreach (TBar item in feed)
@@ -268,7 +275,7 @@ public class SkenderTests
{ {
for (int run = 0; run < iterations; run++) for (int run = 0; run < iterations; run++)
{ {
period = rnd.Next(50) + 5; period = GetRandomNumber(5, 55);
Kama ma = new(period); Kama ma = new(period);
TSeries QL = new(); TSeries QL = new();
foreach (TBar item in feed) foreach (TBar item in feed)
@@ -307,7 +314,7 @@ public class SkenderTests
{ {
for (int run = 0; run < iterations; run++) for (int run = 0; run < iterations; run++)
{ {
period = rnd.Next(50) + 5; period = GetRandomNumber(5, 55);
Mgdi ma = new(period: period); Mgdi ma = new(period: period);
TSeries QL = new(); TSeries QL = new();
foreach (TBar item in feed) foreach (TBar item in feed)
@@ -328,7 +335,7 @@ public class SkenderTests
{ {
for (int run = 0; run < iterations; run++) for (int run = 0; run < iterations; run++)
{ {
period = rnd.Next(50) + 5; period = GetRandomNumber(5, 55);
Atr ma = new(period: period); Atr ma = new(period: period);
TSeries QL = new(); TSeries QL = new();
foreach (TBar item in bars) { QL.Add(ma.Calc(item)); } foreach (TBar item in bars) { QL.Add(ma.Calc(item)); }
@@ -342,5 +349,4 @@ public class SkenderTests
} }
} }
} }
} }
+30 -36
View File
@@ -1,33 +1,36 @@
using Xunit; using Xunit;
using TALib; using TALib;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Security.Cryptography;
namespace QuanTAlib; namespace QuanTAlib;
[SuppressMessage("Security", "SCS0005:Weak random number generator.", Justification = "Acceptable for tests")]
public class TAlibTests public class TAlibTests
{ {
private readonly TBarSeries bars;
private readonly GbmFeed feed; private readonly GbmFeed feed;
private readonly Random rnd; private readonly RandomNumberGenerator rng;
private readonly double range; private readonly double range;
private readonly int iterations; private readonly int iterations;
private readonly double[] data; private readonly double[] data;
private readonly double[] TALIB; private readonly double[] TALIB;
public TAlibTests() public TAlibTests()
{ {
rnd = new((int)DateTime.Now.Ticks); rng = RandomNumberGenerator.Create();
feed = new(sigma: 0.5, mu: 0.0); feed = new(sigma: 0.5, mu: 0.0);
bars = new(feed);
range = 1e-9; range = 1e-9;
feed.Add(10000); feed.Add(10000);
iterations = 3; iterations = 3;
data = feed.Close.v.ToArray(); data = feed.Close.v.ToArray();
TALIB = new double[data.Count()]; TALIB = new double[data.Count()];
}
private int GetRandomNumber(int minValue, int maxValue)
{
byte[] randomBytes = new byte[4];
rng.GetBytes(randomBytes);
int randomInt = BitConverter.ToInt32(randomBytes, 0);
return Math.Abs(randomInt % (maxValue - minValue)) + minValue;
} }
[Fact] [Fact]
@@ -35,7 +38,7 @@ public class TAlibTests
{ {
for (int run = 0; run < iterations; run++) for (int run = 0; run < iterations; run++)
{ {
int period = rnd.Next(50) + 5; int period = GetRandomNumber(5, 55);
Sma ma = new(period); Sma ma = new(period);
TSeries QL = new(); TSeries QL = new();
foreach (TBar item in feed) foreach (TBar item in feed)
@@ -44,7 +47,6 @@ public class TAlibTests
Assert.Equal(QL.Length, TALIB.Count()); Assert.Equal(QL.Length, TALIB.Count());
for (int i = QL.Length - 1; i > period; i--) for (int i = QL.Length - 1; i > period; i--)
{ {
double TL = i < outBegIdx ? double.NaN : TALIB[i - outBegIdx];
Assert.InRange(TALIB[i - outBegIdx] - QL[i].Value, -range, range); Assert.InRange(TALIB[i - outBegIdx] - QL[i].Value, -range, range);
} }
} }
@@ -55,7 +57,7 @@ public class TAlibTests
{ {
for (int run = 0; run < iterations; run++) for (int run = 0; run < iterations; run++)
{ {
int period = rnd.Next(50) + 5; int period = GetRandomNumber(5, 55);
Ema ma = new(period, useSma: true); Ema ma = new(period, useSma: true);
TSeries QL = new(); TSeries QL = new();
foreach (TBar item in feed) foreach (TBar item in feed)
@@ -64,7 +66,6 @@ public class TAlibTests
Assert.Equal(QL.Length, TALIB.Count()); Assert.Equal(QL.Length, TALIB.Count());
for (int i = QL.Length - 1; i > period; i--) for (int i = QL.Length - 1; i > period; i--)
{ {
double TL = i < outBegIdx ? double.NaN : TALIB[i - outBegIdx];
Assert.InRange(TALIB[i - outBegIdx] - QL[i].Value, -range, range); Assert.InRange(TALIB[i - outBegIdx] - QL[i].Value, -range, range);
} }
} }
@@ -75,7 +76,7 @@ public class TAlibTests
{ {
for (int run = 0; run < iterations; run++) for (int run = 0; run < iterations; run++)
{ {
int period = rnd.Next(50) + 5; int period = GetRandomNumber(5, 55);
Dema ma = new(period); Dema ma = new(period);
TSeries QL = new(); TSeries QL = new();
foreach (TBar item in feed) foreach (TBar item in feed)
@@ -84,7 +85,6 @@ public class TAlibTests
Assert.Equal(QL.Length, TALIB.Length); Assert.Equal(QL.Length, TALIB.Length);
for (int i = QL.Length - 1; i > period * 20; i--) for (int i = QL.Length - 1; i > period * 20; i--)
{ {
double TL = i < outBegIdx ? double.NaN : TALIB[i - outBegIdx];
Assert.InRange(TALIB[i - outBegIdx] - QL[i].Value, -range, range); Assert.InRange(TALIB[i - outBegIdx] - QL[i].Value, -range, range);
} }
} }
@@ -95,7 +95,7 @@ public class TAlibTests
{ {
for (int run = 0; run < iterations; run++) for (int run = 0; run < iterations; run++)
{ {
int period = rnd.Next(50) + 5; int period = GetRandomNumber(5, 55);
Tema ma = new(period); Tema ma = new(period);
TSeries QL = new(); TSeries QL = new();
foreach (TBar item in feed) foreach (TBar item in feed)
@@ -104,41 +104,37 @@ public class TAlibTests
Assert.Equal(QL.Length, TALIB.Length); Assert.Equal(QL.Length, TALIB.Length);
for (int i = QL.Length - 1; i > period * 20; i--) for (int i = QL.Length - 1; i > period * 20; i--)
{ {
double TL = i < outBegIdx ? double.NaN : TALIB[i - outBegIdx];
Assert.InRange(TALIB[i - outBegIdx] - QL[i].Value, -range, range); Assert.InRange(TALIB[i - outBegIdx] - QL[i].Value, -range, range);
} }
} }
} }
//TODO fix WMA [Fact]
/* public void WMA()
[Fact] {
public void WMA() for (int run = 0; run < iterations; run++)
{ {
for (int run = 0; run < iterations; run++) int period = GetRandomNumber(5, 55);
Wma ma = new(period);
TSeries QL = new();
foreach (TBar item in feed)
{ QL.Add(ma.Calc(new TValue(item.Time, item.Close))); }
Core.Wma(data, 0, QL.Length - 1, TALIB, out int outBegIdx, out _, period);
Assert.Equal(QL.Length, TALIB.Count());
for (int i = QL.Length - 1; i > period * 10; i--)
{ {
period = rnd.Next(50) + 5; Assert.InRange(TALIB[i - outBegIdx] - QL[i].Value, -range, range);
Wma ma = new(period);
TSeries QL = new();
foreach (TBar item in feed)
{ QL.Add(ma.Calc(new TValue(item.Time, item.Close))); }
Core.Wma(data, 0, QL.Length - 1, TALIB, out int outBegIdx, out _, period);
Assert.Equal(QL.Length, TALIB.Count());
for (int i = QL.Length - 1; i > period*3; i--)
{
double TL = i < outBegIdx ? double.NaN : TALIB[i - outBegIdx];
Assert.InRange(TALIB[i - outBegIdx] - QL[i].Value, -range, range);
}
} }
} }
*/ }
[Fact] [Fact]
public void T3() public void T3()
{ {
for (int run = 0; run < iterations; run++) for (int run = 0; run < iterations; run++)
{ {
int period = rnd.Next(50) + 5; int period = GetRandomNumber(5, 55);
T3 ma = new(period, vfactor: 0.7, useSma: false); T3 ma = new(period, vfactor: 0.7, useSma: false);
TSeries QL = new(); TSeries QL = new();
foreach (TBar item in feed) foreach (TBar item in feed)
@@ -147,10 +143,8 @@ public class TAlibTests
Assert.Equal(QL.Length, TALIB.Length); Assert.Equal(QL.Length, TALIB.Length);
for (int i = QL.Length - 1; i > period * 20; i--) for (int i = QL.Length - 1; i > period * 20; i--)
{ {
double TL = i < outBegIdx ? double.NaN : TALIB[i - outBegIdx];
Assert.InRange(TALIB[i - outBegIdx] - QL[i].Value, -range, range); Assert.InRange(TALIB[i - outBegIdx] - QL[i].Value, -range, range);
} }
} }
} }
} }
+15 -8
View File
@@ -1,11 +1,11 @@
using System.CommandLine.Rendering.Views; using System.Security.Cryptography;
namespace QuanTAlib; namespace QuanTAlib;
public class GbmFeed : TBarSeries public class GbmFeed : TBarSeries
{ {
private readonly double _mu, _sigma; private readonly double _mu, _sigma;
private readonly Random _random; private readonly RandomNumberGenerator _rng;
private double _lastClose, _lastHigh, _lastLow; private double _lastClose, _lastHigh, _lastLow;
public GbmFeed(double initialPrice = 100.0, double mu = 0.05, double sigma = 0.2) public GbmFeed(double initialPrice = 100.0, double mu = 0.05, double sigma = 0.2)
@@ -13,7 +13,7 @@ public class GbmFeed : TBarSeries
_lastClose = _lastHigh = _lastLow = initialPrice; _lastClose = _lastHigh = _lastLow = initialPrice;
_mu = mu; _mu = mu;
_sigma = sigma; _sigma = sigma;
_random = new Random((int)DateTime.Now.Ticks); _rng = RandomNumberGenerator.Create();
this.Name = $"GBM({_sigma:F2})"; this.Name = $"GBM({_sigma:F2})";
} }
@@ -39,9 +39,9 @@ public class GbmFeed : TBarSeries
double newClose = _lastClose * Math.Exp(drift + diffusion); double newClose = _lastClose * Math.Exp(drift + diffusion);
double open = _lastClose; double open = _lastClose;
double high = Math.Max(_lastHigh, Math.Max(open, newClose) * (1 + _random.NextDouble() * 0.01)); double high = Math.Max(_lastHigh, Math.Max(open, newClose) * (1 + GenerateRandomDouble() * 0.01));
double low = Math.Min(_lastLow, Math.Min(open, newClose) * (1 - _random.NextDouble() * 0.01)); double low = Math.Min(_lastLow, Math.Min(open, newClose) * (1 - GenerateRandomDouble() * 0.01));
double volume = 1000 + _random.NextDouble() * 1000; double volume = 1000 + GenerateRandomDouble() * 1000;
if (isNew) if (isNew)
{ {
@@ -62,8 +62,15 @@ public class GbmFeed : TBarSeries
private double GenerateNormalRandom() private double GenerateNormalRandom()
{ {
// Box-Muller transform to generate standard normal random variable // Box-Muller transform to generate standard normal random variable
double u1 = 1.0 - _random.NextDouble(); // Uniform(0,1] random doubles double u1 = 1.0 - GenerateRandomDouble(); // Uniform(0,1] random doubles
double u2 = 1.0 - _random.NextDouble(); double u2 = 1.0 - GenerateRandomDouble();
return Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2); return Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2);
} }
private double GenerateRandomDouble()
{
byte[] bytes = new byte[8];
_rng.GetBytes(bytes);
return (double)BitConverter.ToUInt64(bytes, 0) / ulong.MaxValue;
}
} }
+6 -18
View File
@@ -18,36 +18,24 @@
<DebugType>full</DebugType> <DebugType>full</DebugType>
<ProduceReferenceAssembly>True</ProduceReferenceAssembly> <ProduceReferenceAssembly>True</ProduceReferenceAssembly>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild> <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<PackageTags> <PackageIcon>QuanTAlib2.png</PackageIcon>
<PackageReadmeFile>readme.md</PackageReadmeFile>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
<PackageTags>
Indicators;Stock;Market;Technical;Analysis;Algorithmic;Trading;Trade;Trend;Momentum;Finance;Algorithm;Algo; Indicators;Stock;Market;Technical;Analysis;Algorithmic;Trading;Trade;Trend;Momentum;Finance;Algorithm;Algo;
AlgoTrading;Financial;Strategy;Chart;Charting;Oscillator;Overlay;Equity;Bitcoin;Crypto;Cryptocurrency;Forex; AlgoTrading;Financial;Strategy;Chart;Charting;Oscillator;Overlay;Equity;Bitcoin;Crypto;Cryptocurrency;Forex;
Quantitative;Historical;Quotes; Quantitative;Historical;Quotes;
</PackageTags> </PackageTags>
<NoWarn>$(NoWarn);NU5104</NoWarn>
<PackageIcon>QuanTAlib2.png</PackageIcon>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
<ProduceReferenceAssembly>True</ProduceReferenceAssembly>
<PackageIconUrl>https://raw.githubusercontent.com/mihakralj/QuanTAlib/main/.github/QuanTAlib2.png</PackageIconUrl>
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
<PackageTags>
Indicators;Stock;Market;Technical;Analysis;Algorithmic;Trading;Trade;Trend;Momentum;Finance;Algorithm;Algo;
AlgoTrading;Financial;Strategy;Chart;Charting;Oscillator;Overlay;Equity;Bitcoin;Crypto;Cryptocurrency;Forex;
Quantitative;Historical;Quotes;
</PackageTags>
<PackageIcon>QuanTAlib2.png</PackageIcon> <PackageIcon>QuanTAlib2.png</PackageIcon>
<PackageIconUrl>https://raw.githubusercontent.com/mihakralj/QuanTAlib/main/.github/QuanTAlib2.png</PackageIconUrl> <PackageIconUrl>https://raw.githubusercontent.com/mihakralj/QuanTAlib/main/.github/QuanTAlib2.png</PackageIconUrl>
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild> <EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
</PropertyGroup> </PropertyGroup>
<PropertyGroup>
<PackageIcon>QuanTAlib2.png</PackageIcon>
<PackageIconUrl>https://raw.githubusercontent.com/mihakralj/QuanTAlib/main/.github/QuanTAlib2.png</PackageIconUrl>
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<None Include="..\docs\readme.md" Pack="true" PackagePath=""/> <None Include="..\docs\readme.md" Pack="true" PackagePath=""/>
<None Include="..\.github\QuanTAlib2.png" Pack="true" Visible="false" PackagePath=""/> <None Include="..\.github\QuanTAlib2.png" Pack="true" Visible="false" PackagePath=""/>
<PackageReference Include="System.Text.Json" Version="9.0.0-rc.1.24431.7" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Reference Include="TradingPlatform.BusinessLayer"> <Reference Include="TradingPlatform.BusinessLayer">
<HintPath>..\.github\TradingPlatform.BusinessLayer.dll</HintPath> <HintPath>..\.github\TradingPlatform.BusinessLayer.dll</HintPath>