Merge branch 'dev'

This commit is contained in:
Miha Kralj
2024-10-08 10:47:21 -07:00
56 changed files with 986 additions and 568 deletions
+41 -38
View File
@@ -26,7 +26,7 @@ jobs:
- name: Setup .NET SDK
uses: actions/setup-dotnet@v3
with:
dotnet-version: '8.0.x'
dotnet-version: '8.x'
- name: Install JDK11 for Sonar Scanner
uses: actions/setup-java@v3
@@ -79,7 +79,7 @@ jobs:
- name: Setup .NET SDK
uses: actions/setup-dotnet@v3
with:
dotnet-version: '8.0.x'
dotnet-version: '8.x'
- name: Install dotnet tools
run: |
@@ -134,7 +134,7 @@ jobs:
- name: Setup .NET SDK
uses: actions/setup-dotnet@v3
with:
dotnet-version: '8.0.x'
dotnet-version: '8.x'
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
@@ -150,39 +150,42 @@ jobs:
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
SecurityCodeScan:
runs-on: windows-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup NuGet
uses: nuget/setup-nuget@v1
- name: Setup MSBuild
uses: microsoft/setup-msbuild@v1
- name: Setup .NET SDK
uses: actions/setup-dotnet@v3
with:
dotnet-version: '3.1.x'
- name: Set up projects for analysis
uses: security-code-scan/security-code-scan-add-action@v1
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet build --no-restore --configuration Debug
- name: Convert SARIF for uploading to GitHub
uses: security-code-scan/security-code-scan-results-action@v1
- name: Upload SARIF
uses: github/codeql-action/upload-sarif@v3
# converting SARIF action not working yet.
#SecurityCodeScan:
# runs-on: windows-latest
# steps:
# - name: Checkout repository
# uses: actions/checkout@v4
# with:
# fetch-depth: 0
#
# - name: Setup NuGet
# uses: nuget/setup-nuget@v1
#
# - name: Setup MSBuild
# uses: microsoft/setup-msbuild@v1
#
# - name: Setup .NET SDK
# uses: actions/setup-dotnet@v3
# with:
# dotnet-version: |
# 8.x
# 3.1.x
# dotnet-quality: 'preview'
#
# - name: Set up projects for analysis
# uses: security-code-scan/security-code-scan-add-action@v1
#
# - name: Build
# run: |
# dotnet restore
# dotnet build --no-restore --configuration Debug
#
# - name: Convert SARIF for uploading to GitHub
# uses: security-code-scan/security-code-scan-results-action@v1
#
# - name: Upload SARIF
# uses: github/codeql-action/upload-sarif@v3
Codacy_Scan:
runs-on: ubuntu-latest
@@ -212,7 +215,7 @@ jobs:
sarif_file: results.sarif
build_publish:
needs: [SonarCloud, Code_Coverage, CodeQL, SecurityCodeScan, Codacy_Scan]
needs: [SonarCloud, Code_Coverage, CodeQL, Codacy_Scan]
if: success()
runs-on: ubuntu-latest
steps:
@@ -224,7 +227,7 @@ jobs:
- name: Setup .NET SDK
uses: actions/setup-dotnet@v3
with:
dotnet-version: '8.0.x'
dotnet-version: '8.x'
- name: Install GitVersion
uses: gittools/actions/gitversion/setup@v0
+4 -1
View File
@@ -10,5 +10,8 @@
"sonarlint.connectedMode.project": {
"connectionId": "mihakralj",
"projectKey": "mihakralj_QuanTAlib"
}
},
"dotnet.dotnetPath": "C:/Program Files/dotnet",
"omnisharp.useModernNet": true,
"omnisharp.sdkPath": "C:/Program Files/dotnet/sdk"
}
+3 -14
View File
@@ -1,11 +1,12 @@
<Project>
<PropertyGroup>
<NeutralLanguage>en-US</NeutralLanguage>
<TargetFramework>net8.0</TargetFramework>
<LangVersion>preview</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Deterministic>true</Deterministic>
<LangVersion>preview</LangVersion>
<NeutralLanguage>en-US</NeutralLanguage>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<DisableImplicitNamespaceImports>true</DisableImplicitNamespaceImports>
@@ -20,12 +21,6 @@
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<PlatformTarget>AnyCPU</PlatformTarget>
<IsLocalBuild Condition="'$(GITHUB_ACTIONS)' == ''">true</IsLocalBuild>
<!-- GitVersion Properties -->
<Version>$(GitVersion_NuGetVersion)</Version>
<AssemblyVersion>$(GitVersion_AssemblySemVer)</AssemblyVersion>
<FileVersion>$(GitVersion_AssemblySemFileVer)</FileVersion>
<InformationalVersion>$(GitVersion_InformationalVersion)</InformationalVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
@@ -50,10 +45,6 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All"/>
<PackageReference Include="GitVersion.MsBuild" Version="6.0.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.DotNet.Interactive.Formatting" Version="1.0.0-beta.21459.1" />
</ItemGroup>
@@ -63,5 +54,3 @@
</PropertyGroup>
</Project>
+38 -30
View File
@@ -1,18 +1,21 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "quantalib", "lib\quantalib.csproj", "{A1B2C3D4-E5F6-47G8-H9I0-J1K2L3M4N5O6}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "quantalib", "lib\quantalib.csproj", "{1E050FA4-630E-4801-9DE9-D2536DACA9B0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{B2C3D4E5-F6G7-48H9-I0J1-K2L3M4N5O6P7}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "quantower", "quantower", "{1B9AC248-76F8-44DD-958D-F1DC08EE1E87}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SyntheticVendor", "SyntheticVendor\SyntheticVendor.csproj", "{C3D4E5F6-G7H8-49I0-J1K2-L3M4N5O6P7Q8}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Statistics", "quantower\Statistics\Statistics.csproj", "{2E9427C7-144F-488E-A29D-789ACC1C32AE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Averages", "quantower\Averages\Averages.csproj", "{D4E5F6G7-H8I9-50J1-K2L3-M4N5O6P7Q8R9}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Averages", "quantower\Averages\Averages.csproj", "{6BE10C39-4127-446C-818B-7976FCDD51D5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Statistics", "quantower\Statistics\Statistics.csproj", "{E5F6G7H8-I9J0-51K2-L3M4-N5O6P7Q8R9S0}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volatility", "quantower\Volatility\Volatility.csproj", "{B7DC44F7-D3A3-4C70-9025-513E0182B646}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volatility", "quantower\Volatility\Volatility.csproj", "{F6G7H8I9-J0K1-52L3-M4N5-O6P7Q8R9S0T1}"
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", "{E204F173-5840-4AA3-BED1-98C8D2F813E3}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -23,29 +26,34 @@ Global
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{A1B2C3D4-E5F6-47G8-H9I0-J1K2L3M4N5O6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A1B2C3D4-E5F6-47G8-H9I0-J1K2L3M4N5O6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A1B2C3D4-E5F6-47G8-H9I0-J1K2L3M4N5O6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A1B2C3D4-E5F6-47G8-H9I0-J1K2L3M4N5O6}.Release|Any CPU.Build.0 = Release|Any CPU
{B2C3D4E5-F6G7-48H9-I0J1-K2L3M4N5O6P7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B2C3D4E5-F6G7-48H9-I0J1-K2L3M4N5O6P7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B2C3D4E5-F6G7-48H9-I0J1-K2L3M4N5O6P7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B2C3D4E5-F6G7-48H9-I0J1-K2L3M4N5O6P7}.Release|Any CPU.Build.0 = Release|Any CPU
{C3D4E5F6-G7H8-49I0-J1K2-L3M4N5O6P7Q8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C3D4E5F6-G7H8-49I0-J1K2-L3M4N5O6P7Q8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C3D4E5F6-G7H8-49I0-J1K2-L3M4N5O6P7Q8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C3D4E5F6-G7H8-49I0-J1K2-L3M4N5O6P7Q8}.Release|Any CPU.Build.0 = Release|Any CPU
{D4E5F6G7-H8I9-50J1-K2L3-M4N5O6P7Q8R9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D4E5F6G7-H8I9-50J1-K2L3-M4N5O6P7Q8R9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D4E5F6G7-H8I9-50J1-K2L3-M4N5O6P7Q8R9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D4E5F6G7-H8I9-50J1-K2L3-M4N5O6P7Q8R9}.Release|Any CPU.Build.0 = Release|Any CPU
{E5F6G7H8-I9J0-51K2-L3M4-N5O6P7Q8R9S0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E5F6G7H8-I9J0-51K2-L3M4-N5O6P7Q8R9S0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E5F6G7H8-I9J0-51K2-L3M4-N5O6P7Q8R9S0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E5F6G7H8-I9J0-51K2-L3M4-N5O6P7Q8R9S0}.Release|Any CPU.Build.0 = Release|Any CPU
{F6G7H8I9-J0K1-52L3-M4N5-O6P7Q8R9S0T1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F6G7H8I9-J0K1-52L3-M4N5-O6P7Q8R9S0T1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F6G7H8I9-J0K1-52L3-M4N5-O6P7Q8R9S0T1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F6G7H8I9-J0K1-52L3-M4N5-O6P7Q8R9S0T1}.Release|Any CPU.Build.0 = Release|Any CPU
{1E050FA4-630E-4801-9DE9-D2536DACA9B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1E050FA4-630E-4801-9DE9-D2536DACA9B0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1E050FA4-630E-4801-9DE9-D2536DACA9B0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1E050FA4-630E-4801-9DE9-D2536DACA9B0}.Release|Any CPU.Build.0 = Release|Any CPU
{2E9427C7-144F-488E-A29D-789ACC1C32AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2E9427C7-144F-488E-A29D-789ACC1C32AE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2E9427C7-144F-488E-A29D-789ACC1C32AE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2E9427C7-144F-488E-A29D-789ACC1C32AE}.Release|Any CPU.Build.0 = Release|Any CPU
{6BE10C39-4127-446C-818B-7976FCDD51D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6BE10C39-4127-446C-818B-7976FCDD51D5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6BE10C39-4127-446C-818B-7976FCDD51D5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6BE10C39-4127-446C-818B-7976FCDD51D5}.Release|Any CPU.Build.0 = Release|Any CPU
{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.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
{E204F173-5840-4AA3-BED1-98C8D2F813E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E204F173-5840-4AA3-BED1-98C8D2F813E3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E204F173-5840-4AA3-BED1-98C8D2F813E3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E204F173-5840-4AA3-BED1-98C8D2F813E3}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{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}
EndGlobalSection
EndGlobal
+10 -5
View File
@@ -1,22 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace>QuanTAlib.Tests</RootNamespace>
<AssemblyName>QuanTAlib.Tests</AssemblyName>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<PackageReference Include="xunit" Version="2.9.2" />
<PackageReference Include="coverlet.collector" Version="6.0.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.0.0-pre.35">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="xunit.runner.console" Version="2.9.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
<PackageReference Include="System.Text.RegularExpressions" Version="4.3.1" />
<PackageReference Include="System.Net.Http" Version="4.3.4" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" />
<PackageReference Include="Skender.Stock.Indicators" Version="2.5.0" />
<PackageReference Include="TALib.NETCore" Version="0.4.4" />
<PackageReference Include="Tulip.NETCore" Version="0.8.0.1" />
@@ -29,6 +33,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="xunit" Version="2.4.1" />
<ProjectReference Include="..\lib\quantalib.csproj" />
</ItemGroup>
+74
View File
@@ -0,0 +1,74 @@
using Xunit;
namespace QuanTAlib;
public class EventingTests
{
[Fact]
public void VerifyEventBasedCalculations()
{
// Create a random number generator with a fixed seed for reproducibility
var random = new Random(42);
// Create an input series to hold our random values
var input = new TSeries();
int p = 10;
// 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))
};
// Generate 200 random values and feed them to both direct and event-based indicators
for (int i = 0; i< 200; i++)
{
double randomValue = random.NextDouble() * 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);
}
}
}
+95 -11
View File
@@ -4,55 +4,139 @@ using System.Diagnostics.CodeAnalysis;
namespace QuanTAlib;
/// <summary>
/// Contains unit tests for bar-based indicators in QuanTAlib.
/// </summary>
[SuppressMessage("Security", "SCS0005:Weak random number generator.", Justification = "Acceptable for tests")]
public class BarIndicatorTests
{
private readonly Random rnd;
private const int SeriesLen = 1000;
private const int Corrections = 100;
/// <summary>
/// Initializes a new instance of the BarIndicatorTests class.
/// </summary>
public BarIndicatorTests()
{
rnd = new Random((int)DateTime.Now.Ticks);
}
private static readonly iTValue[] indicators = new iTValue[]
private static readonly ITValue[] indicators = new ITValue[]
{
new Atr(period: 14),
new Atr(period: 14),
// Add other TBar-based indicators here
};
/// <summary>
/// Tests if the indicator produces consistent results when processing new and updated bars.
/// </summary>
/// <param name="indicator">The indicator to test.</param>
[Theory]
[MemberData(nameof(GetIndicators))]
public void IndicatorIsNew(iTValue indicator)
public void IndicatorIsNew(ITValue indicator)
{
var indicator1 = indicator;
var indicator2 = indicator;
MethodInfo calcMethod = indicator.GetType().GetMethod("Calc")!;
MethodInfo calcMethod = FindCalcMethod(indicator.GetType());
if (calcMethod == null)
{
throw new Exception($"Calc method not found for indicator type: {indicator.GetType().Name}");
throw new InvalidOperationException($"Calc method not found for indicator type: {indicator.GetType().Name}");
}
for (int i = 0; i < SeriesLen; i++)
{
TBar item1 = new(Time: DateTime.Now, Open: rnd.Next(-100, 100), High: rnd.Next(-100, 100), Low: rnd.Next(-100, 100), Close: rnd.Next(-100, 100), Volume: rnd.Next(-1000, 1000), IsNew: true);
calcMethod.Invoke(indicator1, new object[] { item1 });
TBar item1 = GenerateRandomBar(isNew: true);
InvokeCalc(indicator1, calcMethod, item1);
for (int j = 0; j < Corrections; j++)
{
item1 = new(Time: DateTime.Now, Open: rnd.Next(-100, 100), High: rnd.Next(-100, 100), Low: rnd.Next(-100, 100), Close: rnd.Next(-100, 100), Volume: rnd.Next(-1000, 1000), IsNew: false);
calcMethod.Invoke(indicator1, new object[] { item1 });
item1 = GenerateRandomBar(isNew: false);
InvokeCalc(indicator1, calcMethod, item1);
}
var item2 = new TBar(item1.Time, item1.Open, item1.High, item1.Low, item1.Close, item1.Volume, IsNew: true);
calcMethod.Invoke(indicator2, new object[] { item2 });
InvokeCalc(indicator2, calcMethod, item2);
Assert.Equal(indicator1.Value, indicator2.Value);
}
}
/// <summary>
/// Finds the appropriate Calc method for the given indicator type.
/// </summary>
/// <param name="type">The type of the indicator.</param>
/// <returns>The MethodInfo for the Calc method.</returns>
private static MethodInfo FindCalcMethod(Type type)
{
while (type != null && type != typeof(object))
{
var methods = type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)
.Where(m => m.Name == "Calc")
.ToList();
if (methods.Count > 0)
{
// Prefer the method with TBar parameter
var method = methods.FirstOrDefault(m =>
{
var parameters = m.GetParameters();
return parameters.Length == 1 && parameters[0].ParameterType == typeof(TBar);
});
// If not found, return the first method
return method ?? methods.First();
}
type = type.BaseType!;
}
return null!;
}
/// <summary>
/// Invokes the Calc method on the given indicator with the provided input.
/// </summary>
/// <param name="indicator">The indicator instance.</param>
/// <param name="calcMethod">The Calc method to invoke.</param>
/// <param name="input">The input TBar.</param>
private static void InvokeCalc(ITValue indicator, MethodInfo calcMethod, TBar input)
{
var parameters = calcMethod.GetParameters();
if (parameters.Length == 1)
{
calcMethod.Invoke(indicator, new object[] { input });
}
else if (parameters.Length == 2)
{
calcMethod.Invoke(indicator, new object[] { input, double.NaN });
}
else
{
throw new InvalidOperationException($"Invalid number of parameters for Calc method in indicator type: {indicator.GetType().Name}");
}
}
/// <summary>
/// Generates a random TBar for testing purposes.
/// </summary>
/// <param name="isNew">Indicates whether the generated bar should be marked as new.</param>
/// <returns>A randomly generated TBar.</returns>
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);
return new TBar(Time: DateTime.Now, Open: open, High: high, Low: low, Close: close, Volume: volume, IsNew: isNew);
}
/// <summary>
/// Provides the list of indicators for parameterized tests.
/// </summary>
/// <returns>An enumerable of object arrays, each containing an indicator instance.</returns>
public static IEnumerable<object[]> GetIndicators()
{
return indicators.Select(indicator => new object[] { indicator });
+98 -49
View File
@@ -5,7 +5,6 @@ using System.Diagnostics.CodeAnalysis;
namespace QuanTAlib;
[SuppressMessage("Security", "SCS0005:Weak random number generator.", Justification = "Acceptable for tests")]
public class IndicatorTests
{
private readonly Random rnd;
@@ -17,82 +16,132 @@ public class IndicatorTests
rnd = new Random((int)DateTime.Now.Ticks);
}
private static readonly iTValue[] indicators =
[
// skipcq: CS-R1055
private static readonly ITValue[] indicators =
{
new Ema(period: 10, useSma: true),
new Alma(period: 14, offset: 0.85, sigma: 6),
new Afirma(periods: 4, taps: 4, window: Afirma.WindowType.Blackman),
new Convolution(new double[] { 1.0, 2, 3, 2, 1 }),
new Dema(period: 14),
new Dsma(period: 14),
new Dwma(period: 14),
new Epma(period: 14),
new Frama(period: 14),
new Fwma(period: 14),
new Gma(period: 14),
new Hma(period: 14),
new Hwma(period: 14),
new Kama(period: 14),
new Mama(fastLimit: 0.5, slowLimit: 0.05),
new Mgdi(period: 14),
new Mma(period: 14),
new Qema(),
new Rema(period: 14),
new Rma(period: 14),
new Sinema(period: 14),
new Sma(period: 14),
new Smma(period: 14),
new T3(period: 14),
new Tema(period: 14),
new Trima(period: 14),
new Vidya(shortPeriod: 14, longPeriod: 30, alpha: 0.2),
new Wma(period: 14),
new Zlema(period: 14),
new Entropy(period: 14),
new Kurtosis(period: 14),
new Max(period: 14, decay: 0.01),
new Min(period: 14, decay: 0.01),
new Median(period: 14),
new Mode(period: 14),
new Percentile(period: 14, percent: 50),
new Skew(period: 14),
new Stddev(period: 14),
new Variance(period: 14),
new Zscore(period: 14)
new Alma(period: 14, offset: 0.85, sigma: 6),
new Afirma(periods: 4, taps: 4, window: Afirma.WindowType.Blackman),
new Convolution(new[] { 1.0, 2, 3, 2, 1 }),
new Dema(period: 14),
new Dsma(period: 14),
new Dwma(period: 14),
new Epma(period: 14),
new Frama(period: 14),
new Fwma(period: 14),
new Gma(period: 14),
new Hma(period: 14),
new Hwma(period: 14),
new Kama(period: 14),
new Mama(fastLimit: 0.5, slowLimit: 0.05),
new Mgdi(period: 14),
new Mma(period: 14),
new Qema(),
new Rema(period: 14),
new Rma(period: 14),
new Sinema(period: 14),
new Sma(period: 14),
new Smma(period: 14),
new T3(period: 14),
new Tema(period: 14),
new Trima(period: 14),
new Vidya(shortPeriod: 14, longPeriod: 30, alpha: 0.2),
new Wma(period: 14),
new Zlema(period: 14),
];
new Curvature(period: 14),
new Entropy(period: 14),
new Kurtosis(period: 14),
new Max(period: 14, decay: 0.01),
new Median(period: 14),
new Min(period: 14, decay: 0.01),
new Median(period: 14),
new Mode(period: 14),
new Percentile(period: 14, percent: 50),
new Skew(period: 14),
new Slope(period: 14),
new Stddev(period: 14),
new Variance(period: 14),
new Zscore(period: 14),
new Historical(period: 14),
new Realized(period: 14)
};
[Theory]
[MemberData(nameof(GetIndicators))]
public void IndicatorIsNew(iTValue indicator)
public void IndicatorIsNew(ITValue indicator)
{
var indicator1 = indicator;
var indicator2 = indicator;
MethodInfo calcMethod = indicator.GetType().GetMethod("Calc")!;
MethodInfo calcMethod = FindCalcMethod(indicator.GetType());
if (calcMethod == null)
{
throw new Exception($"Calc method not found for indicator type: {indicator.GetType().Name}");
throw new InvalidOperationException($"Calc method not found for indicator type: {indicator.GetType().Name}");
}
for (int i = 0; i < SeriesLen; i++)
{
TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true);
calcMethod.Invoke(indicator1, new object[] { item1 });
InvokeCalc(indicator1, calcMethod, item1);
for (int j = 0; j < Corrections; j++)
{
item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false);
calcMethod.Invoke(indicator1, new object[] { item1 });
InvokeCalc(indicator1, calcMethod, item1);
}
var item2 = new TValue(item1.Time, item1.Value, IsNew: true);
calcMethod.Invoke(indicator2, new object[] { item2 });
InvokeCalc(indicator2, calcMethod, item2);
Assert.Equal(indicator1.Value, indicator2.Value);
}
}
private static MethodInfo FindCalcMethod(Type type)
{
while (type != null && type != typeof(object))
{
var methods = type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)
.Where(m => m.Name == "Calc")
.ToList();
if (methods.Count > 0)
{
// Prefer the method with TValue parameter
var method = methods.FirstOrDefault(m =>
{
var parameters = m.GetParameters();
return parameters.Length == 1 && parameters[0].ParameterType == typeof(TValue);
});
// If not found, return the first method
return method ?? methods.First();
}
type = type.BaseType!;
}
return null!;
}
private static void InvokeCalc(ITValue indicator, MethodInfo calcMethod, TValue input)
{
var parameters = calcMethod.GetParameters();
if (parameters.Length == 1)
{
calcMethod.Invoke(indicator, new object[] { input });
}
else if (parameters.Length == 2)
{
calcMethod.Invoke(indicator, new object[] { input, double.NaN });
}
else
{
throw new InvalidOperationException($"Invalid number of parameters for Calc method in indicator type: {indicator.GetType().Name}");
}
}
public static IEnumerable<object[]> GetIndicators()
{
return indicators.Select(indicator => new object[] { indicator });
+9 -5
View File
@@ -12,7 +12,8 @@ public class SkenderTests
private readonly GbmFeed feed;
private readonly Random rnd;
private readonly double range;
private int period, iterations;
private int period;
private readonly int iterations;
private readonly IEnumerable<Quote> quotes;
@@ -288,7 +289,6 @@ public class SkenderTests
{
for (int run = 0; run < iterations; run++)
{
//period = rnd.Next(50) + 5;
Mama ma = new(fastLimit: 0.5, slowLimit: 0.05);
TSeries QL = new();
foreach (TBar item in feed)
@@ -335,12 +335,16 @@ public class SkenderTests
TSeries QL = new();
foreach (TBar item in bars) { QL.Add(ma.Calc(item)); }
var SK = quotes.GetAtr(lookbackPeriods: period).Select(i => i.Atr.Null2NaN()!);
Assert.Equal(QL.Length, QL.Length);
var atrValues = quotes.GetAtr(lookbackPeriods: period).Select(i => i.Atr.Null2NaN()!);
const int AdditionalPeriods = 500;
<<<<<<< HEAD
for (int i = QL.Length - 1; i > period + 500; i--)
=======
for (int i = QL.Length - 1; i > period + AdditionalPeriods; i--)
>>>>>>> dev
{
Assert.InRange(SK.ElementAt(i) - QL[i].Value, -range, range);
Assert.InRange(atrValues.ElementAt(i) - QL[i].Value, -range, range);
}
}
}
+145 -156
View File
@@ -1,162 +1,151 @@
using System;
namespace QuanTAlib;
namespace QuanTAlib
public class Afirma : AbstractBase
{
public class Afirma : AbstractBase
public enum WindowType
{
public enum WindowType
{
Rectangular,
Hanning1,
Hanning2,
Blackman,
BlackmanHarris
}
private readonly int Periods;
private readonly int Taps;
private readonly WindowType Window;
private readonly CircularBuffer _buffer;
private readonly double[] _weights;
private readonly double _wsum;
private readonly double[] _armaBuffer;
private readonly int _n;
private readonly double _sx2, _sx3, _sx4, _sx5, _sx6, _den;
public Afirma(int periods, int taps, WindowType window)
{
if (periods < 1)
{
throw new ArgumentOutOfRangeException(nameof(periods), "Periods must be greater than or equal to 1.");
}
if (taps < 1)
{
throw new ArgumentOutOfRangeException(nameof(taps), "Taps must be greater than or equal to 1.");
}
Periods = periods;
Taps = taps;
Window = window;
WarmupPeriod = taps;
_buffer = new CircularBuffer(taps);
_weights = new double[taps];
_wsum = CalculateWeights();
_armaBuffer = new double[taps];
_n = (Taps - 1) / 2;
// Calculate least squares coefficients in the constructor
_sx2 = (2 * _n + 1) / 3.0;
_sx3 = _n * (_n + 1) / 2.0;
_sx4 = _sx2 * (3 * _n * _n + 3 * _n - 1) / 5.0;
_sx5 = _sx3 * (2 * _n * _n + 2 * _n - 1) / 3.0;
_sx6 = _sx2 * (3 * Math.Pow(_n, 3) * (_n + 2) - 3 * _n + 1) / 7.0;
_den = _sx6 * _sx4 / _sx5 - _sx5;
Name = "Afirma";
Init();
}
public Afirma(object source, int periods, int taps, WindowType window) : this(periods, taps, window)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Input.Value;
_index++;
}
}
protected override double Calculation()
{
ManageState(IsNew);
_buffer.Add(Input.Value, Input.IsNew);
if (_index >= Taps)
{
double a0 = _buffer[_n];
double a1 = _buffer[_n] - _buffer[_n + 1];
double sx2y = 0.0;
double sx3y = 0.0;
for (int i = 0; i <= _n; i++)
{
sx2y += i * i * _buffer[_n - i];
sx3y += i * i * i * _buffer[_n - i];
}
sx2y = 2.0 * sx2y / _n / (_n + 1);
sx3y = 2.0 * sx3y / _n / (_n + 1);
double p = sx2y - a0 * _sx2 - a1 * _sx3;
double q = sx3y - a0 * _sx3 - a1 * _sx4;
double a2 = (p * _sx6 / _sx5 - q) / _den;
double a3 = (q * _sx4 / _sx5 - p) / _den;
for (int k = 0; k <= _n; k++)
{
_armaBuffer[_n - k] = a0 + k * a1 + k * k * a2 + k * k * k * a3;
}
}
double result = 0.0;
for (int k = 0; k < Taps; k++)
{
result += _buffer[k] * _weights[k] / _wsum;
}
IsHot = _index >= WarmupPeriod;
return result;
}
private double CalculateWeights()
{
double wsum = 0.0;
double centerTap = (Taps - 1) / 2.0;
for (int k = 0; k < Taps; k++)
{
double windowWeight;
switch (Window)
{
case WindowType.Rectangular:
windowWeight = 1.0;
break;
case WindowType.Hanning1:
windowWeight = 0.50 - 0.50 * Math.Cos(2.0 * Math.PI * k / (Taps - 1));
break;
case WindowType.Hanning2:
windowWeight = 0.54 - 0.46 * Math.Cos(2.0 * Math.PI * k / (Taps - 1));
break;
case WindowType.Blackman:
windowWeight = 0.42 - 0.50 * Math.Cos(2.0 * Math.PI * k / (Taps - 1)) + 0.08 * Math.Cos(4.0 * Math.PI * k / (Taps - 1));
break;
case WindowType.BlackmanHarris:
windowWeight = 0.35875 - 0.48829 * Math.Cos(2.0 * Math.PI * k / (Taps - 1)) + 0.14128 * Math.Cos(4.0 * Math.PI * k / (Taps - 1)) - 0.01168 * Math.Cos(6.0 * Math.PI * k / (Taps - 1));
break;
default:
windowWeight = 1.0;
break;
}
double sincWeight;
if (Math.Abs(k - centerTap) < 1e-10)
{
sincWeight = 1.0;
}
else
{
sincWeight = Math.Sin(Math.PI * (k - centerTap) / Periods) / (Math.PI * (k - centerTap) / Periods);
}
_weights[k] = windowWeight * sincWeight;
wsum += _weights[k];
}
return wsum;
}
Rectangular,
Hanning1,
Hanning2,
Blackman,
BlackmanHarris
}
private readonly int Periods;
private readonly int Taps;
private readonly WindowType Window;
private readonly CircularBuffer _buffer;
private readonly double[] _weights;
private readonly double _wsum;
private readonly double[] _armaBuffer;
private readonly int _n;
private readonly double _sx2, _sx3, _sx4, _sx5, _sx6, _den;
public Afirma(int periods, int taps, WindowType window)
{
if (periods < 1)
{
throw new ArgumentOutOfRangeException(nameof(periods), "Periods must be greater than or equal to 1.");
}
if (taps < 1)
{
throw new ArgumentOutOfRangeException(nameof(taps), "Taps must be greater than or equal to 1.");
}
Periods = periods;
Taps = taps;
Window = window;
WarmupPeriod = taps;
_buffer = new CircularBuffer(taps);
_weights = new double[taps];
_wsum = CalculateWeights();
_armaBuffer = new double[taps];
_n = (Taps - 1) / 2;
// Calculate least squares coefficients in the constructor
_sx2 = (2 * _n + 1) / 3.0;
_sx3 = _n * (_n + 1) / 2.0;
_sx4 = _sx2 * (3 * _n * _n + 3 * _n - 1) / 5.0;
_sx5 = _sx3 * (2 * _n * _n + 2 * _n - 1) / 3.0;
_sx6 = _sx2 * (3 * Math.Pow(_n, 3) * (_n + 2) - 3 * _n + 1) / 7.0;
_den = _sx6 * _sx4 / _sx5 - _sx5;
Name = "Afirma";
Init();
}
public Afirma(object source, int periods, int taps, WindowType window) : this(periods, taps, window)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Input.Value;
_index++;
}
}
protected override double Calculation()
{
ManageState(IsNew);
_buffer.Add(Input.Value, Input.IsNew);
if (_index >= Taps)
{
double a0 = _buffer[_n];
double a1 = _buffer[_n] - _buffer[_n + 1];
double sx2y = 0.0;
double sx3y = 0.0;
for (int i = 0; i <= _n; i++)
{
sx2y += i * i * _buffer[_n - i];
sx3y += i * i * i * _buffer[_n - i];
}
sx2y = 2.0 * sx2y / _n / (_n + 1);
sx3y = 2.0 * sx3y / _n / (_n + 1);
double p = sx2y - a0 * _sx2 - a1 * _sx3;
double q = sx3y - a0 * _sx3 - a1 * _sx4;
double a2 = (p * _sx6 / _sx5 - q) / _den;
double a3 = (q * _sx4 / _sx5 - p) / _den;
for (int k = 0; k <= _n; k++)
{
_armaBuffer[_n - k] = a0 + k * a1 + k * k * a2 + k * k * k * a3;
}
}
double result = 0.0;
for (int k = 0; k < Taps; k++)
{
result += _buffer[k] * _weights[k] / _wsum;
}
IsHot = _index >= WarmupPeriod;
return result;
}
private double CalculateWeights()
{
double wsum = 0.0;
double centerTap = (Taps - 1) / 2.0;
for (int k = 0; k < Taps; k++)
{
double windowWeight;
switch (Window)
{
case WindowType.Rectangular:
windowWeight = 1.0;
break;
case WindowType.Hanning1:
windowWeight = 0.50 - 0.50 * Math.Cos(2.0 * Math.PI * k / (Taps - 1));
break;
case WindowType.Hanning2:
windowWeight = 0.54 - 0.46 * Math.Cos(2.0 * Math.PI * k / (Taps - 1));
break;
case WindowType.Blackman:
windowWeight = 0.42 - 0.50 * Math.Cos(2.0 * Math.PI * k / (Taps - 1)) + 0.08 * Math.Cos(4.0 * Math.PI * k / (Taps - 1));
break;
case WindowType.BlackmanHarris:
windowWeight = 0.35875 - 0.48829 * Math.Cos(2.0 * Math.PI * k / (Taps - 1)) + 0.14128 * Math.Cos(4.0 * Math.PI * k / (Taps - 1)) - 0.01168 * Math.Cos(6.0 * Math.PI * k / (Taps - 1));
break;
default:
windowWeight = 1.0;
break;
}
double sincWeight;
sincWeight = Math.Abs(k - centerTap) < 1e-10 ? 1.0 : Math.Sin(Math.PI * (k - centerTap) / Periods) / (Math.PI * (k - centerTap) / Periods);
_weights[k] = windowWeight * sincWeight;
wsum += _weights[k];
}
return wsum;
}
}
+1 -1
View File
@@ -25,7 +25,7 @@ public class Alma : AbstractBase
/// <param name="offset">Controls the smoothness and high-frequency filtering. Default is 0.85.</param>
/// <param name="sigma">Controls the shape of the Gaussian distribution. Default is 6.</param>
/// <exception cref="ArgumentException">Thrown when period is less than 1.</exception>
public Alma(int period, double offset = 0.85, double sigma = 6) : base()
public Alma(int period, double offset = 0.85, double sigma = 6)
{
if (period < 1)
{
+2 -2
View File
@@ -4,8 +4,8 @@ public class Convolution : AbstractBase
{
private readonly double[] _kernel;
private readonly int _kernelSize;
private CircularBuffer _buffer;
private double[] _normalizedKernel;
private readonly CircularBuffer _buffer;
private readonly double[] _normalizedKernel;
public Convolution(double[] kernel)
{
+1 -1
View File
@@ -28,7 +28,7 @@ public class Dema : AbstractBase
private double _lastEma2, _p_lastEma2;
private double _k, _e, _p_e;
public Dema(int period) : base()
public Dema(int period)
{
if (period < 1)
{
+6 -4
View File
@@ -27,10 +27,12 @@ public class Ema : AbstractBase
private readonly int _period;
private CircularBuffer _sma;
private double _lastEma, _p_lastEma;
private double _k, _e, _p_e;
private bool _isInit, _p_isInit, _useSma;
private double _e, _p_e;
private readonly double _k;
private bool _isInit, _p_isInit;
private readonly bool _useSma;
public Ema(int period, bool useSma = true) : base()
public Ema(int period, bool useSma = true)
{
if (period < 1)
{
@@ -45,7 +47,7 @@ public class Ema : AbstractBase
Init();
}
public Ema(double alpha) : base()
public Ema(double alpha)
{
_k = alpha;
_useSma = false;
+80 -69
View File
@@ -1,99 +1,110 @@
using System;
namespace QuanTAlib
namespace QuanTAlib;
public class Frama : AbstractBase
{
public class Frama : AbstractBase
private readonly int _period;
private readonly CircularBuffer _buffer;
private double _lastFrama;
private double _prevLastFrama;
public Frama(int period)
{
private readonly int _period;
private readonly double _fc;
private CircularBuffer _buffer;
private double _lastFrama;
private double _prevLastFrama;
if (period < 2)
throw new ArgumentException("Period must be at least 2", nameof(period));
public Frama(int period, double fc = 0.5) : base()
_period = period;
_buffer = new CircularBuffer(period);
WarmupPeriod = period;
}
public Frama(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
public override void Init()
{
base.Init();
_buffer.Clear();
_lastFrama = 0;
_prevLastFrama = 0;
}
protected override void ManageState(bool isNew)
{
if (isNew)
{
if (period < 2)
throw new ArgumentException("Period must be at least 2", nameof(period));
_prevLastFrama = _lastFrama;
_index++;
}
else
{
_lastFrama = _prevLastFrama;
}
}
_period = period;
_fc = fc;
_buffer = new CircularBuffer(period);
WarmupPeriod = period;
protected override double Calculation()
{
ManageState(Input.IsNew);
_buffer.Add(Input.Value, Input.IsNew);
if (_buffer.Count < _period)
{
_lastFrama = _buffer.Average();
return _lastFrama;
}
public override void Init()
{
base.Init();
_buffer.Clear();
_lastFrama = 0;
_prevLastFrama = 0;
}
int half = _period / 2;
double hh = double.MinValue, ll = double.MaxValue;
double hh1 = double.MinValue, ll1 = double.MaxValue;
double hh2 = double.MinValue, ll2 = double.MaxValue;
protected override void ManageState(bool isNew)
for (int i = 0; i < _period; i++)
{
if (isNew)
double price = _buffer[i];
hh = Math.Max(hh, price);
ll = Math.Min(ll, price);
if (i < half)
{
_prevLastFrama = _lastFrama;
_index++;
hh1 = Math.Max(hh1, price);
ll1 = Math.Min(ll1, price);
}
else
{
_lastFrama = _prevLastFrama;
hh2 = Math.Max(hh2, price);
ll2 = Math.Min(ll2, price);
}
}
<<<<<<< HEAD
protected override double Calculation()
{
ManageState(Input.IsNew);
_buffer.Add(Input.Value, Input.IsNew);
=======
double n1 = (hh - ll) / _period;
double n2 = (hh1 - ll1 + hh2 - ll2) / (_period / 2);
>>>>>>> dev
if (_buffer.Count < _period)
{
_lastFrama = _buffer.Average();
return _lastFrama;
}
double d = (Math.Log(n2 + double.Epsilon) - Math.Log(n1 + double.Epsilon)) / Math.Log(2);
int half = _period / 2;
double hh = double.MinValue, ll = double.MaxValue;
double hh1 = double.MinValue, ll1 = double.MaxValue;
double hh2 = double.MinValue, ll2 = double.MaxValue;
double alpha = Math.Exp(-4.6 * (d - 1));
alpha = Math.Max(Math.Min(alpha, 1), 0.01); // Ensure alpha is between 0.01 and 1
for (int i = 0; i < _period; i++)
{
double price = _buffer[i];
hh = Math.Max(hh, price);
ll = Math.Min(ll, price);
_lastFrama = alpha * (Input.Value - _lastFrama) + _lastFrama;
if (i < half)
{
hh1 = Math.Max(hh1, price);
ll1 = Math.Min(ll1, price);
}
else
{
hh2 = Math.Max(hh2, price);
ll2 = Math.Min(ll2, price);
}
}
IsHot = _index >= WarmupPeriod;
return _lastFrama;
}
double n1 = (hh - ll) / _period;
double n2 = (hh1 - ll1 + hh2 - ll2) / (_period / 2);
double d = (Math.Log(n2 + double.Epsilon) - Math.Log(n1 + double.Epsilon)) / Math.Log(2);
double alpha = Math.Exp(-4.6 * (d - 1));
alpha = Math.Max(Math.Min(alpha, 1), 0.01); // Ensure alpha is between 0.01 and 1
_lastFrama = alpha * (Input.Value - _lastFrama) + _lastFrama;
IsHot = _index >= WarmupPeriod;
return _lastFrama;
}
protected override double GetLastValid()
{
return _lastFrama;
}
protected override double GetLastValid()
{
return _lastFrama;
}
}
+4 -6
View File
@@ -1,7 +1,7 @@
//not working yet
//TODO consistency test
using QuanTAlib;
namespace QuanTAlib;
public class Htit : AbstractBase
{
@@ -21,7 +21,7 @@ public class Htit : AbstractBase
private double _lastPd = 0;
private double _p_lastPd = 0;
public Htit() : base()
public Htit()
{
Name = "Htit";
WarmupPeriod = 12;
@@ -138,9 +138,7 @@ public class Htit : AbstractBase
{
return ((4 * _itBuffer[0]) + (3 * _itBuffer[1]) + (2 * _itBuffer[2]) + _itBuffer[3]) / 10;
}
else
{
return pr;
}
return pr;
}
}
+1 -1
View File
@@ -15,7 +15,7 @@ public class Hwma : AbstractBase
{
}
public Hwma(int period, double nA, double nB, double nC) : base()
public Hwma(int period, double nA, double nB, double nC)
{
if (period < 1)
{
+12 -6
View File
@@ -1,20 +1,20 @@
using QuanTAlib;
namespace QuanTAlib;
//TODO consistency test
public class Jma : AbstractBase
{
public readonly int Period;
private readonly double _phase;
private readonly int _vshort, _vlong;
private CircularBuffer _values;
private CircularBuffer _voltyShort;
private CircularBuffer _vsumBuff;
private CircularBuffer _avoltyBuff;
private readonly CircularBuffer _values;
private readonly CircularBuffer _voltyShort;
private readonly CircularBuffer _vsumBuff;
private readonly CircularBuffer _avoltyBuff;
private double _beta, _len1, _pow1;
private double _upperBand, _lowerBand, _prevMa1, _prevDet0, _prevDet1, _prevJma;
private double _p_UpperBand, _p_LowerBand, _p_prevMa1, _p_prevDet0, _p_prevDet1, _p_prevJma;
public Jma(int period, double phase = 0, int vshort = 10) : base()
public Jma(int period, double phase = 0, int vshort = 10)
{
if (period < 1)
{
@@ -35,6 +35,12 @@ public class Jma : AbstractBase
Init();
}
public Jma(object source, int period, double phase = 0, int vshort = 10) : this(period, phase, vshort)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
public override void Init()
{
_upperBand = _lowerBand = _prevMa1 = _prevDet0 = _prevDet1 = _prevJma = 0.0;
+1 -3
View File
@@ -1,5 +1,3 @@
using System;
namespace QuanTAlib;
public class Kama : AbstractBase
@@ -9,7 +7,7 @@ public class Kama : AbstractBase
private CircularBuffer? _buffer;
private double _lastKama, _p_lastKama;
public Kama(int period, int fast = 2, int slow = 30) : base()
public Kama(int period, int fast = 2, int slow = 30)
{
if (period < 1)
{
+1 -1
View File
@@ -10,7 +10,7 @@ public class Ltma : AbstractBase
public double Gamma => _gamma;
public Ltma(double gamma = 0.1) : base()
public Ltma(double gamma = 0.1)
{
if (gamma < 0 || gamma > 1)
throw new ArgumentOutOfRangeException(nameof(gamma), "Gamma must be between 0 and 1.");
+3 -2
View File
@@ -8,12 +8,13 @@ public class Maaf : AbstractBase
{
private readonly CircularBuffer _priceBuffer;
private readonly CircularBuffer _smoothBuffer;
private double _prevFilter, _prevValue2, _threshold;
private double _prevFilter, _prevValue2;
private readonly double _threshold;
private double _p_prevFilter, _p_prevValue2;
private readonly int _period;
public Maaf(int Period = 39, double Threshold = 0.002) : base()
public Maaf(int Period = 39, double Threshold = 0.002)
{
_period = Period;
_threshold = Threshold;
+3 -4
View File
@@ -1,17 +1,16 @@
using QuanTAlib;
using System;
namespace QuanTAlib;
public class Mama : AbstractBase
{
private readonly double _fastLimit, _slowLimit;
private CircularBuffer _pr, _sm, _dt, _i1, _q1, _i2, _q2, _re, _im, _pd, _ph;
private readonly CircularBuffer _pr, _sm, _dt, _i1, _q1, _i2, _q2, _re, _im, _pd, _ph;
private double _mama, _fama;
private double _prevMama, _prevFama, _sumPr;
private double _p_prevMama, _p_prevFama, _p_sumPr;
public TValue Fama { get; private set; }
public Mama(double fastLimit = 0.5, double slowLimit = 0.05) : base()
public Mama(double fastLimit = 0.5, double slowLimit = 0.05)
{
Fama = new TValue();
Name = $"Mama({_fastLimit:F2}, {_slowLimit:F2})";
+1 -1
View File
@@ -5,7 +5,7 @@ public class Mgdi : AbstractBase
private readonly int _period;
private readonly double _kFactor;
private double _prevMd, _p_prevMd;
public Mgdi(int period, double kFactor = 0.6) : base()
public Mgdi(int period, double kFactor = 0.6)
{
if (period <= 0)
{
+65 -69
View File
@@ -1,78 +1,74 @@
using System;
using System.Linq;
namespace QuanTAlib;
namespace QuanTAlib
public class Mma : AbstractBase
{
public class Mma : AbstractBase
private readonly int _period;
private readonly CircularBuffer _buffer;
private double _lastMma;
public Mma(int period)
{
private readonly int _period;
private readonly CircularBuffer _buffer;
private double _lastMma;
public Mma(int period) : base()
if (period < 2)
{
if (period < 2)
{
throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 2.");
}
_period = period;
_buffer = new CircularBuffer(period);
Name = "Mma";
WarmupPeriod = period;
Init();
throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 2.");
}
_period = period;
_buffer = new CircularBuffer(period);
Name = "Mma";
WarmupPeriod = period;
Init();
}
public Mma(object source, int period) : this(period)
public Mma(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
public override void Init()
{
base.Init();
_lastMma = 0;
_buffer.Clear();
}
protected override void ManageState(bool isNew)
{
if (isNew)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
public override void Init()
{
base.Init();
_lastMma = 0;
_buffer.Clear();
}
protected override void ManageState(bool isNew)
{
if (isNew)
{
_index++;
}
}
protected override double Calculation()
{
ManageState(Input.IsNew);
_buffer.Add(Input.Value, Input.IsNew);
if (_index >= _period)
{
double T = _buffer.Sum();
double S = CalculateWeightedSum();
_lastMma = (T / _period) + (6 * S) / ((_period + 1) * _period);
}
else
{
// Use simple average until we have enough data points
_lastMma = _buffer.Average();
}
IsHot = _index >= _period;
return _lastMma;
}
private double CalculateWeightedSum()
{
double sum = 0;
for (int i = 0; i < _period; i++)
{
double weight = (_period - (2 * i + 1)) / 2.0;
sum += weight * _buffer[^(i + 1)];
}
return sum;
_index++;
}
}
}
protected override double Calculation()
{
ManageState(Input.IsNew);
_buffer.Add(Input.Value, Input.IsNew);
if (_index >= _period)
{
double T = _buffer.Sum();
double S = CalculateWeightedSum();
_lastMma = (T / _period) + (6 * S) / ((_period + 1) * _period);
}
else
{
// Use simple average until we have enough data points
_lastMma = _buffer.Average();
}
IsHot = _index >= _period;
return _lastMma;
}
private double CalculateWeightedSum()
{
double sum = 0;
for (int i = 0; i < _period; i++)
{
double weight = (_period - (2 * i + 1)) / 2.0;
sum += weight * _buffer[^(i + 1)];
}
return sum;
}
}
+4
View File
@@ -6,7 +6,11 @@ public class Qema : AbstractBase
private readonly Ema _ema1, _ema2, _ema3, _ema4;
private double _lastQema, _p_lastQema;
<<<<<<< HEAD
public Qema(double k1 = 0.2, double k2 = 0.2, double k3 = 0.2, double k4 = 0.2) : base()
=======
public Qema(double k1 = 0.2, double k2 = 0.2, double k3 = 0.2, double k4 = 0.2)
>>>>>>> dev
{
if (k1 <= 0 || k2 <= 0 || k3 <= 0 || k4 <= 0)
{
+6 -2
View File
@@ -12,7 +12,7 @@ public class Rema : AbstractBase
public int Period => _period;
public double Lambda => _lambda;
public Rema(int period, double lambda = 0.5) : base()
public Rema(int period, double lambda = 0.5)
{
if (period < 1)
throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1.");
@@ -25,7 +25,11 @@ public class Rema : AbstractBase
WarmupPeriod = period;
Init();
}
public Rema(object source, int period, double lambda = 0.5) : this(period, lambda)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
public override void Init()
{
base.Init();
+80 -1
View File
@@ -1,6 +1,7 @@
using System;
using System.Runtime.CompilerServices;
<<<<<<< HEAD
namespace QuanTAlib
{
@@ -29,6 +30,80 @@ namespace QuanTAlib
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
=======
namespace QuanTAlib;
public class Rma : AbstractBase
{
private readonly int _period;
private readonly double _alpha;
private double _lastRMA;
private double _savedLastRMA;
public Rma(int period)
{
if (period < 1)
{
throw new ArgumentException("Period must be greater than or equal to 1.", nameof(period));
}
_period = period;
WarmupPeriod = period * 2;
_alpha = 1.0 / _period; // Wilder's smoothing factor
Name = $"Rma({_period})";
Init();
}
public Rma(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
public override void Init()
{
base.Init();
_lastRMA = 0;
_savedLastRMA = 0;
}
protected override void ManageState(bool isNew)
{
if (!isNew)
{
_lastRMA = _savedLastRMA;
return;
}
_savedLastRMA = _lastRMA;
_lastValidValue = Input.Value;
_index++;
}
protected override double Calculation()
{
ManageState(Input.IsNew);
double rma;
if (_index == 1)
{
return Input.Value;
}
if (_index <= _period)
{
// Simple average during initial period
return (_lastRMA * (_index - 1) + Input.Value) / _index;
}
// Wilder's smoothing method
return _alpha * (Input.Value - _lastRMA) + _lastRMA;
}
_lastRMA = rma;
IsHot = _index >= WarmupPeriod;
>>>>>>> dev
public override void Init()
{
@@ -78,4 +153,8 @@ namespace QuanTAlib
return rma;
}
}
}
<<<<<<< HEAD
}
=======
}
>>>>>>> dev
+1 -1
View File
@@ -6,7 +6,7 @@ public class Sma : AbstractBase
// inherited _value
private readonly CircularBuffer _buffer;
public Sma(int period) : base()
public Sma(int period)
{
if (period < 1)
{
+1 -1
View File
@@ -8,7 +8,7 @@ public class Smma : AbstractBase
private CircularBuffer? _buffer;
private double _lastSmma, _p_lastSmma;
public Smma(int period) : base()
public Smma(int period)
{
if (period < 1)
{
+1 -1
View File
@@ -8,7 +8,7 @@ public class Tema : AbstractBase
private double _lastEma3, _p_lastEma3;
private double _k, _e, _p_e;
public Tema(int period) : base()
public Tema(int period)
{
if (period < 1)
{
+1 -8
View File
@@ -30,14 +30,7 @@ public class Trima : AbstractBase
for (int i = 0; i < period; i++)
{
if (i < halfPeriod)
{
kernel[i] = i + 1;
}
else
{
kernel[i] = period - i;
}
kernel[i] = i < halfPeriod ? i + 1 : period - i;
weightSum += kernel[i];
}
+3 -3
View File
@@ -7,11 +7,11 @@ public class Zlema : AbstractBase
{
private readonly int _period;
private CircularBuffer? _buffer;
private double _alpha;
private int _lag;
private readonly double _alpha;
private readonly int _lag;
private double _lastZLEMA, _p_lastZLEMA;
public Zlema(int period) : base()
public Zlema(int period)
{
if (period < 1)
{
+72 -13
View File
@@ -8,13 +8,16 @@ namespace QuanTAlib;
/// and methods used by inheriting indicator types. It handles the basic flow of
/// receiving data, performing calculations, and publishing results.
/// </remarks>
public abstract class AbstractBase : iTValue
public abstract class AbstractBase : ITValue
{
public DateTime Time { get; set; }
public double Value { get; set; }
public bool IsNew { get; set; }
public bool IsHot { get; set; }
public TValue Input { get; set; }
public TValue Input2 { get; set; }
public TBar BarInput { get; set; }
public TBar BarInput2 { get; set; }
public String Name { get; set; } = "";
public int WarmupPeriod { get; set; }
public TValue Tick => new(Time, Value, IsNew, IsHot);
@@ -34,6 +37,11 @@ public abstract class AbstractBase : iTValue
/// <param name="args">The argument containing the new data point.</param>
public void Sub(object source, in ValueEventArgs args) => Calc(args.Tick);
public void Sub(object source1, object source2, in ValueEventArgs args1, in ValueEventArgs args2) =>
Calc(args1.Tick, args2.Tick);
public void Sub(object source, in TBarEventArgs args) => Calc(args.Bar);
/// <summary>
/// Initializes the indicator's state.
/// </summary>
@@ -43,24 +51,75 @@ public abstract class AbstractBase : iTValue
_lastValidValue = 0;
}
/// <summary>
/// Calculates the indicator value based on the input.
/// </summary>
/// <param name="input">The input value for the calculation.</param>
/// <returns>A TValue representing the calculated indicator value.</returns>
/// <remarks>
/// This method calls the specific Calculation() method where the actual implementation is.
/// If the input value is NaN or infinity, it returns the last valid value instead.
/// </remarks>
public virtual TValue Calc(TValue input)
{
Input = input;
if (double.IsNaN(input.Value) || double.IsInfinity(input.Value))
Input2 = new(Time: Input.Time, Value: double.NaN, IsNew: Input.IsNew, IsHot: Input.IsHot);
return HandleErrorCalculations(input.Value, input.Time, input.IsNew);
}
public virtual TValue Calc(TBar barInput)
{
BarInput = barInput;
return HandleErrorCalculations(barInput.Close, barInput.Time, barInput.IsNew);
}
public virtual TValue Calc(TValue input1, TValue input2)
{
Input = input1;
Input2 = input2;
return HandleErrorCalculations(input1.Value, input2.Value, input1.Time, input1.IsNew);
}
public virtual TValue Calc(TBar input1, TBar input2)
{
BarInput = input1;
BarInput2 = input2;
return HandleErrorCalculations(input1.Close, input2.Close, input1.Time, input1.IsNew);
}
/// <summary>
/// Handles error calculations and invalid input values.
/// </summary>
/// <param name="value">The primary input value to check.</param>
/// <param name="time">The timestamp of the input.</param>
/// <param name="isNew">Indicates if the input is new.</param>
/// <returns>A TValue object with the calculated or last valid value.</returns>
/// <remarks>
/// This method checks for NaN or infinity in the input value. If an invalid value is detected,
/// it returns the last valid value. Otherwise, it proceeds with the calculation.
/// </remarks>
protected virtual TValue HandleErrorCalculations(double value, DateTime time, bool isNew)
{
if (double.IsNaN(value) || double.IsInfinity(value))
{
return Process(new TValue(input.Time, GetLastValid(), input.IsNew, input.IsHot));
return Process(new TValue(time, GetLastValid(), isNew, this.IsHot));
}
this.Value = Calculation();
return Process(new TValue(Time: Input.Time, Value: this.Value, IsNew: Input.IsNew, IsHot: this.IsHot));
return Process(new TValue(Time: time, Value: this.Value, IsNew: isNew, IsHot: this.IsHot));
}
/// <summary>
/// Handles error calculations for inputs with two values.
/// </summary>
/// <param name="value1">The first input value to check.</param>
/// <param name="value2">The second input value to check.</param>
/// <param name="time">The timestamp of the input.</param>
/// <param name="isNew">Indicates if the input is new.</param>
/// <returns>A TValue object with the calculated or last valid value.</returns>
/// <remarks>
/// This method checks for NaN or infinity in both input values. If any invalid value is detected,
/// it returns the last valid value. Otherwise, it proceeds with the calculation.
/// </remarks>
protected virtual TValue HandleErrorCalculations(double value1, double value2, DateTime time, bool isNew)
{
if (double.IsNaN(value1) || double.IsInfinity(value1) ||
double.IsNaN(value2) || double.IsInfinity(value2))
{
return Process(new TValue(time, GetLastValid(), isNew, this.IsHot));
}
this.Value = Calculation();
return Process(new TValue(Time: time, Value: this.Value, IsNew: isNew, IsHot: this.IsHot));
}
/// <summary>
+5
View File
@@ -221,11 +221,16 @@ public class CircularBuffer : IEnumerable<double>
if (_start + _size <= Capacity)
{
return new ReadOnlySpan<double>(_buffer, _start, _size);
<<<<<<< HEAD
}
else
{
return new ReadOnlySpan<double>(ToArray());
=======
>>>>>>> dev
}
return new ReadOnlySpan<double>(ToArray());
}
/// <summary>
+1 -1
View File
@@ -9,7 +9,7 @@ public static class Formatters
const string pad = "18";
public static void Initialize()
{
Formatter.Register<iTValue>((tick, writer) =>
Formatter.Register<ITValue>((tick, writer) =>
{
var sb = new StringBuilder();
sb.Append("<table style='border-collapse: collapse; text-align: left;'><tr>");
+2 -2
View File
@@ -1,6 +1,6 @@
namespace QuanTAlib;
public interface iTBar
public interface ITBar
{
DateTime Time { get; }
double Open { get; }
@@ -11,7 +11,7 @@ public interface iTBar
bool IsNew { get; }
}
public readonly record struct TBar(DateTime Time, double Open, double High, double Low, double Close, double Volume, bool IsNew = true) : iTBar
public readonly record struct TBar(DateTime Time, double Open, double High, double Low, double Close, double Volume, bool IsNew = true) : ITBar
{
public DateTime Time { get; init; } = Time;
public double Open { get; init; } = Open;
+6 -5
View File
@@ -1,6 +1,6 @@
namespace QuanTAlib;
public interface iTValue
public interface ITValue
{
DateTime Time { get; }
double Value { get; }
@@ -8,7 +8,7 @@ public interface iTValue
bool IsHot { get; }
}
public readonly record struct TValue(DateTime Time, double Value, bool IsNew = true, bool IsHot = true) : iTValue
public readonly record struct TValue(DateTime Time, double Value, bool IsNew = true, bool IsHot = true) : ITValue
{
public DateTime Time { get; init; } = Time;
public double Value { get; init; } = Value;
@@ -52,12 +52,13 @@ public class TSeries : List<TValue>
var pubEvent = source.GetType().GetEvent("Pub");
if (pubEvent != null)
{
/*
var nameProperty = source.GetType().GetProperty("Name");
if (nameProperty != null) {
if (nameProperty != null)
{
Name = nameProperty.GetValue(nameProperty)?.ToString()!;
}
*/
pubEvent.AddEventHandler(source, new ValueSignal(Sub));
}
}
+4
View File
@@ -8,7 +8,11 @@ public class GbmFeed : TBarSeries
private readonly Random _random;
private double _lastClose, _lastHigh, _lastLow;
<<<<<<< HEAD
public GbmFeed(double initialPrice = 100.0, double mu = 0.05, double sigma = 0.2) : base()
=======
public GbmFeed(double initialPrice = 100.0, double mu = 0.05, double sigma = 0.2)
>>>>>>> dev
{
_lastClose = _lastHigh = _lastLow = initialPrice;
_mu = mu;
+1 -1
View File
@@ -16,7 +16,7 @@ public class Entropy : AbstractBase
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when the period is less than 2.
/// </exception>
public Entropy(int period) : base()
public Entropy(int period)
{
if (period < 2)
{
+1 -1
View File
@@ -16,7 +16,7 @@ public class Kurtosis : AbstractBase
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when the period is less than 4.
/// </exception>
public Kurtosis(int period) : base()
public Kurtosis(int period)
{
if (period < 4)
{
+2 -2
View File
@@ -20,7 +20,7 @@ public class Max : AbstractBase
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when the period is less than 1 or decay is negative.
/// </exception>
public Max(int period, double decay = 0) : base()
public Max(int period, double decay = 0)
{
if (period < 1)
{
@@ -105,7 +105,7 @@ public class Max : AbstractBase
}
double decayRate = 1 - Math.Exp(-_halfLife * _timeSinceNewMax / Period);
_currentMax = _currentMax - decayRate * (_currentMax - _buffer.Average());
_currentMax -= decayRate * (_currentMax - _buffer.Average());
_currentMax = Math.Min(_currentMax, _buffer.Max());
IsHot = true;
+2 -11
View File
@@ -16,7 +16,7 @@ public class Median : AbstractBase
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when the period is less than 1.
/// </exception>
public Median(int period) : base()
public Median(int period)
{
if (period < 1)
{
@@ -76,16 +76,7 @@ public class Median : AbstractBase
Array.Sort(sortedValues);
int middleIndex = sortedValues.Length / 2;
if (sortedValues.Length % 2 == 0)
{
// Even number of values: average of two middle values
median = (sortedValues[middleIndex - 1] + sortedValues[middleIndex]) / 2.0;
}
else
{
// Odd number of values: middle value
median = sortedValues[middleIndex];
}
median = (sortedValues.Length % 2 == 0) ? (sortedValues[middleIndex - 1] + sortedValues[middleIndex]) / 2.0 : sortedValues[middleIndex];
}
else
{
+5 -1
View File
@@ -26,7 +26,11 @@ public class Min : AbstractBase
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when period is less than 1 or decay is negative.
/// </exception>
<<<<<<< HEAD
public Min(int period, double decay = 0) : base()
=======
public Min(int period, double decay = 0)
>>>>>>> dev
{
if (period < 1)
{
@@ -109,7 +113,7 @@ public class Min : AbstractBase
}
double decayRate = 1 - Math.Exp(-_halfLife * _timeSinceNewMin / Period);
_currentMin = _currentMin + decayRate * (_buffer.Average() - _currentMin);
_currentMin += decayRate * (_buffer.Average() - _currentMin);
_currentMin = Math.Max(_currentMin, _buffer.Min());
IsHot = true;
+4
View File
@@ -21,7 +21,11 @@ public class Mode : AbstractBase
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when period is less than 1.
/// </exception>
<<<<<<< HEAD
public Mode(int period) : base()
=======
public Mode(int period)
>>>>>>> dev
{
if (period < 1)
{
+4
View File
@@ -24,7 +24,11 @@ public class Percentile : AbstractBase
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when period is less than 2 or percent is not between 0 and 100.
/// </exception>
<<<<<<< HEAD
public Percentile(int period, double percent) : base()
=======
public Percentile(int period, double percent)
>>>>>>> dev
{
if (period < 2)
{
+4
View File
@@ -22,7 +22,11 @@ public class Skew : AbstractBase
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when period is less than 3.
/// </exception>
<<<<<<< HEAD
public Skew(int period) : base()
=======
public Skew(int period)
>>>>>>> dev
{
if (period < 3)
{
+4
View File
@@ -26,7 +26,11 @@ public class Stddev : AbstractBase
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when period is less than 2.
/// </exception>
<<<<<<< HEAD
public Stddev(int period, bool isPopulation = false) : base()
=======
public Stddev(int period, bool isPopulation = false)
>>>>>>> dev
{
if (period < 2)
{
+4
View File
@@ -26,7 +26,11 @@ public class Variance : AbstractBase
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when period is less than 2.
/// </exception>
<<<<<<< HEAD
public Variance(int period, bool isPopulation = false) : base()
=======
public Variance(int period, bool isPopulation = false)
>>>>>>> dev
{
if (period < 2)
{
+4
View File
@@ -21,7 +21,11 @@ public class Zscore : AbstractBase
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when period is less than 2.
/// </exception>
<<<<<<< HEAD
public Zscore(int period) : base()
=======
public Zscore(int period)
>>>>>>> dev
{
if (period < 2)
{
+16 -4
View File
@@ -8,7 +8,11 @@ namespace QuanTAlib;
/// of the true range. The true range is the greatest of: current high - current low,
/// absolute value of current high - previous close, or absolute value of current low - previous close.
/// </remarks>
<<<<<<< HEAD
public class Atr : AbstractBarBase
=======
public class Atr : AbstractBase
>>>>>>> dev
{
private readonly Ema _ma;
private double _prevClose, _p_prevClose;
@@ -82,23 +86,31 @@ public class Atr : AbstractBarBase
/// </remarks>
protected override double Calculation()
{
<<<<<<< HEAD
ManageState(Input.IsNew);
=======
ManageState(BarInput.IsNew);
>>>>>>> dev
double trueRange = Math.Max(
Math.Max(
Input.High - Input.Low,
Math.Abs(Input.High - _prevClose)
BarInput.High - BarInput.Low,
Math.Abs(BarInput.High - _prevClose)
),
Math.Abs(Input.Low - _prevClose)
Math.Abs(BarInput.Low - _prevClose)
);
if (_index < 2)
{
<<<<<<< HEAD
trueRange = Input.High - Input.Low;
=======
trueRange = BarInput.High - BarInput.Low;
>>>>>>> dev
}
TValue emaTrueRange = _ma.Calc(new TValue(Input.Time, trueRange, Input.IsNew));
IsHot = _ma.IsHot;
_prevClose = Input.Close;
_prevClose = BarInput.Close;
return emaTrueRange.Value;
}
+4
View File
@@ -25,7 +25,11 @@ public class Historical : AbstractBase
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when period is less than 2.
/// </exception>
<<<<<<< HEAD
public Historical(int period, bool isAnnualized = true) : base()
=======
public Historical(int period, bool isAnnualized = true)
>>>>>>> dev
{
if (period < 2)
{
+4
View File
@@ -25,7 +25,11 @@ public class Realized : AbstractBase
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when period is less than 2.
/// </exception>
<<<<<<< HEAD
public Realized(int period, bool isAnnualized = true) : base()
=======
public Realized(int period, bool isAnnualized = true)
>>>>>>> dev
{
if (period < 2)
{
+10 -2
View File
@@ -16,8 +16,8 @@ namespace QuanTAlib;
public class Rvi : AbstractBase
{
private readonly int Period;
private Stddev _upStdDev, _downStdDev;
private Sma _upSma, _downSma;
private readonly Stddev _upStdDev, _downStdDev;
private readonly Sma _upSma, _downSma;
private double _previousClose;
/// <summary>
@@ -27,7 +27,11 @@ public class Rvi : AbstractBase
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when period is less than 2.
/// </exception>
<<<<<<< HEAD
public Rvi(int period) : base()
=======
public Rvi(int period)
>>>>>>> dev
{
if (period < 2)
{
@@ -105,6 +109,7 @@ public class Rvi : AbstractBase
_downSma.Calc(_downStdDev.Calc(new TValue(Input.Time, downMove, Input.IsNew)));
double rvi;
<<<<<<< HEAD
if (_upSma.Value + _downSma.Value != 0)
{
rvi = 100 * _upSma.Value / (_upSma.Value + _downSma.Value);
@@ -113,6 +118,9 @@ public class Rvi : AbstractBase
{
rvi = 0;
}
=======
rvi = (_upSma.Value + _downSma.Value != 0) ? 100 * _upSma.Value / (_upSma.Value + _downSma.Value) : 0;
>>>>>>> dev
_previousClose = close;
IsHot = _index >= WarmupPeriod;
+13 -9
View File
@@ -10,15 +10,19 @@ QuanTAlib.Formatters.Initialize();
#!csharp
Sma ma1 = new(6);
Gmean ma2 = new (6);
Hmean ma3 = new (6);
TSeries input = new();
Sma ma1 = new (6);
Sma ma2 = new (input, 6);
double[] input = new[]{1.0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,12,13,14,15,16,17,18,19,20};
for (int i=0; i<input.Length; i++) {
double out1 = ma1.Calc(input[i]);
double out2 = ma2.Calc(input[i]);
double out3 = ma3.Calc(input[i]);
Random random = new Random();
Console.WriteLine($"{input[i]:F2}\t {out1:F2}\t {out2:F2}\t {out3:F2}");
for (int i = 0; i < 100; i++) {
double randomValue = random.NextDouble() * 100;
input.Add(randomValue);
ma1.Calc(randomValue);
}
#!csharp
display(ma1);
display(ma2);
+1 -1
View File
@@ -7,7 +7,7 @@ public class AtrIndicator : IndicatorBarBase
public int Period { get; set; } = 20;
private Atr? atr;
protected override AbstractBarBase QuanTAlib => atr!;
protected override AbstractBase QuanTAlib => atr!;
public override string ShortName => $"ATR {Period}";
public AtrIndicator()
{
+18 -19
View File
@@ -19,7 +19,7 @@ public abstract class IndicatorBarBase : Indicator, IWatchlistIndicator
// LineSeries.LineSeries(string, Color, int, LineStyle)'
protected LineSeries? Series;
protected abstract AbstractBarBase QuanTAlib { get; }
protected abstract AbstractBase QuanTAlib { get; }
int IWatchlistIndicator.MinHistoryDepths => 0;
@@ -80,7 +80,7 @@ public abstract class IndicatorBarBase : Indicator, IWatchlistIndicator
int barX = (int)converter.GetChartX(Time(i));
int barY = (int)converter.GetChartY(Series![i]);
int halfBarWidth = CurrentChart.BarsWidth / 2;
Point point = new Point(barX + halfBarWidth, barY);
Point point = new(barX + halfBarWidth, barY);
allPoints.Add(point);
}
@@ -94,22 +94,21 @@ public abstract class IndicatorBarBase : Indicator, IWatchlistIndicator
{
if (allPoints.Count < 2) { return; }
using (Pen defaultPen = new(Series!.Color, Series.Width) { DashStyle = ConvertLineStyleToDashStyle(Series.Style) })
using (Pen coldPen = new(Series!.Color, Series.Width) { DashStyle = DashStyle.Dot })
{
// Draw the hot part
if (hotCount > 0)
{
var hotPoints = allPoints.Take(Math.Min(hotCount + 1, allPoints.Count)).ToArray();
gr.DrawCurve(defaultPen, hotPoints, 0, hotPoints.Length - 1, (float)0.1);
}
using Pen defaultPen = new(Series!.Color, Series.Width) { DashStyle = ConvertLineStyleToDashStyle(Series.Style) };
using Pen coldPen = new(Series!.Color, Series.Width) { DashStyle = DashStyle.Dot };
// Draw the cold part
if (ShowColdValues && hotCount < allPoints.Count)
{
var coldPoints = allPoints.Skip(Math.Max(0, hotCount)).ToArray();
gr.DrawCurve(coldPen, coldPoints, 0, coldPoints.Length - 1, (float)0.1);
}
// Draw the hot part
if (hotCount > 0)
{
var hotPoints = allPoints.Take(Math.Min(hotCount + 1, allPoints.Count)).ToArray();
gr.DrawCurve(defaultPen, hotPoints, 0, hotPoints.Length - 1, (float)0.1);
}
// Draw the cold part
if (ShowColdValues && hotCount < allPoints.Count)
{
var coldPoints = allPoints.Skip(Math.Max(0, hotCount)).ToArray();
gr.DrawCurve(coldPen, coldPoints, 0, coldPoints.Length - 1, (float)0.1);
}
}
private static DashStyle ConvertLineStyleToDashStyle(LineStyle lineStyle)
@@ -125,9 +124,9 @@ public abstract class IndicatorBarBase : Indicator, IWatchlistIndicator
}
protected static void DrawText(Graphics gr, string text, Rectangle clientRect)
{
Font font = new Font("Inter", 8);
Font font = new("Inter", 8);
SizeF textSize = gr.MeasureString(text, font);
RectangleF textRect = new RectangleF(clientRect.Left + 5,
RectangleF textRect = new(clientRect.Left + 5,
clientRect.Bottom - textSize.Height - 10,
textSize.Width + 10, textSize.Height + 10);
gr.FillRectangle(SystemBrushes.ControlDarkDark, textRect);