diff --git a/.github/workflows/main_automation.yml b/.github/workflows/main_automation.yml index 85a883a4..6d4bb577 100644 --- a/.github/workflows/main_automation.yml +++ b/.github/workflows/main_automation.yml @@ -45,6 +45,27 @@ jobs: #configFilePath: GitVersion.yml updateAssemblyInfo: true +############## Install Python + +# - name: Install Python3 +# uses: actions/setup-python@v2 +# with: +# python-version: 3.x + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y python3-dev + sudo apt-get install -y python3-pip + + - name: Install pandas-ta + run: | + pip3 install numpy + pip3 install pandas + pip3 install pandas-ta + +############## Install more tools + - name: Install JDK11 for Sonar Scanner uses: actions/setup-java@v3 with: @@ -158,3 +179,4 @@ jobs: --api-key ${{ secrets.NUGET_DEPLOY_KEY_QUANTLIB }} --source https://api.nuget.org/v3/index.json --skip-duplicate + diff --git a/Calculations/Calculations.csproj b/Calculations/Calculations.csproj index 466a0ced..2502f02a 100644 --- a/Calculations/Calculations.csproj +++ b/Calculations/Calculations.csproj @@ -2,7 +2,9 @@ QuanTAlib - 0.2.0 + 0.2.5 + 0.2.5 + 0.2.5 Library of TA Calculations, Charts and Strategies for Quantower Quantitative Technical Analysis Library in C# for Quantower git @@ -33,9 +35,6 @@ Apache-2.0 - 0.2.1.0 - 0.2.1.0 - 0.2.1-dev.2+Branch.dev.Sha.cb5fe2dc86a78fe9358da810d17952c82299ed3d NETSDK1057 IDE1006 true diff --git a/Calculations/Logic/CROSS_Series.cs b/Calculations/Logic/CROSS_Series.cs index 66e2e2bc..aab93397 100644 --- a/Calculations/Logic/CROSS_Series.cs +++ b/Calculations/Logic/CROSS_Series.cs @@ -29,9 +29,9 @@ public class CROSS_Series : Pair_TSeries_Indicator { val = TValue1.v == TValue2.v ? 0 : val; double over = TValue1.v > TValue2.v ? 1 : val; - val = (this._previous < over) ? 1 : -1; - (System.DateTime t, double v) result = ((TValue1.t > TValue2.t) ? TValue1.t : TValue2.t, - ((this._previous == over) || Double.IsNaN(this._previous) || (this._previous == 0)) ? 0 : val); + val = (_previous < over) ? 1 : -1; + val = ((_previous == over) || Double.IsNaN(this._previous) || (this._previous == 0)) ? 0 : val; + (System.DateTime t, double v) result = ((TValue1.t > TValue2.t) ? TValue1.t : TValue2.t,val); this._previous = over; diff --git a/Tests/Tests.csproj b/Tests/Tests.csproj index 07aed4a3..8ec60052 100644 --- a/Tests/Tests.csproj +++ b/Tests/Tests.csproj @@ -1,6 +1,6 @@  - net8.0 + net7.0 preview enable enable @@ -18,8 +18,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - - + runtime; build; native; contentfiles; analyzers; buildtransitive @@ -29,7 +28,7 @@ - + diff --git a/Tests/Validations/Trends/Pandas_TA.cs b/Tests/Validations/Trends/Pandas_TA.cs index 6c05cbc8..20543765 100644 --- a/Tests/Validations/Trends/Pandas_TA.cs +++ b/Tests/Validations/Trends/Pandas_TA.cs @@ -1,41 +1,37 @@ using Xunit; using System; using QuanTAlib; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; using Python.Runtime; -using Python.Included; namespace Validations; -/* + public class PandasTA : IDisposable { + private bool disposed = false; private readonly GBM_Feed bars; - private readonly Random rnd = new(); + private readonly Random rnd = new(); private readonly int period, skip; - private int digits; - private readonly string dllpath; + private readonly int digits; private readonly dynamic np; private readonly dynamic ta; private readonly dynamic pd; private readonly dynamic df; + public PandasTA() { bars = new(Bars: 5000, Volatility: 0.8, Drift: 0.0); period = rnd.Next(maxValue: 28) + 3; skip = period+10; digits = 8; - Installer.InstallPath = Path.GetFullPath(path: "."); - Installer.SetupPython().Wait(); - Installer.TryInstallPip(); - Installer.PipInstallModule(module_name: "numpy"); - Installer.PipInstallModule(module_name: "pandas"); - Installer.PipInstallModule(module_name: "pandas-ta"); - dllpath = Installer.InstallPath + "\\" + Installer.InstallDirectory + "\\" + Runtime.PythonDLL; - Runtime.PythonDLL = dllpath; + string pythonDLL = PythonLibrary.Locate(); + Runtime.PythonDLL = pythonDLL; PythonEngine.Initialize(); - np = Py.Import(name: "numpy"); + np = Py.Import(name: "numpy"); pd = Py.Import(name: "pandas"); ta = Py.Import(name: "pandas_ta"); @@ -52,11 +48,21 @@ public class PandasTA : IDisposable } public void Dispose() { - PythonEngine.Shutdown(); + Dispose(true); + PythonEngine.Shutdown(); GC.SuppressFinalize(this); } + ~PandasTA() { + Dispose(false); + } + protected virtual void Dispose(bool disposing) { + if (!disposed) { + disposed = true; + } + } - [Fact] void ADL() { + [Fact] + void ADL() { ADL_Series QL = new(bars); var pta = df.ta.ad(high: df.high, low: df.low, close:df.close, volume:df.volume); for (int i = QL.Length-1; i > skip; i--) @@ -67,7 +73,7 @@ public class PandasTA : IDisposable } } - /* + /* [Fact] void ADOSC() { ADOSC_Series QL = new(bars); var pta = df.ta.adosc(high: df.high, low: df.low, close: df.close, volume: df.volume); @@ -124,7 +130,7 @@ public class PandasTA : IDisposable Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); } } - + [Fact] void CMO() { CMO_Series QL = new(bars.Close, period, false); @@ -135,7 +141,7 @@ public class PandasTA : IDisposable Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); } } - + [Fact] void DEMA() { DEMA_Series QL = new(bars.Close, period, false); var pta = df.ta.dema(close: df.close, length: period); @@ -146,7 +152,7 @@ public class PandasTA : IDisposable Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); } } - + [Fact] void EMA() { EMA_Series QL = new(bars.Close, period, false); var pta = df.ta.ema(close: df.close, length: period); @@ -157,7 +163,7 @@ public class PandasTA : IDisposable Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); } } - + [Fact] void ENTROPY() { ENTROPY_Series QL = new(bars.Close, period, useNaN: false); var pta = df.ta.entropy(close: df.close, length: period); @@ -197,6 +203,7 @@ public class PandasTA : IDisposable } } + [Fact] void HWMA() { HWMA_Series QL = new(bars.Close, useNaN: false); var pta = df.ta.hwma(close: df.close); @@ -206,8 +213,8 @@ public class PandasTA : IDisposable double PanTA_item = (double)pta[i - 1]; Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); } - } + [Fact] void KAMA() { KAMA_Series QL = new(bars.Close, period); var pta = df.ta.kama(close: df.close, length: period); @@ -311,7 +318,7 @@ public class PandasTA : IDisposable Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); } } - + [Fact] void SMA() { SMA_Series QL = new(bars.Close, period, false); var pta = df.ta.sma(close: df.close, length: period); @@ -322,7 +329,7 @@ public class PandasTA : IDisposable Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); } } - + [Fact] void SSDEV() { SSDEV_Series QL = new(bars.Close, period, useNaN: false); var pta = df.ta.stdev(close: df.close, length: period, ddof: 1); @@ -434,6 +441,63 @@ public class PandasTA : IDisposable Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); } } - + */ + } -*/ \ No newline at end of file + +public static class PythonLibrary { + public static string Locate() { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { + string[] paths = Environment.GetEnvironmentVariable("PATH")?.Split(';') ?? Array.Empty(); + foreach (string path in paths) { + string[] pythonDLLs = Directory.GetFiles(path, "python3*.dll"); + if (pythonDLLs.Length > 0) { + foreach (string item in pythonDLLs) { + if (!item.EndsWith("python3.dll", StringComparison.OrdinalIgnoreCase)) { + return item; + } + } + + } + } + throw new FileNotFoundException("Python library not found in PATH"); + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { + List pythonLibraries = new List(); + List directoriesToSearch = new List { "/opt/hostedtoolcache/Python" }; // Add more directories as needed + string filePattern = "libpython3.*.so"; + SearchFiles(directoriesToSearch, filePattern, pythonLibraries); + + if (pythonLibraries.Count > 0) { + return pythonLibraries[0]; + } + else { + throw new FileNotFoundException("Python library not found"); + } + } + + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { + throw new NotSupportedException("Not supported yet"); + } + + else { throw new NotSupportedException("Unsupported operating system"); } + } + static void SearchFiles(List directoriesToSearch, string filePattern, List foundFiles) + { + foreach (string directory in directoriesToSearch) + { + if (Directory.Exists(directory)) + { + try + { + string[] files = Directory.GetFiles(directory, filePattern, SearchOption.AllDirectories); + foundFiles.AddRange(files); + } + catch (Exception e) + { + Console.WriteLine("Error searching in directory: " + directory + " - " + e.Message); + } + } + } + } +} \ No newline at end of file diff --git a/Tests/Validations/Trends/Skender.cs b/Tests/Validations/Trends/Skender.cs index 8f985767..fb97799d 100644 --- a/Tests/Validations/Trends/Skender.cs +++ b/Tests/Validations/Trends/Skender.cs @@ -30,6 +30,7 @@ public class Skender }); } +/* [Fact] public void ADL() { @@ -42,6 +43,7 @@ public class Skender Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits)); } } + */ [Fact] public void ALMA() {