From 14ec3cd2b03d43f3f750245cde74d8d879aefb06 Mon Sep 17 00:00:00 2001 From: Miha Kralj Date: Tue, 8 Oct 2024 17:34:58 -0700 Subject: [PATCH] tests --- .github/workflows/Publish.yml | 2 +- Directory.Build.props | 4 +- Tests/test_Trady.cs | 20 +++++--- Tests/test_Tulip.cs | 19 ++++--- Tests/test_eventing.cs | 97 ++++++++++++++++++----------------- Tests/test_iTBar.cs | 41 ++++++++++++--- Tests/test_iTValue.cs | 18 +++++-- Tests/test_skender.stock.cs | 46 +++++++++-------- Tests/test_talib.cs | 66 +++++++++++------------- lib/feeds/GbmFeed.cs | 23 ++++++--- lib/quantalib.csproj | 24 +++------ 11 files changed, 204 insertions(+), 156 deletions(-) diff --git a/.github/workflows/Publish.yml b/.github/workflows/Publish.yml index 4be1d359..ada29549 100644 --- a/.github/workflows/Publish.yml +++ b/.github/workflows/Publish.yml @@ -215,7 +215,7 @@ jobs: sarif_file: results.sarif build_publish: - needs: [SonarCloud, Code_Coverage, CodeQL, Codacy_Scan] + needs: [SonarCloud, Code_Coverage, CodeQL, Codacy_Scan, SecurityCodeScan] if: success() runs-on: ubuntu-latest steps: diff --git a/Directory.Build.props b/Directory.Build.props index 6ee76064..d9353501 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -2,7 +2,7 @@ net8.0 preview - + $(NoWarn);NU1903;NU5104 enable enable true @@ -49,6 +49,8 @@ + + D:\Quantower $([System.IO.Directory]::GetDirectories("$(QuantowerRoot)\TradingPlatform", "v1*")[0]) diff --git a/Tests/test_Trady.cs b/Tests/test_Trady.cs index 44451a2a..5d2f356c 100644 --- a/Tests/test_Trady.cs +++ b/Tests/test_Trady.cs @@ -3,16 +3,16 @@ using Trady.Analysis.Indicator; using Trady.Core; using Trady.Core.Infrastructure; using System.Diagnostics.CodeAnalysis; +using System.Security.Cryptography; namespace QuanTAlib; [SuppressMessage("Security", "SCS0005:Weak random number generator.", Justification = "Acceptable for tests")] - public class TradyTests { private readonly TBarSeries bars; private readonly GbmFeed feed; - private readonly Random rnd; + private readonly RandomNumberGenerator rng; private readonly double range; private readonly int iterations; private readonly int skip; @@ -20,7 +20,7 @@ public class TradyTests public TradyTests() { - rnd = new((int)DateTime.Now.Ticks); + rng = RandomNumberGenerator.Create(); feed = new(sigma: 0.5, mu: 0.0); bars = new(feed); range = 1e-9; @@ -37,12 +37,20 @@ public class TradyTests )).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] public void SMA() { for (int run = 0; run < iterations; run++) { - int period = rnd.Next(50) + 5; + int period = GetRandomNumber(5, 55); Sma ma = new(period); TSeries QL = new(); foreach (TBar item in feed) @@ -72,7 +80,7 @@ public class TradyTests { for (int run = 0; run < iterations; run++) { - int period = rnd.Next(50) + 5; + int period = GetRandomNumber(5, 55); Ema ma = new(period); TSeries QL = new(); foreach (TBar item in feed) @@ -96,6 +104,4 @@ public class TradyTests } } } - - } \ No newline at end of file diff --git a/Tests/test_Tulip.cs b/Tests/test_Tulip.cs index 623f0595..c9d686b8 100644 --- a/Tests/test_Tulip.cs +++ b/Tests/test_Tulip.cs @@ -1,15 +1,15 @@ using Xunit; using Tulip; using System.Diagnostics.CodeAnalysis; +using System.Security.Cryptography; namespace QuanTAlib; [SuppressMessage("Security", "SCS0005:Weak random number generator.", Justification = "Acceptable for tests")] public class TulipTests { - private readonly TBarSeries bars; private readonly GbmFeed feed; - private readonly Random rnd; + private readonly RandomNumberGenerator rng; private readonly double range; private readonly int iterations; private readonly double[] data; @@ -18,9 +18,8 @@ public class TulipTests public TulipTests() { - rnd = new((int)DateTime.Now.Ticks); + rng = RandomNumberGenerator.Create(); feed = new(sigma: 0.5, mu: 0.0); - bars = new(feed); range = 1e-9; feed.Add(10000); iterations = 3; @@ -29,12 +28,20 @@ public class TulipTests 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] public void SMA() { for (int run = 0; run < iterations; run++) { - int period = rnd.Next(50) + 5; + int period = GetRandomNumber(5, 55); Sma ma = new(period); TSeries QL = new(); foreach (TBar item in feed) @@ -58,7 +65,7 @@ public class TulipTests { for (int run = 0; run < iterations; run++) { - int period = rnd.Next(30) + 5; + int period = GetRandomNumber(5, 35); Ema ma = new(period, useSma: false); TSeries QL = new(); foreach (TBar item in feed) diff --git a/Tests/test_eventing.cs b/Tests/test_eventing.cs index dd8063f3..582517f7 100644 --- a/Tests/test_eventing.cs +++ b/Tests/test_eventing.cs @@ -1,4 +1,5 @@ using Xunit; +using System.Security.Cryptography; namespace QuanTAlib; @@ -7,8 +8,8 @@ public class EventingTests [Fact] public void VerifyEventBasedCalculations() { - // Create a random number generator with a fixed seed for reproducibility - var random = new Random(42); + // Create a cryptographically secure random number generator + using var rng = RandomNumberGenerator.Create(); // Create an input series to hold our random values var input = new TSeries(); @@ -16,59 +17,63 @@ public class EventingTests // Create a list of indicator pairs (direct calculation and event-based) var indicators = new List<(AbstractBase Direct, AbstractBase EventBased)> - { - (new Afirma(p,p,Afirma.WindowType.BlackmanHarris), new Afirma(input, p,p,Afirma.WindowType.BlackmanHarris)), - (new Alma(p), new Alma(input, p)), - (new Convolution([1,2,3,2,1]), new Convolution(input, [1,2,3,2,1])), - (new Dema(p), new Dema(input, p)), - (new Dsma(p), new Dsma(input, p)), - (new Dwma(p), new Dwma(input, p)), - (new Ema(p), new Ema(input, p)), - (new Epma(p), new Epma(input, p)), - (new Frama(p), new Frama(input, p)), - (new Fwma(p), new Fwma(input, p)), - (new Gma(p), new Gma(input, p)), - (new Hma(p), new Hma(input, p)), - (new Htit(), new Htit(input)), - (new Hwma(p), new Hwma(input, p)), - (new Jma(p), new Jma(input, p)), - (new Kama(p), new Kama(input, p)), - (new Ltma(gamma: 0.2), new Ltma(input, gamma: 0.2)), - (new Maaf(p), new Maaf(input, p)), - (new Mama(p), new Mama(input, p)), - (new Mgdi(p), new Mgdi(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 Rema(p), new Rema(input, p)), - (new Rma(p), new Rma(input, p)), - - (new Sma(p), new Sma(input, p)), - (new Wma(p), new Wma(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 Zlema(p), new Zlema(input, p)) - }; + { + (new Afirma(p,p,Afirma.WindowType.BlackmanHarris), new Afirma(input, p,p,Afirma.WindowType.BlackmanHarris)), + (new Alma(p), new Alma(input, p)), + (new Convolution([1,2,3,2,1]), new Convolution(input, [1,2,3,2,1])), + (new Dema(p), new Dema(input, p)), + (new Dsma(p), new Dsma(input, p)), + (new Dwma(p), new Dwma(input, p)), + (new Ema(p), new Ema(input, p)), + (new Epma(p), new Epma(input, p)), + (new Frama(p), new Frama(input, p)), + (new Fwma(p), new Fwma(input, p)), + (new Gma(p), new Gma(input, p)), + (new Hma(p), new Hma(input, p)), + (new Htit(), new Htit(input)), + (new Hwma(p), new Hwma(input, p)), + (new Jma(p), new Jma(input, p)), + (new Kama(p), new Kama(input, p)), + (new Ltma(gamma: 0.2), new Ltma(input, gamma: 0.2)), + (new Maaf(p), new Maaf(input, p)), + (new Mama(p), new Mama(input, p)), + (new Mgdi(p), new Mgdi(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 Rema(p), new Rema(input, p)), + (new Rma(p), new Rma(input, p)), + (new Sma(p), new Sma(input, p)), + (new Wma(p), new Wma(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 Zlema(p), new Zlema(input, p)) + }; // 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; - input.Add(randomValue); + double randomValue = GetRandomDouble(rng) * 100; + input.Add(randomValue); // Calculate direct indicators foreach (var (direct, _) in indicators) { direct.Calc(randomValue); } -} + } -// Compare the results of direct and event-based calculations -foreach (var (direct, eventBased) in indicators) -{ - Assert.Equal(direct.Value, eventBased.Value, 9); -} + // Compare the results of direct and event-based calculations + foreach (var (direct, eventBased) in indicators) + { + 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; } } diff --git a/Tests/test_iTBar.cs b/Tests/test_iTBar.cs index 79c8563c..32ec7c37 100644 --- a/Tests/test_iTBar.cs +++ b/Tests/test_iTBar.cs @@ -1,16 +1,16 @@ using Xunit; using System.Reflection; using System.Diagnostics.CodeAnalysis; +using System.Security.Cryptography; namespace QuanTAlib; /// /// Contains unit tests for bar-based indicators in QuanTAlib. /// -[SuppressMessage("Security", "SCS0005:Weak random number generator.", Justification = "Acceptable for tests")] public class BarIndicatorTests { - private readonly Random rnd; + private readonly RandomNumberGenerator rng; private const int SeriesLen = 1000; private const int Corrections = 100; @@ -19,7 +19,7 @@ public class BarIndicatorTests /// public BarIndicatorTests() { - rnd = new Random((int)DateTime.Now.Ticks); + rng = RandomNumberGenerator.Create(); } private static readonly ITValue[] indicators = new ITValue[] @@ -124,15 +124,40 @@ public class BarIndicatorTests /// A randomly generated TBar. private TBar GenerateRandomBar(bool isNew) { - double open = rnd.NextDouble() * 200 - 100; - double close = rnd.NextDouble() * 200 - 100; - double high = Math.Max(open, close) + rnd.NextDouble() * 10; - double low = Math.Min(open, close) - rnd.NextDouble() * 10; - long volume = rnd.Next(0, 10000); + double open = GetRandomDouble() * 200 - 100; + double close = GetRandomDouble() * 200 - 100; + double high = Math.Max(open, close) + GetRandomDouble() * 10; + double low = Math.Min(open, close) - GetRandomDouble() * 10; + long volume = GetRandomNumber(0, 10000); return new TBar(Time: DateTime.Now, Open: open, High: high, Low: low, Close: close, Volume: volume, IsNew: isNew); } + /// + /// Generates a random double between 0 and 1. + /// + /// A random double between 0 and 1. + private double GetRandomDouble() + { + byte[] bytes = new byte[8]; + rng.GetBytes(bytes); + return (double)BitConverter.ToUInt64(bytes, 0) / ulong.MaxValue; + } + + /// + /// Generates a random integer between minValue (inclusive) and maxValue (exclusive). + /// + /// The minimum value (inclusive). + /// The maximum value (exclusive). + /// A random integer between minValue and maxValue. + 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; + } + /// /// Provides the list of indicators for parameterized tests. /// diff --git a/Tests/test_iTValue.cs b/Tests/test_iTValue.cs index f36bec3e..887c78cf 100644 --- a/Tests/test_iTValue.cs +++ b/Tests/test_iTValue.cs @@ -1,19 +1,27 @@ using Xunit; using System.Reflection; using System.Diagnostics.CodeAnalysis; +using System.Security.Cryptography; namespace QuanTAlib; -[SuppressMessage("Security", "SCS0005:Weak random number generator.", Justification = "Acceptable for tests")] public class IndicatorTests { - private readonly Random rnd; + private readonly RandomNumberGenerator rng; private const int SeriesLen = 1000; private const int Corrections = 100; 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 @@ -83,12 +91,12 @@ public class IndicatorTests 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); 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); } diff --git a/Tests/test_skender.stock.cs b/Tests/test_skender.stock.cs index fabc63db..fd1fcca1 100644 --- a/Tests/test_skender.stock.cs +++ b/Tests/test_skender.stock.cs @@ -1,16 +1,15 @@ using Xunit; using Skender.Stock.Indicators; using System.Diagnostics.CodeAnalysis; +using System.Security.Cryptography; namespace QuanTAlib; -[SuppressMessage("Security", "SCS0005:Weak random number generator.", Justification = "Acceptable for tests")] - public class SkenderTests { private readonly TBarSeries bars; private readonly GbmFeed feed; - private readonly Random rnd; + private readonly RandomNumberGenerator rng; private readonly double range; private int period; private readonly int iterations = 3; // Initialized directly at declaration @@ -18,7 +17,7 @@ public class SkenderTests public SkenderTests() { - rnd = new((int)DateTime.Now.Ticks); + rng = RandomNumberGenerator.Create(); feed = new(sigma: 0.5, mu: 0.0); bars = new(feed); 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] public void SMA() { for (int run = 0; run < iterations; run++) { - period = rnd.Next(50) + 5; + period = GetRandomNumber(5, 55); Sma ma = new(period); TSeries QL = new(); foreach (TBar item in feed) @@ -58,7 +65,7 @@ public class SkenderTests { for (int run = 0; run < iterations; run++) { - period = rnd.Next(50) + 5; + period = GetRandomNumber(5, 55); Ema ma = new(period, useSma: true); TSeries QL = new(); foreach (TBar item in feed) @@ -77,7 +84,7 @@ public class SkenderTests { for (int run = 0; run < iterations; run++) { - period = rnd.Next(50) + 5; + period = GetRandomNumber(5, 55); Ema ma = new(period, useSma: false); TSeries QL = new(); foreach (TBar item in feed) @@ -96,7 +103,7 @@ public class SkenderTests { for (int run = 0; run < iterations; run++) { - period = rnd.Next(50) + 5; + period = GetRandomNumber(5, 55); Dema ma = new(period); TSeries QL = new(); foreach (TBar item in feed) @@ -115,7 +122,7 @@ public class SkenderTests { for (int run = 0; run < iterations; run++) { - period = rnd.Next(50) + 5; + period = GetRandomNumber(5, 55); Tema ma = new(period); TSeries QL = new(); foreach (TBar item in feed) @@ -134,7 +141,7 @@ public class SkenderTests { for (int run = 0; run < iterations; run++) { - period = rnd.Next(50) + 5; + period = GetRandomNumber(5, 55); double[] kernel = Enumerable.Repeat(1.0, period).ToArray(); Convolution ma = new(kernel); TSeries QL = new(); @@ -154,7 +161,7 @@ public class SkenderTests { for (int run = 0; run < iterations; run++) { - period = rnd.Next(50) + 5; + period = GetRandomNumber(5, 55); Wma ma = new(period); TSeries QL = new(); foreach (TBar item in feed) @@ -173,7 +180,7 @@ public class SkenderTests { for (int run = 0; run < iterations; run++) { - period = rnd.Next(50) + 5; + period = GetRandomNumber(5, 55); Hma ma = new(period); TSeries QL = new(); foreach (TBar item in feed) @@ -192,7 +199,7 @@ public class SkenderTests { for (int run = 0; run < iterations; run++) { - period = rnd.Next(50) + 5; + period = GetRandomNumber(5, 55); Epma ma = new(period); TSeries QL = new(); foreach (TBar item in feed) @@ -211,7 +218,7 @@ public class SkenderTests { 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); TSeries QL = new(); foreach (TBar item in feed) @@ -230,7 +237,7 @@ public class SkenderTests { 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); TSeries QL = new(); foreach (TBar item in feed) @@ -249,7 +256,7 @@ public class SkenderTests { for (int run = 0; run < iterations; run++) { - period = rnd.Next(50) + 5; + period = GetRandomNumber(5, 55); Smma ma = new(period); TSeries QL = new(); foreach (TBar item in feed) @@ -268,7 +275,7 @@ public class SkenderTests { for (int run = 0; run < iterations; run++) { - period = rnd.Next(50) + 5; + period = GetRandomNumber(5, 55); Kama ma = new(period); TSeries QL = new(); foreach (TBar item in feed) @@ -307,7 +314,7 @@ public class SkenderTests { for (int run = 0; run < iterations; run++) { - period = rnd.Next(50) + 5; + period = GetRandomNumber(5, 55); Mgdi ma = new(period: period); TSeries QL = new(); foreach (TBar item in feed) @@ -328,7 +335,7 @@ public class SkenderTests { for (int run = 0; run < iterations; run++) { - period = rnd.Next(50) + 5; + period = GetRandomNumber(5, 55); Atr ma = new(period: period); TSeries QL = new(); foreach (TBar item in bars) { QL.Add(ma.Calc(item)); } @@ -342,5 +349,4 @@ public class SkenderTests } } } - } diff --git a/Tests/test_talib.cs b/Tests/test_talib.cs index 5b0f23fa..b071a3a6 100644 --- a/Tests/test_talib.cs +++ b/Tests/test_talib.cs @@ -1,33 +1,36 @@ using Xunit; using TALib; using System.Diagnostics.CodeAnalysis; +using System.Security.Cryptography; namespace QuanTAlib; -[SuppressMessage("Security", "SCS0005:Weak random number generator.", Justification = "Acceptable for tests")] - public class TAlibTests { - private readonly TBarSeries bars; private readonly GbmFeed feed; - private readonly Random rnd; + private readonly RandomNumberGenerator rng; private readonly double range; private readonly int iterations; private readonly double[] data; private readonly double[] TALIB; - public TAlibTests() { - rnd = new((int)DateTime.Now.Ticks); + rng = RandomNumberGenerator.Create(); feed = new(sigma: 0.5, mu: 0.0); - bars = new(feed); range = 1e-9; feed.Add(10000); iterations = 3; data = feed.Close.v.ToArray(); 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] @@ -35,7 +38,7 @@ public class TAlibTests { for (int run = 0; run < iterations; run++) { - int period = rnd.Next(50) + 5; + int period = GetRandomNumber(5, 55); Sma ma = new(period); TSeries QL = new(); foreach (TBar item in feed) @@ -44,7 +47,6 @@ public class TAlibTests Assert.Equal(QL.Length, TALIB.Count()); 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); } } @@ -55,7 +57,7 @@ public class TAlibTests { for (int run = 0; run < iterations; run++) { - int period = rnd.Next(50) + 5; + int period = GetRandomNumber(5, 55); Ema ma = new(period, useSma: true); TSeries QL = new(); foreach (TBar item in feed) @@ -64,7 +66,6 @@ public class TAlibTests Assert.Equal(QL.Length, TALIB.Count()); 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); } } @@ -75,7 +76,7 @@ public class TAlibTests { for (int run = 0; run < iterations; run++) { - int period = rnd.Next(50) + 5; + int period = GetRandomNumber(5, 55); Dema ma = new(period); TSeries QL = new(); foreach (TBar item in feed) @@ -84,7 +85,6 @@ public class TAlibTests Assert.Equal(QL.Length, TALIB.Length); 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); } } @@ -95,7 +95,7 @@ public class TAlibTests { for (int run = 0; run < iterations; run++) { - int period = rnd.Next(50) + 5; + int period = GetRandomNumber(5, 55); Tema ma = new(period); TSeries QL = new(); foreach (TBar item in feed) @@ -104,41 +104,37 @@ public class TAlibTests Assert.Equal(QL.Length, TALIB.Length); 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); } } } - //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; - 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); - } + Assert.InRange(TALIB[i - outBegIdx] - QL[i].Value, -range, range); } } - */ + } + [Fact] public void T3() { 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); TSeries QL = new(); foreach (TBar item in feed) @@ -147,10 +143,8 @@ public class TAlibTests Assert.Equal(QL.Length, TALIB.Length); 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); } } } - } \ No newline at end of file diff --git a/lib/feeds/GbmFeed.cs b/lib/feeds/GbmFeed.cs index f4c69a0e..5b2edd2f 100644 --- a/lib/feeds/GbmFeed.cs +++ b/lib/feeds/GbmFeed.cs @@ -1,11 +1,11 @@ -using System.CommandLine.Rendering.Views; +using System.Security.Cryptography; namespace QuanTAlib; public class GbmFeed : TBarSeries { private readonly double _mu, _sigma; - private readonly Random _random; + private readonly RandomNumberGenerator _rng; private double _lastClose, _lastHigh, _lastLow; 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; _mu = mu; _sigma = sigma; - _random = new Random((int)DateTime.Now.Ticks); + _rng = RandomNumberGenerator.Create(); this.Name = $"GBM({_sigma:F2})"; } @@ -39,9 +39,9 @@ public class GbmFeed : TBarSeries double newClose = _lastClose * Math.Exp(drift + diffusion); double open = _lastClose; - double high = Math.Max(_lastHigh, Math.Max(open, newClose) * (1 + _random.NextDouble() * 0.01)); - double low = Math.Min(_lastLow, Math.Min(open, newClose) * (1 - _random.NextDouble() * 0.01)); - double volume = 1000 + _random.NextDouble() * 1000; + double high = Math.Max(_lastHigh, Math.Max(open, newClose) * (1 + GenerateRandomDouble() * 0.01)); + double low = Math.Min(_lastLow, Math.Min(open, newClose) * (1 - GenerateRandomDouble() * 0.01)); + double volume = 1000 + GenerateRandomDouble() * 1000; if (isNew) { @@ -62,8 +62,15 @@ public class GbmFeed : TBarSeries private double GenerateNormalRandom() { // Box-Muller transform to generate standard normal random variable - double u1 = 1.0 - _random.NextDouble(); // Uniform(0,1] random doubles - double u2 = 1.0 - _random.NextDouble(); + double u1 = 1.0 - GenerateRandomDouble(); // Uniform(0,1] random doubles + double u2 = 1.0 - GenerateRandomDouble(); 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; + } } \ No newline at end of file diff --git a/lib/quantalib.csproj b/lib/quantalib.csproj index f5e03213..2ca3d6ba 100644 --- a/lib/quantalib.csproj +++ b/lib/quantalib.csproj @@ -18,36 +18,24 @@ full True True - + QuanTAlib2.png + readme.md + Apache-2.0 + 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; - $(NoWarn);NU5104 - QuanTAlib2.png - Apache-2.0 - True - https://raw.githubusercontent.com/mihakralj/QuanTAlib/main/.github/QuanTAlib2.png - True - - 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; - QuanTAlib2.png https://raw.githubusercontent.com/mihakralj/QuanTAlib/main/.github/QuanTAlib2.png True - - QuanTAlib2.png - https://raw.githubusercontent.com/mihakralj/QuanTAlib/main/.github/QuanTAlib2.png - True - + - + ..\.github\TradingPlatform.BusinessLayer.dll