From cd4723bd011dc2436f107405f6416072c7ab62dd Mon Sep 17 00:00:00 2001 From: Anatoliy Siryi Date: Tue, 12 Mar 2024 18:40:03 +0200 Subject: [PATCH] Set options for some indicators in benchmark --- src/TaTooIne.Benchmark/BenchmarkOrderer.cs | 16 +- src/TaTooIne.Benchmark/IndicatorsBenchmark.cs | 283 +++++++++++++++--- src/TaTooIne.Benchmark/Program.cs | 16 +- 3 files changed, 246 insertions(+), 69 deletions(-) diff --git a/src/TaTooIne.Benchmark/BenchmarkOrderer.cs b/src/TaTooIne.Benchmark/BenchmarkOrderer.cs index 9bb0a5e..cfba884 100644 --- a/src/TaTooIne.Benchmark/BenchmarkOrderer.cs +++ b/src/TaTooIne.Benchmark/BenchmarkOrderer.cs @@ -21,9 +21,7 @@ public IEnumerable GetExecutionOrder( ImmutableArray benchmarkCases, IEnumerable? _) { - return benchmarkCases - .OrderBy(b => b.Parameters["order"]) - .ThenBy(b => b.Descriptor.WorkloadMethodDisplayInfo); + return benchmarkCases.OrderBy(b => b.Parameters["order"]).ThenBy(b => b.Descriptor.WorkloadMethodDisplayInfo); } public IEnumerable GetSummaryOrder( @@ -41,15 +39,9 @@ public IEnumerable GetSummaryOrder( } } - public string GetHighlightGroupKey(BenchmarkCase benchmarkCase) - { - return GetGroupKey(benchmarkCase); - } + public string GetHighlightGroupKey(BenchmarkCase benchmarkCase) => GetGroupKey(benchmarkCase); - public string GetLogicalGroupKey(ImmutableArray _, BenchmarkCase benchmarkCase) - { - return GetGroupKey(benchmarkCase); - } + public string GetLogicalGroupKey(ImmutableArray _, BenchmarkCase benchmarkCase) => GetGroupKey(benchmarkCase); public IEnumerable> GetLogicalGroupOrder( IEnumerable> logicalGroups, @@ -73,7 +65,7 @@ private static string GetGroupKey(BenchmarkCase benchmarkCase) { indicatorParamValue = indicatorParamValue switch { - "Cmo" => "CmoXXX", // TALib uses EMA, Tulip uses SMA + "Cmo" => "XXX", // TALib uses EMA, Tulip uses SMA "LinearReg" => "LinReg", "LinearRegIntercept" => "LinRegIntercept", "LinearRegSlope" => "LinRegSlope", diff --git a/src/TaTooIne.Benchmark/IndicatorsBenchmark.cs b/src/TaTooIne.Benchmark/IndicatorsBenchmark.cs index 4b0630b..338afc3 100644 --- a/src/TaTooIne.Benchmark/IndicatorsBenchmark.cs +++ b/src/TaTooIne.Benchmark/IndicatorsBenchmark.cs @@ -1,5 +1,6 @@ using System.Buffers; using System.Diagnostics; +using System.Reflection; using System.Runtime.InteropServices; using BenchmarkDotNet.Attributes; using TALib; @@ -7,6 +8,9 @@ namespace TaTooIne.Benchmark; +/// +/// Greatly inspired by https://github.com/TulipCharts/tulipindicators/blob/master/benchmark.c +/// public class IndicatorsBenchmark { private IMemoryOwner _inputsMemoryPool = null!; @@ -19,7 +23,10 @@ public class IndicatorsBenchmark private Dictionary _talibOptions = null!; private Dictionary _talibOutputs = null!; - [Params(21000)] public int InputSize; + private int _tulipPeriodOption = 3; + private int _talibPeriodOption = 3; + + [Params(210, 2100, 21000)] public int InputSize; [GlobalSetup(Target = "Tulip")] public void GlobalTulipSetup() @@ -28,6 +35,66 @@ public void GlobalTulipSetup() SetupTulipInputs(); } + [IterationSetup(Target = "Tulip")] + public void TulipIterationSetup() + { + _tulipPeriodOption++; + + var currentIndicator = + (Indicator) GetType().GetField("__argField0", BindingFlags.NonPublic | BindingFlags.Instance)! + .GetValue(this)!; + var currentIndicatorIndex = + (int) GetType().GetField("__argField1", BindingFlags.NonPublic | BindingFlags.Instance)!.GetValue(this)!; + var currentIndicatorOptions = _tulipOptions[currentIndicatorIndex]; + if (currentIndicatorOptions.Length == 0) + { + return; + } + + currentIndicatorOptions[0] = _talibPeriodOption; + switch (currentIndicator.Name.ToLower()) + { + case "adosc": + case "apo": + case "kvo": + case "ppo": + case "vosc": + case "macd": + case "vidya": + currentIndicatorOptions[1] = _tulipPeriodOption + 10; + switch (currentIndicator.Name.ToLower()) + { + case "macd": + currentIndicatorOptions[2] = _tulipPeriodOption + 1; + break; + case "vidya": + currentIndicatorOptions[2] = 0.2; + break; + } + + break; + case "bbands": + currentIndicatorOptions[1] = 1; + break; + case "mama": + currentIndicatorOptions[0] = 0.5; + currentIndicatorOptions[1] = 0.05; + break; + case "psar": + currentIndicatorOptions[0] = 1.0 / _tulipPeriodOption; + currentIndicatorOptions[1] = currentIndicatorOptions[0] * 10; + break; + case "stoch": + currentIndicatorOptions[1] = 3; + currentIndicatorOptions[2] = 4; + break; + case "ultosc": + currentIndicatorOptions[1] = _tulipPeriodOption * 2; + currentIndicatorOptions[2] = _tulipPeriodOption * 4; + break; + } + } + [Benchmark] [ArgumentsSource(nameof(TulipSource))] public void Tulip(Indicator Indicator, int order) @@ -40,23 +107,95 @@ public void Tulip(Indicator Indicator, int order) } public static IEnumerable TulipSource() => - Indicators.All - //.Where(indicator => !indicator.Name.Equals("msw", StringComparison.OrdinalIgnoreCase)) - .Select((indicator, index) => new object[] { indicator, index }); + Indicators.All.Select((indicator, index) => new object[] { indicator, index }); [GlobalCleanup(Target = "Tulip")] - public void GlobalTulipCleanup() - { - _inputsMemoryPool.Dispose(); - } + public void GlobalTulipCleanup() => _inputsMemoryPool.Dispose(); [GlobalSetup(Target = "TALib")] public void GlobalTALibSetup() { GenerateInputs(); + SetupTALib(); SetupTALibInputs(); } + [IterationSetup(Target = "TALib")] + public void TALibIterationSetup() + { + _talibPeriodOption++; + + var currentFunction = + (Function) GetType().GetField("__argField0", BindingFlags.NonPublic | BindingFlags.Instance)! + .GetValue(this)!; + var currentFunctionIndex = + (int) GetType().GetField("__argField1", BindingFlags.NonPublic | BindingFlags.Instance)!.GetValue(this)!; + var currentFunctionOptions = _talibOptions[currentFunctionIndex]; + if (currentFunctionOptions.Length == 0) + { + return; + } + + currentFunctionOptions[0] = _talibPeriodOption; + switch (currentFunction.Name.ToLower()) + { + case "adosc": + case "kvo": + case "apo": + case "ppo": + case "macd": + case "mavp": + currentFunctionOptions[1] = _talibPeriodOption + 10; + switch (currentFunction.Name.ToLower()) + { + case "apo": + case "ppo": + currentFunctionOptions[2] = (int) Core.MAType.Ema; + break; + case "macd": + currentFunctionOptions[2] = _talibPeriodOption + 1; + break; + } + + break; + case "bbands": + currentFunctionOptions[1] = 1.0; + currentFunctionOptions[2] = 1.0; + break; + case "macdext": + currentFunctionOptions[2] = _talibPeriodOption + 10; + currentFunctionOptions[4] = _talibPeriodOption + 1; + break; + case "macdfix": + currentFunctionOptions[0] = _talibPeriodOption + 1; + break; + case "mama": + currentFunctionOptions[0] = 0.5; + currentFunctionOptions[1] = 0.05; + break; + case "sar": + currentFunctionOptions[0] = 1.0 / _talibPeriodOption; + currentFunctionOptions[1] = currentFunctionOptions[0] * 10; + break; + case "stoch": + currentFunctionOptions[1] = 3; + currentFunctionOptions[3] = 4; + break; + case "stochf": + currentFunctionOptions[1] = 3; + break; + case "stochrsi": + currentFunctionOptions[1] = _talibPeriodOption; + currentFunctionOptions[2] = _talibPeriodOption; + currentFunctionOptions[3] = 1; + break; + case "ultosc": + currentFunctionOptions[1] = _talibPeriodOption * 2; + currentFunctionOptions[2] = _talibPeriodOption * 4; + break; + } + } + [Benchmark] [ArgumentsSource(nameof(TALibSource))] public void TALib(Function Indicator, int order) @@ -72,10 +211,7 @@ public static IEnumerable TALibSource() => Functions.All.Select((function, index) => new object[] { function, index }); [GlobalCleanup(Target = "TALib")] - public void GlobalTALibCleanup() - { - _inputsMemoryPool.Dispose(); - } + public void GlobalTALibCleanup() => _inputsMemoryPool.Dispose(); private void GenerateInputs() { @@ -83,7 +219,7 @@ private void GenerateInputs() _inputsMemoryPool = MemoryPool.Shared.Rent(InputSize * 5); - var inputsMemorySpan = _inputsMemoryPool.Memory.Span; + var inputsMemory = _inputsMemoryPool.Memory.Span; var openDataOffset = InputSize * 0; var highDataOffset = InputSize * 1; @@ -91,8 +227,8 @@ private void GenerateInputs() var closeDataOffset = InputSize * 3; var volumeDataOffset = InputSize * 4; - inputsMemorySpan[openDataOffset] = 100; - + inputsMemory[openDataOffset] = 100; + for (var i = 0; i < InputSize; ++i) { var diff1 = (random.NextDouble() - 0.5 + 0.01) * 2.5; @@ -103,39 +239,39 @@ private void GenerateInputs() if (i > 0) { - inputsMemorySpan[openDataOffset + i] = inputsMemorySpan[openDataOffset + i - 1] + diff1; + inputsMemory[openDataOffset + i] = inputsMemory[openDataOffset + i - 1] + diff1; } - inputsMemorySpan[closeDataOffset + i] = inputsMemorySpan[openDataOffset + i] + diff2; + inputsMemory[closeDataOffset + i] = inputsMemory[openDataOffset + i] + diff2; - inputsMemorySpan[highDataOffset + i] = - inputsMemorySpan[openDataOffset + i] > inputsMemorySpan[closeDataOffset + i] - ? inputsMemorySpan[openDataOffset + i] + diff3 - : inputsMemorySpan[closeDataOffset + i] + diff3; + inputsMemory[highDataOffset + i] = + inputsMemory[openDataOffset + i] > inputsMemory[closeDataOffset + i] + ? inputsMemory[openDataOffset + i] + diff3 + : inputsMemory[closeDataOffset + i] + diff3; - inputsMemorySpan[lowDataOffset + i] = - inputsMemorySpan[openDataOffset + i] < inputsMemorySpan[closeDataOffset + i] - ? inputsMemorySpan[openDataOffset + i] - diff4 - : inputsMemorySpan[closeDataOffset + i] - diff4; + inputsMemory[lowDataOffset + i] = + inputsMemory[openDataOffset + i] < inputsMemory[closeDataOffset + i] + ? inputsMemory[openDataOffset + i] - diff4 + : inputsMemory[closeDataOffset + i] - diff4; - inputsMemorySpan[volumeDataOffset + i] = vol; + inputsMemory[volumeDataOffset + i] = vol; - Trace.Assert(inputsMemorySpan[openDataOffset + i] <= inputsMemorySpan[highDataOffset + i]); - Trace.Assert(inputsMemorySpan[closeDataOffset + i] <= inputsMemorySpan[highDataOffset + i]); + Trace.Assert(inputsMemory[openDataOffset + i] <= inputsMemory[highDataOffset + i]); + Trace.Assert(inputsMemory[closeDataOffset + i] <= inputsMemory[highDataOffset + i]); - Trace.Assert(inputsMemorySpan[openDataOffset + i] >= inputsMemorySpan[lowDataOffset + i]); - Trace.Assert(inputsMemorySpan[closeDataOffset + i] >= inputsMemorySpan[lowDataOffset + i]); + Trace.Assert(inputsMemory[openDataOffset + i] >= inputsMemory[lowDataOffset + i]); + Trace.Assert(inputsMemory[closeDataOffset + i] >= inputsMemory[lowDataOffset + i]); - Trace.Assert(inputsMemorySpan[highDataOffset + i] >= inputsMemorySpan[lowDataOffset + i]); - Trace.Assert(inputsMemorySpan[highDataOffset + i] >= inputsMemorySpan[openDataOffset + i]); - Trace.Assert(inputsMemorySpan[highDataOffset + i] >= inputsMemorySpan[closeDataOffset + i]); + Trace.Assert(inputsMemory[highDataOffset + i] >= inputsMemory[lowDataOffset + i]); + Trace.Assert(inputsMemory[highDataOffset + i] >= inputsMemory[openDataOffset + i]); + Trace.Assert(inputsMemory[highDataOffset + i] >= inputsMemory[closeDataOffset + i]); - Trace.Assert(inputsMemorySpan[lowDataOffset + i] <= inputsMemorySpan[openDataOffset + i]); - Trace.Assert(inputsMemorySpan[lowDataOffset + i] <= inputsMemorySpan[closeDataOffset + i]); + Trace.Assert(inputsMemory[lowDataOffset + i] <= inputsMemory[openDataOffset + i]); + Trace.Assert(inputsMemory[lowDataOffset + i] <= inputsMemory[closeDataOffset + i]); } - /* This is a hack, since ta obv uses volume[0] as starting value and ti obv uses 0 as starting value. */ - inputsMemorySpan[volumeDataOffset] = 0; + // HACK: obv uses volume[0] as starting value and ti obv uses 0 as starting value. + inputsMemory[volumeDataOffset] = 0; } private void SetupTulipInputs() @@ -150,10 +286,54 @@ private void SetupTulipInputs() { var indicator = Indicators.All.ElementAt(i); - PrepareInputs(indicator, i, _tulipInputs, _tulipOptions, _tulipOutputs); + var (inputs, options, outputs) = PrepareParams(indicator); + _tulipInputs.Add(i, inputs); + _tulipOptions.Add(i, options); + _tulipOutputs.Add(i, outputs); } } + private void SetupTALib() + { + var candleDefaultConfig = new + { + Period = 10, + + BodyNone = 0.05, + BodyShort = 0.5, + BodyLong = 1.4, + + WickNone = 0.05, + WickLong = 0.6, + + Near = 0.3 + }; + + Core.SetCandleSettings( + Core.CandleSettingType.BodyDoji, Core.RangeType.HighLow, candleDefaultConfig.Period, + candleDefaultConfig.BodyNone); + + Core.SetCandleSettings( + Core.CandleSettingType.BodyShort, Core.RangeType.RealBody, candleDefaultConfig.Period, + candleDefaultConfig.BodyShort); + + Core.SetCandleSettings( + Core.CandleSettingType.BodyLong, Core.RangeType.RealBody, candleDefaultConfig.Period, + candleDefaultConfig.BodyLong); + + Core.SetCandleSettings( + Core.CandleSettingType.ShadowVeryShort, Core.RangeType.HighLow, candleDefaultConfig.Period, + candleDefaultConfig.WickNone); + + Core.SetCandleSettings( + Core.CandleSettingType.ShadowLong, Core.RangeType.RealBody, candleDefaultConfig.Period, + candleDefaultConfig.WickLong); + + Core.SetCandleSettings( + Core.CandleSettingType.Near, Core.RangeType.HighLow, candleDefaultConfig.Period, + candleDefaultConfig.Near); + } + private void SetupTALibInputs() { var functionsCount = Functions.All.Count(); @@ -166,16 +346,14 @@ private void SetupTALibInputs() { var function = Functions.All.ElementAt(i); - PrepareInputs(function, i, _talibInputs, _talibOptions, _talibOutputs); + var (inputs, options, outputs) = PrepareParams(function); + _talibInputs.Add(i, inputs); + _talibOptions.Add(i, options); + _talibOutputs.Add(i, outputs); } } - private void PrepareInputs( - dynamic indicator, - int indicatorIndex, - Dictionary inputsDic, - Dictionary optionsDic, - Dictionary outputsDic) + private (double[][] inputs, double[] options, double[][] outputs) PrepareParams(dynamic indicator) { var inputs = new double[indicator.Inputs.Length][]; var outputs = new double[indicator.Outputs.Length][]; @@ -233,6 +411,15 @@ private void PrepareInputs( inputs[j] = volumeDataArray.AsSpan().ToArray(); } + break; + case "periods": + var inputData = new double[InputSize]; + var random = new Random(); + for (var i = 0; i < InputSize; i++) + { + inputData[i] = random.Next(30); + } + inputs[j] = inputData; break; } } @@ -242,8 +429,6 @@ private void PrepareInputs( outputs[j] = new double[InputSize]; } - inputsDic.Add(indicatorIndex, inputs); - optionsDic.Add(indicatorIndex, options); - outputsDic.Add(indicatorIndex, outputs); + return (inputs, options, outputs); } -} \ No newline at end of file +} diff --git a/src/TaTooIne.Benchmark/Program.cs b/src/TaTooIne.Benchmark/Program.cs index 9a3b115..d074661 100644 --- a/src/TaTooIne.Benchmark/Program.cs +++ b/src/TaTooIne.Benchmark/Program.cs @@ -22,14 +22,14 @@ config = ManualConfig.Union(config, ManualConfig.CreateEmpty() .AddLogger(ConsoleLogger.Default) .AddJob(Job.Default.WithRuntime(CoreRuntime.Core80)) - .AddJob(Job.Default.WithRuntime(CoreRuntime.Core70)) - .AddJob(Job.Default.WithRuntime(CoreRuntime.Core60)) - .AddColumnProvider(new[] - { - DefaultColumnProviders.Descriptor, DefaultColumnProviders.Job, DefaultColumnProviders.Statistics, - BenchmarkParamsColumnProvider.Instance, DefaultColumnProviders.Metrics - }) + .AddColumnProvider([ + DefaultColumnProviders.Descriptor, + DefaultColumnProviders.Job, + DefaultColumnProviders.Statistics, + BenchmarkParamsColumnProvider.Instance, + DefaultColumnProviders.Metrics + ]) .AddExporter(HtmlExporter.Default)); #endif -BenchmarkRunner.Run(config); \ No newline at end of file +BenchmarkRunner.Run(config);